feat(docs): update CLI commands for OTA firmware update options

Enhance the documentation for the `start ota` command to clarify its
behavior when connected to a Wi-Fi network versus when using the
`start ota ap` option. This provides users with better guidance on
how to initiate OTA updates under different network conditions.
This commit is contained in:
agessaman
2026-07-20 09:00:56 -07:00
parent 85c0e1de11
commit 2019a8c0ca
8 changed files with 21 additions and 11 deletions
+2 -1
View File
@@ -72,7 +72,8 @@ 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`
- `start ota` — serves the ElegantOTA web upload page on the station IP if joined to a Wi-Fi network, otherwise raises the `MeshCore-OTA` Wi-Fi hotspot.
- `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.
---
+1 -1
View File
@@ -63,7 +63,7 @@ public:
virtual void setGpio(uint32_t values) {}
virtual uint8_t getStartupReason() const = 0;
virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; }
virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported
virtual bool startOTAUpdate(const char* id, char reply[], bool force_ap = false) { return false; } // not supported
// Pull-based OTA: fetch the firmware build for this variant from a baked-in manifest and flash it.
// current_ver is the running firmware version string (used to skip if already up to date); when
// dry_run is true the build is only reported, not flashed. Observer (ESP32+WiFi) builds only.
+8 -2
View File
@@ -946,8 +946,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
(int)heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
(int)heap_caps_get_total_size(MALLOC_CAP_SPIRAM));
} else if (memcmp(command, "start ota", 9) == 0) {
// Manual OTA: bring up the ElegantOTA SoftAP for a hand-uploaded binary.
if (!_board->startOTAUpdate(_prefs->node_name, reply)) {
// Manual OTA: bring up the ElegantOTA web UI for a hand-uploaded binary.
// Plain "start ota" serves on the station IP when joined to WiFi, else
// raises the MeshCore-OTA SoftAP. "start ota ap" forces the SoftAP even
// when connected, so the UI is reachable when the network applies client
// isolation and the station IP can't be reached. (&& short-circuits keep
// the [10]/[11] reads in-bounds when command == "start ota".)
bool force_ap = (command[9] == ' ' && command[10] == 'a' && command[11] == 'p');
if (!_board->startOTAUpdate(_prefs->node_name, reply, force_ap)) {
strcpy(reply, "Error");
}
} else if (memcmp(command, "clock", 5) == 0) {
+6 -3
View File
@@ -11,14 +11,17 @@
#include <SPIFFS.h>
bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
bool ESP32Board::startOTAUpdate(const char* id, char reply[], bool force_ap) {
inhibit_sleep = true; // prevent sleep during OTA
// If the device is already on a WiFi network (e.g. an observer joined in STA
// mode), serve ElegantOTA on the station IP so it's reachable from the LAN
// without joining a separate AP. Otherwise raise the MeshCore-OTA SoftAP.
// force_ap ("start ota ap") always raises the SoftAP, so the OTA UI stays
// reachable even when the joined network applies client isolation and the
// station IP can't be reached.
IPAddress ip;
if (WiFi.status() == WL_CONNECTED) {
if (!force_ap && WiFi.status() == WL_CONNECTED) {
ip = WiFi.localIP();
} else {
WiFi.softAP("MeshCore-OTA", NULL);
@@ -50,7 +53,7 @@ bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
}
#else
bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
bool ESP32Board::startOTAUpdate(const char* id, char reply[], bool force_ap) {
return false; // not supported
}
#endif
+1 -1
View File
@@ -154,7 +154,7 @@ public:
esp_restart();
}
bool startOTAUpdate(const char* id, char reply[]) override;
bool startOTAUpdate(const char* id, char reply[], bool force_ap = false) override;
bool otaFromManifest(const char* current_ver, bool dry_run, char reply[]) override;
// Heavy body (TLS + JSON / HTTPUpdate). Runs in a dedicated large-stack task
// spawned by otaFromManifest() — public only so that task entry point can call
+1 -1
View File
@@ -365,7 +365,7 @@ bool NRF52Board::getBootloaderVersion(char* out, size_t max_len) {
return false;
}
bool NRF52Board::startOTAUpdate(const char *id, char reply[]) {
bool NRF52Board::startOTAUpdate(const char *id, char reply[], bool force_ap) {
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
+1 -1
View File
@@ -53,7 +53,7 @@ public:
virtual void shutdownPeripherals();
virtual void powerOff() override;
virtual bool getBootloaderVersion(char* version, size_t max_len) override;
virtual bool startOTAUpdate(const char *id, char reply[]) override;
virtual bool startOTAUpdate(const char *id, char reply[], bool force_ap = false) override;
virtual void sleep(uint32_t secs) override;
bool isExternalPowered() override;
+1 -1
View File
@@ -41,5 +41,5 @@ public:
}
#endif
bool startOTAUpdate(const char* id, char reply[]) override { return false; };
bool startOTAUpdate(const char* id, char reply[], bool force_ap = false) override { return false; };
};