I am having trouble setting up a MQTT Cover for my garage. So I have it set up in the NodeMCU so that if it receives a message from home/garage/door with a message of 1 then it will trigger the output pin low for 1 sec then switch to high which turns the relay on then off. And I have a state message at home/garage/status that is hooked to a reed switch to signal open or closed. So I used the command line to send a message and this works however when I take it over to the HA side the garage will open then a second later close then open then close and keep repeating until I turn off the NodeMCU. I then had to switch the MQTT topic because this one became corrupt and I thought I figured it out but the same thing happened on the new topic. Any ideas on what I am doing wrong? Below is my cover.yaml file.
I think that your problem is the payload for open and close, it should be 1 for open and 0 to close. Try setting optimistic to false and check is that fix your problem.
I think from this that you are emulating a push button that
a) opens the door if it is closed
b) closes the door if it is open
If this is true, the main problem I can see is the retain flag. When this is true, if the nodemcu disconnects from the broker and reconnects due to a wifi problem, it will receive a new command and change the state of the door, which is probably not what you want.
I’m not sure what you mean about a corrupt topic, but I suspect it is to do with the retain flag as well. To clear it, you have to publish a null payload with the retain flag set to that topic.
This doesn’t help with your initial problem though, which I think is an interaction between the cover automation and the status of the switch. I haven’t used the MQTT cover so I can’t shed much light on that.
Can you get a log of the mqtt messages being sent? That might explain what is going on.
I tried setting this to 1 and 0 and it didn’t seem to help. If I push close it will stop but when I push open again it will go back to turn on then off then on and off.
Ok I see what you are saying I think about the retain flag and I think it does connect/disconnect often which is why I am seeing this problem. I am unsure how to do a log. I have tried a few other things but am still lost. I have found out if I set my code to publish to a topic and upload with a usb and leave the usb plugged in then the code runs fine however when I unplug the usb cable and plug it back in then I start seeing the clicking of the on/off. To make it stop clicking I have to change the topic to something else and upload to the NodeMCU again. So really the second I unplug the power I start to see the problems. This won’t do since if I lose power at some point my garage door will go haywire the second the power comes back on. Any advice?
I wasn’t able to get the log file before I went to work this morning however here is the arduino code I have on my NodeMCU.
#include <PubSubClient.h>
#include <ESP8266WiFi.h>
// Use these lines to setup MQTT Server and WiFi
#define MQTT_SERVER "XXX.XXX.XXX.XXX"
const char* ssid = "XXXXXX";
const char* password = "XXXXXX";
// Define GPIO on NodeMCU
const int doorPin = 13;
const int relayPin = 14;
// Define Variables
//int garageRelay = 0;
bool isOpen = false;
// Topics to Publish to
char* relayTopic = "home/garage/door";
// Topics to Subscribe to
char* doorTopic = "home/garage/status";
WiFiClient wifiClient;
PubSubClient client(MQTT_SERVER, 1883, callback, wifiClient);
void setup() {
pinMode(doorPin, INPUT);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH);
// Start the serial line for debugging
Serial.begin(115200); // com to computer
delay(100);
// Start wifi subsystem
WiFi.begin(ssid, password);
// Attempt to connect to the WIFI network and then connect to the MQTT server
reconnect();
// Wait for a bit before starting main loop
delay(2000);
}
void loop() {
// Reconnect if connection is lost
if (!client.connected() && WiFi.status() == 3) {reconnect();}
// Maintain MQTT connection
client.loop();
//grab the current garage door state
bool doorState = digitalRead(doorPin);
if(!doorState && !isOpen){ //if door is open and the state closed, publish
client.publish(doorTopic,"0"); //send closed
isOpen = true;
delay(500);
}
else if(doorState && isOpen){ //if door is closed and the state is open, publish
client.publish(doorTopic,"1"); //send open
isOpen = false;
delay(500);
}
// Delay to allow ESP8266 WIFI functions to run
delay(10);
}
// MQTT callback function -(Use only if topics are being subscribed to)
void callback(char* topic, byte* payload, unsigned int length){
// Convert topic to string to make it easier to work with
String topicStr = topic;
char byteToSend = 0;
// Handle relayTopic
if(topicStr.equals(relayTopic)){
if(payload[0] == '1'){
digitalWrite(relayPin, LOW);
delay(1000);
digitalWrite(relayPin, HIGH);
delay(500);
}
else if (payload[0] == '0'){
digitalWrite(relayPin, HIGH);
}
}
else {
delay(10);
}
}
void reconnect() {
//attempt to connect to the wifi if connection is lost
if(WiFi.status() != WL_CONNECTED){
//debug printing
Serial.print("Connecting to ");
Serial.println(ssid);
//loop while we wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
//print out some more debug once connected
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
//make sure we are connected to WIFI before attemping to reconnect to MQTT
if(WiFi.status() == WL_CONNECTED){
// Loop until we're reconnected to the MQTT server
while (!client.connected()) {
Serial.println("");
Serial.print("Attempting MQTT connection...");
// Generate client name based on MAC address and last 8 bits of microsecond counter
String clientName;
clientName += "esp8266-";
uint8_t mac[6];
WiFi.macAddress(mac);
clientName += macToStr(mac);
//if connected, subscribe to the topic(s) we want to be notified about
if (client.connect("(char*) clientName.c_str()", "XXXXXX", "XXXXXX")) {
Serial.println("");
Serial.print("\tMTQQ Connected");
// client.subscribe(doorTopic);
client.subscribe(relayTopic);
}
//otherwise print failed for debugging
else{Serial.println("\tFailed."); abort();}
}
}
}
// Generate unique name from MAC addr
String macToStr(const uint8_t* mac){
String result;
for (int i = 0; i < 6; ++i) {
result += String(mac[i], 16);
if (i < 5){
result += ':';
}
}
return result;
}
This is what oriolism suggested and I tried it and it appeared to be working for awhile but maybe I messed something up. Maybe I just don’t understand what is going on. If the door is closed then I push the open button it fires a message with a payload of 1 to open the door correct? Then if the door is open and I select the close button will it send a message of 0? This won’t do anything because the door only signals when a 1 is published to the topic correct? Maybe I am missing something.
Its easier to write than explain :slight_smile:
void loop() {
// Reconnect if connection is lost
if (!client.connected() && WiFi.status() == 3) {reconnect();}
// Maintain MQTT connection
client.loop();
// Delay to allow ESP8266 WIFI functions to run
delay(10);
}
// MQTT callback function -(Use only if topics are being subscribed to)
void callback(char* topic, byte* payload, unsigned int length){
// Convert topic to string to make it easier to work with
String topicStr = topic;
char byteToSend = 0;
char* DOOR_OPEN = "1";
char* DOOR_CLOSED = "0";
// Handle relayTopic
if(topicStr.equals(relayTopic)){
//grab the current garage door state
bool doorState = digitalRead(doorPin);
if(payload[0] == '1'){
if (doorstate == 0 ) { // Door closed
// Open door
digitalWrite(relayPin, LOW);
delay(1000);
digitalWrite(relayPin, HIGH);
delay(500);
}
client.publish(doorTopic, DOOR_OPEN); //send open
}
else if (payload[0] == '0'){
if (doorstate == 1 ) { // Door open
// Close door
digitalWrite(relayPin, LOW);
delay(1000);
digitalWrite(relayPin, HIGH);
delay(500);
}
client.publish(doorTopic, DOOR_CLOSED); //send open
}
}
}
This seems to work for the most part however it will click continuously until the door state is changed. So i don’t think it will be a problem opening because the sensor will trip quick however when the door is closing it will take much longer because the door has to shut before the sensor is tripped. I could probably do a delay but don’t think this is the best solution. Is there something that I am doing wrong. Below is the log file.
Here is what the new code looks like: #include <PubSubClient.h> #include <ESP8266WiFi.h>
// Use these lines to setup MQTT Server and WiFi
#define MQTT_SERVER "###.###.###.###"
const char* ssid = "######";
const char* password = "######";
// Define GPIO on NodeMCU
const int doorPin = 13;
const int relayPin = 14;
char* DOOR_OPEN = "1";
char* DOOR_CLOSED = "0";
// Define Variables
//int garageRelay = 0;
//bool isOpen = false;
// Topics to Publish to
char* relayTopic = "home/garage/door3";
// Topics to Subscribe to
char* doorTopic = "home/garage/status";
WiFiClient wifiClient;
PubSubClient client(MQTT_SERVER, 1883, callback, wifiClient);
void setup() {
pinMode(doorPin, INPUT);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH);
// Start the serial line for debugging
Serial.begin(115200); // com to computer
delay(100);
// Start wifi subsystem
WiFi.begin(ssid, password);
// Attempt to connect to the WIFI network and then connect to the MQTT server
reconnect();
// Get Initial Door State
bool doorState = digitalRead(doorPin);
if(doorState == 0) { //If Door is Closed
// Publish Door Closed to doorTopic
client.publish(doorTopic, DOOR_CLOSED); //send close
}
else if(doorState == 1) { //If Door is Open
// Publish Door Open to doorTopic
client.publish(doorTopic, DOOR_OPEN); //send close
}
// Wait for a bit before starting main loop
delay(2000);
}
void loop() {
// Reconnect if connection is lost
if (!client.connected() && WiFi.status() == 3) {reconnect();}
// Maintain MQTT connection
client.loop();
// //grab the current garage door state
// bool doorState = digitalRead(doorPin);
//
// if(!doorState && !isOpen){ //if door is open and the state closed, publish
// client.publish(doorTopic,"0"); //send closed
// isOpen = true;
// delay(500);
// }
// else if(doorState && isOpen){ //if door is closed and the state is open, publish
// client.publish(doorTopic,"1"); //send open
// isOpen = false;
// delay(500);
// }
// Delay to allow ESP8266 WIFI functions to run
delay(10);
}
// MQTT callback function -(Use only if topics are being subscribed to)
void callback(char* topic, byte* payload, unsigned int length){
// Convert topic to string to make it easier to work with
String topicStr = topic;
char byteToSend = 0;
char* DOOR_OPEN = "1";
char* DOOR_CLOSED = "0";
// Handle relayTopic
if(topicStr.equals(relayTopic)){
// Grab the current garage door state
bool doorState = digitalRead(doorPin);
if(payload[0] == '1'){
if(doorState == 0) { //If Door is Closed
// Open Door
digitalWrite(relayPin, LOW);
delay(1000);
digitalWrite(relayPin, HIGH);
delay(500);
// End Open Door
}
// Publish Door Open to doorTopic
client.publish(doorTopic,DOOR_OPEN); //send open
}
else if (payload[0] == '0'){
if(doorState == 1) { //If Door is Open
// Close Door
digitalWrite(relayPin, LOW);
delay(1000);
digitalWrite(relayPin, HIGH);
delay(500);
}
// Publish Door Closed to doorTopic
client.publish(doorTopic, DOOR_CLOSED); //send close
}
}
else {
delay(10);
}
}
void reconnect() {
//attempt to connect to the wifi if connection is lost
if(WiFi.status() != WL_CONNECTED){
//debug printing
Serial.print("Connecting to ");
Serial.println(ssid);
//loop while we wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
//print out some more debug once connected
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
//make sure we are connected to WIFI before attemping to reconnect to MQTT
if(WiFi.status() == WL_CONNECTED){
// Loop until we're reconnected to the MQTT server
while (!client.connected()) {
Serial.println("");
Serial.print("Attempting MQTT connection...");
// Generate client name based on MAC address and last 8 bits of microsecond counter
String clientName;
clientName += "esp8266-";
uint8_t mac[6];
WiFi.macAddress(mac);
clientName += macToStr(mac);
//if connected, subscribe to the topic(s) we want to be notified about
if (client.connect("(char*) clientName.c_str()", "######", "######")) {
Serial.println("");
Serial.print("\tMTQQ Connected");
// client.subscribe(doorTopic);
client.subscribe(relayTopic);
}
//otherwise print failed for debugging
else{Serial.println("\tFailed."); abort();}
}
}
}
// Generate unique name from MAC addr
String macToStr(const uint8_t* mac){
String result;
for (int i = 0; i < 6; ++i) {
result += String(mac[i], 16);
if (i < 5){
result += ':';
}
}
return result;
}
I’m not sure why so many status messages are being printed. There should only be one status message for each command, unless I misunderstand how often callback is being called. But if it works generally, that is a minor problem.
I also understand now that your sensor is a closed/not closed sensor rather than closed/open.
I think a flag variable to indicate that you have issued a closing command and are waiting for the door to close. That would prevent issuing another command until it had actually closed.
The flag can be cleared by checking if the doorPin in the main loop.
If you got an open command while the door is open, you have to find out if you can send a pulse to get it to open rather than close, or if you have to wait until it finishes.
p.s.
there is also an else { delay(10 } in the callback script which I don’t understand the reason for, and may cause problems if it receives other commands.
The only problem I see with using the flag or the delay is that if the sensor was tripped for something being in the way of the door closing then either the door would continue to close with it turning on/off after the delay or the door would be stuck open if a flag was used because I could not issue another command with the flag still not triggered.
You can change your code to only send and update to the mqtt server only when the garage door state changes. I use this:
bool gState = 0; //logical placeholder - current status of the garage
bool gPrevious = 1; //logical placeholder - previous status of the garage
// ========{ Garage Magnetic SW Monitoring Input }==================
void garageMagSensor ()
{
gState = digitalRead(13);
if (gState != gPrevious) { //only run if there is a status change from previous state
gPrevious = gState; //reinitialize
if (gState == LOW)
{
client.publish(“GarageDoor/status”, “CLOSE”);
Serial.print(“Garage Door is Closed”);
}
else
{
client.publish(“GarageDoor/status”, “OPEN”);
Serial.print(“Garage Door is Open”);
}
}
}
It seems like this is just becoming a total failure. I think my code is fine but I think it has to do with the cover component and the number of reed switches I have. When I push open button the reed switch will separate almost immediately so there is no problem however when I close the door it takes some time for the reed switch to become triggered. If the state has not changed by the time it runs through the callback then it will send the same message until the state changes. Does this make sense and would adding an additional reed switch help my issue by sending a signal right away when I push the close button?
PS: I have been thinking about this more. Does this mean if I opened or closed it with another device that was not MQTT that the arduino would try to correct this to make the correct state as what was chosen by the arduino previously. For example if I opened the door with the arduino but then closed it from my car remote. Then when the state changed from the reed switch changed would it try to open the door again because that is what I am seeing from my testing. My callback is getting called multiple times if the state is not correct.
Its just more complicated than you thought initially. Which is why many software projects end up late and over budget. Actually, it sounds like you are making good progress.
That is what I was trying to explain with the flag variable in my previous comment. A flag is different from a delay because it allows other commands to be received and acted upon while the door is closing.
The arduino (or NodeMCU which I assume is what you mean) should check the state of the relay to determine what the actual state of the door is before acting. If the door can be opened by other means, your sketch needs to include some code to send the new status via mqtt to HA (or whatever else is listening).
So what I did last night was create a new payload so that if it ran through the open or close command it would publish a payload of “5” instead of “0” or “1” from inside the main loop. Then for this payload it would update the pin status as either open or close. Which I believe is similar to a flag however when I performed this the code would run so if I pressed open the code will click the relay once but if the state does not change then the relay will signal again. But while doing this the payload of “5” was being sent because I saw it in the serial monitor so it should not be clicking the relay I think. Then once the state changes it will stop changing the relay. However if I change the state of the reed switch using something other than the NodeMCU then the relay will start firing again until the reed switch is back to what was called previously. Not sure why this is happening. I guess I will try to put something in the callback as a flag variable to try to prevent it from calling the relay after the first firing of the message.
Sorry for all the messages but this one is just frustrating me a little but I am getting there I hope.
Ok i think i almost have it. i just have one last issue. i threw a flag variable called hold in the callback so now when the relay runs once it wont run again until the reed switch is tripped which resets the flag. The one issue I have is that if I open or shut the garage door manually it will trigger the relay once but then wait for a state change again. i think i have an idea but I haven’t had time to try it yet i will give it a go tonight and let you know how it goes. Here is what the code looks like right now:
#include <PubSubClient.h>
#include <ESP8266WiFi.h>
// Use these lines to setup MQTT Server and WiFi
#define MQTT_SERVER "xxxxxxxx"
const char* ssid = "xxxxxx";
const char* password = "xxxxxx";
// Define GPIO on NodeMCU
const int doorPin = 13;
const int relayPin = 14;
char* DOOR_CLOSED = "0";
char* DOOR_OPEN = "1";
char* DOOR_WAIT = "5";
char* DOOR_CLEAR = "8";
// Define Variables
//int garageRelay = 0;
bool isOpen = false;
int hold = 0;
// Topics to Publish to
char* relayTopic = "home/garage/door";
// Topics to Subscribe to
char* doorTopic = "home/garage/status";
WiFiClient wifiClient;
PubSubClient client(MQTT_SERVER, 1883, callback, wifiClient);
void setup() {
pinMode(doorPin, INPUT);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH);
// Start the serial line for debugging
Serial.begin(115200); // com to computer
delay(100);
// Start wifi subsystem
WiFi.begin(ssid, password);
// Attempt to connect to the WIFI network and then connect to the MQTT server
reconnect();
// Get Initial Door State
bool doorState = digitalRead(doorPin);
if(doorState == 0) { //If Door is Closed
// Publish Door Closed to doorTopic
client.publish(doorTopic, DOOR_CLOSED); //send close
}
else if(doorState == 1) { //If Door is Open
// Publish Door Open to doorTopic
client.publish(doorTopic, DOOR_OPEN); //send close
}
// Wait for a bit before starting main loop
delay(2000);
}
void loop() {
// Reconnect if connection is lost
if (!client.connected() && WiFi.status() == 3) {reconnect();}
// Maintain MQTT connection
client.loop();
//grab the current garage door state
bool doorState = digitalRead(doorPin);
if(!doorState && !isOpen){ //if door is open and the state closed, publish
client.publish(doorTopic,DOOR_CLOSED); //send closed
isOpen = true;
hold=0; // Set hold to 0 to allow MQTT
delay(500);
}
else if(doorState && isOpen){ //if door is closed and the state is open, publish
client.publish(doorTopic,DOOR_OPEN); //send open
isOpen = false;
hold=0; // Set hold to 0 to allow MQTT
delay(500);
}
// Delay to allow ESP8266 WIFI functions to run
delay(50);
}
// MQTT callback function -(Use only if topics are being subscribed to)
void callback(char* topic, byte* payload, unsigned int length){
// Convert topic to string to make it easier to work with
String topicStr = topic;
char byteToSend = 0;
// Handle relayTopic
if(topicStr.equals(relayTopic)){
// Grab the current garage door state
bool doorState = digitalRead(doorPin);
if(hold == 0){
if(payload[0] == '1'){
if(doorState == 0) { //If Door is Closed
// Open Door
digitalWrite(relayPin, LOW);
delay(1000);
digitalWrite(relayPin, HIGH);
delay(1000);
hold = 1;
// End Open Door
Serial.println("");
Serial.println("Open Called");
}
// Publish Door Open to doorTopic
//client.publish(doorTopic, DOOR_OPEN); //send open
}
else if(payload[0] == '0'){
if(doorState == 1) { //If Door is Open
// Close Door
digitalWrite(relayPin, LOW);
delay(1000);
digitalWrite(relayPin, HIGH);
delay(1000);
hold = 1;
// End Close Door
Serial.println("");
Serial.println("Close Called");
}
// Publish Door Closed to doorTopic
//client.publish(doorTopic, DOOR_CLOSED); //send close
}
}
else if(hold == 1){
digitalWrite(relayPin, HIGH);
Serial.println("");
Serial.println("Nothing Called");
}
if(payload[0] == '5'){
digitalWrite(relayPin, HIGH);
hold=0;
}
}
}
void reconnect() {
//attempt to connect to the wifi if connection is lost
if(WiFi.status() != WL_CONNECTED){
//debug printing
Serial.print("Connecting to ");
Serial.println(ssid);
//loop while we wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
//print out some more debug once connected
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
//make sure we are connected to WIFI before attemping to reconnect to MQTT
if(WiFi.status() == WL_CONNECTED){
// Loop until we're reconnected to the MQTT server
while (!client.connected()) {
Serial.println("");
Serial.print("Attempting MQTT connection...");
// Generate client name based on MAC address and last 8 bits of microsecond counter
String clientName;
clientName += "esp8266-";
uint8_t mac[6];
WiFi.macAddress(mac);
clientName += macToStr(mac);
//if connected, subscribe to the topic(s) we want to be notified about
if (client.connect("(char*) clientName.c_str()", "xxxxxx", "xxxxxx")) {
Serial.println("");
Serial.print("\tMTQQ Connected");
// client.subscribe(doorTopic);
client.subscribe(relayTopic);
}
//otherwise print failed for debugging
else{Serial.println("\tFailed."); abort();}
}
}
}
// Generate unique name from MAC addr
String macToStr(const uint8_t* mac){
String result;
for (int i = 0; i < 6; ++i) {
result += String(mac[i], 16);
if (i < 5){
result += ':';
}
}
return result;
}