diff --git a/aquaMonitor.ino b/aquaMonitor.ino index e7e436a..2786725 100644 --- a/aquaMonitor.ino +++ b/aquaMonitor.ino @@ -7,6 +7,7 @@ #include "DS1307.h" #include "aquaNet/interComMsg.h" #include "aquaNet/common.h" +#include // 'admin' phone number defined outside of open source file haha ! // This file should contain a line in the likes of: @@ -33,8 +34,9 @@ struct lightSchedule { int maxAcceptableValue; // Max value for light sensor considered ok for this schedule }; -// If not declaring this prototype explicitly, arduino compiler will create one *before* the definition -// of lightSchedule and issue a crappy error message +// If not declaring this prototype explicitly before calling the function, arduino compiler +// will create one *before* the definition of lightSchedule and issue a crappy error message +// because the parameter will not have the right type. boolean checkLightSchedule(int lightLevel, lightSchedule schedule); #define PHONE_NUMBER_LENGTH 15 @@ -66,13 +68,12 @@ struct eepromConfig { #define ONE_STATUS_MSG_LENGTH 20 struct displayData { - char temperatureMsg[ONE_STATUS_MSG_LENGTH + 1]; - char powerMsg[ONE_STATUS_MSG_LENGTH + 1]; - char lightMsg[ONE_STATUS_MSG_LENGTH + 1]; - char levelMsg[ONE_STATUS_MSG_LENGTH + 1]; + char statusMessage[300]; + // the message is scrolling across the first display line + // offset keeps track of the first message char to display unsigned char offset = 0; // TODO size below should somehow be LCD width, but what if LCD disabled with compilation directives ? - char permanent[17]; // Message displayed permanently + char alertMessage[17]; // Message displayed permanently // Transient message will be displayed for short durations unsigned long transientStartDisplayTime=0; unsigned long transientDisplayDuration=2000; // 2 seconds @@ -80,9 +81,20 @@ struct displayData { unsigned long lastRefresh = 0; } display; +struct measureData { + int light = 0; + boolean lightAlert = false; + int powerLevel = 0; + boolean powerAlert = false; + int temperature; // decadegrees: 2500 for 25.00° + boolean temperatureAlert = false; + boolean waterLevelAlert = false; + boolean oneAlert = false; +} measures; + // Flags for services subscriptions and permissions #define FLAG_SERVICE_ALERT 0x01 -#define FLAG_SERVICE_EVENT 0x02 +#define FLAG_SERVICE_EVENT 0x02 // TODO : not implemented yet #define FLAG_ADMIN 0x80 @@ -95,7 +107,7 @@ GSMVoiceCall vcs; // LCD #if WITH_LCD_SUPPORT -// #include are processed no matter what (known bug) : comment or uncomment them is the only way +// TODO check if still true: #include are processed no matter what (known bug) : comment or uncomment them is the only way #include // initialize the library with the interface pins // GMS uses 2 and 3 (at least) @@ -115,7 +127,8 @@ byte addr[MAX_DS1820_SENSORS][8]; // Okay these globals are pretty bad and may still be the cause of a few bugs, but the // small amount of variable space on UNO left me with little choice. -#define PROGMEM_MSG_MAX_SIZE 40 +// May be worth redesigning all this since using Mega, now... +#define PROGMEM_MSG_MAX_SIZE 60 char progMemMsg[PROGMEM_MSG_MAX_SIZE + 1]; #define TEMPERATURE_CHECK_PERIOD 5000 @@ -138,15 +151,13 @@ unsigned long lastPowerCheck = lastTemperatureCheck; unsigned long lastSmsCheck = lastTemperatureCheck; // Check for incoming voice call: if not handled, will break SMS management -#define CALL_CHECK_PERIOD 10000 +#define CALL_CHECK_PERIOD 6000 unsigned long lastCallCheck = lastTemperatureCheck; // Max size of a received SMS #define MAX_SMS_LENGTH 40 boolean gsmEnabled = false; // TSTWIFI -boolean statusOK = true; - #define LEVEL_PIN 11 char serialMessage[MAX_SERIAL_INPUT_MESSAGE]; @@ -229,28 +240,34 @@ void loop(void) { int incomingChar = 0; int length; - // Display is not refreshed at each cycle but only every refreshPeriod - if(checkElapsedDelay(now, display.lastRefresh, display.refreshPeriod)) { - refreshDisplay(); - } - // Sensors are not checked at each cycle either. if(checkElapsedDelay(now, lastLightCheck, LIGHT_CHECK_PERIOD)) { - statusOK = checkLight() && statusOK; + checkLight(); lastLightCheck = now; } if(checkElapsedDelay(now, lastTemperatureCheck, TEMPERATURE_CHECK_PERIOD)) { - statusOK = checkTemperature() && statusOK; + checkTemperature(); lastTemperatureCheck = now; } if(checkElapsedDelay(now, lastLevelCheck, LEVEL_CHECK_PERIOD)) { - statusOK = checkLevel() && statusOK; + checkWaterLevel(); lastLevelCheck = now; } if(checkElapsedDelay(now, lastPowerCheck, POWER_CHECK_PERIOD)) { - statusOK = checkPower() && statusOK; + checkPower(); lastPowerCheck = now; } + measures.oneAlert = measures.lightAlert || measures.powerAlert || measures.temperatureAlert || measures.waterLevelAlert; + + // Display is not refreshed at each cycle but only every refreshPeriod + if(checkElapsedDelay(now, display.lastRefresh, display.refreshPeriod)) { + refreshDisplay(); + } + + if(measures.oneAlert) { + sendAlert(); + } + if(checkElapsedDelay(now, lastSmsCheck, SMS_CHECK_PERIOD)) { checkSMS(); lastSmsCheck = millis(); // checkSMS is a bit slow. @@ -260,45 +277,77 @@ void loop(void) { lastCallCheck = now; } - if(!statusOK) { - sendAlert(); - statusOK = true; - } - // Check USB serial for incoming messages - checkSerial(&Serial, serialMessage, processMessageFromSerial); + if(readFromSerial(&Serial, serialMessage, 50)) { + processMessageFromSerial(serialMessage); + serialMessage[0] = 0; + } // Check serial1 for incoming messages from wifi module - checkSerial(&Serial1, serialMessageFromESP, processMessageFromESP); + if(readFromSerial(&Serial1, serialMessageFromESP, 50)) { + processMessageFromESP(serialMessageFromESP); + serialMessageFromESP[0] = 0; + } } -// Process a message sent by the ESP module +// Process a message sent by the ESP module +// Messages starting with '@' are arduino commands, need to be processed like SMS received +// Messages starting with '#' are ESP requests +// Other messages just need to be forwarded to usb serial (debug and log stuff) void processMessageFromESP(char *message) { +//Serial.println("got message"); +//Serial.println(message); // Message not starting with '#' just needs to be forwarded to USB serial - char *prefix; + char *colonPosition; char *content; char *firstChar = message; - char answer[100]; + char answer[2000]; if(*firstChar == '@') { - // The message sent by ESP is actually a command for the arduino + // The message sent by ESP is actually a command for the arduino like the one it gets from + // the usb serial or sms firstChar++; // check after first # char processMessageFromSerial(firstChar); } else if(*firstChar == '#') { - // The message sent by ESP is actually a specific ESP request + // The message sent by ESP is a specific ESP request firstChar++; // check after first # char - prefix = strtok(firstChar, ":"); - content = strtok(NULL, ":"); - - if(strcmp(prefix, REQUEST_IF_GSM) == 0) { + colonPosition = strchr(firstChar, ':'); + if(colonPosition != NULL) { + content = colonPosition + 1; + } + if(strncmp(firstChar, REQUEST_IF_GSM, strlen(REQUEST_IF_GSM)) == 0) { + Serial.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); // esp wants to know if module is equipped with GSM sprintf(answer, "%s:%d", REQUEST_IF_GSM, gsmEnabled); Serial1.println(answer); - } else if(strcmp(prefix, REQUEST_MEASURES) == 0) { + } else if(strncmp(firstChar, REQUEST_MEASURES, strlen(REQUEST_MEASURES)) == 0) { + Serial.println("ESP wants measures"); // esp wants to know measures to log them - sprintf(answer, "%s:%s, %s, %s, %s.", REQUEST_MEASURES, display.temperatureMsg, display.lightMsg, display.levelMsg, display.powerMsg); + // We'll pass a json object using ArduinoJson library by Benoît Blanchon + // size : https://rawgit.com/bblanchon/ArduinoJson/master/scripts/buffer-size-calculator.html + StaticJsonBuffer<400> jsonBuffer; + JsonObject& root = jsonBuffer.createObject(); + boolean result; + root["temp"] = measures.temperature; + root["tempAlert"] = measures.temperatureAlert; + root["minTemp"] = config.temperatureLowThreshold; + root["maxTemp"] = config.temperatureHighThreshold; + root["light"] = measures.light; + root["lightAlert"] = measures.lightAlert; + // TODO : need to handle appropriate light Schedule for current time... + root["minLight"] = config.lightOn.minAcceptableValue; + root["maxLight"] = config.lightOn.maxAcceptableValue; + root["waterLevelAlert"] = measures.waterLevelAlert; + root["powerAlert"] = measures.powerAlert; + root["oneAlert"] = measures.oneAlert; + sprintf(answer, "%s:", REQUEST_MEASURES); + firstChar = answer; + firstChar += strlen(answer); + root.printTo(firstChar, sizeof(answer) - strlen(answer)); // ok, not pretty... Serial1.println(answer); + //result = writeToSerial(&Serial1, answer, 100); } + } else { // The message sent by ESP should just be sent to USB serial Serial.println(message); @@ -307,7 +356,12 @@ void processMessageFromESP(char *message) { // Process a message received by USB serial void processMessageFromSerial(char *message) { - processMessage(message, ""); + if(*message == '#') { + // Simulates a message received from ESP: process it as such + processMessageFromESP(message); + } else { + processMessage(message, ""); + } } // return true if current time is after given time + delay @@ -326,23 +380,13 @@ boolean checkElapsedDelay(unsigned long now, unsigned long lastTime, unsigned lo } // Check water level on digital input. Returns false if low -boolean checkLevel() { - boolean levelOK = true; - levelOK = (config.highLevelPinValue == digitalRead(LEVEL_PIN)); - if(levelOK) { - sprintf(display.levelMsg, getProgMemMsg(LEVEL_HIGH_MSG)); - deletePermanent(getProgMemMsg(LEVEL_ALERT_MSG)); - } else { - sprintf(display.levelMsg, getProgMemMsg(LEVEL_LOW_MSG)); - displayPermanent(getProgMemMsg(LEVEL_ALERT_MSG)); - } - return(levelOK); +void checkWaterLevel() { + measures.waterLevelAlert = !(config.highLevelPinValue == digitalRead(LEVEL_PIN)); } // return true if temperature is within low and high thresholds -boolean checkTemperature() { - boolean temperatureOK = true; - int highByte, lowByte, tReading, signBit, tc_100, whole, fract; +void checkTemperature() { + int highByte, lowByte, tReading, signBit, tc_100; byte sensor = 0; byte i; @@ -352,8 +396,9 @@ boolean checkTemperature() { if ( OneWire::crc8( addr[sensor], 7) != addr[sensor][7]) { displayTransient(getProgMemMsg(CRC_NOT_VALID_MSG)); } else if ( addr[sensor][0] != 0x28) { - displayTransient(getProgMemMsg(FAMILY_MSG)); - sprintf(display.temperatureMsg, getProgMemMsg(TEMPERATURE_MSG_FORMAT), '+', 0 ); + // When testing with no sensor, set fake temperature at 25.25° + measures.temperature = 2525; + measures.temperatureAlert = false; } else { ds.reset(); ds.select(addr[sensor]); @@ -381,39 +426,27 @@ boolean checkTemperature() { tc_100 = (6 * tReading) + tReading / 4; // multiply by (100 * 0.0625) or 6.25 // user defined signed value to adjust temperature measure tc_100 += config.temperatureAdjustment; - - whole = tc_100 / 100; // separate off the whole and fractional portions - fract = tc_100 % 100; - sprintf(display.temperatureMsg, getProgMemMsg(TEMPERATURE_MSG_FORMAT), signBit ? '-' : '+', whole, fract < 10 ? 0 : fract); - //Serial.println(display.temperatureMsg); + measures.temperature = tc_100; if((tc_100 < config.temperatureLowThreshold) || (tc_100 > config.temperatureHighThreshold)) { - temperatureOK = false; - displayPermanent(getProgMemMsg(TEMPERATURE_ALERT_MSG)); + measures.temperatureAlert = true; } else { - deletePermanent(getProgMemMsg(TEMPERATURE_ALERT_MSG)); + measures.temperatureAlert = false; } } - return temperatureOK; } // Return true if main power is ok boolean checkPower() { - int powerLevel = 0; - boolean powerOK = true; - powerLevel = analogRead(MAIN_POWER_PIN); + measures.powerLevel = analogRead(MAIN_POWER_PIN); // If power level less than threshold => not ok - if(powerLevel < config.powerThreshold) { - powerOK = false; - displayPermanent(getProgMemMsg(POWER_OFF_MSG)); - sprintf(display.powerMsg, getProgMemMsg(POWER_OFF_MSG)); + if(measures.powerLevel < config.powerThreshold) { + measures.powerAlert = true; } else { - sprintf(display.powerMsg, getProgMemMsg(POWER_ON_MSG)); - deletePermanent(getProgMemMsg(POWER_OFF_MSG)); + measures.powerAlert = false; } - return(powerOK); } -// check if light is ok depending on current time +// Check if light level is within acceptable limits depending on current hour boolean checkLightSchedule(int lightLevel, lightSchedule schedule) { boolean result = true; unsigned int startMin; @@ -444,29 +477,16 @@ boolean checkLightSchedule(int lightLevel, lightSchedule schedule) { } // Return true if light level is within boundaries depending on current time -boolean checkLight() { - int lightLevel = 0; - boolean lightOK = true; - lightLevel = analogRead(LIGHT_PIN); - sprintf(display.lightMsg, getProgMemMsg(LIGHT_MSG_FORMAT), lightLevel); - - lightOK = checkLightSchedule(lightLevel, config.lightOn) && checkLightSchedule(lightLevel, config.lightOff); - - // If light level less than threshold during light on period => not ok - if(lightOK) { - deletePermanent(getProgMemMsg(LIGHT_ALERT_MSG)); - } else { - displayPermanent(getProgMemMsg(LIGHT_ALERT_MSG)); - } - return(lightOK); +void checkLight() { + measures.light = analogRead(LIGHT_PIN); + measures.lightAlert = !checkLightSchedule(measures.light, config.lightOn) || !checkLightSchedule(measures.light, config.lightOff); } // Check for incoming voice call, in order to not break SMS void checkCall() { switch (vcs.getvoiceCallStatus()) { case RECEIVINGCALL: - Serial.println("RECEIVING CALL"); - vcs.answerCall(); + vcs.answerCall(); delay(1000); vcs.hangCall(); break; @@ -809,16 +829,13 @@ void unsubscribe(char *number, char *msgIn) { void sendAlert() { unsigned char i, flag; unsigned long now = millis(); // We don't want to send too many SMS - char txtMsg[160 + 1]; - sprintf(txtMsg, getProgMemMsg(ALERT_MSG_FORMAT), display.permanent, display.temperatureMsg, display.lightMsg, display.levelMsg, display.powerMsg); - txtMsg[160] = 0; // just in case for(i=0; i < MAX_PHONE_NUMBERS; i++) { // If phone number initialized AND subscribed to the alert service if((config.registeredNumbers[i].number[0] != 0) && (0 != (config.registeredNumbers[i].permissionFlags & FLAG_SERVICE_ALERT)) ) { // If enough time since last alert SMS was sent to this number, send a new one if(checkElapsedDelay(now, config.registeredNumbers[i].lastAlertSmsTime, config.registeredNumbers[i].minAlertInterval)) { - sendSMS(config.registeredNumbers[i].number, txtMsg); + sendSMS(config.registeredNumbers[i].number, display.statusMessage); config.registeredNumbers[i].lastAlertSmsTime = now; } } @@ -827,14 +844,7 @@ void sendAlert() { // Send an SMS with the status to the given phone number void sendStatus(char *toNumber) { - char txtMsg[5*ONE_STATUS_MSG_LENGTH + 15 + 1]; - if(display.permanent[0] == 0) { - sprintf(txtMsg, "%s, %s, %s, %s.", display.temperatureMsg, display.lightMsg, display.levelMsg, display.powerMsg); - } else { - sprintf(txtMsg, "%s: %s, %s, %s, %s.", display.permanent, display.temperatureMsg, display.lightMsg, display.levelMsg, display.powerMsg); - } - txtMsg[5*ONE_STATUS_MSG_LENGTH + 15] = 0; // just in case - sendSMS(toNumber, txtMsg); + sendSMS(toNumber, display.statusMessage); } // Send any kind of SMS to any give number @@ -1069,42 +1079,86 @@ void resetConfig(char *toNumber) { sendSMS(toNumber, getProgMemMsg(CONFIG_RESET_MSG)); } -// In charge of displaying messages +// In charge of displaying messages. +// The scrolled message provides all measures, scrolling across the first display line void refreshDisplay() { - char message[100]; - char scrolledMessage[100]; + char scrolledMessage[200]; char transferChar; int remaining; unsigned long now = millis(); + char waterLevelMessage[50]; + char powerMessage[50]; + char format[50]; + char temperature[6] ; + + // AVR does not support %f format in *printf functions. Too bad. + sprintf(temperature, "%04d", measures.temperature); + temperature[5] = 0; + temperature[4] = temperature[3]; + temperature[3] = temperature[2]; + temperature[2] = '.'; + display.alertMessage[0] = 0; + if(measures.lightAlert) { + strcpy(display.alertMessage, getProgMemMsg(LIGHT_ALERT_MSG)); + } + if(measures.waterLevelAlert) { + strcpy(display.alertMessage, getProgMemMsg(LEVEL_ALERT_MSG)); + strcpy(waterLevelMessage, getProgMemMsg(LEVEL_LOW_MSG)); + } else { + strcpy(waterLevelMessage, getProgMemMsg(LEVEL_HIGH_MSG)); + } + if(measures.temperatureAlert) { + strcpy(display.alertMessage, getProgMemMsg(TEMPERATURE_ALERT_MSG)); + } + if(measures.powerAlert) { + strcpy(display.alertMessage, getProgMemMsg(POWER_ALERT_MSG)); + strcpy(powerMessage, getProgMemMsg(POWER_OFF_MSG)); + } else { + strcpy(powerMessage, getProgMemMsg(POWER_ON_MSG)); + } + display.alertMessage[16] = 0; + + if(measures.oneAlert) { + sprintf(display.statusMessage, getProgMemMsg(MEASURE_ALERT_MSG_FORMAT), + display.alertMessage, + temperature, + waterLevelMessage, + measures.light, + powerMessage); + } else { + sprintf(display.statusMessage, getProgMemMsg(MEASURE_MSG_FORMAT), + temperature, + waterLevelMessage, + measures.light, + powerMessage); + } display.lastRefresh = millis(); + // Do not send to serial as often ! if(checkElapsedDelay(now, lastSerialStatus, SERIAL_STATUS_PERIOD)) { - Serial.print(display.temperatureMsg); - Serial.print(" "); - Serial.print(display.lightMsg); - Serial.print(" "); - Serial.print(display.levelMsg); - Serial.print(" "); - Serial.println(display.powerMsg); + Serial.println(display.statusMessage); lastSerialStatus = now; } - sprintf(message, "%s, %s, %s, %s, ", display.temperatureMsg, display.lightMsg, display.levelMsg, display.powerMsg); - remaining = strlen(message) - display.offset; - strncpy(scrolledMessage, message + display.offset, 16); - strncat(scrolledMessage, message, display.offset ); + #if WITH_LCD_SUPPORT + // Handle scrolling message on display first line + strcat(display.statusMessage, ", "); // scrolling message has no begining, no end + remaining = strlen(display.statusMessage) - display.offset; + strncpy(scrolledMessage, display.statusMessage + display.offset, 16); + strncat(scrolledMessage, display.statusMessage, display.offset ); scrolledMessage[16] = 0; display.offset++; if(remaining == 0) { display.offset = 0; } - #if WITH_LCD_SUPPORT + lcd.setCursor(0,0); + lcd.print(scrolledMessage); + if(checkElapsedDelay(millis(), display.transientStartDisplayTime, display.transientDisplayDuration)) { lcd.clear(); - displayPermanent(NULL); + displayAlertMessage(); } - lcd.setCursor(0,0); - lcd.print(scrolledMessage); + #endif } @@ -1120,23 +1174,15 @@ void displayTransient(char *msg) { Serial.println(msg); } -void displayPermanent(char *msg) { - if(msg != NULL) { - strncpy(display.permanent, msg, 16); - display.permanent[16] = 0; - } -#if WITH_LCD_SUPPORT - lcd.setCursor(0,1); - lcd.print(display.permanent); -#endif -} - -void deletePermanent(char *msg) { - if(0 == strncmp(display.permanent, msg, 16)) { -#if WITH_LCD_SUPPORT - lcd.clear(); -#endif - display.permanent[0] = 0; +void displayAlertMessage() { + if(display.alertMessage[0] != 0) { + while(strlen(display.alertMessage) < 16) { + strcat(display.alertMessage, " "); + } + #if WITH_LCD_SUPPORT + lcd.setCursor(0,1); + lcd.print(display.alertMessage); + #endif } } diff --git a/aquaNet/aquaNet.ino b/aquaNet/aquaNet.ino index 6dd1c65..fcc3314 100644 --- a/aquaNet/aquaNet.ino +++ b/aquaNet/aquaNet.ino @@ -4,7 +4,7 @@ #include #include #include - +#include // The aquaMonitorSecret.h should be stored in a same-name sub directory of // your libraries directory. It's obviously not in the git repository :) // It should define these: @@ -22,13 +22,12 @@ // localizable messages intended to be displayed/sent to usb serial #include "aquaNetMessages.h" -#define MAX_SERIAL_INPUT_MESSAGE 200 // Structure to hold all wifi-related configuration -#define CONFIG_VERSION 'D' +#define CONFIG_VERSION 'F' #define DEFAULT_AP_SSID "aquaMonitor" // AP ssid by default, known by all devices, can be changed #define DEFAULT_AP_PWD "aquaPassword" // 8 CHAR OR MORE !! AP pwd by default so that other devices can connect #define DEFAULT_STAT_INTERVAL 300000 // update stats every 5 minutes by default -#define DEFAULT_ARDUINO_CHECK_INTERVAL 2000 // check incoming message from arduino every 10s by default +#define DEFAULT_ARDUINO_CHECK_INTERVAL 2000 // check incoming message from arduino every x milliseconds by default struct eepromConfig { unsigned char version; // Always keep it as first member when modifying this structure char statisticsHost[50]; // hostname to send statistics to; e.g. http://www.myStats.com @@ -49,9 +48,10 @@ MDNSResponder mdns; int clientConnected = 0; boolean homeWifiConnected = false; +char serialMessage[MAX_SERIAL_INPUT_MESSAGE]; unsigned long lastStatSent = 0; unsigned long lastArduinoCheck = 0; -char aquaStatus[100]; +char aquaStatus[500]; // Json string containing data from arduino // return true if current time is after given time + delay boolean checkElapsedDelay(unsigned long now, unsigned long lastTime, unsigned long delay) { @@ -91,7 +91,7 @@ void readConfig() { strcpy(config.homePwd, DEFAULT_WIFI_PWD); strcpy(config.APSsid, DEFAULT_AP_SSID); strcpy(config.APPwd, DEFAULT_AP_PWD); - + strcpy(config.deviceName, "Aquarium"); config.statisticsInterval = DEFAULT_STAT_INTERVAL; // number of milliseconds between two statistic sending config.arduinoCheckInterval = DEFAULT_ARDUINO_CHECK_INTERVAL; // number of milliseconds between two arduino msg checks @@ -113,14 +113,15 @@ void setup(void){ char message[100]; unsigned int configSize = sizeof(config); + serialMessage[0] = 0; - strcpy(aquaStatus, "Not yet known"); + strcpy(aquaStatus, "[]"); // No data yet EEPROM.begin(configSize); // Config is read from and stored to EEPROM Serial.begin(9600); WiFi.mode(WIFI_STA); // ap could be active from previous time delay(10000); // delay to connect monitor - Serial.println(""); + Serial.println("Init aquaNet"); readConfig(); // Connect to home network if any @@ -151,8 +152,9 @@ void setup(void){ server.on("/", [](){ printHTMLPage(); }); + server.on("/msgArduino", [](){ - // We assume there is only one param + // Should be only one param : the command to send to arduino char command[200]; server.arg(0).toCharArray(command, 50); sendArduinoCommand(command); @@ -173,20 +175,18 @@ void setup(void){ }); server.on("/getData.json", [](){ - char data[] = "[{ \"name\": \"The Nursery\", \"type\": 0, \"localIP\": \"192.168.0.29\", \"APName\": \"AquaNet\", \"APIP\": \"192.168.4.9\", \"temperature\": 25.2, \"temperatureAlert\": 0, \"minTemperature\": 25, \"maxTemperature\": 27, \"light\": 500, \"lightAlert\": 0, \"minLight\": 0, \"maxLight\": 500, \"waterLevel\": 1, \"power\": 1 }, { \"name\": \"The Jobert\", \"type\": 1, \"localIP\": \"192.168.0.30\", \"APName\": \"AquaNet\", \"APIP\": \"192.168.4.10\", \"temperature\": 24, \"temperatureAlert\": 1, \"minTemperature\": 25, \"maxTemperature\": 27, \"light\": 800, \"lightAlert\": 0, \"minLight\": 700, \"maxLight\": 1024, \"waterLevel\": 1, \"power\": 1 }]"; - server.send(200, "text/plain", data); + server.send(200, "text/plain", aquaStatus); }); + // Ask Arduino if it is equipped with GSM + sendArduino(REQUEST_IF_GSM); + server.begin(); Serial.println("HTTP server started"); //WiFi.printDiag(Serial); - - // Ask Arduino if it is equipped with GSM - sendArduino(REQUEST_IF_GSM); } char *processMethod(char *method) { - if(strcmp(method, "status") == 0) { return(aquaStatus); } else { @@ -207,7 +207,7 @@ void startAP() { // `WiFi.macAddress(mac)` is for STA, `WiFi.softAPmacAddress(mac)` is for AP. // `WiFi.localIP()` is for STA, `WiFi.softAPIP()` is for AP. // `WiFi.printDiag(Serial)` will print out some diagnostic info - Serial.println("Creating AP"); + Serial.println(CREATING_AP); Serial.println(config.APSsid); Serial.println(config.APPwd); WiFi.mode(WIFI_AP_STA); @@ -215,55 +215,71 @@ void startAP() { } -char serialMessage[MAX_SERIAL_INPUT_MESSAGE]; void loop(void) { - int now = millis(); - + unsigned long now = millis(); + boolean result ; server.handleClient(); if ((config.statisticsHost[0] !=0) && homeWifiConnected && checkElapsedDelay(now, lastStatSent, config.statisticsInterval/10)) { sendArduino(REQUEST_MEASURES); - lastStatSent = now; // Don't wait for actual sending to not request again next loop + lastStatSent = now; // Don't wait for response to not request again next loop } - if(checkElapsedDelay(now, lastArduinoCheck, config.arduinoCheckInterval)) { - // Check serial for incoming messages from arduino - checkSerial(&Serial, serialMessage, processMessage); - lastArduinoCheck = now; + if(readFromSerial(&Serial, serialMessage, 50)) { + processMessage(serialMessage); + serialMessage[0] = 0; } } // Process a message from the arduino. It should contain two parts separated with ':' void processMessage(char *message) { - char *prefix; + char *colonPosition; char *content; - prefix = strtok(message, ":"); - if(prefix != NULL) { - content = message + strlen(prefix) + 1; + +//Serial.println("ESP GOT "); +//Serial.println(message); + + colonPosition = strchr(message, ':'); + if(colonPosition != NULL) { + content = colonPosition + 1; // Check prefix for what needs to be done - if(strcmp(prefix, REQUEST_IF_GSM) == 0) { + if(strncmp(message, REQUEST_IF_GSM, strlen(REQUEST_IF_GSM)) == 0) { // If module equipped with GSM, start Wifi Access Point if(content[0] == '1') { startAP(); } else { + Serial.println(CLOSING_AP); WiFi.mode(WIFI_STA); // Stop the access point } - } else if(strcmp(prefix, REQUEST_MEASURES) == 0) { - strcpy(aquaStatus, content); + } else if(strncmp(message, REQUEST_MEASURES, strlen(REQUEST_MEASURES)) == 0) { + // StaticJsonBuffer size : https://rawgit.com/bblanchon/ArduinoJson/master/scripts/buffer-size-calculator.html + StaticJsonBuffer<600> jsonBuffer; + JsonObject& root = jsonBuffer.parseObject(content); + root["name"] = config.deviceName; + root["type"] = 0; // TODO + root["localIP"] = WiFi.localIP().toString(); + root["APName"] = config.APSsid; + root["APIP"] = WiFi.softAPIP().toString(); + // V3 will handle several modules => array of data. Already handled by webApp. + // Will improve this when implementing V3. Good enough for now + strcpy(aquaStatus, "["); + char *firstChar = aquaStatus; + firstChar++; + root.printTo(firstChar, sizeof(aquaStatus) -1); + strcat(aquaStatus, "]"); + Serial.println(aquaStatus); sendStat(); } } - - } void sendStat() { - char request[200]; - char param[200]; + char request[2100]; // Todo use String + char param[2000]; if (client.connect(config.statisticsHost, 80)) { clientConnected = 1; - sprintf(request, "GET %s?stat=%s HTTP/1.1", config.statisticsPath, urlEncode(aquaStatus, param)); + sprintf(request, "GET %s?stat=%s HTTP/1.1", config.statisticsPath, urlEncode(aquaStatus, param, sizeof(param))); Serial.println(request); client.println(request); sprintf(request, "Host: %s", config.statisticsHost); @@ -281,14 +297,17 @@ void sendStat() { } // Send a request to Arduino. -// Requests for esp-arduino conversation start with '#' +// Requests for esp-arduino specific conversation start with '#' +// It's possible to send arduino commands with '@' as first character // Other messages sent to arduino via Serial will be forwarded to arduino's USB serial void sendArduino(char *msg) { char message[200]; if(*msg == '@') { - sprintf(message, "%s", msg); // ESP command sent to arduino, i.e. chkGSM ... + // Arduino command. i.e. status, or config + sprintf(message, "%s", msg); } else { - sprintf(message, "#%s", msg); // Arduino command. i.e. status, or config + // ESP command sent to arduino, i.e. chkGSM ... + sprintf(message, "#%s", msg); } Serial.println(message); } @@ -302,7 +321,7 @@ void sendArduinoCommand(char *msg) { Serial.println(message); } -char *urlEncode(char *toEncode, char *encoded) { +char *urlEncode(char *toEncode, char *encoded, int size) { // char to not encode char notEncode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"; char *scanIn = toEncode; @@ -314,8 +333,12 @@ char *urlEncode(char *toEncode, char *encoded) { } else { char buf[4]; sprintf(buf, "%%%02X", *scanIn); - strcat(scanOut, buf); - scanOut += 3; + // if((strlen(encoded) + strlen(buf)) < size +1) { + strcat(scanOut, buf); + scanOut += 3; +// } else { +// Serial.println("Message to encode too big"); +// } } scanIn ++; } diff --git a/aquaNet/aquaNetMessages.h b/aquaNet/aquaNetMessages.h index a9b5f4e..f1b41a3 100644 --- a/aquaNet/aquaNetMessages.h +++ b/aquaNet/aquaNetMessages.h @@ -10,3 +10,5 @@ #define CONNECTED_HOME_NETWORK "Connected %s" #define CONNECTION_TIMED_OUT "Wifi time out" #define ACCESS_IP "IP: " +#define CREATING_AP "Creating Access Point" +#define CLOSING_AP "Closing Access Point" diff --git a/aquaNet/common.cpp b/aquaNet/common.cpp index cd5a573..f9d174b 100644 --- a/aquaNet/common.cpp +++ b/aquaNet/common.cpp @@ -1,25 +1,58 @@ +#include // To get millis() #include "common.h" -void checkSerial(HardwareSerial *serial, char *message, void (*processMsgFunc)(char *)) { - char incomingChar; +// Serial has a ~60 bytes buffer. If messages longer than 60 bytes are not read fast enough, they can be lost. +boolean readFromSerial(HardwareSerial *serial, char *message, unsigned long timeOut) { + int incomingChar; int length; + unsigned long now = millis(); - while (serial->available() > 0) { + while (true) { incomingChar = serial->read(); if(incomingChar > 0) { if((incomingChar == '\r') || (incomingChar == '\n')) { - processMsgFunc(message); + if(strlen(message) > 1) { // do not process the extra \n or \r when println was used + return true; + } message[0] = 0; + return false; // Message should not be processed } else { length = strlen(message); if(length < MAX_SERIAL_INPUT_MESSAGE - 2) { message[length] = incomingChar; message[length + 1] = 0; } else { - // Ignore message, for now + // Ignore message message[0] = 0; + Serial.println("Serial message too big"); + return false; } } + } else { + if((millis() - now) > timeOut) { + // Keep what was read, we'll get the rest at next call... ? + //Serial.println("Time out reading on serial"); + return false; + } } } } + +// Write to serial, insuring message is read to not overload the buffer +boolean writeToSerial(HardwareSerial *serial, char *message, long int timeOut) { + char *charPtr; + int length; + unsigned long now = millis(); + charPtr = message; + while(*charPtr) { + if(serial->availableForWrite()) { + serial->write(charPtr++, 1); + } else { + if((millis() - now) > timeOut) { + //Serial.println("Time out writing on serial"); + return false; + } + } + } + serial->write(0x10); // line feed to end message +} \ No newline at end of file diff --git a/aquaNet/common.h b/aquaNet/common.h index dd89b30..2048000 100644 --- a/aquaNet/common.h +++ b/aquaNet/common.h @@ -1,4 +1,5 @@ #include -#define MAX_SERIAL_INPUT_MESSAGE 161 +#define MAX_SERIAL_INPUT_MESSAGE 600 // some "big" Json strings -void checkSerial(HardwareSerial *serial, char *message, void (*processMsgFunc)(char *)); \ No newline at end of file +boolean readFromSerial(HardwareSerial *serial, char *message, unsigned long timeOut); +boolean writeToSerial(HardwareSerial *serial, char *message, unsigned long timeOut); diff --git a/aquaNet/www/getData.json b/aquaNet/www/getData.json index aa8f77d..8b6ec33 100644 --- a/aquaNet/www/getData.json +++ b/aquaNet/www/getData.json @@ -4,47 +4,49 @@ "localIP": "192.168.0.29", "APName": "AquaNet", "APIP": "192.168.4.9", - "temperature": 25.2, - "temperatureAlert": 0, - "minTemperature": 25, - "maxTemperature": 27, + "temp": 2520, + "tempAlert": false, + "minTemp": 2500, + "maxTemp": 2700, "light": 500, - "lightAlert": 0, + "lightAlert": false, "minLight": 0, "maxLight": 500, - "waterLevel": 1, - "power": 1 - + "waterLevelAlert": false, + "powerAlert": false, + "oneAlert": false }, { "name": "My Jobert", "type": 1, "localIP": "192.168.0.30", "APName": "AquaNet", "APIP": "192.168.4.10", - "temperature": 24, - "temperatureAlert": 1, - "minTemperature": 25, - "maxTemperature": 27, + "temp": 2400, + "tempAlert": true, + "minTemp": 2500, + "maxTemp": 2700, "light": 800, - "lightAlert": 0, + "lightAlert": false, "minLight": 700, "maxLight": 1024, - "waterLevel": 1, - "power": 1 + "waterLevelAlert": false, + "powerAlert": false, + "oneAlert": false }, { "name": "My Nursery", "type": 1, "localIP": "192.168.0.31", "APName": "AquaNet", "APIP": "192.168.4.11", - "temperature": 26, - "temperatureAlert": 0, - "minTemperature": 25, - "maxTemperature": 27, + "temp": 2600, + "tempAlert": 0, + "minTemp": 2500, + "maxTemp": 2700, "light": 822, "lightAlert": 0, "minLight": 700, "maxLight": 1024, - "waterLevel": 1, - "power": 1 + "waterLevelAlert": false, + "powerAlert": false, + "oneAlert": false }] \ No newline at end of file diff --git a/aquaNet/www/js/aquaNet.js b/aquaNet/www/js/aquaNet.js index 7288b96..73a921b 100644 --- a/aquaNet/www/js/aquaNet.js +++ b/aquaNet/www/js/aquaNet.js @@ -9,35 +9,33 @@ $(document).ready(function() { localIP: "", // IP on the local domestic wifi network APName: "", // Name of the created wifi network APIP: "", // IP on the Access Point created wifi network - temperature: 0, - temperatureAlert: "no", - minTemperature: 0, - maxTemperature: 0, + temp: 0, + tempAlert: "no", + minTemp: 0, + maxTemp: 0, light: 0, lightAlert: "no", minLight: 0, maxLight: 0, waterLevel: "high", power: "on", // off, on - globalAlert: "no" + oneAlert: "no" }; return model; }, // Server returns some numeric values, not suited to use as class names parse: function(data) { - data.globalAlert = "noalert"; - if(!data.power || !data.waterLevel - || data.lightAlert || data.temperatureAlert) { - data.globalAlert = 'isalert'; - } - data.power = data.power ? "on" : "off"; - data.waterLevel = data.waterLevel ? "high" : "low"; + data.power = data.powerAlert ? "off" : "on"; + data.waterLevel = data.waterLevelAlert ? "low": "high" ; data.lightAlert = data.lightAlert ? "isalert" : "noalert"; - data.temperatureAlert = data.temperatureAlert ? "isalert" : "noalert"; + data.tempAlert = data.tempAlert ? "isalert" : "noalert"; data.type = data.type ? "master" : "station"; - + data.name = data.name || "Aquarium"; + data.temp = data.temp/100; + data.minTemp = data.minTemp/100; + data.maxTemp = data.maxTemp/100; return data; } }); @@ -55,13 +53,13 @@ $(document).ready(function() { var AquaNetView = Backbone.View.extend({ tagName: "div", template: _.template('\ -
\ +
\
<%- name %>
\
<%- localIP %>
\
<%- APName %>
\
<%- APName %><%- APIP %>
\ -
<%- temperature %>
\ -
<%- minTemperature %> - <%- maxTemperature %>
\ +
<%- temp %>
\ +
<%- minTemp %> - <%- maxTemp %>
\
<%- light %>
\
<%- minLight %> - <%- maxLight %>
\
\ diff --git a/messages_en.str b/messages_en.str index feb158e..579fc38 100644 --- a/messages_en.str +++ b/messages_en.str @@ -53,22 +53,25 @@ IN_SMS_TEMP_FORMAT "temp %d %d" IN_SMS_UNSUB "unsub " IN_SMS_UNSUB_FORMAT "unsub %s" LEVEL_ALERT_MSG "Level alert" -LEVEL_HIGH_MSG "Level: high" -LEVEL_LOW_MSG "Level: LOW" +LEVEL_HIGH_MSG "high" +# GSM end SMS message when encountering the letter 'w' => using uppercase +LEVEL_LOW_MSG "LOW" LIGHT_ALERT_MSG "Light alert" -LIGHT_MSG_FORMAT "Light: %d" LIGHT_SCHEDULE_MSG_FORMAT "Light schedule %s: %02d:%02d-%02d:%02d" LIGHT_SCHEDULE_SET_MSG "Light schedule set (save ?)" LIGHT_THRESHOLD_MSG_FORMAT "Light limits %s: %d-%d" LIGHT_THRESHOLD_SET_MSG "Light threshold set (save ?)" +MEASURE_ALERT_MSG_FORMAT "ALERT %s - Temp: %.2f, Level: %s, Light: %d, POWER: %s" +MEASURE_MSG_FORMAT "Temp: %.2f, Level: %s, Light: %d, POWER %s" NEW_CONFIG_MSG "NEW config" NOT_CONNECTED_GSM_MSG "Check GSM PIN" NUMBER_NOT_SUBSCRIBED_MSG "Sub to %s failed" NUMBER_NOT_UNSUBSCRIBED_MSG "Unsub to %s failed: not found" NUMBER_SUBSCRIBED_MSG "Sub to %s done" NUMBER_UNSUBSCRIBED_MSG "Unsub to %s done" -POWER_OFF_MSG "POWER: off" -POWER_ON_MSG "POWER: on" +POWER_ALERT_MSG "POWER Alert" +POWER_OFF_MSG "Off" +POWER_ON_MSG "On" RESET_SUB_DONE_MSG "Reset sub. done (save ?)" SAVING_CONFIG_MSG "Saving config" SENDING_SMS_MSG "Sending SMS..." @@ -78,7 +81,6 @@ SMS_SENT_MSG "SMS sent" TEMPERATURE_ADJUSTMENT_MSG_FORMAT "Temp Adj: %d" TEMPERATURE_ADJUSTMENT_SET_MSG "Temp adjustment set (save ?)" TEMPERATURE_ALERT_MSG "Temp Alert" -TEMPERATURE_MSG_FORMAT "Temp: %c%d.%d" TEMPERATURE_THRESHOLDS_SET_MSG "Temp thresholds set (save ?)" TEMPERATURE_THRESHOLD_MSG_FORMAT "Temp: %d %d" TEMP_INIT_MSG "DS1820 Test" diff --git a/messages_fr.str b/messages_fr.str index 13136f4..bd607bc 100644 --- a/messages_fr.str +++ b/messages_fr.str @@ -15,7 +15,6 @@ CHECK_SMS_MSG "Verification SMS" CLEAR_ALERT_DONE_MSG "Alerte effacee" CONFIG_RESET_MSG "Config reinitialisee" CONFIG_SAVED_MSG "Config sauvee" -# bla bla bla CONNECTED_GSM_MSG "GSM Connecte" CONNECTING_GSM_MSG "Connexion GSM..." CRC_NOT_VALID_MSG "CRC non valide" @@ -54,22 +53,25 @@ IN_SMS_TEMP_FORMAT "temperature %d %d" IN_SMS_UNSUB "desinscrire " IN_SMS_UNSUB_FORMAT "unsub %s" LEVEL_ALERT_MSG "Level alert" -LEVEL_HIGH_MSG "Level: high" -LEVEL_LOW_MSG "Level: LOW" +LEVEL_HIGH_MSG "high" +# GSM end SMS message when encountering the letter 'w' => using uppercase +LEVEL_LOW_MSG "LOW" LIGHT_ALERT_MSG "Light alert" -LIGHT_MSG_FORMAT "Light: %d" LIGHT_SCHEDULE_MSG_FORMAT "Light schedule %s: %02d:%02d-%02d:%02d" LIGHT_SCHEDULE_SET_MSG "Light schedule set (save ?)" LIGHT_THRESHOLD_MSG_FORMAT "Light limits %s: %d-%d" LIGHT_THRESHOLD_SET_MSG "Light threshold set (save ?)" +MEASURE_ALERT_MSG_FORMAT "ALERT %s - Temp: %4.2f, Level: %s, Light: %d, POWER: %s" +MEASURE_MSG_FORMAT "Temp: %4.2f, Level: %s, Light: %d, POWER: %s" NEW_CONFIG_MSG "NEW config" NOT_CONNECTED_GSM_MSG "Check GSM PIN" NUMBER_NOT_SUBSCRIBED_MSG "Sub to %s failed" NUMBER_NOT_UNSUBSCRIBED_MSG "Unsub to %s failed: not found" NUMBER_SUBSCRIBED_MSG "Sub to %s done" NUMBER_UNSUBSCRIBED_MSG "Unsub to %s done" -POWER_OFF_MSG "POWER: off" -POWER_ON_MSG "POWER: on" +POWER_ALERT_MSG "POWER Alert" +POWER_OFF_MSG "off" +POWER_ON_MSG "on" RESET_SUB_DONE_MSG "Reset sub. done (save ?)" SAVING_CONFIG_MSG "Saving config" SENDING_SMS_MSG "Sending SMS..." @@ -79,9 +81,8 @@ SMS_SENT_MSG "SMS sent" TEMPERATURE_ADJUSTMENT_MSG_FORMAT "Temp Adj: %d" TEMPERATURE_ADJUSTMENT_SET_MSG "Temp adjustment set (save ?)" TEMPERATURE_ALERT_MSG "Temp Alert" -TEMPERATURE_MSG_FORMAT "Temp: %c%d.%d" TEMPERATURE_THRESHOLDS_SET_MSG "Temp thresholds set (save ?)" TEMPERATURE_THRESHOLD_MSG_FORMAT "Temp: %d %d" TEMP_INIT_MSG "DS1820 Test" UNKNOWN_MSG "What ???" -UNKNOWN_NUMBER_MSG "Unknown number" +UNKNOWN_NUMBER_MSG "Unknown number" \ No newline at end of file diff --git a/progmemStrings.h b/progmemStrings.h index 5e301d0..13563ab 100644 --- a/progmemStrings.h +++ b/progmemStrings.h @@ -94,68 +94,70 @@ const char inSmsUnsubFormat[] PROGMEM = {"unsub %s"}; #define IN_SMS_UNSUB_FORMAT 44 const char levelAlertMsg[] PROGMEM = {"Level alert"}; #define LEVEL_ALERT_MSG 45 -const char levelHighMsg[] PROGMEM = {"Level: high"}; +const char levelHighMsg[] PROGMEM = {"high"}; #define LEVEL_HIGH_MSG 46 -const char levelLowMsg[] PROGMEM = {"Level: LOW"}; +const char levelLowMsg[] PROGMEM = {"LOW"}; #define LEVEL_LOW_MSG 47 const char lightAlertMsg[] PROGMEM = {"Light alert"}; #define LIGHT_ALERT_MSG 48 -const char lightMsgFormat[] PROGMEM = {"Light: %d"}; -#define LIGHT_MSG_FORMAT 49 const char lightScheduleMsgFormat[] PROGMEM = {"Light schedule %s: %02d:%02d-%02d:%02d"}; -#define LIGHT_SCHEDULE_MSG_FORMAT 50 +#define LIGHT_SCHEDULE_MSG_FORMAT 49 const char lightScheduleSetMsg[] PROGMEM = {"Light schedule set (save ?)"}; -#define LIGHT_SCHEDULE_SET_MSG 51 +#define LIGHT_SCHEDULE_SET_MSG 50 const char lightThresholdMsgFormat[] PROGMEM = {"Light limits %s: %d-%d"}; -#define LIGHT_THRESHOLD_MSG_FORMAT 52 +#define LIGHT_THRESHOLD_MSG_FORMAT 51 const char lightThresholdSetMsg[] PROGMEM = {"Light threshold set (save ?)"}; -#define LIGHT_THRESHOLD_SET_MSG 53 +#define LIGHT_THRESHOLD_SET_MSG 52 +const char measureAlertMsgFormat[] PROGMEM = {"ALERT %s - Temp: %s, Level: %s, Light: %d, POWER: %s"}; +#define MEASURE_ALERT_MSG_FORMAT 53 +const char measureMsgFormat[] PROGMEM = {"Temp: %s, Level: %s, Light: %d, POWER %s"}; +#define MEASURE_MSG_FORMAT 54 const char newConfigMsg[] PROGMEM = {"NEW config"}; -#define NEW_CONFIG_MSG 54 +#define NEW_CONFIG_MSG 55 const char notConnectedGsmMsg[] PROGMEM = {"Check GSM PIN"}; -#define NOT_CONNECTED_GSM_MSG 55 +#define NOT_CONNECTED_GSM_MSG 56 const char numberNotSubscribedMsg[] PROGMEM = {"Sub to %s failed"}; -#define NUMBER_NOT_SUBSCRIBED_MSG 56 +#define NUMBER_NOT_SUBSCRIBED_MSG 57 const char numberNotUnsubscribedMsg[] PROGMEM = {"Unsub to %s failed: not found"}; -#define NUMBER_NOT_UNSUBSCRIBED_MSG 57 +#define NUMBER_NOT_UNSUBSCRIBED_MSG 58 const char numberSubscribedMsg[] PROGMEM = {"Sub to %s done"}; -#define NUMBER_SUBSCRIBED_MSG 58 +#define NUMBER_SUBSCRIBED_MSG 59 const char numberUnsubscribedMsg[] PROGMEM = {"Unsub to %s done"}; -#define NUMBER_UNSUBSCRIBED_MSG 59 -const char powerOffMsg[] PROGMEM = {"POWER: off"}; -#define POWER_OFF_MSG 60 -const char powerOnMsg[] PROGMEM = {"POWER: on"}; -#define POWER_ON_MSG 61 +#define NUMBER_UNSUBSCRIBED_MSG 60 +const char powerAlertMsg[] PROGMEM = {"POWER Alert"}; +#define POWER_ALERT_MSG 61 +const char powerOffMsg[] PROGMEM = {"Off"}; +#define POWER_OFF_MSG 62 +const char powerOnMsg[] PROGMEM = {"On"}; +#define POWER_ON_MSG 63 const char resetSubDoneMsg[] PROGMEM = {"Reset sub. done (save ?)"}; -#define RESET_SUB_DONE_MSG 62 +#define RESET_SUB_DONE_MSG 64 const char savingConfigMsg[] PROGMEM = {"Saving config"}; -#define SAVING_CONFIG_MSG 63 +#define SAVING_CONFIG_MSG 65 const char sendingSmsMsg[] PROGMEM = {"Sending SMS..."}; -#define SENDING_SMS_MSG 64 +#define SENDING_SMS_MSG 66 const char setAdminDoneMsg[] PROGMEM = {"Admin set (save ?)"}; -#define SET_ADMIN_DONE_MSG 65 +#define SET_ADMIN_DONE_MSG 67 const char setTimeDoneMsg[] PROGMEM = {"Time set"}; -#define SET_TIME_DONE_MSG 66 +#define SET_TIME_DONE_MSG 68 const char smsSentMsg[] PROGMEM = {"SMS sent"}; -#define SMS_SENT_MSG 67 +#define SMS_SENT_MSG 69 const char temperatureAdjustmentMsgFormat[] PROGMEM = {"Temp Adj: %d"}; -#define TEMPERATURE_ADJUSTMENT_MSG_FORMAT 68 +#define TEMPERATURE_ADJUSTMENT_MSG_FORMAT 70 const char temperatureAdjustmentSetMsg[] PROGMEM = {"Temp adjustment set (save ?)"}; -#define TEMPERATURE_ADJUSTMENT_SET_MSG 69 +#define TEMPERATURE_ADJUSTMENT_SET_MSG 71 const char temperatureAlertMsg[] PROGMEM = {"Temp Alert"}; -#define TEMPERATURE_ALERT_MSG 70 -const char temperatureMsgFormat[] PROGMEM = {"Temp: %c%d.%d"}; -#define TEMPERATURE_MSG_FORMAT 71 +#define TEMPERATURE_ALERT_MSG 72 const char temperatureThresholdsSetMsg[] PROGMEM = {"Temp thresholds set (save ?)"}; -#define TEMPERATURE_THRESHOLDS_SET_MSG 72 +#define TEMPERATURE_THRESHOLDS_SET_MSG 73 const char temperatureThresholdMsgFormat[] PROGMEM = {"Temp: %d %d"}; -#define TEMPERATURE_THRESHOLD_MSG_FORMAT 73 +#define TEMPERATURE_THRESHOLD_MSG_FORMAT 74 const char tempInitMsg[] PROGMEM = {"DS1820 Test"}; -#define TEMP_INIT_MSG 74 +#define TEMP_INIT_MSG 75 const char unknownMsg[] PROGMEM = {"What ???"}; -#define UNKNOWN_MSG 75 +#define UNKNOWN_MSG 76 const char unknownNumberMsg[] PROGMEM = {"UnknoWn number"}; -#define UNKNOWN_NUMBER_MSG 76 +#define UNKNOWN_NUMBER_MSG 77 const char* const messages[] PROGMEM = { accessDeniedMsg, addrErrMsg, alertMsgFormat, buildMsg, checkSmsMsg, clearAlertDoneMsg, configResetMsg, configSavedMsg, connectedGsmMsg, connectingGsmMsg, @@ -166,11 +168,11 @@ const char* const messages[] PROGMEM = { inSmsSave, inSmsSetAdmin, inSmsSetAdminFormat, inSmsSetTime, inSmsSetTimeFormat, inSmsStatus, inSmsSub, inSmsSubs, inSmsSubFormat, inSmsTemp, inSmsTempAdj, inSmsTempAdjFormat, inSmsTempFormat, inSmsUnsub, inSmsUnsubFormat, - levelAlertMsg, levelHighMsg, levelLowMsg, lightAlertMsg, lightMsgFormat, - lightScheduleMsgFormat, lightScheduleSetMsg, lightThresholdMsgFormat, lightThresholdSetMsg, newConfigMsg, - notConnectedGsmMsg, numberNotSubscribedMsg, numberNotUnsubscribedMsg, numberSubscribedMsg, numberUnsubscribedMsg, - powerOffMsg, powerOnMsg, resetSubDoneMsg, savingConfigMsg, sendingSmsMsg, - setAdminDoneMsg, setTimeDoneMsg, smsSentMsg, temperatureAdjustmentMsgFormat, temperatureAdjustmentSetMsg, - temperatureAlertMsg, temperatureMsgFormat, temperatureThresholdsSetMsg, temperatureThresholdMsgFormat, tempInitMsg, - unknownMsg, unknownNumberMsg + levelAlertMsg, levelHighMsg, levelLowMsg, lightAlertMsg, lightScheduleMsgFormat, + lightScheduleSetMsg, lightThresholdMsgFormat, lightThresholdSetMsg, measureAlertMsgFormat, measureMsgFormat, + newConfigMsg, notConnectedGsmMsg, numberNotSubscribedMsg, numberNotUnsubscribedMsg, numberSubscribedMsg, + numberUnsubscribedMsg, powerAlertMsg, powerOffMsg, powerOnMsg, resetSubDoneMsg, + savingConfigMsg, sendingSmsMsg, setAdminDoneMsg, setTimeDoneMsg, smsSentMsg, + temperatureAdjustmentMsgFormat, temperatureAdjustmentSetMsg, temperatureAlertMsg, temperatureThresholdsSetMsg, temperatureThresholdMsgFormat, + tempInitMsg, unknownMsg, unknownNumberMsg }; \ No newline at end of file