Implementing BLE GATT ESS characteristics with MicroPython

About

We would like to implement some parts of the BLE GATT Environmental Sensing Service (ESS) characteristics with MicroPython in a convenient way.

Pycom MicroPython API documentation

While the documentation on the Pycom pages

doesn’t look bad at first hand, it lacks important details when it comes to crafting the appropriate characteristics and assigning values properly to them.

Yak shaving ahead.

nRF Connect for Mobile

For conducting first tests and getting started with BLE, we will use the fine Android application nRF Connect written by Aleksander Nowakowski and Kamran Saleem Soomro of Nordic Semiconductor’s.

Setup

Android Debug Bridge (adb)


adb (Android Debug Bridge) cheatsheet

nRF Connect for Android

wget https://github.com/NordicSemiconductor/Android-nRF-Connect/releases/download/v4.22.3/nRF.Connect.4.22.3.apk
adb install nRF.Connect.4.22.3.apk

Screenshots

Installation / Download


On day one, we have been able publish the first BLE GATT service characteristics.

On day two, we have been able to find another excellent gem.

Apart from that, MicroPython (and Python!) support for crafting specific characteristics respecting their required value codings looks rather poor these days. We’ve been able to unlock a few but are still struggling with others.

The 2A1C BLE GATT characteristic aka. “Temperature Measurement”

In order to properly support BLE GATT’s Temperature Measurement characteristic (uuid=2A1C) within the ESS characteristics, we are currently looking for a pure-Python implementation for some encoding parts of the http://oss.signove.com/index.php/Antidote:_IEEE_11073-20601_stack.

https://github.com/signove/antidote

which implements things from

In particular, we are interested in appropriate write_sfloat and write_float implementations for Python as seen within Antidote’s antidote/src/util/bytelib.c at master · signove/antidote · GitHub.


# Temperature Measurement
# UUID: 2A1C
# Format: Variable length structure
# Value Format: FLOAT (IEEE-11073 32-bit FLOAT)
# Abstract: The Temperature Measurement characteristic is a variable length structure
# containing a Flags field, a Temperature Measurement Value field and, based upon the
# contents of the Flags field, optionally a Time Stamp field and/or a Temperature Type field.
# https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.temperature_measurement.xml

After some research about 2A1C and correlating this with https://www.bluetooth.com/specifications/assigned-numbers/format-types/, we conclude that this characteristic will require appropriate value encoding to IEEE-11073 32-bit FLOAT. However, we haven’t been able to find any Python implementation for that.

Research about IEEE-11073:20601 32-bit FLOAT

Wat!

Resources

Basic implementation for MicroPython

def encode_ieee11073(value, precision=2):
    """
    Binary representation of float value as IEEE-11073:20601 32-bit FLOAT for
    implementing the BLE GATT "Temperature Measurement 2A1C" characteristic.

    -- https://community.hiveeyes.org/t/convenient-ble-gatts-ess-with-micropython/2413/3

    print('Adding Temperature Measurement')
    payload = bytearray([0b00000000]) + float_ieee11073(42.42)
    service.characteristic(uuid=0x2A1C, value=payload)

    >>> ubinascii.hexlify(encode_ieee11073(42.42))
    b'921000fe'
    """

    # FIXME: Sanity checks dearly required.
    return int(value * (10 ** precision)).to_bytes(3, 'little', True) + pack('<b', -precision)

  1. Well, to begin with, what is a “personal health device” (PHD)? It is a type of medical apparatus that patients (and healthy persons) use without professional help, in the course of their normal lives: house, work, etc. ↩︎

How to use?

Encode UUID 0x2A1C variable length structure

Usage

service.characteristic(uuid=0x2A1C, value=encode_temperature_2a1c(42.42))

Encoding

def encode_temperature_2a1c(value):
    """
    Temperature Measurement
    UUID: 2A1C
    Format: Variable length structure
    Value Format: FLOAT (IEEE-11073 32-bit FLOAT)
    Abstract: The Temperature Measurement characteristic is a variable length structure
    containing a Flags field, a Temperature Measurement Value field and, based upon the
    contents of the Flags field, optionally a Time Stamp field and/or a Temperature Type field.
    https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.temperature_measurement.xml

    IEEE-11073
    ==========
    - https://learn.adafruit.com/introducing-the-adafruit-bluefruit-le-uart-friend/ble-gatt
    - https://devzone.nordicsemi.com/f/nordic-q-a/28727/32-bit-ieee-11073-float-data-type-parser
    - https://stackoverflow.com/questions/11564270/how-to-convert-ieee-11073-16-bit-sfloat-to-simple-float-in-java/14707658#14707658
    - http://www.java2s.com/example/java/language-basics/reads-a-decimal-value-as-a-ieee11073-16bits-float-from-bytebuffer.html
    - https://stackoverflow.com/questions/28899195/converting-two-bytes-to-an-ieee-11073-16-bit-sfloat-in-c-sharp/32950340#32950340
    """
    return bytearray([0b00000000]) + encode_ieee11073(value)

Decoding

Screenshot

Device Firmware Update profile (DFU)

BLE DFU let’s you update the device’s firmware.

Indeed, there are many of the neccesary contexts not described nor there seems to be a possibility to describe these from within the Characteristics itself.

e.g. “Temperature”: “Unit is in degrees Celsius with a resolution of 0.01 degrees Celsius” … ok, proper definition but keep in mind that would we have to disguinish between several (heights, indoor, outdoor, …)

e.g. “Humidity”: “Unit is in percent with a resolution of 0.01 percent” … I guess they mean realative humidity.

e.g. “Rainfall”: “Unit is in meters with a resolution of 1mm” … here we have a problem (besides we couldn’t integrate a water-equivalent of snow): You can’t talk about precipitation without providing a range of time. And the resolution of 1mm is roughly six times too low for a single tick of a simple rainfall-o-meter.

But interesting that there is such a infomation-snippet-data-format … could we occupy our own (more or less private) namespace within the BLE GATT ESS Characteristics? (and do a proper “import” of our and relevant $official definitions (e.g WMO/DWD) of $things-to-be-measured)

1 Like

Dear @wtf,

thanks for writing in.

We have been working hard (on the Python level) to reach these goals. However, we have been hit by a road block where only C++ superpowers might be able to help further.

Cheers,
Andreas.

1 Like