Roomba Hack

Intro

At one point, I modified a roomba 560 to take commands via a Bluetooth controller so I could drive it around. I haven’t had it setup in years but some people still email me asking questions, so here’s the parts list and copy of the Python 2 code.

Parts List

  • Raspberry Pi 3
  • Moga Pro controller or Wii controller
  • I originally used this cable but they made USB to Mini-DIN cables now if you don’t want to try to get power from the Roomba

Code

#Button Mappings
#
#00 : Select = Reset
#03 : Start = Failsafe
#12 : Triangle = Vaccum on/off
#13 : Circle = Spot Clean

import serial
import time
import pygame
import struct
import sys

MAX_TURN_RADIUS = 1000
controlRoomba = True
spotCleanActive = False
vacuumActive = False

#Open serial connection, 115200 is default Roomba BAUD rate
global ser
ser = serial.Serial('/dev/ttyUSB0', 115200)

def start():
    # Start SCI - puts into safe mode
    ser.write(bytes(chr(128), 'UTF-8'))
    # Enable the safe mode
    ser.write(bytes(chr(131), 'UTF-8'))
    # this is required or the command may fail
    #time.sleep(1)

def fix(num):
    # format the number correctly in hex
    num = struct.unpack('>BB', struct.pack('>h', num))
    #print num
    return num[0], num[1]

def move(vel, rad):

    # velocity
    vbit1, vbit2 = fix(vel)
    #radius
    rbit1, rbit2 = fix(rad)

    # send to roomba
    # Init move command
    ser.write(bytes(chr(137), 'UTF-8'))
    # you need this
    ser.write(bytes(chr(vbit1), 'UTF-8'))
    ser.write(bytes(chr(vbit2), 'UTF-8'))
    ser.write(bytes(chr(rbit1), 'UTF-8'))
    ser.write(bytes(chr(rbit2), 'UTF-8'))

def reset():
    print ("Resetting Roomba...")
    start()
    move(0, 0)
    ser.write(bytes(chr(139), 'UTF-8'))
    ser.write(bytes(chr(63), 'UTF-8'))
    ser.write(bytes(chr(32), 'UTF-8'))
    ser.write(bytes(chr(255), 'UTF-8'))
    time.sleep(1.5)
    control()
 
def failsafe():
    global vacuumActive
    print ("Failsafe called")
    ser.write(bytes(chr(131), 'UTF-8')) #Safe mode
    move(0, 0) #Stop Moving
    if (vacuumActive):
        vacuum() # Disable vacuum
    
    #Blink red light 3 times
    ser.write(bytes(chr(139), 'UTF-8'))
    ser.write(bytes(chr(0), 'UTF-8'))
    ser.write(bytes(chr(255), 'UTF-8'))
    ser.write(bytes(chr(255), 'UTF-8'))
    time.sleep(0.7)
    ser.write(bytes(chr(139), 'UTF-8'))
    ser.write(bytes(chr(0), 'UTF-8'))
    ser.write(bytes(chr(255), 'UTF-8'))
    ser.write(bytes(chr(0), 'UTF-8'))
    time.sleep(0.7)
    ser.write(bytes(chr(139), 'UTF-8'))
    ser.write(bytes(chr(0), 'UTF-8'))
    ser.write(bytes(chr(255), 'UTF-8'))
    ser.write(bytes(chr(255), 'UTF-8'))
    time.sleep(0.7)
    ser.write(bytes(chr(139), 'UTF-8'))
    ser.write(bytes(chr(0), 'UTF-8'))
    ser.write(bytes(chr(255), 'UTF-8'))
    ser.write(bytes(chr(0), 'UTF-8'))
    time.sleep(0.7)
    ser.write(bytes(chr(139), 'UTF-8'))
    ser.write(bytes(chr(0), 'UTF-8'))
    ser.write(bytes(chr(255), 'UTF-8'))
    ser.write(bytes(chr(255), 'UTF-8'))
    time.sleep(0.7)
    
    #Turn Roomba off
    ser.write(bytes(chr(133), 'UTF-8'))
    print ("Roomba now in failsafe mode")

def spotClean():
    global spotCleanActive
    if spotCleanActive == True:
        #Stop spot clean
        print ("Stopping spot clean.")
        ser.write(bytes(chr(134), 'UTF-8'))
        spotCleanActive = False
        reset()
    else:
        #Start spot clean
        print ("Starting spot clean.")
        ser.write(bytes(chr(134), 'UTF-8'))
        spotCleanActive = True
        time.sleep(.25)

def vacuum():
    global vacuumActive
    if vacuumActive == True:
        #Stop vacuuming
        print ("Stopping vacuum.")
        ser.write(bytes(chr(138), 'UTF-8'))
        ser.write(bytes(chr(0), 'UTF-8'))
        vacuumActive = False
        time.sleep(1)
    else:
        #Start vacuuming
        print ("Starting vacuum.")
        ser.write(bytes(chr(138), 'UTF-8'))
        ser.write(bytes(chr(1), 'UTF-8'))
        ser.write(bytes(chr(138), 'UTF-8'))
        ser.write(bytes(chr(2), 'UTF-8'))
        ser.write(bytes(chr(138), 'UTF-8'))
        ser.write(bytes(chr(3), 'UTF-8'))
        vacuumActive = True
        time.sleep(1)
    
def control():

    global MAX_TURN_RADIUS

    #turn light to green
    ser.write(bytes(chr(139), 'UTF-8'))
    ser.write(bytes(chr(0), 'UTF-8'))
    ser.write(bytes(chr(0), 'UTF-8')) #Green
    ser.write(bytes(chr(255), 'UTF-8'))
    
    pygame.init()
    pygame.joystick.init()
    clock = pygame.time.Clock()
    joystick = pygame.joystick.Joystick(0)
    joystick.init()

    #print joystick.get_name()
    #print joystick.get_numaxes()
    print ("Reset complete.")
    
    while controlRoomba == True:

        for event in pygame.event.get():
                if event.type == pygame.JOYBUTTONUP:
                    print ('Button')

        if joystick.get_button(0) == 1:
            reset()
        elif joystick.get_button(3) == 1:
            failsafe()
        elif joystick.get_button(12) == 1:
            vacuum()
        elif joystick.get_button(13) == 1:
            spotClean()
        else:
        
            speed = -1*int(500*joystick.get_axis(1))

            jsa = joystick.get_axis(2)
            if (jsa > 0):
                # Right, +1 as Max, Negative output
                radius = -1*((MAX_TURN_RADIUS+1)-int(MAX_TURN_RADIUS*jsa))
            elif (jsa == 0):
                radius = 0
            else:
                # Left, -1 as Max, Positive output
                radius = 1*((MAX_TURN_RADIUS+1)+int(MAX_TURN_RADIUS*jsa))
    

        move(speed, radius)
    
        clock.tick(20)    

# do stuff
print ("Starting...")
#turn light to green
ser.write(bytes(chr(139), 'UTF-8'))
ser.write(bytes(chr(0), 'UTF-8'))
ser.write(bytes(chr(16), 'UTF-8'))
ser.write(bytes(chr(255), 'UTF-8'))

reset()
control()

print ('Exiting...')
pygame.quit()