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

User description in pc-ble-driver-py

When trying to add a user description to the BLEGattsCharMD() I get this error:

TypeError: in method 'ble_gatts_char_md_t_p_char_user_desc_set', argument 2 of type 'uint8_t const *'

This is a code snippet causing this error:

props = BLEGattCharProps(notify=True, read=True, write=True)
desc_md = BLEGattCharProps(read=True)
user_desc = bytearray([1,2,3])
char_md = BLEGattsCharMD(char_props=props, desc_md=desc_md, user_desc=user_desc)

The error is on this line in ble_driver.py:

char_md.p_char_user_desc = util.list_to_char_array(self.user_desc)

(See https://github.com/NordicSemiconductor/pc-ble-driver-py/blob/v0.15.0/pc_ble_driver_py/ble_driver.py#L1267)

Any ideas?

  • Ok, some progress. Instead of

    char_md.p_char_user_desc = util.list_to_char_array(self.user_desc)

    this code at least doesn't produce errors:

    user_desc_array=util.list_to_uint8_array(self.user_desc)
    user_desc_array_cast = user_desc_array.cast()
    char_md.p_char_user_desc = user_desc_array_cast

    But now I get NRF_ERROR_DATA_SIZE when adding the characteristic.

  • Hi,

    Have you tried setting user_desc to a list, and not a bytearray()? The function list_to_char_array seems to expect a list.

    The code you use may not set the size (char_md.char_user_desc_size = len(self.user_desc)) correctly.

    Best regards,
    Jørgen

  • I tried setting user_desc to a list but the error is still there. The char_md.char_user_desc_size  seems to be correct in both cases

    Edit: Aha! I found one issue. It is missing char_user_desc_max_size. Adding it gets rid of the NRF_ERROR_DATA_SIZE error:

        char_md.char_user_desc_size = len(self.user_desc)
        char_md.char_user_desc_max_size = len(self.user_desc)

    I can now connect to the peripheral but the user description contains all 0x00 (len(self.user_desc)  number of 0x00 bytes)

  • Have you tried setting the size and max_size manually, to see if that makes any difference?

    Could you share a sample application that we can use the reproduce and debug this issue?

  • This is a sample application showing the issue (pc_ble_driver_py version 0.15.0):

    import logging
    import sys
    import time
    from threading import Condition, Lock
    from pc_ble_driver_py.observers import BLEDriverObserver
    
    BASE_UUID_DICT                 = {'BV_BASE_UUID': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                                   0x10, 0x00, 
                                   0x80, 0x00, 
                                   0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB]}
    SERVICE1 = (("MyService1"      , 0x3000),
                                    (("Char1"               , 0x3001, 1,   [0x00], "Desc1"),
                                     ("Char2"                , 0x3002, 2,   [0x02, 0xFA], "Desc2"),))
    
    DEVICE_INFORMATION_SERVICE = (("Device Information"   , 0x180A),
                                    (("Manufacturer name"    , 0x2A29, 20,   b"Name", None),
                                    ("Model name"           , 0x2A24,  20,   b"Model1", None),
                                    ("Firmware version"     , 0x2A26,  20,   b"v_0.0.1", None),))
    
    SERVICES_CONFIG = ( DEVICE_INFORMATION_SERVICE,
                        SERVICE1,
                         )
    
    logger = logging.getLogger(__name__)
    
    def init(conn_ic_id):
        # noinspection PyGlobalUndefined
        global BLEAdapter, BLEDriver, BLEAdvData, BLEConfigGapDeviceName, BLEEvtID, BLEEnableParams, config, driver, BLEAdvData, BLEGattsHVXParams, BLEService, BLEUUIDBase, BLEUUID, BLEGattsAttr, BLEGattsAttrMD, BLEGattHandle, BLEGattsCharHandles, BLEGattsCharMD, BLEGattCharProps, BLEConfig, BLEConfigCommon
        from pc_ble_driver_py import config
    
        config.__conn_ic_id__ = conn_ic_id
        # noinspection PyUnresolvedReferences
        from pc_ble_driver_py.ble_driver import (
            BLEDriver,
            BLEAdvData,
            BLEConfigGapDeviceName,
            BLEEvtID,
            BLEEnableParams,
            BLEGattsHVXParams,
            BLEService,
            BLEUUIDBase,
            BLEUUID,
            BLEGattHandle,
            BLEGattsAttr,
            BLEGattsAttrMD,
            BLEGattsCharHandles,
            BLEGattsCharMD,
            BLEGattCharProps,
            BLEConfig,
            BLEConfigCommon,
            driver,
        )
            # noinspection PyUnresolvedReferences
        from pc_ble_driver_py.ble_adapter import BLEAdapter
    
    
    class Peripheral:
        def __init__(self, adapter):
            self.adapter = adapter
            logger.info(
                "Peripheral adapter is %d",
                self.adapter.driver.rpc_adapter.internal,
            )
            self.char_128_handles_dict = {}
            self.serv_128_handle_dict = {}
            self.uuid_128_base_dict = {}
            self.serv_128_uuid_dict = {}
    
        def _add_char(self, base_uuid, service_uuid, char_uuid, max_len, value, user_desc_str):
            """Add characteristic"""
            char_128_handles = BLEGattsCharHandles()
            char_128_uuid = BLEUUID(char_uuid, self.uuid_128_base_dict[base_uuid] )
            
            max_len=16
            
            props = BLEGattCharProps(notify=True, read=True, write=True)
            if user_desc_str:
                user_desc = list(user_desc_str.encode())
                desc_md = BLEGattCharProps(read=True)
                char_md = BLEGattsCharMD(char_props=props, user_desc=user_desc, desc_md=desc_md)         
            else:
                char_md = BLEGattsCharMD(char_props=props)            
            
           
            attr_md = BLEGattsAttrMD()
            attr = BLEGattsAttr(uuid=char_128_uuid, attr_md=attr_md,
                                max_len=max_len, value=value)
            self.adapter.driver.ble_gatts_characteristic_add(
                                self.serv_128_handle_dict[service_uuid].handle,
                                char_md, attr, char_128_handles)
            self.char_128_handles_dict[char_uuid] = char_128_handles
    
        def _add_service(self, base_uuid, service_uuid):
            """Add specific service"""       
            if not base_uuid in self.uuid_128_base_dict:
                uuid_128_base = BLEUUIDBase(BASE_UUID_DICT[base_uuid])
                self.uuid_128_base_dict[base_uuid] = uuid_128_base
                self.adapter.driver.ble_vs_uuid_add(self.uuid_128_base_dict[base_uuid])
    
            self.serv_128_handle_dict[service_uuid]= BLEGattHandle()
            self.serv_128_uuid_dict[service_uuid] = BLEUUID(service_uuid, self.uuid_128_base_dict[base_uuid])
      
            self.adapter.driver.ble_gatts_service_add(
                driver.BLE_GATTS_SRVC_TYPE_PRIMARY, self.serv_128_uuid_dict[service_uuid], self.serv_128_handle_dict[service_uuid])
     
        def setup_services_128bit(self):
            """Setup services"""
            for (_, service_uuid), chars in SERVICES_CONFIG:
                self._add_service(base_uuid='BV_BASE_UUID', service_uuid=service_uuid)
                for (_, char_uuid, max_len, value, user_desc_str) in chars:
                    self._add_char(base_uuid='BV_BASE_UUID', 
                               service_uuid=service_uuid, 
                               char_uuid=char_uuid, max_len=max_len, value=value,
                               user_desc_str=user_desc_str)
    def main(serial_port):
        print("Serial port used: {}".format(serial_port))
        ble_driver = BLEDriver(serial_port=serial_port, 
                           baud_rate=1000000,
                           auto_flash=True,
                           log_severity_level="debug")
        adapter = BLEAdapter(ble_driver)
        observer = TimeoutObserver()
    
        adv_data = BLEAdvData(short_local_name="MyDevice")
    
        ble_driver.observer_register(observer)
        ble_driver.open()
        if config.__conn_ic_id__.upper() == "NRF51":
            ble_driver.ble_enable(
                BLEEnableParams(
                    vs_uuid_count=0,
                    service_changed=0,
                    periph_conn_count=1,
                    central_conn_count=0,
                    central_sec_count=0,
                )
            )
        elif config.__conn_ic_id__.upper() == "NRF52":
    #         configCommon = BLEConfigCommon()
    #         configCommon.vs_uuid_count = 1
    #         driver.ble_cfg_set(BLEConfig.uuid_count, configCommon)
    #         cfg = BLEConfigGap()
    #         cfg.device_name = "test123"
    #         cfg.device_name_read_only = False
    #        ble_tester.driver.ble_cfg_set(BLEConfig.device_name, cfg)
            
            configName = BLEConfigGapDeviceName(device_name="0000000000",device_name_read_only=False)
            ble_driver.ble_cfg_set(BLEConfig(driver.BLE_GAP_CFG_DEVICE_NAME), configName)
    #         ble_driver.ble_cfg_set(BLEConfig.device_name, cfg)
            #sd_ble_gap_device_name_set()
            ble_driver.ble_enable()
            
        peripheral = Peripheral(adapter)
        peripheral.setup_services_128bit()
           
        ble_driver.ble_gap_adv_data_set(adv_data)
        ble_driver.ble_gap_adv_start()
        observer.wait_for_timeout()
    
        while(True):
            time.sleep(10)
        print("Closing")
        ble_driver.close()
    
    
    class TimeoutObserver(BLEDriverObserver):
        def __init__(self, *args, **kwargs):
            super(BLEDriverObserver, self).__init__(*args, **kwargs)
            self.cond = Condition(Lock())
    
        def on_gap_evt_timeout(self, ble_driver, conn_handle, src):
            with self.cond:
                self.cond.notify_all()
    
        def on_gap_evt_disconnected(self, ble_driver, conn_handle, reason):
            with self.cond:
                self.cond.notify_all()
    
        def wait_for_timeout(self):
            with self.cond:
                self.cond.wait()
    
    
    if __name__ == "__main__":
        logFormatter = logging.Formatter("%(asctime)s [%(levelname)-7s][%(name)s] %(message)s")
        rootLogger = logging.getLogger()
         
        fileHandler = logging.FileHandler("ble_test.log")
        fileHandler.setFormatter(logFormatter)
        rootLogger.addHandler(fileHandler)
         
        consoleHandler = logging.StreamHandler(sys.stdout)
        consoleHandler.setFormatter(logFormatter)
        rootLogger.addHandler(consoleHandler)
         
        rootLogger.setLevel(logging.DEBUG)
        
        id = "NRF52"
        serial_port = "/dev/ttyACM0"
        init(id)
        main(serial_port)
    

    Note the following changes that is made to BLEGattsCharMD in ble_driver.py:

    class BLEGattsCharMD(object):
        def __init__(self, char_props, user_desc=None, pf=None,
                     desc_md=None, cccd_md=None, sccd_md=None):
            assert isinstance(char_props, BLEGattCharProps)
            self.char_props = char_props
            self.user_desc = user_desc
            self.pf = pf
            self.desc_md = desc_md
            self.cccd_md = cccd_md
            self.sccd_md = sccd_md
    
        def __str__(self):
            return str(self.__dict__)
    
        def to_c(self):
            char_md = driver.ble_gatts_char_md_t()
            char_md.char_props = self.char_props.to_c()
            if self.user_desc:
                user_desc_array=util.list_to_uint8_array(self.user_desc)
                user_desc_array_cast = user_desc_array.cast()
                char_md.p_char_user_desc = user_desc_array_cast
                char_md.char_user_desc_size = len(self.user_desc)
                char_md.char_user_desc_max_size = len(self.user_desc)
            if self.pf:
                char_md.p_char_pf = self.pf.to_c()
            if self.desc_md:
                char_md.user_desc_md = self.desc_md.to_c()
            if self.cccd_md:
                char_md.p_cccd_md = self.cccd_md.to_c()
            if self.sccd_md:
                char_md.p_sccd_md = self.sccd_md.to_c()
            return char_md

    nRF Connect shows the following in Characteristic User Name:

Related