sl-firmware created pull request seb/saltylab-firmware#35

sl-firmware
Sat, 28 Feb 2026 21:09:57 -0500

35#feat: CRSF/ELRS RC integration (Phase 2)#

Summary

Protocol choice: implemented from spec (CRSFforArduino requires Arduino framework; Betaflight extraction has deep scheduler dependencies). Protocol derived from CRSF spec, cross-checked against Betaflight src/main/rx/crsf.c.

UART4 PA0=TX / PA1=RX, 420000 baud 8N1, oversampling×8. APB1=54MHz → 418604 baud (0.33% error)

DMA1 Stream2 Channel4 circular 64-byte RX buffer. IDLE interrupt drains at frame end; DMA half/complete callbacks protect against buffer wrap

CRC8 DVB-S2 (poly 0xD5) validated on every frame

Parser: SYNC(0xC8)→LEN→DATA state machine with length sanity check

16-channel unpack: 11-bit extraction from 22-byte payload

Frame 0x16 (RC channels): 16ch unpacked, last_rx_ms + armed updated

Frame 0x14 (link stats): RSSI dBm, LQ%, SNR captured

Channel mapping: CH1[0]=steer (-400..+400), CH5[4]=arm switch

Failsafe: disarm if >300ms without frame, but only after first frame received (USB-only mode unaffected)

RC arm: CH5 rising edge → safety_arm_start(), same ARMING_HOLD_MS interlock; falling edge → cancel/disarm

Steering: CH1 → motor_driver_update() steer_cmd with ramping/headroom already handled

RSSI/LQ telemetry: rssi/lq JSON fields when RC alive; web UI hidden row revealed on first packet

Test plan

Build passes (verified: 7.9% Flash, 1.8% RAM)

With ELRS RX on UART4: last_rx_ms updates, crsf_state.channels valid

CH5 high → arm hold → motors enable after 3s

CH5 low while armed → immediate disarm

CH1 stick left/right → differential steering

Disconnect RX → disarm within 300ms

USB-only mode: no spurious disarm (last_rx_ms stays 0)

JSON stream shows rssi/lq fields; web UI row appears

🤖 Generated with Claude Code