Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: multiple wifis #105

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 116 additions & 34 deletions src/EspMQTTClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ EspMQTTClient::EspMQTTClient(
const char* mqttPassword,
const char* mqttClientName,
const short mqttServerPort) :
_wifiSsid(wifiSsid),
_wifiPassword(wifiPassword),
_mqttServerIp(mqttServerIp),
_mqttUsername(mqttUsername),
_mqttPassword(mqttPassword),
Expand All @@ -59,11 +57,9 @@ EspMQTTClient::EspMQTTClient(
_mqttClient(mqttServerIp, mqttServerPort, _wifiClient)
{
// WiFi connection
_handleWiFi = (wifiSsid != NULL);
_wifiConnected = false;
_connectingToWifi = false;
addWifiCredentials(wifiSsid, wifiPassword);
_nextWifiConnectionAttemptMillis = 500;
_lastWifiConnectiomAttemptMillis = 0;
_lastWifiConnectionAttemptMillis = 0;
_wifiReconnectionAttemptDelay = 60 * 1000;

// MQTT client
Expand Down Expand Up @@ -182,7 +178,7 @@ 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)
if(weHandleWifi() && firstLoopCall)
{
WiFi.disconnect(true);
_nextWifiConnectionAttemptMillis = millis() + 500;
Expand All @@ -197,10 +193,12 @@ bool EspMQTTClient::handleWiFi()
/***** Detect ans handle the current WiFi handling state *****/

// Connection established
if (isWifiConnected && !_wifiConnected)
if (isWifiConnected && _wifiState != WifiState::Connected)
{
onWiFiConnectionEstablished();
_connectingToWifi = false;
_wifiState = WifiState::Connected;
_checkWifis.clear();
WiFi.scanDelete();

// At least 500 miliseconds of waiting before an mqtt connection attempt.
// Some people have reported instabilities when trying to connect to
Expand All @@ -210,9 +208,9 @@ bool EspMQTTClient::handleWiFi()
}

// Connection in progress
else if(_connectingToWifi)
else if(_wifiState == WifiState::Connecting)
{
if(WiFi.status() == WL_CONNECT_FAILED || millis() - _lastWifiConnectiomAttemptMillis >= _wifiReconnectionAttemptDelay)
if(WiFi.status() == WL_CONNECT_FAILED || millis() - _lastWifiConnectionAttemptMillis >= _wifiReconnectionAttemptDelay)
{
if(_enableSerialLogs)
Serial.printf("WiFi! Connection attempt failed, delay expired. (%fs). \n", millis()/1000.0);
Expand All @@ -221,22 +219,53 @@ bool EspMQTTClient::handleWiFi()
MDNS.end();

_nextWifiConnectionAttemptMillis = millis() + 500;
_connectingToWifi = false;
_wifiState = WifiState::Disconnected;
}
}

// Connection lost
else if (!isWifiConnected && _wifiConnected)
// searching for wifis done
else if (_wifiState == WifiState::Searching && WiFi.scanComplete() >= 0)
{
const auto number = WiFi.scanComplete();
_wifiState = WifiState::Disconnected; // will be overritten if we have found a network
if (number > 0)
{
for (auto i = 0; i < number; ++i)
{
const auto name = WiFi.SSID(i);
for (const auto &wifi : _knownWifis)
{
if (strcmp(name.c_str(), wifi.wifiSsid) == 0)
{
for (int index = static_cast<int>(_checkWifis.size()) - 1; index >= 0; --index)
{
if (WiFi.RSSI(_checkWifis[index]) < WiFi.RSSI(i))
{
_checkWifis.insert(_checkWifis.begin() + index + 1, i);
goto end_insert_loop;
}
}
_checkWifis.insert(_checkWifis.begin(), i);
break;
}
}
end_insert_loop:;
}
connectToNextWifi();
_nextWifiSearchAttemptMillis = millis() + 500;
}
}

// Connection lost
else if (!isWifiConnected && _wifiState == WifiState::Connected) {
onWiFiConnectionLost();

if(_handleWiFi)
if(weHandleWifi())
_nextWifiConnectionAttemptMillis = millis() + 500;
}

// Connected since at least one loop() call
else if (isWifiConnected && _wifiConnected)
{
else if (isWifiConnected && _wifiState == WifiState::Connected) {
// Web updater handling
if (_httpServer != NULL)
{
Expand All @@ -251,24 +280,26 @@ bool EspMQTTClient::handleWiFi()
}

// 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)
{
// Then, if we handle the wifi reconnection process and the waiting delay has
// expired, we connect to wifi
else if (weHandleWifi() && _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)
if (isWifiConnected && _wifiState != WifiState::Connected)
{
_wifiConnected = isWifiConnected;
_wifiState = WifiState::Connected;
return true;
}
if (!isWifiConnected && _wifiState == WifiState::Connected)
{
_wifiState = WifiState::Disconnected;
_nextWifiConnectionAttemptMillis = millis() + 500;
return true;
}
else
return false;
return false;
}


Expand Down Expand Up @@ -383,7 +414,7 @@ void EspMQTTClient::onWiFiConnectionLost()
Serial.printf("WiFi! Lost connection (%fs). \n", millis()/1000.0);

// If we handle wifi, we force disconnection to clear the last connection
if (_handleWiFi)
if (weHandleWifi())
{
WiFi.disconnect(true);
MDNS.end();
Expand Down Expand Up @@ -538,9 +569,15 @@ void EspMQTTClient::setKeepAlive(uint16_t keepAliveSeconds)

void EspMQTTClient::setWifiCredentials(const char* wifiSsid, const char* wifiPassword)
{
_wifiSsid = wifiSsid;
_wifiPassword = wifiPassword;
_handleWiFi = true;
_knownWifis.clear();
addWifiCredentials(wifiSsid, wifiPassword);
}

void EspMQTTClient::addWifiCredentials(const char* wifiSsid, const char* wifiPassword)
{
if (wifiSsid) {
_knownWifis.push_back(WifiInformation{wifiSsid, wifiPassword});
}
}

void EspMQTTClient::executeDelayed(const unsigned long delay, DelayedExecutionCallback callback)
Expand All @@ -564,10 +601,55 @@ void EspMQTTClient::connectToWifi()
#else
WiFi.hostname(_mqttClientName);
#endif
WiFi.begin(_wifiSsid, _wifiPassword);

WiFi.begin(_knownWifis.front().wifiSsid, _knownWifis.front().wifiPassword);

if (_enableSerialLogs)
Serial.printf("\nWiFi: Connecting to %s ... (%fs) \n", _wifiSsid, millis()/1000.0);
Serial.printf("\nWiFi: Connecting to %s ... (%fs) \n", _knownWifis.front().wifiSsid, millis()/1000.0);

_nextWifiConnectionAttemptMillis = 0;
_wifiState = WifiState::Connecting;
_lastWifiConnectionAttemptMillis = millis();
}

// Start searching for Wifis (non-blocking)
void EspMQTTClient::searchForWifi()
{
WiFi.mode(WIFI_STA);
#ifdef ESP32
WiFi.setHostname(_mqttClientName);
#else
WiFi.hostname(_mqttClientName);
#endif
WiFi.scanNetworks(true /* async */, true /* show_hidden */);
if (_enableSerialLogs)
Serial.printf("\nWiFi: Searching for wifis ... (%fs) \n", millis()/1000.0);

_wifiState = WifiState::Searching;
}

// Start searching for Wifis (non-blocking)
void EspMQTTClient::connectToNextWifi()
{
if (_checkWifis.empty())
return;
const auto name = WiFi.SSID(_checkWifis.back());
_checkWifis.pop_back();
for (const auto & wifi : _knownWifis) {
if (strcmp(wifi.wifiSsid, name.c_str()) == 0) {
WiFi.begin(wifi.wifiSsid, wifi.wifiPassword);
break;
}
}
if (_enableSerialLogs)
Serial.printf("\nWiFi: Connecting to %s ... (%fs) \n", name.c_str(), millis()/1000.0);

_nextWifiConnectionAttemptMillis = 0;
_wifiState = WifiState::Connecting;
_lastWifiConnectionAttemptMillis = millis();

if (_checkWifis.empty())
WiFi.scanDelete();
}

// Try to connect to the MQTT broker and return True if the connection is successfull (blocking)
Expand Down
23 changes: 16 additions & 7 deletions src/EspMQTTClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <ArduinoOTA.h>
#include <PubSubClient.h>
#include <vector>
#include <utility>

#ifdef ESP8266

Expand Down Expand Up @@ -40,14 +41,18 @@ class EspMQTTClient
{
private:
// Wifi related
bool _handleWiFi;
bool _wifiConnected;
bool _connectingToWifi;
unsigned long _lastWifiConnectiomAttemptMillis;
enum class WifiState {Disconnected, Connected, Connecting, Searching};
WifiState _wifiState = WifiState::Disconnected;
unsigned long _lastWifiConnectionAttemptMillis;
unsigned long _nextWifiConnectionAttemptMillis;
unsigned int _wifiReconnectionAttemptDelay;
const char* _wifiSsid;
const char* _wifiPassword;
unsigned long _nextWifiSearchAttemptMillis;
struct WifiInformation {
const char* wifiSsid;
const char* wifiPassword;
};
std::vector<WifiInformation> _knownWifis;
std::vector<uint8_t> _checkWifis; // queue of wifis (for WiFi.SSID(index)) to which we could connect. Front of queue is the back
WiFiClient _wifiClient;

// MQTT related
Expand Down Expand Up @@ -166,12 +171,13 @@ class EspMQTTClient

// Wifi related
void setWifiCredentials(const char* wifiSsid, const char* wifiPassword);
void addWifiCredentials(const char* wifiSsid, const char* wifiPassword);

// 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 isWifiConnected() const { return _wifiState == WifiState::Connected; }; // Return true if wifi is connected
inline bool isMqttConnected() const { return _mqttConnected; }; // Return true if mqtt is connected
inline unsigned int getConnectionEstablishedCount() const { return _connectionEstablishedCount; }; // Return the number of time onConnectionEstablished has been called since the beginning.

Expand All @@ -191,12 +197,15 @@ class EspMQTTClient
private:
bool handleWiFi();
bool handleMQTT();
bool weHandleWifi() const { return !_knownWifis.empty(); }
void onWiFiConnectionEstablished();
void onWiFiConnectionLost();
void onMQTTConnectionEstablished();
void onMQTTConnectionLost();

void connectToWifi();
void searchForWifi();
void connectToNextWifi();
bool connectToMqttBroker();
void processDelayedExecutionRequests();
bool mqttTopicMatch(const String &topic1, const String &topic2);
Expand Down