Difference between revisions of "Spark-controlled gamelamp"
(3 intermediate revisions by the same user not shown) | |||
Line 52: | Line 52: | ||
Picked up a cheap 'one-spark-only' lighter | Picked up a cheap 'one-spark-only' lighter | ||
− | ([[:image | + | ([[:image:Michai_032_lighter_original.jpg|fig.1]]), emptied the gas-container, and |
behold, a spark-generator. | behold, a spark-generator. | ||
When working with the 'receiver' (next), the metal protective cylinder had to be | When working with the 'receiver' (next), the metal protective cylinder had to be | ||
shortened to expose the spark and cause any effect at all; modified version can be seen in | shortened to expose the spark and cause any effect at all; modified version can be seen in | ||
− | + | [[:image:Michai_032_lighter_modified.jpg|fig.2]]. Dremel(tm) to the rescue, once | |
again. | again. | ||
Line 178: | Line 178: | ||
Basically, 'gameplay' will consists of iterations | Basically, 'gameplay' will consists of iterations | ||
# show pattern to user | # show pattern to user | ||
− | # read pattern (presses ''A'', ''B'', and ''C'', with time '' | + | # read pattern (presses ''A'', ''B'', and ''C'', with time ''T'1'' between ''A'' and ''B'', and time ''T'2'' between ''B' and ''C'') from user |
− | # 'game over' (shut down) as soon as failure is detected (i.e. '' | + | # 'game over' (shut down) as soon as failure is detected (i.e. ''T'1'' or ''T'2'' not as expected) |
# congratulate user on getting it right | # congratulate user on getting it right | ||
Line 295: | Line 295: | ||
a superb show, and is given the option to play again (by re-entering ''WAIT''). | a superb show, and is given the option to play again (by re-entering ''WAIT''). | ||
− | == | + | == A demo == |
− | + | Behold, demo of all modes and playing a nice game: | |
− | + | ||
+ | <youtube size="small" align="right">oxqv_tNsUwQ</youtube> | ||
---- | ---- | ||
Have fun -- [[User:Michai|Michai]] | Have fun -- [[User:Michai|Michai]] |
Latest revision as of 17:17, 3 May 2010
Contents
Introduction, or: Do we really need this?
(Well yes we do!) I happen to have 3 Chinese-style lanterns hanging in my living-room. They are red, and very nice, but there's no light in them. I also happen to have one very nice wood-and-glass lamp, with translucent windows, but again, without a lamp.
I Considered...
- putting an actual candle in them (nice effect, bad idea, not doign that again soon)
- pulling a wire over the ceiling to each lamp (much work)
- shining some fixed light through the (red) lamp from behind it, giving nice effect (cumbersome)
- doing nothing (note that this is the most CBA-compliant option)
I basically like a flame-like light, remote-controlled.
El-cheapo remote
To be interesting here, a remote-control mechanism would have to be
- cheap
- simple
- power-friendly, receiver-wise
An electrical spark is a nice simple event; if one could trigger something with it, the mere maximum distance to the receiver might be enough to control multiple targets in the same room.
Transmitter
They sell cheap piezo-lighters everywhere; I know there are ones that emit a 'sequence' of sparks, and those that emit a single spark when pressed/activated. Some have a built-in gas-container, some have not, and are used for lighting stoves and such.
Picked up a cheap 'one-spark-only' lighter (fig.1), emptied the gas-container, and behold, a spark-generator.
When working with the 'receiver' (next), the metal protective cylinder had to be shortened to expose the spark and cause any effect at all; modified version can be seen in fig.2. Dremel(tm) to the rescue, once again.
Receiver
The friendly people on #electronics mentioned some (older) AVR MCU's reset by itself when a spark was near - excellent :-) I had a ATtiny13 in mind for this thing, but unfortunately it seemed very robust to sparks, even when nearby.
Attaching a circle-shaped antenna to the reset-pin, combined with slow startup-time, made the device nicely reset once at every spark. Tested this with a simple board with ATtiny13 that would flash a LED when starting execution; worked like a charm. See fig.3 and fig.4 (zoomed) for the voltage on the reset-pin during an attack.
Flame-lamp
Now for the lamp-effect itself. I know there are some imitation candle-lights you can charge using induction and then put on a table; the ones I saw have a single LED, and alternate between 2 intensities using a randomish pattern. Such flickering would (I guess) be very irritating in a living-room, so let's not do that.
Imitating a flame
Instead, gradually changing intensity from random setpoint to another random setpoint sounded more like it. To do this, I generated a 53-byte list of random bytes, and stored it in EEPROM. The time in which to change between 2 setpoints was also chosen from this list. See fig.5 for an idea; it's all very simple.
A routine simply iterates through the values to get time- and setpoint-values, without much magic. A timer-interrupt then ramps from previous to new setpoint using ATtiny13's 'fast PWM' mode; when the new setpoint is reached, new intensity-/time-values are taken from the list, and a new ramp is started.
Without any obvious highs and lows in the displayed pattern, the repetition is not disturbing at all, and will (probably) not be noticed.
Different light-modes
By varying the ranges of setpoint-intensities and time, different effects can be made: for a 'calm' flame, one would use a relatively small intensity-range, and relatively long ramp-time. For a 'wild' flame, one would use shorter times and a wider range of intensities. Limiting intensities and times is done using division and adding offsets to values from the random list.
I also wanted 2 'steady' modes - low and high intensity. This is done by simply replacing obtained random values by constants; ramp-time is still variable, but harmless.
The user can iterate through different modes as follows (prepare for ASCII-art; yes, I am too lazy to install/use Graphviz):
+---------------------------------------------------------------------+ | | V | +-----+ +-------------+ +-------------+ +--------+ +-----------+ | | OFF |-->| CALM_FLAME |-->| WILD_FLAME |-->| ON_DIM |-->| ON_BRIGHT |-+ +-----+ +-------------+ +-------------+ +--------+ +-----------+
In mode 'OFF', the MCU enters deep sleep (to be awoken by reset); in all other modes, different light-patterns are displayed as described above.
To iterate through the modes, ...
- user presses lighter
- MCU resets and restarts execution
- current mode is read from EEPROM
- next mode is written to EEPROM
- mode-dependent behaviour until reset (except in 'OFF'-mode)
Bonus-feature: "repeat the pattern"
So, all of the above was about 550 bytes of flash, without any serious optimising. A bit of a waste; the ATtiny13 has 1k of flash. So why not make something fun! (eventually, everything fitted into 1022 bytes, with slight optimisation; nothing like the CRAFT ATtiny2313 demo, no... :-) You can take a look at all the code if you want.
MB, the WII and probably many others offer a "repeat the pattern" game - the computer emits a pattern of some sort, and the user has to repeat it (or be sent to Mordor).
So let's do that: a game mode, where the lamp emits randomly-timed light-flashes, and the user has to repeat it with the lighter: press the lighter exactly in the same pattern as in which the flashes appeared.
Keeping track of time
An interesting thing is, since all the user can do is press the lighter and effectively reset the MCU, how to keep track of time. Timer-counters and other things will be set to power-on defaults after each reset.
To have a resettable counter that is not influenced by resetting of the MCU itself, I chose a simple capacitor, being charged by a big resistor to Vcc (i.e. 'counting'), or drained through a small resistor to gnd (i.e. 'resetting'). The pin draining the cap is also one of the MCU's ADC-pins, and can thus serve to read the capacitor-voltage (i.e. 'reading the counter'). See the schematics for an idea.
Because charging a cap this way is not linear at all over the whole voltage-range, let's only look at the first bit of the curve (fig.7): charging from 0 to about 20% of Vcc (20% * 3.2V is about 632 mV) is near-linear, and takes about 2.5 seconds.
The ATtiny13 has a 10-bit ADC, basically meaning that resolution is Vcc/1024. The ADC-readout consists of 2 bytes, but since we only look at, say, the first 20% of the range, only 8 bits (i.e. LSB) are enough.
The counter is reset (i.e. cap is drained) through a 100 Ohm resistor to the low-driven ADC-pin. This ensures not more than the maximum 40mA sink-current flows. (It's only for a short time but ok - we're that nice.) As can be seen in fig.8, a fully charged cap is drained in about 50ms.
Game-FSM
Basically, 'gameplay' will consists of iterations
- show pattern to user
- read pattern (presses A, B, and C, with time T'1 between A and B, and time T'2 between B' and C) from user
- 'game over' (shut down) as soon as failure is detected (i.e. T'1 or T'2 not as expected)
- congratulate user on getting it right
After each succesful iteration, a new game can be started right away.
For the above to fit into the already existing lamp-modes, an extra 'mode' is introduced:
+---------------------------------------------------------+ | | V | +-----+ +-------------+ +-----------+ +======+ | | OFF |-->| CALM_FLAME |--> ... -->| ON_BRIGHT |-->| GAME |-+ +-----+ +-------------+ +-----------+ +======+
The 'GAME' mode itself consists of a state machine as well:
| | .............|............................................................. : | : : GAME | (win) : : | +-------------------------------------------------+ : : | | | : : V V | : : +------+ +------+ +---------+ +---------+ +---------+ : : | WAIT |-->| SHOW |-->| CHECK_A |-->| CHECK_B |-->| CHECK_C | : : | | | | | _WAIT_B | | _WAIT_C | | | : : +------+ +------+ +---------+ +---------+ +---------+ : : | | | | | : : (timeout) | (key) | (lose) | (lose) | (lose) | : : | V V V | : : |<---------------------------------------------------+ : : | : :............|............................................................: | | V
Functionality of each game-state is shortly described below. When 'save state XXX' is mentioned, what is meant is that state 'XXX' is written to EEPROM, so that on next reset, execution resumes at 'XXX'. This is used to enter a certain state when the lighter is pressed within some time, and to fall back to another state (most likely 'OFF') on timeout.
WAIT
This is where the MCU waits to see whether the user wants to play a game or not:
- reset timer
- save state SHOW
- blink LED for 2 seconds
- if timeout, game over (where 'game over' means blink LED in sad pattern, save state 'OFF', and reset MCU from software
When the user presses the lighter within the blink-period, SHOW is entered after the following reset; if not, the lamp shuts down.
SHOW
The MCU displays the target-pattern:
- get random random-list position by using time spent in WAIT
- get random values for T1 and T2
- save state OFF
- wait 1 second; user waits in suspense...
- blink LED once and reset timer
- wait T1
- store ADC-value corresponding to current cap-voltage (thus, corresponding to T1)
- blink LED once and reset timer
- wait T2
- store ADC-value corresponding to current cap-voltage (thus, corresponding to T2)
- blink LED once and reset timer
- save state CHECK_A_WAIT_B
- if timeout, game over
The timer is reset in between LED-flashes A, B and C, the MCU reads its own timer-value just before resetting the timer to learn T1 and T2.
CHECK_A_WAIT_B
User pressed to imitate flash A;
- reset timer and blink LED for acknowledgement
- save state CHECK_B_WAIT_C
- if timeout, game over
Since A (the 1st of 3 target-flashes as given in state SHOW) is just an indication that the user has started to mimic the MCU's pattern, no time-checking can be done yet. However, the timer is reset to measure the user's T'1, next.
CHECK_B_WAIT_C
Time T'1 is verified, and MCU waits for C:
- verify measured time T'1 against stored T1 in EEPROM; game over on failure
- reset timer and blink LED for acknowledgement
- save state CHECK_C
- if timeout, game over
Since the user pressed to mimic flash B, time T'1 between A and B can be measured and verified. Likewise, the timer is reset to prepare for measurement of T'2.
CHECK_C
Time T'2 is verified:
- verify measured time T'2 against stored T2 in EEPROM; game over on failure
- blink LED for acknowledgement
- save state WAIT
- display 'congratulations' light-effect (it's really nice! ;-)
- wait 1 second
- reset MCU from software
When the last mimiced time T'2 matches, the game is won; the user is treated to a superb show, and is given the option to play again (by re-entering WAIT).
A demo
Behold, demo of all modes and playing a nice game:
<youtube size="small" align="right">oxqv_tNsUwQ</youtube>
Have fun -- Michai