[OH2] Control ESP8266 Relay using MQTT Eventbus

This simple tutorial will explain how to configure OpenHab 2 in order to remotely control a Relay (electrically operated Switch).

It is based on my mini project with the NICE SpA Robo 500 Sliding Gate Opener but you can adopt it to your needs and control many other devices from OpenHab 2 using an ESP8266 based board.

Requirements


For this tutorial, I assume that you can install OpenHAB 2 & Mosquitto and get them both up and running with default configurations. I will spend most of my time explaining how to configure OH2 and its components (MQTT Binding) as well as the ESP board. If you need help with OH2 & Mosquitto setup, you can check out this great tutorial also: Install and setup OpenHAB 2.0 with Mosquitto and ESPEasy+HC-SR04

System, Subsystem & Components


A high level overview of the solution is displayed in the image below:

The System (aka Solution) is composed of 3 main Subsystems and each Subsystem has its own components (aka configs).

Subsystem 1 = OpenHAB 2


The core of the solution where all the magic happens :slight_smile:

Following the basic installation of OH2, you need to install the MQTT Binding.
This can be done from the Paper UI -> Extensions -> Bindings -> MQTT Binding (binding-mqtt1 - 1.9.0.SNAPSHOT)
We will configure the MQTT service and the MQTT Event Bus in Subsystem 2 section below.

For now, we must enable our simple ā€œpush-buttonā€ in OH2 and to display it in our sitemap.
Add the following text to your items file:

Switch	Robo500		"Robo 500 Relay"    <siren>	{autoupdate="false"}

and the following text to your sitemap file:

Frame {
	Switch item=Robo500 mappings=[ON="GO!"]
}

This will display a simple button on your sitemap with the text ā€œGO!ā€. Pressing this button will send an ā€œONā€ command to the Switch item called ā€œRobo500ā€. We will use this command later on to pass it over to the ESP8266 via the MQTT Eventbus.

Subsystem 2 = MQTT & Eventbus


Next step is to install and configure our MQTT Binding to connect to our Mosquitto Message Broker and setup the MQTT-Eventbus.
Edit your mqtt.cfg and introduce your settings for the connection to the Mosquitto server:

MQTT.url=tcp://localhost:1883
MQTT.clientId=openhab
MQTT.user=openhab
MQTT.pwd=some_password

Now, we will create a massive link between the OpenHAB 2 Eventbus and MQTT. In this case, ALL commands and status updates will be published to the MQTT Broker.
Edit your mqtt-eventbus.cfg and configure the following settings:

broker=MQTT
statePublishTopic=openhab/out/${item}/state
commandPublishTopic=openhab/out/${item}/command
stateSubscribeTopic=openhab/in/${item}/state
commandSubscribeTopic=openhab/in/${item}/command

In this case, OH2 will publish everything to the openhab/out followed by the item name and the state or command topic.
It will also subscribe to multiple topics, listening for state updates and/or commands to be posted to the openHAB event bus.

Subsystem 3 = ESP8266 & Relay Shield


To be able to control the Robo 500 Sliding Gate Opener, we need to create a short circuit between 2 pins (#9 & #12) found on its motherboard. This executes a Step command that controls the motor (in sequence: Open -> Stop -> Close -> Stop).

We will create an Adruino Sketch that has WiFi enabled, MQTT support, can accept OTA updates and subscribes to the MQTT topic that is populated by OH2 Eventbus for this specific item.

In case a message is published to the monitored topic, we will close the circuit on the relay for 100 milliseconds and then open it up again to issue the control command to the Robo 500 unit.

The Adruino Sketch is the following:

/*
 * Dim - Relay Shield Toggle v1.0
 * Closes the relay for 100 ms, then opens based on OH2 event
 * Relay Shield transistor closes relay when D1 is HIGH
*/

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>

/************************* WiFi Access Point *********************************/
#define WLAN_SSID       "<ssid>"
#define WLAN_PASS       "<pass>"

/************************* MQTT Broker Setup *********************************/
#define mqtt_server      "<mqtt_broker>"
#define mqtt_serverport  1883                   // use 8883 for SSL
#define mqtt_username    "<mqtt_username>"
#define mqtt_password    "<mqtt_password>"

/************************* Constants, Variables, Integers, etc *********************************/
const int relayPin = D1;
const long togDelay = 100;  // pause for 100 milliseconds before toggling to Open
const long postDelay = 200;  // pause for 200 milliseconds after toggling to Open
int value = 0;
int relayState = LOW;

// 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_password);

// Setup subscription 'Robo500' for monitoring topic for changes.
Adafruit_MQTT_Subscribe Robo500 = Adafruit_MQTT_Subscribe(&mqtt, "openhab/out/Robo500/command");

/*************************** Sketch Code ************************************/
void MQTT_connect();

void setup() {
  Serial.begin(115200);
  Serial.println(""); Serial.println(F("Booting... v1.0"));
  pinMode(relayPin, OUTPUT);
  // Connect to WiFi access point.
  Serial.print("Connecting to "); Serial.println(WLAN_SSID);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
  Serial.println("Connection Failed! Rebooting in 5 secs...");
  delay(5000);
  ESP.restart();
  }

  // Setup MQTT subscription for Robo500 feed.
  mqtt.subscribe(&Robo500);

  // Begin OTA
  ArduinoOTA.setPort(8266); // Port defaults to 8266
  ArduinoOTA.setHostname("robo500");   // Hostname defaults to esp8266-[ChipID]
  ArduinoOTA.setPassword((const char *)"<pass>");   // No authentication by default

  ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("");
  Serial.println("Ready & WiFi connected");
  Serial.print("IP address: "); Serial.println(WiFi.localIP());
}

void loop() {
  ArduinoOTA.handle();
  // 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 == &Robo500) {
      Serial.print(F("Got: ")); Serial.println((char *)Robo500.lastread);
      // close the relay for 100 ms
      Serial.println("Close Relay for 100 ms & then Open");
      digitalWrite(relayPin, HIGH);
      delay(togDelay);
      digitalWrite(relayPin, LOW);
      delay(postDelay);
    }
  }

  // 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!");
}

Pictures from the onsite installation:

14 Likes

Awesome write-up! Thanks for posting!

Did you use the Event Bus configuration because you plan on expanding or are already using that configuration? I would have expected to just see an outgoing MQTT binding config on the Robo500 Switch.

1 Like

Thanks for your help @rlkoshak. I took your advice and went with the ESP board and I am super happy.
The response is ultra fast (from the moment I press the ā€œGO!ā€ button to the moment that the Gate opens)

I am planning to use MQTT for multiple use cases, so I went with the easy (lazy) way of channeling ALL stuff to that. I know that I am spamming the broker but I run everything on a powerful laptop, so I have no worries :slight_smile:

I will also create an alternative item using the MQTT binding config but for now, I wrote the tutorial using the simple setup (publish everything there and monitor/subscribe to the selected topic from the ESP).

Hey Dim,
thatā€™s a really nice tutorial! Thanks for sharing the setup with us.
I have the same question as Rich: Why use the MQTT event bus hammer method instead of a directed MQTT topic based communication?

Also regarding your Wemos/ESP8266 sketch: Did you consider using a framework? Iā€™ve played around with these chips myself and at some point itā€™s really nice to have the OTA update feature, the wifi configuration or many other useful things just build inā€¦

1 Like

Thanx :+1:

I am planning to further improve the setup for this mini project. I wrote this tutorial immediately after I installed the solution, so it is currently in alpha versionā€¦ :slight_smile:

I will definitely optimize it with a focused MQTT binding configuration for the Robo500 item as well as try to use a cleaner sketch with a framework for the ESP.

I am a big fan of MQTT in general, so I channel everything there and play with the topics and create use cases with this layout.

1 Like

Hi there!

thanks for this great example. It worked just from the first attempt. I took some time to really understand what was done, and remake it to work with multiple relays. now it is working!

I plan to build it up inside the wall switch box (3 gangs) to controls the lamps from there.

I will make the electrical connection to the lamps using the relays (activated from D1, D2 and D3), and use the switches to input 5v to D5, D6 and D7.

I just have one extra chalange hereā€¦ using your example code, how to add a publish event to inform OH that I used a wall switch?

Thanks!

Last night I got MySQL & jdbc persistence working, but now my mqtt eventbus has stopped functioning :frowning: do any of you know if itā€™s required to have MQTT persistence running alongside MySQL/JDBC?

I doubt itā€¦
Try to troubleshoot the OH2->MQTT broker connection.

It was fixed, donā€™t know how. Apparently Openhab is picky about which config files it deems interesting to load.

1 Like

Just for anyone reading this tutorial and wants to have a simpler configuration (without the massive link between OH2 and MQTT using the event-bus):

Here is the item definition that will publish the message in MQTT (in the topic that the ESP code is subscribing to):

Switch  Robo500  "Robo 500"   <siren> (gESP)   {mqtt=">[HomeR:openhab/out/Robo500/command:command:ON:default]", autoupdate="false"}

In this case, you donā€™t need to configure the mqtt-eventbus.cfg (leave it empty)

1 Like

Iā€™m quite new to openhab. Could someone clarify what these variables should be set to? (my best guess is included).

#define mqtt_server      "192.168.1.110"
#define mqtt_serverport  1883                   // use 8883 for SSL
#define mqtt_username    "openhab"
#define mqtt_password    "openhab"

The serial monitor indicates that WiFi connects fine, but MQTT will not. Do I also need to start/restart any services? I am running OH2 and Mosquitto from a Raspberry Pi. Any other help would be great, I am at a loss.

The settings in your ESP code for MQTT look ok
If you are not using authentication in your Broker, you donā€™t need the username/password combo (just delete these 2 lines)

The ESP does not connect to the Broker at all? (do you get in the serial monitor the messages: "Retrying MQTT connection in 5 seconds..." ?)

Ps: This is ESP to MQTT (not openHAB yet) :slight_smile: Later on, you will have to define your openHAB items to publish to the MQTT topic that the ESP is listening (subscribing) to.

Thanks for helping out. Hereā€™s the relevant code from mqtt.cfg. I just tested again with the user and pwd lines commented out, same problem.

# URL to the MQTT broker, e.g. tcp://localhost:1883 or ssl://localhost:8883
MQTT.url=tcp://192.168.1.110:1883

# Optional. Client id (max 23 chars) to use when connecting to the broker.
# If not provided a default one is generated.
MQTT.clientId=openhab

# Optional. User id to authenticate with the broker.
MQTT.user=openhab

# Optional. Password to authenticate with the broker.
MQTT.pwd=openhab

Hereā€™s the serial printout:


Soft WDT reset

ctx: cont 
sp: 3ffefa30 end: 3ffefc20 offset: 01b0

>>>stack>>>
3ffefbe0:  00000001 3ffee878 3ffeeb3c 40202372  
3ffefbf0:  3fffdad0 00000000 3ffeebf0 402023be  
3ffefc00:  3fffdad0 00000000 3ffeebf0 40206d88  
3ffefc10:  feefeffe feefeffe 3ffeec00 40100718  
<<<stack<<<

 ets Jan  8 2013,rst cause:2, boot mode:(3,7)

load 0x4010f000, len 1384, room 16 
tail 8
chksum 0x2d
csum 0x2d
v3de0c112
~ld
āø®
Booting... v1.0
Connecting to [redacted]

Ready & WiFi connected
IP address: 192.168.1.124
Connecting to MQTT... Connection failed
Retrying MQTT connection in 5 seconds...
Connection failed
Retrying MQTT connection in 5 seconds...
Connection failed
Retrying MQTT connection in 5 seconds...

waitā€¦ donā€™t mix the 2 things (OH2 and ESP)
Both should connect to MQTT but they are different and should be dealt separately
For now, you are experiencing a problem with the ESP connection to the MQTT Broker.
You need to troubleshoot this connection first, before you start configuring OH2.

Can you connect to your broker from any kind of remote client?
Try MQTT.fx and make sure that the Broker is up and running properly

1 Like

Success! MQTT.fx was also refusing. It seems mosquitto wasnā€™t enabled on the RPi (checked with ā€˜sudo systemctl status mosquittoā€™).

Thank you for the help!

1 Like

What are best practice to use event bus ? usage of event bus may limit the freedom on topics .

Iā€™m not sure what you are asking. The purpose of Event Bus is to share EVERY Itemā€™s commands and updates with some other system (could be another instance of OH, could be Node Red, could be something else). By necessity, it requires constraints on the topics.

When using the event bus you define the state and command publish and subscribe topics. Somewhere in those topic definitions you put ${item} which will be replaced with the Item name.

This configuration must be complimentary on the other side. For example, the other client should subscribe to the the Publish topics defined in mqtt-eventbus.cfg and publish to the Subscribe topics in that file.

If you need topics that cannot conform to this or you do not want to share all updates and commands for all Items you shouldnā€™t use Event Bud.

I believe that you can continue using the regular item binding setup in parallel to the MQTT Event Bus link.

For example:
With MQTT Event Bus configured to publish everything to:

statePublishTopic=openhab/out/${item}/state
commandPublishTopic=openhab/out/${item}/command

and a specific item (bound to mqtt) defined as:

Switch	Robo500		"Robo 500"	{mqtt=">[HomeR:HomeAuto/ESP/whatever:command:ON:default]", autoupdate="false"}

I would get the command message published to both topics:

openhab/out/Robo500/command (due to the MQTT Event Bus)
as well as
HomeAuto/ESP/whatever (due to the item config)

So, I am not constrained in any way.
Note: I havenā€™t tested this out but I believe that it will work as I described itā€¦
It could get tricky with subscriptions (2 sources of state updates for the same itemā€¦)

Hi @Dim
I love this tutorial and was able to successfully get it working with OH2. I could really use some help though. Iā€™m attempting to combine your code with the code provided by Matt Kaczynski from MK-SmartHouse.
My hope is to use one ESP8266 to monitor and control my garage door. I have both sketches working separately but meshing them together is where Iā€™m having problems. Here is the code and source Iā€™m trying to modify.
https://www.mksmarthouse.com/door-sensor

/*
 * Device Title: MK-DoorSensor
 * Device Description: MQTT Door Sensor
 * Device information: https://www.MK-SmartHouse.com/door-sensor                
 * Author: Matt Kaczynski
 */

#include <ESP8266WiFi.h>
#include <MQTTClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>

/* WIFI Settings */
// Name of wifi network
const char* ssid = "wifissid";

// Password to wifi network
const char* password = "wifpassword"; 

/* Web Updater Settings */
// Host Name of Device
const char* host = "MK-DoorSensor1";

// Path to access firmware update page (Not Neccessary to change)
const char* update_path = "/firmware";

// Username to access the web update page
const char* update_username = "admin";

// Password to access the web update page
const char* update_password = "Admin";

/* MQTT Settings */
// Topic which listens for commands
char* outTopic = "MK-SmartHouse/security/MK-DoorSensor1"; 

//MQTT Server IP Address
const char* server = "192.168.0.4";

//Unique device ID 
const char* mqttDeviceID = "MK-SmartHouseDevice1"; 


//the time when the sensor outputs a low impulse
long unsigned int lowIn;         

//the amount of milliseconds the sensor has to be low 
//before we assume all detection has stopped
long unsigned int pause = 100;  

//sensor variables
boolean lockLow = true;
boolean takeLowTime;  

//the digital pin connected to the door sensor's output
int sensorPin = 13;  

//webserver 
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

//MQTT 
WiFiClient net;
MQTTClient client;

//Time Variable
unsigned long lastMillis = 0;

//Connect to WiFI and MQTT
void connect();

//Setup pins, wifi, webserver and MQTT
void setup() 
{
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, LOW);
  
  WiFi.mode(WIFI_STA);
  
  WiFi.begin(ssid, password);
  client.begin(server, net);

  connect();

  MDNS.begin(host);

  httpUpdater.setup(&httpServer, update_path, update_username, update_password);
  httpServer.begin();

  MDNS.addService("http", "tcp", 80);
}

//Connect to wifi and MQTT
void connect() 
{
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(1000);
  }

  while (!client.connect(mqttDeviceID)) 
  {
    delay(1000);
  }
}

void loop() 
{
  // MQTT Loop
  client.loop();
  delay(10);

  // Make sure device is connected
  if(!client.connected()) 
  {
    connect();
  }

  httpServer.handleClient();

  //Sensor Detection
  
  if(digitalRead(sensorPin) == HIGH)
  {
    if(lockLow)
    {  
      //makes sure we wait for a transition to LOW before any further output is made:
      lockLow = false;            
      client.publish(outTopic, "OPEN");  
      delay(50);
    }         
    takeLowTime = true;
  }

  if(digitalRead(sensorPin) == LOW)
  {       
    if(takeLowTime)
    {
      lowIn = millis();          //save the time of the transition from high to LOW
      takeLowTime = false;       //make sure this is only done at the start of a LOW phase
    }
    //if the sensor is low for more than the given pause, 
    //we assume that no more detection is going to happen
    if(!lockLow && millis() - lowIn > pause)
    {  
      //makes sure this block of code is only executed again after 
      //a new detection sequence has been detected
      lockLow = true;                        
      client.publish(outTopic, "CLOSED");
      delay(50);
    }
  }

}

void messageReceived(String topic, String payload, char * bytes, unsigned int length) 
{
  //This sensor does not recieve anything from MQTT Server so this is blank
}

I donā€™t mind using your code and just modify it to keep an eye on the door sensor I just found this code above first.
Any help or advise would be greatly appreciated.

You could try to add some code in the last section (messageReceived)
In my example, I monitor the topic from the ESP side but I donā€™t publish anything to the Broker from the ESP
In your example, the reverse is true

So: You want an ESP code that does both (publishes the state as well as subscribes to a topic) basicallyā€¦ correct?