Intro

The Tangible Interfaces Class introduces interaction design using the BBC Microbit device and common sensors (see hardware). These code samples are meant to demonstrate functionality to help you get started exploring hardware interaction.

We code in JavaScript as it is a very common programming language. The Blocks style of coding has, in my experience, been more confusing than helpful. JavaScript programs are text, so it is easy to add // comments to explain the code. AI tools are very familiar with JavaScript, and can help to fix bugs or suggest features.

The Microbit is easy to program, no software needs to be installed. Connect the board to your computer with a USB cable and go to https://makecode.microbit.org for tutorials. You will be able to connect the board and program it from the webpage. It is possible to program the Microbit from the phone or tablet app, but it is harder.

Built-in Code Examples

These programs work with the microbit alone, no wiring or components needed.

Microbit Button

The simplest Microbit program: The LEDs show a number. Button B increases the number, Button A reduces the number.

microbit.org code link

// @ts-nocheck
// The simplest Microbit program: shows a number on the 25 LEDs.
// Button B increases the value, Button A decreases the value.
// No external wiring needed — just the micro:bit itself.

// --- Setup ---
let value = 0;
basic.showNumber(value);

// --- Button Handlers ---
input.onButtonPressed(Button.B, function () {
  value += 1;
  basic.showNumber(value);
});

input.onButtonPressed(Button.A, function () {
  value -= 1;
  basic.showNumber(value);
});

Compass

Reads the micro:bit’s built-in compass and displays an arrow on the LED screen pointing in the compass direction.

microbit.org code link

// @ts-nocheck
// Reads the micro:bit's built-in compass and displays an arrow on the LED screen
// pointing in the compass direction (N, NE, E, SE, S, SW, W, NW).

// --- Setup ---
let angle = 0;
basic.showIcon(IconNames.Triangle);
input.calibrateCompass();

// --- Main Loop ---
basic.forever(function () {
  angle = input.compassHeading();
  if ((angle >= 0 && angle < 45) || angle >= 360) {
    basic.showArrow(ArrowNames.North);
  } else if (angle >= 45 && angle < 90) {
    basic.showArrow(ArrowNames.NorthWest);
  } else if (angle >= 90 && angle < 135) {
    basic.showArrow(ArrowNames.West);
  } else if (angle >= 135 && angle < 180) {
    basic.showArrow(ArrowNames.SouthWest);
  } else if (angle >= 180 && angle < 225) {
    basic.showArrow(ArrowNames.South);
  } else if (angle >= 225 && angle < 270) {
    basic.showArrow(ArrowNames.SouthEast);
  } else if (angle >= 270 && angle < 315) {
    basic.showArrow(ArrowNames.East);
  } else if (angle >= 315 && angle < 360) {
    basic.showArrow(ArrowNames.NorthEast);
  } else {
  }
  serial.writeValue("x", angle);
  basic.pause(100);
});

Accelerometer Tilt Game

Tilt-to-navigate game: tilt the micro:bit to move a bright dot toward a dimmer target dot on the 5x5 LED grid. Press button A when you reach it to check.

microbit.org code link

// @ts-nocheck
// Tilt-to-navigate game: tilt the micro:bit to move a bright dot toward a dimmer
// target dot on the 5x5 LED grid. Press button A when you reach it to check.

// --- Setup ---
let goal: game.LedSprite = null;
let player: game.LedSprite = null;
serial.redirectToUSB();
music.play(music.tonePlayable(262, music.beat(BeatFraction.Sixteenth)), music.PlaybackMode.UntilDone);
player = game.createSprite(0, 0);
goal = game.createSprite(2, 2);

// --- Main Loop ---
basic.forever(function () {
  basic.clearScreen();
  if (input.acceleration(Dimension.X) > 500) {
    player.change(LedSpriteProperty.X, 1);
  } else if (input.acceleration(Dimension.X) < -500) {
    player.change(LedSpriteProperty.X, -1);
  }
  if (input.acceleration(Dimension.Y) > 500) {
    player.change(LedSpriteProperty.Y, 1);
  } else if (input.acceleration(Dimension.Y) < -500) {
    player.change(LedSpriteProperty.Y, -1);
  }
  led.plotBrightness(player.get(LedSpriteProperty.X), player.get(LedSpriteProperty.Y), 255);
  led.plotBrightness(goal.get(LedSpriteProperty.X), goal.get(LedSpriteProperty.Y), 119);
  serial.writeLine("X" + input.acceleration(Dimension.X) + " " + "Y" + input.acceleration(Dimension.Y) + " " + "Z" + input.acceleration(Dimension.Z));
  basic.pause(100);
});

// --- Event Handlers ---
// Runs when button A is pressed to check if player reached the goal
input.onButtonPressed(Button.A, function () {
  if (player.get(LedSpriteProperty.X) == goal.get(LedSpriteProperty.X) && player.get(LedSpriteProperty.Y) == goal.get(LedSpriteProperty.Y)) {
    serial.writeString("WON");
    music.play(music.stringPlayable("C5 B A G F E D C ", 120), music.PlaybackMode.UntilDone);
    basic.pause(1000);
  } else {
    music.play(music.tonePlayable(131, music.beat(BeatFraction.Sixteenth)), music.PlaybackMode.UntilDone);
    basic.clearScreen();
    basic.pause(1000);
  }
});

Simple Digital Input

Digital input is a yes/no or on/off measurement.

Simple External Button

Reads an external push button — the simplest possible digital sensor, just on or off. Use this pattern any time you want a physical button separate from the micro:bit’s built-in A and B buttons.

microbit.org code link

// @ts-nocheck
// Reads an external push button and shows its state on the LED screen.
// This is the simplest possible digital sensor — just on or off.
// Use this pattern any time you want a physical button that is separate
// from the micro:bit's built-in A and B buttons.
//
// Digital read: 1 = button pressed, 0 = button released.
//
// Physical setup:
//   Button module has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND

// --- Setup ---
let buttonState = 0;
basic.showIcon(IconNames.SmallDiamond);

// --- Main Loop ---
basic.forever(function () {
  buttonState = pins.digitalReadPin(DigitalPin.P0);
  if (buttonState == 1) {
    basic.showIcon(IconNames.Diamond);
    serial.writeLine("Button pressed");
  } else {
    basic.showIcon(IconNames.SmallDiamond);
    serial.writeLine("Button released");
  }
  basic.pause(100);
});

Motion Sensor

Detects human movement using a PIR (passive infrared) sensor — it senses body heat moving in front of it. When motion is detected, the LED screen shows an eye icon and plays a tone. Wiring: signal → P0, VCC → 3V, GND → GND.

microbit.org code link

// @ts-nocheck
// Detects human movement using a PIR (passive infrared) motion sensor.
// When motion is detected, the LED screen shows an eye icon and plays a sound.
//
// How it works: the sensor detects body heat moving in front of it.
// Digital read: 1 = motion detected, 0 = no motion.
// The sensor has a ~2 second warm-up time when first powered on.
//
// Physical setup:
//   Sensor has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND

// --- Setup ---
let motionDetected = 0;
basic.showIcon(IconNames.Asleep);

// --- Main Loop ---
basic.forever(function () {
  motionDetected = pins.digitalReadPin(DigitalPin.P0);
  if (motionDetected == 1) {
    basic.showIcon(IconNames.Happy);
    music.play(music.tonePlayable(988, music.beat(BeatFraction.Quarter)), music.PlaybackMode.InBackground);
    serial.writeLine("Motion detected!");
  } else {
    basic.showIcon(IconNames.Asleep);
    serial.writeLine("No motion");
  }
  basic.pause(200);
});

Capacitive Touch

Detects touch using a capacitive sensor — no mechanical button needed. Any conductive surface (metal, fruit, foil, water) can become a touch input. Wiring: signal → P0, VCC → 3V, GND → GND.

microbit.org code link

// @ts-nocheck
// Detects touch using a capacitive touch sensor — no mechanical button needed.
// Any conductive surface (metal, fruit, foil, water, skin) can become a touch input.
// Touching the sensor pad lights up the LED screen and plays a tone.
//
// Digital read: 1 = touched, 0 = not touched.
//
// Physical setup:
//   Sensor has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND
//   You can attach a wire to the sensor pad to extend the touch area.

// --- Setup ---
let isTouched = 0;
basic.showIcon(IconNames.Heart);

// --- Main Loop ---
basic.forever(function () {
  isTouched = pins.digitalReadPin(DigitalPin.P0);
  if (isTouched == 1) {
    basic.showIcon(IconNames.Diamond);
    music.play(
      music.tonePlayable(523, music.beat(BeatFraction.Eighth)),
      music.PlaybackMode.InBackground,
    );
    serial.writeLine("Touched!");
  } else {
    basic.showIcon(IconNames.SmallDiamond);
    serial.writeLine("Not touched");
  }
  basic.pause(100);
});

Hall Magnetic Sensor

Detects a nearby magnet using a Hall effect sensor. Magnets can be hidden inside objects, behind walls, or under surfaces to create invisible triggers — this simple sensor is used everywhere in devices, cars, and homes. Wiring: signal → P0, VCC → 3V, GND → GND.

microbit.org code link

// @ts-nocheck
// Detects a nearby magnet using a Hall effect magnetic sensor.
// Magnets can be hidden inside objects, behind walls, or under surfaces
// to create invisible triggers — no visible button or switch needed.
//
// Digital read: 1 = magnet detected (within ~3cm), 0 = no magnet.
// Try taping a small magnet to a game piece, box lid, or sliding panel.
//
// Physical setup:
//   Sensor has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND
//   Hold a magnet close to the sensor face to trigger it.

// --- Setup ---
let magnetDetected = 0;
basic.showIcon(IconNames.No);

// --- Main Loop ---
basic.forever(function () {
  magnetDetected = pins.digitalReadPin(DigitalPin.P0);
  if (magnetDetected == 1) {
    basic.showIcon(IconNames.Yes);
    music.play(
      music.tonePlayable(784, music.beat(BeatFraction.Eighth)),
      music.PlaybackMode.InBackground,
    );
    serial.writeLine("Magnet detected!");
  } else {
    basic.showIcon(IconNames.No);
    serial.writeLine("No magnet");
  }
  basic.pause(100);
});

Simple Output

Servo simple

Sweeps a servo motor back and forth from 0 to 180 degrees continuously.

microbit.org code link

// @ts-nocheck
// Sweeps a servo motor back and forth from 0 to 180 degrees continuously.
// A basic demo of servo movement on pin P0.

// --- Setup ---
servos.P0.setRange(0, 180);
basic.showIcon(IconNames.Heart);
let direction = 1;
let angle = 0;

// --- Main Loop ---
basic.forever(function () {
  if (angle > 180 || angle < 0) {
    direction = direction * -1;
  }
  angle += direction;
  servos.P0.setAngle(angle);
  serial.writeValue("angle", angle);
});

Fan Module

Controls an L9110 fan module for physical, tactile output — you can feel the wind on your skin. Button A turns the fan on, Button B turns it off. Wiring: INA → P0, INB → P1, VCC → 5V (use battery pack), GND → GND.

microbit.org code link

// @ts-nocheck
// Controls an L9110 fan module — a small motor with a propeller that blows air.
// Most outputs are visual (LEDs) or audible (buzzer). A fan gives physical,
// tactile feedback — you can FEEL the output on your skin.
//
// Button A turns the fan on, Button B turns it off.
//
// Physical setup:
//   Fan module has 4 pins: INA, INB, VCC, GND.
//   Connect INA → micro:bit pin 0
//   Connect INB → micro:bit pin 1
//   Connect VCC → battery pack positive (5V recommended, 3V may be too weak)
//   Connect GND → micro:bit GND (and battery pack negative)
//   Important: use the 6xAA battery pack for enough power to spin the fan.

// --- Setup ---
let fanOn = false;
basic.showIcon(IconNames.Target);
// Make sure fan starts off
pins.digitalWritePin(DigitalPin.P0, 0);
pins.digitalWritePin(DigitalPin.P1, 0);

// --- Event Handlers ---
// Button A: turn fan on
input.onButtonPressed(Button.A, function () {
  fanOn = true;
  pins.digitalWritePin(DigitalPin.P0, 1);
  pins.digitalWritePin(DigitalPin.P1, 0);
  basic.showIcon(IconNames.SmallSquare);
  serial.writeLine("Fan ON");
});

// Button B: turn fan off
input.onButtonPressed(Button.B, function () {
  fanOn = false;
  pins.digitalWritePin(DigitalPin.P0, 0);
  pins.digitalWritePin(DigitalPin.P1, 0);
  basic.showIcon(IconNames.Target);
  serial.writeLine("Fan OFF");
});

Relay Module

Controls a relay — an electrically operated switch that lets your micro:bit turn real-world devices on and off (lamps, fans, motors). You’ll hear a satisfying “click” when it switches. Button A = on, Button B = off.

microbit.org code link

// @ts-nocheck
// Controls a relay module — an electrically operated switch that can turn
// real-world devices on and off. You'll hear a satisfying "click" when it switches.
// A relay lets your micro:bit control things like lamps, fans, or motors
// that need more power than the micro:bit can provide directly.
//
// Button A turns the relay on (click!), Button B turns it off.
// Digital write: 1 = relay ON (closed circuit), 0 = relay OFF (open circuit).
//
// Physical setup:
//   Relay module has 3 low-voltage pins: S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND
//
//   The relay also has screw terminals for the high-voltage side:
//   COM (common), NO (normally open), NC (normally closed).
//   For basic testing, just listen for the click — no need to wire the screw terminals.
//   To control a device: wire it through COM and NO so it turns on when the relay activates.

// --- Setup ---
let relayOn = false;
basic.showIcon(IconNames.Target);
pins.digitalWritePin(DigitalPin.P0, 0);

// --- Event Handlers ---
// Button A: turn relay on
input.onButtonPressed(Button.A, function () {
  relayOn = true;
  pins.digitalWritePin(DigitalPin.P0, 1);
  basic.showIcon(IconNames.Yes);
  serial.writeLine("Relay ON");
});

// Button B: turn relay off
input.onButtonPressed(Button.B, function () {
  relayOn = false;
  pins.digitalWritePin(DigitalPin.P0, 0);
  basic.showIcon(IconNames.No);
  serial.writeLine("Relay OFF");
});

Analog Sensors

Basic Analog sensor

Reads an analog sensor on pin P0 and displays the value as a bar graph on the LED screen. An example sensor is the “potentiometer” (a rotating variable resistor) that changes the voltage that the microbit sees.

microbit.org code link

// @ts-nocheck
// Reads an analog sensor on pin P0 and displays the value as a bar graph
// on the LED screen. Logs the raw value and percentage over serial.

// --- Setup ---
let AnalogReading = 0;
basic.showIcon(IconNames.Yes);

// --- Main Loop ---

basic.forever(function () {
  AnalogReading = pins.analogReadPin(AnalogPin.P0);
  led.plotBarGraph(AnalogReading, 1023);
  serial.writeLine("Analog Reading" + AnalogReading + "|" + Math.map(AnalogReading, 0, 1023, 0, 99) + "%");
});

Pressure/Force Sensor

Reads a thin-film pressure sensor and shows how hard you’re pressing as a bar graph. Unlike a button (on/off), this sensor gives an analog value — it knows HOW MUCH force is applied. Wiring: signal → P0, VCC → 3V, GND → GND.

microbit.org code link

// @ts-nocheck
// Reads a thin-film pressure (force) sensor and displays how hard you're pressing.
// Unlike a button (on/off), this sensor gives an analog value — it knows HOW MUCH
// force is applied. Squeeze harder = higher reading.
//
// Use cases: squeeze toys, sit-on sensors, footstep detection, grip strength.
// Analog read: 0 = no pressure, up to 1023 = maximum force.
//
// Physical setup:
//   Sensor has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND
//   Press the round film area with your finger to see values change.

// --- Setup ---
let forceReading = 0;
basic.showIcon(IconNames.SmallHeart);

// --- Main Loop ---
basic.forever(function () {
  forceReading = pins.analogReadPin(AnalogPin.P0);
  led.plotBarGraph(forceReading, 1023);
  serial.writeValue("force", forceReading);
  serial.writeValue("percent", Math.round(Math.map(forceReading, 0, 1023, 0, 100)));
  basic.pause(100);
});

Joystick Input

Reads an analog joystick and displays position as a dot on the LED screen. Pressing the joystick button plays a sound.

The joystick is actually simply 2 rotating variable resistors

microbit.org code link

// @ts-nocheck
// Reads an analog joystick and displays position as a dot on the LED screen.
// Pressing the joystick button plays a sound. Uses a KEYESTUDIO analog joystick.
//
// Wiring: Y direction → pin 0, X direction → pin 1, Switch → pin 2
// MAP scales the 0-1023 input to 0-4 (pixel positions on the LED grid)
// ROUND converts to whole numbers for LED coordinates
// Digital read pin 2: 1 = button clicked

// --- Setup ---
let Y = 0;
let X = 0;
serial.redirectToUSB();
basic.showIcon(IconNames.Chessboard);
music.play(music.tonePlayable(262, music.beat(BeatFraction.Sixteenth)), music.PlaybackMode.UntilDone);
// --- Main Loop ---
basic.forever(function () {
  basic.clearScreen();
  X = Math.round(Math.map(pins.analogReadPin(AnalogReadWritePin.P1), 0, 1023, 4, 0));
  Y = Math.round(Math.map(pins.analogReadPin(AnalogReadWritePin.P0), 0, 1023, 0, 4));
  if (pins.digitalReadPin(DigitalPin.P2) == 1) {
    music.play(
      music.createSoundExpression(WaveShape.Sine, 5000, 0, 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear),
      music.PlaybackMode.InBackground,
    );
    basic.showIcon(IconNames.SmallDiamond);
  } else {
    led.plotBrightness(X, Y, 255);
  }
  basic.pause(100);
  serial.writeNumbers([X, Y, 0]);
});

Ultraviolet Sensor

Reads a GUVA-S12SD UV sensor and estimates the UV index (0-11). When should you reapply sunscreen? Great for wearable or outdoor projects.

microbit.org code link

// @ts-nocheck
// Reads a GUVA-S12SD ultraviolet (UV) sensor and estimates the UV index.
// UV index 0-2 is low, 3-5 moderate, 6-7 high, 8-10 very high, 11+ extreme.
// Great for wearable projects: when should you reapply sunscreen?
//
// Analog read: the sensor outputs a voltage proportional to UV intensity.
// We map the 0-1023 reading to an approximate UV index of 0-11.
//
// Physical setup:
//   Sensor has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND
//   Point the sensor window toward the sky or a UV light source.

// --- Setup ---
let uvReading = 0;
let uvIndex = 0;
basic.showString("UV");

// --- Main Loop ---
basic.forever(function () {
  uvReading = pins.analogReadPin(AnalogPin.P0);
  // Map sensor range to approximate UV index (0-11)
  uvIndex = Math.round(Math.map(uvReading, 0, 1023, 0, 11));
  // Show the UV index number on the LED screen
  basic.showNumber(uvIndex);
  serial.writeValue("raw", uvReading);
  serial.writeValue("uvIndex", uvIndex);
  basic.pause(1000);
});

Analog Gas Sensor

Reads an MQ-2 gas sensor that detects flammable gases and smoke. The bar graph gets taller as gas concentration increases. The sensor needs 5V and about 20 seconds to warm up.

microbit.org code link

// @ts-nocheck
// Reads an analog gas sensor (MQ-2) that detects flammable gases and smoke.
// The bar graph gets taller as gas concentration increases.
// Useful for safety projects or environmental monitoring.
//
// Analog read: 0 = clean air, higher values = more gas detected.
// The sensor needs about 20 seconds to warm up before readings stabilize.
//
// Physical setup:
//   Sensor has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 5V (use battery pack — this sensor needs 5V)
//   Connect G → micro:bit GND
//   Important: the sensor has a small heater inside and gets warm. This is normal.

// --- Setup ---
let gasReading = 0;
basic.showString("GAS");
// Give sensor time to warm up
basic.pause(3000);
basic.showIcon(IconNames.SmallHeart);

// --- Main Loop ---
basic.forever(function () {
  gasReading = pins.analogReadPin(AnalogPin.P0);
  led.plotBarGraph(gasReading, 1023);
  serial.writeValue("gas", gasReading);
  basic.pause(500);
});

Analog Alcohol Sensor

Reads an MQ-3 alcohol sensor that detects alcohol vapor in the air. Try holding hand sanitizer near the sensor. Needs 5V and about 20 seconds to warm up.

microbit.org code link

// @ts-nocheck
// Reads an analog alcohol sensor (MQ-3) that detects alcohol vapor in the air.
// The bar graph gets taller as alcohol concentration increases.
// Try holding hand sanitizer or rubbing alcohol near the sensor.
//
// Analog read: 0 = clean air, higher values = more alcohol vapor detected.
// The sensor needs about 20 seconds to warm up before readings stabilize.
//
// Physical setup:
//   Sensor has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 5V (use battery pack — this sensor needs 5V)
//   Connect G → micro:bit GND
//   Important: the sensor has a small heater inside and gets warm. This is normal.

// --- Setup ---
let alcoholReading = 0;
basic.showString("ALC");
// Give sensor time to warm up
basic.pause(3000);
basic.showIcon(IconNames.SmallHeart);

// --- Main Loop ---
basic.forever(function () {
  alcoholReading = pins.analogReadPin(AnalogPin.P0);
  led.plotBarGraph(alcoholReading, 1023);
  serial.writeValue("alcohol", alcoholReading);
  basic.pause(500);
});

Sensors and Displays Requiring Extensions

Ultrasonic Distance Sensor

Reads an ultrasonic distance sensor (HC-SR04) and displays the distance as a bar graph on the LED screen. In the microbit code editor, open “extensions”, search for ‘Sonar’ and add. Example project Example video

microbit.org code link

// @ts-nocheck
// Reads an ultrasonic distance sensor (HC-SR04) and displays the distance
// as a bar graph on the LED screen. Also logs distance values over serial.

// --- Setup ---
let DistanceInCM = 0;
led.enable(true);

// --- Main Loop ---
basic.forever(function () {
  DistanceInCM = sonar.ping(DigitalPin.P0, DigitalPin.P1, PingUnit.Centimeters);
  led.plotBarGraph(DistanceInCM, 40);
  serial.writeLine("DistanceInCM=" + DistanceInCM);
});

Temperature And Humidity Sensor

Reads temperature and humidity from a DHT11 sensor — one sensor, two readings. The core of every smart thermostat or weather station. Requires the “DHT11_DHT22” MakeCode extension (search “DHT11” in Extensions).

microbit.org code link

// @ts-nocheck
// Reads temperature and humidity from a DHT11 sensor and shows them on the LED screen.
// One sensor gives you two readings — the core of every smart thermostat or weather station.
// Alternates between showing temperature (C) and humidity (%).
//
// Note: this program requires a MakeCode extension.
//   In the MakeCode editor, click "Extensions" and search for "DHT11".
//   Add the "DHT11_DHT22" extension by Alan Krantas.
//
// Physical setup:
//   Sensor has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND
//   Important: wait at least 1 second between readings (the sensor is slow).

// --- Setup ---
let temperature = 0;
let humidity = 0;
basic.showString("DHT");

// --- Main Loop ---
basic.forever(function () {
  // Read the sensor (must call this before reading temperature or humidity)
  dht11_dht22.queryData(DHTtype.DHT11, DigitalPin.P0, true, false, true);
  // Get the readings
  temperature = dht11_dht22.readData(dataType.temperature);
  humidity = dht11_dht22.readData(dataType.humidity);
  // Show temperature
  basic.showString("T");
  basic.showNumber(temperature);
  basic.pause(1000);
  // Show humidity
  basic.showString("H");
  basic.showNumber(humidity);
  basic.pause(1000);
  // Log to serial
  serial.writeValue("tempC", temperature);
  serial.writeValue("humidity", humidity);
});

Rotary encoder

Demonstrates a KY-040 rotary encoder: twist to change a value shown as a bar graph, press the button to reset. Requires the RotaryEncoder extension.

microbit.org code link

// @ts-nocheck
// Demonstrates a KY-040 rotary encoder: twist to change a value shown as a bar
// graph on the LED screen, press the button to reset. Requires the RotaryEncoder extension.

// --- Setup ---
let count = 0;
count = 50;
basic.showIcon(IconNames.SmallHeart);
RotaryEncoder.init(DigitalPin.P2, DigitalPin.P1, DigitalPin.P0);
led.plotBarGraph(50, 100);

// --- Event Handlers ---
// Runs when encoder is rotated left (counter-clockwise)
RotaryEncoder.onRotateEvent(RotationDirection.Left, function () {
  count += 5;
  serial.writeValue("count", count);
  led.plotBarGraph(count, 100);
});

// Runs when encoder is rotated right (clockwise)
RotaryEncoder.onRotateEvent(RotationDirection.Right, function () {
  count += -5;
  serial.writeValue("count", count);
  led.plotBarGraph(count, 100);
});

// Runs when encoder button is pressed
RotaryEncoder.onPressEvent(function () {
  basic.showIcon(IconNames.Yes);
  count = 50;
  basic.pause(1000);
  led.plotBarGraph(count, 100);
});

IR Remote Reader

Reads button presses from the included IR remote (or any IR remote — try your TV remote). Point a remote at the IR receiver and the micro:bit shows which button code was received. Requires the “MakerBit IR Receiver” MakeCode extension (search “makerbit-ir” in Extensions).

microbit.org code link

// @ts-nocheck
// Reads button presses from the included IR remote control (or any IR remote).
// Point any remote at the IR receiver and press buttons — the micro:bit shows
// which button was pressed. Use this to decode your TV or AC remote's signals.
//
// MakeCode extension required:
//   In the MakeCode editor, click "Extensions" and search for "makerbit-ir".
//   Add the "MakerBit IR Receiver" extension by 1010Technologies.
//
// Physical setup:
//   IR receiver module has 3 pins labeled S (signal), V (power), G (ground).
//   Connect S → micro:bit pin 0
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND
//   Point the IR remote directly at the receiver module (line of sight).

// --- Setup ---
// Connect IR receiver on pin 0 using Keyestudio protocol
makerbit.connectIrReceiver(DigitalPin.P0, IrProtocol.Keyestudio);
basic.showIcon(IconNames.SmallDiamond);

// --- Event Handlers ---
// This runs every time any IR button is pressed.
// The IR remote sends a 32-bit datagram (address + command + error-check bytes).
// irDatagram() returns the full 32-bit hex code as a string like "0x00FF02FD".
// Copy the hex value from the serial monitor and paste it into ir_control_devices.ts
// (remove the "0x" prefix, e.g. "0x00FF02FD" becomes "00FF02FD").
//
// Common codes for the included Keyestudio remote:
//   Power=0xFF02FD, Vol+=0xFF18E7, Vol-=0xFF4AB5
makerbit.onIrDatagram(function () {
  let hex = makerbit.irDatagram();
  basic.showString("IR");
  serial.writeLine("IR hex: " + hex);
});

IR Remote Transmit To Control Your Devices

Record-and-replay: capture an IR signal from any remote, then replay it to control real devices like your TV or AC. Uses both an IR receiver and IR transmitter LED. Requires the “MakerBit IR Receiver” and “MakerBit IR Transmitter” MakeCode extensions (search “makerbit-ir” in Extensions).

microbit.org code link

// @ts-nocheck
// Send IR commands to control a TV using the micro:bit buttons.
//   Button A = Volume Down
//   Button B = Volume Up
//   A+B together = Power On/Off
//
// NOTE: The IR codes below are common NEC protocol values for generic TVs.
//   Your TV may use different codes! To find the right hex codes:
//   1. Load ir_remote_reader.ts, point your TV remote at the IR receiver,
//      and press buttons — the hex codes appear in the serial monitor.
//   2. Or google your TV brand + "IR codes NEC hex".
//   Then update the values in the Setup section below.
//
// MakeCode extensions required:
//   In the MakeCode editor, click "Extensions" and search for "makerbit-ir".
//   Add the "MakerBit IR Transmitter" extension.
//
// Physical setup — IR transmitter LED:
//   Connect signal → micro:bit pin 1
//   Connect V → micro:bit 3V
//   Connect G → micro:bit GND
//   Point the IR transmitter LED at your TV when sending commands.

// --- Setup ---
// Connect IR transmitter on pin 1
makerbit.connectIrSenderLed(AnalogPin.P1);

// IR codes — update these for your specific TV!
// These are common NEC protocol codes; google "[your TV brand] IR hex codes"
let IR_POWER = "00FF02FD";
let IR_VOL_UP = "00FF18E7";
let IR_VOL_DOWN = "00FF4AB5";

basic.showIcon(IconNames.Target);

// --- Event Handlers ---
// Button A: Volume Down
input.onButtonPressed(Button.A, function () {
  makerbit.sendIrDatagram(IR_VOL_DOWN);
  basic.showString("-");
  serial.writeLine("Sent: Volume Down");
  basic.pause(300);
  basic.showIcon(IconNames.Target);
});

// Button B: Volume Up
input.onButtonPressed(Button.B, function () {
  makerbit.sendIrDatagram(IR_VOL_UP);
  basic.showString("+");
  serial.writeLine("Sent: Volume Up");
  basic.pause(300);
  basic.showIcon(IconNames.Target);
});

// A+B together: Power On/Off
input.onButtonPressed(Button.AB, function () {
  makerbit.sendIrDatagram(IR_POWER);
  basic.showIcon(IconNames.Square);
  serial.writeLine("Sent: Power Toggle");
  basic.pause(500);
  basic.showIcon(IconNames.Target);
});

OLED Display

Displays text and numbers on a 0.96” OLED screen (128x64 pixels) — real visual output beyond the 5x5 LED grid. Shows a counter, light level, and temperature. Requires the “OLED_SSD1306” MakeCode extension (search “OLED” in Extensions). Uses I2C: plug into the sensor shield’s I2C port.

microbit.org code link

// @ts-nocheck
// Displays text and numbers on a 0.96" OLED screen (128x64 pixels).
// This tiny screen gives you real visual output beyond the 5x5 LED grid —
// show sensor readings, messages, or simple graphics.
//
// MakeCode extension required:
//   In the MakeCode editor, click "Extensions" and search for "OLED".
//   Add the "OLED_SSD1306" extension by Tinkertanker.
//
// Physical setup:
//   The OLED module has 4 pins. It uses I2C, so it must go on specific pins:
//   Connect GND → micro:bit GND
//   Connect VCC → micro:bit 3V
//   Connect SCL → micro:bit pin 19 (I2C clock)
//   Connect SDA → micro:bit pin 20 (I2C data)
//   Note: on the sensor shield, just plug into one of the I2C ports.

// --- Setup ---
OLED.init(128, 64);
OLED.writeStringNewLine("Hello!");
OLED.writeStringNewLine("micro:bit OLED");
basic.pause(2000);

// --- Main Loop ---
// Show a counter that updates every second
let count = 0;
basic.forever(function () {
  OLED.clear();
  OLED.writeStringNewLine("Count: " + count);
  OLED.writeStringNewLine("Light: " + input.lightLevel());
  OLED.writeStringNewLine("Temp: " + input.temperature() + "C");
  count += 1;
  serial.writeValue("count", count);
  basic.pause(1000);
});

LCD Display

Displays text on a 1602 LCD screen (2 rows of 16 characters) — the classic display found on microwaves, thermostats, and vending machines. Requires the “i2cLCD1602” MakeCode extension (search “LCD1602” in Extensions). Uses I2C: plug into the sensor shield’s I2C port. Tip: if the screen looks blank, adjust the blue potentiometer on the back.

microbit.org code link

// @ts-nocheck
// Displays text and numbers on a 1602 LCD screen (2 rows of 16 characters).
// This classic display is on microwaves, thermostats, and vending machines.
// Great for showing sensor readings or status messages.
//
// MakeCode extension required:
//   In the MakeCode editor, click "Extensions" and search for "LCD1602".
//   Add the "i2cLCD1602" extension.
//
// Physical setup:
//   The LCD module has 4 pins. It uses I2C, so it must go on specific pins:
//   Connect GND → micro:bit GND
//   Connect VCC → micro:bit 5V (use battery pack — LCD needs 5V for backlight)
//   Connect SCL → micro:bit pin 19 (I2C clock)
//   Connect SDA → micro:bit pin 20 (I2C data)
//   Note: on the sensor shield, just plug into one of the I2C ports.
//   Tip: if the screen looks blank, adjust the blue potentiometer on the back.

// --- Setup ---
// Address 0 means auto-detect. Try 39 or 63 if auto doesn't work.
I2C_LCD1602.LcdInit(0);
I2C_LCD1602.ShowString("Hello!", 0, 0);
I2C_LCD1602.ShowString("micro:bit LCD", 0, 1);
basic.pause(2000);

// --- Main Loop ---
// Show a counter and temperature that update every second
let count2 = 0;
basic.forever(function () {
  I2C_LCD1602.ShowString("Count: " + count2 + "   ", 0, 0);
  I2C_LCD1602.ShowString("Temp: " + input.temperature() + "C ", 0, 1);
  count2 += 1;
  serial.writeValue("count", count2);
  basic.pause(1000);
});

Medium complexity examples

Analog data smoothing

Demonstrates smoothing noisy analog sensor data using a rolling average and dynamic min/max tracking. More advanced concept to be aware of.

microbit.org code link

// @ts-nocheck

// Demonstrates smoothing noisy analog sensor data using a rolling average
// and dynamic min/max tracking to produce a stable percentage output.

// --- Setup ---
let ValueAsPercentage = 0;
let SmoothedValue = 0;
let maxValue = 0;
let minValue = 0;
let SensorMax = 0;
let SensorMin = 0;
// What the sensor reads in - will vary for each sensor
let RawSensorValue = 0;
// Min for the sensor (you need to update this with values from using the sensor the way you want)
SensorMin = 0;
// Max for the sensor (you need to update this with values from using the sensor the way you want)
SensorMax = 256;
// This gets reset by this program every run
minValue = 1023;
// This gets reset by this program every run
maxValue = 0;
SmoothedValue = 511;
ValueAsPercentage = 50;

// --- Main Loop ---

basic.forever(function () {
  basic.pause(100);
  RawSensorValue = input.lightLevel();
  CleanAnalogData(RawSensorValue);
  led.plotBarGraph(ValueAsPercentage, 100);
  serial.writeValue("RawSensor", RawSensorValue);
  serial.writeValue("Smoothed", SmoothedValue);
  serial.writeValue("%", ValueAsPercentage);
  serial.writeValue("Max", maxValue);
  serial.writeValue("Min", minValue);
});

// --- Functions ---
function CleanAnalogData(input2: number) {
  // Ignore values outside the normal range.  Differs for each sensor
  if (input2 >= SensorMin && input2 <= SensorMax) {
    // Set new Max Value as new readings come in
    if (input2 > maxValue) {
      maxValue = input2;
    }
    // Set new Min Value as new readings come in
    if (input2 < minValue) {
      minValue = input2;
    }
    SmoothedValue = Math.round(SmoothedValue + (input2 - SmoothedValue) / 5);
    ValueAsPercentage = Math.round(Math.map(SmoothedValue, minValue, maxValue, 0, 100));
  }
}

Rotary encoder with RGB Color Picker

RGB rotary encoder color picker: twist to cycle through colors, press the button to send the selected hex color value over serial.

microbit.org code link

// @ts-nocheck
//
//  RGB rotary encoder color picker: twist to cycle through colors on the built-in
//  RGB LED, press the button to send the selected hex color value over serial.
//
//  The RGB Encoder has 2 sides with pins
//    Facing the 3 pin side, left to right
//    1) Encoder A connect to Pin 9
//    2) Ground
//     3) Encoder B connect to Pin 8
//    Facing the 5 pin side, left to right
//    4) Voltage. 3.3v seems to work
//    5) Blue connect to Pin 2
//    6) Switch connect to Pin 16
//    7) Green connect to Pin 1
//     8) Red connect to Pin 0
//  Note the device is wired different than other simple components, which can cause confusion
//  You set LED pins down to light them.
//  You can use analog out to control brightness,
//  pins.analog_write_pin(AnalogPin.P0, 0) is fully on
//  pins.analog_write_pin(AnalogPin.P0, 1023) is fully off
//  Rotary Encoder can NOT use the standard microbit KY-040 rotary encoder extension
//  very helpful guide https://qbalsdon.github.io/circuitpython/rotary-encoder/python/led/2021/02/27/rgb-rotary-encoder.html

// --- Setup ---
let current_time: number;
let encoder_a: number;
let encoder_b: number;

led.enable(false);
serial.redirectToUSB();
pins.setPull(DigitalPin.P8, PinPullMode.PullUp);
pins.setPull(DigitalPin.P9, PinPullMode.PullUp);
pins.setPull(DigitalPin.P16, PinPullMode.PullDown);
let red = 255;
let green = 0;
let blue = 0;
let color_position = 0;
let encoder_a_prev = 1;
let loop_time = input.runningTime();

// --- Functions ---
function to_hex(value: number) {
  let hex_digits = "0123456789abcdef";
  return hex_digits[Math.idiv(value, 16)] + hex_digits[value % 16];
}

function set_color_from_position(pos: number) {
  if (pos < 85) {
    red = 255 - pos * 3;
    green = pos * 3;
    blue = 0;
  } else if (pos < 170) {
    pos -= 85;
    red = 0;
    green = 255 - pos * 3;
    blue = pos * 3;
  } else {
    pos -= 170;
    red = pos * 3;
    green = 0;
    blue = 255 - pos * 3;
  }
}

// --- Main Loop ---
while (true) {
  set_color_from_position(color_position);
  pins.analogWritePin(AnalogPin.P0, 1023 - red * 4);
  pins.analogWritePin(AnalogPin.P1, 1023 - green * 4);
  pins.analogWritePin(AnalogPin.P2, 1023 - blue * 4);
  //  Button - flash white and send hex color
  if (pins.digitalReadPin(DigitalPin.P16) == 1) {
    serial.writeLine("#" + to_hex(red) + to_hex(green) + to_hex(blue));
    //  Flash white
    pins.analogWritePin(AnalogPin.P0, 0);
    pins.analogWritePin(AnalogPin.P1, 0);
    pins.analogWritePin(AnalogPin.P2, 0);
    basic.pause(200);
  }

  //  Check encoder
  current_time = input.runningTime();
  if (current_time >= loop_time + 5) {
    encoder_a = pins.digitalReadPin(DigitalPin.P8);
    encoder_b = pins.digitalReadPin(DigitalPin.P9);
    if (encoder_a == 0 && encoder_a_prev == 1) {
      if (encoder_b == 1) {
        color_position += 5;
        //  Clockwise - step forward
        if (color_position > 255) {
          color_position = 0;
        }
      } else {
        color_position -= 5;
        //  Counter-clockwise - step back
        if (color_position < 0) {
          color_position = 255;
        }
      }
    }

    encoder_a_prev = encoder_a;
    loop_time = current_time;
  }

  basic.pause(10);
}

Control Servo with analog sensor

Controls a servo motor position using an analog sensor input. The sensor reading on P1 is mapped to a servo angle on P0.

microbit.org code link

// @ts-nocheck
// Controls a servo motor position using an analog sensor input.
// The sensor reading on P1 is mapped to a servo angle on P0.

// --- Setup ---
let servo = 0;
basic.showIcon(IconNames.SmallHeart);
serial.redirectToUSB();
// --- Main Loop ---
basic.forever(function () {
  servo = Math.map(pins.analogReadPin(AnalogReadWritePin.P1), 0, 2, 0, 10);
  led.plotBarGraph(servo, 180);
  pins.servoWritePin(AnalogPin.P0, servo);
  serial.writeLine("" + servo);
});

Servo sonar with smoothing

Controls a servo motor based on an ultrasonic distance sensor reading, with data smoothing applied to prevent jittery movement.

microbit.org code link

// @ts-nocheck
// Controls a servo motor based on an ultrasonic distance sensor reading,
// with data smoothing applied to prevent jittery movement.

// --- Setup ---
let newAngle = 0;
let SonarReading = 0;
let ServoAngle = 180;
servos.P2.setRange(0, 180);
basic.showIcon(IconNames.Chessboard);
// --- Main Loop ---
basic.forever(function () {
  SonarReading = sonar.ping(DigitalPin.P0, DigitalPin.P1, PingUnit.Centimeters);
  if (SonarReading > 1 && SonarReading < 40) {
    newAngle = Math.map(SonarReading, 1, 40, 0, 180);
    ServoAngle = Math.round(ServoAngle + (newAngle - ServoAngle) / 5);
    serial.writeLine("sonarCM:" + SonarReading + " angle:" + ServoAngle + " Angle Difference:" + (newAngle - ServoAngle));
    servos.P2.setAngle(ServoAngle);
    led.plotBarGraph(ServoAngle, 180);
  }
  basic.pause(100);
});

More Advanced programs

Flappy pixel

A minimal version of Flappy Bird on the micro:bit 5x5 LED screen. Press button A to flap upward; avoid the walls scrolling toward you.

microbit.org code link

// @ts-nocheck

// A minimal version of Flappy Bird on the micro:bit 5x5 LED screen.
// Press button A to flap upward; avoid the walls scrolling toward you.

// --- Event Handlers ---
// Runs when button A is pressed - flap the bird upward
input.onButtonPressed(Button.A, function () {
  birdY += -1;
  if (birdY < 0) {
    birdY = 0;
  }
});
// --- Setup ---
let birdY = 0;
basic.clearScreen();
let score = 0;
birdY = 0;
let wallX = 5;
let WallHoleY = 2;
// --- Main Loop ---
basic.forever(function () {
  basic.clearScreen();
  if (wallX < 0) {
    wallX = 4;
    WallHoleY = randint(0, 4);
  }
  for (let index = 0; index <= 4; index++) {
    led.plotBrightness(wallX, index, 28);
  }
  led.unplot(wallX, WallHoleY);
  if (birdY > 4) {
    birdY = 4;
  }
  led.plot(0, birdY);
  if (wallX == 0) {
    if (birdY == WallHoleY) {
      music.play(music.tonePlayable(523, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone);
    } else {
      music.play(music.tonePlayable(131, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone);
    }
  }
  wallX += -1;
  birdY += 1;
  basic.pause(1000);
});

Flappy Servo

Steers a servo motor up and down using buttons A and B. A simple demo of using buttons to control physical output.

microbit.org code link

// @ts-nocheck
// Steers a servo motor up and down using buttons A and B.
// A simple demo of using buttons to control physical output.

// --- Event Handlers ---
// Button A moves the servo up
input.onButtonPressed(Button.A, function () {
  if (birdY < maxY) {
    birdY += 10;
  }
});
// Button B moves the servo down
input.onButtonPressed(Button.B, function () {
  if (birdY > minY) {
    birdY += -10;
  }
});
// --- Setup ---
let minY = 0;
let maxY = 0;
let birdY = 0;
birdY = 100;
maxY = 180;
minY = 0;
servos.P0.setRange(minY, maxY);
// --- Main Loop ---
basic.forever(function () {
  servos.P0.setAngle(birdY);
  basic.pause(200);
});

Smart Thermostat Demo

A thermostat in miniature: reads temperature from a DHT11 sensor, displays it on an OLED screen, and automatically turns on a fan when it gets too hot. Buttons A/B adjust the threshold. Components: DHT11 + OLED + Fan Module. Requires “OLED_SSD1306” and “DHT11_DHT22” MakeCode extensions.

microbit.org code link

// @ts-nocheck
// A mini smart thermostat: reads temperature, shows it on the OLED screen,
// and automatically turns on a fan when it gets too hot. This is how a Nest
// thermostat works at its core: sense → display → actuate.
//
// MakeCode extensions required:
//   Click "Extensions" and search for "OLED". Add "OLED_SSD1306" by Tinkertanker.
//   Click "Extensions" and search for "DHT11". Add "DHT11_DHT22" by Alan Krantas.
//
// Physical setup:
//   DHT11 temperature sensor:
//     Connect S → micro:bit pin 0
//     Connect V → micro:bit 3V
//     Connect G → micro:bit GND
//   OLED display (I2C):
//     Connect GND → micro:bit GND
//     Connect VCC → micro:bit 3V
//     Connect SCL → micro:bit pin 19
//     Connect SDA → micro:bit pin 20
//   Fan module:
//     Connect INA → micro:bit pin 1
//     Connect INB → micro:bit pin 2
//     Connect VCC → battery pack 5V
//     Connect GND → micro:bit GND
//
// Adjust the threshold temperature to suit your environment.

// --- Setup ---
let temp = 0;
let hum = 0;
let fanRunning = false;
let threshold = 26; // Fan turns on above this temperature (Celsius)

OLED.init(128, 64);
OLED.writeStringNewLine("Thermostat");
OLED.writeStringNewLine("Starting...");
pins.digitalWritePin(DigitalPin.P1, 0);
pins.digitalWritePin(DigitalPin.P2, 0);
basic.pause(2000);

// --- Main Loop ---
basic.forever(function () {
  // Read temperature and humidity
  dht11_dht22.queryData(DHTtype.DHT11, DigitalPin.P0, true, false, true);
  temp = dht11_dht22.readData(dataType.temperature);
  hum = dht11_dht22.readData(dataType.humidity);

  // Update OLED display
  OLED.clear();
  OLED.writeStringNewLine("Temp: " + temp + " C");
  OLED.writeStringNewLine("Humidity: " + hum + " %");
  OLED.writeStringNewLine("Threshold: " + threshold + " C");

  // Turn fan on or off based on temperature
  if (temp > threshold) {
    pins.digitalWritePin(DigitalPin.P1, 1);
    pins.digitalWritePin(DigitalPin.P2, 0);
    OLED.writeStringNewLine("Fan: ON");
    fanRunning = true;
  } else {
    pins.digitalWritePin(DigitalPin.P1, 0);
    pins.digitalWritePin(DigitalPin.P2, 0);
    OLED.writeStringNewLine("Fan: OFF");
    fanRunning = false;
  }

  serial.writeValue("temp", temp);
  serial.writeValue("humidity", hum);
  serial.writeValue("fan", fanRunning ? 1 : 0);
  basic.pause(2000);
});

// --- Event Handlers ---
// Button A: lower threshold
input.onButtonPressed(Button.A, function () {
  threshold -= 1;
});

// Button B: raise threshold
input.onButtonPressed(Button.B, function () {
  threshold += 1;
});

Proximity-Reactive Object

An object that reacts as you approach: OLED display shows different messages at different distances, and a servo arm points toward you. Think: interactive museum exhibit, responsive furniture, robot personality. Components: Ultrasonic + OLED + Servo. Requires “OLED_SSD1306” and “Sonar” MakeCode extensions.

microbit.org code link

// @ts-nocheck
// An object that reacts as you approach: the OLED display changes its message
// and a servo arm points toward you based on distance. Think: interactive museum
// exhibit, responsive furniture, or a robot with personality.
//
// MakeCode extensions required:
//   Click "Extensions" and search for "OLED". Add "OLED_SSD1306" by Tinkertanker.
//   Click "Extensions" and search for "Sonar". Add the Sonar extension.
//
// Physical setup:
//   Ultrasonic sensor (HC-SR04):
//     Connect Trig → micro:bit pin 0
//     Connect Echo → micro:bit pin 1
//     Connect VCC → micro:bit 5V (use battery pack)
//     Connect GND → micro:bit GND
//   OLED display (I2C):
//     Connect GND → micro:bit GND
//     Connect VCC → micro:bit 3V
//     Connect SCL → micro:bit pin 19
//     Connect SDA → micro:bit pin 20
//   Servo motor:
//     Connect signal → micro:bit pin 2
//     Connect VCC → battery pack positive
//     Connect GND → micro:bit GND

// --- Setup ---
let distance = 0;
let servoAngle = 90;
OLED.init(128, 64);
OLED.writeStringNewLine("Waiting...");
servos.P2.setRange(0, 180);

// --- Main Loop ---
basic.forever(function () {
  distance = sonar.ping(DigitalPin.P0, DigitalPin.P1, PingUnit.Centimeters);

  // Map distance to servo angle: closer = more servo movement
  // At 40cm+ the servo centers, at 5cm it swings fully
  servoAngle = Math.round(Math.map(distance, 5, 40, 180, 90));
  servoAngle = Math.constrain(servoAngle, 0, 180);
  servos.P2.setAngle(servoAngle);

  // Change OLED message based on distance zones
  OLED.clear();
  OLED.writeStringNewLine("Distance: " + distance + "cm");

  if (distance < 10) {
    OLED.writeStringNewLine("TOO CLOSE!");
    basic.showIcon(IconNames.Surprised);
  } else if (distance < 25) {
    OLED.writeStringNewLine("Hello there!");
    basic.showIcon(IconNames.Happy);
  } else if (distance < 50) {
    OLED.writeStringNewLine("Come closer...");
    basic.showIcon(IconNames.Asleep);
  } else {
    OLED.writeStringNewLine("Waiting...");
    basic.clearScreen();
  }

  serial.writeValue("distance", distance);
  serial.writeValue("servo", servoAngle);
  basic.pause(200);
});

Security / Alert System

A simple security system with three states: DISARMED, ARMED, and TRIGGERED. PIR motion sensor detects intruders, buzzer sounds the alarm, OLED shows status. Press A to arm (with countdown), B to disarm. Demonstrates state machines — how every real product manages modes. Components: PIR + Buzzer + OLED. Requires “OLED_SSD1306” MakeCode extension.

microbit.org code link

// @ts-nocheck
// A simple security system: PIR motion sensor triggers an alarm with buzzer
// and OLED display. Press button A to arm, button B to disarm.
// Demonstrates a state machine — the system is always in one of three states:
// DISARMED → ARMED → TRIGGERED.
//
// MakeCode extension required:
//   Click "Extensions" and search for "OLED". Add "OLED_SSD1306" by Tinkertanker.
//
// Physical setup:
//   PIR motion sensor:
//     Connect S → micro:bit pin 0
//     Connect V → micro:bit 3V
//     Connect G → micro:bit GND
//   Passive buzzer:
//     Connect S → micro:bit pin 1
//     Connect V → micro:bit 3V
//     Connect G → micro:bit GND
//   OLED display (I2C):
//     Connect GND → micro:bit GND
//     Connect VCC → micro:bit 3V
//     Connect SCL → micro:bit pin 19
//     Connect SDA → micro:bit pin 20

// --- Setup ---
// States: 0 = DISARMED, 1 = ARMED, 2 = TRIGGERED
let state = 0;
let motion = 0;
OLED.init(128, 64);
OLED.writeStringNewLine("Security System");
OLED.writeStringNewLine("Press A to arm");

// --- Event Handlers ---
// Button A: arm the system
input.onButtonPressed(Button.A, function () {
  if (state == 0) {
    state = 1;
    OLED.clear();
    OLED.writeStringNewLine("ARMING...");
    // 5 second countdown before armed
    for (let i = 5; i > 0; i--) {
      basic.showNumber(i);
      basic.pause(1000);
    }
    OLED.clear();
    OLED.writeStringNewLine("ARMED");
    OLED.writeStringNewLine("Press B to disarm");
    basic.showIcon(IconNames.SmallDiamond);
    serial.writeLine("System armed");
  }
});

// Button B: disarm the system
input.onButtonPressed(Button.B, function () {
  state = 0;
  music.stopAllSounds();
  OLED.clear();
  OLED.writeStringNewLine("DISARMED");
  OLED.writeStringNewLine("Press A to arm");
  basic.showIcon(IconNames.Heart);
  serial.writeLine("System disarmed");
});

// --- Main Loop ---
basic.forever(function () {
  if (state == 1) {
    // ARMED: check for motion
    motion = pins.digitalReadPin(DigitalPin.P0);
    if (motion == 1) {
      state = 2;
      serial.writeLine("MOTION DETECTED!");
    }
  }

  if (state == 2) {
    // TRIGGERED: alarm!
    OLED.clear();
    OLED.writeStringNewLine("!! ALERT !!");
    OLED.writeStringNewLine("Motion detected!");
    OLED.writeStringNewLine("Press B to disarm");
    basic.showIcon(IconNames.Skull);
    music.play(
      music.tonePlayable(988, music.beat(BeatFraction.Quarter)),
      music.PlaybackMode.InBackground,
    );
    basic.pause(200);
    basic.showIcon(IconNames.No);
    basic.pause(200);
  }

  basic.pause(100);
});

Environmental Dashboard

A mini weather station: reads temperature, humidity, UV level, and light on one OLED display. Multiple sensors feeding one screen — teaches data visualization on physical displays. Components: DHT11 + UV sensor + OLED. Requires “OLED_SSD1306” and “DHT11_DHT22” MakeCode extensions.

microbit.org code link

// @ts-nocheck
// A mini weather station: reads temperature, humidity, and UV level,
// then displays all readings on an OLED screen. A tiny environmental
// dashboard you can carry around or mount in a room.
//
// MakeCode extensions required:
//   Click "Extensions" and search for "OLED". Add "OLED_SSD1306" by Tinkertanker.
//   Click "Extensions" and search for "DHT11". Add "DHT11_DHT22" by Alan Krantas.
//
// Physical setup:
//   DHT11 temperature/humidity sensor:
//     Connect S → micro:bit pin 0
//     Connect V → micro:bit 3V
//     Connect G → micro:bit GND
//   UV sensor (GUVA-S12SD):
//     Connect S → micro:bit pin 1
//     Connect V → micro:bit 3V
//     Connect G → micro:bit GND
//   OLED display (I2C):
//     Connect GND → micro:bit GND
//     Connect VCC → micro:bit 3V
//     Connect SCL → micro:bit pin 19
//     Connect SDA → micro:bit pin 20

// --- Setup ---
let temp2 = 0;
let hum2 = 0;
let uvRaw = 0;
let uvIdx = 0;
OLED.init(128, 64);
OLED.writeStringNewLine("Environment");
OLED.writeStringNewLine("Dashboard");
basic.pause(2000);

// --- Main Loop ---
basic.forever(function () {
  // Read DHT11
  dht11_dht22.queryData(DHTtype.DHT11, DigitalPin.P0, true, false, true);
  temp2 = dht11_dht22.readData(dataType.temperature);
  hum2 = dht11_dht22.readData(dataType.humidity);

  // Read UV sensor
  uvRaw = pins.analogReadPin(AnalogPin.P1);
  uvIdx = Math.round(Math.map(uvRaw, 0, 1023, 0, 11));

  // Update OLED display
  OLED.clear();
  OLED.writeStringNewLine("Temp:  " + temp2 + " C");
  OLED.writeStringNewLine("Humid: " + hum2 + " %");
  OLED.writeStringNewLine("UV:    " + uvIdx + " / 11");
  // Built-in light level for comparison
  OLED.writeStringNewLine("Light: " + input.lightLevel());

  // Log to serial
  serial.writeValue("temp", temp2);
  serial.writeValue("humidity", hum2);
  serial.writeValue("uvIndex", uvIdx);
  serial.writeValue("light", input.lightLevel());
  basic.pause(2000);
});

Plant Nanny

A plant care monitor: reads soil moisture, UV light, and temperature, then shows plant health on an OLED screen. When soil is too dry, a servo opens a water valve (or just alerts you to water manually). Components: Soil moisture sensor + UV sensor + Servo + OLED. Requires “OLED_SSD1306” MakeCode extension.

microbit.org code link

// @ts-nocheck
// A plant care monitor: reads soil moisture, temperature, and UV light,
// then shows plant health status on an OLED screen. When soil is too dry,
// the servo opens a water valve (or you can just use the alert to water manually).
//
// MakeCode extensions required:
//   Click "Extensions"
//  search for "OLED". Add "OLED_SSD1306" by Tinkertanker.
//  search for "servo". Add "microservo" extension
//
// Physical setup:
//   Soil moisture sensor:
//     Connect S → micro:bit pin 0 (analog read)
//     Connect V → micro:bit 3V
//     Connect G → micro:bit GND
//     Insert the metal prongs into the soil.
//   UV sensor (GUVA-S12SD):
//     Connect S → micro:bit pin 1 (analog read)
//     Connect V → micro:bit 3V
//     Connect G → micro:bit GND
//   Servo (for water valve — optional):
//     Connect signal → micro:bit pin 2
//     Connect VCC → battery pack positive
//     Connect GND → micro:bit GND
//   OLED display (I2C):
//     Connect GND → micro:bit GND
//     Connect VCC → micro:bit 3V
//     Connect SCL → micro:bit pin 19
//     Connect SDA → micro:bit pin 20

// --- Setup ---
let soilMoisture = 0;
let soilPercent = 0;
let uvRaw2 = 0;
let uvIdx2 = 0;
let dryThreshold = 30; // Below this % = needs water

OLED.init(128, 64);
OLED.writeStringNewLine("Plant Nanny");
OLED.writeStringNewLine("Starting...");
servos.P2.setAngle(0); // Valve closed
basic.pause(2000);

// --- Main Loop ---
basic.forever(function () {
  // Read soil moisture (higher reading = wetter soil)
  soilMoisture = pins.analogReadPin(AnalogPin.P0);
  soilPercent = Math.round(Math.map(soilMoisture, 0, 1023, 0, 100));

  // Read UV sensor
  uvRaw2 = pins.analogReadPin(AnalogPin.P1);
  uvIdx2 = Math.round(Math.map(uvRaw2, 0, 1023, 0, 11));

  // Update OLED display
  OLED.clear();
  OLED.writeStringNewLine("Soil: " + soilPercent + " %");
  OLED.writeStringNewLine("UV:   " + uvIdx2 + " / 11");
  OLED.writeStringNewLine("Temp: " + input.temperature() + " C");

  // Check if plant needs water
  if (soilPercent < dryThreshold) {
    OLED.writeStringNewLine("NEEDS WATER!");
    basic.showIcon(IconNames.Sad);
    // Open water valve (servo to 90 degrees)
    servos.P2.setAngle(90);
    music.play(music.tonePlayable(262, music.beat(BeatFraction.Half)), music.PlaybackMode.InBackground);
  } else {
    OLED.writeStringNewLine("Plant is happy");
    basic.showIcon(IconNames.Happy);
    // Close water valve
    servos.P2.setAngle(0);
  }

  serial.writeValue("soil", soilPercent);
  serial.writeValue("uvIndex", uvIdx2);
  basic.pause(5000);
});

Wireless Social Network

Simple wireless messaging between two micro:bits using the radio. Press button A to send a message; received messages display on the LED screen.

microbit.org code link

// @ts-nocheck
// Simple wireless messaging between two micro:bits using the radio.
// Press button A to send a message; received messages display on the LED screen.

// --- Setup ---
radio.setGroup(1);

// --- Event Handlers ---
// Sends a message when button A is pressed
input.onButtonPressed(Button.A, function () {
  radio.sendString("Micro Chat");
});

// Runs when a radio message is received
radio.onReceivedString(function (receivedString: string) {
  basic.showString(receivedString);
});

Future

MakeCode Data Logging

The Microbit MakeCode editor has built-in data logging that saves to the microbit’s flash memory. You can log data, then download it as a CSV file when you connect the Microbit to your computer via USB.

Add SD card module (standalone logging)

Connect an SD card reader via SPI pins: Requires additional hardware (~$5)