def main(serial_port): # Create and open the device ble_device = BleDevice(serial_port) ble_device.configure() ble_device.open() # Set up desired security parameters ble_device.client.security.set_security_params(passcode_pairing=False, bond=False, lesc_pairing=False, io_capabilities=IoCapabilities.DISPLAY_ONLY, out_of_band=False) ble_device.client.security.on_pairing_complete.register(on_client_pairing_complete) ble_device.client.security.on_passkey_display_required.register(on_passkey_display) ble_device.client.security.on_passkey_required.register(on_passkey_entry) ble_device.client.security.on_security_level_changed.register(on_security_level_changed) # Create and add the math service service = ble_device.database.add_service(constants.MATH_SERVICE_UUID) # Create and add the hex conversion characteristic to the service hex_conv_char = service.add_characteristic(constants.HEX_CONVERT_CHAR_UUID, constants.HEX_CONVERT_CHAR_PROPERTIES, "Test Data") # Register the callback for when a write occurs and subscription state changes hex_conv_char.on_write.register(on_hex_conversion_characteristic_write) hex_conv_char.on_subscription_change.register(on_gatts_subscription_state_changed) # Create and add the counting characteristic, initializing the data to [0, 0, 0, 0] counting_char = service.add_characteristic(constants.COUNTING_CHAR_UUID, constants.COUNTING_CHAR_PROPERTIES, [0]*4) counting_char.on_subscription_change.register(on_gatts_subscription_state_changed) # Create the thread for the counting characteristic counting_char_thread = CountingCharacteristicThread(counting_char) # Create and add the time service time_service = ble_device.database.add_service(constants.TIME_SERVICE_UUID) # Add the time characteristic and register the callback for when its read time_char = time_service.add_characteristic(constants.TIME_CHAR_UUID, constants.TIME_CHAR_PROPERTIES, "Time") time_char.on_read.register(on_time_char_read) # Initialize the advertising and scan response data adv_data = advertising.AdvertisingData(local_name=constants.PERIPHERAL_NAME, flags=0x06) scan_data = advertising.AdvertisingData(service_uuid128s=constants.TIME_SERVICE_UUID, has_more_uuid128_services=True) ble_device.advertiser.set_advertise_data(adv_data, scan_data) # Start advertising logger.info("Advertising") ble_device.client.on_connect.register(on_connect) ble_device.client.on_disconnect.register(on_disconnect) ble_device.advertiser.start(timeout_sec=0, auto_restart=True) # Create a waitable that will never fire, and wait for some time w = GenericWaitable() w.wait(60*30, exception_on_timeout=False) # Keep device active for 30 mins # Cleanup counting_char_thread.join() logger.info("Done") ble_device.close()
def _configure_device(dev_number, config, optional=False): env_key = BLATANN_DEV_ENVKEY_FORMAT.format(dev_number) comport = os.environ.get(env_key, None) if not comport: if optional: return None raise EnvironmentError( f"Environment variable {env_key} must be defined with the device's comport" ) dev = BleDevice(comport, bond_db_filename=BOND_DB_FILE_FMT.format(dev_number)) dev.configure(**config) dev.event_logger.suppress(nrf_events.GapEvtAdvReport) return dev
def main(serial_port): ble_device = BleDevice(serial_port) # Configure the BLE device to support MTU sizes which allow the max data length extension PDU size # Note this isn't 100% necessary as the default configuration sets the max to this value also ble_device.configure(att_mtu_max_size=MTU_SIZE_FOR_MAX_DLE) ble_device.open() # Create and add the Nordic UART service nus = nordic_uart.add_nordic_uart_service(ble_device.database) nus.on_data_received.register(on_data_rx) nus.on_write_complete.register(on_tx_complete) # Register listeners for when the client connects and disconnects ble_device.client.on_connect.register(on_connect) ble_device.client.on_disconnect.register(on_disconnect) ble_device.client.on_mtu_size_updated.register(on_mtu_size_update) # Configure the client to prefer the max MTU size ble_device.client.preferred_mtu_size = ble_device.max_mtu_size ble_device.client.set_connection_parameters(7.5, 15, 4000) # Advertise the service UUID adv_data = advertising.AdvertisingData(flags=0x06, local_name="Nordic UART Server") scan_data = advertising.AdvertisingData( service_uuid128s=nordic_uart.NORDIC_UART_SERVICE_UUID) ble_device.advertiser.set_advertise_data(adv_data, scan_data) logger.info("Advertising") ble_device.advertiser.start(timeout_sec=0, auto_restart=True) # Create a waitable that waits 5 minutes then exits w = GenericWaitable() try: w.wait(5 * 60, exception_on_timeout=False) except KeyboardInterrupt: pass finally: ble_device.close()
def main(serial_port): # Open the BLE Device and suppress spammy log messages ble_device = BleDevice(serial_port) ble_device.event_logger.suppress(nrf_events.GapEvtAdvReport) # Configure the BLE device to support MTU sizes which allow the max data length extension PDU size # Note this isn't 100% necessary as the default configuration sets the max to this value also ble_device.configure(att_mtu_max_size=MTU_SIZE_FOR_MAX_DLE) ble_device.open() # Set scan duration for 4 seconds ble_device.scanner.set_default_scan_params(timeout_seconds=4) ble_device.set_default_peripheral_connection_params(10, 30, 4000) logger.info("Scanning for peripherals advertising UUID {}".format( nordic_uart.NORDIC_UART_SERVICE_UUID)) target_address = None # Start scan and wait for it to complete scan_report = ble_device.scanner.start_scan().wait() # Search each peer's advertising data for the Nordic UART Service UUID to be advertised for report in scan_report.advertising_peers_found: if nordic_uart.NORDIC_UART_SERVICE_UUID in report.advertise_data.service_uuid128s and report.device_name == "Nordic UART Server": target_address = report.peer_address break if not target_address: logger.info("Did not find peripheral advertising Nordic UART service") return # Initiate connection and wait for it to finish logger.info("Found match: connecting to address {}".format(target_address)) peer = ble_device.connect(target_address).wait() if not peer: logger.warning("Timed out connecting to device") return logger.info("Connected, conn_handle: {}".format(peer.conn_handle)) logger.info("Exchanging MTU") peer.exchange_mtu(peer.max_mtu_size).wait(10) logger.info("MTU Exchange complete, discovering services") # Initiate service discovery and wait for it to complete _, event_args = peer.discover_services().wait(exception_on_timeout=False) logger.info("Service discovery complete! status: {}".format( event_args.status)) uart_service = nordic_uart.find_nordic_uart_service(peer.database) if not uart_service: logger.info( "Failed to find Nordic UART service in peripheral database") peer.disconnect().wait() ble_device.close() return # Initialize the service uart_service.initialize().wait(5) uart_service.on_data_received.register(on_data_rx) while True: data = input("Enter data to send to peripheral (q to exit): ") if data == "q": break uart_service.write(data).wait(10) peer.disconnect().wait() ble_device.close()
def main(serial_port): # Create and open the device ble_device = BleDevice(serial_port) ble_device.configure() ble_device.open() # Create the service service = ble_device.database.add_service( constants.DESC_EXAMPLE_SERVICE_UUID) # Create a characteristic and add some descriptors # NOTE: Some descriptors MUST be added during creation: SCCD, User Description, and Presentation Format # CCCD is added automatically based on the characteristic's Notify and Indicate properties # Define the User Description properties and make it writable # The simplest use-case in which the user description is read-only can provide just the first parameter user_desc_props = gatts.GattsUserDescriptionProperties( "UTC Time", write=True, security_level=smp.SecurityLevel.OPEN, max_length=20, variable_length=True) # Define the presentation format. Returning the time in seconds so set exponent to 0 presentation_format = PresentationFormat(fmt=Format.uint32, exponent=0, unit=Units.time_second) # Create the characteristic properties, including the SCCD, User Description, and Presentation Format char_props = gatts.GattsCharacteristicProperties( read=True, write=False, notify=True, max_length=Uint32.byte_count, variable_length=False, sccd=True, user_description=user_desc_props, presentation_format=presentation_format) char = service.add_characteristic(constants.DESC_EXAMPLE_CHAR_UUID, char_props, Uint32.encode(0)) char.on_read.register(on_read) # Add another descriptor to the list char_range_value = Uint32.encode(5555) + Uint32.encode(2**32 - 1000) desc_props = GattsAttributeProperties(read=True, write=False, variable_length=False, max_length=len(char_range_value)) char.add_descriptor(DescriptorUuid.valid_range, desc_props, char_range_value) # Initialize the advertising and scan response data adv_data = advertising.AdvertisingData( local_name=constants.PERIPHERAL_NAME, flags=0x06) scan_data = advertising.AdvertisingData( service_uuid128s=constants.DESC_EXAMPLE_SERVICE_UUID, has_more_uuid128_services=False) ble_device.advertiser.set_advertise_data(adv_data, scan_data) # Start advertising logger.info("Advertising") ble_device.client.on_connect.register(on_connect) ble_device.client.on_disconnect.register(on_disconnect) ble_device.advertiser.start(timeout_sec=0, auto_restart=True) # Create a waitable that will never fire, and wait for some time w = GenericWaitable() w.wait(60 * 30, exception_on_timeout=False) # Keep device active for 30 mins logger.info("Done") ble_device.close()
def main(serial_port): # Create and open the device ble_device = BleDevice(serial_port) ble_device.configure() ble_device.open() ble_device.scanner.set_default_scan_params(timeout_seconds=4) # Set the target to the peripheral's advertised name target_device_name = constants.PERIPHERAL_NAME logger.info("Scanning for '{}'".format(target_device_name)) target_address = example_utils.find_target_device(ble_device, target_device_name) if not target_address: logger.info("Did not find target peripheral") return # Initiate the connection and wait for it to finish logger.info("Found match: connecting to address {}".format(target_address)) peer = ble_device.connect(target_address).wait() if not peer: logger.warning("Timed out connecting to device") return logger.info("Connected, conn_handle: {}".format(peer.conn_handle)) # Wait up to 10 seconds for service discovery to complete _, event_args = peer.discover_services().wait(10, exception_on_timeout=False) logger.info("Service discovery complete! status: {}".format(event_args.status)) # Find the Time characteristic time_char = peer.database.find_characteristic(constants.DESC_EXAMPLE_CHAR_UUID) if not time_char: logger.info("Did not find the time characteristic") else: logger.info("Reading all characteristic attributes") for attr in time_char.attributes: logger.info(f"Reading UUID {attr.uuid} - {attr.uuid.description or '[unknown]'}") _, event_args = attr.read().wait(5) if event_args.status == GattStatusCode.success: # Show as hex unless it's the user descriptor UUID which should be a string if attr.uuid == DescriptorUuid.user_description: value = event_args.value.decode("utf8") else: value = binascii.hexlify(event_args.value) logger.info(f" Value: {value}") else: logger.warning(f" Failed to read attribute, status: {event_args.status}") # Read the characteristic's Presentation Format descriptor directly and decode the value presentation_fmt_desc = time_char.find_descriptor(DescriptorUuid.presentation_format) if presentation_fmt_desc: # Read, then decode the value using the PresentationFormat type logger.info("Reading the presentation format descriptor directly") _, event_args = presentation_fmt_desc.read().wait(5) if event_args.status == GattStatusCode.success: try: fmt = PresentationFormat.decode(event_args.value) logger.info(f"Presentation Format: {str(fmt.format)}, Exponent: {fmt.exponent}, Unit: {str(fmt.unit)}") except Exception as e: logger.error("Failed to decode the presentation format descriptor") logger.exception(e) else: logger.info("Failed to find the presentation format descriptor") # Clean up logger.info("Disconnecting from peripheral") peer.disconnect().wait() ble_device.close()