diff --git a/Co2Huzzah/Co2Huzzah.ino b/Co2Huzzah/Co2Huzzah.ino index a00f5a5d087a7f0a8eec3576ab7ba3c7e3c63f5d..67ce917cce33220f43118eec2a7285a47abcc800 100644 --- a/Co2Huzzah/Co2Huzzah.ino +++ b/Co2Huzzah/Co2Huzzah.ino @@ -14,6 +14,9 @@ #include "certs.h" #include <ESP8266WebServer.h> +/* MQTT Lib */ +#include <PubSubClient.h> + /* ==================== Variables ==================== */ /* If defined, then mode is 1 measure every 15 min. written in a file. Setup is not done. */ @@ -21,9 +24,9 @@ /* ---------- Default values ---------- */ #define SERIAL_BAUDRATE 115200 -#define MEASUREMENT_INTERVAL 60 /* seconds */ +#define MEASUREMENT_INTERVAL 60 /* seconds */ #define NTP_UPDATE_INTERVAL 1800 /* 30 min = 1800s. */ -#define ALTITUDE 352 /* Clermont les Carmes */ +#define ALTITUDE 352 /* Clermont les Carmes */ #define DEFAULT_SSID "irstea_invite" #define DEFAULT_HOST "CO2opain" #define DEFAULT_AP_PASSWD "CO2chon2" @@ -31,14 +34,14 @@ /* ------------------------------------------------- */ /* Keep some usual Strings static and shared */ /* ------------------------------------------------- */ -static String MSG[7]={ - "[ERROR] ", - "[WARN] ", - "[INFO] ", - "application/octet-stream", - "application/json", - "text/html", - "HTTP1.1 required", +static String MSG[7] = { + "[ERROR] ", + "[WARN] ", + "[INFO] ", + "application/octet-stream", + "application/json", + "text/html", + "HTTP1.1 required", }; #define ERR 0 @@ -51,8 +54,6 @@ static String MSG[7]={ /* ------------------------------------------------- */ - - static char ssid[48]; static char password[72]; static char host[48]; @@ -63,11 +64,11 @@ static bool writeData = false; static String ntpServerName = "fr.pool.ntp.org"; static String timezone = "Europe/Paris"; -const char* timeformat = "Y-m-d~TH:i:s;T"; +const char *timeformat = "Y-m-d~TH:i:s;T"; /* ---------- filesystem ---------- */ -const char* fsName = "LittleFS"; -FS* fileSystem = &LittleFS; +const char *fsName = "LittleFS"; +FS *fileSystem = &LittleFS; LittleFSConfig fileSystemConfig = LittleFSConfig(); static bool fsOK; String unsupportedFiles = String(); @@ -109,57 +110,110 @@ static char cbuf[BUF_SIZE]; static bool LED_STATUS = false; +/* ---------- MQTT ------------------------*/ +const char *mqtt_broker = "10.63.65.95"; +const char *topic_co2 = "N4/ESP8266/SDC30/CO2"; +const char *topic_temp = "N4/ESP8266/SDC30/airTemperature"; +const char *topic_hum = "N4/ESP8266/SDC30/airHumidity"; +const int mqtt_port = 1883; +const char *client_id = "CO2Pain"; + +WiFiClient espClient; +PubSubClient client(espClient); /* ==================== Fonctions ==================== */ +/*----------- MQTT ---------- */ + +void publishMQTT() +{ + for (int i = 0; i < 5; i++) + { + { + if (!client.connected()) + { + client.connect(client_id); + delay(2000); + } + else + { + break; + } + } + char *co2_str[8]; + dtostrf(co2, 5, 0, co2_str); + client.publish(topic_co2, co2_str); + char *temp_str[8]; + dtostrf(temperature, 6, 2, temp_str); + client.publish(topic_temp, temp_str); + char *hum_str[8]; + dtostrf(humidity, 6, 2, hum_str); + client.publish(topic_hum, hum_str); + } +} + /* ---------- HTTP ---------- */ template <typename ServerType> -void replyOK(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void replyOK(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ (*server).send(200, FPSTR(TEXT_PLAIN), ""); } template <typename ServerType> -void replyOKWithMsg(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String msg) { +void replyOKWithMsg(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String msg) +{ (*server).send(200, FPSTR(TEXT_PLAIN), msg); } template <typename ServerType> -void replyNotFound(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String msg) { +void replyNotFound(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String msg) +{ (*server).send(404, FPSTR(TEXT_PLAIN), msg); } template <typename ServerType> -void replyBadRequest(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String msg) { +void replyBadRequest(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String msg) +{ (*server).send(400, FPSTR(TEXT_PLAIN), msg + "\n"); } template <typename ServerType> -void replyServerError(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String msg) { +void replyServerError(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String msg) +{ (*server).send(500, FPSTR(TEXT_PLAIN), msg + "\n"); } /* Reads the given file from the filesystem and stream it back to the client */ template <typename ServerType> -bool handleFileRead(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String path) { - if (!fsOK) { +bool handleFileRead(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server, String path) +{ + if (!fsOK) + { replyServerError(server, FPSTR(FS_INIT_ERROR)); return true; } - if (path.startsWith("/wifiSetup.cfg") || path.startsWith("/apSetup.cfg")) { + if (path.startsWith("/wifiSetup.cfg") || path.startsWith("/apSetup.cfg")) + { replyNotFound(server, "File not found."); return true; } - if (path.endsWith("/")) path += "index.htm"; - if (!fileSystem->exists(path)) path += "l"; - if (!fileSystem->exists(path)) return false; // Re-try with .htm*l* + if (path.endsWith("/")) + path += "index.htm"; + if (!fileSystem->exists(path)) + path += "l"; + if (!fileSystem->exists(path)) + return false; // Re-try with .htm*l* - if ((*server).hasArg("download")) buf = MSG[MIME_STREAM]; - else buf = mime::getContentType(path); + if ((*server).hasArg("download")) + buf = MSG[MIME_STREAM]; + else + buf = mime::getContentType(path); File file = fileSystem->open(path, "r"); - if ((*server).streamFile(file, buf) != file.size()) { + if ((*server).streamFile(file, buf) != file.size()) + { // DBG_OUTPUT_PORT.println("Sent less data than expected!"); } file.close(); @@ -168,25 +222,28 @@ bool handleFileRead(esp8266webserver::ESP8266WebServerTemplate<ServerType> *serv bool HTTPhandleFileRead(String path) { return handleFileRead(&HTTPserver, path); } bool HTTPShandleFileRead(String path) { return handleFileRead(&HTTPSserver, path); } - /* The "Not Found" handler catches all URI not explicitly declared in code First try to find and return the requested file from the filesystem, and if it fails, return a 404 page with debug information */ template <typename ServerType> -void handleNotFound(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { - if (!fsOK) return replyServerError(server, FPSTR(FS_INIT_ERROR)); +void handleNotFound(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ + if (!fsOK) + return replyServerError(server, FPSTR(FS_INIT_ERROR)); buf = ESP8266WebServer::urlDecode((*server).uri()); // required to read paths with blanks - if (handleFileRead(server, buf)) return; + if (handleFileRead(server, buf)) + return; // Dump debug data buf = F("Error: File not found\n\nURI: ") + buf; buf += F("\nMethod: "); - buf += ((*server).method()==HTTP_GET)?"GET":"POST"; + buf += ((*server).method() == HTTP_GET) ? "GET" : "POST"; buf += F("\nArguments: "); buf += (*server).args(); buf += '\n'; - for (i = 0; i < (*server).args(); i++) { + for (i = 0; i < (*server).args(); i++) + { buf += F(" NAME:"); buf += (*server).argName(i); buf += F("\n VALUE:"); @@ -202,67 +259,84 @@ void handleNotFound(esp8266webserver::ESP8266WebServerTemplate<ServerType> *serv void HTTPhandleNotFound() { return handleNotFound(&HTTPserver); } void HTTPShandleNotFound() { return handleNotFound(&HTTPSserver); } - /* Returns the list of files in the directory specified by the "dir" query string parameter. Also demonstrates the use of chunked responses. */ template <typename ServerType> -void handleFileList(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { - if (!fsOK) return replyServerError(server,FPSTR(FS_INIT_ERROR)); +void handleFileList(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ + if (!fsOK) + return replyServerError(server, FPSTR(FS_INIT_ERROR)); - if (!fileSystem->exists("/data")) { + if (!fileSystem->exists("/data")) + { (*server).send(200, MSG[MIME_JSON], "[]"); - return; } + return; + } // use HTTP/1.1 Chunked response to avoid building a huge temporary string - if (!(*server).chunkedResponseModeStart(200, MSG[MIME_JSON])) { + if (!(*server).chunkedResponseModeStart(200, MSG[MIME_JSON])) + { (*server).send(505, MSG[MIME_HTML], MSG[SRV_MSG_HTTP]); - return; } + return; + } Dir dir = fileSystem->openDir("/data"); buf = ""; // Will use the same string for every line - while (dir.next()) { - if (buf.length()) { // send string from previous iteration as an HTTP chunk + while (dir.next()) + { + if (buf.length()) + { // send string from previous iteration as an HTTP chunk (*server).sendContent(buf); buf = ','; - } else buf = '['; + } + else + buf = '['; - if (!dir.isDirectory()) { + if (!dir.isDirectory()) + { buf += F("{\"size\":\""); buf += dir.fileSize(); } buf += F("\",\"name\":\""); // Always return names without leading "/" - if (dir.fileName()[0] == '/') buf += &(dir.fileName()[1]); - else buf += dir.fileName(); + if (dir.fileName()[0] == '/') + buf += &(dir.fileName()[1]); + else + buf += dir.fileName(); buf += "\"}"; } - buf += "]"; // send last string + buf += "]"; // send last string (*server).sendContent(buf); (*server).chunkedResponseFinalize(); } void HTTPhandleFileList() { return handleFileList(&HTTPserver); } void HTTPShandleFileList() { return handleFileList(&HTTPSserver); } - template <typename ServerType> -void handleWifiScan(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void handleWifiScan(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ int n = WiFi.scanNetworks(); - if (!(*server).chunkedResponseModeStart(200, MSG[MIME_JSON])) { + if (!(*server).chunkedResponseModeStart(200, MSG[MIME_JSON])) + { (*server).send(505, MSG[MIME_HTML], MSG[SRV_MSG_HTTP]); - return; } + return; + } #ifdef DEMO_MODE (*server).sendContent(F("[\"* AP_1 (-71)\",\" AP_2 (-59)\",\"* AP_3 (-75)\"]")); #else - buf='['; - for (i = 0; i < n; buf="") { - buf+="\""; - buf+=((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*"); - buf+=" "+WiFi.SSID(i)+" ("+WiFi.RSSI(i)+")\""; - if (++i == n) buf+="]"; - else buf+=","; + buf = '['; + for (i = 0; i < n; buf = "") + { + buf += "\""; + buf += ((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? " " : "*"); + buf += " " + WiFi.SSID(i) + " (" + WiFi.RSSI(i) + ")\""; + if (++i == n) + buf += "]"; + else + buf += ","; (*server).sendContent(buf); } #endif @@ -271,14 +345,15 @@ void handleWifiScan(esp8266webserver::ESP8266WebServerTemplate<ServerType> *serv void HTTPhandleWifiScan() { return handleWifiScan(&HTTPserver); } void HTTPShandleWifiScan() { return handleWifiScan(&HTTPSserver); } - -bool wifiConnect(char* ssId, char* pw) { +bool wifiConnect(char *ssId, char *pw) +{ WiFi.begin(ssId, pw); delay(500); i = 0; /* 60 loops ~30s. */ while ((WiFi.status() != WL_CONNECTED) && (i++ < 60)) delay(500); - if (WiFi.status() == WL_CONNECTED) { + if (WiFi.status() == WL_CONNECTED) + { Serial.print(MSG[INFO]); Serial.print(F("Wifi connected to ")); Serial.print(ssId); @@ -295,7 +370,8 @@ bool wifiConnect(char* ssId, char* pw) { /* Initilizes Wifi. Uses global variables ssid, assword, host, ap_password. * Returns true if connected to an AP. */ -bool initWifi() { +bool initWifi() +{ WiFi.mode(WIFI_AP_STA); /* Client AND AP */ delay(100); WiFi.softAP(host, ap_password); @@ -304,20 +380,26 @@ bool initWifi() { } template <typename ServerType> -void handleWifiSet(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void handleWifiSet(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ bool cnx, donotsave = true; #ifndef DEMO_MODE buf = (*server).arg("ssid"); - if (buf.length() > 0) { + if (buf.length() > 0) + { buf.toCharArray(ssid, sizeof(ssid)); buf = (*server).arg("pass"); buf.toCharArray(password, sizeof(password)); donotsave = (*server).hasArg("save"); - } else return; + } + else + return; cnx = initWifi(); - if (cnx) { - if ((fsOK) && (!donotsave)) { + if (cnx) + { + if ((fsOK) && (!donotsave)) + { Serial.print(MSG[INFO]); Serial.println(F("Saving Wifi configuration.")); File file = fileSystem->open("/wifiSetup.cfg", "w"); @@ -326,7 +408,9 @@ void handleWifiSet(esp8266webserver::ESP8266WebServerTemplate<ServerType> *serve delay(10); file.close(); } - } else { + } + else + { Serial.print(MSG[WARN]); Serial.println(F("Resetting.")); ESP.reset(); @@ -337,37 +421,48 @@ void handleWifiSet(esp8266webserver::ESP8266WebServerTemplate<ServerType> *serve void HTTPhandleWifiSet() { return handleWifiSet(&HTTPserver); } void HTTPShandleWifiSet() { return handleWifiSet(&HTTPSserver); } - template <typename ServerType> -void handleAP(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void handleAP(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ #ifdef DEMO_MODE - buf="{\"ap\":\""; buf+=host; buf+="\",\"pw\":\"demo\"}"; + buf = "{\"ap\":\""; + buf += host; + buf += "\",\"pw\":\"demo\"}"; #else - buf="{\"ap\":\""; buf+=host; buf+="\",\"pw\":\""; buf+=ap_password; buf+="\"}"; + buf = "{\"ap\":\""; + buf += host; + buf += "\",\"pw\":\""; + buf += ap_password; + buf += "\"}"; #endif (*server).send(200, MSG[MIME_JSON], buf); } void HTTPhandleAP() { return handleAP(&HTTPserver); } void HTTPShandleAP() { return handleAP(&HTTPSserver); } - template <typename ServerType> -void handleAPSet(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void handleAPSet(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ bool donotsave = true; #ifndef DEMO_MODE buf = (*server).arg("pass"); - if (buf.length() < 8) { + if (buf.length() < 8) + { replyBadRequest(server, F("Password too short (8 char. min)")); return; } - if (buf.length() > 0) { + if (buf.length() > 0) + { buf.toCharArray(ap_password, sizeof(ap_password)); buf = (*server).arg("ssid"); buf.toCharArray(host, sizeof(host)); donotsave = (*server).hasArg("save"); - } else return; + } + else + return; initWifi(); - if ((fsOK) && (!donotsave)) { + if ((fsOK) && (!donotsave)) + { Serial.print(MSG[INFO]); Serial.println(F("Saving AP configuration.")); File file = fileSystem->open("/apSetup.cfg", "w"); @@ -382,34 +477,37 @@ void handleAPSet(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) void HTTPhandleAPSet() { return handleAPSet(&HTTPserver); } void HTTPShandleAPSet() { return handleAPSet(&HTTPSserver); } - template <typename ServerType> -void handleSensorSet(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void handleSensorSet(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ bool donotsave = (*server).hasArg("save"); bool w = (*server).hasArg("write"); #ifndef DEMO_MODE buf = (*server).arg("ftxt"); i = buf.toInt(); - if ((i>0) && (i!=interval)) { + if ((i > 0) && (i != interval)) + { interval = i; airSensor.setMeasurementInterval(interval); } buf = (*server).arg("alt"); j = buf.toInt(); - if ((j>=0) && (j!=altitude)) { + if ((j >= 0) && (j != altitude)) + { altitude = j; airSensor.setAltitudeCompensation(altitude); } if ((w) && (!writeData)) - datafile = "data/"+tz.dateTime("YmdHi")+".csv"; + datafile = "data/" + tz.dateTime("YmdHi") + ".csv"; writeData = w; - if ((fsOK) && (!donotsave)) { + if ((fsOK) && (!donotsave)) + { Serial.print(MSG[INFO]); Serial.println(F("Saving Sensor configuration.")); File file = fileSystem->open("/sensorSetup.cfg", "w"); file.println(interval); file.println(altitude); - file.println((writeData)?"y":"n"); + file.println((writeData) ? "y" : "n"); delay(10); file.close(); } @@ -420,45 +518,58 @@ void HTTPhandleSensorSet() { return handleSensorSet(&HTTPserver); } void HTTPShandleSensorSet() { return handleSensorSet(&HTTPSserver); } template <typename ServerType> -void handleStatus(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void handleStatus(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ IPAddress ip = WiFi.softAPIP(); FSInfo fs_info; - if (!(*server).chunkedResponseModeStart(200, MSG[MIME_JSON])) { + if (!(*server).chunkedResponseModeStart(200, MSG[MIME_JSON])) + { (*server).send(505, MSG[MIME_HTML], MSG[SRV_MSG_HTTP]); - return; } + return; + } - buf = F("{\"ap\":\""); buf += host; buf += F("\",\"aip\":\""); - for (i=0;i<4; buf+=((i==4)?"":".")) buf += ip[i++]; + buf = F("{\"ap\":\""); + buf += host; + buf += F("\",\"aip\":\""); + for (i = 0; i < 4; buf += ((i == 4) ? "" : ".")) + buf += ip[i++]; buf += "\","; (*server).sendContent(buf); buf = F("\"ssid\":\""); - if (WiFi.status() == WL_CONNECTED) { + if (WiFi.status() == WL_CONNECTED) + { ip = WiFi.localIP(); - buf += ssid; buf += "\",\"ip\":\""; - for (i=0;i<4; buf+=((i==4)?"":".")) buf+=ip[i++]; - } else + buf += ssid; + buf += "\",\"ip\":\""; + for (i = 0; i < 4; buf += ((i == 4) ? "" : ".")) + buf += ip[i++]; + } + else buf += "\",\"ip\":\""; buf += "\","; (*server).sendContent(buf); buf = F("\"file\":\""); - if ((fsOK) && (writeData)) buf += datafile; + if ((fsOK) && (writeData)) + buf += datafile; (*server).sendContent(buf); buf = F("\",\"freq\":"); - buf += (sensorOK)?interval:-1; + buf += (sensorOK) ? interval : -1; buf += F(",\"alt\":"); - buf += (sensorOK)?altitude:-1; + buf += (sensorOK) ? altitude : -1; buf += ","; (*server).sendContent(buf); buf = F("\"du\":"); - if (fsOK) { + if (fsOK) + { fileSystem->info(fs_info); buf += fs_info.usedBytes; buf += F(",\"ds\":"); buf += fs_info.totalBytes; - } else + } + else buf += F("-1,\"ds\":-1"); buf += "}"; (*server).sendContent(buf); @@ -467,33 +578,37 @@ void handleStatus(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server void HTTPhandleStatus() { return handleStatus(&HTTPserver); } void HTTPShandleStatus() { return handleStatus(&HTTPSserver); } - template <typename ServerType> -void handleSetup(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void handleSetup(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ buf = F("{\"freq\":"); - buf += (sensorOK)?interval:-1; + buf += (sensorOK) ? interval : -1; buf += F(",\"alt\":"); - buf += (sensorOK)?altitude:-1; + buf += (sensorOK) ? altitude : -1; buf += F(",\"write\":"); - buf += (writeData)?"true":"false"; + buf += (writeData) ? "true" : "false"; buf += "}"; (*server).send(200, MSG[MIME_JSON], buf); } void HTTPhandleSetup() { return handleSetup(&HTTPserver); } void HTTPShandleSetup() { return handleSetup(&HTTPSserver); } - - /* ---------- Date and time ---------- */ template <typename ServerType> -void handleDateTime(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { - int h,m,d,mo,y; +void handleDateTime(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ + int h, m, d, mo, y; #ifndef DEMO_MODE - buf = (*server).arg("H"); h=buf.toInt(); - buf = (*server).arg("M"); m=buf.toInt(); - buf = (*server).arg("d"); d=buf.toInt(); - buf = (*server).arg("mo"); mo=buf.toInt(); - buf = (*server).arg("y"); y=buf.toInt(); + buf = (*server).arg("H"); + h = buf.toInt(); + buf = (*server).arg("M"); + m = buf.toInt(); + buf = (*server).arg("d"); + d = buf.toInt(); + buf = (*server).arg("mo"); + mo = buf.toInt(); + buf = (*server).arg("y"); + y = buf.toInt(); setTime(h, m, 30, d, mo, y); #endif handleFileRead(server, "/"); @@ -501,35 +616,44 @@ void handleDateTime(esp8266webserver::ESP8266WebServerTemplate<ServerType> *serv void HTTPhandleDateTime() { return handleDateTime(&HTTPserver); } void HTTPShandleDateTime() { return handleDateTime(&HTTPSserver); } - template <typename ServerType> -void handleNTP(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void handleNTP(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ bool donotsave = (*server).hasArg("save"); bool chg = false; #ifndef DEMO_MODE buf = (*server).arg("ntp"); - if (chg = (ntpServerName != buf)) { + if (chg = (ntpServerName != buf)) + { ntpServerName = buf; - setServer(ntpServerName); } + setServer(ntpServerName); + } buf = (*server).arg("tz"); if (timezone != buf) - if (!tz.setLocation(buf)) { + if (!tz.setLocation(buf)) + { Serial.print(MSG[WARN]); Serial.print(F("Timezone setting failed -> ")); Serial.println(buf); - } else { + } + else + { chg = true; - timezone = buf; } + timezone = buf; + } buf = (*server).arg("req"); i = buf.toInt(); - if (ntpUpdateInterval != i) { + if (ntpUpdateInterval != i) + { chg = true; - setInterval(ntpUpdateInterval); } + setInterval(ntpUpdateInterval); + } - if ((fsOK) && (chg) && (!donotsave)) { + if ((fsOK) && (chg) && (!donotsave)) + { Serial.print(MSG[INFO]); Serial.println(F("Saving NTP configuration.")); File file = fileSystem->open("/NTPSetup.cfg", "w"); @@ -537,28 +661,34 @@ void handleNTP(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { file.println(timezone); file.println(ntpUpdateInterval); delay(10); - file.close(); } + file.close(); + } #endif handleFileRead(server, "/"); } void HTTPhandleNTP() { return handleNTP(&HTTPserver); } void HTTPShandleNTP() { return handleNTP(&HTTPSserver); } - template <typename ServerType> -void handleDateTimeZone(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { - if (!(*server).chunkedResponseModeStart(200, MSG[MIME_JSON])) { +void handleDateTimeZone(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ + if (!(*server).chunkedResponseModeStart(200, MSG[MIME_JSON])) + { (*server).send(505, MSG[MIME_HTML], MSG[SRV_MSG_HTTP]); - return; } + return; + } (*server).sendContent(F("{\"tz\":\"")); (*server).sendContent(timezone); (*server).sendContent(F("\",\"ntp\":\"")); (*server).sendContent(ntpServerName); (*server).sendContent(F("\",\"freq\":\"")); - buf=""; buf+=ntpUpdateInterval; if (buf=="") buf="1800"; + buf = ""; + buf += ntpUpdateInterval; + if (buf == "") + buf = "1800"; (*server).sendContent(buf); (*server).sendContent(F("\",\"sync\":")); - buf = (timeStatus() == timeSet)?"true":"false"; + buf = (timeStatus() == timeSet) ? "true" : "false"; buf += "}"; (*server).sendContent(buf); (*server).chunkedResponseFinalize(); @@ -566,46 +696,52 @@ void handleDateTimeZone(esp8266webserver::ESP8266WebServerTemplate<ServerType> * void HTTPhandleDateTimeZone() { return handleDateTimeZone(&HTTPserver); } void HTTPShandleDateTimeZone() { return handleDateTimeZone(&HTTPSserver); } - /* ---------- CO2 sensor ---------- */ -void HTTPhandleSensor() { /* RQ : longueur du message : 68 octets. */ +void HTTPhandleSensor() +{ /* RQ : longueur du message : 68 octets. */ HTTPserver.send(200, MSG[MIME_JSON], sensorJson); } -void HTTPShandleSensor() { HTTPSserver.send(200, MSG[MIME_JSON], sensorJson);} +void HTTPShandleSensor() { HTTPSserver.send(200, MSG[MIME_JSON], sensorJson); } /* Fills co2, temperature and humidity values if new values * are available from sensor. Returns true if new values read. */ -bool getSensorData() { +bool getSensorData() +{ if (!airSensor.dataAvailable()) return false; co2 = airSensor.getCO2(); temperature = airSensor.getTemperature(); humidity = airSensor.getHumidity(); /* sensorJson */ - sensorJson = "{\"d\":\""+tz.dateTime("d/m/Y"); - sensorJson += "\",\"t\":\""+tz.dateTime("H:i"); + sensorJson = "{\"d\":\"" + tz.dateTime("d/m/Y"); + sensorJson += "\",\"t\":\"" + tz.dateTime("H:i"); sensorJson += "\",\"co2\":"; sensorJson += co2; - sensorJson += ",\"temp\":"+String(temperature, 2); - sensorJson += ",\"hum\":"+String(humidity, 2)+"}"; + sensorJson += ",\"temp\":" + String(temperature, 2); + sensorJson += ",\"hum\":" + String(humidity, 2) + "}"; return true; } - template <typename ServerType> -void handleFileManage(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) { +void handleFileManage(esp8266webserver::ESP8266WebServerTemplate<ServerType> *server) +{ String ch; - if (!fsOK) return replyServerError(server, FPSTR(FS_INIT_ERROR)); + if (!fsOK) + return replyServerError(server, FPSTR(FS_INIT_ERROR)); if (!(*server).hasArg("file")) return replyBadRequest(server, F("No file selected")); - buf = "/data/"+(*server).arg("file"); - if ((*server).arg("todo").startsWith("get")) { + buf = "/data/" + (*server).arg("file"); + if ((*server).arg("todo").startsWith("get")) + { /* Already done by javascript code */ return replyOK(server); - } else { - if ((*server).arg("todo").startsWith("del")) { /* to be sure */ + } + else + { + if ((*server).arg("todo").startsWith("del")) + { /* to be sure */ Serial.print(MSG[INFO]); #ifdef DEMO_MODE Serial.print(F("(demo:) Not deleting file ")); @@ -613,7 +749,8 @@ void handleFileManage(esp8266webserver::ESP8266WebServerTemplate<ServerType> *se #else Serial.print(F("Deleting file ")); Serial.println(buf); - if (!LittleFS.remove(buf)) { + if (!LittleFS.remove(buf)) + { Serial.print(MSG[ERR]); Serial.println("Delete failed"); } @@ -626,18 +763,20 @@ void handleFileManage(esp8266webserver::ESP8266WebServerTemplate<ServerType> *se void HTTPhandleFileManage() { return handleFileManage(&HTTPserver); } void HTTPShandleFileManage() { return handleFileManage(&HTTPSserver); } - - /* ---------- Tools ---------- */ -void readLineInFile(File &f, char* c, int len) { +void readLineInFile(File &f, char *c, int len) +{ buf = f.readStringUntil('\n'); - for (i = 0; ((int) buf.charAt(i)) > 31; i++); - if (i < buf.length()) buf.remove(i); + for (i = 0; ((int)buf.charAt(i)) > 31; i++) + ; + if (i < buf.length()) + buf.remove(i); buf.toCharArray(c, len); } /* ==================== Main ==================== */ -void setup() { +void setup() +{ /* --- Initialize default values. --- */ buf.reserve(BUF_SIZE); buf = DEFAULT_SSID; @@ -665,44 +804,58 @@ void setup() { fileSystem->setConfig(fileSystemConfig); fsOK = fileSystem->begin(); - if (!fsOK) { + if (!fsOK) + { Serial.print(MSG[WARN]); Serial.println(F("Filesystem init failed !")); - } else { + } + else + { Serial.print(MSG[INFO]); Serial.println(F("Reading configuration files.")); File file = fileSystem->open("/wifiSetup.cfg", "r"); - if (file) { + if (file) + { readLineInFile(file, ssid, sizeof(ssid)); readLineInFile(file, password, sizeof(password)); file.close(); - } else { + } + else + { Serial.print(MSG[WARN]); Serial.println(F("No wifi setup file found.")); } #ifndef DEMO_MODE file = fileSystem->open("/apSetup.cfg", "r"); - if (file) { + if (file) + { readLineInFile(file, host, sizeof(host)); readLineInFile(file, ap_password, sizeof(ap_password)); file.close(); - } else { + } + else + { Serial.print(MSG[WARN]); Serial.println(F("No AP setup file found.")); } #endif file = fileSystem->open("/sensorSetup.cfg", "r"); - if (file) { + if (file) + { readLineInFile(file, cbuf, sizeof(cbuf)); - buf = cbuf; interval = buf.toInt(); + buf = cbuf; + interval = buf.toInt(); readLineInFile(file, cbuf, sizeof(cbuf)); - buf = cbuf; altitude = buf.toInt(); + buf = cbuf; + altitude = buf.toInt(); readLineInFile(file, cbuf, sizeof(cbuf)); - writeData = (cbuf[0]=='y'); + writeData = (cbuf[0] == 'y'); file.close(); - } else { + } + else + { Serial.print(MSG[WARN]); Serial.println(F("No sensor setup file found.")); } @@ -713,15 +866,19 @@ void setup() { #endif file = fileSystem->open("/NTPSetup.cfg", "r"); - if (file) { + if (file) + { readLineInFile(file, cbuf, sizeof(cbuf)); ntpServerName = cbuf; readLineInFile(file, cbuf, sizeof(cbuf)); timezone = cbuf; readLineInFile(file, cbuf, sizeof(cbuf)); - buf = cbuf; ntpUpdateInterval = buf.toInt(); + buf = cbuf; + ntpUpdateInterval = buf.toInt(); file.close(); - } else { + } + else + { Serial.print(MSG[WARN]); Serial.println(F("No NTP setup file found.")); } @@ -729,20 +886,26 @@ void setup() { digitalWrite(RED_LED, HIGH); /* HIGH value turns it off */ /* WiFi.mode(WIFI_STA); */ /* Client only */ - WiFi.mode(WIFI_AP_STA); /* Client AND AP */ + WiFi.mode(WIFI_AP_STA); /* Client AND AP */ delay(100); WiFi.softAP(host, ap_password); delay(100); wifiConnect(ssid, password); + /* MQTT init */ + client.setServer(mqtt_broker, mqtt_port); + Serial.print(MSG[INFO]); Serial.print(F("AP IP address: ")); Serial.println(WiFi.softAPIP()); - if (MDNS.begin(host)) { + if (MDNS.begin(host)) + { MDNS.addService("http", "tcp", 80); MDNS.addService("https", "tcp", 443); - } else { + } + else + { Serial.print(MSG[WARN]); Serial.println(F("Error setting up MDNS responder!")); } @@ -788,9 +951,10 @@ void setup() { /* NTP */ setDebug(NONE); setServer(ntpServerName); - waitForSync(60); /* Waits NTP sync for max. 60 seconds. */ + waitForSync(60); /* Waits NTP sync for max. 60 seconds. */ setInterval(120); /* Try every 2 minutes until success */ - if (!tz.setLocation(timezone)) { + if (!tz.setLocation(timezone)) + { Serial.print(MSG[WARN]); Serial.print(F("Timezone setting failed -> ")); Serial.println(timezone); @@ -800,12 +964,14 @@ void setup() { delay(1000); i = 0; - while ((!(sensorOK = airSensor.begin())) && (i++ < 6)) { + while ((!(sensorOK = airSensor.begin())) && (i++ < 6)) + { Serial.print(MSG[ERR]); Serial.println(F("Air sensor not detected. Please check wiring. Waiting 10s...")); delay(10000); } - if (sensorOK) { + if (sensorOK) + { airSensor.setMeasurementInterval(interval); airSensor.setAltitudeCompensation(altitude); } @@ -813,76 +979,101 @@ void setup() { /* Set datafile name */ tzOK = (timeStatus() == timeSet); - if (tzOK) { + if (tzOK) + { setInterval(ntpUpdateInterval); - datafile = "data/"+tz.dateTime("YmdHi")+".csv"; + datafile = "data/" + tz.dateTime("YmdHi") + ".csv"; Serial.print(MSG[INFO]); Serial.print(F("NTP date and time set : ")); Serial.println(tz.dateTime(timeformat)); - } else { + } + else + { Serial.print(MSG[WARN]); Serial.println(F("NTP date and time not set.")); - if (fsOK) { /* If !(fsOK), we won't save any data. */ + if (fsOK) + { /* If !(fsOK), we won't save any data. */ Dir dir = fileSystem->openDir("/data"); j = 0; - while (dir.next()) { - if (!dir.isDirectory()) { + while (dir.next()) + { + if (!dir.isDirectory()) + { // Always return names without leading "/" - if (dir.fileName()[0] == '/') buf = &(dir.fileName()[1]); - else buf = dir.fileName(); - i = buf.substring(0,11).toInt(); - if (i>j) j = i; + if (dir.fileName()[0] == '/') + buf = &(dir.fileName()[1]); + else + buf = dir.fileName(); + i = buf.substring(0, 11).toInt(); + if (i > j) + j = i; } } - if (j > 0) datafile = "data/"+String(j)+"-NS.csv"; - else datafile = F("data/197001010000-NS.csv"); - } else { + if (j > 0) + datafile = "data/" + String(j) + "-NS.csv"; + else + datafile = F("data/197001010000-NS.csv"); + } + else + { datafile = ""; } } - if ((datafile.length() > 0) && (writeData)) { + if ((datafile.length() > 0) && (writeData)) + { Serial.print(MSG[INFO]); Serial.print(F("Writing data into ")); Serial.println(datafile); } -} + /* --- MQTT --- */ + publishMQTT(); +} -void loop() { +void loop() +{ File file; /* --- NTP sync --- */ events(); /* NTP time just set */ - if ((!tzOK) && (timeStatus() == timeSet)) { + if ((!tzOK) && (timeStatus() == timeSet)) + { setInterval(NTP_UPDATE_INTERVAL); Serial.print(MSG[INFO]); Serial.print(F("NTP date and time set : ")); Serial.println(tz.dateTime(timeformat)); tzOK = true; - if (fsOK) { /* Have to rename filename */ - buf = "data/"+tz.dateTime("YmdHi")+".csv"; - if ((writeData) && (datafile.length() > 0)) { - if (!fileSystem->rename(datafile, buf)) { + if (fsOK) + { /* Have to rename filename */ + buf = "data/" + tz.dateTime("YmdHi") + ".csv"; + if ((writeData) && (datafile.length() > 0)) + { + if (!fileSystem->rename(datafile, buf)) + { Serial.print(MSG[ERR]); - Serial.println("Couldn't rename "+datafile+" to "+buf); - } else { + Serial.println("Couldn't rename " + datafile + " to " + buf); + } + else + { Serial.print(MSG[INFO]); - Serial.println("Renamed "+datafile+" to "+buf+" and write data into it."); + Serial.println("Renamed " + datafile + " to " + buf + " and write data into it."); datafile = buf; } - } else /* Should never happend. */ - datafile = "data/"+tz.dateTime("YmdHi")+".csv"; + } + else /* Should never happend. */ + datafile = "data/" + tz.dateTime("YmdHi") + ".csv"; } } /* --- Get sensor data if available and write it to file. --- */ - if (getSensorData()) { + if (getSensorData()) + { /* Write to file */ cbuf[0] = ';'; - buf = tzOK?tz.dateTime(timeformat):""; + buf = tzOK ? tz.dateTime(timeformat) : ""; buf += cbuf[0]; /* ';' */ - buf += (int) co2; + buf += (int)co2; buf += cbuf[0]; buf += temperature; buf += cbuf[0]; @@ -891,14 +1082,19 @@ void loop() { buf += millis(); Serial.print(F("[MEASURE] ")); Serial.println(buf); - if ((writeData) && (fsOK)) { /* Write to file */ + if ((writeData) && (fsOK)) + { /* Write to file */ file = LittleFS.open(datafile, "a"); - if (!file) { + if (!file) + { Serial.print(MSG[ERR]); Serial.print(F("Failed to open file for appending : ")); Serial.println(datafile); - } else { - if (!file.println(buf)) { + } + else + { + if (!file.println(buf)) + { Serial.print(MSG[ERR]); Serial.print(F("Failed to write data to file ")); Serial.println(datafile); @@ -908,6 +1104,9 @@ void loop() { } } + /* --- MQTT --- */ + publishMQTT(); + HTTPserver.handleClient(); HTTPSserver.handleClient(); MDNS.update(); diff --git a/Co2Huzzah/README.md b/Co2Huzzah/README.md index 0b9c49297a76da8805c110b1e5bd51f48b9b7752..d7f037b8679683879a1c7e737313bb3aaf966214 100644 --- a/Co2Huzzah/README.md +++ b/Co2Huzzah/README.md @@ -28,7 +28,6 @@ La librairie est nécessaire pour copier le contenu du dossier `data/` sur la mémoire flash du nœud. - ## Utilisation Le nœud fonctionne en serveur HTTP, tant en point d'accès Wifi (IP: 192.168.4.1) @@ -55,8 +54,7 @@ commencera par signaler une alerte de sécurité à laquelle il faudra passer outre. À noter que le mode HTTPS est extrêmement lent (il oblige le nœud à utiliser ses maigres ressources pour crypter toutes les communications). - -#### Génération de certificats +### Génération de certificats Voir [https://github.com/esp8266/Arduino/blob/master/doc/esp8266wifi/bearssl-server-secure-class.rst]() @@ -72,13 +70,17 @@ Il faut ensuite copier le contenu des deux fichiers générés dans `certs.h` : - `cert.pem` dans *serverCert[]*, - `key.pem` dans *serverKey[]*. -##### Remarque +#### Remarque Les certificats fournis dans l'exemple sont des certificats de 512 octets (changer la valeur après rsa: dans la ligne de commande pour générer les certificats). Ceci accélère quelque peu les échanges. En revanche c'est moins sécurisé. +### MQTT + +Une fois connecté à un point d'accès, si celui-ci dispose d'un broker MQTT, le noeud tentera de publier les mesures du capteur. Le format du topic est `IdNoeud/ReferenceNoeud/ReferenceCapteur/TypeMesure`. + ## À noter La page permettant la visualisation des courbes de mesures utilise