Using Python, Gatttool and Bluetooth Low Energy with Hexiwear


Now I can use the data on the Hexiwear over BLE with the gatttool (see “Tutorial: Hexiwear Bluetooth Low Energy Packet Sniffing with Wireshark” and “Tutorial: BLE Pairing the Raspberry Pi 3 Model B with Hexiwear“). This article is taking things a step further and uses a Python script on Linux to access the sensor data on the BLE device:

Accessing Hexiwear Sensor Data with Python

Accessing Hexiwear Sensor Data with Python

Outline

This article is about accessing the Mikroelektronika Bluetooth Low Energy Hexiwear (http://www.mikroe.com/hexiwear/) device with Python scripting from a Raspberry Pi. That way it is possible to send and receive data over BLE and do to whatever I want. Precondition is to have a working BLE connection and pairing with the Hexiwear using Bluez (see “Tutorial: BLE Pairing the Raspberry Pi 3 Model B with Hexiwear“). Python is a powerful scripting language and can be used for all kind of automation.

Installation

I’m using Python with the ‘pexpect’ package. First, make sure that latest packages are used:

sudo apt-get update

Next, make sure the PIP (Python Package Index) is installed:

sudo apt-get install git build-essential python-dev python-pip

Install the pexpect, a package to control other applications from Python:

sudo pip install pexpect

I’m using here the 4.2 version of the pexpect package.

Pexpect

With ‘pexpect‘ I can spawn a process from Python and then control it like I would type in the commands manually. With pexpect I can run the gatttool as I would type the commands on a console/terminal.

Here are the basic blocks to access the data on the Hexiwear with it:

The following Python variable is used to store the address of the BLE device used:

DEVICE = "00:32:40:08:00:12"

I run the gatttool with the -I (interactive) option:

child = pexpect.spawn("gatttool -I")

This returns the child process handle I can use. With

child.sendline("connect {0}".format(DEVICE))

I send the string to the gatttool (spawned process). The format member function is used to build the formatted string: “connect 00:32:40:08:00:12” as I would have it typed in.

Next I need to wait for the connection. For this I use

child.expect("Connection successful", timeout=5)

Which waits for the “Connection successful” string from the gatttool. I have specified that it should timeout after 5 seconds.

To read a BLE characteristics, I use

child.sendline("char-read-hnd 0x30")

The handle 0x30 is for reading the accelerometer values. The gatttool would print something like this:

Characteristic value/descriptor: 02 00 00 00 a1 ff

I’m waiting for the first part of the output:

child.expect("Characteristic value/descriptor: ", timeout=10)

and then for the end of line:

child.expect("\r\n", timeout=10)

with child.before I get the string just before the line end. E.g.

child.before[0:5]

Will return “02 00” as substring (first 4 characters) from “02 00 00 00 a1 ff” string. That way I get substrings of x, y and z accelerometer values.

To transform the hex string (in little endian) into a signed 16bit number, I use the following Python sub-function:

# function to transform hex string like "0a cd" into signed integer
def hexStrToInt(hexstr):
    val = int(hexstr[0:2],16) + (int(hexstr[3:5],16)<<8)
    if ((val&0x8000)==0x8000): # treat signed 16bits
        val = -((val^0xffff)+1)
    return val

The Hexiwear accelerometer values are ‘centi-float’ values, for example the numerical value 123 would be 1.23. With this, I can print the x, y and z values:

print(float(hexStrToInt(child.before[0:5]))/100),
print(float(hexStrToInt(child.before[6:11]))/100),
print(float(hexStrToInt(child.before[12:17]))/100)

Python Code to read Accelerometer, Gyro and Magnetometer

Here is the full source code of the code discussed above:

# Using Hexiwear with Python
import pexpect
import time

DEVICE = "00:32:40:08:00:12"

print("Hexiwear address:"),
print(DEVICE)

# Run gatttool interactively.
print("Run gatttool...")
child = pexpect.spawn("gatttool -I")

# Connect to the device.
print("Connecting to "),
print(DEVICE),
child.sendline("connect {0}".format(DEVICE))
child.expect("Connection successful", timeout=5)
print(" Connected!")

# function to transform hex string like "0a cd" into signed integer
def hexStrToInt(hexstr):
 val = int(hexstr[0:2],16) + (int(hexstr[3:5],16)<<8)
 if ((val&0x8000)==0x8000): # treat signed 16bits
 val = -((val^0xffff)+1)
 return val

#while True:
# Accelerometer
child.sendline("char-read-hnd 0x30")
child.expect("Characteristic value/descriptor: ", timeout=10)
child.expect("\r\n", timeout=10)
print("Accel: "),
print(child.before),
print(float(hexStrToInt(child.before[0:5]))/100),
print(float(hexStrToInt(child.before[6:11]))/100),
print(float(hexStrToInt(child.before[12:17]))/100)

# Accelerometer
child.sendline("char-read-hnd 0x34")
child.expect("Characteristic value/descriptor: ", timeout=10)
child.expect("\r\n", timeout=10)
print("Gyro: "),
print(child.before),
print(float(hexStrToInt(child.before[0:5]))/100),
print(float(hexStrToInt(child.before[6:11]))/100),
print(float(hexStrToInt(child.before[12:17]))/100)

# Magnetometer
child.sendline("char-read-hnd 0x38")
child.expect("Characteristic value/descriptor: ", timeout=10)
child.expect("\r\n", timeout=10)
print("Magneto:"),
print(child.before),
print(hexStrToInt(child.before[0:5])),
print(hexStrToInt(child.before[6:11])),
print(hexStrToInt(child.before[12:17]))

Save the script to a file (e.g. gatttool.py) and run it with

python gatttool.py

This produces something like this:

Hexiwear address: 00:32:40:08:00:12
Run gatttool...
Connecting to 00:32:40:08:00:12 Connected!
Accel: 03 00 ff ff a1 ff 0.03 -0.01 -0.95
Gyro: 00 00 02 00 00 00 0.0 0.02 0.0
Magneto: b8 fc e2 04 c8 28 -840 1250 10440

Instead of printing the values, I can store them to a file or whatever I would like to do.

Python Script to update Date/Time

Below is a Python script similar to the one above which sets the Hexiwear current date and time using the Unix time (seconds after 1970) from the Raspberry Pi:

# Python script to set the time on the Hexiwear
import pexpect
import time
from time import gmtime, strftime

print("---------------------")
print("Setting linux time")
print("local time: "),
print(time.ctime())
unixTime = int(time.time())
print("secs since 1970: "),
print(int(unixTime))
print("---------------------")

DEVICE = "00:32:40:08:00:12"

# Run gatttool interactively.
print("Running gatttool...")
child = pexpect.spawn("gatttool -I")

# Connect to the device.
print("Connecting to"),
print(DEVICE),
child.sendline("connect {0}".format(DEVICE))
child.expect("Connection successful", timeout=5)
print("Connected!")

# Write local time
command = "char-write-req 61 0304{0:02x}{1:02x}{2:02x}{3:02x}0000000000000000000000000000".format(unixTime&0xff, (unixTime>>8)&0xff, (unixTime>>16)&0xff, (unixTime>>24)&0xff)
print(command)
child.sendline(command)
child.expect("Characteristic value was written successfully", timeout=10)

print("done!")

And this is how it looks on the terminal:

Writing Unix Time to Hexiwear

Writing Unix Time to Hexiwear

Summary

Python is great for scripting things. With the pexpect Python class I can spawn a process and then send and receive strings. I’m using this in my article to read sensor values from a BLE device connected to the Raspberry Pi.

Happy Pythoning 🙂

Links

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s