-
-
Save ali1234/5e5758d9c591090291d6 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
# BB-8 Python driver by Alistair Buxton <[email protected]> | |
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() |
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])
That's great ! Thanks loads, we will have a lot of fun mucking about with this !
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!
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!
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.
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 :()
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?
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.
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.
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?
Hello Ali! Thanks for your code!!!
Does it work for ollie too?
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?
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
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 !