Makeblock MBot Ranger Arduino Coding Reference
Makeblock MBot Ranger Arduino Coding Reference
Makeblock overview
Makeblock design and manufacture a number of robot kits and modules. Each robot kit is based on
a number of control boards - each control board is different. At the time of writing there is the MegaPi
Pro, the mCore, the Orion, the me Auriga and the MegaPi (you can find out more about each board
here: https://www.makeblock.com/project_category/main-control-boards).
Mbots are designed for use with Mblock, and Arduino is just a side effect of that, so you’ll discover
that there’s little of the kind of documentation you might expect to help an Arduino/C++ coder get
started.
In addition to the kits, there are a large number of additional components - sensors, servos, displays,
mechanicals which can be added to the robot, once you do that then you need even more
knowledge on how to a) connect these devices correctly and b) how to program them in your code.
We will hopefully cover additional modules in later editions of this guide.
The folder structure of the downloaded library should look like this:
Once, you have installed the libraries into Arduino (see above link for instructions, and also in
the README.md file), on the Arduino IDE you will notice a new folder that contains several
examples, some of them may not be valid for your mBot Ranger board (as general rule if they
contains #include <MeAuriga.h> they programmed for mBot Ranger board)
If the installation of the libraries and examples is correct on your Arduino IDE the following entry
will appear
In term of C++/Arduino, the examples are a bit of a mix - they assume some knowledge, i.e. that the
main boards are all different in terms of how they’re wired up, so an Orion isn’t compatible with an
Auriga etc etc, they are all written for a specific board, not all examples will work on the base kit
(because there are examples for modules which are supplied as ‘extras’). In order to figure it all out,
some ability to read schematics and translate that information into code would be helpful. This guide
is all about the base mbot Ranger model. There will be more examples for additional modules in the
appendices (when I get around to writing them)
Mac Driver
If using Arduino/Ranger with a Mac, then you will need an additional driver :
For macOS Mojave (10.14) and later Apple provides a driver for these USB-serial bridges, so
the driver should not be installed.
See https://github.com/adrianmihalko/ch340g-ch34g-ch34x-mac-os-x-driver
Hardware
Pin Mapping on the Auriga is defined in the meAuriga.h header file. There is a board layout diagram
towards the rear of this document. It will no doubt be useful to familiarise yourself with the various
schematics at some point you will need them! If you don’t know how to read a schematic, don’t
worry, most of the work is done for you in this document.
Note that D means a digital pin, and A means an analogue pin. It’s important to differentiate these,
please check out the following tutorials on arduino pins if you’re not familiar with the concept:
https://www.arduino.cc/en/Tutorial/DigitalPins
https://www.arduino.cc/en/Tutorial/AnalogInputPins
// On-board sensors - some of these can be addressed via PORTS definition (see
below)
// Motor driver pins - the motors supplied are encoder motors. These can be
accessed via the library rather than directly, but it’s useful to know the pin
numbers
There is a servo port on this board (Arduino pin D16/TX2 and D17/RX2). But this is
covered by the expansion board
D0 and D1 are connected to the Blue and Red LEds respectively. These pins are also
connected to the BLE (Bluetooth Low Energy) and UART modules (Universal
Asynchronous Receiver/Transmitter), so programming these LEDs directly will disrupt
these modules.
Although this is a lot of information, it does not mean programming the Makeblock components is
difficult. It is especially important to know which part is connected to which port. This is specified
once when defining the object. After that, this information is no longer necessary
Most of the libraries for the various components have constructor code which can take either pin
numbers or slot numbers or port numbers. This can be useful but also confusing for the first time
coder.
Each Auriga port has 6 pins. SCL, SDA, GND, VCC, S1 and S2. Not all modules use all of these
ports. Some only use the VCC, GND, and a single data port.
Red ports (1-4) have Output voltage of 6-12 Volt and one or two digital ports.
Blue/Yellow/Grey/White ports (6-10) have one or two analogue ports, one or two digital ports and an
I2C port.
The light grey port (Port 5) is a serial port and has 4 pins : GND, 5V, TX2/D16, RX2/D17
To construct the LED object: This instructs the constructor to use port 0 and 12 LEDs
MeRGBLed led( 0, 12 ); // the port is irrelevant because it’s actually just connected to a pin
which has no port.
To initialise the LEDs // LED Ring controller is on Auriga D44/PWM
led.setpin( 44 );
Sample code below will send a spinning colour pattern to the LEDs...
#define ALLLEDS 0
// Auriga on-board light ring has 12 LEDs
#define LEDNUM 12
// on-board LED ring, at PORT0 (onboard)
MeRGBLed led( 0, LEDNUM );
float j, f, k;
void setup()
{
led.setpin( 44 );
}
void loop()
{
color_loop();
}
void color_loop()
{
for (uint8_t t = 0; t < LEDNUM; t++ )
{
uint8_t red = 64 * (1 + sin(t / 2.0 + j / 4.0) );
uint8_t green = 64 * (1 + sin(t / 1.0 + f / 9.0 + 2.1) );
uint8_t blue = 64 * (1 + sin(t / 3.0 + k / 14.0 + 4.2) );
led.setColorAt( t, red, green, blue );
}
led.show();
j += random(1, 6) / 6.0;
f += random(1, 6) / 6.0;
k += random(1, 6) / 6.0;
}
The Auriga board contains an onboard encoder driver with two ports. And the motor speed is
controlled by PWM. This signal is made by the microcontroller and allows the microcontroller to
perform other things while driving. Such as checking the distance to an object. An encoder
motor is different from a DC motor in that it can provide feedback as to distance travelled.
NOTE - If plugged into the PC, it’s easy to forget the power’s off. Make sure the GREEN led is lit
on the board - this means battery power is being applied to the motors. If not, press the red
button to turn the power on. It IS SAFE to connect both the battery supply and the usb cable, it
is also safe to turn on the battery power while the USB cable is connected.
#include <MeAuriga.h>
MeEncoderOnBoard Encoder_1(SLOT1);
MeEncoderOnBoard Encoder_2(SLOT2);
int16_t moveSpeed = 200;
void Forward(void)
{
Encoder_1.setMotorPwm(-moveSpeed); // setMotorPwm writes to the encoder controller
Encoder_2.setMotorPwm(moveSpeed); // so setting the speed change instantly
}
void Backward(void)
{
Encoder_1.setMotorPwm(moveSpeed);
Encoder_2.setMotorPwm(-moveSpeed);
}
void BackwardAndTurnLeft(void)
{
Encoder_1.setMotorPwm(moveSpeed/4);
Encoder_2.setMotorPwm(-moveSpeed);
}
void BackwardAndTurnRight(void)
{
Encoder_1.setMotorPwm(moveSpeed);
Encoder_2.setMotorPwm(-moveSpeed/4);
}
void TurnLeft(void)
{
Encoder_1.setMotorPwm(-moveSpeed);
Encoder_2.setMotorPwm(moveSpeed/2);
}
void TurnRight(void)
{
Encoder_1.setMotorPwm(-moveSpeed/2);
Encoder_2.setMotorPwm(moveSpeed);
}
void TurnLeft1(void)
{
Encoder_1.setMotorPwm(-moveSpeed);
void loop()
{
ChangeSpeed(100);
Forward();
delay(500);
Backward();
delay(500);
TurnLeft1();
delay(500);
TurnRight1();
delay(500);
Stop();
delay(1000);
}
#include <MeAuriga.h>
#define PWMA 11 //Motor Left
#define DIRA1 48
void setup(){
int speed1=120; // full
int speed2= 60; // half
int speed3= 30; // turn
int speed4= 0; // stop
pinMode(PWMA, OUTPUT);
pinMode(DIRA1, OUTPUT);
pinMode(DIRA2, OUTPUT);
pinMode(PWMB, OUTPUT);
pinMode(DIRB1, OUTPUT);
pinMode(DIRB1, OUTPUT);
Buzzer constructor
MeBuzzer buzzer;
// optional parameters:
// none: Sets buzzer pin to 8
// int pin: sets buzzer pin to the variable
// unit8_t port: Sets RJ25 port
// unit8_t port, uint8_t slot
To play a tone
buzzer.tone([int pin], uint16_t frequency, uint32_t duration);
// pin - optional output pin (45 if you’re going to use it)
// Frequency (Hz)
// Duration (ms)
Sample code below will sound the buzzer two times when the program is loaded.
The buzzer.tone() function blocks program execution for the duration of the sound
#include <MeAuriga.h>
MeBuzzer buzzer;
#define BUZZER_PORT 45
void setup() {
buzzer.setpin(BUZZER_PORT);
buzzer.noTone();
void loop(){}
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+----+----+----+----+----+----+-----+-----+-----+-----+
C | 16| 33| 65| 131| 262| 523| 1046| 2093| 4186| 8372|
C# | 17| 35| 69| 139| 277| 554| 1108| 2217| 4434| 8869|
D | 18| 37| 73| 147| 294| 587| 1174| 2349| 4698| 9397|
D# | 19| 39| 78| 156| 311| 622| 1244| 2489| 4978| 9956|
E | 21| 41| 82| 165| 330| 659| 1318| 2637| 5274|10548|
F | 22| 44| 87| 175| 349| 698| 1396| 2793| 5587|11175|
F# | 23| 46| 92| 185| 370| 740| 1479| 2959| 5919|11839|
G | 24| 49| 98| 196| 392| 784| 1567| 3135| 6271|12543|
G# | 26| 52| 104| 208| 415| 831| 1661| 3322| 6644|13289|
A | 28| 55| 110| 220| 440| 880| 1760| 3520| 7040|14080|
A# | 29| 58| 117| 233| 466| 932| 1864| 3729| 7458|14917|
B | 31| 62| 123| 247| 494| 988| 1975| 3951| 7902|15804|
The Auriga uses pin 45 for the buzzer, this is a PWM port which also means that the
analogwrite() function can be used to write a PWM wave e.g.:
The arduino analogWrite() function generate a steady rectangular wave of the specified duty
cycle until the next call to analogWrite() (or a call to digitalRead() or digitalWrite()) on the same
pin
These ports are referred to in the serial library as Serial, Serial2, Serial3 and Serial4. On the
Ranger, these have the following function:
Serial - the connection to the PC, this is normally used to send debug information back to the
arduino app for display on the PC screen.
Serial1 - the ranger uses these for the motor driver
Serial2 - this is connected to PORT_5 (and also to the SERVO white 4 pin connector under the
LED panel)
Serial3 - this is connected to the ““extension header 4”
#include <MeAuriga.h>
void setup() {
// Begin the Serial at 9600 Baud
Serial.begin(9600);
Serial2.begin(9600);
}
void loop() {
Serial2.readBytes(mystr,5); //Read the serial data and store in var
Serial.println(mystr); //Print data on Serial Monitor
delay(1000);
}
MeLineFollower lineFinder(PORT_9);
void setup()
{
Serial.begin(9600);
}
void loop()
{
int sensorState = lineFinder.readSensors();
switch(sensorState)
{
case S1_IN_S2_IN: Serial.println("S1_IN_S2_IN"); break;
case S1_IN_S2_OUT: Serial.println("S1_IN_S2_OUT"); break;
case S1_OUT_S2_IN: Serial.println("S1_OUT_S2_IN"); break;
case S1_OUT_S2_OUT: Serial.println("S1_OUT_S2_OUT"); break;
default: break;
}
delay(200);
}
MeLightSensor lightSensor(PORT_12);
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.print("value = "); // Print the results to the serial monitor
Serial.println(lightSensor.read()); // Brightness value from 0-1023
delay(50); // Wait 50 milliseconds before next measurement
}
Auriga has two sensors - one connected to PORT_11 and the other to PORT_12
#include <MeAuriga.h>
MeOnBoardTemp temp(PORT_13);
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.print("value = "); // Print the results to the serial monitor
Serial.println(temp.readValue()); // temp value in Celcius
delay(50); // Wait 50 milliseconds before next measurement
}
#include <MeAuriga.h>
MeSoundSensor dbsense(14);
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.print("value = "); // Print the results to the serial monitor
Serial.println(dbsense.strength()); // the sound intensity revealed by the sensor
delay(50); // Wait 50 milliseconds before next measurement
}
Input/Output- Bluetooth
Sample code below will read the ultrasonic distance sensor, write the sensed value back to
Serial Port.
MeUltrasonicSensor ultrasonic(PORT_10);
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.print("distance(cm) = "); // Print the results to the serial monitor
Serial.println(ultrasonic.distanceCm()); // Distance value from 3cm - 400cm
delay(50); // Wait 50 milliseconds before next measurement
}
Input - Gyro
The Gyro will measure position and angle of the device. It uses an MPU-6050, and combines a
3-axis gyroscope, 3-axis accelerometer, and a Digital Motion Processor™ (DMP) capable of
processing complex 9-axis Motion Fusion algorithms.
https://media.digikey.com/pdf/Data%20Sheets/Makeblock%20PDFs/11012_Web.pdf
This sounds ideal for turn calculation, but beware of gyroscope drift which means the angles will
rarely be accurate, but the best you can do with the Ranger kit.
This device is extremely capable, but the libraries provided thankfully simplifies it all for us to a
few basic functions:
void setup()
{
Serial.begin(115200);
gyro.begin();
}
void loop()
{
gyro.update();
Serial.read();
Serial.print("X:");
Serial.print(gyro.getAngleX() );
Serial.print(" Y:");
Serial.print(gyro.getAngleY() );
Serial.print(" Z:");
Serial.println(gyro.getAngleZ() );
delay(10);
}
Out Out S1_OUT_S2_OUT (If previously left turn) Left Turn 0 255
#include <MeAuriga.h>
#define BUZZER_PORT 45
MeBuzzer buzzer;
const int buzzerDuration = 20;
MeUltrasonicSensor ultrasonic(PORT_10);
const float distanceThreshold = 10.0;
float distance = 10.0;
boolean detected = false;
unsigned int detectCount = 0;
void setup()
{
buzzer.setpin(BUZZER_PORT);
Serial.begin(115200);
Serial.println("Lap Timer.");
Serial.println("Trigger the sensor to start timing");
Serial.print("Sensor detection distance: ");
Serial.print(distance);
Serial.println("cm");
void loop()
{
currentTime = millis();
if ((currentTime - lastDetectMills) > minimumLapTime) {
distance = ultrasonic.distanceCm();
if ((distance < distanceThreshold)) {
if (!detected) {
detected = true;
if (detectCount == 0) {
firstDetectMills = currentTime;
buzzer.tone(300, buzzerDuration); //Buzzer sounds 600Hz for 1000ms
}
Serial.print("Lap:");
Serial.print(detectCount);
Serial.print(", Time:");
Serial.print(currentTime - firstDetectMills);
Serial.print("ms, LapTime:");
Serial.print(currentTime - lastDetectMills);
Serial.print(" ms, UltrasoundDistance:");
Serial.print(distance);
Serial.println(" cm");
buzzer.tone(600, buzzerDuration); //Buzzer sounds 600Hz for 1000ms
detectCount++;
lastDetectMills = currentTime;
}
} else {
detected = false;
}//distance < distanceThreshold
}//minimumLapTime
}//loop
Appendix A - Terminology
Explaining some terminology
TX/RX are transmit and receive pins for serial communications. These operate at logic levels (5V or
3.3V) https://www.arduino.cc/reference/en/language/functions/communication/serial/. Do NOT
connect these to an RS232 port directly as that uses +/- 12V and will damage the board.
PWM means Pulse width Modulation (The pin can generate a square wave of specified duty
cycle (on to off ratio)) A ration of 0 is off, 255 is on, 64 is on 25% of the time. 191 is on 75% of
the time. This can be used to control the frequency of a buzzer, speed of a motor or the
brightness of an LED (for example) https://www.arduino.cc/en/tutorial/PWM
The Arduino has a built-in library for working with I2C called the Wire Library. It makes it very easy to
communicate on the I2C bus, and it can configure the Arduino to become either a master or a slave.
And if these links stop working, pinout and v1.1 schematic are here:
https://drive.google.com/drive/folders/1WVejv69KuLaoQ3zhe5ld14_TkpRiet5u?usp=sharing
Example 1 - takes keyboard input and programs the PWM mode of each motor to move it
forward or back. Unlike the setMotorPwm() function used in earlier examples, this uses
serTarPWM() which sets a target PWM value, and each call to .loop() increases the speed
towards the target. New speed = (80% current speed + 20% target speed) offering smoother
acceleration
#include <MeAuriga.h>
MeEncoderOnBoard Encoder_1(SLOT1);
MeEncoderOnBoard Encoder_2(SLOT2);
void setup()
{
Serial.begin(115200);
#include <MeAuriga.h>
MeEncoderOnBoard Encoder_1(SLOT1);
MeEncoderOnBoard Encoder_2(SLOT2);
void setup()
{
Serial.begin(115200);
Example 4 takes keyboard input and programs the PID mode to move to a specific position
#include <MeAuriga.h>
MeEncoderOnBoard Encoder_1(SLOT1);
MeEncoderOnBoard Encoder_2(SLOT2);
void isr_process_encoder1(void) // count the ticks - i.e. how far the motor has moved
{
if(digitalRead(Encoder_1.getPortB()) == 0)
{
Encoder_1.pulsePosMinus();
}
else
{
Encoder_1.pulsePosPlus();;
}
}
void isr_process_encoder2(void) // count the ticks - i.e. how far the motor has moved
{
if(digitalRead(Encoder_2.getPortB()) == 0)
{
Encoder_2.pulsePosMinus();
}
else
{
Encoder_2.pulsePosPlus();
}
}
void setup()
{
// these interrupts are necesssary to
// enable the motor to know where it is
attachInterrupt(Encoder_1.getIntNum(), isr_process_encoder1, RISING);
attachInterrupt(Encoder_2.getIntNum(), isr_process_encoder2, RISING);
Serial.begin(115200);
Anyone wondering about my calculations for rotation angle above, there is 14.5cm between the
two tracks on the ranger tank-track build, giving a potential turning circle of 45.6cm.
Assuming the wheels are travelling around a circle of that diameter when turning in opposite
directions, the angle turned should be 360 * (distance travelled by wheels / 45.6). (arc angle =
360 * arc length / circumference)
Some will be lost through friction perhaps and the fact the drive wheels are at the front of the
tracks, not the middle, but I didn’t expect quite so much! Not much accuracy then when using
tracked vehicle, so best to use the Gyro functions (see earlier in the document) - these are more
accurate, but not perfect due to gyroscope drift.
In order to programme a system to reach a specific value (e.g. position of a motor, temperature
of an oven, height of a quadcopter etc, you can imagine that you can easily measure the current
status using sensors, you know what the desired result is, and you can apply an input to make
the system change.
What PID control does is measure the current position and the error (i.e. difference between the
current position and goal), then applies a calculation using three parameters (proportional,
integral and derivative) to adjust the input to move the reading towards the desired goal. Each of
the three terms can be tuned to make the system react appropriately.
The working principle behind a PID controller is that the proportional, integral and derivative
terms must be individually adjusted or "tuned." Based on the difference between these values a
correction factor is calculated and applied to the input. For example, if an oven is cooler than
required, the heat will be increased. Here are the three steps:
These factors are added together to produce a correction factor which is then applied to the
input.