All-in-one Robotics Board + micro:bit - Review

Table of contents

RoadTest: All-in-one Robotics Board + micro:bit

Author: richwilliams

Creation date:

Evaluation Type: Development Boards & Tools

Did you receive all parts the manufacturer stated would be included in the package?: True

What other parts do you consider comparable to this product?: This is the only motor controller I've tried for the micro:bit

What were the biggest problems encountered?: Bugs in the Demo software. They were easy to fix but shouldn't have been there.

Detailed Review:

Table of contents

 

Introduction

First, I want to thank Kitronik and Element14 for selecting me to test the board.  I had fun and learned a lot testing the board.

 

The All-in-one Robotics Board for BBC micro:bit enables the BBC micro:bit to drive 4 motors (or 2 stepper motors )

and 8 servos.  The only I/O used by the board on the micro:bit is the I2C port so all the micro:bit's expansion pins are available

for other I/O devices.  The board has a 3.3V regulator to power the BBC micro:bit.

 

The heart of the board is an NXP PCA9685 16-channel, 12-bit PWM I2C LED controller.  The other chips on the board are two dual H-Bridge drivers for the 4 DC or 2 stepper motors and 2 other chips which I assume are drivers for the 8 servo outputs.  The drivers are controlled by the PWM outputs: one for each servo and two for each H-Bridge.  It's a clever way to make a low cost controller that can control three types of motors.

 

Goals for this review:

  • Verify the signals for different types of motors
  • Use the board with the micro:bit's other I/O
  • Build a robot!
  • Learn MicroPython!
  • Have fun while learning new things.

 

What's in the box?

For the RoadTest, element14 provided at the All-in-one Robotics Board and a BBC micro:bit

image

 

 

image

Here is the unpacked  Kitronik All-in-one Robotics Board

 

Documentation

None in the box but the Kitronik webpage for the board (5641-all-in-one-robotics-board-for-bbc-microbit.html) contains what you need to get started:

  • Datasheet
  • Link to MicroPython demo file on GitHub
  • Short video showing All-in-one Robotics board in action

 

What’s missing?

Schematic

 

Kitronik support

The product page also has a Q&A section. I posted a question to see if I can have the micro:bit plugged into USB while the robotics board is powered from an external supply and they quickly replied that I can.

 

Software provided

From GitHub, you can download a zip file containing:

  • License
  • Readme.md describing the calls
  • robotics.py
  • kitronik-reference-file-robotics-TOO-LARGE.py

 

The version I downloaded to test was commited on Oct 11, 2018.

The robotics.py and the TOO-LARGE files contain the same code but the comments have been removed in robotics.py to save space in the micro:bit.

 

Robotics.py defines a class named KintronikRoboticsBoard that implements methods for stepper, regular DC and servo motors.  The file also contains code that will exercise the methods if the file is loaded as the main file on the micro:bit.

 

MicroPython

After applying for this Roadtest, I read two books on MicroPython: Programming with MicroPython by Nicholas Tollervery and Make: Getting Started with the micro:bit by Wolfram Donat.

I was very impressed with MicroPython. I expected some sort of ‘Tiny Python’ that would be pretty much coding in C with Python-like syntax. MicroPython is a very full implementation of Python. We teach a Python workshop for beginning programmers and it will be very easy for those students to use those skills for Arduino like projects.

There are a couple ways to create MicroPython code for the micro:bit

  1. Use a cloud based python editor and download a hex file to transfer to the micro:bit. ( http://microbit.org )
  2. Use the stand alone Mu-editor, a simple Python editor for beginner programmers. ( http://codewith.mu )
  3. Use your normal Python editor and some tools to create the hex file. I didn’t try this.

I tried #1 and  #2 and used the app for my testing . The mu-editor app was easy to install and use. They kept it to the basic functionality needed making it easy to understand. It supports ‘minifying’ your code which strips out comments and extra spaces when creating the hex file to save memory space in the micro:bit. I later learned that saving space is very important when using MicroPython on the micro:bit.

Mu-editor is very particular about how it wants spaces around comments, commas and parenthesis and limits the lines to 79 characters. I found this annoying but assume it’s there to teach beginning programmers to be consistent.

 

image

Screenshot of mu-editor which has definite opinions about whitespace.

 

Tests to be performed

Testing will be done in three phases: Verifying the motor signals, verifying the ability to use the micro:bit I/O and building a robot.

  1. Verify the motor signals using a scope
    1. Download demo SW to micro:bit
    2. Test the stepper motor signals
    3. Test the DC motor signals
    4. Test the servo motor signals
  2. Verify the ability to use the micro:bit I/O
    1. Add header pins for the micro:bit I/O lines
    2. Breadboard an I2C and other device
    3. Test with and without the Robotics board
  3. Build a robot!
    1. TBD

 

Test setup

  • Mu 1.0.2 running on a Macintosh to edit and download code
  • Digilent Analog Discovery and Waveforms 2015 SW to measure motor signals
  • I will start with the robotics.py file from Kitronik and modify it as needed.

image

Test setup showing the Analog Discovery hooked up to the Kitronik All-in-one Robotics Board and measuring the DC motor signals.

 

Test 1: Verify the motor signals using a scope

 

Test 1-A: Download demo SW to micro:bit

This test will load the robotic.py into mu-editor and then download it into the micro:bit. The micro:bit will then be plugged into and powered by the Kitronik board
What I'm looking for:

  • The code should check and download with no errors and run with no errors when plugged into the robot board.

What I observed:

  • When I checked the code with mu-editor's check button there were many errors about spacing and line length (see the earliar picture for examples). These were easy to fix.
  • More seriously, there were references to an instance variable stepStage as stepStage instead of self.stepStage. This causes the code to fail on the micro:bit. It was easy to fix after examining the code but should not have been there.

image

Screenshot showing errors in Mu

 

Test 1-B: Test stepper motor signals

This test will exercise the stepper motor output Stepper1  using calls in the robotics.py code to move it 100 steps.

 

     theBoard.stepperMotorTurnSteps(theBoard, "Stepper1", "forward", 100)

 

What I'm looking for and why:

  • Two square waves 90° out of phase
    The phase shift, Motor1 preceeding or trailing Motor2 determines if the stepper movers forwards or backwards.
  • A voltage swing of 2 times the supply voltage
    The stepper motor is being controlled by an H-bridge which switches the polarity of the motor signal. This will look like a negative voltage on the scope when the polarity is reversed.
  • A period of around 80 ms. There are 4 stages to the stepper control. At each stage one motor switches polarity. Each stage is one 50Hz PWM cycle.
  • Dead Time Insertion when a signal switches polarity DTI, or Dead Time Insertion, is a brief period of turning off the signal for one polarity before turing on the signal for the reverse polarity. This prevents both polarities being active at the same time which shorts the power supply in a sometimes spectacular fashion.

 

What I observed:

  • The phase relationship is as expected but the square wave isn't as regular as expected.
  • The voltage swing is as expected but sometimes the signal is at 0V indicating the H-bridge is totally off.
  • The period is sometimes longer than 80ms and a stage gets extended but I did not observe a stage being too short or skipped.
  • Dead Time is being inserted but was also being inserted on when there was no polarity change.

 

image

Screen capture from Stepper Motor test.

Channel 1 (Yellow) shows motor1, which would hook up to Coil A of a 4 wire bipolar stepper motor.

Channel 2 (Blue) shows motor2, which would hook up to Coil B of a 4 wire bipolar stepper motor.

 

image

Screen capture zooming in on Dead Time Insertion.

Both motors are off (0V) during the last 500 microseconds of the PWM period.

Motor1 (Yellow) did not need dead time since it was not reversing polarity.

Motor2 (Blue) reversed polarity and needed the dead time.

 

Analysis of results:

The PWM chip has it's own clock which is not synchronized to the micro:bit. If the micro:bit hasn't updted the PWM value by the end of the period, the PWM chip will reload the last value extending the stage by 20ms. Reversing the polarity requires turning on one polarity by setting it's PWM value from 0% to 100% and then setting the other polarity's PWM from 100% to 0%. Sometimes the PWM reload happens between the two changes leaving the H-bridge in braking mode for 20ms.

Dead Time Insertion is done by never allowing a 100% duty cycle. 100% motor speed is mapped to 4000 instead of 4095 on a 12 bit PWM leaving about 460 microseconds of dead time at the end of each PWM period. This is why we see DTI even when the polarity isn't changing.

Is it a problem?

I spent a while pondering this  and thinking of a way to synchronize the micro:bit code with the PWM updates.  I also concluded it's probably harmless when the micro:bit code is late updating the PWM value since it just extends the last step.  Likewise the unneeded DTI insertions should be harmless since the H-bridge will be in coasting mode during this time.

 

Test 1-C: Test DC motor signals

This test will exercise the DC motor outputs Motor3 and Motor4 using calls in the robotics.py code to set Motor3 to 10% in one direction and Motor4 to 100% in the other direction:

 

theBoard.motorOn(theBoard, 3, "forward", 10)

theBoard.motorOn(theBoard, 4, "reverse", 100)

 

What I'm looking for and why:

  • Motor3 should have a 10% duty cycle.
    Motor3 should be at 0V for 90% of the period.
  • Motor4 should have a 100% duty cycle.
    We won't see 100% because of DTI
  • Polarity reversed between motor3 and motor4.
    One direction will show as a positive voltage on the scope and the other will show as a negative voltage.
  • Dead Time Insertion.
    Like the stepper motor, Dead Time is inserted by having a max duty cycle of 4000/4095
  • PWM period of 20ms.
    All the PWMs are running at 50 Hz.

 

What I observed:

  • Motor3 shows about a 10% duty cycle.
    Note that the -6V for 10% of the time is the motor on in the forward direction.
  • Motor4 shows an almost 100% duty cycle.
    It has +6V for 97.5% of the period and the rest is DTI.
  • The polarity is reversed between motor3 and motor4.
  • Dead Time is inserted.
    Just like the stepper motor with DTI even if there is no change.
  • PWM period is about 20ms.

 

image

Screen capture from DC Motor test.

Channel 1 (Yellow) shows motor3. A negative voltage indicates the motor active in the forward direction.

Channel 2 (Blue) shows motor3. A positive voltage indicates the motor active in the reverse direction.

 

Analysis of results:

Everything behaved as expected.

 

Test 1-D: Test servo motor signals

This test will exercise the servo motor outputs SV1 - SV8 using calls in the robotics.py code to set them at 0, 90 or 180 degrees:

 

theBoard.servoWrite(theBoard, 1, 180)
theBoard.servoWrite(theBoard, 2, 90)
theBoard.servoWrite(theBoard, 3, 0)
theBoard.servoWrite(theBoard, 4, 180)
theBoard.servoWrite(theBoard, 5, 90)
theBoard.servoWrite(theBoard, 6, 0)
theBoard.servoWrite(theBoard, 7, 180)
theBoard.servoWrite(theBoard, 8, 90)

 

What I'm looking for and why:

  • 180° gives a PWM signal that is high for 2.5ms each period.
  • 90° gives a PWM signal that is high for 1.5ms each period.
  • 0° gives a PWM signal that is high for 0.5ms each period. These are the common values for the full range of motion in an actuator servo. Some servos have less range in motion and/or use a shorter range of pulses.
  • PWM rate of around 50Hz.
    This is the commonly used value for servo motors.

 

What I observed:

  • the 180° servos are 2ms instead of 2.5ms
  • The 90° servos are 1ms instead of 1.5ms
  • The 0° servos have no pulse instead of 0.5ms
  • The PWM period is 21ms or 46.7Hz instead 50Hz (close enough)

image

Screen capture from Servo Motor test.

 

Analysis of results:

All the pulses are about 0.5ms short.

 

The code in robotics.py to calculate the PWM value is:

PWMVal = (degrees * 100 * self.SERVO_MULTIPLIER) / (10000 + self.SERVO_ZERO_OFFSET)

if degrees = 0, PWMVal will be 0 which explains why we had no pulse. SERVO_ZERO_OFFSET should be added after the division.

PWMVal = ((degrees * 100 * self.SERVO_MULTIPLIER) / 10000) + self.SERVO_ZERO_OFFSET

Changing the code will also fix the 90° and 180° signals.

image

Screen capture from Servo Motor test after changing the code.

All the pulses and the PWM period are now pretty close (about 5% long).

 

Why are we about 5% off?

The PCA9685 PWM controller chip  has an internal 25MHZ oscillator. The datasheet doesn't specify a tolerance for the clock and just says: '25 MHz typical internal oscillator requires no external components'. For LED applications, the accuracy probably isn't important as long as it's fast.  This is a possible source of the difference in timing

The PWM rate is set by setting a prescale register in the PCA9685. Here is the code from robotics.py:

 

buf[0] = self.PRESCALE_REG
buf[1] = 0x85 #50Hz
i2c.write(self.chipAddress, buf, False)

 

The equation from the datasheet for calculating the prescale value is: prescale_value = round(osc_clock/(4096*update_rate))-1

or, we can rearrange it to:
update_rate = osc_clock/(4096*(prescale_value + 1))

If osc_clock = 25MHz and the prescale_value = 0x85, the update rate will be 45.55Hz instead of 50Hz.

The writer was probably adjusting the prescale to make the refresh rate 50Hz on their board. I'll leave it alone for now unless it causes a problem.

 

Now that we have tested and fixed any problems with the motor signals and demo software, we can move on to testing using the micro:bit I/O.

 

Phase 2: Test the ability to use the micro:bit I/O

The only micro:bit  I/O pin used by the All-in-one Robotics board is I2C so all of the micro:bit's I/O is available except for the I2C address used by the robotics board.

 

To test this, I will need to:

  1. Solder header pins to the robotics board.
  2. Breadboard an I2C device (TMP102 temperature sensor) and a NeoPixel strip
  3. Test them with and without the robotics board.
  4. Add the code to to the robotics.py code and test it with the robotics board.

 

Soldering header pins to the robotics board was straightforward.

However, the webpage for the board says:

If using the breakout pads at the back of the board, you will also need Soldering iron, solder and wire cutters.

It should also say that you need header pins.

image

Robotics board with micro:bit I/O headers added

 

I wrote a program to read and display the temperature while scrolling the LEDs on the NeoPixel strip.  I tested the breadboard with an AdaFruit Dragontail breakout board with no problems.

 

I then added the robotics.py code to test it with the robotics board.  My RoadTest then ran into a couple of speed bumps.

 

The combined code was too large to load at startup and the micro:bit failed with a memory error at startup.  I tried keeping the KitronikRoboticsBoard class in a separate file but it still failed.  I'm not using stepper motors so I removed the stepper motor calls for now.  I'll talk more about code space later when making a robot.

 

After removing the stepper code, the code loaded with no problems but would usually fail after a few seconds with an I2C write error.  This was unexpected since it ran with no problems without the robotics board!

 

I put a scope on the I2C line and saw that it was over 1/3 V when a 0 was on the bus.  The micro:bit can only source or sink a small amount of current.  The micro:bit has 4.7KΩ pull-ups on the on the I2C line, the robotics board appears to have 10KΩ pull-ups and the TMP102Ω board has 1KΩ pull-ups.  These three in parallel are about 760Ω which is probably too much current for the micro:bit pin.  The micro:bit can have three pins configured for high current (5ma) which I assume are used for the led array.

 

I changed my code to use an MCP9808 temperature sensor board that has 10KΩ pull-ups and everything worked fine.  A 0 on the I2C line measured 0V on the scope.

The final test:

Display a happy face on the LED array

Turns off the servos

Turns on three servos to 180º, 90º, 0º

Reads temperature over I2C

Displays temperature on LED array

Scrolls a NeoPixel on the strip

I'm only lighting one NeoPixel at a time to keep the current under the 90ma spec of the robotics board 3V pin.

image

Picture of the I/O test with servo outputs, I2C temperature sensor and a NeoPixel strip.

 

Here's a movie of the test in action.

 

We've got motor control and can use other devices so we're ready to build a robot!

 

Test 3: Build and test a robot

For my robot, I'm going to use a Makeblock Starter Robot I bought at the flea market several months ago for $5.  It was missing a bunch of pieces but all I need it the chassis, motors and battery pack.

 

image

Testing the robot motors with the robotics board.

 

My robot will have the following:

  • 2 DC motors driven by the robotics board
  • A US-100 ultrasonic distance sensor read with micro:bit's serial port
  • An 8 LED neopixel strip used to indicate the distance to the nearest obstacle.

 

image

 

image

My finished robot

 

My tale of woe with memory issues

My first pass at the code that loaded the support for the robotics board, US-100 and NeoPixels but didn't do much with them got memory errors when loading.

Failing this early usually means there wasn't enough space to load the .py file and convert it to byte codes.

I then simplified the NeoPixel animation and it loaded but got a memory error when instantiating one of the classes.

I then gave up on the NeoPixels, which probably allocate all sorts of buffers, and removed the code using them.

The code successfully loaded and drove the robot in a straight line but wasn't finished.

I added some code to turn the robot when an obstacle is detected but ran out of memory again.

I removed the unneeded servo code from the Kitronik class and was up, running and avoiding obstacles.

 

Hooray, but I was disappointed to lose my blinking lights!

 

I dug into the Kitronik code and the PCA9685 data sheet and was able to save space by combining routines and using features in the chip like auto increment and writing to all PWMs with one write.   This saved enough space to use the NeoPixels.  Aside from having a couple of bugs (which it shouldn't), the Demo code did it's job: showing me an easy to follow example of how to talk to the board.

 

My robot doing a victory lap

 

Conclusions

I like the board and would recommend it to my students.  With one board you can control three types of motors for a reasonable price and it provides a nice low cost solution for STEM and art projects.  Memory is limited for MicroPython but I'm still amazed the we can run MicroPython at all.

 

Suggested improvements:

  • Add a schematic and theory of operation to the documentation.
  • Fix the bugs in the Demo code
  • Make it easy to add a power switch.  A place to add a couple header pins with a cuttable trace shorting them in series with the power would be convenient.
Anonymous