Exemple #1
0
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
Exemple #2
0
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()
Exemple #3
0
def main(serial_port):
    stop_event = threading.Event()
    threading.Thread(target=wait_for_user_stop, args=(stop_event, )).start()
    time.sleep(2)

    ble_device = BleDevice(serial_port)
    ble_device.open()

    interval_ms = 100  # Send out an advertising packet every 500ms
    timeout_sec = 0  # Advertise forever
    mode = AdvertisingMode.non_connectable_undirected  # Set mode to not allow connections

    adv_flags = AdvertisingFlags.BR_EDR_NOT_SUPPORTED | AdvertisingFlags.GENERAL_DISCOVERY_MODE
    adv_data = AdvertisingData(flags=adv_flags,
                               local_name="Time",
                               service_data=_get_time_service_data())

    ble_device.advertiser.set_advertise_data(adv_data)
    ble_device.advertiser.start(interval_ms,
                                timeout_sec,
                                auto_restart=True,
                                advertise_mode=mode)

    while True:
        # Update the advertising data every 1 second
        stop_event.wait(1)
        if stop_event.is_set(
        ):  # User stopped execution, break out and stop advertising
            break
        # Update the service data and set it in the BLE device
        adv_data.service_data = _get_time_service_data()
        ble_device.advertiser.set_advertise_data(adv_data)

    ble_device.advertiser.stop()
    ble_device.close()
Exemple #4
0
def main(serial_port):
    # Create and open te device
    ble_device = BleDevice(serial_port)
    ble_device.open()

    # Create and add the Device Info Service to the database
    dis = device_info.add_device_info_service(ble_device.database)

    # Set some characteristics in the DIS. The service only contains characteristics which
    # have values set. The other ones are not present
    dis.set_software_revision("14.2.1")
    dis.set_hardware_revision("A")
    dis.set_firmware_revision("1.0.4")
    dis.set_serial_number("AB1234")
    pnp_id = device_info.PnpId(device_info.PnpVendorSource.bluetooth_sig, 0x0058, 0x0002, 0x0013)
    dis.set_pnp_id(pnp_id)

    # Initiate the advertising data. Advertise the name and DIS service UUID
    name = "Peripheral DIS"
    adv_data = advertising.AdvertisingData(local_name=name, service_uuid16s=device_info.DIS_SERVICE_UUID)
    ble_device.advertiser.set_advertise_data(adv_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
    logger.info("Done")
    ble_device.close()
Exemple #5
0
def main(serial_port):
    # Create and open the device
    ble_device = BleDevice(serial_port)
    ble_device.open()

    # Create and add the current time service to the database.
    # Tweak the flags below to change how the service is set up
    current_time_service = current_time.add_current_time_service(ble_device.database,
                                                                 enable_writes=True,
                                                                 enable_local_time_info=True,
                                                                 enable_reference_info=False)

    # Register handlers for when the characteristics are written to.
    # These will only be triggered if enable_writes above is true
    current_time_service.on_current_time_write.register(on_current_time_write)
    current_time_service.on_local_time_info_write.register(on_local_time_info_write)

    # Demo of the different ways to manually or automatically control the reported time

    # Example 1: Automatically reference system time.
    #            All logic is handled within the service and reports the time whenever the characteristic is read
    # Example 2: Manually report the time 1 day behind using callback method.
    #            Register a user-defined callback to retrieve the current time to report back to the client
    # Example 3: Manually report the time 1 hour ahead by setting the base time
    #            Set the characteristic's base time to 1 day ahead and allow the service to auto-increment from there
    example_mode = 1

    if example_mode == 1:
        # configure_automatic() also sets up the Local Time characteristic (if enabled)
        # to just set the automatic time and leave Local Time unconfigured, use set_time() with no parameters
        current_time_service.configure_automatic()
    elif example_mode == 2:
        def on_time_read():
            d = datetime.datetime.now() - datetime.timedelta(days=1)
            logger.info("Getting time: {}".format(d))
            return d
        current_time_service.set_time(characteristic_read_callback=on_time_read)
    elif example_mode == 3:
        base_time = datetime.datetime.now() + datetime.timedelta(hours=1)
        current_time_service.set_time(base_time)

    # 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)

    # Advertise the Current Time service
    adv_data = advertising.AdvertisingData(local_name="Current Time", flags=0x06,
                                           service_uuid16s=current_time.CURRENT_TIME_SERVICE_UUID)
    ble_device.advertiser.set_advertise_data(adv_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()
    w.wait(5 * 60, exception_on_timeout=False)

    logger.info("Done")
    ble_device.close()
def main(serial_port):
    # Create and open the device
    ble_device = BleDevice(serial_port)
    ble_device.open()

    # Create a database to store the readings
    glucose_database = glucose.BasicGlucoseDatabase()
    # Add the service to the BLE database, using the glucose database just created, require encryption at the minimum
    service = glucose.add_glucose_service(ble_device.database,
                                          glucose_database,
                                          glucose.SecurityLevel.JUST_WORKS)

    # Set the features of this "glucose sensor"
    features = glucose.GlucoseFeatures(
        GlucoseFeatureType.low_battery_detection,
        GlucoseFeatureType.strip_insertion_error_detection)
    service.set_features(features)

    # Add some measurements to the glucose database
    add_fake_glucose_readings(glucose_database)

    # 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)

    # Set the connection parameters for the client
    ble_device.client.set_connection_parameters(15, 30, 4000)

    # Set the function to display the passkey
    ble_device.client.security.on_passkey_display_required.register(
        display_passkey)

    # Set the security parameters for the client
    ble_device.client.security.set_security_params(
        passcode_pairing=False,
        bond=False,
        io_capabilities=IoCapabilities.DISPLAY_ONLY,
        out_of_band=False)

    # Advertise the Glucose service
    adv_data = advertising.AdvertisingData(
        local_name="Glucose Test",
        flags=0x06,
        service_uuid16s=glucose.GLUCOSE_SERVICE_UUID)
    ble_device.advertiser.set_advertise_data(adv_data)

    logger.info("Advertising")
    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()
Exemple #7
0
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()
Exemple #8
0
def main(serial_port):
    # Create and open the device
    ble_device = BleDevice(serial_port)
    ble_device.open()

    logger.info("Scanning...")
    # Set scanning for 4 seconds
    ble_device.scanner.set_default_scan_params(timeout_seconds=4)

    # Start scanning and wait for it to complete
    scan_report = ble_device.scanner.start_scan().wait()
    print("")
    logger.info("Finished scanning. Scan reports:")

    # Iterate through all the peers found and print out the reports
    for report in scan_report.advertising_peers_found:
        logger.info(report)

    # Clean up and close the device
    ble_device.close()
Exemple #9
0
def main(serial_port):
    target_device_name = constants.PERIPHERAL_NAME
    # Create a waitable that will block the main thread until notified by one of the classes above
    main_thread_waitable = GenericWaitable()

    # Create and open the BLE device (and suppress spammy logs)
    ble_device = BleDevice(serial_port)
    ble_device.event_logger.suppress(nrf_events.GapEvtAdvReport)
    ble_device.open()

    # Create the connection manager and start the scanning process
    s = ConnectionManager(ble_device, main_thread_waitable)
    s.scan_and_connect(target_device_name)

    # Block the main thread indefinitely until the program finishes
    main_thread_waitable.wait()

    # Clean up, disconnect if a peer was connected to
    if s.peer:
        s.peer.disconnect().wait()
    ble_device.close()
Exemple #10
0
def main(serial_port):
    # Create and open the device
    ble_device = BleDevice(serial_port)
    ble_device.open()

    # Create and add the battery service to the database
    battery_service = battery.add_battery_service(ble_device.database,
                                                  enable_notifications=True)
    battery_service.set_battery_level(100, False)

    # 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)

    # Leaving security and connection parameters as defaults (don't care)

    # Advertise the Battery Service
    adv_data = advertising.AdvertisingData(
        local_name="Battery Test",
        flags=0x06,
        service_uuid16s=battery.BATTERY_SERVICE_UUID)
    ble_device.advertiser.set_advertise_data(adv_data)

    logger.info("Advertising")
    ble_device.advertiser.start(timeout_sec=0, auto_restart=True)

    battery_level = 100
    battery_level_decrement_steps = 1
    time_between_steps = 10

    # Decrement the battery level until it runs out
    while battery_level >= 0:
        time.sleep(time_between_steps)
        battery_level -= battery_level_decrement_steps
        logger.info("Updating battery level to {}".format(battery_level))
        battery_service.set_battery_level(battery_level)

    logger.info("Done")
    ble_device.close()
Exemple #11
0
def main(serial_port):
    # Create and open the device
    ble_device = BleDevice(serial_port)
    ble_device.open()

    logger.info("Scanning...")
    # Set scanning for 4 seconds
    ble_device.scanner.set_default_scan_params(timeout_seconds=4)

    # Start scanning and iterate through the reports as they're received
    for report in ble_device.scanner.start_scan().scan_reports:
        if not report.duplicate:
            logger.info(report)

    scan_report = ble_device.scanner.scan_report
    print("\n")
    logger.info("Finished scanning. Scan reports by peer address:")
    # Iterate through all the peers found and print out the reports
    for report in scan_report.advertising_peers_found:
        logger.info(report)

    # Clean up and close the device
    ble_device.close()
Exemple #12
0
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()
Exemple #13
0
def main(serial_port):
    # Set the target to the peripheral's advertised name
    target_device_name = constants.PERIPHERAL_NAME

    # Create and open the BLE device (and suppress spammy logs)
    ble_device = BleDevice(serial_port)
    ble_device.event_logger.suppress(nrf_events.GapEvtAdvReport)
    ble_device.open()

    # Set the scanner to scan for 4 seconds
    ble_device.scanner.set_default_scan_params(timeout_seconds=4)

    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

    # Wait up to 10 seconds for service discovery to complete
    logger.info("Connected, conn_handle: {}".format(peer.conn_handle))
    _, event_args = peer.discover_services().wait(10, exception_on_timeout=False)
    logger.info("Service discovery complete! status: {}".format(event_args.status))

    # Log each service found
    for service in peer.database.services:
        logger.info(service)

    peer.set_connection_parameters(100, 120, 6000)  # Discovery complete, go to a longer connection interval

    # Pair with the peripheral
    def on_passkey_entry(peer, passkey_event_args):
        """
        Callback for when the user is requested to enter a passkey to resume the pairing process.
        Requests the user to enter the passkey and resolves the event with the passkey entered

        :param peer: the peer the passkey is for
        :param passkey_event_args:
        :type passkey_event_args: blatann.event_args.PasskeyEntryEventArgs
        """
        passkey = input("Enter peripheral passkey: ")
        passkey_event_args.resolve(passkey)

    # Setup the security parameters
    peer.security.set_security_params(passcode_pairing=True, io_capabilities=smp.IoCapabilities.KEYBOARD_DISPLAY,
                                      bond=False, out_of_band=False)
    # Register the callback for when a passkey needs to be entered by the user
    peer.security.on_passkey_required.register(on_passkey_entry)
    # Wait up to 60 seconds for the pairing process
    peer.security.pair().wait(60)

    # Find the counting characteristic
    counting_char = peer.database.find_characteristic(constants.COUNTING_CHAR_UUID)
    if counting_char:
        logger.info("Subscribing to the counting characteristic")
        counting_char.subscribe(on_counting_char_notification).wait(5)
    else:
        logger.warning("Failed to find counting characteristic")

    # Find the hex conversion characteristic. This characteristic takes in a bytestream and converts it to its
    # hex representation. e.g. '0123' -> '30313233'
    hex_convert_char = peer.database.find_characteristic(constants.HEX_CONVERT_CHAR_UUID)
    if hex_convert_char:
        # Generate some data ABCDEFG... Then, incrementally send increasing lengths of strings.
        # i.e. first send 'A', then 'AB', then 'ABC'...
        data_to_convert = bytes(ord('A') + i for i in range(12))
        for i in range(len(data_to_convert)):
            data_to_send = data_to_convert[:i+1]
            logger.info("Converting to hex data: '{}'".format(data_to_send))

            # Write the data, waiting up to 5 seconds for the write to complete
            if not hex_convert_char.write(data_to_send).wait(5, False):
                logger.error("Failed to write data, i={}".format(i))
                break

            # Write was successful, when we read the characteristic the peripheral should have converted the string
            # Once again, initiate a read and wait up to 5 seconds for the read to complete
            char, event_args = hex_convert_char.read().wait(5, False)
            logger.info("Hex: '{}'".format(event_args.value.decode("ascii")))
    else:
        logger.warning("Failed to find hex convert char")

    # Clean up
    logger.info("Disconnecting from peripheral")
    peer.disconnect().wait()
    ble_device.close()
Exemple #14
0
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()
Exemple #15
0
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()
Exemple #16
0
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)
    ble_device.open()

    # Set scan duration to 4 seconds
    ble_device.scanner.set_default_scan_params(timeout_seconds=4)
    logger.info("Scanning for peripherals advertising UUID {}".format(
        device_info.DIS_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 DIS Service UUID to be advertised
    for report in scan_report.advertising_peers_found:
        if device_info.DIS_SERVICE_UUID in report.advertise_data.service_uuid16s:
            target_address = report.peer_address
            break

    if not target_address:
        logger.info("Did not find peripheral advertising DIS 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))
    # Initiate service discovery and wait for it 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 device info service in the peer's database
    dis = device_info.find_device_info_service(peer.database)
    if not dis:
        logger.info(
            "Failed to find Device Info Service in peripheral database")
        peer.disconnect().wait()
        return

    # Example 1:
    # Iterate through all possible device info characteristics, read the value if present in service
    for char in device_info.CHARACTERISTICS:
        if dis.has(char):
            logger.info("Reading characteristic: {}...".format(char))
            char, event_args = dis.get(char).wait()
            if isinstance(event_args.value, bytes):
                value = event_args.value.decode("utf8")
            else:
                value = event_args.value
            logger.info("Value: {}".format(value))

    # Example 2:
    # Read specific characteristics, if present in the service
    if dis.has_software_revision:
        char, event_args = dis.get_software_revision().wait()
        sw_version = event_args.value
        logger.info("Software Version: {}".format(sw_version.decode("utf8")))
    if dis.has_pnp_id:
        char, event_args = dis.get_pnp_id().wait()
        pnp_id = event_args.value  # type: device_info.PnpId
        logger.info("Vendor ID: {}".format(pnp_id.vendor_id))
    if dis.has_system_id:
        char, event_args = dis.get_system_id().wait()
        system_id = event_args.value  # type: device_info.SystemId
        logger.info("System ID: {}".format(system_id))

    # Disconnect and close device
    peer.disconnect().wait()
    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)
    ble_device.open()

    # Set scan duration for 4 seconds
    ble_device.scanner.set_default_scan_params(timeout_seconds=4)
    logger.info("Scanning for peripherals advertising UUID {}".format(
        battery.BATTERY_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 Battery Service UUID to be advertised
    for report in scan_report.advertising_peers_found:
        if battery.BATTERY_SERVICE_UUID in report.advertise_data.service_uuid16s:
            target_address = report.peer_address
            break

    if not target_address:
        logger.info("Did not find peripheral advertising battery 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))
    # Initiate service discovery and wait for it 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 battery service within the peer's database
    battery_service = battery.find_battery_service(peer.database)
    if not battery_service:
        logger.info("Failed to find Battery Service in peripheral database")
        peer.disconnect().wait()
        return

    # Read out the battery level
    logger.info("Reading battery level...")
    _, event_args = battery_service.read().wait()
    battery_percent = event_args.value
    logger.info("Battery: {}%".format(battery_percent))

    if battery_service.can_enable_notifications:
        battery_service.on_battery_level_updated.register(
            on_battery_level_update)
        battery_service.enable_notifications().wait()

        wait_duration = 30
        logger.info("Waiting {} seconds for any battery notifications")
        time.sleep(wait_duration)

    # Clean up
    logger.info("Disconnecting from peripheral")
    peer.disconnect().wait()
    ble_device.close()