Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
e5a7dcd51c | |||
431afd8295 |
@ -21,7 +21,7 @@ PubSubClient mqttclient(espClient);
|
|||||||
|
|
||||||
unsigned long previousMillis = 0;
|
unsigned long previousMillis = 0;
|
||||||
unsigned long previousForceMillis = 0;
|
unsigned long previousForceMillis = 0;
|
||||||
const long interval = 1000;
|
const long interval = 1000; //interval in ms
|
||||||
|
|
||||||
void mqttConnect() {
|
void mqttConnect() {
|
||||||
// Loop until we're reconnected
|
// Loop until we're reconnected
|
||||||
@ -183,7 +183,7 @@ void loop() {
|
|||||||
mqttPublish(&mqttclient);
|
mqttPublish(&mqttclient);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentMillis - previousForceMillis >= (30*interval)) {
|
if (currentMillis - previousForceMillis >= (30 * interval)) {
|
||||||
// save the last time you blinked the LED
|
// save the last time you blinked the LED
|
||||||
previousForceMillis = currentMillis;
|
previousForceMillis = currentMillis;
|
||||||
|
|
||||||
@ -194,6 +194,7 @@ void loop() {
|
|||||||
|
|
||||||
mqttForcePublish(&mqttclient);
|
mqttForcePublish(&mqttclient);
|
||||||
}
|
}
|
||||||
|
|
||||||
mqttclient.loop();
|
mqttclient.loop();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
174
tic.cpp
174
tic.cpp
@ -5,8 +5,6 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <PubSubClient.h>
|
#include <PubSubClient.h>
|
||||||
|
|
||||||
// #define DEBUG 1
|
|
||||||
|
|
||||||
struct GroupDetail TicValues[NB_ETIQUETTE] = {};
|
struct GroupDetail TicValues[NB_ETIQUETTE] = {};
|
||||||
|
|
||||||
// La lecture / ecriture des données tic s'effectue sur les variables data{1,2}.
|
// 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
|
Action actionJp1[11]; // actions définie pour jour +1
|
||||||
int nbActions;
|
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) {
|
static struct GroupDetail processGroup(String group) {
|
||||||
struct GroupDetail gd;
|
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);
|
unsigned char computedChecksum = calcCheckSum(group);
|
||||||
|
|
||||||
|
// Extract the name (etiquette) from the group
|
||||||
int indexgrp = group.indexOf(HT);
|
int indexgrp = group.indexOf(HT);
|
||||||
gd.name = group.substring(0, indexgrp);
|
gd.name = group.substring(0, indexgrp);
|
||||||
|
|
||||||
|
// Move to the value part
|
||||||
group = group.substring(indexgrp + 1);
|
group = group.substring(indexgrp + 1);
|
||||||
indexgrp = group.indexOf(HT);
|
indexgrp = group.indexOf(HT);
|
||||||
gd.value = group.substring(0, indexgrp);
|
gd.value = group.substring(0, indexgrp);
|
||||||
|
|
||||||
|
// Move to the horodate part, if it exists
|
||||||
group = group.substring(indexgrp + 1);
|
group = group.substring(indexgrp + 1);
|
||||||
indexgrp = group.indexOf(HT);
|
indexgrp = group.indexOf(HT);
|
||||||
String key = "";
|
if (indexgrp != -1) // Check if there is an horodate part
|
||||||
if (indexgrp != -1) // some parameters may have hour recording.
|
|
||||||
{
|
{
|
||||||
gd.horodate = gd.value;
|
gd.horodate = gd.value;
|
||||||
gd.value = group.substring(0, indexgrp);
|
gd.value = group.substring(0, indexgrp);
|
||||||
group = group.substring(indexgrp + 1);
|
group = group.substring(indexgrp + 1);
|
||||||
}
|
}
|
||||||
//gd.checksum = group;
|
|
||||||
if (group[0]==computedChecksum) {
|
// Verify the checksum
|
||||||
gd.checkok = true;
|
gd.checkok = (group[0] == computedChecksum);
|
||||||
} else {
|
|
||||||
gd.checkok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gd;
|
return gd;
|
||||||
}
|
}
|
||||||
@ -123,17 +155,21 @@ static void processTrame(String &data) {
|
|||||||
++t;
|
++t;
|
||||||
}
|
}
|
||||||
// If a match is found, update the corresponding TicValues entry if the group confirms the checksum
|
// If a match is found, update the corresponding TicValues entry if the group confirms the checksum
|
||||||
if (t < NB_ETIQUETTE) {
|
if (t < NB_ETIQUETTE) {
|
||||||
//If there is a value update
|
//If there is a value update or an horodate change, the value is identified as "updated"
|
||||||
if (TicValues[t].value.compareTo(gd.value) != 0 && gd.checkok){
|
if (TicValues[t].value.compareTo(gd.value) != 0 || TicValues[t].horodate.compareTo(gd.horodate) != 0) {
|
||||||
//There is some noise on instantaneous value, filter
|
//There is some noise on instantaneous values, make a basic filter. Also helps to reduce MQTT load
|
||||||
if (SelectedEtiquette[t] == "SINSTS" || SelectedEtiquette[t] == "SINSTS1" || SelectedEtiquette[t] == "SINSTS2" || SelectedEtiquette[t] == "SINSTS3") {
|
if (SelectedEtiquette[t] == "SINSTS" || SelectedEtiquette[t] == "SINSTS1" || SelectedEtiquette[t] == "SINSTS2" || SelectedEtiquette[t] == "SINSTS3" | SelectedEtiquette[t] == "SINSTI") {
|
||||||
int oldval = TicValues[t].value.toInt();
|
int oldval = TicValues[t].value.toInt();
|
||||||
int newcal = (gd.value.toInt() + oldval) / 2;
|
int newcal = (gd.value.toInt() + oldval) / 2;
|
||||||
if (newcal < oldval * 0.92 || newcal > oldval * 1.02) {
|
//there is a significant change, so update
|
||||||
|
//consider a change if delta is > 2%
|
||||||
|
if (newcal < oldval * 0.98 || newcal > oldval * 1.02) {
|
||||||
gd.updated = true;
|
gd.updated = true;
|
||||||
TicValues[t] = gd;
|
TicValues[t] = gd;
|
||||||
} else {
|
} 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);
|
TicValues[t].value = String(newcal);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -149,7 +185,13 @@ static void processTrame(String &data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
data = data.substring(index + 1);
|
||||||
@ -301,10 +343,19 @@ String ticValuesAsJson() {
|
|||||||
static char jres[150]; // Adjust size as needed
|
static char jres[150]; // Adjust size as needed
|
||||||
|
|
||||||
// Use snprintf to construct the JSON string efficiently
|
// Use snprintf to construct the JSON string efficiently
|
||||||
snprintf(jres, sizeof(jres),
|
if (TicValues[i].horodate.isEmpty()) {
|
||||||
"\"%s\": \"%s\"",
|
snprintf(jres, sizeof(jres),
|
||||||
SelectedEtiquette[i].c_str(),
|
"\"%s\": \"%s\"",
|
||||||
TicValues[i].value.c_str());
|
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;
|
response += jres;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,24 +392,52 @@ String ticBasicValuesAsJson() {
|
|||||||
return response;
|
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) {
|
void mqttPublish(PubSubClient *mqttclient) {
|
||||||
for (int i = 0; i < NB_ETIQUETTE; ++i) {
|
for (int i = 0; i < NB_ETIQUETTE; ++i) {
|
||||||
if (TicValues[i].updated) {
|
String topic = MQTT_TOPIC;
|
||||||
String topic = MQTT_TOPIC;
|
topic += "/",
|
||||||
topic += "/",
|
topic += SelectedEtiquette[i];
|
||||||
topic += SelectedEtiquette[i];
|
if (TicValues[i].updated && TicValues[i].checkok) {
|
||||||
mqttclient->publish(topic.c_str(), TicValues[i].value.c_str());
|
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;
|
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) {
|
void mqttForcePublish(PubSubClient *mqttclient) {
|
||||||
for (int i = 0; i < NB_ETIQUETTE; ++i) {
|
for (int i = 0; i < NB_ETIQUETTE; ++i) {
|
||||||
String topic = MQTT_TOPIC;
|
String topic = MQTT_TOPIC;
|
||||||
topic += "/",
|
topic += "/",
|
||||||
topic += SelectedEtiquette[i];
|
topic += SelectedEtiquette[i];
|
||||||
mqttclient->publish(topic.c_str(), TicValues[i].value.c_str());
|
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 +512,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;
|
|
||||||
}
|
|
||||||
|
27
tic.h
27
tic.h
@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
#define TIC
|
#define TIC
|
||||||
|
|
||||||
#define TIC_SPEED 9600 //9600 bps en mode standard ou 1500 bps pour le mode historique
|
#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 DEBUG_SPEED 115200 //vitesse port débug
|
||||||
#define HTTP_PORT 80 //port
|
#define HTTP_PORT 80 //port
|
||||||
|
|
||||||
// Définition des constantes pour les délimiteurs de trame TIC
|
// Définition des constantes pour les délimiteurs de trame TIC
|
||||||
//Uniquement le mode standard est supporté
|
//Uniquement le mode standard est supporté
|
||||||
@ -26,16 +26,16 @@
|
|||||||
|
|
||||||
// Constantes pour la taille des étiquettes et le nombre d'étiquettes
|
// Constantes pour la taille des étiquettes et le nombre d'étiquettes
|
||||||
#define MAX_CHAR_ETIQUETTE 9 //CF doc ENEDIS
|
#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
|
// Structure pour stocker les détails d'un groupe TIC
|
||||||
struct GroupDetail {
|
struct GroupDetail {
|
||||||
String globale;
|
//String globale;
|
||||||
String name; // Nom de l'étiquette
|
String name; // Nom de l'étiquette
|
||||||
String value; // Valeur associée à l'étiquette
|
String value; // Valeur associée à l'étiquette
|
||||||
String horodate; // Horodatage de la valeur
|
String horodate; // Horodatage de la valeur
|
||||||
bool updated;
|
bool updated;
|
||||||
bool checkok; //status of checksum
|
bool checkok; //status of checksum
|
||||||
};
|
};
|
||||||
|
|
||||||
// Structure pour les bits de statut du registre
|
// Structure pour les bits de statut du registre
|
||||||
@ -113,9 +113,9 @@ union RegistreStatus {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const static String SelectedEtiquette[NB_ETIQUETTE] = {
|
const static String SelectedEtiquette[NB_ETIQUETTE] = {
|
||||||
"ADSC", // Adresse du compteur
|
//"ADSC", // Adresse du compteur
|
||||||
"DATE", // Date et heure courantes
|
//"DATE", // Date et heure courantes
|
||||||
"NGTF", // Numéro de gestionnaire de réseau de transport
|
//"NGTF", // Numéro de gestionnaire de réseau de transport
|
||||||
"LTARF", // Libellé du tarif en cours
|
"LTARF", // Libellé du tarif en cours
|
||||||
"EAST", // Énergie active soutirée totale
|
"EAST", // Énergie active soutirée totale
|
||||||
"EASF01", "EASF02", "EASF03", "EASF04", // Énergie active soutirée par période tarifaire
|
"EASF01", "EASF02", "EASF03", "EASF04", // Énergie active soutirée par période tarifaire
|
||||||
@ -133,7 +133,10 @@ const static String SelectedEtiquette[NB_ETIQUETTE] = {
|
|||||||
"NTARF", // Nom du tarif en cours
|
"NTARF", // Nom du tarif en cours
|
||||||
"NJOURF", "NJOURF+1", "PJOURF+1", // Couleur du jour et du lendemain
|
"NJOURF", "NJOURF+1", "PJOURF+1", // Couleur du jour et du lendemain
|
||||||
"MSG1", "MSG2", // Messages d'information
|
"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
|
||||||
|
"CCASN", "CCAIN" // point de la courbe de charge soustirée / injectée
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tableau pour stocker les valeurs des groupes TIC identifiés
|
// Tableau pour stocker les valeurs des groupes TIC identifiés
|
||||||
@ -164,7 +167,7 @@ const static String kPointeMobile[4] = { "no", "PM1", "PM2", "PM3" };
|
|||||||
void readTicPort();
|
void readTicPort();
|
||||||
String ticValuesAsJson();
|
String ticValuesAsJson();
|
||||||
String ticBasicValuesAsJson();
|
String ticBasicValuesAsJson();
|
||||||
|
// Prototypes des fonctions pour envoyer les infos MQTT
|
||||||
void mqttPublish(PubSubClient *mqttclient);
|
void mqttPublish(PubSubClient *mqttclient);
|
||||||
void mqttForcePublish(PubSubClient *mqttclient);
|
void mqttForcePublish(PubSubClient *mqttclient);
|
||||||
unsigned char calcCheckSum(const String &data);//(GroupDetail *data);
|
|
||||||
#endif
|
#endif
|
Loading…
Reference in New Issue
Block a user