So I jumped on the bandwagon when I saw a [hackaday post](https://hackaday.com/2024/06/16/new-part-day-a-hackable-smart-ring/) about a $20 smart ring in the SensorWatch discord. Seems cheap enough to have a bit of fun with, and I'd love to be able to turn it into a bog standard heart rate monitor that strava (or whatever else) can use. I think that's part of the bluetooth spec somewhere (maybe ANT?). Something to dig into another day.
For now I want to see if maybe I can build my own app to measure heart rate without using the QRing android app.
I fired up NRFConnect to take a look at the bluetooth services and characteristics available.
`6e40fff0-b5a3-f393-e0a9-e50e24dcca9e` looked particular interesting as it has an RX characteristic that supports writing and a TX characteristic with notify. Seems like a serial port to me. There's also another similarly looking service.
Armed with a magic number, I decided to decompile the android app and have a look through the source code. 5 minutes of googling led me to [JADX](https://github.com/skylot/jadx) which didn't require much to get running and seems to be working well so far. https://github.com/atc1441/ATC_RF03_Ring contains an APK that has apparently been patched already to remove cert pining, I guess so that you could sniff on the network traffic
Reading the source confirmed that `6e40fff0-b5a3-f393-e0a9-e50e24dcca9e` was indeed used as a serial port style thing, manged by `CommandHandle`. It's used in `HeartActivity` and I can see this line `CommandHandle.getInstance().executeReqCmdNoCallback(new RealTimeHeartRate(3));` which sure sounds like it sends a request for real time heart rate.
```java
public byte[] getData() {
byte[] bArr = new byte[Constants.CMD_DATA_LENGTH];
bArr[0] = this.key;
byte[] subData = getSubData();
if (subData != null) {
System.arraycopy(subData, 0, bArr, 1, subData.length);
}
addCRC(bArr);
return bArr;
}
private void addCRC(byte[] bArr) {
int i = 0;
for (int i2 = 0; i2 < bArr.length - 1; i2++) {
i += bArr[i2];
}
bArr[bArr.length - 1] = (byte) (i & 255);
}
```
In this case `key = 30`, `CMD_DATA_LENGTH = 16`. The subdata decompilation seems a bit weird
```java
public class RealTimeHeartRate extends BaseReqCmd {
private byte[] mData;
private int type;
public RealTimeHeartRate(int i) {
super(Constants.CMD_REAL_TIME_HEART_RATE);
this.mData = r0;
this.type = i;
byte[] bArr = {(byte) i};
}
@Override // com.oudmon.ble.base.communication.req.BaseReqCmd
protected byte[] getSubData() {
return this.mData;
}
public int getType() {
return this.type;
}
public void setType(int i) {
this.type = i;
}
}
```
There's no r0 (maybe it's a register?) and the `bArr` seems like it's a no-op. Either it sends 0 or 3, guess I can try both.
The CRC appears to be a sum of all bytes mod 255.
Putting together a little test client in python using [bleak](https://github.com/hbldh/bleak), but more on that tomorrow.
Coincidentally looked at the [bleak uart example](https://github.com/hbldh/bleak/blob/master/examples/uart_service.py) and the characteristics match, which means that this serial port is based on the Nordic UART service. They changed the service UUID ever so slightly