VLL — Visible Light Link
VLL (Visible Light Link) is LEGO's protocol for sending commands as plain on/off light pulses. A light sensor on the brick measures brightness; the sender flashes light with precise timing to encode bits. Shared by Micro Scout, Code Pilot and Scout, which all decode the same codes.
The communication protocol was covered by a LEGO patent application — period LEGO license text notes it as "protected by a pending patent application."
Frame
One command = 10 bits = 3-bit checksum + 7-bit data, wrapped in init/start/stop:
- Init / preamble — light ON ≈ 400 ms (some impls use 1000 ms, see Implementations below) before anything.
- Start — OFF 20 ms.
- Checksum — 3 bits, MSB first (see Checksum below).
- Data — 7 bits, MSB first.
- Stop — ON 20 ms, OFF 60 ms, ON 120 ms (some impls: just ON 20 / OFF 60).
The frame transmits [checksum:3 bits][data:7 bits], both MSB first.
Bit encoding (unit = 20 ms)
| Bit | Light ON | Light OFF |
|---|---|---|
| 0 | 40 ms (2 units) | 20 ms (1 unit) |
| 1 | 20 ms (1 unit) | 40 ms (2 units) |
Total per bit = 60 ms either way (only the ON/OFF ratio differs).
Checksum
A 3-bit checksum is prepended (MSB first) to each 7-bit data value:
function checksum(n) { // n = 7-bit data value
return 7 - ((n + (n >> 2) + (n >> 4)) & 7);
}
Verified against all 27 commands in rcxvll.nqh (pbrick) and used verbatim by Flash Force, lego-blockly and elecbrick (Doug Eaton).
| Command | data n | checksum |
|---|---|---|
| Motor forward | 0 | 7 |
| Motor reverse | 1 | 6 |
| Beep 1 | 4 | 2 |
| Beep 5 | 8 | 5 |
| Run program | 33 | 4 |
| Delete program | 34 | 3 |
Opcode table
The 7-bit data values the Micro Scout recognises (shared codebase with Code Pilot / Scout). Decoded from rcxvll.nqh (pbrick) and cross-checked with elecbrick (Doug Eaton) / Flash Force, and validated against the official Scout SDK "VLL Command Set" table (Nov 1999, pp. 66–68) and the printable MicroScout VLL barcode card.
Direct commands (execute immediately)
| Code | Binary | Action |
|---|---|---|
| 0 | 0000000 | Motor forward |
| 1 | 0000001 | Motor reverse |
| 10 | 0001010 | Stop motors (see note) |
| 4 | 0000100 | Beep 1 |
| 5 | 0000101 | Beep 2 |
| 6 | 0000110 | Beep 3 |
| 7 | 0000111 | Beep 4 |
| 8 | 0001000 | Beep 5 |
| 33 | 0100001 | Run stored program |
| 34 | 0100010 | Delete stored program |
| 70 | 1000110 | "Next" (official spec; no effect on device) |
| 71 | 1000111 | Reset (official spec; no effect on device) |
Script commands (16–32, stored into a program; up to 15 steps)
| Code | Action | Code | Action | |
|---|---|---|---|---|
| 16 | Motor fwd 0.5 s | 24 | Beep 1 | |
| 17 | Motor fwd 1 s | 25 | Beep 2 | |
| 18 | Motor fwd 2 s | 26 | Beep 3 | |
| 19 | Motor fwd 5 s | 27 | Beep 4 | |
| 20 | Motor rev 0.5 s | 28 | Beep 5 | |
| 21 | Motor rev 1 s | 29 | Wait for light | |
| 22 | Motor rev 2 s | 30 | Seek light | |
| 23 | Motor rev 5 s | 31 | Code (talk to another brick) | |
| 32 | Keep alive |
Build a program = Delete (34) → send script steps 16–32 → Run (33).
Notes / gaps
- Stop motors: the official Scout SDK and rcxvll.nqh both define code 10 = Motor Stop (Direct). So 10 is the correct, sanctioned stop code — but it did nothing on a test Micro Scout (firmware/timing-dependent?). The old elecbrick "15" is not in any official list.
- 70 (Next) / 71 (Reset) are real, documented MicroScout codes (Scout SDK + barcode card), but likewise produced no effect on-device. Kept here for completeness; removed from Flash Force's active buttons as unreliable in practice. No direct "stop" code worked reliably in testing.
- Codes 2, 3, 9, 11–14 are unused/unknown for the MicroScout. (On Code Pilot, 9 = Sound Mute, 11 = Motor & Sound, 12–15 = fixed programs, 96–127 = touch/tones/numbers/speed/tacho — see the SDK table.)
- Device-confirmed working: motor 0/1, beeps 4–8, Run 33, Delete 34, and script builder 16–32.
Implementations
Four independent implementations agree on the core protocol:
| Source | Checksum | bit0/bit1 | Preamble | Emitter |
|---|---|---|---|---|
| Flash Force | ✅ | 40/20 · 20/40 | 400 ms | phone torch |
| rcxvll.nqh (pbrick) | ✅ | same | 400 ms | LEGO 9V light brick on RCX |
| lego-blockly | ✅ | same | 1000 ms | LED on FTDI DTR |
| elecbrick (Doug Eaton) | ✅ | same | — | opcode generator |
The protocol is identical everywhere; the tunable parts are preamble length and stop-bit tail (reliability knobs).
Practical
- Timing tolerance is moderate — Flash Force works at scale 1.05–1.35× (default 1.2×).
- Needs strong ON/OFF contrast; hold the emitter close (1–3 cm), a dim room helps.
- Works with any light the sensor sees — visible or IR (see IR LED (invisible VLL)).
Links
- elecbrick VLL reference (Doug Eaton)
- Ben Jackson — "VLL Transmit for legOS"
- RCX → MicroScout VLL writeup (pbrick.info)