Introduction: Controlling a Roomba Robot With Arduino and Android

About: Engineer, writer and forever student. Passionate to share knowledge of electronics with focus on IoT and robotics.

As one of Robotics Contest 2016's winners here at Instructables, I received as a prize an iRobot Roomba Create2. It is a great and very affordable platform for robotics development, costing around US$200. Create 2 allows for a variety of programming methods. As a start, I used it with an Arduino and an Android APP to move the robot around. In this first tutorial, I will explore how to connect the Arduino with the Roomba via serial port and how to command its motors, LEDS and sound. On a future projects, I will explore its sensors and also use a Raspberry Pi to connect the Roomba with internet.

Bellow, a video showing my first results programming a Roomba robot:

Step 1: Bill of Material

  • iRobot Create2
  • Arduino UNO
  • Bluetooth module HC-06
  • Push-button

Step 2: The Roomba Create2

The Roomba is a differential drive robot, with 2 wheels and a front caster. Its velocity goes up to 500 mm/s and can be commanded to go upward or backward.

For signalization, we can count with four 7 segments display and 5 LEDs (see figure):

  1. Clean
  2. Spot
  3. Dock
  4. Warning/Check
  5. Dirt/Debris

As internal sensors, we have among others:

  • Cliff detector (4 in front)
  • Bump detectors (2 in front)
  • Wheel encoders

For programming, the document: iRobot® Create® 2 Open Interface (OI) should be used.

The Roomba can be programmed in 3 modes:

  1. Passive Mode:
    • Upon sending the Start command or any one of the cleaning mode commands (e.g., Spot, Clean, Seek Dock), the OI enters into Passive mode. When the OI is in Passive mode, you can request and receive sensor data using any of the sensor commands, but you cannot change the current command parameters for the actuators (motors, speaker, lights, low side drivers, digital outputs) to something else.
  2. Safe Mode:
    • Gives you full control of Roomba, with the exception of the following safety-related conditions:
      • Charger plugged in and powered.
      • Detection of a wheel drop (on any wheel).
      • Detection of a cliff while moving forward (or moving backward with a small turning radius, less than one robot radius).
    • Should one of the above safety-related conditions occur while the OI is in Safe mode, Roomba stops all motors and reverts to the Passive mode.
  3. Full mode:
    • Gives you complete control over Roomba, all of its actuators, and all of the safety-related conditions that are restricted when the OI is in Safe mode, as Full mode shuts off the cliff, wheel-drop and internal charger safety features.

Step 3: The Serial Connection

For communication between the Roomba and Arduino, the Serial Port will be used. By default, Roomba communicates at 115,200 baud, but in order to communicate with Arduino, we will switch it to 19,200.

There are 2 ways to set Roomba baud rate:

  1. While powering off Roomba, continue to hold down the Clean/Power button after the light has turned off. After about 10 seconds, Roomba plays a tune of descending pitches. Roomba will communicate at 19,200 baud until the processor loses battery power or the baud rate is explicitly changed by way of the OI.
  2. Use the Baud Rate Change pin (pin 5 on the Mini-DIN connector) to change Roomba’s baud rate. After turning on Roomba, wait 2 seconds and then pulse the Baud Rate Change low three times. Each pulse should last between 50 and 500 milliseconds. Roomba will communicate at 19200 baud until the processor loses battery power or the baud rate is explicitly changed by way of the OI.

The above diagram shows how the Arduino should be connected to Roomba Mini-DIN connector

Step 4: Starting Roomba

The first think that must be done when programming a Roomba is to:

  • "Wake up" the robot
  • Define the mode (Safe or full)

We can "wake-up" it, sending a Low pulse to Mini-DIN pin 5 (detect device input) as shown on function bellow:

void wakeUp (void)
{
  setWarningLED(ON);
  digitalWrite(ddPin, HIGH);
  delay(100);
  digitalWrite(ddPin, LOW);
  delay(500);
  digitalWrite(ddPin, HIGH);
  delay(2000);
}

To start Roomba always 2 codes must be sent: "START" [128] and the mode, in our case "SAFE MODE" [131]. If you want a "FULL MODE", the code [132] should be sent instead.

void startSafe()
{  
  Roomba.write(128);  //Start
  Roomba.write(131);  //Safe mode
  delay(1000);
}

Step 5: Turning on the LEDs and Display

Turning on the LEDs

As described at introduction, Roomba has 5 LEDs:

  • Power/Clean (bi-color red/green and intensity controlled)
  • Spot (Green, fixed intensity)
  • Dock (Green, fixed intensity)
  • Warning/Check (Orange, fixed intensity)
  • Dirt/Debris (Blue, fixed intensity)

All LEDs can be commanded using code [139]

To control the Power LED, you must send two data bytes to Roomba: "color" and "intensity".

  • Color:
    • Green = 0
    • Orange = 128
    • red=255
  • Intensity:
    • Low=0
    • Max=255

The function setPowerLED (byte setColor, byte setIntensity) does it:

void setPowerLED(byte setColor, byte setIntensity)
{
    color = setColor;
    intensity = setIntensity;
    Roomba.write(139);
    Roomba.write((byte)0x00);
    Roomba.write((byte)color);
    Roomba.write((byte)intensity);
}

For example, to turn on the POWER LED with orange color at half its intensity, you can call the function as bellow:

setPowerLED (128, 128);

To Turn ON the remaining 4 LEDs, the bellow functions must be used:

setDebrisLED(ON);

setDockLED(ON);

setSpotLED(ON);

setWarningLED(ON);

All the above functions has a code similar to this one:

void setDebrisLED(bool enable)
{
  debrisLED = enable;
  Roomba.write(139);
  Roomba.write((debrisLED ? 1 : 0) + (spotLED ? 2 : 0) + (dockLED ? 4 : 0) + (warningLED ? 8 : 0));
  Roomba.write((byte)color);
  Roomba.write((byte)intensity);
}

Basically the diference will the line:

debrisLED = enable;

that must be changed enabling each one of other LEDs (spotLED, dockLED, warningLED).


Sending Messages to Display

Roomba has four 7 Segment Display that you can use to send messages in two different ways::

  1. Code [163]: Digit LEDs Raw (Numeric)
  2. Code [164]: Digit LEDs ASCII (Approximation of letters and especial codes)

To display numbers is very ease. You must send the code [163], following the 4 digitas to be displayed. The function: setDigitLEDs(byte digit1, byte digit2, byte digit3, byte digit4) does this for you:

void setDigitLEDs(byte digit1, byte digit2, byte digit3, byte digit4)
{
    Roomba.write(163);
    Roomba.write(digit1);
    Roomba.write(digit2);
    Roomba.write(digit3);
    Roomba.write(digit4);
}

For example, to display "1, 2, 3, 4", you must call the function:

setDigitLEDs(1, 2, 3, 4);

With code [164], is possible send approximation of ASCII.

The function setDigitLEDFromASCII(byte digit, char letter) does this to us:

void setDigitLEDFromASCII(byte digit, char letter)
{
  switch (digit){
  case 1:
    digit1 = letter;
    break;
  case 2:
    digit2 = letter;
    break;
  case 3:
    digit3 = letter;
    break;
  case 4:
    digit4 = letter;
    break;
  }
  Roomba.write(164);
  Roomba.write(digit1);
  Roomba.write(digit2);
  Roomba.write(digit3);
  Roomba.write(digit4);
}

To simplify, I create a new function to send the 4 digits at same time:

void writeLEDs (char a, char b, char c, char d)
{
  setDigitLEDFromASCII(1, a);
  setDigitLEDFromASCII(2, b);
  setDigitLEDFromASCII(3, c);
  setDigitLEDFromASCII(4, d);
}

For example, to display "STOP", you must call the function:

writeLEDs ('s', 't', 'o', 'p');

Step 6: Moving the Roomba Around

For mobility, Roomba has 2 independent DC motors that can be programed to run up to 500mm/s. There are several commands that can be used to drive the robot. The main ones are:

  1. Code [137]: Drive ==> Must send +/- Speed in mm/s and +/- Radius in mm
  2. Code [145]: Drive Direct ==> Must send Left/Right Speed in mm/s (+ for Forward and - for Backward)
  3. Code [146]: Drive PWM ==> Must send +/- PWM data for Left and Right wheels

Bellow the code for those 3 options:

void drive(int velocity, int radius)
{
  clamp(velocity, -500, 500); //def max and min velocity in mm/s
  clamp(radius, -2000, 2000); //def max and min radius in mm
  
  Roomba.write(137);
  Roomba.write(velocity >> 8);
  Roomba.write(velocity);
  Roomba.write(radius >> 8);
  Roomba.write(radius);
}

//---------------------------------------------------------------

void driveWheels(int right, int left)
{
  clamp(right, -500, 500);
  clamp(left, -500, 500);
  
  Roomba.write(145);
  Roomba.write(right >> 8);
  Roomba.write(right);
  Roomba.write(left >> 8);
  Roomba.write(left);
  }

//---------------------------------------------------------------
void driveWheelsPWM(int rightPWM, int leftPWM)
{
  clamp(rightPWM, -255, 255);
  clamp(leftPWM, -255, 255);
  
  Roomba.write(146);
  Roomba.write(rightPWM >> 8);
  Roomba.write(rightPWM);
  Roomba.write(leftPWM >> 8);
  Roomba.write(leftPWM);
}

Note that the "clamp" function define the maximum and minimum values that are allowed to input. This function is defined on the file rombaDefines.h:

#define clamp(value, min, max) (value < min ? min : value > max ? max : value)

Using the above code, simpler functions can be created to drive Roomba around:

/---------------------------------------------------------------
void turnCW(unsigned short velocity, unsigned short degrees)
{
  drive(velocity, -1);
  clamp(velocity, 0, 500);
  delay(6600);
  drive(0,0);
}

//---------------------------------------------------------------
void turnCCW(unsigned short velocity, unsigned short degrees)
{
  drive(velocity, 1); 
  clamp(velocity, 0, 500);
  delay(6600);
  drive(0,0);
}

//---------------------------------------------------------------
void driveStop(void)
{
  drive(0,0);
}

//---------------------------------------------------------------
void driveLeft(int left)
{
  driveWheels(left, 0);
}

//---------------------------------------------------------------
void driveRight(int right)
{
  driveWheels(0, right);
}


Note that to turn in angle, the "delay" argument must be calculated specifically for a given speed

Bellow some examples that can be use for testing the motors:

turnCW (40, 180); // spin clockwise 180 degrees and stop

driveWheels(20, -20); // spin

driveLeft(20); // turning left

For testing the motors, it is good to add an external push button (in my case connected to Arduino pin 12), so you can download the code to Arduino, starting the Roomba, but stoping the execution until the push button is pressed. Usually, for motors testing you can do it on the set-up part of the code.

As an example, please see the simple Arduino code bellow (note that the code uses functions and definitions developed before):

#include "roombaDefines.h"
#include <SoftwareSerial.h>

// Roomba Create2 connection
int rxPin=10;
int txPin=11;
SoftwareSerial Roomba(rxPin,txPin);

//---------------------------------------------
void setup() 
{
  Roomba.begin(19200);
  
  pinMode(ddPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP); // connected to Arduino pin 12 and used for "starting"

  delay(2000);
  
  wakeUp ();   // Wake-up Roomba
  startSafe(); // Start Roomba in Safe Mode

  while (digitalRead(buttonPin)) {  } // wait button to be pressed to continous run code
  
  turnCW (40, 180);  //test Roomba spin clock-wise 180 degrees and stop
}

//---------------------------------------------
void loop() 
{
  
}


Step 7: Controlling Roomba Via Bluetooth

To complete our first part of the project, let's install a Bluetooth module (HC-06) to our Arduino. The above diagram shows how to do it. Usually the HC-06 is settle-up from factory with a baud rate of 9,600. It is important that you change it to 19,200 to be compatible with Arduino-Roomba communication speed. You can do that sending an AT command to the module (AT+BAUD5 where "5" is the code for 19,200).

If you have any doubt about how the HC-06 works, please have a look at my instructable: Connecting "stuff" via Bluetooth / Android / Arduino.

To control the Roomba, we will use an generic App that was developed by me to control mobile robots, using the MIT AppInventor 2: "MJRoBot BT Remote Control". The app can be downloaded for free from the Google store via the link: App: MJRoBot BT Remote Control.

The app has a simple interface, allowing you to send commands to the BT module in both, TEXT mode or directly via pre-programmed buttons (each time a button is pressed, a character is sent):

  • w: Foreward
  • s: Backward
  • d: Right
  • a: Left
  • f: Stop
  • p: ON / OFF (not used on this first part)
  • m: Manual / Automatic (Used to restart Roomba if an obstacle as a cliff is found in Safe Mode)
  • +: Speed +
  • -: Speed -

You can also send other commands as text if necessary. There is also a text window for messages received from the BT module. This feature is very important during the testing phase, it can be used in the same way as the "Serial Monitor".

The loop() part of the code will be "listening" the bluetooth device and depending of command received, take an action:

void loop() 
{
   checkBTcmd();  // verify if a comand is received from BT remote control
   manualCmd ();
}

The function checkBTcmd() is shown bellow:

void checkBTcmd()  // verify if a command is received from BT remote control
 { 
    if (BT1.available()) 
    { 
      command = BT1.read();
      BT1.flush();
    }
 }

Once a command is received, the function manualCmd() will take the appropriated action:

void manualCmd()
{
  switch (command)
  {
    
    case 'm': 
      startSafe();
      Serial.print("Roomba in Safe mode");
      BT1.print("Roomba BT Ctrl OK - Safe mode");
      BT1.println('\n');
      command = 'f';
      playSound (3);
      break;
 
    case 'f': 
      driveStop(); //turn off both motors
      writeLEDs ('s', 't', 'o', 'p');
      state = command;
      break;

    case 'w':  
      drive (motorSpeed, 0); 
      writeLEDs (' ', 'g', 'o', ' ');
      state = command;  
      break;

    case 'd':     
      driveRight(motorSpeed);
      writeLEDs ('r', 'i', 'g', 'h');
      break;

   case 'a': 
      driveLeft(motorSpeed);
      writeLEDs ('l', 'e', 'f', 't');
      break;
    
    case 's':  
      drive (-motorSpeed, 0);
      writeLEDs ('b', 'a', 'c', 'k');
      state = command;
      break;

    case '+': 
      if (state == 'w')
      {
        motorSpeed = motorSpeed + 10;
        if (motorSpeed > MAX_SPEED) 
        { 
          motorSpeed = MAX_SPEED;
        }  
        command = 'w';
      } else {command = state;}
      break;

    case '-': 
 
      if (state == 'w')
      {
        motorSpeed = motorSpeed - 10;
      }     
      if (motorSpeed < MIN_SPEED ) 
      { 
        motorSpeed = MIN_SPEED;
      }
      Serial.println(motorSpeed); 
      command = state;
      break;
  }
}

Step 8: Conclusion

The complete Arduino code used in here and related documents can be found at my GITHUB: Roomba_BT_Ctrl.

Please note that not all Roomba actuators were commented on this tutorial. There are other motors, used for cleaning, other LEDs used for schedule, buttons, sensors, etc.

Several of the functions that I created on my program were based on the Create 2 library developed by Dom Amato. You can download the complete library at: https://github.com/brinnLabs/Create2.

My intention here was keep simple and give you a start platform to start playing with Roomba. In the future, I pretend to publish other tutorials, using a Raspberry-Pi, connect the Roomba with internet, read its sensors, etc.

As always, I hope this project can help others find their way in the exciting world of electronics and Robotics!

For more projects, please visit my blog (in Portuguese):

MJRoBot.org

Saludos from the south of the world!

See you at my next instructable!

Thank you

Marcelo

Automation Contest 2016

Participated in the
Automation Contest 2016