After a brief introduction about this project, it is time to know more in detail about our RSL10 device. BTLE devices can reveal quite useful information which I planned to understand first in order to progress with the project. Since I'm planning to use a Raspberry Pi 4 to act as a hub for all the beacons, I wrote a Python program that allowed me to do just that; gather all the information available, in fact, such program turned out to be very useful as I was able to use it to debug my progress since it reveals all the information from the RSL10 device in a user-friendly way.
I started by using an existing example "Custom Service Firmware" available for RSL10-Sense devices in the On Semiconductor IDE which uses the ICS BLE Service Protocol to communicate (location highlighted in the screenshot below). To make things even better, the script can be used as is.
When running the example with the Debug Probe connected and the RSL10 Sense and Mobile app, the information is right there which gives us a clue as to how the information flows. It uses the GATT protocol which basically uses a Master_Request/Slave_Response approach.
Connecting to the RSL10 with a Raspberry Pi 4
At this point of the project, a LM317 Adjustable Voltage Regulator comes in handy to power the RSL10-SENSE from the RPi during the tests and development. The LM317 outputs a voltage very close to 3V with R1 = 240 Ohm and R2 = 330 Ohm
To start the testing, I used the following command which reveals all the BTLE devices around:
sudo hcitool lescan
Our RSL10 is right there in the list of devices
To look more in detail at the data structure of the BTLE device, I used the gatttool command, where I was able effectively see the last data from the device:
GATT python script
Considering that the tools I just presented reveal the data in raw format, this requires extra steps and/or external tools to convert between ASCII and HEX for a user to understand better. At this point, I decided to go with my own approach with a Python script that will display the same data in a friendly manner. It turned out better than I expected as it allowed me to understand better how the LE devices interact and it also worked as Debug tool very well.
As you can see, there is a lot of useful information in the picture above, like the Handles that describe the purpose of each Characteristic and the Battery Level (0x2A19) which I'm surprised is not being used by the RSL10 Sense and Control Mobile app.
Source code
The source code is available for anyone to use. It uses bluepy, a Python module that allows communication with Bluetooth Low Energy devices.
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Luis Ortiz - luislab.com # March 8, 2020 from bluepy import btle import binascii import subprocess import re import sys from textcolor import textColor for pn in btle.Characteristic.propNames: btle.Characteristic.propNames[pn] = btle.Characteristic.propNames[pn].replace(" ", "_") def format_handle_value(value:bin, uuid): if (uuid == btle.AssignedNumbers.batteryLevel): # Assigned Number string = color.yellow(str(int(binascii.hexlify(value).decode('utf-8'), 16)) + "%") elif (uuid == btle.AssignedNumbers.genericAttribute): string = color.yellow(int(value, 16)) else: try: string = value.decode('utf-8') if string.isprintable(): string = color.purple('\'' + string + '\'') else: string = color.cyan("0x" + str(binascii.b2a_hex(value).decode('utf-8'))) except UnicodeDecodeError: string = color.lightcyan('0x' + binascii.b2a_hex(value).decode('utf-8')) return string def print_handles(phe, handle, stopHandle): while handle <= stopHandle: val = phe.readCharacteristic(handle) print ((color.green("0x%04x") + ": Handle <value=%s>") % (handle, format_handle_value(val, ""))) handle += 1 def format_assigned_number(uuid): assigned_number = "" if not (uuid.getCommonName() == str(uuid)): result = re.match('([0-9A-F]{4}){2}(-)', str(uuid).upper()) if result: assigned_number = " assignedNumber=" + color.grey("0x" + result.group(1)) return assigned_number color = textColor() arguments = len(sys.argv) - 1 if arguments == 1: macAddr = sys.argv[1].strip() if not re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", macAddr.lower()): print (color.red("\nInvalid MAC address")); quit() print ("\nConnecting...") elif arguments < 1: print (color.red("\nMissing MAC address")); quit() else: print (color.red("\nWrong number of arguments")); quit() try: p = btle.Peripheral(macAddr, btle.ADDR_TYPE_PUBLIC) except btle.BTLEException: print(color.red("Failed to connecto to BluetoothLE device ") + color.grey("%s") % macAddr) quit() print ("\nConnected [" + color.green(p.addr) + "]") for gs in p.getServices(): result = re.match('(.+uuid=)(.+)( handleStart=)([0-9]+)(.+handleEnd=)([0-9]+)(.*)', str(gs)) service = result.group(1) + \ color.grey(result.group(2)) + \ format_assigned_number(gs.uuid) + \ result.group(3) + \ color.grey(result.group(4)) + \ result.group(5) + \ color.grey(result.group(6)) + \ result.group(7) print (("\n" + color.green("0x%04x") + ": %s") % (gs.hndStart, service)) handle = gs.hndStart + 1 for gc in gs.getCharacteristics(): print_handles(p, handle, gc.getHandle()-1) strProperties = gc.propertiesToString().strip() print ((color.green("0x%04x") + \ ": Characteristic <uuid=" + color.grey("%s") + \ format_assigned_number(gc.uuid) + \ " properties=" + color.grey("0x%02x") + \ " (" + color.red("%s") + ")") \ % (gc.getHandle(), gc.uuid.getCommonName(), gc.properties, strProperties)) if gc.supportsRead(): val = gc.read() print ((color.green(" ") + ": value=%s>") \ % (format_handle_value(val, gc.uuid))) handle = gc.getHandle() + 1 print_handles(p, handle, gs.hndEnd) p.disconnect() print("\nDisconnected.") try: while 1 == 0: print("") except KeyboardInterrupt: #pass GPIO.cleanup() print("Bye")
Blogs in this series
- ON Sweet Home - Introduction
- ON Sweet Home - Getting to know your RSL10
- ON Sweet Home - Collecting the Beacon data
- ON Sweet Home - 3D printed parts
- ON Sweet Home - GUI is alive and the project is complete!