Porting Bit:Bot Code from MicroPython to C++

I will port all of the code created in tutorials 1 – 5 from MicroPython to C++ so we can use it for the tutorials that use Arduino IDE.

PLEASE NOTE; You will need to have the Adafruit Neopixel and newPing libraries installed for this code to compile download links for these libraries can be found at the top of the code as a comment. For instructions on how to install Arduino libraries go to the Neopixel download link and follow the instructions their or follow my guide here that shows you how to install the newPing library. Even if you don’t plan on using the Neopixels or Ultrasonic sensor still import the library and keep the code incase you add these in the future. What’s provided here is a good code base for all Bitbot projects.

This will give us access to the NeoPixels, Motors, Light Sensor and IR Line Detectors when using the Arduino IDE. I will also add methods to use the Ultrasonic sensor by importing the newPing library. If you don’t have he ultrasonic sensor you can either delete this part of the code or install the newPing library as described in the previous tutorial. The Neopixels will on work if you carry out the Neopixel modification shown here. The code is starting to look more complex now and because of this I will arange the code into different tabs. The core Bitbot code will go into a tab named Bitbot_Lib_PORTED and the program code will go into a tabe named _00_programLoop.

Open Arduino IDE and create a new tab and name it BotBot_Lib_PORTED. The tab button is at the top right of the Arduino IDE window under the search icon. It looks like a downwards pointing arrow. Select the new tab and paste the below code into it.

/*
 * Bit:Bot code ported from MicroPython to C++. This code provides basic functions
 * that were worked through from tutorials 1 - 5 from www.l33t.uk/bitbot.
 * Author David Bradshaw 2018
 * 
 * Version 1.2 adds support for Neopixel Library on pin 3 for modified BitBots
 * To see how to modify the Bitbot goto https://www.l33t.uk/bitbot/?page_id=656
 * Dependcies NewPing Library; https://playground.arduino.cc/Code/NewPing
 *            Adafruit NeoPixel Library 1.1.6; https://learn.adafruit.com/adafruit-neopixel-uberguide/arduino-library-installation
 * 
 * If you have a unmodified Bitbot you can delete the neoPixel code however it might be easier to keep it incase you modify your
 * Bitbot at somepoint and later examples will use the neoPixel code so it would be easier to leave it.
 */

//Ultrasonic stuff
#include <NewPing.h> //Imports library to be used with HC-SR04 sensor
NewPing sonar(15, 15, 400); //Create a newPing object with a range of 400cm

//NeoPixel stuff
#include <Adafruit_NeoPixel.h>
#define PIN 3  //Pin number thats connected to the Neopiexels
#define LEDNUM 12  //number of Neopixels
Adafruit_NeoPixel neoPixels = Adafruit_NeoPixel(LEDNUM, PIN, NEO_GRB + NEO_KHZ800);


//Motor pin assignments
#define leftSpeed 0
#define leftDirection 8
#define rightSpeed 1
#define rightDirection 12

//IR assignments
#define leftLineSensor 11
#define rightLineSensor 5

//Light Sensor assignments
#define lightSensor 2
#define sensorSelect 16

/*
 * THE BELOW ARE HELPER METHODS THAT WILL MAKE BIT:BOT DO THINGS
 * SUCH AS MAKING THE MOTORS ROTATE AND TAKING READINGS FROM THE 
 * SENSORS.
 */

/*
 * NeoPixel helper methods
 * It takes 650uS to light up each neoPixel when lighting a strip of 6 neopixels.
 * This means that it will take 4.3mS to light up each side of the Bitbot.
 * With this in mind we will endevour to write to the NeoPixels as little as possible
 * A good update rate would be 1hz i.e. once per second or on colour change.
 * The neopixles brightness will be adjusted if the room is pitch black the nP's
 * will be white in colour regardless of the values you give to them.
 */
void setColour(uint8_t pixelIndex, float bF, uint8_t red, uint8_t green, uint8_t blue) //650uS Execution time
{
    neoPixels.setPixelColor(pixelIndex, neoPixels.Color(applyBF(red, bF), applyBF(green, bF), applyBF(blue, bF)));
    neoPixels.show();
}
void setColourLeft(uint8_t red, uint8_t green, uint8_t blue) //4300uS execution time
{
    float bF = getBrightnessFactor();
    for (uint8_t ii = 0; ii < 6; ii++)
    {
        setColour(ii, bF, red, green, blue);
    }
}
void setColourRight(uint8_t red, uint8_t green, uint8_t blue) //4300uS execution time
{
    float bF = getBrightnessFactor();
    for (uint8_t ii = 6; ii < 12; ii++)
    {
        setColour(ii, bF, red, green, blue);
    }
}
float getBrightnessFactor() //300uS execution time
{
    //Returns a value depending on how bright the room is so we can set the brightness
    //of the neopixels. Bright in bright rooms and dim in dark rooms
    uint32_t brightness = detectLight("LEFT");
    brightness = (brightness + detectLight("RIGHT")) / 2;  //Average brightness for left and right sensor
    float rtnValue = float(brightness) / 1000;
    if(rtnValue > 1){rtnValue = 1;}
    return rtnValue;
}
uint8_t applyBF(uint8_t colour, float brightnessFactor) //Around 12uS execution time
{
    //Changes the colour value so the Neopixels will automatically adjust their brightness
    //If a value is less than 1 it will be rounded up to a 1 in getBrightnessFactor() to 
    //stop the LEDs from switching off. This means that in very dark conditions the LEDS will
    //either turn white or a paler colour.
    colour = colour * brightnessFactor;
    if (colour < 1){colour = 1;}
    return colour;
}

/*
 * Returns the distance between Bit:Bot and an object range == 400CM
 */
int getDistance() //Takes upto 30mS to execute, upto 50mS with the delay
{
    delay(25); //settle time ensures more accurate readings                
    unsigned int uS = sonar.ping();
    int rtnVal = int(uS / US_ROUNDTRIP_CM);
    if(rtnVal == 0){rtnVal = 450;} //Out of range or somethings gone wrong
    return(rtnVal); 
}

/*
 * IR sensor function for line following
 */
boolean detectLine(uint8_t side)
{
    uint8_t isLine;
    if(side == 0)
    {
    isLine = digitalRead(leftLineSensor);
    }
    else
    {
        isLine = digitalRead(rightLineSensor);
    }
    
    if (isLine == 1)
    {
        return true;
    }
    else
    {
        return false;
    }
}

/*
 * Light sensor
 */
uint32_t detectLight(String side)
{
    if(side.equals("LEFT"))
    {
        digitalWrite(sensorSelect, LOW);
    }
    else
    {
         digitalWrite(sensorSelect, HIGH);
    }
    return uint32_t(analogRead(lightSensor));
}

/*
 * Motor Functions
 * Due to the arduino implementation we have lost precision for the motor
 * argument speed. the values it can take is between 0 - 255. In the microPython
 * version the largest value was 1023.
 */
void move(uint8_t _leftSpeed, uint8_t _rightSpeed, uint8_t _leftDirection, uint8_t _rightDirection)
{
    analogWrite(leftSpeed, _leftSpeed);
    analogWrite(rightSpeed, _rightSpeed);
    digitalWrite(leftDirection, _leftDirection);
    digitalWrite(rightDirection, _rightDirection);
}
void forwards(uint8_t speed)
{
    move(speed, speed, 0, 0);
}
void backwards(uint8_t speed)
{
    speed = 254 - speed;
    move(speed, speed, 1, 1);
}
void left(uint8_t speed)
{
    move(254 + -speed, speed, 1, 0);
}
void right(uint8_t speed)
{
    move(speed, 254 + -speed, 0, 1);
}

No rename your original tab by selecting it, clicking the tabe button and selecting rename. Rename _00_programLoop.

Now copy and paste the below code into the program loop tab.

void setup() {
    // put your setup code here, to run once:
    pinMode(leftSpeed, OUTPUT); //Set pin modes, this is not needed for HC-SR04 pins
    pinMode(leftDirection, OUTPUT);
    pinMode(rightSpeed, OUTPUT);
    pinMode(rightDirection, OUTPUT);
      
    pinMode(leftLineSensor, INPUT); 
    pinMode(rightLineSensor, INPUT);

    pinMode(lightSensor, INPUT);
    pinMode(sensorSelect, OUTPUT);
  
    Serial.begin(115200); //Setup the serial port so we can get debug data

    pinMode(PIN, OUTPUT); //Set the pinmode for the neopixels
    neoPixels.begin(); //initilise the neoPixel object
    neoPixels.show(); 
}

/*
 * THIS IS THE PROGRAM LOOP PUT CODE HERE THAT WILL BE ITERATED THROUGH
 * USING THE HELPER METHODS BELOW.
 */
void loop() 
{
    //---- Used to calculate the time taken for the main program loop to iterate ----
    unsigned long startTime;
    unsigned long endTime;
    unsigned long runTime;
    startTime = micros();
    //---- End of program loop calc stuff ----

    /*
    * PUT YOUR MAIN LOOP CODE HERE
    */
    neoPixels.clear();
    setColourRight(255,0,0);
    delay(500);
    neoPixels.clear();
    setColourLeft(0,255,0);
    delay(500);
    
  // ---- Calculate program loop runtime and o/p ----
  endTime = micros();
  runTime = endTime - startTime;
  //Serial.println(runTime);  //This value is in micro seconds 1000uS == 1mS
  //We want the program loop to execute as quickly as possible so the robot is 
  //As responsive as it can be. Functions that take a long time should be called 
  //less frequently. 
  //---- End of program loop calc stuff ----
}

Here we have our setup() and loop() method. The set-up method will be called once on start-up and the loop method will be looped through until the micro-controllers switched off. I have included some code so we can see how long the loop takes to iterate. We want to keep this time down to a minimum. To display the runtime uncomment line 47.

When using tabs they will be order alphabetically, this can have an effect on how the code is compiled. The code will be compiled in the order of the tabs therefore I have numbered the tabs accordingly so I can control in which order they are compiled to stop compilation errors. I will do this for all future projects created using Arduino IDE.

This code will make the left and right lights come on one after the other. Upload the code to the Microbit by clicking the upload button, switch on the batteries and see if the Neopixels are coming on if they are the code worked if they aren’t either something went wrong or your Neopixels aren’t working because you need to modify your Bitbot. Instructions for this can be found here.

As you can see the syntax of C++ and MicroPython is similar. I find C++ more intuitive and easier to read because the code is in code blocks where curly brackets {} are used to shoe the beginning and end of a block. You also use a semicolon at the end of each line (with some exceptions) so the compiler can parse the code correctly. Compare this code with the Microphone code and try to understand the differences. C++ is also strongly typed this means that variables must be declared with a certain type. For instance String, int, boolean, etc.

You can downloaded the code file in the resources section or by clicking here.