This document describes the LEGO UART Message Protocol (LUMP) — the wire-level protocol used to communicate between a LEGO hub (or a compatible controller like Megahub) and LEGO smart devices: sensors, motors, and other peripherals from the WeDo 2.0, BOOST, SPIKE Prime, and Technic product families.
The documentation is structured from big picture to bit-level detail, so you can read as deep as you need.
- What is LUMP?
- Physical Layer — UART
- The Handshake Sequence
- Message Architecture
- The Header Byte — Anatomy
- Payload Size Encoding
- Checksum
- Message Types in Detail
- Data Formats
- Mode System
- Extended Mode — Modes 8–15
- Keep-Alive Mechanism
- Known Device IDs
- Full Handshake Example
- Implementation: LumpParser Internals
LUMP is the protocol that LEGO smart devices use to talk to the hub that powers them. When you plug a LEGO color sensor, distance sensor, or motor into a Powered Up port, the device immediately begins a self-describing handshake: it tells the hub its type, its capabilities, and the format of all the data it will send. After that, the device streams measurements continuously while the hub sends commands.
From an electronics perspective, LUMP runs over a half-duplex single-wire UART — the same data line is used for both directions, but normally only one side transmits at a time.
| Product family | Examples |
|---|---|
| LEGO WeDo 2.0 | Tilt Sensor, Motion Sensor |
| LEGO BOOST | Color & Distance Sensor, Interactive Motor |
| LEGO SPIKE Prime | Color, Ultrasonic, Force sensors; Small/Medium/Large motors |
| LEGO Technic | XL Motor, Medium Motor, Color Light Matrix, Angular Motors |
Note: LEGO MINDSTORMS EV3 sensors (Color, Ultrasonic, Gyro, Infrared) carry LUMP device type IDs (29–33) and can enumerate on LUMP-capable hubs when connected via an adapter. The EV3 hub itself, however, uses the LPF1 protocol — a separate, incompatible communication system.
| Parameter | Value |
|---|---|
| Default (handshake) baud rate | 2400 baud |
| Operating baud rate | Negotiated — typically 9600–115200 baud |
| Data bits | 8 |
| Parity | None |
| Stop bits | 1 |
| Connector | Single data wire (LPF2 connector pin 5/6) |
The device always starts at 2400 baud. During the handshake it announces its preferred operating speed via a CMD_SPEED message. Once the hub acknowledges the handshake, both sides switch to the higher speed simultaneously.
Before any sensor data flows, device and hub go through a structured initialization dialogue. Here is the complete flow:
Device Hub (Megahub)
| |
|--- CMD_TYPE ------------------>| "I am device type 37"
|--- CMD_MODES ----------------->| "I have 10 modes"
|--- CMD_SPEED ----------------->| "Please switch to 115200 baud"
|--- CMD_VERSION --------------->| "FW 1.0.0.0, HW 1.0.0.0"
| |
| (for each mode 0..N-1:) |
|--- INFO NAME (mode X) -------->| "Mode 0 is called COLOR"
|--- INFO RAW (mode X) -------->| "Raw range: 0.0 – 10.0"
|--- INFO PCT (mode X) -------->| "Pct range: 0.0 – 100.0"
|--- INFO SI (mode X) -------->| "SI range: 0.0 – 10.0"
|--- INFO UNITS(mode X) -------->| "Unit symbol: IDX"
|--- INFO MAP (mode X) -------->| "Supports ABS input"
|--- INFO FORMAT(mode X)-------->| "1 dataset × DATA8, 3 sig figs"
| (repeat for all modes) |
| |
|--- SYS ACK ------------------->| "Handshake complete"
| |
|<-- SYS ACK --------------------| Hub acknowledges
| |
[Both sides switch to high baud] |
| |
|<-- CMD SELECT mode 0 ----------| Hub selects default mode
| |
|--- DATA frame (mode 0)-------->| Sensor data streaming begins
|--- DATA frame (mode 0)-------->|
| ... |
|<-- SYS NACK (every ~50 ms) ----| Hub keep-alive
|--- DATA frame (mode 0)-------->|
The hub must acknowledge completion (SYS ACK) and then send keep-alive NACK bytes every ~50 ms or the device will stop sending data and wait to be re-initialized.
All LUMP messages share the same outer envelope:
┌──────────┬──────────────────────────┬───────────┐
│ Header │ Payload (0–32 bytes) │ Checksum │
│ 1 byte │ variable length │ 1 byte │
└──────────┴──────────────────────────┴───────────┘
Exception — System bytes: System messages are single byte only. No payload, no checksum.
Exception — INFO messages: INFO messages insert an extra INFO type byte between the header and the data payload:
┌──────────┬───────────┬──────────────────────┬───────────┐
│ Header │ INFO type │ Data payload │ Checksum │
│ 1 byte │ 1 byte │ variable length │ 1 byte │
└──────────┴───────────┴──────────────────────┴───────────┘
Every non-system message starts with a header byte. The 8 bits are split into three fields:
Bit: 7 6 5 4 3 2 1 0
├───┤ ├───────────┤ ├───┤
MSG TYPE SIZE ENC CMD/MODE
(2 bits) (3 bits) (3 bits)
| Bits | Field | Meaning |
|---|---|---|
| 7–6 | MSG TYPE | Message category (SYS / CMD / INFO / DATA) |
| 5–3 | SIZE ENC | Encoded payload size (see §6) |
| 2–0 | CMD / MODE | Command ID (for CMD/INFO) or mode index (for DATA) |
| Binary | Hex | Type |
|---|---|---|
00 |
0x00 |
SYS — single-byte system message |
01 |
0x40 |
CMD — command message |
10 |
0x80 |
INFO — mode information message |
11 |
0xC0 |
DATA — sensor/motor data |
| Header byte | Type | Size enc | CMD/Mode |
|---|---|---|---|
0x40 |
CMD | 0 (1 byte) | 0 (CMD_TYPE) |
0x49 |
CMD | 1 (2 bytes) | 1 (CMD_MODES) |
0x52 |
CMD | 2 (4 bytes) | 2 (CMD_SPEED) |
0x5F |
CMD | 3 (8 bytes) | 7 (CMD_VERSION) |
0x98 |
INFO | 3 (8 bytes) | 0 (mode 0) |
0xC0 |
DATA | 0 (1 byte) | 0 (mode 0) |
0xC8 |
DATA | 1 (2 bytes) | 0 (mode 0) |
Bits 5–3 of the header encode the payload length as a power of two:
| Encoding (bits 5–3) | Payload bytes |
|---|---|
000 (0) |
1 |
001 (1) |
2 |
010 (2) |
4 |
011 (3) |
8 |
100 (4) |
16 |
101 (5) |
32 |
110 (6) |
Reserved / invalid |
111 (7) |
Reserved / invalid |
So the total frame size (excluding system bytes) is always:
frame_size = 1 (header) + payload_bytes + 1 (checksum)
[+ 1 (INFO type byte) for INFO messages]
The checksum byte is the last byte of every multi-byte frame. It is computed as:
checksum = 0xFF XOR header XOR payload[0] XOR payload[1] XOR ... XOR payload[N-1]
For INFO messages the INFO type byte is included in the XOR sequence before the data payload bytes.
The receiver computes the same XOR over all bytes it has received (excluding the checksum itself) and compares the result to the received checksum byte. A mismatch means the frame is corrupted.
Starting the accumulator at 0xFF means that a correctly formed single-byte payload where payload[0] == 0x00 still produces a non-zero checksum. It is also the same convention used by the LEGO firmware itself.
System messages are single bytes — no payload, no checksum. The message type bits (7–6) are 00 and the message itself is fully encoded in the lower 6 bits.
| Byte value | Name | Direction | Meaning |
|---|---|---|---|
0x00 |
SYNC | Device → Hub | Keep-alive heartbeat during handshake |
0x02 |
NACK | Hub → Device | Keep-alive: "I'm alive, keep sending data" |
0x04 |
ACK | Both | Handshake acknowledgment |
SYNC: Sent by the device during the handshake phase to signal it is alive. The hub silently ignores SYNC bytes.
NACK: Sent by the hub every ~50 ms while the device is streaming data. Despite its name, this is not an error signal — it is simply the hub's keep-alive pulse. If the device stops receiving NACK bytes within ~100 ms it will halt data transmission.
ACK: Sent by the device at the end of its information dump to signal "I have sent all my mode descriptions." The hub responds with its own ACK. After that exchange, both sides switch baud rate and data streaming begins.
CMD messages (type bits = 01, header 0x4x) are used during initialization. The lower 3 bits of the header identify the command.
The device announces its type ID.
Header: 0x40 (CMD, size=1 byte, cmd=0)
Payload[0]: device type ID (uint8)
Example: 0x40 0x25 0x9A — device type 0x25 = 37 (BOOST Color & Distance Sensor)
The device announces how many operating modes it supports.
Header: 0x49 / 0x51 / 0x59 (CMD, size=2/4/8 bytes, cmd=1)
Payload[0]: number of modes - 1 (e.g. 0x09 = 10 modes)
Payload[1]: (if present) number of "view" modes - 1
Payload[2]: (if 4-byte payload) actual mode count - 1
Payload[3]: (if 4-byte payload) actual view count - 1
The number of modes available is always payload[0] + 1. The 4-byte variant appears on newer devices.
The device announces the baud rate it wants to use after the handshake.
Header: 0x52 (CMD, size=4 bytes, cmd=2)
Payload[0..3]: baud rate as little-endian uint32
e.g. 0x00 0xC2 0x01 0x00 = 115200 baud
The hub sends this to select the active mode after the handshake.
Header: 0x43 (CMD, size=1 byte, cmd=3)
Payload[0]: mode index to activate (0-15)
The hub sends data to an output-capable device (e.g., setting motor speed via protocol).
Header: 0x46 / 0x4E / 0x56 ... (CMD, size varies, cmd=4)
Payload: device-specific write data
Selects the "bank" for subsequent mode operations. Modes 0–7 use bank 0, modes 8–15 use bank 8.
Header: 0x46 (CMD, size=1 byte, cmd=6)
Payload[0]: 0x00 (modes 0-7) or 0x08 (modes 8-15)
When a device sends CMD_EXT_MODE with 0x08 before an INFO or DATA frame, the mode index in the next frame's header should be interpreted as mode + 8.
The device announces its firmware and hardware versions, encoded in BCD (Binary-Coded Decimal).
Header: 0x5F (CMD, size=8 bytes, cmd=7)
Payload[0..3]: firmware version (little-endian BCD, nibble-swapped per byte)
Payload[4..7]: hardware version (little-endian BCD, nibble-swapped per byte)
Version decoding example for FW version 0x00 0x00 0x00 0x10:
- Read bytes 3→2→1→0 (big-endian order):
0x10, 0x00, 0x00, 0x00 - Each byte is nibble-swapped before digit extraction:
0x10→ swap →0x01→ digits"01" - Final version string:
"01.00.00.00"
INFO messages (type bits = 10, header 0x8x–0xBx) describe the capabilities of a single mode. They are sent once per mode during the handshake.
The lower 3 bits of the header carry the mode index (0–7). Modes 8–15 are indicated by the MODE_PLUS_8 flag (bit 5 = 0x20) in the INFO type byte.
Frame structure:
[Header] [INFO type byte] [data payload...] [Checksum]
The INFO type byte has the following format:
Bit: 7 6 5 4 3 2 1 0
└─┬─┘ │ └───────┬───────┘
│ │ └─ INFO type (5 bits: 0x00-0x1F)
│ └─ MODE_PLUS_8 flag (0x20)
unused
If bit 5 (0x20) is set in the INFO byte, the effective mode index = header mode bits + 8.
The human-readable name for the mode.
INFO byte: 0x00 (or 0x20 for modes 8-15)
Data: null-terminated ASCII string, max 11 characters
Example: mode 0 of color sensor is named "COLOR", mode 3 might be "REF"
The name must start with an uppercase letter to be accepted.
The raw sensor value range.
INFO byte: 0x01
Data[0..3]: float min (little-endian IEEE 754)
Data[4..7]: float max (little-endian IEEE 754)
The percentage-scaled range corresponding to the raw range.
INFO byte: 0x02
Data[0..3]: float min (typically 0.0)
Data[4..7]: float max (typically 100.0)
The SI-unit range (e.g., meters, degrees, Newtons).
INFO byte: 0x03
Data[0..3]: float min (little-endian IEEE 754)
Data[4..7]: float max (little-endian IEEE 754)
The unit symbol string for this mode.
INFO byte: 0x04
Data: null-terminated ASCII string, max 4 characters
Examples: "IDX", "PCT", "LUX", "mm", "DEG", "N"
Flags describing input and output capabilities of the mode.
INFO byte: 0x05
Data[0]: input capability flags
Data[1]: output capability flags
Flag bit meanings (both input and output bytes):
| Bit | Mask | Meaning |
|---|---|---|
| 7 | 0x80 |
Supports NULL value |
| 6 | 0x40 |
Supports functional mapping 2.0 |
| 4 | 0x10 |
ABS — absolute (discrete numbered) values |
| 3 | 0x08 |
REL — relative values |
| 2 | 0x04 |
DIS — discrete/enum values |
A bitmask describing which modes can be active simultaneously (combi-mode).
INFO byte: 0x06
Data: variable-length bitmask (1 bit per mode combination)
This is informational; combi-mode support in Megahub is a future extension.
The most important INFO message — defines how the DATA frames for this mode will be structured.
INFO byte: 0x80
Data[0]: number of datasets per frame
Data[1]: data type ID (0x00=DATA8, 0x01=DATA16, 0x02=DATA32, 0x03=DATAFLOAT)
Data[2]: number of significant figures for display
Data[3]: number of decimal places for display
Example: a color sensor returning one 8-bit integer value with 3 significant figures and 0 decimal places:
datasets=1, type=DATA8 (0x00), figures=3, decimals=0
DATA messages (type bits = 11, header 0xCx–0xFx) carry the actual sensor readings during normal operation. They are sent by the device continuously.
Header: 0xC0 | (size_enc << 3) | mode_index
Payload: sensor data (layout defined by the mode's FORMAT)
The mode index in the header (bits 2–0) identifies which mode is reporting. If the device previously sent a CMD_EXT_MODE with value 0x08, add 8 to this index.
Example: BOOST Color & Distance Sensor in mode 8 (SPEC_1):
- First sends
CMD_EXT_MODEwith0x08 - Then sends
DATAframe with mode bits =0(header0xC8) - Effective mode = 0 + 8 = 8
The payload bytes are decoded using the format established during handshake (see §10).
The INFO_FORMAT message defines how to interpret the raw bytes in each DATA frame.
| Format ID | Name | Size | Type | Range |
|---|---|---|---|---|
0x00 |
DATA8 | 1 byte | int8_t | −128 to +127 |
0x01 |
DATA16 | 2 bytes | int16_t LE | −32768 to +32767 |
0x02 |
DATA32 | 4 bytes | int32_t LE | −2,147,483,648 to +2,147,483,647 |
0x03 |
DATAFLOAT | 4 bytes | float LE (IEEE 754) | ±3.4 × 10³⁸ |
All multi-byte integers and floats use little-endian byte order.
A DATA frame may contain multiple datasets — for example, a sensor that reports X, Y, Z simultaneously would have datasets=3. In that case the payload contains datasets × sizeof(type) bytes, with each value packed consecutively:
payload = [dataset_0_byte(s)] [dataset_1_byte(s)] [dataset_2_byte(s)] ...
A LEGO device can support up to 16 modes (indices 0–15). Each mode represents a different way to query the device:
- A color sensor might have one mode for raw RGB values, another for detected color index, another for ambient light intensity.
- A motor might have a position mode (degrees), a speed mode (RPM), and a power mode (percent).
Mode lifecycle:
- The device announces all modes during the handshake via INFO messages.
- The hub selects exactly one active mode using
CMD_SELECT. - The device then streams DATA frames for that mode only.
- The hub can switch modes at any time by sending another
CMD_SELECT.
Mode object (in Megahub implementation):
Each mode stores:
- Name (string, ≤ 11 chars)
- Raw min/max (float pair)
- Percent min/max (float pair)
- SI min/max (float pair)
- Unit string (string, ≤ 4 chars)
- Input/output capability flags
- Format (datasets, type, figures, decimals)
- Dataset array (allocated on receipt of INFO_FORMAT)
Devices with more than 8 modes use an extended mode mechanism. The lower 3 bits of a header can only represent modes 0–7. To address modes 8–15, the device precedes the relevant INFO or DATA frame with a CMD_EXT_MODE command:
Sequence for mode 9 INFO_NAME:
[CMD_EXT_MODE, payload=0x08] <- "next frame is in bank 8"
[INFO header, mode bits=1] <- mode 1 + 8 = mode 9
[INFO_NAME data]
[checksum]
The extModeOffset is consumed by exactly one following frame, then automatically reset to 0.
LEGO devices have a built-in watchdog: if they stop receiving NACK bytes from the hub within approximately 100 ms, they stop sending data and wait to be re-initialized.
Megahub's keep-alive strategy:
- The hub sends an immediate NACK when entering data mode to start the watchdog clock.
- Every 50 ms, the hub checks if it is at least 4 ms past the last received byte (to avoid collisions with incoming DATA frames).
- If the wire is idle (inter-frame gap), the NACK is sent.
- As a safety valve, NACK is sent unconditionally after 80 ms regardless of wire activity.
Timeline (not to scale):
← DATA frame → ← gap → NACK ← DATA frame → ← gap → NACK
|______________| 4ms ↑ |______________| ↑
hub sends NACK hub sends NACK
From the device's perspective, NACK means "I acknowledge your last transmission, send more." This is why NACK, despite its literal meaning, is the normal positive keep-alive signal in this protocol.
Device type IDs as reported in the CMD_TYPE message payload:
| ID | Device |
|---|---|
| 29 | MINDSTORMS EV3 Color Sensor |
| 30 | MINDSTORMS EV3 Ultrasonic Sensor |
| 32 | MINDSTORMS EV3 Gyro Sensor |
| 33 | MINDSTORMS EV3 Infrared Sensor |
| 34 | WeDo 2.0 Tilt Sensor |
| 35 | WeDo 2.0 Motion Sensor |
| 37 | BOOST Color and Distance Sensor |
| 38 | BOOST Interactive Motor |
| 45 | Technic XL Motor |
| 46 | Technic Medium Motor |
| 48 | SPIKE Medium Motor |
| 49 | SPIKE Large Motor |
| 61 | SPIKE Color Sensor |
| 62 | SPIKE Ultrasonic Sensor |
| 63 | SPIKE Prime Force Sensor |
| 64 | Technic Color Light Matrix |
| 65 | SPIKE Small Motor |
| 75 | Technic Medium Angular Motor |
| 76 | Technic Large Angular Motor |
Below is a complete annotated byte stream for a WeDo 2.0 Motion Sensor (device ID 35) connecting to Megahub. All bytes are hex.
CMD_TYPE:
40 23 9C
│ │ └─ checksum: 0xFF ^ 0x40 ^ 0x23 = 0x9C
│ └─── payload: device ID 0x23 = 35 (WeDo 2.0 Motion Sensor)
└────── header: 0x40 = CMD (0x40) | size=1 (0x00) | cmd=TYPE (0x0)
CMD_MODES:
49 01 B7
│ │ └─ checksum: 0xFF ^ 0x49 ^ 0x01 = 0xB7
│ └─── payload: 0x01 = modes-1, so device has 2 modes
└────── header: 0x49 = CMD (0x40) | size=2 (0x08) | cmd=MODES (0x1)
(size=2 but only payload[0] is relevant for 2-byte variant)
CMD_SPEED:
52 00 C2 01 00 8A
│ └──────────── payload: 0x0001C200 = 115200 baud (little-endian)
└────────────── header: 0x52 = CMD (0x40) | size=4 (0x10) | cmd=SPEED (0x2)
CMD_VERSION:
5F 00 00 10 00 00 00 10 00 A6
│ └──────────────────────── payload: FW 0x00001000, HW 0x00001000
└────────────────────────── header: 0x5F = CMD (0x40) | size=8 (0x18) | cmd=VERSION (0x7)
--- Mode 0 INFO ---
INFO NAME for mode 0:
98 00 44 49 53 54 00 00 00 00 00 00 00 2D
│ │ └────────────────────────────── "DIST\0" padded to 11 bytes
│ └─ INFO type byte 0x00 = NAME, bit5=0 so mode is 0+0=0
└─── header: 0x98 = INFO (0x80) | size=8 (0x18) | mode=0
INFO RAW for mode 0:
99 01 00 00 00 00 00 00 C8 43 ...
│ │ └─────────────── float min = 0.0, float max = 400.0 (0x43C80000)
│ └─ INFO type byte 0x01 = RAW
└─── header: 0x99 = INFO (0x80) | size=8 (0x18) | mode=1 (but INFO type carries mode)
Actually header lower 3 bits = mode index in header
INFO FORMAT for mode 0 (simplified):
92 80 01 00 03 04 00 7E
│ │ └────────────── datasets=1, type=DATA8(0x00), figures=3, decimals=0
│ └─ INFO type byte 0x80 = FORMAT
└─── header
--- End of mode descriptions ---
SYS ACK:
04
└── single byte: ACK
SYS ACK:
04 (hub acknowledges handshake completion)
[Both sides now switch to 115200 baud]
CMD_SELECT mode 0:
43 00 BC
│ │ └─ checksum: 0xFF ^ 0x43 ^ 0x00 = 0xBC
│ └─── payload: mode index 0
└────── header: 0x43 = CMD | size=1 | cmd=SELECT
DATA frame, mode 0, 1 byte:
C0 05 3A
│ │ └─ checksum: 0xFF ^ 0xC0 ^ 0x05 = 0x3A
│ └─── payload: 0x05 = distance reading 5
└────── header: 0xC0 = DATA (0xC0) | size=1 (0x00) | mode=0
DATA frame, mode 0, 1 byte:
C0 07 38
(distance reading 7)
SYS NACK:
02 (sent every ~50 ms)
The Megahub implementation lives in lib/lpfuart/. This section explains how the parser works at the code level.
┌─────────────────────────────────────────────────────────────┐
│ LegoDevice │
│ loop() ──► parseIncomingData() ──► parser_.feedByte() │
│ loop() ──► needsKeepAlive() ──► sendNack() │
│ loop() ──► finishHandshake() ──► sendAck() + baud switch │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ LumpParser │ │
│ │ feedByte() / feedBytes() │ │
│ │ └──► processBuffer() ← sliding window loop │ │
│ │ ├── SYS byte? → dispatchSystemByte() │ │
│ │ ├── validate checksum │ │
│ │ └── valid frame → dispatchFrame() │ │
│ │ ├── CMD → parse CMD fields │ │
│ │ ├── INFO → parse INFO fields │ │
│ │ └── DATA → device_->onDataFrame() │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Mode[0..15] ◄── populated by INFO messages │
│ └── Dataset[0..N] ◄── populated by DATA messages │
└─────────────────────────────────────────────────────────────┘
The parser maintains a 128-byte circular ring buffer. Bytes from the UART are pushed in via feedByte() / feedBytes(). The inner loop processBuffer() runs a sliding-window algorithm:
- Peek at the head byte (do not consume yet).
- Determine the message type from bits 7–6.
- If SYS (
0x00): consume 1 byte immediately, dispatch. - Otherwise: decode the payload size from bits 5–3.
- Check if the buffer contains a full frame (
1 + payloadSize + 1bytes, plus 1 extra for INFO). - If not enough bytes yet:
breakand wait for more UART data. - Compute the expected checksum over all frame bytes except the last.
- If checksum matches: extract the payload, advance the head past the entire frame, dispatch.
- If checksum mismatches: advance the head by 1 byte only (re-synchronization) and retry from step 1.
This byte-at-a-time sliding on mismatch allows the parser to self-synchronize after line noise or a missed byte without needing a dedicated framing byte or start-of-frame marker.
If more than 300 consecutive checksum errors occur, the parser calls device_->reset(). This resets the LEGO device connection back to 2400 baud and restarts the handshake. The parser logs:
- Number of bytes discarded during the sync-loss episode
- A hex dump of the first 64 discarded bytes (for debugging electrical noise)
The LumpParserStats structure is available via parser_.stats() and is logged every 5 seconds in data mode:
| Field | Meaning |
|---|---|
framesOk |
Total successfully parsed and dispatched frames |
checksumErrors |
Total frames rejected by checksum |
bytesDiscarded |
Total bytes consumed during re-sync attempts |
syncRecoveries |
Number of times sync was lost and re-established |
bufferOverflows |
Times the ring buffer was full (oldest byte dropped) |
unknownSysBytes |
Unrecognised system byte values |
invalidSizeBytes |
Header bytes with reserved size encoding (6 or 7) |
| File | Purpose |
|---|---|
| lib/lpfuart/src/lumpparser.cpp | Protocol parser — frame detection, checksum, dispatch |
| lib/lpfuart/include/lumpparser.h | Parser class and stats struct |
| lib/lpfuart/src/legodevice.cpp | Device state machine, handshake, keep-alive, mode selection |
| lib/lpfuart/include/legodevice.h | Device class, device ID constants |
| lib/lpfuart/src/mode.cpp | Mode metadata storage, data packet processing |
| lib/lpfuart/include/mode.h | Mode class |
| lib/lpfuart/src/dataset.cpp | Individual value parsing (DATA8/16/32/FLOAT) |
| lib/lpfuart/include/dataset.h | Dataset class |
| lib/lpfuart/src/format.cpp | Data format type mapping |
| lib/lpfuart/include/format.h | Format class and FormatType enum |
| lib/lpfuart/include/serialio.h | Abstract UART I/O interface |
This document was generated from the Megahub firmware source code. For the authoritative protocol specification, refer to the LEGO Powered Up documentation and community reverse-engineering resources such as pybricks/micropython-http-client.