In this animation, both the micro:bit LED matrix display and the servo horn point upward as the rest of the setup is rotated. This is an example of “tilt controlling” the servo with sensor measurements from the micro:bit module’s built-in accelerometer. (View in higher resolution .mp4 here. [1])
In this activity, you will:
These three activities (total of 14 web pages) provide a guided micro:bit accelerometer tour and also have script elements that will be reused in this activity. Although they are from the cyber:bot series, a cyber:bot is not required; you will only need a micro:bit and USB cable for these three warm up activities:
Same as Connect and Test the Servo [5].
Also the same as Connect and Test the Servo [5].
Important: Make sure to orient the servo by the micro:bit as shown in this diagram. With the horn in the 90° position, it should be pointing to the left. You will be holding the servo against the edge of the I/O adapter as you rotate it.
# accelerometer_servo_points_up from microbit import * import math while True: x = accelerometer.get_x() y = accelerometer.get_y() angle = round( math.degrees( math.atan2(y, x) ) ) needle = ( angle - 90 + 15 ) // 30 display.show(Image.ALL_CLOCKS[needle]) if angle >= 0: angle = 180 - angle else: continue value = 0.5689 * angle + 25.6 pin16.write_analog(value)
It's time to verify that your micro:bit LED matrix and servo behavior resemble what’s shown in the animation.
This script uses math.degrees and math.atan2 from the math module. So in addition to from microbit import *, this script also imports the math module.
# accelerometer_servo_points_up from microbit import * import math
Using import math instead of from math import * forces any statement that uses a math module function to prepend it with the word math. Examples: math.degrees(...) and math.atan2(x, y). If you instead use from math import *, you could simply use degrees(...) or atan2(x, y). It helps make it clear that degrees and atan2 are from the math module. To save memory, you could also use from math import degrees, atan2. Instead of all the functions in the module, only degrees and atan2 would be imported, and you would call them without prepending them with math. In other words, you would be back to just calling degrees(...) and atan2(x, y).
The while True loop starts each repetition by reading the micro:bit accelerometer’s x and y-axis measurements. These measurements were introduced in Measure Accelerometer Tilt [2].
while True: x = accelerometer.get_x() y = accelerometer.get_y()
Next, the accelerometer measurements are converted to a rotation angle, which was introduced in Measure Rotation Angles [3].
angle = round( math.degrees( math.atan2(y, x) ) )
These two statements use the measured rotation angle to display the upward pointing needle. It’s the pointing-up version of the statements for pointing down that were introduced in Display Tilt Direction [4].
needle = ( angle + 90 + 15 ) // 30 display.show(Image.ALL_CLOCKS[needle])
The tests you’ve done so far involve accelerometer measurements in the 0° to 180° range. However, the range of the positions the servo needs to hold in response are in the 180° to 0° range. So, when the accelerometer reads 180°, the servo horn needs to be at 0°. When the accelerometer reads 0°, the servo horn needs to be at 180°. ...and everything in between. For example, if the accelerometer measures 150°, the servo horn needs to be pointed at 180° - 150° = 30°. Another example, if the accelerometer measures 45°, the servo horn needs to be pointed at 180° - 45° = 135°.
This if… statement executes when the accelerometer measurements are in the 0° to 180° range. In this range, angle = 180 - angle subtracts the measured angle from 180 to produce the angle value needed to correctly position the servo. Another way to say it is that the angle = 180 - angle statement maps the 180° to 0° input range to a 0° to 180° output range. if angle >= 0: angle = 180 - angle The other half of a full rotation ranges from -1° to -179°. We don’t want a servo with a 180° rotation range to try to turn any further than 180°. So the script makes sure the servo does not respond to measurements in this range with a continue statement. The continuestatement causes the script to repeat whatever loop it’s in, skipping any statements below it. In this script, it repeats the while True loop without reaching the value = 0.5689 * angle +25.6 and pin16.write_analog(value) statements. else: continue
The y = 0.5689x + 25.6 formula was introduced in the Connect and Test the Servo Activity’s Did You Know [7] section to map a value in a 0° to 180° range to a value in the 26 to 128 range. Again, this makes it possible to convert the degree measurements we humans normally think about to numbers for the value variable in pin16.write_analog(value). The result is control pulses that make the servo turn to degree positions. Take a close look at value = 0.5689 * angle + 25.6. It’s the same formula with y renamed to value and x renamed to angle.
value = 0.5689 * angle + 25.6
At this point, the value variable is ready for pin16.write_analog, with numbers in the 25.6 to 128.0 range.
pin16.write_analog(value)
Let’s take a closer look at how the angle = 180 - angle statement affects the value stored in the angle variable.
If you don’t rotate the servo with the micro:bit and board, its horn will turn the opposite direction the micro:bit turns. The statement that makes the servo turn the opposite direction from the micro:bit is angle = 180 - angle in this if...else… statement:
if angle >= 0: angle = 180 - angle else: continue
To make it turn the same direction, the angle = 180 - angle statement needs to be removed. That might seem simple enough, but it’s in an if… statement. So, you can’t just comment it or remove it because MicroPython requires an if… statement to contain at least one statement (below and indented). Otherwise, the script would stop executing statements and display an exception message when it reached the if… statement.
To make sure theif… statement still contains a statement MicroPython can execute, you can simply add a pass statement. A pass statement essentially tells MicroPython: “Don’t do anything here, just move on to the next statement.” Even though it’s a statement that doesn’t actually do anything, it’s still a MicroPython statement.
if angle >= 0: # angle = 180 - angle pass else: continue
Here’s another way to rewrite theif… statement so it still skips servo positioning in response to angles outside 0...180. Angles outside the 0...180 range will be negative: -1…-179. So, another approach would be to rewrite the if...else... statement so it’s just an if statement that only uses continue to skip the servo positioning by jumping back to while True when angle is less than zero. When angle is greater than zero, this if… statement allows the script to keep going through the last statement in the loop, which updates the servo horn’s position with pin16.write_analog(value).
if angle <= 0: continue
The Did You Know section above explained how to make the servo horn mirror the board’s rotation if the servo is sitting still and the board is rotated.
Holding the setup like a steering wheel, adjust the script so that the servo horn and display will point up for ‘right hand turns’ only.
1. Solution:
from microbit import * import math while True: x = accelerometer.get_x() y = accelerometer.get_y() angle = round( math.degrees( math.atan2(y, x) ) ) if angle >= 0 and angle <= 90: needle = ( angle - 90 + 15 ) // 30 display.show(Image.ALL_CLOCKS[needle]) angle = 180 - angle else: continue value = 0.5689 * angle + 25.6 pin16.write_analog(value)
2. Solution:
from microbit import * import math while True: x = accelerometer.get_x() y = accelerometer.get_y() angle = round( math.degrees( math.atan2(y, x) ) ) if angle >= 45 and angle <= 135: needle = ( angle - 90 + 15 ) // 30 display.show(Image.ALL_CLOCKS[needle]) angle = 180 - angle else: continue value = 0.5689 * angle + 25.6 pin16.write_analog(value)
Links
[1] https://learn.parallax.com/sites/default/files/content/Python/servo/servo-tilt-ctrl-concept.mp4
[2] https://learn.parallax.com/tutorials/robot/cyberbot/cybersecurity-radio-tilt-control/measure-accelerometer-tilt
[3] https://learn.parallax.com/tutorials/robot/cyberbot/cybersecurity-radio-tilt-control/measure-rotation-angles
[4] https://learn.parallax.com/tutorials/robot/cyberbot/cybersecurity-radio-tilt-control/display-tilt-direction
[5] https://learn.parallax.com/tutorials/language/python/servo-position-control-python-and-microbit/connect-and-test-servo
[6] https://python.microbit.org/v/2
[7] https://learn.parallax.com/tutorials/language/python/servo-position-control-python-and-microbit/connect-and-test-servo-0