FIX: BEEPER_USB suppression when battery present and configurator active - #14976
Conversation
|
Do you want to test this code? You can flash it directly from the Betaflight App:
WARNING: It may be unstable. Use only for testing! |
WalkthroughFixes two gaps in the BEEPER_USB (ON_USB) suppression option by replacing battery-state checks with MSP-configurator-active checks in beeper logic, ensuring piezo and DShot beacon remain silent when USB configurator is active. Includes comprehensive unit test suite for beeper module with test stubs. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
nerdCopter
left a comment
There was a problem hiding this comment.
- approving untested pending/deferring proper testing.
|
I am still able to activate the beeper mode. |
|
@mituritsyn - please be more specific - which beeper modes can be activated and under which circumstances. |
bradselph
left a comment
There was a problem hiding this comment.
Reviewed and independently verified. This fix is correct. LGTM.
I independently audited issue #14975 and arrived at the exact same two changes before discovering this PR — which gives me high confidence in the
correctness.
Gap 1 (beeper() line 260): The original condition getBatteryState() == BATTERY_NOT_PRESENT meant BEEPER_USB only worked on USB-powered bench setups —
completely useless for the real-world case of tuning with a flight battery connected. Replacing it with mspSerialIsConfiguratorActive() correctly keys off
the actual configurator connection state, which is what the user intent behind "ON_USB" always was.
Gap 2 (beeperUpdate() line 446-448): The AUX-triggered DShot beacon path had zero awareness of BEEPER_USB. The new guard !((dshotBeaconOffFlags &
BEEPER_GET_FLAG(BEEPER_USB)) && mspSerialIsConfiguratorActive()) is structurally consistent with how the RX_LOST path (line 438) already suppresses
beacons when the configurator is connected. Good symmetry.
blckmn
left a comment
There was a problem hiding this comment.
Good fix. The logic change from battery-state proxy to direct configurator-active check is the right approach, and the unit tests cover the regression well.
I am able to activate beeper mode from modes tab with tx switch. |
|
@mituritsyn added new commit - please verify |
|
|
the latest version, beeper mode still can be activated versionBetaflight / STM32F405 (F405) 2026.6.0-alpha Mar 19 2026 / 12:29:05 (def9380) MSP API: 1.48beeperDisabled: ON_USB |
def9380 to
7083c1e
Compare
|
@mituritsyn - can you build the PR locally - to avoid cloud build caching issues ? As covered in the analysis, this path (beeperUpdate → The gap 3 fix we just applied wouldn't change this specific scenario since the The most productive next step is asking mituritsyn the two questions from the
Without that info we're guessing. |
|
@mituritsyn ON_USB is no longer working / replaced with a check for MSP activity. Perhaps should include a check for |
7083c1e to
1a53bff
Compare
I've run into this issue a couple of times, so now I always try to compare the hash of the latest commit with the firmware version
I'm talking about the DSHOT beeper, since the problem originally stemmed from a bug affecting the DSHOT functionality.
give me few minutes to test it |
|
Last commit 1a53bff. I can still enable the dshot beep with the USB connected.
I don't think that should make a difference. In fact, there should be a “beeper” mode, and restrictions should apply to its activation just as they do to “arm.” The specific method used to implement this functionality (DSHOT or piezo) should simply follow this logic.
|
|
@SupaflyFPV , maybe i recall wrongly, but hadn't you solved this? or was it not merged? or maybe i'm wrong. |
as I understand it this is an update to my PR, in changing the gate to supress the buzzer...my version stopped buzzer activity on usb connection broadly, and this PR stops it on connection to Configurato - is that correct @coderabbitai? |
|
Tip For best results, initiate chat on the files or code changes.
Your PR This PR
The key semantic difference is:
Whether 🧠 Learnings used |
|
tbh I prefer my setup because it means you can work on your quad with usb connected and the buzzer wont go off when you power, generally quite useful if you need quick changes without having to power a radio....without having to faff with the configurator if you don't want to. But that's just me. This is not a hill I'm willing to die on if others prefer it changed. |
|
@coderabbitai - please update #14976 (comment) and take it to account analyses in #14860. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chainI need to fetch the specific issue 🏁 Script executed: Length of output: 150954
Updated analysis for this PR ( Relationship Between PR
|
| Path | Fixed by | Mechanism |
|---|---|---|
| Piezo beeper (battery present) | PR #14976 |
mspSerialIsConfiguratorActive() replaces getBatteryState() == BATTERY_NOT_PRESENT |
| DShot RX_SET (AUX switch) | PR #14976 |
New BEEPER_USB && mspSerialIsConfiguratorActive() guard in beeperUpdate() |
| DShot RX_LOST (failsafe) | PR #14869 |
mspSerialIsConfiguratorActive() replaces usbCableIsInserted() in failsafe.c |
🧠 Learnings used
Learnt from: ledvinap
Repo: betaflight/betaflight PR: 14578
File: src/main/telemetry/jetiexbus.c:547-556
Timestamp: 2025-08-12T18:26:17.016Z
Learning: For Betaflight Jeti ExBus backport PR `#14578`: ledvinap prefers to keep the PR focused on backporting fixes from iNavFlight rather than expanding into broader architectural improvements. Additional improvements to TX/RX state machine should be handled in separate PRs.
Learnt from: nerdCopter
Repo: betaflight/betaflight PR: 0
File: :0-0
Timestamp: 2026-01-15T15:55:17.785Z
Learning: For betaflight/betaflight reviews, nerdCopter prefers minimizing nitpicks. Avoid purely cosmetic suggestions (e.g., Unicode vs ASCII, redundant null checks) and focus comments on functional correctness, cross-target build safety, and measurable maintainability wins.
Learnt from: nerdCopter
Repo: betaflight/betaflight PR: 0
File: :0-0
Timestamp: 2025-10-29T16:03:23.891Z
Learning: In Betaflight firmware reviews, do not suggest adding DEBUG_PRINTF, logging, or diagnostic output. Betaflight follows embedded systems best practices with silent error handling using return codes and graceful fallbacks. Debug output was explicitly removed per Betaflight standards (see commit 12b2813ac in PR `#14733`).
Learnt from: pzencar
Repo: betaflight/betaflight PR: 14984
File: src/main/cli/settings.c:1277-1278
Timestamp: 2026-03-11T20:27:37.179Z
Learning: In Betaflight PR `#14984` (vbat sag compensation), when `vbat_sag_target >= vbat_sag_max_voltage`, `vbatRangeToCompensate` in `mixer_init.c` remains 0 because the assignment is guarded by `vbatFull > targetVoltage`. This is a safe, intentional no-op — no explicit cross-parameter validation is required, though `validateAndFixConfig()` in `src/main/config/config.c` is the appropriate place to add any such guard in the future.
Learnt from: blckmn
Repo: betaflight/betaflight PR: 0
File: :0-0
Timestamp: 2026-03-20T17:48:49.927Z
Learning: PR `#15016` (betaflight/betaflight) adds an ESP32-S3 platform scaffold with intentional stub driver implementations (ADC, I2C, SPI, DMA, EXTI, IO, UART, USB VCP, PWM, DShot, LED strip, config flash, etc.). All stubs and TODO comments are deliberate placeholders. Real implementations will be added in a follow-up PR. Do not flag unimplemented stubs or TODO items in this PR or its direct follow-ups as issues.
Learnt from: Quick-Flash
Repo: betaflight/betaflight PR: 0
File: :0-0
Timestamp: 2026-03-24T04:14:07.129Z
Learning: In Betaflight PR `#15024` (`remove_int_yaw` branch), the yaw PID integral term upscaling (`pidRuntime.pidCoefficient[FD_YAW].Ki *= 2.5f` in `src/main/flight/pid_init.c`) being made unconditional is intentional. Previously this scaling was skipped only when `use_integrated_yaw` was enabled; with integrated yaw removed entirely, the scaling now always applies on the yaw axis. Quick-Flash (author) confirmed this is the correct and expected behavior.
Learnt from: Quick-Flash
Repo: betaflight/betaflight PR: 14772
File: src/main/drivers/usb_cdc_debug.h:1-15
Timestamp: 2025-11-24T23:28:08.347Z
Learning: In Betaflight, compile-time opt-in debug features (e.g., USE_USB_CDC_DEBUG) that are explicitly intended for development and testing purposes are acceptable, even though diagnostic output in production code was removed. The restriction on DEBUG_PRINTF and logging applies to production builds, not to dev-only features that developers manually enable during testing.
Learnt from: pzencar
Repo: betaflight/betaflight PR: 14984
File: src/main/flight/mixer_init.c:371-381
Timestamp: 2026-03-14T11:41:05.935Z
Learning: In Betaflight PR `#14984` (vbat sag compensation), `mixerRuntime.vbatTarget` is intentionally NOT clamped to `mixerRuntime.vbatFull` in `mixerInitProfile()` in `src/main/flight/mixer_init.c`. Setting `vbat_sag_target` above `vbat_sag_max_voltage` is a supported use case for the throttle compensation path (`applyVBatSagThrottleScale`): it causes a persistent throttle boost even at full charge (e.g., 4S feel on 3S, or HV feel on non-HV packs). Do not flag the absence of a `vbatTarget > vbatFull` clamp as a bug.
Learnt from: pzencar
Repo: betaflight/betaflight PR: 14984
File: src/main/config/config.c:277-280
Timestamp: 2026-03-14T11:48:47.470Z
Learning: In Betaflight PR `#14984` (vbat sag compensation), `vbat_sag_target` is explicitly allowed to exceed `vbat_sag_max_voltage` in `validateAndFixConfig()` in `src/main/config/config.c`. This is intentional: setting target above max_voltage enables a persistent throttle boost (e.g., 4S feel on 3S, or HV feel on non-HV packs). No cross-parameter clamping of `vbat_sag_target` against `vbat_sag_max_voltage` should be added.
Learnt from: demvlad
Repo: betaflight/betaflight PR: 14350
File: src/main/fc/rc_modes.c:59-59
Timestamp: 2026-04-01T05:29:05.919Z
Learning: In betaflight/betaflight, `rc_modes.h` line 46 documents a mandatory convention: whenever a new flight mode (boxId) is added before `BOXID_FLIGHTMODE_LAST` in the `boxId_e` enum, the `PG_REGISTER_ARRAY` version for `modeActivationConditions` (PG_MODE_ACTIVATION_PROFILE) in `src/main/fc/rc_modes.c` MUST be incremented. This resets the RC modes configuration on upgrade and is intentional. Do not flag such PG version bumps as unnecessary when they accompany a new flight mode addition.
Learnt from: Quick-Flash
Repo: betaflight/betaflight PR: 15056
File: src/main/common/filter.c:169-193
Timestamp: 2026-04-05T18:12:04.711Z
Learning: In betaflight/betaflight PR `#15056`, the Chamberlin SVF Butterworth lowpass (`butterworthFilterUpdate` using `f = 2*sin(pi*freq*dt)`) is only stable for filterFreq < ~0.172/dt (~34% of Nyquist). Quick-Flash agreed to switch to the TPT (Topology-Preserving Transform) form using `g = tan(pi*freq*dt)` for the butterworth filter, which is stable all the way to Nyquist. The notch SVF implementations (`notchUpdate`/`rpmNotchUpdate`) already use `f = sn/cs` (i.e., tan), so they are already TPT-stable. Only `butterworthFilterUpdate/Apply` in `src/main/common/filter.c` needs the TPT conversion.
Learnt from: haslinghuis
Repo: betaflight/betaflight PR: 0
File: :0-0
Timestamp: 2025-10-06T14:46:51.765Z
Learning: For the betaflight/betaflight repository, automatically enhance minimal or missing PR descriptions to include comprehensive information (What Changed, Why This Change, Testing) while preserving any existing content, images, and auto-generated summaries. Users often rely on AI-generated summaries instead of writing descriptions.
Learnt from: Quick-Flash
Repo: betaflight/betaflight PR: 15056
File: src/main/common/filter.c:204-215
Timestamp: 2026-04-02T05:49:19.619Z
Learning: In the betaflight/betaflight codebase, Nyquist-frequency clamping (e.g., `filterFreq = MIN(filterFreq, 0.499f / dt)`) for SVF notch filters (`notchUpdate`, `rpmNotchUpdate` in `src/main/common/filter.c`) should be applied at the call sites (e.g., `dynNotchUpdate`, `rpmFilterUpdate`, PID/gyro init paths), not inside the FAST_CODE update functions themselves, to avoid adding branches to the hot path. Quick-Flash explicitly prefers this pattern for performance reasons.
Learnt from: Quick-Flash
Repo: betaflight/betaflight PR: 15056
File: src/main/common/filter.c:204-215
Timestamp: 2026-04-02T05:54:17.869Z
Learning: In the betaflight/betaflight codebase, Nyquist-frequency clamping for SVF notch filters (`notchUpdate`, `rpmNotchUpdate` in `src/main/common/filter.c`) is already handled at the call sites and does NOT need to be added inside the FAST_CODE update functions. The RPM filter call site uses `constrainf((harmonic+1) * getMotorFrequencyHz(motorIndex), rpmFilter.minHz, rpmFilter.maxHz)` before calling `rpmNotchUpdate`, and the dynamic notch internal logic prevents frequencies from approaching Nyquist. Do not flag the absence of in-function Nyquist clamping in these SVF notch update functions as a bug.
Learnt from: haslinghuis
Repo: betaflight/betaflight PR: 0
File: :0-0
Timestamp: 2025-11-18T23:49:48.929Z
Learning: When reviewing changes to the Betaflight codebase that add entries to statically-sized arrays (like I2C pin mappings in bus_i2c_atbsp_init.c), always verify that the number of new entries doesn't exceed the array size limit defined by constants like I2C_PIN_SEL_MAX in the corresponding header files, and flag potential array overflow issues before they cause compilation errors.
Replace getBatteryState() == BATTERY_NOT_PRESENT with mspSerialIsConfiguratorActive() in the beeper() piezo path so BEEPER_USB is honoured regardless of battery state. Add BEEPER_USB + mspSerialIsConfiguratorActive() guard to the AUX-switch-triggered DShot beacon (RX_SET) path in beeperUpdate(). Add beeper_unittest with 10 tests covering the BEEPER_USB truth table. Fixes betaflight#14975
active before allowing BEEP_ON during sequence playback.
beeper_off_flags (where the user sets BEEPER_USB) instead of dshotBeaconOffFlags. This matches the piezo path in beeper() and the sequence playback guard.
170f7ac to
564b978
Compare
# Conflicts: # src/test/Makefile
Fresh gap analysis (independent re-review)Re-reviewed the current branch state ( F1 —
|
beeperUsbSuppressed() previously keyed solely off mspSerialIsConfiguratorActive(), a 5s MSP-activity proxy. That had two gaps: - any break in configurator MSP polling >5s un-muted both the piezo and the DShot beacon while still physically on USB - the suppression never consulted actual USB state, so the "ON_USB" flag no longer reflected its name OR-in usbCableIsInserted() (the literal USB detect pin, valid even at boot and immune to polling gaps), keeping mspSerialIsConfiguratorActive() as a fallback for targets without a USB detect pin and for wireless MSP links. As a side effect the gyro-calibrated boot beep, which routes through beeper(), is now correctly suppressed on USB. Adds a usbCableIsInserted() test stub and two tests covering USB-cable-present suppression and the no-flag pass-through.
The power-on beep in init.c drives BEEP_ON directly, bypassing beeper()/beeperUsbSuppressed(), so it sounded on every USB boot even with BEEPER_USB configured. MSP is not active this early, but usbCableIsInserted() is already valid, so honour BEEPER_USB here directly. The gyro-calibrated boot beep needs no change: it routes through beeper() and is covered by F1.
The two DShot beacon paths in beeperUpdate() use different USB-suppression criteria by design: RX_LOST (lost-model finder) is silenced whenever a configurator is attached regardless of BEEPER_USB, while RX_SET (user-triggered via AUX) is only silenced when the user set the BEEPER_USB flag. Comment the rationale so the difference is not mistaken for the wrong-flag-field bug this series fixed.
The BeepOn step in beeperUpdate() re-checks beeperUsbSuppressed() so a sequence queued while unsuppressed is still silenced if USB suppression engages before it plays (e.g. the configurator reconnects during a >5s MSP polling gap). This path was previously untested. Adds a positive control plus the queue-then-suppress regression test.
BEEPER_USB has a NULL sequence in beeperTable, so beeper(BEEPER_USB) returns without producing any sound. One call also immediately preceded systemResetToMsc() (the system resets before any tone could play). Remove both dead calls. beeper(BEEPER_BLACKBOX_ERASE) is unaffected, so the beeper.h include remains in use.
|
Thanks for the independent audit — much appreciated, and great that you converged on the same two gaps from #14975. One heads-up: both of those approvals were against an earlier revision ( Gap 1 (battery-state → configurator-active). Still correct in spirit, but the exact condition you praised has moved into a single helper and gained a term (F1): beeperUsbSuppressed() =
(beeper_off_flags & BEEPER_USB)
&& (usbCableIsInserted() || mspSerialIsConfiguratorActive());The configurator-active case you validated still suppresses (it's an OR), so this is a superset, not a regression. The one new behavior to re-bless: physically on USB with the configurator closed/idle + Gap 2 (the DShot beacon guard). This is the important one: the snippet you marked as good symmetry — !((dshotBeaconOffFlags & BEEPER_GET_FLAG(BEEPER_USB)) && mspSerialIsConfiguratorActive())— was actually the bug. It reads Net: no functional regression vs. your review; current HEAD is strictly more correct on Gap 2 and a superset on Gap 1. A fresh pass over the F1 |

FIX: BEEPER_USB suppression when battery present and configurator active
TL;DR
BEEPER_USB("ON_USB") is meant to silence the beeper while the board is connected via USB. Over the years the detection heuristic was rewritten repeatedly, and the logic ended up duplicated across three sound paths reading different flag fields. This PR unifies the suppression behind a singlebeeperUsbSuppressed()helper, fixes the confirmed user-reported DShot-beacon bug, and closes a set of follow-up gaps (F1–F5) found in a fresh re-review.History of BEEPER_USB
The ON_USB beeper flag has a long history of bugs going back to 2016, each fix introducing a new detection heuristic that broke a different scenario:
1. Original feature (2016) —
8129f47c6Added
beeper -ON_USBto let users silence the piezo when powered via USB. Detection used raw voltage:feature(FEATURE_VBAT) && (batteryCellCount < 2). Only affected thebeeper()entry point; DShot beacons did not exist yet.2. Battery init race (2017) — PR #4121 (
0e19f7701)Bug: gyro calibration beep was suppressed on battery power because battery detection hadn't finished yet (cell count still 0 during init). Fix changed the check to
getBatteryState() == BATTERY_NOT_PRESENTand added aBATTERY_INITstate. (Issues #3901, #4107)3. Off-by-one in flag macro (2018) — PR #6062 (
52b8fa531)Bug: copy-paste error used
BEEPER_GET_FLAG(BEEPER_USB - 1)instead ofBEEPER_GET_FLAG(BEEPER_USB), so the flag never matched. One-line fix.4. DShot beacon suppression (2025) — PR #14869 (
1d95d67b6)Bug: DShot RX_LOST beacon fired while connected to configurator. The old check used
usbCableIsInserted()which was too coarse — it also blocked legitimate field-retrieval beacons when USB was connected but idle. Fix introducedmspSerialIsConfiguratorActive()(5-second MSP activity timeout) for the RX_LOST beacon path.5. Piezo still sounded with battery present (2025) — PR #14976 (
1145883a2)Bug: the piezo
beeper()guard still usedgetBatteryState() == BATTERY_NOT_PRESENT. When a battery was connected AND the configurator was active, the check failed and beeps passed through. Fix replaced the battery check withmspSerialIsConfiguratorActive()inbeeper(), and added a USB guard to the DShot beacon RX_SET path.6. DShot beacon checked wrong flag field —
564b978ac/45bfc1a00Bug: the DShot beacon RX_SET guard from step 5 checked
dshotBeaconOffFlagsforBEEPER_USB, but the user sets it inbeeper_off_flags. Also: sequence playback had no USB check, and the same inline logic was duplicated in three places.Root cause: USB suppression logic was scattered and inconsistent
The USB suppression check (
beeper_off_flags & BEEPER_USB && mspSerialIsConfiguratorActive()) was duplicated inline across three paths, with no single source of truth. This led to two bugs:dshotBeaconOffFlagsinstead ofbeeper_off_flags. The user setsBEEPER_USBinbeeper_off_flags, so the DShot check never matched.beeperUpdate()played it without re-checking USB suppression.Initial fix:
beeperUsbSuppressed()helperExtracted a single helper function as the sole owner of the suppression logic, used consistently in all three sound paths:
beeper()— primary defense: prevents queuing new sequences + silences existing statebeeperUpdate()sequence playback — secondary defense: catches sequences queued during MSP timeout gapsUser feedback (2026-04-25): BOXBEEPERON via TX switch still sounds
mituritsyn reported activating beeper mode from the modes tab with a TX switch. Confirmed running the fix branch; sound source was the motors (DShot ESC beacons), not the piezo. The DShot beacon path was checking
dshotBeaconOffFlagsforBEEPER_USBwhile the user sets it inbeeper_off_flags. ThebeeperUsbSuppressed()refactor eliminates this class of bug — all paths now read from the same flag field.Fresh gap analysis (independent re-review)
The branch was re-reviewed from scratch rather than trusting the existing write-up. The refactor was confirmed correct (the wrong-flag-field bug is genuinely gone), but the re-review surfaced five further gaps — all now fixed in this PR.
beeperUsbSuppressed()keyed solely offmspSerialIsConfiguratorActive()(a 5 s MSP-activity proxy). Any break in configurator polling > 5 s un-muted both the piezo and the DShot beacon while still physically on USB, and the "ON_USB" flag no longer reflected actual USB state.usbCableIsInserted()(literal USB-detect pin, valid at boot and immune to polling gaps), keepingmspSerialIsConfiguratorActive()as a fallback for targets without a detect pin and for wireless MSP links.fc/init.cdrivesBEEP_ONdirectly, bypassingbeeper(), so it sounded on every USB boot.BEEPER_USBinline viausbCableIsInserted()(MSP isn't up yet, but USB detect is valid).BEEPER_USB), which looked like the wrong-flag-field bug.beeperUpdate()sequence-playback secondary defense was untested.beeper(BEEPER_USB)incms_menu_blackbox.cis a no-op (NULL sequence); one call also immediately precededsystemResetToMsc().Note on F1: the gyro-calibrated boot beep needed no separate fix — it routes through
beeper()and is closed automatically by F1, sinceusbCableIsInserted()is valid before MSP comes up.Suppression flow (after this PR)
Both diagrams share the single source of truth introduced by the refactor:
Piezo path —
beeper()→beeperUpdate()RX_LOST, RX_SET and every other mode enter through
beeper()and share one suppression gate (primary defense), then a second identical check on playback (secondary defense, F4).flowchart TD A1["RX lost (failsafe.c)<br/>beeper(BEEPER_RX_LOST)"] --> B A2["BOXBEEPERON AUX<br/>beeperUpdate(): beeper(BEEPER_RX_SET)"] --> B A3["other modes<br/>arming / battery / gyro / ..."] --> B B["beeper(mode)"] --> C{"mode == SILENCE<br/>OR beeperUsbSuppressed()<br/>OR BOXBEEPERMUTE ?"} C -- yes --> S["beeperSilence()<br/>no piezo"] C -- no --> D["queue sequence<br/>priority-gated"] D --> E["beeperUpdate(): BeepOn step"] E --> F{"beeper_off_flags has mode ?<br/>OR beeperUsbSuppressed() ?<br/>(secondary defense, F4)"} F -- yes --> G["skip BEEP_ON<br/>LED still flashes"] F -- no --> H["BEEP_ON — piezo sounds 🔊"]DShot ESC beacon path —
beeperUpdate()(USE_DSHOT)The RX_LOST and RX_SET branches use different suppression criteria by design (F3): RX_LOST (lost-model finder) is silenced whenever a configurator is attached regardless of
BEEPER_USB; RX_SET (user-triggered) is only silenced when the user setBEEPER_USB.flowchart TD U["beeperUpdate() — USE_DSHOT"] --> M{"areMotorsRunning() ?"} M -- yes --> X["no beacon"] M -- no --> R1{"activeMode == BEEPER_RX_LOST ?"} R1 -- yes --> L1{"!mspSerialIsConfiguratorActive()<br/>AND dshotBeaconOffFlags lacks RX_LOST ?<br/><b>(no BEEPER_USB check)</b>"} L1 -- yes --> REQ["beacon requested"] L1 -- no --> X R1 -- no --> R2{"BOXBEEPERON active<br/>AND failsafeIsReceivingRxData()<br/>AND dshotBeaconOffFlags lacks RX_SET<br/>AND !beeperUsbSuppressed()<br/><b>(requires BEEPER_USB)</b> ?"} R2 -- yes --> REQ R2 -- no --> X REQ --> G{"past disarm guard delay<br/>AND !isTryingToArm()<br/>AND interval since last beacon ?"} G -- yes --> W["dshotCommandWrite(beacon tone)<br/>motors sound 🔊"] G -- no --> XOther bypass paths (lower priority, not addressed here)
flight/pid.cBEEP_ON, bypassesbeeper()Key files
src/main/io/beeper.c—beeperUsbSuppressed()helper, all three call sites, F1/F3src/main/io/beeper.h—beeperMode_eenum,BEEPER_GET_FLAGmacrosrc/main/fc/init.c— system-init boot beep (F2)src/main/cms/cms_menu_blackbox.c— no-op removal (F5)src/test/unit/beeper_unittest.cc— 14 tests (F1 + F4 additions)src/main/msp/msp_serial.c—mspSerialIsConfiguratorActive()(5 s timeout)src/main/drivers/usb_io.c—usbCableIsInserted()(gated onUSE_USB_DETECT)src/main/pg/beeper.h—beeperConfig_t(beeper_off_flags,dshotBeaconOffFlags)Testing
make TARGET=SITLbuilds clean.src/test/unit/beeper_unittest.cc: 14 tests pass (10 original + F1 ×2 + F4 ×2).cms_menu_blackbox.c(F5) relies on CI —USE_USB_MSCis not present in SITL and the change only removes two no-op statements.Summary by CodeRabbit
Bug Fixes
Tests