MicroPython for Micro:bit Workshop

Updated 10 March 2019

Welcome to the MicroPython for micro:bit workshop! This workshop is aimed at makers who want to start programming with MicroPython, we will be using the micro:bit as it is very easy to use so we can cover the important concepts that will apply to the other MicroPython platforms like the Pycom and ESP32.

Microbits and circuit for the programing with MicroPython

Before we get started, we are going to need a few items to get through this tutorial:

Hardware

Software

If you run into any issues throughout the workshop, then please reach out on the forum. We are full-time makers and are here to help!

Course Outline


Chapter 1 – Introduction to MicroPython

What is MicroPython?

MicroPython is a version of the Popular coding Language Python 3, That has been cut down and implemented on microcontrollers. If you have used Python and Arduino, you will know why this is something that would get us excited. The Arduino uses a version of the C programming language and although C is very powerful it is not the friendliest language to work with.

MicroPython. unlike C, is an interpreted language, so the code is not compiled beforehand it is executed at runtime. This can be good as it allows for fast easy coding but can also lead to problems as errors will not be caught until the code is run and introduces some overhead to computing your code.

If you want a more in-depth look at the pros and cons of MicroPython, Stephen has made an excellent MicroPython Primer that covers all the basics. 

The micro:bit hardware

You may be wondering why we have chosen to do an introduction to MicroPython with a board like the micro:bit with its brightly coloured interface and focus on education and fun for children.

The reason is simply that all of those things hide that fact that the micro:bit is a fairly formidable microcontroller that will run circles around many more “serious” development kits like the Arduino Uno.

Microbit compared to the Arduino Uno

Board Arduino Uno BBC Micro:Bit
Language C (Ardunio) MicroPython & C(Ardunio)
Word Size 8 bit 32 Bit
CPU Cores 1 1
Clock Speed 16MHz 16MHz
Flash Memory 32KB 256KB
SRAM 2KB 16KB
WiFi No No
BlueTooth No BLE
GPIO 20 17 (11 Digital, 6 Dual)
    Digital 14 17
    Analogue  6 6
Capacitive Touch No 3
Operating Voltage 5V 3.3V
Compass/Magnetometer No Yes
Accelerometer  No Yes
Temperature (CPU) No Yes
Built-in LED 1 25
Built-in Button 0 2

The micro:bit also supports MakeCode a block-based code editor it is also compatible with other similar languages such as Scratch.

These things combined with the ease at which prototyping can be done with the tab connectors along the bottom make it easy to see why everyone seems to love this board.

 

What can we do with MicroPython?

This is a difficult question to answer, this platform is incredibly adaptable and very easy to code, you will find that your biggest limitation is your imagination (Yes this is a cliche, but accurate :) ).

We are going to be using the micro:bit which has a lot of extra features built in and is very popular and easily available board. 

There is a massive range of MicroPython boards and some of them that will truly astound you with the number of features. A great example it the Pycom range that have a Dual Core CPU and variants are available for IoT, LoRa, GPS and many more.

I am sure that your mind is already buzzing with ideas from this brief introduction, by the end of this workshop you should have a good idea of how to start making them a reality.

Now with the why out of the way let's get to the how!


Chapter 2 – Getting Ready

To start with we need to set up your micro:bit so that it can be programmed with MicroPython. Many of the MicroPython boards come with tools that are designed specifically for it, we are going to be using one our favourite tool for editing the code on the micro:bit, the editor called Mu (Mu is the Greek letter engineers use to represent micro).

Firmware Update

The Firmware Update for the micro:bit could not be easier,  all you need to do is download the micro:bit firmware then power up your micro:bit while holding down the button on the back of the board. 

You can then copy firmware update into the drive called MAINTENANCE. The micro:bit drive will disappear and return as a drive called MICROBIT, ready for coding.

Plug it in!

This step is fairly easy all you need is to get your micro USB cable plug it into your micro:bit and in a microsecond, you will be ready to start coding in MicroPython. The only hard part is dealing with all the micros.

MicroPython Workspace

One of the great things about MicroPython is that you do not actually need a specific IDE to write python code for it. This means that you can write code for it in any editor you like, Including Notepad. All you need to do is save your code as main.py and copy it over to the MICROBIT drive that appears when you plug it into your computer.

As mentioned earlier we are going to be using the Mu editor, this editor gives us access to a lot of special tools for debugging and writing code that will make it a lot easier.

When you start-up Mu for the first time you will be presented with a choice to customise your experience based on the board you are using. If the board you are using is not in the list you can simply use the Python 3 mode.

Mu programming environment in microbit mode

You can write the following Code we will then save this as main.py

import machine # Generic library for MicroPython Hardware
print("Hello I am micro:bit\n") # print some Text.
print("I can do",machine.freq(),"things per second") # Get the Frequency of the CPU

Note: this snippet of code will run on almost any MicroPython board.

By hitting the Flash button we are able to “burn” the code to the chip, then opening the REPL (Read Evaluate Print Loop) option will let us see the output of the code.

Now if we click on the REPL button we will be able to see the output of the code we have just written.

You will now notice that with the REPL window open, the Flash and Files buttons will be greyed out, REPL will need to be disconnected by clicking the REPL button again before we can access these functions again.

You can connect to REPL on any MicroPython board by using a serial Client like PuTTY.

Now that we have had a look at the Mu editor, we are ready to start writing some MicroPython code. Finally!


Chapter 3 – Hello World!

Now that we have everything set up it’s time to have some fun! we are going to write some code that will let us interact with the micro:bit through buttons and an LED.

It is worth pointing out that though many of the details and concepts will translate to other MicroPython boards the specific libraries to interface with the hardware are unique to each board.

Wiring in an extra LED

We will wire an LED in series with a 330ohm Resistor to pin0, you can see the Fritzing diagram below.

Circuit Diagram for wiring an LED to the microbit 

Microbit with a digital output LED.

Let's Write Some Code!

We are going to write a simple python script that will turn an LED off and on by pressing the A button. In computer science, this method of state dependant switch is called a latch, and though it may seem like an obvious piece of code it is very useful.  Below we have the completed piece of code for this tutorial, you can either copy it over and read the explanations of the functions below or watch the video where we will go through writing the code line by line. 

import microbit # import the micro:bit library
import time     # import time library

myButton = microbit.button_a    # define Variable to be our Button
outputPin = microbit.pin0       # define variable to be Output Pin

while True:
    if myButton.is_pressed() == 1:          # check if the button is pressed
        if outputPin.read_digital() == 1:   # read the Current state of the LED
            outputPin.write_digital(0)      # if LED is on Turn it off
        else:
            outputPin.write_digital(1)      # if LED is not on, turn it on
        time.sleep(.5)                      # sleep using the python library time in seconds
    microbit.sleep(125)  

Import

This function is where Python (and by extension MicroPython) gets its power and versatility from if you want something in python chances are there is a library for it.

For this chapter, we are going to be using two libraries that will add some extra functionality that we need for this example.

microbit  

This library gives you access to the MicroPython Hardware, without this we would have a very hard time knowing how to access all of the buttons, lights, and other features of the micro:bit.

time

This is a python library that gives us access to functions that have to do with time, without this python thinks about time in clock cycles and they happen so fast it is hard for humans to keep up.

Variables

Variables are kind of like little boxes that we can store values in, we can store lots of different things in these boxes, for example, we could store a number or a bunch of characters. In this case, we are going to use the variable to store the Pin and button we have chosen this will make it easy to change the output pin or button if we want to later. (Give it a try! change the pin number or button_a to button_b)

While True

For now, we will not look too deeply into this, but this will basically loop through anything that is tabbed in from it.

	while True
		This Block
		Will Run
 		Forever
	This will run after the loop, so will never happen. 

micro:bit Hardware functions

we are going to use a few functions from the micro:bit library, these will let us interface with the hardware. We will have a look at each one individually.

button_a.is_pressed()

This is the built-in function for checking if button A is being pressed it will return a value of True or 1 if the button is pushed and will be 0 or False as all other times. Changing to button_b will swap the button you are working with.

pin0.read_digital()

This will cause the micro:bit to look at what the value on the pin is and if it detects a voltage it will return 1 and 0 if there is not one. This function with any pin from pin0 to pin20

pin0.write_digital(input)

This function will write input value to the digital pin, the value must be 1 or 0, when it is set to one there will be 3V active on that pin when set to 0 there will be no voltage.   

If else and the not yet seen else if

The if statement is one of the most important control structures you will learn in coding as it allows us to make decisions. The if statement will evaluate the term you give it and if it is true it will run the block of code, If we would like to run a different block of code if the evaluation returns False we can use the else statement. We have several evaluations that are we can use with if statements (and anywhere else we want a True/False result)

Operation

Symbol

True

False

Greater Than

6 > 2

2 > 6

Less Than

3 < 9

9 < 3

Equal

==

3 == 3

3 == 9

Not Equal

!=

3 != 9

3 != 3

Greater Than or Equal To

>=

9 >= 3

9 >= 10

Less Than or Equal To

<=

6 <= 6

6 <= 9

There is a third statement that relates to the if block and that is the else-if, which will perform another evaluation after the first. To make this clear I have written some pseudocode below.

If Eval1
	Do this is if Eval1 is True.
elif Eval2  (Test if Eval1 is False)
	Do this if Eval2 is true.
else
	Do this if neither of the above are True. 

Sleep

It is worth making a note of the time.sleep and microbit.sleep functions. If you have come from Python you will be used to the sleep time being set in seconds the micro:bit library uses the more C like (read Arduino) way of using milliseconds and it may catch you out. 

Now we have our first Complete MicroPython script with if statements and Digital I/O, Next we are going to go analogue! 


Chapter 4 – Analogue Inputs and Outputs

Now that we have mastered the digital inputs we age going to have a look at the analogue functionality of the micro:bit. You may think that analogue is less advanced than the digital inputs this is not actually the case Digital Signals are ones that have only two states 0 and 1. Analogue signals are continuous meaning that they can have any value in some range (for example 0-1000). Let's have a look at how to use them.

 

The circuit

The first thing we need to do is add an analogue input to read. To do this we will wire a potentiometer(pot) to pin1. To do this we will wire from the 3.3V pin to the left of the pot, pin1 to the middle pin and the right pin we will wire to another 330ohm resistor then to ground. (see Diagram below.)

 Microbit with LED and potentiometer wired to tab connectors.Analogue output circuit wired to microbit

Code an Adjustable Blinking light.

We are going to create another state-based program this one will use while loops one that will define an On function that will have a flashing light that’s period is controlled by the potentiometer. It will also have an off state when the light is off and nothing will happen. You can start with the completed code and then we will go through and explain all the functions we are using.

from microbit import * 				# another way to import so we don’t need microbit.

analogueOut = pin0 				# Store Pin0 as variable
analogueIn = pin1 				# Store Pin1 as variable

Steps = 1000					# Number of steps and final brightness level
state = "on"					# Current State of the on off switch

while True: 					# Loop forever
    while state == "on":				# Loop while state is on
        cycleTime = analogueIn.read_analog()	# Read Value from Potentiometer 
        timeDelay = cycleTime/Steps		# Calculate  the time delay needed

        for i in range(0,Steps,1):			# Loop while i is between 0 and Steps
            analogueOut.write_analog(i)		# Set analogueOut to i
            sleep(timeDelay)				# Wait for a moment
            if button_a.is_pressed() == 1:		# Check button
                state = "off"				# Set State to off
                break					# End For loop

        for i in range(Steps,0,-1):			# Loop while i is between 0 and Steps
            analogueOut.write_analog(i)		# Set analogueOut to i
            sleep(timeDelay)				# More waiting… 
            if button_a.is_pressed() == 1:		# again with the button check
                state = "off"				# Can you guess?
                break					# Does this mean it is broken?

    analogueOut.write_analog(0)			# Set analogue out to off
    sleep(1000) 					# wait so we don’t turn back on
    while state == "off":				# Loop while off
        if button_a.is_pressed() == 1:		# even more button checking
                state = "on"				# turn back on
        sleep(200)        				# time delay to save cycles.

Import again

This time we are going to use import a little differently, this time we will not need to use the microbit.function() syntax we used in the last chapter. This can be handy as it makes your code easier to type, though can get confusing if you have two libraries that have functions with the same name like time.sleep and micropython.sleep .

Strings

A string is a series of characters that are defined by surrounding the stored value in quotation marks (“). We can compare strings by using the equal-to comparator( ==). It is very important to note that if you compare the string “1” to the number 1 or “a” to “A” it will evaluate as false because to MicroPython they are different.

While loop

Last time we had a quick look at the while True loop, the while loop will evaluate a condition on every cycle so that if the condition evaluates to false the loop will stop running and continue with the script.

 
While Eval1 # This will re evaluate after every loop.
	Do some things
	Make sure you do something 
that may change the outcome of Eval1
or you will be stuck forever
I will run once the Eval1 has evaluated as False

microbit.<pin>.read_analogue()

read_analogue() will return a value between 0 and 1024(largest number that can be represented in 10bits) that represents a voltage at the pin of between 0-3.3V

microbit.<pin>.write_analogue(input)

write_analogue() takes input in the range of 0-1024 and sets the pin to be a voltage between 0-3.3Volts using a PWM method. If you want to know more detail about the I/O on the micro:bit Read the docs(https://microbit-micropython.readthedocs.io/en/latest/pin.html) is a great place to find it.

for loop

A for loop is a structure that will run for a set number of iterations and uses an index variable for each step.

for ValueAtCurrentStep in AvailableValues
		Do this to ValueAtCurrentStep
		Also Do this to ValueAtCurrentStep
	This will happed after the block has been done to every Item in AvailableValues

range(startPoint,endPoint,stepSize)

This function creates a range of values that will begin with the startPoint end on the step before the endPoint and will take a step of size stepSize. The step size is optional if you do not define it the function will just assume you meant 1.  To make this obvious I have made a list below that shows some results.

Start

End

Step

Result

1

5

 

1,2,3,4

-5

0

-1

0,-1,-2,-3,-4

-20

50

10

-20,-10,0,10,20,30,40


Chapter 5 – Talking to other devices

The next thing we are going to look at is how to use the built-in Bluetooth radio, the micro bit uses a cut down version of the Bluetooth. The micro:bit radio broadcasts to out to anyone that is listening to that if you send a message all the micro:bits that are listening will also get the message.  We are going to write some code that will send a message to any listening micro:bits and receive messages from any that are broadcasting. 

Microbits talking to each other over Bluetooth radio

Radio Code

import microbit
import radio                      # Import Radio Functions

radio.on()                      # Turn the Radio on

alphabet = ["a","b","c"]        # Store Our Alphabet, you can add more. 
alphIndex = 0                   # Initialise the index variable.

microbit.display.show(alphabet[alphIndex])              # Display Currently Indexed Character

while True: # our old friend
    if microbit.button_a.is_pressed() == 1:             # Check if button A is pressed
        alphIndex = alphIndex + 1                       # If it is advance the Alphabet index
        if alphIndex > len(alphabet)-1:                 # Check if alphIndex is still in range
            alphIndex = 0;                              # Restet index to zero
        microbit.display.show(alphabet[alphIndex])      # Display Indexed Character 
        microbit.sleep(125)                             # sleep for debouncing
    
    if microbit.button_b.is_pressed() == 1:             # Check if button B is pressed
        radio.send(alphabet[alphIndex])                 # Send current character
        microbit.sleep(125)                             # sleep for debouncing

    incoming = radio.receive()                          # grab incoming message
    
    if incoming:                                        # if there is a message
        microbit.display.scroll(incoming)               # Scroll the incoming character
        microbit.display.show(alphabet[alphIndex])      # Return to displaying what we will send

    microbit.sleep(125)                                 # sleep to save cycles

Lists

Lists are incredibly useful memory structures that let us store a collection of values like our alphabet.

Definition

We can define a list by using square brackets ( [ and ] ) and the values can be any python datatype (including other lists!). We separate values with a comma (,).

	myList = [0, 1, 2, 3, 4]	#index locations of a list

Retrieve

We can get data from a list by using the list name and square brackets.

		myList[0]  # returns the first element
		myList[-1]  # returns the last element

len(myList)

when given a list this function will return the number of items in the list. This is very useful in making as if you try to get a value from a location that is not in the list this will cause python to crash.

Radio

The radio functions let us send and receive messages from and to other micro:bits, we cannot choose who we are sending the message too so any message we send will go to every micro:bit within range (Though they will have to be listening.)

radio.on()

This function turns the radio on, by default, it is off in the micro:bit to save power. Once it is turned on we can use the other functions to send and receive messages.

radio.send(“Gaa Gaa”)

Send will broadcast the input string to all micro:bits in the area. In our code, we are sending a single character but you can also send strings of multiple characters.

radio.receive()

Getting messages is a little more complicated even though we only have the single command. The radio has a small amount of memory that is used to store messages until they are retrieved this is called the queue. The queue will hold onto all messages it gets until you use receive() which will return and remove the oldest value from the queue (by default there are 3 queue locations). 


Chapter 6 – Built-in Sensors

The micro:bit has some sensors built into it, not all MicroPython boards come with sensors built in but many do, and the way that we interface with them is in most cases the same.  The first step is to import the required library then use that library to grab the data from the sensor.

We are also going to look at some more python constructs the list and the function. Lists store a collection of variables under the same name and can be indexed conveniently. A function is a block of code we can call at any time.

The great thing about working with built-in sensors and outputs is that we do not need to wire up any circuits to make this work.

 

Working with sensors.

Sensors are a way that we can let our projects detect the world around it, there are sensors available for almost anything you can imagine. There are a few things that need to be taken into account when working with all sensors though.

Comparisons

To take this into account when you are designing your code with sensors we always avoid using the equals comparator (==). If you want your code to get to a very precise level you will need to use the Logic and to make sure that a value is between some range. For example, if you are checking a water level and you wanted to be at 1 litre you could use the if statement. The distance between the values should be chosen based on how accurate your sensors are or how precise your level needs to be while aiming to have the largest gap between them as possible. 

if (water > 0.999) & (water < 1.001):
	return True

Noise

Noise is anything that is picked up by the sensors that are not the signals you are trying to measure, for example, if you are trying to measure the force of acceleration as a bus accelerates then the vibration of the bus is noise, but if you are interested in the vibration the force of acceleration is the noise.

The Virtual Cup.

We are going to write some code that will allow us to use the micro:bit as a virtual cup. The code will use the accelerometer to detect the acceleration of gravity to determine if our cup is upright or tipped. We are going to make our virtual cup refill whenever it is upright. 

Virtual cup showing almost full

import microbit # import the micro:bit library
import microbit # import the micro:bit library
import time     # import time library

level = 0

cupLevel=[          #Store the Images of out Cup Level
        microbit.Image("99999:" "99999:" "99999:" "99999:" "99999"),
        microbit.Image("00000:" "99999:" "99999:" "99999:" "99999"),
        microbit.Image("00000:" "00000:" "99999:" "99999:" "99999"),
        microbit.Image("00000:" "00000:" "00000:" "99999:" "99999"),
        microbit.Image("00000:" "00000:" "00000:" "00000:" "99999"),
        microbit.Image("00000:" "00000:" "00000:" "00000:" "00000")
        ]

def cupTip(direction, level):                   # define the function cupTip with two inputs
    if (abs(direction) > 400 ) & (level > 0):   # Check direction input and tank Level
        pourRate = level - round(abs(direction)/20)     # Calulate how much level to remove
        return(pourRate)        # Return the amout to remove 
    elif level > 0:             # If not tiped stay at the same level
        return level            # return the level
    else:
        return 0            # Return zero is cupLevel goes negative

while True:
    accelXYZ = microbit.accelerometer.get_values()  # get list of accelerometer values.
    roll = accelXYZ[0]      # first item in list
    pitch = accelXYZ[1]     # second item in list
    yaw = accelXYZ[2]       # second item in list

    if (pitch > 1000) & (level < 1000): # detect the gravity vector on pitch and tank level
        level += 40                     # is less than full
    level = cupTip(roll,level)          # call function and use the returned value as the new level
    level = cupTip(yaw,level)           # call function and use the returned value as the new level
        
    microbit.sleep(125)                 # wait for a moment
    
    # Assign image from list based on the current level 
    if level < 10:
        microbit.display.show(cupLevel[5])
    elif level < 200:    
        microbit.display.show(cupLevel[4])
    elif level < 400:
        microbit.display.show(cupLevel[3])
    elif level < 600:
        microbit.display.show(cupLevel[2])
    elif level < 800:
        microbit.display.show(cupLevel[1])
    else:
        microbit.display.show(cupLevel[0])
        
    print(level) # We will only see this in REPL

Functions

 Functions are block of code that can be called from any point after they are defined to perform some action on some inputs.

Definition

Functions are defined with the def tag and can have as many inputs as you like. You can pass any kind of variable to a function in python this includes lists and any sensors like we have in the example.

def myFunction(input1, intput2, input3, …, inputN)
	#line one of function
	#line two of function
	#last line of function
# This line is not in the function

Return(MyValue)

The return statement will end the end the function and return the value you give  it back to where the function was called. Like the inputs return can return any kind of variable including lists and objects.

def myFunction(input1)
	output1 = input * 2	# run out code
	return(output1) 	#Return Value and end.  
	input1 * 4 		# This line will never run
# This line is not in the function

Microbit.Image()

The microbit Image function will display an image on the front 5 by 5 led display, there are a bunch of predefined Images you can use or you can define your own as we have in the example. Each number in the strings represents an LED 0 indicates off and 9 is maximum brightness. The colon is the end of the line and is not needed on the last line. The following will display a scaled Pascal’s triangle.

myImage = microbit.Image("00000:" "00200:" "02420:" "24642:" "46864")

To display the image we need to use the microbit display function show.

microbit.display.show(myImage)

Microbit sensors

There are three major sensors on the micro:bit, an Accelerometer, compass, and a Temperature sensor.

Note: There is a way to hack a simple light sensor using the LED grid.

We are just going to have a look at how to access the raw data the micro:bit has some advanced features such as direction and gesture detection that you can also use.

Temperature

The micro:bit temperature sensor is inside the CPU and is designed to monitor the chip though because the micro:bit runs very cool it is usually very close to the ambient temperature.   

microbit.temperature()

Compass

The compass is actually a magnetometer and can be used to detect any magnetic fields, not just the earths. (Actually if you have a large magnetic field nearby your compass will not work well). To call the raw values from the micro:bit we use compass.get_x(), it detects in 3 dimensions so we can use get_y() and get_z() as well.

microbit.compass.get_x()		# Get magnetic field in X
microbit.compass.get_y()		# Get magnetic field in Y
microbit.compass.get_z()		# Get magnetic field in Z

Accelerometer

The accelerometer is the sensor that we have used in our example and can be accessed in the same way the compass can with get_x, get_y, get_z. You can use get_values() to return all 3 dimensions mesured in milig(1 thousandths of earths gravity) over a range of -2 to 2 gravities  in a single list.

microbit.accelerometer.get_x()				# Get acceleration in X
microbit.accelerometer.get_y()				# Get acceleration in Y
microbit.accelerometer.get_z()				# Get acceleration in Z
microbit.accelerometer.get_values()			# Return list of dimensions

Well done!

This Workshop is still under construction, Please Check back soon for more Chapters.

You should now have a pretty good idea of how to program in MicroPython on the micro:bit. Thanks for following along I hope you have enjoyed this tutorial and it has helped you become more comfortable and ready to explore the possibilities of MicroPython.

If you are still hungry for more embedded Python you can check out other MicroPython Tutorials or our Circuit Python Tutorials which use Adafruit's own libraries to simplify MicroPython.

As always if you have any further question or comments you would like to add to this tutorial then join the discussion with our community of dedicated makers. 

Have a question? Ask the Author of this guide today!

Please enter minimum 20 characters

Your comment will be posted (automatically) on our Support Forum which is publicly accessible. Don't enter private information, such as your phone number.

Expect a quick reply during business hours, many of us check-in over the weekend as well.

Comments


Loading...
Feedback

Please continue if you would like to leave feedback for any of these topics:

  • Website features/issues
  • Content errors/improvements
  • Missing products/categories
  • Product assignments to categories
  • Search results relevance

For all other inquiries (orders status, stock levels, etc), please contact our support team for quick assistance.

Note: click continue and a draft email will be opened to edit. If you don't have an email client on your device, then send a message via the chat icon on the bottom left of our website.

Makers love reviews as much as you do, please follow this link to review the products you have purchased.