Introduction: Flickering Candle Bridge

This instructable shows how to turn a simple candle bridge with static light into a nice glowing mood light with endless variations of flickering lights, twinkling, wave patterns and whatnot. I bought from After Christmas Sales a candle bridge for 8 €. It has 7 led lights and some 33 V 3 W wall adapter. It shines with a bright and warm white colour and will be perfect for this project, where I will put an Arduino to make the candles flicker. The most popular Arduino is the Arduino Uno. In this project, I'll be using an Arduino Mega 2560.

I'm going to ditch the 30 V power supply and will use a simple 5 V power bank meant for mobile phones as the power supply.

A good thing to know about power banks is that they have an inner circuit, which transforms the battery 3.7 V to 5 V. Because the process uses some power, the power bank shuts itself, if it is not used. If the power bank is used for Arduino based DIY gadgets, the gadget can't just put itself in power saving sleep and start again after a few minutes. That will shut off the power bank. This flickering candle bridge has no sleep mode. It uses constantly power, keeping the power bank active, until the power cable is pulled off.

The video shows the candle bridge in static mode and in full flickering. The full flickering is really quite annoying for the eyes, while the video smoothens it out a bit. After the hardware has been fixed, including cutting cables, soldering new connections and adding some components, all the desired light patterns are created by writing code for the Arduino. The patterns I include in this instructable are:

  • 4 different flickering lights imitating real candles
  • 2 different twinkling (random flashing of otherwise static lights)
  • 2 different wave patterns
  • simple static light

Switching patterns happens through a push button, the single user interface element. The more patterns one wants and the more adjustability one wants, the more buttons and knobs one has to add. But the beauty lies in simplicity. Keep the number of selectable patterns down. Choose the best settings while coding and testing, not by adding lots of controls to the hardware.

Supplies

  • 1 LED candle bridge with 7 bulbs. Make sure it is a low voltage DC model, either with batteries or with a wall mount power source, which transforms the deadly 110 - 240 V AC to some 6 - 30 V DC. So it's perfectly safe to hack the candle bridge.
  • 1 Arduino Mega (any other microcontroller will do, just make sure you can program it)
  • 1 prototyping breadboard
  • jumper wires and other wire
  • soldering tool
  • multimeter
  • 7 resistors, 120 Ω
  • 1 push button (I'll show how you can use the built in button on an Arduino instead)
  • A darlington transistor IC for 7 transistors, ULN2803AP will do (If you use an Arduino Uno or Meaga, you don't really need this)
  • A 5 V power bank meant for mobile phones

Step 1: Examin What You Got

    Find out on what voltage each LED operates and how much current flows through.

    1. Open the bottom of the candle bridge. Find the two wires that go to one candle.
    2. Strip off some insulation from the cables revealing the copper wires without cutting the copper wires.
    3. Turn on the lights (relax, it's only a few volts) and measure the voltage over the revealed copper wires.
    4. Cut the cable at one of the measuring points (at this point the lights go off, of course), strip off some insulation (3 - 4 mm) at both ends. Measure the current going through. What you do is you reconnect the cut cable with your multimeter, letting all current flow through your multimeter, which now tells you the amount of current.

    My readings

    The voltage over one candle (step 3): 3.1 V

    Note that the power source to the candle bridge was 33 V. So seven times 3.1 V is only 21.7 V. On some of the candles there must be an extra resistor. Had I measured that candle voltage, it must have had to be some 11 V.

    The current flowing through when candle lights (step 4): 19 mA

    I'm going to power everything with a 5 V 2 A battery pack. For the candles, I need to drop the voltage from 5 V to 3 V. I need a resistor, which will drop the voltage 2 V at a 19 mA current.

    2 V / 0.019 A = 105 Ω

    The power dissipating is:

    2 V * 19 mA = 38 mW

    That is negligible. A lot more could blow the resistor itself. Yet, without a 105 Ω resistor I might blow the LED. I have 100 Ω and 120 Ω resistors. I go with 120 Ω. It gives more protection.

    Testing all 7 candles with 3 V gave a bright light, except for one candle, which only had a very dim light, with only some 0.8 mA going through. This was my candle with the extra resistor. It turned out that the other candles had no resistors at all. The LED lights used in the chandelier are simply meant for 3 V! The candle with the extra resistor had to be opened using mild violence, but nothing broke. The resistor was found just beneath the tiny LED inside the plastic candle bulb. I had to desolder it away and resolder the wires. It was a bit messy, since the soldering iron warmed up some hot glue, which had been used for the assembly.

    So now I know that whatever power source I use, whatever the voltage is, I have to drop the voltage down to 3 V allowing 19 mA to go through.

    If I had been more familiar with LED technology, I would have recognised the type of LED used and I would have known it needed 3 V.

    Step 2: Some Soldering

    In this step I connect all positive (+) wires from the 5 candles to one wire. Then I add a separate negative (-) wire for each candle. A LED light only lights when the '+' and '-' go right. Since you only have two identical cable ends from each candle, you have to test which one is '+' and which is '-'. For this you need a 3 V power source. I had a small battery package including two AAA batteries. A 3 V coin battery works great for testing, too.

    The candle bridge needs 8 cables to run between the Arduino and the bridge. If you find a cable with 8 insulated wires, that would be great. One wire must hold 120 mA, the rest of them only carry 20 mA at the most. I chose to use 4 double wire cable, which I happened to have.

    The first image shows how I prepared one common wire to connect all '+' wires from the candles. Strip off some insulation of the common wire for each candle. Add a piece of shrink insulation tube (the yellow strip in the image) for each joint and have it placed at the right spot of the common cable. Solder the '+' wire from each candle to its joint, cover the joint with the shrink tube and shrink it. Of course, simple adhesive tape is fine, too, everything will be covered in the end.

    The second image shows the '-' wires that each candle needs. The common '+' wire goes directly to the 5 V pin of the Arduino (or perhaps through the breadboard). Each '-' wire goes to its own pin of the transistor IC (again, probably through the breadboard).

    An Arduino is often called a prototyping board. A breadboard is also something you use in prototypes. What I describe in this instructable is a prototype. I won't develop it into a posh shiny product with everything hidden in nice plastic cases. Taking it from the prototype to the next level would mean replacing the breadboard with a printed circuit board and soldered components and even replacing the Arduino with just a simple microcontroller chip (actually such chip is the brain of the Arduino). And having everything fit in a plastic case or inside the hacked candle bridge.

    Step 3: The Connections

    About Arduinos, taken from this page:

    • Total max current per input/output pin: 40mA
    • Sum of currents out of all input/output pins combined: 200mA

    My candles draw 19 mA each, when powered by 3 V. There are seven of them, which makes 133 mA. So I could power them directly from the output pins. However, I have some spare darlington transistor ICs. So I thought, why not. My circuit does the thing the proper way: data pins are only for signals, not for power. Instead I use the 5 V pin on the Arduino for powering the LED lights. When test running, I have my laptop connected to the Arduino. Everything is powered from the laptop USB, which gives 5 V. The Arduino Mega has a fuse of its own, which blows at 500 mA to protect the computer. My candles draw 133 mA at the most. The Arduino probably much less. Everything runs fine, when powered by the laptop, so using a 5 V battery pack connected to the USB port of the Arduino is just fine.

    The data pins D3 - D9 go to the IC ULN2803APGCN. The LEDs operate on 3 V. Each bulb is connected to the 5 V source and further to a 120 Ω resistor. Further to one channel of the IC, which finally connects the circuit to the ground through a darlington transistor in the IC.

    A push button is added to the circuit to enable some user action. The candle bridge could thus have a few user selectable programs.

    The push button in the circuit is connected to RESET and GND. This is exactly what the built in reset button does. Since I don't encapsulate everything in a plastic case, I'm using the reset button on the Arduino for controlling the program. Adding a button according to the image will work exactly like the board reset button. The program works by remembering what light program was used last time the program ran. Thus, each reset will advance to the next light program.

    The photos show how the new cables come out of the bridge, how I laid the transistor IC and the resistors on the breadboard and how the jumper wires connect to the Arduino Mega. I cut 4 male-male jumper wires into 8 half wires, which I soldered to the 8 cables coming out of the candle bridge. This way I can just stick the cables into the breadboard.

    Alternative without transistors

    In the previous step, I prepared a common '+' wire for the candles and separate '-' wires, which go through the transistor IC to the ground. When one data pin goes high, the corresponding '-' wire gets grounded through its transistor and the LED lights.

    Connecting the '-' wires directly to the data pins of the Arduino would work, too, but always mind how much current the data pins can stand! This approach would need a change in my program. It would need the data pins to go low to turn on the candles. To use my program as it is, you need to switch '+' and '-' in the candles. Have a common '-' wire for the candles, which goes to GND on the Arduino. And the separate wires run between the '+' wire of the candle and a data pin of the Arduino.

    Step 4: The Light Programs

    My program, which I present in the next step, goes through 9 light programs. Pushing the button will black out the lights for a second, then the following light program starts. The programs are as follows:

    1. Strong flickering. The candles flicker randomly. This looks very annoying when you stare at them from close distance, but might look good from a distance and perhaps behind a frosty attic window. Though, your neighbour might call the fire brigade.
    2. Soft flickering. Looks very good. Like real candles in a room without draught.
    3. Varying flickering. The candles alternate smoothly between strong and soft flickering in some 30 s intervals.
    4. Varying flickering. Like #3, but each candle vary in its own pace between 30 s and 60 s.
    5. Fast twinkle. The candles shine at a static dimmed level and randomly twinkle. In average there's one twinkle every second.
    6. Slow twinkle. Like #5, but at a much slower rate.
    7. Fast wave from middle top candle to the lower ones.
    8. Slow wave from middle top candle to the lower ones.
    9. Static bright light. I had to include this, didn't want to get rid of the original function.

    Step 5: The Code

    /*
        FLICKERING CANDLE BRIDGE
    */
    
    // Declare the mode variable to hold the state
    // through a reset operation
    __attribute__((section(".noinit"))) unsigned int mode;
    // When the program starts after a reset, this piece
    // of memory is not initialised, but holds the value
    // it had before the reset. The very first time the
    // program is ran, it holds a random value.
    
    /*
     *   The candle class holds everything needed
     *   for calculating a light level for 
     *   a flickering candle. 
     */
    class candle
    {
      private:
      long maxtime;
      long mintime;
      long maxlite;
      long minlite;
      long meanlite;
      long origmaxtime;
      long origmintime;
      long origmaxlite;
      long origminlite;
      long origmeanlite;
      long deltamaxtime;
      long deltamintime;
      long deltamaxlite;
      long deltaminlite;
      long deltameanlite;
      long lforate;
      long evenout;
      long start;
      long target;
      float phactor;
      
      long targettime;
      long starttime;
      long deltatime;
    
      void newtarget(void);
      long onetarget(void);
      
      public:
      candle(long mat, long mit, long mal, long mil, long mel, long eo);
      long levelnow(void);
      void initlfo(long deltamat, long deltamit, long deltamal, long deltamil, long deltamean, long rate);
      void setlfo(void);  
    };
    
    candle::candle(long mat, long mit, long mal, long mil, long mel, long eo) :
    maxtime(mat), mintime(mit), maxlite(mal), minlite(mil), meanlite(mel), evenout(eo),
    origmaxtime(mat), origmintime(mit), origmaxlite(mal), origminlite(mil), origmeanlite(mel)
    {
      target = meanlite;
      newtarget();
    }
    
    /*
     *  levelnow() returns the light level the candle should have right now.
     *  The function takes care of defining a new random light level and
     *  the time it should take to reach that level. The change is not linear,
     *  but follows a sigmoid curve. When it is not time for defining a new
     *  level, the function simply returns the light level.
     */
    long candle::levelnow(void)
    {
      long help, now;
      float t1, t2;
      now = millis();
      if (now >= targettime)
      {
        help = target;
        newtarget();
        return help;
      }
      else
      {
        //help = target * (millis() - starttime) / deltatime + start * (targettime - millis()) / deltatime;
        t1 = float(targettime - now) / deltatime;
        t2 = 1. - t1;
        
        // This is the sigmoid calculation
        help = t1*t1*t1*start + t1*t1*t2*start*3 + t1*t2*t2*target*3 + t2*t2*t2*target;
        return help;
      }
    }
    
    void candle::newtarget(void)
    {
      long sum;
      sum = 0;
      for (long i = 0; i < evenout; i++)
        sum += onetarget();
      start = target;
      target = sum / evenout;
      starttime = millis();
      targettime = starttime + random(mintime, maxtime);
      deltatime = targettime - starttime;
    }
    
    long candle::onetarget(void)
    {
      if (random(0, 10) < 5)
        return random(meanlite, maxlite);
      else
        return random(minlite, meanlite);
    }
    
    /*
     *  The lfo thing was added later to the class. Its purpose is to alter
     *  values like the flickering rate and flickering amplitude, things
     *  that were meant to be static in the first place.
     *  
     */
    void candle::initlfo(long deltamat, long deltamit, long deltamal, long deltamil, long deltamean, long rate)
    {
      deltamaxtime = deltamat;
      deltamintime = deltamit;
      deltamaxlite = deltamal;
      deltaminlite = deltamil;
      deltameanlite = deltamean;
      lforate = rate;
      phactor = 6.28318530718 / lforate;
    }
    
    /*
     *  Members like maxtime was a static member, when the class was created. When the 
     *  lfo thing was added, maxtime became alterable and origmaxtime was added to 
     *  the class to hold the original, static value.
     */
    void candle::setlfo(void)
    {
      float fase;
      fase = sin(phactor * (millis() % lforate));
      maxtime = origmaxtime + deltamaxtime * fase;
      mintime = origmintime + deltamintime * fase;
      maxlite = origmaxlite + deltamaxlite * fase;
      minlite = origminlite + deltaminlite * fase;
      meanlite = origmeanlite + deltameanlite * fase;
      
    }
    
    /*
     * 
     *     twinkler class has a similar function as
     *     the candle class, but deals with the twinkling
     *     candle. The twinkling candle shines with a 
     *     static brightness, untill it twinkles, a
     *     short flash lasting some 200 ms.
     * 
     */
    
    class twinkler
    {
      long starttime;
      long targettime;
      long maxtime;
      long mintime;
      long maxlite;
      long minlite;
      long lowlite;
      long start;
      long rate;
      long lastcheck;
    
      void twink(void);
    
      public:
      twinkler(long mat, long mit, long mal, long mil, long r);
      int levelnow(void);
          
    };
    
    twinkler::twinkler(long mat, long mit, long mal, long mil, long r) :
    maxtime(mat), mintime(mit), maxlite(mal), minlite(mil), lowlite(64), rate(r)
    {
      twink();
      targettime = millis() + random(rate);
      lastcheck = 0;
    }
    
    int twinkler::levelnow(void)
    {
      long now;
      now = millis();
    
      // check every 100 ms whether to twinkle
      if (now > lastcheck + 100)
      {
        lastcheck = now;
    
        /*
         *   The algo for twinkling "after rate milliseconds":
         *      Start checking after rate / 2 milliseconds
         *      During a period of rate / 2 milliseconds, make
         *      the chance of a twinkle to be 50 %. 
         *      If rate is 10000 ms, during 5000 ms the coin is
         *      flipped 50 times.
         *      1/50 = 0.02
         *      If random(10000) < 200, twinkle
         *      
         */
        if (now > starttime + rate / 2)
        {
          if (random(rate) < 200)
            twink();
        }
      }
      if (now > targettime)
        return lowlite;
      return (start - lowlite) * (targettime - now) / (targettime - starttime) + lowlite;
    }
    
    void twinkler::twink(void)
    {
      starttime = millis();
      targettime = random(mintime, maxtime) + starttime;
      start = random(minlite, maxlite);
    }
    
    void setup() {
      int led;
    
      // Read the magic mode variable, which should tell 
      // what light program was ran last time, increment it
      // and reset to zero if overflow.
      mode++;
      mode %= 9; // This takes care of whatever the value
                 // was the very first time the Arduino
                 // ran this program.
    
      /*
       *  IMPORTANT NOTE
       *  ==============
       *  
       *  The essential thing this program does is outputting PWM
       *  signals to LED lights. Here I set the pins 3 to 9 to 
       *  output mode. On an Arduino Mega2560, these pins output
       *  nicely PWM signals. If you have an other Arduino, check
       *  which pins (and how many) you can use. You can always
       *  rewrite the code to use software PWM, if your Arduino 
       *  can't provide enough hardware PWM pins. 
       *  
       */
      pinMode(3, OUTPUT);
      pinMode(4, OUTPUT);
      pinMode(5, OUTPUT);
      pinMode(6, OUTPUT);
      pinMode(7, OUTPUT);
      pinMode(8, OUTPUT);
      pinMode(9, OUTPUT);
      pinMode(LED_BUILTIN, OUTPUT);
      analogWrite(LED_BUILTIN, 0); // Just switch off the annoying red led on the Arduino
      
      candle *can[7];      // prepare to use the flickering candles, whether you use them or not
      twinkler *twink[7];  // prepare to use the twinkling candles...
      
      if (mode == 8)
      {
        for (int i = 3; i < 10; i++)
          analogWrite(i, 255);
        while (true);      // Each time this program runs, it goes into
                           // this kind of endless loop, until the reset
                           // button is pressed.
      }
      
      if (mode < 2) // flickering
      {
        long  maxtime_;
        long  mintime_;
        long  maxlite_;
        long  minlite_;
        long  meanlite_;
        long  even_;
        if (mode == 0)
        {
          maxtime_ = 250;
          mintime_ = 50;
          maxlite_ = 256;
          minlite_ = 0;
          meanlite_ = 128;
          even_ = 1;
        }
        if (mode == 1)
        {
          maxtime_ = 400;
          mintime_ = 150;
          maxlite_ = 256;
          minlite_ = 100;
          meanlite_ = 200;
          even_ = 1;
        }
        for (int i = 0; i < 7; i++)
        {
          can[i] = new candle(maxtime_, mintime_, maxlite_, minlite_, meanlite_, even_);
        }
        while (true) // The endless loop for flickering candles
        {
          for (int i = 0; i < 7; i++)
            analogWrite(i+3, can[i]->levelnow());
        }
      }
      if (mode < 4) // lfo added to the flickering
      {
        if (mode == 2) // same lfo (30 s) for all candles
        {
          for (int i = 0; i < 7; i++)
          {
            can[i] = new candle(325, 100, 256, 50, 164, 1);
            can[i]->initlfo(75, 50, 0, 50, 36, 30000); 
          }
        }
        if (mode == 3) // varying lfo:s for the candles
        {
          for (int i = 0; i < 7; i++)
          {
            can[i] = new candle(325, 100, 256, 120, 164, 1);
          }
          can[0]->initlfo(75, 50, 0, 50, 36, 20000); 
          can[1]->initlfo(75, 50, 0, 50, 36, 25000); 
          can[2]->initlfo(75, 50, 0, 50, 36, 30000); 
          can[3]->initlfo(75, 50, 0, 50, 36, 35000); 
          can[4]->initlfo(75, 40, 0, 50, 36, 40000); 
          can[5]->initlfo(75, 30, 0, 50, 26, 45000); 
          can[6]->initlfo(75, 20, 0, 50, 16, 50000); 
          can[7]->initlfo(75, 10, 0, 50, 6, 55000); 
          
        }
        while (true) // The endless loop for flickering candles with an lfo
        {
          long lastclock = 0;
          for (int i = 0; i < 7; i++)
            analogWrite(i+3, can[i]->levelnow());
          if (millis() > lastclock + 4000)
          {
            lastclock = millis();
            for (int i = 0; i < 7; i++)
              can[i]->setlfo();
          }
        }
      }
      if (mode < 6) // twinkling candles
      {
        int speedo;
        if (mode == 4)
          speedo = 6000;
        else
          speedo = 22000;
        for (int i = 0; i < 7; i++)
          twink[i] = new twinkler(300, 295, 255, 250, speedo);
    
        while (true)
        {
          for (int i = 0; i < 7; i++)
            analogWrite(i+3, twink[i]->levelnow());
        }
    
      }
      
      // Waves.
      // This section starts with curly brackets just
      // to ensure there are no conflicting variable names.
      // No other need for brackets, no need for checking
      // the value of mode.
      {
        int lolite = 2;
        int hilite = 255;
        int mean;
        int ampl;
        float fasedelta = 2.5;
        float fase;
        int elong;
        float phactor;
        long period;
    
        mean = (lolite + hilite) / 2;
        ampl = hilite - mean;
        
        if (mode == 6)
          period = 1500;
        else
          period = 3500;
        phactor = 6.28318530718 / period;
        while (true)
        {
          fase = phactor * (millis() % period);
          elong = mean + ampl * sin(fase);
          analogWrite(7, elong);
          analogWrite(9, elong);
          
          fase = phactor * ((millis() + period / 4)  % period);
          elong = mean + ampl * sin(fase);
          analogWrite(3, elong);
          analogWrite(8, elong);
          
          fase = phactor * ((millis() + period / 2)  % period);
          elong = mean + ampl * sin(fase);
          analogWrite(4, elong);
          analogWrite(5, elong);
          
          fase = phactor * ((millis() + 3 * period / 4)  % period);
          elong = mean + ampl * sin(fase);
          analogWrite(6, elong);
          
          
        }
            // While connecting the candle wires to the Arduino, 
            // I got them mixed and never got them in order.
            // The order is important for creating wave patterns,
            // so I just wrote this small table for me:
            //
            // Candle# in the bridge: 2 3 5 4 7 6 1
            //   Data pin on Arduino: 3 4 5 6 7 8 9 
    
        
      }
      
    }
    
    void loop() {
      // Since every light program is its own infinite loop,
      // I wrote all the loops in the begin() section
      // and left nothing for this loop() section.
    }
    

    Step 6: About PWM

    The leds shine bright when powered with 3 V. Using only 1.5 V they don't light up at all. LED lights don't fade nicely with the fading voltage, like incandescent lights do. Instead they have to be turned on with full voltage, then turned off. When this happens 50 times per second, they shine nicely with a 50 % brightness, more or less. If they are allowed to be on only 5 ms and off 15 ms, they might shine with a 25 % brightness. This technique is what makes LED light dimmable. This technique is called pulse width modulation or PWM. A microcontroller like Arduino usually have data pins, which can send on/off signals. Some of the data pins have built in capabilities for PWM. But if there are not enough pins with built in PWM, it's usually possible to use dedicated programming libraries to create "software PWM pins".

    In my project, I've used an Arduino Mega2560, which has hardware PWM on pins 3 - 9. If you use an Arduino UNO, you have only six PWM pins. In that case, if you need a 7th (or even more) candle, I can recommend Brett Hagman's software PWM library, which you can find here.

    Make it Glow Contest

    Participated in the
    Make it Glow Contest