From 9adcf6506ecbd8be3b2176ffefb75c6a7449c1c7 Mon Sep 17 00:00:00 2001 From: Nagoydede Date: Mon, 24 Mar 2025 22:11:49 +0100 Subject: [PATCH] Initial commit --- README.md | 37 +++- linky_tic.ino | 121 +++++++++++++ secret-generic.h | 18 ++ serial.h | 8 + tic.cpp | 448 +++++++++++++++++++++++++++++++++++++++++++++++ tic.h | 136 ++++++++++++++ 6 files changed, 767 insertions(+), 1 deletion(-) create mode 100644 linky_tic.ino create mode 100644 secret-generic.h create mode 100644 serial.h create mode 100644 tic.cpp create mode 100644 tic.h diff --git a/README.md b/README.md index f8e332d..2fa6cf4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,37 @@ -# linky_tic +# linkytic +interface basique pour lire les données tic du compteur linky et les partager en format json via un esp8266/node-mcu. +## Configuration +Créer un fichier secret.h (pour définir les logins et pwd du wifi. +''' +// Secrets for your local home network + +// This is a "hard way" to configure your local WiFi network name and passphrase +// into the source code and the uploaded sketch. +// +// Using the WiFi Manager is preferred and avoids reprogramming when your network changes. +// See https://homeding.github.io/#page=/wifimanager.md + +// ssid and passPhrase can be used when compiling for a specific environment as a 2. option. + +// add you wifi network name and PassPhrase or use WiFi Manager +#ifndef STASSID +#define STASSID "Wifi_Id" +#define STAPSK "Wifi_pwd" +#endif + +const char *ssid = STASSID; +const char *passPhrase = STAPSK; +''' + +Le code est prévu pour un linky en mode standard. Il devrait être fonctionel en mode historique en changent la vitesse du port (1500bps en historique, 9600bps en standard). + +##Interface +Un simple optocoupleur suffit. +Le schéma disponible sur https://hallard.me/demystifier-la-teleinfo/ est fonctionel chez moi (sans ajout du transistor). + +##Documents +Enedis-NOI-CPT_54E version 3 (01/06/2018) + + + diff --git a/linky_tic.ino b/linky_tic.ino new file mode 100644 index 0000000..76b4caf --- /dev/null +++ b/linky_tic.ino @@ -0,0 +1,121 @@ +#include "serial.h" +#include "secret.h" +#include "tic.h" + +#include +#include +#include +#include + + +#define WIFI_ENABLE //enable wifi +#define SERVER_NAME "ticweb" +#ifdef WIFI_ENABLE +ESP8266WebServer server(80); +#endif + +void setup_wifi() { + delay(10); + // We start by connecting to a WiFi network + DebugPort.println(); + DebugPort.print("Connecting to "); + DebugPort.println(ssid); + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, passPhrase); + + int c = 0; + while (WiFi.status() != WL_CONNECTED) { + delay(500); + DebugPort.print("."); + } + DebugPort.println(""); + DebugPort.println("WiFi connected"); + DebugPort.println("IP address: "); + DebugPort.println(WiFi.localIP()); +} + +void getSettings() { + String response = "{"; + + response += "\"ip\": \"" + WiFi.localIP().toString() + "\""; + response += ",\"gw\": \"" + WiFi.gatewayIP().toString() + "\""; + response += ",\"nm\": \"" + WiFi.subnetMask().toString() + "\""; + + if (server.arg("signalStrength") == "true") { + response += ",\"signalStrengh\": \"" + String(WiFi.RSSI()) + "\""; + } + + if (server.arg("chipInfo") == "true") { + response += ",\"chipId\": \"" + String(ESP.getChipId()) + "\""; + response += ",\"flashChipId\": \"" + String(ESP.getFlashChipId()) + "\""; + response += ",\"flashChipSize\": \"" + String(ESP.getFlashChipSize()) + "\""; + response += ",\"flashChipRealSize\": \"" + String(ESP.getFlashChipRealSize()) + "\""; + } + if (server.arg("freeHeap") == "true") { + response += ",\"freeHeap\": \"" + String(ESP.getFreeHeap()) + "\""; + } + response += "}"; + + server.send(200, "text/json", response); +} + +void getTicData() { + String response = ticValuesAsJson(); + server.send(200, "text/json", response.c_str()); +} + +void getTicBasicData() { + String response = ticBasicValuesAsJson(); + server.send(200, "text/json", response.c_str()); +} + +// Define routing +void restServerRouting() { + server.on("/", HTTP_GET, []() { + server.send(200, F("text/html"), + F("Welcome to the REST Web Server")); + }); + server.on(F("/settings"), HTTP_GET, getSettings); + server.on(F("/ticdata"), HTTP_GET, getTicData); + server.on(F("/ticbasic"), HTTP_GET, getTicBasicData); +} + +void setup_serial() { + //debug interface + //there is only RX on Serial 0 interface + //As the port is shared with usb debug, need to readress it + //thanks to swap. + //The speed is not appropriate for debug. + //Debug serial port is on Serial 1, TX only + DebugPort.begin(9600, SERIAL_7E1); + +//data acquisition interface +#ifdef TIC + TicPort.begin(9600, SERIAL_7E1); + Serial.swap(); +#else + TicPort.begin(115200); +#endif +} + +void setup() { + + setup_serial(); + +#ifdef WIFI_ENABLE + setup_wifi(); + // Set server routing + restServerRouting(); + + // Start server + DebugPort.println("Start HTTP server"); + server.begin(); + DebugPort.println("HTTP server started"); +#endif +} + +void loop() { + server.handleClient(); + readTicPort(); +} diff --git a/secret-generic.h b/secret-generic.h new file mode 100644 index 0000000..ec7d542 --- /dev/null +++ b/secret-generic.h @@ -0,0 +1,18 @@ +// Secrets for your local home network + +// This is a "hard way" to configure your local WiFi network name and passphrase +// into the source code and the uploaded sketch. +// +// Using the WiFi Manager is preferred and avoids reprogramming when your network changes. +// See https://homeding.github.io/#page=/wifimanager.md + +// ssid and passPhrase can be used when compiling for a specific environment as a 2. option. + +// add you wifi network name and PassPhrase or use WiFi Manager +#ifndef STASSID +#define STASSID "WIFI-ID" +#define STAPSK "WIFI-PWD" +#endif + +const char *ssid = STASSID; +const char *passPhrase = STAPSK; \ No newline at end of file diff --git a/serial.h b/serial.h new file mode 100644 index 0000000..d6ea4a8 --- /dev/null +++ b/serial.h @@ -0,0 +1,8 @@ +#ifndef SERIAL_TIC +#define SERIAL_TIC + +//remap the ports. TODO: replace by a macro +#define DebugPort Serial1 +#define TicPort Serial + +#endif \ No newline at end of file diff --git a/tic.cpp b/tic.cpp new file mode 100644 index 0000000..ba39636 --- /dev/null +++ b/tic.cpp @@ -0,0 +1,448 @@ +#include "lwip/ip.h" +#include "tic.h" +#include "serial.h" +#include + +// #define DEBUG 1 + +struct GroupDetail TicValues[NB_ETIQUETTE] = {}; + +// 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. +String data1 = ""; // Variable pour stocker la trame complète +String data2 = ""; // Variable pour stocker la trame complète + +int nActiveData = 1; +boolean isReceiving = false; // Indicateur pour savoir si on est dans une trame +RegistreStatus regStatus; // definition du registre status +RelaisStatus relaisStatus; // definition du relais status +Action actionJp1[11]; // actions définie pour jour +1 +int nbActions; + +static struct GroupDetail processGroup(String group) +{ + struct GroupDetail gd; + int indexgrp = group.indexOf(HT); + gd.name = group.substring(0, indexgrp); + + group = group.substring(indexgrp + 1); + indexgrp = group.indexOf(HT); + gd.value = group.substring(0, indexgrp); + + group = group.substring(indexgrp + 1); + indexgrp = group.indexOf(HT); + 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.value = key; + } + return gd; +} + +static void processStge(RegistreStatus *rs, String value) +{ + char stge[9] = ""; + // copy in the char array + strncpy(stge, value.c_str(), 8); + stge[8] = '\0'; + unsigned long l = strtoul(stge, NULL, 16); // Convert hex pair to unsigned long + rs->uli = l; +} + +static void processRelais(RelaisStatus *rs, String value) +{ + char stge[4] = ""; + // copy in the char array + strncpy(stge, value.c_str(), 3); + stge[4] = '\0'; + rs->ui = strtoul(stge, NULL, 16); +} + +static void processActionsCalendrier(String value) +{ + nbActions = 0; + String s = value; + while (s.length() > 0) + { + int index = s.indexOf(SP); + if (index == -1) // No space found + { + break; + } + else + { + char data[9] = ""; + data[8] = '\0'; + strncpy(data, s.substring(0, index).c_str(), 8); + if (strncmp(data, NONUTILE, 8) != 0) + { + char stge[5] = ""; + // copy ssss field + memcpy(stge, &data[4], 4); + actionJp1[nbActions].action.ui = strtoul(stge, NULL, 16); + // copt hhmm + memcpy(actionJp1[nbActions].startTime, &data[0], 4); + ++nbActions; + } + s = s.substring(index + 1); + } + } +} + +/** + * Processes a data frame to extract and store relevant information. + * + * This function iterates over the input data string, extracting groups of information + * delimited by carriage return (CR) characters. Each group is processed to update + * the corresponding values in the TicValues array. + * + * @param data A reference to a String containing the data frame to be processed. + */ +static void processTrame(String &data) +{ + while (data.length() > 0) + { + // Find the position of the next carriage return (CR) character + int index = data.indexOf(CR); + // If no CR is found, exit the loop + if (index == -1) + { + break; + } + else + { + // Extract the group string between the start and the CR character + String group = data.substring(1, index); + // Process the group to extract detailed information + auto gd = processGroup(group); + + // Check if the extracted group name matches any user-selected etiquette + int t = 0; + while ((SelectedEtiquette[t] != gd.name) && (t < NB_ETIQUETTE)) + { + ++t; + } + // If a match is found, update the corresponding TicValues entry + if (t < NB_ETIQUETTE) + { + TicValues[t] = gd; + // Depending on the group name, call the appropriate processing function + if (gd.name == "STGE") + { + processStge(®Status, gd.value); + } + else if (gd.name == "RELAIS") + { + processRelais(&relaisStatus, gd.value); + } + else if (gd.name == "PJOURF+1") + { + processActionsCalendrier(gd.value); + } + } + data = data.substring(index + 1); + } + } +} + +static char *actionJp1AsJson() +{ + const int bufferSize = 1000; + static char jsonBuffer[bufferSize]; // Adjust size as needed + snprintf(jsonBuffer, bufferSize, "\"PJOURF+1\": ["); + + for (int i = 0; i < nbActions; i++) + { + // Format each action + char actionJson[256]; // To store individual action JSON string + String relaisSec = ""; + switch ((unsigned int)actionJp1[i].action.bits.relaisSec) + { + case 0: + relaisSec = "no change"; + break; + case 1: + relaisSec = "tempo"; + break; + case 2: + relaisSec = "open"; + break; + case 3: + relaisSec = "closed"; + break; + default: + relaisSec = "unknown"; + } + snprintf(actionJson, sizeof(actionJson), + " { \"startTime\": \"%c%c%c%c\", " + "\"relaisSec\": \"%s\", " + "\"relais7\": %u, \"relais6\": %u, \"relais5\": %u, \"relais4\": %u, " + "\"relais3\": %u, \"relais2\": %u, \"relais1\": %u, \"index\": %u }", + actionJp1[i].startTime[0], actionJp1[i].startTime[1], actionJp1[i].startTime[2], actionJp1[i].startTime[3], + relaisSec.c_str(), + actionJp1[i].action.bits.relais7, actionJp1[i].action.bits.relais6, actionJp1[i].action.bits.relais5, + actionJp1[i].action.bits.relais4, actionJp1[i].action.bits.relais3, actionJp1[i].action.bits.relais2, + actionJp1[i].action.bits.relais1, actionJp1[i].action.bits.index); + + // Append the current action's JSON to the overall JSON buffer + if (i == (nbActions - 1)) + { // Last item, no comma at the end + strncat(jsonBuffer, actionJson, bufferSize - strlen(jsonBuffer) - 1); + } + else + { + strncat(jsonBuffer, actionJson, bufferSize - strlen(jsonBuffer) - 1); + strncat(jsonBuffer, ",", bufferSize - strlen(jsonBuffer) - 1); + } + } + + // End the JSON array + strncat(jsonBuffer, "]", bufferSize - strlen(jsonBuffer) - 1); + return jsonBuffer; +} + +static char *relaisStatusAsJson(RelaisStatusBits *status, String rawValue) +{ + // Pre-allocate buffer large enough to hold the JSON string + static char response[150]; // Adjust size as needed + // Use snprintf to construct the JSON string efficiently + snprintf(response, sizeof(response), + "\"RELAIS\": " + "{" + "\"value\": \"%s\", " + "\"relaisSec\": %d, " + "\"relais1\": %d, " + "\"relais2\": %d, " + "\"relais3\": %d, " + "\"relais4\": %d, " + "\"relais5\": %d, " + "\"relais6\": %d, " + "\"relais7\": %d " + "}", + rawValue.c_str(), + status->relaisSec, + status->relais1, + status->relais2, + status->relais3, + status->relais4, + status->relais5, + status->relais6, + status->relais7); + return response; +} + +static char *registreStatusAsJson(RegistreStatusBits *status, String rawValue) +{ + // Pre-allocate buffer large enough to hold the JSON string + static char response[1000]; // Adjust size as needed + + // Use snprintf to construct the JSON string efficiently + snprintf(response, sizeof(response), + "\"STGE\": " + "{" + "\"value\": \"%s\", " + "\"contactsec\": \"%s\", " + "\"organeCoupure\": \"%s\", " + "\"cache\": \"%s\", " + "\"surtension\": \"%s\", " + "\"depassementPuissance\": \"%s\", " + "\"consoProd\": \"%s\", " + "\"senseActiveEnergy\": \"%s\", " + "\"tarifIndexConso\": %d, " + "\"tarifIndexProd\": %d, " + "\"horlogeState\": \"%s\", " + "\"ticState\": \"%s\", " + "\"comEuridis\": \"%s\", " + "\"cplState\": \"%s\", " + "\"cplSynchro\": \"%s\", " + "\"tempo\": \"%s\", " + "\"tempoNextDay\": \"%s\", " + "\"preavisPM\": \" preavis %s\", " + "\"PM\": \"%s\"" + "}", + rawValue.c_str(), + kContactStatus[status->contactsec].c_str(), + kCoupure[status->organeCoupure].c_str(), + kContactStatus[status->cache].c_str(), + kOverVoltage[status->surtension].c_str(), + kOverPower[status->depassementPuissance].c_str(), + kProducer[status->consoProd].c_str(), + kActivePower[status->senseActiveEnergy].c_str(), + status->tarifIndexConso + 1, + status->tarifIndexProd + 1, + kHour[status->horlogeState].c_str(), + kTicMode[status->ticState].c_str(), + kEuridis[status->comEuridis].c_str(), + kCpl[status->cplState].c_str(), + kCplSynchro[status->cplSynchro].c_str(), + kTempoColor[status->tempo].c_str(), + kTempoColor[status->tempoNextDay].c_str(), + kPointeMobile[status->preavisPM].c_str(), + kPointeMobile[status->PM].c_str()); + return response; +} + +String ticValuesAsJson() +{ + String response = "{"; + + for (int i = 0; i < NB_ETIQUETTE; ++i) + { + + if (SelectedEtiquette[i] == "STGE") + { + response += registreStatusAsJson(®Status.bits, TicValues[i].value); + } + else if (SelectedEtiquette[i] == "RELAIS") + { + response += relaisStatusAsJson(&relaisStatus.bits, TicValues[i].value); + } + else if (SelectedEtiquette[i] == "PJOURF+1") + { + response += actionJp1AsJson(); + } + else + { + 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()); + response += jres; + } + + if (i < (NB_ETIQUETTE - 1)) + { + response += ','; + } + } + response += "}"; + return response; +} + +String ticBasicValuesAsJson() +{ + String response = "{"; + + 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") + { + + static char jres[150]; // Adjust size as needed + + if (response != "{") + { + response += ","; + } + + // Use snprintf to construct the JSON string efficiently + snprintf(jres, sizeof(jres), + "\"%s\": \"%s\"", + SelectedEtiquette[i].c_str(), + TicValues[i].value.c_str()); + response += jres; + } + } + response += "}"; + return response; +} + +/** + * Reads data from the TicPort and processes it according to specific control characters. + * + * This function checks for available data on the TicPort and reads it byte by byte. + * It handles different control characters to manage the state of data reception: + * - EOT (End Of Transmission): Forces the end of transmission and rejects any ongoing data. + * - STX (Start Of Text): Indicates the start of a new data frame. + * - ETX (End Of Text): Indicates the end of the current data frame and processes the collected data. + * + * During data reception, the function appends incoming bytes to the active data buffer (data1 or data2) + * and processes the complete frame when ETX is encountered. Debug information is printed if DEBUG is defined. + * + * The built-in LED is used to indicate the state of data reception. + */ +void readTicPort() +{ + // Check TicPort availability + if (TicPort.available()) + { + byte incomingByte = TicPort.read(); // Read a byte from the TicPort + // Check if the incoming byte is the End Of Transmission (EOT) character + if (incomingByte == EOT) + { + // Force the end of transmission + // Reject everything + isReceiving = false; + digitalWrite(LED_BUILTIN, HIGH); // Turn the built-in LED to indicate the end of transmission + } + + // Check if the system is currently receiving data + if (isReceiving) + { + // Check if the end of the frame is reached (ETX character) + if (incomingByte == ETX) + { + // Extract the useful part of the frame + if (nActiveData == 1) + { + processTrame(data1); // Process the data in data1 + } + else + { + processTrame(data2); // Process the data in data2 + } + // Indicate that the data reception is complete + isReceiving = false; + digitalWrite(LED_BUILTIN, HIGH); +// Debugging information: Print the extracted data +#ifdef DEBUG + for (int i = 0; i < NB_ETIQUETTE; ++i) + { + DebugPort.print(TicValues[i].name); + DebugPort.print(":"); + DebugPort.println(TicValues[i].value); + } +#endif + } + else + { + // Add the incoming byte to the current frame + if (nActiveData == 1) + { + data1 += (char)incomingByte; // Append the byte to data1 + } + else + { + data2 += (char)incomingByte; // Append the byte to data2 + } + } + } + else + { + // Look for the start of the frame (STX character) + if (incomingByte == STX) + { + isReceiving = true; + digitalWrite(LED_BUILTIN, LOW); + if (nActiveData == 1) + { + data2 = ""; + nActiveData = 2; + } + else + { + data1 = ""; + nActiveData = 1; + } + } + } + } +} \ No newline at end of file diff --git a/tic.h b/tic.h new file mode 100644 index 0000000..58debb8 --- /dev/null +++ b/tic.h @@ -0,0 +1,136 @@ + + +#ifndef TIC_DEF +#define TIC_DEF + +#include + +#define TIC + +#define STX 0x02 // Début de la trame : 0x02 () +#define ETX 0x03 // Fin de la trame : 0x03 () End Of Text +#define EOT 0x04 //End Of Transmission alias fin de transmission +#define LF 0x0A // Code ASCII pour , debut de groupe d'information +#define CR 0x0D // Fin de groupe d'information +#define HT 0x09 //separateur dans groupe +#define SP 0x20 //separateur dans groupe +#define NONUTILE "NONUTILE" + +#define MAX_CHAR_ETIQUETTE 9 +#define NB_ETIQUETTE 45 + +struct GroupDetail { + String name; + String value; + String horodate; +}; + +struct RegistreStatusBits { + uint32_t contactsec : 1; //bit 0 + uint32_t organeCoupure : 3; //bit 1 - 3 + uint32_t cache : 1; //bit 4 + uint32_t : 1; //bit 5 + uint32_t surtension : 1; //bit 6 + uint32_t depassementPuissance : 1; //bit 7----- + uint32_t consoProd : 1; //bit 8 + uint32_t senseActiveEnergy : 1; //bit 9 + uint32_t tarifIndexConso : 4; //bit 10 -13 + uint32_t tarifIndexProd : 2; //bit 14 - 15 ----- + uint32_t horlogeState : 1; //bit 16 + uint32_t ticState : 1; //17 + uint32_t : 1; //18 + uint32_t comEuridis : 2; //19-20 + uint32_t cplState : 2; //21-22 + uint32_t cplSynchro : 1; //23 + uint32_t tempo : 2; //24-25 + uint32_t tempoNextDay : 2; //26-27 + uint32_t preavisPM : 2; //28-29 + uint32_t PM : 2; //30-31 +}; + +struct RelaisStatusBits { + uint8_t relaisSec : 1; //bit 1 + uint8_t relais1 : 1; //bit 2 + uint8_t relais2 : 1; //bit 3 + uint8_t relais3 : 1; //bit 4 + uint8_t relais4 : 1; //bit 5 + uint8_t relais5 : 1; //bit 6----- + uint8_t relais6 : 1; //bit 7----- + uint8_t relais7 : 1; //bit 8----- +}; + +struct ActionsCalendrierBits { + uint8_t index : 4; //bit 3 - 0----- + uint8_t relais1 : 1; //bit 4----- + uint8_t relais2 : 1; //bit 8----- + uint8_t relais3 : 1; //bit 8----- + uint8_t relais4 : 1; //bit 8----- + uint8_t relais5 : 1; //bit 7----- + uint8_t relais6 : 1; //bit 6----- + uint8_t relais7 : 1; //bit 10 + uint8_t : 3; //bit 11-13 + uint16_t relaisSec : 2; //Contact Sec bit 15-14 +}; +union ActionCalendrier { + uint16_t ui; + ActionsCalendrierBits bits; +}; + +struct Action { + char startTime[4]; + ActionCalendrier action; +}; + +union RelaisStatus { + uint8_t ui; + RelaisStatusBits bits; +}; + +union RegistreStatus { + uint32_t uli; + RegistreStatusBits bits; +}; + + + +const static String SelectedEtiquette[NB_ETIQUETTE] = { "ADSC", "DATE", + "NGTF", "LTARF", + "EAST", "EASF01", "EASF02", "EASF03", "EASF04", + "EASD01", "EASD02", "EASD03", "EASD04", + "EAIT", "ERQ1", "ERQ2", "ERQ3","ERQ4", + "IRMS1", "IRMS2", "IRMS3", + "URMS1", "URMS2", "URMS3", + "PCOUP", + "SINSTS", "SINSTS1", "SINSTS2", "SINSTS3", + "SINSTI", + "STGE", "DPM1", "FPM1", "DPM2", "FPM2", "DPM3", "FPM3", + "RELAIS", "NTARF", "NJOURF", "NJOURF+1", "PJOURF+1", + "MSG1", "MSG2", + "PPOINTE" }; +extern struct GroupDetail TicValues[NB_ETIQUETTE]; //records the values of the identified group + +//Constantes pour definition des statuts +const static String kContactStatus[2] = { "closed", "open" }; //relais et contacteurs +const static String kCoupure[7] = { "closed", + "open_overpower", + "open_overvoltage", + "open_shedding", + "open_euridis", + "open_overheat-with-overcurrent", + "open_overheat-without-overcurrent" }; +const static String kOverVoltage[2] = { "no_overvoltage", "overvoltage" }; +const static String kOverPower[2] = { "no_overpower", "overpower" }; +const static String kProducer[2] = { "consummer", "producer" }; +const static String kActivePower[2] = { "positive", "negative" }; +const static String kHour[2] = { "ok", "degraded" }; +const static String kTicMode[2] = { "historique", "standard" }; +const static String kEuridis[4] = { "deactivated", "activated_without_security", "!!!", "activated_with_security" }; +const static String kCpl[4] = { "new_unlock", "new_lock", "!!!", "registered" }; +const static String kCplSynchro[2] = { "not_synchro", "synchro" }; +const static String kTempoColor[4] = { "no", "blue", "white", "red" }; +const static String kPointeMobile[4] = { "no", "PM1", "PM2", "PM3" }; + +void readTicPort(); +String ticValuesAsJson(); +String ticBasicValuesAsJson(); +#endif \ No newline at end of file