I wanted to create a Mqtt switch to control an external Motor with Openhab "switch: Item. However I always wanted a Manually Override switch that mechanically alter the switch position. This seems overly simple until you get into te details who becomes the master and how to get a feedback from the external switch.
Here is How I Implemented the switch in OH2:
in the Items file:
Switch Relay7 "Solid State Relay" {mqtt=">[broker:/test/relay:command:ON:c],>[broker:/test/relay:command:OFF:d]"}
in the Sitemap file:
Frame label="SWITCHES"
{
Switch item=Modes label="Lights Mode" icon="pressure" mappings=[0="MANUAL", 1="AUTO"]
Switch item=Relay7 visibility=[Modes==0]
Text item=Motor_switch
Selection item=Light_Time icon="heating" visibility=[Modes==1] mappings=[0 = "Off",15 = "Once- 9AM",30 = "Twice 9AM, 12PM",45 = "Thrice 9AM, 12PM, 6PM"]
}
in the Rules:
rule "Motor Switch Notification"
when
Item Motor_switch received update
then
if(Motor_switch.state== OPEN){
Relay7.postUpdate(ON)
sendBroadcastNotification("MOTOR SWITCH CHANGED OUTSIIDE--OPEN!!")
}
else if (Motor_switch.state== CLOSED){
Relay7.postUpdate(OFF)
sendBroadcastNotification("MOTOR SWITCH CHANGED OUTSIIDE--CLOSED!!")
}
end
in the MQTT node I connected one of the external Switch to a GPIO & and used that GPIO as an input Contact.
The tricky part is saving the states of the Relay & Switch when there is a power failure and restart of either the Node or the OH2 server. In my case a RPi.
Check out the Mqtt code below (Pardon me this is not fully cleaned as far as the comments go) This is meant for an ArmTronix SSR board:
/*
Revision: 21/01/2018 with External switch working as Two Switch
It connects to an MQTT server then:
- on 0 switches off relay
- on 1 switches on relay
- on 2 switches the state of the relay
- sends 0 on off relay
- sends 1 on on relay
It will reconnect to the server if the connection is lost using a blocking
reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
achieve the same result without blocking the main loop.
The current state is stored in EEPROM and restored on bootup
Copied from ARM TRONIX SITE
#define OUTPIN 13 //output pin
#define INPIN 0 // input pin (push button)
#define OUTLED 12
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Bounce2.h>
#include <EEPROM.h>
#include <sstream>
#include <string.h>
#include <base64.h>
//#define TRIGGER 5
//#define ECHO 4
const char* ssid = "**********";
const char* password = "**********";
const char* mqtt_server = "192.168.1.116";
WiFiClient espClientSSR;
PubSubClient client(espClientSSR);
long lastMsg = 0;
long freq=0;
char msg[50];
int value = 0;
const char* outTopic = "/test/status";
const char* inTopic = "/test/relay";
//const char* inTopic = "/test/poll";
int relay_pin_1 = 16;
int relay_pin_2 = 5;
int relay_pin_3 = 4;
int relay_pin_4 = 0;
int relay_pin_5 = 2;
int relay_pin_6 = 12;
int relay_pin_7 = 13;
int relay_pin_8 = 14;
//int button_pin = 0;
bool relayState = LOW;
bool SwitchState_before = HIGH;
bool SwitchState_after = HIGH;
// Instantiate a Bounce object :
Bounce debouncer = Bounce();
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
extButton();
for(int i = 0; i<500; i++){
extButton();
delay(1);
}
Serial.print(".");
}
//digitalWrite(13, LOW);
//delay(500);
//digitalWrite(13, HIGH);
//delay(500);
digitalWrite(13, LOW);
delay(500);
//digitalWrite(13, HIGH);
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
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 the LED if an 1 was received as first character
// uncomment for just 1 relay 0
if ((char)payload[0] == '8') {
digitalWrite(relay_pin_5, LOW); // Turn the LED OFF (Note that HIGH is the voltage level
Serial.println("relay_pin_5 -> LOW RELAY ON");
client.publish(outTopic, "ON");
// relayState = LOW;
//EEPROM.write(0, relayState); // Write state to EEPROM
//EEPROM.commit();
} else if ((char)payload[0] == '9') {
digitalWrite(relay_pin_5, HIGH); // Turn the LED OFF (Note that LOW is the voltage level
Serial.println("relay_pin_5 -> HIGH RELAY OFF");
// relayState = HIGH;
// EEPROM.write(0, relayState); // Write state to EEPROM
// EEPROM.commit();
} else if ((char)payload[0] == 'c') {
digitalWrite(relay_pin_7, HIGH); // Turn the LED ON by making the voltage HIGH
Serial.println("relay_pin_7 -> RELAY ON");
relayState = HIGH;
EEPROM.write(0, relayState); // Write state to EEPROM
EEPROM.commit();
} else if ((char)payload[0] == 'd') {
digitalWrite(relay_pin_7, LOW); // Turn the LED OFF (Note that LOW is the voltage level
Serial.println("relay_pin_7 -> RELAY OFF");
relayState = LOW;
EEPROM.write(0, relayState); // Write state to EEPROM
EEPROM.commit();
}
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266Client2")) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish(outTopic,"NULL");
// ... and resubscribe
client.subscribe(inTopic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
digitalWrite(relay_pin_7, LOW);
digitalWrite(relay_pin_5, LOW);
// Wait 5 seconds before retrying
for(int i = 0; i<5000; i++){
extButton();
delay(1);
}
}
}
}
void extButton() {
debouncer.update();
// Call code if Bounce fell (transition from HIGH to LOW) :
if ( debouncer.fell() ) {
Serial.println("Debouncer fell");
// Toggle relay state :
relayState = !relayState;
digitalWrite(relay_pin_7,relayState);
EEPROM.write(0, relayState); // Write state to EEPROM
if (relayState == 1){
client.publish(outTopic, "1");
}
else if (relayState == 0){
client.publish(outTopic, "0");
}
}
}
void setup() {
EEPROM.begin(512); // Begin eeprom to store on/off state
//pinMode(relay_pin_1, OUTPUT); // Initialize the relay pin as an output
//pinMode(relay_pin_2, OUTPUT); // Initialize the relay pin as an output
pinMode(relay_pin_3, INPUT); // Initialize the relay pin as an output
//pinMode(relay_pin_4, OUTPUT); // Initialize the relay pin as an output
pinMode(relay_pin_5, OUTPUT); // Initialize the relay pin as an output
//pinMode(relay_pin_6, OUTPUT); // Initialize the relay pin as an output
pinMode(relay_pin_7, OUTPUT); // Initialize the relay pin as an output
// pinMode(relay_pin_8, OUTPUT); // Initialize the relay pin as an output
// pinMode(button_pin, INPUT); // Initialize the relay pin as an output
// pinMode(13, OUTPUT);
// pinMode(TRIGGER, OUTPUT);
// pinMode(ECHO, INPUT);
relayState = EEPROM.read(0);
SwitchState_before=EEPROM.read(1);
// digitalWrite(relay_pin_1,relayState);
// digitalWrite(relay_pin_2,LOW);
// digitalWrite(relay_pin_3,relayState);
// digitalWrite(relay_pin_4,relayState);
digitalWrite(relay_pin_5,LOW);
// digitalWrite(relay_pin_6,relayState);
digitalWrite(relay_pin_7,LOW);
// digitalWrite(relay_pin_8,relayState);
// debouncer.attach(button_pin); // Use the bounce2 library to debounce the built in button
debouncer.interval(50); // Input must be low for 50 ms
digitalWrite(2, LOW); // Blink to indicate setup
delay(500);
digitalWrite(2, HIGH);
delay(500);
Serial.begin(115200);
setup_wifi(); // Connect to wifi
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
void loop() {
SwitchState_after=digitalRead(relay_pin_3);
delay(500);
// Serial.print("Switch state after");
// Serial.print(SwitchState_after);
if (!client.connected()) {
reconnect();
}
client.loop();
extButton();
if (SwitchState_after != SwitchState_before){
relayState=!relayState;
SwitchState_before=SwitchState_after;
EEPROM.write(1, SwitchState_before);
digitalWrite(relay_pin_7, relayState);
if(relayState==HIGH){
client.publish(outTopic, "OPEN");
}
else if(relayState==LOW){
client.publish(outTopic, "CLOSED");
}
EEPROM.write(0, relayState);
EEPROM.commit();
}
}