This page has moved into the new guide: Ranger 2-Way

Laser Ranger 2-ch: value & command reference

Back to Laser Ranger 2-ch

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

left distance (cm)
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 cm
mBlock

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 cm

02 right distance (cm) rightCm

right distance (cm)
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 cm
mBlock

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 cm

03 nearer distance (cm) nearCm

nearer distance (cm)
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 cm
mBlock

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 cm

Related block "on distance 2-ch nearer than 10 cm"

mbc.onTof2Near(10, function () {
})

04 left distance (mm) leftMm

left distance (mm)
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 mm
mBlock

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 mm

05 right distance (mm) rightMm

right distance (mm)
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 mm
mBlock

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 mm

06 init bitmap initOk

init bitmap
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 bit
mBlock

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 bit

07 fault bitmap fault

fault bitmap
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 bit
mBlock

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 bit

08 average distance (mm) avgMm

average distance (mm)
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 mm
mBlock

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 mm

Status 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      # 2
EV3

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                # 2
Arduino I2C
int16_t st = mbcRead(0x29, 0xF3);
bool ready = st & (1 << 0);   // ready
bool fault = st & (1 << 1);   // any sensor failed
mBlock

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)   // F3

Shared 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 0x080x77][new], saved to EEPROM, takes effect after reboot; [F8][0x00][0xFF] = back to factory address
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 命令 412 不動(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 重開機用共用的。
Email for a quoteBuying Guide