class RileyLink(PacketRadio): def __init__(self): self.peripheral = None self.pa_level_index = PA_LEVELS.index(0x84) self.data_handle = None self.logger = getLogger() self.address = None if os.path.exists(RILEYLINK_MAC_FILE): with open(RILEYLINK_MAC_FILE, "r") as stream: self.address = stream.read() self.service = None self.response_handle = None self.notify_event = Event() self.initialized = False def connect(self, force_initialize=False): try: if self.address is None: self.address = self._findRileyLink() if self.peripheral is None: self.peripheral = Peripheral() try: state = self.peripheral.getState() if state == "conn": return except BTLEException: pass self._connect_retry(3) self.service = self.peripheral.getServiceByUUID(RILEYLINK_SERVICE_UUID) data_char = self.service.getCharacteristics(RILEYLINK_DATA_CHAR_UUID)[0] self.data_handle = data_char.getHandle() char_response = self.service.getCharacteristics(RILEYLINK_RESPONSE_CHAR_UUID)[0] self.response_handle = char_response.getHandle() response_notify_handle = self.response_handle + 1 notify_setup = b"\x01\x00" self.peripheral.writeCharacteristic(response_notify_handle, notify_setup) while self.peripheral.waitForNotifications(0.05): self.peripheral.readCharacteristic(self.data_handle) if self.initialized: self.init_radio(force_initialize) else: self.init_radio(True) except BTLEException: if self.peripheral is not None: self.disconnect() raise def disconnect(self, ignore_errors=True): try: if self.peripheral is None: self.logger.info("Already disconnected") return self.logger.info("Disconnecting..") if self.response_handle is not None: response_notify_handle = self.response_handle + 1 notify_setup = b"\x00\x00" self.peripheral.writeCharacteristic(response_notify_handle, notify_setup) except BTLEException: if not ignore_errors: raise finally: try: if self.peripheral is not None: self.peripheral.disconnect() self.peripheral = None except BTLEException: if ignore_errors: self.logger.exception("Ignoring btle exception during disconnect") else: raise def get_info(self): try: self.connect() bs = self.peripheral.getServiceByUUID(XGATT_BATTERYSERVICE_UUID) bc = bs.getCharacteristics(XGATT_BATTERY_CHAR_UUID)[0] bch = bc.getHandle() battery_value = int(self.peripheral.readCharacteristic(bch)[0]) self.logger.debug("Battery level read: %d", battery_value) version, v_major, v_minor = self._read_version() return { "battery_level": battery_value, "mac_address": self.address, "version_string": version, "version_major": v_major, "version_minor": v_minor } except BTLEException as btlee: raise PacketRadioError("Error communicating with RileyLink") from btlee finally: self.disconnect() def _read_version(self): version = None try: if os.path.exists(RILEYLINK_VERSION_FILE): with open(RILEYLINK_VERSION_FILE, "r") as stream: version = stream.read() else: response = self._command(Command.GET_VERSION) if response is not None and len(response) > 0: version = response.decode("ascii") self.logger.debug("RL reports version string: %s" % version) try: with open(RILEYLINK_VERSION_FILE, "w") as stream: stream.write(version) except IOError: self.logger.exception("Failed to store version in file") if version is None: return "0.0", 0, 0 try: m = re.search(".+([0-9]+)\\.([0-9]+)", version) if m is None: raise PacketRadioError("Failed to parse firmware version string: %s" % version) v_major = int(m.group(1)) v_minor = int(m.group(2)) self.logger.debug("Interpreted version major: %d minor: %d" % (v_major, v_minor)) return version, v_major, v_minor except Exception as ex: raise PacketRadioError("Failed to parse firmware version string: %s" % version) from ex except IOError: self.logger.exception("Error reading version file") except PacketRadioError: raise response = self._command(Command.GET_VERSION) if response is not None and len(response) > 0: version = response.decode("ascii") self.logger.debug("RL reports version string: %s" % version) try: m = re.search(".+([0-9]+)\\.([0-9]+)", version) if m is None: raise PacketRadioError("Failed to parse firmware version string: %s" % version) v_major = int(m.group(1)) v_minor = int(m.group(2)) self.logger.debug("Interpreted version major: %d minor: %d" % (v_major, v_minor)) return (version, v_major, v_minor) except PacketRadioError: raise except Exception as ex: raise PacketRadioError("Failed to parse firmware version string: %s" % version) from ex def init_radio(self, force_init=False): try: version, v_major, v_minor = self._read_version() if v_major < 2: self.logger.error("Firmware version is below 2.0") raise PacketRadioError("Unsupported RileyLink firmware %d.%d (%s)" % (v_major, v_minor, version)) if not force_init: if v_major == 2 and v_minor < 3: response = self._command(Command.READ_REGISTER, bytes([Register.SYNC1, 0x00])) else: response = self._command(Command.READ_REGISTER, bytes([Register.SYNC1])) if response is not None and len(response) > 0 and response[0] == 0xA5: return self._command(Command.RADIO_RESET_CONFIG) self._command(Command.SET_SW_ENCODING, bytes([Encoding.MANCHESTER])) frequency = int(433910000 / (24000000 / pow(2, 16))) self._command(Command.SET_PREAMBLE, bytes([0x66, 0x65])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ0, frequency & 0xff])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ1, (frequency >> 8) & 0xff])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ2, (frequency >> 16) & 0xff])) self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL1, 0x20])) self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL0, 0x00])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCTRL1, 0x06])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG4, 0xCA])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG3, 0xBC])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG2, 0x06])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG1, 0x70])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG0, 0x11])) self._command(Command.UPDATE_REGISTER, bytes([Register.DEVIATN, 0x44])) self._command(Command.UPDATE_REGISTER, bytes([Register.MCSM0, 0x18])) self._command(Command.UPDATE_REGISTER, bytes([Register.FOCCFG, 0x17])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL3, 0xE9])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL2, 0x2A])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL1, 0x00])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL0, 0x1F])) self._command(Command.UPDATE_REGISTER, bytes([Register.TEST1, 35])) self._command(Command.UPDATE_REGISTER, bytes([Register.TEST0, 0x09])) self._command(Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREND0, 0x00])) self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC1, 0xA5])) self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC0, 0x5A])) response = self._command(Command.GET_STATE) if response != b"OK": raise PacketRadioError("Rileylink state is not OK. Response returned: %s" % response) self.initialized = True except PacketRadioError as rle: self.logger.error("Error while initializing rileylink radio: %s", rle) raise def tx_up(self): if self.pa_level_index < len(PA_LEVELS) - 1: self.pa_level_index += 1 self._set_amp(self.pa_level_index) def tx_down(self): if self.pa_level_index > 0: self.pa_level_index -= 1 self._set_amp(self.pa_level_index) def set_tx_power(self, tx_power): if tx_power is None: return elif tx_power == TxPower.Lowest: self._set_amp(0) elif tx_power == TxPower.Low: self._set_amp(PA_LEVELS.index(0x12)) elif tx_power == TxPower.Normal: self._set_amp(PA_LEVELS.index(0x60)) elif tx_power == TxPower.High: self._set_amp(PA_LEVELS.index(0xC8)) elif tx_power == TxPower.Highest: self._set_amp(PA_LEVELS.index(0xC0)) def get_packet(self, timeout=5.0): try: self.connect() return self._command(Command.GET_PACKET, struct.pack(">BL", 0, int(timeout * 1000)), timeout=float(timeout)+0.5) except PacketRadioError as rle: self.logger.error("Error while receiving data: %s", rle) raise def send_and_receive_packet(self, packet, repeat_count, delay_ms, timeout_ms, retry_count, preamble_ext_ms): try: self.connect() return self._command(Command.SEND_AND_LISTEN, struct.pack(">BBHBLBH", 0, repeat_count, delay_ms, 0, timeout_ms, retry_count, preamble_ext_ms) + packet, timeout=30) except PacketRadioError as rle: self.logger.error("Error while sending and receiving data: %s", rle) raise def send_packet(self, packet, repeat_count, delay_ms, preamble_extension_ms): try: self.connect() result = self._command(Command.SEND_PACKET, struct.pack(">BBHH", 0, repeat_count, delay_ms, preamble_extension_ms) + packet, timeout=30) return result except PacketRadioError as rle: self.logger.error("Error while sending data: %s", rle) raise def _set_amp(self, index=None): try: self.connect() if index is not None: self.pa_level_index = index self._command(Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]])) self.logger.debug("Setting pa level to index %d of %d" % (self.pa_level_index, len(PA_LEVELS))) except PacketRadioError: self.logger.exception("Error while setting tx amplification") raise def _findRileyLink(self): scanner = Scanner() found = None self.logger.debug("Scanning for RileyLink") retries = 10 while found is None and retries > 0: retries -= 1 for result in scanner.scan(1.0): if result.getValueText(7) == RILEYLINK_SERVICE_UUID: self.logger.debug("Found RileyLink") found = result.addr try: with open(RILEYLINK_MAC_FILE, "w") as stream: stream.write(result.addr) except IOError: self.logger.warning("Cannot store rileylink mac radio_address for later") break if found is None: raise PacketRadioError("Could not find RileyLink") return found def _connect_retry(self, retries): while retries > 0: retries -= 1 self.logger.info("Connecting to RileyLink, retries left: %d" % retries) try: self.peripheral.connect(self.address) self.logger.info("Connected") break except BTLEException as btlee: self.logger.warning("BTLE exception trying to connect: %s" % btlee) try: p = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE) out, err = p.communicate() for line in out.splitlines(): if "bluepy-helper" in line: pid = int(line.split(None, 1)[0]) os.kill(pid, 9) break except: self.logger.warning("Failed to kill bluepy-helper") time.sleep(1) def _command(self, command_type, command_data=None, timeout=10.0): try: if command_data is None: data = bytes([1, command_type]) else: data = bytes([len(command_data) + 1, command_type]) + command_data self.peripheral.writeCharacteristic(self.data_handle, data, withResponse=True) if not self.peripheral.waitForNotifications(timeout): raise PacketRadioError("Timed out while waiting for a response from RileyLink") response = self.peripheral.readCharacteristic(self.data_handle) if response is None or len(response) == 0: raise PacketRadioError("RileyLink returned no response") else: if response[0] == Response.COMMAND_SUCCESS: return response[1:] elif response[0] == Response.COMMAND_INTERRUPTED: self.logger.warning("A previous command was interrupted") return response[1:] elif response[0] == Response.RX_TIMEOUT: return None else: raise PacketRadioError("RileyLink returned error code: %02X. Additional response data: %s" % (response[0], response[1:]), response[0]) except Exception as e: raise PacketRadioError("Error executing command") from e
def trigger_switchbot(): p = Peripheral(MAC_ADDR, "random") hand_service = p.getServiceByUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b") hand = hand_service.getCharacteristics( "cba20002-224d-11e6-9fb8-0002a5d5c51b")[0] hand.write(binascii.a2b_hex("570100")) p.disconnect()
def turn_off(self, **kwargs): """Turn device off.""" for connection in range(1, 6): try: p = Peripheral(self._mac, "random") except: _LOGGER.error('Connection attempt failed after %s tries' % connection) time.sleep(10) continue break else: _LOGGER.error('Connection to Switchbot failed after max attempts') try: # p = Peripheral(self._mac, "random") hand_service = p.getServiceByUUID( "cba20d00-224d-11e6-9fb8-0002a5d5c51b") hand = hand_service.getCharacteristics( "cba20002-224d-11e6-9fb8-0002a5d5c51b")[0] hand.write(binascii.a2b_hex("570102")) p.disconnect() self._state = False except: _LOGGER.error("Cannot connect to switchbot.")
class WavePlus(): def __init__(self, MAC_ADDR): self.periph = None self.curr_val_char = None self.MacAddr = MAC_ADDR self.uuid = UUID("b42e4dcc-ade7-11e4-89d3-123b93f75cba") def connect(self): # Connect to device if (self.periph is None): self.periph = Peripheral(self.MacAddr) if (self.curr_val_char is None): self.curr_val_char = self.periph.getCharacteristics( uuid=self.uuid)[0] def read(self): if (self.curr_val_char is None): print("ERROR: Devices are not connected.") sys.exit(1) rawdata = self.curr_val_char.read() rawdata = struct.unpack('BBBBHHHHHHHH', rawdata) sensors = Sensors() sensors.set(rawdata) return sensors def disconnect(self): if self.periph is not None: self.periph.disconnect() self.periph = None self.curr_val_char = None
class RobotController(): def __init__(self, address): # Connect to the Romi over BLE self.robot = Peripheral(addr) print("connected") # Get service from robot self.sv = self.robot.getServiceByUUID(SERVICE_UUID) # Get characteristic handles from service/robot self.waypoint_cmd = self.sv.getCharacteristics(WAYPOINT_CMD_UUID)[0] self.ack = self.sv.getCharacteristics(ACK_UUID)[0] while True: # Check to see if the robot is ready to recieve a command ack, = struct.unpack("i", self.ack.read()) if ack == 0: # The robot is ready to recieve a command # Get command as input from the user r = float(input("Distance (m): ")) t = float(input("Angle (degrees): ")) # Write to the appropriate characteristic # The waypoint command is a sequence of two floats, representing a polar coordiantes format self.waypoint_cmd.write(struct.pack('ff', *[r, t])) # Tell the robot that we want it to start executing our command self.ack.write(struct.pack('Bxxx', *[ack + 1])) # Give the robot some time to update the characteristic value before we read it again time.sleep(1) def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.robot.disconnect()
def run(self): while True: if not switchqueue.empty(): try: item = switchqueue.get() switch = item['on'] if switch: val = '\x01' else: val = '\x00' macaddress = self.convertMac(item['macaddress']) device = Peripheral(macaddress, ADDR_TYPE_RANDOM, int(os.environ['SCAN_HCI'])) if item['newFirmware'] is False: notifications = NotificationDelegate() device.setDelegate(notifications) auth_key = unhexlify(item['authkey']) device.writeCharacteristic(STATE_NOTIFY_HANDLE, NOTIFY_VALUE, True) device.writeCharacteristic( STATE_HANDLE, sign('\x01' + val, auth_key)) while True: if device.waitForNotifications(1.0): device.disconnect() print('Ending session') break print('Waiting for notification. Old Firmware.') else: # new firmware. No notifcation and no auth device.writeCharacteristic(NEW_STATE_HANDLE, val, True) device.disconnect() except Exception as e: print(e) time.sleep(1)
def __get_raw_data(self,force_update = False): if self.__address is None: return starttime = int(time.time()) if force_update or starttime - self.__cached_data['last_update'] > terrariumMiFloraSensor.__CACHE_TIMEOUT: try: miflora_dev = Peripheral(self.__address) #Read battery and firmware version attribute self.__cached_data['battery'], self.__cached_data['firmware'] = unpack('<xB5s',miflora_dev.readCharacteristic(terrariumMiFloraSensor.__MIFLORA_FIRMWARE_AND_BATTERY)) #Enable real-time data reading miflora_dev.writeCharacteristic(terrariumMiFloraSensor.__MIFLORA_REALTIME_DATA_TRIGGER, str(bytearray([0xa0, 0x1f])), True) #Read plant data self.__cached_data['temperature'], self.__cached_data['light'], self.__cached_data['moisture'], self.__cached_data['fertility'] = unpack('<hxIBHxxxxxx',miflora_dev.readCharacteristic(terrariumMiFloraSensor.__MIFLORA_GET_DATA)) # temperature, sunlight, moisture, fertility = unpack('<hxIBHxxxxxx',miflora_dev.readCharacteristic(53)) # Close connection... miflora_dev.disconnect() self.__cached_data['last_update'] = starttime except Exception, ex: print ex
def callback(msg): p = Peripheral("C4:D4:53:AB:ED:5B", "random") hand_service = p.getServiceByUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b") hand = hand_service.getCharacteristics( "cba20002-224d-11e6-9fb8-0002a5d5c51b")[0] hand.write(binascii.a2b_hex("570100")) p.disconnect()
def run(self): while True: if not switchqueue.empty(): try: item = switchqueue.get() switch = item['on'] macaddress = self.convertMac(item['macaddress']) auth_key = unhexlify(item['authkey']) notifications = NotificationDelegate() device = Peripheral(macaddress, ADDR_TYPE_RANDOM) device.setDelegate(notifications) device.writeCharacteristic(STATE_NOTIFY_HANDLE, NOTIFY_VALUE, True) device.writeCharacteristic( STATE_HANDLE, sign('\x01' + ('\x00', '\x01')[switch], auth_key)) while True: if device.waitForNotifications(1.0): print('Ending session') break print('Waiting for notification') device.disconnect() except Exception as e: print(e) time.sleep(1)
def write_state(self, value): from bluepy.btle import Peripheral device = None try: # connect to device device = Peripheral(self._mac) # send PIN code auth device.writeCharacteristic( 0x3c, self._pin.to_bytes(4, byteorder='little'), True) _LOGGER.info("Auth success for {}".format(self._mac)) # set date+time now = datetime.now() device.writeCharacteristic( 0x25, bytes([ now.minute, now.hour, now.day, now.month, now.year - 2000 ])) # handle any outstanding value updates if value: device.writeCharacteristic(0x35, value, True) # writing something here does a "commit" device.writeCharacteristic(0x3a, bytes([randint(0, 255)])) _LOGGER.info("Updated switch to {}".format(value[0])) self._state = (device.readCharacteristic(0x35)[0] & 8 > 0) except Exception as e: _LOGGER.error("Exception: %s ", str(e)) finally: if device is not None: device.disconnect()
def update(self, force_update=False): if force_update or (self._last_update is None) or (datetime.now() - self._min_update_inteval > self._last_update): self._lock.acquire() try: p = Peripheral(self._mac, "public") chList = p.getCharacteristics() for ch in chList: if str(ch.uuid) == SERVICE_UUID: if p.writeCharacteristic(ch.getHandle(), b'\x55\x03' + bytes([self.get_sequence()]) + b'\x11', True): data = ch.read() humihex = data[6:8] temphex = data[4:6] temp10 = int.from_bytes(temphex, byteorder='little') humi10 = int.from_bytes(humihex, byteorder='little') self._temperature = float(temp10) / 100.0 self._humidity = float(humi10) / 100.0 if p.writeCharacteristic(ch.getHandle(), b'\x55\x03' + bytes([self.get_sequence()]) + b'\x10', True): data = ch.read() battery10 = data[4] self._battery = float(battery10) / 10.0 break p.disconnect() except Exception as ex: print("Unexpected error: {}".format(ex)) p.disconnect() finally: self._lock.release()
def get_service_value(addr: str, addr_type: str, offset: str): """ Gets the value from a Blue Tooth Low Energy device. Arguments: addr {string} -- The address to get the value from add_type {string} -- The type of address we are using. offset {string} -- The offset from the device's address to get the value from Returns: {int} -- The result of the fetch """ # Generate fake values for debugging # and for the development of the visuals. if not IS_LINUX: return 0 try: p = Peripheral(addr, addr_type) ch_all = p.getCharacteristics(uuid=offset) if ch_all[0].supportsRead(): res = ch_all[0].read() p.disconnect() return ord(res) except Exception as ex: print(" ex in get_name={}".format(ex)) return None
def readData (self, mqttClient): print("reading data from device " + self.__mac) sys.stdout.flush() p = Peripheral(self.__mac, iface=HCI_INTERFACE) p.withDelegate(self.__delegate) try: battery = p.readCharacteristic(BATTERY_HANDLE) self.__lastBattery = battery[0] except: print("failed to read battery from " + self.__mac) sys.stdout.flush() p.writeCharacteristic(TEMP_HUM_WRITE_HANDLE, TEMP_HUM_WRITE_VALUE) if not p.waitForNotifications(3.0): print("failed to read data from " + self.__mac) sys.stdout.flush() try: p.disconnect() except: pass print("read data from " + self.__mac + " " + str(self.__lastTemp) + "," + str(self.__lastHum) + "," + str(self.__lastBattery)) sys.stdout.flush() msg =\ '{'\ '"idx" : ' + str(self.__id) + ','\ '"nvalue" : 0,'\ '"svalue" : "' + str(self.__lastTemp) + ';' + str(self.__lastHum) + ';0",'\ '"Battery" : ' + str(self.__lastBattery) + ' '\ '}' mqttClient.publish(DEFAULT_IN_TOPIC, msg);
class BluetoothManager: def __init__(self, mac=None): if mac is None: mac = '00:00:00:00:00' # ~ Asumiendo la MAC del ESP32 self.uuid = mac self.Open() def __async(self): while True: if self.per.waitForNotifications(1.0): # ~ Deberia ir al delegate continue print("Esperando...") def Open(self): self.per = Peripheral(self.uuid, ADDR_TYPE_RANDOM) # ~ Servicio definido en el ESP32 self.srvc = self.per.getServiceByUUID('') # ~ Caracteristica definida self.esp_char = self.srvc.getCharacteristics('')[0] self.per.setDelegate(DelegateNotification(self.esp_char.getHandle())) def Send(self, trama): self.esp_char.write(trama) def Close(self): self.per.disconnect()
def get_service_value(addr, addr_type, offset): """ Gets the value from a Blue Tooth Low Energy device. Arguments: addr {string} -- The address to get the value from add_type {string} -- The type of address we are using. offset {string} -- The offset from the device's address to get the value from Returns: {int} -- The result of the fetch """ # Generate fake values for debugging # and for the development of the visuals. if not IS_LINUX: if offset in CO_OFFSET: return int(aithre_co_simulator.get_value()) else: return int(aithre_bat_simulator.get_value()) try: p = Peripheral(addr, addr_type) # bluepy.btle.ADDR_TYPE_PUBLIC) ch_all = p.getCharacteristics(uuid=offset) if ch_all[0].supportsRead(): res = ch_all[0].read() p.disconnect() return ord(res) except Exception as ex: print(" ex in get_name={}".format(ex)) return None
def Dotwatch_Ble(name): print("BLE connecting....") p = Peripheral(DEVICE, "random") LedService = p.getServiceByUUID(led_service_uuid) print(LedService) ch = LedService.getCharacteristics(led_char_uuid)[0] print(ch) print("BLE connected") t = threading.currentThread() while getattr(t, "do_run", True): ch.write(struct.pack('BBB', 0x25, 0x01, 0x00)) # all up time.sleep(0.5) for char in name: #ch.write(struct.pack('BBBB', 0x0A,0x02,0x01,0x00)) #print(type(char[0])) ch.write( struct.pack('BBBBBBBB', 0x06, 0x12, 0x01, 0x01, char[0], char[1], char[2], char[3])) #print ("write : ",binascii.b2a_hex(ch.read())) if not getattr(t, "do_run", True): break time.sleep(8) #finally: #continue print("stop print dotwotch") ch.write(struct.pack('BBB', 0x26, 0x01, 0x00)) # all down p.disconnect() print("BLE disconnected")
def putimage(addr, image): # line below needs to be changed to use your mugs MAC address try: peripheral = Peripheral(addr, addrType=ADDR_TYPE_RANDOM) print("connected to muki") # line below uses characteristics directly. you can also request it characteristic = peripheral.getCharacteristics( uuid='06640002-9087-04a8-658f-ce44cb96b4a1')[0] print("writing image to device") characteristic.write(bytes.fromhex('74'), withResponse=True) # Write image in 291 chunks, 20 bytes at time index = 0 for i in range(0, 291): data = image[index:index + 20] # last one may be too short while len(data) < 20: data.append(0xFF) characteristic.write(data, withResponse=False) index = index + 20 # this one is write without response because it fails if response # is required. Image is written anyway characteristic.write(bytes.fromhex('64'), withResponse=True) peripheral.disconnect() except (BTLEException): print("Write failed") return False print("write successful") return True
class BluepyBackend(AbstractBackend): def __init__(self, adapter='hci0'): super(self.__class__, self).__init__(adapter) self._peripheral = None def connect(self, mac): from bluepy.btle import Peripheral self._peripheral = Peripheral(mac) def disconnect(self): self._peripheral.disconnect() self._peripheral = None def read_handle(self, handle): if self._peripheral is None: raise BluetoothBackendException('not connected to backend') return self._peripheral.readCharacteristic(handle) def write_handle(self, handle, value): if self._peripheral is None: raise BluetoothBackendException('not connected to backend') return self._peripheral.writeCharacteristic(handle, value, True) def check_backend(self): try: import bluepy.btle # noqa: F401 except ImportError: raise BluetoothBackendException('bluepy not found')
class Led: currentColor = '#FF00FF' currentBrightness = 100 def __init__(self, address, uuid, characteristic): self.uuid = uuid self.address = address self.peripheral = Peripheral(address) self.service = self.peripheral.getServiceByUUID(uuid) self.device = self.service.getCharacteristics(characteristic)[0] self.set_brightness(self.currentBrightness) self.set_color(self.currentColor) # Set brightness intensity in percent def set_brightness(self, b): self.currentBrightness = b self.device.write(bytearray(brightness(b))) # Set color in hexadecimal format (ex: #FF00FF) def set_color(self, c): self.currentColor = c self.device.write(bytearray(color(c))) def stop(self): self.peripheral.disconnect()
def push(address): p = Peripheral(address, 'random') hand_service = p.getServiceByUUID('cba20d00-224d-11e6-9fb8-0002a5d5c51b') hand = hand_service.getCharacteristics( 'cba20002-224d-11e6-9fb8-0002a5d5c51b')[0] hand.write(binascii.a2b_hex('570100')) p.disconnect()
def disconnect(self): if (hasattr(self, 'enabled_notifs')): for n in self.enabled_notifs: print("Disabling %s service notifications status..." % n) getattr(self, 'cccd_' + n).write(b"\x00\x00", True) self.enabled_notifs.remove(n) Peripheral.disconnect(self)
def read_from_ble_server(mac_address, characteristic_uuid, struct_unpack_type=None): ''' Connecting to BLE server and reads characteristic by UUID Keyword arguments: mac_address -- MAC Address of BLE server characteristic_uuid -- UUID of characteristic to read value from struct_unpack_type -- char ( https://docs.python.org/3.7/library/struct.html#format-characters ) Returns: string ''' print(mac_address) print("+connecting...") characteristic_uuid = UUID(characteristic_uuid) val = None try: time.sleep(1) peripheral = Peripheral(mac_address, "public") time.sleep(1) val = read_characterictic(peripheral, characteristic_uuid, struct_unpack_type) time.sleep(1) peripheral.disconnect() print("+val: " + str(val)) finally: return val
def specific_scan(addr: str): try: dev = Peripheral(deviceAddr=addr) print_dev(dev) dev.disconnect() except BTLEException as e: print(f'Could not read device: {e}')
class BluepyBackend(AbstractBackend): """Backend for Miflora using the bluepy library.""" def __init__(self, adapter='hci0'): """Create new instance of the backend.""" super(self.__class__, self).__init__(adapter) self._peripheral = None def connect(self, mac): """Connect to a device.""" from bluepy.btle import Peripheral m = re.search(r'hci([\d]+)', self.adapter) if m is None: raise ValueError('Invalid pattern "{}" for BLuetooth adpater. ' 'Expetected something like "hci0".'.format( self.adapter)) iface = int(m.group(1)) self._peripheral = Peripheral(mac, iface=iface) def disconnect(self): """Disconnect from a device.""" self._peripheral.disconnect() self._peripheral = None def read_handle(self, handle): """Read a handle from the device. You must be connected to do this. """ if self._peripheral is None: raise BluetoothBackendException('not connected to backend') return self._peripheral.readCharacteristic(handle) def write_handle(self, handle, value): """Write a handle from the device. You must be connected to do this. """ if self._peripheral is None: raise BluetoothBackendException('not connected to backend') return self._peripheral.writeCharacteristic(handle, value, True) def check_backend(self): """Check if the backend is available.""" try: import bluepy.btle # noqa: F401 except ImportError: raise BluetoothBackendException('bluepy not found') @staticmethod def scan_for_devices(timeout): """Scan for bluetooth low energy devices. Note this must be run as root!""" from bluepy.btle import Scanner scanner = Scanner() result = [] for device in scanner.scan(timeout): result.append((device.addr, device.getValueText(9))) return result
def startPWS(addr, ssid, psk): logger.info(f"Connect to device {addr}") grantorHandler = GrantorHandler(ssid, psk) # Init Peripheral, connect to it an set read delegate peripheral = Peripheral(addr, ADDR_TYPE_RANDOM) peripheral.setDelegate(WPNearbyReadDelegate(grantorHandler)) pwsService = peripheral.getServiceByUUID(PWSGATTServiceUUID) pwsCharacteristic = pwsService.getCharacteristics( forUUID=PWSGATTCharacteristicUUID)[0] logger.debug(f"PWS characteristic: {pwsCharacteristic.getHandle()}") grantorHandler.pwsCharacteristic = pwsCharacteristic time.sleep(0.5) # Subscribe to the PWS characteristics by sending 0x0200 to pwsCharacteristic Handle+1 (Todo clean uo and check if it is always +1) peripheral.writeCharacteristic(pwsCharacteristic.getHandle() + 2, b"\x01\x00", withResponse=True) grantorHandler.sendPWS1() while peripheral.waitForNotifications(2): pass peripheral.disconnect() logger.info("Wi-Fi Password Sharing completed")
class RobotController(): def __init__(self, address): self.robot = Peripheral(addr) print("connected") # keep state for keypresses self.pressed = {"up": False, "down": False, "right": False, "left": False} # TODO get service from robot # TODO get characteristic handles from service/robot # TODO enable notifications if using notifications sv = self.robot.getServiceByUUID(SERVICE_UUID) self.ch_forward = sv.getCharacteristics(CHAR_UUIDS[0])[0] self.ch_backward = sv.getCharacteristics(CHAR_UUIDS[1])[0] self.ch_left = sv.getCharacteristics(CHAR_UUIDS[2])[0] self.ch_right = sv.getCharacteristics(CHAR_UUIDS[3])[0] keyboard.hook(self.on_key_event) def on_key_event(self, event): # print key name print(event.name) # if a key unrelated to direction keys is pressed, ignore if event.name not in self.pressed: return # if a key is pressed down if event.event_type == keyboard.KEY_DOWN: # if that key is already pressed down, ignore if self.pressed[event.name]: return # set state of key to pressed self.pressed[event.name] = True # TODO write to characteristic to change direction if event.name == "up": #print("writing forward") self.ch_forward.write(b'\x01') elif event.name == "down": #print("writing back") self.ch_backward.write(b'\x01') elif event.name == "right": # print("writing right") self.ch_right.write(b'\x01') elif event.name == "left": #print("writing left") self.ch_left.write(b'\x01') else: # set state of key to released self.pressed[event.name] = False # TODO write to characteristic to stop moving in this direction print("Stopping") self.ch_forward.write(b'\x00') self.ch_backward.write(b'\x00') self.ch_right.write(b'\x00') self.ch_left.write(b'\x00') def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.robot.disconnect()
class GAmonitor(object): SERVICE_UUIDS = [ UUID('c0be9c58-a717-4e18-828c-a41836f0c7e5'), # Sensors ] CHARACTERISTIC_UUIDS = {'9a067338-1da7-4e0f-8b4a-1e445e1e2df9': 'ACC'} NOTIFICATION_CHARACTERISTIC_UUIDS = [ 'ACC', ] # Notification data NOTIFICATION_ON = struct.pack("BB", 0x01, 0x00) NOTIFICATION_OFF = struct.pack("BB", 0x00, 0x00) def __init__(self, mac_address): self.macAddress = mac_address self.delegate = ScanDelegate() def set_delegate(self, delegate): self.delegate = delegate def connect(self): print('getting peripheral', sys.stderr) self.peripheral = Peripheral(self.macAddress, addrType='public') # Retrieve all characteristics from desired services and map them from their UUID self.peripheral.getServices() svc = self.peripheral.getServiceByUUID( "c0be9c58-a717-4e18-828c-a41836f0c7e5") characteristics = { svcid: svc.getCharacteristics()[i] for svcid, i in zip(self.CHARACTERISTIC_UUIDS, range(2)) } print(characteristics, sys.stderr) # Store each characteristic's value handle for each characteristic name self.characteristicValueHandles = dict( (name, characteristics[uuid].getHandle()) for uuid, name in self.CHARACTERISTIC_UUIDS.items()) # Subscribe for notifications for name in self.NOTIFICATION_CHARACTERISTIC_UUIDS: print('Enabling notification: ', sys.stderr) self.peripheral.writeCharacteristic( self.characteristicValueHandles[name] + 1, self.NOTIFICATION_ON, True) print(name, sys.stderr) print(self.characteristicValueHandles[name] + 1, sys.stderr) self.peripheral.setDelegate(self.delegate) def disconnect(self): self.peripheral.disconnect() def wait_for_notifications(self, timeout): print('calling wait for notifications') return self.peripheral.waitForNotifications(timeout)
def on_message(client, userdata, msg): print(msg.topic + " " + str(msg.payload)) p = Peripheral(SWITCHBOT, "random") hand_service = p.getServiceByUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b") hand = hand_service.getCharacteristics( "cba20002-224d-11e6-9fb8-0002a5d5c51b")[0] hand.write(binascii.a2b_hex("570100")) p.disconnect()
class MiTemperature(object): '''' Class to read the temperature from the MiTemp sensor from Amazon. Heavily based on https://github.com/JsBergbau/MiTemperature2 but refactored for my application ''' def __init__(self, deviceAddr, addrType=ADDR_TYPE_PUBLIC, iface=0): self.deviceAddr = deviceAddr self.iface = iface self.addrType = addrType self.timeout = 10.0 def _connect(self): self._periph = Peripheral(deviceAddr=self.deviceAddr, addrType=self.addrType, iface=self.iface) enable_notification_temp_humidity = b'\x01\x00' self._periph.writeCharacteristic(0x0038, enable_notification_temp_humidity, True) self._periph.writeCharacteristic(0x0046, b'\xf4\x01\x00', True) self._delegate = _MiDelegate() self._periph.withDelegate(self._delegate) logging.debug("Connected to {}".format(self.deviceAddr)) self.measurement = self._delegate.measurement def _disconnect(self): self._periph.disconnect() def _reading(self): """ Returns the most recent temperature reading :rtype: Measurement """ self._connect() result = None if self._periph.waitForNotifications(self.timeout): logging.debug("Received notification") result = self._delegate.measurement else: logging.error("No trigger from delegate") self._disconnect() return result def reading(self): """" Return the readings a tuple of temperatiure, humidity, battery level :rtype: (float, float, float) """ measurement = self._reading() return measurement.temperature, measurement.humidity, measurement.batterylevel def temperature(self): measurement = self._reading() if measurement is None: return None else: return measurement.temperature
def infect(dev): global connection_error infected = False # Connect. try: log(f"[*] Connecting to {dev.addr}.") peripheral = Peripheral(iface=BT_INTERFACE_INDEX) connection_error = False # Ugly hack to control the connection timout (only possible with threads). thread = Thread(target=connect, args=[peripheral, dev.addr]) thread.start() thread.join(CONNECTION_TIMOUT) if thread.is_alive() or connection_error: raise Exception() except KeyboardInterrupt: raise except: try: peripheral.disconnect() except: pass log("[-] Could not connect.") return False # Check for service. try: service = peripheral.getServiceByUUID( "42230100-2342-2342-2342-234223422342") characteristic = service.getCharacteristics( "42230101-2342-2342-2342-234223422342")[0] except: log("[-] Service or characteristic not found. Not a card10 or connection lost." ) return False # Write file. try: log("[*] Trying to write file.") data = OPEN_OR_CREATE_FILE + FILE_NAME characteristic.write(data) # Sleep is needed if the device was already paired (e.g. to a mobile phone) before. # Without the sleep the file will not be saved. I don't know why... sleep(0.1) # Writing files in this way caused weird errors. I haven't checked why. #data = WRITE_FILE + b"test" #characteristic.write(data) data = SAVE_FILE characteristic.write(data) log(f"[+] File written to {dev.addr}.", True) infected = True except: log("[-] Error on writing.") return False # Disconnect. peripheral.disconnect() return infected
def send_command(self, mac_address, service_uuid, characteristics, command): p = Peripheral(mac_address) service_status = p.getServiceByUUID(service_uuid) ch = service_status.getCharacteristics(characteristics)[0] cmd = int(command) cmd = format(cmd, "b") ch.write(cmd, False) p.disconnect()
def write(self, characteristic, data): try: dev = Peripheral(self, self.addrType) services = sorted(dev.services, key=lambda s: s.hndStart) print_status("Searching for characteristic {}".format(characteristic)) char = None for service in services: if char is not None: break for _, c in enumerate(service.getCharacteristics()): if str(c.uuid) == characteristic: char = c break if char: if "WRITE" in char.propertiesToString(): print_success("Sending {} bytes...".format(len(data))) wwrflag = False if "NO RESPONSE" in char.propertiesToString(): wwrflag = True try: char.write(data, wwrflag) print_success("Data sent") except Exception as err: print_error("Error: {}".format(err)) else: print_error("Not writable") dev.disconnect() except Exception as err: print_error(err) try: dev.disconnect() except Exception: pass return None
def enumerate_services(self): print_status("Starting enumerating {} ({} dBm) ...".format(self.addr, self.rssi)) try: dev = Peripheral(self, self.addrType) services = sorted(dev.services, key=lambda s: s.hndStart) data = [] for service in services: if service.hndStart == service.hndEnd: continue data.append([ "{:04x} -> {:04x}".format(service.hndStart, service.hndEnd), self._get_svc_description(service), "", "", ]) for _, char in enumerate(service.getCharacteristics()): desc = self._get_char_description(char) props = char.propertiesToString() hnd = char.getHandle() value = self._get_char(char, props) data.append([ "{:04x}".format(hnd), desc, props, value ]) dev.disconnect() return data except Exception as err: print_error(err) try: dev.disconnect() except Exception as err: print_error(err) return None
print "Info, reading values!" ch = p.getCharacteristics(uuid=data_uuid)[0] rawVals = ch.read() rawVal = (ord(rawVals[1])<<8)+ord(rawVals[0]) #object temp and ambient temp are calculated as shown below mantissa = rawVal & 0x0FFF; exponent = (rawVal >> 12) & 0xFF; magnitude = pow(2.0, exponent); output = (mantissa * magnitude); print "Lux: %4.2f lx" % (output / 100.0) print "Info, turning sensor off!" ch = p.getCharacteristics(uuid=config_uuid)[0] ch.write(sensorOff, withResponse=True) except: print "Fatal, unexpected error!" traceback.print_exc() raise finally: print "Info, disconnecting!" p.disconnect() finally: quit()
class OpenBCIGanglion(object): """ Handle a connection to an OpenBCI board. Args: port: MAC address of the Ganglion Board. "None" to attempt auto-detect. aux: enable on not aux channels (i.e. switch to 18bit mode if set) impedance: measures impedance when start streaming timeout: in seconds, if set will try to disconnect / reconnect after a period without new data -- should be high if impedance check max_packets_to_skip: will try to disconnect / reconnect after too many packets are skipped baud, filter_data, daisy: Not used, for compatibility with v3 """ def __init__(self, port=None, baud=0, filter_data=False, scaled_output=True, daisy=False, log=True, aux=False, impedance=False, timeout=2, max_packets_to_skip=20): # unused, for compatibility with Cyton v3 API self.daisy = False # these one are used self.log = log # print_incoming_text needs log self.aux = aux self.streaming = False self.timeout = timeout self.max_packets_to_skip = max_packets_to_skip self.scaling_output = scaled_output self.impedance = impedance # might be handy to know API self.board_type = "ganglion" print("Looking for Ganglion board") if port == None: port = self.find_port() self.port = port # find_port might not return string self.connect() self.streaming = False # number of EEG channels and (optionally) accelerometer channel self.eeg_channels_per_sample = 4 self.aux_channels_per_sample = 3 self.imp_channels_per_sample = 5 self.read_state = 0 self.log_packet_count = 0 self.packets_dropped = 0 self.time_last_packet = 0 # Disconnects from board when terminated atexit.register(self.disconnect) def getBoardType(self): """ Returns the version of the board """ return self.board_type def setImpedance(self, flag): """ Enable/disable impedance measure """ self.impedance = bool(flag) def connect(self): """ Connect to the board and configure it. Note: recreates various objects upon call. """ print("Init BLE connection with MAC: " + self.port) print("NB: if it fails, try with root privileges.") self.gang = Peripheral(self.port, 'random') # ADDR_TYPE_RANDOM print("Get mainservice...") self.service = self.gang.getServiceByUUID(BLE_SERVICE) print("Got:" + str(self.service)) print("Get characteristics...") self.char_read = self.service.getCharacteristics(BLE_CHAR_RECEIVE)[0] print("receive, properties: " + str(self.char_read.propertiesToString()) + ", supports read: " + str(self.char_read.supportsRead())) self.char_write = self.service.getCharacteristics(BLE_CHAR_SEND)[0] print("write, properties: " + str(self.char_write.propertiesToString()) + ", supports read: " + str(self.char_write.supportsRead())) self.char_discon = self.service.getCharacteristics(BLE_CHAR_DISCONNECT)[0] print("disconnect, properties: " + str(self.char_discon.propertiesToString()) + ", supports read: " + str(self.char_discon.supportsRead())) # set delegate to handle incoming data self.delegate = GanglionDelegate(self.scaling_output) self.gang.setDelegate(self.delegate) # enable AUX channel if self.aux: print("Enabling AUX data...") try: self.ser_write(b'n') except Exception as e: print("Something went wrong while enabling aux channels: " + str(e)) print("Turn on notifications") # nead up-to-date bluepy, cf https://github.com/IanHarvey/bluepy/issues/53 self.desc_notify = self.char_read.getDescriptors(forUUID=0x2902)[0] try: self.desc_notify.write(b"\x01") except Exception as e: print("Something went wrong while trying to enable notification: " + str(e)) print("Connection established") def init_streaming(self): """ Tell the board to record like crazy. """ try: if self.impedance: print("Starting with impedance testing") self.ser_write(b'z') else: self.ser_write(b'b') except Exception as e: print("Something went wrong while asking the board to start streaming: " + str(e)) self.streaming = True self.packets_dropped = 0 self.time_last_packet = timeit.default_timer() def find_port(self): """Detects Ganglion board MAC address If more than 1 around, will select first. Needs root privilege. """ print("Try to detect Ganglion MAC address. " "NB: Turn on bluetooth and run as root for this to work!" "Might not work with every BLE dongles.") scan_time = 5 print("Scanning for 5 seconds nearby devices...") # From bluepy example class ScanDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) def handleDiscovery(self, dev, isNewDev, isNewData): if isNewDev: print("Discovered device: " + dev.addr) elif isNewData: print("Received new data from: " + dev.addr) scanner = Scanner().withDelegate(ScanDelegate()) devices = scanner.scan(scan_time) nb_devices = len(devices) if nb_devices < 1: print("No BLE devices found. Check connectivity.") return "" else: print("Found " + str(nb_devices) + ", detecting Ganglion") list_mac = [] list_id = [] for dev in devices: # "Ganglion" should appear inside the "value" associated # to "Complete Local Name", e.g. "Ganglion-b2a6" for (adtype, desc, value) in dev.getScanData(): if desc == "Complete Local Name" and value.startswith("Ganglion"): list_mac.append(dev.addr) list_id.append(value) print("Got Ganglion: " + value + ", with MAC: " + dev.addr) break nb_ganglions = len(list_mac) if nb_ganglions < 1: print("No Ganglion found ;(") raise OSError('Cannot find OpenBCI Ganglion MAC address') if nb_ganglions > 1: print("Found " + str(nb_ganglions) + ", selecting first") print("Selecting MAC address " + list_mac[0] + " for " + list_id[0]) return list_mac[0] def ser_write(self, b): """Access serial port object for write""" self.char_write.write(b) def ser_read(self): """Access serial port object for read""" return self.char_read.read() def ser_inWaiting(self): """ Slightly different from Cyton API, return True if ASCII messages are incoming.""" # FIXME: might have a slight problem with thread because of notifications... if self.delegate.receiving_ASCII: # in case the packet indicating the end of the message drops, we use a 1s timeout if timeit.default_timer() - self.delegate.time_last_ASCII > 2: self.delegate.receiving_ASCII = False return self.delegate.receiving_ASCII def getSampleRate(self): return SAMPLE_RATE def getNbEEGChannels(self): """Will not get new data on impedance check.""" return self.eeg_channels_per_sample def getNbAUXChannels(self): """Might not be used depending on the mode.""" return self.aux_channels_per_sample def getNbImpChannels(self): """Might not be used depending on the mode.""" return self.imp_channels_per_sample def start_streaming(self, callback, lapse=-1): """ Start handling streaming data from the board. Call a provided callback for every single sample that is processed Args: callback: A callback function or a list of functions that will receive a single argument of the OpenBCISample object captured. """ if not self.streaming: self.init_streaming() start_time = timeit.default_timer() # Enclose callback funtion in a list if it comes alone if not isinstance(callback, list): callback = [callback] while self.streaming: # should the board get disconnected and we could not wait for notification # anymore, a reco should be attempted through timeout mechanism try: # at most we will get one sample per packet self.waitForNotifications(1. / self.getSampleRate()) except Exception as e: print("Something went wrong while waiting for a new sample: " + str(e)) # retrieve current samples on the stack samples = self.delegate.getSamples() self.packets_dropped = self.delegate.getMaxPacketsDropped() if samples: self.time_last_packet = timeit.default_timer() for call in callback: for sample in samples: call(sample) if (lapse > 0 and timeit.default_timer() - start_time > lapse): self.stop() if self.log: self.log_packet_count = self.log_packet_count + 1 # Checking connection -- timeout and packets dropped self.check_connection() def waitForNotifications(self, delay): """ Allow some time for the board to receive new data. """ self.gang.waitForNotifications(delay) def test_signal(self, signal): """ Enable / disable test signal """ if signal == 0: self.warn("Disabling synthetic square wave") try: self.char_write.write(b']') except Exception as e: print("Something went wrong while setting signal: " + str(e)) elif signal == 1: self.warn("Eisabling synthetic square wave") try: self.char_write.write(b'[') except Exception as e: print("Something went wrong while setting signal: " + str(e)) else: self.warn( "%s is not a known test signal. Valid signal is 0-1" % signal) def set_channel(self, channel, toggle_position): """ Enable / disable channels """ try: # Commands to set toggle to on position if toggle_position == 1: if channel is 1: self.ser.write(b'!') if channel is 2: self.ser.write(b'@') if channel is 3: self.ser.write(b'#') if channel is 4: self.ser.write(b'$') # Commands to set toggle to off position elif toggle_position == 0: if channel is 1: self.ser.write(b'1') if channel is 2: self.ser.write(b'2') if channel is 3: self.ser.write(b'3') if channel is 4: self.ser.write(b'4') except Exception as e: print("Something went wrong while setting channels: " + str(e)) """ Clean Up (atexit) """ def stop(self): print("Stopping streaming...") self.streaming = False # connection might be already down here try: if self.impedance: print("Stopping with impedance testing") self.ser_write(b'Z') else: self.ser_write(b's') except Exception as e: print("Something went wrong while asking the board to stop streaming: " + str(e)) if self.log: logging.warning('sent <s>: stopped streaming') def disconnect(self): if (self.streaming == True): self.stop() print("Closing BLE..") try: self.char_discon.write(b' ') except Exception as e: print("Something went wrong while asking the board to disconnect: " + str(e)) # should not try to read/write anything after that, will crash try: self.gang.disconnect() except Exception as e: print("Something went wrong while shutting down BLE link: " + str(e)) logging.warning('BLE closed') """ SETTINGS AND HELPERS """ def warn(self, text): if self.log: # log how many packets where sent succesfully in between warnings if self.log_packet_count: logging.info('Data packets received:' + str(self.log_packet_count)) self.log_packet_count = 0 logging.warning(text) print("Warning: %s" % text) def check_connection(self): """ Check connection quality in term of lag and number of packets drop. Reinit connection if necessary. FIXME: parameters given to the board will be lost. """ # stop checking when we're no longer streaming if not self.streaming: return # check number of dropped packets and duration without new packets, deco/reco if too large if self.packets_dropped > self.max_packets_to_skip: self.warn("Too many packets dropped, attempt to reconnect") self.reconnect() elif self.timeout > 0 and timeit.default_timer() - self.time_last_packet > self.timeout: self.warn("Too long since got new data, attempt to reconnect") # if error, attempt to reconect self.reconnect() def reconnect(self): """ In case of poor connection, will shut down and relaunch everything. FIXME: parameters given to the board will be lost.""" self.warn('Reconnecting') self.stop() self.disconnect() self.connect() self.init_streaming()
def main(): # uuid definition targetDevice = "" targetUUID = UUID("08590f7e-db05-467e-8757-72f6f66666d4") # targetUUID = UUID(0x2a2b) serviceUUID = UUID("e20a39f4-73f5-4bc4-a12f-17d1ad666661") # scanning for Bluetooth LE device # P.S. root permission is needed print "scanning started..." scanner = Scanner().withDelegate(ScanDelegate()) devices = scanner.scan(5) print "\n\nscanning completed...\n found %d device(s)\n" % len(devices) for dev in devices: print "Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi) for (adtype, desc, value) in dev.getScanData(): print " %s = %s" % (desc, value) try: p = Peripheral(dev.addr, "random") ch = p.getCharacteristics(uuid=targetUUID) if len(ch) > 0: print "the desired target found. the address is", dev.addr targetDevice = dev.addr except: # print "Unexpected error:", sys.exc_info()[0] print "Unable to connect" print " " finally: p.disconnect() # scanning completed, now continue to connect to device if targetDevice == "": # the target is not found. end. print "no target was found." else: # the target found, continue to subscribe. print "\n\nthe target device is ", targetDevice print "now try to subscribe..." try: # try to get the handle first p = Peripheral(targetDevice, "random") p.setDelegate(NotificationDelegate()) # svc = p.getServiceByUUID(serviceUUID) ch = p.getCharacteristics(uuid=targetUUID)[0] # svc.getCharacteristics(targetUUID)[0] handle = ch.getHandle() print handle ch.write(struct.pack('<bb', 0x01, 0x00)) # ch.write(bytes('aa', 'utf-8')) # p.writeCharacteristic(handle, struct.pack('<bb', 0x01, 0x00), True) print # Main loop while True: if p.waitForNotifications(5): # handleNotification() was called continue print "Waiting..." # Perhaps do something else here # except: # print "Unexpected error:", sys.exc_info()[0] finally: p.disconnect()
class LightBlueBean(DefaultDelegate): # https://github.com/PunchThrough/bean-documentation/blob/master/app_message_types.md MSG_ID_SERIAL_DATA = 0x0000 MSG_ID_BT_SET_ADV = 0x0500 MSG_ID_BT_SET_CONN = 0x0502 MSG_ID_BT_SET_LOCAL_NAME = 0x0504 MSG_ID_BT_SET_PIN = 0x0506 MSG_ID_BT_SET_TX_PWR = 0x0508 MSG_ID_BT_GET_CONFIG = 0x0510 MSG_ID_BT_ADV_ONOFF = 0x0512 MSG_ID_BT_SET_SCRATCH = 0x0514 MSG_ID_BT_GET_SCRATCH = 0x0515 MSG_ID_BT_RESTART = 0x0520 MSG_ID_GATING = 0x0550 MSG_ID_BL_CMD = 0x1000 MSG_ID_BL_FW_BLOCK = 0x1001 MSG_ID_BL_STATUS = 0x1002 MSG_ID_CC_LED_WRITE = 0x2000 MSG_ID_CC_LED_WRITE_ALL = 0x2001 MSG_ID_CC_LED_READ_ALL = 0x2002 MSG_ID_CC_LED_DATA = 0x2082 MSG_ID_CC_ACCEL_READ = 0x2010 MSG_ID_CC_ACCEL_DATA = 0x2090 MSG_ID_CC_TEMP_READ = 0x2011 MSG_ID_CC_TEMP_DATA = 0x2091 MSG_ID_CC_BATT_READ = 0x2015 MSG_ID_CC_BATT_DATA = 0x2095 MSG_ID_AR_SET_POWER = 0x3000 MSG_ID_AR_GET_CONFIG = 0x3006 MSG_ID_DB_LOOPBACK = 0xFE00 MSG_ID_DB_COUNTER = 0xFE01 def __init__(self, mac): self.conn = Peripheral(mac) self.conn.setDelegate(self) self.count = 0 self.buffin = [None]*10 self.got1 = False print('connected') self.service = self.conn.getServiceByUUID(_LBN_UUID(0x10)) self.serial = self.service.getCharacteristics(_LBN_UUID(0x11)) [0] #print(self.serial.propertiesToString()) # Turn on notificiations self.conn.writeCharacteristic(0x2f, '\x01\x00', False) i = 0 while True: #print(self.serial.read()) self.write("a" * 60) #self.write("a" * 5) #self.sendCmd(LightBlueBean.MSG_ID_CC_ACCEL_READ) self.conn.waitForNotifications(1) time.sleep(1) self.conn.disconnect() def write(self, data): self.sendCmd(LightBlueBean.MSG_ID_SERIAL_DATA, data) def sendCmd(self, cmd, data = ""): # https://github.com/PunchThrough/bean-documentation/blob/master/serial_message_protocol.md gst = struct.pack("!BxH", len(data)+2, cmd) + data crc = struct.pack("<H", crc16(gst, 0xFFFF)) gst += crc gt_qty = len(gst)/19 if len(gst) % 19 != 0: gt_qty += 1 #amnt = len(gst) / gt_qty optimal_packet_size = 19 for ch in xrange(0, gt_qty): data = gst[:optimal_packet_size] gst = gst[optimal_packet_size:] gt = 0 if ch == 0: gt = 0x80 gt |= self.count << 5 gt |= gt_qty - ch - 1 gt = struct.pack("B", gt) + data #print("<", hexdump(gt)) self.serial.write(gt) #time.sleep(0.1) self.count = (self.count + 1) % 4 def writeRaw(self, data): self.conn.writeCharacteristic(0x0b, data, False) def handleNotification(self, cHandle, data): #print(">", hexdump(data)) gt = struct.unpack("B", data[0]) [0] #gt_cntr = gt & 0x60 gt_left = gt & 0x1F if gt & 0x80: self.got1 = True self.buffin = self.buffin[:gt_left+1] self.buffin[gt_left] = data[1:] if self.got1 and not self.buffin.count(None): #print("Got ", len(self.buffin), "packets") self.buffin.reverse() self.buffin = ''.join(self.buffin) crc_ = crc16(self.buffin[:-2], 0xFFFF) dlen, cmd = struct.unpack("!BxH", self.buffin[:4]) crc = struct.unpack("<H", self.buffin[-2:]) [0] if crc == crc_: print(self.buffin[4:-2]) else: print("CRC check failure") self.buffin = [None]*10 self.got1 = False
class SBrickCommunications(threading.Thread, IdleObject): def __init__(self, sbrick_addr): threading.Thread.__init__(self) IdleObject.__init__(self) self.lock = threading.RLock() self.drivingLock = threading.RLock() self.eventSend = threading.Event() self.sBrickAddr = sbrick_addr self.owner_password = None self.brickChannels = [ SBrickChannelDrive(0, self.eventSend), SBrickChannelDrive(1, self.eventSend), SBrickChannelDrive(2, self.eventSend), SBrickChannelDrive(3, self.eventSend), ] self.SBrickPeripheral = None self.stopFlag = False self.characteristicRemote = None self.need_authentication = False self.authenticated = False self.channel_config_ids = dict() def set_channel_config_id(self, channel, config_id): self.channel_config_ids[config_id] = channel self.brickChannels[channel].set_config_id(config_id) def terminate(self): self.stopFlag = True def is_driving(self): locked = self.drivingLock.acquire(False) if locked: self.drivingLock.release() return not locked def connect_to_sbrick(self, owner_password): self.owner_password = owner_password self.start() def run(self): try: monotime = 0.0 self.SBrickPeripheral = Peripheral() self.SBrickPeripheral.connect(self.sBrickAddr) service = self.SBrickPeripheral.getServiceByUUID('4dc591b0-857c-41de-b5f1-15abda665b0c') characteristics = service.getCharacteristics('02b8cbcc-0e25-4bda-8790-a15f53e6010f') for characteristic in characteristics: if characteristic.uuid == '02b8cbcc-0e25-4bda-8790-a15f53e6010f': self.characteristicRemote = characteristic if self.characteristicRemote is None: return self.emit('sbrick_connected') self.need_authentication = self.get_need_authentication() self.authenticated = not self.need_authentication if self.need_authentication: if self.password_owner is not None: self.authenticate_owner(self.password_owner) while not self.stopFlag: if self.authenticated: if monotonic.monotonic() - monotime >= 0.05: self.send_command() monotime = monotonic.monotonic() self.eventSend.wait(0.01) for channel in self.brickChannels: if channel.decrement_run_timer(): monotime = 0.0 self.drivingLock.release() # print("stop run normal") self.emit("sbrick_channel_stop", channel.channel) if channel.decrement_brake_timer(): self.drivingLock.release() # print("stop brake timer") monotime = 0.0 self.emit("sbrick_channel_stop", channel.channel) if self.authenticated: self.stop_all() self.send_command() self.SBrickPeripheral.disconnect() self.emit('sbrick_disconnected_ok') except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_channel(self, channel): if isinstance(channel, six.integer_types): return self.brickChannels[channel] if isinstance(channel, six.string_types): return self.brickChannels[self.channel_config_ids[channel]] return None def drive(self, channel, pwm, reverse, time, brake_after_time=False): with self.lock: ch = self.get_channel(channel) if ch is not None: ch.drive(pwm, reverse, time, brake_after_time) self.emit("sbrick_drive_sent", ch.channel, time) self.eventSend.set() def stop(self, channel, braked=False): with self.lock: ch = self.get_channel(channel) if ch is not None: ch.stop(braked) self.emit("sbrick_drive_sent", ch.channel, -2) self.eventSend.set() def stop_all(self): with self.lock: for channel in self.brickChannels: channel.stop() self.eventSend.set() def change_pwm(self, channel, pwm, change_reverse=False): with self.lock: ch = self.get_channel(channel) if ch is not None: ch.set_pwm(pwm, change_reverse) self.eventSend.set() def change_reverse(self, channel, reverse): with self.lock: ch = self.get_channel(channel) if ch is not None: ch.set_reverse(reverse) self.eventSend.set() def send_command(self): with self.lock: # try: drivecmd = bytearray([0x01]) brakecmd = bytearray([0x00]) for channel in self.brickChannels: drivecmd = channel.get_command_drive(drivecmd) brakecmd = channel.get_command_brake(brakecmd) if len(drivecmd) > 1: self.drivingLock.acquire() self.characteristicRemote.write(drivecmd, True) self.print_hex_string("drive sent", drivecmd) if len(brakecmd) > 1: self.characteristicRemote.write(brakecmd, True) self.print_hex_string("brake sent", brakecmd) # return True # except Exception as ex: # self.emit("sbrick_disconnected_error",ex.message) # return False def disconnect_sbrick(self): with self.lock: self.stopFlag = True @staticmethod def print_hex_string(what, strin): out = what + " -> " for chrx in strin: out = "%s %0X" % (out, chrx) print(out) def get_voltage(self): with self.lock: try: self.characteristicRemote.write(b"\x0f\x00") value = self.characteristicRemote.read() valueint = struct.unpack("<H", value)[0] return (valueint * 0.83875) / 2047.0 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_temperature(self): with self.lock: try: self.characteristicRemote.write(b"\x0f\x0e") value = self.characteristicRemote.read() valueint = struct.unpack("<H", value)[0] return valueint / 118.85795 - 160 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_thermal_limit(self): with self.lock: try: self.characteristicRemote.write(b'\x15') value = self.characteristicRemote.read() valueint = struct.unpack("<H", value)[0] return valueint / 118.85795 - 160 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_watchdog_timeout(self): with self.lock: try: self.characteristicRemote.write(b'\x0e') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] * 0.1 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_authentication_timeout(self): with self.lock: try: self.characteristicRemote.write(b'\x09') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] * 0.1 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_power_cycle_counter(self): with self.lock: try: self.characteristicRemote.write(b'\x28') value = self.characteristicRemote.read() return struct.unpack("<I", value)[0] except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_uptime(self): with self.lock: try: self.characteristicRemote.write(b'\x29') value = self.characteristicRemote.read() seconds = struct.unpack("<I", value)[0] * 0.1 minutes = seconds // 60 hours = minutes // 60 return "%02d:%02d:%02d" % (hours, minutes % 60, seconds % 60) except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_hardware_version(self): try: return self.SBrickPeripheral.readCharacteristic(0x000c).decode("utf-8") except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_software_version(self): try: return self.SBrickPeripheral.readCharacteristic(0x000a).decode("utf-8") except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_brick_id(self): with self.lock: try: self.characteristicRemote.write(b'\x0a') value = self.characteristicRemote.read() return "%0X %0X %0X %0X %0X %0X" % ( value[0], value[1], value[2], value[3], value[4], value[5]) except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_need_authentication(self): with self.lock: try: self.characteristicRemote.write(b'\x02') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] == 1 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_is_authenticated(self): with self.lock: try: self.characteristicRemote.write(b'\x03') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] == 1 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_user_id(self): with self.lock: try: self.characteristicRemote.write(b'\x04') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] == 1 except BTLEException as ex: self.emit("sbrick_error", ex.message) def authenticate_owner(self, password): with self.lock: try: self.authenticated = False cmd = bytearray([0x05, 0x00]) for ch in password: cmd.append(ord(ch)) self.characteristicRemote.write(cmd) self.authenticated = True except BTLEException as ex: self.emit("sbrick_error", ex.message) def authenticate_guest(self, password): with self.lock: try: self.authenticated = False cmd = bytearray([0x05, 0x01]) for ch in password: cmd.append(ord(ch)) self.characteristicRemote.write(cmd) self.authenticated = True except BTLEException as ex: self.emit("sbrick_error", ex.message) def clear_owner_password(self): with self.lock: try: self.characteristicRemote.write(b'\x06\x00') except BTLEException as ex: self.emit("sbrick_error", ex.message) def clear_guest_password(self): with self.lock: try: self.characteristicRemote.write(b'\x06\x01') except BTLEException as ex: self.emit("sbrick_error", ex.message) def set_owner_password(self, password): with self.lock: try: cmd = bytearray([0x07, 0x00]) for ch in password: cmd.append(ord(ch)) self.characteristicRemote.write(cmd) except BTLEException as ex: self.emit("sbrick_error", ex.message) def set_guest_password(self, password): with self.lock: try: cmd = bytearray([0x07, 0x01]) for ch in password: cmd.append(ord(ch)) self.characteristicRemote.write(cmd) except BTLEException as ex: self.emit("sbrick_error", ex.message) def set_authentication_timeout(self, seconds): with self.lock: try: cmd = bytearray([0x08, seconds / 0.1]) self.characteristicRemote.write(cmd) except BTLEException as ex: self.emit("sbrick_error", ex.message)