diff --git a/src/CMRI.cpp b/src/CMRI.cpp index e359b1e..c899907 100644 --- a/src/CMRI.cpp +++ b/src/CMRI.cpp @@ -202,7 +202,9 @@ uint8_t CMRI::_decode(uint8_t c) else if (c == INIT) _mode = DECODE_DATA; else if (c == POLL) - goto POSTAMBLE_POLL; + // Consume poll body via IGNORE_DATA until ETX (DLE-aware), + // then reply from POSTAMBLE_IGNORE. + _mode = IGNORE_DATA; else _mode = POSTAMBLE_OTHER; break; @@ -261,5 +263,9 @@ uint8_t CMRI::_decode(uint8_t c) POSTAMBLE_IGNORE: _mode = PREAMBLE_1; + // POLL frames consume body to ETX above; reply after ETX + // to avoid RS-485 bus contention (host still transmitting). + if (_rx_packet_type == POLL) + return POLL; return NOOP; } diff --git a/test/test_cmri/test_main.cpp b/test/test_cmri/test_main.cpp index e4b60aa..5ca2f5b 100644 --- a/test/test_cmri/test_main.cpp +++ b/test/test_cmri/test_main.cpp @@ -176,6 +176,53 @@ void test_transmit_escapes_control_bytes(void) TEST_ASSERT_EQUAL_UINT8(CMRI::ETX, s.tx[10]); } +// A well-formed POLL waits for ETX before replying. +void test_poll_waits_for_etx(void) +{ + Stream s; + CMRI cmri(0, 24, 48, s); + + cmri.set_byte(0, 0x55); + + feed_packet(s, 0, CMRI::POLL, nullptr, 0); + TEST_ASSERT_TRUE(cmri.process()); + TEST_ASSERT_EQUAL_UINT8(CMRI::GET, s.tx[4]); + TEST_ASSERT_EQUAL_UINT8(0x55, s.tx[5]); +} + +// A POLL without ETX produces no reply. +void test_poll_truncated_no_reply(void) +{ + Stream s; + CMRI cmri(0, 24, 48, s); + + cmri.set_byte(0, 0x55); + + s.feed(0xFF); + s.feed(0xFF); + s.feed(CMRI::STX); + s.feed('A' + 0); + s.feed(CMRI::POLL); + + TEST_ASSERT_FALSE(cmri.process()); + TEST_ASSERT_EQUAL_UINT(0u, s.tx.size()); +} + +// A POLL with body bytes waits for ETX before replying. +void test_poll_with_body_waits_for_etx(void) +{ + Stream s; + CMRI cmri(0, 24, 48, s); + + cmri.set_byte(0, 0x77); + + uint8_t body[2] = {0x01, 0x02}; + feed_packet(s, 0, CMRI::POLL, body, 2); + TEST_ASSERT_TRUE(cmri.process()); + TEST_ASSERT_EQUAL_UINT8(CMRI::GET, s.tx[4]); + TEST_ASSERT_EQUAL_UINT8(0x77, s.tx[5]); +} + // Garbage before a valid packet is resynced away by the preamble state machine. void test_preamble_resync_after_garbage(void) { @@ -205,6 +252,9 @@ int main(int, char **) RUN_TEST(test_set_packet_updates_outputs); RUN_TEST(test_address_filtering); RUN_TEST(test_transmit_escapes_control_bytes); + RUN_TEST(test_poll_waits_for_etx); + RUN_TEST(test_poll_truncated_no_reply); + RUN_TEST(test_poll_with_body_waits_for_etx); RUN_TEST(test_preamble_resync_after_garbage); return UNITY_END(); }