mirror of
https://gitee.com/beecue/fastbee.git
synced 2026-02-06 00:55:57 +08:00
添加依赖库
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#ifndef ESP32_HTTP_UPDATE_SERVER_H
|
||||
#define ESP32_HTTP_UPDATE_SERVER_H
|
||||
|
||||
/*
|
||||
Based on the HTTP update exemple of ESP32 core
|
||||
*/
|
||||
|
||||
#include <WebServer.h>
|
||||
#include <Update.h>
|
||||
|
||||
#define ESP32_WEB_UPDATE_HTML "<html><body><form method='POST' action='' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form></body></html>"
|
||||
#define ESP32_WEB_UPDATE_SUCCESS_RESPONSE "<META http-equiv=\"refresh\" content=\"10;URL=/\">Update Success! Rebooting...\n"
|
||||
|
||||
class ESP32HTTPUpdateServer
|
||||
{
|
||||
private:
|
||||
WebServer* _server;
|
||||
|
||||
String _username;
|
||||
String _password;
|
||||
bool _serialDebugging;
|
||||
|
||||
public:
|
||||
ESP32HTTPUpdateServer(bool serialDebugging = false)
|
||||
{
|
||||
_server = NULL;
|
||||
_username = "";
|
||||
_password = "";
|
||||
}
|
||||
|
||||
void setup(WebServer* server, const char* path = "/", const char* username = "", const char* password = "")
|
||||
{
|
||||
_server = server;
|
||||
_username = username;
|
||||
_password = password;
|
||||
|
||||
// Get of the index handling
|
||||
_server->on(path, HTTP_GET, [&]() {
|
||||
// Force authentication if a user and password are defined
|
||||
if (_username.length() > 0 && _password.length() > 0 && !_server->authenticate(_username.c_str(), _password.c_str()))
|
||||
return _server->requestAuthentication();
|
||||
|
||||
_server->sendHeader("Connection", "close");
|
||||
_server->send(200, "text/html", ESP32_WEB_UPDATE_HTML);
|
||||
});
|
||||
|
||||
// Post of the file handling
|
||||
_server->on(path, HTTP_POST, [&]() {
|
||||
_server->client().setNoDelay(true);
|
||||
_server->send_P(200, "text/html", (Update.hasError()) ? "FAIL" : ESP32_WEB_UPDATE_SUCCESS_RESPONSE);
|
||||
delay(100);
|
||||
_server->client().stop();
|
||||
ESP.restart();
|
||||
}, [&]() {
|
||||
HTTPUpload& upload = _server->upload();
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START)
|
||||
{
|
||||
// Check if we are authenticated
|
||||
if (!(_username.length() == 0 || _password.length() == 0 || _server->authenticate(_username.c_str(), _password.c_str())))
|
||||
{
|
||||
if (_serialDebugging)
|
||||
Serial.printf("Unauthenticated Update\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Debugging message for upload start
|
||||
if (_serialDebugging)
|
||||
{
|
||||
Serial.setDebugOutput(true);
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
}
|
||||
|
||||
// Starting update
|
||||
bool error = Update.begin(UPDATE_SIZE_UNKNOWN);
|
||||
if (_serialDebugging && error)
|
||||
Update.printError(Serial);
|
||||
}
|
||||
else if (upload.status == UPLOAD_FILE_WRITE)
|
||||
{
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize && _serialDebugging)
|
||||
Update.printError(Serial);
|
||||
}
|
||||
else if (upload.status == UPLOAD_FILE_END)
|
||||
{
|
||||
if (Update.end(true) && _serialDebugging)
|
||||
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
||||
else if(_serialDebugging)
|
||||
Update.printError(Serial);
|
||||
|
||||
if(_serialDebugging)
|
||||
Serial.setDebugOutput(false);
|
||||
}
|
||||
else if(_serialDebugging)
|
||||
Serial.printf("Update Failed Unexpectedly (likely broken connection): status=%d\n", upload.status);
|
||||
});
|
||||
|
||||
_server->begin();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,678 @@
|
||||
#include "EspMQTTClient.h"
|
||||
|
||||
|
||||
// =============== Constructor / destructor ===================
|
||||
|
||||
// MQTT only (no wifi connection attempt)
|
||||
EspMQTTClient::EspMQTTClient(
|
||||
const char* mqttServerIp,
|
||||
const short mqttServerPort,
|
||||
const char* mqttClientName) :
|
||||
EspMQTTClient(NULL, NULL, mqttServerIp, NULL, NULL, mqttClientName, mqttServerPort)
|
||||
{
|
||||
}
|
||||
|
||||
EspMQTTClient::EspMQTTClient(
|
||||
const char* mqttServerIp,
|
||||
const short mqttServerPort,
|
||||
const char* mqttUsername,
|
||||
const char* mqttPassword,
|
||||
const char* mqttClientName) :
|
||||
EspMQTTClient(NULL, NULL, mqttServerIp, mqttUsername, mqttPassword, mqttClientName, mqttServerPort)
|
||||
{
|
||||
}
|
||||
|
||||
// Wifi and MQTT handling
|
||||
EspMQTTClient::EspMQTTClient(
|
||||
const char* wifiSsid,
|
||||
const char* wifiPassword,
|
||||
const char* mqttServerIp,
|
||||
const char* mqttClientName,
|
||||
const short mqttServerPort) :
|
||||
EspMQTTClient(wifiSsid, wifiPassword, mqttServerIp, NULL, NULL, mqttClientName, mqttServerPort)
|
||||
{
|
||||
}
|
||||
|
||||
EspMQTTClient::EspMQTTClient(
|
||||
const char* wifiSsid,
|
||||
const char* wifiPassword,
|
||||
const char* mqttServerIp,
|
||||
const char* mqttUsername,
|
||||
const char* mqttPassword,
|
||||
const char* mqttClientName,
|
||||
const short mqttServerPort) :
|
||||
_wifiSsid(wifiSsid),
|
||||
_wifiPassword(wifiPassword),
|
||||
_mqttServerIp(mqttServerIp),
|
||||
_mqttUsername(mqttUsername),
|
||||
_mqttPassword(mqttPassword),
|
||||
_mqttClientName(mqttClientName),
|
||||
_mqttServerPort(mqttServerPort),
|
||||
_mqttClient(mqttServerIp, mqttServerPort, _wifiClient)
|
||||
{
|
||||
// WiFi connection
|
||||
_handleWiFi = (wifiSsid != NULL);
|
||||
_wifiConnected = false;
|
||||
_connectingToWifi = false;
|
||||
_nextWifiConnectionAttemptMillis = 500;
|
||||
_lastWifiConnectiomAttemptMillis = 0;
|
||||
_wifiReconnectionAttemptDelay = 60 * 1000;
|
||||
|
||||
// MQTT client
|
||||
_mqttConnected = false;
|
||||
_nextMqttConnectionAttemptMillis = 0;
|
||||
_mqttReconnectionAttemptDelay = 15 * 1000; // 15 seconds of waiting between each mqtt reconnection attempts by default
|
||||
_mqttLastWillTopic = 0;
|
||||
_mqttLastWillMessage = 0;
|
||||
_mqttLastWillRetain = false;
|
||||
_mqttCleanSession = true;
|
||||
_mqttClient.setCallback([this](char* topic, byte* payload, unsigned int length) {this->mqttMessageReceivedCallback(topic, payload, length);});
|
||||
_failedMQTTConnectionAttemptCount = 0;
|
||||
|
||||
// Web updater
|
||||
_updateServerAddress = NULL;
|
||||
_httpServer = NULL;
|
||||
_httpUpdater = NULL;
|
||||
|
||||
// other
|
||||
_enableSerialLogs = false;
|
||||
_drasticResetOnConnectionFailures = false;
|
||||
_connectionEstablishedCallback = onConnectionEstablished;
|
||||
_connectionEstablishedCount = 0;
|
||||
}
|
||||
|
||||
EspMQTTClient::~EspMQTTClient()
|
||||
{
|
||||
if (_httpServer != NULL)
|
||||
delete _httpServer;
|
||||
if (_httpUpdater != NULL)
|
||||
delete _httpUpdater;
|
||||
}
|
||||
|
||||
|
||||
// =============== Configuration functions, most of them must be called before the first loop() call ==============
|
||||
|
||||
void EspMQTTClient::enableDebuggingMessages(const bool enabled)
|
||||
{
|
||||
_enableSerialLogs = enabled;
|
||||
}
|
||||
|
||||
void EspMQTTClient::enableHTTPWebUpdater(const char* username, const char* password, const char* address)
|
||||
{
|
||||
if (_httpServer == NULL)
|
||||
{
|
||||
_httpServer = new WebServer(80);
|
||||
_httpUpdater = new ESPHTTPUpdateServer(_enableSerialLogs);
|
||||
_updateServerUsername = (char*)username;
|
||||
_updateServerPassword = (char*)password;
|
||||
_updateServerAddress = (char*)address;
|
||||
}
|
||||
else if (_enableSerialLogs)
|
||||
Serial.print("SYS! You can't call enableHTTPWebUpdater() more than once !\n");
|
||||
}
|
||||
|
||||
void EspMQTTClient::enableHTTPWebUpdater(const char* address)
|
||||
{
|
||||
if(_mqttUsername == NULL || _mqttPassword == NULL)
|
||||
enableHTTPWebUpdater("", "", address);
|
||||
else
|
||||
enableHTTPWebUpdater(_mqttUsername, _mqttPassword, address);
|
||||
}
|
||||
|
||||
void EspMQTTClient::enableMQTTPersistence()
|
||||
{
|
||||
_mqttCleanSession = false;
|
||||
}
|
||||
|
||||
void EspMQTTClient::enableLastWillMessage(const char* topic, const char* message, const bool retain)
|
||||
{
|
||||
_mqttLastWillTopic = (char*)topic;
|
||||
_mqttLastWillMessage = (char*)message;
|
||||
_mqttLastWillRetain = retain;
|
||||
}
|
||||
|
||||
|
||||
// =============== Main loop / connection state handling =================
|
||||
|
||||
void EspMQTTClient::loop()
|
||||
{
|
||||
// WIFI handling
|
||||
bool wifiStateChanged = handleWiFi();
|
||||
|
||||
// If there is a change in the wifi connection state, don't handle the mqtt connection state right away.
|
||||
// We will wait at least one lopp() call. This prevent the library from doing too much thing in the same loop() call.
|
||||
if(wifiStateChanged)
|
||||
return;
|
||||
|
||||
// MQTT Handling
|
||||
bool mqttStateChanged = handleMQTT();
|
||||
if(mqttStateChanged)
|
||||
return;
|
||||
|
||||
// Procewss the delayed execution commands
|
||||
processDelayedExecutionRequests();
|
||||
}
|
||||
|
||||
bool EspMQTTClient::handleWiFi()
|
||||
{
|
||||
// When it's the first call, reset the wifi radio and schedule the wifi connection
|
||||
static bool firstLoopCall = true;
|
||||
if(_handleWiFi && firstLoopCall)
|
||||
{
|
||||
WiFi.disconnect(true);
|
||||
_nextWifiConnectionAttemptMillis = millis() + 500;
|
||||
firstLoopCall = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the current connextion status
|
||||
bool isWifiConnected = (WiFi.status() == WL_CONNECTED);
|
||||
|
||||
|
||||
/***** Detect ans handle the current WiFi handling state *****/
|
||||
|
||||
// Connection established
|
||||
if (isWifiConnected && !_wifiConnected)
|
||||
{
|
||||
onWiFiConnectionEstablished();
|
||||
_connectingToWifi = false;
|
||||
|
||||
// At least 500 miliseconds of waiting before an mqtt connection attempt.
|
||||
// Some people have reported instabilities when trying to connect to
|
||||
// the mqtt broker right after being connected to wifi.
|
||||
// This delay prevent these instabilities.
|
||||
_nextMqttConnectionAttemptMillis = millis() + 500;
|
||||
}
|
||||
|
||||
// Connection in progress
|
||||
else if(_connectingToWifi)
|
||||
{
|
||||
if(WiFi.status() == WL_CONNECT_FAILED || millis() - _lastWifiConnectiomAttemptMillis >= _wifiReconnectionAttemptDelay)
|
||||
{
|
||||
if(_enableSerialLogs)
|
||||
Serial.printf("WiFi! Connection attempt failed, delay expired. (%fs). \n", millis()/1000.0);
|
||||
|
||||
WiFi.disconnect(true);
|
||||
MDNS.end();
|
||||
|
||||
_nextWifiConnectionAttemptMillis = millis() + 500;
|
||||
_connectingToWifi = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Connection lost
|
||||
else if (!isWifiConnected && _wifiConnected)
|
||||
{
|
||||
onWiFiConnectionLost();
|
||||
|
||||
if(_handleWiFi)
|
||||
_nextWifiConnectionAttemptMillis = millis() + 500;
|
||||
}
|
||||
|
||||
// Connected since at least one loop() call
|
||||
else if (isWifiConnected && _wifiConnected)
|
||||
{
|
||||
// Web updater handling
|
||||
if (_httpServer != NULL)
|
||||
{
|
||||
_httpServer->handleClient();
|
||||
#ifdef ESP8266
|
||||
MDNS.update(); // We need to do this only for ESP8266
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnected since at least one loop() call
|
||||
// Then, if we handle the wifi reconnection process and the waiting delay has expired, we connect to wifi
|
||||
else if(_handleWiFi && _nextWifiConnectionAttemptMillis > 0 && millis() >= _nextWifiConnectionAttemptMillis)
|
||||
{
|
||||
connectToWifi();
|
||||
_nextWifiConnectionAttemptMillis = 0;
|
||||
_connectingToWifi = true;
|
||||
_lastWifiConnectiomAttemptMillis = millis();
|
||||
}
|
||||
|
||||
/**** Detect and return if there was a change in the WiFi state ****/
|
||||
|
||||
if (isWifiConnected != _wifiConnected)
|
||||
{
|
||||
_wifiConnected = isWifiConnected;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool EspMQTTClient::handleMQTT()
|
||||
{
|
||||
// PubSubClient main lopp() call
|
||||
_mqttClient.loop();
|
||||
|
||||
// Get the current connextion status
|
||||
bool isMqttConnected = (isWifiConnected() && _mqttClient.connected());
|
||||
|
||||
|
||||
/***** Detect ans handle the current MQTT handling state *****/
|
||||
|
||||
// Connection established
|
||||
if (isMqttConnected && !_mqttConnected)
|
||||
{
|
||||
_mqttConnected = true;
|
||||
onMQTTConnectionEstablished();
|
||||
}
|
||||
|
||||
// Connection lost
|
||||
else if (!isMqttConnected && _mqttConnected)
|
||||
{
|
||||
onMQTTConnectionLost();
|
||||
_nextMqttConnectionAttemptMillis = millis() + _mqttReconnectionAttemptDelay;
|
||||
}
|
||||
|
||||
// It's time to connect to the MQTT broker
|
||||
else if (isWifiConnected() && _nextMqttConnectionAttemptMillis > 0 && millis() >= _nextMqttConnectionAttemptMillis)
|
||||
{
|
||||
// Connect to MQTT broker
|
||||
if(connectToMqttBroker())
|
||||
{
|
||||
_failedMQTTConnectionAttemptCount = 0;
|
||||
_nextMqttConnectionAttemptMillis = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Connection failed, plan another connection attempt
|
||||
_nextMqttConnectionAttemptMillis = millis() + _mqttReconnectionAttemptDelay;
|
||||
_mqttClient.disconnect();
|
||||
_failedMQTTConnectionAttemptCount++;
|
||||
|
||||
if (_enableSerialLogs)
|
||||
Serial.printf("MQTT!: Failed MQTT connection count: %i \n", _failedMQTTConnectionAttemptCount);
|
||||
|
||||
// When there is too many failed attempt, sometimes it help to reset the WiFi connection or to restart the board.
|
||||
if(_failedMQTTConnectionAttemptCount == 8)
|
||||
{
|
||||
if (_enableSerialLogs)
|
||||
Serial.println("MQTT!: Can't connect to broker after too many attempt, resetting WiFi ...");
|
||||
|
||||
WiFi.disconnect(true);
|
||||
MDNS.end();
|
||||
_nextWifiConnectionAttemptMillis = millis() + 500;
|
||||
|
||||
if(!_drasticResetOnConnectionFailures)
|
||||
_failedMQTTConnectionAttemptCount = 0;
|
||||
}
|
||||
else if(_drasticResetOnConnectionFailures && _failedMQTTConnectionAttemptCount == 12) // Will reset after 12 failed attempt (3 minutes of retry)
|
||||
{
|
||||
if (_enableSerialLogs)
|
||||
Serial.println("MQTT!: Can't connect to broker after too many attempt, resetting board ...");
|
||||
|
||||
#ifdef ESP8266
|
||||
ESP.reset();
|
||||
#else
|
||||
ESP.restart();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**** Detect and return if there was a change in the MQTT state ****/
|
||||
|
||||
if(_mqttConnected != isMqttConnected)
|
||||
{
|
||||
_mqttConnected = isMqttConnected;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void EspMQTTClient::onWiFiConnectionEstablished()
|
||||
{
|
||||
if (_enableSerialLogs)
|
||||
Serial.printf("WiFi: Connected (%fs), ip : %s \n", millis()/1000.0, WiFi.localIP().toString().c_str());
|
||||
|
||||
// Config of web updater
|
||||
if (_httpServer != NULL)
|
||||
{
|
||||
MDNS.begin(_mqttClientName);
|
||||
_httpUpdater->setup(_httpServer, _updateServerAddress, _updateServerUsername, _updateServerPassword);
|
||||
_httpServer->begin();
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
|
||||
if (_enableSerialLogs)
|
||||
Serial.printf("WEB: Updater ready, open http://%s.local in your browser and login with username '%s' and password '%s'.\n", _mqttClientName, _updateServerUsername, _updateServerPassword);
|
||||
}
|
||||
}
|
||||
|
||||
void EspMQTTClient::onWiFiConnectionLost()
|
||||
{
|
||||
if (_enableSerialLogs)
|
||||
Serial.printf("WiFi! Lost connection (%fs). \n", millis()/1000.0);
|
||||
|
||||
// If we handle wifi, we force disconnection to clear the last connection
|
||||
if (_handleWiFi)
|
||||
{
|
||||
WiFi.disconnect(true);
|
||||
MDNS.end();
|
||||
}
|
||||
}
|
||||
|
||||
void EspMQTTClient::onMQTTConnectionEstablished()
|
||||
{
|
||||
_connectionEstablishedCount++;
|
||||
_connectionEstablishedCallback();
|
||||
}
|
||||
|
||||
void EspMQTTClient::onMQTTConnectionLost()
|
||||
{
|
||||
if (_enableSerialLogs)
|
||||
{
|
||||
Serial.printf("MQTT! Lost connection (%fs). \n", millis()/1000.0);
|
||||
Serial.printf("MQTT: Retrying to connect in %i seconds. \n", _mqttReconnectionAttemptDelay / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============== Public functions for interaction with thus lib =================
|
||||
|
||||
|
||||
bool EspMQTTClient::setMaxPacketSize(const uint16_t size)
|
||||
{
|
||||
|
||||
bool success = _mqttClient.setBufferSize(size);
|
||||
|
||||
if(!success && _enableSerialLogs)
|
||||
Serial.println("MQTT! failed to set the max packet size.");
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EspMQTTClient::publish(const String &topic, const String &payload, bool retain)
|
||||
{
|
||||
// Do not try to publish if MQTT is not connected.
|
||||
if(!isConnected())
|
||||
{
|
||||
if (_enableSerialLogs)
|
||||
Serial.println("MQTT! Trying to publish when disconnected, skipping.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = _mqttClient.publish(topic.c_str(), payload.c_str(), retain);
|
||||
|
||||
if (_enableSerialLogs)
|
||||
{
|
||||
if(success)
|
||||
Serial.printf("MQTT << [%s] %s\n", topic.c_str(), payload.c_str());
|
||||
else
|
||||
Serial.println("MQTT! publish failed, is the message too long ? (see setMaxPacketSize())"); // This can occurs if the message is too long according to the maximum defined in PubsubClient.h
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EspMQTTClient::subscribe(const String &topic, MessageReceivedCallback messageReceivedCallback, uint8_t qos)
|
||||
{
|
||||
// Do not try to subscribe if MQTT is not connected.
|
||||
if(!isConnected())
|
||||
{
|
||||
if (_enableSerialLogs)
|
||||
Serial.println("MQTT! Trying to subscribe when disconnected, skipping.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = _mqttClient.subscribe(topic.c_str(), qos);
|
||||
|
||||
if(success)
|
||||
{
|
||||
// Add the record to the subscription list only if it does not exists.
|
||||
bool found = false;
|
||||
for (byte i = 0; i < _topicSubscriptionList.size() && !found; i++)
|
||||
found = _topicSubscriptionList[i].topic.equals(topic);
|
||||
|
||||
if(!found)
|
||||
_topicSubscriptionList.push_back({ topic, messageReceivedCallback, NULL });
|
||||
}
|
||||
|
||||
if (_enableSerialLogs)
|
||||
{
|
||||
if(success)
|
||||
Serial.printf("MQTT: Subscribed to [%s]\n", topic.c_str());
|
||||
else
|
||||
Serial.println("MQTT! subscribe failed");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool EspMQTTClient::subscribe(const String &topic, MessageReceivedCallbackWithTopic messageReceivedCallback, uint8_t qos)
|
||||
{
|
||||
if(subscribe(topic, (MessageReceivedCallback)NULL, qos))
|
||||
{
|
||||
_topicSubscriptionList[_topicSubscriptionList.size()-1].callbackWithTopic = messageReceivedCallback;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EspMQTTClient::unsubscribe(const String &topic)
|
||||
{
|
||||
// Do not try to unsubscribe if MQTT is not connected.
|
||||
if(!isConnected())
|
||||
{
|
||||
if (_enableSerialLogs)
|
||||
Serial.println("MQTT! Trying to unsubscribe when disconnected, skipping.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _topicSubscriptionList.size(); i++)
|
||||
{
|
||||
if (_topicSubscriptionList[i].topic.equals(topic))
|
||||
{
|
||||
if(_mqttClient.unsubscribe(topic.c_str()))
|
||||
{
|
||||
_topicSubscriptionList.erase(_topicSubscriptionList.begin() + i);
|
||||
i--;
|
||||
|
||||
if(_enableSerialLogs)
|
||||
Serial.printf("MQTT: Unsubscribed from %s\n", topic.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_enableSerialLogs)
|
||||
Serial.println("MQTT! unsubscribe failed");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EspMQTTClient::setKeepAlive(uint16_t keepAliveSeconds)
|
||||
{
|
||||
_mqttClient.setKeepAlive(keepAliveSeconds);
|
||||
}
|
||||
|
||||
void EspMQTTClient::executeDelayed(const unsigned long delay, DelayedExecutionCallback callback)
|
||||
{
|
||||
DelayedExecutionRecord delayedExecutionRecord;
|
||||
delayedExecutionRecord.targetMillis = millis() + delay;
|
||||
delayedExecutionRecord.callback = callback;
|
||||
|
||||
_delayedExecutionList.push_back(delayedExecutionRecord);
|
||||
}
|
||||
|
||||
|
||||
// ================== Private functions ====================-
|
||||
|
||||
// Initiate a Wifi connection (non-blocking)
|
||||
void EspMQTTClient::connectToWifi()
|
||||
{
|
||||
WiFi.mode(WIFI_STA);
|
||||
#ifdef ESP32
|
||||
WiFi.setHostname(_mqttClientName);
|
||||
#else
|
||||
WiFi.hostname(_mqttClientName);
|
||||
#endif
|
||||
WiFi.begin(_wifiSsid, _wifiPassword);
|
||||
|
||||
if (_enableSerialLogs)
|
||||
Serial.printf("\nWiFi: Connecting to %s ... (%fs) \n", _wifiSsid, millis()/1000.0);
|
||||
}
|
||||
|
||||
// Try to connect to the MQTT broker and return True if the connection is successfull (blocking)
|
||||
bool EspMQTTClient::connectToMqttBroker()
|
||||
{
|
||||
if (_enableSerialLogs)
|
||||
Serial.printf("MQTT: Connecting to broker \"%s\" with client name \"%s\" ... (%fs)", _mqttServerIp, _mqttClientName, millis()/1000.0);
|
||||
|
||||
bool success = _mqttClient.connect(_mqttClientName, _mqttUsername, _mqttPassword, _mqttLastWillTopic, 0, _mqttLastWillRetain, _mqttLastWillMessage, _mqttCleanSession);
|
||||
|
||||
if (_enableSerialLogs)
|
||||
{
|
||||
if (success)
|
||||
Serial.printf(" - ok. (%fs) \n", millis()/1000.0);
|
||||
else
|
||||
{
|
||||
Serial.printf("unable to connect (%fs), reason: ", millis()/1000.0);
|
||||
|
||||
switch (_mqttClient.state())
|
||||
{
|
||||
case -4:
|
||||
Serial.println("MQTT_CONNECTION_TIMEOUT");
|
||||
break;
|
||||
case -3:
|
||||
Serial.println("MQTT_CONNECTION_LOST");
|
||||
break;
|
||||
case -2:
|
||||
Serial.println("MQTT_CONNECT_FAILED");
|
||||
break;
|
||||
case -1:
|
||||
Serial.println("MQTT_DISCONNECTED");
|
||||
break;
|
||||
case 1:
|
||||
Serial.println("MQTT_CONNECT_BAD_PROTOCOL");
|
||||
break;
|
||||
case 2:
|
||||
Serial.println("MQTT_CONNECT_BAD_CLIENT_ID");
|
||||
break;
|
||||
case 3:
|
||||
Serial.println("MQTT_CONNECT_UNAVAILABLE");
|
||||
break;
|
||||
case 4:
|
||||
Serial.println("MQTT_CONNECT_BAD_CREDENTIALS");
|
||||
break;
|
||||
case 5:
|
||||
Serial.println("MQTT_CONNECT_UNAUTHORIZED");
|
||||
break;
|
||||
}
|
||||
|
||||
Serial.printf("MQTT: Retrying to connect in %i seconds.\n", _mqttReconnectionAttemptDelay / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Delayed execution handling.
|
||||
// Check if there is delayed execution requests to process and execute them if needed.
|
||||
void EspMQTTClient::processDelayedExecutionRequests()
|
||||
{
|
||||
if (_delayedExecutionList.size() > 0)
|
||||
{
|
||||
unsigned long currentMillis = millis();
|
||||
|
||||
for(int i = 0 ; i < _delayedExecutionList.size() ; i++)
|
||||
{
|
||||
if (_delayedExecutionList[i].targetMillis <= currentMillis)
|
||||
{
|
||||
_delayedExecutionList[i].callback();
|
||||
_delayedExecutionList.erase(_delayedExecutionList.begin() + i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matching MQTT topics, handling the eventual presence of a single wildcard character
|
||||
*
|
||||
* @param topic1 is the topic may contain a wildcard
|
||||
* @param topic2 must not contain wildcards
|
||||
* @return true on MQTT topic match, false otherwise
|
||||
*/
|
||||
bool EspMQTTClient::mqttTopicMatch(const String &topic1, const String &topic2)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if((i = topic1.indexOf('#')) >= 0)
|
||||
{
|
||||
String t1a = topic1.substring(0, i);
|
||||
String t1b = topic1.substring(i+1);
|
||||
if((t1a.length() == 0 || topic2.startsWith(t1a)) &&
|
||||
(t1b.length() == 0 || topic2.endsWith(t1b)))
|
||||
return true;
|
||||
}
|
||||
else if((i = topic1.indexOf('+')) >= 0)
|
||||
{
|
||||
String t1a = topic1.substring(0, i);
|
||||
String t1b = topic1.substring(i+1);
|
||||
|
||||
if((t1a.length() == 0 || topic2.startsWith(t1a))&&
|
||||
(t1b.length() == 0 || topic2.endsWith(t1b)))
|
||||
{
|
||||
if(topic2.substring(t1a.length(), topic2.length()-t1b.length()).indexOf('/') == -1)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return topic1.equals(topic2);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EspMQTTClient::mqttMessageReceivedCallback(char* topic, byte* payload, unsigned int length)
|
||||
{
|
||||
// Convert the payload into a String
|
||||
// First, We ensure that we dont bypass the maximum size of the PubSubClient library buffer that originated the payload
|
||||
// This buffer has a maximum length of _mqttClient.getBufferSize() and the payload begin at "headerSize + topicLength + 1"
|
||||
unsigned int strTerminationPos;
|
||||
if (strlen(topic) + length + 9 >= _mqttClient.getBufferSize())
|
||||
{
|
||||
strTerminationPos = length - 1;
|
||||
|
||||
if (_enableSerialLogs)
|
||||
Serial.print("MQTT! Your message may be truncated, please set setMaxPacketSize() to a higher value.\n");
|
||||
}
|
||||
else
|
||||
strTerminationPos = length;
|
||||
|
||||
// Second, we add the string termination code at the end of the payload and we convert it to a String object
|
||||
payload[strTerminationPos] = '\0';
|
||||
String payloadStr((char*)payload);
|
||||
String topicStr(topic);
|
||||
|
||||
// Logging
|
||||
if (_enableSerialLogs)
|
||||
Serial.printf("MQTT >> [%s] %s\n", topic, payloadStr.c_str());
|
||||
|
||||
// Send the message to subscribers
|
||||
for (byte i = 0 ; i < _topicSubscriptionList.size() ; i++)
|
||||
{
|
||||
if (mqttTopicMatch(_topicSubscriptionList[i].topic, String(topic)))
|
||||
{
|
||||
if(_topicSubscriptionList[i].callback != NULL)
|
||||
_topicSubscriptionList[i].callback(payloadStr); // Call the callback
|
||||
if(_topicSubscriptionList[i].callbackWithTopic != NULL)
|
||||
_topicSubscriptionList[i].callbackWithTopic(topicStr, payloadStr); // Call the callback
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
#ifndef ESP_MQTT_CLIENT_H
|
||||
#define ESP_MQTT_CLIENT_H
|
||||
|
||||
#include <PubSubClient.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef ESP8266
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ESP8266HTTPUpdateServer.h>
|
||||
|
||||
#define WebServer ESP8266WebServer
|
||||
#define ESPmDNS ESP8266mDNS
|
||||
#define ESPHTTPUpdateServer ESP8266HTTPUpdateServer
|
||||
|
||||
#else // for ESP32
|
||||
|
||||
#include <WiFiClient.h>
|
||||
#include <WebServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include "ESP32HTTPUpdateServer.h"
|
||||
|
||||
#define ESPHTTPUpdateServer ESP32HTTPUpdateServer
|
||||
|
||||
#endif
|
||||
|
||||
void onConnectionEstablished(); // MUST be implemented in your sketch. Called once everythings is connected (Wifi, mqtt).
|
||||
|
||||
typedef std::function<void()> ConnectionEstablishedCallback;
|
||||
typedef std::function<void(const String &message)> MessageReceivedCallback;
|
||||
typedef std::function<void(const String &topicStr, const String &message)> MessageReceivedCallbackWithTopic;
|
||||
typedef std::function<void()> DelayedExecutionCallback;
|
||||
|
||||
class EspMQTTClient
|
||||
{
|
||||
private:
|
||||
// Wifi related
|
||||
bool _handleWiFi;
|
||||
bool _wifiConnected;
|
||||
bool _connectingToWifi;
|
||||
unsigned long _lastWifiConnectiomAttemptMillis;
|
||||
unsigned long _nextWifiConnectionAttemptMillis;
|
||||
unsigned int _wifiReconnectionAttemptDelay;
|
||||
const char* _wifiSsid;
|
||||
const char* _wifiPassword;
|
||||
WiFiClient _wifiClient;
|
||||
|
||||
// MQTT related
|
||||
bool _mqttConnected;
|
||||
unsigned long _nextMqttConnectionAttemptMillis;
|
||||
unsigned int _mqttReconnectionAttemptDelay;
|
||||
const char* _mqttServerIp;
|
||||
const char* _mqttUsername;
|
||||
const char* _mqttPassword;
|
||||
const char* _mqttClientName;
|
||||
const short _mqttServerPort;
|
||||
bool _mqttCleanSession;
|
||||
char* _mqttLastWillTopic;
|
||||
char* _mqttLastWillMessage;
|
||||
bool _mqttLastWillRetain;
|
||||
unsigned int _failedMQTTConnectionAttemptCount;
|
||||
|
||||
PubSubClient _mqttClient;
|
||||
|
||||
struct TopicSubscriptionRecord {
|
||||
String topic;
|
||||
MessageReceivedCallback callback;
|
||||
MessageReceivedCallbackWithTopic callbackWithTopic;
|
||||
};
|
||||
std::vector<TopicSubscriptionRecord> _topicSubscriptionList;
|
||||
|
||||
// HTTP update server related
|
||||
char* _updateServerAddress;
|
||||
char* _updateServerUsername;
|
||||
char* _updateServerPassword;
|
||||
WebServer* _httpServer;
|
||||
ESPHTTPUpdateServer* _httpUpdater;
|
||||
|
||||
// Delayed execution related
|
||||
struct DelayedExecutionRecord {
|
||||
unsigned long targetMillis;
|
||||
DelayedExecutionCallback callback;
|
||||
};
|
||||
std::vector<DelayedExecutionRecord> _delayedExecutionList;
|
||||
|
||||
// General behaviour related
|
||||
ConnectionEstablishedCallback _connectionEstablishedCallback;
|
||||
bool _enableSerialLogs;
|
||||
bool _drasticResetOnConnectionFailures;
|
||||
unsigned int _connectionEstablishedCount; // Incremented before each _connectionEstablishedCallback call
|
||||
|
||||
public:
|
||||
// Wifi + MQTT with no MQTT authentification
|
||||
EspMQTTClient(
|
||||
const char* wifiSsid,
|
||||
const char* wifiPassword,
|
||||
const char* mqttServerIp,
|
||||
const char* mqttClientName = "ESP8266",
|
||||
const short mqttServerPort = 1883);
|
||||
|
||||
// Wifi + MQTT with MQTT authentification
|
||||
EspMQTTClient(
|
||||
const char* wifiSsid,
|
||||
const char* wifiPassword,
|
||||
const char* mqttServerIp,
|
||||
const char* mqttUsername,
|
||||
const char* mqttPassword,
|
||||
const char* mqttClientName = "ESP8266",
|
||||
const short mqttServerPort = 1883);
|
||||
|
||||
// Only MQTT handling (no wifi), with MQTT authentification
|
||||
EspMQTTClient(
|
||||
const char* mqttServerIp,
|
||||
const short mqttServerPort,
|
||||
const char* mqttUsername,
|
||||
const char* mqttPassword,
|
||||
const char* mqttClientName = "ESP8266");
|
||||
|
||||
// Only MQTT handling without MQTT authentification
|
||||
EspMQTTClient(
|
||||
const char* mqttServerIp,
|
||||
const short mqttServerPort,
|
||||
const char* mqttClientName = "ESP8266");
|
||||
|
||||
~EspMQTTClient();
|
||||
|
||||
// Optional functionality
|
||||
void enableDebuggingMessages(const bool enabled = true); // Allow to display useful debugging messages. Can be set to false to disable them during program execution
|
||||
void enableHTTPWebUpdater(const char* username, const char* password, const char* address = "/"); // Activate the web updater, must be set before the first loop() call.
|
||||
void enableHTTPWebUpdater(const char* address = "/"); // Will set user and password equal to _mqttUsername and _mqttPassword
|
||||
void enableMQTTPersistence(); // Tell the broker to establish a persistent connection. Disabled by default. Must be called before the first loop() execution
|
||||
void enableLastWillMessage(const char* topic, const char* message, const bool retain = false); // Must be set before the first loop() call.
|
||||
void enableDrasticResetOnConnectionFailures() {_drasticResetOnConnectionFailures = true;} // Can be usefull in special cases where the ESP board hang and need resetting (#59)
|
||||
|
||||
// Main loop, to call at each sketch loop()
|
||||
void loop();
|
||||
|
||||
// MQTT related
|
||||
bool setMaxPacketSize(const uint16_t size); // Pubsubclient >= 2.8; override the default value of MQTT_MAX_PACKET_SIZE
|
||||
bool publish(const String &topic, const String &payload, bool retain = false);
|
||||
bool subscribe(const String &topic, MessageReceivedCallback messageReceivedCallback, uint8_t qos = 0);
|
||||
bool subscribe(const String &topic, MessageReceivedCallbackWithTopic messageReceivedCallback, uint8_t qos = 0);
|
||||
bool unsubscribe(const String &topic); //Unsubscribes from the topic, if it exists, and removes it from the CallbackList.
|
||||
void setKeepAlive(uint16_t keepAliveSeconds); // Change the keepalive interval (15 seconds by default)
|
||||
inline void setMqttClientName(const char* name) { _mqttClientName = name; }; // Allow to set client name manually (must be done in setup(), else it will not work.)
|
||||
|
||||
// Other
|
||||
void executeDelayed(const unsigned long delay, DelayedExecutionCallback callback);
|
||||
|
||||
inline bool isConnected() const { return isWifiConnected() && isMqttConnected(); }; // Return true if everything is connected
|
||||
inline bool isWifiConnected() const { return _wifiConnected; }; // Return true if wifi is connected
|
||||
inline bool isMqttConnected() const { return _mqttConnected; }; // Return true if mqtt is connected
|
||||
inline bool getConnectionEstablishedCount() const { return _connectionEstablishedCount; }; // Return the number of time onConnectionEstablished has been called since the beginning.
|
||||
|
||||
inline const char* getMqttClientName() { return _mqttClientName; };
|
||||
inline const char* getMqttServerIp() { return _mqttServerIp; };
|
||||
inline const short getMqttServerPort() { return _mqttServerPort; };
|
||||
|
||||
// Default to onConnectionEstablished, you might want to override this for special cases like two MQTT connections in the same sketch
|
||||
inline void setOnConnectionEstablishedCallback(ConnectionEstablishedCallback callback) { _connectionEstablishedCallback = callback; };
|
||||
|
||||
// Allow to set the minimum delay between each MQTT reconnection attempt. 15 seconds by default.
|
||||
inline void setMqttReconnectionAttemptDelay(const unsigned int milliseconds) { _mqttReconnectionAttemptDelay = milliseconds; };
|
||||
|
||||
// Allow to set the minimum delay between each WiFi reconnection attempt. 60 seconds by default.
|
||||
inline void setWifiReconnectionAttemptDelay(const unsigned int milliseconds) { _wifiReconnectionAttemptDelay = milliseconds; };
|
||||
|
||||
private:
|
||||
bool handleWiFi();
|
||||
bool handleMQTT();
|
||||
void onWiFiConnectionEstablished();
|
||||
void onWiFiConnectionLost();
|
||||
void onMQTTConnectionEstablished();
|
||||
void onMQTTConnectionLost();
|
||||
|
||||
void connectToWifi();
|
||||
bool connectToMqttBroker();
|
||||
void processDelayedExecutionRequests();
|
||||
bool mqttTopicMatch(const String &topic1, const String &topic2);
|
||||
void mqttMessageReceivedCallback(char* topic, byte* payload, unsigned int length);
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user