Multiple Relays + Motion sketch, fully customizable, optional timer, manual override



  • Hi!

    I recently modified the relay example sketch so that it fits my needs; maybe some of you can make use of my little work too.

    Here's what you can do / what the features are:

    • Easily add as much Relays / FETs as you want (or as much as the arduino allows) by entering the according pin numbers in the RELAYS[ ]-Array
    • You can trigger each relay from your controller
    • You can add a motion sensor and specify which (MOTION_ACTIVATED_RELAYS[ ]) of your relays should be triggered through motion and for how long (ON_TIMES[ ])
    • The state of the motion sensor will be reported to your controller
    • When a relay is changed through motion, the change will also be reported to your controller
    • You can send a manual override from your controller, so that the motion will still be reported, but none of the relays will be triggered through motion
    /**
       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.
    
     *******************************
    
       REVISION HISTORY
       Version 1.0 - Henrik Ekblad
       Version 1.1 - HenryWhite
    
       DESCRIPTION
       Example sketch showing how to control physical relays.
       This example will remember relay state after power failure.
       Optional attachment of motion sensor to control the relays is possible.
       Notes:
          -- The Child-IDs of the attached relays range from 1 up to (1-(NUMBER_OF_RELAYS))
          -- Make sure to adjust the potentiometer for triggertime on your motion sensor as leftmost as possible,
             because the countdown will not start until the motion sensor reports back a "0" (no movement)
    
    */
    
    //----------------------- Library Configuration ---------------------
    //#define MY_DEBUG                          // uncomment to enable debug prints to serial monitor
    //#define MY_REPEATER_FEATURE               // uncomment to enable repeater functionality for this node
    //#define MY_NODE_ID 20                     // uncomment to define static node ID
    
    // Enable and uncomment attached radio type
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    //#define MY_TRANSPORT_WAIT_READY_MS 1      // uncomment this to enter the loop() and setup()-function even when the node cannot be registered to gw
    
    //----------------------- Relay and Motion Sensor Configuration -----------------------
    #define MOTION                                                    // un-comment to enable motion sensing
    #define NUMBER_OF_RELAYS  2                                       // Total number of attached relays. Must be equal to total number of elements in array below!
    const int RELAYS[]                  =     {3,  5};                // digital pins of attached relays
    const int MOTION_ACTIVATED_RELAYS[] =     {1,  0};                // 1 to trigger the relay through motion, 0 to not trigger. Array length must correspond to RELAYS[] array.
    const long ON_TIMES[]               =     {300, 0};               // Specify for each element in MOTION_ACTIVATED_RELAYS, how long the specified relay should be active in seconds.
    #define RELAY_ON          1                                       // GPIO value to write to turn on attached relay
    #define RELAY_OFF         0                                       // GPIO value to write to turn off attached relay
    #define MOTION_PIN        2                                       // The digital input pin of the motion sensor
    #define MOTION_CHILD_ID   0                                       // Set the child id of the motion sensor
    bool ack = 1;                                                     // set this to 1 if you want destination node to send ack back to this node
    
    //----------------------- DO NOT CHANGE -----------------------------
    #include <MySensors.h>
    MyMessage motion_msg(MOTION_CHILD_ID, V_TRIPPED);   // Initialize motion message
    unsigned long trigger_millis[NUMBER_OF_RELAYS];     // Used for the timer
    bool lastTripped = 0;                               // Used to store last motion sensor value
    bool manual_override = 0;                           // if this gets set to 1 (e.g. by a switch or a command from the gateway), motion triggering of relays is deactivated
    MyMessage relay_msg;                                // Initialize relay message
    
    void before()
    {
      int i;
      for (int sensor = 1, i = 0; sensor <= NUMBER_OF_RELAYS; sensor++, i++) {
        // set relay pins to output mode
        pinMode(RELAYS[i], OUTPUT);
        // Restore relay to last known state (using eeprom storage)
        digitalWrite(RELAYS[i], loadState(sensor) ? RELAY_ON : RELAY_OFF);
      }
      // set motion pin to output mode, if MOTION is defined
    #ifdef MOTION
      pinMode(MOTION_PIN, INPUT);
    #endif
    }
    
    void setup()
    {
    #ifdef MOTION
      // give the motion sensor some time to settle
      Serial.println("Starting up. Please wait 8 seconds...");
      delay(8000);
    #endif
    }
    
    void presentation()
    {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("Relay/Motion", "1.0");
    
      // Register all sensors to gw (they will be created as child devices)
      for (int sensor = 1; sensor <= NUMBER_OF_RELAYS; sensor++) {
        present(sensor, S_BINARY, "Relay", ack);
      }
    #ifdef MOTION
      present(MOTION_CHILD_ID, S_MOTION, "Motion Sensor", ack);
    #endif
    
    }
    
    void loop()
    {
    #ifdef MOTION
      if (!manual_override) {
        // Read digital motion value
        bool tripped = digitalRead(MOTION_PIN) == HIGH;
    
        if (lastTripped != tripped) {
          Serial.print("New Motion State: ");
          Serial.println(tripped);
          // Send tripped value to gw
          send(motion_msg.set(tripped ? "1" : "0"));
          lastTripped = tripped;
    
          // Change relay states, send new state to gw and store state in eeprom
          if (tripped == 1) {
            for (int i = 0; i < NUMBER_OF_RELAYS; i++) {
              if (MOTION_ACTIVATED_RELAYS[i] == 1) {
                digitalWrite(RELAYS[i], RELAY_ON);
                relay_msg_constructor(i + 1, V_STATUS);
                send(relay_msg.set(RELAY_ON));
                trigger_millis[i] = millis();
                Serial.print("Relay ");
                Serial.print(RELAYS[i]);
                Serial.println(" turned on");
                saveState(i, 1);
              }
            }
          }
        }
    
        for (int i = 0; i < NUMBER_OF_RELAYS; i++) {
          if (tripped == 1 and MOTION_ACTIVATED_RELAYS[i] == 1 and trigger_millis[i] != 0) {
            trigger_millis[i] = millis();
          }
          if ((trigger_millis[i] + ON_TIMES[i] * 1000 < millis()) and MOTION_ACTIVATED_RELAYS[i] == 1 and trigger_millis[i] != 0) {
            digitalWrite(RELAYS[i], RELAY_OFF);
            relay_msg_constructor(i + 1, V_STATUS);
            send(relay_msg.set(RELAY_OFF));
            Serial.print("Relay ");
            Serial.print(RELAYS[i]);
            Serial.println(" turned off");
            saveState(i, 0);
            trigger_millis[i] = 0;
          }
        }
      }
      else {
        bool tripped = digitalRead(MOTION_PIN) == HIGH;
        if (lastTripped != tripped) {
          Serial.print("New Motion State: ");
          Serial.println(tripped);
          // Send tripped value to gw
          send(motion_msg.set(tripped ? "1" : "0"));
          lastTripped = tripped;
        }
        for (int i = 0; i < NUMBER_OF_RELAYS; i++) {
          if (MOTION_ACTIVATED_RELAYS[i] == 1 and trigger_millis[i] != 0) {
            trigger_millis[i] = 0;                            // reset running timers
          }
        }
      }
    #endif
    }
    
    void receive(const MyMessage &message)
    {
      // Handle incoming relay commands
      if (message.type == V_STATUS) {
        // Change relay state
        if (RELAYS[message.sensor - 1]) {
          digitalWrite(RELAYS[message.sensor - 1], message.getBool() ? RELAY_ON : RELAY_OFF);
    
          // Store state in eeprom
          saveState(message.sensor - 1, message.getBool());
          // Write some debug info
          Serial.print("Incoming change for sensor:");
          Serial.print(message.sensor);
          Serial.print(", New status: ");
          Serial.println(message.getBool());
        }
      }
    
      // Handle incoming manual override/bypass of motion sensor
      if (message.type == V_ARMED and message.sensor == 0) {
        manual_override = message.getBool();
        Serial.print("Manual Override: ");
        Serial.println(manual_override);
      }
    }
    
    void relay_msg_constructor(int sensor, uint8_t type)
    {
      relay_msg.setSensor(sensor);
      relay_msg.setType(type);
    }
    

    Additional Notes:

    • The Child-IDs of the attached relays range from 1 up to (1-(NUMBER_OF_RELAYS))
    • The default Child-ID for the motion sensor is 0
    • Make sure to adjust the potentiometer for triggertime on your motion sensor as leftmost as possible, because the countdown-timer will not start until the motion sensor reports back a "0" (no movement)


  • awesome work...this sketch is brilliant....

    what happens if you retrigger motion before timer runs out?
    will it always handle a restart of the timer correctly?



  • @markjgabb said in Multiple Relays + Motion sketch, fully customizable, optional timer, manual override:

    awesome work...this sketch is brilliant....

    what happens if you retrigger motion before timer runs out?
    will it always handle a restart of the timer correctly?

    Yes, the timer will be reset.



  • confirmed, im now running this is my office, works perfectly every time

    i have it set to a motion sensor that covers most of the room...
    one relay controls the lights...the other controls my soldering iron perfect work



  • How to send the relay to the gateway every 30 seconds?



  • @AndreD said in Multiple Relays + Motion sketch, fully customizable, optional timer, manual override:

    How to send the relay to the gateway every 30 seconds?

    Why would you need that?



  • Suppose.
    We have a server + gateway + relay module 1 + relay module 2 ..... all relays are on.
    There is a power failure at the relay module ..... the relays turned off.
    The server status is old, not correct.
    The recording of the status of the relay in memory is not acceptable.



  • person
    Next problem....
    Anybody know how edit sketch for relays to add itrms to send repeate update status to gateway? I want eliminate problem when node start and change status , but gateway is disabled. And after gateway will be enable show in my Domoticz old status/last memory before disable gateway. But after this node has changed status and this status is not show in Domoticz after run. I want to work it as Z-Wave. Z-wave has always actual status. Is any chance to also MySensors Gateway and not has always actual status ?


  • Mod

    @AndreD actually the relay state is restored to last state as the node is rebooted. I don't see where is the problem



  • то gohan
    Example.

    1. The server turned on the relay at 11 hours 00 minutes.
      2.In 11-02 minutes. Alarm with relay module power.
      3.In 11-05 minutes the server sends a command to turn off the relay.
    2. in 11-30 minutes. The power to the relay module resumes. (Relay Enabled)
    3. Server status is off (Relay is on)
    4. The result - the neighbors below are happy to repair: (

    Question. How to request real relay status from the server.


  • Hero Member

    @AndreD have a look here for a bit of info on requesting status.



  • I'm having trouble getting Domoticz to see any more than 1 relay. I've tried the MySensors example basic relay sketch and no luck. Then I tried this one (which is amazing btw as I want to have a PIR independent of the relays) and the same problem.

    I've tried to present the sensors individually without the FOR statement, and added a delay between presentation in case that was an issue, also I've tried different output pins on the Arduino, and I've tried multiple boards... Nothing has worked

    I am very much an enthusiastic amateur, so any advice on what to try next much appreciated

    Thanks in advance



  • @Ben-Andrewes My domoticz instance behaves weirdly with relay sketches too. Maybe you can find some help here: https://forum.mysensors.org/topic/7101/domoticz-relay-node-doesn-t-show-up-in-devices/21



  • @thierryd Thanks for the reply. In case it helps anyone I ended up individually defining and presenting the relays and their parameters within the sketch rather than using the FOR statement and it worked fine.


  • Mod

    Did you try to add a small delay in the presentation?



  • @gohan - yep added wait(100) between each sensor. To be honest I made a number of small changes without re-uploading and testing between so I'm not sure what solved it... Oh well, at least it works now 🙂



  • Hello! Sorry for my english. This sketch works fine. But I still need to add a temperature sensor ds18b20. If I add a temperature sensor, the sketch stops working. Please help. I'm new to both Arduino and Mysensors. Many thanks!!!



  • @ostoja Please add your code. Most likely you just transfered the DS18x20-Code into the relay sketch including the sleep() at the end of the loop().
    If you did it that way: This had been discussed here several times, you have to change the code into a so-called non-bocking loop().



  • @rejoe2, MANY thanks for answer! Relay don't work in Domoticz ☹

    // Enable and uncomment attached radio type
    #define MY_RADIO_NRF24
    //#define MY_TRANSPORT_WAIT_READY_MS 1      // uncomment this to enter the loop() and setup()-function even when the node cannot be registered to gw
    
    //----------------------- Relay and Motion Sensor Configuration -----------------------
    #define MOTION                                                    // un-comment to enable motion sensing
    #define NUMBER_OF_RELAYS  1                                       // Total number of attached relays. Must be equal to total number of elements in array below!
    const int RELAYS[]                  =     {4};                    // digital pins of attached relays
    const int MOTION_ACTIVATED_RELAYS[] =     {1,  0};                // 1 to trigger the relay through motion, 0 to not trigger. Array length must correspond to RELAYS[] array.
    const long ON_TIMES[]               =     {300, 0};               // Specify for each element in MOTION_ACTIVATED_RELAYS, how long the specified relay should be active in seconds.
    #define RELAY_ON          1                                       // GPIO value to write to turn on attached relay
    #define RELAY_OFF         0                                       // GPIO value to write to turn off attached relay
    #define MOTION_PIN        3                                       // The digital input pin of the motion sensor
    #define MOTION_CHILD_ID   10                                      // Set the child id of the motion sensor
    bool ack = 1;                                                     // set this to 1 if you want destination node to send ack back to this node
    
    //----------------------- DO NOT CHANGE -----------------------------
    #include <MySensors.h>
    MyMessage motion_msg(MOTION_CHILD_ID, V_TRIPPED);                 // Initialize motion message
    unsigned long trigger_millis[NUMBER_OF_RELAYS];                   // Used for the timer
    bool lastTripped = 0;                                             // Used to store last motion sensor value
    bool manual_override = 1;                                         // if this gets set to 1 (e.g. by a switch or a command from the gateway), motion triggering of relays is deactivated
    MyMessage relay_msg;                                              // Initialize relay message
    
    
    #include <DallasTemperature.h>
    #include <OneWire.h>
    
    #define COMPARE_TEMP 1 // Send temperature only if changed? 1 = Yes 0 = No
    
    #define ONE_WIRE_BUS 2 // Pin where dallase sensor is connected 
    #define MAX_ATTACHED_DS18B20 16
    unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
    OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
    DallasTemperature sensors(&oneWire); // Pass the oneWire reference to Dallas Temperature. 
    float lastTemperature[MAX_ATTACHED_DS18B20];
    int numSensors=0;
    bool receivedConfig = false;
    bool metric = true;
    // Initialize temperature message
    MyMessage msg(0,V_TEMP);
    
    void before()
    
      
    
    {
      // Startup up the OneWire library
      sensors.begin();
      int i;
      for (int sensor = 1, i = 0; sensor <= NUMBER_OF_RELAYS; sensor++, i++) {
        // set relay pins to output mode
        pinMode(RELAYS[i], OUTPUT);
        // Restore relay to last known state (using eeprom storage)
        digitalWrite(RELAYS[i], loadState(sensor) ? RELAY_ON : RELAY_OFF);
      }
      // set motion pin to output mode, if MOTION is defined
    #ifdef MOTION
      pinMode(MOTION_PIN, INPUT);
    #endif
    }
    
    void setup()
    {
      // requestTemperatures() will not block current thread
      sensors.setWaitForConversion(false);
    #ifdef MOTION
      // give the motion sensor some time to settle
      Serial.println("Starting up. Please wait 8 seconds...");
      delay(8000);
    #endif
    }
    
    void presentation()
    {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("Relay/Motion", "1.0");
    
      // Fetch the number of attached temperature sensors  
      numSensors = sensors.getDeviceCount();
    
      // Present all sensors to controller
      for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {   
         present(i, S_TEMP);
      }
    
      // Register all sensors to gw (they will be created as child devices)
      for (int sensor = 1; sensor <= NUMBER_OF_RELAYS; sensor++) {
        present(sensor, S_BINARY, "Relay", ack);
      }
    #ifdef MOTION
      present(MOTION_CHILD_ID, S_MOTION, "Motion Sensor", ack);
    #endif
    
    }
    
    void loop()
    {
    
      // Fetch temperatures from Dallas sensors
      sensors.requestTemperatures();
    
      // query conversion time and sleep until conversion completed
      int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution());
      // sleep() call can be replaced by wait() call if node need to process incoming messages (or if node is repeater)
      sleep(conversionTime);
    
      // Read temperatures and send them to controller 
      for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
     
        // Fetch and round temperature to one decimal
        float temperature = static_cast<float>(static_cast<int>((getControllerConfig().isMetric?sensors.getTempCByIndex(i):sensors.getTempFByIndex(i)) * 10.)) / 10.;
     
        // Only send data if temperature has changed and no error
        #if COMPARE_TEMP == 1
        if (lastTemperature[i] != temperature && temperature != -127.00 && temperature != 85.00) {
        #else
        if (temperature != -127.00 && temperature != 85.00) {
        #endif
     
          // Send in the new temperature
          send(msg.setSensor(i).set(temperature,1));
          // Save new temperatures for next compare
          lastTemperature[i]=temperature;
        }
      }
      sleep(SLEEP_TIME);
    #ifdef MOTION
      if (!manual_override) {
        // Read digital motion value
        bool tripped = digitalRead(MOTION_PIN) == HIGH;
    
        if (lastTripped != tripped) {
          Serial.print("New Motion State: ");
          Serial.println(tripped);
          // Send tripped value to gw
          send(motion_msg.set(tripped ? "1" : "0"));
          lastTripped = tripped;
    
          // Change relay states, send new state to gw and store state in eeprom
          if (tripped == 1) {
            for (int i = 0; i < NUMBER_OF_RELAYS; i++) {
              if (MOTION_ACTIVATED_RELAYS[i] == 1) {
                digitalWrite(RELAYS[i], RELAY_ON);
                relay_msg_constructor(i + 1, V_STATUS);
                send(relay_msg.set(RELAY_ON));
                trigger_millis[i] = millis();
                Serial.print("Relay ");
                Serial.print(RELAYS[i]);
                Serial.println(" turned on");
                saveState(i, 1);
              }
            }
          }
        }
    
        for (int i = 0; i < NUMBER_OF_RELAYS; i++) {
          if (tripped == 1 and MOTION_ACTIVATED_RELAYS[i] == 1 and trigger_millis[i] != 0) {
            trigger_millis[i] = millis();
          }
          if ((trigger_millis[i] + ON_TIMES[i] * 1000 < millis()) and MOTION_ACTIVATED_RELAYS[i] == 1 and trigger_millis[i] != 0) {
            digitalWrite(RELAYS[i], RELAY_OFF);
            relay_msg_constructor(i + 1, V_STATUS);
            send(relay_msg.set(RELAY_OFF));
            Serial.print("Relay ");
            Serial.print(RELAYS[i]);
            Serial.println(" turned off");
            saveState(i, 0);
            trigger_millis[i] = 0;
          }
        }
      }
      else {
        bool tripped = digitalRead(MOTION_PIN) == HIGH;
        if (lastTripped != tripped) {
          Serial.print("New Motion State: ");
          Serial.println(tripped);
          // Send tripped value to gw
          send(motion_msg.set(tripped ? "1" : "0"));
          lastTripped = tripped;
        }
        for (int i = 0; i < NUMBER_OF_RELAYS; i++) {
          if (MOTION_ACTIVATED_RELAYS[i] == 1 and trigger_millis[i] != 0) {
            trigger_millis[i] = 0;                            // reset running timers
          }
        }
      }
    #endif
    }
    
    void receive(const MyMessage &message)
    {
      // Handle incoming relay commands
      if (message.type == V_STATUS) {
        // Change relay state
        if (RELAYS[message.sensor - 1]) {
          digitalWrite(RELAYS[message.sensor - 1], message.getBool() ? RELAY_ON : RELAY_OFF);
    
          // Store state in eeprom
          saveState(message.sensor - 1, message.getBool());
          // Write some debug info
          Serial.print("Incoming change for sensor:");
          Serial.print(message.sensor);
          Serial.print(", New status: ");
          Serial.println(message.getBool());
        }
      }
    
      // Handle incoming manual override/bypass of motion sensor
      if (message.type == V_ARMED and message.sensor == 0) {
        manual_override = message.getBool();
        Serial.print("Manual Override: ");
        Serial.println(manual_override);
      }
    }
    
    void relay_msg_constructor(int sensor, uint8_t type)
    {
      relay_msg.setSensor(sensor);
      relay_msg.setType(type);
    }
    


  • I commented out the "sleep", but when I press the relay switch in Domoticz, it does not work☹



  • @ostoja said in Multiple Relays + Motion sketch, fully customizable, optional timer, manual override:

    // Present all sensors to controller
      for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {   
         present(i, S_TEMP);
      }
    
      // Register all sensors to gw (they will be created as child devices)
      for (int sensor = 1; sensor <= NUMBER_OF_RELAYS; sensor++) {
        present(sensor, S_BINARY, "Relay", ack);
      }
    

    With this two "for" loops you set present(n) multiple times. in the first for() you start with 0 until MAX_ATTACHED_DS18B20-1, in the second for() you start with 1 until NUMBER_OF_RELAYS-1, so this will assign the sensors with identical IDs as the relays.
    I have not checked the internal code of this method, but I would think that you should better user a different range in the second for() loop starting at least with MAX_ATTACHED_DS18B20+1.



  • @dirkc said in Multiple Relays + Motion sketch, fully customizable, optional timer, manual override:

    @ostoja said in Multiple Relays + Motion sketch, fully customizable, optional timer, manual override:

    // Present all sensors to controller
      for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {   
         present(i, S_TEMP);
      }
    
      // Register all sensors to gw (they will be created as child devices)
      for (int sensor = 1; sensor <= NUMBER_OF_RELAYS; sensor++) {
        present(sensor, S_BINARY, "Relay", ack);
      }
    

    With this two "for" loops you set present(n) multiple times. in the first for() you start with 0 until MAX_ATTACHED_DS18B20-1, in the second for() you start with 1 until NUMBER_OF_RELAYS-1
    I have not checked the internal code of this method, but I would think that you should better user a different range in the second for() loop starting at least with MAX_ATTACHED_DS18B20+1.

    Additional remarks:
    For the relays better start with 1.
    Shift the Temp values to higer range (use e.g. a variable like FRIST_DS_SENSOR_ID).
    Check, if the receive()-part and the ID you get really fits together with the relay you want to switch.
    Don't try to read the temperature values every few parts of a second. Use a millis()-routine.

    Please search the forum, there have to be at least 2 threads with exactly the same problems in which I posted how to solve this. You are "trying to reinvent the wheel". 😁



  • @ostoja look at this video with an explanation of this topic and how to handle the IDs
    https://forum.mysensors.org/post/26597



  • @HenryWhite thank you for the excellent sketch. It works like a charm in my HA configuration!


Log in to reply
 

Suggested Topics

  • 1
  • 1
  • 3
  • 3
  • 10
  • 2

1
Online

11.4k
Users

11.1k
Topics

112.7k
Posts