Skip to content

Commit 999e20a

Browse files
authored
Merge pull request #1687 from IoTThinks/MCdev-PowerSaving-for-all-esp32-repeaters-202602
Added PowerSaving for all ESP32-based repeaters
2 parents cb73a4a + aeca6f3 commit 999e20a

18 files changed

Lines changed: 142 additions & 65 deletions

File tree

examples/simple_repeater/main.cpp

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ void halt() {
2020
static char command[160];
2121

2222
// 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
23+
unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot
2524

2625
#if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_)
2726
static unsigned long userBtnDownAt = 0;
@@ -40,9 +39,6 @@ void setup() {
4039
delay(5000);
4140
#endif
4241

43-
// For power saving
44-
lastActive = millis(); // mark last active time since boot
45-
4642
#ifdef DISPLAY_CLASS
4743
if (display.begin()) {
4844
display.startFrame();
@@ -157,16 +153,12 @@ void loop() {
157153
rtc_clock.tick();
158154

159155
if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) {
160-
#if defined(NRF52_PLATFORM)
161-
board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible
162-
#else
163-
if (the_mesh.millisHasNowPassed(lastActive + nextSleepinSecs * 1000)) { // To check if it is time to sleep
164-
board.sleep(1800); // To sleep. Wake up after 30 minutes or when receiving a LoRa packet
165-
lastActive = millis();
166-
nextSleepinSecs = 5; // Default: To work for 5s and sleep again
167-
} else {
168-
nextSleepinSecs += 5; // When there is pending work, to work another 5s
156+
#if defined(NRF52_PLATFORM)
157+
board.sleep(0); // nrf ignores seconds param, sleeps whenever possible
158+
#else
159+
if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep
160+
board.sleep(30); // Sleep. Wake up after a while or when receiving a LoRa packet
169161
}
170-
#endif
162+
#endif
171163
}
172164
}

src/MeshCore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class MainBoard {
5656
// Boards may override to stop a boot-indicator LED sequence or similar.
5757
// Default no-op: boards that don't care need not implement anything.
5858
virtual void onBootComplete() { /* no op */ }
59+
virtual uint32_t getIRQGpio() { return -1; } // not supported. Returns DIO1 (SX1262) and DIO0 (SX127x)
5960
virtual void sleep(uint32_t secs) { /* no op */ }
6061
virtual uint32_t getGpio() { return 0; }
6162
virtual void setGpio(uint32_t values) {}

src/helpers/CommonCLI.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -430,18 +430,28 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
430430
}
431431
#endif
432432
} else if (memcmp(command, "powersaving on", 14) == 0) {
433+
#if defined(NRF52_PLATFORM)
433434
_prefs->powersaving_enabled = 1;
434435
savePrefs();
435-
strcpy(reply, "ok"); // TODO: to return Not supported if required
436+
strcpy(reply, "On - Immediate effect");
437+
#elif defined(ESP32) && !defined(WITH_BRIDGE)
438+
_prefs->powersaving_enabled = 1;
439+
savePrefs();
440+
strcpy(reply, "On - After 2 minutes");
441+
#elif defined(WITH_BRIDGE)
442+
strcpy(reply, "Bridge not supported");
443+
#else
444+
strcpy(reply, "Board not supported");
445+
#endif
436446
} else if (memcmp(command, "powersaving off", 15) == 0) {
437447
_prefs->powersaving_enabled = 0;
438448
savePrefs();
439-
strcpy(reply, "ok");
449+
strcpy(reply, "Off");
440450
} else if (memcmp(command, "powersaving", 11) == 0) {
441451
if (_prefs->powersaving_enabled) {
442-
strcpy(reply, "on");
452+
strcpy(reply, "On");
443453
} else {
444-
strcpy(reply, "off");
454+
strcpy(reply, "Off");
445455
}
446456
} else if (memcmp(command, "log start", 9) == 0) {
447457
_callbacks->setLoggingOn(true);

src/helpers/ESP32Board.h

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,19 @@
1212
#include <rom/rtc.h>
1313
#include <sys/time.h>
1414
#include <Wire.h>
15-
#include "driver/rtc_io.h"
15+
#include "soc/rtc.h"
16+
#include "esp_system.h"
1617

1718
class ESP32Board : public mesh::MainBoard {
1819
protected:
1920
uint8_t startup_reason;
2021
bool inhibit_sleep = false;
22+
static inline portMUX_TYPE sleepMux = portMUX_INITIALIZER_UNLOCKED;
2123

2224
public:
2325
void begin() {
2426
// for future use, sub-classes SHOULD call this from their begin()
25-
startup_reason = BD_STARTUP_NORMAL;
27+
startup_reason = BD_STARTUP_NORMAL;
2628

2729
#ifdef ESP32_CPU_FREQ
2830
setCpuFrequencyMhz(ESP32_CPU_FREQ);
@@ -45,7 +47,7 @@ class ESP32Board : public mesh::MainBoard {
4547
#endif
4648
#else
4749
Wire.begin();
48-
#endif
50+
#endif
4951
}
5052

5153
// Temperature from ESP32 MCU
@@ -60,25 +62,48 @@ class ESP32Board : public mesh::MainBoard {
6062
return raw / 4;
6163
}
6264

63-
void enterLightSleep(uint32_t secs) {
64-
#if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(P_LORA_DIO_1) // Supported ESP32 variants
65-
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
66-
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
67-
esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // To wake up when receiving a LoRa packet
65+
uint32_t getIRQGpio() override {
66+
return P_LORA_DIO_1; // default for SX1262
67+
}
68+
69+
void sleep(uint32_t secs) override {
70+
// Skip if not allow to sleep
71+
if (inhibit_sleep) {
72+
delay(1); // Give MCU to OTA to run
73+
return;
74+
}
6875

69-
if (secs > 0) {
70-
esp_sleep_enable_timer_wakeup(secs * 1000000); // To wake up every hour to do periodically jobs
71-
}
76+
// Set GPIO wakeup
77+
gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio();
7278

73-
esp_light_sleep_start(); // CPU enters light sleep
79+
// Configure timer wakeup
80+
if (secs > 0) {
81+
esp_sleep_enable_timer_wakeup(secs * 1000000ULL); // Wake up periodically to do scheduled jobs
7482
}
75-
#endif
76-
}
7783

78-
void sleep(uint32_t secs) override {
79-
if (!inhibit_sleep) {
80-
enterLightSleep(secs); // To wake up after "secs" seconds or when receiving a LoRa packet
84+
// Disable CPU interrupt servicing
85+
portENTER_CRITICAL(&sleepMux);
86+
87+
// Skip sleep if there is a LoRa packet
88+
if (gpio_get_level(wakeupPin) == HIGH) {
89+
portEXIT_CRITICAL(&sleepMux);
90+
delay(1);
91+
return;
8192
}
93+
94+
// Configure GPIO wakeup
95+
esp_sleep_enable_gpio_wakeup();
96+
gpio_wakeup_enable((gpio_num_t)wakeupPin, GPIO_INTR_HIGH_LEVEL); // Wake up when receiving a LoRa packet
97+
98+
// MCU enters light sleep
99+
esp_light_sleep_start();
100+
101+
// Avoid ISR flood during wakeup due to HIGH LEVEL interrupt
102+
gpio_wakeup_disable(wakeupPin);
103+
gpio_set_intr_type(wakeupPin, GPIO_INTR_POSEDGE);
104+
105+
// Enable CPU interrupt servicing
106+
portEXIT_CRITICAL(&sleepMux);
82107
}
83108

84109
uint8_t getStartupReason() const override { return startup_reason; }
@@ -102,7 +127,7 @@ class ESP32Board : public mesh::MainBoard {
102127
#endif
103128

104129
uint16_t getBattMilliVolts() override {
105-
#ifdef PIN_VBAT_READ
130+
#ifdef PIN_VBAT_READ
106131
analogReadResolution(12);
107132

108133
uint32_t raw = 0;
@@ -141,16 +166,16 @@ class ESP32RTCClock : public mesh::RTCClock {
141166
// start with some date/time in the recent past
142167
struct timeval tv;
143168
tv.tv_sec = 1715770351; // 15 May 2024, 8:50pm
144-
tv.tv_usec = 0;
145-
settimeofday(&tv, NULL);
146-
}
169+
tv.tv_usec = 0;
170+
settimeofday(&tv, NULL);
171+
}
147172
}
148173
uint32_t getCurrentTime() override {
149174
time_t _now;
150175
time(&_now);
151176
return _now;
152177
}
153-
void setCurrentTime(uint32_t time) override {
178+
void setCurrentTime(uint32_t time) override {
154179
struct timeval tv;
155180
tv.tv_sec = time;
156181
tv.tv_usec = 0;

src/helpers/esp32/TBeamBoard.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@
5959
// uint32_t P_LORA_BUSY = 0; //shared, so define at run
6060
// uint32_t P_LORA_DIO_2 = 0; //SX1276 only, so define at run
6161

62-
#define P_LORA_DIO_0 26
63-
#define P_LORA_DIO_1 33
64-
#define P_LORA_NSS 18
65-
#define P_LORA_RESET 23
66-
#define P_LORA_SCLK 5
67-
#define P_LORA_MISO 19
68-
#define P_LORA_MOSI 27
62+
// #define P_LORA_DIO_0 26
63+
// #define P_LORA_DIO_1 33
64+
// #define P_LORA_NSS 18
65+
// #define P_LORA_RESET 23
66+
// #define P_LORA_SCLK 5
67+
// #define P_LORA_MISO 19
68+
// #define P_LORA_MOSI 27
6969

7070
// #define PIN_GPS_RX 34
7171
// #define PIN_GPS_TX 12

variants/heltec_v2/HeltecV2Board.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,28 @@ class HeltecV2Board : public ESP32Board {
1717
esp_reset_reason_t reason = esp_reset_reason();
1818
if (reason == ESP_RST_DEEPSLEEP) {
1919
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
20-
if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
20+
if (wakeup_source & (1 << P_LORA_DIO_0)) { // received a LoRa packet (while in deep sleep)
2121
startup_reason = BD_STARTUP_RX_PACKET;
2222
}
2323

2424
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
25-
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
25+
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_0);
2626
}
2727
}
2828

2929
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1) {
3030
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
3131

3232
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
33-
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
34-
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
33+
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_0, RTC_GPIO_MODE_INPUT_ONLY);
34+
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_0);
3535

3636
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
3737

3838
if (pin_wake_btn < 0) {
39-
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
39+
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
4040
} else {
41-
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
41+
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0) | (1L << pin_wake_btn), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet OR wake btn
4242
}
4343

4444
if (secs > 0) {
@@ -64,4 +64,8 @@ class HeltecV2Board : public ESP32Board {
6464
const char* getManufacturerName() const override {
6565
return "Heltec V2";
6666
}
67+
68+
uint32_t getIRQGpio() override {
69+
return P_LORA_DIO_0; // default for SX1276
70+
}
6771
};

variants/heltec_v2/platformio.ini

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ build_flags =
77
-D HELTEC_LORA_V2
88
-D RADIO_CLASS=CustomSX1276
99
-D WRAPPER_CLASS=CustomSX1276Wrapper
10-
-D P_LORA_DIO_1=26
10+
-D P_LORA_DIO_0=26
11+
-D P_LORA_DIO_1=35
1112
-D P_LORA_NSS=18
12-
-D P_LORA_RESET=RADIOLIB_NC
13-
-D P_LORA_BUSY=RADIOLIB_NC
13+
-D P_LORA_RESET=14
1414
-D P_LORA_SCLK=5
1515
-D P_LORA_MISO=19
1616
-D P_LORA_MOSI=27

variants/heltec_v2/target.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ HeltecV2Board board;
55

66
#if defined(P_LORA_SCLK)
77
static SPIClass spi;
8-
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
8+
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
99
#else
10-
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
10+
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1);
1111
#endif
1212

1313
WRAPPER_CLASS radio_driver(radio, board);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
3+
#include <helpers/ESP32Board.h>
4+
5+
class LilygoT3S3SX1276Board : public ESP32Board {
6+
public:
7+
uint32_t getIRQGpio() override {
8+
return P_LORA_DIO_0; // default for SX1276
9+
}
10+
};

variants/lilygo_t3s3_sx1276/target.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include <Arduino.h>
22
#include "target.h"
33

4-
ESP32Board board;
4+
LilygoT3S3SX1276Board board;
55

66
#if defined(P_LORA_SCLK)
77
static SPIClass spi;

0 commit comments

Comments
 (0)