Pathfinder 16-Way (Line16) × SPIKE × Pybricks
Interactive tutorialSPIKE
First time? Try the interactive tutorial in your browser first, then follow the steps below with your hub.
MicroPython (Pybricks)Pybricks blocksPybricks built-in blocks (no library)
This page covers Pybricks (Python) only: how to set it up for the Pathfinder 16-Way (Line16) on SPIKE Prime / Robot Inventor, how to read every value, the commands it supports and examples. Use the switcher above to change product, host or software.
Setup
What you need
- Sensor: Pathfinder 16-Way (Line16) (SPIKE edition).
- Hub: SPIKE Prime or Robot Inventor.
- Computer: Chrome. Open code.pybricks.com and start writing; nothing to install.
- Hub firmware: must be switched to Pybricks firmware v4.1 first (the stable code.pybricks.com is v4.1.1; v4.1.0 from the MBC SPIKE Blocks website works too). Every example on this site is written for v4.1.
- Library:
MBC_line16_obj_Lib.py(only needed for the "import the library" method). For a new program of your own, import justMBC_sensors.py: one file for every MBC sensor.
Note
The Pybricks 3.6.1 libraries and examples are no longer updated (the 2026 SPIKE Prime hubs cannot run 3.6.1 either). For hubs that have not been upgraded, the old files are under "Legacy (no longer updated)" on the Downloads page.
Install the software
- Open code.pybricks.com in Chrome.
- Switch the hub to Pybricks firmware. The easiest way is the MBC SPIKE Blocks website: top menu "SPIKE Hub → Install/Update Pybricks firmware", about 3 minutes.
- To go back to the official LEGO App later, switch back to the original LEGO firmware with the SPIKE App.
Make the software see the sensor
Pybricks can read the sensor two ways; pick one:
- Built-in commands (no library): read the sensor with Pybricks' own sensor commands. No extra file, quickest to start.
- Import the library: download MBC_line16_obj_Lib.py to get more data, such as the high-resolution position and every channel.
To import the library:
- In code.pybricks.com, click "Import a file" in the file panel on the left.
- Import both the example file and
MBC_line16_obj_Lib.py, so they are in the same project list. - Open the example and press ▶ to run.
Important
New projects: use
MBC_sensors.py. Old projects: just replace the one file they already use.MBC_sensors.pyis one file for every MBC sensor (the EXP6 and Servo6 expander libraries are separate downloads); writefrom MBC_sensors import MBC_LINE8, with the same class names as in the old files. The old file names (such asMBC_line16_obj_Lib.pyused by the examples on this page) are kept for existing programs: swap in the new file with the same name and the program needs no changes. But every old-name file is the whole shared library, so importing 3 or more of them in one program goes over the SPIKE program slot limit of about 51 KB; to mix several sensors, import onlyMBC_sensors.py.
Tip
The file name must match the name in the program's
importexactly, or you will get anImportError. The library files on this site have no version suffix, so you do not need to rename them.
Tip
Most libraries take a
multitaskargument when you create the object:Falseis normal mode, one line after another;Trueis multitask mode, where every call needsawaitin front so the robot can do several things at once.
Wiring
- Plug the sensor into hub port C with the cable (any of A~F works).
- Use the same port in the program: built-in commands use
Port.APort.F; a library'sportargument is usually16for A~F. - Latch up, push the plug in gently.
Calibrate first (only once)
The Pathfinder has to "learn" the black and white of your mat before its numbers are right. Calibration is stored in the sensor and survives power-off (until you change mats).
- Plug in the sensor and turn the hub on (the sensor lights come on).
- Hold the calibration button for about 0.5 s, then release it (do not keep holding it; holding it keeps restarting calibration).
- When the green calibration LED (next to the button) lights up, sweep the sensor back and forth between the black line and the white floor within 5 seconds. All 16 sensing points must pass over the darkest black line and the brightest white area.
- Keep the sensor 3~12 mm above the surface while sweeping; higher reads poorly.
- The green LED goes off = calibration done, saved in the sensor automatically.
How do I know it worked? On the white floor the "line width" is 0; on a normal black line it is 3~5.
Library and example downloads
The hub needs Pybricks firmware v4.1 (tested on Pybricks 4.1.2 from 2026-10-29 to 11-01: the library, built-in ColorSensor, Pybricks Code blocks and the command examples all ran on a hub). The zip has MBC_sensors.py, MBC_line16_obj_Lib.py and 4 examples:
Library only: MBC_sensors.py (use this for new projects: one file for every MBC sensor) · MBC_line16_obj_Lib.py (for the examples on this page and older programs)
Important
The examples say
from MBC_line16_obj_Lib import MBC_LINE16, so importMBC_line16_obj_Lib.py(no renaming needed). For a new program of your own, writefrom MBC_sensors import MBC_LINE16and import onlyMBC_sensors.py. The library must be in the same project list as the main program.
Warning
MBC libraries downloaded before 2026-10-29 stop with
NameError: name 'property' isn't definedas soon as they are imported on Pybricks 4.1.2 (the 4.1.2 hub has noproperty). Download the files on this page again (fixed and tested on 4.1.2).
Note
Pybricks 3.6.1 is no longer updated; the old files are under "Legacy (no longer updated)" on the Downloads page. The 3.6.1 built-in examples only fit sensor firmware before 2026-10-30 (with newer firmware the old split reads the width ×10).

Method 1: built-in sensor commands (no library)
Read with Pybricks' built-in ColorSensor commands, no extra file. These values are computed from the built-in commands:
| Set | Function | Range | Meaning |
|---|---|---|---|
| Basic (less sensitive to calibration, but calibrate once) | getLinePos16() |
-16 ~ +16 |
Line position (integer), 0 centre, negative left, positive right |
getLineWidth16() |
0 ~ 16 |
Line width (sensors triggered), for crossings and T-junctions | |
| High precision (calibrate properly first) | getLinePos100() |
-100 ~ +100 |
High-resolution interpolated position for advanced PID |
getLineWidth100() |
0 ~ 9 |
Line width in high-precision mode, in sync with getLinePos100() |
Tip
Use position and width from the same set:
getLinePos16()withgetLineWidth16(),getLinePos100()withgetLineWidth100(), so both come from the same moment.
Examples: blocks line16_block_native.py; Python line16_python_native.py
Important
Decoding on Pybricks 4.1 (the examples above already do this)
reflection()(mode 5) = coarse position + width. Always convert withint()before splitting:r = int(line.reflection()); 16-ch: add 6399 when negative (at line position +16 the number goes past 3199 and Pybricks shows it as negative), then position =r // 100 - 16(−16 to +16), width =r % 100.
- Use
int()(truncate toward 0), notfloor(): with older firmware's decimals, floor is one too low in the negative range (measured: floor splits into position 15, width 99;int()gives the correct 16, 0).- Sensor firmware from 2026-11-01 (release v1.0-261101) sends an exact integer; older firmware gives one decimal on Pybricks 4.x. Keep the
int()and it works on old and new firmware.- Blocks have no "integer" block: use "floor" for values ≥ 0 and "ceiling" for negative ones, which equals
int()(the block examples above already do this).ambient()(mode 7) = "fine position + 100 . width", width in the first decimal: fine position = integer part − 100, width =round(line.ambient() * 10) % 10(16-ch shows at most 9 = 9 or more). Needs sensor firmware from 2026-10-30 or later; on older firmware the width reads 0 (position still works). With older firmware, take the width from reflection instead, or install the MBC library (mode 9 gives every value on old and new firmware).- Positions are sent with an offset and subtracted after reading; negative numbers are never sent directly.
- Direction: position −16 to +16, line on the left = negative, on the right = positive (seen from behind the robot).
- Advanced (Python only):
line.hsv(surface=False).h= (fine position + 100) × 100 + width, giving the full width.
Method 2: import the library
| Function | Range | Meaning |
|---|---|---|
line_init_port_multitask(port, multitask) |
— | Initialise. port = 1multitask=True enables async mode (use with await) |
line_pos16() |
-16 ~ +16 |
Integer line position, less sensitive to calibration (without calibration every channel reads as black) |
line_pos100() |
-100 ~ +100 |
High-resolution interpolated position, best for PID; calibrate properly |
line_width() |
0 ~ 16 |
Line width, works with both position functions |
line_ir_calibration() |
list[16] |
16 calibrated values (each 0 ~ 15). Seen from behind the robot: index 0 = far left (ch1) |
line_ir_ch(ch) |
0 ~ 15 |
One calibrated channel, ch = 1~16; fast to call repeatedly (shared cache) |
line_junction() |
0, 1, 2+ |
Number of line groups: 0 none, 1 single line, 2+ junction |
line_junction_name() |
str |
"none", "line", "junction" |
line_bin_raw() |
0 ~ 65535 |
16-bit trigger mask, bit0 = ch1, 1 = line seen |
line_on_sensors() |
list[int] |
Channels seeing the line, e.g. [7, 8, 9] |
line_read_all() |
dict |
Everything at once: ir_calibration, line_pos16, line_pos100, line_width, bin_raw, on_sensors, junction, junction_name |
Examples: blocks line16_block_with_lib.py; Python line16_python_with_lib.py
Command examples (calibration, mirror)
These send commands with the shared MBC library (see "Commands" further down this page for each command). First upload MBC_sensors.py to Pybricks Code into the same project list; sensor on port C; follow the hints in the output pane and press the hub's left button to go on (the centre button still stops the program). Needs sensor firmware from 2026-10 or later. All 3 have been run on a hub.
| Example | What it does | How to tell it worked |
|---|---|---|
| line16_cmd_calib_bw.py | Black/white calibration (E2 black, E3 white): put the whole row on black and press left, then on white paper and press left (keep the height fixed) | Black light values all 0; white-paper light values (ir_calibration(), 0–15) 13 or more |
| line16_cmd_calib_auto.py | E7 5-second auto calibration: sweep the sensor across the black line and white surface for 5 s (fixed height, no tilt) | State goes from busy to done; white-paper light values 11 or more (quality depends on how you sweep) |
| line16_cmd_reverse.py | E9 mirror left/right, and shows that a wrong parameter returns "failed" | Line position changes sign (e.g. −15 → +15, send 0 to switch back); sending 5 returns "failed" |
- Keep the sensor at a fixed height while calibrating (mounted on the robot or propped on bricks, about 5–10 mm above the paper), and sweep it flat without tilting. Hand-held calibration gives low white readings. Measured (2026-10-29): resting black/white calibration (E3) gives white 13–15, pass mark ≥ 13; a hand-swept E7 is usable from 11.
PD line-following demos (speed 50)
One PD line-following logic in 4 reading methods, each as blocks and as Python: 8 programs. They stop at a crossing (the whole row on the line). All 8 have been run on a Pybricks hub.
| Method | Reads | Suggested KP / KD | Stops at crossing | Blocks | Python |
|---|---|---|---|---|---|
| Built-in reflection (mode 5) | ColorSensor.reflection(): coarse position + width |
2.5 / 10 | width > 10 | mode5_block | mode5_python |
| Built-in ambient (mode 7) | ColorSensor.ambient(): fine position + width (firmware 2026-10-30 or later) |
0.4 / 1.5 | width > 8 | mode7_block | mode7_python |
| Product library | MBC_line16_obj_Lib: pos100() + width() (mode 9) |
0.4 / 1.5 | width > 10 | lib_block | lib_python |
| Shared library | MBC_sensors: same |
0.4 / 1.5 | width > 10 | sensors_block | sensors_python |
- Wiring: sensor on port C, left motor on E (set to COUNTERCLOCKWISE in the program), right motor on F.
- Speed 50 (power %). KP and KD are starting values; tune them on your robot: lower KP if it wobbles, lower KD if turns are too slow.
- For the two library methods, first upload
MBC_line16_obj_Lib.pyorMBC_sensors.pyto Pybricks Code into the same project list as the program (included in the zip). - The block files open as Pybricks blocks; the first line holds the block data, so import the whole file rather than copying only the Python part.
Want to learn more advanced line following?
These coaches have plenty of competition and teaching experience: Coach LegoLauXiao (demo video), Coach legolaumo (demo video).
First test
- Turn on the hub and connect it in code.pybricks.com.
- Import and open example 1 on this page, then press ▶.
- If the numbers printed in the console change with what is in front of the sensor, everything is connected.
FAQ
ImportError? The library was not imported into the same project, or its file name differs from theimportname. Rename it toMBC_line16_obj_Lib.pyand import it again.- The example does not run, or a command is unknown? The hub firmware is too old: the examples on this site are written for Pybricks v4.1, so update the hub to v4.1 first. 3.6.1 is no longer updated; the old files are on the Downloads page.
- What happens if the sensor cable comes out? The program keeps running: the library returns the last value it read and prints one notice in the console (in Chinese and English:
MBC: Port C 讀不到感應器,先回傳上一次的值 / no sensor, using the last value); if the sensor was never connected it returnsNone. To check in your program, call the object'ssensor_ok():Trueif the last read worked,Falseif not (addawaitin multitask mode). Once you plug it back in the library keeps reading; if the sensor was missing from the start and you use multitask mode, run the program again. - An example marked "Pybricks only" shows strange numbers? It uses a new mode in sensor firmware from 2026-10 on; older firmware returns different numbers.
- The official App cannot connect any more? The hub now runs Pybricks firmware: switch back to the original LEGO firmware with the SPIKE App.
Reading values
Code assumes the sensor is on port A. For the MBC library form, import the library .py into Pybricks too.
line position linePos
| Range | Meaning |
|---|---|
-16~+16 |
which channel the line is under: −16 (far left) to +16 (far right), 0 = centre; seen from behind the robot, negative = left (normally ±15; ±16 only for 200 ms after the line leaves the outermost channel) |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[4]
linePos = (w & 0xFF) - 16 # line position(lo byte)MBC library
from pybricks.parameters import Port
from MBC_line16_obj_Lib import MBC_LINE16
line = MBC_LINE16(Port.A)
linePos = line.pos16()mode 0 (edu) byte 1 −16 🔒; mode 0 (toy) byte 0 −16 🔒; mode 1 byte 0 −16; mode 5 v[0] 🔒; mode 9 v[4] lo −16 🔒. Note: Pybricks reflection() 編碼:X = (線位置+16)×100 + 線寬,共用核心 mbcReflFill8 送出:2026-11-01 以後的韌體 reflection() 剛好是整數(3.x、4.x 都是);舊韌體(R=G=B=a×256+b)在 4.x 帶小數,先 int();X ≥ 3200 時讀到負數 X−6399(F16:905)
fine position linePosFine
| Range | Meaning |
|---|---|
-100~+100 |
sub-channel position: −100 to +100, 0 = centre, negative = left (same sign as line position) |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[5]
linePosFine = (w & 0xFF) - 100 # fine position(lo byte)MBC library
from pybricks.parameters import Port
from MBC_line16_obj_Lib import MBC_LINE16
line = MBC_LINE16(Port.A)
linePosFine = line.pos100()mode 0 (edu) byte 2 −100 🔒; mode 0 (toy) byte 2 −100 🔒; mode 3 byte 0 −100; mode 4 v[0] −100; mode 7 v[0] 🔒; mode 9 v[5] lo −100 🔒. Note: byte2 = 細位置+100(0~200)、byte3 = 0;App 原始紅讀成 int16; 細位置+100 經玩具版查表編碼成 byte2~3; 4 個 int16:第 1、2、4 格 = (細位置+100)×100 + 線寬(0~20016;Pybricks hsv(surface=False).h 讀第 1 格,原封不動);第 3 格 = (細位置+100)×100 + min(線寬,9)×10(Pybricks ambient() 讀這格:4.1.2 先 ÷10 去尾再 ÷10、3.6.1/4.0.0 ÷100,都得到 細位置+100 . 線寬,線寬最多顯示 9)。2026-10-30 起第 3 格改這個編碼
line width lineWidth
| Range | Meaning |
|---|---|
0~16 路 |
number of channels on the line, 0~16; 0 = no line |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[4]
lineWidth = ((w >> 8) & 0xFF) # line width(hi byte)MBC library
from pybricks.parameters import Port
from MBC_line16_obj_Lib import MBC_LINE16
line = MBC_LINE16(Port.A)
lineWidth = line.width()mode 0 (edu) byte 0 🔒; mode 0 (toy) byte 1 🔒; mode 2 byte 0; mode 3 byte 1; mode 9 v[4] hi 🔒. Note: min(線寬, 10)
line segments groupCount
| Range | Meaning |
|---|---|
0~8 |
number of separate line segments: 0 = lost, 1 = one line, ≥2 = fork / crossing |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[5]
groupCount = ((w >> 8) & 0xFF) # line segments(hi byte)MBC library
from pybricks.parameters import Port
from MBC_line16_obj_Lib import MBC_LINE16
line = MBC_LINE16(Port.A)
groupCount = line.junction()mode 9 v[5] hi 🔒
line bitmap bitmap
| Range | Meaning |
|---|---|
0~65535 bit |
bit0 = channel 1 (left) … bit15 = channel 16; 1 = that channel sees the line |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
bitmap = dev.read(9)[6] # line bitmapMBC library
from pybricks.parameters import Port
from MBC_line16_obj_Lib import MBC_LINE16
line = MBC_LINE16(Port.A)
bitmap = line.bin_raw()mode 4 v[1]; mode 9 v[6] 🔒. Note: binRaw 16 bit(byte2 低、byte3 高); byte12 低、byte13 高
channel 1~16 reflect light
| Range | Meaning |
|---|---|
0~100 |
calibrated reflect of channel 1 |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
words = dev.read(9)[0:4]
light = []
for w in words:
for k in range(4):
light.append((w >> (4 * k)) & 0x0F) # low bits firstMBC library
from pybricks.parameters import Port
from MBC_line16_obj_Lib import MBC_LINE16
line = MBC_LINE16(Port.A)
light = line.ir_calibration() # list, channel 1 = [0]mode 0 (edu) byte 4 🔒; mode 0 (toy) byte 4 🔒; mode 3 byte 2; mode 6 v[0]; mode 8 v[0]; mode 9 v[0] 🔒. Note: 每路 2 bit((v×10)>>8:0~25→0、26~51→1、52~76→2、77~100→3);byte4 = 第 5~8 路(第 5 路在 bit7-6)、byte5 = 第 1~4 路、byte6 = 第 13~16 路、byte7 = 第 9~12 路 → App 原始綠 = 第 1~8 路(bit15-14 = 第 1 路)、原始藍 = 第 9~16 路;byte 8~15 = 0; 每路 1 bit(光值 ≥ 門檻(值 07,預設 40)= 1 亮,跟二值化判白同一個判斷;2026-11-03 以前的韌體是光值 > 50):第 1~8 路合成 1 byte(第 1 路在 bit7)經玩具版查表編碼成 byte4~5;第 9~16 路 → byte6~7; byte2 = 第 1 路、byte3 = 第 16 路(描述表只宣告 3×int8,第 4 byte 主機可能砍掉); byte0~5 = 第 6~11 路光值 → 3×int16,每個 lo = 前一路、hi = 後一路; byte0~3 = 第 7~10 路(只送 4 B); 每 int16 四路 4-bit((v×5)>>5,0~15):bit0-3 = 第 1 路、bit4-7 = 第 2 路、bit8-11 = 第 3 路、bit12-15 = 第 4 路;v0~v3 = 第 1~16 路
Not readable here:last exit direction, threshold, calibrations done. Use Arduino / mBlock / micro:bit for these.
Commands
Commands need the MBC library (2026-10 or later) and sensor firmware from 2026-10 or later: send with cmd(code, params…), then wait_done() waits for it (True = done; False = failed, rejected or timed out). Sensor states: 0 done, 1 busy, 2 failed (e.g. parameter out of range), 3 rejected (unknown command, or another command sent during calibration).
- On older firmware
cmd()reports "firmware too old"; readings keep working. - The same command with the same parameters is not repeated (even inside a loop); pass
cmd(…, again=True)to run it again. wait_done()costs 40–60 ms each time: use it for stop-and-do commands such as calibration, not inside a line-following loop.- The official SPIKE App 3 and Pybricks' built-in ColorSensor blocks cannot send commands.
black line mode blackLine
follow a black line (default); RAM only, back to the default after reset
from pybricks.parameters import Port
from MBC_sensors import MBC_LINE16
line = MBC_LINE16(Port.A)
line.cmd(0xE0) # black line mode
ok = line.wait_done() # True = done; False = failed / rejected / timed outPybricks + the MBC library only (not the official app or built-in ColorSensor blocks); needs sensor firmware from 2026-10 or later.
white line mode whiteLine
follow a white line (F3 b1 = 1); RAM only, back to the default after reset
from pybricks.parameters import Port
from MBC_sensors import MBC_LINE16
line = MBC_LINE16(Port.A)
line.cmd(0xE1) # white line mode
ok = line.wait_done() # True = done; False = failed / rejected / timed outPybricks + the MBC library only (not the official app or built-in ColorSensor blocks); needs sensor firmware from 2026-10 or later.
calibrate black calibBlack
store the current reading as black (sensor over the black line), ~50 ms; saved to EEPROM
from pybricks.parameters import Port
from MBC_sensors import MBC_LINE16
line = MBC_LINE16(Port.A)
line.cmd(0xE2) # calibrate black
ok = line.wait_done() # True = done; False = failed / rejected / timed outPybricks + the MBC library only (not the official app or built-in ColorSensor blocks); needs sensor firmware from 2026-10 or later.
calibrate white calibWhite
store the current reading as white (sensor over the white surface), ~50 ms; saved to EEPROM
from pybricks.parameters import Port
from MBC_sensors import MBC_LINE16
line = MBC_LINE16(Port.A)
line.cmd(0xE3) # calibrate white
ok = line.wait_done() # True = done; False = failed / rejected / timed outPybricks + the MBC library only (not the official app or built-in ColorSensor blocks); needs sensor firmware from 2026-10 or later.
set threshold setThreshold
threshold 1~99 (RAM only, back to 40 after reboot); on SPIKE an out-of-range value returns failed (state 2)
from pybricks.parameters import Port
from MBC_sensors import MBC_LINE16
line = MBC_LINE16(Port.A)
line.cmd(0xE4, 50) # set threshold(threshold 1~99)
ok = line.wait_done() # True = done; False = failed / rejected / timed outPybricks + the MBC library only (not the official app or built-in ColorSensor blocks); needs sensor firmware from 2026-10 or later.
5-second auto calibration calibAuto
sweep the sensor across black line and white surface for 5 s; min/max become the references; F3 b2 = 1 meanwhile
from pybricks.parameters import Port
from MBC_sensors import MBC_LINE16
line = MBC_LINE16(Port.A)
line.cmd(0xE7) # 5-second auto calibration
ok = line.wait_done(8000) # True = done; False = failed / rejected / timed outPybricks + the MBC library only (not the official app or built-in ColorSensor blocks); needs sensor firmware from 2026-10 or later.
mirror left/right reverse
[0 normal / 1 mirrored]: for a sensor mounted backwards — channel order and line position are mirrored; calibration stays per physical channel; RAM only, 0 after reset (not in legacy mBlock); on SPIKE an out-of-range value returns failed (state 2)
from pybricks.parameters import Port
from MBC_sensors import MBC_LINE16
line = MBC_LINE16(Port.A)
line.cmd(0xE9, 1) # mirror left/right(mirrored 0~1)
ok = line.wait_done() # True = done; False = failed / rejected / timed outPybricks + the MBC library only (not the official app or built-in ColorSensor blocks); needs sensor firmware from 2026-10 or later.
Examples
Example 1: See the black line
What this example does: the hub display keeps showing the line position (-16 to 16). Slowly move the black line from left to right and the number goes from negative to positive.
What you need
- 1 SPIKE hub
- 1 Pathfinder 16-Way (calibrated)
- A sheet of white paper with a black line drawn on it (or a competition mat)
How to connect: Pathfinder on port C.
Download the program
| Pybricks |
|---|
| ex01_show_position.py + library |
Pybricks code
# 循行者 16 路 範例 1:看見黑線(顯示線位置)
# 接線:循行者 16 路 → C 埠
from pybricks.hubs import PrimeHub
from pybricks.tools import wait
from MBC_line16_obj_Lib import MBC_LINE16
hub = PrimeHub()
line16 = MBC_LINE16(3) # 3 = C 埠(1=A、2=B、3=C、4=D、5=E、6=F)
while True:
pos = line16.pos16() # 線位置:-16(線在左)~ 0(中間)~ 16(線在右)
hub.display.number(pos) # 主機螢幕顯示這個數字
print(pos) # 電腦畫面也印出來
wait(200) # 等 0.2 秒再讀下一次Step by step through the program
- Repeat forever: the blocks inside keep running until you press stop.
- Read the line position: in the official App it is "reflected light minus 16"; the MBC Blocks website and Pybricks have "line position (-16~16)" directly.
- Show it on the display: the official App uses the "write" block, the MBC Blocks website uses "show number on display", and Pybricks uses
hub.display.number(). - Wait 0.2 seconds: lets the number sit still long enough for your eyes to read it.
What you should see
- Line right under the sensor: the display shows around 0 (-2 to 2 all count as center).
- Move the black line to the left: the number goes negative, closer to -16 the farther left you go.
- Move it to the right: it goes positive, closer to 16 the farther right you go.
Common problems
- The number is stuck at 0 and never changes? → Run example 2 first: if the light is still green on white paper, every point thinks it sees black, meaning the sensor is not calibrated yet; go back to "calibrate" above.
- Left and right are swapped? → First make sure the sensor is not mounted upside down (the side with the components must face down, toward the floor). If it is mounted correctly and still reversed, swap the sign of the "turn left" and "turn right" numbers in the later examples.
- The official App shows 16 instead of 0? → You forgot to "subtract 16".
Example 2: Black line alarm
What this example does: when it sees the black line, the hub's center light turns green; when it can't find the line, the light turns red and the hub keeps beeping.
What you need: same as example 1.
How to connect: Pathfinder on port C.
Download the program
| Pybricks |
|---|
| ex02_line_alarm.py + library |
Pybricks code
# 循行者 16 路 範例 2:黑線警報(壓到線亮綠燈;找不到線亮紅燈並嗶一聲)
# 接線:循行者 16 路 → C 埠
from pybricks.hubs import PrimeHub
from pybricks.parameters import Color
from pybricks.tools import wait
from MBC_line16_obj_Lib import MBC_LINE16
hub = PrimeHub()
line16 = MBC_LINE16(3) # 3 = C 埠
while True:
if line16.width() > 0: # 線寬 = 有幾路踩在黑線上;大於 0 就是有看到線
hub.light.on(Color.GREEN)
else: # 線寬 = 0:16 路都沒踩到 → 找不到線
hub.light.on(Color.RED)
hub.speaker.beep(880, 100) # 頻率 880 Hz,響 0.1 秒
wait(100)Step by step through the program
- Read the line width: in the official App use the "color" block; what it returns is the line width.
- If line width > 0 then…: any of the 16 sensor points is on the black line → green light.
- Else…: line width is 0, meaning not a single point is on the line → red light and a beep.
- Wait 0.1 seconds, then check again.
What you should see
- Sensor on the black line: green light.
- Move it onto the white floor: red light + nonstop beeping.
Common problems
- Always green, even on white paper? → Not calibrated yet (every point is being read as black).
- How do I pick the light color in the official App? → Click the color dot on the "set Power Button light to" block to change it.
Example 3: Which side is the junction on (16-Way only)
What this example does: the 16-Way's sensor points are spread wide and packed densely, so at a junction it can tell "where the big black patch is". The hub display tells you:
- A normal line → ⬆
- A branch on the left (T-junction going left) → ⬅
- A branch on the right (T-junction going right) → ➡
- A crossroads → +
- No line found → ✕
What you need: same as example 1, plus three kinds of junction drawn with black tape or a marker: a "T going left", a "T going right", and a "crossroads".
How to connect: Pathfinder on port C.
Download the program
| Pybricks |
|---|
| ex03_junction_side.py + library |
Pybricks code
# 循行者 16 路 範例 3:路口在哪一邊(左邊有岔路、右邊有岔路,還是十字路口)
# 接線:循行者 16 路 → C 埠
# 想法:一般的線只有幾路踩到;碰到路口時黑色會變很寬(線寬變大),
# 再看這一大塊黑色偏左、偏右還是在中間,就知道岔路在哪一邊。
from pybricks.hubs import PrimeHub
from pybricks.parameters import Icon
from pybricks.tools import wait
from MBC_line16_obj_Lib import MBC_LINE16
hub = PrimeHub()
line16 = MBC_LINE16(3) # 3 = C 埠
while True:
pos = line16.pos16() # 黑色那一塊的中心:負 = 偏左,正 = 偏右
width = line16.width() # 有幾路踩到黑色(0 ~ 16)
if width == 0: # 沒看到線(這時位置會停在最後一次的值,不能拿來判斷)
hub.display.icon(Icon.FALSE) # 顯示 ✕
elif width < 6: # 窄窄的一條 → 一般的線
hub.display.icon(Icon.ARROW_UP)
elif pos < -3: # 很寬而且偏左 → 左邊有岔路
hub.display.icon(Icon.ARROW_LEFT)
elif pos > 3: # 很寬而且偏右 → 右邊有岔路
hub.display.icon(Icon.ARROW_RIGHT)
else: # 很寬又在中間 → 十字路口
hub.display.char('+')
wait(100)Step by step through the program
- First read the line position and line width and store them in two "variables" (a variable = a box with a name that can hold one number).
- If line width = 0 → show ✕ (no line found; the line position is not reliable right now, so this check comes first).
- Else, if line width < 6 → just a narrow strip, a normal line → show ⬆.
- Else (line width 6 or more = the black got much wider, so you are at a junction), look at the "line position", which is the center of that black patch:
- line position < -3: the black is off to the left → branch on the left → ⬅
- line position > 3: the black is off to the right → branch on the right → ➡
- Neither: the black is dead center, equally wide on both sides → crossroads → +
What you should see: slowly push the sensor across the three junctions. Passing a T going left flashes ⬅ for a moment, a T going right flashes ➡, a crossroads flashes +, and the rest of the time it shows ⬆.
Common problems
- Always ⬆, junctions are never recognized? → Push it more slowly; or the branch line is too thin, so change "6" to 5.
- Normal curves get treated as junctions? → The line is too wide; change "6" to 7.
- Why is there no "+" symbol in the official App? → The official App uses a hand-drawn 5×5 picture (one horizontal and one vertical bar through the middle); the MBC Blocks website and Pybricks show the "+" character directly.
- How do I change the picture in the official App? → Click the little grid image on the "turn on" block and light up whichever pixels you want.
Example 4: The simplest line following
What this example does: the robot follows the black line on its own. Line on the left → turn left; line on the right → turn right; line in the center → go straight.
What you need
- Everything from example 1, plus
- A two-wheel robot car (two motors as wheels)
- A closed loop of black line (about 2 cm wide, no sharp turns)
How to connect
- Pathfinder on port C, mounted at the front of the car, about 0.5 to 1 cm above the floor
- Left wheel motor on E, right wheel motor on F
Download the program
| Pybricks |
|---|
| ex04_simple_follow.py + library |
Pybricks code
# 循行者 16 路 範例 4:最簡單的循線(線在左就左轉、線在右就右轉、中間就直走)
# 接線:循行者 16 路 → C 埠;左輪馬達 → E 埠;右輪馬達 → F 埠
from pybricks.parameters import Direction, Port
from pybricks.pupdevices import Motor
from pybricks.robotics import DriveBase
from pybricks.tools import wait
from MBC_line16_obj_Lib import MBC_LINE16
line16 = MBC_LINE16(3) # 3 = C 埠
left = Motor(Port.E, Direction.COUNTERCLOCKWISE) # 左輪:反過來裝,所以設成逆時針才是往前
right = Motor(Port.F) # 右輪
robot = DriveBase(left, right, wheel_diameter=56, axle_track=112) # 輪子直徑 56 mm、兩輪距離 112 mm
while True:
pos = line16.pos16()
if pos < -2: # 線在左邊 → 一邊前進一邊左轉
robot.drive(100, -60) # 速度 100 mm/秒,每秒左轉 60 度
elif pos > 2: # 線在右邊 → 右轉
robot.drive(100, 60)
else: # 線在中間(-2 ~ 2)→ 直走
robot.drive(100, 0)
wait(10)Step by step through the program
- Tell the hub where the wheels are: in the official App it is "set movement motors to E+F"; on the MBC Blocks website and in Pybricks it is "drive base: left wheel E, right wheel F".
- Set the speed: 30% in the official App; 100 mm per second in the other two. Start slow, and speed up once it works.
- Repeat forever: read the line position.
- If line position < -2 (line on the left) → turn left while driving.
- Else if line position > 2 (line on the right) → turn right.
- Else (-2 to 2) → go straight.
What you should see: the car follows the line, wobbling left and right as it goes (that is normal; example 5 improves it).
Common problems
- It shoots off right away and gets farther and farther from the line? → The turn directions are reversed: swap the sign of the two turn numbers (official App: swap -40 and 40; the other two: swap -60 and 60).
- The car drives backward? → The motors are mounted the opposite way from LEGO's standard car. Official App: mount the motors the same way as LEGO's "Driving Base" model; MBC Blocks website: in the "drive base" block, flip the "forward" direction of both the left and right wheels; Pybricks: change the left wheel to
Direction.CLOCKWISEand the right wheel toMotor(Port.F, Direction.COUNTERCLOCKWISE). - It overshoots and runs off on sharp turns? → Lower the speed.
- Why is the center -2 to 2? → The 16-Way has finer steps, so the number moves as soon as the line is slightly off; leaving a little margin keeps the car from twitching back and forth all the time.
Example 5: Smooth line following + stop at the crossroads
What this example does: follows the line using "turn as much as you are off the line", which is smoother than example 4; when it reaches a crossroads (nearly the whole row of sensor points is on black), it stops and beeps. It does not stop at T-junctions; it keeps driving straight through.
What you need: same as example 4, and the black line needs one crossroads.
How to connect: Pathfinder on port C; left wheel motor on E, right wheel motor on F.
Download the program
| Pybricks |
|---|
| ex05_smooth_follow_stop.py + library |
Pybricks code
# 循行者 16 路 範例 5:平順循線,遇到十字路口就停車
# 接線:循行者 16 路 → C 埠;左輪馬達 → E 埠;右輪馬達 → F 埠
from pybricks.hubs import PrimeHub
from pybricks.parameters import Direction, Port
from pybricks.pupdevices import Motor
from pybricks.robotics import DriveBase
from pybricks.tools import wait
from MBC_line16_obj_Lib import MBC_LINE16
hub = PrimeHub()
line16 = MBC_LINE16(3) # 3 = C 埠
left = Motor(Port.E, Direction.COUNTERCLOCKWISE)
right = Motor(Port.F)
robot = DriveBase(left, right, wheel_diameter=56, axle_track=112)
while True:
if line16.width() >= 11: # 11 路以上同時踩到黑線 → 十字路口(整排 16 路幾乎全黑)
robot.stop()
hub.speaker.beep(880, 500)
break # 跳出迴圈,程式結束
pos = line16.pos100() # 更精細的位置:-100 ~ 100
robot.drive(120, pos * 1.5) # 偏多少就轉多少:偏越多轉越快
wait(10)Step by step through the program
- Read the fine line position (-100 to 100): in the official App it is "raw red minus 100".
- Read the line width.
- If line width ≥ 11 (the official App writes it as "line width > 10", same meaning) → crossroads: stop → beep → end the program.
- Else → turn amount = line position × a number (0.5 in the official App, 1.5 in the other two). The farther off the line, the harder it turns; slightly off means only a slight turn, so it doesn't wobble left and right.
What you should see: the car glides smoothly along the line, drives through T-junctions without stopping, and stops with a beep at the crossroads.
Common problems
- Still wobbling left and right? → Make "the multiplier" a bit smaller (for example 0.5 → 0.3).
- Can't keep up on turns and runs off the line? → Make "the multiplier" a bit bigger, or lower the speed.
- Didn't stop at the crossroads? → The car is too fast and shot straight through it; lower the speed, or change "11" to 10.
- Stopped at a T-junction before reaching the crossroads? → The line is too wide; change "11" to 12 or 13 (the maximum is 16).
- The official App version never stops at the crossroads? → In our 2026-10 hub tests, the Education SPIKE App 3 dashboard showed NaN for the "color" block once the width went above 10, and we are still re-testing the 16-way crossroads stop in blocks (on the same hub, the official App's Python reads width 16 and stops). In the home edition (Robot Inventor) "color" tops out at 10, so always use "> 9" there. Swap example 1's "reflected light minus 16" for "color", put the sensor on the crossroads and watch the number on the display; if it only goes up to 10, change example 5's "> 10" to "> 9".
- Can't find the "raw red" block in the official App? → Opening the file should automatically add the "More Sensors" block category; if it is missing, click the "+" at the bottom-left of the block palette and add "More Sensors".
More questions
- The hub does not detect the sensor? Unplug and replug, wait 2 s, try another port. The official app shows it as a colour sensor — that is expected.
- Can I use the official app and Pybricks on the same hub? One firmware at a time: Pybricks and the MBC Blocks website need Pybricks firmware, the official app needs LEGO firmware; you can switch back any time with the SPIKE app.
Still stuck? Contact us with your software and a screenshot of the program.
Downloads
- Pybricks library MBC_line16_obj_Lib.py
- Shared library (for new projects, one file for every MBC sensor) MBC_sensors.py
- line16_pybricks_spike_v4.x.zip
- Download centre (libraries, firmware, catalogue for every product)
Sources
- Host and software list:
_build/guides.json - Values and commands:
_build/protocol/line16.json(single source of truth) - Setup:
_build/guides/software/spike-pybricks.en.md、_build/guides/product/line16/spike-pybricks.en.md - Examples:
sensors/line16/spike-examples.md