-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Fix CRSF telemetry corruption from PR #11025 #11189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: maintenance-9.x
Are you sure you want to change the base?
Fix CRSF telemetry corruption from PR #11025 #11189
Conversation
This commit fixes the critical bugs that caused PR iNavFlight#11025 to be reverted (PR iNavFlight#11139). The original implementation caused telemetry stream corruption by emitting malformed frames when sensors were unavailable. Root Cause: The original PR scheduled telemetry frames unconditionally (if feature compiled in) but frame functions only wrote data when sensors were available. This resulted in frames containing only [SYNC][CRC] instead of the proper [SYNC][LENGTH][TYPE][PAYLOAD][CRC] structure, corrupting the CRSF protocol stream and breaking ALL telemetry (not just new frames). Fixes Implemented: 1. RPM Frame (Bug #1 - CRITICAL): - Added conditional scheduling: only schedule if ESC_SENSOR_ENABLED and motorCount > 0 - Added protocol limit enforcement: max 19 RPM values per CRSF spec - Location: src/main/telemetry/crsf.c:705-707, 337-343 2. Temperature Frame (Bug #2 - CRITICAL): - Added conditional scheduling: only schedule if temperature sources are actually available (ESC sensors OR temperature sensors) - Added bounds checking: prevent buffer overflow beyond 20 temperatures - Location: src/main/telemetry/crsf.c:709-732, 361-381 3. Buffer Overflow Protection (Bug #4): - Added MAX_CRSF_TEMPS constant and bounds checks in loops - Prevents array overflow if >20 temperature sources configured - Location: src/main/telemetry/crsf.c:361, 368, 376 4. Protocol Limit Enforcement (Bug #5): - Added MAX_CRSF_RPM_VALUES constant (19 per CRSF spec) - Clamp motorCount to protocol limit before sending - Location: src/main/telemetry/crsf.c:337, 342-344 Implementation Pattern: Follows the GPS frame pattern: ✓ Conditional scheduling - only schedule if sensor available ✓ Unconditional writing - always write complete frame data when called Testing: - Code compiles successfully (verified with SITL build) - No syntax errors or warnings - All fixes follow existing code patterns in crsf.c Related Issues: - Original PR: iNavFlight#11025 (merged Nov 28, 2025, reverted same day) - Revert PR: iNavFlight#11139 - Investigation: claude/developer/sent/pr11025-root-cause-analysis.md Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
PR Compliance Guide 🔍All compliance sections have been disabled in the configurations. |
| #ifdef USE_ESC_SENSOR | ||
| if (currentSchedule & BV(CRSF_FRAME_RPM_INDEX)) { | ||
| crsfInitializeFrame(dst); | ||
| crsfRpm(dst); | ||
| crsfFinalize(dst); | ||
| } | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: In processCrsf, check the return value of crsfRpm and only call crsfFinalize if data was successfully written to the buffer. [possible issue, importance: 7]
| #ifdef USE_ESC_SENSOR | |
| if (currentSchedule & BV(CRSF_FRAME_RPM_INDEX)) { | |
| crsfInitializeFrame(dst); | |
| crsfRpm(dst); | |
| crsfFinalize(dst); | |
| } | |
| #endif | |
| #ifdef USE_ESC_SENSOR | |
| if (currentSchedule & BV(CRSF_FRAME_RPM_INDEX)) { | |
| crsfInitializeFrame(dst); | |
| if (crsfRpm(dst)) { | |
| crsfFinalize(dst); | |
| } | |
| } | |
| #endif |
| sbufWriteU8(dst, CRSF_FRAME_AIRSPEED_PAYLOAD_SIZE + CRSF_FRAME_LENGTH_TYPE_CRC); | ||
| crsfSerialize8(dst, CRSF_FRAMETYPE_AIRSPEED_SENSOR); | ||
| crsfSerialize16(dst, (uint16_t)(getAirspeedEstimate() * 36 / 100)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Fix the airspeed calculation in crsfFrameAirSpeedSensor by using floating-point division (e.g., 36.0f / 100.0f) to prevent the expression from evaluating to zero. [possible issue, importance: 8]
| sbufWriteU8(dst, CRSF_FRAME_AIRSPEED_PAYLOAD_SIZE + CRSF_FRAME_LENGTH_TYPE_CRC); | |
| crsfSerialize8(dst, CRSF_FRAMETYPE_AIRSPEED_SENSOR); | |
| crsfSerialize16(dst, (uint16_t)(getAirspeedEstimate() * 36 / 100)); | |
| sbufWriteU8(dst, CRSF_FRAME_AIRSPEED_PAYLOAD_SIZE + CRSF_FRAME_LENGTH_TYPE_CRC); | |
| crsfSerialize8(dst, CRSF_FRAMETYPE_AIRSPEED_SENSOR); | |
| crsfSerialize16(dst, (uint16_t)(getAirspeedEstimate() * 36.0f / 100.0f)); |
| sbufWriteU8(dst, 1 + (motorCount * 3) + CRSF_FRAME_LENGTH_TYPE_CRC); | ||
| crsfSerialize8(dst, CRSF_FRAMETYPE_RPM); | ||
| // 0 = FC including all ESCs | ||
| crsfSerialize8(dst, 0); | ||
|
|
||
| for (uint8_t i = 0; i < motorCount; i++) { | ||
| const escSensorData_t *escState = getEscTelemetry(i); | ||
| crsfSerialize24(dst, (escState) ? escState->rpm : 0); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Before writing variable-length frames, validate the computed payload/frame length against CRSF_FRAME_SIZE_MAX/CRSF_PAYLOAD_SIZE_MAX (and remaining sbuf capacity) and clamp counts or abort if it won’t fit. [Learned best practice, importance: 5]
| sbufWriteU8(dst, 1 + (motorCount * 3) + CRSF_FRAME_LENGTH_TYPE_CRC); | |
| crsfSerialize8(dst, CRSF_FRAMETYPE_RPM); | |
| // 0 = FC including all ESCs | |
| crsfSerialize8(dst, 0); | |
| for (uint8_t i = 0; i < motorCount; i++) { | |
| const escSensorData_t *escState = getEscTelemetry(i); | |
| crsfSerialize24(dst, (escState) ? escState->rpm : 0); | |
| } | |
| const uint8_t payloadLen = 1 + (motorCount * 3); // source_id + int24 * N | |
| const uint8_t frameLen = payloadLen + CRSF_FRAME_LENGTH_TYPE_CRC; | |
| if ((payloadLen > CRSF_PAYLOAD_SIZE_MAX) || (frameLen > CRSF_FRAME_SIZE_MAX) || (sbufBytesRemaining(dst) < (1 + frameLen))) { | |
| return false; | |
| } | |
| sbufWriteU8(dst, frameLen); | |
| crsfSerialize8(dst, CRSF_FRAMETYPE_RPM); | |
| crsfSerialize8(dst, 0); | |
| for (uint8_t i = 0; i < motorCount; i++) { | |
| const escSensorData_t *escState = getEscTelemetry(i); | |
| crsfSerialize24(dst, (escState) ? escState->rpm : 0); | |
| } |
|
I did not test this against either EdgeTx or OpenTX, only against my own parser. It needs to be tested with OpenTX and EdgeTX. @error414 if you want to do some testing, that would be great. |
|
OOk, I will test it over weekend. I will have to create fake sensors, I will test my code as well. BTW: in my PR i Use dynamic caluclating of number of temp/rpm sensors const uint8_t MAX_CRSF_TEMPS = 20; |
|
I only have my old X9D+ test radio with me... So a ran some tests with it using OpenTX and TBS CRSF. There was talk on the BF channels about RPM appearing as Vspd.. But its data did not correspond to RPM. Its output was typical of vertical climb speed. |
|
@sensei-hacker thank you for taking that further. I am on a business trip so no way to test things, should be possible next week though. I had a different approach (gismo2004@50f1e82) to not send the frame if there is no data available, but was not able to test it myself and didn't want to push this if I haven't tested myself. :-) @Jetrell if I am not wrong you will not get the data shown on OpenTX as it is missing its implementation. At least there was a commit for EdgeTx to make that sensors available. |
I don't have an EdgeTX radio with me. So I can't test that for a while. But having it not work with OpenTX is okay. |
|
I dont have any device with ESC telemetry, so I used fake sensors. Transmitter: Radiomaster boxer, EdgeTx version from this year it works for me |
Summary
This PR fixes the bugs that caused PR #11025 to be immediately reverted (PR #11139) on November 28, 2025. The original implementation corrupted CRSF telemetry streams by emitting malformed frames when sensors were unavailable, breaking all telemetry (not just the new frames).
Root Cause
PR #11025 scheduled telemetry frames unconditionally (compile-time
#ifdefonly) but frame functions only wrote data when sensors were available (runtime checks). This resulted in frames containing only[SYNC][CRC]instead of proper[SYNC][LENGTH][TYPE][PAYLOAD][CRC], corrupting the CRSF protocol stream.Impact: Receivers lost sync, causing all telemetry to stop working (GPS, battery, altitude, vspeed, ESC sensors).
Changes
1. RPM Frame
ESC_SENSOR_ENABLED && motorCount > 0src/main/telemetry/crsf.c:705-7072. Temperature Frame
hasTemperatureSourceschecksrc/main/telemetry/crsf.c:709-7323. Buffer Overflow Protection
MAX_CRSF_TEMPSconstant and bounds checking in loopssrc/main/telemetry/crsf.c:361, 368, 3764. Protocol Limit Enforcement (Bug #5)
MAX_CRSF_RPM_VALUES(19) and clamping logicsrc/main/telemetry/crsf.c:337, 342-344Implementation Pattern
Follows the GPS frame pattern:
This ensures frames are only sent when valid data is available.
Testing
claude/test_tools/inav/crsf/test_pr11025_fix.shRelated PRs
claude/developer/sent/pr11025-root-cause-analysis.mdFiles Changed
src/main/telemetry/crsf.c- 34 insertions, 5 deletionsAdditional Notes
This fix re-enables the valuable telemetry features from PR #11025 (airspeed, RPM, temperature) while ensuring they don't corrupt the CRSF stream when sensors are unavailable.