ELWA DC - Read UART protocol by IR Hichi with ESPhome => How to get into seperate variables?

Hi there,

I need help with following:

Got an ELWA DC heater and successfully installed an IR head on an ESP32 running ESPhome. With the UART reading example template I got things running and see the raw string data getting into HomeAssistant.
Since then I tried to get along with the ESPhome YAML configuration to get the the most important values (voltage, current, etc.) out of the bunch of \t delimitted readings from string but I am just to dumb I guess and my programming skills are about 20 years old.

I would be happy if someone could help, cause I guess this is just an easy finger-play for someone who knows how to deal with lambda, sequence, etc. which for me is cracy… If this could work and we get single variables out of the string to be used in HomeAssistant I would be happy to provide this due to the fact that for ELWA DC I could not find an IR reading solution with ESPhome so far.

This is the current ESPhome YAML where I can see all relevant values each 10sec within the rawString:

esphome:
  name: esphome-web-4d3a10
  friendly_name: ElwaDC PV Zaehler

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable Home Assistant API
api:
  encryption:
    key: "NOTSHOWN"

ota:


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-4D3A10"
    password: "OgwoufPoY2vv"

captive_portal:

# ---------------------------

logger:
  baud_rate: 9600

uart:
  baud_rate: 9600
  tx_pin: 17
  rx_pin: 16
  debug:
    direction: RX
    dummy_receiver: true
    after:
      delimiter: "\r\n"
      #delimiter: "\t"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);  //Still log the data
          
          int sensorID=0;
          float sensorTEMP=0; 
          
          //Example to convert uart text to string
          std::string str(bytes.begin(), bytes.end());

          id(rawString).publish_state(str.c_str());
          //watch for potential problems with non printable or special characters in string
          
          //Sample uart text protocol that sends id:temperature\r\n packets
          if (sscanf(str.c_str(), "%d:%f", &sensorID, &sensorTEMP) == 0 ) {
            if(sensorID==0) 
              id(temp1).publish_state(sensorTEMP); 
          }


text_sensor:
  - platform: template
    name: "Raw String"
    id: "rawString"

sensor:
  - platform: template
    name: "Temp 1"
    id: "temp1"


#if you need to send a request periodically to get response from the uart device 
interval:
  - interval: 10s
    then:
      #//"ping" request for getting just an OK
      #- uart.write:  [0x72, 0x72, 0x71, 0x0D, 0x0A]
      
      - uart.write:  [0x72, 0x73, 0x0D, 0x0A]

I get this UART debug:

[13:00:22][D][sensor:094]: 'Temp 1': Sending state 0.00000  with 1 decimals of accuracy
[13:00:22][W][component:214]: Component uart took a long time for an operation (0.15 s).
[13:00:22][W][component:215]: Components should block for at most 20-30ms.
[13:00:22][D][uart_debug:158]: <<< "dr\tV1.31\t127\t12\t1\t1\t1\t420\t300\t425\t650\t500\t30\t8"
[13:00:22][D][text_sensor:064]: 'Raw String': Sending state 'dr	V1.31	127	12	1	1	1	420	300	425	650	500	30	8'

And the rawString in HomeAssistant looks like this:
image
(I guess it is too long?)

When sun is down the ESP just delivers an ‘rs’ in rawString.
Following mapping would be there if sun is up:

msg.payload;\nvar parts = str.split(/\\x09/);\nmsg.payload = {\n  name: parts[0],\n  
Firmware: (parts[1]),\n  
Betriebstag: parseInt(parts[2]),\n  
Status_MPP_Rack: parseInt(parts[3]),\n  
DC_Trenner: parseInt(parts[4]),\n  
DC_Relais: parseInt(parts[5]),\n  
AC_Relais: parseInt(parts[6]),\n  
Temperatur: parseInt(parts[7]),\n 
Temp_min: parseInt(parts[8]),\n  
Temp_max: parseFloat(parts[9]),\n  
Temp_Soll: parseInt(parts[10]),\n  
Temp_Soll_Netz: parseInt(parts[11]),\n  
Temp_Geraet: parseInt(parts[12]),\n  
ISO_Messung: parseInt(parts[13]),\n  
item14: parseInt(parts[14]),\n  
Spannung: parseInt(parts[15]),\n  
Strom: parseFloat(parts[16]),\n  
Leistung: parseFloat(parts[17]),\n  
Solarenergie_heute: parseInt(parts[18]),\n  
Solarenergie_gesamt: parseInt(parts[19]),\n  
Netzenergie_heute: parseInt(parts[20]),\n  
item21: (parts[21]),\n  
item22: (parts[22]),\n  
item23: (parts[23]),\n  
item24: (parts[24]),\n  
Laufzeit_heute_Minuten: parseInt(parts[25]),\n  
Netzladung_Beginn: (parts[26]),\n  
Position_unten: (parts[27]),\n  
Serien_Nr: parseInt(parts[28]),\n  
item29: (parts[29]),\n  
item30: (parts[30]),\n  
status: parts[31],\n}\nreturn msg;",

Most important values for sure will be [15], [16] and [17] (voltage, current, power).

Would be very happy if you could push me into right direction or help with the right code to seperate the raw string into the right real variables to be used in HA.

Thanks so far!

1 Like

Hello.

I’ve fumbled through some similar things in the past but rely heavily on ChatGPT for writing my lambda’s.

This has been tested and works, however someone who actually knows c++ could possibly point out the approach sucks etc. You would merge my lambda into your existing one.

Perhaps it keeps you moving…

sensor:
  - platform: template
    id: Token_7
    name: "Token_7"
    update_interval: never

text_sensor:
  - platform: template
    id: Token_1
    name: "Token_1"

  - platform: template
    id: test_parse
    name: "test parse"
    lambda: |-
      return {"dr\tV1.31\t127\t12\t1\t1\t1\t420\t300\t425\t650\t500\t30\t"};
    update_interval: 15s
    on_value:
      then:
      - lambda: |-
          auto data = x.c_str();
          std::vector<std::string> tokens;
          size_t start_index = 0;

          // Extract tokens
          while (*data) {
            size_t end_index = start_index;
            while (*data && *data != '\t') {
              data++;
              end_index++;
            }
            tokens.push_back(x.substr(start_index, end_index - start_index));
            if (*data == '\t') data++;
            start_index = end_index + 1;
          }

          // Log tokens
          for (size_t i = 0; i < tokens.size(); i++) {
            ESP_LOGD("main", "Token %d: %s", i, tokens[i].c_str());

            // Set the state of the corresponding sensor
            if (i == 1) id(Token_1).publish_state(tokens[i].c_str());
            else if (i == 7) id(Token_7).publish_state(atof(tokens[i].c_str()));
          }

In logs:

[20:48:57][D][text_sensor:064]: 'test parse': Sending state 'dr	V1.31	127	12	1	1	1	420	300	425	650	500	30	'
[20:48:57][D][main:063]: Token 0: dr
[20:48:57][D][main:063]: Token 1: V1.31
[20:48:57][D][text_sensor:064]: 'Token_1': Sending state 'V1.31'
[20:48:57][D][main:063]: Token 2: 127
[20:48:57][D][main:063]: Token 3: 12
[20:48:57][D][main:063]: Token 4: 1
[20:48:57][D][main:063]: Token 5: 1
[20:48:57][D][main:063]: Token 6: 1
[20:48:57][D][main:063]: Token 7: 420
[20:48:57][D][sensor:094]: 'Token_7': Sending state 420.00000  with 1 decimals of accuracy
[20:48:57][D][main:063]: Token 8: 300
[20:48:57][D][main:063]: Token 9: 425
[20:48:57][D][main:063]: Token 10: 650
[20:48:57][D][main:063]: Token 11: 500
[20:48:57][D][main:063]: Token 12: 30

image

With more comments if you want to understand more what is going on:

    on_value:
      then:
      - lambda: |-
          // Convert the string to a C-style string
          auto data = x.c_str();

          // Vector to store individual tokens extracted from the string
          std::vector<std::string> tokens;

          // Index to keep track of the starting position of each token
          size_t start_index = 0;

          // Extract tokens from the string
          while (*data) {
            // Find the end of the current token
            size_t end_index = start_index;
            while (*data && *data != '\t') {
              data++;
              end_index++;
            }

            // Add the current token to the vector
            tokens.push_back(x.substr(start_index, end_index - start_index));

            // Move to the next character (skip the tab)
            if (*data == '\t') data++;

            // Update the start index for the next token
            start_index = end_index + 1;
          }

          // Log tokens for debugging
          for (size_t i = 0; i < tokens.size(); i++) {
            ESP_LOGD("main", "Token %d: %s", i, tokens[i].c_str());

            // Set the state of the corresponding sensor based on the token index
            if (i == 1) {
              // For Token_1 (a text sensor), publish the token as a string
              id(Token_1).publish_state(tokens[i].c_str());
            } else if (i == 7) {
              // For Token_7 (a float sensor), convert the token to a float and publish
              id(Token_7).publish_state(atof(tokens[i].c_str()));
            }

            // Add similar conditions for other sensors as needed
          }

I know @mulcmu is very good at this kind of thing and often willing to help, so you may be lucky enough to get some help from them;)

1 Like

@Mahko_Mahko
Wow, even if I really don’t get a thing from “your Lamda’s” code, somehow it looks promising and I am keen to give it a try. Hopefully I will find some time within the next days an will let you know.

Question: It looks like you did simulate the code? What’s the easiest way for that?

Thank you so much, so far :slight_smile:
Really appreciated!

If you paste my config straight into yours you should see it working in the logs. I made your uart message a text sensor for testing.

Then once you see that working, if you place my lambda after this part and make some adjustments to align use of “data”, “str” and “x”, I think you should be good to go. But that can be confusing if you haven’t done it before.

Myself I would be asking ChatGpt to adjust it for me and it would likely do a decent job. It’s pretty good with c++ since it’s an old language. However dicussing and use of ChatGPT is at minimum frowned on on this forum, so please keep this in mind and consider your options.

1 Like

@Mahko_Mahko

After failing completely I tried step by step, like you supposed to. First easy things I get (I think) but I still have problems to understand where and how the UART string data needs to “come in” here… I will have to finish for today now and I cannot test further because sun is down and with that the ELWA DC is totally off.

This is my current implementation with prepared entities but I am very sure things will not work like that because I dont understand where and how to place the right lines of code for the serial data…

The

std::string str(bytes.begin(), bytes.end());

from first example and your

auto data = x.c_str();*
std::vector<std::string> tokens;

dont fit together in my crumbled brain right now… will have some next tryouts within the next days.

If you are willing to help an “old man” stepwise and point once more into the right direction, I would appreciate. ChatGPT was a really good hint and I also used this to help with additional information and hints but now I am currently stuck…

esphome:
  name: esphome-web-4d3a10
  friendly_name: ElwaDC PV Zaehler

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable Home Assistant API
api:
  encryption:
    key: "KEY"

ota:


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "SSID"
    password: "PASS"

captive_portal:

# ---------------------------

logger:
  baud_rate: 9600

uart:
  baud_rate: 9600
  tx_pin: 17
  rx_pin: 16
###if no debug is necessary anymore uncomment from here...
  debug:
     direction: RX
     dummy_receiver: true
     after:
       delimiter: "\r\n"
       #delimiter: "\t"
     sequence:
       - lambda: |-
          UARTDebug::log_string(direction, bytes);  //Still log the data
          std::string str(bytes.begin(), bytes.end());
###...to there

       

sensor:

  - platform: template
    id: Token_2
    name: "Betriebstag"
    update_interval: never

  - platform: template
    id: Token_3
    name: "Status MPP"
    update_interval: never
  
  - platform: template
    id: Token_4
    name: "DC Trenner"
    update_interval: never

  - platform: template
    id: Token_5
    name: "DC Relais"
    update_interval: never

  - platform: template
    id: Token_6
    name: "AC Relais"
    update_interval: never
  
  - platform: template
    id: Token_7
    name: "Temperatur"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_8
    name: "Temp Min"
    unit_of_measurement: "°C"
    update_interval: never
  
  - platform: template
    id: Token_9
    name: "Temp Max"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_10
    name: "Temp Soll"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_11
    name: "Temp Soll Netz"
    unit_of_measurement: "°C"
    update_interval: never
  
  - platform: template
    id: Token_12
    name: "Temp Gerät"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_13
    name: "ISO Messung"
    update_interval: never

  - platform: template
    id: Token_14
    name: "Token_14"
    update_interval: never

  - platform: template
    id: Token_15
    name: "Spannung"
    unit_of_measurement: "V"
    update_interval: never
  
  - platform: template
    id: Token_16
    name: "Strom"
    unit_of_measurement: "A"
    update_interval: never

  - platform: template
    id: Token_17
    name: "Leistung"
    unit_of_measurement: "W"
    update_interval: never

  - platform: template
    id: Token_18
    name: "Energie Heute"
    unit_of_measurement: "Wh"
    update_interval: never

  - platform: template
    id: Token_19
    name: "Enerie Gesamt"
    unit_of_measurement: "Wh"
    update_interval: never
  
  - platform: template
    id: Token_20
    name: "Netzenergie Heute"
    unit_of_measurement: "Wh"
    update_interval: never

  - platform: template
    id: Token_21
    name: "Token_21"
    update_interval: never

  - platform: template
    id: Token_22
    name: "Token_22"
    update_interval: never

  - platform: template
    id: Token_23
    name: "Token_23"
    update_interval: never

  - platform: template
    id: Token_24
    name: "Token_24"
    update_interval: never

  - platform: template
    id: Token_25
    name: "Laufzeit Heute"
    unit_of_measurement: "min"
    update_interval: never

  - platform: template
    id: Token_26
    name: "Netzladung Beginn"
    update_interval: never
  
  - platform: template
    id: Token_27
    name: "Position Unten"
    update_interval: never

  - platform: template
    id: Token_28
    name: "Seriennummer"
    update_interval: never

  - platform: template
    id: Token_29
    name: "Token_29"
    update_interval: never
  
  - platform: template
    id: Token_30
    name: "Token_30"
    update_interval: never

  - platform: template
    id: Token_31
    name: "Status"
    update_interval: never


text_sensor:

  - platform: template
    id: Token_0
    name: "Reply"

  - platform: template
    id: Token_1
    name: "Firmware"

  - platform: template
    id: test_parse
    name: "test parse"
    #lambda: |-
    #  return {"dr\tV1.31\t127\t12\t1\t1\t1\t420\t300\t425\t650\t500\t30\t"};
    update_interval: 15s
    on_value:
      then:
      - lambda: |-
          auto data = x.c_str();

          std::vector<std::string> tokens;
          
          size_t start_index = 0;

          // Extract tokens
          while (*data) {
            size_t end_index = start_index;
            while (*data && *data != '\t') {
              data++;
              end_index++;
            }
            tokens.push_back(x.substr(start_index, end_index - start_index));
            if (*data == '\t') data++;
            start_index = end_index + 1;
          }

          // Log tokens
          for (size_t i = 0; i < tokens.size(); i++) {
            ESP_LOGD("main", "Token %d: %s", i, tokens[i].c_str());

            // Set the state of the corresponding sensor
            if (i == 1) id(Token_1).publish_state(tokens[i].c_str());
            else if (i == 7) id(Token_7).publish_state(atof(tokens[i].c_str()));
          }

#if you need to send a request periodically to get response from the uart device 
interval:
  - interval: 20s
    then:
      #request for getting all relevant token values
      - uart.write:  [0x72, 0x73, 0x0D, 0x0A]
      #//"ping" request for getting just an OK
      #- uart.write:  [0x72, 0x72, 0x71, 0x0D, 0x0A]

The x will go and be replaced with str or similar. I should be able to help a over the next few days.

Tried by myself but after exchanging x by str within test_parse sensor I get errors during compiling that I could not resolve, yet.

I am not sure if the std::str are in the right place and if my UART part at the beginning is correct.
Also, do I need the update interval in sensor if I am requesting values each 20sec?

esphome:
  name: esphome-web-4d3a10
  friendly_name: ElwaDC PV Zaehler

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable Home Assistant API
api:
  encryption:
    key: "KEY"

ota:


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-4D3A10"
    password: "PASS"

captive_portal:

# ---------------------------

logger:
  baud_rate: 9600

uart:
  baud_rate: 9600
  tx_pin: 17
  rx_pin: 16
###if no debug is necessary anymore uncomment from here...
  debug:
     direction: RX
     dummy_receiver: true
     after:
       delimiter: "\r\n"
       #delimiter: "\t"
     sequence:
       - lambda: |-
          UARTDebug::log_string(direction, bytes);  //Still log the data
          std::string str(bytes.begin(), bytes.end());
###...to there

       

sensor:

  - platform: template
    id: Token_2
    name: "Betriebstag"
    update_interval: never

  - platform: template
    id: Token_3
    name: "Status MPP"
    update_interval: never
  
  - platform: template
    id: Token_4
    name: "DC Trenner"
    update_interval: never

  - platform: template
    id: Token_5
    name: "DC Relais"
    update_interval: never

  - platform: template
    id: Token_6
    name: "AC Relais"
    update_interval: never
  
  - platform: template
    id: Token_7
    name: "Temperatur"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_8
    name: "Temp Min"
    unit_of_measurement: "°C"
    update_interval: never
  
  - platform: template
    id: Token_9
    name: "Temp Max"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_10
    name: "Temp Soll"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_11
    name: "Temp Soll Netz"
    unit_of_measurement: "°C"
    update_interval: never
  
  - platform: template
    id: Token_12
    name: "Temp Gerät"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_13
    name: "ISO Messung"
    update_interval: never

  - platform: template
    id: Token_14
    name: "Token_14"
    update_interval: never

  - platform: template
    id: Token_15
    name: "Spannung"
    unit_of_measurement: "V"
    update_interval: never
  
  - platform: template
    id: Token_16
    name: "Strom"
    unit_of_measurement: "A"
    update_interval: never

  - platform: template
    id: Token_17
    name: "Leistung"
    unit_of_measurement: "W"
    update_interval: never

  - platform: template
    id: Token_18
    name: "Energie Heute"
    unit_of_measurement: "Wh"
    update_interval: never

  - platform: template
    id: Token_19
    name: "Enerie Gesamt"
    unit_of_measurement: "Wh"
    update_interval: never
  
  - platform: template
    id: Token_20
    name: "Netzenergie Heute"
    unit_of_measurement: "Wh"
    update_interval: never

  - platform: template
    id: Token_21
    name: "Token_21"
    update_interval: never

  - platform: template
    id: Token_22
    name: "Token_22"
    update_interval: never

  - platform: template
    id: Token_23
    name: "Token_23"
    update_interval: never

  - platform: template
    id: Token_24
    name: "Token_24"
    update_interval: never

  - platform: template
    id: Token_25
    name: "Laufzeit Heute"
    unit_of_measurement: "min"
    update_interval: never

  - platform: template
    id: Token_26
    name: "Netzladung Beginn"
    update_interval: never
  
  - platform: template
    id: Token_27
    name: "Position Unten"
    update_interval: never

  - platform: template
    id: Token_28
    name: "Seriennummer"
    update_interval: never

  - platform: template
    id: Token_29
    name: "Token_29"
    update_interval: never
  
  - platform: template
    id: Token_30
    name: "Token_30"
    update_interval: never

  - platform: template
    id: Token_31
    name: "Status"
    update_interval: never


text_sensor:

  - platform: template
    id: Token_0
    name: "Reply"

  - platform: template
    id: Token_1
    name: "Firmware"

  - platform: template
    id: test_parse
    name: "test parse"
    #lambda: |-
    #  return {"dr\tV1.31\t127\t12\t1\t1\t1\t420\t300\t425\t650\t500\t30\t"};
    update_interval: 20s
    on_value:
      then:
      - lambda: |-
          auto data = x.c_str();

          std::vector<std::string> tokens;
          
          size_t start_index = 0;

          // Extract tokens
          while (*data) {
            size_t end_index = start_index;
            while (*data && *data != '\t') {
              data++;
              end_index++;
            }
            tokens.push_back(x.substr(start_index, end_index - start_index));
            if (*data == '\t') data++;
            start_index = end_index + 1;
          }

          // Log tokens
          for (size_t i = 0; i < tokens.size(); i++) {
            ESP_LOGD("main", "Token %d: %s", i, tokens[i].c_str());

            // Set the state of the corresponding sensor
            if (i == 0) id(Token_0).publish_state(tokens[i].c_str());
            else if (i == 1) id(Token_1).publish_state(tokens[i].c_str());
            else if (i == 15) id(Token_15).publish_state(atof(tokens[i].c_str()));
            else if (i == 16) id(Token_16).publish_state(atof(tokens[i].c_str()));
            else if (i == 17) id(Token_17).publish_state(atof(tokens[i].c_str()));
          }

#if you need to send a request periodically to get response from the uart device 
interval:
  - interval: 20s
    then:
      #request for getting all relevant token values
      - uart.write:  [0x72, 0x73, 0x0D, 0x0A]
      #//"ping" request for getting just an OK
      #- uart.write:  [0x72, 0x72, 0x71, 0x0D, 0x0A]

I’m 95% sure this will drop straight in and work (tested on my side).

    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);           // Log UART data
          std::string str(bytes.begin(), bytes.end());       // Convert bytes to string
          std::vector<std::string> tokens;                   // Vector to store extracted tokens
          size_t start_index = 0;                            // Index to keep track of the start position while extracting tokens

          // Extract tokens from the string
          while (start_index < str.size()) {
              size_t end_index = start_index;
              while (end_index < str.size() && str[end_index] != '\t') {
                  end_index++;
              }
              // Push the extracted token into the vector
              tokens.push_back(str.substr(start_index, end_index - start_index));
              if (end_index < str.size() && str[end_index] == '\t') {
                  end_index++; // Move past the tab character
              }
              start_index = end_index;
          }

          // Log extracted tokens.
          for (size_t i = 0; i < tokens.size(); i++) {
              ESP_LOGD("main", "Token %d: %s", i, tokens[i].c_str());

              // Set the state of the corresponding sensor based on token index
              if (i == 1) {
                  // Update state for Token_1
                  id(Token_1).publish_state(tokens[i].c_str());
              } else if (i == 7) {
                  // Update state for Token_7
                  id(Token_7).publish_state(atof(tokens[i].c_str()));
              }
              // Add more conditions for other tokens if needed
          }

And this version should handle your “rs”.

    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);           // Log UART data
          std::string str(bytes.begin(), bytes.end());       // Convert bytes to string
          
          // Check if the content of the string is not equal to "rs"
          if (str != "rs") {
              std::vector<std::string> tokens;               // Vector to store extracted tokens
              size_t start_index = 0;                        // Index to keep track of the start position while extracting tokens

              // Extract tokens from the string
              while (start_index < str.size()) {
                  size_t end_index = start_index;
                  while (end_index < str.size() && str[end_index] != '\t') {
                      end_index++;
                  }
                  // Push the extracted token into the vector
                  tokens.push_back(str.substr(start_index, end_index - start_index));
                  if (end_index < str.size() && str[end_index] == '\t') {
                      end_index++; // Move past the tab character
                  }
                  start_index = end_index;
              }

              // Log extracted tokens.
              for (size_t i = 0; i < tokens.size(); i++) {
                  ESP_LOGD("main", "Token %d: %s", i, tokens[i].c_str());

                  // Set the state of the corresponding sensor based on token index
                  if (i == 1) {
                      // Update state for Token_1
                      id(Token_1).publish_state(tokens[i].c_str());
                  } else if (i == 7) {
                      // Update state for Token_7
                      id(Token_7).publish_state(atof(tokens[i].c_str()));
                  }
                  // Add more conditions for other tokens if needed
              }
          } else {
              // Handle the case where the content of str is "rs"
              // You can add appropriate code or logging here
          }
sensor:
  - platform: template
    id: Token_7
    name: "Token_7"
    update_interval: never

text_sensor:
  - platform: template
    id: Token_1
    name: "Token_1"
    update_interval: never

Notice how in my snippet above I have one token under sensor: and one under text_sensor: ?
You need to put numeric sensors under senor and text ones under text_sensor.

Also notice how the “publish” code is slightly different for the text one and the numeric one?

             if (i == 1) id(Token_1).publish_state(tokens[i].c_str());
             else if (i == 7) id(Token_7).publish_state(atof(tokens[i].c_str()));

No, you are basically manually managing thier updates via your “publish” lambda.
You should only need to change that one - interval: 20s and everything else will happen automagically.

Thank you so much, so far. I will try this within the next days, finalize it and then hopefully post the solution that works for me with (if there is some sun) will show first results… I am very confident looking over your template and things get more and more clear now.

I recognized the different variable types before and I think the only string values should be the reply and firmware… “Betriebstag” should be an integer summing up the days in operation. Will see…

Thanks again! Will give feedback :slight_smile:

1 Like

Sorry, I am still confused and get errors to get the full ESP YAML working without error during compiling or upload.
I think I generally understood your structure but I am not understanding the weird YAML indentation and to be honest, where to put your sequence correctly.

I thought it needs to be part of the test-parse text sensor, right?
Is my UART configuration on top still right then?
Trying to get following ESPhome YAML config running I get errors after compiling and even after several tries I am close to surrender…

Could you please point out where I do the major faults here?

esphome:
  name: esphome-web-4d3a10
  friendly_name: ElwaDC PV Zaehler

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable Home Assistant API
api:
  encryption:
    key: "...KEY..."

ota:


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-4D3A10"
    password: "...PASS..."

captive_portal:

# ---------------------------


logger:
  baud_rate: 9600

uart:
  baud_rate: 9600
  tx_pin: 17
  rx_pin: 16   
  debug:
    direction: RX
    dummy_receiver: true
    after:
      delimiter: "\r\n"

sensor:

  - platform: template
    id: Token_2
    name: "Betriebstag"
    update_interval: never

  - platform: template
    id: Token_3
    name: "Status MPP"
    update_interval: never
  
  - platform: template
    id: Token_4
    name: "DC Trenner"
    update_interval: never

  - platform: template
    id: Token_5
    name: "DC Relais"
    update_interval: never

  - platform: template
    id: Token_6
    name: "AC Relais"
    update_interval: never
  
  - platform: template
    id: Token_7
    name: "Temperatur"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_8
    name: "Temp Min"
    unit_of_measurement: "°C"
    update_interval: never
  
  - platform: template
    id: Token_9
    name: "Temp Max"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_10
    name: "Temp Soll"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_11
    name: "Temp Soll Netz"
    unit_of_measurement: "°C"
    update_interval: never
  
  - platform: template
    id: Token_12
    name: "Temp Gerät"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_13
    name: "ISO Messung"
    update_interval: never

  - platform: template
    id: Token_15
    name: "Spannung"
    unit_of_measurement: "V"
    update_interval: never
  
  - platform: template
    id: Token_16
    name: "Strom"
    unit_of_measurement: "A"
    update_interval: never

  - platform: template
    id: Token_17
    name: "Leistung"
    unit_of_measurement: "W"
    update_interval: never

  - platform: template
    id: Token_18
    name: "Energie Heute"
    unit_of_measurement: "Wh"
    update_interval: never

  - platform: template
    id: Token_19
    name: "Enerie Gesamt"
    unit_of_measurement: "Wh"
    update_interval: never
  
  - platform: template
    id: Token_20
    name: "Netzenergie Heute"
    unit_of_measurement: "Wh"
    update_interval: never

  - platform: template
    id: Token_25
    name: "Laufzeit Heute"
    unit_of_measurement: "min"
    update_interval: never

  - platform: template
    id: Token_26
    name: "Netzladung Beginn"
    update_interval: never
  
  - platform: template
    id: Token_27
    name: "Position Unten"
    update_interval: never

  - platform: template
    id: Token_28
    name: "Seriennummer"
    update_interval: never

  - platform: template
    id: Token_31
    name: "Status"
    update_interval: never


text_sensor:

  - platform: template
    id: Token_0
    name: "Reply"

  - platform: template
    id: Token_1
    name: "Firmware"

  - platform: template
    id: test_parse
    name: "test parse"
    on_value:
      then:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);           // Log UART data
          std::string str(bytes.begin(), bytes.end());       // Convert bytes to string
          
          // Check if the content of the string is not equal to "rs"
          if (str != "rs") {
              std::vector<std::string> tokens;               // Vector to store extracted tokens
              size_t start_index = 0;                        // Index to keep track of the start position while extracting tokens

              // Extract tokens from the string
              while (start_index < str.size()) {
                  size_t end_index = start_index;
                  while (end_index < str.size() && str[end_index] != '\t') {
                      end_index++;
                  }
                  // Push the extracted token into the vector
                  tokens.push_back(str.substr(start_index, end_index - start_index));
                  if (end_index < str.size() && str[end_index] == '\t') {
                      end_index++; // Move past the tab character
                  }
                  start_index = end_index;
              }

              // Log extracted tokens.
              for (size_t i = 0; i < tokens.size(); i++) {
                  ESP_LOGD("main", "Token %d: %s", i, tokens[i].c_str());

                  // Set the state of the corresponding sensor based on token index
                  if (i == 1) {
                      // Update state for Token_1
                      id(Token_1).publish_state(tokens[i].c_str());
                  } else if (i == 15) {
                      // Update state for Token_7
                      id(Token_15).publish_state(atof(tokens[i].c_str()));
                  }
                  // Add more conditions for other tokens if needed
              }
          } else {
              // Handle the case where the content of str is "rs"
              // You can add appropriate code or logging here
              id(Token_1).publish_state("Off");
          }

#if you need to send a request periodically to get response from the uart device 
interval:
  - interval: 20s
    then:
      #request for getting all relevant token values
      - uart.write:  [0x72, 0x73, 0x0D, 0x0A]
      #"ping" request for getting just an OK
      #- uart.write:  [0x72, 0x72, 0x71, 0x0D, 0x0A]

This is the actual error message within HomeAssistants ESPhome upload:

Compiling .pioenvs/esphome-web-4d3a10/src/main.cpp.o
/config/esphome/esphome-web-4d3a10.yaml: In lambda function:
/config/esphome/esphome-web-4d3a10.yaml:192:29: error: 'direction' was not declared in this scope
           UARTDebug::log_string(direction, bytes);           // Log UART data
                             ^~~~~~~~~
/config/esphome/esphome-web-4d3a10.yaml:192:29: note: suggested alternative: 'sigaction'
           UARTDebug::log_string(direction, bytes);           // Log UART data
                             ^~~~~~~~~
                             sigaction
/config/esphome/esphome-web-4d3a10.yaml:192:40: error: 'bytes' was not declared in this scope
           UARTDebug::log_string(direction, bytes);           // Log UART data
                                        ^~~~~
/config/esphome/esphome-web-4d3a10.yaml:192:40: note: suggested alternative: 'byte'
           UARTDebug::log_string(direction, bytes);           // Log UART data
                                        ^~~~~
                                        byte
*** [.pioenvs/esphome-web-4d3a10/src/main.cpp.o] Error 1
========================= [FAILED] Took 18.41 seconds =========================

I think I need to spend already some “coffee” to you… do you have something like this? (or maybe PP?)

No the sequence is now in a similar location to your original code. See below.
I was just using that text sensor as like a “pretend ELWA” to have it "send me some messages to parse. You don’t need that part anymore. We mainly just work with your sensors and the uart section.

Yaml is very fussy about indentation.

Here is a more complete example. It’s tested and working on my side.
Note I changed the “else ifs” to a “case” so it is cleaner.

logger:
  baud_rate: 9600

sensor:
  - platform: template
    id: Token_2
    name: "Betriebstag"
    update_interval: never

  - platform: template
    id: Token_3
    name: "Status MPP"
    update_interval: never
  
  - platform: template
    id: Token_4
    name: "DC Trenner"
    update_interval: never

  - platform: template
    id: Token_5
    name: "DC Relais"
    update_interval: never

  - platform: template
    id: Token_6
    name: "AC Relais"
    update_interval: never
  
  - platform: template
    id: Token_7
    name: "Temperatur"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_8
    name: "Temp Min"
    unit_of_measurement: "°C"
    update_interval: never
  
  - platform: template
    id: Token_9
    name: "Temp Max"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_10
    name: "Temp Soll"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_11
    name: "Temp Soll Netz"
    unit_of_measurement: "°C"
    update_interval: never
  
  - platform: template
    id: Token_12
    name: "Temp Gerät"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_13
    name: "ISO Messung"
    update_interval: never

  - platform: template
    id: Token_15
    name: "Spannung"
    unit_of_measurement: "V"
    update_interval: never
  
  - platform: template
    id: Token_16
    name: "Strom"
    unit_of_measurement: "A"
    update_interval: never

  - platform: template
    id: Token_17
    name: "Leistung"
    unit_of_measurement: "W"
    update_interval: never

  - platform: template
    id: Token_18
    name: "Energie Heute"
    unit_of_measurement: "Wh"
    update_interval: never

  - platform: template
    id: Token_19
    name: "Enerie Gesamt"
    unit_of_measurement: "Wh"
    update_interval: never
  
  - platform: template
    id: Token_20
    name: "Netzenergie Heute"
    unit_of_measurement: "Wh"
    update_interval: never

  - platform: template
    id: Token_25
    name: "Laufzeit Heute"
    unit_of_measurement: "min"
    update_interval: never

  - platform: template
    id: Token_26
    name: "Netzladung Beginn"
    update_interval: never
  
  - platform: template
    id: Token_27
    name: "Position Unten"
    update_interval: never

  - platform: template
    id: Token_28
    name: "Seriennummer"
    update_interval: never

  - platform: template
    id: Token_31
    name: "Status"
    update_interval: never


text_sensor:
  - platform: template
    id: Token_0
    name: "Reply"
  - platform: template
    id: Token_1
    name: "Firmware"

uart:
- id: uart_elwa
  baud_rate: 9600
  tx_pin: 17
  rx_pin: 19
  debug:
    direction: RX
    dummy_receiver: true
    after:
      delimiter: "\r\n"
      #delimiter: "\t"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);           // Log UART data
          std::string str(bytes.begin(), bytes.end());       // Convert bytes to string
          
          // Check if the content of the string is not equal to "rs"
          if (str != "rs") {
              std::vector<std::string> tokens;               // Vector to store extracted tokens
              size_t start_index = 0;                        // Index to keep track of the start position while extracting tokens

              // Extract tokens from the string
              while (start_index < str.size()) {
                  size_t end_index = start_index;
                  while (end_index < str.size() && str[end_index] != '\t') {
                      end_index++;
                  }
                  // Push the extracted token into the vector
                  tokens.push_back(str.substr(start_index, end_index - start_index));
                  if (end_index < str.size() && str[end_index] == '\t') {
                      end_index++; // Move past the tab character
                  }
                  start_index = end_index;
              }

              // Log extracted tokens.
              for (size_t i = 0; i < tokens.size(); i++) {
                  ESP_LOGD("main", "Token %d: %s", i, tokens[i].c_str());

                  // Set the state of the corresponding sensor based on token index
                  switch (i) {
                      case 0: id(Token_0).publish_state(tokens[i].c_str()); break;
                      case 1: id(Token_1).publish_state(tokens[i].c_str()); break;
                      case 2: id(Token_2).publish_state(atof(tokens[i].c_str())); break;
                      case 3: id(Token_3).publish_state(atof(tokens[i].c_str())); break;
                      case 4: id(Token_4).publish_state(atof(tokens[i].c_str())); break;
                      case 5: id(Token_5).publish_state(atof(tokens[i].c_str())); break;
                      case 6: id(Token_6).publish_state(atof(tokens[i].c_str())); break;
                      case 7: id(Token_7).publish_state(atof(tokens[i].c_str())); break;
                      case 8: id(Token_8).publish_state(atof(tokens[i].c_str())); break;
                      case 9: id(Token_9).publish_state(atof(tokens[i].c_str())); break;
                      case 10: id(Token_10).publish_state(atof(tokens[i].c_str())); break;
                      case 11: id(Token_11).publish_state(atof(tokens[i].c_str())); break;
                      case 12: id(Token_12).publish_state(atof(tokens[i].c_str())); break;
                      case 13: id(Token_13).publish_state(atof(tokens[i].c_str())); break;
                      // Add more cases for other tokens if needed
                  }
              }
          } else {
              // Handle the case where the content of str is "rs"
              // You can add appropriate code or logging here
          }
1 Like

Thanks for the offer but not required. I have had many many many hours of help from others and I like to give back a bit where I can.

@Mahko_Mahko

Y O U A R E A W E S O M E ! ! !

:smiley:

Will post new code /full YAML these days… just started to work on value conversion and making things look nice… and it starts to make alot of fun now :slight_smile:

After weeks and month of “turning in circles” its working now (generally).

1 Like

Ok, so the first and maybe a killer point was, that with your last template the UART RX pin was a typo (rx_pin: 19 but correct was 16). Luckily I found this by chance :slight_smile:

But then it started working and step by step I got into it. Now I will give it a try with following implementation and maybe slightly adapt it and work with the values.

Thank you so much, again!

Question: Anyone who knows if this code can be more than a discussion thread here to help other ones with same demands and hardware?
Would be happy to share our snippets.

esphome:
  name: esphome-web-4d3a10
  friendly_name: PV Heizstab

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable Home Assistant API
api:
  encryption:
    key: "KEY"

ota:


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esphome-Web-4D3A10"
    password: "PASSWORD"

captive_portal:


# -------------------------------------------------------------------------
# -------------------CODE-FOR-GETTING-SERIAL-ELWA_DC-DATA------------------
# -------------------------------------------------------------------------

logger:
  baud_rate: 9600
  logs:
    component: ERROR

text_sensor:
  - platform: template
    id: Token_0
    name: "Reply"
  - platform: template
    id: Token_1
    name: "Elwa DC Firmware"

binary_sensor:
  - platform: template
    id: Token_4
    name: "DC Trenner"

  - platform: template
    id: Token_5
    name: "DC Relais"

  - platform: template
    id: Token_6
    name: "AC Relais"

  - platform: template
    id: Token_27
    name: "Position Unten"

sensor:
  - platform: template
    id: Token_2
    name: "Betriebstag"
    unit_of_measurement: "d"
    update_interval: never
    accuracy_decimals: 0

  - platform: template
    id: Token_3
    name: "Status MPP"
    update_interval: never
    accuracy_decimals: 0
  
  - platform: template
    id: Token_7
    name: "Temperatur"
    unit_of_measurement: "°C"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
    update_interval: never
    filters:
      - multiply: 0.1

  - platform: template
    id: Token_8
    name: "Temp Min"
    unit_of_measurement: "°C"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
    update_interval: never
    filters:
      - multiply: 0.1
  
  - platform: template
    id: Token_9
    name: "Temp Max"
    unit_of_measurement: "°C"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
    update_interval: never
    filters:
      - multiply: 0.1

  - platform: template
    id: Token_10
    name: "Temp Soll"
    unit_of_measurement: "°C"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
    update_interval: never
    filters:
      - multiply: 0.1

  - platform: template
    id: Token_11
    name: "Temp Soll Netz"
    unit_of_measurement: "°C"
    device_class: "temperature"
    state_class: "measurement"
    accuracy_decimals: 1
    update_interval: never
    filters:
      - multiply: 0.1
  
  - platform: template
    id: Token_12
    name: "Temp. Gerät"
    unit_of_measurement: "°C"
    update_interval: never

  - platform: template
    id: Token_13
    name: "ISO Messung"
    update_interval: never
    accuracy_decimals: 0
    
  - platform: template
    id: Token_14
    name: "Token_14"
    update_interval: never

  - platform: template
    id: Token_15
    name: "Spannung"
    unit_of_measurement: "V"
    device_class: "voltage"
    state_class: "measurement"
    update_interval: never
    accuracy_decimals: 0
  
  - platform: template
    id: Token_16
    name: "Strom"
    unit_of_measurement: "A"
    device_class: "current"
    state_class: "measurement"
    update_interval: never
    accuracy_decimals: 2

  - platform: template
    id: Token_17
    name: "Leistung Aktuell"
    unit_of_measurement: "W"
    device_class: "power"
    state_class: "measurement"
    update_interval: never

  - platform: template
    id: Token_18
    name: "Energie Heute"
    unit_of_measurement: "kWh"
    device_class: "energy"
    state_class: "total"
    update_interval: never
    accuracy_decimals: 1
    filters:
      - multiply: 0.001

  - platform: template
    id: Token_19
    name: "Energie Gesamt"
    unit_of_measurement: "kWh"
    device_class: "energy"
    state_class:  "total_increasing"
    update_interval: never
    accuracy_decimals: 1
    filters:
      - multiply: 0.001
  
  - platform: template
    id: Token_20
    name: "Netzenergie Heute"
    update_interval: never
    accuracy_decimals: 0

  - platform: template
    id: Token_21
    name: "Token_21"
    update_interval: never

  - platform: template
    id: Token_22
    name: "Token_22"
    update_interval: never

  - platform: template
    id: Token_23
    name: "Token_23"
    update_interval: never

  - platform: template
    id: Token_24
    name: "Token_24"
    update_interval: never

  - platform: template
    id: Token_25
    name: "Laufzeit Heute"
    unit_of_measurement: "min"
    update_interval: never
    accuracy_decimals: 0

  - platform: template
    id: Token_26
    name: "Netzladung Beginn"
    update_interval: never

  - platform: template
    id: Token_28
    name: "Seriennummer"
    update_interval: never
    accuracy_decimals: 0

  - platform: template
    id: Token_29
    name: "Token_29"
    update_interval: never

  - platform: template
    id: Token_30
    name: "Token_30"
    update_interval: never

  #- platform: template
  #  id: Token_31
  #  name: "Status"
  #  update_interval: never

uart:
- id: uart_elwa
  baud_rate: 9600
  rx_buffer_size: 1024
  tx_pin: 17
  rx_pin: 16
  debug:
    direction: RX
    dummy_receiver: true
    #after:
      #delimiter: "\r\n"
      #delimiter: "\t"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);           // Log UART data
          std::string str(bytes.begin(), bytes.end());       // Convert bytes to string
          
          // Check if the content of the string is not equal to "rs\r\n"  =>  starts with "ds\r\n"
          if (str != "rs\r\n") {
              std::vector<std::string> tokens;               // Vector to store extracted tokens
              size_t start_index = 0;                        // Index to keep track of the start position while extracting tokens

              // Extract tokens from the string
              while (start_index < str.size()) {
                  size_t end_index = start_index;
                  while (end_index < str.size() && str[end_index] != '\t') {
                      end_index++;
                  }
                  // Push the extracted token into the vector
                  tokens.push_back(str.substr(start_index, end_index - start_index));
                  if (end_index < str.size() && str[end_index] == '\t') {
                      end_index++; // Move past the tab character
                  }
                  start_index = end_index;
              }

              // Log extracted tokens
              for (size_t i = 0; i < tokens.size(); i++) {
                  ESP_LOGD("main", "Token %d: %s", i, tokens[i].c_str());

                  // Set the state of the corresponding sensor based on token index
                  switch (i) {
                      case 0: id(Token_0).publish_state(tokens[i].c_str()); break;
                      case 1: id(Token_1).publish_state(tokens[i].c_str()); break;
                      case 2: id(Token_2).publish_state(atof(tokens[i].c_str())); break;
                      case 3: id(Token_3).publish_state(atof(tokens[i].c_str())); break;
                      case 4: id(Token_4).publish_state(atof(tokens[i].c_str())); break;
                      case 5: id(Token_5).publish_state(atof(tokens[i].c_str())); break;
                      case 6: id(Token_6).publish_state(atof(tokens[i].c_str())); break;
                      case 7: id(Token_7).publish_state(atof(tokens[i].c_str())); break;
                      case 8: id(Token_8).publish_state(atof(tokens[i].c_str())); break;
                      case 9: id(Token_9).publish_state(atof(tokens[i].c_str())); break;
                      case 10: id(Token_10).publish_state(atof(tokens[i].c_str())); break;
                      case 11: id(Token_11).publish_state(atof(tokens[i].c_str())); break;
                      case 12: id(Token_12).publish_state(atof(tokens[i].c_str())); break;
                      case 13: id(Token_13).publish_state(atof(tokens[i].c_str())); break;
                      case 14: id(Token_14).publish_state(atof(tokens[i].c_str())); break;
                      case 15: id(Token_15).publish_state(atof(tokens[i].c_str())); break;
                      case 16: id(Token_16).publish_state(atof(tokens[i].c_str())); break;
                      case 17: id(Token_17).publish_state(atof(tokens[i].c_str())); break;
                      case 18: id(Token_18).publish_state(atof(tokens[i].c_str())); break;
                      case 19: id(Token_19).publish_state(atof(tokens[i].c_str())); break;
                      case 20: id(Token_20).publish_state(atof(tokens[i].c_str())); break;
                      case 21: id(Token_21).publish_state(atof(tokens[i].c_str())); break;
                      case 22: id(Token_22).publish_state(atof(tokens[i].c_str())); break;
                      case 23: id(Token_23).publish_state(atof(tokens[i].c_str())); break;
                      case 24: id(Token_24).publish_state(atof(tokens[i].c_str())); break;
                      case 25: id(Token_25).publish_state(atof(tokens[i].c_str())); break;
                      case 26: id(Token_26).publish_state(atof(tokens[i].c_str())); break;
                      case 27: id(Token_27).publish_state(atof(tokens[i].c_str())); break;
                      case 28: id(Token_28).publish_state(atof(tokens[i].c_str())); break;
                      case 29: id(Token_29).publish_state(atof(tokens[i].c_str())); break;
                      case 30: id(Token_30).publish_state(atof(tokens[i].c_str())); break;
                      // case 31: id(Token_31).publish_state(atof(tokens[i].c_str())); break;
                  }
              }
          } else {
              // Handle the case where the content of str is "rs\r\n"  =>  ElwaDC is off due to drop of PV input voltage below threshold
              // id(Token_15).publish_state(0);  //set voltage to zero
              // id(Token_16).publish_state(0);  //set current to zero
              // id(Token_17).publish_state(0);  //set power to zero
          }

#-------------CODE-TO-REQUEST-ELWA_DC-SERIAL-DATA---------------------------

#if you need to send a request periodically to get response from the uart device 
interval:
  - interval: 20s     #interval to request measurements
    then:
      #request for getting all relevant token values 'rs'
      - uart.write:  [0x72, 0x73, 0x0D, 0x0A]
      #"ping" request for getting just an OK
      #- uart.write:  [0x72, 0x72, 0x71, 0x0D, 0x0A]

(Sorry, Screenshot with test data and when sun is down, during night the values will not update)

1 Like