Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

How to reconnect when Disconnection appear : BLEHci.conn_failed_to_be_established [pc_ble_driver_py]

Hello, I am using the nordic nRF52840-DK with the package pc-ble-driver-py and the example heart_rate_collector.py.

My problem is that sometimes the Bluetooth connection can't connect to the device with this error : 

New connection, conn_handle: 0

Disconnected, conn_handle: 0, reason: BLEHci.conn_failed_to_be_established

I know that this error can sometimes occurred. But I would like to know how can I reconnect automatically just after this error ? 

Knowing that this error is problematic because it stop my python script lunched from a batch file.

Thanks in advance.

Gaetan

Parents
  • Hello,

    I have check but the disconnection is not because of the range. Indeed, the driver works most of the time. 

    Also I already check if the peripherals keep advertising and it doesn't seem to be related because they keep advertising.

    Concerning the python script, it doesn't crash when I disconnect, It crash when a random disconnect appear.

    My question is this one, how to reconnect to the device after a random disconnection in my script.

    Gaetan

Reply
  • Hello,

    I have check but the disconnection is not because of the range. Indeed, the driver works most of the time. 

    Also I already check if the peripherals keep advertising and it doesn't seem to be related because they keep advertising.

    Concerning the python script, it doesn't crash when I disconnect, It crash when a random disconnect appear.

    My question is this one, how to reconnect to the device after a random disconnection in my script.

    Gaetan

Children
  • Again, pc_ble_driver_py is not my strong side.

    gaetan14 said:
    how to reconnect to the device after a random disconnection in my scrip

    How do you connect in the first place? I assume that it is some API that you have already used to connect to your device? Perhaps you can share with me what your current script looks like?

    Best regards,

    Edvin

  • import sys
    import time
    import logging
    import serial
    from queue import Queue, Empty
    from pc_ble_driver_py.observers import *
    
    TARGET_DEV_NAME = "Nordic_HRM"
    CONNECTIONS = 1
    CFG_TAG = 1
    
    global BLEDriver, BLEAdvData, BLEEvtID, BLEAdapter, BLEEnableParams, BLEGapTimeoutSrc, BLEUUID
    from pc_ble_driver_py import config
    
    config.__conn_ic_id__ = "NRF52"
    
    from pc_ble_driver_py.ble_driver import (
        BLEDriver,
        BLEAdvData,
        BLEEvtID,
        BLEEnableParams,
        BLEGapTimeoutSrc,
        BLEUUID,
        BLEUUIDBase,
        BLEGapScanParams,
        BLEConfigCommon,
        BLEConfig,
        BLEConfigConnGatt,
    )
    
    from pc_ble_driver_py.ble_adapter import BLEAdapter, BLEGapConnParams
    
    global nrf_sd_ble_api_ver
    nrf_sd_ble_api_ver = config.sd_api_ver_get()
    
    
    class HRCollector(BLEDriverObserver, BLEAdapterObserver):
        def __init__(self, adapter):
            super(HRCollector, self).__init__()
            self.adapter = adapter
            self.conn_q = Queue()
            self.adapter.observer_register(self)
            self.adapter.driver.observer_register(self)
            self.adapter.default_mtu = 250
    
        def open(self):
            self.adapter.driver.open()
            gatt_cfg = BLEConfigConnGatt()
            gatt_cfg.att_mtu = self.adapter.default_mtu
            gatt_cfg.tag = CFG_TAG
            self.adapter.driver.ble_cfg_set(BLEConfig.conn_gatt, gatt_cfg)
    
            self.adapter.driver.ble_enable()
    
        def close(self):
            self.adapter.driver.close()
    
        def connect_and_discover(self):
            scan_duration = 15
            params = BLEGapScanParams(interval_ms=200, window_ms=150, timeout_s=scan_duration)
    
            self.adapter.driver.ble_gap_scan_start(scan_params=params)
    
            try:
                new_conn = self.conn_q.get(timeout=scan_duration)
                self.adapter.service_discovery(new_conn)
    
                self.adapter.enable_notification(new_conn, BLEUUID(BLEUUID.Standard.battery_level))
                self.adapter.enable_notification(new_conn, BLEUUID(BLEUUID.Standard.heart_rate))
                return new_conn
            except Empty:
                print(f"No heart rate collector advertising with name {TARGET_DEV_NAME} found.")
                return None
    
        def on_gap_evt_connected(
                self, ble_driver, conn_handle, peer_addr, role, conn_params
        ):
            print("New connection: {}".format(conn_handle))
            self.conn_q.put(conn_handle)
    
        def on_gap_evt_disconnected(self, ble_driver, conn_handle, reason):
            print("Disconnected: {} {}".format(conn_handle, reason))
    
        def on_gap_evt_adv_report(
                self, ble_driver, conn_handle, peer_addr, rssi, adv_type, adv_data
        ):
            if BLEAdvData.Types.complete_local_name in adv_data.records:
                dev_name_list = adv_data.records[BLEAdvData.Types.complete_local_name]
    
            elif BLEAdvData.Types.short_local_name in adv_data.records:
                dev_name_list = adv_data.records[BLEAdvData.Types.short_local_name]
    
            else:
                return
    
            dev_name = "".join(chr(e) for e in dev_name_list)
            address_string = "".join("{0:02X}".format(b) for b in peer_addr.addr)
            print(
                "Received advertisment report, address: 0x{}, device_name: {}".format(
                    address_string, dev_name
                )
            )
    
            if dev_name == TARGET_DEV_NAME:
                conn_params = BLEGapConnParams(min_conn_interval_ms=100, max_conn_interval_ms=200, conn_sup_timeout_ms=4000, slave_latency=0)
                self.adapter.connect(peer_addr, conn_params=conn_params, tag=CFG_TAG)
    
        def on_notification(self, ble_adapter, conn_handle, uuid, data):
            if len(data) > 32:
                data = "({}...)".format(data[0:10])
            print("Connection: {}, {} = {}".format(conn_handle, uuid, data))
    
    
    def main():
        selected_serial_port = "COM60"
        print("Serial port used: {}".format(selected_serial_port))
        driver = BLEDriver(
            serial_port=selected_serial_port, auto_flash=False, baud_rate=1000000, log_severity_level="info"
        )
    
        adapter = BLEAdapter(driver)
        collector = HRCollector(adapter)
        collector.open()
        conn = collector.connect_and_discover()
    
        if conn is not None:
            time.sleep(15)
        if conn is None:
            print("disconnect")
            collector.close()
        collector.close()
    
    
    if __name__ == "__main__":
        main()

    In the first place, I connect the same way as the example heart_rate_collector.py. But unfortunately this script seems to have some issue with the connection

  • So you have this disconnected callback function:
    on_gap_evt_disconnected()

    Is that triggered when you fail to connect? If so, can you try to trigger a new connect from within this function, in addition to only printing  "Disconnected: {} {}"?

    BR,

    Edvin

  • Yes the function on_gap_evt_disconnected() is triggered when it disconnect but as you can see in the picture, a raise NordicSemiException appear and stop the code. Because of that exception the script stop and i can't try to reconnect.

    Do you have an idea to solve this ?

    BR,

    Gaetan

  • My service discovery doesn't look like that. I see that the latest version on github has the same implementation as you, though.

    FYI, mine looks like this:

        @NordicSemiErrorCheck(expected=BLEGattStatusCode.success)
        def service_discovery(self, conn_handle, uuid=None):
            vendor_services = []
            self.driver.ble_gattc_prim_srvc_disc(conn_handle, uuid, 0x0001)
    
            while True:
                response = self.evt_sync[conn_handle].wait(
                    evt=BLEEvtID.gattc_evt_prim_srvc_disc_rsp
                )
    
                if response["status"] == BLEGattStatusCode.success:
                    for s in response["services"]:
                        if s.uuid.value == BLEUUID.Standard.unknown:
                            vendor_services.append(s)
                        else:
                            self.db_conns[conn_handle].services.append(s)
                elif response["status"] == BLEGattStatusCode.attribute_not_found:
                    break
                else:
                    return response["status"]
    
                if response["services"][-1].end_handle == 0xFFFF:
                    break
                else:
                    self.driver.ble_gattc_prim_srvc_disc(
                        conn_handle, uuid, response["services"][-1].end_handle + 1
                    )
    
            for s in vendor_services:
                # Read service handle to obtain full 128-bit UUID.
                self.driver.ble_gattc_read(conn_handle, s.start_handle, 0)
                response = self.evt_sync[conn_handle].wait(evt=BLEEvtID.gattc_evt_read_rsp)
                if response["status"] != BLEGattStatusCode.success:
                    continue
    
                # Check response length.
                if len(response["data"]) != 16:
                    continue
    
                # Create UUIDBase object and register it in softdevice
                base = BLEUUIDBase(
                    response["data"][::-1], driver.BLE_UUID_TYPE_VENDOR_BEGIN
                )
                self.driver.ble_vs_uuid_add(base)
    
                # Rediscover this service.
                self.driver.ble_gattc_prim_srvc_disc(conn_handle, uuid, s.start_handle)
                response = self.evt_sync[conn_handle].wait(
                    evt=BLEEvtID.gattc_evt_prim_srvc_disc_rsp
                )
                if response["status"] == BLEGattStatusCode.success:
                    # Assign UUIDBase manually
                    # See:
                    #  https://github.com/NordicSemiconductor/pc-ble-driver-py/issues/38
                    for s in response["services"]:
                        s.uuid.base = base
                    self.db_conns[conn_handle].services.extend(response["services"])
    
            for s in self.db_conns[conn_handle].services:
                self.driver.ble_gattc_char_disc(conn_handle, s.start_handle, s.end_handle)
                while True:
                    response = self.evt_sync[conn_handle].wait(
                        evt=BLEEvtID.gattc_evt_char_disc_rsp
                    )
                    if response["status"] == BLEGattStatusCode.success:
                        for char in response["characteristics"]:
                            s.char_add(char)
                    elif response["status"] == BLEGattStatusCode.attribute_not_found:
                        break
                    else:
                        return response["status"]
    
                    self.driver.ble_gattc_char_disc(
                        conn_handle,
                        response["characteristics"][-1].handle_decl + 1,
                        s.end_handle,
                    )
    
                for ch in s.chars:
                    self.driver.ble_gattc_desc_disc(
                        conn_handle, ch.handle_value, ch.end_handle
                    )
                    while True:
                        response = self.evt_sync[conn_handle].wait(
                            evt=BLEEvtID.gattc_evt_desc_disc_rsp
                        )
                        if response["status"] == BLEGattStatusCode.success:
                            ch.descs.extend(response["descriptors"])
                        elif response["status"] == BLEGattStatusCode.attribute_not_found:
                            break
                        else:
                            return response["status"]
    
                        if response["descriptors"][-1].handle == ch.end_handle:
                            break
                        else:
                            self.driver.ble_gattc_desc_disc(
                                conn_handle,
                                response["descriptors"][-1].handle + 1,
                                ch.end_handle,
                            )
            return BLEGattStatusCode.success

    Either way, if you want to continue after the disconnection, you should remove the exception that is raised. There is no need to continue the service discovery if they are not connected. Instead, you should start scanning again after the disconnect.

    Best regards,

    Edvin

Related