Pairing micro:bits with Raspberry Pi over Bluetooth


Due to various code clubs I've run over the last couple of years I've ended up with a pair of Microbits gathering dust on the shelf in my office. When I recently came by a Raspberry Pi I wondered if I could combine all these and get them to do something useful together.

When I first bought the Micro:bits the only way to use the Bluetooth was to run micro python the device, but as I found out Bluetooth support has now made it into the make code block language which makes this far easier for anyone to use.

Setting up the Pi and Microbit

I found this excellent blog which showed how to:

  1. Write a simple program for the Micro:bits in the visual block language - which essentially turned them into headless sensors which listen over Bluetooth for commands. They can either report from their sensors or to execute commands such as to display text. 
  2. Install the bluezero tool and pair a microbit
  3. Write my first Python program (or more accurately copy one of the ones from the blog to see if it works.
I'll not repeat anything in the blog as it's really clear - however I did find two things to note:

First - when I set "JustWorks pairing" in the Project Settings of the block editor the pairing failed. When I switched it to "No pairing required" it still needed pairing but the pairing worked this time. This feels really odd but it did seem to work.

Secondly. the code and libraries need python3 (my Rasperry Pi is old enough for /usr/bin/python to be a symlink to python2.7 not python3) so just beware if you get an error like this:
Traceback (most recent call last):
  File "piBitControl.py", line 2, in <module>
    from bluezero import microbit
ImportError: No module named bluezero

Adding a second Microbit

So far all I've done is follow someone else's instructions - so my next step was to wonder if I could get two or more Micro:bits to work with my Pi at the same time. It turns out this is fairly easy.

All the code for this is available at https://github.com/jaglees/pibit

Essentially for each Micro:bit just define a microbit object then connect to it. As I define each microbit I put this into a Python dictionary so this is flexible enough to cope with any number of Micro:bits (although I've only tested it with two).

The code looks a bit like this:

Firstly to define the microbit object using the adapter address of the Pi (here called adapter) and MAC address of the microbit (here called key). This key is also the key used in the dictionary to find this later
                devices[key] = microbit.Microbit(adapter_addr=adapter,
                                    device_addr=key,
                                    accelerometer_service=True,
                                    button_service=True,
                                    led_service=True,
                                    magnetometer_service=False,
                                    pin_service=False,
                                    temperature_service=True)

Then call the connect method - if either the connect or the definition of the object fails we "pop" this device out of the dictionary. This means that at the end of this initialisation phase the dictionary contains only the which have been setup and successfully connected to.

                # Connect to microbit
                print ("....Connecting")
                devices[key].connect()
                print ("....Connected")
            except:
                # If initilisation or connection failed remove this device from device list
                print ("....Initialisation or Connection failed")
                devices.pop(key, None)

This is obviously good defensive coding in general but is important here because if a Micro:bit is turned off then we need a way of failing gracefully.
Note: it takes 30 seconds for a connect to timeout if the microbit isn't turned on (or in range) so each missing Micro:bits does slow down the start of the program quite a bit.

Making the Micro:bits do something

So far we've connected to more than one Micro:bit but now to make them do something. Here my code was fairly simple - more a proof of concept more than anything else as there was nothing obvious I wanted to do with these other than use them as sensors. I decided to:

  1. Read the temperature from each Micro:bit (essentially using them as remote wireless thermometers) and display this on screen on the Pi
  2. Check the buttons on the Micro:bits to allow me to terminate the program using the microbit buttons (using the A key)
  3. Check the Pi keyboard as another way to interrupt the program in a nicer way than Ctrl+C. As I wrote this I extended this to add the ability to broadcast messages to all the Pis (by pressing the "m" for message key).

The way this all works is that we have two loops - one ("for dkey in devices") to circle through each device in the devices dictionary; and an outer while (which loops forever unless one of the two break conditions noted above causes the "looping" variable to be set to false). 

Inside the loops it checks for a keyboard press, checks the value of the A key on the device being checked, and reads the temperature sensor and outputs to the console.

    while looping:

        # Check each device in turn - getting the unique key of each device
        for dKey in devices:
            
            # If the escape key is pressed on the PI then break
            keyboardVal = nbc.get_data()
            if keyboardVal == '\x1b':  # x1b is ESC
                print ("Raspberry Pi Escape key pressed")
                looping=False
            elif keyboardVal == 'm':  
                msg = input("Enter message to output to all Pis:")
                for d in devices:
                    devices[d].text=msg


            # If the A button is pressed - exit the loop (a kill switch)
            if (devices[dKey].button_a > 0):
                print("Device [", dKey, "] Button A Pressed" )
                looping=False
            
            # Print the temperature from the device 
            print('Temperature [', dKey, '] = ', devices[dKey].temperature)
        
        time.sleep(1)

The NonBlockingConsole class is defined in a separate module just for ease of reading but essentially reads from the keyboard without stopping execution.

Overall this was an interesting little challenge which was fairly easy - if anyone fancies hooking up their Micro:bits as sensors with a Pi I hope this is helpful. 
 

Disclaimer

My postings reflect my own views and do not necessarily represent the views of my employer.

All Code is Open Source under the GCP-3 licence - so fill your boots if it's helpful 

For more by me follow me on Twitter @JAGLees

Comments