Browse Source

Added powersaving to all ESP32 boards with RTC-supported DIO1
Added CLI to enable/disable powersaving

Kevin Le 7 months ago
parent
commit
5c6c15942b

+ 5 - 0
examples/simple_repeater/MyMesh.cpp

@@ -1115,3 +1115,8 @@ void MyMesh::loop() {
   uptime_millis += now - last_millis;
   last_millis = now;
 }
+
+// To get the current pending work
+int MyMesh::hasPendingWork() const {
+  return _mgr->getOutboundCount(0xFFFFFFFF);
+}

+ 3 - 0
examples/simple_repeater/MyMesh.h

@@ -225,4 +225,7 @@ public:
     bridge.begin();
   }
 #endif
+
+  // To get the current pending work
+  int hasPendingWork() const;
 };

+ 18 - 0
examples/simple_repeater/main.cpp

@@ -19,12 +19,19 @@ void halt() {
 
 static char command[160];
 
+// For power saving
+unsigned long lastActive = 0; // mark last active time
+unsigned long nextSleepinSecs = 120; // next sleep in seconds. The first sleep (if enabled) is after 2 minutes from boot
+
 void setup() {
   Serial.begin(115200);
   delay(1000);
 
   board.begin();
 
+  // For power saving
+  lastActive = millis(); // mark last active time since boot
+
 #ifdef DISPLAY_CLASS
   if (display.begin()) {
     display.startFrame();
@@ -117,4 +124,15 @@ void loop() {
   ui_task.loop();
 #endif
   rtc_clock.tick();
+
+  if (the_mesh.getNodePrefs()->powersaving_enabled &&                     // To check if power saving is enabled
+      the_mesh.millisHasNowPassed(lastActive + nextSleepinSecs * 1000)) { // To check if it is time to sleep
+    if (the_mesh.hasPendingWork() == 0) { // No pending work. Safe to sleep
+      board.sleep(1800);             // To sleep. Wake up after 30 minutes or when receiving a LoRa packet
+      lastActive = millis();
+      nextSleepinSecs = 5;  // Default: To work for 5s and sleep again
+    } else {
+      nextSleepinSecs += 5; // When there is pending work, to work another 5s
+    }
+  }
 }

+ 1 - 0
src/MeshCore.h

@@ -51,6 +51,7 @@ public:
   virtual void onAfterTransmit() { }
   virtual void reboot() = 0;
   virtual void powerOff() { /* no op */ }
+  virtual void sleep(uint32_t secs)  { /* no op */ }
   virtual uint32_t getGpio() { return 0; }
   virtual void setGpio(uint32_t values) {}
   virtual uint8_t getStartupReason() const = 0;

+ 16 - 2
src/helpers/CommonCLI.cpp

@@ -65,7 +65,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
     file.read((uint8_t *)&_prefs->bridge_baud, sizeof(_prefs->bridge_baud));                       // 131
     file.read((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel));                 // 135
     file.read((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret));                   // 136
-    file.read(pad, 4);                                                                             // 152
+    file.read((uint8_t *)&_prefs->powersaving_enabled, sizeof(_prefs->powersaving_enabled));       // 152
     file.read((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled));                       // 156
     file.read((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval));                     // 157
     file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy));          // 161
@@ -145,7 +145,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
     file.write((uint8_t *)&_prefs->bridge_baud, sizeof(_prefs->bridge_baud));                       // 131
     file.write((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel));                 // 135
     file.write((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret));                   // 136
-    file.write(pad, 4);                                                                             // 152
+    file.write((uint8_t *)&_prefs->powersaving_enabled, sizeof(_prefs->powersaving_enabled));       // 152
     file.write((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled));                       // 156
     file.write((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval));                     // 157
     file.write((uint8_t *)&_prefs->advert_loc_policy, sizeof(_prefs->advert_loc_policy));           // 161
@@ -676,6 +676,20 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
         strcpy(reply, "Can't find GPS");
       }
 #endif
+    } else if (memcmp(command, "powersaving on", 14) == 0) {
+      _prefs->powersaving_enabled = 1;
+      savePrefs();
+      strcpy(reply, "ok"); // TODO: to return Not supported if required
+    } else if (memcmp(command, "powersaving off", 15) == 0) {
+      _prefs->powersaving_enabled = 0;
+      savePrefs();
+      strcpy(reply, "ok");
+    } else if (memcmp(command, "powersaving", 11) == 0) {
+      if (_prefs->powersaving_enabled) {
+        strcpy(reply, "on");
+      } else {
+        strcpy(reply, "off");
+      }
     } else if (memcmp(command, "log start", 9) == 0) {
       _callbacks->setLoggingOn(true);
       strcpy(reply, "   logging on");

+ 2 - 0
src/helpers/CommonCLI.h

@@ -48,6 +48,8 @@ struct NodePrefs { // persisted to file
   uint8_t advert_loc_policy;
   uint32_t discovery_mod_timestamp;
   float adc_multiplier;
+  // Power setting
+  uint8_t powersaving_enabled; // boolean
 };
 
 class CommonCLICallbacks {

+ 23 - 0
src/helpers/ESP32Board.h

@@ -8,6 +8,8 @@
 #include <rom/rtc.h>
 #include <sys/time.h>
 #include <Wire.h>
+#include <WiFi.h>
+#include "driver/rtc_io.h"
 
 class ESP32Board : public mesh::MainBoard {
 protected:
@@ -47,6 +49,27 @@ public:
     return temperatureRead();
   }
 
+  void enterLightSleep(uint32_t secs) {
+#if defined(CONFIG_IDF_TARGET_ESP32S3) // Supported ESP32 variants
+    if (rtc_gpio_is_valid_gpio((gpio_num_t)P_LORA_DIO_1)) { // Only enter sleep mode if P_LORA_DIO_1 is RTC pin
+      esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
+      esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // To wake up when receiving a LoRa packet
+
+      if (secs > 0) {
+        esp_sleep_enable_timer_wakeup(secs * 1000000); // To wake up every hour to do periodically jobs
+      }
+
+      esp_light_sleep_start(); // CPU enters light sleep
+    }
+#endif
+  }
+
+  void sleep(uint32_t secs) override {
+    if (WiFi.getMode() == WIFI_MODE_NULL) { // WiFi is off ~ No active OTA, safe to go to sleep
+      enterLightSleep(secs);                // To wake up after "secs" seconds or when receiving a LoRa packet
+    }
+  }
+
   uint8_t getStartupReason() const override { return startup_reason; }
 
 #if defined(P_LORA_TX_LED)