Skip to content

Commit 4d9b01d

Browse files
eriedclaude
andcommitted
P6 motor temp: correct encoding, was reading MOS by mistake
Two new labelled captures (164349 / 164427) had MOS / IMU / Motor labelled side-by-side with the wheel UI. Motor went 63 -> 63 -> 62 -> 62 -> 61 °C while the byte at offset 31 went 240 -> 239 -> 238 -> 238 -> 237. Linear fit nails the encoding: motor_C = (body[31] − 145) / 1.5 Within 0.3 °C of every labelled value across 9 samples and both logs. Bonus finds from the same labels: MOS = body[28] (direct °C, exact match every time) IMU = 62 − body[78] (3 labels at 42/42/43 °C ↔ bytes 20/20/19) The earlier `°F = byte − 126` fit on body[32] was tracking the controller-MOS sensor, NOT the motor — that's why the rider's 26 °C reading didn't line up with the wheel UI's 33 °C. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f247ca5 commit 4d9b01d

1 file changed

Lines changed: 39 additions & 24 deletions

File tree

app/src/main/java/com/eried/eucplanet/ble/InMotionV2Parser.kt

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -243,30 +243,45 @@ object InMotionV2Parser {
243243
ByteUtils.getUint32LE(data, 58) / 100f
244244
} else 0f
245245

246-
// Temperatures: three sensor bytes at body offsets 30/31/32, decoded
247-
// as `°F = byte − 126`. Labelled-capture cross-check (rider noted
248-
// motor reading on the wheel UI vs the bytes flowing in):
249-
// wheel motor 29°C -> body[30]=27.8 body[31]=26.1 body[32]=31.7
250-
// wheel motor 34°C -> body[30]=28.9 body[31]=28.9 body[32]=33.3
251-
// wheel motor 33°C -> body[30]=28.9 body[31]=28.9 body[32]=32.8
252-
// body[32] tracks motor within ~0.2-0.7°C in the warm samples; the
253-
// other two are nearly identical and noticeably cooler — almost
254-
// certainly the two battery packs' MOS sensors. We surface body[32]
255-
// as motor and keep the others in `temperatures` for future UI.
256-
fun decodeTempC(off: Int): Float? {
257-
if (off >= data.size) return null
258-
val v = data[off].toInt() and 0xFF
259-
val f = v - 126
260-
// Filter implausible values (the byte is 0 before the wheel
261-
// has finished its boot handshake and would otherwise read as
262-
// -126 °F).
263-
if (f !in 32..248) return null
264-
return (f - 32) * 5f / 9f
265-
}
266-
val motorC = decodeTempC(32)
267-
val mosAC = decodeTempC(30)
268-
val mosBC = decodeTempC(31)
269-
val temps = listOfNotNull(motorC, mosAC, mosBC)
246+
// Temperatures: confirmed encodings from a labelled capture where the
247+
// rider read MOS / IMU / Motor off the wheel UI at five moments
248+
// while the wheel cooled from a 63 -> 61 °C motor reading.
249+
//
250+
// Motor = (body[31] − 145) / 1.5 °C
251+
// Within 0.3 °C of every labelled value across 9 samples in two
252+
// separate logs. Range 0..73 °C maps to bytes 145..255 (saturates
253+
// at 73 °C — fine for normal use; pwm-event spikes that briefly
254+
// exceed that just clip).
255+
//
256+
// MOS = body[28] (direct °C)
257+
// Exact match every time — three labels at 36 °C, byte = 36.
258+
//
259+
// IMU = 62 − body[78] °C (lower confidence; 3 data points,
260+
// inverted-scale fit is unusual but matches: 42/42/43 °C ↔ bytes
261+
// 20/20/19.)
262+
//
263+
// The earlier `°F = byte − 126` fit on body[32] was tracking the
264+
// controller-MOS sensor, NOT the motor — that's why the rider's
265+
// wheel UI motor reading (29-34 °C) only loosely lined up.
266+
fun byteOrNull(off: Int): Int? =
267+
if (off < data.size) data[off].toInt() and 0xFF else null
268+
269+
val motorByte = byteOrNull(31)
270+
val motorC: Float? = motorByte
271+
?.takeIf { it >= 145 } // below 145 means uninitialized / pre-boot
272+
?.let { (it - 145).toFloat() / 1.5f }
273+
274+
val mosByte = byteOrNull(28)
275+
val mosC: Float? = mosByte
276+
?.takeIf { it in 5..120 } // plausible ambient..hot range
277+
?.toFloat()
278+
279+
val imuByte = byteOrNull(78)
280+
val imuC: Float? = imuByte
281+
?.takeIf { it in 5..62 } // the formula's valid input window
282+
?.let { (62 - it).toFloat() }
283+
284+
val temps = listOfNotNull(motorC, mosC, imuC)
270285

271286
// Headlight state isn't reliably reported in the realtime stream on
272287
// user-facing firmware. preview4's "byte[84] bit 1" rule worked in

0 commit comments

Comments
 (0)