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