diff --git a/docs/CLI_commands.md b/docs/CLI_commands.md index 8ea9808..6f76ecf 100644 --- a/docs/CLI_commands.md +++ b/docs/CLI_commands.md @@ -292,11 +292,13 @@ Companion builds only. Local only: they run from USB and the app's CLI, and a re ## Observer (ZephCore only) -The observer role (ESP32, `boards/common/observer.conf`) has its own CLI and none of the commands in the other sections: no `reboot`, `ver`, `erase`, `set radio` or `set tx`. USB serial only — an observer never transmits, so there is no remote admin. A `get` replies with the bare value, without the `> ` prefix; a `set` echoes `key=value`; errors start with `ERR`. Every `set` is saved and applied at once, with no reboot. +The observer role (ESP32, `boards/common/observer.conf`) has its own CLI and, apart from `reboot` and `erase`, none of the commands in the other sections: no `ver`, `set radio` or `set tx`. USB serial only — an observer never transmits, so there is no remote admin. A `get` replies with the bare value, without the `> ` prefix; a `set` echoes `key=value`; errors start with `ERR`. Every `set` is saved and applied at once, with no reboot. | Command | Description | |---------|-------------| | `help` | Status banner (node, key, radio, WiFi and MQTT state) followed by the command list. Also printed at boot | +| `reboot` | Restart the node. Replies `OK - rebooting`, as on a repeater | +| `erase` | Factory reset, as on a repeater: erases the whole LittleFS volume (identity, name, radio, WiFi and MQTT settings) and restarts onto defaults. Replies `File system erase: OK - rebooting` | | `get role` | `observer` | | `get name` | Node name. Default `Observer-XXXXXXXX` | | `get board` | Board name | diff --git a/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md b/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md index 813bbf8..b0637b0 100644 --- a/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md +++ b/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md @@ -265,6 +265,10 @@ matches the SoftDevice version of your bootloader. The older ESP32 in the TTGO LoRa32 and T-Beam has no usable sensor and still reports none. On the ThinkNode M9 and Meshnology W12 the radio now also recalibrates itself when the temperature has moved by 5 C or more, as it already does on nRF boards with an LR1110 or LR2021. +- **Observers have `reboot` and `erase`** on their serial console, with the same replies as on a + repeater. `erase` is the factory reset: it wipes the identity, the name and the radio, WiFi and + MQTT settings, and restarts the node on defaults. Until now an observer could only be wiped by + erasing the flash from a computer. - **`sensor list`** with a negative start index is rejected instead of being used ([PR #100](https://github.com/liquidraver/ZephCore/pull/100), thanks to **ptr727**). - **T1000-E**: a pin that was wrongly driven as a sensor enable is left alone. Sensor readings are diff --git a/zephcore/app/main_observer.cpp b/zephcore/app/main_observer.cpp index 0435487..21cd1cf 100644 --- a/zephcore/app/main_observer.cpp +++ b/zephcore/app/main_observer.cpp @@ -207,6 +207,10 @@ static void print_banner(void) cli_println("get mqtt.user MQTT username"); cli_println("get mqtt.iata Location code"); cli_println("get meshtimesync Time-sync consensus state (dry-run)"); + cli_println(""); + cli_println("--- System ---"); + cli_println("reboot Restart the node"); + cli_println("erase Factory reset: erase all settings, then restart"); cli_println("help Show this screen"); cli_println("========================="); } @@ -239,6 +243,10 @@ static void cli_echo_resume(void) static inline void cli_echo_resume(void) {} #endif +/* `reboot` and `erase`: handled here, beside the board and the data store, + * not in ObserverMesh. Defined below the global instances. */ +static bool cli_system_command(const char *line, char *reply, size_t reply_size); + static void process_cli_rx(void) { uint8_t byte; @@ -250,7 +258,9 @@ static void process_cli_rx(void) cli_reply[0] = '\0'; bool want_banner = false; - if (s_mesh_ptr) { + if (cli_system_command(cli_line, cli_reply, sizeof(cli_reply))) { + /* reply set; the node resets shortly */ + } else if (s_mesh_ptr) { want_banner = s_mesh_ptr->handleCLI( cli_line, cli_reply, sizeof(cli_reply)); @@ -312,6 +322,39 @@ static mesh::LoRaRadio lora_radio(lora_dev, s_board); static mesh::ObserverMesh observer_mesh(lora_radio, s_ms_clock, s_rtc_clock); static RepeaterDataStore data_store; +/* ========== reboot / erase ========== */ + +/* Deferred so the reply reaches the console before the reset. */ +static void cli_reboot_work_fn(struct k_work *work) +{ + ARG_UNUSED(work); + s_board.reboot(); +} + +static K_WORK_DELAYABLE_DEFINE(cli_reboot_work, cli_reboot_work_fn); + +/* Same commands and replies as the repeater's CLI (helpers/CommonCLI.cpp). + * `erase` is the factory reset: the whole volume, WiFi and MQTT settings + * included, then a reboot onto defaults. */ +static bool cli_system_command(const char *line, char *reply, size_t reply_size) +{ + if (strcmp(line, "reboot") == 0) { + snprintf(reply, reply_size, "OK - rebooting"); + k_work_schedule(&cli_reboot_work, K_MSEC(500)); + return true; + } + if (strcmp(line, "erase") == 0) { + if (data_store.formatFileSystem()) { + snprintf(reply, reply_size, "File system erase: OK - rebooting"); + k_work_schedule(&cli_reboot_work, K_MSEC(500)); + } else { + snprintf(reply, reply_size, "File system erase: Err"); + } + return true; + } + return false; +} + /* ========== main() ========== */ int main(void)