BitBot and BitBot XL Programming in Python
Programming in Python
For text-based programming there is micro-python, and I prefer to use this offline using the Mu editor. It provides a very neat and easy way of interfacing to the micro:bit without all the fuss of dragging and dropping.
Note on examples: We want to show people how the various features can be used. We don’t want to do the coding for you – that is the learning experience you should be taking away from using it. In the following examples, we will give you the tools to use all the features of Bit:Bot but it is up to you to combine them together in your code to make it do something useful. We do give a complete example of line-following – but it is a very basic algorithm and will get confused at T-junctions and crossings; and it doesn’t use the FireLeds.
Download Python examples at the bottom of this page.
Some great tutorials/examples by Mark Atkinson here
Motors
Each motor has two pins connected to it. If the first pin is High and the second is Low, then the motor will drive Forward. Conversely, if the first is Low and the Second is High then it will drive in Reverse.
For BitBot Classic: Left pins are P0, P8 and Right pins are P1, P12
For BitBot XL: Left pins are P16, P8 and Right pins are P14, P12
The simplest way to make the motors move Forward is to set the First pin to HIGH and the Second pin to LOW. eg for BitBot XL:
Move left motor Forwards:
pin16.write_digital(1)
pin8.write_digital(0)
Move left motor Reverse:
pin16.write_digital(0)
pin8.write_digital(1)
If we want to change the speed of a motor, so that it is not going at full speed all the time, we need to use PWM (Pulse Width Modulation). This is a means of changing the amount of power given to the motor by switching it on and off very fast. The percentage value of PWM determines the amount of each cycle that the output is ON. So a percentage of 100% is the same as being on all the time and thus the same as the examples above. A percentage of 50% would mean that the motor is only energised half the time, so it will go much slower. Note that the actual speed of the motor is not the same as the percentage of PWM – the motor won’t turn at all if the PWM value is too low and you will also get some stuttering at certain values. Nevertheless, being able to change the speed makes for a much better robot. For example, you can make a line follower that smoothly follows the line, rather than the normal shaking left and right.
To change the PWM value of a pin, we must use the analog_write commands. These can be set to a value between 0 (always off = 0%) to 1023 (always on = 100%), so 50% would be 511. Here are the commands to change the speed of the Right motor to approx 75% (value is 770) for the BitBot XL
Move right motor forwards at 75%
pin14.write_analog(770)
pin12.write_analog(0)
To move it in reverse, we simply apply the PWM value to the other pin instead and set the first pin to 0
Move right motor Reverse at 75%
pin14.write_analog(0)
pin12.write_analog(770)
FireLeds
The FireLeds smart RGB pixels are able to display any of 16 million colours by selecting a value of 0 to 255 for each of the Red, Green and Blue LEDs on each chip. The whole thing is controlled by a single pin on the BBC micro:bit (pin 13 for all models of Bit:Bot). It is simple to use the included neopixel libraries to control each FireLed individually.
The pixels are labelled on the Bit:Bot. From 0 to 5 on the left arm and from 6 to 11 on the right arm.
Set FireLed 2 to purple (red and blue)
from microbit import *
import neopixel
fireleds = neopixel.NeoPixel(pin13, 12)
fireleds[2] = (40, 0, 40)
fireleds.show( )
The first line is the standard import all from Microbit library.
The second line imports the neopixel library. We only need to do this once, at the very top of your Python program.
The third line creates a Python list with an element for each pixel. As shown, it specifies 12 pixels connected to pin 13.
The fourth line sets the pixel we have selected (number 2 in this case) to the colour which is set by three values in the brackets, each value can be from 0 to 255 and covers Red, Green and Blue. In our example we have set Red and Blue to 40.
The fifth line tells the neopixel library to copy all the data to the neopixels, from the Python list that we have been working with. It is only at this point that the LEDs change. In general, you would make all the changes you want and only at the end would you use a np.show( )
Line Follower Sensors
The pins used for line following are quite different for the BitBot Classic and the BitBot XL. BitBot Classic uses Pin11 (Left sensor) and Pin5 (Right sensor), which are the same pins as used for the Microbit’s two buttons. This can cause all sorts of issues.
The BitBot XL uses an I2C chip for these pins, with bit 0 of the resulting value being the Left sensor and bit 1 being the Right sensor.
The following two programs perform the same operation. They read the state of each line sensor and set the corresponding FireLed on the end of the stalks to either Red or Green. The BitBot Classic first
from microbit import *
import neopixel
fireleds = neopixel.NeoPixel(pin13, 12)
while True:
if(pin11.read_digital() == 0):
fireleds[5]=(40,0,0)
else:
fireleds[5]=(0,40,0)
if(pin5.read_digital() == 0):
fireleds[11]=(40,0,0)
else:
fireleds[11]=(0,40,0)
fireleds.show()
sleep(200)
And now the equivalent program for the BitBot XL. You can see that we’ve defined a new function getLine( ) that reads the I2C device and returns the state of bit 0 (Left sensor) or bit 1 (Right sensor)
from microbit import *
import neopixel
I2CADDR = 0x1c # address of PCA9557
fireleds = neopixel.NeoPixel(pin13, 12)
def getLine(bit):
mask = 1 << bit
value = 0
try:
value = i2c.read(I2CADDR, 1)[0]
except OSError:
pass
if (value & mask) > 0:
return 1
else:
return 0
while True:
if(getLine(0) == 0):
fireleds[5]=(40,0,0)
else:
fireleds[5]=(0,40,0)
if(getLine(1) == 0):
fireleds[11]=(40,0,0)
else:
fireleds[11]=(0,40,0)
fireleds.show()
sleep(200)
The above programs are complete, with all the imports and defines required. This simple line following algorithm for the BitBot XL uses the getLine( ) function defined above as well as some simple motor functions which aren’t defined here
while True:
lline = getLine(0)
rline = getLine(1)
if (lline == 1):
spinLeft( )
elif (rline == 1):
spinRight( )
else:
forward(speed)
Light Sensors
These are analog sensors and will give a value of 0 to 1023, where 0 is fully dark and 1023 is maximum brightness
BitBot XL: On the BitBot XL these sensors are connected to Pin1 (Right) and Pin2 (Left). To get the current values of each sensor into variables rightVal and leftVal:
rightVal = pin1.read_analog()
leftVal = pin2.read_analog()
BitBot Classic: As there are only 3 analog pins available on the micro:bit (without affecting the LED displays) and we are using 2 of them to control the motors, we only have one left (Pin 2) to read the analog values from 2 line sensors. How can we do this? Well, the Bit:Bot has an analog switch that uses a digital output signal (pin 16) to determine whether the analog input we are reading is for the left sensor or the right sensor.
Therefore, to read the light sensors we need to set the selection output pin first, then read the analog data.
In Python, we can do it like this to read the values into 2 variables called leftVal and rightVal:
pin16.write_digital(0) # select left sensor
leftVal = pin2.read_analog()
pin16.write_digital(1) # select right sensor
rightVal = pin2.read_analog()
Buzzer
The buzzer is a very simply device that outputs a 2.4kHz sound when it is set to ON. It is NOT controlled by the tone signal that can be output by the micro:bit on Pin 0 so you don’t need to install any libraries to operate it.
It is connected to Pin14. Setting this to ON (1) will activate the buzzer and setting to OFF (0) will deactivate it.
In Python, a very simple and annoying beep, beep, beep sound can be made as follows:
while True:
pin14.write_digital(1)
sleep(400)
pin14.write_digital(0)
sleep(400)
Ultrasonic Distance Sensor
This optional HC-SR04 ultrasonic distance sensor addon can be used most easily in Microsoft PXT. In MicroPython we can use the utime module to measure time at microsecond level. Below we have a function called sonar() which returns the number of cm to the nearest object. Then we have a while loop that prints the distance every second:
from microbit import *
from utime import ticks_us, sleep_us
SONAR = pin15
def sonar( ):
SONAR.write_digital(1) # Send 10us Ping pulse
sleep_us(10)
SONAR.write_digital(0)
SONAR.set_pull(SONAR.NO_PULL)
while SONAR.read_digital() == 0: # ensure Ping pulse has cleared
pass
start = ticks_us() # define starting time
while SONAR.read_digital() == 1: # wait for Echo pulse to return
pass
end = ticks_us() # define ending time
echo = end-start
distance = int(0.01715 * echo) # Calculate cm distance
return distance
while True:
display.scroll(sonar())
sleep(1000)