USB Powered Blinking IR motion sensor mounted in a Mario Brothers Candy Tin
(or fun blinky motion detector thingy to put in your cube)
If you have trouble seeing the video above, or you just hate flash download the movie direct here.
With an attiny13A, an IR motion sensor, and a candy Mario Brothers mushroom tin, this project turns them into
a USB powered IR sensor that also blinks and sings. This is a variation of the
marioman project but unlike that one this *this* is practical ;)
Here are the features:
- The container for the project is a metal Super Mario Bro candy tin
- Electronics are powered by a USB cable (for 5v)
- An external, attached, IR sensor is used to detect motion which triggers the LED and musical sequence.
- The attiny13A is programmed to blink on and off first for a set duration, then jump into the musical sequence.
- The LEDs blink to the tune of super mario brothers
- The speaker can be shut off by a switch attached to the speaker
Construction
Materials
Total cost of materials ~ $20
Construction
Drilling the Candy Tin
- A high-carbon drill bit and a hand drill was used to create the three holes in the candy tin.
- One hole is in the back to feed the sensor and USB cable .
- The other two holes were for the LEDs which protrude from each side of the tin.
The wires pirctured below on the right were soldered together and attached to the three wire plug which will plug into the headers on the main circuit board for power, gnd, and the sensor line.
Main Circuit
GND
|
___
---------- --- .1uF
| | |
5V--| PB5 VCC |--------- USB power (5V)
---------| PB3 PB2 |--LED2
| LED1--| PB4 PB1 |---|
| |----| GND PB0 |-
/ | | |
| GND 5V ---------- /switch
| | | |
--------- \ /
| IR | \||
| sensor | GND-||
--------- \
- The ascii diagram above describes how the attiny13 is hooked up to the external components.
- Like the marioman, two LEDs are connected to the microcontroller.
- Instead of dedicating two pins for GND connections one pin is used for the IR sensor.
- The reset pin is tied to 5V to prevent it from accidentally going low, coupling capacitor is required since we don't have a clean power source.
- The speaker is driven by the lower right pin, no extra circuitry is necessary here except a switch to optionally turn it off.
To fit everything in the candy tin a protoboard was cut into a small rectangle and the components were arranged on it.
Headers seen on the left next to the LED (pictured above) are used to connect to the computer cable which is a three wire connection to the IR sensor. (one pin is unused on the connector)
An IC socket is essential to make it easy to re-program the chip, the LEDs on each side will bend to protrude outside of the tin.
Software
The attiny13A has 1K of programmable flash, and 64bytes of SRAM.
Firmware for the attiny13A as well as the source can be downloaded here -
attiny13_mario.tar
Three arrays in the c code were used to generate the music
- freq[] - frequencies of each note
- length[] - length of each note
- delay[] - pause between each note
The frequency array does not have the actual frequencies but rather the value to put in the TTCROB register to generate the square wave off the PB0 pin.
Here is a brief summary of the calculations and pin configuration for square wave generation:
- The attiny13A has an internal oscillator set to 9.6MHz
- The internal clock for IO is the oscillator divded by 8 or 1.2MHz
- An internal timer is setup in a 8bit register to count up every clock cycle with prescale of 8.
- This results in one tick equal to 1 / (1.2MHz / 8) = .006667ms
- The attiny13A is configured to compare what is in the 8bit TCCR0B register with the timer and toggle a pin when they match.
- For example, in order to generate a square wave at 524Hz (one octave above middle C) which has a period of 1.908ms.
- 1.908ms = 286 clock ticks (1.908/.0067)
- Divide 286 by 2 to toggle the pin at t/2 (286/2 = 143)
- Put 143 in the TTCR0B register to generate this note.
This is all the code that is necessary to set up the timer, do the compare and output a square wave:
TCCR0A |= (1<<WGM01); // configure timer 1 for CTC mode
TCCR0A |= (1<<COM0A0); // toggle OC0A on compare match
TCCR0B |= (1<<CS01); // clk/8 prescale
TTCR0B = 143; // generate a square wave at 524Hz
To delay the tones and the pauses between them a delay function was used that counts down from 150 where each NOP takes approx .007ms.
void sleep(int ms) {
int cnt;
for (cnt=0; cnt<(ms); cnt++) {
int i = 150;
while(i--) {
__asm("NOP");
}
}
}
Here is the main code loop:
const uint8_t freq[] PROGMEM = { ... data };
const uint8_t length[] PROGMEM = { ... data };
const uint8_t delay[] PROGMEM = { ... data };
...
for (;;) {
static uint8_t cnt=0;
static int time_delay=0;
static uint8_t led1 = 1; //initial value is on
static uint8_t led2 = 0; //initial value is off
if ( (PINB & 1<<SENSOR) && time_delay > 20) {
//start playing the tune
//restore LED values
(led1) ? (output_high(PORTB, LED1)) : (output_low(PORTB, LED1));
(led2) ? (output_high(PORTB, LED2)) : (output_low(PORTB, LED2));
//start timer
TCCR0B |= (1&&CS01); // clk/8 prescale
OCR0A=pgm_read_byte(&freq[cnt]);
output_toggle(PORTB,LED1);
output_toggle(PORTB,LED2);
sleep( pgm_read_byte(&length[cnt]) );
output_toggle(PORTB,LED1);
output_toggle(PORTB,LED2);
//save values
led1 = read_value(PORTB, LED1);
led2 = read_value(PORTB, LED2);
// stop timer
TCCR0B = 0;
sleep ( pgm_read_word(&delay[cnt]) );
// start timer
TCCR0B |= (1<<CS01); // clk/8 prescale
cnt++;
if (cnt > 155) cnt=0;
} else if (PINB & 1<<SENSOR) {
output_low(PORTB,LED1);
output_low(PORTB,LED2);
sleep(300);
output_high(PORTB,LED1);
output_high(PORTB,LED2);
sleep(300);
time_delay++;
} else {
time_delay = 0; // reset the time delay
//turn of leds
output_low(PORTB,LED1);
output_low(PORTB,LED2);
//stop timer
TCCR0B = 0;
}
}
- When the IR sensor line goes high the chip waits for 20 cycles of blinking the LEDs off and on.
- Following that, if there is still motion detected by the IR sensor it will start into a musical sequence.
- At anytime if the motion stops the leds are turned off and it goes to sleep.
- Whenever the LED alternate the state is saved and restored so the sequence can pick up where it let off.
The cnt variable keeps track of the index into the musical sequence. This is so that if the song is triggered it won't always start from the beggining but instead begin where it last left off.
Large amount of read-only data and pgmspace.h
In the code above you might have notice the two functions pgm_read_byte() and pgm_read_word() as well as the keyword PROGMEM.
With an embedded chip like the attiny the amount of SRAM is very limited, in this case only 64bytes.
The arrays we are using for all of the frequency/delay/length data are much larger than 64bytes and therefore can not be loaded into memory.
By using the special PROGMEM avr-gcc directive these large data arrays are prevented from loading into memory, instead they are read from flash.