BLE: data type for gatts_write

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
poesel
Posts: 22
Joined: Sun Oct 20, 2019 4:58 pm

BLE: data type for gatts_write

Post by poesel » Sun Oct 20, 2019 5:10 pm

From the temperature example I can see that sint16 is "struct.pack('<h', int())".
What about other types? Is this correct?

Date Type
uint8 - struct.pack('<B', int(< value >))
uint16 - struct.pack('<H', int(< value >))
sint8 - struct.pack('<b', int(< value >))
sint16 - struct.pack('<h', int(< value >))

I've also stumbled over these data types from the Bluetooth documentation: "utf8s" & "16bit". How do these work?

Sorry, I'm new to this. I'm struggling a bit to get it to work (and currently it doesn't) so I cannot just test it.
I guess a table in the documentation would help.

Thanks!

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: BLE: data type for gatts_write

Post by jimmo » Mon Oct 21, 2019 12:48 am

These types are listed here: https://www.bluetooth.com/specification ... mat-types/

Strings in MicroPython are utf-8 by default, but if you're appending them to an existing bytearray you might need to use .encode() to get out the underlying bytes.

16-bit is probably in reference to a UUID? I'm just about to this morning send a PR to make it so the bluetooth.UUID class supports the buffer protocol so it can be appended directly to an advertisement. This is the "Extract bytes from UUID class (buffer protocol?) for appending to adv_data." line in https://github.com/micropython/micropython/issues/5186 In the meantime, see https://github.com/micropython/micropyt ... rtising.py which appends 16-bit service uuids to an advertising payload (i.e. using '<h').

poesel
Posts: 22
Joined: Sun Oct 20, 2019 4:58 pm

Re: BLE: data type for gatts_write

Post by poesel » Thu Oct 24, 2019 10:35 am

Lets take 'Battery Level' as an example with a level of 60% (int 60). That is 0x2A19 and a data type of uint8 (according to https://www.bluetooth.com/wp-content/up ... _level.xml).

So I call:

Code: Select all

self._ble.gatts_write(self._BS_handle, struct.pack('<B', int(level)))
with _BS_handle the handle to the battery service.
>>> struct.pack('<B', 60)
b'<'
When I read that back I get a different value (0x7017) so I must be doing something wrong. What am I missing here?

Thanks

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: BLE: data type for gatts_write

Post by jimmo » Thu Oct 24, 2019 11:09 am

That looks right to me, but I will try and replicate here and get back to you.

I presume you're using this with the battery service (0x180F). You said "_BS_handle the handle to the battery service." but I'm guessing it's actually the handle to the battery level characteristic (there's no such thing as a handle to a service in a gatt server).

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: BLE: data type for gatts_write

Post by jimmo » Fri Oct 25, 2019 10:35 am

Hi,

I adapted the temperature example at https://github.com/micropython/micropyt ... erature.py to use the battery service. It seems to do the right thing when I connect from the "NRF Connect" app -- I see a battery service with a battery level characteristic, that correctly shows percentage values.

Code: Select all

import bluetooth
import struct
import time
from ble_advertising import advertising_payload

from micropython import const
_IRQ_CENTRAL_CONNECT                 = const(1 << 0)
_IRQ_CENTRAL_DISCONNECT              = const(1 << 1)

_BATTERY_UUID = bluetooth.UUID(0x180F)
_BATTERY_LEVEL_CHAR = (bluetooth.UUID(0x2A19), bluetooth.FLAG_READ|bluetooth.FLAG_NOTIFY,)
_BATTERY_SERVICE = (_BATTERY_UUID, (_BATTERY_LEVEL_CHAR,),)

class BLEBattery:
    def __init__(self, ble, name='mpy-temp'):
        self._ble = ble
        self._ble.active(True)
        self._ble.irq(handler=self._irq)
        ((self._handle,),) = self._ble.gatts_register_services((_BATTERY_SERVICE,))
        self._connections = set()
        self._payload = advertising_payload(name=name, services=[_BATTERY_UUID])
        self._advertise()

    def _irq(self, event, data):
        # Track connections so we can send notifications.
        if event == _IRQ_CENTRAL_CONNECT:
            conn_handle, _, _, = data
            self._connections.add(conn_handle)
        elif event == _IRQ_CENTRAL_DISCONNECT:
            conn_handle, _, _, = data
            self._connections.remove(conn_handle)
            # Start advertising again to allow a new connection.
            self._advertise()

    def set_level(self, level_percentage, notify=False):
        # Write the local value, ready for a central to read.
        self._ble.gatts_write(self._handle, struct.pack('<B', int(level_percentage)))
        if notify:
            for conn_handle in self._connections:
                # Notify connected centrals to issue a read.
                self._ble.gatts_notify(conn_handle, self._handle)

    def _advertise(self, interval_us=500000):
        self._ble.gap_advertise(interval_us, adv_data=self._payload)


def demo():
    ble = bluetooth.BLE()
    batt = BLEBattery(ble)

    i = 25

    while True:
        # Write every second, notify every 10 seconds.
        i = (i + 1) % 101
        batt.set_level(i, notify=(i % 10 == 0))
        time.sleep_ms(1000)


if __name__ == '__main__':
    demo()

poesel
Posts: 22
Joined: Sun Oct 20, 2019 4:58 pm

Re: BLE: data type for gatts_write

Post by poesel » Sun Nov 03, 2019 12:11 pm

Thanks - I found the problem was not on the writing but on the reading side.
This is how the battery level function looks now:

Code: Select all

    def set_battery_level(self, level=0, notify=False): 
        """
        Level in [%]
        Data is uint8
        Resolution: 1
        """
        self._ble.gatts_write(self._BS_handle, struct.pack('<B', int(level)))
        print('Write BS: ', struct.pack('<B', int(level)))
        if notify:
            for conn_handle in self._connections:   # Notify connected centrals to issue a read.
                self._ble.gatts_notify(conn_handle, self._BS_handle)     
 

Post Reply