Skip to content

Instantly share code, notes, and snippets.

@ali1234
Created October 17, 2015 15:47
Show Gist options
  • Star 42 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save ali1234/5e5758d9c591090291d6 to your computer and use it in GitHub Desktop.
Save ali1234/5e5758d9c591090291d6 to your computer and use it in GitHub Desktop.
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
Copy link

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
Copy link
Author

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
Copy link

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

@jchadwhite
Copy link

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
Copy link

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
Copy link
Author

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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?

@WasylF
Copy link

WasylF commented Sep 16, 2016

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

Copy link

ghost commented Oct 18, 2017

good evening,

Congratulations on the code, I need to get information from the BB8 as the data of your sensors, IMU, accelerometer, gyroscope, can you do something like this?

@harmitbadyal
Copy link

Hello,
Thank you so much for the code!
I have an issue whenever I run the BB8test.py. Here is the output that comes up on my terminal. Any help would be greatly appreciated!

"File "BB8test.py", line 8, in <module>
    bb8.connect()
  File "/home/pi/Projects/SpheroBB8-python/BB8_driver.py", line 259, in connect
    self.bt = BTInterface(self.deviceAddress)
  File "/home/pi/Projects/SpheroBB8-python/BB8_driver.py", line 138, in __init__
    self.peripheral = btle.Peripheral(deviceAddress, btle.ADDR_TYPE_RANDOM)
  File "/usr/local/lib/python2.7/dist-packages/bluepy/btle.py", line 391, in __init__
    self._connect(deviceAddr, addrType, iface)
  File "/usr/local/lib/python2.7/dist-packages/bluepy/btle.py", line 439, in _connect
    raise BTLEDisconnectError("Failed to connect to peripheral %s, addr type: %s" % (addr, addrType), rsp)
bluepy.btle.BTLEDisconnectError: Failed to connect to peripheral D7:74:05:73:09:55, addr type: random

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