mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 02:02:51 +00:00
[mk2pvrouter] Store the frame as bytes and read with read_byte() (#20530)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b10eef537e
commit
da43b6292d
@@ -19,8 +19,8 @@ constexpr uint8_t CRC_OFFSET = 0x20;
|
||||
// Extracts a TAB-delimited field from [buf_start, buf_end) into dest.
|
||||
// Returns the field length, or 0 if no TAB was found, or the (uncopied) field
|
||||
// length if it's >= max_len.
|
||||
static size_t get_field(char *dest, const char *buf_start, const char *buf_end, size_t max_len) {
|
||||
const auto *const field_end = static_cast<const char *>(memchr(buf_start, TAB, buf_end - buf_start));
|
||||
static size_t get_field(char *dest, const uint8_t *buf_start, const uint8_t *buf_end, size_t max_len) {
|
||||
const auto *const field_end = static_cast<const uint8_t *>(memchr(buf_start, TAB, buf_end - buf_start));
|
||||
if (!field_end)
|
||||
return 0;
|
||||
const size_t len = field_end - buf_start;
|
||||
@@ -34,13 +34,13 @@ static size_t get_field(char *dest, const char *buf_start, const char *buf_end,
|
||||
return len;
|
||||
}
|
||||
|
||||
// Calculates the CRC (checksum) for a given group of characters.
|
||||
uint8_t Mk2PVRouter::calculate_crc_(const char *grp, size_t grp_len) {
|
||||
return (sum_bytes(reinterpret_cast<const uint8_t *>(grp), grp_len - CRC_SUFFIX_LEN) & CRC_MASK) + CRC_OFFSET;
|
||||
// Calculates the CRC (checksum) for a given group of bytes.
|
||||
uint8_t Mk2PVRouter::calculate_crc_(const uint8_t *grp, size_t grp_len) {
|
||||
return (sum_bytes(grp, grp_len - CRC_SUFFIX_LEN) & CRC_MASK) + CRC_OFFSET;
|
||||
}
|
||||
|
||||
// Verifies the CRC of a group against its trailing CRC byte.
|
||||
bool Mk2PVRouter::check_crc_(const char *grp, const char *grp_end) {
|
||||
bool Mk2PVRouter::check_crc_(const uint8_t *grp, const uint8_t *grp_end) {
|
||||
const auto grp_len = grp_end - grp;
|
||||
if (grp_len < static_cast<std::ptrdiff_t>(CRC_SUFFIX_LEN)) {
|
||||
ESP_LOGE(TAG, "Empty or too short group");
|
||||
@@ -58,7 +58,7 @@ bool Mk2PVRouter::check_crc_(const char *grp, const char *grp_end) {
|
||||
}
|
||||
|
||||
// Validates, parses, and publishes a single tag/value group.
|
||||
void Mk2PVRouter::process_group_(const char *grp, const char *grp_end) {
|
||||
void Mk2PVRouter::process_group_(const uint8_t *grp, const uint8_t *grp_end) {
|
||||
if (!this->check_crc_(grp, grp_end))
|
||||
return;
|
||||
|
||||
@@ -83,9 +83,9 @@ bool Mk2PVRouter::read_chars_until_(bool drop, uint8_t c) {
|
||||
size_t j{0};
|
||||
|
||||
while (this->available() > 0 && j++ < MAX_ITERATIONS) {
|
||||
const auto received = this->read();
|
||||
if (received < 0)
|
||||
continue;
|
||||
uint8_t received;
|
||||
if (!this->read_byte(&received))
|
||||
break;
|
||||
if (received == c)
|
||||
return true;
|
||||
if (drop)
|
||||
@@ -127,10 +127,10 @@ void Mk2PVRouter::loop() {
|
||||
|
||||
// Each group: 0xa(LF) | Tag | 0x9(TAB) | Data | 0x9(TAB) | CRC | 0xd(CR)
|
||||
// CRC is computed over "Tag | TAB | Data | TAB".
|
||||
while ((buf_finger = static_cast<char *>(memchr(buf_finger, LINE_FEED, buf_end - buf_finger))) != nullptr) {
|
||||
while ((buf_finger = static_cast<uint8_t *>(memchr(buf_finger, LINE_FEED, buf_end - buf_finger))) != nullptr) {
|
||||
++buf_finger; // Skip LF to the start of the group.
|
||||
|
||||
auto *const grp_end = static_cast<char *>(memchr(buf_finger, CARRIAGE_RETURN, buf_end - buf_finger));
|
||||
auto *const grp_end = static_cast<uint8_t *>(memchr(buf_finger, CARRIAGE_RETURN, buf_end - buf_finger));
|
||||
if (!grp_end) {
|
||||
ESP_LOGE(TAG, "No group found");
|
||||
break;
|
||||
|
||||
@@ -57,12 +57,12 @@ class Mk2PVRouter : public Component, public uart::UARTDevice {
|
||||
State state_{State::WAITING_FOR_START};
|
||||
char tag_[MAX_TAG_SIZE];
|
||||
char val_[MAX_VAL_SIZE];
|
||||
char buf_[MAX_BUF_SIZE]; // Large buffer last to reduce padding
|
||||
uint8_t buf_[MAX_BUF_SIZE]; // Large buffer last to reduce padding
|
||||
|
||||
bool read_chars_until_(bool drop, uint8_t c);
|
||||
uint8_t calculate_crc_(const char *grp, size_t grp_len);
|
||||
bool check_crc_(const char *grp, const char *grp_end);
|
||||
void process_group_(const char *grp, const char *grp_end);
|
||||
uint8_t calculate_crc_(const uint8_t *grp, size_t grp_len);
|
||||
bool check_crc_(const uint8_t *grp, const uint8_t *grp_end);
|
||||
void process_group_(const uint8_t *grp, const uint8_t *grp_end);
|
||||
void publish_value_(const char *tag, const char *val);
|
||||
};
|
||||
} // namespace esphome::mk2pvrouter
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
namespace esphome::mk2pvrouter::testing {
|
||||
|
||||
namespace {
|
||||
// The parser works on raw frame bytes; the groups below are written as text for readability.
|
||||
const uint8_t *as_bytes(const char *str) { return reinterpret_cast<const uint8_t *>(str); }
|
||||
|
||||
class TestListener final : public Mk2PVRouterListener {
|
||||
public:
|
||||
explicit TestListener(const char *tag) : Mk2PVRouterListener(tag) {}
|
||||
@@ -25,7 +28,9 @@ class Mk2PVRouterTest : public ::testing::Test {
|
||||
void SetUp() override { this->sut_.register_mk2pvrouter_listener(&this->listener_); }
|
||||
|
||||
// Feeds one "tag<TAB>value<TAB>crc" literal, as the frame parser does after END_FRAME.
|
||||
template<size_t N> void process_(const char (&group)[N]) { this->sut_.process_group_(group, group + N - 1); }
|
||||
template<size_t N> void process_(const char (&group)[N]) {
|
||||
this->sut_.process_group_(as_bytes(group), as_bytes(group) + N - 1);
|
||||
}
|
||||
|
||||
TestableMk2PVRouter sut_;
|
||||
TestListener listener_{"P1"};
|
||||
@@ -40,22 +45,22 @@ TEST(Mk2PVRouterListenerTest, GetTagReturnsConstructorTag) {
|
||||
TEST_F(Mk2PVRouterTest, CalculateCrcMatchesKnownGroup) {
|
||||
// "P1\t1234\t" sums to 0x3D ('=') per the mk2pvrouter CRC algorithm.
|
||||
const char grp[] = "P1\t1234\t=";
|
||||
EXPECT_EQ(this->sut_.calculate_crc_(grp, sizeof(grp) - 1), '=');
|
||||
EXPECT_EQ(this->sut_.calculate_crc_(as_bytes(grp), sizeof(grp) - 1), '=');
|
||||
}
|
||||
|
||||
TEST_F(Mk2PVRouterTest, CheckCrcAcceptsMatchingCrc) {
|
||||
const char grp[] = "P1\t1234\t=";
|
||||
EXPECT_TRUE(this->sut_.check_crc_(grp, grp + sizeof(grp) - 1));
|
||||
EXPECT_TRUE(this->sut_.check_crc_(as_bytes(grp), as_bytes(grp) + sizeof(grp) - 1));
|
||||
}
|
||||
|
||||
TEST_F(Mk2PVRouterTest, CheckCrcRejectsMismatchedCrc) {
|
||||
const char grp[] = "P1\t1234\t!";
|
||||
EXPECT_FALSE(this->sut_.check_crc_(grp, grp + sizeof(grp) - 1));
|
||||
EXPECT_FALSE(this->sut_.check_crc_(as_bytes(grp), as_bytes(grp) + sizeof(grp) - 1));
|
||||
}
|
||||
|
||||
TEST_F(Mk2PVRouterTest, CheckCrcRejectsEmptyGroup) {
|
||||
const char grp[] = "";
|
||||
EXPECT_FALSE(this->sut_.check_crc_(grp, grp));
|
||||
EXPECT_FALSE(this->sut_.check_crc_(as_bytes(grp), as_bytes(grp)));
|
||||
}
|
||||
|
||||
TEST_F(Mk2PVRouterTest, ProcessGroupPublishesValidGroupToMatchingListener) {
|
||||
|
||||
Reference in New Issue
Block a user