flash force Wiki
Interactive browser →
Wiki / Protocols
#protocol#ble#powered-up

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

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):

ActionBytes
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) A08 00 81 00 11 51 00 00
Stop (brake) A08 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:

ByteHub
0x20Duplo Train
0x40BOOST Move Hub (88006)
0x41Powered Up City 2-port (88009)
0x42Powered Up Remote (88010)
0x80Technic / 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

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:

bytehub
0x20DUPLO train base, 1st gen — no pairing
0x21DUPLO train base, 2nd gen — requires BLE pairing, different port map
0x40BOOST Move Hub (88006)
0x41City Hub (88009)
0x42Powered Up Remote (88010)
0x43LEGO Mario
0x44Luigi
0x45Peach
0x80Technic Hub (88012)
0x84Technic 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 0x000x3C, 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