Since the plan is to use this on our bedroom, I added a touch-sensor (just put a wire connected to any metal thingy into a digital input) to also turn the lights on/off.
Its very easy to setup the relay. At the bottom you will see 5 connections, the left 3, the middle goes to your 110/220V power. Left and right to the Arduino Ground and output pin. Them the other 2 are the On and Off outputs for the 110/220 destination, in this case, our lights. 8)
So, I just tested the whole thing with an ATtiny85 using its internal 8Mhz clock and it works perfectly. Here's the complete code as I'm using on the ATtiny85.
I didn't use an Arduino as I need something very small to fit on the power-box that has the current light on/off switch, I will replace the whole thing with this setup: ATtiny85, Mini Relay and 5V USB 110/220V adapter from an old Wii controller charger that was no longer used. (the controllers batteries failed, but the charger still works, 5V @ 500ma)
Everything is working perfectly and its already in place. I will post some pictures and videos soon.
In the end, here's the code I'm using. It only turns the lights off when you clap-clap, to avoid getting them to turn on in the middle of the night if you cough.
After using it on the "real" world, I had to do some changes on the code. Now I save to the EEPROM the Background-Noise levels, so when the power fails, which happens a lot here, it doesn't mess up the system if it startups again and there's some noises going on... Also, fixed some other issues.
// Arduino Clapper - by WilliamK @ Wusik.com & Beat707.com - September 06 2011 - V 1.2.0 //
// On V1.2.0, when powering up, it will flash the output them detect for 10 seconds any ambient noise and save into the EEPROM and flash the output once its finished //
#include <EEPROM.h>
#define inPin A1 // Pin input for the audio sensor
#define outPin 0 // Output pin to active external device (use relay for 110/220V)
#define touchPin PINB4 // Must be in PINB# format (digital pin 8 ~ 13 = PINB0 ~ PINB5)
#define clapTime 200 // How long to wait between claps
#define clapIdle 1000 // How long to wait after 2 claps before turning on external device
#define debounceTime 150 // Debounce time, to remove double inputs due to echoing
#define waitBeforeRetry 2000 // How long to wait before trying again after a failed set of claps or loud background noise/sounds
#define delayAfterSwitch 500 // How long to wait after a switch has happened
#define defaultSensitivity 50 // 50 should be ok for most uses
#define outputToSerial 0 // Outputs Stage information to the Serial Device
#define force_new_background 0 // If set to 1 it will force a new Noise Background check
// ------------------------------------------------------------------------------------------------------------------------ //
int def2, num1, num2, num3, def = 0;
uint8_t isOn = false;
uint8_t prevIsOn = false;
unsigned long lastMillis = millis();
uint8_t stage = 0;
#if outputToSerial
uint8_t prevStage = 9;
#endif
int sensitivity = defaultSensitivity;
int lastValue = 0;
uint8_t prevButton = 0;
uint8_t curButton = 0;
uint8_t wasButtonLow = 1;
// ------------------------------------------------------------------------------------------------------------------------ //
void EEPROMWriteInt(int p_address, int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}
// ------------------------------------------------------------------------------------------------------------------------ //
unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
// ------------------------------------------------------------------------------------------------------------------------ //
void setup()
{
pinMode(outPin, OUTPUT);
pinMode(inPin, INPUT);
if (EEPROM.read(0) != 24 || force_new_background) // Check for the Background Noise //
{
delay(2000);
digitalWrite(outPin, HIGH);
delay(150);
digitalWrite(outPin, LOW);
for (char x=0; x<4; x++)
{
delay(1000);
num1 = analogRead(inPin);
delay(250);
num2 = analogRead(inPin);
delay(250);
num3 = analogRead(inPin);
def2 = ((num1 + num2 + num3) / 3) + sensitivity;
if (def2 > def) def = def2;
}
for (char x=0; x<6; x++)
{
delay(1000);
num1 = analogRead(inPin);
delay(250);
num2 = analogRead(inPin);
delay(250);
num3 = analogRead(inPin);
def2 = ((num1 + num2 + num3) / 3) + sensitivity;
if (def2 < def) def = def2;
}
// Write Default Background Noise //
EEPROMWriteInt(1, def);
EEPROM.write(0, 24);
digitalWrite(outPin, HIGH);
delay(150);
digitalWrite(outPin, LOW);
}
#if outputToSerial
Serial.begin(38400);
#endif
prevButton = getcap(1<<PINB0);
def = EEPROMReadInt(1);
}
// ------------------------------------------------------------------------------------------------------------------------ //
void loop()
{
lastValue = analogRead(inPin);
if (isOn)
{
switch (stage)
{
case 0: if (lastValue > def) { lastMillis = millis() + clapTime; stage = 1; delaySomeTime(debounceTime); } break;
case 1: if (lastValue > def) { stage = 5; lastMillis = millis() + waitBeforeRetry; } else if (millis() > lastMillis) { stage = 2; lastMillis = millis() + clapTime; } break;
case 2: if (lastValue > def) { lastMillis = millis() + clapIdle; stage = 3; delaySomeTime(debounceTime); } else if (millis() > lastMillis) { stage = 5; lastMillis = millis() + waitBeforeRetry; } break;
case 3: if (lastValue > def) { stage = 5; lastMillis = millis() + waitBeforeRetry; } else if (millis() > lastMillis) stage = 4; break;
case 4: if (lastValue < def) { isOn = !isOn; } stage = 0; break;
case 5: // This is the Wait Before Retrying stage //
if (lastValue > def) lastMillis = millis() + waitBeforeRetry; else if (millis() > lastMillis) stage = 0;
break;
}
}
#if outputToSerial
if (stage != prevStage)
{
Serial.println(stage, DEC);
prevStage = stage;
}
#endif
checkButton();
}
// ------------------------------------------------------------------------------------------------------------------------ //
void checkNewOn()
{
if (isOn != prevIsOn)
{
digitalWrite(outPin, isOn);
prevIsOn = isOn;
delay(delayAfterSwitch);
}
}
// ------------------------------------------------------------------------------------------------------------------------ //
void delaySomeTime(unsigned long ms)
{
uint16_t start = (uint16_t)micros();
while (ms > 0)
{
if (((uint16_t)micros() - start) >= 1000)
{
ms--;
start += 1000;
}
checkButton();
}
}
// ------------------------------------------------------------------------------------------------------------------------ //
void checkButton()
{
curButton = getcap(1<<touchPin);
if (curButton != prevButton)
{
prevButton = curButton;
if (wasButtonLow == 1 && curButton > 1)
{
isOn = !isOn;
wasButtonLow = 0;
stage = 0;
checkNewOn();
delay(500);
}
if (curButton <= 1) wasButtonLow = 1;
}
checkNewOn();
}
// ------------------------------------------------------------------------------------------------------------------------ //
char getcap(char pin)
{
char i = 0;
DDRB &= ~pin; // input
PORTB |= pin; // pullup on
for(i = 0; i < 2; i++)
{
if( (PINB & pin) ) break;
}
PORTB &= ~pin; // low level
DDRB |= pin; // discharge
return i;
}
Last night the lights would turn off just by opening the bathroom door or by speaking, so I had to revisit the code. Also, we had some power spikes here and in one of those the device wouldn't work anymore, as it got a loud background noise when it powered up again, so I had to come with another solution.
In the next few days we will know if this is really good now. 8)
I really love working with the Arduino IDE, its just pure fun.
Do you think people need some Fritzing files on how I connected the whole thing up? Its actually easy, as the ATtiny85 has so few pins.
Its working much better with the new code now, yeah! Its still a bit too sensitive at the default values, so, when I close the bathroom door, the clicks the door makes shut down the lights. But that's ok, its not often.
I had to update the code, as the lights were turning off with no apparent reason, so I did some hacks to the code and now its 99% perfectly. Before, closing a door would turn the lights off too.
// Arduino Clapper - by WilliamK @ Wusik.com & Beat707.com - September 06 2011 - V 1.2.0 //
// On V1.2.0, when powering up, it will flash the output them detect for 10 seconds any ambient noise and save into the EEPROM and flash the output once its finished //
#include <EEPROM.h>
#define inPin A1 // Pin input for the audio sensor
#define outPin 0 // Output pin to active external device (use relay for 110/220V)
#define touchPin PINB4 // Must be in PINB# format (digital pin 8 ~ 13 = PINB0 ~ PINB5)
#define clapTime 250 // How long to wait between claps
#define clapIdle 1000 // How long to wait after 2 claps before turning on external device
#define debounceTime 100 // Debounce time, to remove double inputs due to echoing
#define waitBeforeRetry 3000 // How long to wait before trying again after a failed set of claps or loud background noise/sounds
#define delayAfterSwitch 500 // How long to wait after a switch has happened
#define defaultSensitivity 50 // 50 should be ok for most uses
#define outputToSerial 0 // Outputs Stage information to the Serial Device
#define force_new_background 0 // If set to 1 it will force a new Noise Background check
// ------------------------------------------------------------------------------------------------------------------------ //
int def2, num1, num2, num3, def = 0;
uint8_t isOn = false;
uint8_t prevIsOn = false;
unsigned long lastMillis = millis();
uint8_t stage = 0;
#if outputToSerial
uint8_t prevStage = 9;
#endif
int sensitivity = defaultSensitivity;
int lastValue = 0;
uint8_t prevButton = 0;
uint8_t curButton = 0;
uint8_t wasButtonLow = 1;
// ------------------------------------------------------------------------------------------------------------------------ //
void EEPROMWriteInt(int p_address, int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}
// ------------------------------------------------------------------------------------------------------------------------ //
unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
// ------------------------------------------------------------------------------------------------------------------------ //
void setup()
{
pinMode(outPin, OUTPUT);
pinMode(inPin, INPUT);
if (EEPROM.read(0) != 24 || force_new_background) // Check for the Background Noise //
{
delay(2000);
digitalWrite(outPin, HIGH);
delay(150);
digitalWrite(outPin, LOW);
for (char x=0; x<4; x++)
{
delay(1000);
num1 = analogRead(inPin);
delay(250);
num2 = analogRead(inPin);
delay(250);
num3 = analogRead(inPin);
def2 = ((num1 + num2 + num3) / 3) + sensitivity;
if (def2 > def) def = def2;
}
for (char x=0; x<6; x++)
{
delay(1000);
num1 = analogRead(inPin);
delay(250);
num2 = analogRead(inPin);
delay(250);
num3 = analogRead(inPin);
def2 = ((num1 + num2 + num3) / 3) + sensitivity;
if (def2 < def) def = def2;
}
// Write Default Background Noise //
EEPROMWriteInt(1, def);
EEPROM.write(0, 24);
digitalWrite(outPin, HIGH);
delay(150);
digitalWrite(outPin, LOW);
}
#if outputToSerial
Serial.begin(38400);
#endif
prevButton = getcap(1<<PINB0);
def = EEPROMReadInt(1);
}
// ------------------------------------------------------------------------------------------------------------------------ //
void loop()
{
lastValue = analogRead(inPin);
if (isOn)
{
switch (stage)
{
case 0: if (lastValue > (def+10)) { lastMillis = millis() + clapTime; stage = 1; delaySomeTime(debounceTime); } break;
case 1: if (lastValue > (def+10)) { stage = 5; lastMillis = millis() + waitBeforeRetry; } else if (millis() > lastMillis) { stage = 2; lastMillis = millis() + clapTime; } break;
case 2: if (lastValue > (def+10)) { lastMillis = millis() + clapIdle; stage = 3; delaySomeTime(debounceTime); } else if (millis() > lastMillis) { stage = 5; lastMillis = millis() + waitBeforeRetry; } break;
case 3: if (lastValue > (def+10)) { stage = 5; lastMillis = millis() + waitBeforeRetry; } else if (millis() > lastMillis) stage = 4; break;
case 4: if (lastValue < def) { isOn = !isOn; } stage = 0; break;
case 5: // This is the Wait Before Retrying stage //
if (lastValue > (def+10)) lastMillis = millis() + waitBeforeRetry; else if (millis() > lastMillis) stage = 0;
break;
}
}
#if outputToSerial
if (stage != prevStage)
{
Serial.println(stage, DEC);
prevStage = stage;
}
#endif
checkButton();
}
// ------------------------------------------------------------------------------------------------------------------------ //
void checkNewOn()
{
if (isOn != prevIsOn)
{
digitalWrite(outPin, isOn);
prevIsOn = isOn;
delay(delayAfterSwitch);
}
}
// ------------------------------------------------------------------------------------------------------------------------ //
void delaySomeTime(unsigned long ms)
{
uint16_t start = (uint16_t)micros();
while (ms > 0)
{
if (((uint16_t)micros() - start) >= 1000)
{
ms--;
start += 1000;
}
checkButton();
}
}
// ------------------------------------------------------------------------------------------------------------------------ //
void checkButton()
{
curButton = getcap(1<<touchPin);
if (curButton != prevButton)
{
prevButton = curButton;
if (wasButtonLow == 1 && curButton > 1)
{
isOn = !isOn;
wasButtonLow = 0;
stage = 0;
checkNewOn();
delay(500);
}
if (curButton <= 1) wasButtonLow = 1;
}
checkNewOn();
}
// ------------------------------------------------------------------------------------------------------------------------ //
char getcap(char pin)
{
char i = 0;
DDRB &= ~pin; // input
PORTB |= pin; // pullup on
for(i = 0; i < 2; i++)
{
if( (PINB & pin) ) break;
}
PORTB &= ~pin; // low level
DDRB |= pin; // discharge
return i;
}
Did you power it from the Arduino's 5v? Or was it powered by something higher? (Ebay description speaks of supplying "<=12v"... 5v seems a LOT "less than", but it would be so nice if the device was as simple as I am hoping... (^_^)
Can someone suggest a programmable clap project ? Similar to secret knock..so one can program it in a way the light with 2 claps, the TV 3 claps, and so ???