RaspberryPi 3 and SensorTag

I’ve been in a hackey type of mood these last couple of days. Picking up a new Raspberry Pi 3 probably did that, up until now I’ve only had the Raspberry Pi Model B.

The new features that come with the Pi 3 are great. What do I have to say, you had me at build in wifi, bluetooth and 1 GB of memory.

To take full advantage of the bluetooth I decided to learn a little about BLE (Bluetooth low energy). I began poking at my Fitbit Charge HR, but wasn’t able to do anything more than pull the device name. This led me to find the TI SensorTag. This device packs a number of sensors into a tiny package, and offers wireless communication using BLE!


After getting my Pi 3 setup with Raspbian, I installed a few bluetooth packages from the official repositories:

pi@raspberrypi:~ $ dpkg -l | grep blue
ii blueman 1.99~alpha1-1+deb8u1 armhf Graphical bluetooth manager
ii bluez 5.23-2+rpi2 armhf Bluetooth tools and daemons
ii bluez-firmware 1.2-3+rpi1 all Firmware for Bluetooth devices
ii libbluetooth3:armhf 5.23-2+rpi2 armhf Library to use the BlueZ Linux Bluetooth stack
ii pi-bluetooth 0.1.1 armhf Raspberry Pi 3 bluetooth

Using the hcitool tool provided by bluez, I scanned the area for BLE devices and found the Sensor Tag (and it’s Hardware Address).

This tool can take either lescan or scan as a parameter, where the latter will search for traditional bluetooth devices.

pi@raspberrypi:~ $ sudo hcitool lescan
LE Scan ...
A0:XX:XX:XX:XX:XX CC2650 SensorTag

Bluez also provides us gatttool, this tool provides a way to connect to the SensorTag in interactive mode (the -I flag).

Gatttool allows us to communicate with a BLE devices using the GATT protocol.

pi@raspberrypi:~ $ 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

Gatttool provides a number of useful commands, check them all out using the help command:

[A0:XX:XX:XX:XX:XX][LE]> help
help Show this help
exit Exit interactive mode
quit Exit interactive mode
connect [address [address type]] Connect to a remote device
disconnect Disconnect from a remote device
primary [UUID] Primary Service Discovery
included [start hnd [end hnd]] Find Included Services
characteristics [start hnd [end hnd [UUID]]] Characteristics Discovery
char-desc [start hnd] [end hnd] Characteristics Descriptor Discovery
char-read-hnd <handle> Characteristics Value/Descriptor Read by handle
char-read-uuid <UUID> [start hnd] [end hnd] Characteristics Value/Descriptor Read by UUID
char-write-req <handle> <new value> Characteristic Value Write (Write Request)
char-write-cmd <handle> <new value> Characteristic Value Write (No response)
sec-level [low | medium | high] Set security level. Default: low
mtu <value> Exchange MTU for GATT/ATT

The one that I’m the most interested in is char-desc, this command works as discovery, and will give us every handle on the device:

[A0:XX:XX:XX:XX:XX][LE]> char-desc
handle: 0x0001, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0002, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0004, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0006, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0007, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x0008, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0009, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x000a, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x000b, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x000c, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x000d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x000e, uuid: 00002a23-0000-1000-8000-00805f9b34fb
handle: 0x000f, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0010, uuid: 00002a24-0000-1000-8000-00805f9b34fb
handle: 0x0011, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0012, uuid: 00002a25-0000-1000-8000-00805f9b34fb
handle: 0x0013, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0014, uuid: 00002a26-0000-1000-8000-00805f9b34fb
handle: 0x0015, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0016, uuid: 00002a27-0000-1000-8000-00805f9b34fb
handle: 0x0017, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0018, uuid: 00002a28-0000-1000-8000-00805f9b34fb
handle: 0x0019, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x001a, uuid: 00002a29-0000-1000-8000-00805f9b34fb
handle: 0x001b, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x001c, uuid: 00002a2a-0000-1000-8000-00805f9b34fb
handle: 0x001d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x001e, uuid: 00002a50-0000-1000-8000-00805f9b34fb
handle: 0x001f, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0020, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0021, uuid: f000aa01-0451-4000-b000-000000000000
handle: 0x0022, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0023, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0024, uuid: f000aa02-0451-4000-b000-000000000000
handle: 0x0025, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0026, uuid: f000aa03-0451-4000-b000-000000000000
handle: 0x0027, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0028, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0029, uuid: f000aa21-0451-4000-b000-000000000000
handle: 0x002a, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x002b, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x002c, uuid: f000aa22-0451-4000-b000-000000000000
handle: 0x002d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x002e, uuid: f000aa23-0451-4000-b000-000000000000
handle: 0x002f, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0030, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0031, uuid: f000aa41-0451-4000-b000-000000000000
handle: 0x0032, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0033, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0034, uuid: f000aa42-0451-4000-b000-000000000000
handle: 0x0035, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0036, uuid: f000aa44-0451-4000-b000-000000000000
handle: 0x0037, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0038, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0039, uuid: f000aa81-0451-4000-b000-000000000000
handle: 0x003a, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x003b, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x003c, uuid: f000aa82-0451-4000-b000-000000000000
handle: 0x003d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x003e, uuid: f000aa83-0451-4000-b000-000000000000
handle: 0x003f, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0040, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0041, uuid: f000aa71-0451-4000-b000-000000000000
handle: 0x0042, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0043, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0044, uuid: f000aa72-0451-4000-b000-000000000000
handle: 0x0045, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0046, uuid: f000aa73-0451-4000-b000-000000000000
handle: 0x0047, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0048, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0049, uuid: 0000ffe1-0000-1000-8000-00805f9b34fb
handle: 0x004a, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x004b, uuid: 00002901-0000-1000-8000-00805f9b34fb
handle: 0x004c, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x004d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x004e, uuid: f000aa65-0451-4000-b000-000000000000
handle: 0x004f, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0050, uuid: f000aa66-0451-4000-b000-000000000000
handle: 0x0051, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0052, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0053, uuid: f000ac01-0451-4000-b000-000000000000
handle: 0x0054, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0055, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0056, uuid: f000ac02-0451-4000-b000-000000000000
handle: 0x0057, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0058, uuid: f000ac03-0451-4000-b000-000000000000
handle: 0x0059, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x005a, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x005b, uuid: f000ccc1-0451-4000-b000-000000000000
handle: 0x005c, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x005d, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x005e, uuid: f000ccc2-0451-4000-b000-000000000000
handle: 0x005f, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0060, uuid: f000ccc3-0451-4000-b000-000000000000
handle: 0x0061, uuid: 00002800-0000-1000-8000-00805f9b34fb
handle: 0x0062, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0063, uuid: f000ffc1-0451-4000-b000-000000000000
handle: 0x0064, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0065, uuid: 00002901-0000-1000-8000-00805f9b34fb
handle: 0x0066, uuid: 00002803-0000-1000-8000-00805f9b34fb
handle: 0x0067, uuid: f000ffc2-0451-4000-b000-000000000000
handle: 0x0068, uuid: 00002902-0000-1000-8000-00805f9b34fb
handle: 0x0069, uuid: 00002901-0000-1000-8000-00805f9b34fb

Oh boy, that is a lot of text! Luckily Texas Instruments provides a full GATT Table on the devices TearDown page.

To start out easy, I picked a handle that looked descriptive and straight forward, the Device Name!

Handle Type Permissions
0x3 Device Name R

Back in gatttool I’m going to use the char-read-hnd command to read from this handle:

[A0:XX:XX:XX:XX:XX][LE]> char-read-hnd 0x3
Characteristic value/descriptor: 53 65 6e 73 6f 72 54 61 67 20 32 2e 30

It is also worth noting we can use the longer hex value (0x0003) returned from char-desc to get the same value:

[A0:XX:XX:XX:XX:XX][LE]> char-read-hnd 0x0003
Characteristic value/descriptor: 53 65 6e 73 6f 72 54 61 67 20 32 2e 30

The value returned is a set of hexadecimal values. Using Python I can  decode them and learn their secrets 😛

In [1]: val = '53 65 6e 73 6f 72 54 61 67 20 32 2e 30'

In [2]: [ i.decode('hex') for i in val.split() ]
Out[2]: ['S', 'e', 'n', 's', 'o', 'r', 'T', 'a', 'g', ' ', '2', '.', '0']

In [3]: ''.join([ i.decode('hex') for i in val.split() ])
Out[3]: 'SensorTag 2.0'

Check back soon, I plan to post more as I learn about this sweet device.

Process Elasticsearch JSON on the shell

Lets throw security out the window for a moment. Say we store user accounts with clear text passwords in Elasticsearch, what is the easiest way to use the results in a shell script? We can begin by creating two accounts, one for admin and one for john:

# curl -XPUT localhost:9200/site/people/1?pretty=True -d '
  {"name": "admin", "password": "secret", "admin": "true"}
  "_index" : "site",
  "_type" : "people",
  "_id" : "1",
  "_version" : 1,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
 "created" : true
# curl -XPUT localhost:9200/site/people/2?pretty=True -d '
  {"name": "john", "password": "password", "admin": "false"}
  "_index" : "site",
  "_type" : "people",
  "_id" : "1",
  "_version" : 2,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
 "created" : false

Using curl this is very easy to query, we just use the id:

# curl localhost:9200/site/people/2?pretty=True
  "_index" : "site",
  "_type" : "people",
  "_id" : "2",
  "_version" : 1,
  "found" : true,
  "_source" : {
    "name" : "john",
    "password" : "password",
    "admin" : "false"

But what we really want is to get the values of the keys without needing to hack it with sed/awk/grep. We can install a small json parsing package called jq:

# apt-get install jq

Now we can easily pull the keys out:

# curl -s localhost:9200/site/people/2 | jq ._source.name

# curl -s localhost:9200/site/people/2 | jq ._source.password

# curl -s localhost:9200/site/people/2 | jq ._source.admin

Using the power of jq we could easily search Elasticsearch and perform comparisons for login, remember we are throwing security out the window here, this is merely for example:

# curl -s $ip:9200/site/people/_search?q=name:admin | jq -r '.hits.hits'
    "_source": {
      "admin": "true",
      "password": "secret",
      "name": "admin"
    "_score": 0.30685282,
    "_id": "1",
    "_type": "people",
    "_index": "site"

We can assume this query only returns one user, and we are pretty sure the account exists, so lets grab the password key directly:

# curl -s $ip:9200/site/people/_search?q=name:admin |
  jq -r '.hits.hits[0]._source.password'

If the value is not present, and we attempt to access it, we will get a null result:

# curl -s $ip:9200/site/people/_search?q=name:jack |
  jq -r '.hits.hits[0]._source.password'


And here it is all wrapped up in a poorly written shell script, I would not advise using this logic for anything more than toying with:



read -p "Login username: " username
read -p "Login password: " password

account=`curl -s localhost:9200/site/people/_search?q=name:$username |
  jq '.hits.hits[0]'`

if [ "$account" != "null" ] ; then

  account_username=`echo $account | jq -r ._source.name`

  if [ "$username" == "$account_username" ] ; then

    account_password=`echo $account | jq -r ._source.password`

    if [ "$password" == "$account_password" ] ; then
      echo "You are in!"
      echo "Wrong username or password"



And usage looks like this:

# ./auth.sh
Login username: admin
Login password: password
Wrong username or password
# ./auth.sh
Login username: john
Login password: password
You are in!


Telegraf laptop battery plugin

Wanted to expand a little on my previous blog post Custom Telegraf Plugin, and decided to do a simple battery monitor. The end result looks something like this:

Screen Shot 2016-04-11 at 5.40.47 PM

I decided to read from the file /sys/class/power_supply/BAT0/capacity on my Ubuntu 14.04 machine, this file merely shows the current battery percent:

# cat /sys/class/power_supply/BAT0/capacity

All that is needed is a little Python script for converting this output to JSON, my script outputs like this:

# /battery.py
{"status": "Charging", "capacity": 63}

The code is very basic Python:


#!/usr/bin/env python
import json

with open('/sys/class/power_supply/BAT0/capacity') as f:
 capacity = f.read().split('\n')[0]
 capacity = int(capacity)

with open('/sys/class/power_supply/BAT0/status') as f:
 status = f.read().split('\n')[0]

results = dict(capacity=capacity,

print json.dumps(results)

All that is left is to add this script to our Telegraf configuration directory:

# cat /etc/telegraf/telegraf.d/battery.conf
 command = "/battery.py"
 data_format = "json"
 name_suffix = "_battery"
 interval = "5s"

And there you have it, a simple battery tracker.