Commit Graph
42 Commits
Author SHA1 Message Date
mikecarper cf70478c51 Improve companion setup and USB session recovery 2026-09-02 03:44:28 -07:00
mikecarper fcd1f8cc6c Unify companion BLE and WiFi setup flows 2026-09-01 01:25:00 -07:00
mikecarper 42888e6e3a Add Indicator WiFi setup as eighth home page 2026-08-31 17:31:48 -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
mikecarper 400aa710a2 fix: stabilize primary ESP-NOW WiFi coexistence 2026-08-28 21:01:17 -07:00
mikecarper 6d6c89e411 fix: harden ESP-NOW and full companion networking
Add selectable shared ESP-NOW channels, raw/wrapped bridge compatibility, and bounded fragmentation for maximum-size packets.\n\nGive ESP32 Full profiles WiFi coexistence, stabilize WebConfig and USB terminal lifecycles, and keep disabled bridges stopped during observer updates.\n\nUpdate generated UI, firmware selection, documentation, and regression coverage.
2026-08-28 16:36:34 -07:00
mikecarper 44eb8e4ab9 feat: add customizable companion Bluetooth names 2026-08-25 20:51:18 -07:00
mikecarper 5938cbe614 feat: complete Cascade Indicator companion support
Add native-resolution Indicator UI, touch navigation, SD-backed pixel and emoji fonts, LoRa radio support, dual-slot WiFi Companion provisioning, and mesh clock fallback. Harden radio recovery and build defaults, normalize ESP32 setup-AP protocols, and document/test the operator workflows.
2026-08-25 18:37:17 -07:00
mikecarper 5362d36ac7 Unify full firmware profiles and add dual USB logging 2026-08-23 19:22:45 -07:00
mikecarper e4c8ae5a0b Expose Companion WiFi and radio power controls 2026-08-18 16:51:59 -07:00
mikecarper bcedfdd019 Keep T-Beam companion available without radio 2026-08-15 22:34:45 -07:00
mikecarper 933697f65c Fix Companion device power saving 2026-08-15 17:58:37 -07:00
mikecarper 9161dfd7d5 Expose RX power saving in WebConfig 2026-08-15 09:07:41 -07:00
mikecarper 7605b12cc7 Merge remote-tracking branch 'review/observer-firmware' into keymindCascade
# Conflicts:
#	.github/workflows/build-observer-firmwares.yml
#	.gitignore
#	MQTT_IMPLEMENTATION.md
#	MQTT_INTERNALS.md
#	README.md
#	STABILITY_TESTABILITY_HANDOFF.md
#	docs/cli_commands.md
#	examples/companion_radio/MyMesh.cpp
#	examples/simple_repeater/MyMesh.cpp
#	examples/simple_repeater/UITask.cpp
#	examples/simple_room_server/MyMesh.cpp
#	examples/simple_room_server/UITask.cpp
#	examples/simple_sensor/SensorMesh.cpp
#	platformio.ini
#	scripts/generate_webconfig_html.py
#	scripts/webconfig_mock_server.py
#	src/helpers/CommonCLI.cpp
#	src/helpers/CommonCLI.h
#	src/helpers/JWTHelper.cpp
#	src/helpers/MQTTDefaults.h
#	src/helpers/MQTTMessageBuilder.cpp
#	src/helpers/MQTTPayloadBuilder.h
#	src/helpers/MQTTPresets.h
#	src/helpers/bridges/MQTTBridge.h
#	src/helpers/esp32/WebConfigServer.cpp
#	src/helpers/esp32/WebConfigServer.h
#	src/helpers/ui/SH1106Display.cpp
#	variants/lilygo_tbeam_SX1262/platformio.ini
#	variants/lilygo_tbeam_SX1276/platformio.ini
#	variants/lilygo_tlora_v2_1/platformio.ini
#	variants/thinknode_m7/platformio.ini
#	webui/index.html
2026-08-14 08:53:02 -07:00
mikecarper 968ca3363b Add runtime FEM control and fix full Companion boot 2026-08-10 17:10:18 -07:00
agessaman 6cfdc61baf fix(webconfig): define the in-class constants out of line
LilyGo_TLora_V2_1_1_6 observer builds failed to link:

  undefined reference to `WebConfigServer::MAX_BATCH'
    in handleStatus and handleCliPost

An in-class initialiser is only a declaration under C++11, which is what the
xtensa-esp32 toolchain builds with. Every previous use of MAX_BATCH was a
comparison, which reads the value and needs no symbol. Reporting it as
status.max_cmds, and naming it in the "too many commands" error, passes it to
ArduinoJson — which takes `const T&` — and binding a reference odr-uses it.

It linked on most targets because the compiler folded the reference away, and
failed on the ones where it did not. A cast at the two call sites would have
silenced it just as narrowly; defining the symbols is what stops the next use
from depending on the same luck. MAX_BODY and STOP_WARN_MS get the same
treatment for the same reason, before they are the next to be passed by
reference.
2026-08-08 17:13:00 -07:00
agessaman 8abe26ba7b fix(webconfig): stop the CLI reading secrets, and enforce the setup password
Two findings from review, both real, both mine.

The CLI could read secrets the portal has never exposed. CommonCLI splits its
surface by CALLER, not by command: a serial caller (sender_timestamp 0, physical
access) reads secrets in plaintext, a remote one gets "******** (serial only)".
Its own comments say so — "Serial only (WiFi creds grant LAN access); remote
sees set/unset". execCommand passes 0, which is what makes `erase`, `stats-*`
and `set freq` reachable at all, and with it the terminal inherited the serial
console's plaintext answers for an HTTP request: `get prv.key` returned this
node's identity, `get wifi.pwd` the operator's network.

Worse in setup mode, which authenticates by proximity to an open AP — and `start
webconfig ap` can be run on an already-configured node, so the secrets are real
by then, not blank.

I had reasoned that the AP was the trust boundary either way because the wizard
can already rewrite these. That conflated two capabilities: replacing a WiFi
password does not reveal the current one, and replacing an identity does not
reveal the existing private key. /api/config has always masked these on read
(wcIsSecretKey); the CLI simply broke that rule. Now only the READ is masked —
the command surface stays whole — in CommonCLI's own words, keeping the
set/unset signal that is the useful part.

Onboarding could also skip the mandatory password. handleConfigPost refuses to
arm a reboot during initial setup without one; the CLI only warned in the
browser, which a pasted script or a direct POST ignores, so a node could reboot
onto the LAN still holding the factory credential. Same rule now applies at
POST. It is satisfied by a `password` command anywhere in the session rather
than only in the same request, so the natural two-step console flow still works
— the form batch always sends both together and never needed that memory.

wcIsSecretReadCommand lives in WebConfigKeys.h beside the rest of the secret
classification, pinned by three host tests: what must be masked, what must not,
and that only reads are touched. 17 keys + 24 batch tests pass; the audit checks
a masked read round-trips as masked.
2026-08-08 14:05:02 -07:00
agessaman 20826dcccf feat(webconfig): trim the displayed version to base, build and channel
The full embedded string is v1.16.0.5-observer-beta-dev-a1b2c3d. The -observer
tag is on every observer build and the commit is for machines, so neither tells
a person anything at a glance. Both the page header and the console banner now
show base + published build number + channel, paired with the build date the
way `ver` pairs them:

  v1.16.0.5-dev (6 Jun 2026)
  v1.16.0.5 (6 Jun 2026)

`ver` still prints the whole string, commit included, for when that is what you
need. The channel suffix follows the release filenames rather than the embedded
tag — build.sh writes FILENAME_CHANNEL_TAG "-dev" for the same builds it tags
"-observer-beta-dev" internally, so "-dev" is the name these already carry.

Carrying the build date meant /api/status had to report it; WebConfigServer now
takes FIRMWARE_BUILD_DATE alongside FIRMWARE_VERSION, from the same defines
`ver` reads.

A local build has neither build number nor channel to show, so the fact worth
knowing about it moves to the second line: "local build, OTA not configured".
build.sh deliberately leaves OTA_MANIFEST_BASE undefined there, and a bare
version number gives no hint that the node cannot update itself.
2026-08-08 09:01:29 -07:00
agessaman c831e599ec fix(webconfig): tighten CLI failure detection, reboot deferral and refusals
Five findings from review, all confirmed against the source.

Failure classification (P2). Testing replies for an "Err" prefix passed five
other shapes off as success: "Unknown command", "unknown config: x", "??: x",
"Can't find GPS", "(ERR: clock cannot go backwards)" and "File system erase:
Err". They rendered green, and worse, left _batch_all_ok true — so a queued
reboot went ahead after commands that had failed, defeating the gate entirely.

Rather than lengthen one guess, the two questions are now asked separately,
each erring safe:

  - colour asks "does this look like a failure", against every shape CommonCLI
    actually emits, enumerated in WebConfigBatch.h and pinned by a host test
    that uses the literal strings. Getting this wrong is cosmetic.
  - the reboot gate asks something narrower and answerable: "did every setting
    I asked for take". Only `set`/`password` gate it, and only on the "OK"
    prefix every setter keeps. Diagnostics no longer gate a reboot at all, so a
    harmless `memory` cannot strand one and no guess is made about "> value".

Reboot deferral (P2). CommonCLI dispatches on a six-byte prefix, so `reboot
now` and `rebooted` reach Board::reboot() too. Matching exactly meant those
variants skipped both the confirmation and the deferral and took the node down
mid-drain — the precise failure deferral exists to prevent. Both sides now
anchor the way the firmware dispatches, and the UI's risk matcher with them.

Three commands the portal cannot honestly serve are refused at POST with a
reason, and dropped from autocomplete, instead of running and lying:

  - `start ota` builds a second AsyncWebServer on port 80 with no bind check
    and answers "Started" regardless; the portal already holds that port, so it
    could only leak the allocation and inhibit sleep.
  - `clock sync` takes its time from the caller's timestamp, which a web
    request has none of, so CommonCLI always rejected it. `time <epoch>` works
    and remains offered.
  - bare `log` and `get acl` write their real output to Serial and hand back a
    stub the terminal showed as success; `log` also streams a whole file from
    the loop task, stalling the mesh and radio while it does.

The mock now emits the same failure shapes it used to fake as successes, so
these are reproducible off-hardware. 24 batch + 14 keys tests pass; audit
reports 119/119 answered, 0 missing, 4/4 refused with a reason.
2026-08-08 08:40:49 -07:00
agessaman d532e4ea86 fix(webconfig): correct reply classification and the missing MyMesh commands
Two things hardware turned up.

The whole terminal came back red. The endpoint decided a command had succeeded
by testing its reply for an "OK" prefix — the convention the config batch relies
on, and a safe one there because every allowlisted setter uses it. The CLI
reaches the whole surface, where success has no single shape: setters answer
"OK...", getters answer "> value", `erase` answers "File system erase: OK". Only
failure is uniform ("Err", "ERR:", "Error:"), so that is what the CLI now tests
for.

Colour was the visible half. The other half was worse: _batch_all_ok went false
the moment a sequence contained a `get`, so a script ending in `reboot` was told
some commands had failed and the reboot was withheld.

Replies are green now and red means the node said no, which is what red should
have meant all along. The "> " a getter prefixes its value with is dropped for
display — on the serial console it sets the value apart, here it collides with
the prompt glyph that means "you typed this". The mock emits that marker too;
had it done so from the start, this would have shown up before the flash.

Second: discover.neighbors and discover.scopes did not autocomplete, because
MyMesh::handleCommand intercepts a few commands before delegating to CommonCLI
and the table was built by reading CommonCLI alone. setperm, `get acl` and
`shutdown` were missing for the same reason.

The audit could not have caught that: it drove every command the table offered
and checked the mock answered, which only finds gaps in one direction. It now
also reads the command literals the firmware dispatches on — across CommonCLI,
CommonCLI_Observer and MyMesh — and fails on any the table does not offer. That
check found `shutdown` immediately.

122 commands, all answered, none missing. 22 batch + 14 keys tests pass.
2026-08-08 08:14:54 -07:00
agessaman d7109c185c feat(webconfig): implement /api/cli on the device
The terminal has been driving the mock since it was built. This is the firmware
side, so it works on hardware.

Same 202 + reqid + poll contract as a config save, for the same reason:
CommonCLI touches prefs, the radio and the filesystem, none of which may be
reached from the async_tcp task. Commands go into the deferred slot and tick()
drains them on the loop task. Unlike a save this is not allowlisted — reaching
what the serial console reaches is the point, and execCommand() already passes
sender_timestamp 0, so the terminal gets exactly the serial console's
privilege. Authentication is the boundary, as it is there.

The CLI shares the config batch's slot rather than owning a second MAX_BATCH
array: both drain on the loop task, both are single-slot, and a duplicate would
cost ~8 KB of permanently resident RAM. Sharing also makes a save and a CLI run
mutually exclusive, which they must be. Each reader checks the kind, so neither
can serve the other's results.

Three things the mock could not have taught us:

  - Board::reboot() does not return, so a drained `reboot` would take the node
    down before the client read a single result. It is answered rather than
    executed, and the batch arms the existing deferred-reboot path once the
    results have been read — withheld if any command failed, exactly as a save
    withholds one. clkreboot/poweroff/ota update do real work on the way down
    and cannot be faked, so they still drop the connection; the UI warns first.
  - `password <new>` echoes the new password in its reply. The config path
    already scrubbed that by key; a CLI entry has no key, so it is matched on
    the command. CLI commands are also kept out of the serial log entirely —
    the browser session and the serial console are different audiences.
  - MAX_BATCH is 24, not the 64 the page assumed. It is reported as
    status.max_cmds instead of hardcoded, so the cap cannot drift.

Results stream and page (kCliResultPage = 8), and "done" means the client has
been handed every result, not merely that execution finished — otherwise a
client that stops polling at "done" loses the last page. Commands are never
echoed back: they may carry a secret, and the client matches by index.

New decisions live in WebConfigBatch.h with the rest, covered by three host
tests. Builds clean for heltec_v4_repeater_observer_mqtt; 22 batch + 14 keys
tests pass; the CLI audit reports 119/119 against the updated mock.
2026-08-07 23:16:38 -07:00
mikecarper 44ec918303 Harden WiFi and LR1110 recovery 2026-08-03 16:23:23 -07:00
mikecarper a6ace6076c Add WebConfig CLI and standalone WiFi controls 2026-07-29 13:13:37 -07:00
mikecarper e6abf3ea7d Share WiFi OTA seeding across full ESP32 builds 2026-07-29 01:51:01 -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
agessaman e00b29b4b8 feat(mqtt): add per-slot packet filters for MQTT slots
Introduce per-slot packet filters to allow users to specify which
packet types are uploaded for each MQTT slot. This feature enhances
the flexibility of the MQTT bridge by enabling users to configure
allowlists for packet types, improving the efficiency of data
transmissions. The implementation includes updates to the WebConfig
interface, internal handling of packet filters, and necessary
modifications to the MQTT preferences structure.
2026-07-27 18:34:04 -07:00
mikecarper c1caa5ad81 Merge observer firmware and webconfig updates
# Conflicts:
#	MQTT_INTERNALS.md
#	build.sh
#	docs/cli_commands.md
#	examples/simple_repeater/MyMesh.cpp
#	examples/simple_repeater/MyMesh.h
#	examples/simple_repeater/UITask.cpp
#	examples/simple_room_server/MyMesh.cpp
#	examples/simple_room_server/MyMesh.h
#	examples/simple_room_server/UITask.cpp
#	examples/simple_sensor/SensorMesh.cpp
#	platformio.ini
#	scripts/generate_webconfig_html.py
#	src/MeshCore.h
#	src/helpers/CommonCLI.cpp
#	src/helpers/CommonCLI.h
#	src/helpers/CommonCLI_Observer.cpp
#	src/helpers/ESP32Board.h
#	src/helpers/MQTTMessageBuilder.cpp
#	src/helpers/MQTTPresets.h
#	src/helpers/NRF52Board.cpp
#	src/helpers/NRF52Board.h
#	src/helpers/bridges/MQTTBridge.cpp
#	src/helpers/bridges/MQTTBridge.h
#	src/helpers/esp32/WebConfigServer.cpp
#	src/helpers/esp32/WebConfigServer.h
#	src/helpers/radiolib/RadioLibWrappers.cpp
#	src/helpers/radiolib/RadioLibWrappers.h
#	variants/lilygo_tbeam_SX1262/platformio.ini
#	variants/lilygo_tbeam_SX1276/platformio.ini
#	variants/lilygo_tlora_v2_1/platformio.ini
#	webui/index.html
2026-07-24 02:16:47 -07:00
mikecarper 371d86c4b1 Normalize tracked text to ASCII 2026-07-22 00:43:38 -07:00
agessaman 675bc6b55b feat(webconfig): manage the admin password from the portal
Adds an admin-password field to the setup wizard and the LAN editor, so a
node's password can be set during onboarding and rotated later without a
serial console.

The key maps to the top-level `password` CLI command rather than a `set`
handler, so it is classified separately from WC_ALLOWED_SET_KEYS. It is the
only key granted that treatment, which keeps the allowlist the sole route to
`set` and leaves no general path from a batch to arbitrary CLI commands.

Accepted in both modes: MODE_OFF is refused earlier in handleConfigPost, LAN
required a login to get that far, and the setup AP implies physical proximity.
Restricting rotation to the AP would have forced a bridge outage (`set bridge
off` + `start webconfig ap`) just to change a password.

First onboarding is gated server-side: while the setup AP is up and no WiFi is
configured, a batch that reboots or sets wifi.ssid must also carry a password,
so neither the Advanced editor nor a crafted request can save WiFi and strand
the node on the factory password. The flag is latched at AP start, so a save
that fails partway cannot drop the requirement on retry.

The CLI's `password` command echoes the new secret back in its reply, and
replies are served to the client over the open setup AP, so the reply is
overwritten with "OK" before it can be serialized.

UI: the field lives with the other NodePrefs settings (wizard step 2, and the
Node card on the Radio tab) rather than beside the WiFi password, which is a
different credential. Confirm fields mirror their password twin and are cleared
whenever it is, so a stale confirm value cannot fail a later save as a spurious
mismatch. Validation runs ahead of the WiFi-changed split so a password-only
save is still checked, and reveals the Radio tab before reporting, since the
save bar spans every tab.
2026-07-21 19:00:18 -07:00
mikecarper 86905733ff Add configurable FEM RX gain and recalibration 2026-07-20 15:57:46 -07:00
agessaman ebca9bdd3e feat(mqtt): add mesh-chaun14 and wcmesh presets
Support "{pubkey}" username sentinel so brokers can auth with the
device public key without enlarging the prefs username field.
2026-07-19 23:45:28 -07:00
agessaman d6f8a87183 feat(webconfig): expose mqtt.neighbors controls in the web portal
- Allow mqtt.neighbors and mqtt.neighbors.interval in the WebConfigKeys set-key
  allowlist (the CLI enforces the PSRAM guard; the stub reply handles non-PSRAM).
- Emit neighbors + neighbors_interval (hours) in the WebConfigServer config JSON.
- Add a "Publish neighbors" toggle and a "Neighbors interval (hours)" field
  (12-336) to the Publishing card, with getVal() cases in webui/index.html.
- Cover both keys in test_webconfig_keys.

WebConfigHtml.h is a gitignored build artifact regenerated by the pre-build
hook from index.html, so it is not committed. Verified: test_webconfig_keys
passes and the T_Beam_S3_Supreme observer_mqtt firmware builds [SUCCESS].
2026-07-19 22:39:42 -07:00
agessaman cece565e11 refactor(webconfig): wire WebConfigBatch spec into WebConfigServer (Phase 6)
The pure batch/reboot/stop state machine in WebConfigBatch.h was host-tested
but not referenced by production, so the real logic in WebConfigServer.cpp was
untested and the two could drift silently.

Repoint the production decision points at the spec: POST classification and
replay-state naming, drain pacing/all-ok/finish, reboot scheduling and firing,
result classification, confirm-reboot arming, and stop gating. MAX_BATCH and
STOP_WARN_MS now alias kMaxBatch/kStopWarnMs so the constants cannot drift.

Behavior-preserving. Two asymmetries are deliberate and documented in the
header: finishRebootAt()'s 0 return must not be assigned unconditionally
(the manual /api/reboot route also owns _reboot_at and could be cancelled),
and classifyPost() is consulted in two phases because the change count is
only known after parsing, which must not precede the Replay/Busy answer.

Native suite (14 suites) and both MQTT smoke builds green.
2026-07-19 12:35:52 -07:00
agessaman b7b3b4dcf1 feat(mqtt): enhance topic substitution overflow handling
Improve the mqttSubstituteTopic function to ensure that it reports
overflow when the output buffer is full, preventing silent truncation
of topics. Update related tests to verify the new behavior for
literal overflow and exact fit scenarios, enhancing robustness and
correctness of topic handling.
2026-07-18 14:55:21 -07:00
mikecarper ccce0c8d75 feat: prepare v1.16.07 firmware release 2026-07-18 14:53:56 -07:00
agessaman dc44311e49 feat(webconfig): validate request IDs and improve error handling
Add validation for request IDs in the web configuration server to ensure
they conform to the expected format. Enhance error responses for invalid
or unknown request IDs, improving the robustness of request handling.
Update the web UI to reflect these changes, ensuring that clients can
properly handle errors related to request ID mismatches.
2026-07-18 14:25:56 -07: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 b7795720b2 feat(webconfig): improve request handling and server teardown logic
Enhance the WebConfigServer to track in-flight requests, ensuring that
the server is only freed once all requests have completed or a hard
timeout has been reached. This prevents crashes due to live connections
during server deletion. Update route handlers to log requests and
manage their lifecycle more effectively, improving stability and
performance of the web configuration portal.
2026-07-18 10:22:34 -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
agessaman 639c07a414 feat(webconfig): enhance web configuration with flood and loop settings
Add new configuration options for flood traffic management and loop
detection in the web interface. This includes parameters for maximum
flood hops, maximum advert hops, and loop detection modes, improving
the control over mesh network behavior.
2026-07-16 18:35:21 -07:00
agessaman dfee21a0c9 feat(webconfig): implement web configuration portal for ESP32 2026-07-16 17:00:22 -07:00