Compare commits

..

No commits in common. "main" and "ota" have entirely different histories.
main ... ota

7 changed files with 199 additions and 351 deletions

1
.gitignore vendored
View File

@ -1,6 +1,5 @@
#specific files #specific files
secret.h secret.h
build/
# ---> C++ # ---> C++
# Prerequisites # Prerequisites

Binary file not shown.

View File

@ -1,7 +1,6 @@
#include "serial.h" #include "serial.h"
#include "secret.h" #include "secret.h"
#include "tic.h" #include "tic.h"
#include "ota.h"
#include <Arduino.h> #include <Arduino.h>
@ -9,46 +8,50 @@
#include <WiFiClient.h> #include <WiFiClient.h>
#include <ESP8266WebServer.h> #include <ESP8266WebServer.h>
#include <ElegantOTA.h> #include <ElegantOTA.h>
#include <PubSubClient.h>
// Activer le Wi-Fi // Activer le Wi-Fi
#define WIFI_ENABLE #define WIFI_ENABLE
// Initialiser le serveur web si le Wi-Fi est activé // Initialiser le serveur web si le Wi-Fi est activé
ESP8266WebServer server(HTTP_PORT); ESP8266WebServer server(HTTP_PORT);
WiFiClient espClient;
PubSubClient mqttclient(espClient);
unsigned long previousMillis = 0; unsigned long ota_progress_millis = 0;
unsigned long previousForceMillis = 0;
const long interval = 1000; //interval in ms
void mqttConnect() { void onOTAStart() {
// Loop until we're reconnected // Log when OTA has started
while (!mqttclient.connected()) { Serial.println("OTA update started!");
// Create a random client ID // <Add your own code here>
String clientId = MQTT_CLIENTID; }
clientId += String(random(0xffff), HEX);
// Attempt to connect void onOTAProgress(size_t current, size_t final) {
if (mqttclient.connect(clientId.c_str(), MQTT_USERNAME, MQTT_PASSWORD)) { // Log every 1 second
// Once connected, publish an announcement... if (millis() - ota_progress_millis > 1000) {
mqttclient.publish(MQTT_TOPIC, "MQTT TIC interface online"); ota_progress_millis = millis();
} else { Serial.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
delay(1000);
}
} }
} }
void onOTAEnd(bool success) {
// Log when OTA has finished
if (success) {
Serial.println("OTA update finished successfully!");
} else {
Serial.println("There was an error during OTA update!");
}
// <Add your own code here>
}
// Fonction pour configurer et connecter au réseau Wi-Fi // Fonction pour configurer et connecter au réseau Wi-Fi
void setup_wifi() { void setup_wifi() {
delay(10); delay(10);
// Connexion au réseau Wi-Fi // Connexion au réseau Wi-Fi
DebugPort.println(); DebugPort.println();
DebugPort.print("Connecting to "); DebugPort.print("Connecting to ");
DebugPort.println(STASSID); DebugPort.println(ssid);
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.begin(STASSID, STAPSK); WiFi.begin(ssid, passPhrase);
int c = 0; int c = 0;
// Attendre la connexion Wi-Fi // Attendre la connexion Wi-Fi
@ -146,7 +149,7 @@ void setup() {
restServerRouting(); restServerRouting();
ElegantOTA.begin(&server); ElegantOTA.begin(&server);
// ElegantOTA callbacks // ElegantOTA callbacks
ElegantOTA.onStart(onOTAStart); ElegantOTA.onStart(onOTAStart);
ElegantOTA.onProgress(onOTAProgress); ElegantOTA.onProgress(onOTAProgress);
ElegantOTA.onEnd(onOTAEnd); ElegantOTA.onEnd(onOTAEnd);
@ -155,49 +158,16 @@ void setup() {
DebugPort.println("Start HTTP server"); DebugPort.println("Start HTTP server");
server.begin(); server.begin();
DebugPort.println("HTTP server started"); DebugPort.println("HTTP server started");
//Démarrer le serveur MQTT
mqttclient.setServer(MQTT_SERVER, MQTT_PORT);
#endif #endif
} }
// Boucle principale // Boucle principale
void loop() { void loop() {
ElegantOTA.loop(); ElegantOTA.loop();
server.handleClient(); server.handleClient();
readTicPort(); readTicPort();
unsigned long currentMillis = millis(); /*
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
//Interface MQTT
if (!mqttclient.connected()) {
mqttConnect();
}
mqttPublish(&mqttclient);
}
if (currentMillis - previousForceMillis >= (30 * interval)) {
// save the last time you blinked the LED
previousForceMillis = currentMillis;
//Interface MQTT
if (!mqttclient.connected()) {
mqttConnect();
}
mqttForcePublish(&mqttclient);
}
mqttclient.loop();
/*
// Si aucune requête n'est en cours, mettre l'ESP8266 en mode deep sleep // Si aucune requête n'est en cours, mettre l'ESP8266 en mode deep sleep
if (server.client().available() == 0) { if (server.client().available() == 0) {
goToDeepSleep(); goToDeepSleep();

32
ota.cpp
View File

@ -1,32 +0,0 @@
#include "ota.h"
#include "serial.h"
#include <Arduino.h>
#include <stddef.h>
unsigned long ota_progress_millis = 0;
void onOTAStart() {
// Log when OTA has started
DebugPort.println("OTA update started!");
// <Add your own code here>
}
void onOTAProgress(size_t current, size_t final) {
// Log every 1 second
if (millis() - ota_progress_millis > 1000) {
ota_progress_millis = millis();
DebugPort.printf("OTA Progress Current: %u bytes, Final: %u bytes\n", current, final);
}
}
void onOTAEnd(bool success) {
// Log when OTA has finished
if (success) {
DebugPort.println("OTA update finished successfully!");
} else {
DebugPort.println("There was an error during OTA update!");
}
// <Add your own code here>
}

12
ota.h
View File

@ -1,12 +0,0 @@
#ifndef OTA
#define OTA
#include <stddef.h>
void onOTAStart();
void onOTAProgress(size_t current, size_t final);
void onOTAEnd(bool success);
#endif

394
tic.cpp
View File

@ -1,99 +1,60 @@
#include "lwip/ip.h" #include "lwip/ip.h"
#include "tic.h" #include "tic.h"
#include "serial.h" #include "serial.h"
#include "secret.h"
#include <Arduino.h> #include <Arduino.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}.
// Pour éviter des pb, il y a un swap. ce qui permet d'avoir une variable en lecture seule, une en écriture seule. // Pour éviter des pb, il y a un swap. ce qui permet d'avoir une variable en lecture seule, une en écriture seule.
String data1 = ""; // Variable pour stocker la trame complète String data1 = ""; // Variable pour stocker la trame complète
String data2 = ""; // Variable pour stocker la trame complète String data2 = ""; // Variable pour stocker la trame complète
int nActiveData = 1; int nActiveData = 1;
boolean isReceiving = false; // Indicateur pour savoir si on est dans une trame boolean isReceiving = false; // Indicateur pour savoir si on est dans une trame
RegistreStatus regStatus; // definition du registre status RegistreStatus regStatus; // definition du registre status
RelaisStatus relaisStatus; // definition du relais status 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;
/** static struct GroupDetail processGroup(String group)
* 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; struct GroupDetail gd;
//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); 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);
if (indexgrp != -1) // Check if there is an horodate part String key = group.substring(0, indexgrp);
group = group.substring(indexgrp + 1);
indexgrp = group.indexOf(HT);
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 = key;
group = group.substring(indexgrp + 1);
} }
// Verify the checksum
gd.checkok = (group[0] == computedChecksum);
return gd; return gd;
} }
static void processStge(RegistreStatus *rs, String value) { static void processStge(RegistreStatus *rs, String value)
{
char stge[9] = ""; char stge[9] = "";
// copy in the char array // copy in the char array
strncpy(stge, value.c_str(), 8); strncpy(stge, value.c_str(), 8);
stge[8] = '\0'; stge[8] = '\0';
unsigned long l = strtoul(stge, NULL, 16); // Convert hex pair to unsigned long unsigned long l = strtoul(stge, NULL, 16); // Convert hex pair to unsigned long
rs->uli = l; rs->uli = l;
} }
static void processRelais(RelaisStatus *rs, String value) { static void processRelais(RelaisStatus *rs, String value)
{
char stge[4] = ""; char stge[4] = "";
// copy in the char array // copy in the char array
strncpy(stge, value.c_str(), 3); strncpy(stge, value.c_str(), 3);
@ -101,19 +62,24 @@ static void processRelais(RelaisStatus *rs, String value) {
rs->ui = strtoul(stge, NULL, 16); rs->ui = strtoul(stge, NULL, 16);
} }
static void processActionsCalendrier(String value) { static void processActionsCalendrier(String value)
{
nbActions = 0; nbActions = 0;
String s = value; String s = value;
while (s.length() > 0) { while (s.length() > 0)
{
int index = s.indexOf(SP); int index = s.indexOf(SP);
if (index == -1) // No space found if (index == -1) // No space found
{ {
break; break;
} else { }
else
{
char data[9] = ""; char data[9] = "";
data[8] = '\0'; data[8] = '\0';
strncpy(data, s.substring(0, index).c_str(), 8); strncpy(data, s.substring(0, index).c_str(), 8);
if (strncmp(data, NONUTILE, 8) != 0) { if (strncmp(data, NONUTILE, 8) != 0)
{
char stge[5] = ""; char stge[5] = "";
// copy ssss field // copy ssss field
memcpy(stge, &data[4], 4); memcpy(stge, &data[4], 4);
@ -136,14 +102,19 @@ static void processActionsCalendrier(String value) {
* *
* @param data A reference to a String containing the data frame to be processed. * @param data A reference to a String containing the data frame to be processed.
*/ */
static void processTrame(String &data) { static void processTrame(String &data)
while (data.length() > 0) { {
while (data.length() > 0)
{
// Find the position of the next carriage return (CR) character // Find the position of the next carriage return (CR) character
int index = data.indexOf(CR); int index = data.indexOf(CR);
// If no CR is found, exit the loop // If no CR is found, exit the loop
if (index == -1) { if (index == -1)
{
break; break;
} else { }
else
{
// Extract the group string between the start and the CR character // Extract the group string between the start and the CR character
String group = data.substring(1, index); String group = data.substring(1, index);
// Process the group to extract detailed information // Process the group to extract detailed information
@ -151,47 +122,26 @@ static void processTrame(String &data) {
// Check if the extracted group name matches any user-selected etiquette // Check if the extracted group name matches any user-selected etiquette
int t = 0; int t = 0;
while ((SelectedEtiquette[t] != gd.name) && (t < NB_ETIQUETTE)) { while ((SelectedEtiquette[t] != gd.name) && (t < NB_ETIQUETTE))
{
++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 (t < NB_ETIQUETTE) { if (t < NB_ETIQUETTE)
//If there is a value update or an horodate change, the value is identified as "updated" {
if (TicValues[t].value.compareTo(gd.value) != 0 || TicValues[t].horodate.compareTo(gd.horodate) != 0) { TicValues[t] = gd;
//There is some noise on instantaneous values, make a basic filter. Also helps to reduce MQTT load // Depending on the group name, call the appropriate processing function
if (SelectedEtiquette[t] == "SINSTS" || SelectedEtiquette[t] == "SINSTS1" || SelectedEtiquette[t] == "SINSTS2" || SelectedEtiquette[t] == "SINSTS3" | SelectedEtiquette[t] == "SINSTI") { if (gd.name == "STGE")
int oldval = TicValues[t].value.toInt(); {
int newcal = (gd.value.toInt() + oldval) / 2; processStge(&regStatus, gd.value);
//there is a significant change, so update }
//consider a change if delta is > 2% else if (gd.name == "RELAIS")
if (newcal < oldval * 0.98 || newcal > oldval * 1.02) { {
gd.updated = true; processRelais(&relaisStatus, gd.value);
TicValues[t] = gd; }
} else { else if (gd.name == "PJOURF+1")
//the change is limited. Just record the mean to keep it fresh, {
//but doesn't set the updated flag. processActionsCalendrier(gd.value);
TicValues[t].value = String(newcal);
}
} else {
gd.updated = true;
TicValues[t] = gd;
// Depending on the group name, call the appropriate processing function
if (gd.name == "STGE") {
processStge(&regStatus, gd.value);
} else if (gd.name == "RELAIS") {
processRelais(&relaisStatus, gd.value);
} else if (gd.name == "PJOURF+1") {
processActionsCalendrier(gd.value);
}
}
} else {
//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);
@ -199,30 +149,33 @@ static void processTrame(String &data) {
} }
} }
static char *actionJp1AsJson() { static char *actionJp1AsJson()
{
const int bufferSize = 1000; const int bufferSize = 1000;
static char jsonBuffer[bufferSize]; // Adjust size as needed static char jsonBuffer[bufferSize]; // Adjust size as needed
snprintf(jsonBuffer, bufferSize, "\"PJOURF+1\": ["); snprintf(jsonBuffer, bufferSize, "\"PJOURF+1\": [");
for (int i = 0; i < nbActions; i++) { for (int i = 0; i < nbActions; i++)
{
// Format each action // Format each action
char actionJson[256]; // To store individual action JSON string char actionJson[256]; // To store individual action JSON string
String relaisSec = ""; String relaisSec = "";
switch ((unsigned int)actionJp1[i].action.bits.relaisSec) { switch ((unsigned int)actionJp1[i].action.bits.relaisSec)
case 0: {
relaisSec = "no change"; case 0:
break; relaisSec = "no change";
case 1: break;
relaisSec = "tempo"; case 1:
break; relaisSec = "tempo";
case 2: break;
relaisSec = "open"; case 2:
break; relaisSec = "open";
case 3: break;
relaisSec = "closed"; case 3:
break; relaisSec = "closed";
default: break;
relaisSec = "unknown"; default:
relaisSec = "unknown";
} }
snprintf(actionJson, sizeof(actionJson), snprintf(actionJson, sizeof(actionJson),
" { \"startTime\": \"%c%c%c%c\", " " { \"startTime\": \"%c%c%c%c\", "
@ -236,9 +189,12 @@ static char *actionJp1AsJson() {
actionJp1[i].action.bits.relais1, actionJp1[i].action.bits.index); actionJp1[i].action.bits.relais1, actionJp1[i].action.bits.index);
// Append the current action's JSON to the overall JSON buffer // Append the current action's JSON to the overall JSON buffer
if (i == (nbActions - 1)) { // Last item, no comma at the end if (i == (nbActions - 1))
{ // Last item, no comma at the end
strncat(jsonBuffer, actionJson, bufferSize - strlen(jsonBuffer) - 1); strncat(jsonBuffer, actionJson, bufferSize - strlen(jsonBuffer) - 1);
} else { }
else
{
strncat(jsonBuffer, actionJson, bufferSize - strlen(jsonBuffer) - 1); strncat(jsonBuffer, actionJson, bufferSize - strlen(jsonBuffer) - 1);
strncat(jsonBuffer, ",", bufferSize - strlen(jsonBuffer) - 1); strncat(jsonBuffer, ",", bufferSize - strlen(jsonBuffer) - 1);
} }
@ -249,9 +205,10 @@ static char *actionJp1AsJson() {
return jsonBuffer; return jsonBuffer;
} }
static char *relaisStatusAsJson(RelaisStatusBits *status, String rawValue) { static char *relaisStatusAsJson(RelaisStatusBits *status, String rawValue)
{
// Pre-allocate buffer large enough to hold the JSON string // Pre-allocate buffer large enough to hold the JSON string
static char response[150]; // Adjust size as needed static char response[150]; // Adjust size as needed
// Use snprintf to construct the JSON string efficiently // Use snprintf to construct the JSON string efficiently
snprintf(response, sizeof(response), snprintf(response, sizeof(response),
"\"RELAIS\": " "\"RELAIS\": "
@ -278,9 +235,10 @@ static char *relaisStatusAsJson(RelaisStatusBits *status, String rawValue) {
return response; return response;
} }
static char *registreStatusAsJson(RegistreStatusBits *status, String rawValue) { static char *registreStatusAsJson(RegistreStatusBits *status, String rawValue)
{
// Pre-allocate buffer large enough to hold the JSON string // Pre-allocate buffer large enough to hold the JSON string
static char response[1000]; // Adjust size as needed static char response[1000]; // Adjust size as needed
// Use snprintf to construct the JSON string efficiently // Use snprintf to construct the JSON string efficiently
snprintf(response, sizeof(response), snprintf(response, sizeof(response),
@ -328,38 +286,39 @@ static char *registreStatusAsJson(RegistreStatusBits *status, String rawValue) {
return response; return response;
} }
String ticValuesAsJson() { String ticValuesAsJson()
{
String response = "{"; String response = "{";
for (int i = 0; i < NB_ETIQUETTE; ++i) { for (int i = 0; i < NB_ETIQUETTE; ++i)
{
if (SelectedEtiquette[i] == "STGE") { if (SelectedEtiquette[i] == "STGE")
{
response += registreStatusAsJson(&regStatus.bits, TicValues[i].value); response += registreStatusAsJson(&regStatus.bits, TicValues[i].value);
} else if (SelectedEtiquette[i] == "RELAIS") { }
else if (SelectedEtiquette[i] == "RELAIS")
{
response += relaisStatusAsJson(&relaisStatus.bits, TicValues[i].value); response += relaisStatusAsJson(&relaisStatus.bits, TicValues[i].value);
} else if (SelectedEtiquette[i] == "PJOURF+1") { }
else if (SelectedEtiquette[i] == "PJOURF+1")
{
response += actionJp1AsJson(); response += actionJp1AsJson();
} else { }
static char jres[150]; // Adjust size as needed else
{
static char jres[150]; // Adjust size as needed
// Use snprintf to construct the JSON string efficiently // Use snprintf to construct the JSON string efficiently
if (TicValues[i].horodate.isEmpty()) { snprintf(jres, sizeof(jres),
snprintf(jres, sizeof(jres), "\"%s\": \"%s\"",
"\"%s\": \"%s\"", SelectedEtiquette[i].c_str(),
SelectedEtiquette[i].c_str(), TicValues[i].value.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;
} }
if (i < (NB_ETIQUETTE - 1)) { if (i < (NB_ETIQUETTE - 1))
{
response += ','; response += ',';
} }
} }
@ -367,16 +326,20 @@ String ticValuesAsJson() {
return response; return response;
} }
String ticBasicValuesAsJson() { String ticBasicValuesAsJson()
{
String response = "{"; String response = "{";
for (int i = 0; i < NB_ETIQUETTE; ++i) { for (int i = 0; i < NB_ETIQUETTE; ++i)
{
if (SelectedEtiquette[i] == "LTARF" || SelectedEtiquette[i] == "EAST" || SelectedEtiquette[i] == "EASF01" || SelectedEtiquette[i] == "EASF02" || SelectedEtiquette[i] == "EASF03" || SelectedEtiquette[i] == "EASF04" || SelectedEtiquette[i] == "EASD01" || SelectedEtiquette[i] == "EASD02" || SelectedEtiquette[i] == "EASD03" || SelectedEtiquette[i] == "EASD04" || SelectedEtiquette[i] == "EAIT" || SelectedEtiquette[i] == "ERQ1" || SelectedEtiquette[i] == "ERQ2" || SelectedEtiquette[i] == "ERQ3" || SelectedEtiquette[i] == "ERQ4" || SelectedEtiquette[i] == "IRMS1" || SelectedEtiquette[i] == "IRMS2" || SelectedEtiquette[i] == "IRMS3" || SelectedEtiquette[i] == "URMS1" || SelectedEtiquette[i] == "URMS2" || SelectedEtiquette[i] == "URMS3" || SelectedEtiquette[i] == "SINSTS" || SelectedEtiquette[i] == "SINSTSI" || SelectedEtiquette[i] == "SINSTS1" || SelectedEtiquette[i] == "SINSTS2" || SelectedEtiquette[i] == "SINSTS3" || SelectedEtiquette[i] == "SINSTSI") { if (SelectedEtiquette[i] == "LTARF" || SelectedEtiquette[i] == "EAST" || SelectedEtiquette[i] == "EASF01" || SelectedEtiquette[i] == "EASF02" || SelectedEtiquette[i] == "EASF03" || SelectedEtiquette[i] == "EASF04" || SelectedEtiquette[i] == "EASD01" || SelectedEtiquette[i] == "EASD02" || SelectedEtiquette[i] == "EASD03" || SelectedEtiquette[i] == "EASD04" || SelectedEtiquette[i] == "EAIT" || SelectedEtiquette[i] == "ERQ1" || SelectedEtiquette[i] == "ERQ2" || SelectedEtiquette[i] == "ERQ3" || SelectedEtiquette[i] == "ERQ4" || SelectedEtiquette[i] == "IRMS1" || SelectedEtiquette[i] == "IRMS2" || SelectedEtiquette[i] == "IRMS3" || SelectedEtiquette[i] == "URMS1" || SelectedEtiquette[i] == "URMS2" || SelectedEtiquette[i] == "URMS3" || SelectedEtiquette[i] == "SINSTS" || SelectedEtiquette[i] == "SINSTSI" || SelectedEtiquette[i] == "SINSTS1" || SelectedEtiquette[i] == "SINSTS2" || SelectedEtiquette[i] == "SINSTS3" || SelectedEtiquette[i] == "SINSTSI")
{
static char jres[150]; // Adjust size as needed static char jres[150]; // Adjust size as needed
if (response != "{") { if (response != "{")
{
response += ","; response += ",";
} }
@ -392,55 +355,6 @@ 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) {
for (int i = 0; i < NB_ETIQUETTE; ++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");
}
}
/** /**
* Reads data from the TicPort and processes it according to specific control characters. * Reads data from the TicPort and processes it according to specific control characters.
* *
@ -455,60 +369,80 @@ void mqttForcePublish(PubSubClient *mqttclient) {
* *
* The built-in LED is used to indicate the state of data reception. * The built-in LED is used to indicate the state of data reception.
*/ */
void readTicPort() { void readTicPort()
{
// Check TicPort availability // Check TicPort availability
if (TicPort.available()) { if (TicPort.available())
byte incomingByte = TicPort.read(); // Read a byte from the TicPort {
byte incomingByte = TicPort.read(); // Read a byte from the TicPort
// Check if the incoming byte is the End Of Transmission (EOT) character // Check if the incoming byte is the End Of Transmission (EOT) character
if (incomingByte == EOT) { if (incomingByte == EOT)
{
// Force the end of transmission // Force the end of transmission
// Reject everything // Reject everything
isReceiving = false; isReceiving = false;
digitalWrite(LED_BUILTIN, HIGH); // Turn the built-in LED to indicate the end of transmission digitalWrite(LED_BUILTIN, HIGH); // Turn the built-in LED to indicate the end of transmission
} }
// Check if the system is currently receiving data // Check if the system is currently receiving data
if (isReceiving) { if (isReceiving)
{
// Check if the end of the frame is reached (ETX character) // Check if the end of the frame is reached (ETX character)
if (incomingByte == ETX) { if (incomingByte == ETX)
{
// Extract the useful part of the frame // Extract the useful part of the frame
if (nActiveData == 1) { if (nActiveData == 1)
processTrame(data1); // Process the data in data1 {
} else { processTrame(data1); // Process the data in data1
processTrame(data2); // Process the data in data2 }
else
{
processTrame(data2); // Process the data in data2
} }
// Indicate that the data reception is complete // Indicate that the data reception is complete
isReceiving = false; isReceiving = false;
digitalWrite(LED_BUILTIN, HIGH); digitalWrite(LED_BUILTIN, HIGH);
// Debugging information: Print the extracted data // Debugging information: Print the extracted data
#ifdef DEBUG #ifdef DEBUG
for (int i = 0; i < NB_ETIQUETTE; ++i) { for (int i = 0; i < NB_ETIQUETTE; ++i)
{
DebugPort.print(TicValues[i].name); DebugPort.print(TicValues[i].name);
DebugPort.print(":"); DebugPort.print(":");
DebugPort.println(TicValues[i].value); DebugPort.println(TicValues[i].value);
} }
#endif #endif
} else { }
else
{
// Add the incoming byte to the current frame // Add the incoming byte to the current frame
if (nActiveData == 1) { if (nActiveData == 1)
data1 += (char)incomingByte; // Append the byte to data1 {
} else { data1 += (char)incomingByte; // Append the byte to data1
data2 += (char)incomingByte; // Append the byte to data2 }
else
{
data2 += (char)incomingByte; // Append the byte to data2
} }
} }
} else { }
else
{
// Look for the start of the frame (STX character) // Look for the start of the frame (STX character)
if (incomingByte == STX) { if (incomingByte == STX)
{
isReceiving = true; isReceiving = true;
digitalWrite(LED_BUILTIN, LOW); digitalWrite(LED_BUILTIN, LOW);
if (nActiveData == 1) { if (nActiveData == 1)
{
data2 = ""; data2 = "";
nActiveData = 2; nActiveData = 2;
} else { }
else
{
data1 = ""; data1 = "";
nActiveData = 1; nActiveData = 1;
} }
} }
} }
} }
} }

27
tic.h
View File

@ -4,16 +4,14 @@
#define TIC_DEF #define TIC_DEF
#include <Arduino.h> #include <Arduino.h>
#include <PubSubClient.h>
#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é
//CF document ENEDIS //CF document ENEDIS
#define STX 0x02 // Début de la trame : 0x02 (<STX>) #define STX 0x02 // Début de la trame : 0x02 (<STX>)
#define ETX 0x03 // Fin de la trame : 0x03 (<ETX>) End Of Text #define ETX 0x03 // Fin de la trame : 0x03 (<ETX>) End Of Text
@ -26,16 +24,13 @@
// 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 49 #define NB_ETIQUETTE 45
// 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 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 checkok; //status of checksum
}; };
// Structure pour les bits de statut du registre // Structure pour les bits de statut du registre
@ -113,9 +108,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,10 +128,7 @@ 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
@ -167,7 +159,4 @@ 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 mqttForcePublish(PubSubClient *mqttclient);
#endif #endif