Skip to content

Picooc: add a driver for the connection-oriented scale line - #1482

Open
LOGAET wants to merge 1 commit into
oliexdev:masterfrom
LOGAET:picooc-driver
Open

Picooc: add a driver for the connection-oriented scale line#1482
LOGAET wants to merge 1 commit into
oliexdev:masterfrom
LOGAET:picooc-driver

Conversation

@LOGAET

@LOGAET LOGAET commented Aug 22, 2026

Copy link
Copy Markdown

Adds a handler for the Picooc scales that speak the 0xFFF0 GATT protocol (PICOOC-C1..CQ, PICOOC-14..25, PICOOC-IS, the S3 Lite V2.0 which advertises as PICOOC-CQ, and the Latin series, which advertises with no vendor prefix).

The scale publishes weight, whole-body impedance and heart rate. It does not report body composition of its own, so fat/water/muscle/bone/LBM/BMR are derived from the impedance via StandardImpedanceLib, as the other impedance-only scales in this package do. The 0x32 composition frame is parsed and preferred when present.

Protocol notes, verified against a PICOOC-CQ capture:

  • The scale opens with 0x3A (time request plus a BOM block) and expects a 10-byte reply; the length field counts the 0xF1 prefix.
  • It never sends the documented 0x30 profile request, so the sex/height/age frame is pushed unprompted after the handshake. Without it the scale has nothing to run its BIA engine on.
  • There is no live weight stream on this model: a single 0x39 arrives with the completion flag already set.
  • The pound field in 0x39 is 15 bits wide. 0x862C/10 reads 3434.8 lb, while (0x862C & 0x7FFF)/10 is 158.0 lb, which is exactly the 71.7 kg reported in the same frame. The top bit is a separate flag of unknown meaning.
  • 0x3C must be acknowledged. Unacknowledged, the scale resends a byte-identical "3C 05 00 00 01" ten times at 1 Hz and then drops the link, never reaching the reading its own display was showing. Acknowledged, it sends the in-progress frame once and then "3C 05 00 44 02" - 0x44 = 68 bpm, matching the display. Layout is [bpm BE 2B][status], status 1 measuring, 2 final.
  • Publication waits for the scale to hang up, which it does on its own once finished; a re-armed quiet period is the fallback. Heart rate lands 10 s after the weight, so publishing on the weight frame loses it.

Opcodes 0x35, 0x36, 0x37, 0x3B, 0x3E and 0x3F are acknowledged and dumped to the log but not decoded: no capture has produced them yet. The metabolic body age in 0x32 and the phase angle in 0x39 are decoded and logged but not stored, as ScaleMeasurement has no field for either.

PICOOC-L (Mini) is deliberately left unclaimed. It is broadcast-only and never accepts a GATT connection, so it needs a separate BROADCAST_ONLY handler.

The handler is registered ahead of ScaleupHandler, which claims any device whose manufacturer data carries a 0xD0/0xE0 key regardless of name.

https://garden.1900.live/22-knowledge/%E5%BC%80%E5%8F%91/picooc%E4%BD%93%E8%84%82%E7%A7%A4ble%E5%8D%8F%E8%AE%AE%E5%88%86%E6%9E%90

Adds a handler for the Picooc scales that speak the 0xFFF0 GATT protocol
(PICOOC-C1..CQ, PICOOC-14..25, PICOOC-IS, the S3 Lite V2.0 which advertises
as PICOOC-CQ, and the Latin series, which advertises with no vendor prefix).

The scale publishes weight, whole-body impedance and heart rate. It does not
report body composition of its own, so fat/water/muscle/bone/LBM/BMR are
derived from the impedance via StandardImpedanceLib, as the other
impedance-only scales in this package do. The 0x32 composition frame is
parsed and preferred when present.

Protocol notes, verified against a PICOOC-CQ capture:

- The scale opens with 0x3A (time request plus a BOM block) and expects a
  10-byte reply; the length field counts the 0xF1 prefix.
- It never sends the documented 0x30 profile request, so the sex/height/age
  frame is pushed unprompted after the handshake. Without it the scale has
  nothing to run its BIA engine on.
- There is no live weight stream on this model: a single 0x39 arrives with
  the completion flag already set.
- The pound field in 0x39 is 15 bits wide. 0x862C/10 reads 3434.8 lb, while
  (0x862C & 0x7FFF)/10 is 158.0 lb, which is exactly the 71.7 kg reported in
  the same frame. The top bit is a separate flag of unknown meaning.
- 0x3C must be acknowledged. Unacknowledged, the scale resends a byte-identical
  "3C 05 00 00 01" ten times at 1 Hz and then drops the link, never reaching
  the reading its own display was showing. Acknowledged, it sends the
  in-progress frame once and then "3C 05 00 44 02" - 0x44 = 68 bpm, matching
  the display. Layout is [bpm BE 2B][status], status 1 measuring, 2 final.
- Publication waits for the scale to hang up, which it does on its own once
  finished; a re-armed quiet period is the fallback. Heart rate lands 10 s
  after the weight, so publishing on the weight frame loses it.

Opcodes 0x35, 0x36, 0x37, 0x3B, 0x3E and 0x3F are acknowledged and dumped to
the log but not decoded: no capture has produced them yet. The metabolic body
age in 0x32 and the phase angle in 0x39 are decoded and logged but not stored,
as ScaleMeasurement has no field for either.

PICOOC-L (Mini) is deliberately left unclaimed. It is broadcast-only and never
accepts a GATT connection, so it needs a separate BROADCAST_ONLY handler.

The handler is registered ahead of ScaleupHandler, which claims any device
whose manufacturer data carries a 0xD0/0xE0 key regardless of name.

https://garden.1900.live/22-knowledge/%E5%BC%80%E5%8F%91/picooc%E4%BD%93%E8%84%82%E7%A7%A4ble%E5%8D%8F%E8%AE%AE%E5%88%86%E6%9E%90
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.

1 participant