How to extract value from uart text sensor

Hello i’m not so good developing in cpp but maybe i don’t need.
i have an uart text sensor that return me a string where the value in bold is the temperature i want to make an entity.

55:AA:03:07:00:05:01:01:00:01:00:11:55:AA:03:07:00:08:02:02:00:04:00:00:00:16:2F:55:AA:03:07:00:08:03:02:00:04:00:00:00:48:62:55:AA:03:07:00:05:04:01:00:01:00:14:55:AA:03:07:00:08:05:02:00:04:00:00:00:12:2E:55:AA:03:07:00:08:06:02:00:04:00:00:00:3F:5C:55:AA:03:07:00:08:07:02:00:04:00:00:00:00:1E:55:AA:03:07:00:05:08:01:00:01:00:18

to recover this information used the text uart text sensor tutorial

my yaml file is like that:

esphome:
  name: XXXXXXXXXXXXXXXXXXXX
  includes:
    - my_uart_read_line_sensor.h
    - common_includes.h # #include <regex>

esp8266:
  board: esp01_1m

# Enable logging
logger:
  level: DEBUG #makes uart stream available in esphome logstream
  baud_rate: 0 #disable logging over uart

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

ota:
  password: "**********************************"

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

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

captive_portal:

uart:
  id: uart_bus
  rx_pin: GPIO13
  tx_pin: GPIO15
  baud_rate: 9600
  debug:
    direction: BOTH
    dummy_receiver: false
    after:
      delimiter: "\n"

text_sensor:
  - platform: custom
    id: "temp"
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(uart_bus));
      App.register_component(my_custom_sensor);
      return {my_custom_sensor};
    text_sensors:
      id: "uart_readline"

sensor:
  - platform: template
    id: "temp_room1"
    unit_of_measurement: "°C"
    name: "Temperatura dormitorio"
    accuracy_decimals: 0
    lambda: |-
      std::regex r("55:AA:03:07:00:08:05:02:00:04:00:00:00:([0-9A-F]{2})");
      std::smatch m;
      std::regex_search(id(uart_readline).state, m, r);
      return stoi(m.str(0));
    update_interval: 5s

now it flash but i have an error :

INFO Starting log output from fpg-home-calefactor-1.local using esphome API
WARNING Can't connect to ESPHome API for fpg-home-calefactor-1.local: Timeout while connecting to ('192.168.11.142', 6053)
INFO Trying to reconnect to fpg-home-calefactor-1.local in the background
1 Like

How is the data formatted in the uart packet, is the hexadecimal 0x12 value for temperature equal to reading of 18°C?

Is the temperature value always in bytes 64 to 67 of the uart packet?

@mulcmu all is hexa yes, byte separator “:”
required temperature value is always at that position yes and more as it can not be > 100C° (or i would be dead), in hexa it will never occupy the 00:00:00 value is so at byte 68.

55:AA: header
03: version
07: command
00:05: length total
01: sensor/actor id
01: type here bool
00:01: length sensor/actor value
00: value (switch off)
11: checksum
next sensor is type string and 4bytes
55:AA:03:07:00:08:02:02:00:04:00:00:00:16:2F:55:AA:03:07:00:08:03:02:00:04:00:00:00:48:62:55:AA:03:07:00:05:04:01:00:01:00:14:55:AA:03:07:00:08:05:02:00:04:00:00:00:12:2E:55:AA:03:07:00:08:06:02:00:04:00:00:00:3F:5C:55:AA:03:07:00:08:07:02:00:04:00:00:00:00:1E:55:AA:03:07:00:05:08:01:00:01:00:18

So I would change the example code to be a sensor directly and just return the value for the 68th byte. I didn’t test this code but should get you close

#include "esphome.h"

class UartReadLineSensor : public Component, public UARTDevice, public /*Make this just a sensor and not a TextSenor*/Sensor {
 public:
  UartReadLineSensor(UARTComponent *parent) : UARTDevice(parent) {}

  void setup() override {
    // nothing to do here
  }

  int readline(int readch, char *buffer, int len)
  {
    static int pos = 0;
    int rpos;

    if (readch > 0) {
      switch (readch) {
        case '\n': // Ignore new-lines
          break;  
        case '\r': // Return on CR
          rpos = pos;
          pos = 0;  // Reset position index ready for next time
          return rpos;
        default:
          if (pos < len-1) {
            buffer[pos++] = readch;
            buffer[pos] = 0;
          }
      }
    }
    // No end of line has been found, so return -1.
    return -1;
  }

  void loop() override {
    const int max_line_length = 180;  //increase this to make sure everything gets read in until newline
    static char buffer[max_line_length];
    while (available()) {
      if(readline(read(), buffer, max_line_length) > 0) {
        //only return the temperature value
        publish_state( (float) buffer[67]);  //array index start at 0, return 68 byte with the temperature value casted to a floating point value
      }
    }
  }
};

And the yaml would get changed for no text sensor and a custom sensor:

sensor:
  - platform: custom
    id: "temp"
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(uart_bus));
      App.register_component(my_custom_sensor);
      return {my_custom_sensor};
    sensors:
      id: "temp_room1"
      unit_of_measurement: "°C"
      name: "Temperatura dormitorio"
      accuracy_decimals: 0

Thank you, it compile but,
I recieve no values , unknown appear on the interface in front of the entity.

I have made a button to ask for datas,

button:
  - platform: template
    name: "status"
    # icon: "mdi:emoticon-outline"
    on_press:
      - uart.write: [0x55, 0xAA, 0x00, 0x08, 0x00, 0x00, 0x07]

so i press and in the log receive

[16:15:27][D][button:013]: ‘status’ Pressed.
[16:15:27][D][uart_debug:114]: >>> 55:AA:00:08:00:00:07
[16:15:28][D][uart_debug:114]: <<< 55:AA:03:07:00:05:01:01:00:01:01:12:55:AA:03:07:00:08:02:02:00:04:00:00:00:07:20:55:AA:03:07:00:08:03:02:00:04:00:00:00:48:62:55:AA:03:07:00:05:04:01:00:01:00:14:55:AA:03:07:00:08:05:02:00:04:00:00:00:13:2F:55:AA:03:07:00:08:06:02:00:04:00:00:00:00:1D:55:AA:03:07:00:08:07:02:00:04:00:00:00:00:1E:55:AA:03:07:00:05:08:01:00:01:00:18

but i add, in the lambda, to see which value is in the state

ESP_LOGD("main", "Value of my sensor: %f", my_custom_sensor);

and it return me nothing in the console

You can take a look at this approach. No custom component/sensor required!

The sensor lambda only gets called once during setup. You could add the debug output to the custom sensor class to try to troubleshoot.

Your uart debug delimiter was only \n. The sample readline code ignores \n and only returns when \r is received.

      switch (readch) {
        case '\n': // Ignore new-lines
         // break;  //comment this line so both \n and \r will return that end of line was reached  

not sure how to do that

Oh I just noticed I pointed to a post that was by the person supporting in the thread lol…

2 Likes

If you go this route the lambda for the one data value becomes pretty simple. No messing with other code to process reading the data. interval can be used to write the bytes to request uart data.

uart:
  baud_rate: 9600
  tx_pin: GPIO13
  rx_pin: GPIO15
  id: UART3
  debug:
    direction: RX
    dummy_receiver: true
    after:
      delimiter: "\n"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);
          if(bytes.size() > 68)
            id(temp_room1).publish_state( (float) bytes[67] ); 
          else
            ESP_LOGD("main", "Response data only had %d bytes!!", bytes.size() );

sensor:
  - platform: template
    id: "temp_room1"
    unit_of_measurement: "°C"
    name: "Temperatura dormitorio"
    accuracy_decimals: 0

button:
  - platform: template
    name: "status"
    # icon: "mdi:emoticon-outline"
    on_press:
      - uart.write: [0x55, 0xAA, 0x00, 0x08, 0x00, 0x00, 0x07]

interval:
  - interval: 1min
    then:
      - uart.write: [0x55, 0xAA, 0x00, 0x08, 0x00, 0x00, 0x07]
2 Likes

ok it fall in work i don’t understand why
i think i received datas as hex 55:AA:03 … not as string U\xAA\x03\a …

Big thank you @mulcmu
thank you to present me mulcmu @Mahko_Mahko :wink:

conclusion

esphome:
  name: xxxxxxx
  includes:
     - my_uart_read_line_sensor.h

esp8266:
  board: esp01_1m

# Enable logging
logger:
  level: DEBUG #makes uart stream available in esphome logstream
  baud_rate: 0 #disable logging over uart

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

ota:
  password: "*******************"

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

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

captive_portal:

uart:
  id: uart_bus
  rx_pin: GPIO13
  tx_pin: GPIO15
  baud_rate: 9600
  debug:
    direction: BOTH
    dummy_receiver: false
    after:
      delimiter: "\n"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);
          if(bytes.size() > 68)
            id(temp_room1).publish_state( (float) bytes[67] ); 
          else
            ESP_LOGD("main", "Response data only had %d bytes!!", bytes.size() );

interval:
  - interval: 1min
    then:
      - uart.write: [0x55, 0xAA, 0x00, 0x08, 0x00, 0x00, 0x07]
          
sensor:
  - platform: custom
    id: "temp"
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(uart_bus));
      App.register_component(my_custom_sensor);
      ESP_LOGD("main", "Value of my sensor: %f", my_custom_sensor);
      return {my_custom_sensor};
    sensors:
      id: "temp_room1"
      unit_of_measurement: "°C"
      name: "Temperatura dormitorio"

and

#include "esphome.h"

class UartReadLineSensor : public Component, public UARTDevice, public /*Make this just a sensor and not a TextSenor*/Sensor {
 public:
  UartReadLineSensor(UARTComponent *parent) : UARTDevice(parent) {}

  void setup() override {
    // nothing to do here
  }

  int readline(int readch, char *buffer, int len)
  {
    static int pos = 0;
    int rpos;

    if (readch > 0) {
      switch (readch) {
        case '\n': // Ignore new-lines
          // break;  
        case '\r': // Return on CR
          rpos = pos;
          pos = 0;  // Reset position index ready for next time
          return rpos;
        default:
          if (pos < len-1) {
            buffer[pos++] = readch;
            buffer[pos] = 0;
          }
      }
    }
    // No end of line has been found, so return -1.
    return -1;
  }

  void loop() override {
    const int max_line_length = 180;  //increase this to make sure everything gets read in until newline
    static char buffer[max_line_length];
    while (available()) {
      if(readline(read(), buffer, max_line_length) > 0) {
        //only return the temperature value
        publish_state( (float) buffer[67]);  //array index start at 0, return 68 byte with the temperature value casted to a floating point value
      }
    }
  }
};

now i will try to include other information from the same buffer

You should use either the lambda in the uart debug for processing or the custom component. Right now it looks like you have implemented both and both are duplicating the effort to update the temp sensor.

I’d recommend just going the route of using the uart debug to process the data. Adding multiple sensors in a custom component gets even more complex. For each value you want to send back create another sensor template with a different id. Then in the lambda use the publish_state() to set the value.

uart:
  baud_rate: 9600
  tx_pin: GPIO13
  rx_pin: GPIO15
  id: UART3
  debug:
    direction: RX
    dummy_receiver: true
    after:
      delimiter: "\n"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);
          if(bytes.size() == 111)
          {
            id(temp_room1).publish_state( (float) bytes[67] ); 
            id(temp_room2).publish_state( (float) bytes[25] );
          }
          else
            ESP_LOGD("uart3 debug lambda", "Response data unexpected size.  %d bytes!!", bytes.size() );

sensor:
  - platform: template
    id: "temp_room1"
    unit_of_measurement: "°C"
    name: "Temperatura dormitorio"
    accuracy_decimals: 0
  - platform: template
    id: "temp_room2"
    unit_of_measurement: "°C"
    name: "Temperatura 2"
    accuracy_decimals: 0    

button:
  - platform: template
    name: "status"
    # icon: "mdi:emoticon-outline"
    on_press:
      - uart.write: [0x55, 0xAA, 0x00, 0x08, 0x00, 0x00, 0x07]

interval:
  - interval: 1min
    then:
      - uart.write: [0x55, 0xAA, 0x00, 0x08, 0x00, 0x00, 0x07]

do i need to remove the inclueded file ?
it’s not working in this new way.

uart:
  id: uart_bus
  rx_pin: GPIO13
  tx_pin: GPIO15
  baud_rate: 9600
  debug:
    direction: RX
    dummy_receiver: false
    after:
      delimiter: "\n"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);
          if (bytes.size() == 111 ) {
            // id(power_room1).publish_state( (float) bytes[10] ); 
             id(temp_room1).publish_state( (float) bytes[67] ); 
          }else{
            ESP_LOGD("main", "Response data only had %d bytes!!", bytes.size() );
          }

interval:
  - interval: 1min
    then:
      - uart.write: [0x55, 0xAA, 0x00, 0x08, 0x00, 0x00, 0x07]

sensor:
  - platform: template
    id: "temp_room1"
    unit_of_measurement: "°C"
    name: "Temperatura dormitorio"

# switch:
#   - platform: template
#     id: "power_room1"
#     name: "Power"
#     # lambda: |-
#     #   auto my_custom_sensor = new UartReadLineSensor(id(uart_bus));
#     #   App.register_component(my_custom_sensor);
#     #   //ESP_LOGD("main", "Value of my sensor: %f", my_custom_sensor);
#     #   return {my_custom_sensor};
#     turn_on_action:
#       - uart.write: [0x55, 0xAA, 0x00, 0x06, 0x00, 0x05, 0x01, 0x01, 0x00, 0x01, 0x01, 0x0E]
#     turn_off_action:
#       - uart.write: [0x55, 0xAA, 0x00, 0x06, 0x00, 0x05, 0x01, 0x01, 0x00, 0x01, 0x00, 0x0D]

Dummy receiver needs to be true for this option.

Is there a message in the log with size of the response data. I might have counted wrong for the 111. If some of the data has a dynamic length then that check might need to change.

1 Like

i count 111 too but I have no message in log. i set direction to RX well, and when i put BOTH, i receive the error from my 7 byte short query message.
let me check the dummy receiver to true

with dummy receiver it’s working

1 Like