feat: implement frame parse and streaming reader
cel_crsf_frame_parse() parses ELRS USB format frames: [addr][length][type][payload...][crc] cel_crsf_stream_* provides incremental parsing from a byte stream: skips invalid sync bytes, discards bad CRC frames, buffers partial frames across feed calls.
This commit is contained in:
+84
-1
@@ -18,7 +18,6 @@ void test_crc_single_byte(void) {
|
||||
}
|
||||
|
||||
void test_crc_known_value(void) {
|
||||
/* Verify CRC is deterministic */
|
||||
uint8_t data[6] = {0x10, 0x80, 0x03, 0x02, 0x80, 0x01};
|
||||
uint8_t crc = cel_crsf_crc(data, 6);
|
||||
TEST_ASSERT_TRUE(crc != 0);
|
||||
@@ -39,6 +38,83 @@ void test_channel_clamp_mid(void) {
|
||||
TEST_ASSERT_EQUAL_INT16(CEL_CRSF_CH_MID, cel_crsf_channel_clamp(CEL_CRSF_CH_MID));
|
||||
}
|
||||
|
||||
/* Frame parse tests — ELRS format: [addr][length][type][payload][crc] */
|
||||
|
||||
/* Build a valid test frame with known CRC */
|
||||
static void build_test_frame(uint8_t* dst, uint8_t addr, uint8_t type,
|
||||
uint8_t const* payload, uint8_t payload_len) {
|
||||
uint8_t length = 1 + payload_len + 1; /* type + payload + crc */
|
||||
dst[0] = addr;
|
||||
dst[1] = length;
|
||||
dst[2] = type;
|
||||
memcpy(dst + 3, payload, payload_len);
|
||||
uint8_t crc = cel_crsf_crc(dst + 2, 1 + payload_len);
|
||||
dst[2 + length - 1] = crc;
|
||||
}
|
||||
|
||||
void test_parse_valid_frame(void) {
|
||||
uint8_t buf[32];
|
||||
uint8_t payload[2] = {0x80, 0x01};
|
||||
build_test_frame(buf, 0xC8, CEL_CRSF_TYPE_HEARTBEAT, payload, 2);
|
||||
|
||||
cel_crsf_frame frame;
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_frame_parse(&frame, buf, sizeof(buf)));
|
||||
TEST_ASSERT_EQUAL_UINT8(0xC8, frame.addr);
|
||||
TEST_ASSERT_EQUAL_UINT8(CEL_CRSF_TYPE_HEARTBEAT, frame.type);
|
||||
TEST_ASSERT_EQUAL_UINT8(2, frame.payload_len);
|
||||
TEST_ASSERT_EQUAL_UINT8(0x80, frame.payload[0]);
|
||||
TEST_ASSERT_EQUAL_UINT8(0x01, frame.payload[1]);
|
||||
}
|
||||
|
||||
void test_parse_null_frame(void) {
|
||||
uint8_t buf[8];
|
||||
TEST_ASSERT_EQUAL_INT(-1, cel_crsf_frame_parse(NULL, buf, 8));
|
||||
}
|
||||
|
||||
void test_parse_null_buf(void) {
|
||||
cel_crsf_frame frame;
|
||||
TEST_ASSERT_EQUAL_INT(-1, cel_crsf_frame_parse(&frame, NULL, 8));
|
||||
}
|
||||
|
||||
void test_parse_too_short(void) {
|
||||
cel_crsf_frame frame;
|
||||
uint8_t buf[2] = {0xC8, 0x03};
|
||||
TEST_ASSERT_EQUAL_INT(-1, cel_crsf_frame_parse(&frame, buf, 2));
|
||||
}
|
||||
|
||||
void test_parse_bad_crc(void) {
|
||||
uint8_t buf[32];
|
||||
uint8_t payload[2] = {0x80, 0x01};
|
||||
build_test_frame(buf, 0xC8, CEL_CRSF_TYPE_HEARTBEAT, payload, 2);
|
||||
buf[5] ^= 0xFF; /* corrupt the CRC */
|
||||
|
||||
cel_crsf_frame frame;
|
||||
TEST_ASSERT_EQUAL_INT(-1, cel_crsf_frame_parse(&frame, buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
void test_parse_empty_payload(void) {
|
||||
uint8_t buf[32];
|
||||
build_test_frame(buf, 0xEE, CEL_CRSF_TYPE_HEARTBEAT, NULL, 0);
|
||||
|
||||
cel_crsf_frame frame;
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_frame_parse(&frame, buf, sizeof(buf)));
|
||||
TEST_ASSERT_EQUAL_UINT8(0xEE, frame.addr);
|
||||
TEST_ASSERT_EQUAL_UINT8(CEL_CRSF_TYPE_HEARTBEAT, frame.type);
|
||||
TEST_ASSERT_EQUAL_UINT8(0, frame.payload_len);
|
||||
}
|
||||
|
||||
void test_parse_module_addr(void) {
|
||||
uint8_t buf[32];
|
||||
uint8_t payload[4] = {0xAA, 0xBB, 0xCC, 0xDD};
|
||||
build_test_frame(buf, 0xEE, CEL_CRSF_TYPE_GPS, payload, 4);
|
||||
|
||||
cel_crsf_frame frame;
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_frame_parse(&frame, buf, sizeof(buf)));
|
||||
TEST_ASSERT_EQUAL_UINT8(0xEE, frame.addr);
|
||||
TEST_ASSERT_EQUAL_UINT8(CEL_CRSF_TYPE_GPS, frame.type);
|
||||
TEST_ASSERT_EQUAL_UINT8(4, frame.payload_len);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_crc_empty);
|
||||
@@ -47,5 +123,12 @@ int main(void) {
|
||||
RUN_TEST(test_channel_clamp_min);
|
||||
RUN_TEST(test_channel_clamp_max);
|
||||
RUN_TEST(test_channel_clamp_mid);
|
||||
RUN_TEST(test_parse_valid_frame);
|
||||
RUN_TEST(test_parse_null_frame);
|
||||
RUN_TEST(test_parse_null_buf);
|
||||
RUN_TEST(test_parse_too_short);
|
||||
RUN_TEST(test_parse_bad_crc);
|
||||
RUN_TEST(test_parse_empty_payload);
|
||||
RUN_TEST(test_parse_module_addr);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user