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

Fix non-blocking network timeouts #116

Merged
merged 9 commits into from
Aug 11, 2021
Merged
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ WipperSnapper currently supports the following hardware. Don't see your board? D

ESP32-S2
* Adafruit FunHouse
* Adafruit MagTag 2.9"
* Adafruit Metro ESP32-S2

SAMD51
* Adafruit Metro M4 AirLift Lite
Expand Down
118 changes: 52 additions & 66 deletions src/Wippersnapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -856,51 +856,6 @@ void Wippersnapper::set_ssid_pass() {
WS_DEBUG_PRINTLN("ERROR: Please define a network interface!");
}

/**************************************************************************/
/*!
@brief Checks and handles network interface connection.
@returns Network status as ws_status_t.
*/
/**************************************************************************/
ws_status_t Wippersnapper::checkNetworkConnection() {
if (status() < WS_NET_CONNECTED) {
WS_DEBUG_PRINTLN("WiFi connection failed out, reconnecting...");
unsigned long startRetry = millis();
connect();
while (status() < WS_CONNECTED) { // return an error on timeout
if (millis() - startRetry > 60000) {
return status();
}
delay(500);
}
}
return status();
}

/**************************************************************************/
/*!
@brief Handles MQTT connection.
@param timeStart
The time which this function was called, in milliseconds.
@returns Network status, as ws_status_t.
*/
/**************************************************************************/
ws_status_t Wippersnapper::checkMQTTConnection(uint32_t timeStart) {
// Return quickly
if (mqttStatus() == WS_CONNECTED) {
return status();
}

// loop until we have a connection
// mqttStatus() will try to reconnect before returning
while (mqttStatus() != WS_CONNECTED && millis() - timeStart < 60000) {
}
if (mqttStatus() != WS_CONNECTED) {
WS_DEBUG_PRINTLN("ERROR: Disconnected from MQTT!");
}
return status();
}

/**************************************************************************/
/*!
@brief Pings MQTT broker.
Expand Down Expand Up @@ -952,23 +907,25 @@ bool Wippersnapper::encodePinEvent(
*/
/**************************************************************************/
ws_status_t Wippersnapper::run() {
// this should be a state machine, dept. on the network...

// WS_DEBUG_PRINTLN("exec::run()");
uint32_t curTime = millis();

// Handle network connection
checkNetworkConnection();
// Handle MQTT connection
checkMQTTConnection(curTime);
// Handle networking
handleNetworking();
feedWDT();

// Process all incoming packets from Wippersnapper MQTT Broker
WS._mqtt->processPackets(10);
feedWDT();

// Process digital inputs, digitalGPIO module
WS._digitalGPIO->processDigitalInputs();
feedWDT();

// Process analog inputs
WS._analogIO->processAnalogInputs();

feedWDT();

return status();
Expand Down Expand Up @@ -1012,6 +969,45 @@ ws_status_t Wippersnapper::status() {
return _status;
}

void Wippersnapper::handleNetworking() {
bool mqttDisconnected = false;
// Handle WiFi connection, BLOCKING
while (keepAliveWiFi() != WS_NET_CONNECTED) {
setStatusLEDColor(LED_ERROR);
WS_DEBUG_PRINT("Network Status: ");
WS_DEBUG_PRINTLN(networkStatus());
delay(20000);
feedWDT();
};
// Handle MQTT connection, BLOCKING
while (mqttStatus() != WS_CONNECTED) {
setStatusLEDColor(LED_ERROR);
WS_DEBUG_PRINT("MQTT Error: ");
WS_DEBUG_PRINTLN(WS._mqtt->connectErrorString(_status));
mqttDisconnected = true;
delay(20000);
feedWDT();
}
// Perform re-registration if we disconnect from MQTT broker as well
if (mqttDisconnected)
registerBoard(10);
}

ws_status_t Wippersnapper::keepAliveWiFi() {
WS_DEBUG_PRINTLN("keepAliveWiFi()");
if (networkStatus() == WS_NET_CONNECTED) {
if (usingStatusNeoPixel)
statusLEDDeinit();
return WS_NET_CONNECTED; // return immediately if WL_CONNECTED
}
// Otherwise, try to reconnect
WS_DEBUG_PRINTLN("Attempting to reconnect...");
feedWDT();
setStatusLEDColor(LED_NET_CONNECT);
_connect();
return networkStatus();
}

/**************************************************************************/
/*!
@brief Returns the board definition status
Expand All @@ -1027,19 +1023,7 @@ ws_board_status_t Wippersnapper::getBoardStatus() { return WS._boardStatus; }
*/
/**************************************************************************/
ws_status_t Wippersnapper::mqttStatus() {
// if the connection failed,
// return so we don't hammer IO
if (_status == WS_CONNECT_FAILED) {
WS_DEBUG_PRINT("mqttStatus() failed to connect");
#ifdef USE_TINYUSB
WS._fileSystem->writeErrorToBootOut(
"ERROR: Failed to connect to WipperSnapper, retrying...");
#endif
WS_DEBUG_PRINTLN(WS._mqtt->connectErrorString(_status));
setStatusLEDColor(LED_ERROR);
return _status;
}

// First, handle the case where we're connected
if (WS._mqtt->connected()) {
// ping within keepalive to keep connection open
if (millis() > (_prv_ping + WS_KEEPALIVE_INTERVAL_MS)) {
Expand All @@ -1049,7 +1033,8 @@ ws_status_t Wippersnapper::mqttStatus() {
// blink status LED every STATUS_LED_KAT_BLINK_TIME millis
if (millis() > (_prvKATBlink + STATUS_LED_KAT_BLINK_TIME)) {
if (!statusLEDInit()) {
WS_DEBUG_PRINTLN("Can not blink, status-LED in use");
WS_DEBUG_PRINTLN(
"Can not blink, status-LED in use by WipperSnapper Application");
} else {
statusLEDBlink(WS_LED_STATUS_KAT);
statusLEDDeinit();
Expand All @@ -1059,10 +1044,11 @@ ws_status_t Wippersnapper::mqttStatus() {
return WS_CONNECTED;
}

// prevent fast reconnect attempts, except for the first time through
// Next, handle the case where we are not connected and attempt to reconnect
if (_last_mqtt_connect == 0 ||
millis() - _last_mqtt_connect > WS_KEEPALIVE_INTERVAL_MS) {
_last_mqtt_connect = millis();
setStatusLEDColor(LED_IO_CONNECT);
switch (WS._mqtt->connect(WS._username, WS._key)) {
case 0:
return WS_CONNECTED;
Expand Down Expand Up @@ -1115,4 +1101,4 @@ void Wippersnapper::enableWDT(int timeoutMS) {
}
}
#endif
}
}
5 changes: 5 additions & 0 deletions src/Wippersnapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ class Wippersnapper {
virtual void setupMQTTClient(const char *clientID);

virtual ws_status_t networkStatus();
ws_status_t keepAliveWiFi();
ws_status_t status();
ws_status_t mqttStatus();
ws_board_status_t getBoardStatus();
Expand All @@ -210,7 +211,11 @@ class Wippersnapper {
ws_status_t run();
ws_status_t checkNetworkConnection();
ws_status_t checkMQTTConnection(uint32_t timeStart);
// Networking
void handleNetworking();
void ping();

// WDT
void enableWDT(int timeoutMS = 0);
void feedWDT();

Expand Down
14 changes: 9 additions & 5 deletions src/network_interfaces/Wippersnapper_ESP32.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,16 @@ class Wippersnapper_ESP32 : public Wippersnapper {
_status = WS_NET_DISCONNECTED;
}

// wait until connection is established
while (WiFi.status() != WL_CONNECTED) {
delay(100);
// wait for connection to be established
long startRetry = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) {
// do nothing, busy loop during the timeout
}
// timeout expired and connected
if (WiFi.status() == WL_CONNECTED) {
_mqtt_client->setCACert(_aio_root_ca);
return;
}

_mqtt_client->setCACert(_aio_root_ca);
}

/**************************************************************************/
Expand Down