How impOS And Squirrel Manage Low-Power Operation
The imp API provides access to a number of hardware facilities all nominally concerned with putting the imp to sleep, ie. into a low-power or paused state. However, despite the use of the word ‘sleep’, the various low-power states offered — and the API methods used to manage them — vary considerably in their purpose and impact on how your code is run.
The imp API method imp.sleep() when used for periods of 20ms or less provides a busy wait period. The imp is awake, but Squirrel is unresponsive to incoming messages (the WiFi network stack will acknowledge and queue incoming data in the background). This does not reduce the imp’s power consumption.
If imp.sleep() is passed a period greater than 20ms, then the imp is placed in a ‘Shallow Sleep’ mode. This reduces the power consumption a little, though not as much as that provided when the imp goes idle — Squirrel has come to the end of its current code path but one or more wake-up timers have yet to fire (see Scheduling Future Events, below).
Not only that, but imp.sleep() blocks, making the imp unresponsive, even to incoming messages from the server. Therefore for long periods (20ms or above) it is better to use imp.wakeup() to register a timer and then allow the imp to idle. You will get better power preservation, and impOS™ will be able to continue to monitor the server connection.
That said, there is another trade-off to be made here: short (20ms or less) imp.sleep() (synchronous) sleeps are rounded up to the nearest microsecond, whereas imp.wakeup() (asynchronous) sleeps and synchronous sleeps of more than 20ms duration are rounded to the nearest centisecond (10ms).
The imp API method imp.deepsleepfor() puts the imp entirely to sleep — we call this mode ‘Deep Sleep’. Squirrel code is halted, WiFi is turned off and the imp CPU is put into an ultra-low power state consuming just microamps. Essentially the system is all but shut down: it is just running a wake-up timer (not related to imp.wakeup(), above).
When the imp wakes, Squirrel code restarts from the top. The only thing preserved is the nv table. WiFi remains powered down until code calls an API method that requires a connection to the server. If you configure pin 1 (imp001, imp002) or pin W (imp003, imp004m; imp005 does not support deep sleep) as the wake-up pin, then this can also bring the imp out of sleep before the requested sleep time has elapsed.
imp.deepsleepfor() does not cleanly close the connection, so it is most often used when the WiFi has already been disconnected. The imp API method server.sleepfor() is like imp.deepsleepfor(), but it does cleanly close the connection before putting the imp to sleep. Either method can be used to get years of battery life. Neither method should be called until the imp goes idle (Squirrel has come to the end of its current code path). This can be ensured by placing the above methods in a function registered using imp.onidle(). This function is only called when the device goes idle.
To reduce the imp’s power consumption while staying connected to WiFi, use:
This drops down to around one tenth of the normal operational power, with only a small impact on incoming message latency.
However, for the time being you should not use powersave mode on imp004m-based designs due to issues with the Cypress WiFi chip used in that module.
The imp API method imp.wakeup() registers a function that will be called after a certain period of time, provided in seconds but with centisecond granularity, has elapsed. Memory permitting, you can have as many of these registered at any one time as you like. A timer-triggered callback function can’t interrupt running Squirrel code — the function will be queued and run when the imp goes idle.
While idle and waiting for timers to trigger — or other events, such as GPIO changes or data from the server — impOS puts the imp into as low power a state as possible by idling the CPU, clock-gating peripherals, dropping bus clocks, etc.
imp.wakeup() can also be used in agent code to trigger future agent actions. However, unlike the device implementation, on the agent there is a maximum limit of 20 timers at any one time. Neither agent nor device can directly trigger events on the other by using this method, ie. the registered function has to exist in the calling code.