Micro:bitArduinoMakeCodePythonC++ExplorersBuilders

Rainbow lightsaber: Micro:bit or Arduino

Addressable LEDs: one data wire tells every LED its own colour. Make a rainbow that runs, a lightsaber that ignites with a button, and a blade that changes colour. On the Micro:bit or on the Arduino.

QR code to this pageScan to open
Your board:

1What you need

A glowing LED strip connected to a Micro:bit on a breadboard
  • Addressable LED strip (ElecFreaks rainbow LED strip): 10 LEDs. Strips plug together, each one adds 10 LEDs
  • A breadboard and jumper wires
  • Micro:bit V2 and a USB cable
  • Breakout board for the Micro:bit (pin:bit)
  • For level 4, if you like: a battery pack, 2 × AAA, to swing the Micro:bit without a cable
  • Arduino UNO (R3 or WiFi Rev2) and its USB cable
  • 2 push buttons (level 3) and a knob, a 10 kΩ potentiometer (level 4)
  • The Arduino IDE with the Adafruit NeoPixel library (see level 1)
The LED strip with its three wires: red, yellow and black, and the labels +5V, DIN, DO and GND printed on the strip The pin:bit breakout board: the Micro:bit slot and a row of pins labelled 0, 1, 2 and so on up to 3v and gnd

Look closely at the strip. Each LED is a tiny square with three colours inside: red, green and blue, and a tiny chip. Between the LEDs you can read +5V, DIN (data in), DO (data out) and GND, with small arrows. The arrows show which way the data travels: connect your board to the end where they start.

LEDs like these are called addressable LEDs: every LED has a number, its address, and the program sets each one on its own. You will also see the names NeoPixel (the MakeCode extension and the Arduino library) and ZIP LEDs.

New here? Start at level 1 and follow the code. Done quickly? Every level has a "your turn" task, and the challenges at the end go much further.

The breakout board with the Micro:bit: the strip's red wire goes to 3v, yellow to pin 1, black to gnd; a USB cable or a battery pack powers the Micro:bit

Push the Micro:bit into the breakout board, and the breakout board into the breadboard. The pin names are printed on the board: find 1, 3v and gnd (GND, ground). We use pin 1 (P1 in MakeCode) for the data, because pin 0 also plays the music. Power the Micro:bit with the USB cable; the battery pack is only for swinging it around at level 4.

Keep the brightness low (we use 40 out of 255). One strip on full white needs about 600 mA, three times more than the Micro:bit can give. Then it restarts or the colours go wrong.

The Arduino UNO with a breadboard: the strip's yellow data wire to pin 6, red and black to the plus and minus rails, push buttons on pins 2 and 3, a knob on A0, 5V and GND to the rails

Build it bit by bit: levels 1 and 2 need only the strip. Add the two buttons for level 3 and the knob for level 4.

The strip gets 5 V from the Arduino through the breadboard rails: red to the + rail, black to the − rail, and the yellow data wire to pin 6. The two buttons sit across the middle gap. The knob has three legs: the outer ones go to + and −, the middle one to A0.

Keep the brightness low (we use 40 out of 255). One strip on full white needs about 600 mA; a laptop's USB port gives the Arduino about 500 mA in total.

3Try it on screen

The LED strip lab works like the real strip, with three strips plugged together: 30 LEDs. Try the rainbow, the running light and the lightsaber, and move the brightness slider: watch how much power the strip needs.

Open the LED strip lab in a new tab

How addressable LEDs workmore depth

Not every LED strip works like this. In a simple strip all LEDs share the same wires, so they all show the same colour. In an addressable LED strip every LED has its own tiny chip, so each LED can show its own colour.

The board sends a long row of bits on the yellow wire, one after the other: 24 bits for each LED, 8 for green, 8 for red and 8 for blue. 8 bits give a number from 0 to 255, so each LED can show 256 × 256 × 256 = about 16.7 million colours.

The first chip keeps the first 24 bits and passes everything else on through DO. The second chip keeps the next 24 bits, and so on. So the position in the row is the LED's address: one data wire is enough for the whole strip, even with many strips plugged together.

The bits travel at 800,000 per second: 10 LEDs get their colours in about 0.3 milliseconds.

Power: each of the three colours takes about 20 mA at full brightness. One LED on white: 60 mA. One strip of 10 LEDs on white: 600 mA, and every extra strip adds 600 mA. The Micro:bit can give the strips roughly 200 mA, an Arduino on USB about 500 mA, so brightness 40 of 255 is a good limit.

Brightness does not send less data: the program makes every colour number smaller before it sends it.

See it happen in the lab above: Inside the strip, then Send the next 24 bits.

4Program it, level by level

Open MakeCode, start a new project and add the extension: Extensions, search neopixel. Its blocks appear in the blue Neopixel drawer. Go as far as you like: every level works on its own. Click a level to open it.

Start the MakeCode tutorial all 4 levels, step by step

The Arduino needs a library for the LED strip: ready-made code that someone wrote and shared, so you do not have to send the 24 bits per LED yourself.

  1. In the Arduino IDE: Tools, Manage Libraries. Search Adafruit NeoPixel and click Install.
  2. The first line of the sketch brings the library in: #include <Adafruit_NeoPixel.h>
  3. Then you make a strip object: Adafruit_NeoPixel strip(10, 6, NEO_GRB + NEO_KHZ800); means 10 LEDs on pin 6, colours in the order green, red, blue.
  4. Now you call the library's functions on it: strip.begin(), strip.setBrightness(40), strip.setPixelColor(index, color), strip.show(), strip.clear(), strip.rainbow(hue).

Go as far as you like: every level works on its own. Click a level to open it.

Level 1: Rainbowfollow the code

on start
set strip to NeoPixel at pin P1 with 10 leds as RGB (GRB format)
strip set brightness 40
strip show rainbow from 1 to 360
forever
strip rotate pixels by 1
strip show
pause (ms) 50

rotate moves every colour one LED along, and the last one comes back to the start. show sends the new colours to the strip: nothing changes until you show.

Your turn: make it run faster, slower, or the other way (rotate by -1). Rainbow from 1 to 120?

#include <Adafruit_NeoPixel.h>

const int stripPin = 6;
const int ledCount = 10;
Adafruit_NeoPixel strip(ledCount, stripPin, NEO_GRB + NEO_KHZ800);   // the strip object from the library

unsigned int firstHue = 0;   // where the rainbow starts on the colour wheel (0 to 65535)

void setup() {
  strip.begin();
  strip.setBrightness(40);   // of 255: keep it low
}

void loop() {
  strip.rainbow(firstHue);   // the whole colour wheel along the strip
  strip.show();              // send the colours to the strip
  firstHue += 512;           // move the rainbow a little
  delay(20);
}

Sketch file: level1_rainbow.ino

rainbow paints the whole colour wheel along the strip, starting at firstHue. Adding 512 each time moves it a little. show sends the new colours to the strip: nothing changes until you show.

Your turn: make it run faster, slower, or the other way (firstHue -= 512).

Level 2: Running lighta loop

forever
for index from 0 to 9
strip clear
strip set pixel color at index to red
strip show
pause (ms) 30

The LEDs are numbered from 0, so 10 LEDs go from 0 to 9. LED 0 is the one next to the wires: its address is 0. Two strips plugged together? Then it is 20 LEDs, from 0 to 19.

Your turn: run back again (set pixel color at 9 − index), or leave a tail: remove strip clear and turn off the LED three places behind.

#include <Adafruit_NeoPixel.h>

const int stripPin = 6;
const int ledCount = 10;
Adafruit_NeoPixel strip(ledCount, stripPin, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();
  strip.setBrightness(40);
}

void loop() {
  for (int index = 0; index < ledCount; index++) {
    strip.clear();                                         // all LEDs off
    strip.setPixelColor(index, strip.Color(255, 0, 0));    // LED number index: red, green, blue
    strip.show();
    delay(30);
  }
}

Sketch file: level2_running_light.ino

The LEDs are numbered from 0, so 10 LEDs go from 0 to 9. strip.Color(255, 0, 0) makes a colour from red, green and blue, each 0 to 255.

Your turn: run back again (ledCount - 1 - index), or leave a tail of dimmer LEDs.

Level 3: Lightsaberbuttons

on button A pressed
for index from 0 to 9
strip set pixel color at index to blue
strip show
pause (ms) 20
on button B pressed
for index from 0 to 9
strip set pixel color at 9 − index to black
strip show
pause (ms) 20

Button A ignites the blade from the handle to the tip, button B pulls it back. Keep the level 1 on start blocks, but without the rainbow.

Your turn: pick your own blade colour with a red, green, blue block. Add a sound on the Micro:bit speaker when the blade ignites.

#include <Adafruit_NeoPixel.h>

const int stripPin = 6;
const int ledCount = 10;
const int igniteButtonPin = 2;
const int retractButtonPin = 3;
Adafruit_NeoPixel strip(ledCount, stripPin, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();
  strip.setBrightness(40);
  strip.show();                              // start with all LEDs off
  pinMode(igniteButtonPin, INPUT_PULLUP);    // LOW when pressed
  pinMode(retractButtonPin, INPUT_PULLUP);
}

void loop() {
  if (digitalRead(igniteButtonPin) == LOW) {
    for (int index = 0; index < ledCount; index++) {          // from the handle to the tip
      strip.setPixelColor(index, strip.Color(0, 0, 255));
      strip.show();
      delay(20);
    }
  }
  if (digitalRead(retractButtonPin) == LOW) {
    for (int index = ledCount - 1; index >= 0; index--) {     // from the tip back to the handle
      strip.setPixelColor(index, 0);
      strip.show();
      delay(20);
    }
  }
}

Sketch file: level3_lightsaber.ino

The ignite button lights the blade from the handle to the tip, the retract button turns it off from the tip. INPUT_PULLUP makes a pin read LOW while its button is pressed.

Your turn: your own blade colour with strip.Color(red, green, blue).

Level 4: Swing itTurn the knobmotion sensoranalog input

on shake
strip show color white
pause (ms) 80
strip show color hue hue saturation 100 luminosity 50
forever
set hue to map acceleration (mg) x from −1023 to 1023 to 0 to 359
strip show color hue hue saturation 100 luminosity 50
pause (ms) 50

The Micro:bit feels movement with its accelerometer. A swing flashes the blade white. Tilting left and right gives a number from −1023 to 1023, and map turns it into a colour on the colour wheel (the hue, 0 to 359).

Your turn: only change colour while the blade is on. Hint: a variable lit that buttons A and B set to true and false.

#include <Adafruit_NeoPixel.h>

const int stripPin = 6;
const int ledCount = 10;
const int igniteButtonPin = 2;
const int retractButtonPin = 3;
const int knobPin = A0;
Adafruit_NeoPixel strip(ledCount, stripPin, NEO_GRB + NEO_KHZ800);

bool lit = false;   // is the blade on?

// The knob gives 0 to 1023; map turns it into a place on the colour wheel (0 to 65535).
uint32_t bladeColor() {
  long hue = map(analogRead(knobPin), 0, 1023, 0, 65535);
  return strip.gamma32(strip.ColorHSV(hue));
}

void setup() {
  strip.begin();
  strip.setBrightness(40);
  strip.show();
  pinMode(igniteButtonPin, INPUT_PULLUP);
  pinMode(retractButtonPin, INPUT_PULLUP);
}

void loop() {
  if (digitalRead(igniteButtonPin) == LOW && !lit) {
    for (int index = 0; index < ledCount; index++) {
      strip.setPixelColor(index, bladeColor());
      strip.show();
      delay(20);
    }
    lit = true;
  }
  if (digitalRead(retractButtonPin) == LOW && lit) {
    lit = false;
    for (int index = ledCount - 1; index >= 0; index--) {
      strip.setPixelColor(index, 0);
      strip.show();
      delay(20);
    }
  }
  if (lit) {
    strip.fill(bladeColor());   // the whole blade in the knob's colour
    strip.show();
  }
  delay(20);
}

Sketch file: level4_knob.ino

The Arduino has no motion sensor, so a knob turns the colour. analogRead gives a number from 0 to 1023, and map turns it into a place on the colour wheel, 0 to 65535. The variable lit remembers if the blade is on.

Your turn: print the knob's value in the Serial Monitor (Serial.println).

The finished lightsaber

The finished program, all levels in one, for your board. Try it yourself first; if you are really stuck, ask your teacher for the code.

Next step, real Python: lightsaber.py is the same lightsaber in MicroPython. Open it in python.microbit.org and download it to the Micro:bit.

5Challenges

Challenges

  • Comet: a bright head with a tail that fades, using set brightness or darker colours behind it.
  • Sound meter: show the microphone's sound level as a bar with show bar graph, green at the bottom, red at the top.
  • Swing button: a third button on pin 4 flashes the blade white (the finished program has it).
  • Level ruler: one LED that runs to the lower end when you tilt the strip, like a bubble in a spirit level.
  • Knob bar: the knob lights 0 to 10 LEDs, like a volume bar.
  • Lightsaber sounds: a low hum while the blade is on, a crash sound on every swing (a piezo buzzer and tone()).

Explore further

  • Your own rainbow: build it pixel by pixel with hue, without show rainbowColorHSV, without rainbow.
  • Two sabers: two Micro:bits by radio. When one swings, the other flashes too.
  • Swing strength: use acceleration (mg) strength instead of shake: the harder the swing, the brighter the flash.
  • Without waiting: use millis() instead of delay(), so the buttons work at any moment.
  • Power budget: write a program that adds up the colour numbers of all LEDs and keeps the total current under 200 mA.

Poster to print (PDF)

Poster to print (PDF)