Microbit Beacons

Microbit Beacons

Radio on the microbit is one of it's best features for interactivity between learners. Here I'll show a couple of examples of using the radio to create static beacons (and may be hidden) to broadcast information that can be picked up by someone moving around the space with a suitably coded receiver.

Hot and Cold

As well as simple receiving text messages over the radio, the microbit also supports a full mode which returns not just the message text but also the signal strength and a timestamp. We can use the signal strength to get a measure of how close or far away we are from a beacon. Signal strength is measured in decibels but comes to us as a score from 0 (strongest) down to -255 (weakest).

Beacon

We'll start off with a simple beacon, broadcasting some text every half a second.


from microbit import *
import radio

radio.on()

while True:

    radio.send('ping')
    sleep(500)

Receiver

Our receiver shouldn't care what the message is, just how strong the signal is. We convert the 0-255 scale into a percentage signal strength.


from microbit import *
import radio

radio.on()

while True:
    display.show("?")

    message = radio.receive_full()

    if message:
        signal_strength = message[1]
        percent_strength = ((signal_strength + 255)* 100) / 255
        display.scroll(str(percent_strength))

    sleep(200)

Place the beacon somewhere out of the way (or have someone else place it) then walk around and try to find it based purely on signal strength. You can use the radio config option to change the radio power to suit the size of the space you are working in.

Simple Treasure Hunt

Now that we can send and receive messages from a single beacon, we can program several of them with unique ids, hide them around the local environment and use them as the basis for a simple treasure hunt. Each beacon can be given a text name or a number which it broadcasts. The treasure hunter has a receiver listening for any radio signal and completes the "hunt" by checking off each of the beacons that they find.

Beacon

Here's a simple beacon with a hard-coded id. Each beacon will need a different id flashed onto it.


from microbit import *
import radio

beacon_id = 1

# Lowest power transmit
radio.config(power=0) 
radio.on()

while True:

    radio.send(str(beacon_id))
    display.show(str(beacon_id))

    sleep(1000)

Alternatively, a slightly fancier beacon can be programmed with a dynamic id using the a and b buttons like this:


from microbit import *
import radio

beacon_count = 5
beacon_id = 1

# Lowest power transmit
radio.config(power=0) # power is 0 -> 7
radio.on()

while True:
    # decrease beacon_id
    if button_a.was_pressed():
        beacon_id = max(beacon_id - 1, 1)
   # increase beacon_id
    elif button_b.was_pressed():
        beacon_id = min(clue +  1, beacon_id)

    # broadcast the current beacon_id
    else:
        radio.send(str(beacon_id))

    display.show(str(beacon_id))
    sleep(500)

Each beacon is flashed with the same code and it's up to the person doing the hiding to program each one with a unique number before hiding them.

Full Treasure Hunt

Finally, instead of a simple id, we can embed clues to a treasure hunt trail in the beacons so that each beacon can be configured to broadcast one clue. By sending the beacon's id out with the clue, we can even program the receiver to accept the clues in sequence so that the hunter has to visit the beacons in the correct order to make sense of the clues.

Beacon


from microbit import *
import radio

all_clues = [
    "Walk Fifteen paces east",
    "Ten paces north",
    "Walk forwards",
    "Look under the seat",
    "Hooray, you win!"
]

clue_count = len(all_clues)
clue = 1

radio.config(power=4) # power is 0 -> 7
radio.on()

while True:
    # decrease clue number
    if button_a.was_pressed():
        clue = max(clue - 1, 1)
   # increase clue number
    elif button_b.was_pressed():
        clue = min(clue +  1, clue_count)

    # broadcast the current clue
    else:
        clue_text = str(clue) + ":" + all_clues[clue - 1]
        radio.send(clue_text)

    display.show(str(clue))
    sleep(500)

Receiver


from microbit import *
import radio

# starting clue
clue = "hint: Walk around to find the first clue"
display.scroll(clue)
next_clue = 1

radio.on()

while True:
    display.show("?")

    # are we near any clue beacons?
    message = radio.receive()

    if message:
        # picked up a clue !
        number, text = message.split(":")
        # is it the one we want?
        if int(number) == next_clue:
            clue = "clue " + str(number) + ":" + text
            next_clue += 1
            display.scroll(clue)

    sleep(250)

There are lots of variations you can explore once the system is up and running:

  • Change the receiver to remind you about the last clue
  • Draw a starting image
  • Draw an image to show when the beacon is broadcasting.
  • Change the code to make a Pokemon Go collecting game. Instead of clues, beacons can broadcast an image of a Pokemon and the hunter has to collect them all.

Adjust the power from each beacon to make it more challenging. If you are playing with more than one group in an area, consider changing the radio channel for each group so the clues belonging to each group don't interfere with each other.