LWP (Powered Up BLE)
Bluetooth, not infrared: there is no carrier frequency to quote, and the link goes both ways. This is how every modern LEGO hub is driven. A hub accepts a connection from any Bluetooth central, which is why a web page can drive a stock hub with nothing reflashed.
GATT
- Service
00001623-1212-EFDE-1623-785FEABCD123 - Characteristic
00001624-1212-EFDE-1623-785FEABCD123— Write-Without-Response + Notify. All commands are written here; all hub messages (I/O attach, sensor values, feedback) arrive as notifications on the same characteristic. - (WeDo 2.0 predates LWP 3 and uses a different service
00001523-…— see WeDo.)
Message header
Every message: [Length] [Hub ID = 0x00] [Message Type] [payload…]. Length includes the header (1 byte if ≤127). Types: Hub Attached I/O 0x04 (hub to client), Port Output Command 0x81 (client to hub), Port Output Feedback 0x82, Port Value 0x45, Hub Properties 0x01.
Drive a motor — Port Output Command (0x81)
[Len] 00 81 [Port] [Startup/Completion] [SubCmd] [payload]. Simplest path is WriteDirectModeData (0x51) mode 0 (PWM):
| Action | Bytes |
|---|---|
| Motor port A, +50% | 08 00 81 00 11 51 00 32 |
| Motor port A, −50% | 08 00 81 00 11 51 00 CE |
| Stop (float/coast) A | 08 00 81 00 11 51 00 00 |
| Stop (brake) A | 08 00 81 00 11 51 00 7F |
| Motor port B, +50% | 08 00 81 01 11 51 00 32 |
Power is signed int8 −100..100 (0x00 = float, 0x7F = brake). 0x11 = execute-immediately + request-feedback (0x10 = no feedback). Closed-loop variants: StartSpeed 0x07, StartSpeedForDegrees 0x0B, GotoAbsolutePosition 0x0D (encoder motors only).
Identify hub & ports (no connection needed)
The advertising manufacturer data (company 0x0397) carries a system-type byte — read it while scanning to know the model:
| Byte | Hub |
|---|---|
0x20 | Duplo Train |
0x40 | BOOST Move Hub (88006) |
0x41 | Powered Up City 2-port (88009) |
0x42 | Powered Up Remote (88010) |
0x80 | Technic / Control+ (88012) |
After connecting, the hub sends Hub Attached I/O (0x04) notifications — one per port — with a device-type ID (train motor 0x02, colour+distance 88007 0x25, medium motor 88008 0x26, Technic L/XL 0x2E/0x2F, …), so a controller can auto-discover what is plugged where. Ports: A=0x00 B=0x01 C=0x02 D=0x03, hub RGB LED 0x32.
Notes
- Prefer write-without-response; pace ~60 ms and re-send changed values a few times.
- BOOST Move Hub is fussiest about malformed frames; City ignores commands to ports C/D; Technic floods ~8 attach messages at boot before "ready".
- The 88007 sensor can emit PF IR — a bridge from modern BLE hubs to old optical PF.
Hub kinds seen in the wild (August 2026)
Beyond the kinds in LEGO's own spec, community and Pybricks sources add five the document never listed:
| byte | hub |
|---|---|
0x20 | DUPLO train base, 1st gen — no pairing |
0x21 | DUPLO train base, 2nd gen — requires BLE pairing, different port map |
0x40 | BOOST Move Hub (88006) |
0x41 | City Hub (88009) |
0x42 | Powered Up Remote (88010) |
0x43 | LEGO Mario |
0x44 | Luigi |
0x45 | Peach |
0x80 | Technic Hub (88012) |
0x84 | Technic Move Hub (set 42176, Aug 2024) |
The official spec repo LEGO/lego-ble-wireless-protocol-docs publishes LEGO Wireless Protocol 3.0.00 r17 and has not been updated since 8 February 2019 (18 commits total, MIT, not archived). Mario/Luigi/Peach and the Technic Move Hub are community identifiers, cross-checked against pybricks lwp3.h.
Separately: LEGO does publish a first-party BLE spec — for a different hub
github.com/LEGO/spike-prime-docs (Apache-2.0 + LEGO trademark clause, "Copyright 2024 The LEGO Group", rendered at lego.github.io/spike-prime-docs) documents the SPIKE Prime hub: GATT service 0000FD02-0000-1000-8000-00805F9B34FB, RX/TX characteristics …-0001-… / …-0002-…, message types 0x00–0x3C, and COBS framing. This is not LWP3 — it is a separate protocol for a separate hub. Last commit 4 March 2024.
Sources for this block
- Official spec: lego.github.io/lego-ble-wireless-protocol-docs
- Reference implementations: BrickController2 · Legoino github.com/corneliusmunz/legoino · Bluepad32 (gamepad side).