4. BitBotXL module

The BitBotXL module is required to control the BitBotXL buggy.
Download the python file BitBotXL.py module.
Place it in the mu_code folder: C:\Users\username\mu_code
The file needs to be copied onto the microbit.
In Mu editor, with the microbit attached by USB, click the Files icon.
Files on the microbit are shown on the left.
Files in the mu_code folder are listed on the right.
Click and drag the BitBotXL.py file from the right window to the left window to copy it to the microbit.
The images below are for the MOVEmotor, but illustrate the idea.
Before copying:
../../_images/Mu_files.png

After copying:

../../_images/Mu_files_copied.png

4.1. Use BitBotXL library

To use the BitBotXL module, import it via: import BitBotXL.
from microbit import *
import BitBotXL

4.2. BitBotXL code

The BitBotXL module is shown below.
  1# bitBotXL module for motors, line following and distance sensing
  2# requires microbit v2
  3# GMC-code; 2022-4
  4# The MIT License (MIT)
  5
  6# A microbit v2 micropython module
  7# line sensors use i2c
  8# speed from -10 to 10
  9
 10
 11from microbit import *
 12import neopixel
 13import utime
 14
 15# constants
 16RIGHT_LIGHT_SENSOR_PIN = pin1
 17LEFT_LIGHT_SENSOR_PIN = pin2
 18DISTANCE_SENSOR_PIN = pin15
 19LEFT_FWD_PIN = pin16
 20LEFT_BWD_PIN = pin8
 21RIGHT_FWD_PIN = pin14
 22RIGHT_BWD_PIN = pin12
 23LEDS_PIN = pin13
 24BUZZER_PIN = pin0
 25__I2CADDR = 0x1C  # address of PCA9557
 26
 27leds = neopixel.NeoPixel(LEDS_PIN, 12)
 28
 29
 30class BitBotXLMotors:
 31    def __init__(self):
 32        # left and right motor adjustments
 33        self.dec_left = 1
 34        self.dec_right = 1
 35
 36    def stop_left(self):
 37        LEFT_FWD_PIN.write_digital(0)
 38        LEFT_BWD_PIN.write_digital(0)
 39
 40    def stop_right(self):
 41        RIGHT_FWD_PIN.write_digital(0)
 42        RIGHT_BWD_PIN.write_digital(0)
 43
 44    def stop(self):
 45        self.stop_left()
 46        self.stop_right()
 47
 48    def set_bias_correction(self, direction="left", percent=0):
 49        """Set left/right bias to match motors."""
 50        factor = (percent + 100) / 100
 51        if direction == "left":
 52            self.dec_left = factor
 53        elif direction == "right":
 54            self.dec_right = factor
 55
 56    @staticmethod
 57    def _analog_speed(speed):
 58        # input speed = -10 to 0 to 10
 59        # output = 0 to 1023
 60        if speed < 0 and speed >= -10:
 61            return -int((speed * 100) - 23)
 62        elif speed > 0 and speed <= 10:
 63            return int((speed * 100) + 23)
 64        else:
 65            return 0
 66
 67    def left_motor(self, speed=2, duration=None):
 68        an_speed = self._analog_speed(speed / self.dec_left)
 69        # an_speed = self._analog_speed(speed)
 70        # display.scroll(an_speed, delay=60)
 71        if speed > 0:
 72            LEFT_FWD_PIN.write_analog(an_speed)
 73            LEFT_BWD_PIN.write_digital(0)
 74        else:
 75            LEFT_FWD_PIN.write_digital(0)
 76            LEFT_BWD_PIN.write_analog(an_speed)
 77        if duration is not None:
 78            utime.sleep_ms(duration)
 79            self.stop_left()
 80
 81    def right_motor(self, speed=2, duration=None):
 82        an_speed = self._analog_speed(speed / self.dec_right)
 83        # an_speed = self._analog_speed(speed)
 84        # display.scroll(an_speed, delay=60)
 85        if speed > 0:
 86            RIGHT_FWD_PIN.write_analog(an_speed)
 87            RIGHT_BWD_PIN.write_digital(0)
 88        else:
 89            RIGHT_FWD_PIN.write_digital(0)
 90            RIGHT_BWD_PIN.write_analog(an_speed)
 91        if duration is not None:
 92            utime.sleep_ms(duration)
 93            self.stop_right()
 94
 95    def forwards(self, speed=2, duration=None):
 96        self.right_motor(speed)
 97        self.left_motor(speed)
 98        if duration is not None:
 99            utime.sleep_ms(duration)
100            self.stop()
101
102    def backwards(self, speed=2, duration=None):
103        self.right_motor(-speed)
104        self.left_motor(-speed)
105        if duration is not None:
106            utime.sleep_ms(duration)
107            self.stop()
108
109    @staticmethod
110    def _inner_turn_speed(speed, tightness=2):
111        return speed / tightness
112
113    def left(self, speed=2, tightness=2, duration=None):
114        # right motor faster than left
115        # display.scroll(outer_speed)
116        # display.scroll(tightness)
117        inner_speed = self._inner_turn_speed(speed, tightness)
118        # display.scroll(inner_speed)
119        self.left_motor(inner_speed)
120        self.right_motor(speed)
121        if duration is not None:
122            utime.sleep_ms(duration)
123            self.stop()
124
125    def right(self, speed=2, tightness=2, duration=None):
126        # left motor faster than right
127        inner_speed = self._inner_turn_speed(speed, tightness)
128        self.left_motor(speed)
129        self.right_motor(inner_speed)
130        if duration is not None:
131            utime.sleep_ms(duration)
132            self.stop()
133
134    def spin_left(self, speed=2, duration=None):
135        self.left_motor(-speed)
136        self.right_motor(speed)
137        if duration is not None:
138            utime.sleep_ms(duration)
139            self.stop()
140
141    def spin_right(self, speed=2, duration=None):
142        self.left_motor(speed)
143        self.right_motor(-speed)
144        if duration is not None:
145            utime.sleep_ms(duration)
146            self.stop()
147
148
149class BitBotXLDistanceSensor:
150    def distance(self):
151        DISTANCE_SENSOR_PIN.write_digital(1)
152        utime.sleep_us(10)
153        DISTANCE_SENSOR_PIN.write_digital(0)
154
155        while DISTANCE_SENSOR_PIN.read_digital() == 0:
156            pulse_start = utime.ticks_us()
157        while DISTANCE_SENSOR_PIN.read_digital() == 1:
158            pulse_end = utime.ticks_us()
159
160        pulse_duration = pulse_end - pulse_start
161        distance = int(0.01715 * pulse_duration)
162        return distance
163
164
165class BitBotXLBuzzer:
166    def buzz(self, duration):
167        """Sound a buzz for duration milliseconds."""
168        BUZZER_PIN.write_digital(1)
169        sleep(duration)
170        BUZZER_PIN.write_digital(0)
171
172
173class BitBotXLLineSensor:
174    def linesensor(self, direction):
175        """Read line sensor."""
176
177        dir = direction
178        if dir == "LEFT":
179            return self._getLine(0)
180        elif dir == "RIGHT":
181            return self._getLine(1)
182
183    def _getLine(self, bit):
184        """Reads value of left or right line sensor."""
185
186        mask = 1 << bit
187        value = 0
188        try:
189            value = i2c.read(__I2CADDR, 1)[0]
190        except OSError:
191            pass
192        if (value & mask) > 0:
193            return 1
194        else:
195            return 0