pc-ble-driver-py example of writing a characteristic.

I'm using the pc-ble-driver-py: Standard Heart Rate example to read a characteristic just fine.

My issue is i now need to write a to a characteristic.

How would I do that given the example above that reads great but can hypothetically write to the same characteristic read?

Link to HR example:

github.com/.../heart_rate_collector.py

Parents
  • The hearth rate device doesn't contain any characteristics that you can write to. Perhaps you can try to modify the heart_rate_collector.py to connect to a device running the ble_app_uart example instead. You should see that this device has two characteristics. One that you can only enable subscriptions on (notifications, just like you do on the battery service in the heart rate example), and another that you can write to. Please check it out without pc-ble-driver-py (connect to it with your phone using the nRF Connect for iOS/Android app) to see how the example is working.

    Best regards,

    Edvin

  • Hi Edvin,

    Can you send me link to example?

    Is that on Github?

  • I do write in python.  Its pretty obvious as my first post.  Your port of pc-ble-driver to pc-ble-driver-py is more or less there. 

    I do use the SDK and nRF Connect to send and write characteristics which works just fine.

    If you are implying that I need to look at examples in pc-ble-driver to help that's fine too.  I just wanted to know if you had an already working example in python that just demonstrated writing a characteristic.  But you do not.

    I already have a working example writing characteristic in python after some investigation and testing and I believe this ticket is closed.

  • I am in the position you were in: wanting to write a characteristic. Can you pass on any examples, tips, or links to information that helped you?

    Thanks,
    Chris

  • I figured it out. It's pretty simple, something like this for a custom service: (unverified after sanitizing)

    self.ourBaseUUID = BLEUUIDBase([0x5e, 0x0a, 0xc3, 0x95, 0x70, 0xb0, 0xab, 0xae,
                                    0xfd, 0x23, 0x6b, 0x79, 0x4a, 0x9b, 0x58, 0xda],
                                   2)  # base UUID type=2 (why?)
    self.ourCharUUID = BLEUUID(0x2001, self.ourBaseUUID)

    byte_array = [1, 2, 3, 4]
    self.adapter.write_req(conn_handle, self.ourCharUUID, byte_array)
  • Hi Chris,

    I am doing something like you but I am facing issue. Can you share your working example script?

    I use a nRF52840 Dongle to connect with my Bluetooth Device by modifying heart_rate_collector.py and it is successful. Now I would like to send some HEX code to the Bluetooth Device with python. The action should be similar with the nRF Connect Desktop at the 1401 below. I am sending the HEX command to the device.

  • Hi Wei Jian,

    My full script includes proprietary information, but the following functions that I added to the HRCollector class might be useful. My service has 32-bit characteristics that I call "addr" and "data". In my firmware, I map what I call "registers" to different addresses, so to read a register, I write the address to "addr" and then "data" notifies with the value. To write a register, I then write the value to "data":

    REG_ADDR = {
        "":                 0x0000,
        "FOO":              0x0000,
        "BAR":              0x0004,
    }
    
    # in class HRCollector:
    # in __init__:
            self.return_q   = Queue.Queue()
            self.return_q_q = Queue.Queue()
    # /in __init__
    
        def on_notification(self, ble_adapter, conn_handle, uuid, data):
            if (not self.return_q_q.empty()):  # We are expecting this data, so put it in the next return q:
                ret_q = self.return_q_q.get()
                ret_q.put(data)
            else:                              # Unexpected data, so just print it:
                print('Connection: {}, {} = {}'.format(conn_handle, uuid, data))
    
        def write_word_characteristic(self, conn_handle, uuid, data):
            byte_array = []
            for byte_num in range (0, 4):
                byte_array.append(data >> (8*byte_num) & 0xFF)
            self.adapter.write_req(conn_handle, uuid, byte_array)
       
        def write_addr(self, conn_handle, data):
            self.return_q_q.put(self.return_q)
            self.write_word_characteristic(conn_handle, self.clovisAddrUUID, data)
            byte_array = self.return_q.get(block=True, timeout=30)
            return_data = 0
            for byte_num in range (0, 4):
                return_data = return_data | (byte_array[byte_num] << (8*byte_num))
            return return_data
    
        def write_data(self, conn_handle, data):
            self.write_word_characteristic(conn_handle, self.clovisDataUUID, data)
    
        def write_addr_and_data(self, conn_handle, addr, data):
            self.write_addr(conn_handle, addr)
            self.write_data(conn_handle, data)
    
        def read_register(self, conn_handle, base, addr_name, print_en=False):
            addr_int = base + REG_ADDR[addr_name]
            data = self.write_addr(conn_handle, addr_int)
            if print_en:
                print ('Read register ' + addr_name + '(' + hex(addr_int) + '): ' + hex(data) + " (" + str(data) + " decimal)")
            return(data)
    
        def write_register(self, conn_handle, base, addr_name, data, print_en=False):
            addr_int = base + REG_ADDR[addr_name]
            if print_en:
                print ('Write register ' + addr_name + '(' + hex(addr_int) + ')=' + hex(data))
            self.write_addr(conn_handle, addr_int)
            self.write_data(conn_handle, data)

    I know this is more than you asked for. To just write a characteristic, write_word_characteristic() is all you need. Or, starting from my original example, you should be able to just assign byte_array with the 6 bytes that you want to write, and pass that to write_req. You can do this in main() after connect_and_discover(). What problem are you having?

    Chris

Reply
  • Hi Wei Jian,

    My full script includes proprietary information, but the following functions that I added to the HRCollector class might be useful. My service has 32-bit characteristics that I call "addr" and "data". In my firmware, I map what I call "registers" to different addresses, so to read a register, I write the address to "addr" and then "data" notifies with the value. To write a register, I then write the value to "data":

    REG_ADDR = {
        "":                 0x0000,
        "FOO":              0x0000,
        "BAR":              0x0004,
    }
    
    # in class HRCollector:
    # in __init__:
            self.return_q   = Queue.Queue()
            self.return_q_q = Queue.Queue()
    # /in __init__
    
        def on_notification(self, ble_adapter, conn_handle, uuid, data):
            if (not self.return_q_q.empty()):  # We are expecting this data, so put it in the next return q:
                ret_q = self.return_q_q.get()
                ret_q.put(data)
            else:                              # Unexpected data, so just print it:
                print('Connection: {}, {} = {}'.format(conn_handle, uuid, data))
    
        def write_word_characteristic(self, conn_handle, uuid, data):
            byte_array = []
            for byte_num in range (0, 4):
                byte_array.append(data >> (8*byte_num) & 0xFF)
            self.adapter.write_req(conn_handle, uuid, byte_array)
       
        def write_addr(self, conn_handle, data):
            self.return_q_q.put(self.return_q)
            self.write_word_characteristic(conn_handle, self.clovisAddrUUID, data)
            byte_array = self.return_q.get(block=True, timeout=30)
            return_data = 0
            for byte_num in range (0, 4):
                return_data = return_data | (byte_array[byte_num] << (8*byte_num))
            return return_data
    
        def write_data(self, conn_handle, data):
            self.write_word_characteristic(conn_handle, self.clovisDataUUID, data)
    
        def write_addr_and_data(self, conn_handle, addr, data):
            self.write_addr(conn_handle, addr)
            self.write_data(conn_handle, data)
    
        def read_register(self, conn_handle, base, addr_name, print_en=False):
            addr_int = base + REG_ADDR[addr_name]
            data = self.write_addr(conn_handle, addr_int)
            if print_en:
                print ('Read register ' + addr_name + '(' + hex(addr_int) + '): ' + hex(data) + " (" + str(data) + " decimal)")
            return(data)
    
        def write_register(self, conn_handle, base, addr_name, data, print_en=False):
            addr_int = base + REG_ADDR[addr_name]
            if print_en:
                print ('Write register ' + addr_name + '(' + hex(addr_int) + ')=' + hex(data))
            self.write_addr(conn_handle, addr_int)
            self.write_data(conn_handle, data)

    I know this is more than you asked for. To just write a characteristic, write_word_characteristic() is all you need. Or, starting from my original example, you should be able to just assign byte_array with the 6 bytes that you want to write, and pass that to write_req. You can do this in main() after connect_and_discover(). What problem are you having?

    Chris

Children
Related