begin cleanup of sleep code for new state machine

This commit is contained in:
geeksville
2020-02-21 17:01:26 -08:00
parent 4fa6b64c3d
commit 045529d91f
11 changed files with 345 additions and 269 deletions

View File

@@ -173,7 +173,7 @@ uint32_t sendOwnerCb()
{
service.sendOurOwner();
return radioConfig.preferences.send_owner_secs * 1000;
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
}
Periodic sendOwnerPeriod(sendOwnerCb);

View File

@@ -49,8 +49,13 @@ void NodeDB::init()
devicestate.node_db_count = 0;
devicestate.receive_queue_count = 0;
radioConfig.preferences.send_owner_secs = 60 * 60; // default to once an hour
radioConfig.preferences.position_broadcast_secs = 15 * 60; // default to once every 15 mins
radioConfig.preferences.send_owner_interval = 4; // per sw-design.md
radioConfig.preferences.position_broadcast_secs = 20; // 15 * 60;
radioConfig.preferences.wait_bluetooth_secs = 10; // 30;
radioConfig.preferences.screen_on_secs = 30;
radioConfig.preferences.mesh_sds_timeout_secs = 60 * 60;
radioConfig.preferences.phone_sds_timeout_sec = 60 * 60;
radioConfig.preferences.sds_secs = 60 * 60;
#ifdef GPS_RX_PIN
// some hardware defaults to have a built in GPS
@@ -244,7 +249,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
switch (p.which_variant)
{
case SubPacket_position_tag: {
case SubPacket_position_tag:
{
// we carefully preserve the old time, because we always trust our local timestamps more
uint32_t oldtime = info->position.time;
info->position = p.variant.position;

View File

@@ -57,11 +57,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define DEBUG_PORT Serial // Serial debug port
#define SERIAL_BAUD 115200 // Serial debug baud rate
#define SLEEP_MSECS (30 * 24 * 60 * 60 * 1000LL) // Sleep for these many millis (or a button press or a lora msg?)
#define MESSAGE_TO_SLEEP_DELAY 5000 // Time after message before going to sleep
#define LOGO_DELAY 2000 // Time to show logo on first boot
#define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found
// If not defined, we will wait for lock forever

4
src/main.h Normal file
View File

@@ -0,0 +1,4 @@
#pragma once
extern bool axp192_found;
extern bool ssd1306_found;

View File

@@ -36,6 +36,7 @@
#include "esp32/pm.h"
#include "esp_pm.h"
#include "MeshRadio.h"
#include "sleep.h"
#ifdef T_BEAM_V10
#include "axp20x.h"
@@ -48,11 +49,6 @@ bool isUSBPowered = false;
bool ssd1306_found = false;
bool axp192_found = false;
bool packetSent, packetQueued;
// deep sleep support
RTC_DATA_ATTR int bootCount = 0;
esp_sleep_source_t wakeCause; // the reason we booted this time
#define xstr(s) str(s)
#define str(s) #s
@@ -61,224 +57,6 @@ esp_sleep_source_t wakeCause; // the reason we booted this time
// 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)
{
setCpuFrequencyMhz(on ? 240 : 80);
}
static void setLed(bool ledOn)
{
#ifdef LED_PIN
// toggle the led so we can get some rough sense of how often loop is pausing
digitalWrite(LED_PIN, ledOn);
#endif
#ifdef T_BEAM_V10
if (axp192_found)
{
// blink the axp led
axp.setChgLEDMode(ledOn ? AXP20X_LED_LOW_LEVEL : AXP20X_LED_OFF);
}
#endif
}
void setGPSPower(bool on)
{
#ifdef T_BEAM_V10
if (axp192_found)
axp.setPowerOutPut(AXP192_LDO3, on ? AXP202_ON : AXP202_OFF); // GPS main power
#endif
}
void doDeepSleep(uint64_t msecToWake)
{
DEBUG_MSG("Entering deep sleep for %llu seconds\n", msecToWake / 1000);
// not using wifi yet, but once we are this is needed to shutoff the radio hw
// esp_wifi_stop();
BLEDevice::deinit(false); // We are required to shutdown bluetooth before deep or light sleep
screen_off(); // datasheet says this will draw only 10ua
// Put radio in sleep mode (will still draw power but only 0.2uA)
service.radio.rf95.sleep();
nodeDB.saveToDisk();
#ifdef RESET_OLED
digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power
#endif
#ifdef VEXT_ENABLE
digitalWrite(VEXT_ENABLE, 1); // turn off the display power
#endif
setLed(false);
#ifdef T_BEAM_V10
if (axp192_found)
{
// 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 receving 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 listinging for packets
// all the time.
// axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // LORA radio
setGPSPower(false);
}
#endif
/*
Some ESP32 IOs have internal pullups or pulldowns, which are enabled by default.
If an external circuit drives this pin in deep sleep mode, current consumption may
increase due to current flowing through these pullups and pulldowns.
To isolate a pin, preventing extra current draw, call rtc_gpio_isolate() function.
For example, on ESP32-WROVER module, GPIO12 is pulled up externally.
GPIO12 also has an internal pulldown in the ESP32 chip. This means that in deep sleep,
some current will flow through these external and internal resistors, increasing deep
sleep current above the minimal possible value.
Note: we don't isolate pins that are used for the LORA, LED, i2c, spi or the wake button
*/
static const uint8_t rtcGpios[] = {/* 0, */ 2,
/* 4, */
#ifndef USE_JTAG
12, 13, /* 14, */ /* 15, */
#endif
/* 25, */ 26, /* 27, */
32, 33, 34, 35, 36, 37, /* 38, */ 39};
for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
// to detect wake and in normal operation the external part drives them hard.
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#ifdef BUTTON_PIN
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
uint64_t gpioMask = (1ULL << BUTTON_PIN);
// Not needed because both of the current boards have external pullups
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of just the first)
// gpio_pullup_en((gpio_num_t)BUTTON_PIN);
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
#endif
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
}
#include "esp_bt_main.h"
bool bluetoothOn = true; // we turn it on during setup() so default on
void setBluetoothEnable(bool on)
{
if (on != bluetoothOn)
{
DEBUG_MSG("Setting bluetooth enable=%d\n", on);
bluetoothOn = on;
if (on)
{
if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK)
DEBUG_MSG("error reenabling bt controller\n");
if (esp_bluedroid_enable() != ESP_OK)
DEBUG_MSG("error reenabling bluedroid\n");
}
else
{
if (esp_bluedroid_disable() != ESP_OK)
DEBUG_MSG("error disabling bluedroid\n");
if (esp_bt_controller_disable() != ESP_OK)
DEBUG_MSG("error disabling bt controller\n");
}
}
}
/**
* 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
*/
void doLightSleep(uint32_t sleepMsec = 20 * 1000) // FIXME, use a more reasonable default
{
DEBUG_MSG("Enter light sleep\n");
uint64_t sleepUsec = sleepMsec * 1000LL;
gps.prepareSleep(); // abandon in-process parsing
setLed(false); // Never leave led on while in light sleep
// NOTE! ESP docs say we must disable bluetooth and wifi before light sleep
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
gpio_wakeup_enable((gpio_num_t)BUTTON_PIN, GPIO_INTR_LOW_LEVEL); // when user presses, this button goes low
gpio_wakeup_enable((gpio_num_t)DIO0_GPIO, GPIO_INTR_HIGH_LEVEL); // RF95 interrupt, active high
#ifdef PMU_IRQ
gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_HIGH_LEVEL); // pmu irq
#endif
esp_sleep_enable_gpio_wakeup();
esp_sleep_enable_timer_wakeup(sleepUsec);
esp_light_sleep_start();
DEBUG_MSG("Exit light sleep\n");
}
/**
* 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_esp32_t config; // filled with zeros because bss
config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
config.min_freq_mhz = 10; // 10Mhz is minimum recommended
config.light_sleep_enable = false;
DEBUG_MSG("Sleep request result %x\n", esp_pm_configure(&config));
}
void sleep()
{
#ifdef SLEEP_MSECS
// If the user has a screen, tell them we are about to sleep
if (ssd1306_found)
{
// Show the going to sleep message on the screen
char buffer[20];
snprintf(buffer, sizeof(buffer), "Sleeping in %3.1fs\n", (MESSAGE_TO_SLEEP_DELAY / 1000.0));
screen_print(buffer);
// Wait for MESSAGE_TO_SLEEP_DELAY millis to sleep
delay(MESSAGE_TO_SLEEP_DELAY);
}
// We sleep for the interval between messages minus the current millis
// this way we distribute the messages evenly every SEND_INTERVAL millis
doDeepSleep(SLEEP_MSECS);
#endif
}
void scanI2Cdevice(void)
{
@@ -409,21 +187,6 @@ void axp192Init()
#endif
}
// Perform power on init that we do on each wake from deep sleep
void initDeepSleep()
{
bootCount++;
wakeCause = esp_sleep_get_wakeup_cause();
/*
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];
*/
DEBUG_MSG("booted, wake cause %d (boot count %d)\n", wakeCause, bootCount);
}
const char *getDeviceName()
{
@@ -617,7 +380,7 @@ void loop()
// !isUSBPowered <- doesn't work yet because the axp192 isn't letting the battery fully charge when we are awake - FIXME
if (millis() - lastPressMs > MINWAKE_MSECS)
{
sleep();
doDeepSleep(radioConfig.preferences.sds_secs);
}
#endif

View File

@@ -33,8 +33,8 @@ typedef enum _ChannelSettings_ModemConfig {
typedef enum _DeviceState_Version {
DeviceState_Version_Unset = 0,
DeviceState_Version_Minimum = 15,
DeviceState_Version_Current = 15
DeviceState_Version_Minimum = 16,
DeviceState_Version_Current = 16
} DeviceState_Version;
/* Struct definitions */
@@ -69,8 +69,15 @@ typedef struct _Position {
typedef struct _RadioConfig_UserPreferences {
uint32_t position_broadcast_secs;
uint32_t send_owner_secs;
uint32_t send_owner_interval;
uint32_t num_missed_to_fail;
uint32_t wait_bluetooth_secs;
uint32_t screen_on_secs;
uint32_t phone_timeout_secs;
uint32_t phone_sds_timeout_sec;
uint32_t mesh_sds_timeout_secs;
uint32_t sds_secs;
uint32_t ls_secs;
bool keep_all_packets;
bool promiscuous_mode;
} RadioConfig_UserPreferences;
@@ -175,7 +182,7 @@ typedef struct _ToRadio {
#define MeshPacket_init_default {0, 0, false, SubPacket_init_default, 0}
#define ChannelSettings_init_default {0, 0, _ChannelSettings_ModemConfig_MIN, {0}, ""}
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
#define MyNodeInfo_init_default {0, 0, 0}
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default}, _DeviceState_Version_MIN, false, MeshPacket_init_default}
@@ -188,7 +195,7 @@ typedef struct _ToRadio {
#define MeshPacket_init_zero {0, 0, false, SubPacket_init_zero, 0}
#define ChannelSettings_init_zero {0, 0, _ChannelSettings_ModemConfig_MIN, {0}, ""}
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
#define MyNodeInfo_init_zero {0, 0, 0}
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero}, _DeviceState_Version_MIN, false, MeshPacket_init_zero}
@@ -213,8 +220,15 @@ typedef struct _ToRadio {
#define Position_from_hardware_tag 5
#define Position_time_tag 6
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
#define RadioConfig_UserPreferences_send_owner_secs_tag 2
#define RadioConfig_UserPreferences_send_owner_interval_tag 2
#define RadioConfig_UserPreferences_num_missed_to_fail_tag 3
#define RadioConfig_UserPreferences_wait_bluetooth_secs_tag 4
#define RadioConfig_UserPreferences_screen_on_secs_tag 5
#define RadioConfig_UserPreferences_phone_timeout_secs_tag 6
#define RadioConfig_UserPreferences_phone_sds_timeout_sec_tag 7
#define RadioConfig_UserPreferences_mesh_sds_timeout_secs_tag 8
#define RadioConfig_UserPreferences_sds_secs_tag 9
#define RadioConfig_UserPreferences_ls_secs_tag 10
#define RadioConfig_UserPreferences_keep_all_packets_tag 100
#define RadioConfig_UserPreferences_promiscuous_mode_tag 101
#define User_id_tag 1
@@ -311,8 +325,15 @@ X(a, STATIC, OPTIONAL, MESSAGE, channel_settings, 2)
#define RadioConfig_UserPreferences_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, position_broadcast_secs, 1) \
X(a, STATIC, SINGULAR, UINT32, send_owner_secs, 2) \
X(a, STATIC, SINGULAR, UINT32, send_owner_interval, 2) \
X(a, STATIC, SINGULAR, UINT32, num_missed_to_fail, 3) \
X(a, STATIC, SINGULAR, UINT32, wait_bluetooth_secs, 4) \
X(a, STATIC, SINGULAR, UINT32, screen_on_secs, 5) \
X(a, STATIC, SINGULAR, UINT32, phone_timeout_secs, 6) \
X(a, STATIC, SINGULAR, UINT32, phone_sds_timeout_sec, 7) \
X(a, STATIC, SINGULAR, UINT32, mesh_sds_timeout_secs, 8) \
X(a, STATIC, SINGULAR, UINT32, sds_secs, 9) \
X(a, STATIC, SINGULAR, UINT32, ls_secs, 10) \
X(a, STATIC, SINGULAR, BOOL, keep_all_packets, 100) \
X(a, STATIC, SINGULAR, BOOL, promiscuous_mode, 101)
#define RadioConfig_UserPreferences_CALLBACK NULL
@@ -402,11 +423,11 @@ extern const pb_msgdesc_t ToRadio_msg;
#define SubPacket_size 261
#define MeshPacket_size 292
#define ChannelSettings_size 50
#define RadioConfig_size 78
#define RadioConfig_UserPreferences_size 24
#define RadioConfig_size 120
#define RadioConfig_UserPreferences_size 66
#define NodeInfo_size 157
#define MyNodeInfo_size 24
#define DeviceState_size 15037
#define DeviceState_size 15079
#define FromRadio_size 301
#define ToRadio_size 295

252
src/sleep.cpp Normal file
View File

@@ -0,0 +1,252 @@
#include "configuration.h"
#include "rom/rtc.h"
#include <driver/rtc_io.h>
#include <TinyGPS++.h>
#include <Wire.h>
#include "BluetoothUtil.h"
#include "MeshBluetoothService.h"
#include "MeshService.h"
#include "GPS.h"
#include "screen.h"
#include "NodeDB.h"
#include "Periodic.h"
#include "esp32/pm.h"
#include "esp_pm.h"
#include "MeshRadio.h"
#include "main.h"
#ifdef T_BEAM_V10
#include "axp20x.h"
extern AXP20X_Class axp;
#endif
// deep sleep support
RTC_DATA_ATTR int bootCount = 0;
esp_sleep_source_t wakeCause; // the reason we booted this time
#define xstr(s) str(s)
#define str(s) #s
#include "esp_bt_main.h"
bool bluetoothOn = true; // we turn it on during setup() so default on
// -----------------------------------------------------------------------------
// 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)
{
setCpuFrequencyMhz(on ? 240 : 80);
}
void setLed(bool ledOn)
{
#ifdef LED_PIN
// toggle the led so we can get some rough sense of how often loop is pausing
digitalWrite(LED_PIN, ledOn);
#endif
#ifdef T_BEAM_V10
if (axp192_found)
{
// blink the axp led
axp.setChgLEDMode(ledOn ? AXP20X_LED_LOW_LEVEL : AXP20X_LED_OFF);
}
#endif
}
void setGPSPower(bool on)
{
#ifdef T_BEAM_V10
if (axp192_found)
axp.setPowerOutPut(AXP192_LDO3, on ? AXP202_ON : AXP202_OFF); // GPS main power
#endif
}
// Perform power on init that we do on each wake from deep sleep
void initDeepSleep()
{
bootCount++;
wakeCause = esp_sleep_get_wakeup_cause();
/*
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];
*/
DEBUG_MSG("booted, wake cause %d (boot count %d)\n", wakeCause, bootCount);
}
void doDeepSleep(uint64_t msecToWake)
{
DEBUG_MSG("Entering deep sleep for %llu seconds\n", msecToWake / 1000);
// not using wifi yet, but once we are this is needed to shutoff the radio hw
// esp_wifi_stop();
BLEDevice::deinit(false); // We are required to shutdown bluetooth before deep or light sleep
screen_off(); // datasheet says this will draw only 10ua
// Put radio in sleep mode (will still draw power but only 0.2uA)
service.radio.rf95.sleep();
nodeDB.saveToDisk();
#ifdef RESET_OLED
digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power
#endif
#ifdef VEXT_ENABLE
digitalWrite(VEXT_ENABLE, 1); // turn off the display power
#endif
setLed(false);
#ifdef T_BEAM_V10
if (axp192_found)
{
// 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 receving 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 listinging for packets
// all the time.
// axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // LORA radio
setGPSPower(false);
}
#endif
/*
Some ESP32 IOs have internal pullups or pulldowns, which are enabled by default.
If an external circuit drives this pin in deep sleep mode, current consumption may
increase due to current flowing through these pullups and pulldowns.
To isolate a pin, preventing extra current draw, call rtc_gpio_isolate() function.
For example, on ESP32-WROVER module, GPIO12 is pulled up externally.
GPIO12 also has an internal pulldown in the ESP32 chip. This means that in deep sleep,
some current will flow through these external and internal resistors, increasing deep
sleep current above the minimal possible value.
Note: we don't isolate pins that are used for the LORA, LED, i2c, spi or the wake button
*/
static const uint8_t rtcGpios[] = {/* 0, */ 2,
/* 4, */
#ifndef USE_JTAG
12, 13, /* 14, */ /* 15, */
#endif
/* 25, */ 26, /* 27, */
32, 33, 34, 35, 36, 37, /* 38, */ 39};
for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
// to detect wake and in normal operation the external part drives them hard.
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#ifdef BUTTON_PIN
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
uint64_t gpioMask = (1ULL << BUTTON_PIN);
// Not needed because both of the current boards have external pullups
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of just the first)
// gpio_pullup_en((gpio_num_t)BUTTON_PIN);
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
#endif
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
}
void setBluetoothEnable(bool on)
{
if (on != bluetoothOn)
{
DEBUG_MSG("Setting bluetooth enable=%d\n", on);
bluetoothOn = on;
if (on)
{
if (esp_bt_controller_enable(ESP_BT_MODE_BTDM) != ESP_OK)
DEBUG_MSG("error reenabling bt controller\n");
if (esp_bluedroid_enable() != ESP_OK)
DEBUG_MSG("error reenabling bluedroid\n");
}
else
{
if (esp_bluedroid_disable() != ESP_OK)
DEBUG_MSG("error disabling bluedroid\n");
if (esp_bt_controller_disable() != ESP_OK)
DEBUG_MSG("error disabling bt controller\n");
}
}
}
/**
* 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
*/
void doLightSleep(uint64_t sleepMsec) // FIXME, use a more reasonable default
{
DEBUG_MSG("Enter light sleep\n");
uint64_t sleepUsec = sleepMsec * 1000LL;
setBluetoothEnable(false); // has to be off before calling light sleep
gps.prepareSleep(); // abandon in-process parsing
setLed(false); // Never leave led on while in light sleep
// NOTE! ESP docs say we must disable bluetooth and wifi before light sleep
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
gpio_wakeup_enable((gpio_num_t)BUTTON_PIN, GPIO_INTR_LOW_LEVEL); // when user presses, this button goes low
gpio_wakeup_enable((gpio_num_t)DIO0_GPIO, GPIO_INTR_HIGH_LEVEL); // RF95 interrupt, active high
#ifdef PMU_IRQ
gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_HIGH_LEVEL); // pmu irq
#endif
esp_sleep_enable_gpio_wakeup();
esp_sleep_enable_timer_wakeup(sleepUsec);
esp_light_sleep_start();
DEBUG_MSG("Exit light sleep\n");
}
#if 0
// 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_esp32_t config; // filled with zeros because bss
config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
config.min_freq_mhz = 10; // 10Mhz is minimum recommended
config.light_sleep_enable = false;
DEBUG_MSG("Sleep request result %x\n", esp_pm_configure(&config));
}
#endif

17
src/sleep.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
void doDeepSleep(uint64_t msecToWake);
void doLightSleep(uint64_t msecToWake);
void setBluetoothEnable(bool on);
// Perform power on init that we do on each wake from deep sleep
void initDeepSleep();
void setCPUFast(bool on);
void setLed(bool ledOn);
extern int bootCount;
extern esp_sleep_source_t wakeCause;
// is bluetooth sw currently running?
extern bool bluetoothOn;