I am working on a project to send serial data to HA and I needed to be able to split a string into 3 sensors. Many examples on how to put a single line into a text sensor, but nothing on splitting text.
I figured it out with some trial and error but figured I would post this here for the next time I go looking on how to do this
uart_read_line_sensor.h
#include "esphome.h"
class UartReadLineSensor : public Component, public UARTDevice, public TextSensor {
public:
TextSensor *playtime = new TextSensor();
TextSensor *switchhits = new TextSensor();
TextSensor *score = new TextSensor();
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 = 80;
static char buffer[max_line_length];
while (available()) {
if(readline(read(), buffer, max_line_length) > 0) {
playtime->publish_state(strtok(buffer, ":"));
switchhits->publish_state(strtok(NULL, ":"));
score->publish_state(strtok(NULL, ":"));
}
}
}
};
If you have a combination of different sensor types in your serial data, for example, a text_sensor and a (float) sensor, this is a bit more complicated. I solved it by making use of some kind of global variable to pass the data to the other sensor type. Like below:
#include "esphome.h"
namespace MyTextData {
TextSensor *my_text_sensor = new TextSensor();
}
class MyTextSensor : public TextSensor {
public:
TextSensor *my_text_sensor = MyTextData::my_text_sensor;
};
class MySensor : public Sensor {
public: Sensor *my_sensor = new Sensor();
...
my_sensor->publish_state(...);
MyTextData::my_text_sensor->publish_state(...);
}
And then in the yaml file you can register the different components:
text_sensor:
- platform: custom
lambda: |-
auto textsensor = new MyTextSensor();
App.register_component(textsensor);
return {textsensor->my_text_sensor};
text_sensors:
- name: my_text_sensor
sensor:
- platform: custom
lambda: |-
auto sensor = new MySensor();
App.register_component(sensor);
return {sensor->my_sensor};
sensors:
- name: my_sensor
By the way, it’s simpler to do id(my_sensor).publish_state(...) id(my_text_sensor).publish_state(...)
from within your c++ instead of using the globals but it is a nice trick and can be really useful.
Hey im new to esphome. im able to get values from uart. i have configuerd ardunio to send data to esphome. but i need to split my values. because im sending two values like “0.00V 0.00V” this is come from valtage sensore. im monitoing two battories now. so need to to split this value to two value like “0.00V” and “0.00V”. anybody can help me ?
I’m onto doing something similar to what @euchre did in the OP above, but my case is slightly different:
I have three-character status messages coming in, with one of them having some extra payload separated with spaces. I would like to populate the normal/ordinary “publish_state” return value in both cases (extra payload or not), whilst only updating the extra text sensors when there is payload data available. (otherwise they should remain static)
Am I making sense?
This is how far I have understood it, but C++ is not my “native” language, and I’m really stuggeling to understand what kind of syntactical errors I have made here.
...trunkated. This part of the include file ...
int ChRead;
while (available()) {
ChRead = readline(read(), buffer, max_line_length);
if(ChRead = 3) publish_state(buffer);
if(ChRead > 3) {
publish_state(strtok(buffer, " "));
localtime->publish_state(strtok(NULL, " "));
watercl->publish_state(strtok(NULL, " "));
}
}
And this is the part in YAML:
text_sensor:
- platform: custom
lambda: |-
auto my_custom_sensor = new UartReadLineSensor(id(uart_bus));
App.register_component(my_custom_sensor);
return {my_custom_sensor, my_custom_sensor->localtime, my_custom_sensor->watercl};
text_sensors:
- id: "uart_readline"
on_value:
then:
- lambda: |-
if (x == "CCS") id(UV_cycle).publish_state(true);
if (x == "CCE") id(UV_cycle).publish_state(false);
if (x == "UVE") id(UVLampSensor).publish_state(true);
if (x == "UVD") id(UVLampSensor).publish_state(false);
if (x == "PME") id(WaterPumpMotorSensor).publish_state(true);
if (x == "PMD") id(WaterPumpMotorSensor).publish_state(false);
if (x == "TPA") id(FootSwitchSensor).publish_state(true);
if (x == "TPR") id(FootSwitchSensor).publish_state(false);
- name: "localtime"
- name: "watercl"
Sorry for the noob question; I’ve just got the feeling that I’m not too far off from what I want to accomplish