diff --git a/README.md b/README.md index fcba5a5..2bbf13d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,37 @@ # Teleinfo Universal Library This is a fork of Teleinfo Universal Library for the ESP8266 MCU This is a generic Teleinfo French Meter Measure Library -- Github source : +- Initial Github source : +- Modified Github source : + +# Modifications par Doume (version 1.0.5) : + +- Add support for request /emoncms.json + + return a json list formated to interface emoncms server from a 3rd party + That allow to don't activate emoncms http client inside Wifinfo module + +- Add possibility to compile a version of sketch for module not connected to EDF counter + + Activate #define SIMU into Wifinfo.h, to obtain a version which will create + 2 variables and update one of them each second, to verify package + +- Add a check on variable names, and force a reinit of LibTeleinfo interface if an + alteration is detected (each restart is counted, and displayed in 'system' page ) + +- Change library LibTeleinfo.cpp, to don't use anymore malloc/free system APIs + + Variables are now stored in static table, allocated on start + + 50 entries max, name length 16 bytes max, value length 16 bytes max + + To use this library version : + + First, backup your old version of LibTeleinfo.... + + copy files src/LibTeleinfo.cpp and src/LibTeleinfo.h into your Arduino + environment (generally ~/Arduino/libraries/LibTeleinfo-master/src ) + before to compile sketch # Added features : - Add possibility to configure HttpRequest to send parameters/values to Domoticz diff --git a/examples/Wifinfo/Binaires/SPIFFS/README b/examples/Wifinfo/Binaires/SPIFFS/README new file mode 100644 index 0000000..af49111 --- /dev/null +++ b/examples/Wifinfo/Binaires/SPIFFS/README @@ -0,0 +1,6 @@ +Attention : Le fichier wifinfo.spiffs.bin disponible dans ce dossier +ne peut être utilisé que sur une Wemos D1 mini configurée pour 1M de sketch et 3M de SPIFFS + +Si vous utilisez un module différent, vous devrez compiler vous-même le fichier SPIFFS +avec Arduino IDE configuré pour votre module.... + diff --git a/examples/Wifinfo/Binaires/Switch+Debug/README b/examples/Wifinfo/Binaires/Switch+Debug/README new file mode 100644 index 0000000..3258259 --- /dev/null +++ b/examples/Wifinfo/Binaires/Switch+Debug/README @@ -0,0 +1,8 @@ +Le fichier Wifinfo.ino.bin disponible dans ce dossier a été compilé +avec l'option DEBUG activée (sur Serial1) et la gestion d'un contact sec +entre Ground et D5 (option SENSOR activée) + +Il peut être flashé via port série (outil ESPTOOL), +ou via l'interface Web de Wifinfo, à condition que vous ayez au préalable +flashé le SPIFFS permettant la prise en compte des fichiers .ino.bin + diff --git a/examples/Wifinfo/Binaires/no-Switch_no-Debug/README b/examples/Wifinfo/Binaires/no-Switch_no-Debug/README new file mode 100644 index 0000000..1afa234 --- /dev/null +++ b/examples/Wifinfo/Binaires/no-Switch_no-Debug/README @@ -0,0 +1,9 @@ +Le fichier Wifinfo.ino.bin disponible dans ce dossier a été compilé +sans l'option DEBUG et sans la gestion d'un contact sec + (option SENSOR désactivée) + Il s'agit donc d'une version 'standard' production + +Il peut être flashé via port série (outil ESPTOOL), +ou via l'interface Web de Wifinfo, à condition que vous ayez au préalable +flashé le SPIFFS permettant la prise en compte des fichiers .ino.bin + diff --git a/examples/Wifinfo/Wifinfo.h b/examples/Wifinfo/Wifinfo.h index e6eab12..133a4b0 100644 --- a/examples/Wifinfo/Wifinfo.h +++ b/examples/Wifinfo/Wifinfo.h @@ -45,12 +45,34 @@ extern "C" { #include "webclient.h" #include "config.h" +// Décommenter SIMU pour compiler une version de test +// pour un module non connecté au compteur EDF (simule un ADCO et une valeur HCHC) +//#define SIMU -#define DEBUG +// Décommenter DEBUG pour une version capable d'afficher du Debug +// soit sur Serial, soit sur Serial1 si compteur EDF raccordé sur Serial +//#define DEBUG + +//Décommenter SENSOR pour compiler une version capable de gérer +// un contact sec connecté entre Ground et D5 (GPIO-14) +//#define SENSOR + +// En mode SIMU, cela signifie que rien n'est connecté au port Serial +// On peut donc laisser le debug sur ce port, pour beneficier de +// l'affichage via Arduino IDE +#ifdef DEBUG +#ifdef SIMU +#define DEBUG_SERIAL Serial +#else #define DEBUG_SERIAL Serial1 -#define DEBUG_SERIAL1 +#define DEBUG_SERIAL1 +#endif //SIMU +#endif //DEBUG + +#define WIFINFO_VERSION "1.0.5a" -#define WIFINFO_VERSION "1.0.2" +// voir : https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino +// le Serial.print sous toutes ses formes.... // I prefix debug macro to be sure to use specific for THIS library // debugging, this should not interfere with main sketch or other @@ -68,7 +90,7 @@ extern "C" { #define DebugF(x) {} #define DebuglnF(x) {} #define Debugf(...) {} -#define Debugflush {} +#define Debugflush() {} #endif #define BLINK_LED_MS 50 // 50 ms blink diff --git a/examples/Wifinfo/Wifinfo.ino b/examples/Wifinfo/Wifinfo.ino index 58065e1..fba1de3 100644 --- a/examples/Wifinfo/Wifinfo.ino +++ b/examples/Wifinfo/Wifinfo.ino @@ -18,6 +18,28 @@ // // All text above must be included in any redistribution. // +// Modifié par Dominique DAMBRAIN 2017-07-10 (http://www.dambrain.fr) +// Version 1.0.5 +// Librairie LibTeleInfo : Allocation statique d'un tableau de stockage +// des variables (50 entrées) afin de proscrire les malloc/free +// pour éviter les altérations des noms & valeurs +// Modification en conséquence des séquences de scanning du tableau +// ATTENTION : Nécessite probablement un ESP-8266 type Wemos D1, +// car les variables globales occupent 42.284 octets +// +// Version 1.0.5a (11/01/2018) +// Permettre la mise à jour OTA à partir de fichiers .ino.bin (Auduino IDE 1.8.3) +// Ajout de la gestion d'un switch (Contact sec) relié à GND et D5 (GPIO-14) +// Décommenter le #define SENSOR dans Wifinfo.h +// Pour être utilisable avec Domoticz, au moins l'URL du serveur et le port +// doivent être renseignés dans la configuration HTTP Request, ainsi que +// l'index du switch (déclaré dans Domoticz) +// L'état du switch (On/Off) est envoyé à Domoticz au boot, et à chaque +// changement d'état +//  Note : Nécessité de flasher le SPIFFS pour pouvoir configurer l'IDX du switch +// et flasher le sketch winfinfo.ino.bin via interface Web +// Rendre possible la compilation si define SENSOR en commentaire +// et DEFINE_DEBUG en commentaire (aucun debug, version Production...) // ********************************************************************************** // Include Arduino header #include @@ -41,7 +63,6 @@ //WiFiManager wifi(0); ESP8266WebServer server(80); - bool ota_blink; // Teleinfo @@ -70,13 +91,37 @@ volatile boolean task_1_sec = false; volatile boolean task_emoncms = false; volatile boolean task_jeedom = false; volatile boolean task_httpRequest = false; +volatile boolean task_updsw = false; unsigned long seconds = 0; - +char buff[132]; //To format debug strings // sysinfo data _sysinfo sysinfo; // count Wifi connect attempts, to check stability -int nb_reconnect = 0; +int nb_reconnect = 0; +bool need_reinit = false; +unsigned int nb_reinit = 0; +bool first_info_call=true; + +#ifdef SIMU +//for tests +uint8_t flags = 8; +int loop_cpt = 60000; +String name2 = "HCHC"; +char * s2 = (char *)name2.c_str(); +String value2 = "000060000"; +char * v2 = (char *) value2.c_str(); +#endif + +#ifdef SENSOR +// Le contact sec devra etre connecte entre GND et D5 (GPIO-14) +const int SensorPin = 14; +int reading ; +int SwitchState = -1; // the current reading from the input pin +int lastSwitchState = -1; // the previous reading from the input pin +unsigned long lastChangeTime = 0; // the last time the input pin was toggled +unsigned long tempo = 200; // temps necessaire a la stabilisation du switch (0,2 seconde) +#endif /* ====================================================================== Function: UpdateSysinfo @@ -111,7 +156,6 @@ void Task_1_Sec() task_1_sec = true; seconds++; } - /* ====================================================================== Function: Task_emoncms Purpose : callback of emoncms ticker @@ -298,8 +342,8 @@ void NewFrame(ValueList * me) rgb_ticker.once_ms( (uint32_t) BLINK_LED_MS, LedOff, (int) RGB_LED_PIN); } - sprintf_P( buff, PSTR("New Frame (%ld Bytes free)"), ESP.getFreeHeap() ); - Debugln(buff); + //sprintf_P( buff, PSTR("New Frame (%ld Bytes free)"), ESP.getFreeHeap() ); + //Debugln(buff); } /* ====================================================================== @@ -322,8 +366,8 @@ void UpdatedFrame(ValueList * me) rgb_ticker.once_ms(BLINK_LED_MS, LedOff, RGB_LED_PIN); } - sprintf_P( buff, PSTR("Updated Frame (%ld Bytes free)"), ESP.getFreeHeap() ); - Debugln(buff); + //sprintf_P( buff, PSTR("Updated Frame (%ld Bytes free)"), ESP.getFreeHeap() ); + //Debugln(buff); /* // Got at least one ? @@ -410,13 +454,16 @@ Comments: - int WifiHandleConn(boolean setup = false) { int ret = WiFi.status(); - + char toprint[20]; + IPAddress ad; + if (setup) { - +#ifdef DEBUG DebuglnF("========== SDK Saved parameters Start"); WiFi.printDiag(DEBUG_SERIAL); DebuglnF("========== SDK Saved parameters End"); Debugflush(); +#endif // no correct SSID if (!*config.ssid) { @@ -485,8 +532,9 @@ int WifiHandleConn(boolean setup = false) nb_reconnect++; // increase reconnections count DebuglnF("connected!"); WiFi.mode(WIFI_STA); - - DebugF("IP address : "); Debugln(WiFi.localIP()); + ad = WiFi.localIP(); + sprintf(toprint,"%d.%d.%d.%d", ad[0],ad[1],ad[2],ad[3]); + DebugF("IP address : "); Debugln(toprint); DebugF("MAC address : "); Debugln(WiFi.macAddress()); // not connected ? start AP @@ -556,14 +604,36 @@ Input : - Output : - Comments: - ====================================================================== */ -void setup() -{ - char buff[32]; +void setup() { + boolean reset_config = true; // Set CPU speed to 160MHz system_update_cpu_freq(160); + // Check File system init +#ifdef DEBUG + DEBUG_SERIAL.begin(115200); +#endif + + if (! SPIFFS.begin() ) + { + // Serious problem + DebuglnF("SPIFFS Mount failed !"); + } else { + DebuglnF(""); + DebuglnF("SPIFFS Mount succesfull"); + + Dir dir = SPIFFS.openDir("/"); + while (dir.next()) { + String fileName = dir.fileName(); + size_t fileSize = dir.fileSize(); + sprintf(buff,"FS File: %s, size: %d\n", fileName.c_str(), fileSize); + Debug(buff); + } + DebuglnF(""); + } + //WiFi.disconnect(false); // Set WiFi to station mode and disconnect from an AP if it was previously connected @@ -578,12 +648,11 @@ void setup() // Init the serial 1, Our Debug Serial TXD0 // note this serial can only transmit, just // enough for debugging purpose - DEBUG_SERIAL.begin(115200); Debugln(F("\r\n\r\n==============")); Debug(F("WifInfo V")); Debugln(F(WIFINFO_VERSION)); Debugln(); - Debugflush(); +// Debugflush(); // Clear our global flags config.config = 0; @@ -596,30 +665,14 @@ void setup() DebugF(" (emoncms="); Debug(sizeof(_emoncms)); DebugF(" jeedom="); Debug(sizeof(_jeedom)); DebugF(" http request="); Debug(sizeof(_httpRequest)); - Debugln(')'); - Debugflush(); + Debugln(" )"); +// Debugflush(); - // Check File system init - if (!SPIFFS.begin()) - { - // Serious problem - DebuglnF("SPIFFS Mount failed"); - } else { - - DebuglnF("SPIFFS Mount succesfull"); - - Dir dir = SPIFFS.openDir("/"); - while (dir.next()) { - String fileName = dir.fileName(); - size_t fileSize = dir.fileSize(); - Debugf("FS File: %s, size: %d\n", fileName.c_str(), fileSize); - } - DebuglnF(""); - } + // Read Configuration from EEP if (readConfig()) { - DebuglnF("Good CRC, not set!"); + DebuglnF("Good CRC, not set! From now, we can use EEPROM config !"); } else { // Reset Configuration ResetConfig(); @@ -650,7 +703,7 @@ void setup() ArduinoOTA.onEnd([]() { LedRGBOFF(); - DebuglnF("Update finished restarting"); + DebuglnF("Update finished : restarting"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { @@ -665,12 +718,15 @@ void setup() ArduinoOTA.onError([](ota_error_t error) { LedRGBON(COLOR_RED); - Debugf("Update Error[%u]: ", error); +#ifdef DEBUG + sprintf(buff,"Update Error[%u]: ", error); + Debug(buff); if (error == OTA_AUTH_ERROR) DebuglnF("Auth Failed"); else if (error == OTA_BEGIN_ERROR) DebuglnF("Begin Failed"); else if (error == OTA_CONNECT_ERROR) DebuglnF("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) DebuglnF("Receive Failed"); else if (error == OTA_END_ERROR) DebuglnF("End Failed"); +#endif ESP.restart(); }); @@ -681,6 +737,7 @@ void setup() server.on("/config_form.json", handleFormConfig); server.on("/json", sendJSON); server.on("/tinfo.json", tinfoJSONTable); + server.on("/emoncms.json", emoncmsJSONTable); server.on("/system.json", sysJSONTable); server.on("/config.json", confJSONTable); server.on("/spiffs.json", spiffsJSONTable); @@ -712,7 +769,8 @@ void setup() if(upload.status == UPLOAD_FILE_START) { uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; WiFiUDP::stopAll(); - Debugf("Update: %s\n", upload.filename.c_str()); + sprintf(buff,"Update: %s\n", upload.filename.c_str()); + Debug(buff); LedRGBON(COLOR_MAGENTA); ota_blink = true; @@ -733,9 +791,10 @@ void setup() } else if(upload.status == UPLOAD_FILE_END) { //true to set the size to the current progress - if(Update.end(true)) - Debugf("Update Success: %u\nRebooting...\n", upload.totalSize); - else + if(Update.end(true)) { + sprintf(buff,"Update Success: %u\nRebooting...\n", upload.totalSize); + Debug(buff); + } else Update.printError(Serial1); LedRGBOFF(); @@ -747,9 +806,10 @@ void setup() } delay(0); } - ); - - // All other not known + ); + + /////////////////////////////////////////////////////// + // All other not known server.onNotFound(handleNotFound); // serves all SPIFFS Web file with 24hr max-age control @@ -766,7 +826,7 @@ void setup() // Teleinfo is connected to RXD2 (GPIO13) to // avoid conflict when flashing, this is why - // we swap RXD1/RXD1 to RXD2/TXD2 + // we swap RXD1/TXD1 to RXD2/TXD2 // Note that TXD2 is not used teleinfo is receive only #ifdef DEBUG_SERIAL1 Serial.begin(1200, SERIAL_7E1); @@ -774,6 +834,7 @@ void setup() #endif // Init teleinfo + need_reinit=false; tinfo.init(); // Attach the callback we need @@ -803,6 +864,29 @@ void setup() // HTTP Request Update if needed if (config.httpReq.freq) Tick_httpRequest.attach(config.httpReq.freq, Task_httpRequest); + +//To simulate Teleinfo on not connected module +#ifdef SIMU + String name1 = "ADCO"; + String value1 = "01234546789012"; + + char * s1 = (char *)name1.c_str(); + char * v1 = (char *)value1.c_str(); + flags = TINFO_FLAGS_ADDED; + tinfo.addCustomValue(s1, v1, &flags); //ADCO arbitrary value + tinfo.addCustomValue(s2, v2, &flags); //counter value + flags = TINFO_FLAGS_NONE; + tinfo.valuesDump(); +#endif + +#ifdef SENSOR + pinMode(SensorPin, INPUT_PULLUP); + DebuglnF("Switch sensor initialized"); + reading = digitalRead(SensorPin); + sprintf(buff,"Initial State: %d\n", reading); + Debug(buff); +#endif + } /* ====================================================================== @@ -822,10 +906,24 @@ void loop() //webSocket.loop(); - // Only once task per loop, let system do it own task + // Only once task per loop, let system do its own task if (task_1_sec) { UpdateSysinfo(false, false); task_1_sec = false; + +//To simulate Teleinfo on not connected module +#ifdef SIMU + loop_cpt++; + if(loop_cpt % 10) + { + // each 10 second, try to change HCHC value + //Increase v2 value + sprintf(v2, "%09d", (loop_cpt) ); + // and update ListValues + flags = TINFO_FLAGS_UPDATED; + tinfo.addCustomValue(s2, v2, &flags); + } +#endif } else if (task_emoncms) { emoncmsPost(); task_emoncms=false; @@ -835,16 +933,56 @@ void loop() } else if (task_httpRequest) { httpRequest(); task_httpRequest=false; + } +#ifdef SENSOR + else if (task_updsw) { + UPD_switch(); + task_updsw=false; } - // Handle teleinfo serial - if ( Serial.available() ) { - // Read Serial and process to tinfo - c = Serial.read(); - //Serial1.print(c); - tinfo.process(c); + // read the state of the switch into a local variable: + reading = digitalRead(SensorPin); + + // check to see if you just pressed the button + // (i.e. the input went from LOW to HIGH), and you've waited long enough + // since the last press to ignore any noise: + + // If the switch changed, due to noise or pressing: + if (reading != lastSwitchState) { + // reset the debouncing timer + lastChangeTime = millis(); + lastSwitchState = reading; } - //delay(10); -} + if ((millis() - lastChangeTime) > tempo) { + // whatever the reading is at, it's been there for longer than the tempo + // delay, so take it as the actual current state: + + // if the switch state has changed: + if (reading != SwitchState) { + sprintf(buff,"Switch changed from %d to %d\n", SwitchState, reading); + Debug(buff); + SwitchState = reading; + //Notify HTTP server that switch has changed, on next loop + task_updsw=true; + } + } +#endif + if (need_reinit) { + //Some polluted entries have been detected in Teleinfo ListValues + need_reinit=false; + nb_reinit++; //account of reinit operations, for system infos + tinfo.init(); //Clear ListValues, buffer, and wait for next STX + } else { + // Handle teleinfo serial + if ( Serial.available() ) { + // Read Serial and process to tinfo + c = Serial.read(); + tinfo.process(c); + } + + //delay(10); + } + +} diff --git a/examples/Wifinfo/config.cpp b/examples/Wifinfo/config.cpp index d2b9caf..06d3341 100644 --- a/examples/Wifinfo/config.cpp +++ b/examples/Wifinfo/config.cpp @@ -62,11 +62,13 @@ void eepromDump(uint8_t bytesPerRow) // First byte of the row ? if (j==0) { // Display Address - Debugf("%04X : ", i); + sprintf(buff,"%04X : ", i); + Debug(buff); } // write byte in hex form - Debugf("%02X ", EEPROM.read(i)); + sprintf(buff,"%02X ", EEPROM.read(i)); + Debug(buff); // Last byte of the row ? // start a new line @@ -153,11 +155,13 @@ bool saveConfig (void) ret_code = readConfig(false); Debug(F("Write config ")); - + +#ifdef DEBUG if (ret_code) Debugln(F("OK!")); else Debugln(F("Error!")); +#endif //eepromDump(32); @@ -185,10 +189,11 @@ void showConfig() if (config.config & CFG_RGB_LED) DebugF(" RGB"); if (config.config & CFG_DEBUG) DebugF(" DEBUG"); if (config.config & CFG_LCD) DebugF(" LCD"); + DebuglnF("\r\n===== Emoncms"); DebugF("host :"); Debugln(config.emoncms.host); - DebugF("port :"); Debugln(config.emoncms.port); + DebugF("port :"); Debugln((int)config.emoncms.port); DebugF("url :"); Debugln(config.emoncms.url); DebugF("key :"); Debugln(config.emoncms.apikey); DebugF("node :"); Debugln(config.emoncms.node); @@ -207,5 +212,6 @@ void showConfig() DebugF("port :"); Debugln(config.httpReq.port); DebugF("path :"); Debugln(config.httpReq.path); DebugF("freq :"); Debugln(config.httpReq.freq); + DebugF("sw idx :"); Debugln(config.httpReq.swidx); } diff --git a/examples/Wifinfo/config.h b/examples/Wifinfo/config.h index 7c105a1..71dae4c 100644 --- a/examples/Wifinfo/config.h +++ b/examples/Wifinfo/config.h @@ -69,6 +69,7 @@ #define CFG_FORM_AP_PSK FPSTR("ap_psk") #define CFG_FORM_OTA_AUTH FPSTR("ota_auth") #define CFG_FORM_OTA_PORT FPSTR("ota_port") +#define CFG_FORM_DBGFILE FPSTR("dbg_file") #define CFG_FORM_EMON_HOST FPSTR("emon_host") #define CFG_FORM_EMON_PORT FPSTR("emon_port") @@ -88,6 +89,7 @@ #define CFG_FORM_HTTPREQ_PORT FPSTR("httpreq_port") #define CFG_FORM_HTTPREQ_PATH FPSTR("httpreq_path") #define CFG_FORM_HTTPREQ_FREQ FPSTR("httpreq_freq") +#define CFG_FORM_HTTPREQ_SWIDX FPSTR("httpreq_swidx") #define CFG_FORM_IP FPSTR("wifi_ip"); #define CFG_FORM_GW FPSTR("wifi_gw"); @@ -128,9 +130,10 @@ typedef struct { char host[CFG_HTTPREQ_HOST_SIZE+1]; // FQDN char path[CFG_HTTPREQ_PATH_SIZE+1]; // Path - uint16_t port; // Protocol port (HTTP/HTTPS) + uint16_t port; // Protocol port (HTTP/HTTPS) uint32_t freq; // refresh rate - uint8_t filler[24]; // in case adding data in config avoiding loosing current conf by bad crc*/ + uint16_t swidx; // Switch index (into Domoticz) + uint8_t filler[22]; // in case adding data in config avoiding loosing current conf by bad crc*/ } _httpRequest; // Config saved into eeprom @@ -154,7 +157,8 @@ typedef struct // Exported variables/object instancied in main sketch // =================================================== -extern _Config config; +extern _Config config; +extern char buff[]; #pragma pack(pop) diff --git a/examples/Wifinfo/data/index.htm.gz b/examples/Wifinfo/data/index.htm.gz index ff52728..d0b2819 100644 Binary files a/examples/Wifinfo/data/index.htm.gz and b/examples/Wifinfo/data/index.htm.gz differ diff --git a/examples/Wifinfo/webclient.cpp b/examples/Wifinfo/webclient.cpp index 184548d..6d470fb 100644 --- a/examples/Wifinfo/webclient.cpp +++ b/examples/Wifinfo/webclient.cpp @@ -1,5 +1,5 @@ // ********************************************************************************** -// ESP8266 Teleinfo WEB Client, web server function +// ESP8266 Teleinfo WEB Client, web client functions // ********************************************************************************** // Creative Commons Attrib Share-Alike License // You are free to use/extend this library but please abide with the CC-BY-SA license: @@ -43,7 +43,8 @@ boolean httpPost(char * host, uint16_t port, char * url) //http.begin("http://emoncms.org/input/post.json?node=20&apikey=2f13e4608d411d20354485f72747de7b&json={PAPP:100}"); //http.begin("emoncms.org", 80, "/input/post.json?node=20&apikey=2f13e4608d411d20354485f72747de7b&json={}"); //HTTP - Debugf("http%s://%s:%d%s => ", port==443?"s":"", host, port, url); + sprintf(buff,"http%s://%s:%d%s => ", port==443?"s":"", host, port, url); + Debug(buff); // start connection and send HTTP header int httpCode = http.GET(); @@ -60,12 +61,118 @@ boolean httpPost(char * host, uint16_t port, char * url) } else { DebugF("failed!"); } - Debugf(" in %d ms\r\n",millis()-start); + sprintf(buff," in %d ms\r\n",millis()-start); + Debug(buff); return ret; } +/* ====================================================================== +Function: build_emoncms_json string (usable by webserver.cpp) +Purpose : construct the json part of emoncms url +Input : - +Output : String if some Teleinfo data available +Comments: - +====================================================================== */ +String build_emoncms_json(void) +{ + boolean first_item = true; + + String url = "{" ; + + ValueList * me = tinfo.getList(); + + if (me) { + // Loop thru the node + while (me->next) { + + if(! first_item) + // go to next node + me = me->next; + + if( ! me->free ) { + + + // On first item, do not add , separator + if (first_item) + first_item = false; + else + url += ","; + + + if(validate_value_name(me->name)) { + url += me->name ; + url += ":" ; + + // EMONCMS ne sait traiter que des valeurs numériques, donc ici il faut faire une + // table de mappage, tout à fait arbitraire, mais c"est celle-ci dont je me sers + // depuis mes débuts avec la téléinfo + if (!strcmp(me->name, "OPTARIF")) { + // L'option tarifaire choisie (Groupe "OPTARIF") est codée sur 4 caractères alphanumériques + /* J'ai pris un nombre arbitraire codé dans l'ordre ci-dessous + je mets le 4eme char à 0, trop de possibilités + BASE => Option Base. + HC.. => Option Heures Creuses. + EJP. => Option EJP. + BBRx => Option Tempo + */ + char * p = me->value; + + if (*p=='B'&&*(p+1)=='A'&&*(p+2)=='S') url += "1"; + else if (*p=='H'&&*(p+1)=='C'&&*(p+2)=='.') url += "2"; + else if (*p=='E'&&*(p+1)=='J'&&*(p+2)=='P') url += "3"; + else if (*p=='B'&&*(p+1)=='B'&&*(p+2)=='R') url += "4"; + else url +="0"; + } else if (!strcmp(me->name, "HHPHC")) { + // L'horaire heures pleines/heures creuses (Groupe "HHPHC") est codé par un caractère A à Y + // J'ai choisi de prendre son code ASCII + int code = *me->value; + url += String(code); + } else if (!strcmp(me->name, "PTEC")) { + // La période tarifaire en cours (Groupe "PTEC"), est codée sur 4 caractères + /* J'ai pris un nombre arbitraire codé dans l'ordre ci-dessous + TH.. => Toutes les Heures. + HC.. => Heures Creuses. + HP.. => Heures Pleines. + HN.. => Heures Normales. + PM.. => Heures de Pointe Mobile. + HCJB => Heures Creuses Jours Bleus. + HCJW => Heures Creuses Jours Blancs (White). + HCJR => Heures Creuses Jours Rouges. + HPJB => Heures Pleines Jours Bleus. + HPJW => Heures Pleines Jours Blancs (White). + HPJR => Heures Pleines Jours Rouges. + */ + if (!strcmp(me->value, "TH..")) url += "1"; + else if (!strcmp(me->value, "HC..")) url += "2"; + else if (!strcmp(me->value, "HP..")) url += "3"; + else if (!strcmp(me->value, "HN..")) url += "4"; + else if (!strcmp(me->value, "PM..")) url += "5"; + else if (!strcmp(me->value, "HCJB")) url += "6"; + else if (!strcmp(me->value, "HCJW")) url += "7"; + else if (!strcmp(me->value, "HCJR")) url += "8"; + else if (!strcmp(me->value, "HPJB")) url += "9"; + else if (!strcmp(me->value, "HPJW")) url += "10"; + else if (!strcmp(me->value, "HPJR")) url += "11"; + else url +="0"; + } else { + url += me->value; + } + } else { + //Value name not valid : ignore this value, and + // force Teleinfo to reinit on next loop ! + need_reinit=true; + } + } //not free entry + } // While next + + } //if me + // Json end + url += "}"; + + return url; +} /* ====================================================================== -Function: emoncmsPost +Function: emoncmsPost (called by main sketch on timer, if activated) Purpose : Do a http post to emoncms Input : Output : true if post returned 200 OK @@ -81,7 +188,7 @@ boolean emoncmsPost(void) // Got at least one ? if (me && me->next) { String url ; - boolean first_item; + url = *config.emoncms.url ? config.emoncms.url : "/"; url += "?"; @@ -93,83 +200,15 @@ boolean emoncmsPost(void) url += F("apikey=") ; url += config.emoncms.apikey; - url += F("&json={") ; - - first_item = true; - - // Loop thru the node - while (me->next) { - // go to next node - me = me->next; - // First item do not add , separator - if (first_item) - first_item = false; - else - url += ","; - - url += me->name ; - url += ":" ; - - // EMONCMS ne sais traiter que des valeurs numériques, donc ici il faut faire une - // table de mappage, tout à fait arbitraire, mais c"est celle-ci dont je me sers - // depuis mes débuts avec la téléinfo - if (!strcmp(me->name, "OPTARIF")) { - // L'option tarifaire choisie (Groupe "OPTARIF") est codée sur 4 caractères alphanumériques - /* J'ai pris un nombre arbitraire codé dans l'ordre ci-dessous - je mets le 4eme char à 0, trop de possibilités - BASE => Option Base. - HC.. => Option Heures Creuses. - EJP. => Option EJP. - BBRx => Option Tempo - */ - char * p = me->value; - - if (*p=='B'&&*(p+1)=='A'&&*(p+2)=='S') url += "1"; - else if (*p=='H'&&*(p+1)=='C'&&*(p+2)=='.') url += "2"; - else if (*p=='E'&&*(p+1)=='J'&&*(p+2)=='P') url += "3"; - else if (*p=='B'&&*(p+1)=='B'&&*(p+2)=='R') url += "4"; - else url +="0"; - } else if (!strcmp(me->name, "HHPHC")) { - // L'horaire heures pleines/heures creuses (Groupe "HHPHC") est codé par un caractère A à Y - // J'ai choisi de prendre son code ASCII - int code = *me->value; - url += String(code); - } else if (!strcmp(me->name, "PTEC")) { - // La période tarifaire en cours (Groupe "PTEC"), est codée sur 4 caractères - /* J'ai pris un nombre arbitraire codé dans l'ordre ci-dessous - TH.. => Toutes les Heures. - HC.. => Heures Creuses. - HP.. => Heures Pleines. - HN.. => Heures Normales. - PM.. => Heures de Pointe Mobile. - HCJB => Heures Creuses Jours Bleus. - HCJW => Heures Creuses Jours Blancs (White). - HCJR => Heures Creuses Jours Rouges. - HPJB => Heures Pleines Jours Bleus. - HPJW => Heures Pleines Jours Blancs (White). - HPJR => Heures Pleines Jours Rouges. - */ - if (!strcmp(me->value, "TH..")) url += "1"; - else if (!strcmp(me->value, "HC..")) url += "2"; - else if (!strcmp(me->value, "HP..")) url += "3"; - else if (!strcmp(me->value, "HN..")) url += "4"; - else if (!strcmp(me->value, "PM..")) url += "5"; - else if (!strcmp(me->value, "HCJB")) url += "6"; - else if (!strcmp(me->value, "HCJW")) url += "7"; - else if (!strcmp(me->value, "HCJR")) url += "8"; - else if (!strcmp(me->value, "HPJB")) url += "9"; - else if (!strcmp(me->value, "HPJW")) url += "10"; - else if (!strcmp(me->value, "HPJR")) url += "11"; - else url +="0"; - } else { - url += me->value; - } - } // While me - // Json end - url += "}"; + //append json list of values + url += F("&json=") ; + + url += build_emoncms_json(); //Get Teleinfo list of values + // And submit all to emoncms ret = httpPost( config.emoncms.host, config.emoncms.port, (char *) url.c_str()) ; + } // if me } // if host return ret; @@ -289,43 +328,43 @@ boolean httpRequest(void) { url.replace("%PAPP%", me->value); } - if (valName == "ADCO") + if (valName == "ADCO") { url.replace("%ADCO%", me->value); } - if (valName == "OPTARIF") + if (valName == "OPTARIF") { url.replace("%OPTARIF%", me->value); } - if (valName == "ISOUC") + if (valName == "ISOUC") { url.replace("%ISOUC%", me->value); } - if (valName == "PTEC") + if (valName == "PTEC") { url.replace("%PTEC%", me->value); } - if (valName == "IINST") + if (valName == "IINST") { url.replace("%IINST%", me->value); } - if (valName == "IMAX") + if (valName == "IMAX") { url.replace("%IMAX%", me->value); } - if (valName == "HHPHC") + if (valName == "HHPHC") { url.replace("%HHPHC%", me->value); } - if (valName == "MOTDETAT") + if (valName == "MOTDETAT") { url.replace("%MOTDETAT%", me->value); } - if (valName == "BASE") + if (valName == "BASE") { url.replace("%BASE%", me->value); } - } + } } // While me ret = httpPost( config.httpReq.host, config.httpReq.port, (char *) url.c_str()) ; @@ -334,3 +373,39 @@ boolean httpRequest(void) return ret; } +#ifdef SENSOR +/* ====================================================================== +Function: UPD_switch +Purpose : Do a http request to update Switch state into Domoticz +Input : +Output : true if post returned 200 OK +Comments: - +====================================================================== */ +boolean UPD_switch(void) +{ + boolean ret = false; + + // Some basic checking + if (*config.httpReq.host && (config.httpReq.swidx != 0) ) + { + char url[128]; + char State[5]; + uint16_t port = config.httpReq.port; + + if(port == 0) + port = 80; + + if(SwitchState) + sprintf(State,"Off"); //switch ouvert + else + sprintf(State,"On"); //switch fermé : portail fermé + + sprintf(url,"/json.htm?type=command¶m=switchlight&idx=%d&switchcmd=%s",(int)config.httpReq.swidx, State); + //Debugf("Updating switch: <%s>\n", url ); + ret = httpPost( config.httpReq.host, port, url) ; + + } // if host & idx + return ret; +} +#endif + diff --git a/examples/Wifinfo/webclient.h b/examples/Wifinfo/webclient.h index a7f6acc..b41171f 100644 --- a/examples/Wifinfo/webclient.h +++ b/examples/Wifinfo/webclient.h @@ -28,12 +28,20 @@ // Exported variables/object instancied in main sketch // =================================================== +extern bool need_reinit; +extern char buff[]; -// declared exported function from route.cpp +// Exported function instancied in webserver.cpp +// ============================================= +extern bool validate_value_name(String name); + +// declared exported function from webclient.cpp // =================================================== boolean httpPost(char * host, uint16_t port, char * url); boolean emoncmsPost(void); boolean jeedomPost(void); boolean httpRequest(void); +boolean UPD_switch(void); +String build_emoncms_json(void); #endif diff --git a/examples/Wifinfo/webserver.cpp b/examples/Wifinfo/webserver.cpp index 2c283f3..eb8f74c 100644 --- a/examples/Wifinfo/webserver.cpp +++ b/examples/Wifinfo/webserver.cpp @@ -16,6 +16,8 @@ // // History : V1.00 2015-06-14 - First release // +// Modified by Doume 2017-06-29 : Try to avoid polluted ListedValues with Triphase counter +// // All text above must be included in any redistribution. // // ********************************************************************************** @@ -31,6 +33,16 @@ const char FP_QCNL[] PROGMEM = "\",\r\n\""; const char FP_RESTART[] PROGMEM = "OK, Redémarrage en cours\r\n"; const char FP_NL[] PROGMEM = "\r\n"; +//List of authorized value names in Teleinfo, to detect polluted entries +const String tabnames[35] = { + "ADCO" , "OPTARIF" , "ISOUSC" , "BASE", "HCHC" , "HCHP", + "IMAX" , "IINST" , "PTEC", "PMAX", "PAPP", "HHPHC" , "MOTDETAT" , "PPOT", + "IINST1" , "IINST2" , "IINST3", "IMAX1" , "IMAX2" , "IMAX3" , + "EJPHN" , "EJPHPM" , "BBRHCJB" , "BBRHPJB", "BBRHCJW" , "BBRHPJW" , "BBRHCJR" , + "BBRHPJR" , "PEJP" , "DEMAIN" , "ADPS" , "ADIR1", "ADIR2" , "ADIR3" + }; + + /* ====================================================================== Function: formatSize Purpose : format a asize to human readable format @@ -197,6 +209,12 @@ void handleFormConfig(void) } config.httpReq.freq = itemp; + itemp = server.arg("httpreq_swidx").toInt(); + if (itemp > 0 && itemp <= 65535) + config.httpReq.swidx = itemp; + else + config.httpReq.swidx = 0; + if ( saveConfig() ) { ret = 200; response = "OK"; @@ -292,77 +310,76 @@ Comments: - ====================================================================== */ void tinfoJSONTable(void) { + // we're there + ESP.wdtFeed(); //Force software wadchog to restart from 0 + ValueList * me = tinfo.getList(); String response = ""; // Just to debug where we are - Debug(F("Serving /tinfo page...\r\n")); + //Debug(F("Serving /tinfo page...\r\n")); + if (! me ) //&& first_info_call) + { + //Let tinfo such time to build a list.... + first_info_call=false; + unsigned long topdebut = millis(); + bool expired = false; + while (! expired ) { + if( (millis() - topdebut ) >= 3000 ) { + expired = true; // 3 seconds delay expired + } else { + yield(); //Let CPU to other threads + } + } + // continue, hoping list values is now ready + me = tinfo.getList(); + } + //tinfo.valuesDump(); // Got at least one ? if (me) { uint8_t index=0; - + + first_info_call=false; boolean first_item = true; // Json start response += F("[\r\n"); // Loop thru the node while (me->next) { + index++; - // we're there - ESP.wdtFeed(); - - // go to next node - me = me->next; - - // First item do not add , separator - if (first_item) - first_item = false; - else - response += F(",\r\n"); - -/* - Debug(F("(")) ; - Debug(++index) ; - Debug(F(") ")) ; - - if (me->name) Debug(me->name) ; - else Debug(F("NULL")) ; - - Debug(F("=")) ; - - if (me->value) Debug(me->value) ; - else Debug(F("NULL")) ; - - Debug(F(" '")) ; - Debug(me->checksum) ; - Debug(F("' ")); - - // Flags management - if ( me->flags) { - Debug(F("Flags:0x")); - Debugf("%02X => ", me->flags); - if ( me->flags & TINFO_FLAGS_EXIST) - Debug(F("Exist ")) ; - if ( me->flags & TINFO_FLAGS_UPDATED) - Debug(F("Updated ")) ; - if ( me->flags & TINFO_FLAGS_ADDED) - Debug(F("New ")) ; - } + if(! first_item) + // go to next node + me = me->next; - Debugln() ; -*/ - response += F("{\"na\":\""); - response += me->name ; - response += F("\", \"va\":\"") ; - response += me->value; - response += F("\", \"ck\":\"") ; - if (me->checksum == '"' || me->checksum == '\\' || me->checksum == '/') - response += '\\'; - response += (char) me->checksum; - response += F("\", \"fl\":"); - response += me->flags ; - response += '}' ; + + + if( ! me->free ) { + // First item do not add , separator + if (first_item) + first_item = false; + else + response += F(",\r\n"); + + if(validate_value_name(me->name)) { + //It's a known name : process the entry + response += F("{\"na\":\""); + response += me->name ; + response += F("\", \"va\":\"") ; + response += me->value; + response += F("\", \"ck\":\"") ; + if (me->checksum == '"' || me->checksum == '\\' || me->checksum == '/') + response += '\\'; + response += (char) me->checksum; + response += F("\", \"fl\":"); + response += me->flags ; + response += '}' ; + } else { + //Don't put this line in table : name is corrupted ! + need_reinit=true; + } + } } // Json end @@ -372,15 +389,13 @@ void tinfoJSONTable(void) Debugln(F("sending 404...")); server.send ( 404, "text/plain", "No data" ); } - Debug(F("sending...")); + //Debug(F("sending...")); server.send ( 200, "text/json", response ); - Debugln(F("OK!")); + //Debugln(response); + //Debugln(F("OK!")); + yield(); //Let a chance to other threads to work } - - - - /* ====================================================================== Function: getSysJSONData Purpose : Return JSON string containing system data @@ -400,7 +415,17 @@ void getSysJSONData(String & response) response += "{\"na\":\"Uptime\",\"va\":\""; response += sysinfo.sys_uptime; response += "\"},\r\n"; - + +#ifdef SENSOR + response += "{\"na\":\"Switch\",\"va\":\""; + if (SwitchState) + response += F("Open"); //switch ouvert + else + response += F("Closed"); //switch fermé + + response += "\"},\r\n"; +#endif + if (WiFi.status() == WL_CONNECTED) { response += "{\"na\":\"Wifi RSSI\",\"va\":\""; @@ -421,6 +446,10 @@ void getSysJSONData(String & response) response += nb_reconnect; response += "\"},\r\n"; + response += "{\"na\":\"Altérations Data détectées\",\"va\":\""; + response += nb_reinit; + response += "\"},\r\n"; + response += "{\"na\":\"WifInfo Version\",\"va\":\"" WIFINFO_VERSION "\"},\r\n"; response += "{\"na\":\"Compile le\",\"va\":\"" __DATE__ " " __TIME__ "\"},\r\n"; @@ -492,13 +521,35 @@ Comments: - void sysJSONTable() { String response = ""; - + + ESP.wdtFeed(); //Force software watchdog to restart from 0 getSysJSONData(response); // Just to debug where we are - Debug(F("Serving /system page...")); + //Debug(F("Serving /system page...")); server.send ( 200, "text/json", response ); + //Debugln(F("Ok!")); + yield(); //Let a chance to other threads to work +} + +/* ====================================================================== +Function: emoncmsJSONTable (added by Doume) +Purpose : prepare the JSON table needed to fill emoncms server with values + some values have been translated, because emoncms only + accept numeric values +Input : - +Output : Teleinfo values translated and filtered +Comments: - +====================================================================== */ +void emoncmsJSONTable() +{ + Debug(F("Serving /emoncms.json page...")); + String response = build_emoncms_json(); + + server.send ( 200, "text/json", response ); + //Debugln(response); Debugln(F("Ok!")); + yield(); //Let a chance to other threads to work } @@ -528,18 +579,17 @@ void getConfJSONData(String & r) r+=CFG_FORM_EMON_FREQ; r+=FPSTR(FP_QCQ); r+=config.emoncms.freq; r+= FPSTR(FP_QCNL); r+=CFG_FORM_OTA_AUTH; r+=FPSTR(FP_QCQ); r+=config.ota_auth; r+= FPSTR(FP_QCNL); r+=CFG_FORM_OTA_PORT; r+=FPSTR(FP_QCQ); r+=config.ota_port; r+= FPSTR(FP_QCNL); - r+=CFG_FORM_JDOM_HOST; r+=FPSTR(FP_QCQ); r+=config.jeedom.host; r+= FPSTR(FP_QCNL); r+=CFG_FORM_JDOM_PORT; r+=FPSTR(FP_QCQ); r+=config.jeedom.port; r+= FPSTR(FP_QCNL); r+=CFG_FORM_JDOM_URL; r+=FPSTR(FP_QCQ); r+=config.jeedom.url; r+= FPSTR(FP_QCNL); r+=CFG_FORM_JDOM_KEY; r+=FPSTR(FP_QCQ); r+=config.jeedom.apikey; r+= FPSTR(FP_QCNL); r+=CFG_FORM_JDOM_ADCO; r+=FPSTR(FP_QCQ); r+=config.jeedom.adco; r+= FPSTR(FP_QCNL); r+=CFG_FORM_JDOM_FREQ; r+=FPSTR(FP_QCQ); r+=config.jeedom.freq; r+= FPSTR(FP_QCNL); - r+=CFG_FORM_HTTPREQ_HOST; r+=FPSTR(FP_QCQ); r+=config.httpReq.host; r+= FPSTR(FP_QCNL); r+=CFG_FORM_HTTPREQ_PORT; r+=FPSTR(FP_QCQ); r+=config.httpReq.port; r+= FPSTR(FP_QCNL); r+=CFG_FORM_HTTPREQ_PATH; r+=FPSTR(FP_QCQ); r+=config.httpReq.path; r+= FPSTR(FP_QCNL); - r+=CFG_FORM_HTTPREQ_FREQ; r+=FPSTR(FP_QCQ); r+=config.httpReq.freq; + r+=CFG_FORM_HTTPREQ_FREQ; r+=FPSTR(FP_QCQ); r+=config.httpReq.freq; r+= FPSTR(FP_QCNL); + r+=CFG_FORM_HTTPREQ_SWIDX; r+=FPSTR(FP_QCQ); r+=config.httpReq.swidx; r+= F("\""); @@ -558,11 +608,13 @@ Comments: - void confJSONTable() { String response = ""; + //ESP.wdtFeed(); //Force software watchdog to restart from 0 getConfJSONData(response); // Just to debug where we are Debug(F("Serving /config page...")); server.send ( 200, "text/json", response ); Debugln(F("Ok!")); + yield(); //Let a chance to other threads to work } /* ====================================================================== @@ -630,8 +682,10 @@ Comments: - void spiffsJSONTable() { String response = ""; + //ESP.wdtFeed(); //Force software watchdog to restart from 0 getSpiffsJSONData(response); server.send ( 200, "text/json", response ); + yield(); //Let a chance to other threads to work } /* ====================================================================== @@ -644,9 +698,13 @@ Comments: - ====================================================================== */ void sendJSON(void) { + boolean first_item = true; ValueList * me = tinfo.getList(); String response = ""; + ESP.wdtFeed(); //Force software watchdog to restart from 0 + + Debug(F("Serving /json page...")); // Got at least one ? if (me) { // Json start @@ -656,13 +714,25 @@ void sendJSON(void) // Loop thru the node while (me->next) { - // go to next node - me = me->next; - response += F(",\"") ; - response += me->name ; - response += F("\":") ; - formatNumberJSON(response, me->value); - } + if(! first_item) + // go to next node + me = me->next; + + if( ! me->free ) { + if (first_item) + first_item = false; + + if(validate_value_name(me->name)) { + //It's a known name : process the entry + response += F(",\"") ; + response += me->name ; + response += F("\":") ; + formatNumberJSON(response, me->value); + } else { + need_reinit=true; + } // name validity + } //free entry + } //while // Json end response += FPSTR(FP_JSON_END) ; @@ -670,6 +740,9 @@ void sendJSON(void) server.send ( 404, "text/plain", "No data" ); } server.send ( 200, "text/json", response ); + //Debugln(response); + Debugln(F("Ok!")); + yield(); //Let a chance to other threads to work } @@ -722,6 +795,7 @@ void wifiScanJSON(void) Debug(F("sending...")); server.send ( 200, "text/json", response ); Debugln(F("Ok!")); + yield(); //Let a chance to other threads to work } @@ -795,7 +869,8 @@ void handleNotFound(void) // convert uri to char * for compare uri = server.uri().c_str(); - Debugf("handleNotFound(%s)\r\n", uri); + sprintf(buff,"handleNotFound(%s)\r\n", uri); + Debug(buff); // Got at least one and consistent URI ? if (me && uri && *uri=='/' && *++uri ) { @@ -850,4 +925,22 @@ void handleNotFound(void) // Led off LedBluOFF(); } +/* ====================================================================== +Function: validate_value_name +Purpose : check if value name is in known range of values.... +Input : name to check +Output : true if OK, false otherwise +Comments: - +====================================================================== */ +bool validate_value_name(String name) +{ + + for (int i=0 ; i < 35; i++ ) { + if( (tabnames[i].length() == name.length()) && (tabnames[i] == name) ) { + return true; + } + } + return false; //Not an existing name ! + //return true; +} diff --git a/examples/Wifinfo/webserver.h b/examples/Wifinfo/webserver.h index 3a33f90..c3e6066 100644 --- a/examples/Wifinfo/webserver.h +++ b/examples/Wifinfo/webserver.h @@ -31,11 +31,20 @@ // Exported variables/object instancied in main sketch // =================================================== -extern char response[]; -extern uint16_t response_idx; -extern int nb_reconnect; +extern char response[]; +extern uint16_t response_idx; +extern int nb_reconnect; +extern unsigned int nb_reinit; +extern bool need_reinit; +extern bool first_info_call; +extern int SwitchState; +extern char buff[]; -// declared exported function from route.cpp +// Exported function instancied in webclient.cpp +// ============================================= +extern String build_emoncms_json(void); + +// declared exported function from webserver.cpp // =================================================== void handleTest(void); void handleRoot(void); @@ -44,6 +53,7 @@ void handleNotFound(void); void tinfoJSONTable(void); void getSysJSONData(String & r); void sysJSONTable(void); +void emoncmsJSONTable(void); //Added by Doume void getConfJSONData(String & r); void confJSONTable(void); void getSpiffsJSONData(String & r); @@ -52,5 +62,6 @@ void sendJSON(void); void wifiScanJSON(void); void handleFactoryReset(void); void handleReset(void); +bool validate_value_name(String name); #endif diff --git a/library.json b/library.json index e6d3bdb..bbfb054 100644 --- a/library.json +++ b/library.json @@ -5,13 +5,15 @@ "repository": { "type": "git", - "url": "https://github.com/hallard/LibTeleinfo.git" + "url": "https://github.com/Doume/LibTeleinfo.git" }, - "version": "1.0.2", + "version": "1.0.5", "authors": { "name": "Charles-Henri Hallard", "url": "http://hallard.me" + "name": "Dominique DAMBRAIN", + "url": "http://www.dambrain.fr" }, "frameworks": "arduino", "platforms": "*" diff --git a/library.properties b/library.properties index 8278793..fc06ebe 100644 --- a/library.properties +++ b/library.properties @@ -1,7 +1,7 @@ name=LibTeleinfo -version=1.0.2 +version=1.0.5 author=Charles-Henri Hallard -maintainer=Charles-Henri Hallard +maintainer=Charles-Henri Hallard , Dominique DAMBRAIN sentence=Teleinfo French power meter reader and decoding paragraph=This is a generic Teleinfo French Meter Measure Library, it can be used on Arduino, Particle, ESP8266, Raspberry PI or anywhere you can do Cpp coding. category=Communication diff --git a/src/LibTeleinfo.cpp b/src/LibTeleinfo.cpp index 0ec99fa..b98bcf9 100644 --- a/src/LibTeleinfo.cpp +++ b/src/LibTeleinfo.cpp @@ -19,9 +19,22 @@ // // Edit : Tab size set to 2 but I converted tab to sapces // +// Modifié par Dominique DAMBRAIN 2017-07-10 (http://www.dambrain.fr) +// Version 1.0.5 +// Librairie LibTeleInfo : Allocation statique d'un tableau de stockage +// des variables (50 entrées) afin de proscrire les malloc/free +// pour éviter les altérations des noms & valeurs +// Modification en conséquence des séquences de scanning du tableau +// ATTENTION : Nécessite probablement un ESP-8266 type Wemos D1, +// car les variables globales occupent 42.284 octets +// // ********************************************************************************** #include "LibTeleinfo.h" +int ValueItem = 0; //Index of next position to use +struct _ValueList ValuesTab[50]; //Allocate static table of 50 items + // to don't use anymore malloc & free + /* ====================================================================== Class : TInfo @@ -32,11 +45,25 @@ Comments: - ====================================================================== */ TInfo::TInfo() { + ValueList * me; // Init of our linked list +/* _valueslist.name = NULL; _valueslist.value = NULL; +*/ +/* _valueslist.checksum = '\0'; _valueslist.flags = TINFO_FLAGS_NONE; +*/ + for(int i = 0; i < 50; i++) { + me = &ValuesTab[i]; + memset(&ValuesTab[i], 0, sizeof(_ValueList) ); //Also reset the 'free' marker + me->free=1; //Init each entry as free + me->flags = TINFO_FLAGS_NONE; + if(i < 49) + me->next = &ValuesTab[i+1]; + } + // callback _fn_ADPS = NULL; @@ -177,12 +204,12 @@ Comments: - state of the label changed by the function ====================================================================== */ ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t * flags) { - // Get our linked list - ValueList * me = &_valueslist; - + uint8_t lgname = strlen(name); uint8_t lgvalue = strlen(value); uint8_t thischeck = calcChecksum(name,value); + int firstfree = -1; + ValueList * me; // just some paranoia if (thischeck != checksum ) { @@ -190,121 +217,76 @@ ValueList * TInfo::valueAdd(char * name, char * value, uint8_t checksum, uint8_t TI_Debug('='); TI_Debug(value); TI_Debug(F(" '")); - TI_Debug((char) cheksum); + TI_Debug((char) checksum); TI_Debug(F("' Not added bad checksum calculated '")); TI_Debug((char) thischeck); TI_Debugln(F("'")); } else { // Got one and all seems good ? - if (me && lgname && lgvalue && checksum) { - // Create pointer on the new node - ValueList *newNode = NULL; - ValueList *parNode = NULL ; - - // Loop thru the node - while (me->next) { - // save parent node - parNode = me ; - - // go to next node - me = me->next; - - // Check if we already have this LABEL - if (strncmp(me->name, name, lgname) == 0) { - // Already got also this value, return US - if (strncmp(me->value, value, lgvalue) == 0) { - *flags |= TINFO_FLAGS_EXIST; - me->flags = *flags; - return ( me ); - } else { - // We changed the value - *flags |= TINFO_FLAGS_UPDATED; - me->flags = *flags ; - // Do we have enought space to hold new value ? - if (strlen(me->value) >= lgvalue ) { - // Copy it - strncpy(me->value, value , lgvalue ); - me->checksum = checksum ; - - // That's all - return (me); - } else { - // indicate our parent node that the next node - // is not us anymore but the next we have - parNode->next = me->next; - - // free up this node - free (me); - - // Return to parent (that will now point on next node and not us) - // and continue loop just in case we have sevral with same name - me = parNode; - } - } - } - } - - // Our linked list structure sizeof(ValueList) - // + Name + '\0' - // + Value + '\0' - size_t size ; - #ifdef ESP8266 - lgname = ESP8266_allocAlign(lgname+1); // Align name buffer - lgvalue = ESP8266_allocAlign(lgvalue+1); // Align value buffer - // Align the whole structure - size = ESP8266_allocAlign( sizeof(ValueList) + lgname + lgvalue ) ; - #else - size = sizeof(ValueList) + lgname + 1 + lgvalue + 1 ; - #endif - - // Create new node with size to store strings - if ((newNode = (ValueList *) malloc(size) ) == NULL) - return ( (ValueList *) NULL ); - - // get our buffer Safe - memset(newNode, 0, size); - - // Put the new node on the list - me->next = newNode; - - // First String located after last struct element - // Second String located after the First + \0 - newNode->checksum = checksum; - newNode->name = (char *) newNode + sizeof(ValueList); - newNode->value = (char *) newNode->name + lgname + 1; - - // Copy the string data - memcpy(newNode->name , name , lgname ); - memcpy(newNode->value, value , lgvalue ); - - // So we just created this node but was it new - // or was matter of text size ? - if ( (*flags & TINFO_FLAGS_UPDATED) == 0) { + if (lgname && lgvalue && checksum) { + // Parameters seems to be coherent + // Scan the existing table + int i; + for(i=0; i < ValueItem, i < 50 ; i++) { + me = &ValuesTab[i]; + if( ! me->free) { + if (strncmp(me->name, name, lgname) == 0) { + //entry found for the same value name : reuse it ! + if (strncmp(me->value, value, lgvalue) == 0) { + *flags |= TINFO_FLAGS_EXIST; + me->flags = *flags; + return ( me ); + } else { + //Exist, but value changed + *flags |= TINFO_FLAGS_UPDATED; + me->flags = *flags ; + // Copy new value + memset(me->value, 0, 16); + memcpy(me->value, value , lgvalue ); + me->checksum = checksum ; + + // That's all + return (me); + } + } //name comparison + } else { + //This entry is free + if(firstfree < 0) + firstfree=i; //It's the 1st one detected + } + } //for + + //No existing entry for this name : Create a new one + if(firstfree >= 0) { + //Use the 1st free entry found + i=firstfree; + } else { + if(i < 50) + ValueItem=i; //Note new entry as last one + else + return ( (ValueList *) NULL ); //Table saturated ! + } + + // i points the entry to use : get our buffer Safe + me = &ValuesTab[i]; + memset(me, 0, sizeof(_ValueList) ); //Also reset the 'free' marker + me->checksum = checksum; + if(i < 49) + me->next = &ValuesTab[i+1]; + + // Copy the string data (name & value) + memcpy(me->name, name , lgname ); + memcpy(me->value, value , lgvalue ); + if ( (*flags & TINFO_FLAGS_UPDATED) == 0) { // so we added this node ! *flags |= TINFO_FLAGS_ADDED ; - newNode->flags = *flags; + me->flags = *flags; } - - TI_Debug(F("Added '")); - TI_Debug(name); - TI_Debug('='); - TI_Debug(value); - TI_Debug(F("' '")); - TI_Debug((char) cheksum); - TI_Debugln(F("'")); - - // return pointer on the new node - return (newNode); - } - - } // Checksum OK - - - // Error or Already Exists - return ( (ValueList *) NULL); -} - - + // That's all + return (me); + } + } //Checksum check +} /* ====================================================================== Function: valueRemoveFlagged @@ -315,40 +297,21 @@ Comments: - ====================================================================== */ boolean TInfo::valueRemoveFlagged(uint8_t flags) { - boolean deleted = false; - - // Get our linked list - ValueList * me = &_valueslist; - ValueList *parNode = NULL ; - - // Got one and all seems good ? - if (me) { - // Loop thru the node - while (me->next) { - // save parent node - parNode = me ; - - // go to next node - me = me->next; - - // found the flags? - if (me->flags & flags ) { - // indicate our parent node that the next node - // is not us anymore but the next we have - parNode->next = me->next; - - // free up this node - free (me); - - // Return to parent (that will now point on next node and not us) - // and continue loop just in case we have sevral with same name - me = parNode; - deleted = true; - } - } - } - - return (deleted); + boolean deleted = false; + int i; + ValueList * me; + + for(i=0; i < ValueItem, i < 50; i++) { + me = &ValuesTab[i]; + if(! me->free ) { + if (me->flags & flags ) { + //memset(me, 0, sizeof(_ValueList) ); + me->free=1; + deleted=true; + } + } + } + return deleted; } /* ====================================================================== @@ -361,41 +324,26 @@ Comments: - boolean TInfo::valueRemove(char * name) { boolean deleted = false; - - // Get our linked list - ValueList * me = &_valueslist; - ValueList *parNode = NULL ; - uint8_t lgname = strlen(name); - - // Got one and all seems good ? - if (me && lgname) { - // Loop thru the node - while (me->next) { - // save parent node - parNode = me ; - - // go to next node - me = me->next; - - // found ? - if (strncmp(me->name, name, lgname) == 0) { - // indicate our parent node that the next node - // is not us anymore but the next we have - parNode->next = me->next; - - // free up this node - free (me); - - // Return to parent (that will now point on next node and not us) - // and continue loop just in case we have sevral with same name - me = parNode; - deleted = true; - } - } - } - - return (deleted); + int i; + ValueList * me; + + for(i=0 ; i < ValueItem, i < 50; i++) { + me = &ValuesTab[i]; + if( ! me->free ) { + //This entry is busy + // found ? + if (strncmp(me->name, name, lgname) == 0) { + memset(me->name, 0, 16 ); + // free up this entry + me->free=1; + + // and continue loop just in case we have several with same name + deleted = true; + } + } + } + return (deleted); } /* ====================================================================== @@ -408,30 +356,28 @@ Output : pointer to the value where we filled data NULL is not found char * TInfo::valueGet(char * name, char * value) { // Get our linked list - ValueList * me = &_valueslist; uint8_t lgname = strlen(name); + int i; + ValueList * me; // Got one and all seems good ? - if (me && lgname) { - - // Loop thru the node - while (me->next) { - - // go to next node - me = me->next; - - // Check if we match this LABEL - if (strncmp(me->name, name, lgname) == 0) { - // this one has a value ? - if (me->value) { - // copy to dest buffer - uint8_t lgvalue = strlen(me->value); - strncpy(value, me->value , lgvalue ); - return ( value ); + if (lgname) { + // Loop thru the table + for(i = 0; i < ValueItem, i < 50; i++) { + me = &ValuesTab[i]; + if( ! me->free) { + // Check if we match this LABEL + if (strncmp(me->name, name, lgname) == 0) { + // copy to dest buffer + uint8_t lgvalue = strlen(me->value); + strncpy(value, me->value , lgvalue ); + return ( value ); + } } - } - } - } + } //for + + } //lgname + // not found return ( NULL); } @@ -444,8 +390,9 @@ Output : Pointer ====================================================================== */ ValueList * TInfo::getList(void) { + ValueList * me = &ValuesTab[0]; // Get our linked list - return &_valueslist; + return me; } /* ====================================================================== @@ -457,51 +404,50 @@ Output : total number of values uint8_t TInfo::valuesDump(void) { // Get our linked list - ValueList * me = &_valueslist; + ValueList * me = &ValuesTab[0]; uint8_t index = 0; // Got one ? if (me) { // Loop thru the node - while (me->next) { - // go to next node - me = me->next; - - index++; - TI_Debug(index) ; - TI_Debug(F(") ")) ; - - if (me->name) - TI_Debug(me->name) ; - else - TI_Debug(F("NULL")) ; - - TI_Debug(F("=")) ; - - if (me->value) - TI_Debug(me->value) ; - else - TI_Debug(F("NULL")) ; - - TI_Debug(F(" '")) ; - TI_Debug(me->checksum) ; - TI_Debug(F("' ")); - - // Flags management - if ( me->flags) { - TI_Debug(F("Flags:0x")); - TI_Debugf("%02X =>", me->flags); - if ( me->flags & TINFO_FLAGS_EXIST) - TI_Debug(F("Exist ")) ; - if ( me->flags & TINFO_FLAGS_UPDATED) - TI_Debug(F("Updated ")) ; - if ( me->flags & TINFO_FLAGS_ADDED) - TI_Debug(F("New ")) ; - } - - TI_Debugln() ; - } - } + for(int i=0; i<50; i++) { + me = &ValuesTab[i]; + if( ! me->free ) { + index++; + TI_Debug(i) ; + TI_Debug(F(") ")) ; + + if (me->name) + TI_Debug(me->name) ; + else + TI_Debug(F("NULL")) ; + + TI_Debug(F("=")) ; + + if (me->value) + TI_Debug(me->value) ; + else + TI_Debug(F("NULL")) ; + + TI_Debug(F(" '")) ; + TI_Debug(me->checksum) ; + TI_Debug(F("' ")); + + // Flags management + if ( me->flags) { + TI_Debug(F("Flags:0x")); + TI_Debugf("%02X =>", me->flags); + if ( me->flags & TINFO_FLAGS_EXIST) + TI_Debug(F("Exist ")) ; + if ( me->flags & TINFO_FLAGS_UPDATED) + TI_Debug(F("Updated ")) ; + if ( me->flags & TINFO_FLAGS_ADDED) + TI_Debug(F("New ")) ; + } + TI_Debugln() ; + } //test if free + } //for + } //me exists return index; } @@ -515,14 +461,12 @@ Output : element numbers int TInfo::labelCount() { int count = 0; - - // Get our linked list - ValueList * me = &_valueslist; - - if (me) - while ((me = me->next)) - count++; - + ValueList * me; + for(int i=0 ; i < 50 ; i++) { + me = &ValuesTab[i]; + if( ! me->free) + count++; + } return (count); } @@ -534,30 +478,19 @@ Output : True if Ok False Otherwise ====================================================================== */ boolean TInfo::listDelete() { - // Get our linked list - ValueList * me = &_valueslist; - - // Got a pointer - if (me) { - ValueList *current; - // For each linked list - while ((current = me->next)) { - // Get the next - me->next = current->next; + ValueList * me; - // Free the current - free(current); - } - - // Free the top element - me->next = NULL ; - - // Ok - return (true); - } + for(int i = 0; i < 50; i++) { + me = &ValuesTab[i]; + memset(&ValuesTab[i], 0, sizeof(_ValueList) ); //Also reset the 'free' marker + me->free=1; //Init each entry as free + me->flags = TINFO_FLAGS_NONE; + if(i < 49) + me->next = &ValuesTab[i+1]; + } - return (false); + return(true); } /* ====================================================================== diff --git a/src/LibTeleinfo.h b/src/LibTeleinfo.h index e88758a..2f38b20 100644 --- a/src/LibTeleinfo.h +++ b/src/LibTeleinfo.h @@ -19,6 +19,15 @@ // // Edit : Tab size set to 2 but I converted tab to sapces // +// Modifié par Dominique DAMBRAIN 2017-07-10 (http://www.dambrain.fr) +// Version 1.0.5 +// Librairie LibTeleInfo : Allocation statique d'un tableau de stockage +// des variables (50 entrées) afin de proscrire les malloc/free +// pour éviter les altérations des noms & valeurs +// Modification en conséquence des séquences de scanning du tableau +// ATTENTION : Nécessite probablement un ESP-8266 type Wemos D1, +// car les variables globales occupent 42.284 octets +// // ********************************************************************************** #ifndef LibTeleinfo_h @@ -76,18 +85,22 @@ #pragma pack(push) // push current alignment to stack #pragma pack(1) // set alignment to 1 byte boundary + + // Linked list structure containing all values received +// Will be allocated statically typedef struct _ValueList ValueList; struct _ValueList { - ValueList *next; // next element - uint8_t checksum;// checksum - uint8_t flags; // specific flags - char * name; // LABEL of value name - char * value; // value + ValueList *next; // next element (for compatibility) + char name[16]; // LABEL of value name + char value[16]; // value + uint8_t checksum; // checksum + uint8_t flags; // specific flags + uint8_t free; // checksum + uint8_t filler; // unused, for boundary }; -#pragma pack(pop) // Library state machine enum _State_e {