I’ve already written about the Arduino owl night light I built for one of my daughters. Her sister got a torch. Not just any torch, though. A torch that does colours.

So in October 2024 I picked up an old yellow lantern torch from a charity shop, pulled its guts out and put an Arduino in it. It runs on a rechargeable battery, has a knob for colour, a knob for brightness and a button that turns on a rainbow mode. Here’s what it does and how it works, circuit diagram and code included.

Yellow lantern torch with a silver push button, a small red LED, a red knob and a white knob on the side
The finished torch. Charity shop body, Arduino brains.

What does it do?

The controls are all on the side of the torch:

  • Black button on top of the handle: the main power switch.
  • Silver button: changes mode. Each press goes from on, to rainbow, to off, then back to on. When you first switch the torch on it starts in normal mode.
  • Red knob: picks the colour. Turn it and the beam goes from red to green to blue and back towards red. Turn it right to the end and it goes white.
  • White knob: brightness.
  • Small red LED: a low battery warning, there to flash when the battery drops below 15%.

In rainbow mode the torch cycles through every colour on its own, and the red knob changes how fast it goes. At the quick end it does a full lap in about 5 seconds. At the slow end it takes close to 2 minutes.

The Arduino torch shining aqua and blue light onto a ceiling
Aqua and blue on the wall. The beam comes out in a fun flower shape.

What’s inside?

The brains is a SparkFun Qduino Mini. It’s a small Arduino compatible board that runs at 3.3V, and the best bit for a torch is that it has a LiPo battery charger and a battery fuel gauge built in. That means no separate charging board, and the code can ask the battery how full it is.

The parts list is pretty short:

  • SparkFun Qduino Mini
  • 3.7V 1100mAh LiPo battery
  • 3W RGB LED on a heat sink
  • 3 N-channel MOSFETs (60V 30A), one for each colour
  • 2 potentiometers for the knobs
  • 1 silver push button for the modes
  • A small red LED and a 220Ω resistor for the low battery warning
  • 10k resistors for the button and the MOSFETs
  • A micro USB port on the back of the torch for charging
  • One yellow lantern torch from a charity shop

I took out the original LEDs and fitted the 3W RGB LED and its heat sink in the middle of the old reflector.

Front of the Arduino torch glowing blue in the middle of the original reflector
The 3W RGB LED sitting in the middle of the original reflector.
Micro USB charging port fitted to the back of the yellow Arduino torch
The micro USB charging port on the back of the torch. It connects to the Qduino Mini, which charges the battery.

The build

It started as a pile of parts and wires on the garage floor.

LiPo battery, MOSFETs, Qduino Mini and a tangle of wires laid out on a garage floor
Everything wired up on the garage floor before going anywhere near the torch.
Holding the Qduino Mini board with wires running to the MOSFETs, LiPo battery and LED
The Qduino Mini, with the MOSFETs, battery and LED hanging off it.
Qduino Mini and LiPo battery wired to the 3W LED on its round heat sink
The 3W LED on its heat sink, bottom right.
Opened yellow torch body on a table with the electronics about to go inside
Moving into the torch body.
Inside the yellow torch body packed with coloured wires, MOSFETs and the Qduino
Fitting it all in. Neat it is not, but it works.

The circuit diagram

Here’s how everything is wired up. The pin numbers match the code.

Circuit diagram of the Arduino torch showing the Qduino Mini, knobs on A5 and A2, button on D12, LED drivers on D9, D6 and D5 and the LiPo battery
Circuit diagram of the Arduino torch.

Here’s how the pieces hook up:

  • Power: the LiPo battery goes through the power switch on top of the handle. After the switch it feeds both the Qduino Mini and the positive side of the LED. The micro USB port on the back charges the battery through the Qduino’s built in charger.
  • Knobs are potentiometers wired across 3.3V and ground. The colour knob is on A5 and the brightness knob is on A2. The Arduino reads each one as a number from 0 to 1023.
  • The silver button goes to D12, with a 10k resistor down to ground. The pin reads LOW until the button is pressed, then the button connects it to 3.3V and it reads HIGH.
  • The low battery LED is on D0 through a 220Ω resistor.
  • The 3W RGB LED is driven from D9 (red), D6 (green) and D5 (blue). Each pin goes to the gate of its own MOSFET, with a 10k resistor from the gate to ground so it stays off while the Arduino starts up. The LED’s common positive goes to the battery, and each MOSFET connects one colour to ground when the Arduino turns it on.
  • Dimming comes from PWM. D9, D6 and D5 are PWM pins, which means the Arduino can turn them on and off really fast. The longer each pin spends on, the brighter that colour looks.

How does the code work?

The sketch is called LEDtorchv6, so it took a few goes. The colour wheel, rainbow mode and button handling all came straight from the owl night light.

The torch is always in one of three modes: on, rainbow or off. Every time through the main loop it reads the two knobs and the button, changes mode if the button was pressed, then does whatever that mode does.

Turning a knob into a colour

The colour knob gives a number from 0 to 1023. Like the night light, the code splits that into thirds:

  • Bottom third: red fades out while green fades in
  • Middle third: green fades out while blue fades in
  • Top third: blue fades out while red fades in

The torch has one extra trick. From 1000 up to the end of the knob it turns all three colours on at full, which gives white. Sometimes you just need a normal torch.

Brightness

Each colour value gets multiplied by the brightness knob’s position. The code also caps the maximum at 200 out of 255, or about 78% of full power. At 100% it was too bright, and the LED got hot.

Rainbow mode

Rainbow mode uses the same colour wheel, but steps a counter from 0 to 1023 and starts again. The red knob sets the wait between steps, anywhere from about 5 to 105 milliseconds. Over 1,023 steps that works out to roughly 5 seconds per lap at the fast end and close to 2 minutes at the slow end. Because the white section is part of the wheel, each lap ends with a quick flash of white.

Keeping an eye on the battery

Once a minute the code asks the Qduino’s fuel gauge how much charge is left. Below 15%, the red LED on the side flashes on and off every half second.

Making it kid proof

Like the night light, the code ignores the button for half a second after each press. That stops one enthusiastic press being read as three.

If you like this sort of dad project, check out the owl night light or the playing time calculator for kids’ sport.

The full code

Here’s the whole sketch if you want to build your own. It uses the Qduino library for the fuel gauge, so you’ll need to install that in the Arduino IDE first.

Show the Arduino code (LEDtorchv6.ino)
#include "Arduino.h"
#include "Wire.h"
#include "Qduino.h"

fuelGauge battery;  // initialize the library
qduino q;

int battlow_led = 0; //battery low indicator LED
int torchbuttonstate = 0;

int red = 9; //red LED
int blue = 5;  //blue LED
int green = 6;  // the PWM pin the green LED is attached to

int redVal = 0;   // Variables to store the values to send to the pins
int grnVal = 0;
int bluVal = 0;
int potVal = 0; // Variable to store the input from the potentiometer

int mode = 1;
int disablebutton=0;
int buttoninterval = 500;
float millisinterval = 15;
int battery_flash_mode=0;
int batt_charge = 100;

float maxbrightness = 200;
float brightness =0;

unsigned long previousbuttonMillis = 0;
unsigned long previousMillis = 0;
unsigned long batteryflashMillis = 0;
unsigned long batterycheckMillis = 0;

const int torchbutton = 12;

void setup()
{

  Wire.begin();

  //Serial.begin(9600);  // start Serial port
  battery.setup();  // setup fuel gauge
  q.setup();

  //while(!Serial);  // wait for Serial port to be opened

  pinMode(battlow_led, OUTPUT);

  pinMode(red, OUTPUT);
  pinMode(blue, OUTPUT);
  pinMode(green, OUTPUT);

  //Serial.println("Begin");
}

void get_colour_old(int potVal) //give it a number 1-1023 and it will return 3 colours - RGB
{
  if (potVal < 341)  // Lowest third of the potentiometer's range (0-340)
  {
    potVal = (potVal * 3) / 4; // Normalize to 0-255

    redVal = 256 - potVal;  // Red from full to off
    grnVal = potVal;        // Green from off to full
    bluVal = 1;             // Blue off
  }
  else if (potVal < 682) // Middle third of potentiometer's range (341-681)
  {
    potVal = ( (potVal-341) * 3) / 4; // Normalize to 0-255

    redVal = 1;            // Red off
    grnVal = 256 - potVal; // Green from full to off
    bluVal = potVal;       // Blue from off to full
  }
  else  // Upper third of potentiometer"s range (682-1023)
  {
    potVal = ( (potVal-683) * 3) / 4; // Normalize to 0-255

    redVal = potVal;       // Red from off to full
    grnVal = 1;            // Green off
    bluVal = 256 - potVal; // Blue from full to off
  }
}

void get_colour(int potVal) //give it a number 1-1023 and it will return 3 colours - RGB
{
  if (potVal < 341)  // Lowest third of the potentiometer's range (0-340)
  {
    potVal = (potVal * 3) / 4; // Normalize to 0-255

    redVal = 256 - potVal;  // Red from full to off
    grnVal = potVal;        // Green from off to full
    bluVal = 1;             // Blue off
  }
  else if (potVal < 682) // Middle third of potentiometer's range (341-681)
  {
    potVal = ( (potVal-341) * 3) / 4; // Normalize to 0-255

    redVal = 1;            // Red off
    grnVal = 256 - potVal; // Green from full to off
    bluVal = potVal;       // Blue from off to full
  }
  else  if (potVal < 1000) // Upper third of potentiometer"s range (682-1020)
  {
    potVal = ( (potVal-683) * 3) / 4; // Normalize to 0-255

    redVal = potVal;       // Red from off to full
    grnVal = 1;            // Green off
    bluVal = 256 - potVal; // Blue from full to off
  }
  else
  {
    redVal = 255;            // Make white at the very end
    grnVal = 255;
    bluVal = 255;
  }
}

void loop()
{

  unsigned long currentMillis = millis();
  unsigned long currentbuttonMillis = millis();

  //read buttons
  torchbuttonstate = digitalRead(torchbutton);

  //mode=1;

  //read knobs
  float dimmingknob = analogRead(A2); // green wire
  float colourknob = analogRead(A5); // bue wire
  //int charge = battery.chargePercentage();  // get %
  //battery.reset();  // reset for next data request

  if (currentbuttonMillis - previousbuttonMillis >= buttoninterval)
    {disablebutton=0;}

  if ((torchbuttonstate == HIGH)&&(disablebutton==0))
    {

      disablebutton=1; //put timer on to stop reading button
      previousbuttonMillis =millis();

      mode=mode +1;
      if (mode==3){mode=0;}
      potVal=0;
    }

   if (mode==0) // Off
  {

    delay(5);
    analogWrite(red, 0);
    analogWrite(blue, 0);
    analogWrite(green, 0);

  }
  else if (mode==1) // normal mode - colour changing and dimming with knobs
  {
    brightness = ((dimmingknob/1024)*(maxbrightness/255));
    //Serial.println("brightness");
    //Serial.println(brightness);
    //analogWrite(red, random(10, 255));
    // analogWrite(blue, random(10, 255));
    //analogWrite(green, random(10, 255));

    //Serial.println("Dimming");
    //Serial.println(dimmingknob);
    //Serial.println("Colour");
    //Serial.println(colourknob);

    potVal=colourknob;

    get_colour(potVal);

    analogWrite(red, (redVal*brightness));   // Write values to LED pins
    analogWrite(green, (grnVal*brightness));
    analogWrite(blue, (bluVal*brightness));

  }
  else if (mode==2) //  button - first press - rainbow cycle
  {

    brightness = ((dimmingknob/1024)*(maxbrightness/255));
    millisinterval = ((colourknob/1024)*100)+5;
    //Serial.println("Millis");
   // Serial.println(millisinterval);

    get_colour(potVal);
    analogWrite(red, (redVal*brightness));   // Write values to LED pins
    analogWrite(green, (grnVal*brightness));
    analogWrite(blue, (bluVal*brightness));

    if (currentMillis - previousMillis >= millisinterval)
    {
      potVal=potVal+1;

      previousMillis =millis();
    }
    if (potVal>1022){potVal=0;}
    //Serial.println(potVal);
  }

  //check battery status and if low, flash LED
  //check battery every minute
  if (currentMillis - batterycheckMillis > 60000 )
  {
    batt_charge = battery.chargePercentage();
    battery.reset();
    //Serial.println("Batt charge: ");
    //Serial.println(batt_charge);
    batterycheckMillis =millis();

  }

  if (batt_charge <= 15)
  {
    if (currentMillis - batteryflashMillis >= 500)
    {
      if (battery_flash_mode == 0)
      {
        digitalWrite(battlow_led, HIGH);
        battery_flash_mode =1;
      }
      else
      {
        digitalWrite(battlow_led, LOW);
        battery_flash_mode =0;
      }
      batteryflashMillis =millis();
    }
  }

  delay(10);  // wait a second to read again
}

If you build one, I’d love to see it.