This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

pc-ble-driver-py - sd_ble_gap_sec_params_reply fails

I have been using the pc-ble-driver Python bindings to implement a peripheral. I have extended BLEDriver to support sd_ble_gatts_* the functions. I now have a working peripheral, however I have a problem when trying to support bonding.

When the central tries to bond, my code receives a BLE_GAP_EVT_SEC_PARAMS_REQUEST event. I then call BleDriver.ble_gap_sec_params_reply. However the driver appears to hang at this point, as sd_ble_gap_sec_params_reply never returns (I don't even get an error code). I'm using the latest pc-ble-driver-py module on Linux with connectivity_1.0.1_115k2_with_s130_2.0.1.hex on a NRF51 development board.

UPDATE: I've changed the code to call sd_ble_gap_sec_params_reply on the main thread, as suggested by below. The call now returns instead of hanging, but fails with error code 3 (Internal Error). I've updated the below code to show this:

import sys
import time 
import logging
import threading

from pc_ble_driver_py.observers import BLEDriverObserver

logging.basicConfig(level=logging.DEBUG)

def init(conn_ic_id):
    global BLEDriver, BLEAdvData, BLEGapSecStatus, BLEGapSecParams, BLEGapIOCaps, BLEGapSecKDist
    from pc_ble_driver_py import config
    config.__conn_ic_id__ = conn_ic_id
    from pc_ble_driver_py.ble_driver import BLEDriver, BLEAdvData, BLEGapSecStatus, BLEGapSecParams, BLEGapIOCaps, BLEGapSecKDist

class PeripheralTest(BLEDriverObserver):

    def on_gap_evt_connected(self, ble_driver, conn_handle, peer_addr, role, conn_params):
        print('on_gap_evt_connected - conn_handle: {} role: {}'.format(conn_handle, role))

    def on_gap_evt_disconnected(self, ble_driver, conn_handle, reason):
        print('on_gap_evt_disconnected - conn_handle: {} reason: {}'.format(conn_handle, reason))

    def on_gap_evt_sec_params_request(self, ble_driver, conn_handle, peer_params):
        print('on_gap_evt_sec_params_request - conn_handle'.format('conn_handle'))
        for k in ('bond', 'mitm', 'lesc', 'keypress', 'io_caps', 'oob', 'min_key_size', 'max_key_size'):
            val = getattr(peer_params, k)
            print('\t{}: {}'.format(k, val))
        self.sec_params_requested = (ble_driver, conn_handle)
        
    def do_sec_params_reply(self):
        ble_driver, conn_handle = self.sec_params_requested
        self.sec_params_requested = None
        sec_params = BLEGapSecParams(bond = 1, mitm = 0, lesc = 0,keypress = 0, io_caps = BLEGapIOCaps.none, oob = 0, 
            min_key_size = 7, max_key_size = 16,
            kdist_own = BLEGapSecKDist(enc=1, id=1, sign=0, link=0),
            kdist_peer = BLEGapSecKDist(enc=1, id=1, sign=0, link=0))
        
        # this is now called on the main thread, but returns error code 3 - NRF_ERROR_INTERNAL
        ble_driver.ble_gap_sec_params_reply(conn_handle, BLEGapSecStatus.success, sec_params, None, None)

    def loop(self):
        self.sec_params_requested = None
        while True:
            if self.sec_params_requested:
                self.do_sec_params_reply()
            time.sleep(0.1)

def main(serial_port):
    print('Serial port used: {}'.format(serial_port))
    driver = BLEDriver(serial_port=serial_port, auto_flash=False)
    periph = PeripheralTest()
    driver.observer_register(periph)
    driver.open()
    driver.ble_enable()
    driver.ble_gap_adv_data_set(BLEAdvData(complete_local_name='Bond Test'))
    driver.ble_gap_adv_start()
    print('Started advertising')
    try:
        periph.loop()
    except KeyboardInterrupt:
        pass
    print('Closing')
    driver.close()

if __name__ == '__main__':
    serial_port = None
    if len(sys.argv) < 3:
        print("Please specify connectivity IC identifier (NRF51, NRF52) and serial port")
        exit(1)
    init(sys.argv[1])
    serial_port = sys.argv[2]
    main(serial_port)
    quit()
  • I think this is a general problem with the connectivity firmware. I used two nRF Connect for Desktop instances on a pair of nRF51 dev boards. When bonding one to the other I get the same error as my Python script - 'Failed to reply security parameters. (NRF_ERROR_INTERNAL)'. This is using the latest 1.1.0 connectivity firmware, tested on both Windows 10 and Linux. See this similar question. Could it be my board rev? I have two PCA10026 nRF51 dev boards.

  • I have also described the same problem here. After using flag and calling sd_ble_gap_sec_params_reply() form main loop I also get the NRF_ERROR_INTERNAL. Tested with PCA10040.

  • Using gdb, I found that the NRF_ERROR_INTERNAL is caused by a NRF_ERROR_TIMEOUT in H5Transport::send. I tried increasing the timeouts for sd_rpc_data_link_layer_create_bt_three_wire and sd_rpc_transport_layer_create but the error still occurs. I also enabled the log message handler for sd_rpc_open and got the following output:

    on_gap_evt_connected - conn_handle: 0 role: BLEGapRoles.periph
    12/ 0 <-  [02 13 00 00 00 47 00 10 0f 0f ]
                type:     VENDOR_SPECIFIC reliable:yes seq#:4 ack#:3 payload_length:a data_integrity:1 header_checksum:76 err_code:0
        15 ->  []
                type:                 ACK reliable: no seq#:0 ack#:5 payload_length:0 data_integrity:0 err_code:0
    on_gap_evt_sec_params_request - conn_handle
    13/ 0 <-  [02 12 00 00 00 06 00 06 00 00 00 d0 07 ]
                type:     VENDOR_SPECIFIC reliable:yes seq#:5 ack#:3 payload_length:d data_integrity:1 header_checksum:45 err_code:0
        16 ->  []
                type:                 ACK reliable: no seq#:0 ack#:6 payload_length:0 data_integrity:0 err_code:0
    14/ 0 <-  [02 12 00 00 00 28 00 28 00 00 00 d0 07 ]
                type:     VENDOR_SPECIFIC reliable:yes seq#:6 ack#:3 payload_length:d data_integrity:1 header_checksum:44 err_code:0
        17 ->  []
                type:                 ACK reliable: no seq#:0 ack#:7 payload_length:0 data_integrity:0 err_code:0
        18 ->  [00 7f 00 00 00 01 47 00 10 03 03 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ]
                type:     VENDOR_SPECIFIC reliable:yes seq#:3 ack#:7 payload_length:118 data_integrity:1 header_checksum:66 err_code:0
        19 ->  [00 7f 00 00 00 01 47 00 10 03 03 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ]
                type:     VENDOR_SPECIFIC reliable:yes seq#:3 ack#:7 payload_length:118 data_integrity:1 header_checksum:66 err_code:0
        20 ->  [00 7f 00 00 00 01 47 00 10 03 03 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ]
                type:     VENDOR_SPECIFIC reliable:yes seq#:3 ack#:7 payload_length:118 data_integrity:1 header_checksum:66 err_code:0
        21 ->  [00 7f 00 00 00 01 47 00 10 03 03 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ]
                type:     VENDOR_SPECIFIC reliable:yes seq#:3 ack#:7 payload_length:118 data_integrity:1 header_checksum:66 err_code:0
    Traceback (most recent call last):
    File "bond_test2.py", line 60, in <module>
        main(serial_port)
    File "bond_test2.py", line 46, in main
        driver.ble_gap_sec_params_reply(0, BLEGapSecStatus.success, sec_params, None, None)     
    File "/usr/local/lib/python2.7/dist-packages/pc_ble_driver_py/ble_driver.py", line 126, in wrapper
        raise NordicSemiException('Failed to {}. Error code: {}'.format(wrapped.__name__, err_code))
    pc_ble_driver_py.exceptions.NordicSemiException: Failed to ble_gap_sec_params_reply. Error code: 3
    

    edit: I compared the bytes in the debug output to the documented serialization for sd_ble_gap_sec_params_reply. The serialized message is 280 bytes long - but the documentation says it should be no longer than 148 bytes. So it would seem that the message is malformed and causing the connectivity firmware to crash.

    edit 2: i was looking at an old version of the docs. The encoding is correct, but I believe the error is caused by not filling in the ble_gap_sec_keyset_t struct correctly. I'll post the answer here if I manage to get it working.

  • I finally figured out how to make this work. The connectivity firmware will simply not respond if sec_params and sec_keyset are not filled in exactly right. I had to call sd_ble_gap_sec_params_reply directly, instead of using ble_gap_sec_params reply in the BLEDriver class.

    The following worked for me:

    sec_status = ble_driver.BLE_GAP_SEC_STATUS_SUCCESS
    sec_params = ble_driver.ble_gap_sec_params_t()
    sec_params.bond = 1
    sec_params.mitm = 0
    sec_params.lesc = 0
    sec_params.keypress = 0
    sec_params.io_caps = 3
    sec_params.oob = 0
    sec_params.min_key_size = 7 # fails if not 7
    sec_params.max_key_size = 16
    sec_params.kdist_own.enc = 0
    sec_params.kdist_own.id = 0
    sec_params.kdist_own.sign = 0
    sec_params.kdist_own.link = 0
    sec_params.kdist_peer.enc = 0
    sec_params.kdist_peer.id = 0
    sec_params.kdist_peer.sign = 0
    sec_params.kdist_peer.link = 0
    sec_keyset = ble_driver.ble_gap_sec_keyset_t()
    ble_driver.ble_gap_sec_params_reply(conn_handle, sec_status, sec_params, sec_keyset)
    

    After this I get a BLE_GAP_EVT_CONN_SEC_UPDATE event followed by a BLE_GAP_EVT_AUTH_STATUS event.

  • If others experience this error (0x03), with the pc-ble-driver on Linux/OSX, you can try this:

    There is an issue with the Segger firmware when using long packets over UART on Linux/OSX. The solutions is to disable the Mass Storage Device (MSD). The procedure to disable it is described in Workareound 2 on this page.

    I know the page say that the issue is not present on Linux, but it solved this exact problem for another user, so it might be worth trying.

    Best regards,

    Jørgen

Related