This is an Arduino library that implements the LEGO UART Message Protocol (LUMP) for building custom devices.
Hi Makers! I'm HsiangYi from the OFDL Robotics Lab. We've open-sourced our internal library for building custom devices for LEGO hosts. For the open-source release, we've significantly refactored it and turned it into an Arduino library that's easy for both novices and professionals to use.
Have fun building your own devices, and don't forget to share your creations with the community!
- API Reference page
- Advanced Topics page
- Publish to Arduino Library Manager
- Publish to PlatformIO Library Registry
- Troubleshooting page
- FAQ page
- Features
- Quickstart
- Compatible Lego Hosts and Firmwares
- Compatible Dev Boards
- Limitations
- Acknowledgements
- Disclaimers
- License
- LUMP Communication
- Communicates with LEGO hosts using the LEGO UART Message Protocol (LUMP).
- Supports up to 16 modes for SPIKE Hub and 8 modes for EV3.
- Configurable constant power on SPIKE Hub Pin 2, enabling external peripheralsโsuch as servo motors or camera modulesโto be powered at battery voltage.
- See Advanced Topics - Enable Constant Power on SPIKE Hub Pin 2.
- Automatically detects host type for high-speed handshake, allowing SPIKE Hub to rapidly complete the handshake process.
- Easy to Use
- Designed as an Arduino library, making it easy for both novices and professionals to use.
- Non-Blocking Architecture
- Enabling programs to remain responsive while handling messages.
- Easy Watchdog Timer Integration
- Just register the callback functions for watchdog timer initialization, feeding and deinitialization. The library handles the rest.
- See Advanced Topics - Watchdog Timer.
- Compatible with Multiple Dev Boards
- Provides Basic Debugging Information
- Including device state tracking, decoded host messages, etc.
- See Advanced Topics - Debug Mode.
To use the quickstart guide, you need:
- A LEGO host
- A Dev board with UART interface.
- An IDE
- Arduino IDE 1.5.x+
- PlatformIO
- Debugging tools
- USB to TTL Serial Adapter (optional)
- Logic Analyzer (optional, but highly recommended)
This guide uses a SPIKE Hub with Pybricks and an ESP32-C3 SuperMini as an example.
Tip
Using SPIKE Hub with Pybricks is suggested for most users, as this combination is the simplest.
Other combinations require additional steps. For example, using SPIKE Hub with SPIKE 3 requires emulating an existing LEGO sensor, while using the EV3 with EV3 Lab requires developing custom blocks.
Tip
Choosing a dev board with at least two UART interfacesโone for programming and one for device communicationโmakes development easier.
- In the menu bar, select
Tools
>Manage Librariesโฆ
. - Search for
LumpDeviceBuilder
. - Click install.
- Go to
PlatformIO Home
>Libraries
>Registry
. - Search for
LumpDeviceBuilder
. - Click
Add to Project
.
- Download the latest version of the library from the GitHub Releases page and unzip it.
- Move the library folder to the proper location.
Warning
Do not use multiple power sources simultaneously unless you are certain that the dev board has power management circuits.
-
Development Wiring Diagram
โโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ Burner โ โ Dev Board โ โ Host Port โ โโโโโโโโโโโโค โโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโค โ โ โ โ โ 1. M1 โ LPF2 Socket Pinout โ โ โ โ โ 2. M2 โ โโโโโโโโโโโโโโโโโโโ โ GND โโโโโโโค GND โโโโโโโค 3. GND โ โ 6 5 4 3 2 1 โ โ VCC โโโโโโโค VIN โ โ 4. VCC โ โ โโโดโโดโโดโโดโโดโโดโโ โ โ โ โ RX โโโโโโโค 5. TX โ โโโ โโโ โ โ โ TX โโโโโโโค 6. RX โ โ ... โโโโโโโค ... โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโ
-
Production Wiring Diagram
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ Dev Board โ โ Host Port โ โโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโค โ โ โ 1. M1 โ LPF2 Socket Pinout โ โ โ 2. M2 โ โโโโโโโโโโโโโโโโโโโ โ GND โโโโโโโค 3. GND โ โ 6 5 4 3 2 1 โ โ VIN โโโโโโโค 4. VCC โ โ โโโดโโดโโดโโดโโดโโดโโ โ โ RX โโโโโโโค 5. TX โ โโโ โโโ โ TX โโโโโโโค 6. RX โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
-
Development Wiring Diagram
โโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ Burner โ โ Dev Board โ โ Host Port โ RJ12 Socket โโโโโโโโโโโโค โโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโค Pinout โโโโ โ โ โ โ โโโโค 1. M1 โ โโโ โโโ โ โ โ โ โ โ 2. M2 โ โโโโโโโโ โ โ GND โโโโโโโค GND โโโโดโโโค 3. GND โ โ โ โ VCC โโโโโโโค VIN โ โ 4. VCC โ โ โ โ โ โ RX โโโโโโโค 5. TX โ โ 6 5 4 3 2 1 โ โ โ โ TX โโโโโโโค 6. RX โ โโโดโโดโโดโโดโโดโโดโโ โ ... โโโโโโโค ... โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโ
-
Production Wiring Diagram
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ Dev Board โ โ Host Port โ RJ12 Socket โโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโค Pinout โโโโ โ โ โโโโค 1. M1 โ โโโ โโโ โ โ โ โ 2. M2 โ โโโโโโโโ โ โ GND โโโโดโโโค 3. GND โ โ โ โ VIN โโโโโโโค 4. VCC โ โ โ โ RX โโโโโโโค 5. TX โ โ 6 5 4 3 2 1 โ โ TX โโโโโโโค 6. RX โ โโโดโโดโโดโโดโโดโโดโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
#include <LumpDeviceBuilder.h>
For this device, two modes are implemented:
- mode 0: Analog Reader
- mode 1: Digital Reader
LumpMode modes[]{
{"Analog", DATA16, 1, 4, 0, "raw", {0, 4095}, {0, 100}, {0, 4095}},
{"Digital", DATA8, 1, 1, 0, "raw", {0, 1}, {0, 100}, {0, 1}}
};
uint8_t numModes = sizeof(modes) / sizeof(LumpMode);
Parameter descriptions:
LumpMode(const char *name, uint8_t dataType, uint8_t numData,
uint8_t figures, uint8_t decimals, const char *symbol,
LumpValueSpan raw, LumpValueSpan pct, LumpValueSpan si,
...)
-
name
: Mode name.-
Naming rules:
- Must not be an empty string
""
ornullptr
. - Must start with a letter (
AโZ
,aโz
).
If invalid, the name will be set to the string
"null"
. - Must not be an empty string
-
Length limit (excluding null terminator):
- If
power
parameter isfalse
:11
(default) - If
power
parameter istrue
:5
Names exceeding the limit will be truncated.
- If
-
-
dataType
: Data type.- Possible values:
DATA8
,DATA16
,DATA32
,DATAF
.
- Possible values:
-
numData
: Number of data.- The maximum depends on data type due to 32-byte payload limit:
DATA8
(1 byte) :[1..32]
DATA16
(2 bytes):[1..16]
DATA32
(4 bytes):[1..8]
DATAF
(4 bytes):[1..8]
- The maximum depends on data type due to 32-byte payload limit:
-
figures
: Number of characters shown in view and data log (including the decimal point).- Valid range:
[0..15]
- Valid range:
-
decimals
: Number of decimals shown in view and data log.- Valid range:
[0..15]
- Valid range:
-
symbol
: Symbol of the measurement unit (default:""
).- Rules:
- Set to empty string
""
ornullptr
if not required. - Length limit (excluding null terminator):
4
- Set to empty string
- Rules:
-
raw
: Raw value span (default:false
).- When set to
false
, this value span will not be provided to the host. The host will use the default range[0, 1023]
.
- When set to
-
pct
: Percentage value span (default:false
).- When set to
false
, this value span will not be provided to the host. The host will use the default range[0, 100]
.
- When set to
-
si
: Scaled value span (default:false
).- When set to
false
, this value span will not be provided to the host. The host will use the default range[0, 1023]
.
- When set to
Note
For a comprehensive list and detailed descriptions, please refer to the LumpMode
API Reference.
Select a serial interface for device communication and initialize the device with the defined modes.
// Select a serial interface for device communication.
#define DEVICE_SERIAL Serial0
#define RX_PIN 20
#define TX_PIN 21
LumpDevice<HardwareSerial> device(&DEVICE_SERIAL, RX_PIN, TX_PIN, 68, 115200, modes, numModes);
Parameter descriptions:
template <typename T>
LumpDevice(T *uart, uint8_t rxPin, uint8_t txPin,
uint8_t type, uint32_t speed,
LumpMode *modes, uint8_t numModes,
...);
-
T
: Type of the serial interface (typicallyhardwareSerial
). -
uart
: Serial interface used for UART communication (e.g.,Serial0
,Serial1
). -
rxPin
: RX pin number of the serial interface. -
txPin
: TX pin number of the serial interface. -
type
: Device type. -
speed
: Communication speed. -
modes
: Device modes (must be an array ofLumpMode
). -
numModes
: Number of modes.-
The maximum depends on the host type:
- SPIKE Hub:
[1..16]
- EV3:
[1..8]
Modes beyond the limit will be ignored.
- SPIKE Hub:
-
Note
For a comprehensive list and detailed descriptions, please refer to the LumpDevice
API Reference.
The library uses a state machine to manage communication and reserves 2 states for developers to implement mode-specific actions.
Developers must implement the following states:
-
LumpDeviceState::InitMode
- Initializes the mode.
- e.g., resetting values, configuring hardware.
- Initializes the mode.
-
LumpDeviceState::Communicating
- Acquires data.
- e.g., analog or digital reading.
- Sends data to the host.
- It is suggested to keep the data sending rate below 1000 Hz to prevent UART overrun errors.
- For best practice, refer to Event-Driven Data Transmission in Advanced Topics.
- Handles NACK from the host.
- Upon receiving a NACK, send data to the host immediately.
- Acquires data.
When the mode is changed, the device state transitions to LumpDeviceState::InitMode
, and then to LumpDeviceState::Communicating
after one iteration. These transitions are handled automatically by the library.
Warning
The entire program must be non-blocking when using this library. Avoid using blocking functions such as delay()
. Use non-blocking techniques instead.
Let's design a function to run the device modes:
// Pin definitions.
#define ANALOG_PIN 3
#define DIGITAL_PIN 4
void runDeviceModes() {
// Get the device state and mode.
auto state = device.state();
auto mode = device.mode();
// Check the device state and perform actions accordingly.
switch (state) {
case LumpDeviceState::InitMode:
// Initialize the mode.
switch (mode) {
case 0:
pinMode(ANALOG_PIN, INPUT);
break;
case 1:
pinMode(DIGITAL_PIN, INPUT);
break;
default:
break;
}
break;
case LumpDeviceState::Communicating: {
static uint16_t value0 = 0;
static uint8_t value1 = 0;
static uint32_t period = 5; // 200Hz
static uint32_t prevMillis = 0;
uint32_t currentMillis = millis();
// Send data to the host.
if (device.hasNack() || (currentMillis - prevMillis > period)) {
switch (mode) {
case 0:
value0 = analogRead(ANALOG_PIN);
device.send(value0);
break;
case 1:
value1 = digitalRead(DIGITAL_PIN);
device.send(value1);
break;
default:
break;
}
prevMillis = currentMillis;
}
break;
}
default:
break;
}
}
Note
The value passed to send()
must match the mode's dataType
defined in LumpMode
.
For example, use uint16_t
/ int16_t
for DATA16
, and uint8_t
/ int8_t
for DATA8
.
void setup() {
device.begin();
}
void loop() {
device.run();
runDeviceModes();
}
The full code is here.
Ensure that the sketch is successfully uploaded to the dev board.
Connect the dev board to SPIKE Hub Port A. Copy and paste the following code into the Pybricks IDE, then run it to verify device functionality:
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
from pybricks.tools import wait
device = PUPDevice(Port.A)
print(device.info())
while True:
print(f"analog (mode 0): {device.read(0)}")
print(f"digital (mode 1): {device.read(1)}")
wait(1000)
You should see the following output in the console:
{'id': 68, 'modes': (('Analog', 1, 1), ('Digital', 1, 0))}
analog (mode 0): (4095,)
digital (mode 1): (1,)
All steps are done!
You can start building your own devices.
See Advanced Topics for in-depth guides.
The firmware versions listed below were used for testing during the library's initial release.
Host | Firmware | Version |
---|---|---|
SPIKE Hub | Pybricks | 3.6.1 |
Hub OS | 1.6.62 | |
EV3 | EV3-G | 1.09E |
The Dev boards listed below are ones we have used before and can run stably.
However, the library is designed to be compatible with any board that has a UART interface. If you have a board that is not listed here, please feel free to try it out.
Dev Board | MCU | Add'l Config |
---|---|---|
ESP32-C3 SuperMini | ESP32 | |
ESP32-DevKitC | ||
NodeMCU-32s | ||
NodeMCU DEVKIT V1.0 | ESP8266 | |
Raspberry Pi Pico | RP2040 | Yes |
RP2040-Zero | ||
Seeed Studio XIAO-RP2040 | ||
STM32 Blue Pill | STM32F103C8T6 | |
Arduino UNO R3 | ATmega328/P | Yes |
Arduino Nano | ||
Arduino Pro Mini |
Important
Some Dev boards and MCUs require additional configuration.
See Additonal Configuration for MCUs for details.
- The entire program must be non-blocking when using this library.
- No support for sending the Combined Mode information during handshake.
- Not all MCUs support automatic host type detection. This feature must be manually disabled.
Thanks to the Pybricks team for publishing a detailed LUMP specification. For our open-source release, we adopted the more comprehensive LUMP header file defined by Pybricks.
LEGOยฎ is a trademark of the LEGO Group of companies which does not sponsor, authorize or endorse this project.
This library is licensed under the LGPL 3.0 or later license.