2021-06-27 17:56:28 +00:00
# include "configuration.h"
2020-03-18 20:51:32 +00:00
# include "PowerFSM.h"
# include "GPS.h"
2020-02-22 20:01:59 +00:00
# include "MeshService.h"
# include "NodeDB.h"
2020-07-07 08:46:49 +00:00
# include "graphics/Screen.h"
2020-09-21 19:41:39 +00:00
# include "main.h"
2020-03-18 20:51:32 +00:00
# include "sleep.h"
2020-04-10 19:18:48 +00:00
# include "target_specific.h"
2020-02-22 20:01:59 +00:00
2021-03-25 00:54:43 +00:00
/// Should we behave as if we have AC power now?
static bool isPowered ( )
{
2021-11-06 15:03:10 +00:00
// Completely circumvents the battery / power sensing logic and assumes constant power source
if ( radioConfig . preferences . always_powered ) {
return true ;
}
2021-03-25 00:54:43 +00:00
bool isRouter = radioConfig . preferences . is_router ;
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
// We assume routers might be powered all the time, but from a low current (solar) source
bool isLowPower = radioConfig . preferences . is_low_power | | isRouter ;
/* To determine if we're externally powered, assumptions
1 ) If we ' re powered up and there ' s no battery , we must be getting power externally . ( because we ' d be dead otherwise )
2 ) If we detect USB power from the power management chip , we must be getting power externally .
*/
return ! isLowPower & & powerStatus & & ( ! powerStatus - > getHasBattery ( ) | | powerStatus - > getHasUSB ( ) ) ;
}
2020-02-22 20:01:59 +00:00
static void sdsEnter ( )
{
2021-08-03 04:34:14 +00:00
DEBUG_MSG ( " Enter state: SDS \n " ) ;
2020-09-27 01:13:16 +00:00
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
2020-10-06 01:43:00 +00:00
doDeepSleep ( getPref_sds_secs ( ) * 1000LL ) ;
2020-02-22 20:01:59 +00:00
}
2020-03-25 19:25:46 +00:00
# include "error.h"
2020-06-10 21:11:43 +00:00
static uint32_t secsSlept ;
2020-02-22 20:01:59 +00:00
static void lsEnter ( )
{
2020-10-06 01:43:00 +00:00
DEBUG_MSG ( " lsEnter begin, ls_secs=%u \n " , getPref_ls_secs ( ) ) ;
2020-10-10 01:57:57 +00:00
screen - > setOn ( false ) ;
2020-06-10 21:11:43 +00:00
secsSlept = 0 ; // How long have we been sleeping this time
2020-03-05 02:59:10 +00:00
2021-08-03 04:07:32 +00:00
// DEBUG_MSG("lsEnter end\n");
2020-02-22 20:01:59 +00:00
}
static void lsIdle ( )
{
2020-10-06 01:43:00 +00:00
// DEBUG_MSG("lsIdle begin ls_secs=%u\n", getPref_ls_secs());
2020-03-14 05:38:58 +00:00
2020-04-15 03:22:27 +00:00
# ifndef NO_ESP32
2020-02-23 01:40:31 +00:00
esp_sleep_source_t wakeCause = ESP_SLEEP_WAKEUP_UNDEFINED ;
2020-06-10 21:11:43 +00:00
// Do we have more sleeping to do?
2020-10-06 01:43:00 +00:00
if ( secsSlept < getPref_ls_secs ( ) ) {
2020-02-23 01:40:31 +00:00
// Briefly come out of sleep long enough to blink the led once every few seconds
2020-06-10 21:11:43 +00:00
uint32_t sleepTime = 30 ;
2020-02-23 01:40:31 +00:00
2020-06-10 21:11:43 +00:00
// If some other service would stall sleep, don't let sleep happen yet
if ( doPreflightSleep ( ) ) {
setLed ( false ) ; // Never leave led on while in light sleep
wakeCause = doLightSleep ( sleepTime * 1000LL ) ;
2020-02-23 01:40:31 +00:00
2020-10-06 03:48:53 +00:00
switch ( wakeCause ) {
case ESP_SLEEP_WAKEUP_TIMER :
2020-06-10 21:11:43 +00:00
// Normal case: timer expired, we should just go back to sleep ASAP
2020-02-23 02:02:44 +00:00
2020-06-10 21:11:43 +00:00
setLed ( true ) ; // briefly turn on led
wakeCause = doLightSleep ( 1 ) ; // leave led on for 1ms
2020-02-23 01:40:31 +00:00
2020-06-10 21:11:43 +00:00
secsSlept + = sleepTime ;
// DEBUG_MSG("sleeping, flash led!\n");
2020-10-06 03:48:53 +00:00
break ;
case ESP_SLEEP_WAKEUP_UART :
2020-06-10 21:36:11 +00:00
// Not currently used (because uart triggers in hw have problems)
powerFSM . trigger ( EVENT_SERIAL_CONNECTED ) ;
2020-10-06 03:48:53 +00:00
break ;
default :
// We woke for some other reason (button press, device interrupt)
2020-06-11 01:23:20 +00:00
// uint64_t status = esp_sleep_get_ext1_wakeup_status();
2020-06-10 21:11:43 +00:00
DEBUG_MSG ( " wakeCause %d \n " , wakeCause ) ;
2020-03-14 05:38:58 +00:00
2020-04-01 04:56:35 +00:00
# ifdef BUTTON_PIN
2020-06-10 21:11:43 +00:00
bool pressed = ! digitalRead ( BUTTON_PIN ) ;
2020-04-01 04:56:35 +00:00
# else
2020-06-10 21:11:43 +00:00
bool pressed = false ;
2020-04-01 04:56:35 +00:00
# endif
2020-06-10 21:11:43 +00:00
if ( pressed ) // If we woke because of press, instead generate a PRESS event.
{
powerFSM . trigger ( EVENT_PRESS ) ;
} else {
// Otherwise let the NB state handle the IRQ (and that state will handle stuff like IRQs etc)
2020-10-06 03:48:53 +00:00
// we lie and say "wake timer" because the interrupt will be handled by the regular IRQ code
2020-06-10 21:11:43 +00:00
powerFSM . trigger ( EVENT_WAKE_TIMER ) ;
}
2020-10-06 03:48:53 +00:00
break ;
2020-06-10 21:11:43 +00:00
}
2020-06-11 01:23:20 +00:00
} else {
// Someone says we can't sleep now, so just save some power by sleeping the CPU for 100ms or so
delay ( 100 ) ;
2020-03-18 20:51:32 +00:00
}
2020-06-10 21:11:43 +00:00
} else {
// Time to stop sleeping!
setLed ( false ) ;
DEBUG_MSG ( " reached ls_secs, servicing loop() \n " ) ;
powerFSM . trigger ( EVENT_WAKE_TIMER ) ;
2020-03-03 21:31:44 +00:00
}
2020-04-15 03:22:27 +00:00
# endif
2020-02-22 20:01:59 +00:00
}
static void lsExit ( )
{
2021-08-03 04:34:14 +00:00
DEBUG_MSG ( " Exit state: LS \n " ) ;
2020-03-14 03:30:48 +00:00
// setGPSPower(true); // restore GPS power
2021-03-23 06:44:50 +00:00
if ( gps )
gps - > forceWake ( true ) ;
2020-02-22 20:01:59 +00:00
}
static void nbEnter ( )
{
2021-08-03 04:34:14 +00:00
DEBUG_MSG ( " Enter state: NB \n " ) ;
2020-10-10 01:57:57 +00:00
screen - > setOn ( false ) ;
2020-02-22 20:01:59 +00:00
setBluetoothEnable ( false ) ;
2020-02-22 22:45:58 +00:00
// FIXME - check if we already have packets for phone and immediately trigger EVENT_PACKETS_FOR_PHONE
2020-02-22 20:01:59 +00:00
}
static void darkEnter ( )
{
2020-04-05 20:09:46 +00:00
setBluetoothEnable ( true ) ;
2020-10-10 01:57:57 +00:00
screen - > setOn ( false ) ;
2020-02-22 20:01:59 +00:00
}
2020-06-08 23:35:26 +00:00
static void serialEnter ( )
{
2021-08-03 04:34:14 +00:00
DEBUG_MSG ( " Enter state: SERIAL \n " ) ;
2020-06-08 23:35:26 +00:00
setBluetoothEnable ( false ) ;
2020-10-10 01:57:57 +00:00
screen - > setOn ( true ) ;
2021-03-25 00:54:43 +00:00
screen - > print ( " Serial connected \n " ) ;
}
static void serialExit ( )
{
screen - > print ( " Serial disconnected \n " ) ;
2020-06-08 23:35:26 +00:00
}
2020-09-21 19:41:39 +00:00
static void powerEnter ( )
{
2021-08-03 04:34:14 +00:00
DEBUG_MSG ( " Enter state: POWER \n " ) ;
2021-03-25 00:54:43 +00:00
if ( ! isPowered ( ) ) {
// If we got here, we are in the wrong state - we should be in powered, let that state ahndle things
DEBUG_MSG ( " Loss of power in Powered \n " ) ;
powerFSM . trigger ( EVENT_POWER_DISCONNECTED ) ;
} else {
screen - > setOn ( true ) ;
setBluetoothEnable ( true ) ;
screen - > print ( " Powered... \n " ) ;
}
}
static void powerIdle ( )
{
if ( ! isPowered ( ) ) {
// If we got here, we are in the wrong state
DEBUG_MSG ( " Loss of power in Powered \n " ) ;
powerFSM . trigger ( EVENT_POWER_DISCONNECTED ) ;
}
2020-10-12 04:19:22 +00:00
}
static void powerExit ( )
{
screen - > setOn ( true ) ;
setBluetoothEnable ( true ) ;
screen - > print ( " Unpowered... \n " ) ;
2020-10-10 06:07:37 +00:00
}
2020-02-22 20:01:59 +00:00
static void onEnter ( )
{
2021-08-03 04:34:14 +00:00
DEBUG_MSG ( " Enter state: ON \n " ) ;
2020-10-10 01:57:57 +00:00
screen - > setOn ( true ) ;
2020-02-22 20:01:59 +00:00
setBluetoothEnable ( true ) ;
2020-03-18 20:51:32 +00:00
static uint32_t lastPingMs ;
2020-03-05 00:46:57 +00:00
2020-09-05 19:34:48 +00:00
uint32_t now = millis ( ) ;
2020-03-18 20:51:32 +00:00
2020-12-22 01:42:00 +00:00
if ( now - lastPingMs >
30 * 1000 ) { // if more than a minute since our last press, ask node we are looking at to update their state
2020-05-22 00:21:44 +00:00
if ( displayedNodeNum )
service . sendNetworkPing ( displayedNodeNum , true ) ; // Refresh the currently displayed node
2020-03-18 20:51:32 +00:00
lastPingMs = now ;
}
2020-02-22 20:01:59 +00:00
}
2021-03-25 00:54:43 +00:00
static void onIdle ( )
{
if ( isPowered ( ) ) {
// If we got here, we are in the wrong state - we should be in powered, let that state ahndle things
powerFSM . trigger ( EVENT_POWER_CONNECTED ) ;
}
}
2020-02-22 20:01:59 +00:00
static void screenPress ( )
{
2020-10-10 01:57:57 +00:00
screen - > onPress ( ) ;
2020-02-22 20:01:59 +00:00
}
2021-08-03 04:34:14 +00:00
static void bootEnter ( ) {
DEBUG_MSG ( " Enter state: BOOT \n " ) ;
}
2020-03-18 22:00:17 +00:00
2020-02-22 21:14:10 +00:00
State stateSDS ( sdsEnter , NULL , NULL , " SDS " ) ;
State stateLS ( lsEnter , lsIdle , lsExit , " LS " ) ;
State stateNB ( nbEnter , NULL , NULL , " NB " ) ;
State stateDARK ( darkEnter , NULL , NULL , " DARK " ) ;
2021-03-25 00:54:43 +00:00
State stateSERIAL ( serialEnter , NULL , serialExit , " SERIAL " ) ;
2020-03-19 02:15:51 +00:00
State stateBOOT ( bootEnter , NULL , NULL , " BOOT " ) ;
2021-03-25 00:54:43 +00:00
State stateON ( onEnter , onIdle , NULL , " ON " ) ;
State statePOWER ( powerEnter , powerIdle , powerExit , " POWER " ) ;
2020-03-18 22:00:17 +00:00
Fsm powerFSM ( & stateBOOT ) ;
2020-02-22 20:01:59 +00:00
void PowerFSM_setup ( )
{
2020-12-25 02:16:12 +00:00
bool isRouter = radioConfig . preferences . is_router ;
2021-03-25 00:54:43 +00:00
bool hasPower = isPowered ( ) ;
2020-12-21 03:38:03 +00:00
2020-09-21 19:41:39 +00:00
DEBUG_MSG ( " PowerFSM init, USB power=%d \n " , hasPower ) ;
powerFSM . add_timed_transition ( & stateBOOT , hasPower ? & statePOWER : & stateON , 3 * 1000 , NULL , " boot timeout " ) ;
2020-03-18 22:00:17 +00:00
2020-10-06 03:48:53 +00:00
// wake timer expired or a packet arrived
// if we are a router node, we go to NB (no need for bluetooth) otherwise we go to DARK (so we can send message to phone)
powerFSM . add_transition ( & stateLS , isRouter ? & stateNB : & stateDARK , EVENT_WAKE_TIMER , NULL , " Wake timer " ) ;
2020-02-22 22:45:58 +00:00
2021-08-03 04:07:32 +00:00
// We need this transition, because we might not transition if we were waiting to enter light-sleep, because when we wake from light sleep we _always_ transition to NB or dark and
2021-08-03 05:07:39 +00:00
powerFSM . add_transition ( & stateLS , isRouter ? & stateNB : & stateDARK , EVENT_PACKET_FOR_PHONE , NULL , " Received packet, exiting light sleep " ) ;
powerFSM . add_transition ( & stateNB , & stateNB , EVENT_PACKET_FOR_PHONE , NULL , " Received packet, resetting win wake " ) ;
2020-02-22 21:14:10 +00:00
2020-06-08 23:35:26 +00:00
// Handle press events - note: we ignore button presses when in API mode
2020-03-18 20:51:32 +00:00
powerFSM . add_transition ( & stateLS , & stateON , EVENT_PRESS , NULL , " Press " ) ;
2020-02-22 21:14:10 +00:00
powerFSM . add_transition ( & stateNB , & stateON , EVENT_PRESS , NULL , " Press " ) ;
powerFSM . add_transition ( & stateDARK , & stateON , EVENT_PRESS , NULL , " Press " ) ;
2020-09-21 19:41:39 +00:00
powerFSM . add_transition ( & statePOWER , & statePOWER , EVENT_PRESS , screenPress , " Press " ) ;
2020-02-22 21:14:10 +00:00
powerFSM . add_transition ( & stateON , & stateON , EVENT_PRESS , screenPress , " Press " ) ; // reenter On to restart our timers
2020-09-21 19:41:39 +00:00
powerFSM . add_transition ( & stateSERIAL , & stateSERIAL , EVENT_PRESS , screenPress ,
" Press " ) ; // Allow button to work while in serial API
2020-02-22 20:01:59 +00:00
2020-05-17 11:51:36 +00:00
// Handle critically low power battery by forcing deep sleep
powerFSM . add_transition ( & stateBOOT , & stateSDS , EVENT_LOW_BATTERY , NULL , " LowBat " ) ;
powerFSM . add_transition ( & stateLS , & stateSDS , EVENT_LOW_BATTERY , NULL , " LowBat " ) ;
powerFSM . add_transition ( & stateNB , & stateSDS , EVENT_LOW_BATTERY , NULL , " LowBat " ) ;
powerFSM . add_transition ( & stateDARK , & stateSDS , EVENT_LOW_BATTERY , NULL , " LowBat " ) ;
powerFSM . add_transition ( & stateON , & stateSDS , EVENT_LOW_BATTERY , NULL , " LowBat " ) ;
2020-06-08 23:35:26 +00:00
powerFSM . add_transition ( & stateSERIAL , & stateSDS , EVENT_LOW_BATTERY , NULL , " LowBat " ) ;
2020-05-17 11:51:36 +00:00
2020-02-24 18:23:07 +00:00
powerFSM . add_transition ( & stateDARK , & stateON , EVENT_BLUETOOTH_PAIR , NULL , " Bluetooth pairing " ) ;
powerFSM . add_transition ( & stateON , & stateON , EVENT_BLUETOOTH_PAIR , NULL , " Bluetooth pairing " ) ;
2020-02-23 02:02:44 +00:00
2020-10-06 03:48:53 +00:00
// if we are a router we don't turn the screen on for these things
if ( ! isRouter ) {
2021-08-03 05:07:39 +00:00
// if any packet destined for phone arrives, turn on bluetooth at least
powerFSM . add_transition ( & stateNB , & stateDARK , EVENT_PACKET_FOR_PHONE , NULL , " Packet for phone " ) ;
2020-10-06 03:48:53 +00:00
// show the latest node when we get a new node db update
powerFSM . add_transition ( & stateNB , & stateON , EVENT_NODEDB_UPDATED , NULL , " NodeDB update " ) ;
powerFSM . add_transition ( & stateDARK , & stateON , EVENT_NODEDB_UPDATED , NULL , " NodeDB update " ) ;
powerFSM . add_transition ( & stateON , & stateON , EVENT_NODEDB_UPDATED , NULL , " NodeDB update " ) ;
// Show the received text message
powerFSM . add_transition ( & stateLS , & stateON , EVENT_RECEIVED_TEXT_MSG , NULL , " Received text " ) ;
powerFSM . add_transition ( & stateNB , & stateON , EVENT_RECEIVED_TEXT_MSG , NULL , " Received text " ) ;
powerFSM . add_transition ( & stateDARK , & stateON , EVENT_RECEIVED_TEXT_MSG , NULL , " Received text " ) ;
powerFSM . add_transition ( & stateON , & stateON , EVENT_RECEIVED_TEXT_MSG , NULL , " Received text " ) ; // restarts the sleep timer
}
2020-02-22 20:01:59 +00:00
2021-03-25 00:54:43 +00:00
// If we are not in statePOWER but get a serial connection, suppress sleep (and keep the screen on) while connected
2020-06-08 23:35:26 +00:00
powerFSM . add_transition ( & stateLS , & stateSERIAL , EVENT_SERIAL_CONNECTED , NULL , " serial API " ) ;
powerFSM . add_transition ( & stateNB , & stateSERIAL , EVENT_SERIAL_CONNECTED , NULL , " serial API " ) ;
powerFSM . add_transition ( & stateDARK , & stateSERIAL , EVENT_SERIAL_CONNECTED , NULL , " serial API " ) ;
powerFSM . add_transition ( & stateON , & stateSERIAL , EVENT_SERIAL_CONNECTED , NULL , " serial API " ) ;
2021-03-25 00:54:43 +00:00
powerFSM . add_transition ( & statePOWER , & stateSERIAL , EVENT_SERIAL_CONNECTED , NULL , " serial API " ) ;
2020-06-08 23:35:26 +00:00
2021-03-25 00:54:43 +00:00
// If we get power connected, go to the power connect state
powerFSM . add_transition ( & stateLS , & statePOWER , EVENT_POWER_CONNECTED , NULL , " power connect " ) ;
powerFSM . add_transition ( & stateNB , & statePOWER , EVENT_POWER_CONNECTED , NULL , " power connect " ) ;
powerFSM . add_transition ( & stateDARK , & statePOWER , EVENT_POWER_CONNECTED , NULL , " power connect " ) ;
powerFSM . add_transition ( & stateON , & statePOWER , EVENT_POWER_CONNECTED , NULL , " power connect " ) ;
2020-09-21 19:41:39 +00:00
powerFSM . add_transition ( & statePOWER , & stateON , EVENT_POWER_DISCONNECTED , NULL , " power disconnected " ) ;
2021-03-25 00:54:43 +00:00
// powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
2020-09-21 19:41:39 +00:00
2021-03-25 00:54:43 +00:00
// the only way to leave state serial is for the client to disconnect (or we timeout and force disconnect them)
// when we leave, go to ON (which might not be the correct state if we have power connected, we will fix that in onEnter)
powerFSM . add_transition ( & stateSERIAL , & stateON , EVENT_SERIAL_DISCONNECTED , NULL , " serial disconnect " ) ;
2020-06-08 23:35:26 +00:00
2020-02-23 16:53:52 +00:00
powerFSM . add_transition ( & stateDARK , & stateDARK , EVENT_CONTACT_FROM_PHONE , NULL , " Contact from phone " ) ;
2021-03-23 06:44:50 +00:00
// each time we get a new update packet make sure we are staying in the ON state so the screen stays awake (also we don't
// shutdown bluetooth if is_router)
powerFSM . add_transition ( & stateDARK , & stateON , EVENT_FIRMWARE_UPDATE , NULL , " Got firmware update " ) ;
powerFSM . add_transition ( & stateON , & stateON , EVENT_FIRMWARE_UPDATE , NULL , " Got firmware update " ) ;
2020-10-06 01:43:00 +00:00
powerFSM . add_timed_transition ( & stateON , & stateDARK , getPref_screen_on_secs ( ) * 1000 , NULL , " Screen-on timeout " ) ;
2020-02-22 20:01:59 +00:00
2020-10-16 06:00:56 +00:00
// On most boards we use light-sleep to be our main state, but on NRF52 we just stay in DARK
State * lowPowerState = & stateLS ;
2020-02-22 21:50:08 +00:00
2021-08-10 08:07:40 +00:00
uint32_t meshSds = 0 ;
2021-08-10 07:23:26 +00:00
2020-04-24 18:21:10 +00:00
# ifndef NRF52_SERIES
2020-10-16 06:00:56 +00:00
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
2021-03-30 15:11:56 +00:00
// I don't think this transition is correct, turning off for now - @geeksville
// powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout");
2020-10-06 01:43:00 +00:00
powerFSM . add_timed_transition ( & stateNB , & stateLS , getPref_min_wake_secs ( ) * 1000 , NULL , " Min wake timeout " ) ;
powerFSM . add_timed_transition ( & stateDARK , & stateLS , getPref_wait_bluetooth_secs ( ) * 1000 , NULL , " Bluetooth timeout " ) ;
2021-08-10 07:23:26 +00:00
meshSds = getPref_mesh_sds_timeout_secs ( ) ;
2020-10-30 09:05:32 +00:00
# else
lowPowerState = & stateDARK ;
2021-08-10 07:23:26 +00:00
meshSds = UINT32_MAX ; //Workaround for now: Don't go into deep sleep on the RAK4631
2020-04-24 18:21:10 +00:00
# endif
2020-02-22 20:01:59 +00:00
2020-10-06 03:48:53 +00:00
if ( meshSds ! = UINT32_MAX )
2020-10-16 06:00:56 +00:00
powerFSM . add_timed_transition ( lowPowerState , & stateSDS , meshSds * 1000 , NULL , " mesh timeout " ) ;
2020-03-05 02:59:10 +00:00
// removing for now, because some users don't even have phones
2020-10-16 06:00:56 +00:00
// powerFSM.add_timed_transition(lowPowerState, &stateSDS, getPref_phone_sds_timeout_sec() * 1000, NULL, "phone
2020-03-18 20:51:32 +00:00
// timeout");
2020-03-03 21:31:44 +00:00
2020-02-22 21:14:10 +00:00
powerFSM . run_machine ( ) ; // run one interation of the state machine, so we run our on enter tasks for the initial DARK state
2020-03-15 23:47:38 +00:00
}