The Adeept Starry:Bit BBC Micro:Bit Robot With Arduino IDE
by kd8bxp in Circuits > Arduino
1637 Views, 2 Favorites, 0 Comments
The Adeept Starry:Bit BBC Micro:Bit Robot With Arduino IDE
I received this robot to attempt to port the python code to the Arduino IDE, For the most part this robot is very easy to use, and the code was easy to port. The robot is well constructed, has metal gear motors, 40 WS2812 LEDs, photo-resistors for light sensing, IR sensor for obstacle detection, an expansion header, and line follow sensors. It uses a 14500 3.7v LiPo battery, this battery is the same size as a AA battery (But a AA battery will not work - it is only 1.5v).
The BBC Micro:Bit has a 5x5 LED matrix for display, bluetooth, compass. Typically you program these with MakeCode by Microsoft, or python. But with this instructable I'll show a way to install the BBC Micro:bit into the Arduino with the use of the nRF5 board core by Sandeepmistry (more on that later).
The robot can be found here: http://www.adeept.com/starrybit_p0116.html
My ported code can be found here: Arduino Code repository: https://github.com/kd8bxp/Adeept-StarryBit-Arduino-Code
For the most part I've been able to port most of the python code. There are some limitations with the Arduino IDE that I didn't foresee. For one, the neopixel library has a limitation on which pins can be used with the Micro:bit - unfortunately one of the pins that can't be used is the pin that the WS2812 LEDs are connected to. (I have something of a work around, but you do it at your own risk).
I did base the bluetooth control code on Adafruits Bluefruit app, and example. Other than that I believe I've been able to get the rest of this robot working without too many problems.
1st thing we need to do is install the board core into the Arduino.
Install the BBC Micro:Bit Core Into the Arduino IDE
I will be referring to Adafruits Tutorial for this step. https://learn.adafruit.com/use-micro-bit-with-ardu...
The video above shows additional steps needed if you are a Linux users, for Windows 10 users the install is straight forward, and didn't cause me any problems. * I don't have a Windows 7 system, so I don't know about the additional step in the tutorial. Nor do I own a Mac or OSX device, so hopefully you'll not have problems - but remember google is your friend if you do.
* FOR LINUX users this addition sites were very helpful: udev rules were found here: https://github.com/RedBearLab/nRF51822-Arduino/is...
And addition libraries which are needed were found on the arduino-nRF5 board core project page: https://github.com/RedBearLab/nRF51822-Arduino/is...
Also for Linux users your USER must be part of the dial up group (or the "Connect to Internet using a modem" group) or maybe (depending on your distro the Use modems group) - Generally this setting is found under Administration and "Users and Groups"
Steps in common with all OSes:
- In Arduino, go to Preferences and add 'https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json' into the Additional Board Manager URL Text box.
- Open Tools>Board>Boards Manager from the menu bar, search for nRF5 and install "Nordic Semiconductor nRF5 Boards" by Sandeep Mistry - This step takes a while, and installs a number of things needed, just be patient.
- Once done, look for the BBC Micro:Bit in the Tools>Board List of boards. And select it.
- Next you'll want to open Tools and look for Softdevice change that to S110.
- Last plug the BBC Micro:bit into your computer, if this is the first time, (Windows will install device drivers) (Linux it should just open a folder up), either way, you'll need to look at Tools and select the right serial Port for your board.
At this point, Linux users will likely need to follow the above link to install additional libraries, and probably need these udev rules settings:
bapowell wrote on Sep 4, 2016After ensuring that my userid was a member of the plugdev group, I created a new udev rules file,
/etc/udev/rules.d/98-blenano.rules, with these rules; then reloaded with
$ sudo udevadm control --reload-rules, plugged in the MK20 device, and it worked.
# mbed CMSIS-DAPATTRS{idVendor}=="0d28", ATTRS{idProduct}=="0204", MODE="664", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0d28", ATTRS{idProduct}=="0204", MODE="664", GROUP="plugdev"
Now you should be able to upload the sample code from the Adafruit tutorial, and you should see a LED on the matrix blinking.
const int COL1 = 3; // Column #1 control
const int LED = 26; // 'row 1' led void setup() { Serial.begin(9600); Serial.println("microbit is ready!"); // because the LEDs are multiplexed, we must ground the opposite side of the LED pinMode(COL1, OUTPUT); digitalWrite(COL1, LOW); pinMode(LED, OUTPUT); } void loop(){ Serial.println("blink!"); digitalWrite(LED, HIGH); delay(500); digitalWrite(LED, LOW); delay(500); }
Some Install Notes at this point:
IF this still doesn't work you may need a softdevice installed on your Micro:bit, Adafruit has a simple one here:
https://learn.adafruit.com/use-micro-bit-with-ardu...
(I have found that anytime you change the softdevice for the Micro:Bit it fails the first time, you have to then put a good .hex file on the device, and re-upload your sketch, it will then work - this is an annoyance. But it is how I've over come some issues with uploading. It should also be noted that the Adafruit library that will be installed really only works with softdevice 110 or 130, but use 110).
** For this robot I'm not using the Adafruit library (expect for the bonus examples).
Once you have a working "Blinky" - we need to install some libraries that will make life so much easier.
The Magnetometer and Accelerometer library can be found in the Adafruit Tutorial, these libraries need to be installed manually - Adafruit has a great tutorial if you don't know how to do that. (Library is really a Sparkfun Library - How cool is that) https://learn.adafruit.com/use-micro-bit-with-ard...
The other libraries can be found in the library manager: https://learn.adafruit.com/use-micro-bit-with-ard...
Sketch->Include Library->Library Manager 1. "BLE Peripheral Library" by Sandeep Mistry 2. "Adafruit GFX Library" by Adafruit 3. "Adafruit Microbit" by Adafruit
Starry:Bit Project 2 Motor
Grab my Arduino code from here: https://github.com/kd8bxp/Adeept-StarryBit-Arduino...
I'm going to go over the examples that have been ported from python. Since Project 1 is all about the WS2812 LEDs, and since there is a limitation with the Arduino library, we are skipping ahead to Project 2 - Movement/Motors.
For this lets look at the python code from Project 2 Lesson 1: (The important part is below)
<p># define Drive<br>def Drive(lft, rgt): pin14.write_digital(0) pin13.write_digital(0) if lft < 0: pin14.write_digital(1) lft = 1023 + lft if rgt < 0: pin13.write_digital(1) rgt = 1023 + rgt pin16.write_analog(lft) pin15.write_analog(rgt)</p>
Here we have a function called "drive", we can see the motors are hooked up to pins P14, P13, P16 & P15 (15 and 16 are the speed controls, how do we know - because of the write_analog). P14 & P13 are the direction pins. They are using only 2 wires per motor, so four wire total - this means we need to do use inverse logic in our Arduino project. Looking at the above code, we can see that pin P14 and P16 are for the left motor (lft), and P13 and P15 are for the right motor (rgt). The above code is the basis for the rest of the code in the series, all the python code from this point on uses the same drive function.
1023 is the fastest speed (or in Arduino terms that would be 255) - but we need to change the code a little to make this work with the Arduino. So lets look at the Arduino code:
<p>#define left1 14<br>#define left2 16 //pwm #define right1 13 #define right2 15 //pwm</p><p>void setup() { pinMode(left1, OUTPUT); pinMode(left2, OUTPUT); pinMode(right1, OUTPUT); pinMode(right2, OUTPUT); stop(); //make sure motors are stopped</p><p>}</p>
First we are going to define our motor pins, and set them for output, Just for the sake of "safety" we are also going to set the motors to stop. stop will be our first function. The Arduino code is broken into three functions, but they are simple, and will be the basis for all the rest of the code. We will have a ccw, cw and stop function.
ccw (or counter clock wise) will move the robot in the forward direction.
cw (clock wise) will move the robot in the backward direction. And of course stop will stop the robot movement.
<p>void cw(int lft, int rgt) { <br> //This causes the car to move backward digitalWrite(left1, 1); digitalWrite(right1, 1); analogWrite(left2, map(lft,50,255,50,255)); analogWrite(right2, map(rgt,50,255,50,255)); }</p><p>void ccw(int lft, int rgt) { //This causes the car to move forward digitalWrite(left1, 0); digitalWrite(right1, 0); analogWrite(left2, lft); analogWrite(right2, rgt); }</p><p>void stop() { digitalWrite(left1, 0); digitalWrite(right1, 0); analogWrite(left2, 0); analogWrite(right2, 0); }</p>
When we call these functions we put our needed speed in PWM, so it would be something like
ccw(255,255); //move car forward with PWM for left and right wheel.
The ccw function is pretty straight forward, it sets the directions pins to a low, and cause the PWM pins to go higher, causing the car to move forward.
The stop function is also straight forward, setting all the pins to a low (zero), this causes the car to stop.
The cw function has a little more happening, first we set the direction pins again, this time to a high. But we can't also have a high on the PWM the car wouldn't move, or wouldn't move correctly.
So we are going to use the Arduino map command to change the PWM we send the car to be the opposite.
map(x,50,255,50,255); this would take some number between 50 and 255 and change it to 255 to 50.
So if we send it cw(255,255) we want the car to move at it's fast speed backward. But 255 is also the same as a high, so we turn that into a 50 (50 is the lowest number that will still let the car move). That is what the map function does. It is kind of like saying replace 255 with the number 50.
So if we use cw(255,255) in effect we are saying:
<p>void cw(int lft, int rgt) { //at this point lft = 255, rgt = 255<br> //This causes the car to move backward digitalWrite(left1, 1); digitalWrite(right1, 1);</p><p>int x1= map(lft,50,255,50,255); // moved outside of analogWrite and is the same as map(255,50,255,50,255) x1=50 int x2 = map(rgt,50,255,50,255); //same for the right motor, x2 will end up being 50 (x2=50)</p><p> analogWrite(left2, x1); //x1= 50 analogWrite(right2, x2); //x2 = 50 }</p>
Let's look at how this works in python: (the while TRUE: is the same as the void loop() in Arduino).
<p>while True:<br> Drive(500, 500) LightAll(green) sleep(4000) LightAll(red) Drive(-500, -500) sleep(4000) Drive(0, 0) npix.clear() sleep(100000000)</p>
For the python code, they are using negative numbers to cause the car to go backward, and only have the drive function. (There is also code here for the WS2812 leds). But they are having the car go forward for about 4 seconds, then go backward for 4 seconds, and wait for a while, and start over again. I believe 500,500 is the fast speed set for both motors (as PWM). Let's look at the Arduino void loop()
<p>void loop() {<br> //Arduino PWM is from 0 to 255 ccw(255,255); //Move counter clockwise (forward) PWM for each wheel Left, Right delay(4000); //wait 4 seconds stop(); //stop the car cw(255,255); //Move clockwise (backward) PWM for each wheel left, right delay(4000); //wait 4 seconds stop(); //stop the car delay(10000); //wait for 10 seconds start over</p><p>}</p>
Again, ccw causes the car to go forward, and cw causes it to go backward. But we are doing the same thing with just a little bit different function call. And 255 would be set for the fastest speed (PWM) of both motors. We go forward for about 4 seconds, You might notice that I also call a stop before changing directions, (I've just gotten into the habit of doing this before changing directions, I don't think it would be required). We next move backward for about 4 seconds, stop, and wait for about 10 seconds, and start the whole thing over again. So really the loops of both the python and Arduino code are pretty similar.
So, now that we have a basis for moving the motor, you might ask how to make it turn - For that we need to look at the "motor" sketch from Project 2. I'm only going to show the Arduino code, but the python code is included in the github repository if you'd like to look it over.
(remember our base functions will remain the same for the rest of these sketches, so I'm going to look at the void loop for the "motor" sketch)
<p>void loop() {<br> //Arduino PWM is from 0 to 255 //ccw (counter clockwise) will move the car forward //in the python examples this is positive numbers. //cw (clockwise) will move the car backward //in the python examples this is the negitive numbers. //PWM for left, right motor ccw(255,255); delay(2000); cw(255,255); delay(2000); ccw(50,255); //turn wide left forward delay(2000); cw(255,50); //trun wide right backward delay(2000); ccw(255,50); //turn wide right forward delay(2000); cw(50,255); //turn wide left backward delay(2000); ccw(255,0); //turn tight right forward delay(500); ccw(0,255); delay(500); stop(); delay(10000); //wait 10 seconds then start over</p><p>}</p>
As said before when calling either the ccw or cw function, you include the speed you want for each wheel.
such as ccw(left PWM, right PWM); setting the PWM to the same for both wheels, should make the car go straight-ish - in reality it will take a little playing with your robot each motor, wheel, and surface that it's on will change how well it can maintain a straight line.
So we can turn the car left and right, by setting the speed of each wheel to a different rate. To turn left, we set the left motor to a slow speed (or no speed which will leave it turned off), and the right motor to a faster speed. To turn to the right just the opposite is done, the left motor needs to be faster than the right motor.
Varying the length of time the motors are on will cause the turn to short or fast, with Arduino we do this with the delay. There are no encoders on the wheels, so these turns will vary based on the surface that the car is on.
Starry:Bit Project 3 Avoid Obstacle
Let's look at the python code, and see if we can figure out what is going on.
def way():
# define IR sensor IR = pin0.read_analog() if IR > 250: # Turn on LightAll(green) Drive(500, 500) else: LightAll(red) Drive(-300, 50) sleep(500) print(IR)while True: way()
The function they use is called way(), This function takes an analog reading from the IR pin (P0), it then decides if something is close and if so turn the Starry:bit away from it. (There is also code here for the WS2812 LEDs).
The while True: function is the same as the Arduino void loop, and it does just one thing, calls the way() function over and over and over again.
All this seems pretty straight forward, so let's look at the Arduino code:
We are going to add the pin number of the IR sensor to our previous define list.
#define ir 0 //IR obstacle avoidance sensor
void setup() {
pinMode(left1, OUTPUT); pinMode(left2, OUTPUT); pinMode(right1, OUTPUT); pinMode(right2, OUTPUT); pinMode(ir, INPUT); stop();}
And in void setup(), we are going to add a INPUT for the IR sensor.
I've changed the name of "way()" to "avoid()" - seemed to make a little more since to me because we are trying to avoid something. Let's look at the avoid() function, and the void loop.
void loop() {
//Arduino PWM is from 0 to 255 //ccw (counter clockwise) will move the car forward //in the python examples this is positive numbers. //cw (clockwise) will move the car backward //in the python examples this is the negitive numbers. //PWM for left, right motor avoid();}
void avoid() {
int IR = analogRead(ir); if (IR > 250) { ccw(128,128); } else { cw(75,0); delay(200); } }
In void loop, we are just calling the "avoid()" function over and over again, and the avoid() function looks a lot like the python code. Remember, our drive functions are the same for all the sketches, so we telling the car to go forward if nothing is in it's way, at about half speed, if something gets in the way, the car will turn to the right and try to avoid the obstacle.
It's a pretty simple conversion here.
I've added a bonus "Avoid Obstacle" sketch in the Extras examples, the bonus extras mostly require the Adafruit Micro:bit Library, many of them use the 5x5 LED matrix.
The bonus sketch adds a happy face when the robot is moving forward, and a sad face when it's turning and backing.
Starry:Bit Project 4 Follow
The "Follow" code does more or less the opposite of the "avoid" code.
*** I don't believe the original code works as it should. We'll go over it ***
Let's look at the python code, then the Arduino, then we will look at the "bonus" code, which I believe works the way this is intended to work.
I'll say this up front - the python code, the make code hex file, and the Arduino code all work the same, and all do the same thing. (The corrected "bonus" code, works slightly different.)
The other thing about this for it to be a true "follow" you would need at least two additional sensors on the front of the robot, one for left and one for right.
The Python code:
def Flow():
# define IR sensor IR = pin0.read_analog() if IR > 150 and IR < 600: Drive(300, 300) LightAll(green) if IR < 150: Drive(-800, -800) LightAll(red) if IR > 600: Drive(0, 0) LightAll(yellow)
We have a different function now, and we are going to take analog reading again, this time we are looking for values between 150 and 600, the robot should move forward. Less than 150 it will move backward, and greater than 600 it should stop. (Stop never happens BTW).
There is also some code in here for the WS2812 LEDs. It seems pretty simple and straight forward.
The while True loop, just calls this function over and over, with a small delay added.
And in the Arduino code we see:
void follow() {
int IR = analogRead(ir); if (IR > 150 && IR < 600) { ccw(125,125); } if (IR < 150) { cw(200,200); } if (IR > 600) { stop(); } }
Once again, the code for the Arduino is pretty close to the python code, if our reading is between 150 and 600 we move forward at about half speed. If the reading is less than 150, we move backward (close to full speed). And if we are greater than 600, we stop - (We never stop).....
What this seems to do is move the robot to what ever is in front of it, until it gets "too" close, it then backs it off, but once it gets "too" far, it move right back, causing it too go forward and backwards way too much. This happens with the python code, and make code. And I duplicated it with the Arduino code.
In the Extra folder, you'll see a follow_2 sketch, this sketch made a couple of small (but important) changes,
with the changes the robot will move to an object, once it is too close it will back off, but once it gets too far away it will stop and wait for something to get close to it and move toward that new object. I think this is closer to what "we" were trying to do.
Here is the code that changed:
void follow() {
int IR = analogRead(ir); if (IR > 150 && IR < 600) { microbit.show(smile_bmp); ccw(125,125); //delay(75); } else if (IR < 150) { microbit.show(sad); cw(200,200); //delay(75); } else { //microbit.clear(); stop(); } }
We changed the IF statements to ELSE IF, we removed the greater than 600 condition turning that into a else and a stop. With those small changes I believe we have something that works a bit better.
We also added code for the LED matrix, so you need the Adafruit Micro:bit library to use this sketch.
Starry:Bit Project 5 Follow Light
The Follow Light examples, use the two photoresistors that are on the board. A photoresistor can change it's resistance value bases on the amount of/or lack of light hitting it. The resistance of a photoresistor decreases with increasing incident light intensity. On the Starry:Bit we have two photoresistors one on the left side, and one on the right side, They setup like a voltage divider. And connected to Pin P2.
A voltage divider is a circuit of resistors or capacitors tht can be tapped at any intermediate point ot produce a specific fraction of the voltage applied between its ends.
We use analog read to get the value, and a little compassion to figure out which photoresistor has more light.
Let's look at the python code:
def Findlight():
LightAll(green) Pho = pin2.read_analog() if Pho < 400: # left Drive(100, 800) elif Pho > 750: # right Drive(800, 100) else: # Go on Drive(400, 400) sleep(20)
In this example, a reading of less than 400 will drive the car to the left, and anything over 750 will drive the car to the right. Everything else will drive the car forward (In this example it goes forward a lot).
You'll notice there is no stopping here, it is always moving. The while True: loop just calls this function over and over again.
* There is also code here for the WS2812 LEDs.
Let's look at the Arduino code: *Note: I changed the behavior of the robot a little, this time, it stops....
void findLight() {
int readPhoto = analogRead(photo); if (readPhoto < 400) { //left ccw(50,150); } else if (readPhoto > 750) { //right ccw(150,50); } else { //ccw(125,125); stop(); } }
I also setup a #define photo 2 //this is the pin number of the photoresistors, and set the pinMode to input.
The readings are generally the same, a reading of less than 400 will turn the car left, and greater than 750 will turn it right. The difference in my code is the "else" condition, if there is no light it stops.
If you want the Arduino code to act more like the python code, comment out the stop(); and uncomment the ccw(125,125); and you'll have code that works much the same as the python code.
** Since this robot was meant to be educational, I've decided that a challenge around this function might be interesting. Educational challenge: Using either the above Arduino code, or Python code, can you make the car reject light, and move away from it? Challenge two - can you add to the Arduino code to make it move forward when light is seen by both sensors, but still stop when there is no light applied. (You may need to do an ambient, background reading for this challenge.) **
Starry:Bit Project 7 Mobile Remote Control Using Bluetooth
Ok, here I go off script a little, Adeept provided a hex file for bluetooth control, that apparently uses features of the Micro:bit app for IOS devices (in otherword, for Apple phones and iPads), They didn't provide a way to do this with python. And the Micro:bit app for Android appears to lack a lot of features.
Adafruit to the rescue, they provide a couple of bluetooth examples, and an Android app called Bluefruit LE Connect https://play.google.com/store/apps/details?id=com....
First you will need the Adafruit Micro:bit library installed, and the BLE Peripheral library
https://learn.adafruit.com/use-micro-bit-with-ardu...
You should have done this already from the first step of this instructable.
I can't stress this enough, you need softdevice 110 for this to work. Install the Arduino sketch, and open the bluefruit app, follow the rest of Adafruit's tutorial here, but you should have a Starry:bit that you can control with the app.
https://learn.adafruit.com/use-micro-bit-with-ardu...
The Arduino sketch is a modified version of the ble_controller sketch included with the Micro:bit Library
It sets the bluetooth into UART mode, and then listens for events. A lot happens in the void loop() of this sketch, and in the packetParser.cpp file, I encourage you to modified the ble_controller sketch, and play around, I learned quite abit after I did.
Open the app, and find your Micro:bit in the list of Bluetooth devices (you don't need to pair it first, in fact it is better if you don't pair it). Once connected, You should see a list of "services", click on the "controller" and then "control pad", Now you should see four arrows, and a number pad. Again, I keep this simple, I am only using the arrows. The interesting thing about this app is it sends when the button is pushed, and when it is released.
So for the sketch, each time the button is released, we stop, each time it is pushed we move. Nice and simple.
The other buttons are 1, 2, 3, 4 and that is the number you would use in the sketches if you wanted to expand on this example.
Extras and Bonus Sketches
You'll find a directory called extra which contains some examples of code idea that didn't come from Adeept. Most of these extra sketches need the Adafruit Micro:bit library. We have looked at a couple of the bonus sketches already.
So I'll just hit a few of the high lights, The Adafruit Matrix Demo shows what you can do with the 5x5 matrix.
The Bitmap_demo, has some of the "images" that Makecode have built into it, some not all, things like
smile, small heart, sad, confused, up down left and right arrow, ghost and skull.
The demo just shows them off, to use them copy the images.h file into your sketch directory, and include it:
#include <Adafruit_Microbit.h>
#include "images.h"Adafruit_Microbit_Matrix microbit;
in void setup start the microbit led with
void setup() { microbit.begin(); }
and when you want to display an image use:
microbit.show(imagename); or for the built in Adafruit examples microbit.show(microbit.HEART);
The adafruit library has HEART, EMPTYHEART, NO and YES built into it.
Buttondemo uses the serial console and the A and B button the micro:bit it just tell if a button has been pushed.
It shows a good way to use these, and doesn't require the Adafruit Micro:bit library.
Blink_Led Sketch, is another demo that uses the LED matrix, but doesn't require the Adafruit Micro:bit Library,
The matrix looks like a 5x5 matrix, but is wired very differently, it takes two pins to control each LED, and the pins are shared across the matrix. It looks something like this:
COL0 COL1 COL2 COL3 COL4
ROW 0: (3,26) (23,27) (4, 26) (24,27) (10,26) ROW 1: (23,28) (24,28) (25,28) (9,28) (7,28) ROW 2: (4, 27) (6,26) (10,27) (6,28) (3,27) ROW 3: (7,26) (9,26) (25,26) (24,26) (23,26) ROW 4: (10,28) (9,27) (3, 28) (25,27) (4,28)
The first number is set low, and the second number high to make a led light up. For doing simple things you don't need the library, but the library does make it easy to do images, scroll text and make simple animations.
Understanding how the LED matrix works is interesting thou, so feel free to play around with that blink_led sketch and see what happens.