Introduction: Smart Appliance Switch

Easy to use device that turns on appliance when it is dark and movement is detected. I use it to turn on light when I go at night from one room to another.

We will define "device" as this Arduino-based project (Smart Appliance Switch - SAS) you are about to create and "appliance" as everything that you want to turn on/off (for example, a lamp).

SAS features 3 different modes:

  1. default mode - turns on after you power up device. Light threshold is hard coded, you may need to change it;
  2. permanent mode - appliance is permanently turned on;
  3. manual mode - you set light threshold by pressing a button and fine tuning it with potentiometer. Current level of light will be threshold.

It has 1 switch that turns device on/off, 2 buttons that change mode, 1 knob to fine-tune light threshold.

Word of caution: this device operates with high voltage which may be deadly for humans. Unless you are a certified electrician (I am not), test circuit and relay using 5 - 9 V electric current. Only after you have clear understanding of what you are doing and your small voltage test circuit works, you may carry on. Don't forget to insulate all connections.

Step 1: What You Need

In brackets I indicate parts that I have used in my device, if there was a name or a value.

  1. Arduino board (UNO, although on my photos you may see Leonardo)
  2. Arduino power adapter
  3. Relay (SONGLE SRD-05-VDC-SL-C)
  4. Potentiometer (250 kOm)
  5. 2 buttons
  6. 1 switch
  7. Plastic case (85x85x55 mm)
  8. Wires (22 AWG for all parts that are connected to Arduino. Wires that conduct high voltage must have bigger diameter and depend on appliance you are going to control. If you are not sure, use the same wires as in your home sockets).
  9. Plug that you are able to take apart and make two parts from it or two independent plugs.
  10. Resistors (3 pcs - 1 kOm, 1 pc - 1MOm)
  11. Passive infrared sensor PIR (HC-SR501)
  12. Photocell
  13. LED
  14. Protoshield (optional but very convenient)
  15. 4 and 2 - way stereo speaker terminal plate (optional)
  16. Screws (better plastic)

Step 2: Wiring Up

You may see how to connect everything together on diagram.

All the necessary information I got from Adafruit:

  1. Photocells
  2. Passive infrared sensor (PIR)

About relays you may read here

Some notes:

  1. LED turns on when permanent mode is on.
  2. Relay has jumper - put it in LOW position.
  3. I use 1 MOm resistor with photocell, because this way it's possible to distinguish better between "it's dark but I see where I am going" and "I can't see a thing". Read more about it on Adafruit via the link above.
  4. 250 kOm potentiometer gives quite wide range by which you may increase or decrease threshold during twilight.
  5. PIR has 2 adjustable knobs: delay time and sensitivity. I turned it all the way down counter clockwise.

At this stage I strongly suggest you assemble only parts which are connected to breadboard on the picture. Make another simple circuit with battery and LED and connect it to the relay.

Step 3: Coding

At first logic seemed straightforward: if it's dark and there is movement - turn on the appliance (lamp in my case), otherwise turn it off. Wait n seconds and check again.

But as I was testing and adding new modes to my device, none of above mentioned worked that simple.

You can't use if (dark == true && movement == true) then {turn on} else {turn off}; delay (n);

because you will get nasty feedback effect: dark? - yes, movement? - yes then turn it on; wait; dark? - no (because we have just turned on the lights), movement? - yes then turn it off; dark? - yes ... So with this logic you will get constant blinking.

That's why in procedure light_on() first we check if it is dark and then go into loop, switching on light and constantly checking if motion is present. We don't check these two conditions simultaneously.

As for command delay (n), which pauses execution for n milliseconds, we can't use it because during delay, if you press permanent or manual mode buttons, Arduino will not notice it, because it sleeps. That is why we use in the same loop, which checks for motion in light_on () procedure, function millis() and assign it's value to variable "now". Millis() returns number of milliseconds that passed since Arduino was turned on. This way we are able to get out of loop when now + delay_time > millis().

So, all conditions to get out of loop in light_on() procedure:

  • there is no movement;
  • delay_time has passed since last time movement was detected;
  • device mode was changed by pressing one of the buttons.

Other comments I included in code itself.

I made two classes: photocell and pir.

For button debouncing I use Bounce2 library that you can download from here. Download library and unpack its contents into ~/Documents/Arduino/libraries (on a Mac) or My Documents\Arduino\libraries (on a Windows machine).

<p>//#define __DEBUG__ // uncomment this define for debugging information in serial monitor<br>#include "Bounce2.h"
#include "photocell.h"
#include "pir.h"</p><p>//SETUP
const unsigned int PHOTOCELL_PIN = A0;
const unsigned int RELAY_PIN = 10;
const unsigned int PERMANENT_LED_PIN = 13;
const unsigned int PERMANENT_MODE_PIN = 12;
const unsigned int MANUAL_MODE_PIN = 11;
const unsigned int PIR_INPUT_PIN = 2;
const unsigned int BAUD_RATE = 9600;
const unsigned int LIGHT_DELAY = 5000;
const unsigned int DEBOUNCE_DELAY = 20;
const int DEFAULT_THRESHOLD = 130;
//SETUP</p><p>int threshold = 0;
Bounce permanent_mode_button, manual_mode_button;
boolean button_state = false;
photocell p_cell(PHOTOCELL_PIN);
PIR ir_sensor(PIR_INPUT_PIN);
enum modes {default_, permanent, manual, threshold_changed}; // we also need to be able to determine, when during manual mode, manual mode button was pressed again
int prev_mode = default_;
int mode = default_;</p><p>//check state of button
boolean button_pressed(Bounce& button, boolean& state) {
  if (button.update() && button.read() == 1) {
    state = !state;
  }
  return state;
}</p><p>//determine current mode
int check_mode() {
  if (button_pressed(permanent_mode_button, button_state))
    mode = permanent; // permanent mode button pressed</p><p>  else if (manual_mode_button.update() && manual_mode_button.read() == 1) {
    threshold = p_cell.value();</p><p>#ifdef __DEBUG__
    Serial.println("threshold: " + String(threshold));
#endif</p><p>    mode = threshold_changed; // threshold was changed
  }</p><p>  else if (threshold > 0) {
    mode = manual; // manual mode
  }</p><p>  else
    mode = default_; // default mode</p><p>  return mode;
}</p><p>//check, wheter mode was changed
bool mode_changed() {
  if (prev_mode != check_mode()) {
    prev_mode = check_mode();</p><p>#ifdef __DEBUG__
    Serial.println("true, previous mode: " + String(prev_mode));
#endif
    return true;
  }
  else {
#ifdef __DEBUG__
    Serial.println("false, previous mode: " + String(prev_mode));
#endif
    return false;
  }
}</p><p>//turn on the light
void light_on (int threshold, const unsigned int& pin, const unsigned int& delay_time) {
  digitalWrite (PERMANENT_LED_PIN, 0);
  if (p_cell.value() <= threshold) {</p><p>    unsigned long now = 0;
    boolean mode_change = mode_changed();</p><p>    for (; ((ir_sensor.motion_detected() && !mode_change) && (now = millis())) || ((millis() < (now + delay_time)) && !mode_change); mode_change = mode_changed()) {
      digitalWrite(pin, 1);			// we get out of this loop when there is no movement or device mode was changed or delay time has passed
#ifdef __DEBUG__
      Serial.println("inside loop, motion: " + String(ir_sensor.motion_detected()));
      Serial.println("now: " + String(now));
#endif
    }
    digitalWrite(pin, 0);
  }
  digitalWrite(pin, 0);
}</p><p>void setup() {
#ifdef __DEBUG__
  Serial.begin(BAUD_RATE);
#endif
  permanent_mode_button.attach(PERMANENT_MODE_PIN);
  permanent_mode_button.interval(DEBOUNCE_DELAY);
  pinMode(PERMANENT_MODE_PIN, INPUT);
  manual_mode_button.attach(MANUAL_MODE_PIN);
  manual_mode_button.interval(DEBOUNCE_DELAY);
  pinMode(MANUAL_MODE_PIN, INPUT);
  pinMode(RELAY_PIN, OUTPUT);
  pinMode(PERMANENT_LED_PIN, OUTPUT);
}</p><p>void loop() {
  if (mode == permanent) {
    digitalWrite(RELAY_PIN, 1);
    digitalWrite(PERMANENT_LED_PIN, 1);
#ifdef __DEBUG__
    Serial.println("permanent mode on");
#endif
  }</p><p>  else if (mode == manual) {
#ifdef __DEBUG__
    Serial.println("manual mode on");
#endif
    light_on (threshold, RELAY_PIN, LIGHT_DELAY);
  }</p><p>  else if (mode == default_) {
#ifdef __DEBUG__
    //Serial.println("default mode on");
    Serial.println("value: " + String(p_cell.value()));
#endif
    light_on (DEFAULT_THRESHOLD, RELAY_PIN, LIGHT_DELAY);
  }
  check_mode();
}</p>

photocell.h

<p>#include "arduino.h"</p><p>class photocell {
  private:
    unsigned int _pin;
    int buffer_size;</p><p>  public:
    photocell(const unsigned int pin) {
      _pin = pin;
      buffer_size = 10;
    }</p><p>    int value () {
      float sum = 0;
      for (int i = 1; i <= buffer_size; i++) {  //we use buffer to get nice average value
        sum += analogRead(_pin);
      }
      
      return round(sum / buffer_size);
    }
};</p>

pir.h

<p>#ifndef __PIR__<br>#define __PIR__
#include "arduino.h"</p><p>class PIR {
    int _input_pin;</p><p>  public:
    PIR(const unsigned int input_pin) {
      _input_pin = input_pin;
      pinMode(_input_pin, INPUT);
    }</p><p>    bool motion_detected() {
      return digitalRead(_input_pin) == HIGH;
    }
};
#endif</p>

That's all the code you need. Now you may upload it to your Arduino and test, whether everything works as it should, before dealing with high voltage and assembling case for your device.

Also I attached files with codes. On my machine everything compiles without problems.

Step 4: Assembling Case

Everything is pretty straightforward. If necessary, make adjustments for your convenience, for example, if you are left-handed.

It's good idea to use protoshield, where you can nicely solder resistors and wires.

On the bottom of the device I use 4 and 2-way stereo speaker terminal plate, with it you can easily attach and remove photocell and PIR.

On the sides I made some holes for the air to flow, because power adapter heats when in use.

Don't forget that you need wire with bigger diameter to connect all parts through which high voltage will flow. If you are not sure, take wires that are normally used to connect your home sockets.

Don't forget to insulate all contacts inside the device. Also I strongly recommend to solder everything.

There are some metal parts that you may still touch when the case is closed (for example, screws with which I attached Arduino and relay to the lid). It is really a good idea to use plastic screws. In my case when everything was finished, I insulated them too.

Finally, there must be no metal parts that you can touch with your body. This device is not grounded, so better not to risk.

Step 5: Afterword

This is my first device which I use every day and I got really a lot of fun assembling it. Please, do not spoil my fun by hurting yourself. If you don't know something or aren't sure, please, stop at once and ask somebody for help. You still can test and experiment using only Arduino and breadboard.

Although my device works well for quite some time now and behaves exactly as I intended, I am no electrician nor programmer. I would be really grateful, if you could share with me some of your thoughts on my device. Thank you.

Make It Glow! Contest

Participated in the
Make It Glow! Contest