Dimable Security light with PIR/Temp/Humidity and Voltage sense.



  • I've mashed together a few sketches to create a kind of security light. I've not put any logic for when the light should come on in the sketch and instead let my Domoticz controller decide when and what intensity the light should come on. In this way i have it set to a very low level of around %15 from 30minutes after sunset till around 11 but if motion is detected i bump it up to %75 for 1 minute. I've done it this way as i can control it remotely and also modify the delay/intensity etc without having to open up the case. I'm also sensing the battery voltage of a 12v battery. I'm using a 1M and 470k resistor although i had to play with the resistor values, the 470k one in particular in the code to get it to be accurate. I am sending the temp every 2 mins and the battery voltage every 5 mins using a timer and the main loop monitors the motion sensor and sends that it's been triggered otherwise it just loops around again. I had looked at interrupts and sleeping inbetwen but then i couldn't see a way to control it remotely as Domoticz wouldn't know when it was awake. It seems to be working ok for me so hope it helps some people.

    I housed it all in a small project box which is secured to the underside of my garage and the light then connects directly to the circuit. If anyone is interested in a fritzing diagram then please let me know.

    0_1462452224492_IMG_4579.JPG
    0_1462452234108_IMG_4576.JPG
    0_1462452213582_IMG_4580.JPG
    0_1462452143142_IMG_4582.JPG

    /**
     * The MySensors Arduino library handles the wireless radio link and protocol
     * between your home built sensors/actuators and HA controller of choice.
     * The sensors forms a self healing radio network with optional repeaters. Each
     * repeater and gateway builds a routing tables in EEPROM which keeps track of the
     * network topology allowing messages to be routed to nodes.
     *
     * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
     * Copyright (C) 2013-2015 Sensnology AB
     * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
     *
     * Documentation: http://www.mysensors.org
     * Support Forum: http://forum.mysensors.org
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * version 2 as published by the Free Software Foundation.
     *
     *******************************
    
     *
     * DESCRIPTION
     * This sketch provides a Dimmable LED Light along with Temp/Humidity/voltage sensing as well as Motion sensing.
     */
    
    #define SN "SecurityLED"
    #define SV "1.2"
    #define CHILD_ID_HUM 0
    #define CHILD_ID_TEMP 1
    #define CHILD_ID_VOLTAGE 2
    #define CHILD_ID_PIR 3
    
    #define HUMIDITY_SENSOR_DIGITAL_PIN 6
    #define DIGITAL_INPUT_SENSOR 3   // The digital input you attached your motion sensor.
    #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
    
    #include <MySensor.h> 
    #include <SPI.h>
    #include <DHT.h> 
    #include <SimpleTimer.h>
    
    // the timer object
    SimpleTimer timer;
    
    #define LED_PIN 5      // Arduino pin attached to MOSFET Gate pin
    //#define 
    int FADE_DELAY=50;  // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
    
    MySensor gw;
    
    DHT dht;
    float lastTemp;
    boolean metric = true;
    float lastHum;
    float lastVolt;
    int oldBatteryPcnt = 0;
    int analogInput = A0;
    float vout = 0.0;
    float vin = 0.0;
    int value = 0;
    float R1 = 1000000.0;  
    float R2 = 454500.0;
    boolean sensordelay;
    
    static int currentLevel = 0;  // Current dim level...
    MyMessage dimmerMsg(0, V_DIMMER);
    MyMessage lightMsg(0, V_LIGHT);
    MyMessage msgHum(CHILD_ID_HUM, V_HUM);
    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    MyMessage msgVolt(CHILD_ID_VOLTAGE, V_VOLTAGE); 
    MyMessage msg(CHILD_ID_PIR, V_TRIPPED);
    
    /***
     * Dimmable LED initialization method
     */
    void setup()  
    { 
      Serial.println( SN ); 
      gw.begin( incomingMessage );
      dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
      
      pinMode(DIGITAL_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input
      // Register the LED Dimmable Light with the gateway
      gw.present( 0, S_DIMMER );
     
      gw.sendSketchInfo(SN, SV);
      // Pull the gateway's current dim level - restore light level upon sendor node power-up
      //gw.request( 0, V_DIMMER );
      gw.present(CHILD_ID_HUM, S_HUM);
      gw.present(CHILD_ID_TEMP, S_TEMP);
      gw.present(CHILD_ID_VOLTAGE, V_VOLTAGE);
      gw.present(CHILD_ID_PIR, S_MOTION);
    
      pinMode(analogInput, INPUT);
      timer.setInterval(120000,readTempHum);  // Send Temp every 2 mins
      timer.setInterval(300000,measureBattery);  // Send voltage every 5 mins
      fadeToLevel( 0 );
      readTempHum();
      measureBattery();
      sensordelay=0;
    }
    
    void ResetTripped(){
          sensordelay=0;
          FADE_DELAY=50;
    }
    
    /***
     *  Main processing loop 
     */
    void loop() 
    {
      gw.process();  
    
      // Read digital motion value
      boolean tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH; 
      if (tripped ==1 && sensordelay==0) {
        FADE_DELAY=0;
        Serial.print("Security Sensor state : ");
        Serial.println(tripped);
        gw.send(msg.set(tripped?"1":"0"));  // Send tripped value to gw
        tripped=0;
        gw.send(msg.set(tripped?"1":"0"));  // Send tripped value to gw
        sensordelay=1;
        timer.setTimeout(10000,ResetTripped);
      }
      timer.run();
    }
    
    
    
    void incomingMessage(const MyMessage &message) {
      if (message.type == V_LIGHT || message.type == V_DIMMER) {
        
        //  Retrieve the power or dim level from the incoming request message
        int requestedLevel = atoi( message.data );
        
        // Adjust incoming level if this is a V_LIGHT variable update [0 == off, 1 == on]
        requestedLevel *= ( message.type == V_LIGHT ? 100 : 1 );
        
        // Clip incoming level to valid range of 0 to 100
        requestedLevel = requestedLevel > 100 ? 100 : requestedLevel;
        requestedLevel = requestedLevel < 0   ? 0   : requestedLevel;
        
        Serial.print( "Changing level to " );
        Serial.print( requestedLevel );
        Serial.print( ", from " ); 
        Serial.println( currentLevel );
    
        fadeToLevel( requestedLevel );
        
        // Inform the gateway of the current DimmableLED's SwitchPower1 and LoadLevelStatus value...
        gw.send(lightMsg.set(currentLevel > 0 ? 1 : 0));
    
        // hek comment: Is this really nessesary?
        gw.send( dimmerMsg.set(currentLevel) );
        
        }
    }
    
    /***
     *  This method provides a graceful fade up/down effect
     */
    void fadeToLevel( int toLevel ) {
    
      int delta = ( toLevel - currentLevel ) < 0 ? -1 : 1;
      
      while ( currentLevel != toLevel ) {
        currentLevel += delta;
        analogWrite( LED_PIN, (int)(currentLevel / 100. * 255) );
        delay( FADE_DELAY );
      }
    }
    
    void readTempHum() {
     delay(dht.getMinimumSamplingPeriod());
      float temperature = dht.getTemperature();
      if (isnan(temperature)) {
          Serial.println("Failed reading temperature from DHT");
      } else if (temperature != lastTemp) {
        lastTemp = temperature;
        if (!metric) {
          temperature = dht.toFahrenheit(temperature);
        }
        gw.send(msgTemp.set(temperature, 1));
        Serial.print("T: ");
        Serial.println(temperature);
      }
      
      float humidity = dht.getHumidity();
      if (isnan(humidity)) {
          Serial.println("Failed reading humidity from DHT");
      } else if (humidity != lastHum) {
          lastHum = humidity;
          gw.send(msgHum.set(humidity, 1));
          Serial.print("H: ");
          Serial.println(humidity);
      } 
    }
    
    void measureBattery() {
       // read the value at analog input
       value = analogRead(analogInput);
       vout = (value * 5.0) / 1024.0; // see text
       vin = vout / (R2/(R1+R2)); 
       int batteryPcnt = value / 10;
          
       #ifdef DEBUG
       Serial.print("Battery Voltage: ");
       Serial.print(vin);
       Serial.println(" V");
    
       Serial.print("Battery percent: ");
       Serial.print(batteryPcnt);
       Serial.println(" %");
       #endif
       
       if (oldBatteryPcnt != batteryPcnt) {
         // Power up radio after sleep
         gw.sendBatteryLevel(batteryPcnt);
         gw.send(msgVolt.set(vin, 1));
         oldBatteryPcnt = batteryPcnt;
       }
    }
    


  • Very nice solution.
    One question; does the using the timer function for temp, hum etc block the processing of incoming messages from domoticz?
    i mean that during the temp , humidity or batpercentage calculation no icoming messages from domoticz are being processed?



  • @koen01 when the timer is running the main loop is still running so it's able to respond to dimming requests. I have implemented the PIR alerting poorly though so need some kind of delay as when the PIR is activated it spams domoticz for half a second so I'd rather is delayed so it's not tripping it so much. I'm traveling at the moment but plan to change it when I'm back at the end of the week so will publish the changed codes.

    Col



  • @koen01 I spent some time after work and updated the code so i've updated the first post with the code in. It now works for the PIR part.



  • Updated the sketch again and have updated the first post to reflect the changes. I realised after i'd re-assembled everything that by putting the delay in to stop the PIR sensor spamming the controller that i was actually stopping the controller turning on the light for a full 10 seconds which resulted in a rather rubbish security light lol I've now modified the code so that a second variable is used in conjuction with the PIR status itself, a timer is then used for 10 seconds so that it will only send one update that it's been tripped. After 10 seconds it's able to be tripped again. I'm still using domoticz to control the logic of turning the light on but having a few issues so i'll post in the domoticz forum for help.



  • good looking project love to see the diagram. just ordered some 12v outdoor lights interested in seeing the quality of light, could be a cheap/safe way to get some security lighting



  • @Ashley-Savage 0_1464007588143_security light.fzz

    Not the easiest thing to fritz as it looks really messy.

    Also i've found the design done by a guy called quindor which uses an esp8266 is working much more reliably for me with regards to dimming etc. He's got a great write up on using it with domoticz. I have two on my garage and also in my kitchen as accent lighting. Blog

    0_1464174477894_security light_bb.jpg



  • hey chickey, thanks for the time I will give it a go



  • @Ashley-Savage No worries, btw i made a couple of changes last time which i never mentioned. I've made it so that if the PIR is tripped it sets the fade to 0 so that if Domoticz then sets the light to on it won't gradually fade up and will come on straight away which is more the behaviour you'd want from a security light. After 10 seconds however it resets the fade back to 50 which gives a 5 second delay which means that when domoticz turns the light off after 1 minute it'll nicely fade down. It also means if your manually controlling the light it'll fade on and off nice and smoothly.

    I've also just made a change today as i was lurking on the forums and read a post about pwm being flickery intermittently which i've experienced personally and it was down to TX/RX and ERR led's being set to use PIN 5 which conflicts with what were using the pin for sometimes causing flickering. It now seems to work nice and smooth every time.

    I've only just a few remaining issues in that if i turn the light on rather than asking for a percentage it goes to 100% which i can see the logic to but what i'd ideally like to do is have it turn on at sunset at say %15 then turn to %100 if it's tripped then go back to %15 but it returns it to %100 because of the above%100 at one command i think, might have to put it in the blocky script but it'll be difficult as i also like it to turn off at 11pm and only come on if tripped so just used as a security light. Last issue is it refuses to respond to on/off command sometimes but you can bring it back to life by using the dimmer slider to gradually power it off then it'll respond to on/off commands again which is weird.

    I think i might try the default sketch again for just the dimmer to see if i get the above behavior.



  • @Ashley-Savage Had a big rewrite this evening and all seems good now. If you set it to a level then switch it off the level is remembered so if you issue an on command it goes to the previous level. It seems a little bit more stable as well but i'll not know fully until i put it outside for use in the wild as it where. I'm thinking i might change the DHT11 for a Dallas sensor though as i received a few waterproof ones this week as they should be more accurate and can support negative temps which living in the UK i'll need come the autumn.

    /**
     * The MySensors Arduino library handles the wireless radio link and protocol
     * between your home built sensors/actuators and HA controller of choice.
     * The sensors forms a self healing radio network with optional repeaters. Each
     * repeater and gateway builds a routing tables in EEPROM which keeps track of the
     * network topology allowing messages to be routed to nodes.
     *
     * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
     * Copyright (C) 2013-2015 Sensnology AB
     * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
     *
     * Documentation: http://www.mysensors.org
     * Support Forum: http://forum.mysensors.org
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * version 2 as published by the Free Software Foundation.
     *
     *******************************
     *
     * DESCRIPTION
     * This sketch provides a Dimmable LED Light along with Temp/Humidity/voltage sensing as well as Motion sensing.
     */
     
    #include <SPI.h>
    #include <MySensor.h>  
    #include <DHT.h> 
    #include <SimpleTimer.h>
    
    #define SN "SecurityLED"
    #define SV "1.3"
    #define CHILD_ID_LIGHT 1
    #define CHILD_ID_HUM 0
    #define CHILD_ID_TEMP 1
    #define CHILD_ID_VOLTAGE 2
    #define CHILD_ID_PIR 3
    
    #define EPROM_LIGHT_STATE 1
    #define EPROM_DIMMER_LEVEL 2
    
    #define LIGHT_OFF 0
    #define LIGHT_ON 1
    
    #define LED_PIN 5                         // Arduino pin attached to MOSFET Gate pin
    #define HUMIDITY_SENSOR_DIGITAL_PIN 6
    #define DIGITAL_INPUT_SENSOR 3            // The digital input you attached your motion sensor.
    #define INTERRUPT DIGITAL_INPUT_SENSOR-2  // Usually the interrupt = pin -2 (on uno/nano anyway)
    
    #define MY_DEFAULT_TX_LED_PIN A2
    #define MY_DEFAULT_RX_LED_PIN A3
    #define MY_DEFAULT_ERR_LED_PIN A4
     
    int FADE_DELAY=50;                        // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
    
    // the timer object
    SimpleTimer timer;
    
    int LastLightState=LIGHT_OFF;
    int LastDimValue=100;
    
    DHT dht;
    float lastTemp;
    boolean metric = true;
    float lastHum;
    float lastVolt;
    int oldBatteryPcnt = 0;
    int analogInput = A0;
    float vout = 0.0;
    float vin = 0.0;
    int value = 0;
    float R1 = 1000000.0;  
    float R2 = 454500.0;
    boolean sensordelay;
    
    MySensor gw;
    MyMessage lightMsg(CHILD_ID_LIGHT, V_LIGHT);
    MyMessage dimmerMsg(CHILD_ID_LIGHT, V_DIMMER);
    MyMessage msgHum(CHILD_ID_HUM, V_HUM);
    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    MyMessage msgVolt(CHILD_ID_VOLTAGE, V_VOLTAGE); 
    MyMessage msg(CHILD_ID_PIR, V_TRIPPED);
    
    void setup()  
    { 
      gw.begin(incomingMessage, AUTO, false);
    
      // Send the Sketch Version Information to the Gateway
      gw.sendSketchInfo(SN, SV);
      
      dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
      
      pinMode(DIGITAL_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input
      pinMode(analogInput, INPUT);               // sets the battery voltage reading input
        
      gw.present(CHILD_ID_LIGHT, S_DIMMER );
      gw.present(CHILD_ID_HUM, S_HUM);
      gw.present(CHILD_ID_TEMP, S_TEMP);
      gw.present(CHILD_ID_VOLTAGE, V_VOLTAGE);
      gw.present(CHILD_ID_PIR, S_MOTION);
    
      sensordelay=0;
    
      timer.setInterval(120000,readTempHum);  // Send Temp every 2 mins
      timer.setInterval(300000,measureBattery);  // Send voltage every 5 mins
      
      //Retreive our last light state from the eprom
      int LightState=gw.loadState(EPROM_LIGHT_STATE); 
      if (LightState<=1) {
        LastLightState=LightState;
        int DimValue=gw.loadState(EPROM_DIMMER_LEVEL); 
        if ((DimValue>0)&&(DimValue<=100)) {
          //There should be no Dim value of 0, this would mean LIGHT_OFF
          LastDimValue=DimValue;
        }
      }
    
      //Here you actualy switch on/off the light with the last known dim level
      SetCurrentState2Hardware();
      
      Serial.println( "Node ready to receive messages..." );  
    }
    
    void loop()      
    {
      // Process incoming messages (like config and light state from controller)
      gw.process();
    
      // Read digital motion value
      boolean tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH; 
      if (tripped ==1 && sensordelay==0) {
        FADE_DELAY=0;
        Serial.print("Security Sensor state : ");
        Serial.println(tripped);
        gw.send(msg.set(tripped?"1":"0"));  // Send tripped value to gw
        tripped=0;
        gw.send(msg.set(tripped?"1":"0"));  // Send tripped value to gw
        sensordelay=1;
        timer.setTimeout(10000,ResetTripped);
      }
      timer.run();
    }
    
    void incomingMessage(const MyMessage &message)
    {
      if (message.type == V_LIGHT) {
        Serial.println( "V_LIGHT command received..." );
        
        int lstate= atoi( message.data );
        if ((lstate<0)||(lstate>1)) {
          Serial.println( "V_LIGHT data invalid (should be 0/1)" );
          return;
        }
        LastLightState=lstate;
        gw.saveState(EPROM_LIGHT_STATE, LastLightState);
        
        if ((LastLightState==LIGHT_ON)&&(LastDimValue==0)) {
           //In the case that the Light State = On, but the dimmer value is zero,
           //then something (probably the controller) did something wrong,
           //for the Dim value to 100%
          LastDimValue=100;
          gw.saveState(EPROM_DIMMER_LEVEL, LastDimValue);
        }
        
        //When receiving a V_LIGHT command we switch the light between OFF and the last received dimmer value
        //This means if you previously set the lights dimmer value to 50%, and turn the light ON
        //it will do so at 50%
      }
      else if (message.type == V_DIMMER) {
        Serial.println( "V_DIMMER command received..." );  
        int dimvalue= atoi( message.data );
        if ((dimvalue<0)||(dimvalue>100)) {
          Serial.println( "V_DIMMER data invalid (should be 0..100)" );
          return;
        }
        if (dimvalue==0) {
          LastLightState=LIGHT_OFF;
          LastDimValue=0;
        }
        else {
          LastLightState=LIGHT_ON;
          LastDimValue=dimvalue;
          gw.saveState(EPROM_DIMMER_LEVEL, LastDimValue);
        }
      }
      else {
        Serial.println( "Invalid command received..." );  
        return;
      }
         Serial.println( LastDimValue );
      //Here you set the actual light state/level
      SetCurrentState2Hardware();
    }
    
    static int currentLevel = 0;  // Current dim level...
    
    void fadeToLevel( int toLevel ) {
    
      int delta = ( toLevel - currentLevel ) < 0 ? -1 : 1;
      
      while ( currentLevel != toLevel ) {
        currentLevel += delta;
        analogWrite( LED_PIN, (int)(currentLevel / 100. * 255) );
        delay( FADE_DELAY );
      }
    }
    
    void SetCurrentState2Hardware()
    {
      if (LastLightState==LIGHT_OFF) {
         Serial.println( "Light state: OFF" );
         fadeToLevel(0);
      }
      else {
         Serial.print( "Light state: ON, Level: " );
         Serial.println( LastDimValue );
         fadeToLevel( LastDimValue );
      }
    
      //Send current state to the controller
      SendCurrentState2Controller();
    }
    
    void SendCurrentState2Controller()
    {
      if ((LastLightState==LIGHT_OFF)||(LastDimValue==0)) {
        gw.send(dimmerMsg.set(0));
      }
      else {
        gw.send(dimmerMsg.set(LastDimValue));
      }
    }
    
    void ResetTripped(){
          sensordelay=0;
          FADE_DELAY=50;
    }
    
    void readTempHum() {
     delay(dht.getMinimumSamplingPeriod());
      float temperature = dht.getTemperature();
      if (isnan(temperature)) {
          Serial.println("Failed reading temperature from DHT");
      } else if (temperature != lastTemp) {
        lastTemp = temperature;
        if (!metric) {
          temperature = dht.toFahrenheit(temperature);
        }
        gw.send(msgTemp.set(temperature, 1));
        Serial.print("T: ");
        Serial.println(temperature);
      }
      
      float humidity = dht.getHumidity();
      if (isnan(humidity)) {
          Serial.println("Failed reading humidity from DHT");
      } else if (humidity != lastHum) {
          lastHum = humidity;
          gw.send(msgHum.set(humidity, 1));
          Serial.print("H: ");
          Serial.println(humidity);
      } 
    }
    
    void measureBattery() {
       // read the value at analog input
       value = analogRead(analogInput);
       vout = (value * 5.0) / 1024.0; // see text
       vin = vout / (R2/(R1+R2)); 
       int batteryPcnt = value / 10;
          
       #ifdef DEBUG
       Serial.print("Battery Voltage: ");
       Serial.print(vin);
       Serial.println(" V");
    
       Serial.print("Battery percent: ");
       Serial.print(batteryPcnt);
       Serial.println(" %");
       #endif
       
       if (oldBatteryPcnt != batteryPcnt) {
         // Power up radio after sleep
         gw.sendBatteryLevel(batteryPcnt);
         gw.send(msgVolt.set(vin, 1));
         oldBatteryPcnt = batteryPcnt;
       }
    }
    

Log in to reply
 

Suggested Topics

53
Online

11.4k
Users

11.1k
Topics

112.7k
Posts