Friday, October 12, 2012

Motion Sensing Fog Machine Trigger

For halloween I always use a fog machine on my porch, however it's a pain to have to turn it on at just the right time. I decided to make a motion sensing trigger.

I started by taking apart the control box that came with the fog machine.  On the outside it looks like this:

It has a light that comes on when the fog machine is heated up, as well as a switch that turns on the pump for the fog.

The connector for the fog machine looks like this:
It's a typical IEC320C13 connector

The inside is pretty straightforward, the white and green wires attach to the light, and the green (really red, but it is connected to the green) and black connect to the switch 


I measured the voltage at the switch and the light when they were activated and found that they were mains AC 120v in both cases.

The basic design I came up with was to use a microcontroller, powered somehow off the power at the light, that way when the fog machine was ready it would turn the microcontroller on and it could control the fog machine using a relay attached to the green and black wires.

Since I didn't want to destroy my stock fog machine controller, I bought an extension cord from amazon (http://www.amazon.com/gp/product/B000067RRX).  After cutting the extension cord open I found that the black and white wires were swapped with respect to the pins on the IEC connector compared to the stock controller.

I found an old 5v cell phone charger adapter, and soldered the black and green lines to it, this would power the microcontroller when the fog machine was sending the signal that the heater in the fog machine was ready.

The green and white lines were then wired into the relay. I used a relay board from SparkFun (https://www.sparkfun.com/products/10733) that was donated by a friend.  I know the SparkFun site says to not use this board, but I decided I'd go for it anyway ;)


For a controller, I used an Atmel attiny85 I had lying around. The program was simple. I added a capacitor for power conditioning and an LED to indicate when the controller was in the "armed" state, ready to trigger fog at the next motion detected.  This controller is connected to the relay board on the right in the picture below.


Attached to the attiny85 is a Parallax PIR sensor that I bought at Fry's electronics in Plano, TX last thanksgiving, why the heck aren't there Fry's electronics stores in Utah?

Here's the finished assembly, this will all be stuffed in a fairly small project enclosure box.



In the end it works perfectly, the program is written to activate the fog for 5 seconds when it detects motion, then wait until it hasn't seen motion for 10 seconds before re-arming, and then triggering again on the next motion detected, etc.  Here is a demo of it in operation, you can see the LED light up when the controller is armed.  You can also see the project box that will be the controller's final home.


Here's the code for the attiny85:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#ifndef F_CPU
    #define F_CPU 16000000UL
#endif

//Hardware definitions

#define LED1  PB1
#define FOG   PB0
#define LED2  PB2
#define OUTPUT_LEDS    ((1 << LED1) | (1 << LED2) | (1 << FOG))

#define MOTION  PB4
#define INPUT_LEDS (1<<MOTION)
//DEADPERIOD * DELAY = delay in MS the motion sensor has to be quiet before rearming
#define DEADPERIOD 100
#define DELAY 100
//Time in seconds to activate the fog
#define FOGTIME 5

volatile unsigned char armed = 0;
volatile unsigned char tripped = 1;
volatile int count = 0;

unsigned char readPin(int pin) {
 unsigned char i = PINB;
 i>>=pin;
 return i&1;
}

void writePin(int pin, unsigned char value) {
 if (value > 0) {
  PORTB |= (1<<pin);
 } else {
  PORTB &= ~(1<<pin);
 }
}

void fog() {
 writePin(FOG,1);
 _delay_ms(1000*FOGTIME);
 writePin(FOG,0);
}

void setup() {
 DDRB = OUTPUT_LEDS;
 PORTB &= ~OUTPUT_LEDS;
 while(readPin(MOTION)==1){}
}

void loop() {
 writePin(LED1,armed);

 if (armed && readPin(MOTION)) {
     fog();
     tripped = 1;
     armed = 0;
     count = DEADPERIOD;
   } else if (tripped && (count > 0)) {
  if (readPin(MOTION)) { // repeat trigger, reset count
       count = DEADPERIOD;
  }
  count--;
 } else if ((tripped) && (count == 0)) {
      armed = 1;
      tripped = 0;
 }
 _delay_ms(DELAY);
}


int main(void){
 setup();
 
 for (;;) {
 loop();
 }

 return 0;
}