mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-20 20:12:12 +00:00
WIP state machine builds
This commit is contained in:
parent
045529d91f
commit
509f9b6e2b
1
TODO.md
1
TODO.md
@ -72,6 +72,7 @@ until the phone pulls those packets. Ever so often power on bluetooth just so w
|
||||
|
||||
Items after the first final candidate release.
|
||||
|
||||
* read the PMU battery fault indicators and blink/led/warn user on screen
|
||||
* make a no bluetooth configured yet screen - include this screen in the loop if the user hasn't yet paired
|
||||
* the AXP debug output says it is trying to charge at 700mA, but the max I've seen is 180mA, so AXP registers probably need to be set to tell them the circuit can only provide 300mAish max. So that the low charge rate kicks in faster and we don't wear out batteries.
|
||||
* increase the max charging rate a bit for 18650s, currently it limits to 180mA (at 4V). Work backwards from the 500mA USB limit (at 5V) and let the AXP charge at that rate.
|
||||
|
@ -7,44 +7,50 @@ This is a mini design doc for various core behaviors...
|
||||
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)
|
||||
|
||||
* 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
|
||||
(Not currently used)
|
||||
|
||||
* 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.
|
||||
onEntry: setBluetoothOn(false), setGPSPower(false), call doLightSleep
|
||||
onExit: start trying to get gps lock: gps.startLock(), once lock arrives service.sendPosition(BROADCAST)
|
||||
onEntry: setBluetoothOn(false), setGPSPower(false) - happens inside doLightSleep()
|
||||
onIdle: (if we wake because our led blink timer has expired) blink the led then go back to sleep until we sleep for ls_secs
|
||||
onExit: setGPSPower(true), 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)
|
||||
onEntry: setBluetoothOn(false)
|
||||
onExit:
|
||||
|
||||
* running dark (DARK) - Everything is on except screen
|
||||
onEntry: setBluetoothOn(true)
|
||||
onExit:
|
||||
onExit:
|
||||
|
||||
* full on (ON) - Everything is on
|
||||
onEntry: setBluetoothOn(true), screen.setOn(true)
|
||||
onExit: screen.setOn(false)
|
||||
|
||||
|
||||
## Behavior
|
||||
|
||||
### events that increase CPU activity
|
||||
|
||||
* At cold boot: The initial state (after setup() has run) is DARK
|
||||
* While in DARK: if we receive EVENT_BOOT, transition to ON (and show the bootscreen). This event will be sent if we detect we woke due to reset (as opposed to deep sleep)
|
||||
* 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.
|
||||
* While in LS/NB/DARK: If the user presses a button we go to full ON mode for screen_on_secs (default 30 seconds). Multiple presses keeps resetting this timeout
|
||||
* 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.
|
||||
* While in LS/NB/DARK: If the user presses a button (EVENT_PRESS) we go to full ON mode for screen_on_secs (default 30 seconds). Multiple presses keeps resetting this timeout
|
||||
* While in LS/NB/DARK: If we receive new text messages (EVENT_RECEIVED_TEXT_MSG), 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 (EVENT_RECEIVED_PACKET) we will wake and handle them and stay awake in NB mode for min_wake_secs (default 10 seconds)
|
||||
* While in NB: If we do have packets the phone (EVENT_PACKETS_FOR_PHONE) would want we transition to DARK mode for wait_bluetooth secs.
|
||||
|
||||
### events that decrease cpu activity
|
||||
|
||||
* While in ON: If PRESS event occurs, reset screen_on_secs timer and tell the screen to handle the pess
|
||||
* 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)
|
||||
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 (default 1 hr) (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.
|
||||
|
||||
|
106
src/PowerFSM.cpp
Normal file
106
src/PowerFSM.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
|
||||
#include "sleep.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
#include "screen.h"
|
||||
#include "PowerFSM.h"
|
||||
|
||||
static void sdsEnter()
|
||||
{
|
||||
/*
|
||||
|
||||
// Don't deepsleep if we have USB power or if the user as pressed a button recently
|
||||
// !isUSBPowered <- doesn't work yet because the axp192 isn't letting the battery fully charge when we are awake - FIXME
|
||||
if (millis() - lastPressMs > radioConfig.preferences.mesh_sds_timeout_secs)
|
||||
{
|
||||
doDeepSleep(radioConfig.preferences.sds_secs);
|
||||
}
|
||||
*/
|
||||
|
||||
doDeepSleep(radioConfig.preferences.sds_secs * 1000LL);
|
||||
}
|
||||
|
||||
static void lsEnter()
|
||||
{
|
||||
/*
|
||||
// while we have bluetooth on, we can't do light sleep, but once off stay in light_sleep all the time
|
||||
// we will wake from light sleep on button press or interrupt from the RF95 radio
|
||||
if (!bluetoothOn && !is_screen_on() && service.radio.rf95.canSleep() && gps.canSleep())
|
||||
doLightSleep(radioConfig.preferences.ls_secs);
|
||||
else
|
||||
{
|
||||
delay(msecstosleep);
|
||||
} */
|
||||
|
||||
doLightSleep(radioConfig.preferences.ls_secs * 1000LL);
|
||||
}
|
||||
|
||||
static void lsIdle()
|
||||
{
|
||||
// FIXME - blink led when we occasionally wake from timer, then go back to light sleep
|
||||
}
|
||||
|
||||
static void lsExit()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
static void nbEnter()
|
||||
{
|
||||
setBluetoothEnable(false);
|
||||
}
|
||||
|
||||
static void darkEnter()
|
||||
{
|
||||
DEBUG_MSG("screen timeout, turn it off for now...\n");
|
||||
screen.setOn(true);
|
||||
}
|
||||
|
||||
static void onEnter()
|
||||
{
|
||||
screen.setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
}
|
||||
|
||||
static void onExit()
|
||||
{
|
||||
screen.setOn(false);
|
||||
}
|
||||
static void wakeForPing()
|
||||
{
|
||||
}
|
||||
|
||||
static void screenPress()
|
||||
{
|
||||
screen.onPress();
|
||||
}
|
||||
|
||||
State stateSDS(sdsEnter, NULL, NULL);
|
||||
State stateLS(lsEnter, lsIdle, lsExit);
|
||||
State stateNB(nbEnter, NULL, NULL);
|
||||
State stateDARK(darkEnter, NULL, NULL);
|
||||
State stateON(onEnter, NULL, onExit);
|
||||
Fsm powerFSM(&stateDARK);
|
||||
|
||||
void PowerFSM_setup()
|
||||
{
|
||||
powerFSM.add_transition(&stateDARK, &stateON, EVENT_BOOT, NULL);
|
||||
powerFSM.add_transition(&stateLS, &stateDARK, EVENT_WAKE_TIMER, wakeForPing);
|
||||
powerFSM.add_transition(&stateLS, &stateNB, EVENT_RECEIVED_PACKET, NULL);
|
||||
|
||||
powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL);
|
||||
powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL);
|
||||
powerFSM.add_transition(&stateDARK, &stateON, EVENT_PRESS, NULL);
|
||||
powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress); // reenter On to restart our timers
|
||||
|
||||
powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL);
|
||||
powerFSM.add_transition(&stateNB, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL);
|
||||
powerFSM.add_transition(&stateDARK, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL);
|
||||
|
||||
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL);
|
||||
|
||||
powerFSM.add_timed_transition(&stateON, &stateDARK, radioConfig.preferences.screen_on_secs, NULL);
|
||||
|
||||
powerFSM.add_timed_transition(&stateDARK, &stateNB, radioConfig.preferences.phone_timeout_secs, NULL);
|
||||
}
|
14
src/PowerFSM.h
Normal file
14
src/PowerFSM.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "Fsm.h"
|
||||
|
||||
// See sw-design.md for documentation
|
||||
|
||||
#define EVENT_PRESS 1
|
||||
#define EVENT_WAKE_TIMER 2
|
||||
#define EVENT_RECEIVED_PACKET 3
|
||||
#define EVENT_PACKET_FOR_PHONE 4
|
||||
#define EVENT_RECEIVED_TEXT_MSG 5
|
||||
#define EVENT_BOOT 6
|
||||
|
||||
extern Fsm powerFSM;
|
@ -60,10 +60,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#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
|
||||
|
||||
#define MINWAKE_MSECS (60 * 60 * 1000) // stay awake a long time (30 mins) for debugging - FIXME, change per the TBD sleep rules doc
|
||||
// #define MINWAKE_MSECS (30 * 1000) // Wait after every boot for GPS lock (may need longer than 5s because we turned the gps off during deep sleep)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// DEBUG
|
||||
|
24
src/main.ino
24
src/main.ino
@ -37,6 +37,7 @@
|
||||
#include "esp_pm.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "sleep.h"
|
||||
#include "PowerFSM.h"
|
||||
|
||||
#ifdef T_BEAM_V10
|
||||
#include "axp20x.h"
|
||||
@ -337,7 +338,7 @@ void loop()
|
||||
// if user presses button for more than 3 secs, discard our network prefs and reboot (FIXME, use a debounce lib instead of this boilerplate)
|
||||
static bool wasPressed = false;
|
||||
static uint32_t minPressMs; // what tick should we call this press long enough
|
||||
static uint32_t lastPingMs, lastPressMs;
|
||||
static uint32_t lastPingMs;
|
||||
if (!digitalRead(BUTTON_PIN))
|
||||
{
|
||||
if (!wasPressed)
|
||||
@ -349,7 +350,6 @@ void loop()
|
||||
wasPressed = true;
|
||||
|
||||
uint32_t now = millis();
|
||||
lastPressMs = now;
|
||||
minPressMs = now + 3000;
|
||||
|
||||
if (now - lastPingMs > 60 * 1000)
|
||||
@ -358,7 +358,7 @@ void loop()
|
||||
lastPingMs = now;
|
||||
}
|
||||
|
||||
screen_press();
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
}
|
||||
}
|
||||
else if (wasPressed)
|
||||
@ -375,15 +375,6 @@ void loop()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MINWAKE_MSECS
|
||||
// Don't deepsleep if we have USB power or if the user as pressed a button recently
|
||||
// !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)
|
||||
{
|
||||
doDeepSleep(radioConfig.preferences.sds_secs);
|
||||
}
|
||||
#endif
|
||||
|
||||
// No GPS lock yet, let the OS put the main CPU in low power mode for 100ms (or until another interrupt comes in)
|
||||
// i.e. don't just keep spinning in loop as fast as we can.
|
||||
//DEBUG_MSG("msecs %d\n", msecstosleep);
|
||||
@ -391,12 +382,5 @@ void loop()
|
||||
// FIXME - until button press handling is done by interrupt (see polling above) we can't sleep very long at all or buttons feel slow
|
||||
msecstosleep = 10;
|
||||
|
||||
// while we have bluetooth on, we can't do light sleep, but once off stay in light_sleep all the time
|
||||
// we will wake from light sleep on button press or interrupt from the RF95 radio
|
||||
if (!bluetoothOn && !is_screen_on() && service.radio.rf95.canSleep() && gps.canSleep())
|
||||
doLightSleep(60 * 1000); // FIXME, wake up to briefly flash led, then go back to sleep (without repowering bluetooth)
|
||||
else
|
||||
{
|
||||
delay(msecstosleep);
|
||||
}
|
||||
delay(msecstosleep);
|
||||
}
|
@ -60,7 +60,7 @@ static bool showingBootScreen = true; // start by showing the bootscreen
|
||||
|
||||
uint32_t lastPressMs;
|
||||
|
||||
bool is_screen_on() { return screenOn; }
|
||||
bool Screen::isOn() { return screenOn; }
|
||||
|
||||
void msOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
|
||||
{
|
||||
@ -491,22 +491,19 @@ void _screen_header()
|
||||
}
|
||||
#endif
|
||||
|
||||
void screen_off()
|
||||
{
|
||||
if (!disp)
|
||||
return;
|
||||
|
||||
dispdev.displayOff();
|
||||
screenOn = false;
|
||||
}
|
||||
|
||||
void screen_on()
|
||||
|
||||
void Screen::setOn(bool on)
|
||||
{
|
||||
if (!disp)
|
||||
return;
|
||||
|
||||
if(on != screenOn) {
|
||||
if(on)
|
||||
dispdev.displayOn();
|
||||
screenOn = true;
|
||||
else
|
||||
dispdev.displayOff();
|
||||
screenOn = on;
|
||||
}
|
||||
}
|
||||
|
||||
static void screen_print(const char *text, uint8_t x, uint8_t y, uint8_t alignment)
|
||||
@ -579,7 +576,7 @@ void Screen::setup()
|
||||
// Scroll buffer
|
||||
dispdev.setLogBuffer(3, 32);
|
||||
|
||||
screen_on(); // update our screenOn bool
|
||||
setOn(true); // update our screenOn bool
|
||||
|
||||
#ifdef BICOLOR_DISPLAY
|
||||
dispdev.flipScreenVertically(); // looks better without this on lora32
|
||||
@ -612,7 +609,7 @@ void Screen::doTask()
|
||||
if (wakeScreen) // If a new text message arrived, turn the screen on immedately
|
||||
{
|
||||
lastPressMs = millis(); // if we were told to wake the screen, reset the press timeout
|
||||
screen_on(); // make sure the screen is not asleep
|
||||
screen.setOn(true); // make sure the screen is not asleep
|
||||
wakeScreen = false;
|
||||
}
|
||||
|
||||
@ -655,11 +652,12 @@ void Screen::doTask()
|
||||
nodeDB.updateTextMessage = false;
|
||||
}
|
||||
|
||||
/*
|
||||
if (millis() - lastPressMs > SCREEN_SLEEP_MS)
|
||||
{
|
||||
DEBUG_MSG("screen timeout, turn it off for now...\n");
|
||||
screen_off();
|
||||
}
|
||||
screen.setOn(false);
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
@ -716,7 +714,7 @@ void screen_set_frames()
|
||||
}
|
||||
|
||||
/// handle press of the button
|
||||
void screen_press()
|
||||
void Screen::onPress()
|
||||
{
|
||||
// screen_start_bluetooth(123456);
|
||||
|
||||
|
11
src/screen.h
11
src/screen.h
@ -4,7 +4,6 @@
|
||||
|
||||
void screen_print(const char * text);
|
||||
|
||||
void screen_on(), screen_off(), screen_press();
|
||||
|
||||
// Show the bluetooth PIN screen
|
||||
void screen_start_bluetooth(uint32_t pin);
|
||||
@ -12,7 +11,6 @@ void screen_start_bluetooth(uint32_t pin);
|
||||
// restore our regular frame list
|
||||
void screen_set_frames();
|
||||
|
||||
bool is_screen_on();
|
||||
|
||||
/**
|
||||
* Slowly I'm moving screen crap into this class
|
||||
@ -27,6 +25,15 @@ public:
|
||||
|
||||
/// Turn on the screen asap
|
||||
void doWakeScreen();
|
||||
|
||||
/// Is the screen currently on
|
||||
bool isOn();
|
||||
|
||||
/// Turn the screen on/off
|
||||
void setOn(bool on);
|
||||
|
||||
/// Handle a button press
|
||||
void onPress();
|
||||
};
|
||||
|
||||
extern Screen screen;
|
@ -65,6 +65,8 @@ void setLed(bool ledOn)
|
||||
|
||||
void setGPSPower(bool on)
|
||||
{
|
||||
DEBUG_MSG("Setting GPS power=%d\n", on);
|
||||
|
||||
#ifdef T_BEAM_V10
|
||||
if (axp192_found)
|
||||
axp.setPowerOutPut(AXP192_LDO3, on ? AXP202_ON : AXP202_OFF); // GPS main power
|
||||
@ -97,7 +99,7 @@ void doDeepSleep(uint64_t msecToWake)
|
||||
|
||||
BLEDevice::deinit(false); // We are required to shutdown bluetooth before deep or light sleep
|
||||
|
||||
screen_off(); // datasheet says this will draw only 10ua
|
||||
screen.setOn(false); // datasheet says this will draw only 10ua
|
||||
|
||||
// Put radio in sleep mode (will still draw power but only 0.2uA)
|
||||
service.radio.rf95.sleep();
|
||||
@ -212,6 +214,7 @@ void doLightSleep(uint64_t sleepMsec) // FIXME, use a more reasonable default
|
||||
|
||||
setBluetoothEnable(false); // has to be off before calling light sleep
|
||||
gps.prepareSleep(); // abandon in-process parsing
|
||||
setGPSPower(false); // kill GPS power
|
||||
setLed(false); // Never leave led on while in light sleep
|
||||
|
||||
// NOTE! ESP docs say we must disable bluetooth and wifi before light sleep
|
||||
@ -228,6 +231,8 @@ void doLightSleep(uint64_t sleepMsec) // FIXME, use a more reasonable default
|
||||
esp_sleep_enable_timer_wakeup(sleepUsec);
|
||||
esp_light_sleep_start();
|
||||
DEBUG_MSG("Exit light sleep\n");
|
||||
|
||||
setGPSPower(true); // restore GPS power
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
void doDeepSleep(uint64_t msecToWake);
|
||||
void doLightSleep(uint64_t msecToWake);
|
||||
void setBluetoothEnable(bool on);
|
||||
|
Loading…
Reference in New Issue
Block a user