Micro:Bit Bubble Level
Lesson Objectives:
- Students will learn about conditionals and functions
- Students will learn about trigonometry
- Students will learn to use micro-controllers
Materials:
Let's Get Started!
A bubble level, like the one in the picture above, is used to tell whether a surface is flat. If you put a level on a flat surface, the bubble will rest in the middle. If the surface is tilted, the bubble will move to the highest side, indicating visually that there is a slope.
You can use accelerometer values to determine whether the micro:bit is on a flat surface or not and use images on the display to indicate a tilt.
Goal:
Create a bubble level with your micro:Bit. The goal is to have the micro:Bit light up the center LED when the micro:Bit is roughly level. When it is not level, use the other LEDs to indicate the tilt. (The simplest version would use an arrow to display the direction of tilt or you could emulate a bubble level by lighting up an LED to indicate the direction and magnitude of the tilt.
Step 1: Read accelerometer values from micro:bit
To start, let's make a script that reads accelerometer values from the microbit and prints them to the REPL in Mu.
In order to have your code repeatedly take samples, put it inside a while True loop. While loops will be explained more in future lessons, but for now just know that the syntax looks like this:
To start, let's make a script that reads accelerometer values from the microbit and prints them to the REPL in Mu.
In order to have your code repeatedly take samples, put it inside a while True loop. While loops will be explained more in future lessons, but for now just know that the syntax looks like this:
Every line indented below the loop header will now repeat in order forever until the micro:bit is unplugged or restarted.
Inside this while loop, we need to first read the accelerometer values and then print them. We want to be able to read the output so let’s also add a sleep of 100 ms at the end of the loop. Information on how to read accelerometer values and sleep can be found in the microbit documentation. Excerpts are including in the References section below. The code for this is shown below.
Flash your micro:bit and then open REPL and you should see the accelerometer values printing every tenth of a second. You may need to press the restart button on the micro:bit after opening REPL for the values to print.
Inside this while loop, we need to first read the accelerometer values and then print them. We want to be able to read the output so let’s also add a sleep of 100 ms at the end of the loop. Information on how to read accelerometer values and sleep can be found in the microbit documentation. Excerpts are including in the References section below. The code for this is shown below.
Flash your micro:bit and then open REPL and you should see the accelerometer values printing every tenth of a second. You may need to press the restart button on the micro:bit after opening REPL for the values to print.
Step 1 Code
Step 1 Code
Step 2: Calculate Angle from accelerometer values
Now that we have the accelerometer values, we can use trigonometry to determine the micro:bit’s orientation.
You may find this article helpful for understanding how to calculate the tilt of the board. You can either use 2-axis tilt calculations, which will only work for small amounts of tilt, or you can use 3-axis tilt calculations that will handle larger angles better. The 3-axis calculations are shown below.
Now that we have the accelerometer values, we can use trigonometry to determine the micro:bit’s orientation.
You may find this article helpful for understanding how to calculate the tilt of the board. You can either use 2-axis tilt calculations, which will only work for small amounts of tilt, or you can use 3-axis tilt calculations that will handle larger angles better. The 3-axis calculations are shown below.
While X, Y, and Z change position within the equations, the two equations are still fundamentally identical. We could write the equation twice in the body of our script. However, since this section of code is repeated, this is the perfect time to create a function.
Let’s create a function called calculate_3D_tilt(a, b, c) that takes 3 accelerometer values (a, b, c) as inputs. ‘a’ is the value for the axis for which we wish to calculate the angle and ‘b’ and ‘c’ are the other 2 values. This function should perform the above calculation and return the result. You will need to use functions from the math library to perform this calculation.
The angles this function calculates are in radians, so let’s create another function called degree_from_radians(radians) to convert radians to degrees. This function takes an angle in radians as an input and returns that angle in degrees. The conversion is degrees = radians * 180/pi.
Now, instead of printing the 3 accelerometer values, print the x angle and y angle. You can also view the data graphically using the Mu Plotter. The code for this step is shown below.
Let’s create a function called calculate_3D_tilt(a, b, c) that takes 3 accelerometer values (a, b, c) as inputs. ‘a’ is the value for the axis for which we wish to calculate the angle and ‘b’ and ‘c’ are the other 2 values. This function should perform the above calculation and return the result. You will need to use functions from the math library to perform this calculation.
The angles this function calculates are in radians, so let’s create another function called degree_from_radians(radians) to convert radians to degrees. This function takes an angle in radians as an input and returns that angle in degrees. The conversion is degrees = radians * 180/pi.
Now, instead of printing the 3 accelerometer values, print the x angle and y angle. You can also view the data graphically using the Mu Plotter. The code for this step is shown below.
Step 2 Code
Step 2 Code
Using Mu Plotter
Read the instructions (https://codewith.mu/en/tutorials/1.0/plotter) for using the MU Plotter.
You will notice that the plotter can only use a TYPE of value called a TUPLE. The tuple is a collection of values that are contained in parentheses and separated by commas. (1,2) is a tuple, as is (“apple”, “banana”, “orange”). However, (4) is not a tuple, it is just a 4 in parentheses, to tell python that it is a single value tuple you must write (4,) in the case of the plotter example for a single digit you would write: print((random.randint(0, 100),))
You will notice that the plotter can only use a TYPE of value called a TUPLE. The tuple is a collection of values that are contained in parentheses and separated by commas. (1,2) is a tuple, as is (“apple”, “banana”, “orange”). However, (4) is not a tuple, it is just a 4 in parentheses, to tell python that it is a single value tuple you must write (4,) in the case of the plotter example for a single digit you would write: print((random.randint(0, 100),))
Step 3: Display image to micro:bit
Now, we will use conditional statements to display the level indication on the micro:bit itself. We will do this by creating one void function that takes two arguments, X and Y tilt (in degrees) and uses conditional statements to change the micro:bit display.
Let's start by considering what we want to display and when.
You will find the instructions for displaying images on the micro:bit in the documentation here
Once that is working, consider the limitations of this design vs a real bubble level. One limitation is that it does not tell us how much tilt there is (e.g., the severity or magnitude of the tilt). There are several ways you can address this including:
Now, we will use conditional statements to display the level indication on the micro:bit itself. We will do this by creating one void function that takes two arguments, X and Y tilt (in degrees) and uses conditional statements to change the micro:bit display.
Let's start by considering what we want to display and when.
- If the micro:bit is within 5 degrees of flat, we want to display a SMALL_SQUARE or SMILE (or anything else you feels make sense).
- We consider within 5 degrees to be flat because, as you can see in REPL, when you run the code from step 2, python stores floating-point numbers with a lot of significant figures, so it is very unlikely that the angle calculations will equal 0 even when the micro:bit is flat to the human eye. For our purposes, 5 degrees is close enough, though you can test different values once your program works.
- If the angle is more than 5 degrees in any direction, we want to display an arrow pointing towards the high side of the micro:bit. If both the x and y direction are tilted we want to display a diagonal arrow in the appropriate direction.
You will find the instructions for displaying images on the micro:bit in the documentation here
Once that is working, consider the limitations of this design vs a real bubble level. One limitation is that it does not tell us how much tilt there is (e.g., the severity or magnitude of the tilt). There are several ways you can address this including:
- Adjust the intensity of the LED’s based on the severity of the tilt.
- Make your display show a single lit up LED that moves around in imitation of a real bubble level.
Step 3 Code
Step 3 Code
References
The following functions (documentation copied from Python math documentation and microBit microPython) documentation may be helpful.
The full documentation may be found here:
The following functions (documentation copied from Python math documentation and microBit microPython) documentation may be helpful.
The full documentation may be found here:
Useful Functions
math.pi
the ratio of a circle’s circumference to its diameter
math.atan2(y, x)
Return atan(y / x), in radians. The result is between -pi and pi. The vector in the plane from the origin to point (x, y) makes this angle with the positive X axis. The point of atan2() is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle. For example, atan(1) and atan2(1, 1) are both pi/4, but atan2(-1, -1) is -3*pi/4
microbit.accelerometer.get_x()
Get the acceleration measurement in the x axis, as a positive or negative integer, depending on the direction. The measurement is given in milli-g. By default the accelerometer is configured with a range of +/- 2g, and so this method will return within the range of +/- 2000mg.
microbit.accelerometer.get_y()
Get the acceleration measurement in the y axis, as a positive or negative integer, depending on the direction. The measurement is given in milli-g. By default the accelerometer is configured with a range of +/- 2g, and so this method will return within the range of +/- 2000mg.
microbit.accelerometer.get_z()
Get the acceleration measurement in the z axis, as a positive or negative integer, depending on the direction. The measurement is given in milli-g. By default the accelerometer is configured with a range of +/- 2g, and so this method will return within the range of +/- 2000mg.
microbit.display.scroll(value)
Scrolls value horizontally on the display. If value is an integer or float it is first converted to a string using str().
the ratio of a circle’s circumference to its diameter
math.atan2(y, x)
Return atan(y / x), in radians. The result is between -pi and pi. The vector in the plane from the origin to point (x, y) makes this angle with the positive X axis. The point of atan2() is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle. For example, atan(1) and atan2(1, 1) are both pi/4, but atan2(-1, -1) is -3*pi/4
microbit.accelerometer.get_x()
Get the acceleration measurement in the x axis, as a positive or negative integer, depending on the direction. The measurement is given in milli-g. By default the accelerometer is configured with a range of +/- 2g, and so this method will return within the range of +/- 2000mg.
microbit.accelerometer.get_y()
Get the acceleration measurement in the y axis, as a positive or negative integer, depending on the direction. The measurement is given in milli-g. By default the accelerometer is configured with a range of +/- 2g, and so this method will return within the range of +/- 2000mg.
microbit.accelerometer.get_z()
Get the acceleration measurement in the z axis, as a positive or negative integer, depending on the direction. The measurement is given in milli-g. By default the accelerometer is configured with a range of +/- 2g, and so this method will return within the range of +/- 2000mg.
microbit.display.scroll(value)
Scrolls value horizontally on the display. If value is an integer or float it is first converted to a string using str().