From 431afd8295ede18aa42ccf855709bb52bc817047 Mon Sep 17 00:00:00 2001 From: Nagoydede Date: Tue, 29 Apr 2025 00:06:24 +0200 Subject: [PATCH] enhance valus update and mqtt refresh. implements status in mqtt. Confirm horodate OK. Update parameter list --- linky_tic.ino | 5 +- tic.cpp | 167 ++++++++++++++++++++++++++++++-------------------- tic.h | 21 ++++--- 3 files changed, 115 insertions(+), 78 deletions(-) diff --git a/linky_tic.ino b/linky_tic.ino index 846b0eb..f67f695 100644 --- a/linky_tic.ino +++ b/linky_tic.ino @@ -21,7 +21,7 @@ PubSubClient mqttclient(espClient); unsigned long previousMillis = 0; unsigned long previousForceMillis = 0; -const long interval = 1000; +const long interval = 1000; //interval in ms void mqttConnect() { // Loop until we're reconnected @@ -183,7 +183,7 @@ void loop() { mqttPublish(&mqttclient); } - if (currentMillis - previousForceMillis >= (30*interval)) { + if (currentMillis - previousForceMillis >= (30 * interval)) { // save the last time you blinked the LED previousForceMillis = currentMillis; @@ -194,6 +194,7 @@ void loop() { mqttForcePublish(&mqttclient); } + mqttclient.loop(); /* diff --git a/tic.cpp b/tic.cpp index d37a7c3..0f649c6 100644 --- a/tic.cpp +++ b/tic.cpp @@ -5,8 +5,6 @@ #include #include -// #define DEBUG 1 - struct GroupDetail TicValues[NB_ETIQUETTE] = {}; // La lecture / ecriture des données tic s'effectue sur les variables data{1,2}. @@ -21,33 +19,67 @@ RelaisStatus relaisStatus; // definition du relais status Action actionJp1[11]; // actions définie pour jour +1 int nbActions; +/** + * Calculates the checksum for a given data string according to ENEDIS specifications. + * Supports standard data mode (not history mode). [well to be honest, who has interest to keep the history mode.] + * Group format in standard mode with timestamp (horodatage) - Last HT included in checksum + * LF etiquette HT horodatage HT donnee HT Chk CR + * 0A 09 09 09 0D + * \____________checkum_______________/ + + * Group format in standard mode without timestamp (horodatage) - Last HT included in checksum + * LF etiquette HT donnee HT Chk CR + * 0A 09 09 0D + * \_____checkum________/ + * + * + * @param data The input string for which the checksum is to be calculated. + * @return The calculated checksum as an unsigned char. + */ +unsigned char calcCheckSum(const String &data) { + unsigned int sum = 0; + + // Calculate the sum of ASCII values, excluding the checksum character + // The string does not contain the CR char. The char before CR is the checksum. + for (size_t i = 0; i < data.length() - 1; ++i) { + sum += data[i]; + } + + // Truncate the sum to 6 bits + sum &= 0x3F; + + // Add 0x20 to get the final checksum + return (unsigned char)sum + 0x20; +} + static struct GroupDetail processGroup(String group) { struct GroupDetail gd; - gd.globale = group; + //gd.globale = group; // Store the entire group for reference during debug + + // Calculate the checksum for the entire group unsigned char computedChecksum = calcCheckSum(group); + // Extract the name (etiquette) from the group int indexgrp = group.indexOf(HT); gd.name = group.substring(0, indexgrp); + // Move to the value part group = group.substring(indexgrp + 1); indexgrp = group.indexOf(HT); gd.value = group.substring(0, indexgrp); + // Move to the horodate part, if it exists group = group.substring(indexgrp + 1); indexgrp = group.indexOf(HT); - String key = ""; - if (indexgrp != -1) // some parameters may have hour recording. + if (indexgrp != -1) // Check if there is an horodate part { gd.horodate = gd.value; gd.value = group.substring(0, indexgrp); group = group.substring(indexgrp + 1); } - //gd.checksum = group; - if (group[0]==computedChecksum) { - gd.checkok = true; - } else { - gd.checkok = false; - } + + // Verify the checksum + gd.checkok = (group[0] == computedChecksum); return gd; } @@ -123,17 +155,20 @@ static void processTrame(String &data) { ++t; } // If a match is found, update the corresponding TicValues entry if the group confirms the checksum - if (t < NB_ETIQUETTE) { - //If there is a value update - if (TicValues[t].value.compareTo(gd.value) != 0 && gd.checkok){ + if (t < NB_ETIQUETTE) { + //If there is a value update.... + if (TicValues[t].value.compareTo(gd.value) != 0 || TicValues[t].horodate.compareTo(gd.horodate) != 0) { //There is some noise on instantaneous value, filter if (SelectedEtiquette[t] == "SINSTS" || SelectedEtiquette[t] == "SINSTS1" || SelectedEtiquette[t] == "SINSTS2" || SelectedEtiquette[t] == "SINSTS3") { int oldval = TicValues[t].value.toInt(); int newcal = (gd.value.toInt() + oldval) / 2; + //there is a significant change, so update if (newcal < oldval * 0.92 || newcal > oldval * 1.02) { gd.updated = true; TicValues[t] = gd; } else { + //the change is limited. Just record the mean to keep it fresh, + //but doesn't set the updated flag. TicValues[t].value = String(newcal); } } else { @@ -149,7 +184,13 @@ static void processTrame(String &data) { } } } else { - TicValues[t].updated = false; + //there is no value update, but the checksum is not ok (strange case, but...) + if (TicValues[t].checkok != gd.checkok) { + TicValues[t] = gd; + TicValues[t].updated = true; + } else { + TicValues[t].updated = false; + } } } data = data.substring(index + 1); @@ -301,10 +342,19 @@ String ticValuesAsJson() { static char jres[150]; // Adjust size as needed // Use snprintf to construct the JSON string efficiently - snprintf(jres, sizeof(jres), - "\"%s\": \"%s\"", - SelectedEtiquette[i].c_str(), - TicValues[i].value.c_str()); + if (TicValues[i].horodate.isEmpty()) { + snprintf(jres, sizeof(jres), + "\"%s\": \"%s\"", + SelectedEtiquette[i].c_str(), + TicValues[i].value.c_str()); + } else { + // Include horodate if it is not empty + snprintf(jres, sizeof(jres), + "\"%s\": {\"value\": \"%s\", \"horodate\": \"%s\"}", + SelectedEtiquette[i].c_str(), + TicValues[i].value.c_str(), + TicValues[i].horodate.c_str()); + } response += jres; } @@ -341,24 +391,52 @@ String ticBasicValuesAsJson() { return response; } +/** + * Publishes the list of updated TIC values to the MQTT broker. + * + * This function iterates over the TicValues array and publishes the values + * that have been updated to the corresponding MQTT topics. + * After the publication, the values are not anymore identified as Updated. + * @param mqttclient Pointer to the PubSubClient instance used to publish messages. + */ void mqttPublish(PubSubClient *mqttclient) { for (int i = 0; i < NB_ETIQUETTE; ++i) { - if (TicValues[i].updated) { - String topic = MQTT_TOPIC; - topic += "/", - topic += SelectedEtiquette[i]; + String topic = MQTT_TOPIC; + topic += "/", + topic += SelectedEtiquette[i]; + if (TicValues[i].updated && TicValues[i].checkok) { mqttclient->publish(topic.c_str(), TicValues[i].value.c_str()); + + if (!TicValues[i].horodate.isEmpty()) { + mqttclient->publish((topic + "/date").c_str(), TicValues[i].horodate.c_str()); + } + TicValues[i].updated = false; } + + if (TicValues[i].updated && !TicValues[i].checkok) { + topic += "/status"; + mqttclient->publish(topic.c_str(), TicValues[i].checkok ? "Ok" : "Not Ok"); + } } } +/** + * Forces the publication of all TIC values to the MQTT broker. + * + * This function iterates over all TIC values and publishes them to their + * corresponding MQTT topics, regardless of whether they have been updated. + * + * @param mqttclient Pointer to the PubSubClient instance used to publish messages. + */ void mqttForcePublish(PubSubClient *mqttclient) { for (int i = 0; i < NB_ETIQUETTE; ++i) { String topic = MQTT_TOPIC; topic += "/", topic += SelectedEtiquette[i]; mqttclient->publish(topic.c_str(), TicValues[i].value.c_str()); + topic += "/status"; + mqttclient->publish(topic.c_str(), TicValues[i].checkok ? "Ok" : "Not Ok"); } } @@ -433,46 +511,3 @@ void readTicPort() { } } } - -// Function to calculate the checksum -unsigned char calcCheckSum(const String &data) { - unsigned int sum = 0; - - // Calculate the sum of ASCII values - for (size_t i = 0; i < data.length() - 1; ++i) { - sum += data[i]; - } - - // Truncate the sum to 6 bits - sum &= 0x3F; - - // Add 0x20 to get the final checksum - unsigned char checksum = sum + 0x20; - - return checksum; -} - -unsigned char calcCheckSum2(GroupDetail *data) { - char c; - uint sum = 2 * HT; - - if (data->name.length() && data->value.length()) { - for (char &c : data->name) { - if (c >= 0x20 && c <= 0x7E) { - sum += c; - } else { - return 0; - } - } - - for (char &c : data->value) { - if (c >= 0x20 && c <= 0x7E) { - sum += c; - } else { - return 0; - } - } - return (sum & 0x3f) + 0x20; - } - return 0; -} diff --git a/tic.h b/tic.h index c930010..8fa97ce 100644 --- a/tic.h +++ b/tic.h @@ -8,9 +8,9 @@ #define TIC -#define TIC_SPEED 9600 //9600 bps en mode standard ou 1500 bps pour le mode historique -#define DEBUG_SPEED 115200 //vitesse port débug -#define HTTP_PORT 80 //port +#define TIC_SPEED 9600 //9600 bps en mode standard ou 1500 bps pour le mode historique +#define DEBUG_SPEED 115200 //vitesse port débug +#define HTTP_PORT 80 //port // Définition des constantes pour les délimiteurs de trame TIC //Uniquement le mode standard est supporté @@ -26,16 +26,16 @@ // Constantes pour la taille des étiquettes et le nombre d'étiquettes #define MAX_CHAR_ETIQUETTE 9 //CF doc ENEDIS -#define NB_ETIQUETTE 45 +#define NB_ETIQUETTE 49 // Structure pour stocker les détails d'un groupe TIC struct GroupDetail { - String globale; + //String globale; String name; // Nom de l'étiquette String value; // Valeur associée à l'étiquette String horodate; // Horodatage de la valeur bool updated; - bool checkok; //status of checksum + bool checkok; //status of checksum }; // Structure pour les bits de statut du registre @@ -114,7 +114,7 @@ union RegistreStatus { const static String SelectedEtiquette[NB_ETIQUETTE] = { "ADSC", // Adresse du compteur - "DATE", // Date et heure courantes + //"DATE", // Date et heure courantes "NGTF", // Numéro de gestionnaire de réseau de transport "LTARF", // Libellé du tarif en cours "EAST", // Énergie active soutirée totale @@ -133,7 +133,9 @@ const static String SelectedEtiquette[NB_ETIQUETTE] = { "NTARF", // Nom du tarif en cours "NJOURF", "NJOURF+1", "PJOURF+1", // Couleur du jour et du lendemain "MSG1", "MSG2", // Messages d'information - "PPOINTE" // Préavis de pointe mobile + "PPOINTE", // Préavis de pointe mobile + "SMAXSN", "SMAXSN1", "SMAXSN2", "SMAXSN3", //Puissance app max soutiree + "SMAXIN" //Puissance app max injectée }; // Tableau pour stocker les valeurs des groupes TIC identifiés @@ -165,6 +167,5 @@ void readTicPort(); String ticValuesAsJson(); String ticBasicValuesAsJson(); void mqttPublish(PubSubClient *mqttclient); -void mqttForcePublish(PubSubClient *mqttclient); -unsigned char calcCheckSum(const String &data);//(GroupDetail *data); +void mqttForcePublish(PubSubClient *mqttclient); #endif \ No newline at end of file