diff --git a/include/Configuration.h b/include/Configuration.h index eb3f8fe..e723808 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -3,7 +3,7 @@ #include #define CONFIG_FILENAME "/config.bin" -#define CONFIG_VERSION 0x00010500 // 0.1.5 // make sure to clean all after change +#define CONFIG_VERSION 0x00010600 // 0.1.6 // make sure to clean all after change #define WIFI_MAX_SSID_STRLEN 31 #define WIFI_MAX_PASSWORD_STRLEN 31 @@ -42,6 +42,7 @@ struct CONFIG_T { char Mqtt_Username[MQTT_MAX_USERNAME_STRLEN + 1]; char Mqtt_Password[MQTT_MAX_PASSWORD_STRLEN + 1]; char Mqtt_Topic[MQTT_MAX_TOPIC_STRLEN + 1]; + bool Mqtt_Retain; }; class ConfigurationClass { diff --git a/include/MqttSettings.h b/include/MqttSettings.h index 05888a4..9e0d0e2 100644 --- a/include/MqttSettings.h +++ b/include/MqttSettings.h @@ -1,15 +1,32 @@ #pragma once +#include #include +#include +#include #include class MqttSettingsClass { public: MqttSettingsClass(); void init(); + void performReconnect(); + bool getConnected(); + void publish(String subtopic, String payload); private: - std::unique_ptr mqttClient; + void WiFiEvent(WiFiEvent_t event); + + void onMqttDisconnect(AsyncMqttClientDisconnectReason reason); + void onMqttConnect(bool sessionPresent); + + void performConnect(); + void performDisconnect(); + + AsyncMqttClient mqttClient; + String clientId; + String willTopic; + Ticker mqttReconnectTimer; }; extern MqttSettingsClass MqttSettings; \ No newline at end of file diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 9a7c41d..dbdeba2 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -28,6 +28,7 @@ void ConfigurationClass::init() strlcpy(config.Mqtt_Username, MQTT_USER, sizeof(config.Mqtt_Username)); strlcpy(config.Mqtt_Password, MQTT_PASSWORD, sizeof(config.Mqtt_Password)); strlcpy(config.Mqtt_Topic, MQTT_TOPIC, sizeof(config.Mqtt_Topic)); + config.Mqtt_Retain = true; } bool ConfigurationClass::write() @@ -76,6 +77,10 @@ void ConfigurationClass::migrate() strlcpy(config.Mqtt_Topic, MQTT_TOPIC, sizeof(config.Mqtt_Topic)); } + if (config.Cfg_Version < 0x00010600) { + config.Mqtt_Retain = true; + } + config.Cfg_Version = CONFIG_VERSION; write(); } diff --git a/src/MqttSettings.cpp b/src/MqttSettings.cpp index 604e04c..e774be0 100644 --- a/src/MqttSettings.cpp +++ b/src/MqttSettings.cpp @@ -1,12 +1,94 @@ #include "MqttSettings.h" +#include "Configuration.h" +#include "WiFiSettings.h" +#include +#include +#include MqttSettingsClass::MqttSettingsClass() : mqttClient() { } +void MqttSettingsClass::WiFiEvent(WiFiEvent_t event) +{ + switch (event) { + case SYSTEM_EVENT_STA_GOT_IP: + Serial.println(F("WiFi connected")); + performConnect(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + Serial.println(F("WiFi lost connection")); + mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi + break; + } +} + +void MqttSettingsClass::onMqttConnect(bool sessionPresent) +{ + Serial.println(F("Connected to MQTT.")); + mqttClient.publish(willTopic.c_str(), 2, Configuration.get().Mqtt_Retain, "online"); +} + +void MqttSettingsClass::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) +{ + Serial.println(F("Disconnected from MQTT.")); + + mqttReconnectTimer.once( + 2, +[](MqttSettingsClass* instance) { instance->performConnect(); }, this); +} + +void MqttSettingsClass::performConnect() +{ + if (WiFi.isConnected() && Configuration.get().Mqtt_Enabled) { + Serial.println("Connecting to MQTT..."); + CONFIG_T& config = Configuration.get(); + mqttClient.setServer(config.Mqtt_Hostname, config.Mqtt_Port); + mqttClient.setCredentials(config.Mqtt_Username, config.Mqtt_Password); + + willTopic = String(config.Mqtt_Topic) + "status"; + mqttClient.setWill(willTopic.c_str(), 2, Configuration.get().Mqtt_Retain, "offline"); + + clientId = WiFiSettings.getApName(); + mqttClient.setClientId(clientId.c_str()); + + mqttClient.connect(); + } +} + +void MqttSettingsClass::performDisconnect() +{ + mqttClient.publish(willTopic.c_str(), 2, Configuration.get().Mqtt_Retain, "offline"); + mqttClient.disconnect(); +} + +void MqttSettingsClass::performReconnect() +{ + performDisconnect(); + + mqttReconnectTimer.once( + 2, +[](MqttSettingsClass* instance) { instance->performConnect(); }, this); +} + +bool MqttSettingsClass::getConnected() +{ + return mqttClient.connected(); +} + +void MqttSettingsClass::publish(String subtopic, String payload) +{ + String topic = Configuration.get().Mqtt_Topic; + topic += subtopic; + mqttClient.publish(topic.c_str(), 2, Configuration.get().Mqtt_Retain, payload.c_str()); +} + void MqttSettingsClass::init() { + using namespace std::placeholders; + WiFi.onEvent(std::bind(&MqttSettingsClass::WiFiEvent, this, _1)); + + mqttClient.onConnect(std::bind(&MqttSettingsClass::onMqttConnect, this, _1)); + mqttClient.onDisconnect(std::bind(&MqttSettingsClass::onMqttDisconnect, this, _1)); } MqttSettingsClass MqttSettings; \ No newline at end of file