From 0dcc0d20d41b81863b1a238a9adf1aaa7309f61a Mon Sep 17 00:00:00 2001 From: Commander1024 Date: Sun, 23 Aug 2026 16:14:19 +0200 Subject: [PATCH] Add reboot command and late terminal activation --- README.md | 12 ++++++++++++ src/CMakeLists.txt | 2 ++ src/console_completion.c | 19 +++++++++++++++++++ src/console_completion.h | 2 +- src/main.c | 4 +++- src/system_console.c | 39 +++++++++++++++++++++++++++++++++++++++ src/system_console.h | 8 ++++++++ wiring.md | 2 ++ 8 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 src/system_console.c create mode 100644 src/system_console.h diff --git a/README.md b/README.md index e3c0fe5..f753fef 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,18 @@ The firmware starts an interactive console on UART0 with the prompt `serial-tool The console provides line editing, history for the current session, terminal-aware cursor movement, and Tab completion. ESP-IDF supplies completion for root commands; the project extends it to nested `debug`, `serial`, `broker`, `usb`, and `wifi` subcommands plus safe fixed values such as AP policy and serial framing. Password values are intentionally never completion candidates. +ESP-IDF normally probes terminal cursor support once while constructing the UART REPL. If the board boots without a terminal attached, that probe times out and selects dumb mode. The DevKit's USB-to-UART bridge exposes no host-open signal that firmware can monitor, and entering enhanced mode before a terminal exists would block linenoise while it waits for a cursor-position response. + +The project therefore preserves safe dumb mode until the first real UART byte arrives. Attach picoterm, picocom, minicom, PuTTY, or another ANSI-capable terminal and press Enter once. That empty line is consumed by the basic reader and promotes the next prompt to enhanced mode, enabling history, Delete, cursor keys, and completion without rebooting. This detects terminal activity rather than electrical USB attachment; a genuinely non-ANSI terminal is not automatically distinguishable on this hardware profile. + +The root-level lifecycle command is: + +```text +reboot +``` + +It acknowledges the request, waits briefly for UART output to drain, and calls the ESP-IDF software restart. RAM-only serial or Wi-Fi changes are lost unless they were persisted first with `serial save` or `wifi save`. + ### Phase 1 serial service The `serial` command manages the working configuration and UART1 service: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a4d7bfe..7066b34 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,7 @@ idf_component_register( "main.c" "console_completion.c" "network_console.c" + "system_console.c" "status_led.c" "rs232_hw_test.c" "rs232_port_owner.c" @@ -25,6 +26,7 @@ idf_component_register( esp_event esp_netif esp_psram + esp_system esp_timer esp_tinyusb esp_wifi diff --git a/src/console_completion.c b/src/console_completion.c index 5c4cc69..de86fde 100644 --- a/src/console_completion.c +++ b/src/console_completion.c @@ -5,6 +5,7 @@ #include #include +#include #include "esp_console.h" #include "linenoise/linenoise.h" @@ -109,6 +110,22 @@ static const char *const s_completion_candidates[] = { "wifi traceroute", }; +static ssize_t console_read_with_late_terminal_upgrade(int file_descriptor, + void *buffer, + size_t size) +{ + ssize_t received = read(file_descriptor, buffer, size); + if (received > 0 && linenoiseIsDumbMode()) { + /* + * The current line remains in the safe dumb reader. The next call to + * linenoise() starts enhanced mode only after a terminal has proven it + * can send data, so its cursor-position query cannot block unattended. + */ + linenoiseSetDumbMode(0); + } + return received; +} + static void console_completion_callback(const char *buffer, linenoiseCompletions *completions) { /* Preserve ESP-IDF completion for registered root command names. */ @@ -134,5 +151,7 @@ static void console_completion_callback(const char *buffer, linenoiseCompletions void console_completion_install(void) { + /* Preserve ESP-IDF's safe boot-time mode until actual UART input arrives. */ + linenoiseSetReadFunction(&console_read_with_late_terminal_upgrade); linenoiseSetCompletionCallback(&console_completion_callback); } diff --git a/src/console_completion.h b/src/console_completion.h index 7247e63..dbe6e86 100644 --- a/src/console_completion.h +++ b/src/console_completion.h @@ -6,7 +6,7 @@ extern "C" { #endif -/* Install project-specific nested command completion for the console. */ +/* Install late-terminal upgrade handling and project-specific completion. */ void console_completion_install(void); #ifdef __cplusplus diff --git a/src/main.c b/src/main.c index a1c0526..02cc973 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,7 @@ #include "session_broker.h" #include "session_console.h" #include "status_led.h" +#include "system_console.h" #include "usb_cdc_transport.h" #include "usb_console.h" #include "wifi_config.h" @@ -132,7 +133,8 @@ void app_main(void) ESP_ERROR_CHECK(usb_console_register_commands()); ESP_ERROR_CHECK(wifi_console_register_commands()); ESP_ERROR_CHECK(network_console_register_root_commands()); - /* ESP-IDF handles root completion; this wrapper adds nested subcommands. */ + ESP_ERROR_CHECK(system_console_register_commands()); + /* Upgrade late UART terminals safely and add nested completion. */ console_completion_install(); ESP_ERROR_CHECK(esp_console_start_repl(repl)); diff --git a/src/system_console.c b/src/system_console.c new file mode 100644 index 0000000..8c40399 --- /dev/null +++ b/src/system_console.c @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-3.0-only */ +/* Root-level system lifecycle commands for the physical administration console. */ + +#include "system_console.h" + +#include + +#include "esp_console.h" +#include "esp_system.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static int command_reboot(int argc, char **argv) +{ + (void)argv; + if (argc != 1) { + printf("Usage: reboot\n"); + return 1; + } + + printf("Rebooting now; unsaved RAM-only configuration changes will be lost.\n"); + fflush(stdout); + /* Give the UART driver time to transmit the acknowledgement before reset. */ + vTaskDelay(pdMS_TO_TICKS(100U)); + esp_restart(); + return 0; +} + +esp_err_t system_console_register_commands(void) +{ + const esp_console_cmd_t command = { + .command = "reboot", + .help = "Restart the ESP32; unsaved RAM-only configuration is lost", + .hint = NULL, + .func = &command_reboot, + .argtable = NULL, + }; + return esp_console_cmd_register(&command); +} diff --git a/src/system_console.h b/src/system_console.h new file mode 100644 index 0000000..1008cc9 --- /dev/null +++ b/src/system_console.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-3.0-only */ + +#pragma once + +#include "esp_err.h" + +/* Register root-level system lifecycle commands such as `reboot`. */ +esp_err_t system_console_register_commands(void); diff --git a/wiring.md b/wiring.md index 467c9c5..a12fee4 100644 --- a/wiring.md +++ b/wiring.md @@ -100,6 +100,8 @@ This hardware profile uses both USB controllers for different purposes: GPIO19 and GPIO20 are connected to the native USB connector and must not be assigned to the MAX3243, display, buttons, or other peripherals while USB CDC is enabled. Both connectors may be attached to the development host during testing so UART0 logs remain available while native USB carries broker data. Compatible DevKit clones can differ in connector labels and power-path design; verify the board schematic before assuming that two attached cables cannot back-power one another. +The USB-to-UART bridge's host DTR/RTS controls are used for automatic boot/reset circuitry and do not provide firmware with a reliable host-open indication. Consequently, attachment cannot be detected like native CDC DTR. After an unattended boot, the UART0 REPL remains in safe basic mode until it receives its first byte; pressing Enter once after attaching an ANSI terminal enables full line editing for the next prompt. + CDC DTR indicates that the host application has opened the native serial port. It controls the lifetime of the `usb-cdc` broker client but is not directly forwarded to the physical DE-9 DTR output. Physical DTR follows the firmware's `serial` configuration. Likewise, host CDC RTS is status information only; GPIO15/DE-9 RTS remains dedicated to UART1 receive flow control when `flow=rts-cts` is enabled. ## Phase 0 loopback tests