lego-protocol
LEGO Powered Up protocol context for the Megahub project. Use when working on LEGO device communication, lpfuart library, UART parsing, motor control, sensor modes, device initialization, or debugging
SKILL.md.claude/skills/lego-protocol/SKILL.md
GitHubLEGO Powered Up Protocol — Megahub ESP32
Hardware Setup
- UART chip: SC16IS752 (I²C-to-dual-UART bridge)
- Ports: 0–3 (physical LEGO connectors)
- Baud rate: negotiated per device during handshake (typically starts at low speed, switches to 115200)
- Library:
lib/lpfuart/—legodevice.cpp,lumpparser.cpp,SC16IS752serialadapter.cpp
Device Initialization Sequence
Devices send identification messages on connect. Never skip any step.
LUMP_CMD_TYPE— device reports its type ID and nameLUMP_CMD_MODES— device reports number of supported modesLUMP_CMD_SPEED— device requests baud rate to switch toLUMP_CMD_VERSION— device reports firmware + hardware versionsLUMP_INFO_NAME/RAW/PCT/SI/UNITS/MAPPING/FORMAT— per-mode metadata (repeats for each mode)LUMP_INFO_MODE_COMBOS— supported mode combinationsfinishHandshake()— switch to negotiated baud rate (e.g., 115200)selectMode()— set default operating mode
Example (BOOST Interactive Motor, device ID 0x26):
- 4 modes: POWER (0), SPEED (1), POS (2), TEST (3)
- Default mode: 2 (POS — position feedback)
- Handshake takes ~3 seconds from connect to ready
Protocol Rules
- Command rate limit: ~20ms minimum between commands for most devices
- Device timeout: some devices sleep if no command within ~1 second — send keepalives
- Byte order: LEGO uses little-endian in most messages
- Port cleanup: on disconnect, cancel all pending commands for that port
- Mode selection: always use device type to validate supported modes before
CMD_SELECT
UART Corruption from Motor Noise
This is a known hardware issue. When motors run, EMI corrupts the UART RX line.
Symptoms
[WARN] LUMP sync lost after N good frames (checksum expected=0xXX actual=0xYY)- Repeated
0xD2bytes in discarded data (spurious detections of LEGO header byte) uartOvr=1in parser stats — SC16IS752 FIFO overflow
Root Cause
Motor control via M1/M2 generates high-frequency switching noise → current spikes → EMI coupling into UART RX trace → SC16IS752 FIFO fills faster than ESP32 reads via I²C.
Software Mitigations (already implemented)
- UART read batch size increased to 64 bytes per call (reduces FIFO overrun probability)
delayMicroseconds(100)after motor speed changes (allows transients to settle)- FIFO overrun counter tracked and logged as
uartOvr
Hardware Fixes (if software mitigations are insufficient)
| Priority | Fix | Expected improvement |
|---|---|---|
| 1 (best) | 68–100pF NP0/C0G ceramic cap on UART RX pin to GND | 50–70% error reduction |
| 2 | Clip-on ferrite bead on RX wire near PCB | 40–60% error reduction |
| 3 | 100Ω + 100pF RC filter on UART TX | 10–20% additional |
| 4 | 100nF decoupling cap on SC16IS752 VCC pin | standard practice |
Target: < 5 sync recoveries per minute with motor at 100% speed.
Parser Recovery
The parser (lumpparser.cpp) handles corruption gracefully:
- Detects checksum mismatch
- Scans forward to find next valid frame header
- Logs discarded bytes
- Recovers without device reset
Good state: Parser: ok=XXX csErr=0 discarded=0 recoveries=0 uartOvr=0
Debugging LEGO
Watch parser stats in serial log (logged every ~5 seconds):
[INFO] logParserStats() - Parser: ok=138 csErr=0 discarded=0 recoveries=0 overflow=0 unknownSys=0 invalidSize=0 uartOvr=1
| Field | Meaning |
|---|---|
ok |
Good frames parsed |
csErr |
Checksum errors |
discarded |
Bytes thrown away during resync |
recoveries |
Number of sync-loss recovery events |
uartOvr |
SC16IS752 FIFO overflow count |