ESPHome 2026.9.0-dev
Loading...
Searching...
No Matches
mk2pvrouter.cpp
Go to the documentation of this file.
1#include "mk2pvrouter.h"
2#include "esphome/core/log.h"
3#include <cstring>
4
6
7static const char *const TAG = "mk2pvrouter";
8
9constexpr uint8_t START_FRAME = 0x2;
10constexpr uint8_t END_FRAME = 0x3;
11constexpr uint8_t LINE_FEED = 0xa;
12constexpr uint8_t CARRIAGE_RETURN = 0xd;
13constexpr uint8_t TAB = 0x9;
14constexpr uint8_t MAX_ITERATIONS = 128;
15constexpr uint8_t CRC_MASK = 0x3F;
16constexpr uint8_t CRC_OFFSET = 0x20;
17
18// Extracts a TAB-delimited field from [buf_start, buf_end) into dest.
19// Returns the field length, or 0 if no TAB was found, or the (uncopied) field
20// length if it's >= max_len.
21static size_t get_field(char *dest, const char *buf_start, const char *buf_end, size_t max_len) {
22 const auto *const field_end = static_cast<const char *>(memchr(buf_start, TAB, buf_end - buf_start));
23 if (!field_end)
24 return 0;
25 const size_t len = field_end - buf_start;
26 if (len >= max_len) {
27 ESP_LOGE(TAG, "Field too long: %zu bytes (max %zu)", len, max_len);
28 return len;
29 }
30
31 memcpy(dest, buf_start, len);
32 dest[len] = '\0'; // Null-terminate
33 return len;
34}
35
36// Calculates the CRC (checksum) for a given group of characters.
37uint8_t Mk2PVRouter::calculate_crc_(const char *grp, size_t grp_len) {
38 uint8_t crc_tmp{0};
39 const auto effective_len = grp_len - CRC_SUFFIX_LEN;
40 for (size_t i = 0; i < effective_len; i++) {
41 crc_tmp += grp[i];
42 }
43 crc_tmp &= CRC_MASK;
44 crc_tmp += CRC_OFFSET;
45 return crc_tmp;
46}
47
48// Verifies the CRC of a group against its trailing CRC byte.
49bool Mk2PVRouter::check_crc_(const char *grp, const char *grp_end) {
50 const auto grp_len = grp_end - grp;
51 if (grp_len < static_cast<decltype(grp_len)>(CRC_SUFFIX_LEN)) {
52 ESP_LOGE(TAG, "Empty or too short group");
53 return false;
54 }
55 const auto raw_crc = grp[grp_len - 1];
56
57 const auto calculated_crc = this->calculate_crc_(grp, grp_len);
58
59 if (raw_crc != calculated_crc) {
60 ESP_LOGE(TAG, "CRC mismatch: expected %d, got %d", calculated_crc, raw_crc);
61 return false;
62 }
63 return true;
64}
65
66// Validates, parses, and publishes a single tag/value group.
67void Mk2PVRouter::process_group_(const char *grp, const char *grp_end) {
68 if (!this->check_crc_(grp, grp_end))
69 return;
70
71 size_t field_len = get_field(this->tag_, grp, grp_end, MAX_TAG_SIZE);
72 if (!field_len || field_len >= MAX_TAG_SIZE) {
73 ESP_LOGE(TAG, "Invalid tag");
74 return;
75 }
76 const auto *val_start = grp + field_len + 1; // Skip tag + TAB.
77
78 field_len = get_field(this->val_, val_start, grp_end, MAX_VAL_SIZE);
79 if (!field_len || field_len >= MAX_VAL_SIZE) {
80 ESP_LOGE(TAG, "Invalid value for tag %s", this->tag_);
81 return;
82 }
83
84 this->publish_value_(this->tag_, this->val_);
85}
86
87// Reads characters until `c` is found or the internal buffer is full.
88bool Mk2PVRouter::read_chars_until_(bool drop, uint8_t c) {
89 size_t j{0};
90
91 while (this->available() > 0 && j++ < MAX_ITERATIONS) {
92 const auto received = this->read();
93 if (received < 0)
94 continue;
95 if (received == c)
96 return true;
97 if (drop)
98 continue;
99 if (this->buf_index_ >= (sizeof(this->buf_) - 1)) {
100 ESP_LOGW(TAG, "Internal buffer full");
101 this->buf_index_ = 0;
103 return false;
104 }
105 this->buf_[this->buf_index_++] = received;
106 }
107
108 return false;
109}
110
112 switch (this->state_) {
114 ESP_LOGVV(TAG, "State: WAITING_FOR_START");
115 if (this->read_chars_until_(true, START_FRAME))
117 break;
119 ESP_LOGVV(TAG, "State: START_FRAME_RECEIVED");
120 if (this->read_chars_until_(false, END_FRAME))
122 break;
124 ESP_LOGVV(TAG, "State: END_FRAME_RECEIVED -> processing");
125
126 if (this->buf_index_ == 0) {
128 break;
129 }
130
131 auto *buf_finger = this->buf_;
132 auto *buf_end = this->buf_ + this->buf_index_;
133
134 // Each group: 0xa(LF) | Tag | 0x9(TAB) | Data | 0x9(TAB) | CRC | 0xd(CR)
135 // CRC is computed over "Tag | TAB | Data | TAB".
136 while ((buf_finger = static_cast<char *>(memchr(buf_finger, LINE_FEED, buf_end - buf_finger))) != nullptr) {
137 ++buf_finger; // Skip LF to the start of the group.
138
139 auto *const grp_end = static_cast<char *>(memchr(buf_finger, CARRIAGE_RETURN, buf_end - buf_finger));
140 if (!grp_end) {
141 ESP_LOGE(TAG, "No group found");
142 break;
143 }
144
145 this->process_group_(buf_finger, grp_end);
146
147 buf_finger = grp_end; // grp_end is always < buf_end, so this stays in bounds.
148 }
149 this->buf_index_ = 0;
151 break;
152 }
153 }
154}
155
156void Mk2PVRouter::publish_value_(const char *tag, const char *val) {
157#ifdef MK2PVROUTER_LISTENER_COUNT
158 for (auto *element : this->mk2pvrouter_listeners_) {
159 if (strcmp(tag, element->get_tag()) != 0)
160 continue;
161 element->publish_val(val);
162 }
163#endif
164}
165
167 ESP_LOGCONFIG(TAG, "Mk2PVRouter:");
169}
170
171#ifdef MK2PVROUTER_LISTENER_COUNT
175#endif
176
177} // namespace esphome::mk2pvrouter
bool check_crc_(const char *grp, const char *grp_end)
static constexpr size_t CRC_SUFFIX_LEN
Definition mk2pvrouter.h:45
void process_group_(const char *grp, const char *grp_end)
void publish_value_(const char *tag, const char *val)
bool read_chars_until_(bool drop, uint8_t c)
uint8_t calculate_crc_(const char *grp, size_t grp_len)
static constexpr uint32_t BAUD_RATE
Definition mk2pvrouter.h:46
void register_mk2pvrouter_listener(Mk2PVRouterListener *listener)
StaticVector< Mk2PVRouterListener *, MK2PVROUTER_LISTENER_COUNT > mk2pvrouter_listeners_
Definition mk2pvrouter.h:55
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]
constexpr uint8_t MAX_ITERATIONS
constexpr uint8_t CARRIAGE_RETURN
constexpr uint8_t START_FRAME
constexpr uint8_t CRC_OFFSET
constexpr uint8_t LINE_FEED
constexpr uint8_t TAB
constexpr uint8_t END_FRAME
constexpr uint8_t CRC_MASK
const void size_t len
Definition hal.h:64