Initial commit
This commit is contained in:
parent
5b8ff279d2
commit
9adcf6506e
37
README.md
37
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
121
linky_tic.ino
Normal file
121
linky_tic.ino
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include "serial.h"
|
||||||
|
#include "secret.h"
|
||||||
|
#include "tic.h"
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <WiFiClient.h>
|
||||||
|
#include <ESP8266WebServer.h>
|
||||||
|
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
18
secret-generic.h
Normal file
18
secret-generic.h
Normal file
@ -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;
|
8
serial.h
Normal file
8
serial.h
Normal file
@ -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
|
448
tic.cpp
Normal file
448
tic.cpp
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
#include "lwip/ip.h"
|
||||||
|
#include "tic.h"
|
||||||
|
#include "serial.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// #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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
tic.h
Normal file
136
tic.h
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#ifndef TIC_DEF
|
||||||
|
#define TIC_DEF
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define TIC
|
||||||
|
|
||||||
|
#define STX 0x02 // Début de la trame : 0x02 (<STX>)
|
||||||
|
#define ETX 0x03 // Fin de la trame : 0x03 (<ETX>) End Of Text
|
||||||
|
#define EOT 0x04 //End Of Transmission alias fin de transmission
|
||||||
|
#define LF 0x0A // Code ASCII pour <LF>, 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
|
Loading…
Reference in New Issue
Block a user