Skip to content

ES-CS20M: broadcast-only variant (non-connectable) findings #1308

@KristianP26

Description

@KristianP26

Cross-project findings: ES-CS20M hardware variants

I maintain ble-scale-sync (Node.js/TypeScript alternative for BLE scales) and we've been debugging the same ES-CS20M problems in our issue #34. Sharing our findings here since they're relevant to openScale users.

The ES-CS20M ships in at least 3 hardware variants

Variant Advertised name Protocol Services GATT connectable
1 "QN-Scale" QN/Qingniu 0xFFE0 / 0xFFF0 Yes
2 "ES-CS20M" or unnamed Yunmai 0x1A10 Yes
3 "Renpho Scale" or unnamed QN/Qingniu (broadcast) none No (ADV_NONCONN_IND)

Variant 1 works with the QN handler. Variant 2 was fixed by PR #1300. Variant 3 is the problematic one.

Variant 3: broadcast-only (non-connectable)

Some ES-CS20M units advertise as non-connectable (ADV_NONCONN_IND). No BLE stack on any OS can establish a GATT connection to these devices. This explains the "unable to connect" reports from users where the scale is visible but connection always fails, on both Windows and Android.

These devices use QN/Qingniu manufacturer data advertisements with the AABB marker:

FF FF AA BB [MAC 6 bytes] [data bytes...] [stability byte near end]

Impedance is not available in broadcast mode (confirmed independently by two users).

Weight byte position is still unconfirmed. Our initial assumption (bytes [10-11] as big-endian uint16 / 100) produced incorrect readings: 135 kg for a user who actually weighs ~70 kg. We are currently collecting controlled data samples (known weights + raw hex) from a user with this variant to determine the correct byte positions and divisor. Will update once the format is confirmed.

Two raw samples captured so far:

Idle (stable):      FF FF AA BB ED 67 39 DC 60 60 08 D6 00 00 FF FF FF 02 00 18 1A 4C 09 03 FC 01
With weight (meas): FF FF AA BB ED 67 39 DC 60 60 13 00 00 00 FF FF FF 02 00 55 05 4C 09 03 27 00

Bytes that change between samples: [10-11], [19-20], [24], [25]. The last byte appears to be a stability flag (0x01 = stable, 0x00 = measuring).

How to identify the variant

The easiest way is to check the advertisement type in nRF Connect:

  • Connectable + service UUID 0xFFE0/0xFFF0 = Variant 1 (QN handler)
  • Connectable + service UUID 0x1A10 = Variant 2 (ES-CS20M handler)
  • Non-connectable + manufacturer data starting with AABB = Variant 3 (needs broadcast parser)

Implementation in ble-scale-sync

Added broadcast mode support that reads weight from advertisement data without requiring a GATT connection. Body composition is estimated via the Deurenberg BMI formula since impedance is unavailable. The broadcast parser is functional but the weight byte layout needs correction (see above). This also includes a BLE diagnostic tool that detects broadcast-only devices and shows parsed weight from advertisements.

Possibly related: #1302 (unhandled opcode 0xa1)

Issue #1302 shows a connectable QN variant ("Renpho-Scale", manufacturer: "Qing Niu Technology") that completes the full handshake (0x12 → 0x13 → 0x14 → 0x20 → 0x21 → 0xA0) but the 0xA1 response is unhandled. The 0xA1 frame is likely the ACK to the user profile command (0xA0). After this, no weight data arrives. This might be a separate issue with the QN handler not completing the handshake for certain firmware versions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    scale supportIndicates that this issue is a request for support for a specific scale.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions