Create a gist now

Instantly share code, notes, and snippets.

Control Sphero BB-8 from Linux.
#!/usr/bin/env python
# BB-8 Python driver by Alistair Buxton <a.j.buxton@gmail.com>
from bluepy import btle
import time
class BB8(btle.DefaultDelegate):
def __init__(self, deviceAddress):
btle.DefaultDelegate.__init__(self)
# Address type must be "random" or it won't connect.
self.peripheral = btle.Peripheral(deviceAddress, btle.ADDR_TYPE_RANDOM)
self.peripheral.setDelegate(self)
self.seq = 0
# Attribute UUIDs are identical to Ollie.
self.antidos = self.getSpheroCharacteristic('2bbd')
self.wakecpu = self.getSpheroCharacteristic('2bbf')
self.txpower = self.getSpheroCharacteristic('2bb2')
self.roll = self.getSpheroCharacteristic('2ba1')
self.notify = self.getSpheroCharacteristic('2ba6')
# This startup sequence is also identical to the one for Ollie.
# It even uses the same unlock code.
print 'Sending antidos'
self.antidos.write('011i3', withResponse=True)
print 'Sending txpower'
self.txpower.write('\x0007', withResponse=True)
print 'Sending wakecpu'
self.wakecpu.write('\x01', withResponse=True)
def getSpheroCharacteristic(self, fragment):
return self.peripheral.getCharacteristics(uuid='22bb746f'+fragment+'75542d6f726568705327')[0]
def dumpCharacteristics(self):
for s in self.peripheral.getServices():
print s
for c in s.getCharacteristics():
print c, hex(c.handle)
def cmd(self, did, cid, data=[], answer=True, resetTimeout=True):
# Commands are as specified in Sphero API 1.50 PDF.
# https://github.com/orbotix/DeveloperResources/
seq = (self.seq&255)
self.seq += 1
sop2 = 0xfc
sop2 |= 1 if answer else 0
sop2 |= 2 if resetTimeout else 0
dlen = len(data)+1
chk = (sum(data)+did+cid+seq+dlen)&255
chk ^= 255
msg = [0xff, sop2, did, cid, seq, dlen] + data + [chk]
print 'cmd:', ' '.join([chr(c).encode('hex') for c in msg])
# Note: withResponse is very important. Most commands won't work without it.
self.roll.write(''.join([chr(c) for c in msg]), withResponse=True)
def handleNotification(self, cHandle, data):
print 'Notification:', cHandle, data.encode('hex')
def waitForNotifications(self, time):
self.peripheral.waitForNotifications(time)
def disconnect(self):
self.peripheral.disconnect()
if __name__ == '__main__':
# Connect by address. Use "sudo hcitool lescan" to find address.
bb = BB8('EE:D7:9A:A7:79:77')
# Dump all GATT stuff.
#bb.dumpCharacteristics()
# Request some sensor stream.
bb.cmd(0x02, 0x11, [0, 80, 0, 1, 0x80, 0, 0, 0, 0])
for i in range(255):
# Set RGB LED colour.
bb.cmd(0x02, 0x20, [254, i, 2, 0])
# Wait for streamed data.
bb.waitForNotifications(1.0)
# Must manually disconnect or you won't be able to reconnect.
bb.disconnect()
@rubricsinger

Hi Ali1234, hoping you are monitoring this. I am experimenting with bb-8 for a fun project with my daughter and am a bit new to python. I was trying to figure out how to make bb8 move by modifying a copy of your code. I understand that the API explains the message structure for rolling, but am having trouble translating what the API document says about 'Roll - 30h' and how to generate a bb.cmd string to match it. Can you put a tip in here as to what it might need to look like, say for example if i wanted it to go a short distance forward, or turn 180? thanks loads, it would help me learn this a lot quicker !

@ali1234
Owner
ali1234 commented Nov 1, 2015

Rather than modifying this code, you can reuse it by importing it into another source file in the same directory by doing:

from bb8 import BB8

I have to admit that I don't really understand the Roll command. But I managed to get it driving around using this code. This needs pygame to read the keyboard. Drive with the arrow keys. Down makes you go forwards faster.

#!/usr/bin/env python

import pygame

from bb8 import BB8

pygame.display.set_mode((320, 240))
c = pygame.time.Clock()

bb = BB8('EE:D7:9A:A7:79:77')
bb.cmd(0x02, 0x20, [0x10, 0x10, 0x10, 0])
bb.cmd(0x02, 0x21, [0xff])

keys = [False] * 1024
h = 0

while True:
    c.tick(10)
    events = pygame.event.get()
    for event in events:
        if event.type == pygame.KEYDOWN:
            keys[event.key] = True
        elif event.type == pygame.KEYUP:
            keys[event.key] = False

    if keys[pygame.K_UP]:
        v = 100
    elif keys[pygame.K_DOWN]:
        v = 255
    else:
        v = 0

    if keys[pygame.K_LEFT]:
        h -= 15
    elif keys[pygame.K_RIGHT]:
        h += 15
    while h<0:
        h += 360
    while h>359:
        h -= 360
    print h

    bb.cmd(0x02, 0x30, [v, (h&0xff00)>>8, h&0xff, 1])
@rubricsinger

That's great ! Thanks loads, we will have a lot of fun mucking about with this !

@jchadwhite

Ali1234, I took your btle code and tweaked it to work with a Sphero python library found at: https://github.com/mmwise/sphero_ros/tree/groovy-devel/sphero_driver/src/sphero_driver

Makes for easier coding as now you only need the command names and parameters.
Also I made a script for driving BB8 with a Xbox 360 controller

All that can be found here: https://github.com/jchadwhite/SpheroBB8-python

so thank you greatly for the btle work!

@snoozesecurity

Hello Ali, I have been playing around with this and it's a lot of fun so far. However, I can no longer appear to connect BB-8 to my phone. I have tried bb.disconnect in many different ways; I have also used hcitool ledc to no avail. Is there something else I need to be doing to tear down the connection sufficiently so I can pair it to the BB-8 app on my phone once more? Thanks!

@ali1234
Owner
ali1234 commented Dec 19, 2015

What can happen is that if the program crashes it does not tear down the connection. Try waiting for BB-8 to go to sleep, that should fix it.

@amfg
amfg commented Dec 24, 2015

Somehow this does not work on my BB8. I have a bad feeling about this. I am able to sense BB's mac via hcitool. Using gentoo with Bluez5. I can even see the droid I was looking for with bluetoothctl. However when I run this snippet (with correct mac), it seems it connects, everything runs, I get loads of output (expected), but no errors or exceptions. BB doesn't even blink. No reaction. The force isn't strong enough. With phone app it runs just fine. Any ideas? (sorry for the puns :()

@crvogt
crvogt commented Dec 30, 2015

Yeah, I'm having a similar issue of the phone not reconnecting afterwards. Does anyone know of a definite way of disconnecting at the end of the program such that the phone can connect later?

@redhowe
redhowe commented Jan 6, 2016

Hi all, unfortunately I am having the same issue as others where the BB8 will not allow a phone connection after connecting with the python driver. Advice please as allowing it to 'sleep' didn't do the trick here.

@NickLeBoeuf

Hello, did you try to press the small reset button on the base when bb8 is charging ? I did not yet test the python driver, but with this reset button i was able to pair/unpair different phones/tablets on my bb8. Hope this helps.

@redhowe
redhowe commented Jan 7, 2016

NickLeBoeuf - thanks! I had completely forgotten about that button which did the trick. Any idea what is causing the issue with switching back to other apps?

@WslF
WslF commented Sep 16, 2016

Hello Ali! Thanks for your code!!!
Does it work for ollie too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment