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.

    capture.png

    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:
    schema.jpg
    circuit.jpg

    In terms of code, I'm using Hallard's LibTeleinfo library.
    It's a very well made library, available here: https://github.com/hallard/LibTeleinfo

    Here 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


  • Mod

    @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:

    capture.png
    (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


Log in to reply
 

Suggested Topics

0
Online

11.4k
Users

11.1k
Topics

112.7k
Posts