When I started programming hardware with Arduino, MicroPython and CircuitPython years ago, I had a look at demo programs from the internet. Most of them were simple and used loops and sleeps, like this one:
import board
import digitalio
import time
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
while True:
led.value = True
time.sleep(0.5)
led.value = False
time.sleep(0.5)
The situation improved as can be seen in this guide. But that's still not as sophisticated as I wanted.
In science and technology, we are standing on the shoulders of giants. So before I started reinventing the wheel, I tried to find work of others on this topic. Maybe from someone who has experience with robots and computer controlled hardware for decades. And someone who doesn't treat know-how as trade secret, but publishes it on the internet.
What I found was a supplement volume of an encyclopedia published by the NASA History Office: "Computers in Spaceflight: The NASA Experience".
Most of this astonishing long read focuses on computer hardware. But there are some hints on the software that was running on the Apollo Guidance Computer, on the avionics computers of the Space Shuttle or in space probes. If you extract common patterns from these different pieces of software, you will find:
- queues with waiting tasks,
- tasks have priorities and
- there are interrupts.
My plan was to steal these ideas and code them into a Python library - here it is: cooperative_multitasking in source and compiled form.
Let's have a look at a simple example using the cooperative_multitasking library before diving into complexity in the second and third part of this guide.
Example 1: Blink with Gemma M0You will find the code in the code section of this project.
The first lines do the necessary imports. Line 6 initializes the data structure needed for cooperative multitasking (the queue). Line 7 initializes the DotStar on Gemma M0. Line 8 constructs an array with five colors: red, yellow, green, blue and violet.
When using the cooperative_multitasking library, the rest of the code follows stereotypical patterns or rules:
1. There is a procedure for every state.
For making the DotStar of a Gemma M0 blink, there are obviously two states: the DotStar is on and the DotStar is off. This is represented by the two procedures on() and off().
2. The body of a procedure for a state first brings the hardware into the desired state. Then it defines what should happen next.
So for the on state, the procedure on() makes the DotStar show a random color with lines 11-14. And then it defines in line 15 that after one second (that is 1000 milliseconds), the hardware should be put to the off state.
For the off state, the procedure off() switches off the DotStar in lines 18-19. Then it defines in line 20 that after 2 seconds (or 2000 milliseconds), the hardware should be put back to the on state. If you wonder why color (1, 1, 1) is used to switch off the DotStar - (0, 0, 0) seems to have a special meaning and doesn't change the color.
3. Choose what happens when the hardware is powered on.
Line 22 states that when the Gemma M0 is powered on, the DotStar should be in the on state, too.
4. Let the hardware run.
That's done with while loop in lines 24-25: As long as there are tasks in the queue, run them.
Try it yourselfIf you want to try the example:
- Update Gemma M0 to CircuitPython 9.2.8, see https://learn.adafruit.com/adafruit-gemma-m0/circuitpython#set-up-circuitpython-quick-start-2979187.
- Download the driver bundle for CircuitPython 9 from here https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/tag/20250810.
- Extract adafruit_pixelbuf.mpy and adafruit_dotstar.mpy from the driver bundle and copy them to the lib folder of Gemma M0.
- Download cooperative_multitasking.mpy from https://bitbucket.org/amotzek/circuit-python/downloads/ and copy it to the lib folder of Gemma M0.
- Copy the source code of the example to code.py in the root directory of Gemma M0.
This was a really simple example. Of queue with tasks, priorities and interrupts, we saw the queue only. And the queue holds at most one task. The second part of this guide will have a more complex example.
Comments