mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-24 01:16:55 +00:00
begin cleanup of sleep code for new state machine
This commit is contained in:
parent
4fa6b64c3d
commit
045529d91f
8
TODO.md
8
TODO.md
@ -1,8 +1,8 @@
|
||||
|
||||
# High priority
|
||||
|
||||
Items to complete before the first alpha release.
|
||||
|
||||
* implement sleep state machine
|
||||
* have gps implement canSleep(), print nmea for debugging and discard buffers on the way into sleep
|
||||
* implement CustomRF95::canSleep
|
||||
* make gps prevent light sleep if we are waiting for data
|
||||
@ -13,10 +13,9 @@ for it (because it will redownload the nodedb when it comes back)
|
||||
* don't enter light sleep while the screen is on
|
||||
* any time we wake from light sleep, briefly blink the led
|
||||
|
||||
* turn light sleep on agressively (while lora is on but BLE off)
|
||||
* turn light sleep on aggressively (while lora is on but BLE off)
|
||||
* retest BLE software update for both board types
|
||||
* default to enter deep sleep if no LORA received for two hours (indicates user has probably left the meshS)
|
||||
* article writeup for hackaday?
|
||||
* send note about Adafruit Clue
|
||||
* send note to the guy who designed the cases
|
||||
* update the prebuilt bins for different regulatory regions
|
||||
@ -47,7 +46,8 @@ Items to complete before the first beta release.
|
||||
* How do avalanche beacons work? Could this do that as well? possibly by using beacon mode feature of the RF95?
|
||||
* use std::map<NodeInfo*, std::string> in node db
|
||||
* make a HAM build: yep - that's a great idea. I'll add it to the TODO. should be pretty painless - just a new frequency list, a bool to say 'never do encryption' and use hte callsign as that node's unique id. -from Girts
|
||||
* add frequency hopping
|
||||
* add frequency hopping
|
||||
* publish update articles on the web
|
||||
|
||||
# Pre-beta priority
|
||||
|
||||
|
@ -2,21 +2,36 @@ This is a mini design doc for various core behaviors...
|
||||
|
||||
# Rules for sleep
|
||||
|
||||
## Terms
|
||||
## States
|
||||
|
||||
From lower to higher power consumption.
|
||||
|
||||
* Super-deep-sleep (SDS) - everything is off, CPU, radio, bluetooth, GPS. Only wakes due to timer or button press
|
||||
|
||||
* deep-sleep (DS) - CPU is off, radio is on, bluetooth and GPS is off. Note: This mode is never used currently, because it only saves 1.5mA vs light-sleep
|
||||
onEntry: setBluetoothOn(false), call doDeepSleep
|
||||
onExit: (standard bootup code, starts in DARK)
|
||||
|
||||
* light-sleep (LS) - CPU is suspended (RAM stays alive), radio is on, bluetooth is off, GPS is off. Note: currently GPS is not turned
|
||||
off during light sleep, but there is a TODO item to fix this.
|
||||
* No bluetooth (NB) - CPU is running, radio is on, GPS is on but bluetooth is off, screen is off. Note: We might not need this mode
|
||||
* running dark (DARK) - Everything is on except screen
|
||||
* full on (ON) - Everything is on
|
||||
onEntry: setBluetoothOn(false), setGPSPower(false), call doLightSleep
|
||||
onExit: start trying to get gps lock: gps.startLock(), once lock arrives service.sendPosition(BROADCAST)
|
||||
|
||||
* No bluetooth (NB) - CPU is running, radio is on, GPS is on but bluetooth is off, screen is off.
|
||||
onEntry: setGPSPower(true), setBluetoothOn(false)
|
||||
onExit:
|
||||
|
||||
* running dark (DARK) - Everything is on except screen
|
||||
onEntry: setBluetoothOn(true)
|
||||
onExit:
|
||||
|
||||
* full on (ON) - Everything is on
|
||||
onEntry: setBluetoothOn(true), screen.setOn(true)
|
||||
onExit: screen.setOn(false)
|
||||
|
||||
## Behavior
|
||||
|
||||
### things that increase CPU activity
|
||||
### events that increase CPU activity
|
||||
|
||||
* While in LS: Once every position_broadcast_secs (default 15 mins) - the unit will wake into DARK mode and broadcast a "networkPing" (our position) and stay alive for wait_bluetooth_secs (default 30 seconds). This allows other nodes to have a record of our last known position if we go away and allows a paired phone to hear from us and download messages.
|
||||
* While in LS: Every send_owner_interval (defaults to 4, i.e. one hour), when we wake to send our position we _also_ broadcast our owner. This lets new nodes on the network find out about us or correct duplicate node number assignments.
|
||||
@ -24,10 +39,11 @@ off during light sleep, but there is a TODO item to fix this.
|
||||
* While in LS/NB/DARK: If we receive text messages, we go to full ON mode for screen_on_secs (same as if user pressed a button)
|
||||
* While in LS: if we receive packets on the radio we will wake and handle them and stay awake in NB mode for min_wake_secs (default 10 seconds) - if we don't have packets we need to deliver to our phone. If we do have packets the phone would want we instead stay in DARK mode for wait_bluetooth secs.
|
||||
|
||||
### Things that decrease cpu activity
|
||||
### events that decrease cpu activity
|
||||
|
||||
* If time since last contact by our phone exceeds phone_timeout_secs (15 minutes) and we are in DARK mode, we transition down into NB mode
|
||||
* If nothing above is forcing us to stay in a higher mode (wait_bluetooth_secs, screen_on_secs, or min_wake_secs) we will lower down
|
||||
* While in ON: If it has been more than screen_on_secs since a press, lower to DARK
|
||||
* While in DARK: If time since last contact by our phone exceeds phone_timeout_secs (15 minutes), we transition down into NB mode
|
||||
* While in DARK or NB: If nothing above is forcing us to stay in a higher mode (wait_bluetooth_secs, min_wake_secs) we will lower down
|
||||
into either LS or SDS levels. If either phone_sds_timeout_secs (default 1 hr) or mesh_sds_timeout_secs (default 1 hr) are exceeded we will lower into SDS mode for sds_secs (or a button press). Otherwise we will lower into LS mode for ls_secs (default 1 hr) (or until an interrupt, button press)
|
||||
|
||||
TODO: Eventually these scheduled intervals should be synchronized to the GPS clock, so that we can consider leaving the lora receiver off to save even more power.
|
||||
|
@ -64,6 +64,7 @@ lib_deps =
|
||||
OneButton
|
||||
CRC32 ; explicitly needed because dependency is missing in the ble ota update lib
|
||||
Wire ; explicitly needed here because the AXP202 library forgets to add it
|
||||
https://github.com/geeksville/arduino-fsm.git
|
||||
|
||||
;[env:tbeam]
|
||||
;board = ttgo-t-beam
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
4
src/main.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
extern bool axp192_found;
|
||||
extern bool ssd1306_found;
|
241
src/main.ino
241
src/main.ino
@ -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
|
||||
|
||||
|
@ -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
252
src/sleep.cpp
Normal 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
17
src/sleep.h
Normal 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;
|
Loading…
Reference in New Issue
Block a user