6#define PARSE_INT(field, field_name) \
8 get_token(token_buf); \
9 auto val = parse_number<int>(token_buf); \
10 if (val.has_value()) { \
11 (field) = val.value(); \
13 ESP_LOGD(TAG, "invalid " field_name " in line %s", buffer.substr(0, buffer.size() - 2).c_str()); \
18#define PARSE_STR(field, field_name) \
21 if (strlen(field) < 2) { \
22 ESP_LOGD(TAG, "too short " field_name " in line %s", buffer.substr(0, buffer.size() - 2).c_str()); \
29static const char *
const TAG =
"pylontech";
30static const int MAX_DATA_LENGTH_BYTES = 256;
31static const uint8_t ASCII_LF = 0x0A;
36 ESP_LOGCONFIG(TAG,
"pylontech:");
38 ESP_LOGE(TAG,
"Connection with pylontech failed!");
42 listener->dump_config();
45 LOG_UPDATE_INTERVAL(
this);
64 size_t to_read = std::min(avail,
sizeof(buf));
71 for (
size_t i = 0; i < to_read; i++) {
79 ESP_LOGV(TAG,
"received %d bytes", recv);
91 ESP_LOGV(TAG,
"Read from serial: %s",
buffer.substr(0,
buffer.size() - 2).c_str());
104 const char *cursor =
buffer.c_str();
105 char token_buf[TEXT_SENSOR_MAX_LEN] = {0};
108 auto get_token = [&](
char *token_buf) ->
void {
110 while (*cursor ==
' ' || *cursor ==
'\t') {
114 if (*cursor ==
'\0') {
119 const char *start = cursor;
122 while (*cursor !=
'\0' && *cursor !=
' ' && *cursor !=
'\t' && *cursor !=
'\r') {
126 size_t token_len = std::min(
static_cast<size_t>(cursor - start),
static_cast<size_t>(TEXT_SENSOR_MAX_LEN - 1));
127 memcpy(token_buf, start, token_len);
128 token_buf[token_len] = 0;
132 get_token(token_buf);
134 if (
val.has_value() &&
val.value() > 0) {
135 l.bat_num =
val.value();
136 }
else if (strcmp(token_buf,
"Power") == 0) {
138 this->
has_tlow_id_ = buffer.find(
"Tlow.Id") != std::string::npos;
139 ESP_LOGD(TAG,
"header line %s Tlow.Id: %s",
140 this->
has_tlow_id_ ? LOG_STR_LITERAL(
"with") : LOG_STR_LITERAL(
"without"),
144 ESP_LOGD(TAG,
"unknown line %s",
buffer.substr(0,
buffer.size() - 2).c_str());
148 PARSE_INT(
l.volt,
"Volt");
149 PARSE_INT(
l.curr,
"Curr");
150 PARSE_INT(
l.tempr,
"Tempr");
151 PARSE_INT(
l.tlow,
"Tlow");
153 get_token(token_buf);
155 PARSE_INT(
l.thigh,
"Thigh");
157 get_token(token_buf);
159 PARSE_INT(
l.vlow,
"Vlow");
161 get_token(token_buf);
163 PARSE_INT(
l.vhigh,
"Vhigh");
165 get_token(token_buf);
167 PARSE_STR(
l.base_st,
"Base.St");
168 PARSE_STR(
l.volt_st,
"Volt.St");
169 PARSE_STR(
l.curr_st,
"Curr.St");
170 PARSE_STR(
l.temp_st,
"Temp.St");
172 get_token(token_buf);
173 for (
char &i : token_buf) {
180 if (coul_val.has_value()) {
181 l.coulomb = coul_val.value();
183 ESP_LOGD(TAG,
"invalid Coulomb in line %s",
buffer.substr(0,
buffer.size() - 2).c_str());
187 get_token(token_buf);
188 get_token(token_buf);
189 get_token(token_buf);
190 get_token(token_buf);
191 PARSE_INT(
l.mostempr,
"Mostempr");
193 ESP_LOGD(TAG,
"successful line %s",
buffer.substr(0,
buffer.size() - 2).c_str());
196 listener->on_line_read(&
l);
void loop() override
Read data once available.
std::vector< PylontechListener * > listeners_
void process_line_(std::string &buffer)
void update() override
Schedule data readings.
void setup() override
Setup the sensor and test for a connection.
std::string buffer_[NUM_BUFFERS]
void dump_config() override
optional< std::array< uint8_t, N > > read_array()
void write_str(const char *str)
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.