# UM24C / UM25C / UM34C Notes from `floriandotorg/um24c`

Source project:

- https://github.com/floriandotorg/um24c
- file reviewed: `src/App.js`

## Scope

This note captures protocol knowledge inferred from the external `floriandotorg/um24c` project.

It applies to:

- `UM24C`
- `UM25C`
- `UM34C`

It does not appear to cover:

- `TC-66C`

## BLE Transport Hints

The external project uses:

- service `0xFFE0`
- characteristic `0xFFE1`
- notifications on `0xFFE1`
- request command `0xF0` to trigger a new measurement snapshot

The connection flow matches the HM-10 style radio path already present in our project.

The imported `HM-10` and `DX-BT18` module references strengthen this:

- generic `HM-10` documentation explicitly uses `FFE0` / `FFE1`
- the `DX-BT18` manual documents default UUIDs `FFE0`, `FFE1`, and `FFE2`
- the `DX-BT18` manual also explains how a UM-family device could support both desktop serial-style workflows and iOS BLE workflows

## Model Identifiers

The external project maps:

- `0x0963 -> UM24C`
- `0x09c9 -> UM25C`
- `0x0d4c -> UM34C`

This is useful as a concrete reference for the 16-bit model field found at payload offset `0`.

## Snapshot Framing

The external project accumulates notification fragments until a total payload size of `130` bytes is available.

It then parses that `130`-byte buffer as one UM snapshot and schedules the next `0xF0` request after about `500 ms`.

Important note:

- this confirms a `130`-byte UM payload
- it does not prove that the final notification is always `10` bytes long as a protocol guarantee
- that detail may only reflect the behavior observed by that project

## Payload Layout Extracted From The External Project

All offsets below are byte offsets into the `130`-byte UM payload.

| Offset | Size | External interpretation | Notes |
|---|---:|---|---|
| `0` | 2 | `modelId` | Used with the model map above |
| `2` | 2 | `voltage` | `UM25C`: raw / 1000 V, `UM24C` and `UM34C`: raw / 100 V |
| `4` | 2 | `amperage` | External project displays raw / 1000 A |
| `6` | 4 | `wattage` | Displayed as raw / 1000 W |
| `10` | 2 | `temperature` | External project uses only one temperature field here |
| `14` | 2 | `group` | Selected data group |
| `16 + group * 8` | 4 | `capacityAmperage` | Group-specific accumulated charge |
| `20 + group * 8` | 4 | `capacityWattage` | Group-specific accumulated energy |
| `96` | 2 | `negativeDataLineVoltage` | External project treats offset `96` as D- |
| `98` | 2 | `positiveDataLineVoltage` | External project treats offset `98` as D+ |
| `100` | 2 | `chargingMode` | Uses the charging mode map below |
| `112` | 4 | `duration` | External project interprets as seconds and multiplies by `1000` for ms formatting |
| `122` | 4 | `impedance` | Displayed as raw / 10 ohms |

## Charging Mode Map

The external project maps the charging mode field at offset `100` as:

- `1 -> QC2`
- `2 -> QC3`
- `3 -> APP2.4A`
- `4 -> APP2.1A`
- `5 -> APP1.0A`
- `6 -> APP0.5A`
- `7 -> DCP1.5A`
- `8 -> SAMSUNG`

## Comparison With Our Current Implementation

The external project confirms several parts of our existing UM parser:

- request command `0xF0`
- expected response length `130`
- model number at offset `0`
- selected data group at offset `14`
- duration at offset `112`
- load resistance / impedance data at offset `122`

It also exposes a few differences that should be treated as verification targets:

### 1. Current scaling for `UM25C`

Our code currently uses:

- `UM25C`: raw / `10000`
- `UM34C`: raw / `1000`

The external project uses:

- raw / `1000` for the shared `amperage` field

This is a high-value point to verify against real captures or device behavior.

### 2. Group capacity scaling

Our code currently divides group values by `1000` for:

- accumulated charge
- accumulated energy

The external project appears to present these values directly, without that division.

This may indicate:

- a difference in unit assumptions
- UI-only formatting choices
- or a real decoding discrepancy

### 3. D+ / D- offset naming

Our code currently interprets:

- offset `96` as `usbPlusVoltage`
- offset `98` as `usbMinusVoltage`

The external project interprets:

- offset `96` as negative data line voltage
- offset `98` as positive data line voltage

This should be verified carefully.

### 4. Extra fields present only in our code

Our UM parser also handles fields that the external project does not expose in its UI, including:

- Fahrenheit temperature at offset `12`
- recorded AH / WH
- recording threshold
- recording state
- screen timeout
- screen brightness
- current screen

These may still be correct, but the external project does not help validate them.

## Suggested Verification Priorities

When new captures or device access become available, verify in this order:

1. `UM25C` current scaling
2. group AH / WH scaling
3. D+ / D- offset naming
4. charging mode values
5. meaning of the extra UM fields parsed only by our code

## Practical Value

This external project is useful mainly because it provides:

- confirmed model identifiers
- a second implementation of the UM snapshot parser
- concrete charging mode names
- a reference point for disputed field scaling

It should be treated as a strong comparison source, but not as final protocol truth.
