Ok, so now time to see if I can decode the HR packets I'm getting back. I've seen
`bytearray(b'i\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00i')` and `bytearray(b'i\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00j')` when the ring is off. When on my finger I got `bytearray(b'i\x00\x00F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaf')`
Ok, reading through the source code for the response handling, the first byte is used to indicate the response type that matches the request. The high bit is used to indicate an error
This checks out as `i` == `105` which is `CMD_START_HEART_RATE`.
Now checking out the parsing code. We have a snippet like this
```java
public boolean acceptData(byte[] bArr) {
this.type = bArr[0];
this.errCode = bArr[1];
this.value = bArr[2];
if (bArr.length >= 5) {
this.sbp = bArr[3];
this.dbp = bArr[4];
}
return false;
}
```
and `bArr` is `Arrays.copyOfRange(bArr, 1, bArr.length - 1))` which drops the first byte, which is the response type/error code byte.
It looks like the code only care about the first 5 elements though, so I don't really know what the final byte is for.
Happy path
```java
if (resultEntity.getErrCode() == 0 && resultEntity.getType() == 1) {
if (resultEntity.getValue() > 0) {
...
```
```java
if (resultEntity.getErrCode() == 1) {
String string2 = HeartActivity.this.getString(R.string.qc_text_6666064);
```
This string is "Not Worn Properly"
```java
if (resultEntity.getErrCode() == 0 || resultEntity.getErrCode() == 2) {
return;
}
```
There's a 2 error code as well, but it looks like it isn't handled really.
```python
def parse_response(packet: bytearray) -> dict[str, int]:
if packet[0] > 127: # high bit is set
assert False, f"Packet has error bit set {packet}"
if packet[0] == CMD_START_HEART_RATE:
return {
"type": packet[1],
"errCode": packet[2],
"value": packet[3],
}
else:
assert False, "unknown packet type"
```
This produces some heat rate values! They're even pretty close to what I'm measuring with beurer finger mounted heart rate / blood oxygen monitor across two people!! Woo!
I think the last byte might be the CRC again, but it looks like it's not checked by the Android app where I was looking
Anyway, here's the full source code so far: https://github.com/tahnok/ATC_RF03_Ring/blob/16a3018b143853b5b3e6a16a0d4015fd4e2b1890/python_client/client.py