The emergence of energy-harvesting devices allows extremely low-power devices to operate using the energy that they extract from their environment only. Energy-harvesting frees a device from its tethered power supply, enabling the use of these devices in a broad range of emerging sensing, computing, and communication applications “beyond the edge”. However, an application can run only intermittently, when the energy-harvesting device has sufficient energy available to do some useful work. A typical device works by collecting energy from an extremely low-power input source (e.g., radio waves, weak solar energy, vibration), slowly accumulating the energy in a capacitor. After sufficient energy accumulates, the device runs a burst of operations, quickly consuming the energy and powering off, awaiting more energy in the future. Applications on these devices operate according to the intermittent execution model, making application progress in short bursts interleaved by recharge periods.
What makes intermittent computing challenging?
Without careful programming and system support, applications can fail to make progress, intermittent operations can leave non-volatile and volatile memory states inconsistent, and implicit atomicty and timeliness constraints in the program might break.
If the execution state is not saved across power failures, the program will run from the beginning on re-boot, and it will never progress further than what can be executed in one power cycle. To make reliable forward progress, the programmer needs to use some sort of checkpointing system and to ensure that the next checkpoint is a reachable distance with one capacitor charge.
Since the microcontrollers used for intermittent computing can have a mixture of volatile and non-volatile memory, a naive system might allow the two memory states to become inconsistent. In the example image, the program is incrementing a counter located in non-volatile memory and then storing a data sample into a buffer at the counter index. If the power cycle ends between the increment and store into the buffer, the volatile execution state is cleared and reverts to what it was at the beginning of the program, but the non-volatile counter does not. In the second execution attempt, the program increments the counter again unecessarily, and then stores into the buffer, skipping a slot.
If the runtime system allows portions of the code to re-execute, it must also track and restore memory locations that can lead to inconsistencies.
Preserving program semantics
Even if a program has well placed checkpoints with a system that maintains a consistent state between non-volatile and volatile memory, it still might not execute as the programmer originally intended. Once the capicitor drains and the power fails, the energy harvesting device will be powered off for an arbitrary period of time, and all the peripheral state will clear. This behavior can break assumptions that a programmer can (reasonably) make on continuously powered devices regarding the atomicty of operations and the timeliness of data.
Some code regions need to execute atomically - without any checkpoints in the middle - to remain correct. For example, the “read” function of a sensor driver might first perform some assertions that the sensor is powered on and the bus is ready before actually reading from the sensor. If there is a checkpoint between the asserts and the sensor read, and the power fails, execution will return to the checkpoint and attempt to read from the sensor without repeating the assertions.
Programmers should be able to specify atomic regions which must finish on one energy burst.
Some data lose value over time. Since the device can be off for an unknown amout of time once the power fails, putting checkpoints between the collection and use of certain data limits their usefullness.
For example, consider an application that monitors the temperature of an area and sends out a radio message saying wether the temperature is acceptable or over a threshold. The application gathers a temperature within the limits but powers down before it’s finished processing. While the device is off, the temperature drastically changes. On reboot, the device will continue processing the now stale data and message (incorrectly) that everything is still ok.
Programmers should be able to express which events need to execute in a timely fashion.