Skip to content

Commit f9720f0

Browse files
authored
Merge pull request #1266 from IoTThinks/MCdev-Powersaving-for-esp32-202512
Added powersaving to all ESP32 boards with RTC-supported DIO1
2 parents 8edbb08 + d911a34 commit f9720f0

8 files changed

Lines changed: 84 additions & 12 deletions

File tree

examples/simple_repeater/MyMesh.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,3 +1116,8 @@ void MyMesh::loop() {
11161116
uptime_millis += now - last_millis;
11171117
last_millis = now;
11181118
}
1119+
1120+
// To check if there is pending work
1121+
bool MyMesh::hasPendingWork() const {
1122+
return _mgr->getOutboundCount(0xFFFFFFFF) > 0;
1123+
}

examples/simple_repeater/MyMesh.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
225225
bridge.begin();
226226
}
227227
#endif
228+
229+
// To check if there is pending work
230+
bool hasPendingWork() const;
228231
};

examples/simple_repeater/main.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,19 @@ void halt() {
1919

2020
static char command[160];
2121

22+
// For power saving
23+
unsigned long lastActive = 0; // mark last active time
24+
unsigned long nextSleepinSecs = 120; // next sleep in seconds. The first sleep (if enabled) is after 2 minutes from boot
25+
2226
void setup() {
2327
Serial.begin(115200);
2428
delay(1000);
2529

2630
board.begin();
2731

32+
// For power saving
33+
lastActive = millis(); // mark last active time since boot
34+
2835
#ifdef DISPLAY_CLASS
2936
if (display.begin()) {
3037
display.startFrame();
@@ -117,4 +124,15 @@ void loop() {
117124
ui_task.loop();
118125
#endif
119126
rtc_clock.tick();
127+
128+
if (the_mesh.getNodePrefs()->powersaving_enabled && // To check if power saving is enabled
129+
the_mesh.millisHasNowPassed(lastActive + nextSleepinSecs * 1000)) { // To check if it is time to sleep
130+
if (!the_mesh.hasPendingWork()) { // No pending work. Safe to sleep
131+
board.sleep(1800); // To sleep. Wake up after 30 minutes or when receiving a LoRa packet
132+
lastActive = millis();
133+
nextSleepinSecs = 5; // Default: To work for 5s and sleep again
134+
} else {
135+
nextSleepinSecs += 5; // When there is pending work, to work another 5s
136+
}
137+
}
120138
}

src/MeshCore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class MainBoard {
5151
virtual void onAfterTransmit() { }
5252
virtual void reboot() = 0;
5353
virtual void powerOff() { /* no op */ }
54+
virtual void sleep(uint32_t secs) { /* no op */ }
5455
virtual uint32_t getGpio() { return 0; }
5556
virtual void setGpio(uint32_t values) {}
5657
virtual uint8_t getStartupReason() const = 0;

src/helpers/CommonCLI.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
6565
file.read((uint8_t *)&_prefs->bridge_baud, sizeof(_prefs->bridge_baud)); // 131
6666
file.read((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 135
6767
file.read((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 136
68-
file.read(pad, 4); // 152
68+
file.read((uint8_t *)&_prefs->powersaving_enabled, sizeof(_prefs->powersaving_enabled)); // 152
69+
file.read(pad, 3); // 153
6970
file.read((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156
7071
file.read((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157
7172
file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)); // 161
@@ -93,6 +94,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
9394
_prefs->bridge_baud = constrain(_prefs->bridge_baud, 9600, 115200);
9495
_prefs->bridge_channel = constrain(_prefs->bridge_channel, 0, 14);
9596

97+
_prefs->powersaving_enabled = constrain(_prefs->powersaving_enabled, 0, 1);
98+
9699
_prefs->gps_enabled = constrain(_prefs->gps_enabled, 0, 1);
97100
_prefs->advert_loc_policy = constrain(_prefs->advert_loc_policy, 0, 2);
98101

@@ -145,7 +148,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
145148
file.write((uint8_t *)&_prefs->bridge_baud, sizeof(_prefs->bridge_baud)); // 131
146149
file.write((uint8_t *)&_prefs->bridge_channel, sizeof(_prefs->bridge_channel)); // 135
147150
file.write((uint8_t *)&_prefs->bridge_secret, sizeof(_prefs->bridge_secret)); // 136
148-
file.write(pad, 4); // 152
151+
file.write((uint8_t *)&_prefs->powersaving_enabled, sizeof(_prefs->powersaving_enabled)); // 152
152+
file.write(pad, 3); // 153
149153
file.write((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156
150154
file.write((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157
151155
file.write((uint8_t *)&_prefs->advert_loc_policy, sizeof(_prefs->advert_loc_policy)); // 161
@@ -676,6 +680,20 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
676680
strcpy(reply, "Can't find GPS");
677681
}
678682
#endif
683+
} else if (memcmp(command, "powersaving on", 14) == 0) {
684+
_prefs->powersaving_enabled = 1;
685+
savePrefs();
686+
strcpy(reply, "ok"); // TODO: to return Not supported if required
687+
} else if (memcmp(command, "powersaving off", 15) == 0) {
688+
_prefs->powersaving_enabled = 0;
689+
savePrefs();
690+
strcpy(reply, "ok");
691+
} else if (memcmp(command, "powersaving", 11) == 0) {
692+
if (_prefs->powersaving_enabled) {
693+
strcpy(reply, "on");
694+
} else {
695+
strcpy(reply, "off");
696+
}
679697
} else if (memcmp(command, "log start", 9) == 0) {
680698
_callbacks->setLoggingOn(true);
681699
strcpy(reply, " logging on");

src/helpers/CommonCLI.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ struct NodePrefs { // persisted to file
4242
uint32_t bridge_baud; // 9600, 19200, 38400, 57600, 115200 (default 115200)
4343
uint8_t bridge_channel; // 1-14 (ESP-NOW only)
4444
char bridge_secret[16]; // for XOR encryption of bridge packets (ESP-NOW only)
45+
// Power setting
46+
uint8_t powersaving_enabled; // boolean
4547
// Gps settings
4648
uint8_t gps_enabled;
4749
uint32_t gps_interval; // in seconds

src/helpers/ESP32Board.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <rom/rtc.h>
99
#include <sys/time.h>
1010
#include <Wire.h>
11+
#include "esp_wifi.h"
12+
#include "driver/rtc_io.h"
1113

1214
class ESP32Board : public mesh::MainBoard {
1315
protected:
@@ -54,6 +56,31 @@ class ESP32Board : public mesh::MainBoard {
5456
return raw / 4;
5557
}
5658

59+
void enterLightSleep(uint32_t secs) {
60+
#if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(P_LORA_DIO_1) // Supported ESP32 variants
61+
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
62+
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
63+
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // To wake up when receiving a LoRa packet
64+
65+
if (secs > 0) {
66+
esp_sleep_enable_timer_wakeup(secs * 1000000); // To wake up every hour to do periodically jobs
67+
}
68+
69+
esp_light_sleep_start(); // CPU enters light sleep
70+
}
71+
#endif
72+
}
73+
74+
void sleep(uint32_t secs) override {
75+
// To check for WiFi status to see if there is active OTA
76+
wifi_mode_t mode;
77+
esp_err_t err = esp_wifi_get_mode(&mode);
78+
79+
if (err != ESP_OK) { // WiFi is off ~ No active OTA, safe to go to sleep
80+
enterLightSleep(secs); // To wake up after "secs" seconds or when receiving a LoRa packet
81+
}
82+
}
83+
5784
uint8_t getStartupReason() const override { return startup_reason; }
5885

5986
#if defined(P_LORA_TX_LED)

src/helpers/esp32/TBeamBoard.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,7 @@
22

33
#if defined(TBEAM_SUPREME_SX1262) || defined(TBEAM_SX1262) || defined(TBEAM_SX1276)
44

5-
#include <Wire.h>
6-
#include <Arduino.h>
7-
#include "XPowersLib.h"
8-
#include "helpers/ESP32Board.h"
9-
#include <driver/rtc_io.h>
10-
//#include <RadioLib.h>
11-
//#include <helpers/RadioLibWrappers.h>
12-
//#include <helpers/CustomSX1262Wrapper.h>
13-
//#include <helpers/CustomSX1276Wrapper.h>
14-
5+
// Define pin mappings BEFORE including ESP32Board.h so sleep() can use P_LORA_DIO_1
156
#ifdef TBEAM_SUPREME_SX1262
167
// LoRa radio module pins for TBeam S3 Supreme SX1262
178
#define P_LORA_DIO_0 -1 //NC
@@ -90,6 +81,13 @@
9081
// SX1276
9182
// };
9283

84+
// Include headers AFTER pin definitions so ESP32Board::sleep() can use P_LORA_DIO_1
85+
#include <Wire.h>
86+
#include <Arduino.h>
87+
#include "XPowersLib.h"
88+
#include "helpers/ESP32Board.h"
89+
#include <driver/rtc_io.h>
90+
9391
class TBeamBoard : public ESP32Board {
9492
XPowersLibInterface *PMU = NULL;
9593
//PhysicalLayer * pl;

0 commit comments

Comments
 (0)