Skip to content

Commit

Permalink
Implement OTA feature (#92)
Browse files Browse the repository at this point in the history
Co-authored-by: hallard <hallard04@free.fr>
  • Loading branch information
EdJoPaTo and hallard authored Nov 20, 2021
1 parent 4fb4a37 commit 81d3f2f
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 38 deletions.
41 changes: 20 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
# MQTT and Wifi handling for ESP8266 and ESP32

This library is intended to encapsulate the handling of WiFi and MQTT connections of an ESP8266/ESP32.
You just need to provide your credentials and it will manage the following things:
You just need to provide your credentials and it will manage the following things:
- Connecting to a WiFi network.
- Connecting to a MQTT broker.
- Automatically detecting connection lost either from the WiFi client or the MQTT broker and it will retry a connection automatically.
- Subscribing/unsubscrubing to/from MQTT topics by a friendly callback system.
- Subscribing/unsubscribing to/from MQTT topics by a friendly callback system.
- Supports a single occurrence of a '+' or '#' wildcard in subscriptions
- Provide a callback handling to advise once everything is connected (Wifi and MQTT).
- Provide a function to enable printing of useful debug information related to MQTT and Wifi connections.
- Provide some other useful utilities for MQTT and Wifi management.
- Provide a function to enable an HTTP Update server secured by a password to allow remote update.
- Provide a function to enable OTA secured by a password to allow remote update.

## Dependency

The MQTT communication depends on the PubSubClient Library (https://github.com/knolleary/pubsubclient).
The MQTT communication depends on the [PubSubClient Library](https://github.com/knolleary/pubsubclient).

## Example

Expand Down Expand Up @@ -46,14 +47,14 @@ void loop() {
}
```

See "SimpleMQTTClient.ino" for the complete example.
See `SimpleMQTTClient.ino` for the complete example.


## Documentation

### Construction

For Wifi and MQTT connection handling (Recommended) :
For Wifi and MQTT connection handling (Recommended):
```c++
EspMQTTClient(
const char* wifiSsid,
Expand All @@ -65,7 +66,7 @@ For Wifi and MQTT connection handling (Recommended) :
const short mqttServerPort = 1883);
```
MQTT connection handling only :
MQTT connection handling only:
```c++
EspMQTTClient(
const char* mqttServerIp,
Expand All @@ -77,7 +78,7 @@ MQTT connection handling only :

### Functions

IMPORTANT : Must be called at each loop() of your sketch
IMPORTANT: Must be called at each loop() of your sketch
```c++
void loop();
```
Expand All @@ -94,12 +95,12 @@ Change the maximum packet size that can be sent over MQTT. The default is 128 by
bool setMaxPacketSize(const uint16_t size);
```

Change the keepalive interval (15 seconds by default)
Change the keep alive interval (15 seconds by default)
```c++
void setKeepAlive(uint16_t keepAliveSeconds);
```
Enable the display of usefull debugging messages that will output to serial.
Enable debugging messages that will output to serial.
```c++
void enableDebuggingMessages(const bool enabled = true);
```
Expand Down Expand Up @@ -140,7 +141,7 @@ bool isMqttConnected(); // Return true if MQTT is connected.
bool getConnectionEstablishedCount() // Return the number of time onConnectionEstablished has been called since the beginning.
```

As ESP8266 does not like to be interrupted too long with the delay() function, this function will allow a delayed execution of a function without interrupting the sketch.
As ESP8266 does not like to be interrupted too long with the `delay()` function, this function will allow a delayed execution of a function without interrupting the sketch.
```c++
void executeDelayed(const long delay, DelayedExecutionCallback callback);
```
Expand All @@ -154,7 +155,7 @@ const short getMqttServerPort();

### Connection established callback

To allow this library to work, you need to implement the onConnectionEstablished() function in your sketch.
To allow this library to work, you need to implement the `onConnectionEstablished()` function in your sketch.

```c++
void onConnectionEstablished()
Expand All @@ -163,18 +164,18 @@ void onConnectionEstablished()
}
```

In some special cases, like if you want to handle more than one MQTT connection in the same sketch, you can override this callback to another one for the second MQTT client using this function :
In some special cases, like if you want to handle more than one MQTT connection in the same sketch, you can override this callback to another one for the second MQTT client using this function:
```c++
void setOnConnectionEstablishedCallback(ConnectionEstablishedCallback callback);
```
See exemple "twoMQTTClientHandling.ino" for more details.
See example `twoMQTTClientHandling.ino` for more details.
### Subscribing to topics
The function `subscribe` allow to subscribe to a specific topic.
The function `subscribe` allows subscribing a specific topic.
For exemple, if you want to subscribe to topic `test/mytopic`, you can do this :
For example, if you want to subscribe to topic `test/mytopic`, you can do this:
```c++
void onTestMessageReceived(const String& message) {
Serial.print("message received from test/mytopic: " + message);
Expand All @@ -183,7 +184,7 @@ void onTestMessageReceived(const String& message) {
client.subscribe("test/mytopic", onTestMessageReceived);
```

You can also use lambdas to shorten the code like this :
You can also use lambdas to shorten the code like this:
```c++
client.subscribe("test/mytopic", [](const String& message) {
Serial.print("message received from test/mytopic: " + message;
Expand All @@ -192,9 +193,9 @@ client.subscribe("test/mytopic", [](const String& message) {

#### Wildcards

This library also handle MQTT topic wilcards. Most of the time, you will want to see what was the original topic when the callback is called. Here is how to do that.
This library also handle MQTT topic wildcards. Most of the time, you will want to see what was the original topic when the callback is called. Here is how to do that.

Exemple : Subscribe to "wildcardtest/#" and display received topic and message to Serial
Example: Subscribe to `wildcardtest/#` and display received topic and message to Serial
```c++
void onMessageReceived(const String& topic, const String& message) {
Serial.println(topic + ": " + message);
Expand All @@ -203,11 +204,9 @@ void onMessageReceived(const String& topic, const String& message) {
client.subscribe("wildcardtest/#", onMessageReceived);
```
The same thing with lambdas :
The same thing with lambdas:
```c++
client.subscribe("wildcardtest/#", [](const String& topic, const String& message) {
Serial.println(topic + ": " + message);
});
```


5 changes: 3 additions & 2 deletions examples/SimpleMQTTClient/SimpleMQTTClient.ino
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ void setup()
{
Serial.begin(115200);

// Optionnal functionnalities of EspMQTTClient :
// Optional functionalities of EspMQTTClient
client.enableDebuggingMessages(); // Enable debugging messages sent to serial output
client.enableHTTPWebUpdater(); // Enable the web updater. User and password default to values of MQTTUsername and MQTTPassword. These can be overrited with enableHTTPWebUpdater("user", "password").
client.enableHTTPWebUpdater(); // Enable the web updater. User and password default to values of MQTTUsername and MQTTPassword. These can be overridded with enableHTTPWebUpdater("user", "password").
client.enableOTA(); // Enable OTA (Over The Air) updates. Password defaults to MQTTPassword. Port is the default OTA port. Can be overridden with enableOTA("password", port).
client.enableLastWillMessage("TestClient/lastwill", "I am going offline"); // You can activate the retain flag by setting the third parameter to true
}

Expand Down
25 changes: 24 additions & 1 deletion src/EspMQTTClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ EspMQTTClient::EspMQTTClient(
_mqttClient.setCallback([this](char* topic, uint8_t* payload, unsigned int length) {this->mqttMessageReceivedCallback(topic, payload, length);});
_failedMQTTConnectionAttemptCount = 0;

// Web updater
// HTTP/OTA update related
_updateServerAddress = NULL;
_httpServer = NULL;
_httpUpdater = NULL;
_enableOTA = false;

// other
_enableSerialLogs = false;
Expand Down Expand Up @@ -127,6 +128,22 @@ void EspMQTTClient::enableHTTPWebUpdater(const char* address)
enableHTTPWebUpdater(_mqttUsername, _mqttPassword, address);
}

void EspMQTTClient::enableOTA(const char *password, const uint16_t port)
{
_enableOTA = true;

if (_mqttClientName != NULL)
ArduinoOTA.setHostname(_mqttClientName);

if (password != NULL)
ArduinoOTA.setPassword(password);
else if (_mqttPassword != NULL)
ArduinoOTA.setPassword(_mqttPassword);

if (port)
ArduinoOTA.setPort(port);
}

void EspMQTTClient::enableMQTTPersistence()
{
_mqttCleanSession = false;
Expand Down Expand Up @@ -228,6 +245,9 @@ bool EspMQTTClient::handleWiFi()
MDNS.update(); // We need to do this only for ESP8266
#endif
}

if (_enableOTA)
ArduinoOTA.handle();
}

// Disconnected since at least one loop() call
Expand Down Expand Up @@ -352,6 +372,9 @@ void EspMQTTClient::onWiFiConnectionEstablished()
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);
}

if (_enableOTA)
ArduinoOTA.begin();
}

void EspMQTTClient::onWiFiConnectionLost()
Expand Down
34 changes: 20 additions & 14 deletions src/EspMQTTClient.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef ESP_MQTT_CLIENT_H
#define ESP_MQTT_CLIENT_H

#include <ArduinoOTA.h>
#include <PubSubClient.h>
#include <vector>

Expand All @@ -11,9 +12,10 @@
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>

#define WebServer ESP8266WebServer
#define ESPmDNS ESP8266mDNS
#define DEFAULT_MQTT_CLIENT_NAME "ESP8266"
#define ESPHTTPUpdateServer ESP8266HTTPUpdateServer
#define ESPmDNS ESP8266mDNS
#define WebServer ESP8266WebServer

#else // for ESP32

Expand All @@ -22,6 +24,7 @@
#include <ESPmDNS.h>
#include "ESP32HTTPUpdateServer.h"

#define DEFAULT_MQTT_CLIENT_NAME "ESP32"
#define ESPHTTPUpdateServer ESP32HTTPUpdateServer

#endif
Expand Down Expand Up @@ -71,12 +74,13 @@ class EspMQTTClient
};
std::vector<TopicSubscriptionRecord> _topicSubscriptionList;

// HTTP update server related
// HTTP/OTA update related
char* _updateServerAddress;
char* _updateServerUsername;
char* _updateServerPassword;
WebServer* _httpServer;
ESPHTTPUpdateServer* _httpUpdater;
bool _enableOTA;

// Delayed execution related
struct DelayedExecutionRecord {
Expand All @@ -93,52 +97,54 @@ class EspMQTTClient

public:
EspMQTTClient(
const short mqttServerPort = 1883, // port and client name are swapped here to prevent a collision
const char* mqttClientName = "ESP8266"); // with the MQTT w/o auth constructor
// port and client name are swapped here to prevent a collision with the MQTT w/o auth constructor
const short mqttServerPort = 1883,
const char* mqttClientName = DEFAULT_MQTT_CLIENT_NAME);

// Wifi + MQTT with no MQTT authentification
/// Wifi + MQTT with no MQTT authentification
EspMQTTClient(
const char* wifiSsid,
const char* wifiPassword,
const char* mqttServerIp,
const char* mqttClientName = "ESP8266",
const char* mqttClientName = DEFAULT_MQTT_CLIENT_NAME,
const short mqttServerPort = 1883);

// Wifi + MQTT with MQTT authentification
/// 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 char* mqttClientName = DEFAULT_MQTT_CLIENT_NAME,
const short mqttServerPort = 1883);

// Only MQTT handling (no wifi), with MQTT authentification
/// 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");
const char* mqttClientName = DEFAULT_MQTT_CLIENT_NAME);

// Only MQTT handling without MQTT authentification
/// Only MQTT handling without MQTT authentification
EspMQTTClient(
const char* mqttServerIp,
const short mqttServerPort,
const char* mqttClientName = "ESP8266");
const char* mqttClientName = DEFAULT_MQTT_CLIENT_NAME);

~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 enableOTA(const char *password = NULL, const uint16_t port = 0); // Activate OTA updater, must be set before the first loop() call.
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()
/// Main loop, to call at each sketch loop()
void loop();

// MQTT related
Expand Down

0 comments on commit 81d3f2f

Please sign in to comment.