Skip to content

fix(bluetooth): decode the short live frame sent by PICOOC-CC - #1565

Open
dferrandizmont wants to merge 2 commits into
oliexdev:masterfrom
dferrandizmont:fix/picooc-cc-short-live-frame
Open

dferrandizmont wants to merge 2 commits into
oliexdev:masterfrom
dferrandizmont:fix/picooc-cc-short-live-frame

Conversation

@dferrandizmont

Copy link
Copy Markdown

My PICOOC-CC connects fine on 3.1.3 and goes through the whole handshake, but nothing ever gets saved. The log shows why:

Picooc RX 0x39 len=10 | 39 0A 6A D6 52 D7 06 6E 11 8A
Picooc live frame 0x39 too short to decode (len=10)

This model sends a shorter live frame: opcode, length, the timestamp from our time reply, weight ×20 and impedance ×10, and that's it (no pounds/unit/phase/flag). In that capture it's 82.3 kg and 449.0 Ω. It only comes once, after the profile exchange, and the scale hangs up a few seconds later, so it's already the settled reading.

Full session from the log:

RX 35 02                          TX F1 0A 35 6A D6 52 D7 00 00 00
RX 37 02                          TX F1 03 37
RX 30 05 00 06 6E                 TX F1 03 30, 31 06 01 01 8A 22
RX 39 0A 6A D6 52 D7 06 6E 11 8A  TX F1 03 39
(disconnect ~6 s later)

No 0x32 or 0x3C ever arrives, so composition comes from StandardImpedanceLib.

The fix makes parseLiveFrame recognise the short layout by its length byte (0x0A) and treat it as complete. A truncated 16-byte frame is still rejected since its length byte is 0x10. I added a parser test with the captured frame and a replay of the CC session that expects a single record.

Tested with a debug build on my scale (Pixel 10 Pro, Android 17): the measurement is stored with weight, impedance and the estimated composition. PicoocHandlerTest passes.

The PICOOC-CC sends a 10-byte 0x39 frame (timestamp, weight, impedance)
instead of the 16-byte one, so parseLiveFrame rejected it and no
measurement was ever stored. Recognise it by its length byte and treat it
as a settled reading.
@shcglv

shcglv commented Oct 22, 2026

Copy link
Copy Markdown

Same failure on a PICOOC-S3 — the short frame is not CC-specific.

openScale 3.1.3-debug, the scale advertises as PICOOC-S3. The handshake and the profile exchange complete normally, and then the only weight frame is rejected:

I/PicoocHandler: Picooc RX 0x35 len=2 | 35 02
I/PicoocHandler: Picooc RX 0x37 len=2 | 37 02
I/PicoocHandler: Picooc RX 0x30 len=5 | 30 05 00 06 A6
I/PicoocHandler: Picooc RX 0x39 len=10 | 39 0A 6A D9 EF 8A 06 A6 00 00
W/PicoocHandler: Picooc live frame 0x39 too short to decode (len=10)

What the frame contains:

  • 0x06A6 / 20 = 85.1 kg, which matched the scale's own display.
  • Bytes 2..5 are the timestamp from our own time reply: we sent F1 0A 35 6A D9 EF 84 00 00 00 and the scale echoed 6A D9 EF 8A six seconds later.
  • The trailing 00 00 is a zero impedance, because that weighing was done with socks on.

Barefoot, the same 10-byte layout carries a real reading: 39 0A 6A DA 30 28 06 AA 12 DE — 85.3 kg and 0x12DE / 10 = 483.0 Ω. The S3 never sends a 0x32 frame, so body composition is derived from that impedance via StandardImpedanceLib, which works fine once the frame is accepted.

So the layout matches your CC capture byte for byte, and the fix you describe covers the S3 as well.

What needs to be done (I tested this locally on the scale; no patch attached, since it is your PR):

  1. Accept the 10-byte 0x39 when its own length byte is 0x0A: [op][0x0A][ts BE 4][weight/20 BE 2][impedance/10 BE 2] — no pounds, unit, phase angle or completion flag.
  2. Treat it as already settled (complete), because it only arrives once the reading is final and the scale hangs up right after.
  3. Keep rejecting anything shorter than 16 bytes whose length byte says 0x10, so a truncated long frame is still dropped rather than misread.
  4. Optionally add device("PICOOC-S3") to the catalog fixture, so the model appears in the generated table with its own row.

With those four things the S3 stores the weight and, in the barefoot capture, the derived composition.

…atalog

The PICOOC-S3 uses the same 10-byte live frame as the CC. Add its captured
frames (barefoot and with socks, where impedance is 0 and only the weight is
stored) and give both models their own row in the scale catalog.
@dferrandizmont

Copy link
Copy Markdown
Author

Thanks a lot for trying it on the S3 and sharing the captures, good to know it's the same frame and not just a CC quirk.

Points 1–3 were already in the PR exactly like that. I've pushed c2fb071 for the rest:

  • PICOOC-CC and PICOOC-S3 added to the catalog fixtures, so both get their own row.
  • Your two S3 frames are now tests: the barefoot one (85.3 kg, 483 Ω) and the socks one, where the impedance is 0 and only the weight gets stored, with no composition.

PicoocHandlerTest (40) and ScaleCatalogTest pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants