From 647d712ae8a553d3f3aab3036076db7a2667e1ee Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Wed, 4 Jun 2025 21:33:48 +1000 Subject: [PATCH] * Companion: long-press in first 8 seconds now enters CLI Rescue mode --- examples/companion_radio/MyMesh.cpp | 84 +++++++++++++++++++++++++- examples/companion_radio/MyMesh.h | 7 +++ examples/companion_radio/UITask.cpp | 7 ++- examples/companion_radio/UITask.h | 4 +- variants/heltec_tracker/platformio.ini | 2 +- 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 121584a6..730747e1 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -728,6 +728,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe : BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables), _serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4) { _iter_started = false; + _cli_rescue = false; offline_queue_len = 0; app_target_ver = 0; _identity_store = NULL; @@ -1529,9 +1530,78 @@ void MyMesh::handleCmdFrame(size_t len) { } } -void MyMesh::loop() { - BaseChatMesh::loop(); +void MyMesh::enterCLIRescue() { + _cli_rescue = true; + cli_command[0] = 0; + Serial.println("========= CLI Rescue ========="); +} +bool MyMesh::formatFileSystem() { +#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) + return InternalFS.format(); +#elif defined(RP2040_PLATFORM) + return LittleFS.format(); +#elif defined(ESP32) + return SPIFFS.format(); +#else + #error "need to implement file system erase" + return false; +#endif +} + +void MyMesh::checkCLIRescueCmd() { + int len = strlen(cli_command); + while (Serial.available() && len < sizeof(cli_command)-1) { + char c = Serial.read(); + if (c != '\n') { + cli_command[len++] = c; + cli_command[len] = 0; + } + Serial.print(c); // echo + } + if (len == sizeof(cli_command)-1) { // command buffer full + cli_command[sizeof(cli_command)-1] = '\r'; + } + + if (len > 0 && cli_command[len - 1] == '\r') { // received complete line + cli_command[len - 1] = 0; // replace newline with C string null terminator + + if (memcmp(cli_command, "set ", 4) == 0) { + const char* config = &cli_command[4]; + if (memcmp(config, "pin ", 4) == 0) { + _prefs.ble_pin = atoi(&config[4]); + savePrefs(); + Serial.printf(" > pin is now %06d\n", _prefs.ble_pin); + } else { + Serial.printf(" Error: unknown config: %s\n", config); + } + } else if (strcmp(cli_command, "rebuild") == 0) { + bool success = formatFileSystem(); + if (success) { + saveMainIdentity(self_id); + saveContacts(); + Serial.println(" > erase and rebuild done"); + } else { + Serial.println(" Error: erase failed"); + } + } else if (strcmp(cli_command, "erase") == 0) { + bool success = formatFileSystem(); + if (success) { + Serial.println(" > erase done"); + } else { + Serial.println(" Error: erase failed"); + } + } else if (strcmp(cli_command, "reboot") == 0) { + board.reboot(); // doesn't return + } else { + Serial.println(" Error: unknown command"); + } + + cli_command[0] = 0; // reset command buffer + } +} + +void MyMesh::checkSerialInterface() { size_t len = _serial->checkRecvFrame(cmd_frame); if (len > 0) { handleCmdFrame(len); @@ -1556,6 +1626,16 @@ void MyMesh::loop() { } else if (!_serial->isWriteBusy()) { checkConnections(); } +} + +void MyMesh::loop() { + BaseChatMesh::loop(); + + if (_cli_rescue) { + checkCLIRescueCmd(); + } else { + checkSerialInterface(); + } // is there are pending dirty contacts write needed? if (dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) { diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 9d7da0df..92da8c9d 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -92,6 +92,7 @@ public: void loop(); void handleCmdFrame(size_t len); bool advert(); + void enterCLIRescue(); protected: float getAirtimeBudgetFactor() const override; @@ -143,6 +144,10 @@ private: int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override; bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], int len) override; + void checkCLIRescueCmd(); + void checkSerialInterface(); + bool formatFileSystem(); + private: FILESYSTEM *_fs; IdentityStore *_identity_store; @@ -157,6 +162,8 @@ private: uint32_t _most_recent_lastmod; uint32_t _active_ble_pin; bool _iter_started; + bool _cli_rescue; + char cli_command[80]; uint8_t app_target_ver; uint8_t *sign_data; uint32_t sign_data_len; diff --git a/examples/companion_radio/UITask.cpp b/examples/companion_radio/UITask.cpp index 00263e5e..f0f780d6 100644 --- a/examples/companion_radio/UITask.cpp +++ b/examples/companion_radio/UITask.cpp @@ -75,6 +75,7 @@ void UITask::begin(DisplayDriver* display, NodePrefs* node_prefs) { _userButton->onLongPress([this]() { handleButtonLongPress(); }); _userButton->onAnyPress([this]() { handleButtonAnyPress(); }); #endif + ui_started_at = millis(); } void UITask::soundBuzzer(UIEventType bet) { @@ -365,5 +366,9 @@ void UITask::handleButtonTriplePress() { void UITask::handleButtonLongPress() { MESH_DEBUG_PRINTLN("UITask: long press triggered"); - shutdown(); + if (millis() - ui_started_at < 8000) { // long press in first 8 seconds since startup -> CLI/rescue + the_mesh.enterCLIRescue(); + } else { + shutdown(); + } } \ No newline at end of file diff --git a/examples/companion_radio/UITask.h b/examples/companion_radio/UITask.h index 6914e9ec..fff0bbe1 100644 --- a/examples/companion_radio/UITask.h +++ b/examples/companion_radio/UITask.h @@ -36,6 +36,7 @@ class UITask { int _msgcount; bool _need_refresh = true; bool _displayWasOn = false; // Track display state before button press + unsigned long ui_started_at; // Button handlers #if defined(PIN_USER_BTN) || defined(PIN_USER_BTN_ANA) @@ -57,7 +58,8 @@ class UITask { public: UITask(mesh::MainBoard* board) : _board(board), _display(NULL) { - _next_refresh = 0; + _next_refresh = 0; + ui_started_at = 0; _connected = false; } void begin(DisplayDriver* display, NodePrefs* node_prefs); diff --git a/variants/heltec_tracker/platformio.ini b/variants/heltec_tracker/platformio.ini index d62771f4..ed4cbe28 100644 --- a/variants/heltec_tracker/platformio.ini +++ b/variants/heltec_tracker/platformio.ini @@ -39,7 +39,7 @@ extends = Heltec_tracker_base build_flags = ${Heltec_tracker_base.build_flags} -I src/helpers/ui - ; -D ARDUINO_USB_CDC_ON_BOOT=1 ; need for debugging + -D ARDUINO_USB_CDC_ON_BOOT=1 ; need for Serial -D DISPLAY_ROTATION=1 -D DISPLAY_CLASS=ST7735Display -D MAX_CONTACTS=100