Will o' the Wisp using PWM

Last Modified: September 23, 2014, at 05:59 PM
By:
Author: Garth Fielding II

Intro

This sketch is a program to simulate the Will o' the Wisps that you see in the movie "Brave" by Disney. The LED's will slowly fade on one by one until all are on. At that point, they pulse giving the appearance of a breathing pulsing wisp. After a few seconds of them all being on the first one will start to flicker and fade away. The next one starts to flicker as the first is fading and so on down the line until they are all off. After a few seconds the cycle will begin again.

Setup in your yard

You can find some 22/4 security wire at many hardware stores (solid core is preferred). It's 22awg wire with 4 wires. Strip just a little bit off the end of each of the 4 wires on both ends. Attach 2 LED's to the 4 wires (pick a color - one end you'll plug into ground and the other to where you have a current limiting resistor coming from a pin). You can solder the wires if you want or just use electrician's tape to affix the LED's to the wires like I did. Either way, wrap some electrician's tape between and around the exposed wires and LED leads to prevent them from touching each other.

Take three 1x2 eight foot pieces of lumber. Cut 16 inches off the middle one so you have a total of 8'x8'x6'8"=22 feet 8 inches lying end to end. Drill 10 holes 30" (2 1/2 feet) apart from each other and place wooden dowels in them so they come up about waist high. Optionally glue some side braces on to help with stability. It looks best if you paint all of the wood black at this point. Run the security wire from the Arduino along the sides of the 1x2 and up the wooden dowel. Wrap electrician's tape around the wire just before the top of the wooden dowel. The LED should now be sitting flush with the wooden dowel and about eye level with children.

The Circuit

LEDs with current limiting resistors attached from each of the 10 pins (2-11) to ground. The perfect wisp in my opinion is a microtivity IL142 5mm Diffused Blue LED with a 3.0-3.1V forward voltage and a current of 20mA using the 100 ohm resistors that came with the LED's.

R = (VS - VL) / I ResistorNeeded = (SourceVoltage - LEDForwardVoltage) / Current

Example of LED setup (where the LED has a Forward Voltage of 3.0v).
R=(5v-3.0v)/.02=100= 100 ohm
							  -
							  `

Note: Forward Voltages are different for every LED. Your package should tell you what they are. For example: Clear Red and Clear Yellow 1.88-2.0V, Clear Green: 2.89-3.1V, Clear Blue, Clear White and Clear Pink: 3.0-3.2V. If you are unsure of what voltage your LED is rated at, connect it up to a circuit with 5V power, a 10k-ohm resistor and use a voltmeter to measure the voltage across the 2 wires of the LED. That will tell you the approximate voltage. Once you have that setting, do the math above to find a 20ma current and then after building that check the LED with the voltmeter again. The settings for the forward voltage may change slightly if at all.

You can easily build this entire circuit on your breadboard to get it working the way you want then come Halloween just pull the LED's off the board and plug in the 22/4 security wire's you've attached to the LED's and voila, instant wisps.

Warning on overloading your Arduino

The code below is setup so that it currently outputs to only 10 pins.

MAKE SURE YOU DO NOT EXCEED 20mA OF CURRENT ON ANY ONE LED (See description above for calculating your current)

Ever wondered why every example of lighting up LED's only lights up 10 pins total? While each pin can send 40mA of power through it, the total power sent through the system at one time cannot exceed 150mA. That means that if you were running LED's on more than 7 pins at full brightness (20mA) you could hit that limit. Some people say you have 200mA and in that case you'd be able to have 10 pins running LED's at full brightness simultaneously (like you see on other examples of PWM with LED's). This program will run at times near the 200mA limit if you use 20mA for each LED. If you are worried, I suggest you decrease the amount of current running through your system by using a larger resistor.


Code Listing #1 - Analog and Digital Fireflies

/*  Will o' the Wisp

    This sketch is a program to simulate the Will o' the Wisps that you see in the movie "Brave" by Disney.
    The LED's will slowly fade on one by one until all are on.  At that point, they pulse giving
    the appearance of a breathing pulsing wisp.  After a few seconds of them all being on the
    first one will start to flicker and fade away.  The next one starts to flicker as the first
    is fading and so on down the line until they are all off.  After a few seconds the cycle will begin again.

    created 2014
    by Garth Fielding II



  The circuit:
    LEDs with current limiting resistors attached from each of the 10 pins (2-11) to ground.
    The perfect wisp in my opinion is a microtivity IL142 5mm Diffused Blue LED with a 3.0-3.1V forward voltage
    and a current of 20mA using the 100 ohm resistors that came with the LED's.

    R = (VS - VL) / I
    ResistorNeeded = (SourceVoltage - LEDForwardVoltage) / Current

    Example of LED setup (where the LED has a Forward Voltage of 3.0v).
    R=(5v-3.0v)/.02=100= 100 ohm
           100 ohm    LED (3.0v)   Ground
    PIN ---`/\/\/\/`------|>|--------___
                                      -
                                      `

    Note: Forward Voltages are different for every LED.  Your package should tell you what they are.
    For example: Clear Red and Clear Yellow 1.88-2.0V, Clear Green: 2.89-3.1V, Clear Blue, Clear White and Clear Pink: 3.0-3.2V.
    If you are unsure of what voltage your LED is rated at, connect it up to a circuit with 5V power, a 10k-ohm resistor and
    use a voltmeter to measure the voltage across the 2 wires of the LED.  That will tell you the approximate voltage.  Once
    you have that setting, do the math above to find a 20ma current and then after building that check the LED with the voltmeter
    again.  The settings for the forward voltage may change slightly if at all.

    You can easily build this entire circuit on your breadboard to get it working the way you want then come Halloween just pull
    the LED's off the board and plug in the 22/4 security wire's you've attached to the LED's and voila, instant wisps.

  Setup in your yard   
    You can find some 22/4 security wire at many hardware stores (solid core is preferred). It's 22awg wire with 4 wires.
    Strip just a little bit off the end of each of the 4 wires on both ends. Attach 2 LED's to the 4 wires (pick a color - one
    end you'll plug into ground and the other to where you have a current limiting resistor coming from a pin). You can solder the wires
    if you want or just use electrician's tape to affix the LED's to the wires like I did. Either way, wrap some
    electrician's tape between and around the exposed wires and LED leads to prevent them from touching each other.

    Take three 1x2 eight foot pieces of lumber.  Cut 16 inches off the middle one so you have a total of 8'x8'x6'8"=22 feet 8 inches lying end to end.
    Drill 10 holes 30" (2 1/2 feet) apart from each other and place wooden dowels in them so they come up about waist high.
    Optionally glue some side braces on to help with stability.
    It looks best if you paint all of the wood black at this point.
    Run the security wire from the Arduino along the sides of the 1x2 and up the wooden dowel.  Wrap electrician's tape around the
    wire just before the top of the wooden dowel.  The LED should now be sitting flush with the wooden dowel and about eye level with children.

    In the picture below, the "X" is an "LED", the "|" is a "wooden dowel", and the "-" is the "1x2".

    X          X          X          X          X          X          X           X          X          X
    |          |          |          |          |          |          |           |          |          |
    |          |          |          |          |          |          |           |          |          |
    |          |          |          |          |          |          |           |          |          |
---------------------------------------------------------------------------------------------------------

    This is currently timed so someone walking at a fairly normal pace (very subjective) would keep pace with the LED's going
    off at 2 1/2 feet apart.
 */



/*
  ---DO NOT BLOW UP YOUR ARDUINO BY SENDING TOO MUCH POWER THROUGH IT---
    The code below is setup so that it currently outputs to only 10 pins.

    MAKE SURE YOU DO NOT EXCEED 20mA OF CURRENT ON ANY ONE LED (See description above for calculating your current)

    Ever wondered why every example of lighting up LED's only lights up 10 pins total?
    While each pin can send 40mA of power through it, the total power sent through the system at one time cannot exceed 150mA.
    That means that if you were running LED's on more than 7 pins at full brightness (20mA) you could hit that limit.
    Some people say you have 200mA and in that case you'd be able to have 10 pins running LED's at full brightness
    simultaneously (like you see on other examples of PWM with LED's).  This program will run at times near the 200mA limit
    if you use 20mA for each LED.
    If you are worried, I suggest you decrease the amount of current running through your system by using a larger resistor.
  ---DO NOT BLOW UP YOUR ARDUINO BY SENDING TOO MUCH POWER THROUGH IT---
*/

#define PULSING_INTERVAL 1000
#define MAX_PULSING_BRIGHTNESS 700
#define MIN_PULSING_BRIGHTNESS 300

#define DIGITAL_FADE_INTERVAL 2000 //5000
#define DIGITAL_FADEDOWN_WAIT 10 //30
#define DIGITAL_FADEUP_WAIT 600

#define FLICKERING_DURATION 900000

//Used to indicate the 4 states we can be in.
typedef enum {PULSING, FLICKERING, FADEUP, FADEDOWN, OFF} WispState;

typedef struct WispStruct {
  byte pin;                //PWM output pins are 3,5,6,9,10,and 11
  WispState state;         //used to determine what state the LED is in
  unsigned int pulsePeriod;
  unsigned long lastPulseTime;         //used to calculate how long the led will be on
  unsigned long pulsingChangeTime;         //used to calculate how long the led will be on
  unsigned long TEMPtimeToFade;         //used to calculate how long the led will be on
  boolean toggle;          //Used for toggling the LED on/off with PWM

  //fading
  unsigned long fadeWait;  //used to increase the time it takes to fade so it lasts just a little longer.
} Wisp;

int wispPins[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
const int MAXPINS = sizeof(wispPins)/sizeof(*wispPins);
Wisp wisps[MAXPINS];

boolean wispPinsOff(){
  for (int i=0; i<MAXPINS; i++) {
    if (wisps[i].state != OFF)
      return false;
  }
  return true;
}


void setup(){
  for (int i=0; i<MAXPINS; i++) {
    wisps[i].pin = wispPins[i];
    pinMode(wisps[i].pin, OUTPUT);
  }
  wisps[0].state = FADEUP;
}

void pwm(unsigned long currentTime, int interval, int i) {
  if (currentTime - wisps[i].lastPulseTime > interval - wisps[i].pulsePeriod && wisps[i].toggle == true) {
    wisps[i].toggle = false;
    wisps[i].lastPulseTime = currentTime;
  }
  else if (currentTime - wisps[i].lastPulseTime > wisps[i].pulsePeriod && wisps[i].toggle == false) {
    wisps[i].toggle = true;
    wisps[i].lastPulseTime = currentTime;
  }

  if (wisps[i].toggle == true)
    digitalWrite(wisps[i].pin, LOW);
  else
    digitalWrite(wisps[i].pin, HIGH);
}

void flicker(unsigned long currentTime, int i) {
  if (currentTime > wisps[i].pulsingChangeTime) {
    if (wisps[i].toggle == true)
      wisps[i].pulsingChangeTime = currentTime + (unsigned long)random(30)*1000;
    else
      wisps[i].pulsingChangeTime = currentTime + (unsigned long)random(100)*1000;
    wisps[i].toggle = !wisps[i].toggle;
  }

  if (wisps[i].toggle == true)
    digitalWrite(wisps[i].pin, LOW);
  else
    digitalWrite(wisps[i].pin, HIGH);
}

void pulsing(unsigned long currentTime, int i) {
  pwm(currentTime, PULSING_INTERVAL, i);

  if (currentTime - wisps[i].pulsingChangeTime > (unsigned long)50*1000) { //50ms
    wisps[i].pulsePeriod = random(MIN_PULSING_BRIGHTNESS, MAX_PULSING_BRIGHTNESS+1);
    wisps[i].pulsingChangeTime = currentTime;
  }
}

void flickering(unsigned long currentTime, int i) {
  flicker(currentTime, i);

  if (micros() - wisps[i].TEMPtimeToFade > FLICKERING_DURATION) {
    wisps[i].state = FADEDOWN;
    wisps[i].pulsePeriod = DIGITAL_FADE_INTERVAL;
    wisps[i].fadeWait = micros();
    wisps[i].TEMPtimeToFade = micros();

    if (i < MAXPINS - 1) {
      wisps[i+1].TEMPtimeToFade = currentTime;
      wisps[i+1].state = FLICKERING;
    }
  }
}

void fadeDown(unsigned long currentTime, int i) {
  //When we are processing 10 wisps, there is additional overhead to process the flicker, pulse, fade.
  //This results in the wisps having a slow fade at the beginning but then when we get to the last
  //couple of wisps it's almost an instant off (no fade at all) because the other LED's are off and
  //no processing is taking place (which slows down this processing).  That's why we add this additional fade here.
  unsigned long waitDueToLessProcessingTime = i*15ul;
  pwm(currentTime, DIGITAL_FADE_INTERVAL + waitDueToLessProcessingTime, i);

  if (wisps[i].pulsePeriod == 1) {
    wisps[i].state=OFF;
    digitalWrite(wisps[i].pin, LOW);
  }
  else if (currentTime - wisps[i].fadeWait > DIGITAL_FADEDOWN_WAIT + waitDueToLessProcessingTime) {
    //We have a short wait before incrementing the value to make the fade take just a little longer.
    wisps[i].pulsePeriod--;
    wisps[i].fadeWait = micros();
  }
}

void fadeUp(unsigned long currentTime, int i) {
  pwm(currentTime, DIGITAL_FADE_INTERVAL, i);

  if (wisps[i].pulsePeriod >= DIGITAL_FADE_INTERVAL/4) {
    wisps[i].pulsePeriod = 0;
    wisps[i].state=PULSING;

    if (i < MAXPINS - 1) {
      wisps[i+1].pulsePeriod = 0;
      wisps[i+1].state = FADEUP;
      wisps[i+1].TEMPtimeToFade = millis();
    }
  }
  else if (currentTime - wisps[i].fadeWait > DIGITAL_FADEUP_WAIT) {
    //We have a short wait before incrementing the value to make the fade take just a little longer.
    wisps[i].pulsePeriod++;
    wisps[i].fadeWait = micros();
  }
}


void off(unsigned long currentTime, int i) {
  digitalWrite(i, LOW);
}

unsigned long timeToSwitch;
void loop(){
  unsigned long currentTime = micros();

  if (millis()-timeToSwitch > 15000) {
    if (wispPinsOff()) {
      wisps[0].state = FADEUP;
    }
    else {
      wisps[0].TEMPtimeToFade = currentTime;
      wisps[0].state = FLICKERING;
    }
    timeToSwitch = millis();
  }

  for (int i=0; i<MAXPINS; i++) {
    if (wisps[i].state == FADEUP) {
      fadeUp(currentTime, i);
    }
    else if (wisps[i].state == PULSING) {
      pulsing(currentTime, i);
    }
    else if (wisps[i].state == FLICKERING) {
      flickering(currentTime, i);
    }
    else if (wisps[i].state == FADEDOWN) {
      fadeDown(currentTime, i);
    }
    else { //OFF
      off(currentTime, i);    
    }
  }
}

Share