Showing 3 changed files with 179 additions and 0 deletions
+176 -0
Documentation/Research Resources/Payload Notes/UM24C-UM25C-UM34C from floriandotorg-um24c.md
@@ -0,0 +1,176 @@
1
+# UM24C / UM25C / UM34C Notes from `floriandotorg/um24c`
2
+
3
+Source project:
4
+
5
+- https://github.com/floriandotorg/um24c
6
+- file reviewed: `src/App.js`
7
+
8
+## Scope
9
+
10
+This note captures protocol knowledge inferred from the external `floriandotorg/um24c` project.
11
+
12
+It applies to:
13
+
14
+- `UM24C`
15
+- `UM25C`
16
+- `UM34C`
17
+
18
+It does not appear to cover:
19
+
20
+- `TC-66C`
21
+
22
+## BLE Transport Hints
23
+
24
+The external project uses:
25
+
26
+- service `0xFFE0`
27
+- characteristic `0xFFE1`
28
+- notifications on `0xFFE1`
29
+- request command `0xF0` to trigger a new measurement snapshot
30
+
31
+The connection flow matches the HM-10 style radio path already present in our project.
32
+
33
+## Model Identifiers
34
+
35
+The external project maps:
36
+
37
+- `0x0963 -> UM24C`
38
+- `0x09c9 -> UM25C`
39
+- `0x0d4c -> UM34C`
40
+
41
+This is useful as a concrete reference for the 16-bit model field found at payload offset `0`.
42
+
43
+## Snapshot Framing
44
+
45
+The external project accumulates notification fragments until a total payload size of `130` bytes is available.
46
+
47
+It then parses that `130`-byte buffer as one UM snapshot and schedules the next `0xF0` request after about `500 ms`.
48
+
49
+Important note:
50
+
51
+- this confirms a `130`-byte UM payload
52
+- it does not prove that the final notification is always `10` bytes long as a protocol guarantee
53
+- that detail may only reflect the behavior observed by that project
54
+
55
+## Payload Layout Extracted From The External Project
56
+
57
+All offsets below are byte offsets into the `130`-byte UM payload.
58
+
59
+| Offset | Size | External interpretation | Notes |
60
+|---|---:|---|---|
61
+| `0` | 2 | `modelId` | Used with the model map above |
62
+| `2` | 2 | `voltage` | `UM25C`: raw / 1000 V, `UM24C` and `UM34C`: raw / 100 V |
63
+| `4` | 2 | `amperage` | External project displays raw / 1000 A |
64
+| `6` | 4 | `wattage` | Displayed as raw / 1000 W |
65
+| `10` | 2 | `temperature` | External project uses only one temperature field here |
66
+| `14` | 2 | `group` | Selected data group |
67
+| `16 + group * 8` | 4 | `capacityAmperage` | Group-specific accumulated charge |
68
+| `20 + group * 8` | 4 | `capacityWattage` | Group-specific accumulated energy |
69
+| `96` | 2 | `negativeDataLineVoltage` | External project treats offset `96` as D- |
70
+| `98` | 2 | `positiveDataLineVoltage` | External project treats offset `98` as D+ |
71
+| `100` | 2 | `chargingMode` | Uses the charging mode map below |
72
+| `112` | 4 | `duration` | External project interprets as seconds and multiplies by `1000` for ms formatting |
73
+| `122` | 4 | `impedance` | Displayed as raw / 10 ohms |
74
+
75
+## Charging Mode Map
76
+
77
+The external project maps the charging mode field at offset `100` as:
78
+
79
+- `1 -> QC2`
80
+- `2 -> QC3`
81
+- `3 -> APP2.4A`
82
+- `4 -> APP2.1A`
83
+- `5 -> APP1.0A`
84
+- `6 -> APP0.5A`
85
+- `7 -> DCP1.5A`
86
+- `8 -> SAMSUNG`
87
+
88
+## Comparison With Our Current Implementation
89
+
90
+The external project confirms several parts of our existing UM parser:
91
+
92
+- request command `0xF0`
93
+- expected response length `130`
94
+- model number at offset `0`
95
+- selected data group at offset `14`
96
+- duration at offset `112`
97
+- load resistance / impedance data at offset `122`
98
+
99
+It also exposes a few differences that should be treated as verification targets:
100
+
101
+### 1. Current scaling for `UM25C`
102
+
103
+Our code currently uses:
104
+
105
+- `UM25C`: raw / `10000`
106
+- `UM34C`: raw / `1000`
107
+
108
+The external project uses:
109
+
110
+- raw / `1000` for the shared `amperage` field
111
+
112
+This is a high-value point to verify against real captures or device behavior.
113
+
114
+### 2. Group capacity scaling
115
+
116
+Our code currently divides group values by `1000` for:
117
+
118
+- accumulated charge
119
+- accumulated energy
120
+
121
+The external project appears to present these values directly, without that division.
122
+
123
+This may indicate:
124
+
125
+- a difference in unit assumptions
126
+- UI-only formatting choices
127
+- or a real decoding discrepancy
128
+
129
+### 3. D+ / D- offset naming
130
+
131
+Our code currently interprets:
132
+
133
+- offset `96` as `usbPlusVoltage`
134
+- offset `98` as `usbMinusVoltage`
135
+
136
+The external project interprets:
137
+
138
+- offset `96` as negative data line voltage
139
+- offset `98` as positive data line voltage
140
+
141
+This should be verified carefully.
142
+
143
+### 4. Extra fields present only in our code
144
+
145
+Our UM parser also handles fields that the external project does not expose in its UI, including:
146
+
147
+- Fahrenheit temperature at offset `12`
148
+- recorded AH / WH
149
+- recording threshold
150
+- recording state
151
+- screen timeout
152
+- screen brightness
153
+- current screen
154
+
155
+These may still be correct, but the external project does not help validate them.
156
+
157
+## Suggested Verification Priorities
158
+
159
+When new captures or device access become available, verify in this order:
160
+
161
+1. `UM25C` current scaling
162
+2. group AH / WH scaling
163
+3. D+ / D- offset naming
164
+4. charging mode values
165
+5. meaning of the extra UM fields parsed only by our code
166
+
167
+## Practical Value
168
+
169
+This external project is useful mainly because it provides:
170
+
171
+- confirmed model identifiers
172
+- a second implementation of the UM snapshot parser
173
+- concrete charging mode names
174
+- a reference point for disputed field scaling
175
+
176
+It should be treated as a strong comparison source, but not as final protocol truth.
+1 -0
Documentation/Research Resources/README.md
@@ -11,6 +11,7 @@ Use it to collect:
11 11
 - payload descriptions
12 12
 - decoded and undecoded field mappings
13 13
 - references to related external projects
14
+- extracted notes derived from external implementations
14 15
 
15 16
 Suggested rule:
16 17
 
+2 -0
USB Meter.xcodeproj/project.pbxproj
@@ -59,6 +59,7 @@
59 59
 		1C6B6B8F2A2D4F5100A0B001 /* Manuals README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
60 60
 		1C6B6B912A2D4F5100A0B001 /* Specifications README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
61 61
 		1C6B6B932A2D4F5100A0B001 /* Payload Notes README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
62
+		1C6B6B962A2D4F5100A0B001 /* UM24C-UM25C-UM34C from floriandotorg-um24c.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "UM24C-UM25C-UM34C from floriandotorg-um24c.md"; sourceTree = "<group>"; };
62 63
 		4308CF8524176CAB0002E80B /* DataGroupRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataGroupRowView.swift; sourceTree = "<group>"; };
63 64
 		4308CF872417770D0002E80B /* DataGroupsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataGroupsView.swift; sourceTree = "<group>"; };
64 65
 		430CB4FB245E07EB006525C2 /* ChevronView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChevronView.swift; sourceTree = "<group>"; };
@@ -159,6 +160,7 @@
159 160
 			isa = PBXGroup;
160 161
 			children = (
161 162
 				1C6B6B932A2D4F5100A0B001 /* Payload Notes README.md */,
163
+				1C6B6B962A2D4F5100A0B001 /* UM24C-UM25C-UM34C from floriandotorg-um24c.md */,
162 164
 			);
163 165
 			path = "Payload Notes";
164 166
 			sourceTree = "<group>";