Browse Source

Merge pull request #1687 from IoTThinks/MCdev-PowerSaving-for-all-esp32-repeaters-202602

Added PowerSaving for all ESP32-based repeaters
pull/2408/merge
ripplebiz 20 hours ago
committed by GitHub
parent
commit
999e20a4c2
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 22
      examples/simple_repeater/main.cpp
  2. 1
      src/MeshCore.h
  3. 18
      src/helpers/CommonCLI.cpp
  4. 69
      src/helpers/ESP32Board.h
  5. 14
      src/helpers/esp32/TBeamBoard.h
  6. 16
      variants/heltec_v2/HeltecV2Board.h
  7. 6
      variants/heltec_v2/platformio.ini
  8. 4
      variants/heltec_v2/target.cpp
  9. 10
      variants/lilygo_t3s3_sx1276/LilygoT3S3SX1276Board.h
  10. 2
      variants/lilygo_t3s3_sx1276/target.cpp
  11. 4
      variants/lilygo_t3s3_sx1276/target.h
  12. 7
      variants/lilygo_tbeam_SX1262/platformio.ini
  13. 10
      variants/lilygo_tbeam_SX1276/LilygoTBeamSX1276Board.h
  14. 7
      variants/lilygo_tbeam_SX1276/platformio.ini
  15. 2
      variants/lilygo_tbeam_SX1276/target.cpp
  16. 4
      variants/lilygo_tbeam_SX1276/target.h
  17. 7
      variants/lilygo_tbeam_supreme_SX1262/platformio.ini
  18. 4
      variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h

22
examples/simple_repeater/main.cpp

@ -20,8 +20,7 @@ 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
unsigned long POWERSAVING_FIRSTSLEEP_SECS = 120; // The first sleep (if enabled) from boot
#if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_)
static unsigned long userBtnDownAt = 0;
@ -40,9 +39,6 @@ void setup() {
delay(5000);
#endif
// For power saving
lastActive = millis(); // mark last active time since boot
#ifdef DISPLAY_CLASS
if (display.begin()) {
display.startFrame();
@ -157,16 +153,12 @@ void loop() {
rtc_clock.tick();
if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) {
#if defined(NRF52_PLATFORM)
board.sleep(1800); // nrf ignores seconds param, sleeps whenever possible
#else
if (the_mesh.millisHasNowPassed(lastActive + nextSleepinSecs * 1000)) { // To check if it is time 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
#if defined(NRF52_PLATFORM)
board.sleep(0); // nrf ignores seconds param, sleeps whenever possible
#else
if (the_mesh.millisHasNowPassed(POWERSAVING_FIRSTSLEEP_SECS * 1000)) { // To check if it is time to sleep
board.sleep(30); // Sleep. Wake up after a while or when receiving a LoRa packet
}
#endif
#endif
}
}

1
src/MeshCore.h

@ -56,6 +56,7 @@ public:
// Boards may override to stop a boot-indicator LED sequence or similar.
// Default no-op: boards that don't care need not implement anything.
virtual void onBootComplete() { /* no op */ }
virtual uint32_t getIRQGpio() { return -1; } // not supported. Returns DIO1 (SX1262) and DIO0 (SX127x)
virtual void sleep(uint32_t secs) { /* no op */ }
virtual uint32_t getGpio() { return 0; }
virtual void setGpio(uint32_t values) {}

18
src/helpers/CommonCLI.cpp

@ -430,18 +430,28 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
}
#endif
} else if (memcmp(command, "powersaving on", 14) == 0) {
#if defined(NRF52_PLATFORM)
_prefs->powersaving_enabled = 1;
savePrefs();
strcpy(reply, "ok"); // TODO: to return Not supported if required
strcpy(reply, "On - Immediate effect");
#elif defined(ESP32) && !defined(WITH_BRIDGE)
_prefs->powersaving_enabled = 1;
savePrefs();
strcpy(reply, "On - After 2 minutes");
#elif defined(WITH_BRIDGE)
strcpy(reply, "Bridge not supported");
#else
strcpy(reply, "Board not supported");
#endif
} else if (memcmp(command, "powersaving off", 15) == 0) {
_prefs->powersaving_enabled = 0;
savePrefs();
strcpy(reply, "ok");
strcpy(reply, "Off");
} else if (memcmp(command, "powersaving", 11) == 0) {
if (_prefs->powersaving_enabled) {
strcpy(reply, "on");
strcpy(reply, "On");
} else {
strcpy(reply, "off");
strcpy(reply, "Off");
}
} else if (memcmp(command, "log start", 9) == 0) {
_callbacks->setLoggingOn(true);

69
src/helpers/ESP32Board.h

@ -12,17 +12,19 @@
#include <rom/rtc.h>
#include <sys/time.h>
#include <Wire.h>
#include "driver/rtc_io.h"
#include "soc/rtc.h"
#include "esp_system.h"
class ESP32Board : public mesh::MainBoard {
protected:
uint8_t startup_reason;
bool inhibit_sleep = false;
static inline portMUX_TYPE sleepMux = portMUX_INITIALIZER_UNLOCKED;
public:
void begin() {
// for future use, sub-classes SHOULD call this from their begin()
startup_reason = BD_STARTUP_NORMAL;
startup_reason = BD_STARTUP_NORMAL;
#ifdef ESP32_CPU_FREQ
setCpuFrequencyMhz(ESP32_CPU_FREQ);
@ -45,7 +47,7 @@ public:
#endif
#else
Wire.begin();
#endif
#endif
}
// Temperature from ESP32 MCU
@ -60,25 +62,48 @@ public:
return raw / 4;
}
void enterLightSleep(uint32_t secs) {
#if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(P_LORA_DIO_1) // 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
uint32_t getIRQGpio() override {
return P_LORA_DIO_1; // default for SX1262
}
void sleep(uint32_t secs) override {
// Skip if not allow to sleep
if (inhibit_sleep) {
delay(1); // Give MCU to OTA to run
return;
}
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000); // To wake up every hour to do periodically jobs
}
// Set GPIO wakeup
gpio_num_t wakeupPin = (gpio_num_t)getIRQGpio();
esp_light_sleep_start(); // CPU enters light sleep
// Configure timer wakeup
if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000ULL); // Wake up periodically to do scheduled jobs
}
#endif
}
void sleep(uint32_t secs) override {
if (!inhibit_sleep) {
enterLightSleep(secs); // To wake up after "secs" seconds or when receiving a LoRa packet
// Disable CPU interrupt servicing
portENTER_CRITICAL(&sleepMux);
// Skip sleep if there is a LoRa packet
if (gpio_get_level(wakeupPin) == HIGH) {
portEXIT_CRITICAL(&sleepMux);
delay(1);
return;
}
// Configure GPIO wakeup
esp_sleep_enable_gpio_wakeup();
gpio_wakeup_enable((gpio_num_t)wakeupPin, GPIO_INTR_HIGH_LEVEL); // Wake up when receiving a LoRa packet
// MCU enters light sleep
esp_light_sleep_start();
// Avoid ISR flood during wakeup due to HIGH LEVEL interrupt
gpio_wakeup_disable(wakeupPin);
gpio_set_intr_type(wakeupPin, GPIO_INTR_POSEDGE);
// Enable CPU interrupt servicing
portEXIT_CRITICAL(&sleepMux);
}
uint8_t getStartupReason() const override { return startup_reason; }
@ -102,7 +127,7 @@ public:
#endif
uint16_t getBattMilliVolts() override {
#ifdef PIN_VBAT_READ
#ifdef PIN_VBAT_READ
analogReadResolution(12);
uint32_t raw = 0;
@ -141,16 +166,16 @@ public:
// start with some date/time in the recent past
struct timeval tv;
tv.tv_sec = 1715770351; // 15 May 2024, 8:50pm
tv.tv_usec = 0;
settimeofday(&tv, NULL);
}
tv.tv_usec = 0;
settimeofday(&tv, NULL);
}
}
uint32_t getCurrentTime() override {
time_t _now;
time(&_now);
return _now;
}
void setCurrentTime(uint32_t time) override {
void setCurrentTime(uint32_t time) override {
struct timeval tv;
tv.tv_sec = time;
tv.tv_usec = 0;

14
src/helpers/esp32/TBeamBoard.h

@ -59,13 +59,13 @@
// uint32_t P_LORA_BUSY = 0; //shared, so define at run
// uint32_t P_LORA_DIO_2 = 0; //SX1276 only, so define at run
#define P_LORA_DIO_0 26
#define P_LORA_DIO_1 33
#define P_LORA_NSS 18
#define P_LORA_RESET 23
#define P_LORA_SCLK 5
#define P_LORA_MISO 19
#define P_LORA_MOSI 27
// #define P_LORA_DIO_0 26
// #define P_LORA_DIO_1 33
// #define P_LORA_NSS 18
// #define P_LORA_RESET 23
// #define P_LORA_SCLK 5
// #define P_LORA_MISO 19
// #define P_LORA_MOSI 27
// #define PIN_GPS_RX 34
// #define PIN_GPS_TX 12

16
variants/heltec_v2/HeltecV2Board.h

@ -17,12 +17,12 @@ public:
esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_DEEPSLEEP) {
long wakeup_source = esp_sleep_get_ext1_wakeup_status();
if (wakeup_source & (1 << P_LORA_DIO_1)) { // received a LoRa packet (while in deep sleep)
if (wakeup_source & (1 << P_LORA_DIO_0)) { // received a LoRa packet (while in deep sleep)
startup_reason = BD_STARTUP_RX_PACKET;
}
rtc_gpio_hold_dis((gpio_num_t)P_LORA_NSS);
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_deinit((gpio_num_t)P_LORA_DIO_0);
}
}
@ -30,15 +30,15 @@ public:
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// Make sure the DIO1 and NSS GPIOs are hold on required levels during deep sleep
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_1, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_1);
rtc_gpio_set_direction((gpio_num_t)P_LORA_DIO_0, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_pulldown_en((gpio_num_t)P_LORA_DIO_0);
rtc_gpio_hold_en((gpio_num_t)P_LORA_NSS);
if (pin_wake_btn < 0) {
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
esp_sleep_enable_ext1_wakeup( (1L << P_LORA_DIO_0), ESP_EXT1_WAKEUP_ANY_HIGH); // wake up on: recv LoRa packet
} else {
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
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
}
if (secs > 0) {
@ -64,4 +64,8 @@ public:
const char* getManufacturerName() const override {
return "Heltec V2";
}
uint32_t getIRQGpio() override {
return P_LORA_DIO_0; // default for SX1276
}
};

6
variants/heltec_v2/platformio.ini

@ -7,10 +7,10 @@ build_flags =
-D HELTEC_LORA_V2
-D RADIO_CLASS=CustomSX1276
-D WRAPPER_CLASS=CustomSX1276Wrapper
-D P_LORA_DIO_1=26
-D P_LORA_DIO_0=26
-D P_LORA_DIO_1=35
-D P_LORA_NSS=18
-D P_LORA_RESET=RADIOLIB_NC
-D P_LORA_BUSY=RADIOLIB_NC
-D P_LORA_RESET=14
-D P_LORA_SCLK=5
-D P_LORA_MISO=19
-D P_LORA_MOSI=27

4
variants/heltec_v2/target.cpp

@ -5,9 +5,9 @@ HeltecV2Board board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
#else
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1);
#endif
WRAPPER_CLASS radio_driver(radio, board);

10
variants/lilygo_t3s3_sx1276/LilygoT3S3SX1276Board.h

@ -0,0 +1,10 @@
#pragma once
#include <helpers/ESP32Board.h>
class LilygoT3S3SX1276Board : public ESP32Board {
public:
uint32_t getIRQGpio() override {
return P_LORA_DIO_0; // default for SX1276
}
};

2
variants/lilygo_t3s3_sx1276/target.cpp

@ -1,7 +1,7 @@
#include <Arduino.h>
#include "target.h"
ESP32Board board;
LilygoT3S3SX1276Board board;
#if defined(P_LORA_SCLK)
static SPIClass spi;

4
variants/lilygo_t3s3_sx1276/target.h

@ -3,7 +3,7 @@
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/radiolib/RadioLibWrappers.h>
#include <helpers/ESP32Board.h>
#include <LilygoT3S3SX1276Board.h>
#include <helpers/radiolib/CustomSX1276Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
#include <helpers/SensorManager.h>
@ -12,7 +12,7 @@
#include <helpers/ui/MomentaryButton.h>
#endif
extern ESP32Board board;
extern LilygoT3S3SX1276Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
extern SensorManager sensors;

7
variants/lilygo_tbeam_SX1262/platformio.ini

@ -5,6 +5,13 @@ build_flags =
${esp32_base.build_flags}
-I variants/lilygo_tbeam_SX1262
-D TBEAM_SX1262
-D P_LORA_DIO_0=26
-D P_LORA_DIO_1=33
-D P_LORA_NSS=18
-D P_LORA_RESET=23
-D P_LORA_SCLK=5
-D P_LORA_MISO=19
-D P_LORA_MOSI=27
-D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=140

10
variants/lilygo_tbeam_SX1276/LilygoTBeamSX1276Board.h

@ -0,0 +1,10 @@
#pragma once
#include <helpers/esp32/TBeamBoard.h>
class LilygoTBeamSX1276Board : public TBeamBoard {
public:
uint32_t getIRQGpio() override {
return P_LORA_DIO_0; // default for SX1276
}
};

7
variants/lilygo_tbeam_SX1276/platformio.ini

@ -5,6 +5,13 @@ build_flags =
${esp32_base.build_flags}
-I variants/lilygo_tbeam_SX1276
-D TBEAM_SX1276
-D P_LORA_DIO_0=26
-D P_LORA_DIO_1=33
-D P_LORA_NSS=18
-D P_LORA_RESET=23
-D P_LORA_SCLK=5
-D P_LORA_MISO=19
-D P_LORA_MOSI=27
-D SX127X_CURRENT_LIMIT=120
-D RADIO_CLASS=CustomSX1276
-D WRAPPER_CLASS=CustomSX1276Wrapper

2
variants/lilygo_tbeam_SX1276/target.cpp

@ -1,7 +1,7 @@
#include <Arduino.h>
#include "target.h"
TBeamBoard board;
LilygoTBeamSX1276Board board;
#if defined(P_LORA_SCLK)
static SPIClass spi;

4
variants/lilygo_tbeam_SX1276/target.h

@ -3,7 +3,7 @@
#define RADIOLIB_STATIC_ONLY 1
//#include <RadioLib.h>
#include <helpers/radiolib/RadioLibWrappers.h>
#include <helpers/esp32/TBeamBoard.h>
#include <LilygoTBeamSX1276Board.h>
#include <helpers/radiolib/CustomSX1276Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
#include <helpers/sensors/EnvironmentSensorManager.h>
@ -12,7 +12,7 @@
#include <helpers/ui/MomentaryButton.h>
#endif
extern TBeamBoard board;
extern LilygoTBeamSX1276Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
extern EnvironmentSensorManager sensors;

7
variants/lilygo_tbeam_supreme_SX1262/platformio.ini

@ -5,6 +5,13 @@ build_flags =
${esp32_base.build_flags}
-I variants/lilygo_tbeam_supreme_SX1262
-D TBEAM_SUPREME_SX1262
-D P_LORA_DIO_0=26
-D P_LORA_DIO_1=33
-D P_LORA_NSS=18
-D P_LORA_RESET=23
-D P_LORA_SCLK=5
-D P_LORA_MISO=19
-D P_LORA_MOSI=27
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
-D USE_SX1262

4
variants/lilygo_tlora_v2_1/LilyGoTLoraBoard.h

@ -21,4 +21,8 @@ public:
return (2 * raw);
}
uint32_t getIRQGpio() override {
return P_LORA_DIO_0; // default for SX1276
}
};
Loading…
Cancel
Save