IMU 9-Axis Gyro × 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 IMU 9-Axis Gyro 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 0x6A. 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).

IMU 9-Axis Gyro × BBC micro:bit V1 / V2

Setup

What you need

  • Sensor: IMU 9-Axis Gyro (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 0x6A; products with different addresses can share the same P19 / P20 bus.

First test

  1. Check the wiring first with the "Device → wait for device at 0x6A ready" or "device at 0x6A 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 0x6A.

gyro 3D heading headingGyro3D

Range Meaning
-32768~32767 ° heading from the 3D quaternion, degrees, 0 at boot/zero, accumulates past 360 (wraps at ±32767, ~91 turns)
(IMU heading [gyro 3D v] \(°\) :: #00A4E4)
let headingGyro3D = mbc.imuHeading(MbcImuHeading.Gyro3D)   // gyro 3D heading:-32768~32767 °

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

fused heading headingFused

Range Meaning
-32768~32767 ° 3D gyro heading slowly corrected by the magnetometer, degrees, relative to the zero point; may exceed 360
(IMU heading [fused v] \(°\) :: #00A4E4)
let headingFused = mbc.imuHeading(MbcImuHeading.Fused)   // fused heading:-32768~32767 °

Related block "on IMU turned by 90 °"

mbc.onImuTurned(90, function () {
})

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

gyro 1D heading headingGyro1D

Range Meaning
-32768~32767 ° heading integrated about the yaw axis only, degrees, 0 at boot/zero; may exceed 360
(IMU heading [gyro 1D v] \(°\) :: #00A4E4)
let headingGyro1D = mbc.imuHeading(MbcImuHeading.Gyro1D)   // gyro 1D heading:-32768~32767 °

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

magnetic heading headingMag

Range Meaning
-32768~32767 ° tilt-compensated magnetometer heading, degrees, relative to the zero point
(IMU heading [magnetic v] \(°\) :: #00A4E4)
let headingMag = mbc.imuHeading(MbcImuHeading.Magnetic)   // magnetic heading:-32768~32767 °

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

yaw rate yawRate

yaw rate
Range Meaning
-32768~32767 0.1 dps angular rate about the yaw axis (getYawAxis()), 0.1 deg/s, same sign as the heading
(IMU turn rate \(0.1 °/s\) :: #00A4E4)
let yawRate = mbc.imuYawRate()   // yaw rate:-32768~32767 0.1 dps

Related block "IMU is [still]"

let imuFlag = mbc.imuFlag(MbcImuFlag.Still)

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

pitch angle pitch

pitch angle
Range Meaning
-90~+90 ° elevation of the board's printed X axis above horizontal: +90 pointing up, −90 pointing down, 0 level (from the gravity vector)
(IMU pitch \(°\) :: #00A4E4)
let pitch = mbc.imuPitch()   // pitch angle:-90~+90 °

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

pitch rate pitchRate

pitch rate
Range Meaning
-32768~32767 0.1 dps angular rate about the board's printed X axis, 0.1 °/s (right-hand rule, CCW seen from +X is positive); this is gyro X, not the derivative of 06
(IMU pitch rate \(0.1 °/s\) :: #00A4E4)
let pitchRate = mbc.imuPitchRate()   // pitch rate:-32768~32767 0.1 dps

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

magnetometer trust magTrust

magnetometer trust
Range Meaning
0~255 how much the fusion trusts the magnetometer, 0255 (= 01 × 255)
(IMU magnetic trust :: #00A4E4)
let magTrust = mbc.imuMagTrust()   // magnetometer trust:0~255

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

magnetic disturbance magDisturb

magnetic disturbance
Range Meaning
0~255 0.01° magnetic disturbance level 0~255 (degrees × 100, capped at 255)
(IMU magnetic disturbance :: #00A4E4)
let magDisturb = mbc.imuMagDisturbance()   // magnetic disturbance:0~255 0.01°

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

face up upFace

face up
Range Meaning
0~5 which axis points up: 0 +X, 1 +Y, 2 +Z, 3 −X, 4 −Y, 5 −Z (physical axes)
(IMU face up :: #00A4E4)
let upFace = mbc.imuFaceUp()   // face up:0~5

Related block "IMU face up is [+Z]"

let imuFaceUpIs = mbc.imuFaceUpIs(MbcImuFace.PlusZ)

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

gyro axis 1~3 gyro

gyro axis 1~3
Range Meaning
-32768~32767 0.1 dps 10 X, 11 Y, 12 Z angular rate, 0.1 deg/s (physical axes, sign-corrected)
(IMU [gyro \(0.1 °/s\) v] axis [X v] :: #00A4E4)
let gyro = [mbc.imuAxis(MbcImuSensor.Gyro, MbcAxis.X), mbc.imuAxis(MbcImuSensor.Gyro, MbcAxis.Y), mbc.imuAxis(MbcImuSensor.Gyro, MbcAxis.Z)]   // gyro axis 1~3:-32768~32767 0.1 dps

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

accel axis 1~3 accel

accel axis 1~3
Range Meaning
-32768~32767 mg 13 X, 14 Y, 15 Z acceleration, mg (1000 = 1 g)
(IMU [accelerometer \(mg\) v] axis [X v] :: #00A4E4)
let accel = [mbc.imuAxis(MbcImuSensor.Accel, MbcAxis.X), mbc.imuAxis(MbcImuSensor.Accel, MbcAxis.Y), mbc.imuAxis(MbcImuSensor.Accel, MbcAxis.Z)]   // accel axis 1~3:-32768~32767 mg

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

mag axis 1~3 mag

mag axis 1~3
Range Meaning
-32768~32767 mG 16 X, 17 Y, 18 Z magnetic field, mG (milligauss)
(IMU [magnetometer \(mG\) v] axis [X v] :: #00A4E4)
let mag = [mbc.imuAxis(MbcImuSensor.Mag, MbcAxis.X), mbc.imuAxis(MbcImuSensor.Mag, MbcAxis.Y), mbc.imuAxis(MbcImuSensor.Mag, MbcAxis.Z)]   // mag axis 1~3:-32768~32767 mG

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

command status cmdStatus

Range Meaning
0~0xEF03 (last command code << 8) | state; state 0 done, 1 busy, 2 failed, 3 rejected (same codes as the SPIKE command reply (mode 8 v3)); reported for E1~EB; undefined codes after EB: ignored on I2C, 0B unchanged (SPIKE replies 3 rejected)

No dedicated block; in JavaScript read it with mbc.readValue(address, id) (−32768 when nothing answers).

let cmdStatus = mbc.readValue(0x6A, 0x0B)   // command status:0~0xEF03

Commands

zero heading zeroHeading

zero all four headings (zeroHeadings()); EV3 also accepts the stock gyro-reset WRITE 0x11

IMU zero heading :: #00A4E4
mbc.imuZeroHeading()   // zero heading

start accelerometer calibration accelCalStart

start accelerometer calibration (= v1 0x20=0x01); F3 b4 = 1 meanwhile; ignored during magnetometer calibration (0B state 3 rejected)

IMU calibration [accelerometer start v] :: #00A4E4
mbc.imuCalibrate(MbcImuCalib.AccelStart)   // start accelerometer calibration

end accelerometer calibration accelCalEnd

finish accelerometer calibration and save to EEPROM (= v1 0x20=0x02); on success the mount axis is re-captured and headings are zeroed once the sensor is level and still (not immediately); rejected if not calibrating (0B state 3); failed check → 0B state 2

IMU calibration [accelerometer end v] :: #00A4E4
mbc.imuCalibrate(MbcImuCalib.AccelEnd)   // end accelerometer calibration

start magnetometer calibration magCalStart

start magnetometer calibration (= v1 0x20=0x03); F3 b5 = 1 meanwhile; ignored if the magnetometer has failed (0B state 3)

IMU calibration [magnetometer start v] :: #00A4E4
mbc.imuCalibrate(MbcImuCalib.MagStart)   // start magnetometer calibration

end magnetometer calibration magCalEnd

finish magnetometer calibration and save to EEPROM (= v1 0x20=0x04); headings zeroed right away on success; rejected if not calibrating (0B state 3); failed check → 0B state 2

IMU calibration [magnetometer end v] :: #00A4E4
mbc.imuCalibrate(MbcImuCalib.MagEnd)   // end magnetometer calibration

set heading setHeading

[angle int16 little-endian]: all four headings set to this angle (RAM); rejected while calibrating (0B state 3)

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

north as zero zeroNorth

gyro heading set to the current magnetic bearing 0~359, then only whole turns are removed (absolute bearing); fusion re-init within ~3 s, timeout = failed; rejected while calibrating (0B state 3)

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

re-estimate gyro bias gyroBiasReset

re-estimates the gyro bias over ~1.5 s of stillness (3 still windows); moved and not done within 4 s = failed; rejected while calibrating (0B state 3)

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

re-capture mount axis recaptureAxis

re-detects the mounting orientation (headings zeroed after capture); RAM; rejected while calibrating (0B state 3)

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

cancel calibration cancelCalib

cancels the running accelerometer / magnetometer calibration without saving (old values restored)

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

heading correction headingCorrection

[uint16 little-endian] actual degrees per turn × 10 (0.1°/turn), 3500~3700; 0 = back to 360.0. Saved to EEPROM 0x60 (written after 3 s, skipped if unchanged), headings zeroed afterwards; missing or out of range → 0B state 2 (SPIKE also 2)

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

calibration factory reset calibFactoryReset

[A5]: accelerometer, magnetometer calibration and heading correction back to factory values, saved immediately; wrong magic → 0B state 2; rejected while calibrating (3)

No dedicated block; in JavaScript use mbc.writeCmd(0x6A, 0xEB, [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 20 mA, so the micro:bit 3V pin (limit about 90 mA on V1, 190 mA on V2) may do 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 0x6A, different from every other MBC product, so it can share the bus with the line follower, ranger and gamepad as it is —
Ready time ready (F3 b0) about 2 s after power-on; keep the board still and flat at power-on and when zeroing, or the heading drifts 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)

IMU Gyro × 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

Headings are in degrees, 0 at power-on or after zeroing, growing when turning right, shrinking when turning left, and they keep accumulating past a full turn (no wrap at 360). The "fused" heading = gyro + magnetometer, the steadiest over time.

Block JavaScript Range Meaning Used in
IMU heading fused (°) mbc.imuHeading(MbcImuHeading.Fused) −32768~32767 current heading, 0 = direction when zeroed, right = positive 1, 3, 4, 5
IMU zero heading mbc.imuZeroHeading() 命令 E0 make the current direction 0 1, 2, 3, 4, 5
IMU pitch (°) mbc.imuPitch() −90~+90 nose up / down in degrees 2
on IMU turned by … ° mbc.onImuTurned(90, ...) 事件 fires each time the fused heading has changed by that many degrees 2
IMU is still mbc.imuFlag(MbcImuFlag.Still) true / false status bit 1: the board is not moving 2
wait for device at … ready mbc.waitReady(mbc.ADDR_IMU, 3000) true / false waits up to 3 s, true once the IMU is initialised (Advanced → Device) 1

Example 1: Electronic compass

What this example does: Turn the micro:bit and the IMU together and the LED arrow turns the other way, always pointing where "front" was at power-on. Press A to show the heading, B to make the current direction 0.

What you need

  • micro:bit V1 or V2 + USB cable
  • IMU Gyro (Universal I2C edition) + 4-pin cable
  • both boards fixed to one piece of cardboard

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

Download: ex01_compass.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

// IMU × micro:bit example 1: electronic compass (the arrow keeps pointing where 'front' was at power-on)
// What it does: turn the micro:bit and the IMU together and the LED arrow turns the other way, always pointing at the original front. Press A to show the heading, B to make the current direction 0.
// Wiring: IMU 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. Keep the IMU flat and still for 2 s after power-on.
// You see: tick at power-on, arrow up; turn right 90° → arrow points left; 180° → arrow down; turn back → arrow up.

const ARROWS = [ArrowNames.North, ArrowNames.NorthEast, ArrowNames.East, ArrowNames.SouthEast,
    ArrowNames.South, ArrowNames.SouthWest, ArrowNames.West, ArrowNames.NorthWest]   // 8 directions clockwise
let busy = false

if (mbc.waitReady(mbc.ADDR_IMU, 3000)) {
    basic.showIcon(IconNames.Yes)
} else {
    basic.showIcon(IconNames.No)                          // no answer: check P19 / P20 and power
}
mbc.imuZeroHeading()                                      // current direction = 0°

basic.forever(function () {
    if (busy) {
        basic.pause(100)
        return
    }
    let h = mbc.imuHeading(MbcImuHeading.Fused)           // degrees, grows turning right, shrinks turning left, may exceed 360
    let a = ((h % 360) + 360) % 360                       // fold to 0..359
    let idx = Math.round(a / 45) % 8                      // how many 45° steps turned
    basic.showArrow(ARROWS[(8 - idx) % 8], 0)             // the arrow points the opposite way, back to the original front
    basic.pause(50)
})

input.onButtonPressed(Button.A, function () {
    busy = true
    basic.showNumber(mbc.imuHeading(MbcImuHeading.Fused))
    busy = false
})
input.onButtonPressed(Button.B, function () {
    mbc.imuZeroHeading()
})

Step by step

  1. ARROWS: 8 arrows clockwise starting from up.
  2. After mbc.waitReady(mbc.ADDR_IMU, 3000) ticks, mbc.imuZeroHeading(): the current direction = 0°.
  3. forever: h = mbc.imuHeading(Fused); a = ((h % 360) + 360) % 360 folds it into 0..359 (negative values too).
  4. idx = Math.round(a / 45) % 8: how many 45° steps turned.
  5. ARROWS[(8 − idx) % 8]: the board turned right by idx steps, so the original front is idx steps to the left; the arrow takes the opposite direction.
  6. A: show the heading; B: zero.

What you should see

  • Power-on (board still): tick, arrow up.
  • Turn right 90°: arrow points left; A shows about 90.
  • 180°: arrow down. Back: up.
  • Press B: whichever way you face, the arrow becomes up.

FAQ

  • The number creeps although it is not moving? → Gyro drift. Keep the board still at power-on; leave it 10 s to self-calibrate, or press B to zero.
  • The arrow turns with the board instead of against it? → Change (8 − idx) % 8 to idx for the other kind of compass.
  • 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: Turn counter + tilt alarm

What this example does: Every 90° turned → a beep and the count goes up (event block); tilted more than 20° nose up / down → sad face + low alarm; still → asleep face, moving → happy. Press B to reset.

What you need

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

Wiring: Same as example 1.

Download: ex02_turn_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

// IMU × micro:bit example 2: turn counter + tilt alarm
// What it does: every 90° turned → a beep and the count goes up by 1; tilted more than 20° nose up / down → sad face + low alarm; still → asleep face, moving → happy. Press B to reset.
// Wiring: IMU SDA → P20, SCL → P19, VCC → 3V, GND → GND. On a V1 connect a buzzer or headphones to P0 for sound (V2 has a speaker).
// Setup: extension and paste steps as in example 1.
// You see: turn it slowly through a full circle on the table → 4 beeps and 1 2 3 4; lift the front edge → sad face + alarm; leave it still → asleep face.

let turns = 0

mbc.imuZeroHeading()

mbc.onImuTurned(90, function () {                         // fires each time the fused heading has changed by another 90°
    turns += 1
    music.playTone(Note.C5, 150)
    basic.showNumber(turns)
})

input.onButtonPressed(Button.B, function () {
    turns = 0
    mbc.imuZeroHeading()
    basic.showNumber(0)
})

basic.forever(function () {
    let p = mbc.imuPitch()                                // pitch in degrees: nose up or down
    if (Math.abs(p) > 20) {
        basic.showIcon(IconNames.Sad, 0)
        music.playTone(Note.C, 200)                       // low alarm
    } else if (mbc.imuFlag(MbcImuFlag.Still)) {           // status bit 1: still
        basic.showIcon(IconNames.Asleep, 0)
    } else {
        basic.showIcon(IconNames.Happy, 0)
    }
    basic.pause(100)
})

Step by step

  1. mbc.imuZeroHeading() zeroes first.
  2. mbc.onImuTurned(90, ...): runs once each time the fused heading has changed by another 90° → turns + 1, Note.C5, show the count.
  3. forever: p = mbc.imuPitch(). Math.abs(p) > 20 → sad face + low Note.C.
  4. Otherwise mbc.imuFlag(MbcImuFlag.Still) true → asleep face; false → happy.
  5. B: turns = 0, zero, show 0.

What you should see

  • Turn it slowly through a full circle on the table: 4 beeps, 1 2 3 4.
  • Lift the front edge more than 20°: sad face + alarm; flat again: stops.
  • Leave it still for 1 s: asleep face; wave it: happy.

FAQ

  • 5 beeps for one circle? → Overshoot when turning fast, or turning back counts too (both directions accumulate). It is a "change" event, direction does not matter.
  • The tilt alarm never stops? → The IMU is not flat, or the mounting axis is not captured yet (status bit 2). Power on flat and try again.

Example 3: Stabilised turret (the servo keeps pointing the same way)

What this example does: A servo next to the IMU with an arrow on its arm: rotate the base and the servo counter-rotates so the arrow keeps pointing the original way (within ±90°). Press A to reset the reference direction.

What you need

  • Everything from example 1
  • one 9 g servo (SG90) with a paper arrow
  • a 4.8–6 V battery pack and jumper wires
  • a board that holds the micro:bit, the IMU and the servo together

Wiring: IMU 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_stabilize_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

// IMU × micro:bit example 3: stabilised turret (the servo keeps pointing the same way)
// What it does: a servo mounted next to the IMU with an arrow on top: rotate the base and the servo counter-rotates so the arrow keeps pointing the original way (within ±90°). Press A to reset the reference direction.
// Wiring: IMU 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.
// Setup: a 9 g servo (SG90) and a board that holds the micro:bit, the IMU and the servo together.
// You see: turn the base right by 45° → the servo turns left 45°, the arrow still points the same way; beyond 90° the servo hits its end stop.

mbc.imuZeroHeading()
basic.showIcon(IconNames.Yes)

basic.forever(function () {
    let h = mbc.imuHeading(MbcImuHeading.Fused)           // positive when turned right
    let angle = Math.constrain(90 - h, 0, 180)            // base turns right → servo turns left
    pins.servoWritePin(AnalogPin.P1, angle)
    basic.pause(20)
})

input.onButtonPressed(Button.A, function () {
    mbc.imuZeroHeading()                                  // the current direction becomes the new 0°
    basic.showIcon(IconNames.Yes, 0)
})

Step by step

  1. mbc.imuZeroHeading(): current direction = 0.
  2. forever: h = fused heading (right = positive).
  3. angle = Math.constrain(90 − h, 0, 180): the base turned right by h, so the servo goes to 90 − h (h to the left) and the arrow still points the original way.
  4. pins.servoWritePin(AnalogPin.P1, angle) every 20 ms.
  5. A: zero again; the current direction becomes the new reference.

What you should see

  • Turn the base right 45°: the servo goes left 45°, the arrow stays put.
  • Beyond 90°: the servo hits its end stop (0° or 180°).
  • Press A: servo back to 90°, the current direction becomes the reference.

FAQ

  • The servo turns with the base? → The servo is mounted the other way: change 90 − h to 90 + h.
  • It slowly drifts? → Gyro drift; press A to zero. Leaving it still for 10 s lets it self-calibrate.

Example 4: Drive straight (gyro correction)

What this example does: Press A and the car drives forward; whenever it drifts, the heading steers it back, so it runs straight. Press B to stop. The LEDs show which way it corrects. The basic skill of every gyro robot.

What you need

  • A two-wheel car: two continuous-rotation servos (e.g. FS90R) as wheels plus a caster
  • the IMU flat on the car, printed arrow forward
  • a 4.8–6 V battery pack

Wiring: IMU as in example 1, flat on the car with the printed arrow toward the front. 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_drive_straight.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

// IMU × micro:bit example 4: drive straight (gyro correction)
// What it does: press A and the car drives forward; whenever it drifts, the IMU heading steers it back, so it runs straight. Press B to stop. The LEDs show which way it is correcting.
// Wiring: IMU SDA → P20, SCL → P19, VCC → 3V, GND → GND. Left continuous-rotation servo signal → P1, right → P2; servos on a 4.8–6 V battery pack whose − is joined to micro:bit GND.
// Setup: a two-wheel car with the IMU flat on it (printed arrow facing forward).
// You see: after A the car drives straight; nudge the front sideways and it turns back to the original heading and carries on.

const KP = 2                                              // 1° off → 2% speed difference
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 () {
    mbc.imuZeroHeading()                                  // the starting direction = 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) {
        basic.pause(100)
        return
    }
    let h = mbc.imuHeading(MbcImuHeading.Fused)           // positive = the nose has drifted right
    let turn = Math.constrain(h * KP, -40, 40)
    drive(50 - turn, 50 + turn)                           // drifted right → left slower, right faster → steer back left
    if (turn > 5) {
        basic.showArrow(ArrowNames.NorthWest, 0)
    } else if (turn < -5) {
        basic.showArrow(ArrowNames.NorthEast, 0)
    } else {
        basic.showArrow(ArrowNames.North, 0)
    }
    basic.pause(20)
})

Step by step

  1. function drive(left, right): left wheel P1, right wheel P2, −100..100, positive = forward; the right servo is mirrored so it subtracts.
  2. A: mbc.imuZeroHeading() makes the start direction 0, running = true; B: stop.
  3. forever: h = fused heading, positive = the nose drifted right.
  4. turn = Math.constrain(h × KP, −40, 40), KP = 2: 1° off → 2% difference.
  5. drive(50 − turn, 50 + turn): drifted right → left slower, right faster → steer back left. The arrow shows the correction.

What you should see

  • Press A: the car runs straight, much straighter than without the gyro.
  • Nudge the nose 30° sideways: the arrow tilts and the car turns back to its original heading.
  • Press B: stop.

FAQ

  • It wags left and right? → Change KP from 2 to 1.
  • It corrects the wrong way (drifts more)? → The IMU is mounted backwards (arrow to the rear) or the servo signs are flipped. Run example 1 first and confirm the number grows when turning right.

Example 5: Drive a square (all together)

What this example does: Press A: drive straight for 1.5 s (gyro keeps it straight) → spin right exactly 90° (by heading, not by time) → repeat 4 times back to the start; the LEDs show which side. Press B to abort.

What you need

  • The car from example 4
  • about 1 m square of clear floor

Wiring: Same as example 4.

Download: ex05_square_path.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

// IMU × micro:bit example 5: drive a square (all together)
// What it does: press A: drive straight for 1.5 s (gyro keeps it straight) → spin right exactly 90° → repeat 4 times back to the start; the LEDs show which side it is on. Press B to abort.
// Wiring: as example 4 (IMU P19 / P20; left wheel P1, right wheel P2, servos on their own battery, common ground).
// Setup: the two-wheel car and about 1 m square of clear floor.
// You see: after A the car drives a square showing 1 2 3 4, then stops with a tick. Under- or overshooting corners → lower TURN_SPEED.

const SIDE_MS = 1500                                      // how long each side takes
const TURN_SPEED = 35
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
}

// drive straight for ms milliseconds, holding the heading target with the gyro
function straight(target: number, ms: number): void {
    let t0 = control.millis()
    while (running && control.millis() - t0 < ms) {
        let err = mbc.imuHeading(MbcImuHeading.Fused) - target
        let turn = Math.constrain(err * 2, -40, 40)
        drive(50 - turn, 50 + turn)
        basic.pause(20)
    }
}

// spin on the spot until the heading is within 3° of target
function turnTo(target: number): void {
    while (running) {
        let err = target - mbc.imuHeading(MbcImuHeading.Fused)   // positive = still need to turn right
        if (Math.abs(err) < 3) {
            break
        }
        let s = err > 0 ? TURN_SPEED : -TURN_SPEED
        drive(s, -s)                                      // left forward, right back = turn right
        basic.pause(20)
    }
    drive(0, 0)
    basic.pause(300)                                      // settle
}

input.onButtonPressed(Button.A, function () {
    if (running) {
        return
    }
    running = true
    mbc.imuZeroHeading()
    let target = 0
    for (let side = 1; side <= 4; side++) {
        basic.showNumber(side)
        straight(target, SIDE_MS)
        target += 90                                      // heading of the next side
        turnTo(target)
    }
    drive(0, 0)
    if (running) {
        basic.showIcon(IconNames.Yes)
    }
    running = false
})
input.onButtonPressed(Button.B, function () {
    running = false
    drive(0, 0)
    basic.showIcon(IconNames.No)
})

Step by step

  1. straight(target, ms): drive for ms milliseconds, correcting with "heading − target" every 20 ms, as in example 4.
  2. turnTo(target): err = target − heading; stop when |err| < 3; otherwise drive(s, −s) spins on the spot (positive err → right), then 0.3 s to settle.
  3. A: zero, target = 0; for side = 1..4: show the side → straight(target, 1500) → target += 90 → turnTo(target).
  4. After 4 sides drive(0, 0) and a tick.
  5. B: running = false; the while (running) loops in straight and turnTo exit at once.

What you should see

  • Press A: shows 1, straight, right turn; 2, straight, right turn … after 4 sides a tick, back near the start.
  • Every corner is 90° and errors do not add up (it watches the heading).

FAQ

  • It overshoots and jitters at corners? → Change TURN_SPEED 35 to 25, or < 3 to < 5.
  • Sides are not equal? → Sides are timed, so a sagging battery shortens them; use a line follower or encoders for accuracy.
  • A triangle instead? → for 3 times and target += 120.

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/imu.json (single source of truth)
  • Setup:_build/guides/software/microbit-makecode.en.md、_build/guides/product/imu/microbit-makecode.en.md
  • Examples:sensors/imu/microbit-examples.md
Email for a quoteBuying Guide