■ FIGURE 10. Serial I/O.
dimmed back down to the idle level.
Again, this is very simple and with a little extra hardware
and code you could even add audio control. The circuit in
Figure 10 can be used to start audio players that require an
open collector start pulse, e.g., Cowlacious (set the jumper
to select the transistor and pulse the TX pin low-high-low),
or to communicate with a serial audio player (e.g., EFX-TEK,
Rogue Robotics) when the transistor is bypassed. Serial devices
will need a serial cog but that’s not a problem as the base
Jack-o-Lantern code only uses three of the eight available.
I suspect many will stop right here and head to the
workbench to build a candle controller. For those hearty types
who want to know how I generate realistic flames, read on!
Mimicking Nature is Difficult
My friend, Jen, was having dinner at a nice restaurant
and noticed they were using electronic candles – and they
looked awful. I understand; having experimented with
candle algorithms, I can tell you it’s a little tricky to get a
natural look (though it won’t be after you read this!).
There is good news: We don’t have to do a perfect
simulation. You see, our brains have this incredible ability
to take partial information and fill it out with what we
know; this gives us the ability to recognize friends from
bad photos at odd angles. So, you see, we only have to
get close; our brain will say, “Okay, that’s a candle.”
Don’t believe me? A few years back I had two candles —
one real, one electronic — “burning” side by side so I could
watch them. The air conditioner kicked on and blew out the
real candle. Without thinking, I picked up a match and held
■ FIGURE 11. Wick behavior.
42
October 2010
it to the electric candle expecting it to light. After about a
second, I laughed at myself – out loud! – for being fooled by
my own creation. This illustrates the power of our subconscious
and its ability to deal with partial (even false) information.
After lots of experiments, I’ve developed an algorithm
that works like this: Each “wick” requires three variables:
1) current brightness; 2) target brightness; and 3) ramp rate
moving from the current brightness to the target. When
the current level reaches the target level, a new random
target is generated. Of late, I am also keeping track of the
directional change to ensure that the next update always
passes through the mid-point of the brightness range.
Figure 11 is a simplified illustration of the algorithm’s
behavior (the ramp is actually a bit “wobbly” because the
ramp variable is recreated when it hits zero). When the
new direction is ramping up, the brightness will be selected
in the upper region; when the new direction is ramping
down, the brightness is selected in the lower region. This
seems to give the wicks a bit more life (as we would want
in a display). In the final code, I also apply an overall level
control so I can control the maximum brightness.
Coding Candles
Keeping track of up to eight “live” wicks really dictates
that it happens as a background process — this allows us
to monitor sensors and other things in the main code
without affecting wick behavior (unless we want to). As
discussed earlier, the process is coded into a Spin method
which launched into its own cog:
pri flicker(wix) | lotto, idx, tmp, direction {
} base[8], target[8], ramp[8]
lotto := cnt
repeat idx from 0 to 7
target[idx] := ?lotto & $FF | $80
base[idx] := 0
ramp[idx] := (?lotto & %11) + 1
direction := %00000000
repeat ‘ flicker loop
?lotto
repeat idx from 0 to (wix-1)
if (—ramp[idx] == 0)
ramp[idx] := (?lottery & %11) + 1
if (base[idx] < target[idx])
++base[idx]
elseif (base[idx] > target[idx])
—————————base[idx]
else
direction := togglebit(direction,
idx)
if (getbit(direction, idx) == 0)
target[idx] := ?lotto & $FF | $80
else
target[idx] := ?lotto & $7F
tmp := (base[idx] level[idx]) / 255
leds.set(idx, tmp)
if (flickerdelay > 0)
tmp := (flickerdelay US_001) #> 400
waitcnt(tmp + cnt)
Okay, it looks a little scary, right? Hey, this is the
Halloween issue! But just like your friend behind the mask,