Thursday, May 10, 2012

Feeping Creaturitis

Physiologists are continuing to refine just how they feel about stretching.  I know that back when I was doing some serious cross-country running, I did not believe in stretching while my muscles were cold.  Instead I would warm up by jogging in place, shallow bends, and so forth.  And I'd stretch out following a run, because it seemed to me I cramped up less if I eased my muscles down to rest again.  Except even when I converted to treadmill running, I found a better cool-down was using the Treadwall my gym had for far too short a time.

Anyhow...stretching may or may not damage your body, but stretch goals can damsure hurt a project.

One of the basic -- and hardest -- elements of good engineering is parameterization.  You have to identify the problem at a larger level than "how do I best make a machine do this" or, worse, "how can I make this machine I happen to have do this?"  Good enough parameterization can ofter parameterize the engineering itself right out of the game; "Oh, I guess there IS a commercial machine that already does it well enough," or "Well, it doesn't look like anyone would even use this application."

Which is all a round-about way of getting to my current work with the BlinkM.  This started with bottom-up practice; there is a device which puts on one very small footprint PCB a bright RGB LED and an AVR.  And it is configured out of the box to upload and run both commands and programs via an I2C serial port.  So I started with a solution in search of an application; could this product be of any use in addressing the various kinds of controllable lights I've identified a need for in live theater?

I wasted a fair amount of time in trying to implement a minimum-component serial-over-wireless link for these devices before I finally got around to doing the higher-level parameterization.  And one thing leapt out at me; there was no identified theatrical application for which full serial communication was necessary.

Of course the basic problem in this kind of geekery for theater is that you are not just replacing existing solutions with more elegant ones.  You are trying to identify solutions for what no-one has identified as a problem before.  Few people have come up and said "In our production of 'Gypsy,' it would be very useful if Electra's boob lights would light every time she bumped."  Fewer still have come up and said "In our production of 'Godot,' it would be great if the spectral plot of the wind sound altered in response to how close Estragon gets to the bush, tree, shrub...whatever the heck that thing is."

In the first example, someone might have imagined the effect, but assumed it was technically unfeasible and thus never brought it up in production or design meetings.  In the latter example, no-one had even envisioned the potential of dynamic alteration of the soundscape of the play via sensor networks and DSP.

Actually, honestly, by the time the resident geek is even consulted even easily solvable problems have already been "solved" either by changing the blocking, or altering the set, or using one of the venerable theater tools like fishline.

(Although sometimes there is the flip side -- a company like Berkeley Rep, that might ask for "blood to come pouring out of the giant translucent eye"; a problem that required electro-pneumatics and some kitchen chemistry to solve.)

The point being, the set of known applications is much, much smaller than the total set of solutions for what had never previously been identified as potentially soluable problems!

Anyhow.  The one BlinkM-derivative application I am absolutely sure of is the candle or lantern.  Because I've done so many of them in the past.

My best lantern solution was a half-dozen grain-of-wheat bulbs or amber LEDs, wrapped with diffusion from the lighting supply closet, and a 9V battery pack stuck in the original fuel reservoir.   The most elegant switch I made was wired to the wick adjustment wheel.  The most intriguing one I saw, however, was at Berkeley Rep, which used an R/C model car servo to turn a potentiometer, when thus allowed the lamp to be turned off remotely and on cue.

Which allows this application to be readily parameterized; "A battery-powered, fully portable simulated lantern light that will fit into a prop lantern, that is bright enough to be visible under stage lights, and that can be turned on and off remotely -- preferably from within lighting cues (aka it becomes a wireless DMX device)."

The first round of stretch goals would be to allow a range of stable intensities (not just fade up, fade out), and to flicker.

The next round of stretch goals says, since this is a potential "drop in any light" solution -- aka candles, small torches, even a wall sconce on a portable set piece -- we should add control of the color, and control of the range of the flicker effect.

I can't actually imagine any other application so specific and well-described.  I can think of, say, a crystal ball (such as is used by the Witch in "Wizard of Oz."  Or a Vial of Mysterious Liquid that glows from within (it might even be Atomic!)

But it seems plausible that whatever the application, the very process of being small, battery powered, not that bright, and installed in a prop, seems to imply that the wanted behavior for each application can be worked out easily before installation.  Which is to say -- it doesn't have to be re-programmed over the air.

And in many, many applications, it makes more sense to have a button in the hands of the actor than it does a wireless connection to the lighting console.

So throw out the attempts to create a serial link.  Instead, as the BlinkM board makes available one analog input of the AVR, simply set it to be controlled via a resistor ladder.  As many different pre-programmed behaviors as the resolution of the analog in port allows can be called up with nothing more complicated than a button.

For the lantern, the most sensible is two commands; fade to lantern-on flicker loop, and fade to all LEDs off. 

And if you do desire wireless control?  Why, then, you can use the pin-passing mode of a pair of XBee nodes to pass an analog value.  Assuming you can work out the balance between the 4.5V nominal of the BlinkM and the 3.3V supply desired for the XBee, you should be able to press a button (or run a line of code on a host AVR) and have the XBee network pass that button value wirelessly to the lantern.

So here I am coding.  I am still using microseconds() to keep the size of the PWM loop consistent, but even though it is a lot more work I may want to switch to an interrupt routine based on one of the ATtiny45's two hardware timers.  The basic idea is straight-forward; for each loop through the PWM loop, the LEDs are all switched off, then at set values from the first step to the last step of the loop they are switched on, thus creating a duty cycle varying from (basically) 0% to 100%.

An added wrinkle is that human perception is roughly log, not linear, thus it makes sense to either include a gamma correction calculation or look-up table prior to determining the duty cycle.  This has the advantage of meaning you need less resolution (fewer steps) in the loop in order to maintain resolution (visible changes in brightness) at the lowest end of the scale.

The simplest possible shell code to this just feeds three numbers into the PWM loop; one each for R, G, and B.

The next iteration is a fade-to.  For this, we have the known RGB state, and a target RGB state, and a rate (the number of program steps between one and the other).  At this point the central statement looks more like  float current_value = (old_value - new_value/rate); if (timer > gamma_table[int (float current_value)) { LED = high; }

Which is to say; we need to use a floating-point variable in order to correctly track fractions of increase, convert it to Int to find the next whole-number step, and feed that into the look-up table in order to find the moment the LED needs to switch on.

But what of, say, a pulse like the sleep light on a computer?  This alternates between two steps; fadeTo on, and fadeTo out.  Or, rather, fade to, in alternating order, two defined RGB values.  So the program flags when all fade steps are completed, and then grabs the new target value and re-commenses.

Except.  It is easy to imagine a program in which more than two states follow.  A traffic-light pattern, say.  So rather than make an arbitrary list, instead we will establish an array of RGB values, and test on the completion of each fade loop if there is an array element in the ++ location.  And, of course, since not all steps would necessarily be the same length, the fourth element in the array is unique fade rates for each target.

And now the goals are getting reeeeaaallly stretchy.  Because it is easy enough to imagine having to fade from one multi-element loop to another.  In the simplest case, imagine a flickering candle slowly fading.  You don't want to freeze the flicker and fade from whatever the current color is.  Instead, you want to continue running the flicker program whilst changing ALL of the values in an incremental way according to the external fade loop.

And since you are being silly already, why not ask how you would go from a steady pulsing white light to a steady pulsing RED light -- without a stutter?  Well, by putting in the command that says "fade to new pulse" a flag that requires the new loop to synchronize with the old loop.

And at some point you realize the iterations become endless -- that there is always some scenario you can imagine by which a multi-stage program could be cross-faded with another multi-stage program.

And that's the point at which stretching really starts to hurt.  Better to get back to the known basics -- things like a flickering candle that can be dimmed -- and actually finish the code for that before adding on more and more bells and whistles.

No comments:

Post a Comment