ESPHome 2026.5.0b1
Loading...
Searching...
No Matches
teleinfo.cpp
Go to the documentation of this file.
1#include "teleinfo.h"
2#include "esphome/core/log.h"
3
4namespace esphome::teleinfo {
5
6static const char *const TAG = "teleinfo";
7
8/* Helpers */
9static int get_field(char *dest, char *buf_start, char *buf_end, int sep, int max_len) {
10 char *field_end;
11 int len;
12
13 field_end = static_cast<char *>(memchr(buf_start, sep, buf_end - buf_start));
14 if (!field_end)
15 return 0;
16 len = field_end - buf_start;
17 if (len >= max_len)
18 return len;
19 strncpy(dest, buf_start, len);
20 dest[len] = '\0';
21
22 return len;
23}
24/* TeleInfo methods */
25bool TeleInfo::check_crc_(const char *grp, const char *grp_end) {
26 int grp_len = grp_end - grp;
27 uint8_t raw_crc = grp[grp_len - 1];
28 uint8_t crc_tmp = 0;
29 int i;
30
31 for (i = 0; i < grp_len - checksum_area_end_; i++)
32 crc_tmp += grp[i];
33
34 crc_tmp &= 0x3F;
35 crc_tmp += 0x20;
36 if (raw_crc != crc_tmp) {
37 ESP_LOGE(TAG, "bad crc: got %d except %d", raw_crc, crc_tmp);
38 return false;
39 }
40
41 return true;
42}
43bool TeleInfo::read_chars_until_(bool drop, uint8_t c) {
44 uint8_t received;
45 int j = 0;
46
47 while (available() > 0 && j < 128) {
48 j++;
49 received = read();
50 if (received == c)
51 return true;
52 if (drop)
53 continue;
54 /*
55 * Internal buffer is full, switch to OFF mode.
56 * Data will be retrieved on next update.
57 */
58 if (buf_index_ >= (MAX_BUF_SIZE - 1)) {
59 ESP_LOGW(TAG, "Internal buffer full");
60 state_ = OFF;
61 return false;
62 }
63 buf_[buf_index_++] = received;
64 }
65
66 return false;
67}
68void TeleInfo::setup() { state_ = OFF; }
70 if (state_ == OFF) {
71 buf_index_ = 0;
72 state_ = ON;
73 }
74}
76 switch (state_) {
77 case OFF:
78 break;
79 case ON:
80 /* Dequeue chars until start frame (0x2) */
81 if (read_chars_until_(true, 0x2))
82 state_ = START_FRAME_RECEIVED;
83 break;
85 /* Dequeue chars until end frame (0x3) */
86 if (read_chars_until_(false, 0x3))
87 state_ = END_FRAME_RECEIVED;
88 break;
90 char *buf_finger;
91 char *grp_end;
92 char *buf_end;
93 int field_len;
94
95 buf_finger = buf_;
96 buf_end = buf_ + buf_index_;
97
98 /* Each frame is composed of multiple groups starting by 0xa(Line Feed) and ending by
99 * 0xd ('\r').
100 *
101 * Historical mode: each group contains tag, data and a CRC separated by 0x20 (Space)
102 * 0xa | Tag | 0x20 | Data | 0x20 | CRC | 0xd
103 * ^^^^^^^^^^^^^^^^^^^^
104 * Checksum is computed on the above in historical mode.
105 *
106 * Standard mode: each group contains tag, data and a CRC separated by 0x9 (\t)
107 * 0xa | Tag | 0x9 | Data | 0x9 | CRC | 0xd
108 * ^^^^^^^^^^^^^^^^^^^^^^^^^
109 * Checksum is computed on the above in standard mode.
110 *
111 * Note that some Tags may have a timestamp in Standard mode. In this case
112 * the group would looks like this:
113 * 0xa | Tag | 0x9 | Timestamp | 0x9 | Data | 0x9 | CRC | 0xd
114 *
115 * The DATE tag is a special case. The group looks like this
116 * 0xa | Tag | 0x9 | Timestamp | 0x9 | 0x9 | CRC | 0xd
117 *
118 */
119 while ((buf_finger = static_cast<char *>(memchr(buf_finger, (int) 0xa, buf_index_ - 1))) &&
120 ((buf_finger - buf_) < buf_index_)) { // NOLINT(clang-diagnostic-sign-compare)
121 /*
122 * Make sure timesamp is nullified between each tag as some tags don't
123 * have a timestamp
124 */
125 timestamp_[0] = '\0';
126 /* Point to the first char of the group after 0xa */
127 buf_finger += 1;
128
129 /* Group len */
130 grp_end = static_cast<char *>(memchr(buf_finger, 0xd, buf_end - buf_finger));
131 if (!grp_end) {
132 ESP_LOGE(TAG, "No group found");
133 break;
134 }
135
136 if (!check_crc_(buf_finger, grp_end))
137 continue;
138
139 /* Get tag */
140 field_len = get_field(tag_, buf_finger, grp_end, separator_, MAX_TAG_SIZE);
141 if (!field_len || field_len >= MAX_TAG_SIZE) {
142 ESP_LOGE(TAG, "Invalid tag.");
143 continue;
144 }
145
146 /* Advance buf_finger to after the tag and the separator. */
147 buf_finger += field_len + 1;
148
149 /*
150 * If there is two separators and the tag is not equal to "DATE" or
151 * historical mode is not in use (separator_ != 0x20), it means there is a
152 * timestamp to read first.
153 */
154 if (std::count(buf_finger, grp_end, separator_) == 2 && strcmp(tag_, "DATE") != 0 && separator_ != 0x20) {
155 field_len = get_field(timestamp_, buf_finger, grp_end, separator_, MAX_TIMESTAMP_SIZE);
156 if (!field_len || field_len >= MAX_TIMESTAMP_SIZE) {
157 ESP_LOGE(TAG, "Invalid timestamp for tag %s", timestamp_);
158 continue;
159 }
160
161 /* Advance buf_finger to after the first data and the separator. */
162 buf_finger += field_len + 1;
163 }
164
165 field_len = get_field(val_, buf_finger, grp_end, separator_, MAX_VAL_SIZE);
166 if (!field_len || field_len >= MAX_VAL_SIZE) {
167 ESP_LOGE(TAG, "Invalid value for tag %s", tag_);
168 continue;
169 }
170
171 /* Advance buf_finger to end of group */
172 buf_finger += field_len + 1 + 1 + 1;
173
174 publish_value_(std::string(tag_), std::string(val_));
175 }
176 state_ = OFF;
177 break;
178 }
179}
180void TeleInfo::publish_value_(const std::string &tag, const std::string &val) {
181 for (auto *element : teleinfo_listeners_) {
182 if (tag != element->tag)
183 continue;
184 element->publish_val(val);
185 }
186}
188 ESP_LOGCONFIG(TAG, "TeleInfo:");
190}
191TeleInfo::TeleInfo(bool historical_mode) {
192 if (historical_mode) {
193 /*
194 * Historical mode doesn't contain last separator between checksum and data.
195 */
197 separator_ = 0x20;
198 baud_rate_ = 1200;
199 } else {
201 separator_ = 0x9;
202 baud_rate_ = 9600;
203 }
204}
206
207} // namespace esphome::teleinfo
void publish_value_(const std::string &tag, const std::string &val)
Definition teleinfo.cpp:180
char timestamp_[MAX_TIMESTAMP_SIZE]
Definition teleinfo.h:41
char buf_[MAX_BUF_SIZE]
Definition teleinfo.h:37
TeleInfo(bool historical_mode)
Definition teleinfo.cpp:191
char val_[MAX_VAL_SIZE]
Definition teleinfo.h:40
void register_teleinfo_listener(TeleInfoListener *listener)
Definition teleinfo.cpp:205
bool check_crc_(const char *grp, const char *grp_end)
Definition teleinfo.cpp:25
enum esphome::teleinfo::TeleInfo::State OFF
std::vector< TeleInfoListener * > teleinfo_listeners_
Definition teleinfo.h:31
char tag_[MAX_TAG_SIZE]
Definition teleinfo.h:39
void dump_config() override
Definition teleinfo.cpp:187
bool read_chars_until_(bool drop, uint8_t c)
Definition teleinfo.cpp:43
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits=1, UARTParityOptions parity=UART_CONFIG_PARITY_NONE, uint8_t data_bits=8)
Check that the configuration of the UART bus matches the provided values and otherwise print a warnin...
Definition uart.cpp:16
mopeka_std_values val[3]
const char *const TAG
Definition spi.cpp:7
const char * tag
Definition log.h:74
std::string size_t len