0

I'm trying to create a Garage door sensor / motor controller and connect it to my instance of Home Assistant. My broker is the local mosquito on the Raspberry pi.

The device is a Wemos D1 mini with a relay connected to D1 pin and an analog input to determine the current state of the door.

The problem is that the device keeps coming up as unavailable as soon as the MQTT connection is made to the broker. I can't see anything in the logs to suggest a problem. On top of that I can still successfully publish to and from the device while it's offline.

This is the specific code I'm using for the LWT / availability topic.

String clientId = "GarageDoorSensor-" + String(random(0xffff), HEX);
if (client.connect(clientId.c_str(), mqtt_user, mqtt_password, availabilityTopic, 0, true, payloadNotAvailable)) {
  Serial.println("connected");
  client.publish(availabilityTopic, payloadAvailable, true);  
          
  client.subscribe(commandTopic);
          
  Serial.println("Subscribed to: ");
  Serial.println(commandTopic);
  Serial.println(availabilityTopic);
   
  getAndSendDoorStatus();
}

Here is the full code:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "My_Helper.h"

WiFiClient espClient;
PubSubClient client(espClient);

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  //Set WiFi mode so we don't create an access point.
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  Serial.println(WiFi.mode(WIFI_STA));
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  randomSeed(micros());
  
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    String clientId = "GarageDoorSensor-" + String(random(0xffff), HEX);
    if (client.connect(clientId.c_str(), mqtt_user, mqtt_password, availabilityTopic, 0, true, payloadNotAvailable)) {
      Serial.println("connected");
      client.publish(availabilityTopic, payloadAvailable, true);  
      
      client.subscribe(commandTopic);
      
      Serial.println("Subscribed to: ");
      Serial.println(commandTopic);
      Serial.println(availabilityTopic);

      getAndSendDoorStatus();
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


void setup() {

  pinMode(relaySwitch, OUTPUT);

  pinMode(doorInput, INPUT);
  
  Serial.begin(115200);
  // put your setup code here, to run once:
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  while (!client.connected()) {
    reconnect();
  }

  StaticJsonDocument<1024> mqttConfig;
  mqttConfig["name"] = mqttDeviceName;
  mqttConfig["device_class"] = mqttDeviceClass; 
  mqttConfig["state_topic"] = stateTopic;
  mqttConfig["command_topic"] = commandTopic; 
  mqttConfig["state_open"] = opened;
  mqttConfig["state_closed"] = closed;
  mqttConfig["state_closing"] = closing;
  mqttConfig["state_opening"] = opening;
  mqttConfig["payload_open"] = payloadOpen;
  mqttConfig["payload_close"] = payloadClose;
  mqttConfig["payload_stop"] = payloadStop;
  mqttConfig["optimistic"] = false;
  mqttConfig["retain"] = true;
  mqttConfig["availability_topic"] = availabilityTopic;
  mqttConfig["payload_available"] = payloadAvailable;
  mqttConfig["payload_not_available"] = payloadNotAvailable;
  char json[1024];
  serializeJsonPretty(mqttConfig, json);
  client.publish(configTopic, json, true); 
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  
  delay(1000);
  getAndSendDoorStatus();
}

void callback(char* topic, byte* message, unsigned int length) {

  String messageStr;
  
  for (int i = 0; i < length; i++) {
    messageStr += (char)message[i];
  }
  
  if (String(topic) == commandTopic) {
     Serial.print("Home Assistant Command: ");
     Serial.println(messageStr);

     if((messageStr == payloadOpen && doorStatus != opened) || messageStr == "forceOpen"){
        //open door is not already open or we are running a test.
        openTheDoor();
     }else if((messageStr == payloadClose && doorStatus != closed) || messageStr == "forceClose"){
        //close door is not already closed or we are running a test
        closeTheDoor(); 
     }else if(messageStr == payloadStop){

        //make sure we undo the relevant switches to stop the motion based on the previous status
        if(doorStatus == opened){
          closeTheDoor();
        }else if(doorStatus == closed){
          openTheDoor();
        }
    }

    prevDoorStatus = doorStatus;

    if(messageStr == "incrementOpenThreshold"){
      openThreshold = openThreshold + 1;
      String msg = "Set open threshold to: " + openThreshold;
      client.publish(stateTopic, msg.c_str(), true); 
    }

    if(messageStr == "decrementOpenThreshold"){
      openThreshold = openThreshold - 1;
      String msg = "Set open threshold to: " + openThreshold;
      client.publish(stateTopic, msg.c_str(), true); 
    }

  }
}

void getAndSendDoorStatus(){
  int doorState = analogRead(doorInput);

  //Door fully open?
  if(doorState < openThreshold){
    statusToOpen();
  } else {
    statusToClosed();
  }
 
  if(prevDoorStatus != doorStatus){
    Serial.print("Door ");
    Serial.println(doorStatus);
    client.publish(stateTopic, doorStatus, true); 

    delay(500);
    if(doorStatus == opened){
       openTheDoor(); 
    } else {
       closeTheDoor();
    }
    
    prevDoorStatus = doorStatus;
  } 
  
}

void closeTheDoor(){
  digitalWrite(relaySwitch, closeDoor);
}

void statusToClosed(){
  doorStatus = closed;
}

void openTheDoor(){
  digitalWrite(relaySwitch, openDoor);
}

void statusToOpen(){
  doorStatus = opened;  
}

MY_HELPER.H

#ifndef MY_HELPER_H
#define MY_HELPER_H

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* mqtt_user = "YOUR_MQTT_USER";
const char* mqtt_password = "YOUR_MQTT_PASSWORD";
const char* mqtt_server = "YOUR_MQTT_SERVER";
const char* stateTopic = "homeassistant/cover/garage/door/state";
const char* commandTopic = "homeassistant/cover/garage/door/set";
const char* configTopic = "homeassistant/cover/garage/door/config";
const char* availabilityTopic = "homeassistant/cover/garage/door/availability";
const char* doorStatus = "";
const char* prevDoorStatus = "";

const char* mqttDeviceName = "Garage Door";
const char* mqttDeviceClass = "garage";

int prevDoorState;
int openThreshold = 15;

const int relaySwitch = D1;

const int doorInput = A0;

const int conexT1 = LOW;
const int conexT2 = HIGH;

const int openDoor = conexT1;
const int closeDoor = conexT2;

//Statuses
const char* opened = "open";
const char* closed = "closed";
const char* closing = "closing";
const char* opening = "opening";
const char* stopped = "stopped";

//pay loads
const char* payloadOpen = "OPEN";
const char* payloadClose = "CLOSE";
const char* payloadStop = "STOP";
const char* payloadAvailable = "online";
const char* payloadNotAvailable = "offline";

#endif
2

First, if you just have 1 garage door opener, why are you generating a random client id every time? using a fixed clientID would be the right thing to do here.

The only time a random clientID makes any sense is when you are using a transient client e.g. a webpage using MQTT over Websockets.

Second, you appear to be double looping your reconnect function. Once in setup() and in loop() then again in reconnect(). You should only need to loop once on the condition client.connected() probably in the reconnect() function.

Turn on verbose logging on the broker and look to see what broker thinks is happening when it publishes the LWT.

Also following along with mosquitto_sub will be useful at the same time.

| improve this answer | |
  • Thanks for your suggestions. – jampez77 Sep 28 at 17:15
  • FYI I'm only using a random string here as it was taken from another project. Will update that and look at your other suggestions when I get time tomorrow. Thanks again – jampez77 Sep 28 at 17:16
  • It was the client ID. Started working as soon as I updated it. :). Also followed your advice about the reconnect loops. Thanks man! – jampez77 Sep 29 at 10:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.