In the course of the program, I want to do some
random timing so I’ve created a variable called lottery to
hold a random number. This variable is initially seeded
with the value of the system counter. I like to do this
because the Propeller boots using an internal RC clock
source which will always have a bit of variance.
We’ve already discussed starting the BAM driver, so we
can move on to getting the flame simulation going. An eight-byte array called level will be used to control the maximum
brightness of each candle output. As with the BAM driver,
the values in level are 0 (off) to 255 (maximum brightness).
All of the output levels are set to zero by using
bytefill. The next step is to start the flame generator which
is, in fact, a method called flicker() that is part of the main
code listing. Yes, that’s right: We can launch a method
into its own cog to run parallel with other processes. The
cool thing about using this technique is that the method
we launch can still access global variables and other
methods in the program.
This is how we launch the flicker() method into its
own cog:
cognew(flicker(3), @stack)
In this particular case, we’re telling the flicker
generator that we have three wicks (pins 0, 1, and 2); by
setting the actual number of wicks used, we have more
flexibility in how fast the routine runs.
When using cognew to launch another Spin cog, we
need to provide the name of the method (along with
parameters, if any) and the address of a block of longs
that will serve as stack space. The stack is used to hold
local variables, as well as working variables and return
addresses so that other methods can be called.
To be honest, setting the stack size can be something
akin to black magic — this is not unique to the Propeller. In
the past, I’ve tended to start with 16 and then add up the
number of local variables used in the method. That strategy
didn’t work in this case and the program just stopped. As
soon as I bumped up the stack, everything worked fine. I
generally believe it’s best to be generous with stack space.
If you get concerned about memory usage, you can find
tools in the Parallax Object Exchange that will analyze stack
usage and let you optimize for the smallest RAM footprint.
Now that all of the elements are running, we can get
into the working code. I start by ramping up the brightness
of each candle output just so I can see that they’re
working. With 256 steps and a 4 ms delay between each,
it will take about a second for each candle to light to full
brightness. This holds for two seconds and then drops into
the loop that will run until we power-down.
I often joke that my Halloween prop control programs
are like a Quentin Tarantino movie in that they start at the
end and then loop back. You can see this at the start of the
loop: The brightness of all candles is being dimmed (from
255 to 64) and the flicker speed is being modified to go from
0 (fast, angry) to 1,000 (slow, calm). At the end of this loop,
we’re back at the idle state with calm, low brightness wicks.
You’ll remember that the circuit is set up to accept a
Parallax PIR or a simple, normally open switch (usually a
mat switch). We’re going to use this input to bring Jack to
life when a To T approaches. It’s always a good idea to
debounce digital inputs and it is especially important with
PIRs as they can be twitchy. We can wait for a good input
with a call to waitstart():
waitstart(150)
This call waits for the start input to be active — and
stay active — for 150 milliseconds before being considered
a valid input. This timing will prevent spurious outputs
from the PIR from triggering the prop. The waitstart()
method is pretty straightforward:
pub waitstart(ms) | debounce
ms := 10 #> ms <# 1_000
debounce := 0
repeat until (debounce => ms)
pause(1)
debounce := ++debounce ina[START]
This method “fixes” the input parameter so that the
valid range for debouncing an input is 10 milliseconds to a
full second. Spin has a numeric range from negative two
billion (plus) to positive two billion (plus) — this little
correction keeps a typo from hanging up the program.
Here’s how the method works: A counter (called
debounce) is cleared and then we drop into a loop that
waits one millisecond before incrementing debounce and
then multiplying it by the state of the START input pin. If
the pin is active (1), then the count will be maintained; if
the input is not active (0), then the debounce variable will
be cleared. This process ensures that the input is active
and stays active for the desired period. Once that
happens, we return back to the main program.
In my props, I like to insert a randomized delay
between the trigger event and the active prop code. To do
this we randomize the value in lottery with and then
perform a little math to get a value between two and 10
seconds. Note that we have to take the absolute (||) value
of lottery in the code; this is necessary because a negative
value (which is possible with the randomization) will
create a problem for the modulus (//) operator.
After the random delay, the flicker rate is set to zero
which makes the flames more active and then each is
bumped to full brightness with a short delay in between.
It’s easy and very effective.
Here’s why: Imagine To Ts approaching your home where
they see a friendly Jack-o-Lantern on the porch, gently lit and
looking peaceful. As they draw near, the flames are magically
brightened and seem a bit angry. In the minds of the To Ts,
you’ve just created a “What’s next?” scenario. They will be
[happily] on edge until they’re safely back on the sidewalk
with the treats you’ve just provided. The reason for the
random delay after the trigger point is to fool returning To Ts.
The candles burn brightly for 30 seconds before going
back to the top of the loop (where we started) and are
October 2010 41