knolleary / pubsubclient

A client library for the Arduino Ethernet Shield that provides support for MQTT.
http://pubsubclient.knolleary.net/
MIT License
3.84k stars 1.47k forks source link

mqtt client stops the button from working correctly while disconnected #663

Open Zaborejszyn opened 5 years ago

Zaborejszyn commented 5 years ago

ok, i have my device with one relay and one button witch connects to mqtt. While im connected to broker button works just fine, but when im not connected to broker the device reads the state of the button and changes the state of the switch sometimes (sometimes i mean once (sometimes two) while trying to reconnect :/) but not as it should. Something is blocking the loop where is my btnCheck() funcion but i dont know where beacouse i usually runs code without delays and that wasnt a problem before i started exploring MQTT service. And thats how i know thats MQTT thing GOAL: I want my button to controll the relay properly even while client is disconnected heres the code:

/*
    Name:       sonoffS201.ino
    Created:    09.09.2019 17:43:08
    Author:     Zabor
*/

#include <ArduinoOTA.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>
#define led 13
#define btn 0
#define relay 12

const char* host = "****";
const char* ssid = "****";
const char* password = "****";
IPAddress ip(****);
uint16_t port = ****;

const char* OTApassword = "****";

char* relayTopic = "****";
char* statusTopic = "****";

WiFiClient espClient;
PubSubClient client(espClient);
Ticker ledTicker;

void callback(char* topic, byte* payload, unsigned int length) {
    String msgString = "";
    for (int i = 0; i < length; i++) {
        msgString += (char)payload[i];
    }
    if (msgString.equals("1")) {
        digitalWrite(relay, HIGH);
    }
    if (msgString.equals("0")) {
        digitalWrite(relay, LOW);
    }
}

void ledTick() {
    digitalWrite(led, !digitalRead(led));
}

unsigned long btnTimeout = 0;
bool unpressed = true;
void btnCheck() {
    if (digitalRead(btn) == 0 && unpressed && millis() - btnTimeout >= 250) {
        if (digitalRead(relay) == HIGH) {
            digitalWrite(relay, LOW);
            client.publish(relayTopic, "0", true);
        } else {
            digitalWrite(relay, HIGH);
            client.publish(relayTopic, "1", true);
        }
        unpressed = false;
    }
    if (digitalRead(btn) == 1 && !unpressed) {
        btnTimeout = millis();
        unpressed = true;
    }
}

bool boot = false;
unsigned long lastReconnectAttempt = 0;
void clientHandle() {
    if (!client.connected()) {
        digitalWrite(led, HIGH);
        if (millis() - lastReconnectAttempt > 5000) {
            lastReconnectAttempt = millis();
            if (client.connect(host, statusTopic, 1, true, "disconnected")) {
                if (!boot) {
                    client.publish(statusTopic, "connected", true);
                    boot = true;
                } else client.publish(statusTopic, "reconnected", true);
                client.subscribe(relayTopic, 1);
            }
            if (client.connected()) {
                digitalWrite(led, LOW);
                lastReconnectAttempt = 0;
            }
        }
    } else {
        client.loop();
    }
}

void setup() {
    pinMode(led, OUTPUT);
    pinMode(relay, OUTPUT);
    pinMode(btn, INPUT);

    WiFi.hostname(host);
    WiFi.mode(WIFI_STA);

    ledTicker.attach(0.1, ledTick);

    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) { delay(500); };

    ledTicker.detach();
    digitalWrite(led, LOW);

    ArduinoOTA.onError([](ota_error_t error) { ESP.restart(); });
    ArduinoOTA.setHostname(host);
    ArduinoOTA.setPassword(OTApassword);
    ArduinoOTA.begin();

    client.setServer(ip, port);
    client.setCallback(callback);
}

void loop() {
    ArduinoOTA.handle();
    clientHandle();
    btnCheck();
}
brianrho commented 5 years ago

This is related to the PR in #640. Try using the mqtt_esp8266_nonblocking example as a guide, and make sure to reduce the client connection timeout from the default 5 seconds with espClient.setTimeout()