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

Regarding Python Scripts for nrf52840 to connect and transfer data.

Hi ,

What I am trying:- 

-connect the custom board with nrf52840 chip using nrf52 dongle on pc with python.

-Already set some commands on custom board so when my pc (via dongle) connects to custom board, I want to send some commands and accordingly receive the response.

For this I got my hands-on nrf dongle to give my laptop a BLE access.

And I am trying to access the board using dongle (using python script) and I am getting below error,


raise NordicSemiException('Failed to {}. Error code: {}'.format(wrapped.__name__, err_code))
pc_ble_driver_py.exceptions.NordicSemiException: Failed to open. Error code: 13.

Would anyone able to tell me how do i resolve this. 

I want to have python code that can connect and do some data transfer over BLE using Dongle connected to my pc and I have tried with hart_rate_monitor unsuccessfully. 

EDIT 1:- I am able to scan and connect my board using the nRF Connect windows app. Same I wanted to implement over python.

Thanks,

Hinesh

Parents
  • Hi,

    Can you provide the full command that you run (for starting the python script) and all output to the command line afterwards? (Both what parameters you send to the script, any debug output, and the full traceback may give clues as to what is the problem.)

    Regards,
    Terje

  • HI

    Actually I have resolved the above error.

    By going through various questions and answers here I came to know some tools.

    So When I was opening 'nrf connect' windows app , it was flashing firmware and I was having impression that this will be samefirmware will be used for scanning and connect via python. But It was not working.

    And the I have installed windows command line tools and format the board using 'nrfproject' command. And Let the python  application 'Hear rate monitor' do the flashing. Then it started working.

    It seems the mismatch on the firmware version on nrf52840 PDK Board (that I am using as dongle to connect to custom board via my pc).

    So To come on another query I have,

    Can you point me any example or script that can do the same work as 'nrf connect' does?

    Scan, connect (to particular given mac id) and explore services of connected device.


    Basically I am trying what I have explained on above question.

    This would be very help full for us as we are already on advance stage for development.

    Thanks,

    Hinesh

  • So Basically in my understanding, Please check comments on below code.

    And peer address here is the mac id of the advertising device or the name ?

        def on_gap_evt_adv_report(self, ble_driver, conn_handle, peer_addr, rssi, adv_type, adv_data): #peer address is my adverstiseing device that needs to be connected
            dev_name_list = None #Here change with dongle name 
            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]
    I will do a trial now and let you know about result.

    EDIT:- I have edited the parameters but upon running, It's asking for 
    'Please specify connectivity IC identifier (NRF51, NRF52)'

    I have also hard coded the device in config.py file on __conn_ic_id__ = "NRF52".

    But still while running the code it's asking for the IC identifier.

    Here I am trying to connect to device (example) with mac :- 11:22:33:44:55:66.
    This device I am able to see in the scan list as address and I can see the device name as well.

    Any comments on this?

    Thanks,

    Hinesh

  • Hi,

    You will need something along the lines of:

    if (peer_addr == TARGET_PEER_ADDR)
        self.adapter.connect(peer_addr)

    Instead of the current if that checks the dev_name. And also, you must define the TARGET_PEER_ADDR constant to be the address of the device, similar to how in the example they define a constant TARGET_DEV_NAME for the name of the device.

    For the error that you see, you will see at at the very end of the heart_rate_collector.py the code that leads to that error being displayed. It is code handling the arguments that you provide when running the example. If you want to use a hard coded connection IC ID then you can change that test, and also change the line "init(sys.argv[1])" to use the hard coded connection IC ID instead of sys.argv[1] as its argument.

    Please note that the argv array contains all the arguments that you provide on the command line, and so if you do not use the first argument any more then you must do additional changes in order for the other arguments to work as intended. I will leave it up to you to decide what arguments you want and for what purposes.

    I cannot do the work for you, but hopefully this should be enough for you to get going.

    Regards,
    Terje

  • Hi 

    From your suggestions I am able to connect to specific device via dongle/dk connected to any com port on my pc.

    Thank you very much for this.

    While going through the forums and also over internet I found for read and write operation gatt libraries are used in python.

    Well, I am using windows and I was not able to find the same for windows in python for read and write from my nordic board via my pc using python.

    I can find below function in ble_driver.py. but 

    def ble_gattc_write(self, conn_handle, write_params)
    def ble_gattc_read(self, conn_handle, handle, offset)
    (using the BLEDriver class I tried, but not success ,May be I am doing something wrong here or lacking knowledge.)

    It will be good if you can input here,

    Thanks, 

  • Hi,

    There is a close mapping between those python functions and the SoftDevice API. More specifically, the functions that you refer to correspond to sd_ble_gattc_write() and sd_ble_gattc_read().

    Please note that the current version of pc-ble-driver-py uses the s132 SoftDevice version 3.1.0. We are currently working on a new version of pc-ble-driver using s132 and s140 versions 6.1.0, and pc-ble-driver-py is likely to be updated further down the road.

    Unfortunately we do not seem to have any other examples than the ones in the python/pc-ble-driver-py/examples folder.

    Regards,
    Terje

  • Hi 

    Thank you very much for the input.

    From example Hart rate collector, I have made some changes require to connect to my device (having soft device 140).

    I am able to scan and I also thought I am able to connect (by hard coding the device mac and name on the serial port) as well.

    But I am getting below error.,

    "Received advertisment report, address: 0xAABBCCDDEEFF, device_name: MYDEVICE
    New connection: 0
    ATT MTU exchanged: conn_handle=0 att_mtu=23
    ATT MTU exchange response: conn_handle=0
    Traceback (most recent call last):
      File "C:/My_Projects/Script.py", line 256, in <module>
        main(serial_port)
      File "C:/My_Projects/Script.py", line 237, in main
        conn_handle = collector.connect_and_discover()
      File "C:/My_Projects/Script.py", line 187, in connect_and_discover
        self.adapter.enable_notification(new_conn, BLEUUID(BLEUUID.Standard.battery_level))
      File "C:\Python27\lib\site-packages\pc_ble_driver_py\ble_driver.py", line 125, in wrapper
        err_code = wrapped(*args, **kwargs)
      File "C:\Python27\lib\site-packages\pc_ble_driver_py\ble_adapter.py", line 254, in enable_notification
        raise NordicSemiException('CCCD not found')
    pc_ble_driver_py.exceptions.NordicSemiException: CCCD not found
    
    Process finished with exit code 1
    "

    I checked in

    self.adapter.enable_notification(new_conn, BLEUUID(BLEUUID.Standard.battery_level))

    Where I have tried to input my UUID as below,

    self.adapter.enable_notification(new_conn,MY_UUID, BLEUUID(BLEUUID.Standard.battery_level))

    From the file ble_driver.py
    def enable_notification(self, conn_handle, uuid):

    where
    I am passing collection handle but
    handle = self.db_conns[conn_handle].get_cccd_handle(uuid)

    still I get NONE on conn_handle. so is it my device that is not sending or I am not sharing proper UUID?
    The UUID IS checked by me for correctness, But If that's not correct I can correct by trying.

    It will be really helpful to have your input So that I can do read and write to my device via nordic dongle.

    By the way:- I am working with nordic dongle to remove other hw requirement possibility.
    we have checked the communication working but by different hw to verify the ID's on customized device.
    and we successfully checked and received data.

    Here your input will be very very helpful to complete read and write.
    Also to check if that's the right way.


    Thanks,
    Hinesh
Reply
  • Hi 

    Thank you very much for the input.

    From example Hart rate collector, I have made some changes require to connect to my device (having soft device 140).

    I am able to scan and I also thought I am able to connect (by hard coding the device mac and name on the serial port) as well.

    But I am getting below error.,

    "Received advertisment report, address: 0xAABBCCDDEEFF, device_name: MYDEVICE
    New connection: 0
    ATT MTU exchanged: conn_handle=0 att_mtu=23
    ATT MTU exchange response: conn_handle=0
    Traceback (most recent call last):
      File "C:/My_Projects/Script.py", line 256, in <module>
        main(serial_port)
      File "C:/My_Projects/Script.py", line 237, in main
        conn_handle = collector.connect_and_discover()
      File "C:/My_Projects/Script.py", line 187, in connect_and_discover
        self.adapter.enable_notification(new_conn, BLEUUID(BLEUUID.Standard.battery_level))
      File "C:\Python27\lib\site-packages\pc_ble_driver_py\ble_driver.py", line 125, in wrapper
        err_code = wrapped(*args, **kwargs)
      File "C:\Python27\lib\site-packages\pc_ble_driver_py\ble_adapter.py", line 254, in enable_notification
        raise NordicSemiException('CCCD not found')
    pc_ble_driver_py.exceptions.NordicSemiException: CCCD not found
    
    Process finished with exit code 1
    "

    I checked in

    self.adapter.enable_notification(new_conn, BLEUUID(BLEUUID.Standard.battery_level))

    Where I have tried to input my UUID as below,

    self.adapter.enable_notification(new_conn,MY_UUID, BLEUUID(BLEUUID.Standard.battery_level))

    From the file ble_driver.py
    def enable_notification(self, conn_handle, uuid):

    where
    I am passing collection handle but
    handle = self.db_conns[conn_handle].get_cccd_handle(uuid)

    still I get NONE on conn_handle. so is it my device that is not sending or I am not sharing proper UUID?
    The UUID IS checked by me for correctness, But If that's not correct I can correct by trying.

    It will be really helpful to have your input So that I can do read and write to my device via nordic dongle.

    By the way:- I am working with nordic dongle to remove other hw requirement possibility.
    we have checked the communication working but by different hw to verify the ID's on customized device.
    and we successfully checked and received data.

    Here your input will be very very helpful to complete read and write.
    Also to check if that's the right way.


    Thanks,
    Hinesh
Children
  • Hi,

    Yes, that error would be expected if you try enable_notification() with a characteristic UUID that has not been found on the device. For instance, the line...

    self.adapter.enable_notification(new_conn, BLEUUID(BLEUUID.Standard.battery_level))

    ...will try to enable notifications on the Bluetooth SIG specified Battery Level characteristic (i.e. UUID 0x2A19). If you use a custom UUID then you do not have a BLEUUID() but a full 128 bit UUID. Now, if your MY_UUID is correctly formatted then you should be able to use

    self.adapter.enable_notification(new_conn, MY_UUID)

    You can connect to the device from nRF Connect in order to investigate what characteristics (and UUIDs) are present on the device.

    Regards,
    Terje

  • Hi ,

    This are good suggestions and I have tried them as well without success.

    in self.adapter.enable_notification(new_conn,MY_UUID) I kept and also double checked in nRF connect app but still not able to send data.

    When I run the script

    I am able to scan and connect to my device.(provide your input for below debug output)

    Connected to pydev debugger (build 181.4892.64)
    Serial port used: COM10
    Enabling larger ATT MTUs
    Received advertisment report, address: 0x5031ADC0000A, device_name: MY_DEVICE
    New connection: 0
    ATT MTU exchanged: conn_handle=0 att_mtu=23
    ATT MTU exchange response: conn_handle=0

    The UUID I am passing I have also checked in python debug and it's same.

    And on Below line From file ble_adapter.py in

    def enable_notification(self, conn_handle, uuid):
    I went to for more debug.

    and I found is
    assert isinstance(uuid, BLEUUID), 'Invalid argument type'
    AssertionError: Invalid argument type

    I am not able to understand what is BLEUUID here. I changed BLEUUID in
    self.adapter.enable_notification to MY_UUID. and Still no connection.

    could you help in here. Also If you can tell about read and write function which
    I intent to use so that I will complete my script. and once tested
    I can share here so other can also be benefited.


    Thanks,

  • Hi,

    BLEUUID is a class, and you use BLEUUID() to create an instance of that class. I.e., you use the BLEUUID() function to generate a BLEUUID object.

    BLEUUID takes one or two arguments. The first argument is the 16 bit UUID. The second argument is optional, and is the base UUID, of type BLEUUIDBase. If the second argument is not provided then a 16 bit UUID is generated (using the special Bluetooth SIG assigned base UUID to be used with 16 bit UUIDs.)

    I did find an example using both standard (16 bit) UUIDs and user generated (128 bit) UUIDs, as part of pc-nrfutil:

        BASE_UUID = BLEUUIDBase([0x8E, 0xC9, 0x00, 0x00, 0xF3, 0x15, 0x4F, 0x60,
                                 0x9F, 0xB8, 0x83, 0x88, 0x30, 0xDA, 0xEA, 0x50])
                                 
        # Buttonless characteristics
        BLE_DFU_BUTTONLESS_CHAR_UUID        = BLEUUID(0x0003, BASE_UUID)
        BLE_DFU_BUTTONLESS_BONDED_CHAR_UUID = BLEUUID(0x0004, BASE_UUID)
        SERVICE_CHANGED_UUID                = BLEUUID(0x2A05)

    Notice how in order to create a custom UUID, you first use BLEUUIDBase() to generate the BLEUUIDBase, and then provide that BLEUUIDBase object as the second argument to BLEUUID().

    Regards,
    Terje

  • Hi 

    MY_UUID = '00001111-0000-1000-8000-00805f9b34fb'
    

    My UUID is as above, And I have used as per your prev. comment. I checked with nrf connect mobile app.
    Still I was getting error.

    Below is my console output after I used

    BLE_DFU_BUTTONLESS_CHAR_UUID = BLEUUID(0x0003, BASE_UUID)

    in

    self.adapter.enable_notification(new_conn, BLEUUID(0x0003, MY_UUID)

    Serial port used: COM10
    Enabling larger ATT MTUs
    Received advertisment report, address: 0x5031ADC0000A, device_name: POC1_2-3
    New connection: 0
    ATT MTU exchanged: conn_handle=0 att_mtu=23
    ATT MTU exchange response: conn_handle=0
    Traceback (most recent call last):
      File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.1.3\helpers\pydev\pydevd.py", line 1664, in <module>
        main()
      File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.1.3\helpers\pydev\pydevd.py", line 1658, in main
        globals = debugger.run(setup['file'], None, None, is_module)
      File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.1.3\helpers\pydev\pydevd.py", line 1068, in run
        pydev_imports.execfile(file, globals, locals)  # execute the script
      File "C:/My_Projects/Script.py", line 263, in <module>
        main(serial_port)
      File "C:/My_Projects/Script.py", line 244, in main
        conn_handle = collector.connect_and_discover()
      File "C:/My_Projects/Script.py", line 194, in connect_and_discover
        self.adapter.enable_notification(new_conn, BLEUUID(0x0003, DC_SERVICE_UUID))#BLEUUID(BLEUUID.Standard.battery_level))
      File "C:\Python27\lib\site-packages\pc_ble_driver_py\ble_driver.py", line 882, in __init__
        assert isinstance(base, BLEUUIDBase), 'Invalid argument type'
    AssertionError: Invalid argument type
    

    Still the error.

    I am attaching my script so that you can check and provide input.

    This is for your information on how I am trying to implement.

    import Queue
    
    import json
    import time
    import sys
    
    from datetime import datetime, timedelta
    from bitstruct import *
    from pprint import pformat
    import binascii
    from pc_ble_driver_py.observers import *
    from nordicsemi.ble_adapter import *
    
    try:
        with open("Config_Files/Connection_Settings.json")as conn:
            config = json.load(conn)
    except Exception as e:
        print str(e)
        print 'Could not open settings file'
        raw_input("Press ENTER to exit...")
        quit()
    
    
    DEV_MAC = config['dev_mac']
    
    
    REPEAT_COUNT = 25
    
    RAW_TYPE = TEMP  # Temp sensor
    RAW_COUNT = 10  # Sample count
    RAW_RATE = 0  # Sampling freq
    
    TARGET_DEV_NAME ="50:31:AD:C0:00:0A"
    port_enter = "COM10"
    device = "NRF52"
    CONNECTIONS = 1
    serial_port = "COM10"
    dev_name = "50:31:AD:C0:00:0A"
    # Creating command packet
    temp = pack('u8u16f32', RAW_TYPE, RAW_COUNT, RAW_RATE)
    pkt = byteswap('124', temp)
    
    HEADER_SIZE = 3
    
    _EOT_T = 'u1'
    _TYPE_T = 'u3'
    _TID_T = 'u4'
    _SEQ_NUM_T = 'u16'
    
    __BYTEALIGN = '12'
    
    __DATA_FORMAT = _TID_T + _TYPE_T + _EOT_T + _SEQ_NUM_T
    
    sensorType = RAW_TYPE
    sampleCount = RAW_COUNT
    samplingFreq = RAW_RATE
    ##
    #Data saving part 
    ##
    
    ##### NRF PART ################
    
    CCCD_UUID = '00002902-0000-1000-8000-00805f9b34fb'
    
    DC_SERVICE_UUID = '00001111-0000-1000-8000-00805f9b34fb'
    
    BASE_UUID = BLEUUIDBase([0x8E, 0xC9, 0x00, 0x00, 0xF3, 0x15, 0x4F, 0x60,
                             0x9F, 0xB8, 0x83, 0x88, 0x30, 0xDA, 0xEA, 0x50])
    
    # Buttonless characteristics
    BLE_DFU_BUTTONLESS_CHAR_UUID = BLEUUID(0x0003, BASE_UUID)
    BLE_DFU_BUTTONLESS_BONDED_CHAR_UUID = BLEUUID(0x0004, BASE_UUID)
    SERVICE_CHANGED_UUID = BLEUUID(0x2A05)
    def init(conn_ic_id):
        global BLEDriver, BLEAdvData, BLEEvtID, BLEAdapter, BLEEnableParams, BLEGapTimeoutSrc, BLEUUID
        from pc_ble_driver_py import config
        config.__conn_ic_id__ = conn_ic_id
        from pc_ble_driver_py.ble_driver import BLEDriver, BLEAdvData, BLEEvtID, BLEEnableParams, BLEGapTimeoutSrc, BLEUUID
        from pc_ble_driver_py.ble_adapter import BLEAdapter
        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.Queue()
            self.adapter.observer_register(self)
            self.adapter.driver.observer_register(self)
    
        def open(self):
            self.adapter.driver.open()
    
            ble_enable_params = BLEEnableParams(vs_uuid_count=1,
                                                service_changed=False,
                                                periph_conn_count=0,
                                                central_conn_count=CONNECTIONS,
                                                central_sec_count=CONNECTIONS)
            if nrf_sd_ble_api_ver >= 3:
                print("Enabling larger ATT MTUs")
                ble_enable_params.att_mtu = 50
    
            self.adapter.driver.ble_enable(ble_enable_params)
    
        def close(self):
            self.adapter.driver.close()
    
        @property
        def connect_and_discover(self):
            self.adapter.driver.ble_gap_scan_start()
            new_conn = self.conn_q.get(timeout=60)
    
            if nrf_sd_ble_api_ver >= 3:
                att_mtu = self.adapter.att_mtu_exchange(new_conn)
    
            self.adapter.service_discovery(new_conn)
            self.adapter.enable_notification(new_conn, BLEUUID(0x0003, DC_SERVICE_UUID))#BLEUUID(BLEUUID.Standard.battery_level))
            # self.adapter.enable_notification(new_conn, BLEUUID(BLEUUID.Standard.heart_rate))
            return new_conn
    
        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_timeout(self, ble_driver, conn_handle, src):
            if src == BLEGapTimeoutSrc.scan:
                ble_driver.ble_gap_scan_start()
    
        def on_gap_evt_adv_report(self, ble_driver, conn_handle, peer_addr, rssi, adv_type, adv_data):
            dev_name_list = None
            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):
                self.adapter.connect(peer_addr)
    
        def on_notification(self, ble_adapter, conn_handle, uuid, data):
            print('Connection: {}, {} = {}'.format(conn_handle, uuid, data))
    
        def on_att_mtu_exchanged(self, ble_driver, conn_handle, att_mtu):
            print('ATT MTU exchanged: conn_handle={} att_mtu={}'.format(conn_handle, att_mtu))
    
        def on_gattc_evt_exchange_mtu_rsp(self, ble_driver, conn_handle, **kwargs):
            print('ATT MTU exchange response: conn_handle={}'.format(conn_handle))
    
    
    def main(serial_port):
        print('Serial port used: {}'.format(serial_port))
        driver = BLEDriver(serial_port=serial_port, auto_flash=True)
        adapter = BLEAdapter(driver)
        collector = HRCollector(adapter)
        collector.open()
        conn_handle = collector.connect_and_discover()
        # adapter.write_req(conn_handle, CCCD_UUID, [3])#RAW_TYPE)
        # collector.connect_and_discover()
       
    
    
    if __name__ == "__main__":
        serial_port = None
        # if len(sys.argv) < 2:
        #     print("Please specify connectivity IC identifier (NRF51, NRF52)")
        #     exit(1)
        init("NRF52")
        if len(sys.argv) == 3:
            serial_port = sys.argv[2]
        else:
            descs = BLEDriver.enum_serial_ports()
            choices = ['{}: {}'.format(d.port, d.serial_number) for d in descs]
            choice = '0'  # item_choose(choices)
            serial_port = port_enter  # "COM12" #descs[choice].port
            main(serial_port)
            quit()
    
    ##### NRF PART ###### #########
    

    Thnaks,

  • Hi,

    You need to make the DC_SERVICE_UUID in the same way as the BLE_DFU_BUTTONLESS_CHAR_UUID was made in the example code. (You should not just copy and paste the code, as you are using different UUIDs than what was used in the example code.)

    More specifically, on the line "self.adapter.enable_notification(new_conn, BLEUUID(0x0003, DC_SERVICE_UUID))" you provide DC_SERVICE_UUID, which should be a BLEUUIDBase object and not a string.

    Regards,
    Terje

Related