Laser Ranger 2-ch: value & command reference
Every value and command the Laser Ranger 2-ch reports to each host: what it means, its range, and how to read it on SPIKE / EV3 / Arduino I2C / mBlock / MATRIX / micro:bit. Append ?host=spike (or ev3, arduino, mblock, matrix, microbit) to the URL to expand that host by default.
SPIKEEV3Arduino I2CmBlockMATRIXmicro:bit
| Item | Detail |
|---|---|
| Factory I2C address | 0x29 |
| hw[0] | 2(F0 high byte; ID high byte in SPIKE mode 8 / EV3 mode 5) |
| Model | LASER2 |
| Protocol version | 2 |
| Hosts | SPIKE, EV3, Arduino I2C, mBlock, MATRIX, micro:bit |
Values at a glance
| Id | Value | Range | SPIKE | EV3 | Arduino I2C | mBlock | MATRIX | micro:bit |
|---|---|---|---|---|---|---|---|---|
01 |
left distance (cm) | 0~120 cm | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
02 |
right distance (cm) | 0~120 cm | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
03 |
nearer distance (cm) | 0~120 cm | — | — | ✓ | ✓ | ✓ | ✓ |
04 |
left distance (mm) | 0~1200 mm | ✓ | ✓ | ✓ | ✓ | — | ✓ |
05 |
right distance (mm) | 0~1200 mm | ✓ | ✓ | ✓ | ✓ | — | ✓ |
06 |
init bitmap | 0~3 bit | ✓ | — | ✓ | ✓ | — | ✓ |
07 |
fault bitmap | 0~3 bit | ✓ | — | ✓ | ✓ | — | ✓ |
08 |
average distance (mm) | 0~1200 mm | ✓ | ✓ | ✓ | ✓ | — | ✓ |
✓ = available on that host; — = not sent.
Reading each value
01 left distance (cm) leftCm
| Item | Detail |
|---|---|
| Meaning | distance from the left sensor, 0~120 cm (left = sensor 0 seen from behind the robot); 120 = nothing in range / sensor failed |
| Range | 0~120 cm |
| I2C / mBlock id | 01 |
| Locked | 🔒 LEGO mode layout will not change |
How to read it
SPIKE
mode 0 (edu) byte 0 🔒; mode 0 (toy) byte 0 🔒; mode 1 byte 0; mode 2 byte 0; mode 3 byte 0 🔒; mode 4 v[0] lo; mode 5 v[0] 🔒; mode 8 v[0] lo; mode 9 v[0] lo 🔒. Note: 16 B = [左, 右, 0, 左, 0, 右, 0, 平均, 0×8]:App 顏色 = 左、原始紅 = 256×左; 8 B 原樣(不壓縮); 韌體註解寫「最近」,實際送左(INO:261-263); 網站 read(3) 在用:[左, 右, 0]; Pybricks reflection() 編碼:L = 左 cm,a = L/25、b = 10×(L mod 25)+9,3 個 int16 = a×256+b → reflection() = L; 只送 4 B,v0 = 左 + 256×右
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[0]
leftCm = (w & 0xFF) # left distance (cm)(lo byte)MBC library
from pybricks.parameters import Port
from MBC_laser2_obj_Lib import MBC_LASER2
laser = MBC_LASER2(Port.A)
leftCm = laser.left()EV3
mode 1 · int8 · #67 新:IR-SEEK 8×int8 = [左, 右, 左, 右, 左, 右, 左, 右](index 0/2/4/6 = 左;原本 index 2~7 = 0); mode 2 · int8 · IR-REMOTE [左, 右, 0, 0](INO:137-139); mode 4 · int8 · IR-S-ALT 同模式 2 (locked mode: firmware and hosts will not change it)
from pybricks.iodevices import LUMPDevice
from pybricks.parameters import Port
sensor = LUMPDevice(Port.S1) # sensor on port 1
leftCm = sensor.read(1)[0] # left distance (cm):cm×100/120 (0~100)
leftCm = sensor.read(2)[0] # left distance (cm):cm×100/120 (0~100)
leftCm = sensor.read(4)[0] # left distance (cm):cm×100/120 (0~100)' clev3r (BASIC)
Sensor.SetMode(1, 1)
leftCm = Sensor.ReadRawValue(1, 0)
Sensor.SetMode(1, 2)
leftCm = Sensor.ReadRawValue(1, 0)
Sensor.SetMode(1, 4)
leftCm = Sensor.ReadRawValue(1, 0)Arduino I2C
Returns MBC_NONE (−32768) when the module does not answer.
int16_t leftCm = mbcRead(0x29, 0x01); // left distance (cm):0~120 cmmBlock
Block returns left distance (cm)(0~120 cm)。Host sends [01] → reply [lo][hi][01 ^ lo ^ hi], value = int16 little-endian.
(Ranger 2 port [1 v] left distance cm :: sensing)
MATRIX
0x22 R.
// A. stock 0x22 blocks:R = left distance (cm)
int leftCm = MiniR4.I2C1.MXColor.getColor(R);micro:bit
Returns 0 when the sensor does not answer; check with "device at … is ready" first.
(distance 2-ch [left v] \([cm v]\) :: #00A4E4)
let leftCm = mbc.tof2Distance(MbcTofSide.Left, MbcDistUnit.Cm) // left distance (cm):0~120 cm02 right distance (cm) rightCm
| Item | Detail |
|---|---|
| Meaning | distance from the right sensor, 0~120 cm (right = sensor 1); 120 = nothing in range / sensor failed |
| Range | 0~120 cm |
| I2C / mBlock id | 02 |
| Locked | 🔒 LEGO mode layout will not change |
How to read it
SPIKE
mode 0 (edu) byte 1 🔒; mode 0 (toy) byte 1 🔒; mode 3 byte 1 🔒; mode 4 v[0] hi; mode 7 v[2] 🔒; mode 8 v[0] hi; mode 9 v[0] hi 🔒. Note: App 反射光 = 右、原始綠 = 256×右; 網站 read(3) 在用; Pybricks ambient() = int16[2]/100 = 右 cm(INO:300-312)
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[0]
rightCm = ((w >> 8) & 0xFF) # right distance (cm)(hi byte)MBC library
from pybricks.parameters import Port
from MBC_laser2_obj_Lib import MBC_LASER2
laser = MBC_LASER2(Port.A)
rightCm = laser.right()EV3
mode 1 · int8 · #67 新:index 1/3/5/7 = 右; mode 2 · int8; mode 4 · int8 (locked mode: firmware and hosts will not change it)
from pybricks.iodevices import LUMPDevice
from pybricks.parameters import Port
sensor = LUMPDevice(Port.S1) # sensor on port 1
rightCm = sensor.read(1)[1] # right distance (cm):cm×100/120 (0~100)
rightCm = sensor.read(2)[1] # right distance (cm):cm×100/120 (0~100)
rightCm = sensor.read(4)[1] # right distance (cm):cm×100/120 (0~100)' clev3r (BASIC)
Sensor.SetMode(1, 1)
rightCm = Sensor.ReadRawValue(1, 1)
Sensor.SetMode(1, 2)
rightCm = Sensor.ReadRawValue(1, 1)
Sensor.SetMode(1, 4)
rightCm = Sensor.ReadRawValue(1, 1)Arduino I2C
Returns MBC_NONE (−32768) when the module does not answer.
int16_t rightCm = mbcRead(0x29, 0x02); // right distance (cm):0~120 cmmBlock
Block returns right distance (cm)(0~120 cm)。Host sends [02] → reply [lo][hi][02 ^ lo ^ hi], value = int16 little-endian.
(Ranger 2 port [1 v] right distance cm :: sensing)
MATRIX
0x22 G.
// A. stock 0x22 blocks:G = right distance (cm)
int rightCm = MiniR4.I2C1.MXColor.getColor(G);micro:bit
Returns 0 when the sensor does not answer; check with "device at … is ready" first.
(distance 2-ch [right v] \([cm v]\) :: #00A4E4)
let rightCm = mbc.tof2Distance(MbcTofSide.Right, MbcDistUnit.Cm) // right distance (cm):0~120 cm03 nearer distance (cm) nearCm
| Item | Detail |
|---|---|
| Meaning | the smaller of left and right, 0~120 cm |
| Range | 0~120 cm |
| I2C / mBlock id | 03 |
| Locked | 🔒 LEGO mode layout will not change |
How to read it
SPIKE
Not sent in any SPIKE mode (locked modes stay as they are). Use Arduino I2C / mBlock if you need it.
EV3
Not sent in any EV3 mode (the locked EV3 modes stay as they are). Use Arduino I2C / mBlock if you need it.
Arduino I2C
Returns MBC_NONE (−32768) when the module does not answer.
int16_t nearCm = mbcRead(0x29, 0x03); // nearer distance (cm):0~120 cmmBlock
Block returns nearer distance (cm)(0~120 cm)。Host sends [03] → reply [lo][hi][03 ^ lo ^ hi], value = int16 little-endian.
(Ranger 2 port [1 v] nearer distance cm :: sensing)
MATRIX
0x22 B.
// A. stock 0x22 blocks:B = nearer distance (cm)(B(命令 12 也回較近,schema 不收 12))
int nearCm = MiniR4.I2C1.MXColor.getColor(B);micro:bit
Returns 0 when the sensor does not answer; check with "device at … is ready" first.
(distance 2-ch nearest \(cm\) :: #00A4E4)
let nearCm = mbc.tof2NearestCm() // nearer distance (cm):0~120 cmRelated block "on distance 2-ch nearer than 10 cm"
mbc.onTof2Near(10, function () {
})04 left distance (mm) leftMm
| Item | Detail |
|---|---|
| Meaning | left sensor distance 0~1200 mm; 1200 = nothing in range / sensor failed |
| Range | 0~1200 mm |
| I2C / mBlock id | 04 |
How to read it
SPIKE
mode 6 v[0]. Note: #67 新:HSV 3×int16 = [左 mm, 右 mm, 平均 mm](原本是基底緩衝合併值)
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
leftMm = dev.read(6)[0] # left distance (mm)MBC library
from pybricks.parameters import Port
from MBC_laser2_obj_Lib import MBC_LASER2
laser = MBC_LASER2(Port.A)
leftMm = laser.left_mm()EV3
mode 5 · int16 · #67 新:IR-CAL 2×int16 = [左 mm, 右 mm](原本 word0 = 左'+256×右')
from pybricks.iodevices import LUMPDevice
from pybricks.parameters import Port
sensor = LUMPDevice(Port.S1) # sensor on port 1
leftMm = sensor.read(5)[0] # left distance (mm):0~1200 mm' clev3r (BASIC)
Sensor.SetMode(1, 5)
leftMm = Sensor.ReadRawValue(1, 0)Arduino I2C
Returns MBC_NONE (−32768) when the module does not answer.
int16_t leftMm = mbcRead(0x29, 0x04); // left distance (mm):0~1200 mmmBlock
Block returns left distance (mm)(0~1200 mm)。Host sends [04] → reply [lo][hi][04 ^ lo ^ hi], value = int16 little-endian.
(Ranger 2 port [1 v] left distance mm :: sensing)
MATRIX
Not available with the stock MATRIX 0x22 blocks.
micro:bit
Returns 0 when the sensor does not answer; check with "device at … is ready" first.
(distance 2-ch [left v] \([mm v]\) :: #00A4E4)
let leftMm = mbc.tof2Distance(MbcTofSide.Left, MbcDistUnit.Mm) // left distance (mm):0~1200 mm05 right distance (mm) rightMm
| Item | Detail |
|---|---|
| Meaning | right sensor distance 0~1200 mm; 1200 = nothing in range / sensor failed |
| Range | 0~1200 mm |
| I2C / mBlock id | 05 |
How to read it
SPIKE
mode 6 v[1]. Note: #67 新
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
rightMm = dev.read(6)[1] # right distance (mm)MBC library
from pybricks.parameters import Port
from MBC_laser2_obj_Lib import MBC_LASER2
laser = MBC_LASER2(Port.A)
rightMm = laser.right_mm()EV3
mode 5 · int16 · #67 新
from pybricks.iodevices import LUMPDevice
from pybricks.parameters import Port
sensor = LUMPDevice(Port.S1) # sensor on port 1
rightMm = sensor.read(5)[1] # right distance (mm):0~1200 mm' clev3r (BASIC)
Sensor.SetMode(1, 5)
rightMm = Sensor.ReadRawValue(1, 1)Arduino I2C
Returns MBC_NONE (−32768) when the module does not answer.
int16_t rightMm = mbcRead(0x29, 0x05); // right distance (mm):0~1200 mmmBlock
Block returns right distance (mm)(0~1200 mm)。Host sends [05] → reply [lo][hi][05 ^ lo ^ hi], value = int16 little-endian.
(Ranger 2 port [1 v] right distance mm :: sensing)
MATRIX
Not available with the stock MATRIX 0x22 blocks.
micro:bit
Returns 0 when the sensor does not answer; check with "device at … is ready" first.
(distance 2-ch [right v] \([mm v]\) :: #00A4E4)
let rightMm = mbc.tof2Distance(MbcTofSide.Right, MbcDistUnit.Mm) // right distance (mm):0~1200 mm06 init bitmap initOk
| Item | Detail |
|---|---|
| Meaning | bit0 = left, bit1 = right; 1 = that sensor initialised at power-on (no retry); 3 = both OK |
| Range | 0~3 bit |
| I2C / mBlock id | 06 |
| Locked | 🔒 LEGO mode layout will not change |
How to read it
SPIKE
mode 9 v[1] lo 🔒
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[1]
initOk = (w & 0xFF) # init bitmap(lo byte)MBC library
from pybricks.parameters import Port
from MBC_laser2_obj_Lib import MBC_LASER2
laser = MBC_LASER2(Port.A)
initOk = laser.is_ok(1)EV3
Not sent in any EV3 mode (the locked EV3 modes stay as they are). Use Arduino I2C / mBlock if you need it.
Arduino I2C
Returns MBC_NONE (−32768) when the module does not answer.
int16_t initOk = mbcRead(0x29, 0x06); // init bitmap:0~3 bitmBlock
Block returns init bitmap(0~3 bit)。Host sends [06] → reply [lo][hi][06 ^ lo ^ hi], value = int16 little-endian.
(Ranger 2 port [1 v] init bitmap :: sensing)
MATRIX
Not available with the stock MATRIX 0x22 blocks.
micro:bit
No dedicated block; in JavaScript read it with mbc.readValue(address, id) (−32768 when nothing answers). Related block: "distance 2-ch [left] sensor is OK".
let initOk = mbc.readValue(0x29, 0x06) // init bitmap:0~3 bit07 fault bitmap fault
| Item | Detail |
|---|---|
| Meaning | bit0 = left, bit1 = right; 1 = that sensor is currently failing (I2C error or no update for 200 ms); its distance reads 120 / 1200 |
| Range | 0~3 bit |
| I2C / mBlock id | 07 |
| Locked | 🔒 LEGO mode layout will not change |
How to read it
SPIKE
mode 9 v[1] hi 🔒
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[1]
fault = ((w >> 8) & 0xFF) # fault bitmap(hi byte)MBC library
from pybricks.parameters import Port
from MBC_laser2_obj_Lib import MBC_LASER2
laser = MBC_LASER2(Port.A)
fault = laser.is_ok(1)EV3
Not sent in any EV3 mode (the locked EV3 modes stay as they are). Use Arduino I2C / mBlock if you need it.
Arduino I2C
Returns MBC_NONE (−32768) when the module does not answer.
int16_t fault = mbcRead(0x29, 0x07); // fault bitmap:0~3 bitmBlock
Block returns fault bitmap(0~3 bit)。Host sends [07] → reply [lo][hi][07 ^ lo ^ hi], value = int16 little-endian.
(Ranger 2 port [1 v] fault bitmap :: sensing)
MATRIX
Not available with the stock MATRIX 0x22 blocks.
micro:bit
No dedicated block; in JavaScript read it with mbc.readValue(address, id) (−32768 when nothing answers). Related block: "distance 2-ch [left] sensor is OK".
let fault = mbc.readValue(0x29, 0x07) // fault bitmap:0~3 bit08 average distance (mm) avgMm
| Item | Detail |
|---|---|
| Meaning | (left mm + right mm) / 2, 0~1200; same number as EV3 mode 3 and the 3rd value of SPIKE mode 6 |
| Range | 0~1200 mm |
| I2C / mBlock id | 08 |
How to read it
SPIKE
mode 0 (edu) byte 7 🔒; mode 6 v[2]. Note: byte7 = (左 cm + 右 cm)>>1;App 原始藍 = 256×平均; #67 新:平均 mm
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
avgMm = dev.read(0)[7] # average distance (mm)MBC library
from pybricks.parameters import Port
from MBC_laser2_obj_Lib import MBC_LASER2
laser = MBC_LASER2(Port.A)
avgMm = laser.average_mm()EV3
mode 0 · int8 · IR-PROX = (左'+右')>>1,cm 縮放後平均(鎖住,INO:131-133); mode 3 · int16 · #67 新:IR-REM-A 1×int16 = 左右平均 mm(原本 = 左 0~100) (locked mode: firmware and hosts will not change it)
from pybricks.iodevices import LUMPDevice
from pybricks.parameters import Port
sensor = LUMPDevice(Port.S1) # sensor on port 1
avgMm = sensor.read(0)[0] # average distance (mm):cm average ×100/120 (0~100)
avgMm = sensor.read(3)[0] # average distance (mm):0~1200 mm' clev3r (BASIC)
Sensor.SetMode(1, 0)
avgMm = Sensor.ReadRawValue(1, 0)
Sensor.SetMode(1, 3)
avgMm = Sensor.ReadRawValue(1, 0)Arduino I2C
Returns MBC_NONE (−32768) when the module does not answer.
int16_t avgMm = mbcRead(0x29, 0x08); // average distance (mm):0~1200 mmmBlock
Block returns average distance (mm)(0~1200 mm)。Host sends [08] → reply [lo][hi][08 ^ lo ^ hi], value = int16 little-endian.
(Ranger 2 port [1 v] average distance mm :: sensing)
MATRIX
Not available with the stock MATRIX 0x22 blocks.
micro:bit
Returns 0 when the sensor does not answer; check with "device at … is ready" first.
(distance 2-ch average \(mm\) :: #00A4E4)
let avgMm = mbc.tof2AverageMm() // average distance (mm):0~1200 mmStatus flags F3
One status per bit of F3; read directly over I2C, mBlock, micro:bit.
| bit | Name | Meaning |
|---|---|---|
| b0 | ready (ready) |
1 once both sensors are initialised and snapshots are running |
| b1 | any sensor failed (fault) |
1 if any sensor failed to initialise, has an I2C error or no update for 200 ms (07 ≠ 0 or 06 ≠ 3) |
SPIKE
SPIKE has no F3 flags; mode 8 returns ID / version. The official app cannot read mode 8.
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
id_word, proto, fw, seq = dev.read(8) # mode 8: [ID | hw0<<8, protocol 2, major×100+minor, counter]
addr = id_word & 0xFF # 0x29
hw0 = id_word >> 8 # 2EV3
EV3 has no F3 flags; mode 5 returns the device ID and firmware version (ID format B).
from pybricks.iodevices import LUMPDevice
from pybricks.parameters import Port
sensor = LUMPDevice(Port.S1) # sensor on port 1
id_word, fw = sensor.read(5) # mode 5: [ID | hw0<<8, major×100+minor]
addr = id_word & 0xFF # 0x29
hw0 = id_word >> 8 # 2Arduino I2C
int16_t st = mbcRead(0x29, 0xF3);
bool ready = st & (1 << 0); // ready
bool fault = st & (1 << 1); // any sensor failedmBlock
The extension splits status flags F3 into boolean blocks (ready, calibrating, …).
MATRIX
The status flags are not available with the stock MATRIX 0x22 blocks.
micro:bit
let ready = mbc.deviceReady(0x29) // F3 bit0; false when nothing answers
let st = mbc.status(0x29) // F3Shared registers and commands
Identical on every product, documented once in the universal I2C protocol:
| Id | Name | Meaning |
|---|---|---|
F0 |
device ID | low byte = factory I2C address (unchanged after re-addressing), high byte = hw[0] (channels / sensors / variant) |
F1 |
protocol version | 2 for this release (same number reported in SPIKE mode 8 v[1]) |
F2 |
firmware version | major×100 + minor (e.g. 102 = 1.2) |
F3 |
status flags | b0 = ready; other bits per product (see flags) |
F4 |
update counter | +1 every 10 ms snapshot, wraps 0~255 (2.56 s per cycle, not usable for reboot detection; frozen = module busy) |
F5 |
uptime seconds | seconds since boot, saturates at 32767 (~9 h); a drop means the module rebooted |
F8 |
change I2C address | [F8][new 0x08 |
EF |
reboot | [EF][0xA5]; mBlock sends ACK first, then reboots |
Sources and assumptions
Generated by _build/gen-reference.mjs from _build/protocol/tof2.json (single source of truth, transcribed from the 2026-10-21 protocol decisions).
- 共識 #8:08 左右平均 mm 補進 I2C/mBlock 值表(定案 §5 已列)。
- 0x22 命令 4
12 不動(4 左、5 右、6 最近、711 = 0、12 = 最近);schema 不收 11/12,所以 12 只寫在 03 的 note。 - EV3:模式 0、2、4 鎖住(照盤點);模式 1 改 [左,右]×4(index 0/1 各代表左右)、模式 3 改平均 mm、模式 5 改 [左 mm, 右 mm];模式 1 描述表宣告 int8 −100~100,cm×100/120 在範圍內。
- SPIKE:0/5/7/9 鎖住,另加模式 3 鎖住(網站 sensors/tof2/index.md read(3) = 左/右 cm,共識 #11);模式 6 改 [左 mm, 右 mm, 平均 mm];模式 1、2、4、8 維持不動;模式 8 v[0] 改 ID|hw[0]<<8(統一後共用函式)。
- 「左」= 站在車後往前看的左(第 0 顆,XSHUT PA6);韌體只有 L/R 巨集名,盤點檔標為推測。
- 沒有寫入命令;F8 換位址/EF 重開機用共用的。