MQTT NodeMCU
Smart Relay

230V Relay

Internet of Things

Programming

Arduino

SRE | Arduino & MQTT with Svelte UI

Arduino and MQTT with Svelte UI

Intro

This is demo project for the Home Automation. NodeMCU is controling the relay connected to the garden lights. This way, lights are controled using the mobile phone (by the web page or MQTT client app).

MQTT

MQTT is one of the standards for Internet of Things communication. It is publish/subscribe network protocol for message queuing. It is simple to install and configure it on Ubuntu using Mosquitto package.

By default it will listen for MQTT connections on TCP port 1883 or WebSocket connection (with SSL) on port 9001. Communication is via topics where multiple clients subscribe or publish messages. When message is published in some topics, all subscribers will receive it. This communication will work even if the subscribers are behind NAT because they will initiate the connection.

Security Issue

This device will have to connect to WiFi and to the MQTT Broker, so the code must contain sensitive info: SSID, password, MQTT address and user/pass. If you plan to share the code or use Git it would be good to separate the code in 2 parts: header file with sensitive info and sketch file with the logic. Then you can control using .gitignore what you are sharing. Also, Arduino IDE has mechanism to control secrets and prevent sharing.

    
    // secret_data.h
    #define SECRET_SSID "my wifi network"
    #define SECRET_WIFI_PASS "my pass"
    #define SECRET_MQTT_ADDRESS "my.mqtt.host"
    #defin SECRET_MQTT_ID "unique_secret_ID"    
                

Code

Here is the device logic, providing protection from loss of wifi or MQTT connections. In this case, device will switch off connected 230 VAC hardware just to be safe and try to reconnect to wifi and/or mqtt.

    
    #include <ESP8266WiFi.h>          // ESP8266 Core WiFi Library 
    #include <WiFiManager.h>          // https://github.com/tzapu/WiFiManag
    #include <PubSubClient.h>         // Mqtt
    #include "secret_data.h"
    
    // Library Init
    const char* mqtt_server = SECRET_MQTT_SERVER;
    WiFiClient wifiClient;
    PubSubClient mqtt_client(wifiClient);
    WiFiManager wifiManager;
    
    // WiFi Event Handlers
    WiFiEventHandler gotIpEventHandler, disconnectedEventHandler; 
    
    #define ON 0
    #define OFF 1
    
    // Built-in LED
    uint8_t relayPin = D4;
    
    int relayState = OFF;
    long milliseconds = 0;
    long lastMqttReconnectAttempt = 0;

    void setup()
    {
        Serial.begin(115200);
        Serial.println("Here comes...");

        pinMode(relayPin, OUTPUT);
        delay(1000);

        digitalWrite(relayPin, OFF);
        Serial.print("relayState = ");
        Serial.println(relayState == OFF ? "OFF" : "ON");

        // Connect to WiFi
        gotIpEventHandler = WiFi.onStationModeGotIP([](const WiFiEventStationModeGotIP& event)
        {
            Serial.print("Connected to WiFi: ");
            Serial.println(WiFi.localIP());
        });

        disconnectedEventHandler = WiFi.onStationModeDisconnected([] (const WiFiEventStationModeDisconnected& event)
        {
            Serial.println("WiFi disconnected!");
            digitalWrite(relayPin, OFF);
            relayState = OFF;
        });

        Serial.println("Connecting to WiFi...");
        WiFi.begin(SECRET_SSID, SECRET_PASS);

        delay(2000);

        // Connect to MQTT
        mqtt_client.setServer(mqtt_server, 1883);
        mqtt_client.setCallback(callback);

        // Name must be unique to avoid disconnect
        mqtt_client.connect(SECRET_MQTT_ID);
    
        mqtt_client.publish("newOutTopic", "Hello World!");
        mqtt_client.subscribe("newCommands");

        // Set LOOP Timer
        milliseconds = millis();

        lastMqttReconnectAttempt = 0;
    }

    void loop()
    {
        // Non-blicking MQTT reconnect
        if(!mqtt_client.connected()) {
            long now = millis();
            if(now - lastMqttReconnectAttempt > 5000) {
                lastMqttReconnectAttempt = now;
                if(mqttReconnect()) {
                    lastMqttReconnectAttempt = 0;
                }
            }
        } else {
            mqtt_client.loop();
        }

        // Every minute
        if(millis() > milliseconds + 60000) {
            milliseconds = millis();

            // check if MQTT is alive
            if(!mqtt_client.connected()) {
                Serial.println("Lost connection to MQTT");
                digitalWrite(relayPin, OFF);
                relayState = OFF;

                // reconnect
            }
        }
    }

    // Callback is triggered when message arrives in one of subscribed Topics
    void callback(char* topic, byte* payload, unsigned int length) {
        Serial.print("Message arrived [");
        Serial.print(topic);
        Serial.print("] ");
        for (int i = 0; i < length; i++) {
            Serial.print((char)payload[i]);
        }
        Serial.println();

        // Switch on/off
        if ((char)payload[0] == '1') {
            handle_on();
        }
    
        if((char)payload[0] == '0') {
            handle_off();
        }

    }

    void handle_on() {
        Serial.println("Palim Relay!");
        digitalWrite(relayPin, ON);
        relayState = ON;
        mqtt_client.publish("newOutTopic", "Palim relej!");
    }
    
    void handle_off() {
        Serial.println("Gasim Relay!");
        digitalWrite(relayPin, OFF);
        relayState = OFF;
        mqtt_client.publish("newOutTopic", "Gasim relej!");
    }
    
    boolean mqttReconnect() {
        // Name must be unique to avoid disconnect!
        if(mqtt_client.connect(SECRET_MQTT_ID)) {
    
            mqtt_client.publish("newOutTopic", "Just reconnected... :-/");
            mqtt_client.subscribe("newOutTopic");
            mqtt_client.subscribe("newCommands");
        }
        return mqtt_client.connected();
    }