Microbit Motoring

For Christmas last year (I know, a long time ago), I got a BitBot as a present. This is a small robot - motors, lights, sensors, and power pack - you can plug a microbit into an be able to control the two motors independently, switch on lights and use the sensors to give the robot a bit of "intelligence".

The API for controlling the robots is a little confusing to keep in your head; I kept finding I had to turn the robot over to refresh my memory on which pin was connected where. This told me I need to write some abstractions to make motor control and using the BitBot a bit easier from client code.

Motors

I started by creating a Motor class to represent one of the left or right motors on the BitBot. Each is wired to two different output pins, for direction and speed, on the microbit but that detail will be handled by the next level of code up as you will see in a minute.

To move the motor forward at full speed, we set the speed pin to 1 (high) and the direction pin to 0 (low). To reverse at full speed, we reverse those settings.

We can control the speed of each motor using Pulse Width Modulation (PWM) which is a technique to vary the power going to the motor by switching it on and off quickly so that the average power is proportionate to the full power. E.g. switching the motor on for 50% of the time and off for the other 50% will give us 50% of the speed.

PWM values range between 0 (off) and 1023 (on). For reverse mode, we need to provide the complement of the forward value, so a 75% speed forward would be a 25% PWM in reverse.


from microbit import *

class MicrobitMotor:
    
    def __init__(self, speed_pin, direction_pin):
        self.speed = speed_pin
        self.direction = direction_pin

    def forward(self, percent):
        self.pwm_speed(percent * 10.23, 0)
    
    def reverse (self, percent):
        self.pwm_speed(1023-(percent * 10.23), 1)

    def stop(self):
        self.pwm_speed(0, 0)

    def full_speed(self, digital_direction):
        self.speed.write_digital(1 if digital_direction == 0 else 0)
        self.direction.write_digital(digital_direction)

    def pwm_speed(self, analog_speed, digital_direction):
        if 1023 >= analog_speed >= 0:
            self.speed.write_analog(analog_speed)
            self.direction.write_digital(digital_direction)

BitBot

Now we can create a BitBot class which will, at it's simplest, let us display the direction we are moving in and move the motors together, forward or reverse, or independently to turn in a circle.



class BitBot:
    
    def __init__(self):
        self.left_motor = MicrobitMotor(pin0, pin8)
        self.right_motor = MicrobitMotor(pin1, pin12)
        
    def forward(self, percent = 25):
        display.show(Image.ARROW_N)
        self.left_motor.forward(percent)
        self.right_motor.forward(percent)

    def reverse (self, percent = 25):
        display.show(Image.ARROW_S)
        self.left_motor.reverse(percent)
        self.right_motor.reverse(percent)
        
    def circle_left (self, percent = 25):
        display.show(Image.ARROW_W)
        self.left_motor.reverse(percent)
        self.right_motor.forward(percent)
        
    def circle_right (self, percent = 25):
        display.show(Image.ARROW_E)
        self.left_motor.forward(percent)
        self.right_motor.reverse(percent)

    def stop(self):
        display.show(Image.SQUARE_SMALL)
        self.left_motor.stop()
        self.right_motor.stop()       

During development I found displaying an icon on the microbit to indicate the desired direction hugely useful in debugging when the bitbot would veer away from my intended track.

Program

The client program becomes a bit easier to read and understand



bitbot = BitBot()

move_duration = 3000 # 3 seconds
motor_speed_percent = 50

while True:
    bitbot.forward(motor_speed_percent)
    sleep(move_duration)
    bitbot.circle_right(motor_speed_percent)
    sleep(move_duration)
    bitbot.reverse(motor_speed_percent)
    sleep(move_duration)
    bitbot.circle_left(motor_speed_percent)
    sleep(move_duration)
    bitbot.stop()
    sleep(move_duration)