telemetry(crsf): move CRSF telemetry rate-limit gate from checkFunc into processCrsf()
AI Generated issue-ticket
Background
This is a post-merge follow-up for PR #15358 (fix for scheduler starvation, see issue #15066),
which added a 50Hz rate-limit gate inside crsfTelemetryUpdateCheck() (src/main/telemetry/crsf.c),
the scheduler checkFunc for TASK_TELEMETRY. The gate uses a local static timeUs_t lastTelemetryCheckTimeUs, with ad-hoc response paths (mspReplyPending, deviceInfoReplyPending)
bypassing the gate so MSP-over-telemetry and device-info replies still drain at inbound frame rate.
This refactor was originally proposed by @haslinghuis across both review rounds on PR #15358,
who identified both edge cases below individually and concluded both "dissolve if the 20 ms gate
is moved into processCrsf() (which already gates on crsfLastCycleTime) instead of gating the
whole checkFunc." @blckmn's later review (https://github.com/betaflight/betaflight/pull/15358#pullrequestreview-4648805312)
independently restated the same suggestion.
Follow-up concurrence: https://github.com/betaflight/betaflight/pull/15358#issuecomment-4915955786
Problem
The current gate has two known residual edge cases, both assessed as non-blocking (self-healing, do not affect flight control):
- Baud renegotiation staleness:
crsfRxUpdateBaudrate()(src/main/rx/crsf.c:687) swaps theTASK_TELEMETRYcheckFunc NULL↔crsfTelemetryUpdateCheckduring CRSF V3 baud negotiation. Thestatic lastTelemetryCheckTimeUsis not reset on reinstatement, which can suppress the first periodic telemetry frame for up to 20ms after a baud renegotiation cycle. - Long-idle staleness: if
telemetryResponsePendingstays false long enough forlastTelemetryCheckTimeUsto go stale relative tocmpTimeUs's signed 32-bit rollover window (~35 minutes), the first check after link reactivation can be delayed by up to one gate interval.
Proposed fix
Move the 20ms rate-limiting decision out of the checkFunc and into processCrsf(), which already
tracks crsfLastCycleTime (currently a local static inside handleCrsfTelemetry(),
src/main/telemetry/crsf.c:1317) and already performs its own interval gate at
src/main/telemetry/crsf.c:1035 (CRSF_CYCLETIME_US / crsfScheduleCount).
Why this needs its own design pass, not a quick patch
crsfLastCycleTimeis not a drop-in replacement forlastTelemetryCheckTimeUs.crsfLastCycleTimeis reset by every ad-hoc path (MSP reply, device-info, displayport clear, displayport chunk) as well as by periodic frames — it means "last time any telemetry activity happened." The checkFunc'slastTelemetryCheckTimeUsonly advances on the periodic path by design, which is what allows ad-hoc traffic to be exempted from the gate. Naively reusingcrsfLastCycleTimefor the checkFunc gate means frequent ad-hoc traffic (e.g. a Lua script polling MSP) would continuously re-arm the periodic gate, potentially suppressing periodic sensor telemetry during heavy ad-hoc use.- Different interval semantics. The checkFunc's gate is a flat
CRSF_TELEMETRY_FRAME_INTERVAL_MAX_US(20ms).processCrsf()'s own gate isCRSF_CYCLETIME_US / crsfScheduleCount, which varies from ~16.7ms to 50ms+ depending on sensor configuration (schedule slot count). Merging the two requires deciding whether 20ms becomes a floor, a replacement, or coexists with the schedule-based interval. - Cross-function state promotion.
crsfTelemetryUpdateCheck()runs before the task wakes;processCrsf()/crsfLastCycleTimerun insidehandleCrsfTelemetry()after the task wakes. Sharing state between them requires promoting a local static to file scope and re-verifying every call site that touches it.
References
- Issue: #15066 (original scheduler starvation report)
- PR: #15358 (fix, branch
fix/crsf-telemetry-rate-limit, headf872a1c05f) - Original proposal: @haslinghuis, PR #15358 review rounds 1 and 2
- Restated by: @blckmn, https://github.com/betaflight/betaflight/pull/15358#pullrequestreview-4648805312
- Concurrence comment: https://github.com/betaflight/betaflight/pull/15358#issuecomment-4915955786
- Relevant symbols:
crsfTelemetryUpdateCheck(),processCrsf(),handleCrsfTelemetry(),crsfLastCycleTime,crsfRxUpdateBaudrate()(src/main/rx/crsf.c:687)
Source: betaflight/betaflight