Unable to read data from my (Linky) electricity meter
-
Hello everyone!
I am working on a new project, which goal is to read data sent by my "Linky" electricity meter here in France. This meter has a 3 pole terminal block on which I can connect a circuit to grab data about my power consumption.
Here is a quick overview of how this works on the Linky meter: https://lucidar.me/en/home-automation/linky-customer-tele-information/
A bit of context
To do that, I'm using (as usual) a barebone ATMEGA328P running at 8MHz with its internal RC oscillator, an RFM69w radio and a few other components.The DIY circuit to get UART data out of the Linky meter can be found on various websites: an optocoupler makes the 50kHz modulated signal a digital signal and a mosfet ensures it outputs values between 0V and Vcc.
This corresponds to the "Linky decoder" part of the schematics posted below.I tested that on a breadboard with an FTDI, so far so good, I can get data on my computer.
On this screenshot, I'm running picocom like this:
picocom -b 1200 -d 7 -p e -f n /dev/ttyUSB0
Indeed, the Linky UART format is 7 bits data, even parity, one stop bit. The data is sent at 1200 bauds.
Where I am now
I'm currently integrating that circuit with my MySensors setup.
I've made the same circuit on a breadboard, added components to directly power the board via the "I1 and A" terminals.Since only 130mW of current can be extracted between the I1 and A terminals, I'm using an LM234 to charge a 0.47F tank supercapacitor at a constant current of 5mA.
Since the circuit (ATMEGA328P + "linky decoder") consume less than 5mA, it works pretty well. The radio is disabled between each loop so that the circuit only reads téléinfo frames (if not, the radio would consume 16mA in "listen mode").Here are the schematics and a photograph of the board:
In terms of code, I'm using Hallard's LibTeleinfo library.
It's a very well made library, available here: https://github.com/hallard/LibTeleinfoHere is the code running on the microcontroller:
// MySensors configuration #define MY_RADIO_RFM69 #define MY_RFM69_RST_PIN 8 #define MY_RFM69_NEW_DRIVER #define MY_RFM69_ENABLE_ENCRYPTION #define MY_DISABLED_SERIAL // Global settings #define REPORT_TIMEOUT 180000 #define PTEC_CHILD_ID 0 #define PAPP_CHILD_ID 1 #define HCHC_CHILD_ID 2 #define HCHP_CHILD_ID 3 // Include libraries #include <MySensors.h> #include <LibTeleinfo.h> // Global variables char ptec_value[3]; unsigned long last_report = 0; long last_metrics[3] = {0, 0, 0}; // papp_max, hchc, hchp long cur_metrics[3] = {0, 0, 0}; TInfo tinfo; MyMessage ptec_msg(PTEC_CHILD_ID, V_TEXT); MyMessage metrics_msg[3] = { MyMessage(PAPP_CHILD_ID, V_VA), MyMessage(HCHC_CHILD_ID, V_KWH), MyMessage(HCHP_CHILD_ID, V_KWH) }; // Callback when a frame is received void data_callback(ValueList *me, uint8_t flags) { // Only take into account new / updated frames if (!(flags & (TINFO_FLAGS_ADDED | TINFO_FLAGS_UPDATED))) { return; } // Max apparent power if (strcmp(me->name, "PAPP") == 0) { cur_metrics[0] = max(cur_metrics[0], atol(me->value)); } // "Heure creuse" counter else if (strcmp(me->name, "HCHC") == 0) { cur_metrics[1] = atol(me->value); } // "Heure pleine" counter else if (strcmp(me->name, "HCHP") == 0) { cur_metrics[2] = atol(me->value); } // Heure pleine / heure creuse else if (strcmp(me->name, "PTEC") == 0) { // Extract the two first characters ("HP" / "HC") strncpy(me->value, ptec_value, 2); ptec_value[2] = "\0"; // Send the value immediately transportReInitialise(); send(ptec_msg.set(ptec_value)); transportDisable(); } } void presentation() { // Present the sensors sendSketchInfo("Power meter", "1.0"); present(PTEC_CHILD_ID, S_INFO, "ptec"); present(PAPP_CHILD_ID, S_POWER, "papp_max"); present(HCHC_CHILD_ID, S_POWER, "hchc"); present(HCHP_CHILD_ID, S_POWER, "hchp"); } void setup() { // Setup serial Serial.begin(1200); // Serial.begin(1200, SERIAL_7E1); // Initialise the teleinfo object tinfo.init(TINFO_MODE_HISTORIQUE); tinfo.attachData(data_callback); } void loop() { // Disable the radio transportDisable(); // Read Linky frames for REPORT_TIMEOUT ms while ((unsigned long)(millis() - last_report) < REPORT_TIMEOUT) { // Read Teleinfo data and process it if (Serial.available()) { tinfo.process(Serial.read()); } } // Re-initialise the radio to send metrics transportReInitialise(); // Potentially send the value of metrics that changed for (byte i = 0 ; i <= 2 ; i++) { // If the value indeed changed, send a message if (cur_metrics[i] != last_metrics[i]) { send(metrics_msg[i].set(cur_metrics[i])); last_metrics[i] = cur_metrics[i]; } } // Reset the PAPP max value cur_metrics[0] = 0; // Set the new counter value last_report = millis(); }
My problem
So, for an unknown reason (yet), I don't get any data on HomeAssistant.
The node presents itself but that basically doesn't go further.Nov 20 17:53:48 DEBUG TSF:MSG:FPAR REQ,ID=7 Nov 20 17:53:48 DEBUG TSF:CKU:OK,FCTRL Nov 20 17:53:48 DEBUG TSF:MSG:GWL OK Nov 20 17:53:49 DEBUG TSF:MSG:SEND,0-0-7-7,s=255,c=3,t=8,pt=1,l=1,sg=0,ft=0,st=OK:0 Nov 20 17:53:50 DEBUG TSF:MSG:READ,7-7-0,s=255,c=3,t=24,pt=1,l=1,sg=0:1 Nov 20 17:53:50 DEBUG TSF:MSG:PINGED,ID=7,HP=1 Nov 20 17:53:50 DEBUG TSF:MSG:SEND,0-0-7-7,s=255,c=3,t=25,pt=1,l=1,sg=0,ft=0,st=OK:1 Nov 20 17:53:50 DEBUG TSF:MSG:READ,7-7-0,s=255,c=3,t=15,pt=6,l=2,sg=0:0100 Nov 20 17:53:50 DEBUG TSF:MSG:SEND,0-0-7-7,s=255,c=3,t=15,pt=6,l=2,sg=0,ft=0,st=OK:0100 Nov 20 17:53:50 DEBUG TSF:MSG:READ,7-7-0,s=255,c=0,t=17,pt=0,l=5,sg=0:2.3.1 Nov 20 17:53:50 DEBUG TSF:MSG:READ,7-7-0,s=255,c=3,t=6,pt=1,l=1,sg=0:0 Nov 20 17:53:51 DEBUG TSF:MSG:SEND,0-0-7-7,s=255,c=3,t=6,pt=0,l=1,sg=0,ft=0,st=OK:M Nov 20 17:53:51 DEBUG TSF:MSG:READ,7-7-0,s=255,c=3,t=11,pt=0,l=11,sg=0:Power meter Nov 20 17:53:51 DEBUG TSF:MSG:READ,7-7-0,s=255,c=3,t=12,pt=0,l=3,sg=0:1.0 Nov 20 17:53:51 DEBUG TSF:MSG:READ,7-7-0,s=0,c=0,t=36,pt=0,l=4,sg=0:ptec Nov 20 17:53:51 DEBUG TSF:MSG:READ,7-7-0,s=1,c=0,t=13,pt=0,l=8,sg=0:papp_max Nov 20 17:53:51 DEBUG TSF:MSG:READ,7-7-0,s=2,c=0,t=13,pt=0,l=4,sg=0:hchc Nov 20 17:53:51 DEBUG TSF:MSG:READ,7-7-0,s=3,c=0,t=13,pt=0,l=4,sg=0:hchp Nov 20 17:53:51 DEBUG TSF:MSG:READ,7-7-0,s=255,c=3,t=26,pt=1,l=1,sg=0:2 Nov 20 17:53:51 DEBUG TSF:MSG:SEND,0-0-7-7,s=255,c=3,t=27,pt=1,l=1,sg=0,ft=0,st=OK:1
As you could see earlier, I've managed to successfully read data out of the Linky meter via an FTDI. So I guess that somehow the ATMEGA328P should be able to read this as well...
According to what I've understood, I have to use "SERIAL_7E1" to read the Linky frames via the UART hardware bus. I tried both with and without it, but without success yet.
My question
Do you eventually know what could be wrong here?
Is there eventually a bug in my code (another pair of eyes won't hurt for sure)?I hope I explained everything. If I forgot a detail, please ask me for details!
Thanks in advance for your help!
Encrypt
-
@Encrypt the sketch looks good to me, with the exception that there is no easy way to debug what is happening.
I would use SoftwareSerial (or another library that does the same thing) for the connection to the Linky, so the hardware serial can be used for writing debug messages. Also, I would power the Arduino from a reliable power source when debugging, to make sure power supply isn't causing problems.
An alternative can be to use SoftwareSerial to print your own debug messages. Maybe start with printing whatever is received on the Linky serial, to make sure you're receiving correctly.
-
Hello everyone!
Thank you @mfalkvidd for your answer.
I finally found the issue...Timing is particularly critical when reading UART and using the internal RC oscillator isn't a good idea... at all. Especially knowing that its frequency varies (quite a lot in my opinion) with temperature:
(Page 274 of the ATMEGA328P datasheet available here: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf)So, I changed the fuses of the ATMEGA328P to use an external 8 MHz crystal oscillator and now everything works perfectly!
Regarding the code, I've initialized the "Serial" object with the configuration SERIAL_7E1, it works as expected!
Cheers!
Encrypt