Micro:bit

Micro:bit 2 – Timeout as Events

In the last post we discussed the need for a new programming model for our “hello world” program. Our initial version uses a common sequential programming model in which actions (or code) are executed one by one in a loop. If a loop contains the actions A, B and C, the execution order is then A, B, C, A, B, C, … In our hello world example, each loop contains the actions of (i) toggling an LED and (ii) waiting for 500ms.

While this looping architecture works just fine for simple programs, it is unscalable as complexity grows. Whenever we add actions to the loop, we inevitably alter the timing of the loop. It will take longer for the loop to get back to any given action. The system will become less responsive or even fail its intended functions.

A more suitable programming model for real-time systems is the event-driven model. It is not a new concept and has been used in graphical user interface and protocol stacks for decades. Nowadays many coding platforms such as MakeCode for Micro:bit and Scratch depend heavily on the event-driven model.

In MakeCodemost system inputs such as button press, motion detection and radio messaging are captured as events. Rather than requiring us to poll (i.e. periodically check) for those inputs in a loop, all we need to do is to implement an event handler to specify what actions to perform upon the occurrence of each event, e.g.

input.onButtonPressed(Button.A, function () {
    // Actions when Button A is pressed.
})

There is an exception though – timeout. Unlike other input sources which arise from external and physical interaction, timeout is internal and abstract. Waiting in MakeCode still relies on traditional function calls such as delay()pause() or sleep() which blocks (or pauses execution) for a specified duration. To be truly event-driven, abstract concepts like the completion of waiting (or timeout) should be treated as events.  

Let us assume our coding platform provides an API to (i) start a timer by specifying a duration to wait for and the event to generate upon timeout, and (ii) assign an event handler for the timeout event. Our “hello world” program becomes like this: 

enum Evt {
    EVT_START,
    TIMER_500MS,            // Timeout event.
}
// Assigns an event handler for the timeout event.
event.on(Evt.TIMER_500MS, function () {
    led.toggle(0, 0)
})
// Starts a periodic timer with a duration of 500ms.
timer.start(Evt.TIMER_500MS, 500, true)

This modern version does the same job as the original version, yet they look rather different in source code and are based on fundamentally different paradigms – event-driven vs sequentialnon-blocking vs blocking and asynchronous vs synchronous. Since real-world software often originates from simple hello-world-like programs, it is very important to do it right in the very beginning – an event-driven hello world.