Arduino Based Garage Door multi sensor and state - Need Help

Hey guys:

I’m hoping someone can help me, I have a sensor built with the following:

Arduino Uno
Relay - PIN 4
Reed switch - PIN 2

I wanted to share this with everyone, but also hoping someone can help perfect it…

It is connected to my push button garage door opener to send a momentary press to open / close the door

The Reed switch determines if the door is open or closed, and the PIR / DHT are really there just for fun ( Temp and motion triggers )

Basically It works great, and integrates very nicely into HA ( Thanks @martinhjelmare ) for this!

Where I am stuck, and have tried many things is keeping the arduino relay state after power outage, but cannot figure it out. I believe the sketch is ok, and is based off mysensors 2, but honestly looking for some feedback if possible, and help with keeping the relay state in memory I have tried adding:

        // Set relay to last known state (using eeprom storage)
        digitalWrite(pin, loadState(sensor)?RELAY_ON:RELAY_OFF);

But obviously am not doing it right…

Below is my sketch, and if anyone can please help / comment it would be greatly appreciated. I think many folks can benefit from this once completed

#define MY_DEBUG
#define MY_RADIO_NRF24
#define MY_NODE_ID 50

#include <SPI.h>
#include <MySensors.h>
#include <Bounce2.h>  
#include <DHT.h>  

#define CHILD_ID_TEMP 11
#define CHILD_ID_HUM 10
#define CHILD_ID_MOT 3   // Id of the sensor child
#define CHILD_ID_REL 1
#define RELAY_PIN  4
#define RELAY_ON 1
#define RELAY_OFF 0
#define TOGGLE_INTERVAL 350  //Tells how many milliseconds the relay will be held closed
#define CLOSED 0
#define OPEN 1
#define BUTTON_PIN_1  2
#define CHILD_ID_SW 2
#define DIGITAL_INPUT_SENSOR 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
#define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)

// Set this offset if the sensor has a permanent small offset to the real temperatures

Bounce debouncer = Bounce();
int oldValue=-1;

bool state = false;
bool initialValueSent = false;
byte StatePIR=0;
byte oldStatePIR=0;
unsigned long SLEEP_TIME = 60000; // Sleep time between reads (in milliseconds)

DHT dht;
float lastTemp;
float lastHum;
boolean metric = true; 
bool StateREL=0, StateREL1=0;
MyMessage msgHum(CHILD_ID_HUM, V_HUM);
MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
MyMessage msgMot(CHILD_ID_MOT, V_TRIPPED);
MyMessage msg(CHILD_ID_REL, V_STATUS);

void presentation()  
  // Send the Sketch Version Information to the Gateway
  sendSketchInfo("Garage Multi Sensor", "1.0");

  // Register all sensors to gw (they will be created as child devices)
  present(CHILD_ID_TEMP, S_TEMP);
  present(CHILD_ID_HUM, S_HUM);
  present(CHILD_ID_MOT, S_MOTION);
  present(CHILD_ID_REL, S_BINARY);
  present(CHILD_ID_SW, S_DOOR);

  metric = getConfig().isMetric;

void setup()
  // Setup the button
  // Activate internal pull-up
  // After setting up the button, setup debouncer

  // Make sure relays are off when starting up
  digitalWrite(RELAY_PIN, StateREL1);

  if (SLEEP_TIME <= dht.getMinimumSamplingPeriod()) {
    Serial.println("Warning: UPDATE_INTERVAL is smaller than supported by the sensor!");
  // Sleep for the time of the minimum sampling period to give the sensor time to power up
  // (otherwise, timeout errors might occure for the first reading)

//  metric = getConfig().isMetric;

  pinMode(DIGITAL_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input

void loop()
  // Get the update value
  int value =;

  if (value != oldValue) {
     // Send in the new value
     send(msgSW.set(value==HIGH ? 1 : 0));
     oldValue = value;
  if (StatePIR != oldStatePIR) {
    send(msgMot.set(StatePIR ? 1 : 0));
  if (!initialValueSent) {
    Serial.println("Sending initial value");
    Serial.println("Requesting initial value from controller");
    request(CHILD_ID_REL, V_STATUS);
    wait(2000, C_SET, V_STATUS);
  if (debouncer.update()) {
    if ( {
      state = !state;
      // Send new state and request ack back
      send(msg.set(state?RELAY_ON:RELAY_OFF), true);
  // Read digital motion value
  boolean tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH; 
  send(msgMot.set(tripped?"1":"0"));  // Send tripped value to gw 
  // Fetch temperatures from DHT sensor
  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);
    temperature += SENSOR_TEMP_OFFSET;
    send(msgTemp.set(temperature, 1));
    #ifdef MY_DEBUG
    Serial.print("T: ");
  // Fetch humidity from DHT sensor
  float humidity = dht.getHumidity();
  if (isnan(humidity)) {
      Serial.println("Failed reading humidity from DHT");
  } else if (humidity != lastHum) {
      lastHum = humidity;
      send(msgHum.set(humidity, 1));
      #ifdef MY_DEBUG
      Serial.print("H: ");
  sleep(INTERRUPT,CHANGE, SLEEP_TIME); //sleep a bit

void receive(const MyMessage &message) {
  if (message.isAck()) {
     Serial.println("This is an ack from gateway");
  if (message.type == V_STATUS) {
    if (!initialValueSent) {
      Serial.println("Receiving initial value from controller");
      initialValueSent = true;
    // Change relay state
    state = (bool)message.getInt();
    digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF);
    //Keep the relay on for the amount of time defined by TOGGLE_INTERVAL
    delay( TOGGLE_INTERVAL );
    digitalWrite( RELAY_PIN, RELAY_OFF );
    // Store state in eeprom
    saveState(message.sensor, message.getBool());

Thanks Again!


I can’t connect your description with what your sketch shows. You wrote you have a reed switch to detect the state of the door. But I only see a button that controls the relay.

Besides this there are some issues in the sketch.

  • Never use delay, use either wait for non battery driven sensors, or sleep for battery driven sensors.
  • Since you expect to receive messages to the device, you can’t sleep the device. Use wait instead. This will process messages in the background.
  • You update and read the debouncer twice in the loop. You don’t need to do that.
  • You also read the PIR state twice in the loop. Don’t do that.

I’m guessing loadState doesn’t work cause the call to saveState in the receive method is never run, since the device is sleeping and doesn’t process incoming messages.