Driver

The Roomba Serial Communication Interface (SCI) Specification is very useful and contains most of the information one needs to control the Roomba.  All the information about the Roomba SCI in this report was extracted from the specification and repackaged in a useful format.  I have also written a simple API for sending commands to the Roomba over its serial port, which I will describe here along with some information on using it with our real-time operating system (RTOS).

Roomba States

The Roomba transitions between four states: Off, Passive, Safe, and Full.  The controller can instruct the Roomba to move between states by sending the commands START, CONTROL, FULL, SAFE, POWER, and BAUD.  After changing the baud rate, the controller must wait for 100 ms before sending commands.  After changing the state directly (via the CONTROL, FULL, SAFE, and POWER commands) the controller must wait for 20 ms before sending further commands.

State
Description
Off The Roomba will not respond to any SCI commands except START.  The START command puts the Roomba into its passive state.
Passive The Roomba looks like it’s still off, but the SCI is now active.  The output commands, such as those controlling the wheels and LEDs, will have no effect.  The Roomba’s sensor state can be read over the SCI, songs can be loaded (but not played), and the pre-defined vacuuming programs can be activated (SPOT, CLEAN, and MAX commands) and deactivated (POWER command).
Safe All of the SCI commands will work.  If the Roomba detects that it is going over a cliff (via the cliff sensors and wheel drop sensors) or is plugged into the charger, then the Roomba immediately returns to passive mode.  The Roomba will not respond to button presses in this mode (but button presses will be reflected in the sensor packet).
Full All of the SCI commands will work.  The cliff-sensing safety features in safe mode are disabled, so the Roomba will not return to passive mode unless it is plugged into the charger or sent the POWER command.  The Roomba will not respond to button presses in this mode (but button presses will be reflected in the sensor packet).

Roomba API

There’s a lot of documentation in roomba.h, but I’ll summarize the functions here.

Roomba_Init()

Roomba_Init() should be called once during system start-up, with interrupts disabled.  It brings the Roomba through the state diagram above, into Safe mode.  It performs the following steps:

  1. Wake up the Roomba using the DD pin.
  2. Wait for 2 seconds, then set the Roomba to communicate at 19200 baud using the DD pin.
  3. Configure the microcontroller’s UART to run at 19200 Bd and send the START command.
  4. Send the BAUD command to set the Roomba to communicate at 38400 Bd, and re-configure the microcontroller’s UART to run at that speed.
  5. Send the CONTROL command to put the Roomba into Safe mode.
  6. Send an LED command with the default LED values.

In total, the function takes around 3 seconds to run.  It’s not strictly necessary for Roomba_Init() to be called with interrupts disabled, but it’s a good idea (especially when using the RTOS) to do so to keep the timing correct.

Steps 2-4 are useful because you can’t be sure what the Roomba’s baud rate is when the system starts.  When the Roomba does a power cycle (e.g. when the battery is removed or the Roomba is plugged in), it will reset its serial speed to 57600 Bd.  Otherwise the baud rate setting will be persistent.  We want the baud rate to be 38400 Bd because microcontrollers running at 8 MHz and 16 MHz don’t generate the UART signal perfectly.  They generate a 57600 Bd signal with an error rate of between 0.8% and 3.5% (1 bit out of every 28 to 125), and a 38400 Bd signal with an error rate of 0.2% (1 bit out of every 500).  The Roomba sensor packet is 260 bits long, so it can be transferred completely with no error at 38400 Bd but not at 57600 Bd.

Roomba_UpdateSensorPacket(ROOMBA_SENSOR_GROUP group, roomba_sensor_data_t* sensor_packet)

Used to query the Roomba for some sensor information.  It fills out the part of the roomba_sensor_data_t structure corresponding to the requested group.

The distance and angle sensor values in the Chassis group get reset to 0 on the Roomba every time the Chassis group data are downloaded.  If you’re using the distance or angle sensors, you need to keep a running sum of the sensor values and update it every time you call Roomba_UpdateSensorPacket(CHASSIS, …) to update the chassis data.  You should update the Chassis group at the start of your program to make sure that the sensor values start at 0 instead of whatever total is left over from previous tests.  The Roomba doesn’t reset its sensor data when it is powered down, only when it enters a charge cycle or the battery is removed.

The sensor packet structure is defined in sensor_struct.h.  Most of the sensor values are straightforward 8-bit integers, but several are 16-bit integers.  The 16-bit values are defined in unions to make them easier to work with.  The 16-bit values are written to memory using the 8-bit high and low members of the union, which respectively access the high and low bytes of the integer.  The Roomba sends data in big-endian order, so the high byte is sent first; AVR is little-endian, so in memory the low byte comes first.  The 16-bit number is accessed using the union’s value member.

See the page on the Roomba Sensors for more information.

Roomba_ChangeState(ROOMBA_STATE newState)

Allows the application to move the Roomba between the three states shown in the state diagram above (passive, safe, and full).  When switching to full mode, the function might recurse once.

Roomba_Drive(int16_t velocity, int16_t radius)

Roomba_Drive() is a straightforward mapping of the Roomba’s DRIVE command.

The velocity can be between -500 and +500, and is measured in mm/s.  A negative value means drive backwards.  It’s best to avoid travelling at the full 500 mm/s.  The Roomba isn’t very responsive, so it’s hard to control at that speed.  Also, switching from +500 mm/s to -500 mm/s without slowing down in betweencan cause the Roomba’s bumper to lift off the floor, which triggers the cliff sensor and boots the Roomba into passive mode (if it’s in safe mode).  200 mm/s is a good cruising speed.  The Roomba’s actual velocity usually differs from the programmed value by a couple dozen mm/s.

The Roomba was made to go in circles, and the radius parameter determines how big the circle should be (again, this value is approximate).  The radius is measured in mm; it can range from -2000 to 2000 with negative radii corresponding to clockwise turns.  The value 0x8000 is a special case that means the Roomba should go straight, but of course it can’t actually go straight.  Even with a radius value of 0x8000 it will still drift from a straight line by between 3 and 6 degrees per metre.

Roomba_ConfigPowerLED(uint8_t colour, uint8_t intensity)

Roomba_ConfigStatusLED(STATUS_LED_STATE state)

Roomba_ConfigSpotLED(LED_STATE state)

Roomba_ConfigCleanLED(LED_STATE state)

Roomba_ConfigMaxLED(LED_STATE state)

Roomba_ConfigDirtDetectLED(LED_STATE state)

The functions that control the Roomba’s LEDs are all pretty much the same.  The Spot, Clean, Max, and Dirt Detect LEDs are all on-off LEDs.  The Status LED has four states: off, red, green, and amber (i.e. red and green at the same time).  The Power LED allows you to specify one of 255 colours, with intensity ranging from 0 (off) to 255 (full brightness).  The Roomba SCI specification contains more information on the Power LED on page 4.

The driver keeps track of the expected states of all the LEDs.  All the LEDs are configured using one command packet, so the correct data for all the LEDs have to be sent every time one of the LED states change.  Unfortunately there’s no way to tell what the LED states actually are, but typically if they have changed due to external stimuli then the Roomba has been kicked back into passive mode so there’s nothing you can do about it anyway.

Roomba_LoadSong(uint8_t songNum, uint8_t* notes, uint8_t* durations, uint8_t numNotes)

Load data into one of the Roomba’s song slots.  The Roomba provides storage for 16 songs, each of which can have up to 16 notes.  The notes are integers that match the MIDI numbering scheme for notes from 49 Hz to 12543.9 Hz (both G notes, 8 octaves apart).  The durations are integers, measured in 64ths of a second.  For example:

uint8_t notes[2] = { 69, 60 };
uint8_t durations[2] = { 64, 32 };
Roomba_LoadSong(0, notes, durations, 2);

This will load a 2-note song into song slot 0.  When the song is played, the Roomba will generate a 1-second 440 Hz tone (note 69, which is A above middle C) followed by a 0.5-second 261.6 Hz tone (note 60, which is middle C).  The Roomba SCI specification has a table of note numbers on page 5.

It is possible—but difficult—to string multiple songs together to create a song longer than 16 notes.  The note durations are not very accurate: a song that should theoretically take 8 seconds to play might actually finish after about 7.8 seconds.  There is no way to tell when a song has completed using the Roomba sensors, so you have to tune a delay to wait for one song to end before starting the next.  If you try to play the next song before the first one completes then the second one will not play.  I have found that a song needs to start at most 5 to 10 ms after the previous song finishes for the break in songs to be not noticeable.  The Roomba puts a 5 to 10 ms delay between notes within a song.

Roomba_PlaySong(int songNum)

Command the Roomba to play the specified song.  If there is already a song playing, or if there is no song loaded into the slot, then nothing happens.