Interaction at a distance feels like magic.
Magic might be useful if you’re an elf, wizard or unicorn, but such things only exist in stories.
However, there’s something much better than magic: physics!
Wireless interaction is all about physics: radio waves (a type of electromagnetic radiation, similar to visible light) have some sort of property (such as their amplitude, phase or pulse width) modulated by a transmitter in such a way that information can be encoded and, thus, broadcast. When radio waves encounter an electrical conductor (i.e. an aerial), they cause an alternating current from which the information in the waves can be extracted and transformed back into its original form.
Layers upon Layers¶
If you remember, networks are built in layers.
The most fundamental requirement for a network is some sort of connection that allows a signal to get from one device to the other. In our networking tutorial we used wires connected to the I/O pins. Thanks to the radio module we can do away with wires and use the physics summarised above as the invisible connection between devices.
The next layer up in the network stack is also different from the example in the networking tutorial. With the wired example we used digital on and off to send and read a signal from the pins. With the built-in radio on the micro:bit the smallest useful part of the signal is a byte.
A byte is a unit of information that (usually) consists of eight bits. A bit is the smallest possible unit of information since it can only be in two states: on or off.
Bytes work like a sort of abacus: each position in the byte is like a column in an abacus - they represent an associated number. In an abacus these are usually thousands, hundreds, tens and units (in UK parlance). In a byte they are 128, 64, 32, 16, 8, 4, 2 and 1. As bits (on/off signals) are sent over the air, they are re-combined into bytes by the recipient.
Have you spotted the pattern? (Hint: base 2.)
By adding the numbers associated with the positions in a byte that are set to “on” we can represent numbers between 0 and 255. The image below shows how this works with five bits and counting from zero to 32:
If we can agree what each one of the 255 numbers (encoded by a byte) represents ~ such as a character ~ then we can start to send text one character per byte at a time.
Funnily enough, people have already thought of this ~ using bytes to encode and decode information is commonplace. This approximately corresponds to the Morse-code “protocol” layer in the wired networking example.
A really great series of child (and teacher) friendly explanations of “all things bytes” can be found at the CS unplugged website.
The problem with radio is that you can’t transmit directly to one person. Anyone with an appropriate aerial can receive the messages you transmit. As a result it’s important to be able to differentiate who should be receiving broadcasts.
The way the radio built into the micro:bit solves this problem is quite simple:
- It’s possible to tune the radio to different channels (numbered 0-83). This works in exactly the same way as kids’ walkie-talkie radios: everyone tunes into the same channel and everyone hears what everyone else broadcasts via that channel. As with walkie-talkies, if you use adjacent channels there is a slight possibility of interference.
- The radio module allows you to specify two pieces of information: an address and a group. The address is like a postal address whereas a group is like a specific recipient at the address. The important thing is the radio will filter out messages that it receives that do not match your address and group. As a result, it’s important to pre-arrange the address and group your application is going to use.
Of course, the micro:bit is still receiving broadcast messages for other address/group combinations. The important thing is you don’t need to worry about filtering those out. Nevertheless, if someone were clever enough, they could just read all the wireless network traffic no matter what the target address/group was supposed to be. In this case, it’s essential to use encrypted means of communication so only the desired recipient can actually read the message that was broadcast. Cryptography is a fascinating subject but, unfortunately, beyond the scope of this tutorial.
This is a firefly:
It’s a sort of bug that uses bioluminescence to signal (without wires) to its friends. Here’s what they look like when they signal to each other:
The BBC have rather a beautiful video of fireflies available online.
We’re going to use the radio module to create something akin to a swarm of fireflies signalling to each other.
import radio to make the functions available to your Python program.
At this point the radio module is configured to sensible defaults that make
it compatible with other platforms that may target the BBC micro:bit. It is
possible to control many of the features discussed above (such as channel and
addressing) as well as the amount of power used to broadcast messages and the
amount of RAM the incoming message queue will take up. The API documentation
contains all the information you need to configure the radio to your needs.
Assuming we’re happy with the defaults, the simplest way to send a message is like this:
The example uses the
send function to simply broadcast the string
“a message”. To receive a message is even easier:
new_message = radio.receive()
As messages are received they are put on a message queue. The
function returns the oldest message from the queue as a string, making space
for a new incoming message. If the message queue fills up, then new incoming
messages are ignored.
That’s really all there is to it! (Although the radio module is also powerful enough that you can send any arbitrary type of data, not just strings. See the API documentation for how this works.)
Armed with this knowledge, it’s simple to make micro:bit fireflies like this:
# A micro:bit Firefly. # By Nicholas H.Tollervey. Released to the public domain. import radio import random from microbit import display, Image, button_a, sleep # Create the "flash" animation frames. Can you work out how it's done? flash = [Image().invert()*(i/9) for i in range(9, -1, -1)] # Event loop. while True: # Button A sends a "flash" message. if button_a.was_pressed(): radio.send('flash') # a-ha # Read any incoming messages. incoming = radio.receive() if incoming == 'flash': # If there's an incoming "flash" message display # the firefly flash animation after a random short # pause. sleep(random.randint(50, 350)) display.show(flash, delay=100, wait=False) # Randomly re-broadcast the flash message after a # slight delay. if random.randint(0, 9) == 0: sleep(500) radio.send('flash') # a-ha
The important stuff happens in the event loop. First, it checks if button A was
pressed and, if it was, uses the radio to send the message “flash”. Then it
reads any messages from the message queue with
radio.receive(). If there is
a message it sleeps a short, random period of time (to make the display more
interesting) and uses
display.show() to animate a firefly flash. Finally,
to make things a bit exciting, it chooses a random number so that it has a 1 in
10 chance of re-broadcasting the “flash” message to anyone else (this is how
it’s possible to sustain the firefly display among several devices). If it
decides to re-broadcast then it waits for half a second (so the display from
the initial flash message has chance to die down) before sending
the “flash” signal again. Because this code is enclosed within a
block, it loops back to the beginning of the event loop and repeats this
The end result (using a group of micro:bits) should look something like this: