Attiny85 AVR PWM Haunted House Model LED Lights

by CScientific in Circuits > Electronics

486 Views, 0 Favorites, 0 Comments

Attiny85 AVR PWM Haunted House Model LED Lights

finalResults
Cover.jpg

Using an attiny85 and a 5 volt LED light strip to add spooky light effects to a haunted house model.

The attiny85 is configured to run in very low power idle mode. An interrupt on one of the pins triggered by a momentary switch, initiates the light sequence.

The LED light strip is cut into pieces and then soldered together into three strips, one for the first, second and third floors of the haunted house.

The first and second floors operate off of the two attiny85 PWM (pulse width modulation) pins allowing them to dim and brighten. The third floor just cycles on and off. The current coded sequence runs about four and a half minutes.

Supplies

Supplies1.jpg
Supplies2.jpg
HauntedHouseModel1.jpg
HauntedHouseModel2.jpg
HauntedHouseModel3.jpg
FavoriteToolsAndMeters.jpg

First pic is the components to be used laid out.

Second pic is the 5 volt LED light strip to be cut up for the haunted house lights.

Third, fourth, and fifth pics are the homemade haunted house. It's just a couple of amazon shipping boxes cut to form and attached together with black duct tape. Once put together the whole thing was spray painted with some cheap black spray paint and finally Halloween images from a sales catalog were cut out and pasted all over.

The last pic shows some of my favorite tools used in a project like this.

Initial Programming and Testing the Attiny85

ProgrammingAndTesting1.jpg
ProgrammingAndTesting2.jpg

The attiny85 was programmed with the SparkFun AVR Programmer using the Arduino IDE.

The chip is configured (boot loaded) to run on the 1 MHZ internal clock (to minimize idle power usage).

The most interesting things here are the low power configuration settings in the code and using the two PWM pins to dim and brighten the lights on the first and second floor. A momentary switch is used to trigger the light show. The light sequence will continue for about four and a half minutes before returning the attiny85 to idle/low power mode.

More information on the programming board in the picture and the LED test strip can be found in this instructable:

https://www.instructables.com/Attiny85-Attiny84-and-Atmega328-Programming-Boards

Here is a link to the final source code:

http://www.cscientific.com/cgi-bin/displaySource.pl?dir=/etc/freesource/&file=hauntedHouse.ino

It's also pasted below:

/*
Using an attiny85 for low power light effects.
A push button will trigger an interrupt that turns on light effect sequence.
Includes use of the two PWM pins.
Code set to run on an attiny85 at 1 Mhz internal clock.

Interrupt pin:
Arduino Attiny85 Attiny85 Attiny85
ref port Intrpt physical
4 PB4 PCINT4 3

Mosfet pins:
Arduino Attiny85 Attiny85 Attiny85
ref port Intrpt physical
0 PB0 PCINT0 5 // first floor PWM
1 PB1 PCINT1 6 // second floor PWM
2 PB2 PCINT2 7 // third floor
*/

#include "avr/sleep.h"

#define firstFloor PB0
#define secondFloor PB1
#define thirdFloor PB2

#define firstFloorMask 0b11111110
#define secondFloorMask 0b11111101
#define thirdFloorMask 0b11111011

#define prrTimer0Mask 0b11111011

// interrupt pin
int intPin = 4; // Attiny85 PB4 Physical pin 3

int inInterrupt=0;

int duration = 10800; // seconds (3600=1hr) (7200=2hr) (10800=3hr) (14400=4hr)

// _delay_ms() cannot use large arguments, breaking delays into loops
void delay_a_half(void) { _delay_ms(500); }
void delay_a_second(void) { _delay_ms(1000); }
void delay_seconds(int j) { for (int i = 0; i < j; i++) delay_a_second(); }
void delay_a_minute(void) { for (int i = 0; i < 60; i++) delay_a_second(); }
void delay_minutes(int j) { for (int i = 0; i < j; i++) delay_a_minute(); }

void setup() {
// initialize all pins as inputs
DDRB=0;
PORTB=0; // set all pins low
// Set 'floor' pins to be a outputs to turn on mosfets
DDRB |= (1<<firstFloor);
DDRB |= (1<<secondFloor);
DDRB |= (1<<thirdFloor);
PORTB &= firstFloorMask; // turn off mosfet
PORTB &= secondFloorMask; // turn off mosfet
PORTB &= thirdFloorMask; // turn off mosfet
// allow PIR a few seconds to stabilize
// _delay_ms(10*1000);
// disable watchdog timer
WDTCR |= (1<<WDCE) | (1<<WDE); // two steps must be performed within four clock cycles
WDTCR &= ~(1<<WDE);
} // end setup

// configure an interrupt on PB4 (PCINT4)
void initDelayPinInterrupt(void)
{
GIMSK |= (1<<PCIE);
PCMSK |= (1<<PCINT4);
sei();
} // end initPIRInterrupt

// action performed on interrupt
ISR(PCINT0_vect)
{
if (digitalRead(intPin)==HIGH)
{
lightShow();
} // end if
GIFR |= (1<<PCIF); // clear the interrupt
} // end ISR

void lightShow(void)
{
inInterrupt=1;
PRR &= prrTimer0Mask; // turn timer0 PRR bit off
for (int i=0;i<3;i++)
{
// 1st cycle
PORTB |= (1<<thirdFloor); // turn on mosfet
delay_a_second();
// turn on first floor slowly
for (int j=0;j<255;j++) { analogWrite(firstFloor,j); _delay_ms(30); } // end for
delay_a_half();
PORTB &= thirdFloorMask; // turn off mosfet
// turn on second floor slowly
for (int j=0;j<255;j++) { analogWrite(secondFloor,j); _delay_ms(30); } // end for
delay_a_half();
PORTB |= (1<<thirdFloor); // turn on mosfet
// 2nd cycle
// turn off first floor slowly
// blink first
analogWrite(firstFloor,0);
_delay_ms(200);
analogWrite(firstFloor,255);
for (int j=255;j>=0;j--) { analogWrite(firstFloor,j); _delay_ms(30); } // end for
// turn off second floor slowly
// blink first
analogWrite(secondFloor,0);
_delay_ms(200);
analogWrite(secondFloor,255);
for (int j=255;j>=0;j--) { analogWrite(secondFloor,j); _delay_ms(30); } // end for
PORTB &= thirdFloorMask; // turn off mosfet
// 3rd cycle
// turn on first and second floor slowly
for (int j=0;j<255;j++) { analogWrite(firstFloor,j); analogWrite(secondFloor,j); _delay_ms(30); } // end for
delay_a_half();
PORTB |= (1<<thirdFloor); // turn on mosfet
// 4th cycle
// turn off first and second floor slowly
// blink first
analogWrite(firstFloor,0);
analogWrite(secondFloor,0);
_delay_ms(200);
analogWrite(firstFloor,255);
analogWrite(secondFloor,255);
for (int j=255;j>=0;j--) { analogWrite(firstFloor,j); analogWrite(secondFloor,j); _delay_ms(30); } // end for
// 5th cycle
PORTB |= (1<<thirdFloor); // turn on mosfet
delay_a_second();
// turn on second floor slowly
for (int j=0;j<255;j++) { analogWrite(secondFloor,j); _delay_ms(30); } // end for
delay_a_half();
PORTB |= (1<<thirdFloor); // turn on mosfet
// turn on first floor slowly
for (int j=0;j<255;j++) { analogWrite(firstFloor,j); _delay_ms(30); } // end for
delay_a_half();
PORTB &= thirdFloorMask; // turn off mosfet
// 6th cycle
// turn off second floor slowly
// blink first
analogWrite(secondFloor,0);
_delay_ms(200);
analogWrite(secondFloor,255);
for (int j=255;j>=0;j--) { analogWrite(secondFloor,j); _delay_ms(30); } // end for
// turn off first floor slowly
// blink first
analogWrite(firstFloor,0);
_delay_ms(200);
analogWrite(firstFloor,255);
for (int j=255;j>=0;j--) { analogWrite(firstFloor,j); _delay_ms(30); } // end for
PORTB &= thirdFloorMask; // turn off mosfet
// 7th cycle
// turn on first and second floor slowly
for (int j=0;j<255;j++) { analogWrite(firstFloor,j); analogWrite(secondFloor,j); _delay_ms(30); } // end for
delay_a_half();
PORTB |= (1<<thirdFloor); // turn on mosfet
// 8th cycle
// turn off first and second floor slowly
// blink first
analogWrite(firstFloor,0);
analogWrite(secondFloor,0);
_delay_ms(200);
analogWrite(firstFloor,255);
analogWrite(secondFloor,255);
for (int j=255;j>=0;j--) { analogWrite(firstFloor,j); analogWrite(secondFloor,j); _delay_ms(30); } // end for
} // end for
analogWrite(firstFloor,0);
analogWrite(secondFloor,0);
PORTB &= firstFloorMask; // turn off mosfet
PORTB &= secondFloorMask; // turn off mosfet
PORTB &= thirdFloorMask; // turn off mosfet
delay_a_second();
// flash to alert that mosfet is about to turn off
for (int k=0;k<3;k++)
{
PORTB |= (1<<firstFloor); // turn on mosfet
PORTB |= (1<<secondFloor); // turn on mosfet
PORTB |= (1<<thirdFloor); // turn on mosfet
delay_a_half();
PORTB &= firstFloorMask; // turn off mosfet
PORTB &= secondFloorMask; // turn off mosfet
PORTB &= thirdFloorMask; // turn off mosfet
delay_a_half();
} // end if
PORTB &= firstFloorMask; // turn off mosfet
PORTB &= secondFloorMask; // turn off mosfet
PORTB &= thirdFloorMask; // turn off mosfet
PRR |= (1<<PRTIM1) | (1<<PRTIM0) | (1<<PRUSI) | (1<<PRADC); // Use power reduction register
inInterrupt=0;
} // end lightShow

void loop()
{
volatile uint8_t dataFlush;
uint8_t byte1st,byte2nd;
initDelayPinInterrupt(); // initialize the interrupt pin
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
while (1)
{
// sleep mode will block sleeping until the interrupt pin is triggered
// disable components to reduce power usage while sleeping
ADCSRA &= ~(1<<ADEN); // Disable ADC set bit ADEN to zero
ACSR &= ~(1<<ACIE); // Clear Analog Comparator interrupt bit (set to zero)
ACSR |= (1<<ACD); // Disable Analog Comparator set bit ACD to one
byte2nd = MCUCR|(1<<BODS); // Disable Brown Out detector
byte2nd &= ~(1<<BODSE); // Make sure BODSE is zero on second assignment
byte1st=byte2nd|(1<<BODSE); // To enable BOD change.
dataFlush=byte1st+byte2nd; // dataFlush being volatile forces byte1st and byte2nd
MCUCR=byte1st; // Set with BODSE.
MCUCR=byte2nd; // And without within 4 cycles.
PRR |= (1<<PRTIM1) | (1<<PRTIM0) | (1<<PRUSI) | (1<<PRADC); // Use power reduction register
sleep_mode();
// delay until interrupt action is finished before returning to sleep
_delay_ms(100);
while (inInterrupt==1) { _delay_ms(100); } // end while
} // end while
} // end loop

Soldering the PCB

attiny85HauntedHouseLights.jpg
Step1a.jpg
Step1b.jpg
Step2a.jpg
Step2b.jpg
Step3a.jpg
Step3b.jpg
Step4a.jpg
Step4b.jpg
Step5a.jpg
Step5b.jpg
Step6.jpg
Step7.jpg
Step8.jpg
Step9.jpg
Step10.jpg
FinishedPCB.jpg

The pics here show the steps to solder the components onto the PCB to match the schematic in the first pic. The backside of the board is shown in steps 1 through 5. After that there are too many solder connections to keep track of.

Prepping the LED Light Strip

LightStripCut.jpg
SolderingLightStrip1.jpg
SolderingLightStrip2.jpg
SolderingLightStrip3.jpg
FirstFloorLEDStrips.jpg
SecondFloorLEDStrips.jpg
ThirdFloorLEDStrips.jpg

The 5 volt LED light strip was cut into pieces to be used for under eave lighting across the front and sides of the three floors of the haunted house.

Pics two, three, and four show a cut section of the LED strip (there is an indicator for where to cut on the strip), a piece of the clear covering lifted for access to the solder pads (an alligator clip helps hold up the cover, limiting damage from heating), and an example of a soldered connection.

The last three pics show the LED sections for the first, second, and third floor respectively.

More Testing

TestingCircuitAndSoftware.jpg
testingCircuitandSoftware

Before proceeding with the house wiring, the circuit and software was tested with three sections of the prepped LED strips,

Video is about thirty seconds, showing some of the light sequence.

Final Results

WiringFromTheBack.jpg
LightsIlluminated.jpg
finalResults

First pic shows the wiring from the back. The 3D cell battery pack and the PCB board are glued into place with a hot glue gun.

Second pic shows view from the bottom of the lights illuminated.

Video shows about a minute of the lights sequencing on and off.

Thanks for checking it out.

Any questions, feel free to inquire.

Future

There is one pin left on the attiny85 (besides the reset). I have a small cheap audio board on order from China (about $5). If I get it in time for Halloween and I can get it to work I may add it to the project to play some spooky sounds when the lights are triggered.