Files
firmware/src/sleep.cpp
T
Manuelandcoderabbitai[bot] 36c89fa3a7 feat: Support Seeed Wio Tracker L2 (#10909)
* initial commit

* enable power save

* implement mesh LED

* add ADS1115+AW35615 for wio tracker L2

* add ES8311, GT911, AW35615, LP5814 to I2C scanner

* update commit references

* move variant.cpp to extras

* update hw_model

* update lovyanGFX

* point to device-ui commit

* trunk fmt

* fix IO expander (have to take from SensorLib for now as long as AudioThread has the limitation to only support SensorLib and the previous IO expander clashes with duplicate names in arduino-audio-driver)

* workaround duplicate defined symbol

* remove SensorLib; add lightweight Pca9555 class and use unified USE_PCA95X5; add wake button detection

* keep TP_INT disabled(OUTPUT) as we use wake button for wakeup

* PA off by default, enabled when playing sound; add some delay because typical class-D amps (NS4150 family) spec 20–50ms for the output stage to reach full swing after power-on

* refactored AW35615 into new external library

* local revert of PR10571 as this PR completely breaks the alert sound

* fix detection of ADS1115

* update device-ui commit reference

* add synchronisation to IO expander and call toggleDisplay() on wake button press

* add battery curve, fix io expander sync

* add SPILock, simplify macro usage

* update device-ui

* fix wakeup from sleep

* revert because of #11604

* use new AUDIO_AMP_SETTLE_MS

* remove test logs

* enable BaseUI

* use touch screen

* refactor wakekey thread

* fix wake button toggle screen on/off

* fix battery percentage and plugIn state

* Update src/graphics/TFTDisplay.cpp

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* consider to return I2C errors to make coderabbi happy

* fix warnings

* use Throttle for millis comparison

* fix endTransmission in write

* make the rabbit happy

* spli targets -tft / non-tft

* fix compile

* revert forced use of Throttle

* remove MeshLED

* add HW_MODEL

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-08-28 23:30:27 +00:00

635 lines
21 KiB
C++

#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
#endif
#include "Default.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerMon.h"
#include "TransmitHistory.h"
#include "detect/LoRaRadioType.h"
#include "error.h"
#include "main.h"
#include "modules/StatusLEDModule.h"
#include "sleep.h"
#include "target_specific.h"
#ifdef ARCH_ESP32
#include "esp_pm.h"
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
#include "rom/rtc.h"
#include <RadioLib.h>
#include <driver/rtc_io.h>
#include <driver/uart.h>
esp_sleep_source_t wakeCause; // the reason we booted this time
#endif
#include "Throttle.h"
#ifdef USE_PCA95X5
#include PCA95X5_INC
extern PCA95X5_CLS io;
#endif
#ifdef HAS_PPM
#include <XPowersLib.h>
extern XPowersPPM *PPM;
#endif
#ifndef INCLUDE_vTaskSuspend
#define INCLUDE_vTaskSuspend 0
#endif
/// Called to ask any observers if they want to veto sleep. Return 1 to veto or 0 to allow sleep to happen
Observable<void *> preflightSleep;
/// Called to tell observers we are now entering (deep) sleep and you should prepare. Must return 0
Observable<void *> notifyDeepSleep;
/// Called to tell observers we are rebooting ASAP. Must return 0
Observable<void *> notifyReboot;
#ifdef ARCH_ESP32
/// Called to tell observers that light sleep is about to begin
Observable<void *> notifyLightSleep;
/// Called to tell observers that light sleep has just ended, and why it ended
Observable<esp_sleep_wakeup_cause_t> notifyLightSleepEnd;
#endif
// deep sleep support
RTC_DATA_ATTR int bootCount = 0;
// -----------------------------------------------------------------------------
// Application
// -----------------------------------------------------------------------------
/**
* Control CPU core speed (80MHz vs 240MHz)
*
* We leave CPU at full speed during init, but once loop is called switch to low speed (for a 50% power savings)
*
*/
void setCPUFast(bool on)
{
#if defined(ARCH_ESP32) && HAS_WIFI && !HAS_TFT && !defined(T_LORA_PAGER) && !defined(T_DECK)
if (isWifiAvailable()) {
/*
*
* There's a newly introduced bug in the espressif framework where WiFi is
* unstable when the frequency is less than 240MHz.
*
* This mostly impacts WiFi AP mode but we'll bump the frequency for
* all WiFi use cases.
* (Added: Dec 23, 2021 by Jm Casler)
*/
#ifndef CONFIG_IDF_TARGET_ESP32C3
LOG_DEBUG("Set CPU to 240MHz because WiFi is in use");
setCpuFrequencyMhz(240);
#endif
return;
}
// The Heltec LORA32 V1 runs at 26 MHz base frequency and doesn't react well to switching to 80 MHz...
#if !defined(ARDUINO_HELTEC_WIFI_LORA_32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
setCpuFrequencyMhz(on ? 240 : 80);
#endif
#endif
}
// Perform power on init that we do on each wake from deep sleep
void initDeepSleep()
{
#ifdef ARCH_ESP32
bootCount++;
const char *reason;
wakeCause = esp_sleep_get_wakeup_cause();
switch (wakeCause) {
case ESP_SLEEP_WAKEUP_EXT0:
reason = "ext0 RTC_IO";
break;
case ESP_SLEEP_WAKEUP_EXT1:
reason = "ext1 RTC_CNTL";
break;
case ESP_SLEEP_WAKEUP_TIMER:
reason = "timer";
break;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
reason = "touchpad";
break;
case ESP_SLEEP_WAKEUP_ULP:
reason = "ULP program";
break;
default:
reason = "reset";
break;
}
/*
Not using yet because we are using wake on all buttons being low
wakeButtons = esp_sleep_get_ext1_wakeup_status(); // If one of these buttons is set it was the reason we woke
if (wakeCause == ESP_SLEEP_WAKEUP_EXT1 && !wakeButtons) // we must have been using the 'all buttons rule for waking' to
support busted boards, assume button one was pressed wakeButtons = ((uint64_t)1) << buttons.gpios[0];
*/
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
// If we booted because our timer ran out or the user pressed reset, send those as fake events
RESET_REASON hwReason = rtc_get_reset_reason(0);
#ifdef CONFIG_IDF_TARGET_ESP32P4
if (hwReason == BROWN_OUT_RESET)
reason = "brownout";
else if (hwReason == HP_CORE_HP_WDT_RESET)
reason = "taskWatchdog";
else if (hwReason == HP_CORE_LP_WDT_RESET)
reason = "intWatchdog";
else if (hwReason == CHIP_LP_WDT_RESET)
reason = "chipWatchdog";
else if (hwReason == SUPER_WDT_RESET)
reason = "superWatchdog";
else if (hwReason == HP_SYS_HP_WDT_RESET)
reason = "systemWatchdog";
else if (hwReason == HP_SYS_LP_WDT_RESET)
reason = "systemLowPowerWatchdog";
#else
if (hwReason == RTCWDT_BROWN_OUT_RESET)
reason = "brownout";
else if (hwReason == RTCWDT_RTC_RESET)
reason = "rtcWatchdog";
else if (hwReason == TG0WDT_SYS_RESET)
reason = "taskWatchdog";
else if (hwReason == TG1WDT_SYS_RESET)
reason = "intWatchdog";
#endif
LOG_INFO("Booted, wake cause %d (boot count %d), reset_reason=%s", wakeCause, bootCount, reason);
#endif
#if SOC_RTCIO_HOLD_SUPPORTED
// If waking from sleep, release any and all RTC GPIOs
if (wakeCause != ESP_SLEEP_WAKEUP_UNDEFINED) {
LOG_DEBUG("Disable any holds on RTC IO pads");
for (uint8_t i = 0; i <= GPIO_NUM_MAX; i++) {
if (rtc_gpio_is_valid_gpio((gpio_num_t)i))
rtc_gpio_hold_dis((gpio_num_t)i);
// ESP32 (original)
else if (GPIO_IS_VALID_OUTPUT_GPIO((gpio_num_t)i))
gpio_hold_dis((gpio_num_t)i);
}
}
#endif
#endif
}
bool doPreflightSleep(bool deepSleep)
{
// Observers only get a void*: non-NULL means the hardware (radio) is about to be powered
// down (deep sleep / shutdown), NULL means a light sleep where the radio keeps running
static const bool deepSleepFlag = true;
if (preflightSleep.notifyObservers(deepSleep ? (void *)&deepSleepFlag : NULL) != 0)
return false; // vetoed
else
return true;
}
/// Tell devices we are going to sleep and wait for them to handle things
static void waitEnterSleep(bool skipPreflight, bool deepSleep)
{
if (!skipPreflight) {
uint32_t now = millis();
while (!doPreflightSleep(deepSleep)) {
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
if (!Throttle::isWithinTimespanMs(now,
THIRTY_SECONDS_MS)) { // If we wait too long just report an error and go to sleep
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_SLEEP_ENTER_WAIT);
assert(0); // FIXME - for now we just restart, need to fix bug #167
break;
}
}
}
// Code that still needs to be moved into notifyObservers
console->flush(); // send all our characters before we stop cpu clock
setBluetoothEnable(false); // has to be off before calling light sleep
}
void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveNodeDb = false)
{
if (INCLUDE_vTaskSuspend && (msecToWake == portMAX_DELAY)) {
LOG_INFO("Enter deep sleep forever");
} else {
LOG_INFO("Enter deep sleep for %u seconds", msecToWake / 1000);
}
// not using wifi yet, but once we are this is needed to shutoff the radio hw
// esp_wifi_stop();
waitEnterSleep(skipPreflight, true);
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
// Full shutdown of bluetooth hardware
if (nimbleBluetooth)
nimbleBluetooth->deinit();
#endif
#ifdef ARCH_ESP32
if (!shouldLoraWake(msecToWake))
notifyDeepSleep.notifyObservers(NULL);
#else
notifyDeepSleep.notifyObservers(NULL);
#endif
powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep);
if (screen)
screen->doDeepSleep(); // datasheet says this will draw only 10ua
if (!skipSaveNodeDb) {
nodeDB->saveToDisk();
}
// Persist broadcast transmit times so throttle survives reboot
if (transmitHistory)
transmitHistory->saveToDisk();
#ifdef PIN_POWER_EN
digitalWrite(PIN_POWER_EN, LOW);
pinMode(PIN_POWER_EN, INPUT); // power off peripherals
#endif
#ifdef RAK_WISMESH_TAP_V2
digitalWrite(SDCARD_CS, LOW);
#endif
#if defined(TRACKER_T1000_E) || defined(MESH_TRACKER_X1)
#ifdef GNSS_AIROHA
digitalWrite(GPS_VRTC_EN, LOW);
digitalWrite(PIN_GPS_RESET, LOW);
digitalWrite(GPS_SLEEP_INT, LOW);
digitalWrite(GPS_RTC_INT, LOW);
#ifdef GPS_RESETB_OUT
pinMode(GPS_RESETB_OUT, OUTPUT);
digitalWrite(GPS_RESETB_OUT, LOW);
#endif
#endif
#ifdef BUZZER_EN_PIN
digitalWrite(BUZZER_EN_PIN, LOW);
#endif
#ifdef PIN_DRV_EN
digitalWrite(PIN_DRV_EN, LOW);
#endif
#ifdef PIN_3V3_EN
digitalWrite(PIN_3V3_EN, LOW);
#endif
#ifdef PIN_WD_EN
digitalWrite(PIN_WD_EN, LOW);
#endif
#endif
statusLEDModule->setPowerLED(false);
#ifdef RESET_OLED
digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power
#endif
#if defined(VEXT_ENABLE)
digitalWrite(VEXT_ENABLE, !VEXT_ON_VALUE); // turn on the display power
#endif
#ifdef ARCH_ESP32
if (shouldLoraWake(msecToWake)) {
enableLoraInterrupt();
}
#ifdef BUTTON_PIN
// Avoid leakage through button pin
if (GPIO_IS_VALID_OUTPUT_GPIO(BUTTON_PIN)) {
#ifdef BUTTON_NEED_PULLUP
pinMode(BUTTON_PIN, INPUT_PULLUP);
#else
pinMode(BUTTON_PIN, INPUT);
#endif
gpio_hold_en((gpio_num_t)BUTTON_PIN);
}
#endif
#ifdef SENSECAP_INDICATOR
// Portexpander definition does not pass GPIO_IS_VALID_OUTPUT_GPIO
pinMode(LORA_CS, OUTPUT);
digitalWrite(LORA_CS, HIGH);
gpio_hold_en((gpio_num_t)LORA_CS);
#elif defined(ELECROW_PANEL)
// Elecrow panels do not use LORA_CS, do nothing
#else
if (GPIO_IS_VALID_OUTPUT_GPIO(LORA_CS)) {
// LoRa CS (RADIO_NSS) needs to stay HIGH, even during deep sleep
pinMode(LORA_CS, OUTPUT);
digitalWrite(LORA_CS, HIGH);
gpio_hold_en((gpio_num_t)LORA_CS);
}
#endif
#endif
#ifdef HAS_PPM
if (PPM) {
// BQ25896 PMIC shutdown is a hard power-off state.
// Only use it for "sleep forever" / explicit shutdown, because timed deep sleep
// must remain wakeable by RTC timer.
if (msecToWake == portMAX_DELAY) {
LOG_INFO("PPM shutdown");
console->flush();
PPM->shutdown();
}
}
#endif
#ifdef HAS_PMU
if (pmu_found && PMU) {
// Obsolete comment: from back when we we used to receive lora packets while CPU was in deep sleep.
// We no longer do that, because our light-sleep current draws are low enough and it provides fast start/low cost
// wake. We currently use deep sleep only for 'we want our device to actually be off - because our battery is
// critically low'. So in deep sleep we DO shut down power to LORA (and when we boot later we completely reinit it)
//
// No need to turn this off if the power draw in sleep mode really is just 0.2uA and turning it off would
// leave floating input for the IRQ line
// If we want to leave the radio receiving in would be 11.5mA current draw, but most of the time it is just waiting
// in its sequencer (true?) so the average power draw should be much lower even if we were listening for packets
// all the time.
PMU->setChargingLedMode(XPOWERS_CHG_LED_OFF);
uint8_t model = PMU->getChipModel();
if (model == XPOWERS_AXP2101) {
if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
// t-beam v1.2 radio power channel
PMU->disablePowerOutput(XPOWERS_ALDO2); // lora radio power channel
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE ||
HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3 || HW_VENDOR == meshtastic_HardwareModel_T_WATCH_ULTRA) {
PMU->disablePowerOutput(XPOWERS_ALDO3); // lora radio power channel
}
} else if (model == XPOWERS_AXP192) {
// t-beam v1.1 radio power channel
PMU->disablePowerOutput(XPOWERS_LDO2); // lora radio power channel
}
if (msecToWake == portMAX_DELAY) {
LOG_INFO("PMU shutdown");
console->flush();
PMU->shutdown();
}
}
#endif
#if !MESHTASTIC_EXCLUDE_I2C && defined(ARCH_ESP32) && defined(I2C_SDA)
// Added by https://github.com/meshtastic/firmware/pull/4418
// Possibly to support Heltec Capsule Sensor?
Wire.end();
pinMode(I2C_SDA, ANALOG);
pinMode(I2C_SCL, ANALOG);
#endif
console->flush();
cpuDeepSleep(msecToWake);
}
#ifdef ARCH_ESP32
/**
* enter light sleep (preserves ram but stops everything about CPU).
*
* Returns (after restoring hw state) when the user presses a button or we get a LoRa interrupt
*/
esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more reasonable default
{
// LOG_DEBUG("Enter light sleep");
// LORA_DIO1 is an extended IO pin (on an I/O expander). Setting it as a wake-up pin will cause problems,
// such as the device not entering light sleep. Boards opt in with LORA_DIO1_EXTENDED_IO in their variant.
#if defined(LORA_DIO1_EXTENDED_IO)
return ESP_SLEEP_WAKEUP_TIMER;
#endif
waitEnterSleep(false, false);
notifyLightSleep.notifyObservers(NULL); // Button interrupts are detached here
uint64_t sleepUsec = sleepMsec * 1000LL;
// NOTE! ESP docs say we must disable bluetooth and wifi before light sleep
#if SOC_PM_SUPPORT_RTC_PERIPH_PD
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif
#if defined(BUTTON_PIN) && defined(BUTTON_NEED_PULLUP)
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#endif
#ifdef SERIAL0_RX_GPIO
// We treat the serial port as a GPIO for a fast/low power way of waking, if we see a rising edge that means
// someone started to send something
// gpio 3 is RXD for serialport 0 on ESP32
// Send a few Z characters to wake the port
// this doesn't work on TBEAMs when the USB is depowered (causes bogus interrupts)
// So we disable this "wake on serial" feature - because now when a TBEAM (only) has power connected it
// never tries to go to sleep if the user is using the API
// gpio_wakeup_enable((gpio_num_t)SERIAL0_RX_GPIO, GPIO_INTR_LOW_LEVEL);
// doesn't help - I think the USB-UART chip losing power is pulling the signal low
// gpio_pullup_en((gpio_num_t)SERIAL0_RX_GPIO);
// alas - can only work if using the refclock, which is limited to about 9600 bps
// assert(uart_set_wakeup_threshold(UART_NUM_0, 3) == ESP_OK);
// assert(esp_sleep_enable_uart_wakeup(0) == ESP_OK);
#endif
#ifdef ROTARY_PRESS
// The enableLoraInterrupt() method is using ext0_wakeup, so we are forced to use GPIO wakeup
gpio_wakeup_enable((gpio_num_t)ROTARY_PRESS, GPIO_INTR_LOW_LEVEL);
#endif
#ifdef KB_INT
#if KB_INT_WAKE_ON_HIGH
gpio_wakeup_enable((gpio_num_t)KB_INT, GPIO_INTR_HIGH_LEVEL);
#else
gpio_wakeup_enable((gpio_num_t)KB_INT, GPIO_INTR_LOW_LEVEL);
#endif // KB_INT_WAKE_ON_HIGH
#endif // KB_INT
#ifdef BOARD_PCA9535_INT
// Side-key interrupt line from PCA9535 expander (active low).
gpio_wakeup_enable((gpio_num_t)BOARD_PCA9535_INT, GPIO_INTR_LOW_LEVEL);
#endif
#ifdef BUTTON_PIN
gpio_num_t pin = (gpio_num_t)(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN);
gpio_wakeup_enable(pin, GPIO_INTR_LOW_LEVEL);
#endif
#if defined(INPUTDRIVER_TWO_WAY_ROCKER_BTN) || defined(INPUTDRIVER_ENCODER_BTN)
#if defined(INPUTDRIVER_TWO_WAY_ROCKER_BTN)
#define INPUTDRIVER_WAKE_BTN_PIN INPUTDRIVER_TWO_WAY_ROCKER_BTN
#else
#define INPUTDRIVER_WAKE_BTN_PIN INPUTDRIVER_ENCODER_BTN
#endif
gpio_wakeup_enable((gpio_num_t)INPUTDRIVER_WAKE_BTN_PIN, GPIO_INTR_LOW_LEVEL);
#endif
#if defined(WAKE_ON_TOUCH)
gpio_wakeup_enable((gpio_num_t)SCREEN_TOUCH_INT, GPIO_INTR_LOW_LEVEL);
#endif
enableLoraInterrupt();
#ifdef PMU_IRQ
// wake due to PMU can happen repeatedly if there is no battery installed or the battery fills
if (pmu_found)
gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL); // pmu irq
#endif
auto res = esp_sleep_enable_gpio_wakeup();
if (res != ESP_OK) {
LOG_ERROR("esp_sleep_enable_gpio_wakeup result %d", res);
}
assert(res == ESP_OK);
res = esp_sleep_enable_timer_wakeup(sleepUsec);
if (res != ESP_OK) {
LOG_ERROR("esp_sleep_enable_timer_wakeup result %d", res);
}
assert(res == ESP_OK);
console->flush();
res = esp_light_sleep_start();
if (res != ESP_OK) {
LOG_ERROR("esp_light_sleep_start result %d", res);
}
// commented out because it's not that crucial;
// if it sporadically happens the node will go into light sleep during the next round
// assert(res == ESP_OK);
#ifdef ROTARY_PRESS
gpio_wakeup_disable((gpio_num_t)ROTARY_PRESS);
#endif
#ifdef KB_INT
gpio_wakeup_disable((gpio_num_t)KB_INT);
#endif
#ifdef BOARD_PCA9535_INT
gpio_wakeup_disable((gpio_num_t)BOARD_PCA9535_INT);
#endif
#ifdef BUTTON_PIN
// Disable wake-on-button interrupt. Re-attach normal button-interrupts
gpio_wakeup_disable(pin);
#endif
#ifdef INPUTDRIVER_WAKE_BTN_PIN
gpio_wakeup_disable((gpio_num_t)INPUTDRIVER_WAKE_BTN_PIN);
#undef INPUTDRIVER_WAKE_BTN_PIN
#endif
#if defined(WAKE_ON_TOUCH)
gpio_wakeup_disable((gpio_num_t)SCREEN_TOUCH_INT);
#endif
#if !defined(SOC_PM_SUPPORT_EXT_WAKEUP) && defined(LORA_DIO1) && (LORA_DIO1 != RADIOLIB_NC)
if (radioType != RF95_RADIO) {
gpio_wakeup_disable((gpio_num_t)LORA_DIO1);
}
#endif
#if defined(RF95_IRQ) && (RF95_IRQ != RADIOLIB_NC)
if (radioType == RF95_RADIO) {
gpio_wakeup_disable((gpio_num_t)RF95_IRQ);
}
#endif
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
notifyLightSleepEnd.notifyObservers(cause); // Button interrupts are reattached here
if (cause == ESP_SLEEP_WAKEUP_GPIO) {
LOG_INFO("Exit light sleep gpio");
// If we woke because of a GPIO, it's possible power needs to run to handle.
power->setIntervalFromNow(0);
runASAP = true;
} else {
LOG_INFO("Exit light sleep cause: %d", cause);
}
return cause;
}
// not legal on the stock android ESP build
/**
* enable modem sleep mode as needed and available. Should lower our CPU current draw to an average of about 20mA.
*
* per https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/power_management.html
*
* supposedly according to https://github.com/espressif/arduino-esp32/issues/475 this is already done in arduino
*/
void enableModemSleep()
{
static esp_pm_config_t esp32_config; // filled with zeros because bss
#if CONFIG_IDF_TARGET_ESP32S3
esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ;
#elif CONFIG_IDF_TARGET_ESP32S2
esp32_config.max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ;
#elif CONFIG_IDF_TARGET_ESP32C6
esp32_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
#elif CONFIG_IDF_TARGET_ESP32C3
esp32_config.max_freq_mhz = CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ;
#elif CONFIG_IDF_TARGET_ESP32P4
#if CONFIG_ESP32P4_REV_MIN_FULL < 300
esp32_config.max_freq_mhz = 360;
#else
esp32_config.max_freq_mhz = 400;
#endif
#else
esp32_config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
#endif
esp32_config.min_freq_mhz = 20; // 10Mhz is minimum recommended
esp32_config.light_sleep_enable = false;
int rv = esp_pm_configure(&esp32_config);
LOG_DEBUG("Sleep request result %x", rv);
}
bool shouldLoraWake(uint32_t msecToWake)
{
return msecToWake < portMAX_DELAY && (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE);
}
void enableLoraInterrupt()
{
#if defined(LORA_DIO1_EXTENDED_IO)
// DIO1 is a virtual pin on an I/O expander - it cannot be a GPIO wakeup source
#elif SOC_PM_SUPPORT_EXT_WAKEUP && defined(LORA_DIO1) && (LORA_DIO1 != RADIOLIB_NC)
esp_err_t res;
res = gpio_pulldown_en((gpio_num_t)LORA_DIO1);
if (res != ESP_OK) {
LOG_ERROR("gpio_pulldown_en(LORA_DIO1) result %d", res);
}
#if defined(LORA_RESET) && (LORA_RESET != RADIOLIB_NC)
res = gpio_pullup_en((gpio_num_t)LORA_RESET);
if (res != ESP_OK) {
LOG_ERROR("gpio_pullup_en(LORA_RESET) result %d", res);
}
#endif
#if defined(LORA_CS) && (LORA_CS != RADIOLIB_NC) && !defined(ELECROW_PANEL)
gpio_pullup_en((gpio_num_t)LORA_CS);
#endif
#if HAS_LORA_FEM
loraFEMInterface.setRxModeEnableWhenMCUSleep();
#endif
LOG_INFO("Wake on LORA_DIO1 (GPIO%02d) gpio interrupt", LORA_DIO1);
gpio_wakeup_enable((gpio_num_t)LORA_DIO1, GPIO_INTR_HIGH_LEVEL);
#elif defined(LORA_DIO1) && (LORA_DIO1 != RADIOLIB_NC)
if (radioType != RF95_RADIO) {
LOG_INFO("Wake on LORA_DIO1 (GPIO%02d) gpio interrupt", LORA_DIO1);
gpio_wakeup_enable((gpio_num_t)LORA_DIO1, GPIO_INTR_HIGH_LEVEL); // SX126x/SX128x interrupt, active high
}
#endif
#if defined(RF95_IRQ) && (RF95_IRQ != RADIOLIB_NC)
if (radioType == RF95_RADIO) {
LOG_INFO("Wake on RF95_IRQ (GPIO%02d) gpio interrupt", RF95_IRQ);
gpio_wakeup_enable((gpio_num_t)RF95_IRQ, GPIO_INTR_HIGH_LEVEL); // RF95 interrupt, active high
}
#endif
}
#endif