Oh no! Not again! Yes, more silliness from that weirdo in Texas.
This time it's candle sketches -- something I like to amuse myself with
from time to time. Below is my latest tinkerings combined with an idea
or two I got from Jon McPhalen's article in October's Nuts & Volts
magazine. Just hook up an LED to ground through an appropriately
valued resistor on pin 10 to your Arduino, download the sketch, and
you're ready to melt some wax. One of the best ways to enjoy the
'candle' is to diffuse the LED with a "faux" flame tip off of a cheap,
electric tea candle.
Compared to some of the better commercial candles I don't think it
flickers as quickly as some but this candle sketch doesn't get on my
nerves as badly. (I can be a cranky OF
So give it try and let me know
what you think.
If you like this code, keep in mind that it compiles down to a mere
1414-bytes for a 168/328. For an ATTiny45/85 it compiles to 972-bytes
under v021 of the Arduino IDE on Linux. I also knocked out an
assembler version for the ATTiny13 that uses a whopping 140-bytes
and it's been working just peachy for the past two weeks.
One more thing before the code... The algorithm is simple enough that
it only takes, at most, a small number of clocks during updates. The
PWM output is updated every 12-milliseconds. I've extended my code
in two ways. One is by using all six PWM outputs on a 328 for multiple
candles. Another is to use a TLC5940, with 12-bit PWM, for an up to
16-candle candelabra. So far I've only built up 8 wicks with the 5940
and it does put off a nice glow with yellow/amber colored LEDS.
OK, here's the code. Have fun!
-Rusty-
//
// Candle - Simulate a candle flicker with one LED.
// Rusty Haddock, 10/31/2010 -- with an idea or two from Jon McPhalen's
// article in Nuts&Volts, 10/2010 issue.
//
// The main idea behind this candle program is to set a target brightness
// and step to it (by brightnessStep). Once the target is reached a new
// target and brightnessStep is generated and the process starts again.
// As each target is reached the direction to the new target is opposite
// of the last. This is done by selecting a new target across the midpoint
// of brightness (e.g. if the current target has a value of 128-255 then
// the new target will be picked in the range 0-127, and vice versa.
//
// I've played a bit with the brightnessStep setup, random(4,12).
// I'm liking those numbers.
// #define LEDPIN 0 // For Tiny25/45/85 (physical pin 5, PortB0)
#define LEDPIN 10 // For Mega168/328
byte currentLevel, // Current brightness of candle
targetLevel; // Brightness level we're going to.
char brightnessStep; // How much to change brightness each step
unsigned int prnSeed = 0xa5a5u; // Do we really need to generate a "random"
// number for the seed? Save the memory...
// Any non-zero number will work just fine.
// Generate a 16-bit pseudo-random from lo to hi-1, inclusive.
// Using this instead of random() saves about 600-bytes.
unsigned int random16(unsigned int lo, unsigned int hi)
{
byte cnt, mask;
unsigned int ans, bits;
bits = prnSeed & 0xd008u; // Feedback pattern for 16-bits
mask = highByte(bits) | lowByte(bits); // quickens the bit count
// Count the bits we masked off.
for (cnt=0; mask != 0; mask >>= 1) {
if (mask & 0x01)
cnt++;
}
prnSeed <<= 1;
if (cnt & 1) // If there was an odd # bits we feedback a 1-bit
prnSeed++;
ans = (prnSeed & (hi-lo-1)) + lo; // (hi-low) needs to be a power of two
// ans = (prnSeed % (hi-lo)) + lo; // otherwise use this line.
return ans;
}
void setup()
{
currentLevel = 255; // Initially the candle will "flare up" and
targetLevel = 1; // then ramp down. Kinda let user know we're
brightnessStep = -1; // peachy keen.
}
void loop()
{
boolean goingUp; // Ramping up or down in brightness?
goingUp = (brightnessStep > 0);
// Have we reached our target?
if ((goingUp && (currentLevel >= targetLevel))
|| (!goingUp && (currentLevel <= targetLevel)))
{
// New target value please!
targetLevel = random16(0,128);
brightnessStep = random16(4, 12);
if (goingUp) // if were goingUp -- now go down.
brightnessStep = -brightnessStep;
else
targetLevel += 128;
}
// The PWM is only 8-bits so... constrain ourselves. :-)
currentLevel = constrain(currentLevel+brightnessStep, 0, 255);
analogWrite(LEDPIN, currentLevel);
delay(12); // I have NO IDEA where I got this value
// but it seems to work for me.
}