SensorTag metrics with Grafana

If you haven’t been following, I’ve done a little research into the Texas Instruments SensorTag starting with post RaspberryPi 3 and SensorTag.

Just the other day I stumbled upon the official Texas Instruments wiki for the SensorTag CC2650, the wiki does a good job outlining the formulas to calculate each sensors value.

This lead me to write a minimal Python class (see on Github) using some of the formulas found in the wiki. Beyond just the Python classes, I wrote in a periodic process; this process will save the sensor data to screen, and write to /root/sensor.json every five seconds.

pi@raspberrypi:~# cat /sensor.json
 "pressure": 987.2,
 "humidity": 58.44,
 "temperature": 75.65,
 "light": 19.43

To better visualize the gathered data I decided to store all metrics in Telegraf, all I needed was a simple execution plugin:

pi@raspberrypi:~# cat /etc/telegraf/telegraf.d/sensor_tag.conf
 command = "cat /sensor.json"
 data_format = "json"
 name_suffix = "_sensor_tag"
 interval = "5s"

Then on my workstation I installed Grafana, and connected Telegraf as a remote data source.

This is the type of data I’ve been gathering and storing from the SensorTag, seems like we could make a weather station pretty easy.



SensorTag Temperature Readings in Python

I wanted to wrap up my previous post (TI SensorTag Temperature Readings) with a little python example, thus this write is going to be short and sweet.

Using the bluepy python library (written by Ian Harvey) I’ve been able to to capture temperature readings, then covert them to Fahrenheit.

To demonstrate I captured a couple temperature samples, a few while sitting on my desk, and a few held up to my air condition vent:

root@raspberrypi:~# python
79.31 degrees fahrenheit
79.31 degrees fahrenheit
79.31 degrees fahrenheit
78.46 degrees fahrenheit
78.01 degrees fahrenheit
76.1 degrees fahrenheit
76.49 degrees fahrenheit
76.27 degrees fahrenheit
75.99 degrees fahrenheit
76.16 degrees fahrenheit

The Python is pretty straight forward:

#!/usr/bin/env python

from bluepy import btle
from time import sleep

sensor = btle.Peripheral('A0:xx:xx:xx:xx:xx')

sensor.writeCharacteristic(0x24, '\x01', withResponse=True)

sleep(1) # warm up

for i in range(10):
    t_data = sensor.readCharacteristic(0x21)
    msb = ord(t_data[3])
    lsb = ord(t_data[2])
    c = ((msb * 256 + lsb) / 4) * 0.03125
    f = c * 9/5.0 + 32
    print '%s degrees fahrenheit' % round(f, 2)

sensor.writeCharacteristic(0x24, '\x00', withResponse=True)

Happy hacking!

TI SensorTag Temperature Readings

Now that I’m a little more familiar with the Texas Instruments SensorTag, it’s time to process more of the sensors. I went over basic gatttool usage in my previous post, I would suggest giving that a read if you haven’t already.

This time I want to grab simple temperature reading from the BLE (Bluetooth Low Energy) handle. Reading the TearDown specs I managed to find the GATT table for the Infrared and Ambient temperature sensor:


Apparently, this is using the TMP007 sensor. The specifications for this component are located at Section 7.3.7 gives of a bit of detail on how to calculate temperatures:


Alright, with that out of the way, let’s get into reading reading some BLE handles.

The first handle from our GATT table is to read temperature data. It provides values for both IR and Ambient temperatures, and does so using MSB (most significant byte) and LSB (least significant bit). I’ll explain this in much more detail once we’ve gather some readings.

Handle Type Description/Value (text)
0x21 Temp Data ObjectLSB:ObjectMSB:AmbientLSB:AmbientMSB

The last handle we need is to have the sensor start (and stop) reading temperature data:

Handle Type Description/Value (text)
0x24 Temp Config Write “01” to start Sensor and Measurements, “00” to put to sleep

Let’s jump into gatttool and do some exploration:

$ sudo gatttool -b A0:XX:XX:XX:XX:XX -I
[A0:XX:XX:XX:XX:XX][LE]> connect
Attempting to connect to A0:XX:XX:XX:XX:XX
Connection successful

If we try to read from the temperature data handle, and the sensor is not measuring we will receive empty sets:

[A0:XX:XX:XX:XX:XX][LE]> char-read-hnd 0x21
Characteristic value/descriptor: 00 00 00 00

However, once we enable measuring, we get that sweet, sweet data 😀

[A0:XX:XX:XX:XX:XX][LE]> char-write-cmd 0x24 01
[A0:XX:XX:XX:XX:XX][LE]> char-read-hnd 0x21
Characteristic value/descriptor: 78 09 9c 0c

I’m now going to turn off measuring, we’ve captured the data we want.

[A0:XX:XX:XX:XX:XX][LE]> char-write-cmd 0x24 00

I’m going to take the values returned for ambient temperature and demonstrate converting to Celsius in Python:

In [1]: ((0x0c * 256 + 0x9c) / 4) * 0.03125
Out[1]: 25.21875

The measurement here is close to the reading from SensorTag’s iPhone application:


So how does this formula work?

Well in the above example for ambient temperature, the MSB was 0c, while the LSB was 9c. Let’s have a look at these hexadecimal numbers in Python, with their binary representations:

In [1]: 0x0c
Out[1]: 12

In [2]: bin(0x0c)
Out[2]: '0b1100'

In [3]: 0x9c
Out[3]: 156

In [4]: bin(0x9c)
Out[4]: '0b10011100'

We know a binary byte consists of 8 bits, so the below should also make sense, I’m just padding our MSB with zeros to show a full binary byte:

In [1]: int('00001100', 2)
Out[1]: 12

In [2]: int('10011100', 2)
Out[2]: 156

So here is where it gets fun. What we do is merge the full binary representations of MSB and LSB and form 16 bit value, this is what the first part of the formula does:

In [1]: int('0000110010011100', 2)
Out[1]: 3228

In [2]: (0x0c * 256 + 0x9c)
Out[2]: 3228

The next part of the formula performs the right shift by two (explained in the sensor specifications above):

In [1]: int('00001100100111', 2)
Out[1]: 807

In [2]: ((0x0c * 256 + 0x9c) / 4)
Out[2]: 807

And finally, we multiply by 0.03125, also mentioned in the sensor specifications:

In [1]: int('00001100100111', 2) * 0.03125
Out[1]: 25.21875

In [2]: ((0x0c * 256 + 0x9c) / 4) * 0.03125
Out[2]: 25.21875

Hope you enjoyed, and maybe learned something. It took me some time, and digging to fully comprehend this specification.