celrs
A C23 project for interfacing with ELRS TX modules (e.g., BAYCK Nano Dual Band) via serial USB using the CRSF (Crossfire Serial) protocol.
Built on the same TDD foundation as ctdd using Unity and CMock.
All dependencies are fetched automatically via CMake FetchContent — no
manual installation required beyond the tools listed below.
Requirements
| Tool | Purpose |
|---|---|
| CMake ≥ 3.21 | Build system |
| Ninja | Build backend |
| C23 compiler | GCC 14+, Clang 18+ |
| Ruby ≥ 3.0 | CMock mock generation |
| gcovr ≥ 6.0 | Coverage reports — optional (uv tool install gcovr) |
Build
Configure:
cmake -S . -B build -G Ninja
Build:
ninja -C build
Test
Full Unity output with colors:
ninja -C build check
CTest summary only:
ninja -C build test
check builds all suites and runs CTest with --output-on-failure,
so assertion-level detail appears on any failure without running
binaries by hand.
Coverage
Configure with coverage instrumentation:
cmake -S . -B build-cov -G Ninja -DENABLE_COVERAGE=ON
Generate the HTML report:
ninja -C build-cov coverage
Open build-cov/coverage/index.html in a browser to view results.
Only celrs/ source files are measured. Unity, CMock, and generated
mock files are excluded. Requires GCC or Clang with gcov support, and
gcovr on PATH.
Windows note: requires GCC (e.g.
scoop install gcc) or a Clang build that includes compiler-rt. A custom Clang without compiler-rt will fail at link time.
Run
Connect your ELRS TX module via USB, then run:
Windows:
./build/main.exe COM3
Linux / macOS:
./build/main /dev/ttyUSB0
Architecture
celrs/
crsf.h / crsf.c CRSF protocol: CRC8, frame parse/build
serial.h / serial.c Serial port abstraction (Win/POSIX)
logger.h / logger.c Level-filtering logger
log_write.h/.c stdout log sink
main.c Entry point — demo heartbeat + read
tests/
test_crsf.c CRSF CRC, parse, build tests
test_serial.c Serial open/close/stub tests
test_logger.c Logger level-filtering tests
deps/
FindUnity.cmake Fetches Unity v2.6.1 via ZIP
FindCMock.cmake Fetches CMock v2.6.0 via ZIP
CRSF Protocol
CRSF (Crossfire Serial Protocol) is the serial protocol used by ELRS for communication between ground station and TX/RX modules.
Frame format
+------+------+------+------+-------+-------+
| 0xC8 | dest | src | type | size | ... | CRC |
+------+------+------+------+-------+-------+
1 byte 1B 1B 1B 1B N B 1 byte
- Header: Always
0xC8 - Destination: Target device address
- Source: Sender device address
- Type: Frame type (heartbeat, RC channels, telemetry, etc.)
- Size: Payload length in bytes
- Payload: Frame-specific data
- CRC: CRC8-CCITT over dest+src+type+size+payload
Common device addresses
| Address | Device |
|---|---|
| 0x00 | FC Broadcast |
| 0x10 | Flight Controller |
| 0x80 | TBS Ground Station |
| 0xEA | Custom Module |
| 0xDD | RC Device |
Common frame types
| Type | Name |
|---|---|
| 0x01 | RC Channels Packed |
| 0x02 | Packet Link Telemetry |
| 0x03 | Heartbeat |
| 0x08 | Device Info |
| 0x09 | Parameter List |
| 0x17 | MSP Read |
| 0x18 | MSP Write |
Adding a new module (TDD workflow)
1. Write the header
// celrs/telemetry.h
#pragma once
#include <stdint.h>
typedef struct {
int16_t rssi;
uint8_t link_quality;
int16_t tx_power;
} telemetry_data;
int telemetry_parse(telemetry_data* out, uint8_t const* buf, size_t len);
2. Write a failing test
// tests/test_telemetry.c
#include "unity.h"
#include "celrs/telemetry.h"
void setUp(void) {}
void tearDown(void) {}
void test_parse_rssi(void) {
telemetry_data data;
uint8_t buf[8] = { /* ... */ };
TEST_ASSERT_EQUAL_INT(0, telemetry_parse(&data, buf, sizeof(buf)));
TEST_ASSERT_EQUAL_INT16(-42, data.rssi);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_parse_rssi);
return UNITY_END();
}
3. Register the test in tests/CMakeLists.txt
add_executable(test_telemetry test_telemetry.c)
target_include_directories(test_telemetry PRIVATE "${CMAKE_SOURCE_DIR}")
target_link_libraries(test_telemetry PRIVATE celrs_telemetry Unity::Unity)
target_compile_features(test_telemetry PRIVATE c_std_23)
add_test(NAME test_telemetry COMMAND test_telemetry)
list(APPEND TEST_TARGETS test_telemetry)
4. Add a stub, confirm RED, then implement GREEN
Stub celrs/telemetry.c with return -1;, run tests to see the failure,
then implement the real logic and confirm they pass.