micro:bits and raspberry pis

whoami

Luke Spademan

0) Student

1) GCSEs

3) gave a lightening talk last year

2) @lukespademan: [twitter, github, etc]

Project

Project

Connect 4 Game

Micro:bits have 5x5 led matrix

Connect 3 Game

micro:bits

micro:bits

2 micro:bits play connect 3

They use the radio module to communicte

import radio

radio.config(channel=10)
radio.on()

data = radio.receive()
print(data)
radio.send("Hello World")

and raspberry pis

and raspberry pis

It then sends the data over serial to a raspberry pi

Another micro:bit listens in on the game play

The raspberry pi then displays that data using a sense hat with RGB LED matrix

print("Hello World")

and tensor flow

and tensor flow

I was going to have AI play you at connect 3

But reaslised with the hardware I was using it wasn't going to work

The lessons learnt part of the title, is the lesson that neural networks and raspberry pis are difficult to get talking

Especially because I wanted to do the training on the RPi

microbits = 3  # for game play and sending data to RPi
raspberrypis = 1  # for displaying game
tensorflow = 0  # sorry

Plan

Plan

  • 2 micro:bits play connect 3
  • Your turn and you update the display, you also send the data using the radio
    • Opponent to see board
    • 3rd micro:bit to see board
    • 3rd micro:bit sends data over serial
    • raspberry pi displays that data on an RGB LED matrix

Let's do this

Step 1

moving the counter along the top

Step 1.0

from microbit import *

board = "00000:" \
        "00000:" \
        "00000:" \
        "00000:" \
        "00000"
x = 0
while True:
    if button_a.is_pressed():
        x += 1
        if x == 5:
            x = 0
            board = board[:4] + "0" + board[5:]
        else:
            board = board[:x-1] + "0" + board[x:]
        board = board[:x] + "9" + board[x+1:]
        print(board)
        display.show(Image(board))

Step 1.1

from microbit import *

board = "00000:" \
        "00000:" \
        "00000:" \
        "00000:" \
        "00000:"
x = 0
while True:
    if button_a.is_pressed():
        x += 1
        if x == 5:
            x = 0
            board = board[:4] + "0" + board[5:]
        else:
            board = board[:x-1] + "0" + board[x:]
        board = board[:x] + "9" + board[x+1:]
        display.show(Image(board))
        sleep(250)  # allows recognition on one press

Step 1.2

from microbit import *


def set_board_pixel(x, y, board, colour):
    pos = get_pos(x, y)
    board = board[:pos] + str(colour) + board[pos+1:]
    return board

def get_pos(x, y):
    pos = (6*y) + x
    return pos

board = "00000:" \
        "00000:" \
        "00000:" \
        "00000:" \
        "00000:"
x = 0
while True:
    if button_a.is_pressed():
        x += 1
        if x == 5:
            x = 0
            board = set_board_pixel(4, 0, board, "0")
        else:
            board = set_board_pixel(x-1, 0, board, "0")
        board = set_board_pixel(x, 0, board, "9")
        print(board)
        display.show(Image(board))
        sleep(250)

Step 2

send data to the other micro:bit

Step 2

# player1.py
from microbit import *
import radio
radio.on()
radio.config(channel=27)

def show_board(board):
    display.show(Image(board))
    radio.send(board)

# more functions and setup

while True:
    if button_a.is_pressed():
        x += 1
        if x == 5:
            x = 0
            board = set_board_pixel(4, 0, board, "0")
        else:
            board = set_board_pixel(x-1, 0, board, "0")
        board = set_board_pixel(x, 0, board, "9")
        print(board)
        show_board(board)
        sleep(250)
# player2.py
from microbit import *
import radio

radio.on()
radio.config(channel=27)

board = "00000:" \
        "00000:" \
        "00000:" \
        "00000:" \
        "00000"

while True:
    data = radio.recive()
    if data:
        board = data
        display.show(Image(board))

Step 3

dropping the counter

Step 3

# player1.py

# imports, radio setup, functions


def get_board_pixel(x, y, board):
    pos = get_pos(x, y)
    value = board[pos]
    return value

Step 3

# player1.py

# imports, radio setup, functions

def get_board_pixel(x, y, board):
    # code

def fall(x, y, board, colour):
    set_board_pixel(x, y, board, colour)
    show_board(board)
    falling = True  # while counter has not hit bottom or other counter
    while falling:
        if y >= 4 or get_board_pixel(x, y+1, board) != "0":
            falling = False
        else:
            y += 1
            board = set_board_pixel(x, y, board, colour)  # moves counter down by one
            board = set_board_pixel(x, y-1, board, 0)  # removes counter from above
            show_board(board)
            sleep(500)  # waits for 0.5 seconds so animation can be seen
    return board

while True:
    if button_a.is_pressed():
        # moving counter code...
    elif button_b.is_pressed():
        board = fall(x, y, board, my_colour)

Step 4

taking it in turns to have a go

Step 4

# player1.py

# imports
# functions, setup

my_turn = True
my_colour = "9"

while True:
    if my_turn:
        if button_a.is_pressed():
            # moving counter code...
        elif button_b.is_pressed():
            board = fall(x, y, board, my_colour)
            my_turn = False
            radio.send("YT")
    else:
        data = radio.receive()
        if data:
            if data == "YT":
                my_turn = True
            else:
                board = data
                display.show(Image(data))
# player2.py

# imports
# functions, setup

my_turn = False
my_colour = "4"

while True:
    if my_turn:
        if button_a.is_pressed():
            # moving counter code...
        elif button_b.is_pressed():
            board = fall(x, y, board, my_colour)
            my_turn = False
            radio.send("YT")
    else:
        data = radio.receive()
        if data:
            if data == "YT":
                my_turn = True
            else:
                board = data
                display.show(Image(data))
my_turn = True
my_colour = "9"
my_turn = False
my_colour = "4"

Step 5

stop players placing counters on the top row

Step 5

# imports
# setup
# functons
# more setup

while True:
    if my_turn:
        if button_a.is_pressed():
            x += 1
            while get_board_pixel(x, 1, board) != "0":
                x += 1  # move along.
                if x > 5:  # when your at the end go to the start
                    x = 0
        
                row = ["0", "0", "0", "0", "0"]
                row[x] = my_colour
                for c, pixel_colour in enumerate(row):  # counter acts as x co-ord
                    board = set_board_pixel(c, 0, board, pixel_colour)
# more code...
c = 0
for item in list:
    print(c, item)
    c += 1
for c, item in enumerate(list):
    print(c, item)

Step 6

sending data to the raspberry pi

Step 6.0

# viewer.py
from microbit import *
import radio

radio.config(channel=86)
radio.on()


while True:
    data = radio.receive()
    if data:
        if data != "YT":
            display.show(Image(data))
            print(data)
# display.py
import serial
from sense_hat import SenseHat

sense = SenseHat()  # setup sense hat (RGB LED matrix)

# copied and adapted from
# projects.raspberrypi.org/en/projects/microbit-game-controller
PORT = "/dev/ttyACM0"
BAUD = 115200
s = serial.Serial(PORT)
s.baudrate = BAUD
s.parity = serial.PARITY_NONE
s.databits = serial.EIGHTBITS
s.stopbits = serial.STOPBITS_ONE
# copy ends
sense.show_letter("~")  # lets me know that it has started
player1 = (0, 0, 255)  # colours to be displayed for
player2 = (255, 0, 0)  # each player
blank = (0, 0, 0)  # for empty spaces
edge = (100, 100, 100)  # and the edge of the board
# ^ because RPi Sense Hat LED matrix is > 5x5 on micro:bit

Step 6.1

# viewer.py
from microbit import *
import radio

radio.config(channel=86)
radio.on()


while True:
    data = radio.receive()
    if data:
        if data != "YT":
            display.show(Image(data))
            print(data)
# display.py
# imports, setup

while True:
    data = s.readline().decode("UTF-8").rstrip()
    data_s = data.split(":")

    formatted = []  # will store data to send to LED matrix

    for i in range(8*2):  # top two rows
        formatted.append(edge)

    for row in data_s:
        formatted.append(edge)  # first col is an edge
        for c in row:
            if c == "4":
                formatted.append(player2)
            if c == "9":
                formatted.append(player1)
            if c == "0":
                formated.append(blank)
        formatted.append(edge)  # two thick edge on right side
        formatted.append(edge)

    for i in range(8):
        formatted.append(edge)
    sense.set_pixels(formated)

https://trinket.io/sense-hat

Step 7

win detection

Step 7.0

def detect_win(board):
    """detects is a player has won connect 3"""
    rows = board.split(":")
rows = [
    ["00000"],
    ["00000"],
    ["00000"],
    ["00000"],
    ["00000"]
]

Formatting the board

Step 7.1

def detect_win(board):
    # setup

    """detects win on x axis"""
    for row in rows:
        for i in range(3):
            colour = row[i]
            if colour != "0":
                if row[i] == row[i+1] == row[i+2]:
                    return colour
rows = [
    ["00000"],
    ["00000"],
    ["00000"],
    ["00000"],
    ["00999"]
]

Detecting in in a row

Step 7.2

def detect_win(board):
    # setup and x axis detection
    
    """"detects win on y axis"""
    for col in range(3):
        for row in range(5):
            colour = rows[col][row]
            if colour != "0":
                if rows[col][row] == rows[col+1][row] == rows[col+2][row]:
                    return colour
rows = [
    ["00000"],
    ["00000"],
    ["00009"],
    ["00009"],
    ["00009"]
]

Detecting a win in a column

Step 7.3

def detect_win(board):
    # setup x axis and y axis detection
    
    """"detects win on y = -x + c"""
        for col in range(3):
            for row in range(3):
                colour = rows[col][row]
                if colour != "0":
                    if rows[col][row] == rows[col+1][row+1] == rows[col+2][row+2]:
                        return colour
rows = [
    ["00000"],
    ["00000"],
    ["00900"],
    ["00090"],
    ["00009"]
]

Detecting a win in a y=-x+c diagonal

Step 7.4

def detect_win(board):
    # setup, x axis, y axis and y=-x+c detection
    
    """"detects win on y = x + c"""
        for col in range(4, 1, -1):
            for row in range(3):
                colour = rows[col][row]
                if colour != "0":
                    if rows[col][row] == rows[col-1][row+1] == rows[col-2][row+2]:
                        return colour
rows = [
    ["00000"],
    ["00000"],
    ["00009"],
    ["00090"],
    ["00900"]
]

Detecting a win in y=x+c diagonal

Step 7.5


def detect_win(board):
    """detects is a player has won connect 3"""

    # [:-1] is needed as board ends with :
    # causing an emtpy list at the end or rows
    rows = board.split(":")[:-1]

    """detects win on x axis"""
    # code

    """"detects win on y axis"""
    # code

    """detects win on y=-x+c"""
    # code

    """detects win on y=x+c"""
    # code

    return None

Overview

Step 7.6

# player1.py & player2.py

# imports
# functions, setup

def detect_win(board):
    # code

# setup
while True:
    if my_turn:
        # code
        elif button_b.is_pressed():
            board = fall(x, y, board, my_colour)
            my_turn = False
            winner = detect_win(board)
            if winner == my_colour:
                congradulate()
            else:
                radio.send("YT")
    else:
        data = radio.receive()
        if data:
            if data == "YT":
                my_turn = True
            elif data == "IW":
                defeat()
            # code...

Implementing win_detection

Step 7.7

# player1.py

# imports
import music
# functions, setup

def detect_win(board):
    # code

def congradulate():
    radio.send("IW")
    display.show(Image.HAPPY)
    music.play(music.BA_DING)

def defeat():
    display.show(Image.SAD)
    music.play(music.DADADADUM)

Congradulating the winner

Step 8

ending game after win/loss

Step 8

# player1.py & player2.py

# imports
# functions, setup

def detect_win(board):
    # code

# setup
while True:
    if my_turn:
        # code
        elif button_b.is_pressed():
            board = fall(x, y, board, my_colour)
            my_turn = False
            winner = detect_win(board)
            if winner == my_colour:
                congradulate()
                break
            else:
                radio.send("YT")
    else:
        data = radio.receive()
        if data:
            if data == "YT":
                my_turn = True
            elif data == "IW":
                defeat()
                break
            # code...

That's it

so far

What have I learnt?

What have I learnt?

  • Don't be confined by the specifications of a device, or what other people tell you a device can be/do.
  • If I have an issue, search the web, and see if anyone else has solved your issue before / can help you.
  • Python programmers are the nicest out there.

 

Thank you for listening

micro:bits and raspberry pis

By Luke

micro:bits and raspberry pis

  • 2,048