Explorar el Código

* Companion: long-press in first 8 seconds now enters CLI Rescue mode

Scott Powell hace 1 año
padre
commit
647d712ae8

+ 82 - 2
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)) {

+ 7 - 0
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;

+ 6 - 1
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();
+  }
 }

+ 3 - 1
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);

+ 1 - 1
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