Tracker role wakeup and sleep cycle when power.is_power_saving true (#2846)

* WIP

* Sleepy sleepy low power tracker

* Sleepy tracker clear

* NRF52 PoC

* Simplify NRF52 "sleep"

* Trackers aren't polite

* Remove unnecessary include

* Removed accidental commit

* Fixed not-so-sleepy T-Beam due to button gpio mask precendence

* Added sleepOnNextExecution for allowing fulfillment of pending messages before shutting down

* Cleanup

* Don't wantResponse for trackers

* Heltec wireless tracker doesn't like the button interrupt (maybe all s3 because user button press doubles as bootloader mode trigger?)
This commit is contained in:
Ben Meadors 2023-09-30 21:09:17 -05:00 committed by GitHub
parent 6ebec8fcd9
commit 1552aa0081
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 68 additions and 33 deletions

View File

@ -414,7 +414,7 @@ void Power::shutdown()
#ifdef PIN_LED3 #ifdef PIN_LED3
ledOff(PIN_LED2); ledOff(PIN_LED2);
#endif #endif
doDeepSleep(DELAY_FOREVER); doDeepSleep(DELAY_FOREVER, false);
#endif #endif
} }

View File

@ -45,7 +45,7 @@ static void sdsEnter()
{ {
LOG_DEBUG("Enter state: SDS\n"); LOG_DEBUG("Enter state: SDS\n");
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw // FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(getConfiguredOrDefaultMs(config.power.sds_secs)); doDeepSleep(getConfiguredOrDefaultMs(config.power.sds_secs), false);
} }
extern Power *power; extern Power *power;

View File

@ -67,6 +67,7 @@ class OSThread : public Thread
* Returns desired period for next invocation (or RUN_SAME for no change) * Returns desired period for next invocation (or RUN_SAME for no change)
*/ */
virtual int32_t runOnce() = 0; virtual int32_t runOnce() = 0;
bool sleepOnNextExecution = false;
// Do not override this // Do not override this
virtual void run(); virtual void run();

View File

@ -1,4 +1,5 @@
#include "PositionModule.h" #include "PositionModule.h"
#include "GPS.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h" #include "RTC.h"
@ -7,6 +8,8 @@
#include "airtime.h" #include "airtime.h"
#include "configuration.h" #include "configuration.h"
#include "gps/GeoCoord.h" #include "gps/GeoCoord.h"
#include "sleep.h"
#include "target_specific.h"
PositionModule *positionModule; PositionModule *positionModule;
@ -15,7 +18,21 @@ PositionModule::PositionModule()
concurrency::OSThread("PositionModule") concurrency::OSThread("PositionModule")
{ {
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup) if (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER)
setIntervalFromNow(60 * 1000);
// Power saving trackers should clear their position on startup to avoid waking up and sending a stale position
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER && config.power.is_power_saving) {
clearPosition();
}
}
void PositionModule::clearPosition()
{
LOG_DEBUG("Clearing position on startup for sleepy tracker (ー。ー) zzz\n");
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
node->position.latitude_i = 0;
node->position.longitude_i = 0;
} }
bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Position *pptr) bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Position *pptr)
@ -148,7 +165,7 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
} }
p->to = dest; p->to = dest;
p->decoded.want_response = wantReplies; p->decoded.want_response = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ? false : wantReplies;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER) if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE; p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else else
@ -159,10 +176,23 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
p->channel = channel; p->channel = channel;
service.sendToMesh(p, RX_SRC_LOCAL, true); service.sendToMesh(p, RX_SRC_LOCAL, true);
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER && config.power.is_power_saving) {
LOG_DEBUG("Starting next execution in 3 seconds and then going to sleep.\n");
sleepOnNextExecution = true;
setIntervalFromNow(3000);
}
} }
int32_t PositionModule::runOnce() int32_t PositionModule::runOnce()
{ {
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
uint32_t nightyNightMs = getConfiguredOrDefaultMs(config.position.position_broadcast_secs);
LOG_DEBUG("Sleeping for %ims, then awaking to send position again.\n", nightyNightMs);
doDeepSleep(nightyNightMs, false);
}
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum()); meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
// We limit our GPS broadcasts to a max rate // We limit our GPS broadcasts to a max rate
@ -172,7 +202,7 @@ int32_t PositionModule::runOnce()
if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) { if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) {
// Only send packets if the channel is less than 40% utilized. // Only send packets if the channel is less than 40% utilized.
if (airTime->isTxAllowedChannelUtil()) { if (airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER)) {
if (hasValidPosition(node)) { if (hasValidPosition(node)) {
lastGpsSend = now; lastGpsSend = now;

View File

@ -49,6 +49,9 @@ class PositionModule : public ProtobufModule<meshtastic_Position>, private concu
private: private:
struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition); struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition);
/** Only used in power saving trackers for now */
void clearPosition();
}; };
struct SmartPosition { struct SmartPosition {

View File

@ -200,22 +200,15 @@ void cpuDeepSleep(uint32_t msecToWake)
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#ifdef BUTTON_PIN #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.
#if SOC_RTCIO_HOLD_SUPPORTED #if SOC_PM_SUPPORT_EXT_WAKEUP && !defined(HELTEC_WIRELESS_TRACKER)
uint64_t gpioMask = (1ULL << config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN); esp_sleep_enable_ext0_wakeup((gpio_num_t)(1ULL << (config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN)), LOW);
#endif #endif
#ifdef BUTTON_NEED_PULLUP #ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)BUTTON_PIN); gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#endif #endif
// 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);
#if SOC_PM_SUPPORT_EXT_WAKEUP
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
#endif
#endif #endif
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs

View File

@ -184,11 +184,17 @@ void cpuDeepSleep(uint32_t msecToWake)
// FIXME, use non-init RAM per // FIXME, use non-init RAM per
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled // https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER && config.power.is_power_saving == true) {
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
delay(msecToWake);
NVIC_SystemReset();
} else {
auto ok = sd_power_system_off(); auto ok = sd_power_system_off();
if (ok != NRF_SUCCESS) { if (ok != NRF_SUCCESS) {
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n"); LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n");
NRF_POWER->SYSTEMOFF = 1; NRF_POWER->SYSTEMOFF = 1;
} }
}
// The following code should not be run, because we are off // The following code should not be run, because we are off
while (1) { while (1) {

View File

@ -135,8 +135,9 @@ bool doPreflightSleep()
} }
/// Tell devices we are going to sleep and wait for them to handle things /// Tell devices we are going to sleep and wait for them to handle things
static void waitEnterSleep() static void waitEnterSleep(bool skipPreflight = false)
{ {
if (!skipPreflight) {
uint32_t now = millis(); uint32_t now = millis();
while (!doPreflightSleep()) { while (!doPreflightSleep()) {
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives) delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
@ -147,6 +148,7 @@ static void waitEnterSleep()
break; break;
} }
} }
}
// Code that still needs to be moved into notifyObservers // Code that still needs to be moved into notifyObservers
console->flush(); // send all our characters before we stop cpu clock console->flush(); // send all our characters before we stop cpu clock
@ -155,7 +157,7 @@ static void waitEnterSleep()
notifySleep.notifyObservers(NULL); notifySleep.notifyObservers(NULL);
} }
void doDeepSleep(uint32_t msecToWake) void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false)
{ {
if (INCLUDE_vTaskSuspend && (msecToWake == portMAX_DELAY)) { if (INCLUDE_vTaskSuspend && (msecToWake == portMAX_DELAY)) {
LOG_INFO("Entering deep sleep forever\n"); LOG_INFO("Entering deep sleep forever\n");
@ -165,7 +167,7 @@ void doDeepSleep(uint32_t msecToWake)
// not using wifi yet, but once we are this is needed to shutoff the radio hw // not using wifi yet, but once we are this is needed to shutoff the radio hw
// esp_wifi_stop(); // esp_wifi_stop();
waitEnterSleep(); waitEnterSleep(skipPreflight);
notifyDeepSleep.notifyObservers(NULL); notifyDeepSleep.notifyObservers(NULL);
screen->doDeepSleep(); // datasheet says this will draw only 10ua screen->doDeepSleep(); // datasheet says this will draw only 10ua
@ -227,7 +229,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
{ {
// LOG_DEBUG("Enter light sleep\n"); // LOG_DEBUG("Enter light sleep\n");
waitEnterSleep(); waitEnterSleep(false);
uint64_t sleepUsec = sleepMsec * 1000LL; uint64_t sleepUsec = sleepMsec * 1000LL;

View File

@ -4,7 +4,7 @@
#include "Observer.h" #include "Observer.h"
#include "configuration.h" #include "configuration.h"
void doDeepSleep(uint32_t msecToWake), cpuDeepSleep(uint32_t msecToWake); void doDeepSleep(uint32_t msecToWake, bool skipPreflight), cpuDeepSleep(uint32_t msecToWake);
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
#include "esp_sleep.h" #include "esp_sleep.h"