tahnok / colmi_r02_client

A python client + documentation for the Colmi R02 smart ring
MIT License
355 stars 22 forks source link

My device probably has different attributes? [Observation] #6

Open crosser opened 1 month ago

crosser commented 1 month ago

I got a a ring (from a European vendor), this is how it announces itself when I do passive scan:

service_uuids=[
    '00001800-0000-1000-8000-00805f9b34fb', # GAP
    '00001801-0000-1000-8000-00805f9b34fb', # GATT
    '0000180a-0000-1000-8000-00805f9b34fb', # Device Information
    '00003802-0000-1000-8000-00805f9b34fb', # <unassigned> (while "1802" is "Immediate Alert")
    '6e40fff0-b5a3-f393-e0a9-e50e24dcca9e', # ?
    'de5bf728-d711-4e47-af26-65e3012a5dc7' # ?
]

Your app finds the device with scan:

$ ~/.local/bin/colmi_r02_util scan
Found device(s)
                Name  | Address
--------------------------------------------
            R02_79B5  |  FF:A2:C5:34:79:B5

but attempts to perform any commands result in different errors, and sometimes the device disappears from scan for a minute (probably stays "connected" while the app has already terminated). One interesting is

$ ~/.local/bin/colmi_r02_client --address FF:A2:C5:34:79:B5 set-time
colmi_r02_client.client: Did not expect this packet: bytearray(b'/\xf1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 ')

Anyway I am getting these values from the "180a" service:

Firmware Revision String: R02_3.00.02_240327
Model Number String: BX-BLE-5.0
System ID: b'\x124V\xff\xfe\x9a\xbc\xde'
Hardware Revision String: R02_V3.0
Serial Number String: BX-DEVICE-001
PnP ID: (b'\x02^\x04@\x00\x00\x03'
IEEE 11073-20601 Regulatory Cert. Data List: b'\xff\xee\xdd\xcc\xbb\xaa'
Manufacturer Name String: Bluex

In case it may be of any use to you...

Edit: after more attempts, sometimes I get reasonable response. I was able to enable periodic hr measurements. But more than 50% of the time, it either cannot find the device, or gets a timeout reading an attribute etc.

crosser commented 1 month ago

After some playing, looks like most of the glitches are explained by aggressive power saving by the ring, at least my model. It may take minutes for it to show up in the scan. After it does, communication is reliable enough.

tahnok commented 1 month ago

Interesting, I haven't seen that behaviour before. Can you try plugging the ring in so it's charging and maybe it disables some of the power saving?

The other thing is the range is quite bad on the ring, so you might need to have it be closer the antenna in your computer

crosser commented 1 month ago

You are right, most of instabilities are explained either by long silence during discovery, or bad signal. I was surprised by the latter, because my desktop's bluetooth is rather sensitive, compared to the usual usb dongles. But yes, bringing the hand close to the antenna helps to keep connection working.

As to long discovery, yes, it responds readily when on the charger. But I don't want that, so I ended up with not-time-limited passive scan, which usually succeeds in a few seconds, but sometimes I have to wait minutes until it sees the device.

Stability issues out of the way, I have few observations that I think might be of interest for you:

I did not try to implement realtime measurement, and have no idea how to interpret "step data" (not too interested in it either :rofl: )

Would be good to know if you find more interesting things (and of course happy to share if I learn anything new).

You can find my code here if you are curious.

tahnok commented 1 month ago

Cool, a parallel implementation!

I have noticed the extra packet from set-time, I try to keep a note of any weird things I noticed in https://github.com/tahnok/colmi_r02_client/blob/main/MYSTERIES.md and want to track them down at some point

Is the timestamp thing you're talking about in your library or mine? I know mine is a bit messed up with timestamps right now, gonna make an issue to fix that now actually

crosser commented 1 month ago

There are more extra packets in the pcap trace of the official android app. I think in most cases, the last packet has the opcode matching the request.

There are also cases when the central sends some packet, and no response comes (and apparently none expected).

Another observation: when the response has opcode matching the request, but with high bit raised ( | 0x80 ), it seems to be the error response, with the error code (probably) in the next byte (I see 0xee in such packets).

About timestamp, I first copied your approach (but may have made a mistake, don't know), then started to compare logs with the official app, and realized that they are for the previous day. So, to my current understanding, in order to get the log for today, 2024-10-28, I need to send timestamp 2024-10-29T00:00:00.

        print("Time ref", ref)
        return pack(
            "<L", 86400 + round(datetime(*ref.timetuple()[:3]).timestamp())
        )

i.e. start of the reference day plus number of seconds in a day.

crosser commented 1 month ago

Having collected packet capture file on Android, ran it through this script, I have this log. This is just launching the app and waiting while it finishes "synchronizing data". In case you want to analyze the log, or to run the script yourself :)

snoop.txt