From 2019a8c0caef92f7b337094011ec8cd003cf6bfa Mon Sep 17 00:00:00 2001 From: agessaman Date: Mon, 20 Jul 2026 09:00:56 -0700 Subject: [PATCH] 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. --- docs/cli_commands.md | 3 ++- src/MeshCore.h | 2 +- src/helpers/CommonCLI.cpp | 10 ++++++++-- src/helpers/ESP32Board.cpp | 9 ++++++--- src/helpers/ESP32Board.h | 2 +- src/helpers/NRF52Board.cpp | 2 +- src/helpers/NRF52Board.h | 2 +- src/helpers/stm32/STM32Board.h | 2 +- 8 files changed, 21 insertions(+), 11 deletions(-) diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 11aefb5d..9ad9a576 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -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. --- diff --git a/src/MeshCore.h b/src/MeshCore.h index f0c8f387..05f7d021 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -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. diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 28eff082..c583283d 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -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) { diff --git a/src/helpers/ESP32Board.cpp b/src/helpers/ESP32Board.cpp index 6f7573d0..120203ab 100644 --- a/src/helpers/ESP32Board.cpp +++ b/src/helpers/ESP32Board.cpp @@ -11,14 +11,17 @@ #include -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 diff --git a/src/helpers/ESP32Board.h b/src/helpers/ESP32Board.h index b37aeee8..3ecf6735 100644 --- a/src/helpers/ESP32Board.h +++ b/src/helpers/ESP32Board.h @@ -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 diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index 5fb1e55e..ea3d7a45 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -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() diff --git a/src/helpers/NRF52Board.h b/src/helpers/NRF52Board.h index dba15f97..de716855 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -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; diff --git a/src/helpers/stm32/STM32Board.h b/src/helpers/stm32/STM32Board.h index 06bc768f..cc33dfe5 100644 --- a/src/helpers/stm32/STM32Board.h +++ b/src/helpers/stm32/STM32Board.h @@ -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; }; }; \ No newline at end of file