IoT ready relay to operate 220v AC home appliances

When I am away from home during long vacations I felt need of having control of some lights back at home, specially during Diwali holidays to turn on lights at night.
I had already interfaced my 4 channel relay with raspberry and NodeMCU and control it via ssh terminal on raspberry and serial port of NodeMCU.

Yesterday I extended the code on NodeMCU to leverage MQTT technology. Now IoT is enabled on board and I can instruct NodeMCU over WiFi or internet to switch on|off each channel of relay and can query current state of each channel of relay.

You just need a NodeMCU board, 4 channel relay and 5V power adapter.

Connections:

1) 3v3 (3.3V) on NodeMCU connects to VCC of relay board.
2) GND (Ground) on NodeMCU connects to GND on relay board.
3) D5, D6, D7, D8 pins on NodeMCU connect to IN1, IN2, IN3, IN4 on relay board.
4) 220v AC load on Normally open terminals of each relay channel. You can hook 4 220v AC appliances on this relay.

I have defined two topics in code to give input signal and read state of relay channels on MQTT network.

1) /home/ggn/relay/input to publish intructions from any MQTT client. These instrcutions are read by NodeMCU board on same network.
2) /home/ggn/relay/read to read current state of relay channels. MQTT clients subscribed to this Topic will receive current state of relay channels.

Predefined commands to turn ON | OFF | QUERY each relay channel:

1) RCH1ON, RCH2ON, RCH3ON, RCH4ON to turn on relay channels.
2) RCH1OFF, RCH2OFF, RCH3OFF, RCH4OFF to turn off relay channels.
3) RCH1Q, RCH2Q, RCH3Q, RCH4Q to query currect state of each channel on relay.

How it works ?

1) NodeMCU and any MQTT client (like MQTT dash, or simple linux mosquitto_pub cmd) connected to the same MQTT broker can communicate.
2) MQTT client gives RCHx{ON|OFF|Q} commands on MQTT network. These instrcutions are picked up by NodeMCU to perform asked action on relay.

Here is the code that you need to flash on board


#include "ESP8266WiFi.h"
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

/*********** WiFi Access Point **************/

#define WLAN_SSID       "Your WiFi SSID"
#define WLAN_PASS       "Your WiFi password"

/********* Broker config ********/

#define MQTT_SERVER      "your MQTT broker IP or hostname"
#define MQTT_SERVERPORT  1883                   // default MQTT port
#define MQTT_USERNAME    "MQTT login user"
#define MQTT_KEY         "MQTT login password"

/******** Define Relay pins on board to be used ******/

#define RCH1 D5
#define RCH2 D6
#define RCH3 D7
#define RCH4 D8

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, MQTT_SERVERPORT, MQTT_USERNAME, MQTT_KEY);

/****************************** Feeds ***************************************/

Adafruit_MQTT_Publish relayread = Adafruit_MQTT_Publish(&mqtt, "/home/ggn/relay/read");

Adafruit_MQTT_Subscribe relayin = Adafruit_MQTT_Subscribe(&mqtt, "/home/ggn/relay/input");

/*************************** Sketch Code ************************************/

void MQTT_connect();

void setup() {
  Serial.begin(115200);
  delay(10);

  Serial.println(F("Relay control over MQTT"));

  // Connect to WiFi access point.
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("WiFi connected");
  Serial.println("IP address: "); Serial.println(WiFi.localIP());

  // Setup MQTT subscription for onoff feed.
  pinMode(RCH1, OUTPUT);
  pinMode(RCH2, OUTPUT);
  pinMode(RCH3, OUTPUT);
  pinMode(RCH4, OUTPUT);

// Set initial value to HIGH for active low relays
  digitalWrite(RCH1, HIGH);
  digitalWrite(RCH2, HIGH);
  digitalWrite(RCH3, HIGH);
  digitalWrite(RCH4, HIGH);

  mqtt.subscribe(&relayin);
}

char * inputval;

void loop() {
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();

  // this is our 'wait for incoming subscription packets' busy subloop
  // try to spend your time here

  Adafruit_MQTT_Subscribe *subscription;
  
  while ((subscription = mqtt.readSubscription(5000))) {
    if (subscription == &relayin) 
    {
      inputval=(char *)relayin.lastread;
      Serial.print(F("\nGot: "));
      Serial.println(inputval);

//Relay on channel 1
      
      if (strcmp(inputval,"RCH1ON")==0)
      {
        Serial.print(F("\nTurning ON Relay on ch1"));
        digitalWrite(RCH1, LOW);
          if (! relayread.publish("RCH1ON")) 
          {
            Serial.println(F("Failed to publish RCH1 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH1 state"));
          }           
      }
      if (strcmp(inputval,"RCH1OFF")==0)
      {
        Serial.print(F("\nTurning OFF Relay on ch1"));
        digitalWrite(RCH1, HIGH);
          if (! relayread.publish("RCH1OFF")) 
          {
            Serial.println(F("Failed to publish RCH1 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH1 state"));
          }           
      }
      if (strcmp(inputval,"RCH1Q")==0)
      {
        Serial.print(F("\nQuerying RCH1"));
        
        if (digitalRead(RCH1) == HIGH)
        {
          Serial.print(F("\nRCH1 is OFF"));
          if (! relayread.publish("RCH1OFF")) 
          {
            Serial.println(F("Failed to publish RCH1 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH1 state"));
          }            
        }
        if(digitalRead(RCH1) == LOW)
        {
          Serial.print(F("\nRCH1 is ON"));
          if (! relayread.publish("RCH1ON")) 
          {
            Serial.println(F("Failed to publish RCH1 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH1 state"));
          }   
        }
      }

 // Relay on channel 2

      if (strcmp(inputval,"RCH2ON")==0)
      {
        Serial.print(F("\nTurning ON Relay on ch2"));
        digitalWrite(RCH2, LOW);
          if (! relayread.publish("RCH2ON")) 
          {
            Serial.println(F("Failed to publish RCH2 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH2 state"));
          }           
      }
      if (strcmp(inputval,"RCH2OFF")==0)
      {
        Serial.print(F("\nTurning OFF Relay on ch2"));
        digitalWrite(RCH2, HIGH);
          if (! relayread.publish("RCH2OFF")) 
          {
            Serial.println(F("Failed to publish RCH2 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH2 state"));
          }           
      }
      if (strcmp(inputval,"RCH2Q")==0)
      {
        Serial.print(F("\nQuerying RCH2"));
        
        if (digitalRead(RCH2) == HIGH)
        {
          Serial.print(F("\nRCH2 is OFF"));
          if (! relayread.publish("RCH2OFF")) 
          {
            Serial.println(F("Failed to publish RCH2 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH2 state"));
          }            
        }
        if(digitalRead(RCH2) == LOW)
        {
          Serial.print(F("\nRCH2 is ON"));
          if (! relayread.publish("RCH2ON")) 
          {
            Serial.println(F("Failed to publish RCH2 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH2 state"));
          }   
        }
      }

 //Relay on channel 3

       if (strcmp(inputval,"RCH3ON")==0)
      {
        Serial.print(F("\nTurning ON Relay on ch3"));
        digitalWrite(RCH3, LOW);
          if (! relayread.publish("RCH3ON")) 
          {
            Serial.println(F("Failed to publish RCH3 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH3 state"));
          }           
      }
      if (strcmp(inputval,"RCH3OFF")==0)
      {
        Serial.print(F("\nTurning OFF Relay on ch3"));
        digitalWrite(RCH3, HIGH);
          if (! relayread.publish("RCH3OFF")) 
          {
            Serial.println(F("Failed to publish RCH3 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH3 state"));
          }           
      }
      if (strcmp(inputval,"RCH3Q")==0)
      {
        Serial.print(F("\nQuerying RCH3"));
        
        if (digitalRead(RCH3) == HIGH)
        {
          Serial.print(F("\nRCH3 is OFF"));
          if (! relayread.publish("RCH3OFF")) 
          {
            Serial.println(F("Failed to publish RCH3 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH3 state"));
          }            
        }
        if(digitalRead(RCH3) == LOW)
        {
          Serial.print(F("\nRCH3 is ON"));
          if (! relayread.publish("RCH3ON")) 
          {
            Serial.println(F("Failed to publish RCH3 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH3 state"));
          }   
        }
      }

//Relay on channel 4

      if (strcmp(inputval,"RCH4ON")==0)
      {
        Serial.print(F("\nTurning ON Relay on ch4"));
        digitalWrite(RCH4, LOW);
          if (! relayread.publish("RCH4ON")) 
          {
            Serial.println(F("Failed to publish RCH4 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH4 state"));
          }           
      }
      if (strcmp(inputval,"RCH4OFF")==0)
      {
        Serial.print(F("\nTurning OFF Relay on ch4"));
        digitalWrite(RCH4, HIGH);
          if (! relayread.publish("RCH4OFF")) 
          {
            Serial.println(F("Failed to publish RCH4 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH4 state"));
          }           
      }
      if (strcmp(inputval,"RCH4Q")==0)
      {
        Serial.print(F("\nQuerying RCH4"));
        
        if (digitalRead(RCH4) == HIGH)
        {
          Serial.print(F("\nRCH4 is OFF"));
          if (! relayread.publish("RCH4OFF")) 
          {
            Serial.println(F("Failed to publish RCH4 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH4 state"));
          }            
        }
        if(digitalRead(RCH4) == LOW)
        {
          Serial.print(F("\nRCH4 is ON"));
          if (! relayread.publish("RCH4ON")) 
          {
            Serial.println(F("Failed to publish RCH4 state"));
          } 
          else 
          {
            Serial.println(F("Published RCH4 state"));
          }   
        }
      }

    }
  }

  // Publish relay state
  /* Disabled
  Serial.print(F("\nSending photocell val "));
  Serial.print(x);
  Serial.print("...");
  if (! relayread.publish(x++)) {
    Serial.println(F("Failed"));
  } else {
    Serial.println(F("OK!"));
  }*/

  // ping the server to keep the mqtt connection alive
  // NOT required if you are publishing once every KEEPALIVE seconds
  /*
  if(! mqtt.ping()) {
    mqtt.disconnect();
  }
  */
}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
       retries--;
       if (retries == 0) {
         // basically die and wait for WDT to reset me
         while (1);
       }
  }
  Serial.println("MQTT Connected!");
}