* fix(ble): reliably expose and update BLE battery level (BAS)
The Battery Service (0x180F / 0x2A19) is now wired up per the Bluetooth
BAS spec: the Battery Level characteristic always holds a valid 0-100
value and is pushed on change.
- NimBLE: seed an initial level at setup and cache the value on every
update so a READ returns the current level even while disconnected;
only notify when a client is connected.
- Power: mirror the battery level to the Battery Service from
readPowerStatus() on change, so it updates independent of GPS/position
events (previously the only push path was MeshService).
Also fixes two regressions the above would otherwise introduce:
- NimBLE use-after-free: BLEDevice::deinit(true) frees the GATT objects
but left the global BatteryCharacteristic dangling. Several AdminModule
paths (e.g. serial-config entry) deinit BLE while config.bluetooth.enabled
stays true, so the periodic push would deref freed memory. Null the
pointer in deinit().
- NRF52: guard blebas.write() on the nrf52Bluetooth instance so the new
periodic push can't call it before the Battery Service is begun in setup().
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ble): clamp BAS battery level to 0-100 and skip redundant updates
Address review feedback on the Battery Level characteristic (0x2A19):
- Clamp the value to the BAS-mandated 0-100 range at the platform write
boundary (NimBLE seed + update, NRF52 update), so a misbehaving battery
backend can't put an out-of-range value on the characteristic.
- Skip the write/notify when the level is unchanged, so repeated callers
(e.g. MeshService refresh paths) don't emit redundant notifications.
- Simplify Power.cpp to a direct guarded call now that clamping and
de-duplication live at the boundary, which also removes the implicit
int->uint8_t narrowing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* First attempt at Ham Mode implementation
* Simplify licensedOnly check
* Move related code closer together
* TX Disabled if N0CALL, enabled if properly set
* Only disable if callsign is N0CALL, don't enable at this stage.
* Allow users back to Normal mode if they don't pick an ITU region
* Add LTO support for nrf52840 while preserving interrupt handlers
* nrf52840: enable whole-image LTO on all targets via nrf52_base
Moves -flto + the nrf52_lto.py exclusion middleware from the rak4631 env
(771018ca8) up to [nrf52_base], so every nrf52840 target inherits it.
nrf52_lto.py keeps the interrupt handlers out of LTO (framework core +
TinyUSB USBD_IRQHandler) -- they're referenced only from the asm vector
table, so whole-program LTO would otherwise drop them and the chip hangs.
HW-validated: RAK4631 (SX1262, -60KB) and muzi-base (LR1121) both boot and
init their radios. Build-verified on canaryone (fresh board, base-inherited).
Caveat: the RAK "1-Watt" variant's radio does not tolerate global-LTO
(SX126x init fails); it shares the rak4631 binary, so keep that hardware on
src-only or split it into a separate target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* nrf52840: move -fmerge-all-constants to nrf52_base (all targets)
It had been trialed on rak4631 only; it's a general image-wide flag (the
same one stm32 uses globally, ~0.7KB), so move it up to [nrf52_base] next
to -flto so every nrf52840 target gets it instead of just rak4631.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* nrf52_lto.py: normalize path separators for Windows (Copilot review)
get_abspath() returns backslash-separated paths on Windows, so the
"/FrameworkArduino/" / "/cores/nRF5/" substring matches would miss and the
ISR-owning objects would still be LTO'd -> hang on first IRQ. Replace
backslashes with forward slashes before matching. No-op on macOS/Linux.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix directory handling for LTO
* Add post-link guard to check for dropped ISR handlers in nrf52 LTO
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jason P <applewiz@mac.com>
* GPS cache
* trunk and CRLF fix
* Fix GPS.cpp formatting for trunk
* Format GPS.cpp for trunk clang-format
* Show gps model instead of model number
* Potential fix for pull request finding
Useful fix
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Update GPS.cpp
* Update GPS.cpp
* Trunk fix
* Update GPS.cpp
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: add WIZnet W5500-EVB-Pico2 + E22P-868M30S variant
Adds a community variant for the WIZnet W5500-EVB-Pico2 development
board (https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico2)
paired with an external EBYTE E22P-868M30S LoRa module.
This is the hardware twin of variants/rp2350/diy/pico2_w5500_e22 — same
LoRa pinout, same W5500 pin mapping — and reuses its variant.h verbatim
via `-I variants/rp2350/diy/pico2_w5500_e22`. No duplicated pinout file.
Two differences vs pico2_w5500_e22 justify the separate env:
1. Board target: `wiznet_5500_evb_pico2` (2 MB flash) instead of
`rpipico2` (4 MB flash). The WIZnet PCB ships with a smaller Q-SPI
chip than a stock Pi Pico 2. Selecting the correct board makes the
linker fail fast on overflow instead of producing a UF2 that gets
silently truncated when flashed to the device.
2. W5500 PHY is integrated on the PCB (hard-wired SPI0 GP16-20) rather
than supplied as an external module the user wires manually.
The E22P-868M30S uses a single combined RFEN pin (LNA + PA enable)
instead of the older E22-900M30S's split RXEN/TXEN pins. RFEN is held
HIGH at all times via `SX126X_ANT_SW 3`; TX switching still uses the
on-module DIO2 → TXEN bridge through `SX126X_DIO2_AS_RF_SWITCH`.
Build verified: wiznet_5500_evb_pico2_e22p SUCCESS
(RAM 19.2%, Flash 65.6% of 1.5 MB partition / 2 MB chip).
* style(wiznet-evb-pico2-e22p): prettier-format README tables
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
13302 and 13300 had missing module config for slot 2, also the rf switch and voltage info was missing.
Without that the auto setting in the config.yaml will try to use the default lora-hat-rak-6421-pi-hat.yaml and things do not work correctly.
* fix: release Heltec v4 FEM sleep holds
* fix: increase FEM power settle delay
* Fix heltec_V4 documentation
Fixing the heltec_v4 documentation for enabling the PA after sleep.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Add comment for the next guy about why 5ms was chosen.
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* asdf
* Implement SphereOfInfluenceModule for traffic management and eviction tracking
* Implement Sphere of Influence module for dynamic hop limit adjustment and role-based floor
* Update SAMPLING_DENOMINATOR to improve mesh size estimation accuracy
* Add debug logging for scale factor estimation and per-hop node counts in SphereOfInfluenceModule
* Enable variable hop limits and role-based hop floors in Sphere of Influence module
* Respond to copilot review
* Disable variable hop limits and role-based hop floors in Sphere of Influence module
* Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Implement adaptive sampling for unique node ID tracking in Sphere of Influence module
* Add state persistence for Sphere of Influence module
* Enhance Sphere of Influence module with state management and adaptive sampling adjustments
* Refactor hop scaling functionality: remove SphereOfInfluenceModule and introduce HopScalingModule
- Deleted SphereOfInfluenceModule.h, consolidating its responsibilities into the new HopScalingModule.
- Added HopScalingModule.h and HopScalingModule.cpp to manage hop scaling logic, including eviction tracking and sampling-based mesh size estimation.
- Implemented methods for recording evictions and packet senders, estimating scale factors, and computing required hops based on node activity.
- Introduced state persistence for hop scaling parameters to maintain continuity across reboots.
- Enhanced thread safety and modularity by utilizing concurrency features.
* Guard out STM32. Sowwy.
* Refactor HopScalingModule: enhance sampling logic and improve state management
* Add unit tests for HopScalingModule: implement mock database and various test scenarios
* Refactor test output in HopScalingModule tests: replace printf with TEST_MESSAGE for better integration with Unity
* Refactor HopScalingModule logging: replace lastStatusMode with descriptive mode names for improved readability
* Refactor test_main.cpp: change NodeNum variable to static and improve comments for clarity
* Remove unnecessary delay in setup function for improved test performance
* Add missing include for MeshTypes in test_main.cpp
* Refactor HopScalingModule tests: enhance mesh topology scenarios and improve test clarity
* Update HopScalingModule tests: flesh out node scenarios and improve clarity for dense and sparse mesh cases
* Fix politeness factor calculations in HopScalingModule and update related test scenarios for clarity. Remove outdated design doc.
* Enhance HopScalingModule: add sampled estimate for scaling decisions and refactor initial run state management
* Add sample traffic injection for HopScaling tests to enhance sampledEst visibility
* Enhance HopScalingModule: adjust windowFraction calculation for early triggers and improve test output formatting
* Enhance HopScalingModule: add jitter functionality to sampling denominator and update tests for consistent behavior
* Enhance HopScalingModule: implement adaptive sampling denominator adjustment and add reset functionality for tests
* Enhance HopScalingTestShim: add test-only clock and window helpers, update injectSampleTraffic for adaptive sampling, and improve scenario summary output
* Enhance HopScalingModule: add detailed documentation for functions, improve clarity of jitter and sampling logic, and reset functionality in tests
* Enhance HopScalingModule: add evictionEstimate parameter to estimateScaleFactor and update related logging for improved mesh size estimation
* Enhance HopScalingModule: adjust effective rolls calculation for improved accuracy, add eviction estimate logic, responding to all copilot review points
* Implement CompactHistogram for parallel hop scaling sampling
- Added CompactHistogram class to track node hop distances with bitwise sampling.
- Integrated CompactHistogram into HopScalingModule for independent packet sampling.
- Updated NodeDB to feed both the hop scaling module and the new histogram sampler.
- Enhanced HopScalingModule with methods to sample packets for the histogram and retrieve hop distribution statistics.
- Implemented tests for CompactHistogram functionality, including sampling, window rolling, and adaptive denominator scaling.
- Updated existing tests to validate the integration of the new histogram sampling mechanism.
* Enhance CompactHistogram and HopScalingModule: add per-hop distribution functionality, improve time handling for unit tests, and refine test setup for deterministic behavior
* CompactHistogram: add mesh size estimation, improve entry replacement logic, and update logging for per-hop distribution
* Refactor HopScalingModule and CompactHistogram integration
- Removed the suggestedHopFromCompactHistogram function to streamline hop suggestion logic.
- Updated HopScalingModule to directly utilize CompactHistogram's internal methods for hop suggestions and sampling.
- Enhanced logging in HopScalingModule to provide detailed histogram statistics.
- Modified test cases to ensure comprehensive coverage of new histogram behaviors and sampling logic.
- Improved node ID distribution in tests to better exercise sampling mechanisms.
- Ensured that filtering denominators are held for 12 hours before dropping, enhancing stability in sampling.
* Refactor CompactHistogram to support 13-hour activity tracking and introduce politeness regimes
- Updated the bitfield structure to accommodate 13-hour seen tracking.
- Changed the logic in rollHour() to analyze activity over the last 0-2 hours vs. 1-3 hours for politeness factor calculation.
- Introduced three politeness levels: GENEROUS, DEFAULT, and STRICT based on recent activity ratios.
- Adjusted filtering and sampling logic to reflect the new 13-hour tracking period.
- Updated unit tests to validate new behavior and ensure proper functionality of politeness regimes.
* Enhance CompactHistogram and HopScalingModule for improved sampling and decision-making
- Introduced a session-specific hash seed in CompactHistogram to reduce bias in node ID sampling.
- Updated sampling logic to use hashed node IDs instead of raw IDs for filtering and entry management.
- Added histogram rollover tracking in HopScalingModule to ensure proper decision-making after initial data collection.
- Adjusted logging to reflect the active state of the histogram and its comparison with NodeDB advisory hops.
- Enhanced unit tests to validate new sampling logic and memory layout changes.
* Expose hashNodeId for testing in CompactHistogram
* Add mesh trend statistics to CompactHistogram for enhanced activity tracking
* Implement histogram state persistence in CompactHistogram with save and load functions
* Refactor CompactHistogram to improve entry management and enhance rollHour logging
* feat: add HopScalingModule for adaptive hop limit recommendations
Introduces HopScalingModule, a sampled hop-distance histogram that
recommends the minimum hop limit needed to reach ~40 nodes, and
automatically reducing the hops as the mesh grows.
Key design:
- 512-byte packed histogram (128 × 4-byte Record entries) embedded
in a new HopScalingModule.
- Each Record: 16-bit node hash, 3-bit hop distance, 13-bit seen bitmap
- Sampling filter: only nodes where (hash & (denom-1)) == 0 are kept;
denominator doubles on overflow and halves when utilisation is low
- Hourly rollHour(): tallies per-hop counts, walks scaled buckets to
find the minimum hop satisfying TARGET_AFFECTED_NODES (40), applies a
politeness extension based on recent/older activity ratio, shifts all
seen bitmaps, and persists state to /prefs/hopScalingState.bin
- Hop recommendation gated by bootstrap (requires >=1 rollHour before
overriding HOP_MAX)
- NodeDB calls samplePacketForHistogram() on every non-MQTT rx packet
- Module also estimates total mesh size and logs useful information about
mesh characteristics.
Changes:
- src/modules/HopScalingModule.h/.cpp: new module
- src/mesh/NodeDB.cpp: wire up samplePacketForHistogram
- src/mesh/Router.cpp: consume getLastRequiredHop()
- test/test_hop_scaling/: 12-test suite covering all mesh topologies and
anticipated operational requirements
* test: increase run iterations in sparse to dense transition test
* feat: refactor HopScalingModule to use RUNS_PER_HOUR constant and improve logging
* feat: enhance HopScalingModule with filtering denominator management and add tests for state transitions
* refactor: remove CompactHistogram module and related files
* address copilot review comments
* Tweak: packet sampling only lora
* ove role-based hop floor logic and related definitions into the module - keep it in one place.
* Refactor MockNodeDB to use nodeInfoLiteSetBit for MQTT flag setting
* Refactor hop scaling parameters and logic to integer maths and put default values in defaults.h. Small flash size reduction, no functional impact.
* Update unit test preprocessor directives to PIO_UNIT_TESTING for consistency
* refactor: improve test output organization and clarity in hop scaling tests
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix(t5s3-epaper): increase ED047TC1 margins to 16px on all sides
Tested on device — 16px uniform margins look noticeably cleaner than
the previous asymmetric 8–9px values. Canvas shrinks from 944×523 to
928×508 (H_OFFSET_BYTES=2, V_OFFSET_TOP/BOTTOM=16).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(t5s3-epaper): remove EINK_EDGE_LINES calibration diagnostic from ED047TC1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(t5s3-epaper): align TOUCH_THRESHOLD_X with TOUCH_THRESHOLD_Y (40px)
X axis threshold was 60 while Y was 40, causing asymmetric swipe detection.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
* Add low bandwidth conversions to MeshRadio
* Add test cases for new bandwidth conversion values (8/10/16/21/42) and round-trip tests
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Move default frequency (slot) override from RegionProfile to RegionInfo (set per-region).
This is usually set to `0`, but will be especially useful for Ham modes where each region default must fit within a band plan.
Co-authored-by: Copilot <copilot@github.com>