Commit Graph
1033 Commits
Author SHA1 Message Date
agessaman a3a0a94dc4 feat(mqtt-neighbors): support non-PSRAM observer boards
Neighbors publication was gated on BOARD_HAS_PSRAM. Removing that gate alone
was not enough: the feature built but was inert without PSRAM, because three
allocation sites asked for MALLOC_CAP_SPIRAM (which returns null with no
PSRAM), the bridge's persistent buffer was allocated inside a BOARD_HAS_PSRAM
block, and neighborDiscoverReady() rejected every pass at runtime on
psramFound(). The entry table also did not fit: finishNeighborDiscover put
pubkey_hex[50][65] plus entries[50] on the stack, a 4752-byte frame against
the mesh loop task's 8 KB.

- Gate on MAX_NEIGHBOURS plus PSRAM or an explicit per-variant
  MQTT_NEIGHBORS_WITHOUT_PSRAM opt-in.
- Move the entry table and its hex strings into one heap block sized to the
  pass; the frame drops from 4752 to 304 bytes.
- Prefer PSRAM and fall back to internal DRAM in the mesh-side allocations
  and the ArduinoJson pool; hoist the bridge's persistent buffer out of the
  BOARD_HAS_PSRAM block (psram_malloc already falls back).
- Keep the runtime psramFound() check only where the buffers are sized for
  PSRAM, so a board whose PSRAM failed to init still refuses.
- Size for internal DRAM without PSRAM: 4 KB text buffer and 20 entries per
  publish, keeping the pool to a single block and the peak near 13 KB rather
  than ~35 KB. Oversized tables truncate and report total_neighbors as before.

Enabled on the ESP32-S3 observer envs (Heltec V3/WSL3, RAK3112, Heltec
Tracker v1.1/v2). Left off for the classic ESP32 T-LoRa V2.1-1.6, which is
already limited to one active TLS slot.

Costs ~7.4 KB static DRAM on repeaters and ~9.6 KB on room servers. The
prefs layout is unchanged, so this is neutral for existing devices.
2026-08-03 10:29:54 -07:00
agessaman 34037f202d fix(mqtt-neighbors): budget the JSON pool separately from the publish buffer
ArduinoJson v7 hands out document-pool blocks in fixed 4096-byte chunks, so
the pool is not bounded by the size of the text it serialises to. Budgeting
it at NEIGHBORS_JSON_BUFFER_SIZE starved it once the table grew: a 50-entry
table needs 12541 B of pool against the 10240 B cap, and a starved allocator
sets doc.overflowed(), which makes buildNeighborsMessage return 0 and drop
the entire publish rather than truncating the tail.

Repeaters with roughly 40 or more neighbours therefore published no neighbors
message at all, silently, while smaller tables published normally.

Give the pool its own NEIGHBORS_DOC_POOL_BUDGET and add an explicit
NEIGHBORS_MAX_PUBLISH_ENTRIES cap alongside the existing text-size check.
2026-08-03 10:29:32 -07:00
agessaman 5828a3c563 fix(mqtt-neighbors): report unusable heard ages as null
A neighbour heard before the clock is set carries the firmware's unset-clock
default (1715770351, 15 May 2024). Subtracting that from an NTP-synced clock
published ages of ~806 days for neighbours that had just answered a live scope
query, and a backwards clock step reported 0, i.e. "heard just now".

- finishNeighborDiscover() reports the age as unknown ("heard_secs_ago": null)
  when the stored stamp and the current clock come from different epochs, or
  when the clock has stepped backwards
- handleNeighborDiscoverResponse() re-stamps heard_timestamp on a zero-hop scope
  reply, in both the snapshot and the live table, so entries heal once per
  discovery cycle instead of waiting for the neighbour's next advert
- publish ordering places usable ages ahead of unknown ones so a poisoned stamp
  cannot displace fresh entries when the JSON buffer truncates
- document the null case, and the always-present total_neighbors /
  queried_neighbors / truncated fields the payload sample omitted
- add UPSTREAM_BUGS.md, tracking the monotonic-uptime fix to propose upstream
  plus the unclamped subtraction in the companion and CLI readouts

Containment only: upstream still stamps neighbours from the wall clock at
packet-reception time, which on a cold boot always precedes NTP.
2026-07-31 08:54:39 -07:00
agessaman 612c52132c merge: upstream/dev into observer-firmware-dev (2026-07-30, db232808)
Absorbs 106 upstream commits. Seven files conflicted; the substantive one
was upstream's new JSON ConfigSerializer (PR #2982), which replaces the
binary /com_prefs layout with /prefs.json and makes NodePrefs a
ConfigSerializer subclass.

Prefs migration
- Adopt upstream's ConfigSerializer. writeCommonPrefsImage() and its
  documented offsets (0-294) are deleted, along with the now-unreachable
  saveCommonPrefsImageAtomically()/CommonPrefsFileStore atomic rename path.
- Load order is /prefs.json, then /com_prefs, then /node_prefs. Upstream
  dropped the /node_prefs fallback; it is restored here so devices that
  never advanced past that filename keep their config.
- Legacy files are never removed, so migration cannot destroy its own
  source and a deferred or failed save simply retries next boot.
- /com_prefs is treated as a format migration only, not an "upgrade", so
  it does not trip the bridge.source tx->rx flip on existing nodes.
- The MQTTPrefsAtomicStore legacy gate is retained: the observer tail
  recovered from an old-format file still commits to /mqtt_prefs before
  /prefs.json is written.
- MQTTPrefs and /mqtt_prefs are untouched; savePrefs keeps its save_mqtt
  parameter and now returns upstream's bool.

Fixes to upstream code
- RadioPrefs::structure() bound both "rxgain" and "fem_rxgain" to
  rx_boosted_gain, so radio_fem_rxgain was never persisted. Bound to the
  correct field.
- discovery_mod_timestamp was dropped from structure(); it gates
  'since'-filtered DISCOVER replies and is set on every config change, so
  losing it would silently stop discovery responses after a reboot. Added
  as "disc_mod".

Merge artifacts repaired
- Restored bblanchon/ArduinoJson to the native test env; a clean but wrong
  auto-merge at the lib_deps block boundary dropped it and broke all 19
  host test suites.
- Migrated the fork's WebConfig UITask screens off the removed
  DisplayDriver::Color enum to upstream's UIColor element types.
- Removed duplicate getCADEnabled() definitions in companion MyMesh.cpp
  and simple_sensor SensorMesh.cpp that both sides had added.
- Dropped memset(&_prefs, 0, ...) in the four example meshes; NodePrefs now
  has a vtable. guard gains an initializer that memset used to provide.

Other resolutions
- simple_room_server keeps both the fork's discover.* commands and
  upstream's new room.post.
- docs/payloads.md taken from upstream, undoing content earlier merges had
  reverted (Control data section, split login tables).

Verified: 273/273 host tests pass across native and native_kiss_modem;
Heltec v3 repeater, repeater_observer_mqtt, room_server_observer_mqtt,
sensor, and companion_radio_ble all build clean.
2026-07-30 14:56:23 -07:00
ripplebiz db232808aa Merge pull request #2688 from Che177/feature/room-server-system-posts
room_server: add room.post command for server-originated posts
2026-07-30 13:08:18 +10:00
agessaman 8b16af3830 feat(mqtt): add default_scope to neighbor discovery JSON handling
Enhance the neighbor discovery JSON structure by introducing a
default_scope field, which indicates the region name this node
floods to by default. This change improves clarity in the
neighbor discovery process and aligns with the unscoped flood
behavior when no default region is set. Updates include
modifications to the MyMesh class and related message building
functions to accommodate the new field.
2026-07-29 15:00:32 -07:00
Che177 5a04e060a5 room_server: use standard mesh debug logging 2026-07-28 21:57:42 -07:00
agessaman 52bd27190f feat(mqtt): enhance packet filter functionality with named types
Add support for named packet types in per-slot filters, allowing users
to specify packet types using descriptive names alongside numeric values.
This improves usability and clarity in configuring MQTT slot filters.
Updates include modifications to the parsing logic, WebConfig interface,
and related documentation to reflect the new naming conventions.
2026-07-27 22:29:11 -07:00
Che177 3a9f19c14b room_server: add room.post system posts 2026-07-27 19:50:13 -07:00
liamcottle dd778e2189 fix bluetooth toggle on ui tiny 2026-07-28 03:50:08 +12:00
liamcottle 203fd4e407 refactor rak13800 to new ethernet interface class 2026-07-28 03:18:54 +12:00
liamcottle e672679d6e refactored companion interfaces to allow for multiple active connection modes 2026-07-28 03:18:54 +12:00
liamcottle b4143a4402 rename class 2026-07-28 03:18:54 +12:00
liamcottle 5de857f8f8 initial ch390 ethernet support for thinknode m7 companion 2026-07-28 03:18:54 +12:00
ripplebiz c86e63d2f5 Merge pull request #2982 from meshcore-dev/config-serializer
Config serializer
2026-07-27 17:58:03 +10:00
ripplebiz 301bc10dd3 Merge pull request #3034 from meshcore-dev/ui-color-refactor
UI color refactor
2026-07-27 17:45:12 +10:00
agessaman 3c5b25c2cf feat(mqtt): enhance neighbor discovery JSON handling
Introduce new methods to manage neighbor discovery JSON budget and
entries in MyMesh. This includes tracking the number of queried and
published neighbors, measuring JSON sizes, and handling truncation
when the buffer limit is reached. These improvements optimize the
neighbor discovery process and ensure efficient JSON message
construction for MQTT communications.
2026-07-26 19:10:51 -07:00
agessaman aba571ed7d feat(mqtt): enhance neighbor discovery with request management
Add functionality to manage neighbor discovery requests in MyMesh.
This includes handling the status of requests, updating the state
upon transmission success or failure, and implementing a timeout
mechanism for neighbor discovery queries. The changes improve the
efficiency and reliability of neighbor discovery operations in
MQTT-enabled environments.
2026-07-26 14:03:04 -07:00
Scott Powell f83ad03e5e * T114 display fix 2026-07-24 18:35:56 +10:00
Scott Powell ee79df4c4f Merge branch 'dev' into config-serializer
# Conflicts:
#	test/mocks/Arduino.h
#	test/mocks/Stream.h
2026-07-24 15:06:48 +10:00
Liam Cottle d531a98da1 Merge pull request #2819 from ViezeVingertjes/fix/kiss-modem-usb-backpressure
fix: prevent ESP32 KISS modem stalls under USB backpressure
2026-07-24 11:51:34 +12:00
Scott Powell 56274db4ad * build fixes for old ESP32 boards 2026-07-22 19:52:23 +10:00
Scott Powell b8504e55c6 * refactor of Color mapping in UITasks / DisplayDrivers
* color displays now with a new light theme
2026-07-22 16:03:33 +10:00
agessaman 36272384f3 fix(ota): require clean MQTT stop before flashing 2026-07-21 16:46:02 -07:00
agessaman a71cc23854 fix(room_server): support RX boosted gain
The room server never supported RX boosted gain, while the repeater
does. Three consequences on SX1262/SX1268 boards:

- boosted gain was never applied to the radio at boot, so a room server
  ran in power-saving RX mode while an identical repeater ran boosted -
  a real receive-sensitivity difference with nothing pointing at it
- _prefs.rx_boosted_gain was never initialised (the prefs are memset to
  0 before defaults are set), so it defaulted to off
- 'set radio.rxgain on' replied "Error: unsupported" but CommonCLI had
  already written the value to prefs and saved them, so a stale setting
  survived reboot and was never applied

Mirror the repeater's implementation: initialise the pref default under
the same USE_SX1262/USE_SX1268 and SX126X_RX_BOOSTED_GAIN guards, apply
it during radio init, and override setRxBoostedGain() so the CLI reports
success and takes effect.

Built Heltec_v3_room_server (SX1262) and LilyGo_T3S3_sx1276_room_server
(guards compile out cleanly).
2026-07-21 12:23:20 -07:00
agessaman fbe608d6c0 feat(mqtt): add SNMP support for radio diagnostics and stats
Implement SNMP functionality in MyMesh to provide radio diagnostics
and statistics updates. This includes a new method for formatting
radio diagnostic replies and periodic updates to the SNMP agent
with radio stats, enhancing monitoring capabilities for users
with SNMP enabled.
2026-07-21 11:03:04 -07:00
agessaman bd58d8af5b feat(mqtt): implement deferred OTA update scheduling in MyMesh
Add functionality to schedule deferred OTA updates in the MyMesh class.
This includes a new member variable to track the update time and a method
to initiate the update process, allowing for a smoother OTA experience
by managing timing and state transitions effectively.

Cherry-picked from observer-firmware (76f44d7d). Resolved a conflict in
loop() where the WITH_WEBCONFIG block occupies the same anchor and shares
an identical closing tail; both blocks retained.
2026-07-21 10:32:56 -07:00
agessaman 7b68371a7f feat(alerts): enhance alert test command response for clarity
Improve the response message for the `alert test` command to clarify
that a successful test send does not guarantee automatic alerts are
enabled. This change helps prevent confusion for operators regarding
the status of the alert system after testing.
2026-07-20 19:21:29 -07:00
agessaman 85c0e1de11 feat(mqtt): implement neighbor discovery and management in MyMesh
Add support for neighbor discovery and management in the MyMesh class.
This includes structures for neighbor information, methods for handling
neighbor advertisements, and control data reception. The new features
allow for dynamic neighbor management, enhancing the mesh network's
capabilities. Additionally, the maximum number of neighbors is set to
50 across various platform configurations.
2026-07-20 08:47:37 -07:00
Adam Gessaman 329d7f86c2 Merge pull request #36 from agessaman/webconfig
Implement MQTT neighbors feature with webconfig integration
2026-07-19 23:28:02 -07:00
agessaman 634d98c2f7 feat(mqtt): implement custom JSON document allocator for neighbors 2026-07-19 23:22:55 -07:00
agessaman 34a8220111 feat(mqtt): add neighbor-scope discovery + periodic publish to MyMesh
Port the neighbor-discovery state machine from mqtt-bridge-implementation-flex,
adapted to this branch's MyMesh (WebConfig members shifted the insertion points;
applied by content).

- Two-stage periodic refresh in loop() driven by mqtt_neighbors_interval:
  stage 1 is a zero-hop sendNodeDiscoverReq() (reuses the existing 60s window),
  stage 2 (startNeighborDiscover) fires one anon-regions scope query per heard
  neighbour, then finishNeighborDiscover() builds the table JSON via
  MQTTMessageBuilder::buildNeighborsMessage and hands it to
  bridge->requestPublishNeighbors().
- Peer overlay at NEIGHBOR_DISCOVER_PEER_BASE lets scope-query RESPONSE packets
  from non-ACL neighbours decrypt: searchPeersByHash prepends heard neighbours
  (bounded by MAX_CLIENTS), getPeerSharedSecret derives the secret on the fly,
  and onPeerDataRecv routes both overlay-index and ACL-client-that-is-a-neighbour
  responses into handleNeighborDiscoverResponse.
- Entries ordered most- to least-useful (recent, then stronger SNR) so the JSON
  builder's tail-drop keeps the useful head.
- `discover.scopes` CLI command (manual trigger), with a WITH_MQTT_BRIDGE stub
  replying "requires PSRAM" on non-PSRAM builds.
- Reports schedule to the bridge each loop via setNeighborsSchedule().
- Uses ArduinoJson v7 JsonDocument (not deprecated DynamicJsonDocument).

All gated on WITH_MQTT_NEIGHBORS. Reuses the existing MQTTBridge* member.
Verified: T_Beam_S3_Supreme_SX1262_repeater_observer_mqtt builds [SUCCESS].
2026-07-19 22:39:41 -07:00
agessaman 76a3300cc4 fix(repeater): restore node name at top of home screen
A merge conflict resolution collapsed the `// node name` comment and its
`_display->setCursor(0, 0)` call onto a single line, which commented out
the cursor reset. The node name then rendered at whatever cursor position
was left over from the previous frame (the end of the IP: line), so it
trailed the IP address and wrapped onto the next line instead of appearing
at the top of the home screen.

Split the comment and setCursor back onto separate lines so the name is
drawn at (0, 0) again.
2026-07-19 18:17:21 -07:00
agessaman 25bfac0e93 fix: remove duplicate declarations left by the upstream merge
The 2026-07-19 upstream merge auto-merged two additions into the same class
bodies without conflicting, producing duplicate declarations:

  examples/simple_room_server/MyMesh.h  - getCADEnabled()
  variants/heltec_v4/HeltecV4Board.h    - setLoRaFemLnaEnabled(),
                                          canControlLoRaFemLna(),
                                          isLoRaFemLnaEnabled()

Same class of breakage as the RadioLibWrapper::_cad_enabled and
MyMesh::getCADEnabled() duplicates already fixed in the merge commit. These
survived because the merge was validated with the two prescribed MQTT smoke
builds, and neither of them compiles simple_room_server or the heltec_v4
variant - so CI was the first thing to touch the broken files.

Now verified across ALL 32 observer envs locally, not a sample: 32/32 build.
2026-07-19 15:49:27 -07:00
agessaman 15e3400a28 merge: upstream/dev into webconfig (v1.16.0 base -> 2026-07-19)
First upstream merge since the 2026-06-06 base (191 upstream commits). 14 files
conflicted; resolutions below.

Fleet-critical check (Constraint 1): upstream reordered NodePrefs members
(rx_boosted_gain / path_hash_mode moved to the struct tail) but did NOT change
/com_prefs. Persistence is written field-by-field at explicit offsets, so member
order is in-memory only. Verified the fork's writeCommonPrefsImage() is
byte-identical to upstream's inline writer at every offset (79 pad, 121, 122,
290-294). No migration needed.

Resolutions:
- CommonCLI.h: kept the fork's NodePrefs (superset) and adopted upstream's
  setRxBoostedGain(bool)->bool signature change, which CommonCLI.cpp now uses to
  report unsupported. Corrected a stale comment claiming rx_boosted_gain lives at
  offset 79 (it is a pad; the field is at 290).
- CommonCLI.cpp: kept the fork's legacy /com_prefs migration and the extracted
  writeCommonPrefsImage() call.
- UITask.cpp: three-way merge - upstream's drawTextCentered + powering-off
  screen, plus the fork's WITH_WEBCONFIG portal/reboot screens.
- ESP32Board.cpp, MeshCore.h, platformio.ini: kept both sides (fork OTA additions
  alongside upstream powerOff/enterDeepSleep and Packet.cpp).
- MicroNMEALocationProvider.h: took upstream's claim/release and added the
  _claims member they depend on.
- MyMesh.cpp/.h (repeater + room server): kept the fork's superset defaults.
- Removed duplicate declarations auto-merge produced: RadioLibWrapper::_cad_enabled
  and MyMesh::getCADEnabled().

Verification: native suite 15/15 (incl. upstream's new test_mesh_tables), both
MQTT smoke builds green, ArduinoJson pin check passes. Hardware validation next.
2026-07-19 12:41:29 -07:00
agessaman f68a0fb9e7 feat(ota): broadcast OTA start/fail milestones on the alert channel
An operator who triggers `ota update` via remote management only sees the
command's reply; the actual OTA runs ~2.5s later and reboots on success,
so the start/fail notifications land outside the reply window. Mirror the
key OTA milestones onto the configured alert channel (in addition to the
Serial log):

- START ("OTA update starting") from beginDeferredOtaUpdate(), i.e. while
  the loop still runs -- a flood queued at the deferred fire could never
  transmit before the flash blocks the loop / reboots on success.
- FAIL ("OTA aborted: ...") at both abort points (teardown barrier
  withheld flashing; preflight/download error).

Success has no message: a successful flash reboots into the new image, so
the node returning on the new version is the signal.

New MyMesh::otaAlert() gates on the `alert on/off` master switch and rides
the configured alert scope (AlertReporter::sendText -> sendChannel ->
resolveAlertScope); no-op when alerts are off or no channel is set. Only
these start/fail milestones -- routine slot connect/disconnect is
unaffected (stays in AlertReporter's fault logic). Documented in ALERTS.md.
Both observer firmwares build.
2026-07-19 07:51:52 -07:00
agessaman 2e1a1410ce feat(mqtt): cooperative MQTT shutdown + OTA teardown barrier (Phase 5)
Wire the Phase 4 MQTTLifecycle state machine into MQTTBridge to replace the
blind vTaskDelete teardown that could kill the MQTT task mid-mbedTLS and then
free client buffers on a corrupted heap (the observed OTA teardown panic).

- MQTTBridge owns a MQTTLifecycle::Coordinator driven only by the loop task
  (Core 1) from begin()/end(); a nested LifecycleOps binds the host-tested Ops
  spec to FreeRTOS/PsychicMqttClient.
- end() requests a cooperative stop; the MQTT task (Core 0) tears down its own
  clients where the mbedTLS contexts live, acks via _stop_acked, and
  self-terminates. end() waits (bounded) for the ack, then frees queue/buffers.
- Bounded stop timeout -> reviewed fallback (force kill + Core-1 teardown) sets
  a dirty latch that withholds OTA flashing.
- begin() gains an idempotent double-call guard and syncs the Coordinator to
  Running.
- OTA teardown barrier: simple_repeater's deferred flash aborts/resumes unless
  end() reported a clean stop (canFlashAfterStop()).

Scope: minimal cooperative-shutdown unit. The volatile NTP/reconfigure handshake
replacement and the plain-data snapshot / consumer repointing (MQTT_OWNERSHIP.md
sections 1-3) are deferred. MQTT_STOP_TIMEOUT_MS is a Phase-0 placeholder pending
on-hardware characterization.

Native suite green (incl. test_mqtt_lifecycle); both observer firmware smoke
builds compile. Not yet hardware-validated (Phase 7 gate).
2026-07-19 00:25:37 -07:00
ViezeVingertjesandAdam Gessaman 3b69e7873b fix: guard HWCDC serial config behind ARDUINO_USB_MODE
setTxTimeoutMs() and setTxBufferSize() are HWCDC-only APIs; calling
them on HardwareSerial (UART-bridge boards like T-Beam) fails to compile.

Co-Authored-By: Adam Gessaman <adam@gessaman.com>
2026-07-18 22:43:55 +02:00
ViezeVingertjes fb2c61f862 fix: prevent ESP32 KISS modem stalls under USB backpressure
Switch ESP32-S3 KISS modem environments to HWCDC and move outbound KISS writes to a non-blocking queued frame path so loop() and TX completion keep progressing when the host reads slowly. Add native backpressure regression tests and correct the native_kiss_modem test filter so this suite runs directly with pio test.
2026-07-18 22:43:55 +02:00
agessaman cbbd01b620 feat(docs): add local testing instructions for MQTT functionality
Include detailed instructions for local testing of observer and WiFi
functionality without hardware. Document the use of a mock backend and
Wokwi ESP32-S3 simulation for easier development and testing.
Enhance the MQTT implementation documentation to improve developer
experience and facilitate testing workflows.
2026-07-18 13:19:27 -07:00
agessaman a7c1cc631f feat(webconfig): add web configuration portal and MQTT enhancements
Introduce a web configuration portal for easier node management and
provisioning without serial CLI. Enhance MQTT functionality with
improved IATA code validation, dynamic slot management, and
background NTP synchronization. Update web UI elements for better
user experience and security notes regarding open AP usage.
2026-07-18 09:34:11 -07:00
Scott Powell a0b52be4ad * RP2040 build fixes
* new prefs file now "/prefs.json"
2026-07-18 20:12:21 +10:00
Scott Powell 2189f2294c * added prefs initialisers to match original zeroed state 2026-07-18 19:22:53 +10:00
ripplebiz 795989b918 Merge pull request #2966 from Quency-D/heltec-rc32
Add Heltec rc32 board
2026-07-17 23:23:29 +10:00
Florent c644720ea7 uitask: screen and radio poweroff moved to board 2026-07-17 08:29:31 -04:00
fdlamotte 76c6e73fb6 Merge branch 'dev' into repeater_btn 2026-07-17 06:55:19 -04:00
Quency-D b26f53a263 Avoid waking display from rotary input 2026-07-17 15:35:07 +08:00
Scott Powell f515716032 * new NodePrefs for companion 2026-07-17 16:05:10 +10:00
agessaman dfee21a0c9 feat(webconfig): implement web configuration portal for ESP32 2026-07-16 17:00:22 -07:00
Scott Powell 85285c6cbc Merge branch 'dev' into config-serializer 2026-07-16 17:22:28 +10:00