docs: refresh observer docs and document ota commands

Add ota check/update/branch, display, and diagnostic commands to the
observer guide; condense MQTT_IMPLEMENTATION.md and ALERTS.md; fix the
nonexistent 'set bridge off' instruction; mark the T-LoRa observer envs
parked; update the SNMP build-target list; move local-testing notes to
MQTT_INTERNALS.md; drop MEMORY_MONITORING.md (its script never existed).
This commit is contained in:
agessaman committed 2026-10-18 15:00:47 -07:00
1 parent 480c540bf8
commit f339beb22e
7 files changed
+508 -1097

No files matched your search

+63 -103
View File
@@ -1,112 +1,46 @@
# Fault Alerts (Group Channel)
# Fault Alerts
This document describes MeshCore repeater fault alerts, including configuration, CLI commands, and operational behavior.
An observer can post a one-line message on a LoRa group channel when its network link or
an MQTT broker stays down too long. Alerts go out over the mesh as a flood `GRP_TXT`
packet, not over MQTT, since MQTT is what has failed. Anyone subscribed to the channel in
a companion app sees them as normal channel messages, sent under the node's name.
The repeater can broadcast a one-line fault notification on a configured group channel when WiFi or any active MQTT slot has been disconnected longer than a configurable threshold.
## Setup
The alert is sent over **LoRa** as a `PAYLOAD_TYPE_GRP_TXT` flood packet on the configured channel (with sender = device name) - *not* over MQTT. This is intentional: the MQTT path is what's broken, so the only working delivery is the mesh itself. Anyone in radio range subscribed to the same channel/hashtag in their companion app will see the alert inline with normal channel chat.
Point alerts at a private channel, test it, then turn them on.
> **A small list of community channels is intentionally NOT supported.** Fault alerts are operator-infrastructure noise - broadcasting them on shared community channels would spam every node in the area (and on `#test` / `#bot` would amplify via well-known auto-responders). The currently banned destinations are:
>
> - The well-known **Public** group PSK (`izOH6cXN6mrJ5e26oRXNcg==`)
> - **`#test`** (`sha256("#test")[0..15]`)
> - **`#bot`** (`sha256("#bot")[0..15]`)
>
> The list lives in `BANNED_ALERT_CHANNELS[]` in [src/helpers/AlertReporter.cpp](src/helpers/AlertReporter.cpp); adding a new entry is one line (label + 32 hex chars). The matcher runs at both the CLI validation step (`set alert.psk`, `set alert.hashtag`) and the alert-send path, so a saved-config bypass is still refused at runtime. You must point alerts at a **private PSK** (`set alert.psk`) or a non-banned **hashtag channel** (`set alert.hashtag`) before alerts can fire.
**Hashtag channel** (easiest for an operator group):
## Scope and routing
```bash
set alert.hashtag ops-alerts # key = sha256("#ops-alerts")[0..15]
alert test
set alert on
```
Alert floods ride the **repeater's default scope** by default (the same TransportKey used for adverts and channel broadcasts - set via `region default ...`). Operators can override on a per-alert-feature basis with `set alert.region <name>`:
**Private key** (`openssl rand -hex 16`, or paste the companion app's "Share Channel" output):
- If `alert.region` is set and the name resolves via `RegionMap`, that region's TransportKey is used.
- If `alert.region` is unset, or the name doesn't resolve, the repeater's `default_scope` is used.
- If both are null, the alert is sent unscoped (matches the pre-scoped firmware's behavior).
```bash
set alert.psk <32_hex_chars>
alert test
set alert on
```
`alert.region` is stored as-is - it does **not** create the region. Use `region put <name>` first if it doesn't exist.
Subscribers add a channel with the same hashtag or key.
Alerts refuse to use the Public channel, `#test`, or `#bot`, because they would spam every
node in range and trigger auto-responders. The list is `BANNED_ALERT_CHANNELS[]` in
[src/helpers/AlertReporter.cpp](src/helpers/AlertReporter.cpp). It is checked both when you
set the channel and when an alert is sent.
## What triggers an alert
- **WiFi**: continuously down for at least `alert.wifi` minutes (default 30)
- **MQTT slot N**: enabled, has connected at least once since boot, and has been disconnected for at least `alert.mqtt` minutes (default 240, i.e. 4 h)
| Fault | Fires after | Setting |
|-------|-------------|---------|
| Network link (Ethernet or WiFi) down | 30 min | `alert.wifi` |
| MQTT slot down, after connecting at least once since boot | 240 min | `alert.mqtt` |
A "recovered" message is sent once when the underlying connection comes back. After firing, a fault is rate-limited by `alert.interval` (default 60 minutes) before it can re-fire - this prevents flapping links from spamming the channel.
## OTA milestone alerts
Key `ota update` milestones are also broadcast on the alert channel (in addition to the Serial log), so an operator who triggered the update via remote management still gets feedback: the real OTA work runs ~2.5 s after the command and reboots on success, both **outside** the command's reply window.
- **Start** - `OTA update starting`, sent when the update is confirmed and scheduled (during the reply window, so it transmits before the flash blocks the loop).
- **Fail/abort** - `OTA aborted: MQTT stop unclean, bridge resumed` (the teardown barrier withheld flashing) or `OTA aborted: <reason>` (preflight/download error). The bridge is resumed either way.
- **Success** has no dedicated message: a successful flash reboots into the new image, so the node reappearing on the new version is the success signal.
These share the alert channel, scope (`alert.region`), and the `alert` master switch with fault alerts - if `alert` is `off` or no channel is configured, OTA milestones are silent. Unlike fault alerts they are **not** rate-limited (OTA is operator-initiated and rare), and they are limited to these start/fail milestones - routine MQTT slot connect/disconnect never triggers an OTA alert.
## Defaults
| Setting | Default | Notes |
|---------|---------|-------|
| `alert` | `off` | Master enable for automatic fault alerts |
| `alert.psk` | *(unset)* | Private channel secret as **32 hex chars** (16-byte channel key) - the same format the mobile app's "Share Channel" emits, and what every other secret-shaped CLI command (e.g. `prv.key`) uses. |
| `alert.hashtag` | *(unset)* | Informational only; set via `set alert.hashtag` to pre-derive `alert.psk` from `sha256("#name")[0..15]`. Cleared when `alert.psk` is set directly. |
| `alert.region` | *(unset)* | Optional region name; overrides the repeater's `default_scope` for alert sends only. Empty = use `default_scope`. Looked up lazily via `RegionMap`; unknown names silently fall back to `default_scope`. |
| `alert.wifi` | `30` (min) | 0 disables WiFi alerts |
| `alert.mqtt` | `240` (min) | 0 disables MQTT alerts |
| `alert.interval` | `60` (min) | Minutes between repeat alerts of the same fault. **Hard floor of 60 min** so a flapping link can't spam the mesh; the CLI rejects lower values and AlertReporter clamps stale prefs at runtime. |
> `alert.psk` is unset on a fresh flash. **Alerts cannot fire and `alert test` will refuse to send until you configure either `alert.psk` directly or `alert.hashtag` (which derives one).** The sender shown on outgoing alert messages is always the node name (`set name ...`); there is no separate `alert.name`.
## CLI
Get:
- `get alert` - master on/off
- `get alert.psk` - the active 32-hex-char PSK (or `(unset)`) (**serial console only**)
- `get alert.hashtag` - the originating hashtag (or `(unset)`, e.g. after `set alert.psk` overrides the hashtag-derived key)
- `get alert.region` - alert-only scope override (or `(unset, using default scope)`)
- `get alert.wifi` / `get alert.mqtt` / `get alert.interval`
Set:
- `set alert on` / `set alert off`
- `set alert.psk <hex>` - 32 hex chars (16-byte channel secret); rejects banned channels (Public, `#test`, `#bot`). Paste the mobile app's "Share Channel" output as-is. Clears `alert.hashtag` since the new key is operator-supplied.
- `set alert.psk` (no argument) - clears both `alert.psk` and `alert.hashtag`
- `set alert.hashtag <name>` - derives the 16-byte key from `sha256("#name")` *once*, stores it as `alert.psk`, and remembers the hashtag for `get alert.hashtag`. `#` prefix is added if omitted (so `alerts` and `#alerts` are equivalent). Refuses banned hashtag names.
- `set alert.hashtag` (no argument) - clears both `alert.psk` and `alert.hashtag`
- `set alert.region <name>` - alert-only scope override (no region-map mutation; unknown names silently fall back to `default_scope`)
- `set alert.region` (no argument) - clear override, use `default_scope`
- `set alert.wifi <minutes>` (0-1440; 0 = disabled)
- `set alert.mqtt <minutes>` (0-10080; 0 = disabled)
- `set alert.interval <minutes>` (60-10080; 60-minute floor to protect mesh airtime)
Action:
- `alert test` - send a one-off `[test] alert channel ok` immediately on the configured channel; ignores `alert on/off` so operators can verify the channel before enabling fault firing. Returns an error if no channel is configured. If the send succeeds but the `alert` master switch is still `off`, the reply says so (`OK - test sent, but automatic alerts are OFF`) - a working test alone does **not** mean automatic WiFi/MQTT/OTA alerts will fire.
- `alert test <message>` - send a custom test message: `[test] <message>`.
## Example: dedicated hashtag channel (recommended for operator groups)
```bash
set alert.hashtag ops-alerts # stored as "#ops-alerts"; key = sha256("#ops-alerts")[0..15]
set alert.wifi 10 # tighter for ops monitoring
set alert.mqtt 60
set alert on
alert test
```
Anyone running a companion app and subscribed to the `#ops-alerts` hashtag channel will see the alerts inline.
## Example: dedicated alerts channel with a private PSK
Generate a 16-byte random PSK as 32 hex chars (`openssl rand -hex 16`), or use the companion app's "Add channel" feature and copy the "Share Channel" output. Then:
```bash
set alert.psk <32_hex_chars> # 16-byte channel secret; mobile "Share Channel" pastes in directly
set alert.wifi 10
set alert.mqtt 60
set alert on
alert test
```
Subscribers running a MeshCore companion app should add a channel with the same PSK; alerts will appear in that channel's chat view. (Pick any local name for it - the sender of incoming alert messages is the repeater's node name.)
## Sample messages
A recovery message goes out once when the link returns. A fault can't fire again until
`alert.interval` has passed (minimum 60 min).
```
MyObserver: WiFi down 47m (reason 201)
@@ -115,11 +49,37 @@ MyObserver: MQTT slot 1 (analyzer-us) down 4h12m
MyObserver: MQTT slot 1 (analyzer-us) recovered after 4h45m
```
**OTA updates** also post on the alert channel, so a remote operator gets feedback after
the command's reply window closes: `OTA update starting`, or `OTA aborted: <reason>` (the
bridge resumes either way). Success has no message; the node comes back on the new
version. OTA messages are not rate-limited.
## CLI
| Command | Purpose | Default |
|---------|---------|---------|
| `get/set alert on\|off` | Master switch for fault and OTA alerts | off |
| `get/set alert.psk <32 hex>` | Channel key. `get` is serial only. Clears `alert.hashtag` | unset |
| `get/set alert.hashtag <name>` | Derive the key from `#name` (`#` optional) | unset |
| `set alert.psk` / `set alert.hashtag` (no value) | Clear the channel | |
| `get/set alert.region <name>` | Send on this region's scope instead of the default scope; bare `set` clears | unset |
| `get/set alert.wifi <0-1440>` | Minutes before a link alert; `0` disables | 30 |
| `get/set alert.mqtt <0-10080>` | Minutes before a slot alert; `0` disables | 240 |
| `get/set alert.interval <60-10080>` | Minutes between repeats of one fault | 60 |
| `alert test [message]` | Send `[test] <message>` now, even with `alert off` | |
`alert test` warns when `alert` is still off: a successful test doesn't mean automatic
alerts will fire.
## Scope
Alerts flood on the node's default scope (`region default`). `alert.region` overrides it
for alerts only. It must name an existing region (`region put <name>`); an unknown name
falls back to the default scope. With neither set, alerts go out unscoped. Listeners need
the same scope to hear them.
## Notes
- A reboot during an outage resets the timer; the alert won't double-fire because `millis()` starts at 0 at boot. The fault must persist `alert.wifi` / `alert.mqtt` minutes from boot.
- Fault state is stored in RAM only - no persistence across reboots.
- The MQTT-slot watcher uses a separate per-slot `current_outage_started_ms` field that is reset on each reconnect, distinct from the `first_disconnect_time` shown in `mqttN.diag` (which remains a "first disconnect since boot" counter for diagnostics).
- WiFi-down alerts can only be delivered if the LoRa radio is up. There is no fallback path.
- Banned channels (Public, `#test`, `#bot`) are **rejected** at both `set alert.psk` / `set alert.hashtag` and at the alert-send path, so even if you somehow set one via a saved config file, the firmware will silently refuse to broadcast on it. To add another banned channel, append a row to `BANNED_ALERT_CHANNELS[]` in [src/helpers/AlertReporter.cpp](src/helpers/AlertReporter.cpp); the format is `{ "label", "32-lowercase-hex-chars" }` (compute as `printf '#name' | openssl dgst -sha256 | cut -c1-32`).
- Alerts are sent via `sendFlood` with the resolved TransportKey codes attached, so they appear on the configured scope just like other broadcast traffic. Operators monitoring a specific region need to be subscribed to that region's scope to hear alerts.
- Outage timers live in RAM. A reboot restarts them, so the fault must last the full
threshold after boot.
- Link alerts need a working LoRa radio; there is no other delivery path.
-194
View File
@@ -1,194 +0,0 @@
# MeshCore Memory Monitoring Guide
## Quick Start
### 1. Find Your Device Port
```bash
# Linux/macOS
ls /dev/tty* | grep -E "(USB|ACM)"
# Common ports:
# /dev/ttyUSB0 - Linux USB serial
# /dev/ttyACM0 - Linux USB CDC
# /dev/cu.usbserial-* - macOS USB serial
# /dev/cu.usbmodem* - macOS USB CDC
```
### 2. Run Monitoring
```bash
# Monitor for 4 hours (default)
python3 monitor_memory.py /dev/ttyUSB0
# Monitor for 24 hours
python3 monitor_memory.py /dev/ttyUSB0 24
# Monitor for 2 hours with 60-second intervals
python3 monitor_memory.py /dev/ttyUSB0 2 --interval 60
```
## What It Monitors
### Memory Metrics
- **Free Heap**: Available memory in bytes
- **Min Heap**: Minimum free heap since boot
- **Max Alloc**: Largest allocatable block
- **Queue Size**: Number of queued MQTT packets
### Calculated Metrics
- **Heap Usage %**: Percentage of total memory used
- **Fragmentation %**: How fragmented the heap is
### Automatic Alerts
- **LOW_MEMORY**: Free heap < 50KB
- **HIGH_FRAGMENTATION**: Fragmentation > 50%
- **QUEUE_BUILDUP**: Queue size > 20 packets
- **POSSIBLE_LEAK**: Memory decreasing over time
## Output Files
### Console Output
```
[ 30.0m] Free: 102796, Min: 83544, Max: 75764, Queue: 0, Usage: 68.6%, Frag: 26.3%
[ 60.0m] Free: 101234, Min: 82345, Max: 74321, Queue: 2, Usage: 69.1%, Frag: 26.5%
⚠️ WARNING: HIGH_FRAGMENTATION
```
### CSV Log File
```csv
Timestamp,Elapsed_Minutes,Free_Heap,Min_Heap,Max_Alloc,Queue_Size,Heap_Usage_Percent,Fragmentation_Percent
2024-01-15T10:30:00,0.0,102796,83544,75764,0,68.6,26.3
2024-01-15T11:00:00,30.0,101234,82345,74321,2,69.1,26.5
```
## Understanding Results
### Healthy System
- **Free Heap**: 150KB+ (stable)
- **Min Heap**: 120KB+ (stable)
- **Max Alloc**: 100KB+ (stable)
- **Fragmentation**: < 30%
- **Queue**: 0-10 packets
### Warning Signs
- **Free Heap**: < 100KB or decreasing
- **Min Heap**: < 80KB or decreasing
- **Fragmentation**: > 50%
- **Queue**: > 20 packets consistently
### Memory Leak Indicators
- **Consistent decrease** in Free Heap over time
- **Min Heap dropping** below previous minimums
- **Max Alloc shrinking** (fragmentation increasing)
- **POSSIBLE_LEAK** alert triggered
## Long-Term Monitoring
### 24-Hour Test
```bash
python3 monitor_memory.py /dev/ttyUSB0 24
```
- Tests for memory leaks over extended period
- Monitors system stability under normal load
- Identifies gradual memory degradation
### 48-Hour Stress Test
```bash
python3 monitor_memory.py /dev/ttyUSB0 48 --interval 60
```
- Extended monitoring for critical deployments
- 60-second intervals reduce log file size
- Tests system under continuous operation
## Troubleshooting
### Device Not Responding
1. Check port is correct: `ls /dev/tty*`
2. Ensure device is connected and powered
3. Try different baud rate if needed
4. Check device is in correct mode
### No Data in CSV
1. Verify device responds to `memory` command manually
2. Check serial connection is stable
3. Ensure device has MQTT bridge enabled
### High Memory Usage
1. Check if it's stable or increasing
2. Look for memory leak patterns
3. Monitor queue size for packet buildup
4. Consider reducing debug logging
## Analysis Tools
### Plot Memory Usage
```python
import pandas as pd
import matplotlib.pyplot as plt
# Load CSV data
df = pd.read_csv('memory_monitor_20240115_103000.csv')
# Plot memory over time
plt.figure(figsize=(12, 8))
plt.subplot(2, 2, 1)
plt.plot(df['Elapsed_Minutes'], df['Free_Heap'])
plt.title('Free Heap Over Time')
plt.ylabel('Bytes')
plt.subplot(2, 2, 2)
plt.plot(df['Elapsed_Minutes'], df['Heap_Usage_Percent'])
plt.title('Heap Usage Percentage')
plt.ylabel('%')
plt.subplot(2, 2, 3)
plt.plot(df['Elapsed_Minutes'], df['Fragmentation_Percent'])
plt.title('Heap Fragmentation')
plt.ylabel('%')
plt.subplot(2, 2, 4)
plt.plot(df['Elapsed_Minutes'], df['Queue_Size'])
plt.title('Queue Size')
plt.ylabel('Packets')
plt.tight_layout()
plt.savefig('memory_analysis.png')
plt.show()
```
### Check for Trends
```python
# Calculate memory trend
df['Free_Heap_Trend'] = df['Free_Heap'].rolling(window=10).mean()
df['Trend_Slope'] = df['Free_Heap_Trend'].diff()
# Identify decreasing trends
decreasing = df[df['Trend_Slope'] < -1000]
if not decreasing.empty:
print("Memory decreasing trend detected!")
print(decreasing[['Elapsed_Minutes', 'Free_Heap', 'Trend_Slope']])
```
## Best Practices
1. **Start with 4-hour baseline** to establish normal patterns
2. **Monitor during peak usage** times for worst-case scenarios
3. **Run 24-hour tests** before production deployment
4. **Check logs regularly** for warning signs
5. **Keep historical data** for trend analysis
6. **Test after code changes** to verify fixes
## Emergency Procedures
### If Memory Leak Detected
1. **Stop monitoring** (Ctrl+C)
2. **Check recent code changes**
3. **Look for unfreed allocations**
4. **Test with reduced functionality**
5. **Deploy memory leak fix**
### If System Crashes
1. **Check last known good memory values**
2. **Identify crash threshold**
3. **Add more frequent monitoring**
4. **Implement memory safeguards**
5. **Consider hardware upgrade**
+411 -775
View File
File diff suppressed because it is too large. Load diff
+12
View File
@@ -305,3 +305,15 @@ ABIs with size/offset assertions. The runtime `MQTTPrefs` layout is not an on-fl
- Settings the pre-split firmware stored *inside* the `/com_prefs` MQTT gap (the MQTT
slot/WiFi config itself) are **not** recovered — users upgrading from firmware that
old must re-enter their MQTT and WiFi configuration.
### Local testing without hardware
- **Portal UI:** `python3 scripts/webconfig_mock_server.py` (add `--setup` for the
first-boot wizard), then open `http://localhost:8080/`. It serves `webui/index.html` and
mimics the firmware's `/api/*` behavior. Standard library only.
- **Boot, WiFi, MQTT, CLI, OLED:** the Wokwi ESP32-S3 sim. Build
`pio run -e Heltec_v3_repeater_observer_mqtt_sim -t mergebin` (radio stubbed by
`SimRadio`, WiFi set to `Wokwi-GUEST`), then run it from `wokwi.toml`/`diagram.json`.
Outbound MQTT works on the free gateway; reaching the on-device portal needs Wokwi's
paid Private Gateway.
- **Host unit tests:** `pio test -e native`; see [test/README.md](test/README.md).
+10 -17
View File
@@ -1,15 +1,12 @@
# SNMP Agent for MeshCore Observer Firmware
This document describes the optional SNMP v2c agent that allows network monitoring tools to poll MeshCore observer nodes for health and performance metrics.
## Overview
The SNMP agent exposes radio statistics, MQTT connectivity status, memory usage, and network information via standard SNMP v2c GET/GETNEXT/GETBULK operations. It is designed for use with monitoring tools such as Nagios, Zabbix, LibreNMS, PRTG, or any SNMP-capable system.
Observer nodes can run a read-only SNMP v2c agent so tools such as Zabbix, LibreNMS, or
PRTG can poll radio, MQTT, memory, and network stats.
- **Protocol**: SNMPv2c (read-only, no SET support)
- **Port**: UDP 161 (standard SNMP)
- **Library**: [0neblock/SNMP_Agent](https://github.com/0neblock/Arduino_SNMP)
- **Disabled by default** -- must be explicitly enabled via CLI
- **Off by default**
## Quick Start
@@ -107,17 +104,12 @@ The SNMP agent runs inside the existing MQTT FreeRTOS task on Core 0 (the WiFi/n
- **MQTT stats** are updated from Core 0 each MQTT task loop iteration
- **Memory and WiFi stats** are read directly in the SNMP loop on Core 0
The agent starts automatically once WiFi connects and `snmp_enabled = 1` in prefs. It adds approximately 2-3 KB RAM overhead plus per-OID handler storage.
The agent starts once the network connects with SNMP enabled, and uses about 2-3 KB of RAM.
## Build Configuration
SNMP is enabled at compile time with the `WITH_SNMP=1` build flag and included in observer firmware targets:
- `heltec_v3_repeater_observer_mqtt`
- `heltec_v4_repeater_observer_mqtt`
- `Station_G2_repeater_observer_mqtt`
To add SNMP to another observer variant, add the following to its `platformio.ini`:
`WITH_SNMP=1` is set on most ESP32 observer environments. Check a board with
`grep -n WITH_SNMP variants/<board>/platformio.ini`. To add it to another observer env:
```ini
build_flags =
@@ -139,11 +131,12 @@ lib_deps =
|---|---|
| `src/helpers/SNMPAgent.h` | SNMP agent wrapper class declaration |
| `src/helpers/SNMPAgent.cpp` | OID registration and stats update implementation |
| `src/helpers/CommonCLI.h` | `NodePrefs` struct (`snmp_enabled`, `snmp_community` fields) |
| `src/helpers/CommonCLI.cpp` | CLI command parsing and prefs persistence |
| `src/helpers/CommonCLI.h` | `MQTTPrefs` (`snmp_enabled`, `snmp_community`) |
| `src/helpers/CommonCLI_Observer.cpp` | CLI commands |
| `src/helpers/bridges/MQTTBridge.cpp` | SNMP loop integration in `mqttTaskLoop()` |
| `examples/simple_repeater/MyMesh.cpp` | Radio stats push and agent initialization |
## Private Enterprise Number
The OIDs currently use a temporary unregistered enterprise number (`99999`). A proper Private Enterprise Number (PEN) can be registered with IANA at no cost at https://pen.iana.org/pen/PenApplication.page.
The OIDs use `99999`, a placeholder. A real Private Enterprise Number is free from
[IANA](https://pen.iana.org/pen/PenApplication.page).
+1 -1
View File
@@ -7,7 +7,7 @@ MeshCore is a lightweight, portable C++ library that enables multi-hop packet ro
MeshCore now supports a range of LoRa devices, allowing for easy flashing without the need to compile firmware manually. Users can flash a pre-built binary using tools like Adafruit ESPTool and interact with the network through a serial console.
MeshCore provides the ability to create wireless mesh networks, similar to Meshtastic and Reticulum but with a focus on lightweight multi-hop packet routing for embedded projects. Unlike Meshtastic, which is tailored for casual LoRa communication, or Reticulum, which offers advanced networking, MeshCore balances simplicity with scalability, making it ideal for custom embedded solutions, where devices (nodes) can communicate over long distances by relaying messages through intermediate nodes. This is especially useful in off-grid, emergency, or tactical situations where traditional communication infrastructure is unavailable.
> **MQTT Observer Setup** — Prebuilt observer firmware, docs, and a changelog are at
> **MQTT Observer Setup:** prebuilt observer firmware, docs, and a changelog are at
> [observer.gessaman.com](https://observer.gessaman.com/). See the
> [MQTT Implementation Guide](./MQTT_IMPLEMENTATION.md) for configuration, CLI commands, and
> troubleshooting.
+11 -7
View File
@@ -85,15 +85,19 @@ This document provides an overview of CLI commands that can be sent to MeshCore
### Start an Over-The-Air (OTA) firmware update
**Usage:**
- `start ota` — serves the ElegantOTA web upload page on the selected Ethernet or Wi-Fi IP when connected, otherwise raises the `MeshCore-OTA` Wi-Fi hotspot. The manual-OTA session stops after 15 minutes unless an upload is in progress.
- `start ota ap` — always raises the `MeshCore-OTA` Wi-Fi hotspot, even when joined to a network. Use this when the network applies client isolation and the station IP isn't reachable.
- `stop ota` — stops an idle manual-OTA web server, releases port 80, and re-enables automatic network switching. It refuses while a firmware upload is in progress.
- `start ota`: serves the ElegantOTA web upload page on the selected Ethernet or Wi-Fi IP when connected, otherwise raises the `MeshCore-OTA` Wi-Fi hotspot. The manual-OTA session stops after 15 minutes unless an upload is in progress.
- `start ota ap`: always raises the `MeshCore-OTA` Wi-Fi hotspot, even when joined to a network. Use this when the network applies client isolation and the station IP isn't reachable.
- `stop ota`: stops an idle manual-OTA web server, releases port 80, and re-enables automatic network switching. It refuses while a firmware upload is in progress.
### Switch the OTA release channel (observer builds)
### Online firmware update (observer builds)
- `ota branch` — shows the selected channel, the channel this build was made for, and the manifest base `ota check`/`ota update` will use.
- `ota branch prod` (alias `stable`) / `ota branch beta` (alias `dev`) — pull future `ota update`s from that channel. The selection is saved; run `ota update` to switch.
- `ota branch default` — follow the channel this build was made for.
- `ota check`: reports whether the release manifest has a newer build for this board. Does not flash.
- `ota update`: downloads and flashes the newer build, then reboots. Refuses builds that change the partition table (flash those by cable).
- `ota branch`: shows the selected channel, the channel this build came from, and the manifest URL.
- `ota branch prod` (alias `stable`) / `ota branch beta` (alias `dev`): pull future updates from that channel. The choice is saved; run `ota update` to switch.
- `ota branch default`: follow the channel this build came from.
See [MQTT_IMPLEMENTATION.md](../MQTT_IMPLEMENTATION.md#firmware-updates-ota) for replies and channel-switch rules.
---