Загрузка данных


// #include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <GY21.h>
#include <FS.h>
#include <SD.h>
#include <ArduinoJson.h>


String modes[] = {"Idle", "Calibration", "Send report"};
char chrmode[] = {'m', 'i', 'c', 'r'};
struct UserData {
  String data;
  String value;
  
};


UserData userdata[50] {};

struct ConfigData {
  String data;
  int value;
  
};


ConfigData configdata[50] {};


char mode = 'm';

int Checktime2 = 0;
int Checktime = 0;
int cursor = 0;

int buzzervolume = 100; // %
int maxbuzzervolume = 2048; //hz


int MAXnormal_gas_level = 800;
int MAXhigh_gas_level = 1500;
int dangerous_gas_level = 2000;


int normaltemp[] = {20, 25};
int hightemp[] = {30, 40};
int dangeroustemp = 50;




bool OKBTN = false;
bool DOWNBTN = false;
bool UPBTN = false;
bool BACKBTN = false;
bool critical;
bool emergency;


const char* userDataPath = "/UserData.json";
const char* configDataPath = "/ConfigData.json";



float startgas;
float starttemp;
float totalDanger;




unsigned long lastSmsTime = 0;
bool smsSent = false;



#define GASPIN 39
#define BUZZERPIN 26
#define REDPIN 12
#define GREENPIN 27
#define BLUEPIN 16
#define SDCS 5
#define UPBUTTON 32
#define OKBUTTON 13
#define DOWNBUTTON 14
#define GSMTX 17
#define GSMRX 16


GY21 sensor;



#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels



#define OLED_RESET     -1 
#define SCREEN_ADDRESS 0x3C




Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setRGB(bool red, bool green, bool blue) {
  digitalWrite(REDPIN, red);
  digitalWrite(GREENPIN, green);
  digitalWrite(BLUEPIN, blue);

}

void setBuzzer(bool status) {
  if (status) {
    float freq = (maxbuzzervolume * buzzervolume) / 100;
    tone(BUZZERPIN, freq);
  } else {
    noTone(BUZZERPIN);
  }
}

bool getuserdata() {
  File file = SD.open(userDataPath, FILE_READ);
  if (!file) {
    Serial.println("Failed to open userdata, all internet and sim features disabled!");
    return false;
  }
  JsonDocument doc;
  DeserializationError error = deserializeJson(doc, file); 
  if (!error) {
        userdata[0].data = "name";
        userdata[0].value = doc["name"].as<String>();

        userdata[1].data = "email";
        userdata[1].value = doc["email"].as<String>();

        userdata[2].data = "address";
        userdata[2].value = doc["address"].as<String>();

        userdata[3].data = "app_password";
        userdata[3].value = doc["app_password"].as<String>();

        userdata[4].data = "wifi_name";
        userdata[4].value = doc["wifi_name"].as<String>();

        userdata[5].data = "wifipass";
        userdata[5].value = doc["wifi_pass"].as<String>();

        userdata[6].data = "emergencyphone";
        userdata[6].value = doc["emergencyphone"].as<String>();
        
        for (int i = 0; i <= 7; i++) {
          Serial.print(userdata[i].data);
          Serial.print(": ");
          Serial.print(userdata[i].value);
        }
        file.close();
        return true;
  } else {
    
    Serial.print("JSON Parsing failed: ");
    Serial.println(error.f_str());
    file.close();
    return false;
  }
  
  
}



bool getconfig() {
  File file = SD.open(configDataPath, FILE_READ);
  if (!file) {
    
    Serial.println("Failed to open config!");
    return false;
  }
  JsonDocument doc;
  DeserializationError error = deserializeJson(doc, file); 
  if (!error) {
        configdata[0].data = "buzzer";
        configdata[0].value = doc["buzzerVolume"].as<int>();

        configdata[1].data = "gas";
        configdata[1].value = doc["normalgas"].as<int>();

        configdata[2].data = "temp";
        configdata[2].value = doc["normaltemp"].as<int>();
        
        for (int i = 0; i <= 3; i++) {
          Serial.print(configdata[i].data);
          Serial.print(": ");
          Serial.print(configdata[i].value);
        }
        file.close();
        return true;
  } else {
    
    Serial.print("JSON Parsing failed: ");
    Serial.println(error.f_str());
    file.close();
    return false;
  }
  
}

void startsaving(char type, float data) {

  if (type == 'T') {
    configdata[2].value = data;
  } else if (type == 'G') {
    configdata[1].value = data;
  }

  JsonDocument doc;

  for (int i = 0; i < 3; i++) {
    if (configdata[i].data.length() > 0) {
      doc[configdata[i].data] = configdata[i].value;
    }
  }
  File file = SD.open(configDataPath, FILE_WRITE);
  if (!file) {
    Serial.print("failed to open config!");
    return;
  }
  if (serializeJsonPretty(doc, file) == 0) {
    Serial.println("failed to dump json!");
  } else {
    Serial.println("Json dumped succsesfully!");
  }
  mode = 'm';
  file.close();
}


void mainmenu() {

}


void idlemode() {
  if (millis() - Checktime >= 300) {
    Checktime = millis();
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(70, 20);
    display.print(analogRead(39));
    display.print(" ppm");
    display.setCursor(10, 20);
    display.print(sensor.GY21_Temperature());
    display.print((char)247);
    display.print("C");
    display.setCursor(20, 50);
    display.print("Danger%: ");
    display.print(totalDanger);
    display.display();
  }

}

void starttempcalibration() {
  display.clearDisplay();
  display.setCursor(0, 10);
  display.print("TEMP: ");
  display.print(starttemp);
  display.setCursor(0, 30);
  display.print("choose level with buttons");
  if (UPBTN) {
    UPBTN = false;
    starttemp += 1;
  } else if (DOWNBTN) {
    DOWNBTN = false;
    starttemp -= 1;
  } else if (OKBTN) {
    startsaving('T', starttemp);
  }
  display.display();

}
void startgascalibration() {
  display.clearDisplay();
  display.setCursor(0, 10);
  display.print("GAS: ");
  display.print(startgas);
  display.setCursor(0, 30);
  display.print("choose level with buttons");
  if (UPBTN) {
    UPBTN = false;
    startgas += 1;
  } else if (DOWNBTN) {
    DOWNBTN = false;
    startgas -= 1;
  } else if (OKBTN) {
    startsaving('G', startgas);
  }
  display.display();
}
void calibration() {
  display.clearDisplay();
  display.setCursor(0, 10);
  display.print("Temp calibration");
  display.setCursor(0, 30);
  display.print("Gas calibration");
  if (cursor == 0) {
    display.setCursor(60, 10);
  } else if (cursor == 1) {
    display.setCursor(60, 30);
  }
  display.print("<");
  if (OKBTN) {
    if (cursor == 0) {
      starttemp = sensor.GY21_Temperature();
      starttempcalibration();
      mode = 'T';
    }
    else if (cursor == 1) {
      startgas = analogRead(39);
      startgascalibration();
      mode = 'G';
    }
    }
  }
  display.display();
  
  
  
}


void sendreport() {

}



void setup() {
  Serial.begin(115200);
  Serial2.begin(9600, SERIAL_8N1, GSMRX, GSMTX); 
  delay(1000);
  Serial2.println("AT");          
  delay(200);
  Serial2.println("AT+CMGF=1");   
  delay(200);
  Serial2.println("AT+CSCS=\"GSM\""); 
  delay(200);
  // SPI.begin();
  Wire.begin(21, 22);
  // Wire.setClock(1000000);
  pinMode(BUZZERPIN, OUTPUT);
  pinMode(REDPIN, OUTPUT);
  pinMode(GREENPIN, OUTPUT);
  pinMode(BLUEPIN, OUTPUT);
  pinMode(SDCS, OUTPUT);
  pinMode(UPBUTTON, INPUT);
  pinMode(DOWNBUTTON, INPUT);
  pinMode(OKBUTTON, INPUT);

  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  delay(100); 
  if (!SD.begin(SDCS)) {
    Serial.println("Sd mount failed!");
    return;
  }
  display.display();

  display.setCursor(0,0);
  display.setTextSize(1);
  
  bool importeduser = getuserdata();
  bool importedconf = getconfig();

  if (importeduser) {
    display.print("Userdata found and imported");
  }
  if (importedconf) {
    display.print("Config found and imported");
    MAXnormal_gas_level = configdata[1].value;
    MAXhigh_gas_level = MAXnormal_gas_level + 700;
    dangerous_gas_level = MAXnormal_gas_level + 1400;

    normaltemp[0] = configdata[2].value;
    normaltemp[1] = normaltemp[0] + 5;
    hightemp[0] = normaltemp[0] + 10;
    hightemp[1] = normaltemp[0] + 20;
    dangeroustemp = 50;
    
    if (configdata[0].value == 0) {
      Serial.println("Buzzer volume is 0, please change it, or it can be problem, when emergency starts");
    }

    buzzervolume = configdata[0].value;
  }


  
  setBuzzer(true);
  delay(400);
  setBuzzer(false);

  delay(2000);
  setRGB(1, 0, 0);
  delay(100);
  setRGB(0, 1, 0);
  delay(100);
  setRGB(0, 0, 1);

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0,0);
  display.setTextSize(1);
  display.print("Hello world!:)");
  display.display();
  Checktime = millis();
  Checktime2 = millis();
}

float calculateDanger(float value,
                      float normal,
                      float dangerous,
                      float high)
{
  float percent;
  if (value <= normal) {
    percent = 0.0;
  }
  else if (value < dangerous) {
    percent = 50.0 * (value - normal) / (dangerous - normal);
  }
  else if (value < high) {
    percent = 50.0 + 50.0 * (value - dangerous) / (high - dangerous);
  }
  else {
    percent = 100.0;
  }
  return constrain(percent, 0.0, 100.0);
}

void modechoose() {
  switch (mode) {
    case 'm':
      mainmenu();
      break;

    case 'i':
      idlemode();
      break;

    case 'c':
      calibration();
      break;

    //case 's':
      //settings();
      //break;
    
    case 'r':
      sendreport();
      break;
    
    case 'T':
      starttempcalibration();
      break;

    case 'G':
      startgascalibration();
      break;

    default:
      break;
  }
}
String compileReport() {
  String message = "FIRE ALARM!";
  message += "Current temperature: " + String(sensor.GY21_Temperature()) + " C\n";
  message += "Gas: " + String(analogRead(39)) + " ppm\n";
  message += "Danger: " + String(totalDanger) + "%\n";
  message += "Name: " + userdata[0].value + "\n";
  message += "Email: " + userdata[1].value + "\n";
  message += "Address: " + userdata[2].value + "\n";
  return message;
}
void sendSMS(String message) {
  if (userdata[6].value.length() <= 5) {
    Serial.println("Phone is incorrect or not included, aborting sending");
    return;
  }
  Serial2.print("AT+CMGS=\"");
  Serial2.print(userdata[6].value);
  Serial2.println("\"");
  delay(500);

  Serial2.print(message);
  delay(100);

  Serial2.write(26); 
  delay(3000); 

  Serial.println("GSM: SMS sent command executed.");
}

void loop() {

  if (critical) {
    if (millis() - Checktime2 >= 300) {
      Checktime2 = millis();
      flashwarning();
      setBuzzer(true);
      delay(10);
    } else {
      setBuzzer(false);
    }
    smsSent = false; 
  }
  if (millis() - Checktime2 >= 2000) {
    Checktime2 = millis();
  }


  if (emergency) {
    flashwarning();
    setBuzzer(true); 
    if (!smsSent) {
      String message = compileReport();
      sendSMS(message);
      smsSent = true; 
    }
  } else if (!critical) {
    setBuzzer(false);
    smsSent = false; 
  }


  float TEMPpercentage = calculateDanger(sensor.GY21_Temperature(), normaltemp[0], dangeroustemp, hightemp[0]);
  float GASpercentage = calculateDanger(analogRead(39), MAXnormal_gas_level, dangerous_gas_level, MAXhigh_gas_level);
  totalDanger = 0.4 * TEMPpercentage + 0.6 * GASpercentage;
  totalDanger = max(totalDanger, max(TEMPpercentage, GASpercentage));
  if (totalDanger >= 70) {
    critical = true;
    emergency = false;
  } 
  else {critical = false;}

  if (totalDanger >= 90) {
    emergency = true;
    critical = false;

  } else {emergency = false;}

  modechoose();
  if (millis() - Checktime >= 300 && mode == 'm') {
    Checktime = millis();
    display.clearDisplay();
    display.setCursor(0, 8);
    display.print("Gas level: ");
    display.print(analogRead(39));
    display.setCursor(0, 16);
    display.print("Temperature: ");
    display.print(sensor.GY21_Temperature());
    display.setCursor(0, 32);
    display.print("Danger%: ");
    display.print(totalDanger);
    display.setTextSize(1);
    display.setCursor(15, 48);
    display.print("--");
    display.print(modes[cursor]);
    display.print("--");
    display.setTextSize(1);
    display.display();
  }
  
  if (digitalRead(DOWNBUTTON) == LOW && digitalRead(UPBUTTON) == LOW) {
    BACKBTN = true;
    DOWNBTN = false;
    UPBTN = false;
    mode = 'm';
    delay(150);

  } else if (digitalRead(DOWNBUTTON) == LOW) {
    DOWNBTN = true;
    if (cursor <= 0) {
      cursor = 0;
    } else {
      cursor -= 1;
    }
    delay(150);

  } else if (digitalRead(UPBUTTON) == LOW) {
    UPBTN = true;
    int len = sizeof(modes) / sizeof(modes[0]);
    if (cursor >= len - 1 && mode = 'm') {
      cursor = len - 1;
    }

    else if (cursor >= 1 && mode == 'c') {
      cursor = 1;
    } 

    else {
      cursor += 1;
    }
    
    delay(150);
  } else {
    BACKBTN = false;
    DOWNBTN = false;
    UPBTN = false;
  }

  if (digitalRead(OKBUTTON) == LOW) {
    OKBTN = true;
    if (mode == 'm') {
       if (cursor == 0) mode = 'i';
       if (cursor == 1) mode = 'c';
       if (cursor == 2) mode = 'r';
       cursor = 0;
    }
    delay(150);
  } else {OKBTN = false;}
  

}