Pathfinder 8-Way (Line8) × micro:bit × MakeCode

Interactive tutorialmicro:bit

First time? Try the interactive tutorial in your browser first, then follow the steps below with your hub.

This page covers MakeCode (blocks / JavaScript) only: how to set it up for the Pathfinder 8-Way (Line8) on BBC micro:bit V1 / V2, how to read every value, the commands it supports and examples. Use the switcher above to change product, host or software.

Which edition and how to connect

Get the Universal I2C edition and connect it to the I2C header of a micro:bit breakout (SDA = P20, SCL = P19), factory address 0x16. It draws little current, so VCC can go to the micro:bit 3V pin (about 90 mA on V1, 190 mA on V2); on batteries the rail can sag below 3 V, so switch to external 5 V if readings are unstable (share GND with the micro:bit).

Pathfinder 8-Way (Line8) × BBC micro:bit V1 / V2

Setup

What you need

  • Sensor: Pathfinder 8-Way (Line8) (Universal I2C edition). SPIKE and EV3 editions run different firmware and cannot be read by a micro:bit.
  • Board: BBC micro:bit V1 or V2, plus a breakout board or crocodile-clip adapter to reach P19 / P20.
  • Software: MakeCode for micro:bit in the browser (makecode.microbit.org, blocks / JavaScript); the offline app (Windows / Mac) also works, but adding an extension the first time needs internet.
  • Extension: pxt-mbc-sensors v2 (MIT, nothing to download, just paste the URL).

Important

Bench test pending: extension v2.0.5 compiles in MakeCode (V1 + V2) but has not yet been checked block by block on real hardware. Block names follow the GitHub release.

Install the software

  1. Open makecode.microbit.org → New Project.
  2. Traditional Chinese block names are included: switch the editor language (gearwheel → Language) if you prefer.

Make the software see the sensor

  1. Click Advanced → Extensions at the bottom of the toolbox (also in the gearwheel menu).
  2. Paste https://github.com/contactmbcrobot/pxt-mbc-sensors into the search box and press Enter.
  3. Click the mbc-sensors card. An "MBC Sensors" category appears with six groups: Line, Distance, Gamepad, IMU, Camera and Device.
  4. To use an example: switch to JavaScript at the top right → paste the example → download the .hex and drag it onto the MICROBIT drive.

Tip

No approval, no download: teachers just hand the URL to students. To pin a version, paste https://github.com/contactmbcrobot/pxt-mbc-sensors#v2.0.5.

Wiring

The micro:bit I2C pins are fixed: P19 = SCL, P20 = SDA (on the edge connector, so you need a breakout board or clip adapter). On the MBC 4-pin sensor cable TX goes to SCL and RX to SDA, exactly as when wiring to an Arduino.

Sensor micro:bit Note
SCL (TX) P19 I2C clock
SDA (RX) P20 I2C data
VCC 3V or external 5 V power-hungry products must not draw from the micro:bit 3V pin
GND GND with external power, share GND with the micro:bit
  • 3V pin current: V1 about 90 mA, V2 about 190 mA; on batteries the rail can drop to just over 2 V. Ranger 8-way, Controller PS4 / PS5 and the SharpShot must use external 5 V; other products can try 3V first, with external 5 V recommended on batteries.
  • Logic level: MBC product I2C lines are 3.3 V, compatible with the micro:bit without a level shifter.
  • This product's factory address is 0x16; products with different addresses can share the same P19 / P20 bus.

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

  1. Plug in the sensor and turn the hub on (the sensor lights come on).
  2. Hold the calibration button for about 0.5 s, then release it (do not keep holding it; holding it keeps restarting calibration).
  3. 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 8 sensing points must pass over the darkest black line and the brightest white area.
  4. Keep the sensor 3~12 mm above the surface while sweeping; higher reads poorly.
  5. 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 2~3.

Tip

You can also calibrate with a block: "line follower calibrate …" with "as black" (E2), "as white" (E3) or "5-second sweep" (E7); the result is saved in the sensor.

First test

  1. Check the wiring first with the "Device → wait for device at 0x16 ready" or "device at 0x16 is ready" block.
  2. Download the .hex of example 1 on this page to the micro:bit.
  3. If the number on the LEDs changes with what is in front of the sensor, everything is connected.

FAQ

  • Values are always 0? With no device connected the reporter blocks return 0 (the internal −32768 is filtered), every yes / no block returns false, and nothing throws. Check in order: wiring (SDA = P20, SCL = P19) → external power on → "device at … is ready" is true.
  • On a V1, the compass and accelerometer stop working too? On V1 the external I2C bus is shared with the onboard accelerometer, so a sensor holding SDA low also kills them. V2 has a separate external bus.
  • Works on and off? STM32 products (Pathfinder, Ranger, PS2, IMU) clock-stretch, and the nRF52 on the V2 has a known clock-stretching issue (errata 219), so V1 and V2 both need a bench test. ESP32 products (PS4, SharpShot) do not stretch; the extension already waits 2 ms after writing the value id.
  • Two products of the same kind? They share an address: first change one with "Device → change device at … to address …" (applied after reboot), then tell the extension with that product's "use address" block.

Reading values

After adding pxt-mbc-sensors, find the blocks in the MBC Sensors category; the JavaScript form is shown too, address 0x16.

line position linePos

line position
Range Meaning
-8~+8 which channel the line is under: −8 (far left) to +8 (far right), 0 = centre; seen from behind the robot, negative = left
(line position :: #00A4E4)
let linePos = mbc.linePosition()   // line position:-8~+8

Returns 0 when the sensor does not answer; check with "device at … is ready" first.

fine position linePosFine

fine position
Range Meaning
-100~+100 sub-channel position: −100 to +100, 0 = centre, negative = left (same sign as line position)
(fine line position :: #00A4E4)
let linePosFine = mbc.linePositionFine()   // fine position:-100~+100

Returns 0 when the sensor does not answer; check with "device at … is ready" first.

line width lineWidth

line width
Range Meaning
0~8 路 number of channels on the line, 0~8; 0 = no line
(line width :: #00A4E4)
let lineWidth = mbc.lineWidth()   // line width:0~8 路

Returns 0 when the sensor does not answer; check with "device at … is ready" first.

line segments groupCount

line segments
Range Meaning
0~4 number of separate line segments: 0 = lost, 1 = one line, ≥2 = fork / crossing
(line segments :: #00A4E4)
let groupCount = mbc.lineSegments()   // line segments:0~4

Related block "line is seen"

let lineSeen = mbc.lineSeen()

Related block "on line [lost]"

mbc.onLineEvent(MbcLineEvent.Lost, function () {
})

Returns 0 when the sensor does not answer; check with "device at … is ready" first.

line bitmap bitmap

line bitmap
Range Meaning
0~255 bit bit0 = channel 1 (left) … bit7 = channel 8; 1 = that channel sees the line
(line bitmap :: #00A4E4)
let bitmap = mbc.lineBitmap()   // line bitmap:0~255 bit

Related block "channel 1 is on the line"

let lineChannelOnLine = mbc.lineChannelOnLine(1)

Returns 0 when the sensor does not answer; check with "device at … is ready" first.

last exit direction lastDir

Range Meaning
-8~+8 where the line was before it was lost: −8 to +8 (negative = left); keeps the last value
(last exit direction :: #00A4E4)
let lastDir = mbc.lineLastDirection()   // last exit direction:-8~+8

Returns 0 when the sensor does not answer; check with "device at … is ready" first.

threshold threshold

Range Meaning
1~99 reflect threshold for 'on line', 1~99, default 50; set with E4
(line threshold :: #00A4E4)
let threshold = mbc.lineThreshold()   // threshold:1~99

Returns 0 when the sensor does not answer; check with "device at … is ready" first.

calibrations done calibCount

Range Meaning
0~255 次 number of calibrations completed since power-on (button calibrations included)
(line calibration count :: #00A4E4)
let calibCount = mbc.lineCalibrationCount()   // calibrations done:0~255 次

Related block "line follower is calibrating"

let lineCalibrating = mbc.lineCalibrating()

Returns 0 when the sensor does not answer; check with "device at … is ready" first.

channel 1~8 reflect light

channel 1~8 reflect
Range Meaning
0~100 calibrated reflect of channel 18, 0100 (channel 1 = left); ids 10~17
(channel (1) reflect :: #00A4E4)
let light = mbc.lineChannel(1)   // channel 1~8 reflect:0~100 (N = 1~8)

Returns 0 when the sensor does not answer; check with "device at … is ready" first.

Commands

black line mode blackLine

follow a black line (default); RAM only, back to the default after reset

line follower follow [black v] line :: #00A4E4
mbc.lineSetColor(MbcLineColor.Black)   // black line mode

white line mode whiteLine

follow a white line (F3 b1 = 1); RAM only, back to the default after reset

line follower follow [white v] line :: #00A4E4
mbc.lineSetColor(MbcLineColor.White)   // white line mode

calibrate black calibBlack

store the current reading as black (sensor over the black line), ~50 ms; saved to EEPROM

line follower calibrate [current reading is black v] :: #00A4E4
mbc.lineCalibrate(MbcLineCalib.Black)   // calibrate black

calibrate white calibWhite

store the current reading as white (sensor over the white surface), ~50 ms; saved to EEPROM

line follower calibrate [current reading is white v] :: #00A4E4
mbc.lineCalibrate(MbcLineCalib.White)   // calibrate white

set threshold setThreshold

threshold 1~99 (RAM only, back to 50 after reboot); on SPIKE an out-of-range value returns failed (state 2)

line follower set threshold (50) :: #00A4E4
mbc.lineSetThreshold(50)   // set threshold

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

line follower calibrate [auto \(sweep for 5 s\) v] :: #00A4E4
mbc.lineCalibrate(MbcLineCalib.Auto)   // 5-second auto calibration

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)

No dedicated block; in JavaScript use mbc.writeCmd(0x16, 0xE9, [params]).

Examples

This page is for people who have never written code. The 5 examples start with "show a value on the LEDs" and build up to "drive a car". Everything is written for the MakeCode web editor with the MBC extension pxt-mbc-sensors v2: switch to JavaScript, paste an example and it runs; switch back to Blocks to see which blocks it becomes.

Important

  • Extension pxt-mbc-sensors v2 has not been verified on hardware yet. Every example on this page compiles with the MakeCode CLI (once for V1, once for V2); value directions, ready times and power are still to be confirmed on a real board, see "to be tested" in the hardware checklist below.
  • Use the Universal I2C edition of the product. The SPIKE and EV3 editions have different firmware and cannot be read by a micro:bit.

Software

Editor micro:bit V1 micro:bit V2 How
MakeCode web editor (makecode.microbit.org, Blocks / JavaScript) ✔ (less memory, but every example here fits) ✔ New Project → gear → Extensions → paste https://github.com/contactmbcrobot/pxt-mbc-sensors → switch to JavaScript (top) → paste the example → Download the .hex and drag it onto the MICROBIT drive
MakeCode offline app (Windows / Mac) ✔ ✔ Same; adding the extension the first time needs internet
Python Editor (MicroPython) — — no MBC module yet
  • Sound: V2 has a built-in speaker; a V1 needs a buzzer or headphones on P0.
  • Servos / motors: the examples use pins.servoWritePin: one 9 g servo (SG90) on P1; for driving, two continuous-rotation servos (e.g. FS90R), left wheel P1, right wheel P2. Always power servos from an external 4.8–6 V battery pack whose − is joined to the micro:bit GND (common ground); never drive servos from the micro:bit 3V pin.
  • Block names: with the editor language set to English the blocks read as in the tables below; the tables also give the JavaScript call.

Before you start: hardware checklist (to be tested)

The micro:bit is a 3.3 V board. Each check must pass before an MBC product is connected; "to be tested" marks items extension v2 has not yet confirmed on real hardware.

Check This product Status
Voltage MBC products use 3.3 V I2C levels: connect straight to P19 / P20, no level shifter ✔
Power about 30 mA, so the micro:bit 3V pin (limit about 90 mA on V1, 190 mA on V2) may do; on batteries the rail can sag below 3 V, use external 5 V if readings are unstable to be tested
I2C pull-ups the micro:bit already has 4.7 kΩ; if the product also has pull-ups, check the low level stays ≤ 0.3×VDD to be tested
Clock stretching the STM32 slave stretches the clock by default; the V2 (nRF52833) has a known issue with stretching, test on V1 and V2 to be tested
Address clash factory address 0x16; to use two line followers change one with the "change device address" block (Device group) —
Ready time ready immediately after power-on (F3 b0 is always 1) to be tested

If you read nothing: check the wiring (SDA = P20, SCL = P19) → the external power → whether the "device at … is ready" block (Device group, advanced) returns true.

Wiring (the same for every example)

Line Follower 8-ch × micro:bit
Sensor micro:bit
SDA P20
SCL P19
VCC 3V (or external 5 V)
GND GND (the servo battery − goes here too)

The values used on this page

"Left / right" always means sensor mounted on the robot, you standing behind it looking forward: line on the left = negative, channel 1 = far left.

Block JavaScript Range Meaning Used in
line position mbc.linePosition() −8~+8 which channel the line is under, 0 = centre, negative = left 1, 3, 4
fine line position mbc.linePositionFine() −100~+100 finer version of the position, same sign 5
line is seen mbc.lineSeen() true / false at least one channel sees the line 1, 3, 4, 5
line width mbc.lineWidth() 0~8 channels on the line, 0 = no line 1
last exit direction mbc.lineLastDirection() −8~+8 where the line was before it was lost, negative = left 5
on line lost / found / fork mbc.onLineEvent(...) 事件 fires once when the segment count becomes 0 / >0 again / ≥2 2, 5
calibrate auto (sweep for 5 s) mbc.lineCalibrate(MbcLineCalib.Auto) 命令 E7 sweep over black line and white surface for 5 s, saved in the sensor 2
is calibrating / calibration count mbc.lineCalibrating() / mbc.lineCalibrationCount() true / false、0~255 calibrating flag (F3 b2) and calibrations since power-on 2
follow black / white line mbc.lineSetColor(MbcLineColor.Black) 命令 E0/E1 black line on white (default) or white line on black 2
wait for device at … ready mbc.waitReady(mbc.ADDR_LINE, 3000) true / false waits up to 3 s, true when the sensor answers (Advanced → Device) 1

Example 1: Where is the line

What this example does: A column of LEDs follows the line: line on the left, column on the left; line centred, column in the middle. Press A to show the line position number, B the line width. Use it to check the wiring and that left / right are not swapped.

What you need

  • micro:bit V1 or V2 + USB cable
  • Line Follower 8-ch (Universal I2C edition) + 4-pin cable
  • a sheet of white paper with a strip of black electrical tape (about 2 cm wide)

Wiring: As in the wiring table above: SDA → P20, SCL → P19, VCC → 3V, GND → GND.

Download: ex01_show_position.ts (right-click, save) → MakeCode New Project → gear → Extensions → paste https://github.com/contactmbcrobot/pxt-mbc-sensors → switch to JavaScript (top) → select all, delete, paste the file → Download and drag the .hex onto the MICROBIT drive.

Code

// Line follower 8-ch × micro:bit example 1: where is the line (an LED column follows the line)
// What it does: slide the sensor across a black line and a column of LEDs follows it; press A to show the line position number, B to show the line width.
// Wiring: sensor SDA → P20, SCL → P19, VCC → 3V (or external 5 V), GND → GND.
// Setup: MakeCode new project → Extensions → paste https://github.com/contactmbcrobot/pxt-mbc-sensors → switch to JavaScript, paste this whole file → download to the micro:bit.
// You see: a tick at power-on (sensor found); line on the left → column on the left; centred → middle column; no line → an X.

const CH = 8                                              // channels: 8 on the 8-ch, 16 on the 16-ch
let busy = false                                          // pause the column while a button shows a number

if (mbc.waitReady(mbc.ADDR_LINE, 3000)) {                 // wait up to 3 s for the sensor to answer
    basic.showIcon(IconNames.Yes)
} else {
    basic.showIcon(IconNames.No)                          // no answer: check P19 / P20 and power
}

basic.forever(function () {
    if (busy) {
        basic.pause(100)
        return
    }
    if (mbc.lineSeen()) {
        let pos = mbc.linePosition()                      // −CH (far left) .. +CH (far right), 0 = centre
        let x = Math.round(Math.map(pos, -CH, CH, 0, 4))  // map to LED column 0..4
        basic.clearScreen()
        for (let y = 0; y < 5; y++) {
            led.plot(x, y)                                // draw the whole column
        }
    } else {
        basic.showIcon(IconNames.No, 0)                   // no line seen
    }
    basic.pause(50)
})

input.onButtonPressed(Button.A, function () {
    busy = true
    basic.showNumber(mbc.linePosition())                  // line position: negative = line on the left (seen from behind the robot)
    busy = false
})
input.onButtonPressed(Button.B, function () {
    busy = true
    basic.showNumber(mbc.lineWidth())                     // line width: channels on the line, 0 = no line
    busy = false
})

Step by step

  1. const CH = 8: the channel count. On the 16-ch change 8 to 16, nothing else.
  2. mbc.waitReady(mbc.ADDR_LINE, 3000): wait up to 3 s for the sensor to answer; tick if it does, X if not.
  3. basic.forever: repeat forever. First ask mbc.lineSeen() "is a line seen"; if not, show an X.
  4. Line seen: mbc.linePosition() reads −8..+8, Math.map turns it into LED column 0..4, and a loop of led.plot lights all 5 LEDs of that column.
  5. Buttons A / B: basic.showNumber scrolls the number; the busy variable pauses the column meanwhile so the two do not fight over the screen.

What you should see

  • Power-on: a tick (sensor found).
  • Sensor on the paper with the line under its centre: middle column; A shows 0.
  • Move the sensor right (the line is now on its left): the column moves left; A shows a negative number.
  • Lift it off the paper: X.

FAQ

  • The column moves the opposite way to the line? → The sensor is mounted backwards (channel 1 must be on the left of the robot). Turn the sensor 180°, do not change the code.
  • The column never leaves the middle? → Not calibrated yet: run example 2 and press A for auto calibration, or every value is 0 (check whether example 1 showed the X).
  • An X right at power-on (sensor not found)? → Check SDA = P20 and SCL = P19 are not swapped, that the external power is on, and with crocodile clips that no clip touches the neighbouring pad.
  • All zeros in the simulator? → Normal. The MakeCode simulator has no real I2C, every read is 0; download to the micro:bit to see real values.
  • No sound on a V1? → The V1 has no speaker; connect a buzzer or headphones between P0 and GND.

Example 2: Lost-line alarm + one-key calibration

What this example does: Line seen → happy face; line gone → sad face and a beep. Press A for the 5-second auto calibration (LED counts 5 4 3 2 1), B to switch black / white line mode. It uses the "event" blocks: they fire the moment the line is lost, instead of asking all the time.

What you need

  • Everything from example 1
  • On a V1 also a buzzer or headphones on P0

Wiring: Same as example 1.

Download: ex02_line_alarm.ts (right-click, save) → MakeCode New Project → gear → Extensions → paste https://github.com/contactmbcrobot/pxt-mbc-sensors → switch to JavaScript (top) → select all, delete, paste the file → Download and drag the .hex onto the MICROBIT drive.

Code

// Line follower 8-ch × micro:bit example 2: lost-line alarm + one-key calibration
// What it does: line seen → happy face; line lost → sad face and a beep. Press A for the 5-second auto calibration (LED countdown), B to switch black / white line.
// Wiring: sensor SDA → P20, SCL → P19, VCC → 3V (or external 5 V), GND → GND. On a V1 connect a buzzer or headphones to P0 for sound (V2 has a speaker).
// Setup: a sheet of white paper with a strip of black tape. Extension and paste steps as in example 1.
// You see: sensor over the line → happy; lifted away → sad + beep; press A → 5 4 3 2 1, the calibration count, then a tick.

let white = false                                         // white-line mode on?

mbc.lineSetColor(MbcLineColor.Black)                      // start in black-line mode (the default)
basic.showIcon(IconNames.Happy)

mbc.onLineEvent(MbcLineEvent.Lost, function () {          // fires once when the segment count drops to 0
    basic.showIcon(IconNames.Sad, 0)
    music.playTone(Note.C, music.beat(BeatFraction.Half)) // low C for half a beat
})
mbc.onLineEvent(MbcLineEvent.Found, function () {         // line found again
    basic.showIcon(IconNames.Happy, 0)
})

input.onButtonPressed(Button.A, function () {
    mbc.lineCalibrate(MbcLineCalib.Auto)                  // sweep the sensor over black line and white paper for 5 s
    for (let i = 5; i > 0; i--) {
        basic.showNumber(i)                               // countdown
        basic.pause(1000)
    }
    while (mbc.lineCalibrating()) {                       // wait until the calibrating flag clears
        basic.pause(100)
    }
    basic.showNumber(mbc.lineCalibrationCount())          // calibrations done since power-on
    basic.showIcon(IconNames.Yes)
})

input.onButtonPressed(Button.B, function () {
    white = !white
    mbc.lineSetColor(white ? MbcLineColor.White : MbcLineColor.Black)   // black line on white ↔ white line on black
    basic.showString(white ? "W" : "B")
})

Step by step

  1. mbc.lineSetColor(MbcLineColor.Black): start in black-line mode (command E0).
  2. mbc.onLineEvent(MbcLineEvent.Lost, ...): runs once when the segment count drops from >0 to 0 → sad face + music.playTone for half a beat. Found is the line coming back → happy face.
  3. Button A: mbc.lineCalibrate(MbcLineCalib.Auto) sends command E7 and the sensor starts its 5-second calibration; a for loop counts down meanwhile.
  4. while (mbc.lineCalibrating()): while the calibrating flag (F3 b2) is still 1 wait another 0.1 s; continue when it clears.
  5. mbc.lineCalibrationCount(): shows how many calibrations finished since power-on, proving it really ran.
  6. Button B: the white variable flips and lineSetColor switches between black (E0) and white (E1); the screen shows B or W.

What you should see

  • Sensor over the line: happy. Lifted away: sad + one beep (only once, not continuous).
  • Press A and sweep the sensor over black line and white paper a few times within 5 s → the count (1 the first time) → a tick. Positions are more accurate afterwards.
  • Press B, W shows: now only a white line on black is treated as a line.

FAQ

  • The count never shows after A? → The calibrating flag never clears, usually because the sensor is not connected (with all-zero reads lineCalibrating() is false and it skips straight on). Run example 1 first and look for the tick.
  • No sound when the line is lost? → A V1 needs a buzzer; on a V2 make sure the speaker is not turned off in MakeCode.

Example 3: A steering servo follows the line

What this example does: A 9 g servo acts as a steering wheel: line on the left, the servo turns left, the further off-centre the bigger the turn (80° at the outermost channel); line lost, back to centre. The first example where the sensor moves something. Press A to show the angle.

What you need

  • Everything from example 1, calibrated once with example 2
  • one 9 g servo (SG90)
  • a 4.8–6 V battery pack (4 × AA) and jumper wires

Wiring: Sensor as in example 1. Servo orange (signal) → P1, red → external 4.8–6 V battery +, brown → battery −, and run a second wire from battery − to micro:bit GND (common ground).

Download: ex03_steer_servo.ts (right-click, save) → MakeCode New Project → gear → Extensions → paste https://github.com/contactmbcrobot/pxt-mbc-sensors → switch to JavaScript (top) → select all, delete, paste the file → Download and drag the .hex onto the MICROBIT drive.

Code

// Line follower 8-ch × micro:bit example 3: a steering servo follows the line
// What it does: a 9 g servo (SG90) acts as a steering wheel: line on the left → servo turns left, further off-centre → bigger turn; line lost → back to centre. Press A to show the angle.
// Wiring: sensor SDA → P20, SCL → P19, VCC → 3V, GND → GND. Servo orange (signal) → P1, red → external 4.8–6 V battery +, brown → battery − joined to micro:bit GND (common ground).
// Setup: the black-line sheet from example 2, calibrated once.
// You see: move the sensor so the line is on its left → the servo arm swings one way; line on the right → the other way; lift it away → back to 90°.

const CH = 8                                              // channels: 8 on the 8-ch, 16 on the 16-ch
const GAIN = Math.idiv(80, CH)                            // degrees per channel: the outermost channel gives ±80°
let angle = 90

basic.forever(function () {
    if (mbc.lineSeen()) {
        angle = 90 + mbc.linePosition() * GAIN            // line on the left (negative) → angle below 90
    } else {
        angle = 90                                        // no line: centre
    }
    angle = Math.constrain(angle, 0, 180)
    pins.servoWritePin(AnalogPin.P1, angle)               // 0..180°
    basic.pause(30)
})

input.onButtonPressed(Button.A, function () {
    basic.showNumber(angle)
})

Step by step

  1. GAIN = Math.idiv(80, CH): degrees per channel. 10° on the 8-ch, 5° on the 16-ch, so the outermost channel is 80° either way.
  2. Line seen: angle = 90 + position × GAIN; line on the left (negative) gives less than 90, on the right more than 90.
  3. No line: angle = 90, centre.
  4. Math.constrain(angle, 0, 180): a servo only goes 0..180°, clamp anything beyond.
  5. pins.servoWritePin(AnalogPin.P1, angle): send the angle to the servo on P1, every 30 ms.

What you should see

  • Line centred: servo at 90°.
  • Move the sensor right (line on its left): the arm swings one way, more the further off-centre.
  • Lift it off: back to 90°. A shows 90.

FAQ

  • The servo jitters or the micro:bit keeps resetting? → Never power the servo from the micro:bit 3V pin: use the battery pack, with battery − joined to micro:bit GND.
  • It turns the wrong way? → Change 90 + ... to 90 - ..., it depends on how the servo is mounted.

Example 4: Line-following car (three-way)

What this example does: Press A to drive: line on the left → turn left, on the right → turn right, centred → straight; line lost → stop. Press B to stop. Only three actions, the easiest line follower to understand.

What you need

  • A two-wheel car: two continuous-rotation servos (e.g. FS90R) as wheels plus a caster
  • the line follower at the front, 0.5–1 cm above the floor
  • a 4.8–6 V battery pack
  • a black-line track (white paper + black tape, no sharp bends)

Wiring: Sensor as in example 1, mounted at the front 0.5–1 cm above the floor. Left continuous-rotation servo signal → P1, right → P2; both servo red wires to the 4.8–6 V battery pack +, brown to battery −, and battery − also to micro:bit GND.

Download: ex04_simple_follow.ts (right-click, save) → MakeCode New Project → gear → Extensions → paste https://github.com/contactmbcrobot/pxt-mbc-sensors → switch to JavaScript (top) → select all, delete, paste the file → Download and drag the .hex onto the MICROBIT drive.

Code

// Line follower 8-ch × micro:bit example 4: line-following car (three-way)
// What it does: press A to drive: line on the left → turn left, on the right → turn right, centred → straight; line lost → stop. Press B to stop.
// Wiring: sensor SDA → P20, SCL → P19, VCC → 3V, GND → GND. Left continuous-rotation servo signal → P1, right → P2; both servos powered from a 4.8–6 V battery pack whose − is joined to micro:bit GND.
// Setup: a two-wheel car (two continuous-rotation servos as wheels), the sensor at the front 0.5–1 cm above the floor, a black-line track.
// You see: after A the arrow points up and the car follows the line; the arrow points the way it turns; off the line → sad face, stop.

let running = false

// left servo P1, right servo P2 (continuous rotation); left / right = −100..100, positive = forward
function drive(left: number, right: number): void {
    left = Math.constrain(left, -100, 100)
    right = Math.constrain(right, -100, 100)
    pins.servoWritePin(AnalogPin.P1, Math.round(90 + left * 0.9))    // 90 = stop, 180 = full forward, 0 = full reverse
    pins.servoWritePin(AnalogPin.P2, Math.round(90 - right * 0.9))   // the right servo is mounted mirrored, so subtract
}

input.onButtonPressed(Button.A, function () {
    running = true
    basic.showArrow(ArrowNames.North, 0)
})
input.onButtonPressed(Button.B, function () {
    running = false
    drive(0, 0)
    basic.showIcon(IconNames.No, 0)
})

basic.forever(function () {
    if (!running) {
        basic.pause(100)
        return
    }
    if (!mbc.lineSeen()) {
        drive(0, 0)                                       // stop when the line is lost
        basic.showIcon(IconNames.Sad, 0)
    } else {
        let pos = mbc.linePosition()                      // negative = line on the left
        if (pos < 0) {
            drive(20, 60)                                 // left slow, right fast = turn left
            basic.showArrow(ArrowNames.West, 0)
        } else if (pos > 0) {
            drive(60, 20)                                 // turn right
            basic.showArrow(ArrowNames.East, 0)
        } else {
            drive(50, 50)                                 // straight
            basic.showArrow(ArrowNames.North, 0)
        }
    }
    basic.pause(20)
})

Step by step

  1. function drive(left, right): every driving example on this page uses it. Numbers −100..100, positive = forward. Left wheel 90 + left × 0.9 (90 stop, 180 full forward); the right servo is mounted mirrored, so 90 − right × 0.9.
  2. Button A: running = true; button B: running = false and drive(0, 0).
  3. basic.forever: when not running do nothing (return).
  4. !mbc.lineSeen(): line lost → stop and sad face, so the car does not run off the track.
  5. pos < 0 (line on the left) → drive(20, 60): left slow, right fast = turn left. pos > 0 the other way. pos == 0 → both wheels equal, straight.

What you should see

  • Press A: up arrow, the car follows the line; in bends the arrow points the way it turns.
  • Lift the car: sad face, both wheels stop.
  • Press B: X, stop.

FAQ

  • The car reverses after A? → Both servos are mounted the other way: in drive change + left to - left and - right to + right. Only one wheel wrong → change only that line.
  • The car wiggles along the line? → Change 60 / 20 to 50 / 30 (gentler turns), or use the proportional control of example 5.
  • It never fully stops, the wheels creep? → "Stop" on a continuous-rotation servo is not always exactly 90: adjust the trim screw on the servo, or try 88..92 instead of 90.

Example 5: Smooth line following + stop at the 3rd crossing (all together)

What this example does: Proportional control on the fine position: the correction is as big as the error, so the car does not wiggle; crossings (fork event) are counted and the car stops at the 3rd; when the line is lost the car spins back using the last exit direction. Everything learned so far, reading values, events and driving, in one program.

What you need

  • The car from example 4
  • a black-line track with 3 crossings

Wiring: Same as example 4.

Download: ex05_smooth_follow_stop.ts (right-click, save) → MakeCode New Project → gear → Extensions → paste https://github.com/contactmbcrobot/pxt-mbc-sensors → switch to JavaScript (top) → select all, delete, paste the file → Download and drag the .hex onto the MICROBIT drive.

Code

// Line follower 8-ch × micro:bit example 5: smooth line following + stop at the 3rd crossing (all together)
// What it does: proportional control on the fine position, so the car steers smoothly; counts crossings and stops at the 3rd; when the line is lost it spins back using the last exit direction. A = start, B = stop.
// Wiring: as example 4 (sensor P19 / P20, left wheel P1, right wheel P2, servos on their own battery, common ground).
// Setup: a black-line track with 3 crossings.
// You see: after A the car follows smoothly, the LEDs show 1, 2, 3 at each crossing and the car stops with a tick at 3; lift the track away and the car turns toward where the line went, then carries on when it finds it.

const SPEED = 50
const KP = 0.5                                            // fine position ±100 → correction ±50
let running = false
let crossings = 0

// left servo P1, right servo P2 (continuous rotation); left / right = −100..100, positive = forward
function drive(left: number, right: number): void {
    left = Math.constrain(left, -100, 100)
    right = Math.constrain(right, -100, 100)
    pins.servoWritePin(AnalogPin.P1, Math.round(90 + left * 0.9))    // 90 = stop, 180 = full forward, 0 = full reverse
    pins.servoWritePin(AnalogPin.P2, Math.round(90 - right * 0.9))   // the right servo is mounted mirrored, so subtract
}

mbc.onLineEvent(MbcLineEvent.Fork, function () {          // segment count ≥ 2: crossing or fork
    if (!running) {
        return
    }
    crossings += 1
    basic.showNumber(crossings)
    if (crossings >= 3) {
        running = false
        drive(0, 0)
        basic.showIcon(IconNames.Yes)
    }
})
mbc.onLineEvent(MbcLineEvent.Lost, function () {
    if (!running) {
        return
    }
    if (mbc.lineLastDirection() < 0) {                    // the line left on the left → spin left to find it
        drive(-30, 30)
    } else {
        drive(30, -30)
    }
    basic.showIcon(IconNames.Confused, 0)
})

input.onButtonPressed(Button.A, function () {
    crossings = 0
    running = true
    basic.showArrow(ArrowNames.North, 0)
})
input.onButtonPressed(Button.B, function () {
    running = false
    drive(0, 0)
    basic.showIcon(IconNames.No, 0)
})

basic.forever(function () {
    if (running && mbc.lineSeen()) {
        let err = mbc.linePositionFine()                  // −100..+100, negative = line on the left
        let turn = err * KP                               // line on the left → negative turn → left wheel slows = turn left
        drive(SPEED + turn, SPEED - turn)
    }
    basic.pause(20)
})

Step by step

  1. KP = 0.5: fine position −100..+100 × 0.5 = correction −50..+50. Bigger = sharper steering.
  2. basic.forever: while running and the line is seen, err = fine position, turn = err × KP, drive(SPEED + turn, SPEED − turn). Line on the left (negative) → left wheel slows, right speeds up = smooth left turn.
  3. onLineEvent(Fork): segment count ≥ 2 (a crossing) → crossings + 1 shown on the LEDs; at 3 running = false, stop, tick.
  4. onLineEvent(Lost): mbc.lineLastDirection() < 0 means the line left on the left → drive(−30, 30) spin left; otherwise spin right. The forever loop leaves the motors alone while no line is seen, so it keeps spinning until the line is back.
  5. A: reset the count and start; B: stop.

What you should see

  • Press A: the car follows smoothly, steadier than example 4.
  • Each crossing: the LEDs show 1, 2, 3; at 3 the car stops with a tick.
  • Pull the track away from under the car: puzzled face, the car spins toward the side the line went; put it back and it carries on.

FAQ

  • One crossing counted twice? → Too slow: the segment count bounced twice while crossing. Raise SPEED, or add basic.pause(500) inside the Fork handler so one crossing is not counted again.
  • The car still wiggles? → Lower KP from 0.5 to 0.3; if it still does, change basic.pause(20) to 10 (faster updates).
  • It spins forever looking for the line? → The line is too far away. It only spins, it does not move forward; to search in an arc change the Lost handler to something like drive(10, 40).

Next steps

More questions

Still stuck? Contact us with your software and a screenshot of the program.

Downloads

Sources

  • Host and software list:_build/guides.json
  • Values and commands:_build/protocol/line8.json (single source of truth)
  • Setup:_build/guides/software/microbit-makecode.en.md、_build/guides/product/line8/microbit-makecode.en.md
  • Examples:sensors/line8/microbit-examples.md
Email for a quoteBuying Guide