BBC micro:bit
Bit:Commander Pushbuttons

Introduction

The 4 pushbuttons on the Bit:Commander are arranged as they would be on a game controller, where they might be used for shooting, action buttons, jumping, selecting and so on. They are also conveniently placed if you want to use them for directional control. When the screen is small that is sometimes better than a joystick.

micro:bit circuit

Programming

The buttons don't need any special treatment. You just need to remember which pins are connected to which button. The relevant section of the pinout is,

ComponentPin
Red Pushbutton12
Blue Pushbutton15
Green Pushbutton14
Yellow Pushbutton16

Simple State Check

In MicroPython, you don't have any events. For something like a button press, which happens at an arbitrary moment in time, you need to have your program keep checking the state of the button. In its simplest form, you can write a program like this,

from microbit import *

while True:
    btn_red = pin12.read_digital()
    if btn_red:
        display.show(Image.HAPPY)
    else:
        display.show(Image.SAD)
    sleep(20)

The program has an infinite loop, a while loop with a stopping condition that cannot be met. The state of the red button is read. If it is held down, a happy face is shown, otherwise a sad face is shown. The small delay determines the frequency with which the button is checked or 'polled'.

State Change

You don't always want to have a user holding a button down for something to happen. Often you want to trigger something once when a button is pressed. To do this, you can use a 'follower' variable to keep track of the previous button state.

from microbit import *

last = 0
while True:
    btn_red = pin12.read_digital()
    if btn_red!=last:
        if btn_red:
            # pressed
            display.show(Image.HAPPY)
        else:
            #released
            display.show(Image.SAD)
    last = btn_red
    sleep(20)

In this program, the display is only changed if the button state changes. When the program starts, there is nothing on the display. The moment that the button is pressed, it changes to a happy face. A sad face is shown when the button is released.

In the previous example, the display was being updated every single iteration, even if the button wasn't being used at all. In this version, the display only gets updated when the button state changes.

Press Or Release

Each time you read a button, there are 4 things you could determine. You can work it if it wasn't touched since your last reading. You can know if it was pressed, released or held down too. In some projects, I want to be able to trigger an event for a single press of the button. Depending on the application, it might feel more natural to do this as the button is released, rather than when it is pressed.

Since each button state could be stored as a bit, I like to form a binary number from them, by left shifting the last reading and adding it to the current. This makes an integer from 0 to 3. By examining the value of that integer, you can determine a few more things about your button reading to decide how to respond.

from microbit import *

last = 0
while True:
    btn_red = pin12.read_digital()
    e = (last<<1) + btn_red
    if e==0:
        display.show(Image.SAD)
    elif e==1:
        print("Pressed")
    elif e==2:
        print("Released")
    else:
        display.show(Image.HAPPY)
    last = btn_red
    sleep(20)

All Button States

We can also use a binary pattern to represent all of the button states. That gives you a single integer that you can examine to find out the state of any button. This program prints out the integer in denary and binary.

from microbit import *

# read the buttons and use binary 4 bits to represent
# the button states
def get_btns():
    pattern = 0
    for i,p in enumerate([pin12,pin15,pin14,pin16]):
        pattern += p.read_digital() << i
    return pattern
    
while True:
    b = get_btns()
    print(b, '{:04b}'.format(b))
    sleep(50)

Using a follower with the 4 bit integer lets you know if there was a change to the state of any button. That can be particularly useful to avoid having a display refreshing too often or a musical note changing.

More Button Stuff

This program was written when I was trying to work out a tidy approach to using the buttons together.

# Reading the buttons on the Bit:Commander
from microbit import *

# read the buttons and use binary 4 bits to represent
# the button states
def get_btns():
    pattern = 0
    for i,p in enumerate([pin12,pin15,pin14,pin16]):
        pattern += p.read_digital() << i
    return pattern
    
# function to compare current and last readings
# and return a list of buttons pressed 
def btn_pressed(prev, current):
    # concatenate each pair of bits
    evts = [((prev >> i & 1)<<1) + (current >> i & 1) for i in range(4)]
    # return the bit positions equalling 1 (01)
    return [i for i,e in enumerate(evts) if e==1]
    
# function to compare current and last readings
# and return a list of buttons released
def btn_released(prev, current):     
    # concatenate each pair of bits
    evts = [((prev >> i & 1)<<1) + (current >> i & 1) for i in range(4)]
    # return the bit positions equalling 2 (10)
    return [i for i,e in enumerate(evts) if e==2]

# button colour names
btns = ["Red", "Blue", "Green", "Yellow"]

# store previous button state for each reading
last = 0
while True:
    btnstate = get_btns()
    if btnstate!=last:
        # get presses and releases
        p = btn_pressed(last, btnstate)
        r = btn_released(last, btnstate)
        # match up button names
        pp = [btns[i] for i in p]
        rr = [btns[i] for i in r]        
        print("State:", btnstate,"Pressed:",pp, p, "Released:",rr,r)
    last = btnstate
    sleep(20)

I use the function to make a binary integer of the button states in most of the programs where I use the Bit:Commander buttons. I also find the line labelled # concatenate each pair of bits very useful. It creates a list of the 2 bit integers for each button.