Commit Graph
232 Commits
Author SHA1 Message Date
mikecarper d83fa7cf8e Add signed private fleet controls for filtering and secondary radio
Enroll a private channel and publisher public key on repeaters and room servers. Local Companion fleet sends sign targeted or whole-fleet commands, with expiry, durable replay protection, and a restricted command allowlist. Preserve exact transmitted-reply barriers for secondary-radio changes, bound validation and replies, and qualify crypto, persistence, transport scope, role permissions, and compact-profile exclusions with regression tests.
2026-10-08 16:45:19 -07:00
mikecarper 1e421ff4fc Fix MQTT startup after ESP32 WebConfig provisioning 2026-10-06 19:28:10 -07:00
mikecarper 219e936ef8 Preserve requested ESP-NOW across manual setup and master restore 2026-10-06 11:46:10 -07:00
mikecarper 97f16dedd0 Fix ESP32 Full idle radio teardown and compact AP startup 2026-10-06 11:46:10 -07:00
mikecarper df5e591319 Bound telemetry replies and preserve Sensor send retries 2026-10-06 09:56:00 -07:00
mikecarper ba6e48b5ce Fix Room and Sensor requests and add G3 Sensor builds
Bound ACL replies for encrypted routes, authorize Sensor ACL access using the actual Admin role, and preserve request retries after failed queue admission. Validate stored paths for replies, while keeping Room keep-alives direct-only.

Add both G3 RF-slot Sensor profiles, suppress battery alarms on unavailable measurements, and remove the unsupported Sensor WebConfig capability. Add executable regression coverage and CI checks.
2026-10-06 09:20:05 -07:00
mikecarper 95913c5301 Retry saved WebConfig WiFi when the MQTT worker is absent 2026-10-05 22:10:36 -07:00
mikecarper 1d0ae06800 Keep the console responsive during MQTT shutdown 2026-10-05 19:05:58 -07:00
mikecarper 0b9a80d7ed Avoid repeated missing SPIFFS scans during ESP32 startup 2026-10-05 14:12:44 -07:00
mikecarper 63f4642252 Merge observer OTA channel switching into keymindCascade 2026-10-04 00:26:46 -07:00
mikecarper eeb25a8b5f Merge USB session safety and preserve GPS preference compatibility 2026-10-03 16:12:32 -07:00
agessaman baf01a5c16 Merge branch 'feat/ota-branch-switch' into merge/ota-branch-into-observer-firmware-dev
# Conflicts:
#	examples/simple_repeater/MyMesh.cpp
2026-10-03 15:47:01 -07:00
mikecarper 65512661c3 Fix USB logging session safety and add hourly GPS sync interval 2026-10-03 15:43:35 -07:00
mikecarper 9b82c74275 Merge origin/keymindCascade USB reconnect fixes 2026-10-03 15:37:49 -07:00
agessaman f652e7d185 fix(ota): flash the channel ota update checked, not a later ota branch
The deferred flash re-read the channel preference ~2.5 s after ota update
acknowledged its target; capture it when the update is scheduled.
2026-10-03 15:37:49 -07:00
mikecarper 3add4695cc Harden USB logging and persist watchdog events in management reports 2026-10-03 15:29:19 -07:00
agessaman ee88192acb Merge branch 'feat/ota-branch-switch' into merge/ota-branch-into-observer-firmware-dev
# Conflicts:
#	.github/workflows/build-observer-firmwares.yml
#	examples/simple_repeater/MyMesh.cpp
#	examples/simple_room_server/MyMesh.cpp
#	src/helpers/ESP32Board.cpp
#	src/helpers/ESP32Board.h
#	test/test_config_serializer/test_config_serializer.cpp
2026-10-03 15:24:33 -07:00
Elektr0Vodka 942a19eb63 feat(ota): pass runtime manifest base through otaFromManifest
(cherry picked from commit 0bee84c0546780b2e7147148c32087e92d894ac0)
2026-10-03 15:17:36 -07:00
mikecarper b4c8c0ab23 Fix USB logging ownership, persistence, and reconnect safety 2026-10-03 14:45:57 -07:00
agessaman da6406e92f fix(network): rebuild the DHCP hostname when the node is renamed
The hostname was built once in begin() from the node name, so `set name` left
the node advertising its old name to DHCP until the next reflash. It is now
rebuilt on rename through a new CommonCLICallbacks::onNodeNameChanged() hook,
and begin() shares the same helper.

What this cannot do is rename a live lease. IDF 4.4 states that a hostname
changed after the interface is up "would only be reflected once the interface
restarts/reconnects", and Arduino 2.x's WiFi.setHostname() does not touch a
running netif at all — it writes a static string that the netif reads at
bring-up. Forcing the issue would mean bouncing the link, which costs every
MQTT slot a reconnect; that is not a reasonable price for a rename, so the new
name lands at the next reconnect or boot instead.

Only automatic (Ethernet-preferred) links carry a hostname today, exactly as
before. Giving plain Wi-Fi observers one would change the DHCP identity of the
existing fleet and is a separate decision.
2026-09-19 15:57:45 -07:00
agessaman 25f26c3058 Merge fix/mqtt-review-2026-09-09 into feat/abstract-networking
#54 lands first, so the network abstraction is reworked on top of the
per-slot client ownership model instead of alongside it.

Two resolutions are semantic, not textual:

- Link-transition teardown goes through the ownership API. It called
  softDisconnect() on every started client, which bypassed client_state and
  ignored the typed result. A slot that is still Starting now gets a real
  stop: softDisconnect() returns immediately on a client that is not yet
  connected, so its attempt would otherwise complete against the old route and
  deliver a CONNECTED event indistinguishable from the new one's (F04). On a
  medium switch the old route can still be briefly usable, so that is not
  hypothetical. A connected client keeps the cheap bounded path, and a
  quarantined one is left alone.

- NetworkLink::applyPowerPrefs() adopts WifiPowerSavePolicy. Its local
  `2 ? MAX : NONE` mapping would have reintroduced F11 and read the new stored
  value 3 (explicit `min`) as none. It also applies the setting when the link
  starts already associated, which is the case the bridge used to cover.

The NTP probe from #54 keeps its validation and its one-attempt-per-server
bound, but resolves and gates on the selected link rather than on WiFi, so it
works on an Ethernet-preferred node.
2026-09-19 15:15:07 -07:00
agessaman 16562621a2 fix(mqtt): gate room-server OTA on an unproven stop; reap late acks
- The room server checked canFlashAfterStop() only when the bridge was
  running at OTA time. After a timed-out restart the bridge reads as
  stopped while its unacknowledged task may still own TLS/client state,
  so an OTA could erase and write flash under it. It now refuses while
  the stop is unproven, after first reaping any late ack. The repeater
  already gated unconditionally.
- MyMesh::loop() now reaps a late stop acknowledgement whenever it lands,
  releasing the withheld queue and buffers, and restarts only when a
  resume is pending and the bridge is enabled. Before, a bridge disabled
  during StopUnproven kept those resources until re-enabled or rebooted.
  pollLateStopAck() is public for this.
- The wrapper's destructor no longer stops an already-stopped client:
  destroySlotClients() had just stopped it, so every shutdown logged five
  spurious "esp_mqtt_client_stop failed: ESP_FAIL" errors.

Hardware (Heltec V4, 1 s test stop deadline): restart -> StopUnproven ->
`set bridge.enabled off`; the late ack was reaped ("releasing withheld
resources"), status read "not running", and `set bridge.enabled on`
started cleanly without a second release.
2026-09-19 14:22:00 -07:00
agessaman d180f097ea fix(mqtt): an explicit bridge stop cancels a pending late-ack resume
setBridgeState(false) returned early when the bridge was already down,
so `set bridge.enabled off` after a timed-out restart left the resume
pending and MQTT restarted against the operator's choice once the task
acknowledged. Disabling now clears the pending resume before that early
return, and the resume also requires bridge_enabled. The StopUnproven
log and `get mqtt.status` no longer say only "reboot to recover", since
a late ack now recovers on its own.

Hardware (Heltec V4, 5 live slots, 1 s test stop deadline): a restart
went StopUnproven, the task acknowledged 27 s later and the bridge
resumed with all slots reconnecting; with `set bridge.enabled off` in
between it stayed stopped until `set bridge.enabled on`.
2026-09-19 13:58:49 -07:00
agessaman f50a6a3940 fix(mqtt): resume after a late stop ack; bound the hourly NTP refresh
- A stop that timed out into StopUnproven refused begin(), and only a
  later begin() honoured the task's late acknowledgement. Nothing called
  it, so restartBridge() (any `set mqtt...` restart) or an aborted OTA left
  the observer offline for the rest of the boot. MyMesh now records that a
  start was refused and restarts the bridge once stopAcknowledgedLate()
  reports the ack. The OTA-abort alert no longer claims "bridge resumed"
  when the restart was refused.
- refreshNTP() now runs the validated probe with one attempt per server.
  With two attempts and a 1 s pause it blocked the MQTT task ~18 s every
  hour on networks that drop UDP/123, where the old async SNTP cost
  nothing. Loop comment and docs updated to match.
2026-09-19 11:27:01 -07:00
agessaman 3c39908720 fix(webconfig): read the LAN address after pinning the route
startLanMode() took the caller's IP, read before the route lock. A
Wi-Fi/Ethernet switch in between left WebConfig locked to the new link
while advertising the old link's address. It now reads the selected
link's address after lockSwitching(), as startOTAUpdate() already does.
2026-09-19 10:53:39 -07:00
agessaman 4c683b13b3 fix(network): pre-merge review fixes
- Guard the boot-time link bootstrap to observer ESP32 builds. It ran
  unconditionally in MyMesh::begin(), breaking every non-observer
  repeater/room server build (ESP32 and nRF52).
- Rename NetworkInterface -> NetworkLink (class, accessor, files).
  Arduino-ESP32 3.x ships its own NetworkInterface class and header,
  which broke the ESP32-C6 builds. Drop WiFi.setAutoConnect(), a no-op
  on 2.x and removed in 3.x.
- Refresh stored Wi-Fi credentials every bridge tick so the STA
  reconnect loop picks up `set wifi.ssid` / `set wifi.pwd` without a
  reboot, as the bridge did before the link moved out of it. Skip
  reconnects while the SSID is empty.
- Restore the "WiFi connected: <ip>" / "WiFi disconnected: reason N"
  debug lines the bridge used to print.
- Alert on Ethernet only once it has held a lease this boot or when no
  Wi-Fi is configured; Wi-Fi-only installs of an Ethernet-preferred
  image keep Wi-Fi alerts instead of reporting "Ethernet down".
- Record wifi.setup_complete only for Ethernet LAN onboarding, so
  Wi-Fi builds keep the SSID-based first-boot portal rule.
- Use seq_cst for the route-switch lock/mutation flag handshake.
- Docs: SNMP RSSI sentinel is -127; describe link-return vs medium-switch
  reconnect behavior accurately; note runtime credential pickup.
- Test: unknown keys inside a known /mqtt.json group are ignored, which
  keeps wifi.setup_complete downgrade-safe.
2026-09-19 10:14:16 -07:00
mikecarper 30067cb5cd Split MQTT and ESP-NOW controls in Full ESP32 builds 2026-09-17 01:13:10 -07:00
mikecarper 9a149eaea1 Combine ESP-NOW and MQTT in ESP32 Full builds 2026-09-17 00:37:32 -07:00
mikecarper 4ba26ff4af Add secure management reports and honor GPS off 2026-09-16 20:42:30 -07:00
mikecarper 8ef2d41fb7 Report CLI save failures and validate advert intervals
Roll back failed password and advert changes, propagate identity-store results, and report unsaved common and observer settings. Correct observer filter persistence and KISS OTA capability claims. Add fault-injection and packaging regressions.
2026-09-15 20:05:30 -07:00
mikecarper b847b46ef7 Retry identity startup and harden replay state and permissions
Read identities up to three times before allowing replacement, publish only complete reads, and require new startup keys to be saved with bounded retries.

Use authoritative replay-file metadata throughout persistence and clock recovery. Validate setperm input before narrowing or mutating roles, and add production-path regression coverage.
2026-09-15 17:33:50 -07:00
mikecarper 95aca52b4e Add per-profile TX routing and bridge filtering
Persist Companion TX preferences per contact and channel. Default repeater,
room, and sensor replies to both active TX profiles, with a reply-only force
option for an RX-only secondary profile. Track both reply copies before a
temporary-radio handoff and account for OTA copies under queue backpressure.

Add bridge/crossover filter modes and the third built-in wardriving filter,
plus compact filter CLI syntax, documentation, and CI coverage.

Fix quoted target names being interpreted as slot/key selectors, partial
recovery of invalid saved reply settings, filter suspension during tempradio2,
and unnecessary packet allocation while OTA traffic is throttled.

Validation: 1,477 native tests, 52 Python integration tests, 29 filter UI tests,
and five firmware builds covering ESP32, nRF52, and STM32 passed.
2026-09-15 01:58:43 -07:00
mikecarper 3b6c894ee2 Add two-profile LoRa operation and radio CLI regression coverage
Implement radio2, tempradio2, radioat2 and tempradioat2 across Mesh roles,
with RX-only/RX+TX operation, optional preambles and persistent cross-TX
policy. Keep temporary OTA traffic on its profile by default and maintain
independent retry ownership and backoff for each profile.

Use slow-first receive scanning with 4.8-symbol visits and automatic
preambles rounded up in steps of eight. Preserve pending RX, restore power
saving on exit, and discard work bound to changed or expired profiles.

Restore the infrastructure path.hash.mode setter and report unsupported
extra.sf settings consistently. Add CLI, scheduling, scan and retry tests,
setup documentation, and the V4/XIAO hardware validation results.

Validation: 1,422 native tests, eight KISS tests, 63 final focused profile
tests, 18 checks from the staged source, sanitizer-enabled OTA transfers,
and builds for V4 Mesh roles, Full XIAO Companion and nRF52 T1000-E.
Hardware checks cover reception, cross-TX policy, expiry, reboot and OTA
discovery while receiving main-channel adverts.
2026-09-13 08:25:00 -07:00
mikecarper fa010196b1 Merge PR #7 with reviewed ExpressLRS power and memory fixes
Integrate ExpressLRS TX module support and its stacked ESP32 heap changes.
Use the approved Linkflow calibration (17-30 dBm), preserve the PA drive
and output path after radio recovery, and keep LoRa OTA enabled.

Preserve stored ACLs and filters on allocation failure, release owned
client/filter buffers, service heap OTA contexts on Companions, release
self-serving workspaces after TempRadio, and reset staged-resume state
when a context is released. Keep manual staging and active operations
alive. Account for all moved allocations in the runtime RAM gate.

Validation: 1,393 native cases; radio-power, heap-context, ACL persistence,
shared-queue transfer, display/inbox, radio receive, and memory regressions.
Firmware builds passed for Linkflow, Heltec V2 Companion, T-Beam MQTT
repeater, Heltec V4 R8 MQTT repeater, RAK4631 repeater, and Indicator Full.
Physical verification awaits access to the currently offline lab Pi.
2026-09-10 22:06:16 -07:00
mikecarper d83153e935 Restore clearing all neighbors with an empty prefix
Accept the upstream empty-prefix neighbor.remove command after whitespace normalization while preserving invalid-key checks. Cover repeater and room-server removal paths with regression tests.

Fixes #8
2026-09-10 19:33:54 -07:00
mikecarper d6869b0af9 Default Cascade profiles to CAD off 2026-09-10 17:44:55 -07:00
mikecarper 7dd48ff0e2 Allow authenticated LoRa admins to read paged ACLs 2026-09-10 07:40:34 -07:00
MUSTARDTIGERFPV c6d71b969a Enable the on-demand mOTA context for every classic ESP32 build
Move OTA_HEAP_CONTEXT from two hand-edited envs to a pre-script gated on
build.mcu == "esp32", so every classic ESP32 image gets it and none can drift.
S2/S3/C-series and nRF52 keep the .bss singleton: they have no equivalent
static-DRAM ceiling, so there a guaranteed-present workspace is the better
trade for what is ultimately a recovery path. It has to be a build flag rather
than a header default, because OTA_HEAP_CONTEXT must hold the same value in
every translation unit that sees OtaContext.h - OtaContext.cpp included - and a
header test on CONFIG_IDF_TARGET_ESP32 would depend on include order relative
to sdkconfig.h. The script defers to OTA_SHARED_COMPANION_QUEUE where a Full
Companion recipe already owns the storage.

Fix a gap this exposes in the roles that now use it. Only CLI entry points
acquire the context, so once it was released, a repeater with the temporary
radio profile up would no longer serve or announce its own firmware - LoRa OTA
would look enabled and silently do nothing. ota_service_temp_radio_context()
holds the workspace for exactly the temp-radio window and hands it back
outside it, which is where the saving was coming from anyway. Wired into the
repeater, room server and sensor loops; the Companion keeps its own
acquire-on-host-demand policy, since its context is borrowed from the offline
message queue and must not be taken speculatively.

Static DRAM, classic ESP32 (bytes occupied of a 124,580 region):
  Heltec_v2_repeater                   108,332 -> 70,732
  Heltec_v2_room_server                 83,716 -> 68,300
  Heltec_v2_companion_radio_ble        123,236 -> 107,772  (was failing on main)
  Tbeam_SX1262_repeater                 86,932 -> 71,468
  Tbeam_SX1262_repeater_observer_mqtt  121,132 -> 83,532   (was failing)
2026-09-09 01:24:00 -07:00
mikecarper b9138c13b2 Allow local CLI maintenance across direct connections 2026-09-08 21:44:57 -07:00
mikecarper aa8caa4ed2 Unify device controls and restore the WiFi console 2026-09-08 18:36:23 -07:00
mikecarper 51ce1f8f0d Protect ESP32 HWCDC console sessions 2026-09-04 17:18:54 -07:00
agessaman 9b739a9b43 fix(network): harden Ethernet-preferred lifecycle 2026-09-04 08:42:46 -07:00
mikecarper a8cf4fd12f Fix USB radio stalls and add durable replay timestamp recovery 2026-09-03 23:58:32 -07:00
agessaman 17409e56a0 feat(network): implement hostname configuration for automatic interfaces 2026-09-03 17:16:30 -07:00
agessaman cdde4bf673 feat(mqtt): add link diagnostics and improve network logging 2026-09-02 21:37:30 -07:00
mikecarper 610a88e8d5 Fix nRF52 contact persistence and ExtraFS recovery 2026-09-02 11:41:38 -07:00
agessaman 50e2b2acba feat: add automatic network failover 2026-09-01 21:18:22 -07:00
agessaman 3975bc0b01 feat: abstract MQTT network transport 2026-09-01 16:31:28 -07:00
mikecarper 1629725d7d Add safe ESP32 transports and Indicator UI
Reserve board-specific GPIOs, make ESP-NOW channel/format handling configurable, and coordinate WiFi, BLE, NTP, MQTT, and host actions safely.

Add the Indicator message page, exclusive transport selector, dynamic 480/320 rendering, larger transport typography, pairing cleanup, and corresponding tests and documentation.
2026-08-31 14:29:42 -07:00
mikecarper 9fd580c89e Harden marathon-tested OTA and hardware paths
Add durable TempRadio, ACL, clock, bridge, GPS/I2C, USB logging, and Indicator recovery safeguards together with expanded automation, build/profile contracts, release metadata, and the hardware validation ledger.
2026-08-30 19:26:15 -07:00