Work in progress for RadonEye200 Bluetooth LE communication.
##########################
TL:DR:
Send “0x50” to Bluetooth LE GATT characteristic UUID = 00001524-1212-efde-1523-785feabcd123
Receive a hex value like 50 10 AE 47 61 3F 14 AE 87 3F 00 00 00 00 02 00 05 00 00 00
The 3rd to 6th bytes of this are the IEEE 754 representation of the floating point value for the current Radon level (in big-endian format)
##########################
I own a RadonEye200 Bluetooth LE Radon monitor.
It’s a great monitor with a small display on the device, as well as an app that lets you view live readings and download months-long history stored on the device.
I’ve wanted to find a way to get this data into HomeAssistant, but there is no official computer client interface.
I recently learned that Bluetooth LE (BLE) is a pretty basic protocol and completely separate from typical Bluetooth protocols used for audio, keyboards, mice, etc, etc. For starters, BLE devices often do not require pairing in the same way that a normal Bluetooth connection would, given the target use is for IoT devices.
A quick BLE overview is, the device acts as the server, and your phone or data receiver is called the client.
The server advertises ‘characteristics’ that can be written to or read by the client. The server can also send notifications to a connected client. The data is sent by the server in 20 byte chunks. This protocal is called GATT. You can use a program called nRF Connect on Android to view local devices, what characteristics they are advertising, as well as send and receive data.
First, I used Androids built-in Bluetooth packet logger to record a basic session of the RadonEye app communicating with the device, and viewed the log in WireShark.
The app initially sends a series of commands, I believe to verify the services and read some data like the serial number and firmware version. From what I can tell, no authentication is happening.
It then alternates sending two commands, “50” and “51” to elicit data from the device.
These are sent to the bluetooth characteristic UUID = 00001524-1212-efde-1523-785feabcd123
In response to each write on UUID 00001524…, the device quickly sends a notification on a different bluetooth characteristic UUID = 00001525-1212-efde-1523-785feabcd123
The sequence I captured was like this, while the RadonEye displayed 0.92 pCi/L:
Write: 50
Receive: 50 10 1F 85 6B 3F 14 AE 87 3F 00 00 00 00 03 00 02 00 00 00
Write: 51
Receive: 51 0E 02 00 17 25 00 00 87 61 08 00 67 94 ED 3F 02 00 00 00
I captured another sequence while the RadonEye displayed 0.88 pCi/L:
Write (hex): 0x50
Receive (hex): 50 10 AE 47 61 3F 14 AE 87 3F 00 00 00 00 02 00 05 00 00 00
Write (hex): 0x51
Receive (hex): 51 0E 02 00 1E 25 00 00 23 66 08 00 67 94 ED 3F 05 00 00 00
There are some constants between the two, but at this point, it’s not clear what any of this means.
I confirmed using the nRF Connect Android app, that I could send “0x50” or “0x51” to the device and receive data back, so I’m pretty sure there is no handshake/authentication/etc required to read the data.
I have some experience doing static analysis of Android app source code, that seemed like the easiest next step to decode the data being sent out. I used a program called Bytecodeviewer, which decompiles APK files to give you readable java source code. It actually lets you compare the output of several different decompilers, and in this case I found Procyon to be the most readable.
I gathered that, when the app receives a notification, the method onCharacteristicNotified() is triggered in class RD200Manager$1. This splits out the 1st byte to indicate the data-type, the 2nd byte as a data-length variable, and what I call the data payload starts at the 3rd byte.
A few intermediate functions are called that don’t do anything but send the data-type and the data payload as arguments to the method recDataParse() in class ActivityDetail.
recDataParse looks at the data-type byte to decide how to interpret the data payload.
In the case of data type 0x50 (int = 80), it takes the 1st 4 bytes of the data payload, runs them through the method arr2float in class Utils, and assigns the result to this.mDevice.MeasData.readValue
This seems promising, and the rest of the payload is similarly processed and assigned to:
this.mDevice.MeasData.dayValue
this.mDevice.MeasData.monthValue
this.mDevice.MeasData.PulseCount
this.mDevice.MeasData.PulseCount_10min
Focusing on this.mDevice.MeasData.readValue, I assume this will store the current Radon level. Looking at arr2float, what it’s doing is taking the 4 bytes, bit shifting them into a single 32 bit integer, and converting this to a floating point number using the builtin Float.intBitsToFloat() method. Note that the bytes are sent as big-endian for compatibility with Java.
So essentially, the 3rd to 6th bytes of the original value sent by the RadonEye in response to the command “0x50” are the IEEE 754 representation of the floating point value for the current Radon level (in big-endian format).
In the 1st data I captured above, these bytes are 1F 85 6B 3F (0x3F6B851F as little-endian), and that is the IEE754 representation of ‘0.92’
Looking at how recDataParse treats values starting with 0x51(int=81), it seems to be a bunch of other configuration information, like what the units are, information about the optional Alarm settings, etc.
That’s as far as I’ve gotten.
It looks like it will be pretty straightforward now to read this data from the device. The only issue I’m not sure of, is the device timeout. It seems to drop the connection after ~30-60s of no command sent by the client. If that can’t be changed the service will have to query more quickly than that, or just set up the connection every time.
My ultimate goal, however, is to get this work on FreeBSD (FreeNAS), which is where my homeassistant install lives. FreeBSD has pretty minimal bluetooth drivers, and pybluez doesn’t support FreeBSD. There are some options for USB Bluetooth LE devices that expose an AT-command interface to their onboard Bluetooth stack over a serial connection, so no drivers are required. There are several potential options but the ESP32 seems to be the best. https://github.com/espressif/esp32-at/blob/master/docs/ESP32_AT_Commands_Set.md
If anyone has suggestions for this please post here!!
Some other options:
There may be a way with the HM-10/AT-09 modules based on CC2540
http://www.martyncurrey.com/hm-10-bluetooth-4ble-modules/#Using_The_HM-10_With_non-HM-10_Modules
Although it seems a bit ridiculous to bridge BLE to wifi (MQTT), its an option as well
ESP32 BLE to MQTT bridge:
Or similar on rPi
(No Central profile by default): https://learn.adafruit.com/introducing-adafruit-ble-bluetooth-low-energy-friend/faq
This is promising but I haven’t found an easy USB version:
https://www.telit.com/m2m-iot-products/wifi-bluetooth-modules/bluemods/