Search code examples
bluetoothbluetooth-lowenergyadafruit

Bluetooth Low Energy Weight Measurement Characteristic Timestamps


I'm buffering data when my device is not connected so I need to implement timestamps so I can tell what was measured when.

fortunately the weight measurement characteristic includes a timestamp.

unfortunately it's not clear how to write this data to the package since it's not a normal data type, and it's surely not just a single byte.

https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.weight_measurement.xml

I'm using Adafruit's bluefruit arduino library, so I've tried just ignoring the schema and writing a unix timestamp after the SI weight but unsurprisingly it seems like the schema is not allowing that so I'm not seeing the timestamp when I receive the notifications (but i'm still seeing the correct weight readings)

Here's the link for the date_time characteristic, which is apparently the format it expects https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.date_time.xml

I tried looking a bit in the nRF52 SDK so see if this is perhaps handled better through their API but the learning curve is a bit steep and I just need to finish this last bit to make my device work.

Update:

For anyone else with this issue the solution turned out to be that I was using the notify method the same way as it's written in the adafruit example

wmc.notify(notification, sizeof(notification))

because I was indexing through an N x 7 array of buffered data however notification is a pointer to the first element in the 1 x 7 array i was going to feed, so sizeof was always returning 4 (the size of the address I assume) instead of the length of the array originally written which was 7


Solution

  • The weight_scale service has two manadatory characteristics:

    <Characteristic type="org.bluetooth.characteristic.weight_scale_feature" name="Weight Scale Feature">
      <Requirement>Mandatory</Requirement>
    <Characteristic type="org.bluetooth.characteristic.weight_measurement" name="Weight Measurement">
      <Requirement>Mandatory</Requirement>
    

    In the weight_measurement characteristic (uuid="2A9D"), the first byte is flags. Where <Bit index="1" size="1" name="Time stamp present"> needs to be 1 to say there will be a "Time Stamp" field. That "Time Stamp" field will be:

    <Field name="Year"> <Format>uint16</Format> = 2 bytes
    <Field name="Month"> <Format>uint8</Format> = 1 byte
    <Field name="Day"> <Format>uint8</Format> = 1 byte
    <Field name="Hours"> <Format>uint8</Format> = 1 byte
    <Field name="Minutes"> <Format>uint8</Format> = 1 byte
    <Field name="Seconds"> <Format>uint8</Format> = 1 byte
    

    This makes the "Time Stamp" field 7 bytes wide.

    To give a worked example of how to create the full packet if you wanted weight (in Kg) and a time stamp it will need to 10 bytes long:

    <Field name="Flags"> <Format>8bit</Format>  = 1 byte
    <Field name="Weight - SI "> <Format>uint16</Format> = 2 bytes
    <Field name="Time Stamp"> = 7 bytes
    

    I've used Python to calculate the value of a packet:

    import struct
    
    flags = 0b00000010 # Include time. SI units
    weight_raw = 38.1 #  Weight of 38.1 Kg
    weight = int((weight_raw/5)*1000) # Formula from XML
    year = 1972
    month = 12
    day = 11
    hour = 23
    minute = 22
    second = 8
    
    packet = struct.pack('<BHHBBBBB', flags, weight, year, month, day, hour, minute, second)
    print(packet)
    

    Which would give a packet of 10 bytes long:

    b'\x02\xc4\x1d\xb4\x07\x0c\x0b\x17\x16\x08'