MQTT Cover

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;
}

I have just been thinking about a more reliable design.

At the moment, you have HA sending the same message for on and off, which doesn’t really tell your nodemcu whether the door should be open or closed.

I suggest

  1. Change the payload of the mqtt message to 1 for open and 0 for closed.
  2. If the nodemcu gets a 1 and the door is not open, it sends a pulse to make the door change state
  3. Similarly, if the nodemcu gets a 0 and the door is not closed, it sends a pulse.
  4. Whenever the nodemcu receives a command from HA, it publishes the state of the door, with the retain flag set.

This way the door should always end up in the state you want.

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.

home/garage/door3 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/door3 1
home/garage/status 0
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/door3 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/door3 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1
home/garage/status 1

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.

I will remove the delay(10) next time I try it.

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”);
}
}
}

Then in void loop() add garageMagSensor();

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.:slight_smile: 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;
}

Well my idea didn’t work so any advice would be welcome.

I’m afraid you’ll have to explain that again. I don’t understand the problem.

Ok maybe I didn’t explain it correctly but now what happens is if I only use home assistant to open/close the door it is fine and won’t misfire because of the flag. The problem I am having is that if I open it with home assistant but then close it with a garage door opener then the NodeMCU will fire once because the state is no longer the same and the flag gets reset when the Reed switch state changes. I don’t know why the message is still being fired even after the state of the reed switch is changed. I think it has to do with the retain flag because my program reconnects to mqtt broker all the time but not sure how to fix that

To clear out any topics that have been set with the retain flag, you need to send a null payload with the retain flag sent e.g.

$ mosquitto_pub -t "topic" -r -n

Even so, sorting out the re connections does seem like the next step. Can you tell from the serial output if the network is disconnecting, or just the MQTT connection?

I believe it is just the MQTT connection. There have been a few times the network has gone out but it reconnects and does not seem to be the issue here.

How often you see the status messages on your broker? Maybe you are flooding the mqtt broker with this unnecessary updates.

home/garage/door3 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0

etc…

How often you see the status messages on your broker? Maybe you are flooding the mqtt broker with this unnecessary updates.

home/garage/door3 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0
home/garage/status 0

etc…

The only time home/garage/status is posted to the broker now is after a state change in the relay. This is also the time when the flag is set to zero. I commented out all the unnecessary stuff that was posting to the serial port and it seems to be running better however I still have the issue of if i were to open or close with a manual remote that does not post to mqtt like a car remote then it will try to open/shut the garage door one more time.

Ok so I think I figured it out although it is not perfect and not exactly how I want it but I think I will be ok with this. Thanks everyone for all the help I really appreciate it. So how I have it set up is that every time the open or close button is pushed in MQTT a hold flag is triggered and to reset this flag the stop button has to be pushed first then the open/close button can be pushed. The stop button also updates the state of the reed switch. Also I added some code so if the open/close button is pushed but the stop button was not pushed then after the counter reaches 500 it will update the status of the reed switch. So below is my code:

#include <PubSubClient.h>
#include <ESP8266WiFi.h>

// Use these lines to setup MQTT Server and WiFi
#define MQTT_SERVER "XXXXXXXXXXXX"
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 = 1;
int counter = 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();

  if(counter == 500){
    bool doorState = digitalRead(doorPin);
      if (doorState == LOW) { //if door is open and the state closed, publish
        client.publish(doorTopic, DOOR_CLOSED); //send closed
        client.publish(relayTopic, "3");
        Serial.println("Low");
      }
      else if (doorState == HIGH) { //if door is closed and the state is open, publish
        client.publish(doorTopic, DOOR_OPEN); //send open
        client.publish(relayTopic, "3");
        Serial.println("High");
      }
      counter=0;
  }

  //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
//    Serial.println("Hold = 0");
  }
  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
    //    Serial.println("Hold = 0");
  }

  // Delay to allow ESP8266 WIFI functions to run
  delay(50);
  counter = counter + 1;
  Serial.println(counter);
}

// 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);
          hold = 1;
          // End Open Door
          Serial.println("");
          Serial.println("Open Called");
          Serial.println("Hold = 1");
        }
      }

      else if (payload[0] == '0') {
        if (doorState == 1) { //If Door is Open
          // Close Door
          digitalWrite(relayPin, LOW);
          delay(1000);
          digitalWrite(relayPin, HIGH);
          hold = 1;
          // End Close Door
          Serial.println("");
          Serial.println("Close Called");
          Serial.println("Hold = 1");
        }
      }
    }

    if (payload[0] == '5') {
      digitalWrite(relayPin, HIGH);
      hold = 0; // Set hold to 0 to allow MQTT
//      Serial.println("Hold = 0");
      //grab the current garage door state
      bool doorState = digitalRead(doorPin);
      if (doorState == LOW) { //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
//        Serial.println("Hold = 0");
      }
      else if (doorState == HIGH) { //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
//        Serial.println("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;
}