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 __get_services_and_chars(self): for device in self.__devices_around: try: if self.__devices_around.get( device) is not None and self.__devices_around[ device].get('scanned_device') is not None: log.debug('Connecting to device: %s', device) if self.__devices_around[device].get('peripheral') is None: address_type = self.__devices_around[device][ 'device_config'].get('addrType', "public") peripheral = Peripheral( self.__devices_around[device]['scanned_device'], address_type) self.__devices_around[device][ 'peripheral'] = peripheral else: peripheral = self.__devices_around[device][ 'peripheral'] try: log.info(peripheral.getState()) except BTLEInternalError: peripheral.connect( self.__devices_around[device]['scanned_device']) try: services = peripheral.getServices() except BTLEDisconnectError: self.__check_and_reconnect(device) services = peripheral.getServices() for service in services: if self.__devices_around[device].get( 'services') is None: log.debug( 'Building device %s map, it may take a time, please wait...', device) self.__devices_around[device]['services'] = {} service_uuid = str(service.uuid).upper() if self.__devices_around[device]['services'].get( service_uuid) is None: self.__devices_around[device]['services'][ service_uuid] = {} try: characteristics = service.getCharacteristics() except BTLEDisconnectError: self.__check_and_reconnect(device) characteristics = service.getCharacteristics() if self.__config.get('buildDevicesMap', False): for characteristic in characteristics: descriptors = [] self.__check_and_reconnect(device) try: descriptors = characteristic.getDescriptors( ) except BTLEDisconnectError: self.__check_and_reconnect(device) descriptors = characteristic.getDescriptors( ) except BTLEGattError as e: log.debug(e) except Exception as e: log.exception(e) characteristic_uuid = str( characteristic.uuid).upper() if self.__devices_around[device][ 'services'][service_uuid].get( characteristic_uuid) is None: self.__check_and_reconnect(device) self.__devices_around[device][ 'services'][service_uuid][ characteristic_uuid] = { 'characteristic': characteristic, 'handle': characteristic.handle, 'descriptors': {} } for descriptor in descriptors: log.debug(descriptor.handle) log.debug(str(descriptor.uuid)) log.debug(str(descriptor)) self.__devices_around[device][ 'services'][service_uuid][ characteristic_uuid][ 'descriptors'][ descriptor. handle] = descriptor else: for characteristic in characteristics: characteristic_uuid = str( characteristic.uuid).upper() self.__devices_around[device]['services'][ service_uuid][characteristic_uuid] = { 'characteristic': characteristic, 'handle': characteristic.handle } if self.__devices_around[device]['is_new_device']: log.debug('New device %s - processing.', device) self.__devices_around[device]['is_new_device'] = False self.__new_device_processing(device) for interest_char in self.__devices_around[device][ 'interest_uuid']: characteristics_configs_for_processing_by_methods = {} for configuration_section in self.__devices_around[ device]['interest_uuid'][interest_char]: characteristic_uuid_from_config = configuration_section[ 'section_config'].get("characteristicUUID") if characteristic_uuid_from_config is None: log.error( 'Characteristic not found in config: %s', pformat(configuration_section)) continue method = configuration_section[ 'section_config'].get('method') if method is None: log.error('Method not found in config: %s', pformat(configuration_section)) continue characteristics_configs_for_processing_by_methods[ method.upper()] = { "method": method, "characteristicUUID": characteristic_uuid_from_config } for method in characteristics_configs_for_processing_by_methods: data = self.__service_processing( device, characteristics_configs_for_processing_by_methods[ method]) for section in self.__devices_around[device][ 'interest_uuid'][interest_char]: converter = section['converter'] converted_data = converter.convert( section, data) self.statistics[ 'MessagesReceived'] = self.statistics[ 'MessagesReceived'] + 1 log.debug(data) log.debug(converted_data) self.__gateway.send_to_storage( self.get_name(), converted_data) self.statistics[ 'MessagesSent'] = self.statistics[ 'MessagesSent'] + 1 except BTLEDisconnectError: log.debug('Connection lost. Device %s', device) continue except Exception as e: log.exception(e)
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.packet_logger = get_packet_logger() self.address = g_rl_address self.service = None self.response_handle = None self.notify_event = Event() self.initialized = False self.manchester = ManchesterCodec() 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 as be: if self.peripheral is not None: self.disconnect() raise PacketRadioError("Error while connecting") from be except Exception as e: raise PacketRadioError("Error while connecting") from e 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 Exception as e: if not ignore_errors: raise PacketRadioError("Error while disconnecting") from e finally: try: if self.peripheral is not None: self.peripheral.disconnect() self.peripheral = None except BTLEException as be: if ignore_errors: self.logger.exception( "Ignoring btle exception during disconnect") else: raise PacketRadioError("Error while disconnecting") from be except Exception as e: raise PacketRadioError("Error while disconnecting") from e 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 Exception as e: raise PacketRadioError("Error communicating with RileyLink") from e finally: self.disconnect() def _read_version(self): global g_rl_version, g_rl_v_major, g_rl_v_minor version = None try: if g_rl_version is not None: return g_rl_version, g_rl_v_major, g_rl_v_minor 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) g_rl_version = version 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) g_rl_v_major = int(m.group(1)) g_rl_v_minor = int(m.group(2)) self.logger.debug("Interpreted version major: %d minor: %d" % (g_rl_v_major, g_rl_v_minor)) return g_rl_version, g_rl_v_major, g_rl_v_minor except Exception as ex: raise PacketRadioError( "Failed to parse firmware version string: %s" % version) from ex except PacketRadioError: raise except Exception as e: raise PacketRadioError("Error while reading version") from e 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.PKTLEN, 0x00])) else: response = self._command(Command.READ_REGISTER, bytes([Register.PKTLEN])) if response is not None and len( response) > 0 and response[0] == 0x50: return self._command(Command.RADIO_RESET_CONFIG) self._command(Command.SET_SW_ENCODING, bytes([Encoding.NONE])) self._command(Command.SET_PREAMBLE, bytes([0x66, 0x65])) #self._command(Command.SET_PREAMBLE, bytes([0, 0])) frequency = int(433910000 / (24000000 / pow(2, 16))) 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.FREQ0, 0x5f])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ1, 0x14])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ2, 0x12])) self._command(Command.UPDATE_REGISTER, bytes([Register.DEVIATN, 0x44])) # self._command(Command.UPDATE_REGISTER, bytes([Register.DEVIATN, 0x44])) 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.PKTLEN, 0x50])) # self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL1, 0x60])) # self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL0, 0x04])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCTRL1, 0x06])) # 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.MCSM0, 0x18])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG4, 0xDA])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG3, 0xB5])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG2, 0x12])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG1, 0x23])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG0, 0x11])) # 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, 0x31])) self._command(Command.UPDATE_REGISTER, bytes([Register.TEST0, 0x09])) # 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.TEST2, 0x81])) ## register not defined on RL # self._command(Command.UPDATE_REGISTER, bytes([Register.TEST1, 0x35])) # 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])) # 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 Exception as e: raise PacketRadioError( "Error while initializing rileylink radio: %s", e) def tx_up(self): try: if self.pa_level_index < len(PA_LEVELS) - 1: self.pa_level_index += 1 self._set_amp(self.pa_level_index) except Exception as e: raise PacketRadioError("Error while setting tx up") from e def tx_down(self): try: if self.pa_level_index > 0: self.pa_level_index -= 1 self._set_amp(self.pa_level_index) except Exception as e: raise PacketRadioError("Error while setting tx down") from e def set_tx_power(self, tx_power): try: 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(0x1D)) elif tx_power == TxPower.Normal: self._set_amp(PA_LEVELS.index(0x84)) elif tx_power == TxPower.High: self._set_amp(PA_LEVELS.index(0xC8)) elif tx_power == TxPower.Highest: self._set_amp(PA_LEVELS.index(0xC0)) except Exception as e: raise PacketRadioError("Error while setting tx level") from e def get_packet(self, timeout=5.0): try: self.connect() result = self._command(Command.GET_PACKET, struct.pack(">BL", 0, int(timeout * 1000)), timeout=float(timeout) + 0.5) if result is not None: return result[0:2] + self.manchester.decode(result[2:]) else: return None except Exception as e: raise PacketRadioError("Error while getting radio packet") from e def send_and_receive_packet(self, packet, repeat_count, delay_ms, timeout_ms, retry_count, preamble_ext_ms): try: self.connect() data = self.manchester.encode(packet) result = self._command( Command.SEND_AND_LISTEN, struct.pack(">BBHBLBH", 0, repeat_count, delay_ms, 0, timeout_ms, retry_count, preamble_ext_ms) + data, timeout=30) if result is not None: return result[0:2] + self.manchester.decode(result[2:]) else: return None except Exception as e: raise PacketRadioError( "Error while sending and receiving data") from e def send_packet(self, packet, repeat_count, delay_ms, preamble_extension_ms): try: self.connect() data = self.manchester.encode(packet) result = self._command( Command.SEND_PACKET, struct.pack(">BBHH", 0, repeat_count, delay_ms, preamble_extension_ms) + data, timeout=30) return result except Exception as e: raise PacketRadioError("Error while sending data") from e def _set_amp(self, index=None): try: if index is not None: previous_level = self.pa_level_index self.pa_level_index = index if PA_LEVELS[previous_level] == PA_LEVELS[index]: return self.connect() self._command( Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]])) self.packet_logger.debug("Setting pa to %02X (%d of %d)" % (PA_LEVELS[self.pa_level_index], self.pa_level_index, len(PA_LEVELS))) except PacketRadioError: self.logger.exception("Error while setting tx amplification") raise def _findRileyLink(self): global g_rl_address scanner = Scanner() g_rl_address = None self.logger.debug("Scanning for RileyLink") retries = 10 while g_rl_address 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") g_rl_address = result.addr if g_rl_address is None: raise PacketRadioError("Could not find RileyLink") return g_rl_address 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 PacketRadioError: raise except Exception as e: raise PacketRadioError("Error executing command") from e
class BleVestDevice(VestDevice): def __init__(self, deviceAddr): try: self._peripheral = Peripheral(deviceAddr) serviceUUID = UUID("713d0000-503e-4c75-ba94-3148f18d941e") characteristicUUID = UUID("713d0003-503e-4c75-ba94-3148f18d941e") s = self._peripheral.getServiceByUUID(serviceUUID) self._characteristic = s.getCharacteristics(characteristicUUID)[0] except Exception as e: print("Error: " + str(e)) def __isValidState(self): return self._peripheral.getState() == "conn" def __write(self, byteArr): self._peripheral.writeCharacteristic(self._characteristic.getHandle(), byteArr) def set_pin(self, index, intensity): """Sets a pin to a given intensity. index: an integer from 0 - 6 intensity: an integer from 0 - 255 """ if self.__isValidState(): rList=[0,index,intensity] self.__write(bytes(rList)) def set_frequency(self,frequency): """Sets the frequency of the entire vest. frequency. """ if self.__isValidState(): rList=[4, frequency & (255), (frequency & (255 << 8)) >> 8, (frequency & (255 << 16)) >> 16, (frequency & (255 << 24)) >> 24] b = bytes(rList) self.__write(b) def mute(self): """Stops all motors on the vest from vibrating""" if self.__isValidState(): rList=[3] self.__write(bytes(rList)) def set_motor(self,index,rotation): """ Sets a given motor index to a given target rotation. """ if self.__isValidState(): rList = [11,index,rotation] self.__write(bytes(rList)) def set_motor_speed(self,speed): """ Changes how long it takes to move 1 degree per millisecond. """ if speed <= 0: raise ValueError("speed must be greater than 0.") rList = [12,speed] self.__write(bytes(rList)) def set_pins_batched(self, values = dict): for pin in values: self.set_pin(pin, values[pin])
import struct from bluepy.btle import Scanner, DefaultDelegate, Peripheral device = Peripheral() device.connect('78:A3:52:00:04:2D') services = device.getServices() characteristics = device.getCharacteristics() descriptors = device.getDescriptors() state = device.getState() #displays all services for service in services: print(service) print("Handle UUID Properties") print("-------------------------------------------------------") for ch in characteristics: print(" 0x"+ format(ch.getHandle(),'02X') +" "+str(ch.uuid) +" " + ch.propertiesToString()) if ch.uuid == '19b10011-e8f2-537e-4f6c-d104768a1214': print(struct.unpack('i', ch.read())[0])
class RileyLink: def __init__(self, address=None): self.peripheral = None self.data_handle = None if address is None: if os.path.exists(RILEYLINK_MAC_FILE): with open(RILEYLINK_MAC_FILE, "r") as stream: address = stream.read() self.address = address self.service = None self.response_handle = None self.notify_event = Event() 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) self.init_radio(force_initialize) except BTLEException: if self.peripheral is not None: self.disconnect() raise def disconnect(self, ignore_errors=True): try: if self.peripheral is None: logging.info("Already disconnected") return logging.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 as btlee: if ignore_errors: logging.warning( "Ignoring btle exception during disconnect: %s" % btlee) 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]) logging.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 RileyLinkError( "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") logging.debug("RL reports version string: %s" % version) try: with open(RILEYLINK_VERSION_FILE, "w") as stream: stream.write(version) except IOError: logging.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 RileyLinkError( "Failed to parse firmware version string: %s" % version) v_major = int(m.group(1)) v_minor = int(m.group(2)) logging.debug("Interpreted version major: %d minor: %d" % (v_major, v_minor)) return version, v_major, v_minor except Exception as ex: raise RileyLinkError( "Failed to parse firmware version string: %s" % version) from ex except IOError: logging.exception("Error reading version file") except RileyLinkError: raise response = self._command(Command.GET_VERSION) if response is not None and len(response) > 0: version = response.decode("ascii") logging.debug("RL reports version string: %s" % version) try: m = re.search(".+([0-9]+)\\.([0-9]+)", version) if m is None: raise RileyLinkError( "Failed to parse firmware version string: %s" % version) v_major = int(m.group(1)) v_minor = int(m.group(2)) logging.debug("Interpreted version major: %d minor: %d" % (v_major, v_minor)) return (version, v_major, v_minor) except RileyLinkError: raise except Exception as ex: raise RileyLinkError( "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: logging.error("Firmware version is below 2.0") raise RileyLinkError( "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, 0x31])) self._command(Command.UPDATE_REGISTER, bytes([Register.TEST0, 0x09])) self._command(Command.UPDATE_REGISTER, bytes([Register.PATABLE0, 0x84])) 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 RileyLinkError( "Rileylink state is not OK. Response returned: %s" % response) except RileyLinkError as rle: logging.error("Error while initializing rileylink radio: %s", rle) raise 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 RileyLinkError as rle: logging.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): logging.debug("sending packet: %s" % packet.hex()) 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 RileyLinkError as rle: logging.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 RileyLinkError as rle: logging.error("Error while sending data: %s", rle) raise def _findRileyLink(self): scanner = Scanner() found = None logging.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: logging.debug("Found RileyLink") found = result.addr try: with open(RILEYLINK_MAC_FILE, "w") as stream: stream.write(result.addr) except IOError: logging.warning( "Cannot store rileylink mac address for later") break if found is None: raise RileyLinkError("Could not find RileyLink") return found def _connect_retry(self, retries): while retries > 0: retries -= 1 logging.info("Connecting to RileyLink, retries left: %d" % retries) try: self.peripheral.connect(self.address) logging.info("Connected") break except BTLEException as btlee: logging.warning("BTLE exception trying to connect: %s" % btlee) time.sleep(2) def _command(self, command_type, command_data=None, timeout=10.0): 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 RileyLinkError( "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 RileyLinkError("RileyLink returned no response") else: if response[0] == Response.COMMAND_SUCCESS: return response[1:] elif response[0] == Response.COMMAND_INTERRUPTED: logging.warning("A previous command was interrupted") return response[1:] elif response[0] == Response.RX_TIMEOUT: return None else: raise RileyLinkError( "RileyLink returned error code: %02X. Additional response data: %s" % (response[0], response[1:]), response[0])
from bluepy.btle import Scanner, Peripheral import signal scanner = Scanner() devices = scanner.scan(3) addr = None for d in devices: for (adtype, desc, value) in d.getScanData(): if adtype == 9 and value == 'CC2650 SensorTag': print('SensorTag: {}, RSSI: {}dB'.format(d.addr, d.rssi)) addr = d.addr if addr != None: # 接続開始 tag = Peripheral(addr) # 接続状態の確認 状態が`conn`なら接続できている print('State: {}'.format(tag.getState())) print('Press Ctrl+C to exit.') signal.pause() print('SensorTag not found.')