* feat: resolve event mode hop limit
* feat: bake event mode hop limit
* fix: honor event mode hop cap in routing
* docs: expose event mode hop limit preference
* fix: enforce event hop defaults across routing
* docs: clarify event hop override behavior
* refactor: simplify event mode hop preference
* fix: cap equal event hop limit
* feat: isolate event radio profiles
* fix: preserve identity on degraded config boot
* fix: guard event profile storage
* style: align event profile storage names
* fix: discard event profile on normal boot
* refactor: simplify event profile cleanup
* fix: limit inactive profile migration to event firmware
* fix(event): make event-profile capacity check portable across filesystems
The USERPREFS_EVENT_MODE storage preflight called FSCom.totalBytes() and
FSCom.usedBytes(), which only exist on ESP32's LittleFS wrapper. Every other
backend failed to compile once event mode was enabled:
error: 'class InternalFileSystem' has no member named 'totalBytes' (nRF52)
error: no member named 'totalBytes' in 'fs::FS' (Portduino)
This was not caught earlier because the event block is behind
#if USERPREFS_EVENT_MODE, and the existing unit tests only cover the pure
helpers, which compile identically either way.
Add fsTotalBytes()/fsUsedBytes() to FSCommon and implement them per backend:
littlefs v1 traversal for nRF52 (Adafruit_LittleFS) and STM32
(STM32_LittleFS), FSInfo for RP2040, statvfs for Portduino, and the native
methods for ESP32 and nRF54L15. The nRF52/STM32 path reports "full" if the
traversal errors so the capacity check fails safe.
Verified with USERPREFS_EVENT_MODE=1: native-macos test_event_profile_storage
passes 5/5, and heltec-v3, rak4631, rak11310 and rak3172 all build clean.
* fix(event): use std::filesystem for Portduino capacity, bump native-suite-count
Two CI fixes:
- native-windows has no <sys/statvfs.h>, so the Portduino branch of
fsTotalBytes()/fsUsedBytes() broke the Windows build. Switch to
std::filesystem::space(), which is already used under ARCH_PORTDUINO in
HostMetrics.cpp and works on both POSIX and MinGW. Errors report "full"
so the capacity check still fails safe.
- This PR adds test/test_event_profile_storage, taking test/ from 40 to 41
suite directories, which trips the native-suite-count reconciliation check.
* fix(event): scope boot-write deferral to the radio profile, address review
Review feedback:
- Copilot: saveProto()'s boot-write deferral applied to every file, so
loadFromDisk()'s recovery writes (e.g. restoring owner fields into
devicestate) were silently dropped and never retried, because the ctor CRC
baselines are computed after loadFromDisk(). Deferral now applies only to
the radio-profile files, via isRadioProfileFile().
- jp-bennett: eventConfigFromStandard() wrapped a struct copy plus one field
assignment in a header helper with its own unit test. Inlined at its single
call site and removed; the behaviour is covered end-to-end by hardware
validation instead (RAK4631 normal -> event -> normal preserves NodeNum and
public key while swapping LoRa, and restores the original channel
byte-identically).
- jp-bennett: the active-backup encrypted-storage migration ran for normal
builds too, which changed non-event behaviour and contradicted the PR's
stated event-only scope. Scoped to USERPREFS_EVENT_MODE; the adjacent block
already covers the inactive standard backup in event builds.
New tests (all verified to fail under mutation, not vacuous):
- test_event_paths_never_collide_with_standard_or_shared_files: the core
safety property. A path-table slip would make event firmware overwrite the
user's real config, channels or backup, or capture a shared file like
devicestate/nodedb. Nothing asserted this before.
- test_only_radio_profile_files_defer_boot_writes: guards the deferral fix
above so re-widening it fails loudly.
- test_event_reservation_fits_smallest_supported_filesystem: the reservation
is compile-time but must fit filesystems as small as 14 KiB (STM32WL) and
28 KiB (nRF52840). Protobuf growth pushing it past those would silently
stop event profiles persisting - the exact failure mode confirmed by
fault injection on a RAK4631.
Verified with USERPREFS_EVENT_MODE both on and off: 7/7 tests pass, and
rak4631, heltec-v3, rak11310 and rak3172 all build clean.
* fix: preserve event profile storage recovery
---------
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Benjamin Faershtein <benjaminfaershtein@Benjamins-MacBook-Pro-2.local>
* Fix T1000-E QMA6100P I2C probing
* Refactored to make generic
* address copilot comments
* fix(i2c): address copilot comments on QMA6100P scanning and hardware init
- ScanI2CTwoWire: gate bounded QMA6100P probing to addresses 0x12/0x13 only
(Copilot comment: avoid blocking other I2C device detection on nRF52)
Falls back to normal Wire probing for all other addresses, allowing BMM150
and other devices at overlapping addresses to be properly detected.
- Nrf52Twim: suppress cppcheck redundantAssignment on ENABLE register write
(intentional disable→configure→enable pattern for hardware safety; explicit
disable ensures known state before configuration even if already disabled)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
* Nrf52Twim: gate on HAS_QMA6100P; suppress cppcheck redundantAssignment
The TWIM helper is only used by the QMA6100P probing path (T1000-E, the sole
board defining HAS_QMA6100P). It was gated only on ARCH_NRF52, so it compiled
as dead code on every other nRF52 board and was analyzed by the rak4631
cppcheck job -- which failed on a redundantAssignment false positive.
Gate the header and source on HAS_QMA6100P so the file is only built/analyzed
where it is actually used. Also keep an inline suppression on the ENABLE
re-assignment (a required volatile disable->reconfigure->enable sequence) for
the case where the T1000-E build is run through cppcheck locally.
Fixes the rak4631 cppcheck CI failure for PR #10713.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: nomdetom <nomdetom@protonmail.com>
Co-authored-by: Tom <116762865+NomDeTom@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
* add noise floor
* Sliding window noise floor
* Add getCurrentRSSI() to SimRadio for noise floor support
* Remove sendLocalStatsToPhone call from runOnce
* Change noise floor to int32_t type
* Use int32_t for RSSI sample storage in noise floor
* Remove float cast from noise floor assignment
* Fix Copilot review issues: fix noise floor logic, types, and null pointer
- Use robust busyTx/busyRx checks instead of simple isReceiving check
- Initialize noiseFloorSamples to NOISE_FLOOR_MIN instead of 0
- Move noise_floor assignment inside null check to prevent potential crash
- Change getNoiseFloor() and getAverageNoiseFloor() to return int32_t
- Fix RSSI validation to check for positive values (rssi > 0)
- Fix format specifier from %.1f to %d for int32_t
- Update comments to accurately reflect the sampling logic
* Fix RSSI condition to include zero value
* Change noise floor initialization to zero
* Disable noise floor for LR11x0 chips: getRSSI(bool) unsupported
* Remove updateNoiseFloor call from onNotify to avoid radio queue overflow
Per PR review feedback, calling updateNoiseFloor() in onNotify() for every
ISR event (ISR_TX, ISR_RX, TRANSMIT_DELAY_COMPLETED) can cause the LoRa
radio queue to get full. The noise floor sampling still happens in
startReceive() and after transmitting.
* fix lr11x0 current rssi
* Address noise floor review comments
* Address Copilot SimRadio noise floor comments
* Fix RadioLibInterface formatting
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>