diff --git a/CLAUDE.md b/CLAUDE.md index 5ab92f4..bdcf66d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -386,7 +386,7 @@ bytes `\x01\x2c` = 300 (5-minute default histogram interval); changes when inter | Offset | Field | Format | Notes | |---|---|---|---| -| anchor − 7 (write) / anchor − 8 (read) | recording_mode | uint8 | E5 read has extra `0x10` at anchor−7 | +| anchor − 8 | recording_mode | uint8 | **Same offset for both read and write.** The byte at anchor−7 is a `0x10` DLE marker regenerated by device firmware in every E5 response — do NOT overwrite it. Writing to anchor−7 causes anchor drift (+1 per write cycle). CORRECTION 2026-04-21: previous doc stated anchor−7 for write; empirically wrong. | | anchor − 6 | sample_rate | uint16 BE | same in read & write | | anchor − 4 | histogram_interval_sec | uint16 BE | seconds; same in read & write ✅ 2026-04-20 | | anchor − 2 | `0x00 0x00` | padding | | diff --git a/minimateplus/client.py b/minimateplus/client.py index 7bb6e8f..42cf492 100644 --- a/minimateplus/client.py +++ b/minimateplus/client.py @@ -1770,10 +1770,13 @@ def _encode_compliance_config( DLE-jitter shifts): Anchor: b'\\xbe\\x80\\x00\\x00\\x00\\x00' (confirmed stable, both BE11529 and BE18189) - recording_mode → uint8 at anchor_pos - 7 (write payload) + recording_mode → uint8 at anchor_pos - 8 (BOTH read and write) Values: 0x00=Single Shot, 0x01=Continuous, 0x03=Histogram, 0x04=Histogram+Continuous - NOTE: In the E5 read response (decode) field is at anchor_pos - 8 due to an - extra 0x10 byte at read anchor_pos - 7. Write payload has no extra byte. + NOTE: The byte at anchor_pos - 7 is always 0x10 (a DLE marker regenerated by + device firmware in every E5 response). It must NOT be overwritten during + write — doing so causes anchor drift (+1 per write cycle). + CORRECTION 2026-04-21: previous doc stated anchor-7 for write; empirically + confirmed wrong — writing to anchor-7 shifts the anchor by 1 on every cycle. sample_rate → uint16 BE at anchor_pos - 6 histogram_interval_sec → uint16 BE at anchor_pos - 4 (seconds; mode-gated to Histogram/Histogram+Continuous) Valid values: 2, 5, 15, 60, 300, 900 (= 2s, 5s, 15s, 1m, 5m, 15m) @@ -1844,9 +1847,10 @@ def _encode_compliance_config( else: log.info( "_encode_compliance_config: anchor at cfg[%d] buf_len=%d " - "(expected ~15; fields: recording_mode@%d sample_rate@%d:%d " + "(recording_mode@%d DLE_marker@%d sample_rate@%d:%d " "histogram_interval@%d:%d record_time@%d:%d)", _anc, len(buf), + _anc - 8, _anc - 7, _anc - 6, _anc - 4, _anc - 4, _anc - 2, @@ -1854,12 +1858,18 @@ def _encode_compliance_config( ) if recording_mode is not None: - if _anc < 7: + if _anc < 8: log.warning("_encode_compliance_config: anchor not found — cannot write recording_mode") else: - buf[_anc - 7] = recording_mode & 0xFF + # Write to anchor-8, same physical position as the E5 read format. + # The byte at anchor-7 is a DLE marker (0x10) that the device firmware + # regenerates in every E5 response — it must NOT be overwritten. + # Writing to anchor-7 causes the device to add an extra byte on the + # next read-back, drifting the anchor by +1 on every write cycle. + # (CLAUDE.md "anchor-7 write" was incorrect — confirmed 2026-04-21) + buf[_anc - 8] = recording_mode & 0xFF log.debug("_encode_compliance_config: recording_mode=0x%02X -> offset %d", - recording_mode, _anc - 7) + recording_mode, _anc - 8) if sample_rate is not None: if _anc < 6: