BluzDK — How to gain stability and save battery power!
This is intended for the average hacker using the BluzDK BlueTooth Low Energy module … who may not have the benefit of a Computer Science degree under their belts … and is written by one of similar ilk.
“Now, I’m no expert. But …” — Someone famous, for those very words!
BluzDK is Special
Battery life is everything
BluzDK differs from Particle’s Photon, SparkCore etc, in an important way. It does not have built-in multi-tasking abilities – and in fact if it did, it would not be so light on our batteries.
The upshot of this is that we need to take care that the setup()
or loop()
functions never block, certainly not for more than a very short while. If we do block, BluzDK wouldn’t be able to process cloud events, such as OTA updates, and would consume a LOT more power!
A, “blocking” function is simply one that hogs CPU clocks by not returning to its caller as swiftly as possible. Something like …
while(true) { /* do stuff forever */ }
The above example will block forever! But even blocking for a long time – just a second or more, mind you – can cause real problems.
For example, the following pseudo code could easily prevent BluzDK from being able to process radio events for too long, causing it to disconnect or worse …
void main() { while (somethingTrueForTooLongTime) { /* sit in this loop for a too long a time, just waiting for something */ } }
But wait!
Isn’t waiting an unknown and often lengthy time for something just exactly what we almost always want to do? Yup!
So HOW then?
One could invent any manner of cleverness to deal with such a problem. Luckily though, some clever people long ago came up with a programming pattern, called a Finite State Machine or FSM. By all means Google that to learn the juicy details. Suffice to say for now, it’s a beautifully simple, easy to read and understand pattern and is possibly the most uses programming pattern of all time.
Here, I present a simple (standard C – not C++) FSM style example of how we can wait a potentially long time for something without ever staying in `loop()` for more than a few microseconds. (Feel free to Google up on some fancy C++ classy versions.)
void setup() { } #define TIME_TO_WAIT 5000 /* milliseconds */ typedef enum { WAIT_ONLINE, ALIVE, PUBLISH, SET_TIMER, WAIT } FSM_state_t; void loop() { static FSM_state_t myState = WAIT_ONLINE; static uint32_t saveTime; switch (myState) { case WAIT_ONLINE: // stay in this state until we're connected to the big old cloud in the sky if (Particle.connected()) myState = ALIVE; break; case ALIVE: // we're alive! shout about it Particle.publish("Came online at", String(millis()) ); myState = SET_TIMER; // next time through loop(), we'll set up our custom timer break; case PUBLISH: // it's been TIME_TO_WAIT milliseconds -- make some noise! if ( !Particle.connected() ) { // first though ... did we get disconnected somehow? myState = WAIT_ONLINE; } else { Particle.publish("PING!! at ", String(millis()) ); myState = SET_TIMER; } break; case SET_TIMER: // record the current time for the WAIT state to reference back to saveTime = millis(); myState = WAIT; break; case WAIT: // stay in this state until TIME_TO_WAIT milliseconds have gone by since the SET_TIMER state if ( millis() > (saveTime + TIME_TO_WAIT) ) { // Time's up! But we'll drop out of loop() for and get it done next time through // This allows all the background network stuff the best chance of keeping up with business myState = PUBLISH; } break; default: myState = WAIT_ONLINE; } // We can save MUCH more battery drain this way too! ... System.sleep(SLEEP_MODE_CPU); }
Notice that each time loop()
is called by the BluzDK system, only two or three instructions get executed (in this example) … even though the state machine as a whole is achieving quite a bit more – including not one but two potentially long waits.
Want to have virtual multitasking but could never find an efficient way to structure the code? Just use two or more state machines – each with its own internal state tracking.
void loop() { cycle_power-monitor_state_machine(); cycle_user_keypad_state_machine(); cycle_camera_tracking_state_machine(); }
Just so long as each state machine takes care of itself and always drops through to return to loop()
as soon as possible, then your system could appear to be three distinctly separate devices in one. Multitasking for the poor — whilst maintaining a degree of code readability, for next year, when you come back to make changes.
Leave a Reply