UART String Split

Hi Guys,

I need a bit help in case of a UART string return.
To readout data from a RS485 I have to send a command like

~22014A42E00201FD28\r

and get back an answer like

~22014A00E0C601221B14B8100CF60CF00CF50CF40CF40CF30CF30CF20CF20CF40CF50CF20CF50CF30CF20CF201180118010E040118011801180118FDD500000064002710221B0016000000020000000000230000000000000000000000000000000000000000000000D48A\r

Out of this answer I’d like to extract for example: Position 10 to 12
and convert this HEX data to decimal data
Which means, if I convert:

hex: 0CF3 to decimal: 3315

and collect it in a sensor

I’m not good in custom_uart components, so I tried to solve it like this in my YAML

substitutions:
tx_pin: GPIO18
rx_pin: GPIO19
uart:
- id: uart_0
baud_rate: 9600
tx_pin: ${tx_pin}
rx_pin: ${rx_pin}
stop_bits: 1
debug:
direction: BOTH
dummy_receiver: true
after:
delimiter: “\r\n”
sequence:
- lambda: |-
UARTDebug::log_string(direction, bytes);
std::string str(bytes.begin(), bytes.end());
interval:
- interval: 30s
then:
- uart.write: “~22014A42E00201FD28\r”

I receive the string back … but I don’t know how to extract the exact position of the HEX and than convert it to decimal
Thought there should be a function like

split(“string”, “position1”,“position2”)

and than with

format_hex_pretty()

to format it to decimal numbers?

can somebody give me support ?
thanks to all

You can use std::stoul(value.substr(startPos,len), nullptr,16);

The value is the variable that holds the string value, startPos the position to extract and len the number of chars to copy. In your case resp 9 and 2.

If you don’t know how to incorporate in your yaml, post the content.

Thank You very much,

But I received this error while compiling the YAML

/config/esphome/esphome-web-dd8a60.yaml: In lambda function:
/config/esphome/esphome-web-dd8a60.yaml:54:40: error: conversion from ‘long unsigned int’ to non-scalar type ‘std::__cxx11::string’ {aka ‘std::__cxx11::basic_string’} requested
std::string socdata1 = std::stoul(socdata, nullptr, 16);
^~~~~~~~~~~~
*** [/data/esphome-web-dd8a60/.pioenvs/esphome-web-dd8a60/src/main.cpp.o] Error 1

this is partly the lambda function

std::string rawdata(bytes.begin(), bytes.end());
std::string socdata = rawdata.substr(15,4);
std::string socdata1 = std::stoul(socdata, nullptr, 16);
ESP_LOGD(“Battery SOC”, “%s”, socdata.c_str());
ESP_LOGD(“Battery SOC”, “%s”, socdata1.c_str());

The HEX code is perfectly extracting (thanks for this)
but the std::stoul function is not working, maybe I have to convert the string first correct for the std:stoul function ?

What stoul produces is an unsigned long which is a numerical value. You can’t assign it to a string variable. Instead, change it to the following:

unsigned long socdataDecimal = std::stoul(socdata, nullptr, 16);
ESP_LOGD(“Battery SOC”, “%s”, socdata.c_str());
ESP_LOGD(“Battery SOC”, “%lu”, socdataDecimal);

Thanks again ckxsmart
you’re the best.

Now it works, but I have one more Problem.
The ESP32 get a second answer directly after the first.
And for both, he will do this operation, which overwrites me the right value.

[13:41:44][D][uart_debug:158]: <<< “~22014A00E0C601271015C1100DE00DD90D680D630D850D810D”
[13:41:44][D][Battery SOC:054]: 10000
[13:41:45][D][uart_debug:158]: <<< “00000000000002300000000000000009515D4C7\r”
[13:41:45][D][Battery SOC:054]: 0

which means, the first decimal value I’ve extracted with your help is 10000
This I can publish as an sensor which is no problem !
but directly after the ESP dedects the second message and overwrites the sensor with 0

I tried to do stuff like

- lambda: |-
UARTDebug::log_string(direction, bytes);
std::string rawdata(bytes.begin(), bytes.end());
unsigned long databatterysoc = std::stoul(rawdata.substr(15,4), nullptr, 16);
id(batterysoc).publish_state(databatterysoc);
ESP_LOGD(“Battery SOC”, “%lu”, databatterysoc);

float testdatasplit=0;
sscanf(rawdata, “22014%d”, &testdatasplit)
ESP_LOGD(“Testausgabe”, “%s”, testdatasplit);

which means, I’d like to search for 22014 (which is only included in the first answer, and always the same) and put it in the variable testdatasplit
With this variable I could search again for the right position and convert it to decimal

But I get again a compiling error

/config/esphome/esphome-web-dd8a60.yaml: In lambda function:
/config/esphome/esphome-web-dd8a60.yaml:56:14: error: cannot convert ‘std::__cxx11::string’ {aka ‘std::__cxx11::basic_string’} to ‘const char*’
sscanf(rawdata, “22014%d”, &testdatasplit)

maybe you can help again!,

It looks like the two responses are different lengths so you could use the size of the response to distinguish the data. You can treat bytes like an array to compare just one character at time. Here was example for checking size and the first character is ‘~’ before processing the data:

          if (bytes.size()==51) {
            if(bytes[0]==0x7E)  //~ is 0x7E
              id(binData).publish_state( bytes[4] );

it worked, big thanks to all =)

1 Like

Hi Again, Guys,

I have one more Problem.
I’d like to convert from HEX to Binary format,
Which means, if I use “23” as HEX I will get “00100011”
I didn’t found any convertation which seems to be functional here