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