├── BTDevice.py ├── HCIEvents.py ├── README └── bluetooth.py /BTDevice.py: -------------------------------------------------------------------------------- 1 | import serial,struct,binascii 2 | import time 3 | 4 | import os,sys 5 | from threading import Thread 6 | 7 | 8 | class keythread(Thread): 9 | def __init__(self,dev): 10 | self.BTDev=dev 11 | Thread.__init__(self) 12 | def run(self): 13 | while self.BTDev.ser.isOpen(): 14 | x=raw_input() 15 | if x=="d": 16 | self.BTDev.doDiscovery() 17 | if x=="e" and self.BTDev.foundDevices!={}: 18 | self.BTDev.doEstablishLink(0) 19 | if x=="t" and self.BTDev.connHandle!="": 20 | self.BTDev.doTerminateLink() 21 | if x=="1": 22 | self.BTDev.writeStack.append(self.BTDev.activateAccelerometer) 23 | self.BTDev.writeStack.append(self.BTDev.setUpZAccNotifications) 24 | self.BTDev.writeStack.append(self.BTDev.setUpXAccNotifications) 25 | self.BTDev.writeStack.append(self.BTDev.setUpYAccNotifications) 26 | self.BTDev.setUpButtNotifications() 27 | if x=="2": 28 | self.BTDev.writeStack.append(self.BTDev.deactNotificationForSensor) 29 | self.BTDev.writeStack.append(self.BTDev.deactNotificationForSensor) 30 | self.BTDev.writeStack.append(self.BTDev.deactNotificationForSensor) 31 | self.BTDev.writeStack.append(self.BTDev.deactivateAccelerometer) 32 | self.BTDev.deactNotificationForSensor() 33 | if len(x)>1: 34 | if x[0]=="c" and len(x)==6: # c UUID with UUID=192A for 2A19 35 | self.BTDev.discCharsByUUID(x[2:]) 36 | if x[0]=="r" and len(x)==6: # c handle with handle=2200 for 0x0022 37 | self.BTDev.readCharValue(x[2:]) 38 | if x[0]=="w": # w handle value [values] with handle=2200 for 0x0022 39 | args = x.split()[1:] 40 | if len(args)>1: 41 | self.BTDev.writeReq(args[0],args[1:]) 42 | else: 43 | print "Requires at least one value, e.g., w 2200 7" 44 | 45 | def sendNextPacket(self): 46 | print self.BTDev.writeStack 47 | if self.BTDev.writeStack != []: 48 | self.BTDev.writeStack.pop(0)() 49 | else: 50 | print "No Packets to send" 51 | 52 | 53 | class BTDevice(object): 54 | _shared = {} 55 | def __init__(self,ser): 56 | self.__dict__ = self._shared 57 | self.ser = ser 58 | deviceReady=0 59 | dongleAddress="\x00\x00\x00\x00\x00\x00\x00\x00" 60 | IRK="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 61 | CSRK="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 62 | foundDevices={} 63 | connHandle="" 64 | thread=Thread() 65 | writeStack=[] 66 | def doDiscovery(self): 67 | print "Doing Discovery" 68 | st='\x01' #command 69 | st=st+'\x04\xFE' # 0xFE GAP_DeviceDiscoveryRequest 70 | st=st+'\x03' # Datalength, well static ^^ 71 | st=st+'\x03' #Mode (all) 72 | st=st+'\x01' #Enable Name Mode 73 | st=st+'\x00' #Disable Whitelist 74 | self.ser.write(st) 75 | def doEstablishLink(self,device): 76 | print "Sending establish link request" 77 | st='\x01' #command 78 | st=st+'\x09\xfe' #0xfe09 gap_establishlinkrequest 79 | st=st+'\x09' #datalength 80 | st=st+'\x00' #Highdutycycle 81 | st=st+'\x00' #whitelist 82 | st=st+'\x00' #AddrTypePeer (our keyfob is 00) 83 | st=st+self.foundDevices[device]['BinAddr'] 84 | self.ser.write(st) 85 | def doTerminateLink(self): 86 | print "Sending terminate link request" 87 | st='\x01' #command 88 | st=st+'\x0A\xFE' #0xFE0A GAP Terminatelinkrequest 89 | st=st+'\x02' #data 90 | st=st+str(self.connHandle) #conn handle GetBY BLA!!!! 91 | self.ser.write(st) 92 | 93 | #wir setzen hier nur die bewegung in gang, HCIEvents wird den rest der pakete schicken parsen etc. 94 | def setUpXAccNotifications(self): 95 | st='\x01' #command 96 | st=st+'\x88\xFD' # 0xFD88 (GATT_DiscCharsByUUID) 97 | st=st+'\x08' #data length 98 | st=st+'\x00\x00' #connectionhandle 99 | st=st+'\x01\x00' #Starting handle 100 | st=st+'\xFF\xFF' #end handle 101 | st=st+'\xA3\xFF' #UUID we are searching for (X) 102 | self.ser.write(st) 103 | def setUpYAccNotifications(self): 104 | st='\x01' #command 105 | st=st+'\x88\xFD' # 0xFD88 (GATT_DiscCharsByUUID) 106 | st=st+'\x08' #data length 107 | st=st+'\x00\x00' #connectionhandle 108 | st=st+'\x01\x00' #Starting handle 109 | st=st+'\xFF\xFF' #end handle 110 | st=st+'\xA4\xFF' #UUID we are searching for (Y) 111 | self.ser.write(st) 112 | def setUpZAccNotifications(self): 113 | st='\x01' #command 114 | st=st+'\x88\xFD' # 0xFD88 (GATT_DiscCharsByUUID) 115 | st=st+'\x08' #data length 116 | st=st+'\x00\x00' #connectionhandle 117 | st=st+'\x01\x00' #Starting handle 118 | st=st+'\xFF\xFF' #end handle 119 | st=st+'\xA5\xFF' #UUID we are searching for (Z) 120 | self.ser.write(st) 121 | def setUpButtNotifications(self): 122 | st='\x01' #command 123 | st=st+'\x88\xFD' # 0xFD88 (GATT_DiscCharsByUUID) 124 | st=st+'\x08' #data length 125 | st=st+'\x00\x00' #connectionhandle 126 | st=st+'\x01\x00' #Starting handle 127 | st=st+'\xFF\xFF' #end handle 128 | st=st+'\xE1\xFF' #UUID we are searching for (Button) 129 | self.ser.write(st) 130 | 131 | def discCharsByUUID(self,UUID): 132 | st='\x01' #command 133 | st=st+'\x88\xFD' # 0xFD88 (GATT_DiscCharsByUUID) 134 | st=st+'\x08' #data length 135 | st=st+'\x00\x00' #connectionhandle 136 | st=st+'\x01\x00' #Starting handle 137 | st=st+'\xFF\xFF' #end handle 138 | st=st+binascii.a2b_hex(UUID) #UUID we are searching for 139 | self.ser.write(st) 140 | 141 | def writeReq(self,handle,value): 142 | #Write Command 143 | st = '\x01' #command 144 | st = st+'\x12\xFD' #0xFD12 (ATT_WriteReq) 145 | st = st+chr(6+len(value)) #datalength 146 | st = st+self.connHandle #handle 147 | st = st+'\x00' #Signature off 148 | st = st+'\x00' #command off 149 | st = st+binascii.a2b_hex(handle) #attribute Address 150 | for i in value: 151 | st = st+struct.pack('B',int(i)) #AttrValue 152 | self.ser.write(st) 153 | 154 | def readCharValue(self,handle): 155 | st='\x01' #command 156 | st=st+'\x8A\xFD' # 0xFD88 (GATT_ReadCharValue) 157 | st=st+'\x04' #data length 158 | st=st+'\x00\x00' #connectionhandle 159 | st=st+binascii.a2b_hex(handle) #handle we are searching for 160 | self.ser.write(st) 161 | 162 | def activateAccelerometer(self): 163 | #Write Command 164 | st = '\x01' #command 165 | st = st+'\x12\xFD' #0xFD12 (ATT_WriteReq) 166 | st = st+'\x07' #datalength 167 | st = st+self.connHandle #handle 168 | st = st+'\x00' #Signature off 169 | st = st+'\x00' #command off 170 | st = st+'\x33\x00' #CHANGED from \x21\x00! attribute Address 171 | st = st+'\x01' #AttrValue 172 | self.ser.write(st) 173 | def deactivateAccelerometer(self): 174 | #Write Command 175 | st = '\x01' #command 176 | st = st+'\x12\xFD' #0xFD12 (ATT_WriteReq) 177 | st = st+'\x07' #datalength 178 | st = st+self.connHandle #handle 179 | st = st+'\x00' #Signature off 180 | st = st+'\x00' #command off 181 | st = st+'\x33\x00' #attribute Address 182 | st = st+'\x00' #AttrValue 183 | self.ser.write(st) 184 | notificationAttributeAddresses=[] 185 | #notificationAttributeAddresses2=['\x28\x00','\x2C\x00','\x30\x00','\x28\x00'] 186 | def setUpNotificationForSensor(self): 187 | #Write Command 188 | st = '\x01' #command 189 | st = st+'\x12\xFD' #0xFD12 (ATT_WriteReq) 190 | st = st+'\x08' #datalength 191 | st = st+self.connHandle #handle 192 | st = st+'\x00' #Signature off 193 | st = st+'\x00' #command off 194 | #x=self.notificationAttributeAddresses2.pop() 195 | x=self.notificationAttributeAddresses.pop() 196 | self.notificationAttributeAddressesAct.append(x) 197 | print x 198 | st = st+x#'\x28\x00' #attribute Address 199 | st = st+'\x01\x00' #AttrValue 200 | self.ser.write(st) 201 | notificationAttributeAddressesAct=[] 202 | def deactNotificationForSensor(self): 203 | print self.notificationAttributeAddressesAct 204 | if self.notificationAttributeAddressesAct == []: 205 | self.thread.sendNextPacket() 206 | return 207 | #Write Command 208 | st = '\x01' #command 209 | st = st+'\x12\xFD' #0xFD12 (ATT_WriteReq) 210 | st = st+'\x08' #datalength 211 | st = st+self.connHandle #handle 212 | st = st+'\x00' #Signature off 213 | st = st+'\x00' #command off 214 | x=self.notificationAttributeAddressesAct.pop() 215 | print x 216 | st = st+x#'\x28\x00' #attribute Address 217 | st = st+'\x00\x00' #AttrValue 218 | self.ser.write(st) 219 | -------------------------------------------------------------------------------- /HCIEvents.py: -------------------------------------------------------------------------------- 1 | import serial 2 | from BTDevice import BTDevice,keythread 3 | import struct 4 | import binascii 5 | 6 | 7 | class HCIEvents: 8 | def nomatch(self,i,bt,device): 9 | print "no match found" 10 | 11 | def do_process_att_writeResponse_event(self,i,bt,device): 12 | if bt.read()=='\x00': 13 | print "Write succeeded" 14 | bt.read(3) 15 | device.thread.sendNextPacket() 16 | else: 17 | print "Write failed" 18 | 19 | def do_process_att_readbytypeResponse_event(self,length,bt,device): #we also use this for setting up notifications 20 | i=bt.read() 21 | if i=='\x1A': 22 | print "ATT ReadbyTypeResponse A1" 23 | #device is just telling me that search has ended 24 | #so lets read the rest 25 | bt.read(size=3) 26 | device.thread.sendNextPacket() 27 | elif i=='\x00': 28 | P=struct.unpack(' python-serial in ubuntu 3 | 4 | 1. ls /dev 5 | 2. if it's linux look for ttyACMX, it it's MAC look for tty.usbmodemXXX 6 | 3. write it into bluetooth.py (default is ttyACM0 for linux, and tty.usbmodem471 for MAC-Darwin) 7 | 4. start with "python bluetooth.py" 8 | 5. type d for discovery 9 | 6. type e for establishing the link 10 | 7. type 1 for activating all notifications 11 | 8. enjoy and type 2 then t for terminating the link 12 | 13 | 2 deactivates the notifications 14 | c 192A discovers the characteristic with UUID 0x2A19 15 | r 3300 reads the value at the handle 0x0033 16 | w 3300 1 writes 1 at the handle 0x0033 17 | -------------------------------------------------------------------------------- /bluetooth.py: -------------------------------------------------------------------------------- 1 | #Main 2 | import serial 3 | from HCIEvents import HCIEvents 4 | from BTDevice import BTDevice,keythread 5 | import struct 6 | 7 | import os,sys,platform 8 | from threading import Thread 9 | 10 | def initserial(): 11 | bt = serial.Serial() 12 | if os.name == 'posix': 13 | if platform.system() == 'Darwin': 14 | bt.port = "/dev/tty.usbmodem471" 15 | else: 16 | bt.port = "/dev/ttyACM0" 17 | 18 | else: 19 | bt.port = "COM3" 20 | bt.baudrate = 57600 21 | bt.open() 22 | return bt 23 | 24 | def initdevice(bt): 25 | str = '\x01' #command 26 | str = str+'\x00\xFE' #GAP_DeviceInit 27 | str = str+struct.pack('B',struct.calcsize('BB16s16sL'))+struct.pack('BB16s16sL',8,3,'\x00','\x00',1) #ProfileRole,MaxScanRsp,IRK,CSRK,SignCounter 28 | bt.write(str) 29 | print "Sent device init command!" 30 | 31 | bt = initserial() 32 | dev = BTDevice(bt) 33 | print("Connected to Dongle") 34 | initdevice(bt) 35 | print "" 36 | print("Starting Read loop") 37 | 38 | 39 | #useless key thread :) 40 | 41 | thr = keythread(dev) 42 | thr.start() 43 | dev.thread=thr 44 | # 45 | 46 | while(bt.isOpen()): #Neues DatenPAKET wird gelesen 47 | HCI_Packet_Type = bt.read() 48 | print("\t======================") 49 | if HCI_Packet_Type == '\x04': #verzweigungen... hier event 50 | EVENT_CODE=bt.read() 51 | if EVENT_CODE=='\xFF': 52 | print "\tFound Vendor Specific Event Code" 53 | X=bt.read(size=3)#enthaelt auch opcode 54 | DATA_LENGTH = struct.unpack('