Rework WiFi code (#435)

* Rework WiFi code

* Add missing trySavedAttempt()

* Remap status values to new failure values

* Skip saved credentials properly
This commit is contained in:
gorbit99
2025-09-17 17:03:09 +02:00
committed by GitHub
parent 1a7619e2e6
commit 23390ddb39
8 changed files with 352 additions and 215 deletions

View File

@@ -28,6 +28,8 @@
#include "configuration/Configuration.h"
#include "network/connection.h"
#include "network/manager.h"
#include "network/wifihandler.h"
#include "network/wifiprovisioning.h"
#include "sensors/SensorManager.h"
#include "status/LEDManager.h"
#include "status/StatusManager.h"
@@ -40,3 +42,5 @@ extern SlimeVR::Sensors::SensorManager sensorManager;
extern SlimeVR::Network::Manager networkManager;
extern SlimeVR::Network::Connection networkConnection;
extern BatteryMonitor battery;
extern SlimeVR::WiFiNetwork wifiNetwork;
extern SlimeVR::WifiProvisioning wifiProvisioning;

View File

@@ -42,6 +42,8 @@ SlimeVR::Status::StatusManager statusManager;
SlimeVR::Configuration::Configuration configuration;
SlimeVR::Network::Manager networkManager;
SlimeVR::Network::Connection networkConnection;
SlimeVR::WiFiNetwork wifiNetwork;
SlimeVR::WifiProvisioning wifiProvisioning;
#if DEBUG_MEASURE_SENSOR_TIME_TAKEN
SlimeVR::Debugging::TimeTakenMeasurer sensorMeasurer{"Sensors"};

View File

@@ -26,14 +26,14 @@
namespace SlimeVR::Network {
void Manager::setup() { ::WiFiNetwork::setUp(); }
void Manager::setup() { wifiNetwork.setUp(); }
void Manager::update() {
WiFiNetwork::upkeep();
wifiNetwork.upkeep();
auto wasConnected = m_IsConnected;
m_IsConnected = ::WiFiNetwork::isConnected();
m_IsConnected = wifiNetwork.isConnected();
if (!m_IsConnected) {
return;

View File

@@ -22,29 +22,20 @@
*/
#include "GlobalVars.h"
#include "globals.h"
#include "logging/Logger.h"
#if !ESP8266
#include "esp_wifi.h"
#endif
unsigned long lastWifiReportTime = 0;
unsigned long wifiConnectionTimeout = millis();
bool isWifiConnected = false;
uint8_t wifiState = SLIME_WIFI_NOT_SETUP;
bool hadWifi = false;
unsigned long last_rssi_sample = 0;
namespace SlimeVR {
// TODO: Cleanup with proper classes
SlimeVR::Logging::Logger wifiHandlerLogger("WiFiHandler");
void reportWifiError() {
void WiFiNetwork::reportWifiProgress() {
if (lastWifiReportTime + 1000 < millis()) {
lastWifiReportTime = millis();
Serial.print(".");
}
}
void setStaticIPIfDefined() {
void WiFiNetwork::setStaticIPIfDefined() {
#ifdef WIFI_USE_STATICIP
const IPAddress ip(WIFI_STATIC_IP);
const IPAddress gateway(WIFI_STATIC_GATEWAY);
@@ -53,43 +44,35 @@ void setStaticIPIfDefined() {
#endif
}
bool WiFiNetwork::isConnected() { return isWifiConnected; }
bool WiFiNetwork::isConnected() const {
return wifiState == WiFiReconnectionStatus::Success;
}
void WiFiNetwork::setWiFiCredentials(const char* SSID, const char* pass) {
stopProvisioning();
setStaticIPIfDefined();
WiFi.begin(SSID, pass);
wifiProvisioning.stopProvisioning();
WiFi.persistent(true);
tryConnecting(false, SSID, pass);
retriedOnG = false;
// Reset state, will get back into provisioning if can't connect
hadWifi = false;
wifiState = SLIME_WIFI_SERVER_CRED_ATTEMPT;
wifiConnectionTimeout = millis();
wifiState = WiFiReconnectionStatus::ServerCredAttempt;
}
IPAddress WiFiNetwork::getAddress() { return WiFi.localIP(); }
void WiFiNetwork::setUp() {
// Don't need to save the already saved credentials or the hardcoded ones
WiFi.persistent(false);
wifiHandlerLogger.info("Setting up WiFi");
WiFi.persistent(true);
WiFi.mode(WIFI_STA);
#if ESP8266
#if USE_ATTENUATION
WiFi.setOutputPower(20.0 - ATTENUATION_N);
#endif
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
#endif
WiFi.hostname("SlimeVR FBT Tracker");
wifiHandlerLogger.info(
"Loaded credentials for SSID '%s' and pass length %d",
WiFi.SSID().c_str(),
WiFi.psk().length()
);
setStaticIPIfDefined();
wl_status_t status = WiFi.begin(
); // Should connect to last used access point, see
// https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/station-class.html#begin
wifiHandlerLogger.debug("Status: %d", status);
wifiState = SLIME_WIFI_SAVED_ATTEMPT;
wifiConnectionTimeout = millis();
trySavedCredentials();
#if ESP8266
#if POWERSAVING_MODE == POWER_SAVING_NONE
@@ -121,156 +104,249 @@ void WiFiNetwork::setUp() {
#endif
}
void onConnected() {
WiFiNetwork::stopProvisioning();
void WiFiNetwork::onConnected() {
wifiState = WiFiReconnectionStatus::Success;
wifiProvisioning.stopProvisioning();
statusManager.setStatus(SlimeVR::Status::WIFI_CONNECTING, false);
isWifiConnected = true;
hadWifi = true;
wifiHandlerLogger.info(
"Connected successfully to SSID '%s', IP address %s",
WiFi.SSID().c_str(),
WiFi.localIP().toString().c_str()
);
// Reset it, in case we just connected with server creds
WiFi.persistent(false);
}
uint8_t WiFiNetwork::getWiFiState() { return wifiState; }
WiFiNetwork::WiFiReconnectionStatus WiFiNetwork::getWiFiState() { return wifiState; }
void WiFiNetwork::upkeep() {
upkeepProvisioning();
if (WiFi.status() != WL_CONNECTED) {
if (isWifiConnected) {
wifiHandlerLogger.warn("Connection to WiFi lost, reconnecting...");
isWifiConnected = false;
wifiProvisioning.upkeepProvisioning();
if (WiFi.status() == WL_CONNECTED) {
if (!isConnected()) {
onConnected();
return;
}
statusManager.setStatus(SlimeVR::Status::WIFI_CONNECTING, true);
reportWifiError();
if (wifiConnectionTimeout + 11000 < millis()) {
switch (wifiState) {
case SLIME_WIFI_NOT_SETUP: // Wasn't set up
return;
case SLIME_WIFI_SAVED_ATTEMPT: // Couldn't connect with first set of
// credentials
#if ESP8266
// Try again but with 11G but only if there are credentials,
// otherwise we just waste time before switching to hardcoded
// credentials.
if (WiFi.SSID().length() > 0) {
#if USE_ATTENUATION
WiFi.setOutputPower(20.0 - ATTENUATION_G);
#endif
WiFi.setPhyMode(WIFI_PHY_MODE_11G);
setStaticIPIfDefined();
WiFi.begin();
wifiConnectionTimeout = millis();
wifiHandlerLogger.error(
"Can't connect from saved credentials, status: %d.",
WiFi.status()
);
wifiHandlerLogger.debug(
"Trying saved credentials with PHY Mode G..."
);
} else {
wifiHandlerLogger.debug(
"Skipping PHY Mode G attempt on 0-length SSID..."
);
}
#endif
wifiState = SLIME_WIFI_SAVED_G_ATTEMPT;
return;
case SLIME_WIFI_SAVED_G_ATTEMPT: // Couldn't connect with first set of
// credentials with PHY Mode G
#if defined(WIFI_CREDS_SSID) && defined(WIFI_CREDS_PASSWD)
// Try hardcoded credentials now
#if ESP8266
#if USE_ATTENUATION
WiFi.setOutputPower(20.0 - ATTENUATION_N);
#endif
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
#endif
setStaticIPIfDefined();
WiFi.begin(WIFI_CREDS_SSID, WIFI_CREDS_PASSWD);
wifiConnectionTimeout = millis();
wifiHandlerLogger.error(
"Can't connect from saved credentials, status: %d.",
WiFi.status()
);
wifiHandlerLogger.debug("Trying hardcoded credentials...");
#endif
wifiState = SLIME_WIFI_HARDCODE_ATTEMPT;
return;
case SLIME_WIFI_HARDCODE_ATTEMPT: // Couldn't connect with second set
// of credentials
#if defined(WIFI_CREDS_SSID) && defined(WIFI_CREDS_PASSWD) && ESP8266
// Try hardcoded credentials again,
// but with PHY Mode G
#if USE_ATTENUATION
WiFi.setOutputPower(20.0 - ATTENUATION_G);
#endif
WiFi.setPhyMode(WIFI_PHY_MODE_11G);
setStaticIPIfDefined();
WiFi.begin(WIFI_CREDS_SSID, WIFI_CREDS_PASSWD);
wifiConnectionTimeout = millis();
wifiHandlerLogger.error(
"Can't connect from saved credentials, status: %d.",
WiFi.status()
);
wifiHandlerLogger.debug(
"Trying hardcoded credentials with WiFi PHY Mode G..."
);
#endif
wifiState = SLIME_WIFI_HARDCODE_G_ATTEMPT;
return;
case SLIME_WIFI_SERVER_CRED_ATTEMPT: // Couldn't connect with
// server-sent credentials.
#if ESP8266
// Try again silently but with 11G
#if USE_ATTENUATION
WiFi.setOutputPower(20.0 - ATTENUATION_G);
#endif
WiFi.setPhyMode(WIFI_PHY_MODE_11G);
setStaticIPIfDefined();
WiFi.begin();
wifiConnectionTimeout = millis();
wifiState = SLIME_WIFI_SERVER_CRED_G_ATTEMPT;
#endif
return;
case SLIME_WIFI_HARDCODE_G_ATTEMPT: // Couldn't connect with second set
// of credentials with PHY Mode G.
case SLIME_WIFI_SERVER_CRED_G_ATTEMPT: // Or if couldn't connect with
// server-sent credentials
// Return to the default PHY Mode N.
#if ESP8266
#if USE_ATTENUATION
WiFi.setOutputPower(20.0 - ATTENUATION_N);
#endif
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
#endif
// Start smart config
if (!hadWifi && !WiFi.smartConfigDone()
&& wifiConnectionTimeout + 11000 < millis()) {
if (WiFi.status() != WL_IDLE_STATUS) {
wifiHandlerLogger.error(
"Can't connect from any credentials, status: %d.",
WiFi.status()
);
wifiConnectionTimeout = millis();
}
startProvisioning();
}
return;
}
}
return;
}
if (!isWifiConnected) {
onConnected();
return;
} else {
if (millis() - last_rssi_sample >= 2000) {
last_rssi_sample = millis();
if (millis() - lastRssiSample >= 2000) {
lastRssiSample = millis();
uint8_t signalStrength = WiFi.RSSI();
networkConnection.sendSignalStrength(signalStrength);
}
return;
}
if (isConnected()) {
statusManager.setStatus(SlimeVR::Status::WIFI_CONNECTING, true);
wifiHandlerLogger.warn("Connection to WiFi lost, reconnecting...");
trySavedCredentials();
return;
}
reportWifiProgress();
if (millis() - wifiConnectionTimeout
< static_cast<uint32_t>(WiFiTimeoutSeconds * 1000)
&& WiFi.status() == WL_DISCONNECTED) {
return;
}
switch (wifiState) {
case WiFiReconnectionStatus::NotSetup: // Wasn't set up
return;
case WiFiReconnectionStatus::SavedAttempt: // Couldn't connect with
// first set of
// credentials
if (!trySavedCredentials()) {
tryHardcodedCredentials();
}
return;
case WiFiReconnectionStatus::HardcodeAttempt: // Couldn't connect with
// second set of credentials
if (!tryHardcodedCredentials()) {
wifiState = WiFiReconnectionStatus::Failed;
}
return;
case WiFiReconnectionStatus::ServerCredAttempt: // Couldn't connect with
// server-sent credentials.
if (!tryServerCredentials()) {
wifiState = WiFiReconnectionStatus::Failed;
}
return;
case WiFiReconnectionStatus::Failed: // Couldn't connect with second set of
// credentials or server credentials
// Return to the default PHY Mode N.
#if ESP8266
if constexpr (USE_ATTENUATION) {
WiFi.setOutputPower(20.0 - ATTENUATION_N);
}
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
#endif
// Start smart config
if (!hadWifi && !WiFi.smartConfigDone()
&& millis() - wifiConnectionTimeout
>= static_cast<uint32_t>(WiFiTimeoutSeconds * 1000)) {
if (WiFi.status() != WL_IDLE_STATUS) {
wifiHandlerLogger.error(
"Can't connect from any credentials, error: %d, reason: %s.",
static_cast<int>(statusToFailure(WiFi.status())),
statusToReasonString(WiFi.status())
);
wifiConnectionTimeout = millis();
}
wifiProvisioning.startProvisioning();
}
return;
}
return;
}
const char* WiFiNetwork::statusToReasonString(wl_status_t status) {
switch (status) {
case WL_DISCONNECTED:
return "Timeout";
#ifdef ESP8266
case WL_WRONG_PASSWORD:
return "Wrong password";
case WL_CONNECT_FAILED:
return "Connection failed";
#elif ESP32
case WL_CONNECT_FAILED:
return "Wrong password";
#endif
case WL_NO_SSID_AVAIL:
return "SSID not found";
default:
return "Unknown";
}
}
WiFiNetwork::WiFiFailureReason WiFiNetwork::statusToFailure(wl_status_t status) {
switch (status) {
case WL_DISCONNECTED:
return WiFiFailureReason::Timeout;
#ifdef ESP8266
case WL_WRONG_PASSWORD:
return WiFiFailureReason::WrongPassword;
#elif ESP32
case WL_CONNECT_FAILED:
return WiFiFailureReason::WrongPassword;
#endif
case WL_NO_SSID_AVAIL:
return WiFiFailureReason::SSIDNotFound;
default:
return WiFiFailureReason::Unknown;
}
}
void WiFiNetwork::showConnectionAttemptFailed(const char* type) const {
wifiHandlerLogger.error(
"Can't connect from %s credentials, error: %d, reason: %s.",
type,
static_cast<int>(statusToFailure(WiFi.status())),
statusToReasonString(WiFi.status())
);
}
bool WiFiNetwork::trySavedCredentials() {
if (WiFi.SSID().length() == 0) {
wifiHandlerLogger.debug("Skipping saved credentials attempt on 0-length SSID..."
);
wifiState = WiFiReconnectionStatus::HardcodeAttempt;
return false;
}
if (wifiState == WiFiReconnectionStatus::SavedAttempt) {
showConnectionAttemptFailed("saved");
if (WiFi.status() != WL_DISCONNECTED) {
return false;
}
if (retriedOnG) {
return false;
}
retriedOnG = true;
wifiHandlerLogger.debug("Trying saved credentials with PHY Mode G...");
return tryConnecting(true);
}
retriedOnG = false;
wifiState = WiFiReconnectionStatus::SavedAttempt;
return tryConnecting();
}
bool WiFiNetwork::tryHardcodedCredentials() {
#if defined(WIFI_CREDS_SSID) && defined(WIFI_CREDS_PASSWD)
if (wifiState == WiFiReconnectionStatus::HardcodeAttempt) {
showConnectionAttemptFailed("hardcoded");
if (WiFi.status() != WL_DISCONNECTED) {
return false;
}
if (retriedOnG) {
return false;
}
retriedOnG = true;
wifiHandlerLogger.debug("Trying hardcoded credentials with PHY Mode G...");
return tryConnecting(true, WIFI_CREDS_SSID, WIFI_CREDS_PASSWD);
}
retriedOnG = false;
wifiState = WiFiReconnectionStatus::HardcodeAttempt;
return tryConnecting(false, WIFI_CREDS_SSID, WIFI_CREDS_PASSWD);
#else
wifiState = WiFiReconnectionStatus::HardcodeAttempt;
return false;
#endif
}
bool WiFiNetwork::tryServerCredentials() {
if (WiFi.status() != WL_DISCONNECTED) {
return false;
}
if (retriedOnG) {
return false;
}
retriedOnG = true;
return tryConnecting(true);
}
bool WiFiNetwork::tryConnecting(bool phyModeG, const char* SSID, const char* pass) {
#if ESP8266
if (phyModeG) {
WiFi.setPhyMode(WIFI_PHY_MODE_11G);
if constexpr (USE_ATTENUATION) {
WiFi.setOutputPower(20.0 - ATTENUATION_G);
}
} else {
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
if constexpr (USE_ATTENUATION) {
WiFi.setOutputPower(20.0 - ATTENUATION_N);
}
}
#else
if (phyModeG) {
return false;
}
#endif
setStaticIPIfDefined();
if (SSID == nullptr) {
WiFi.begin();
} else {
WiFi.begin(SSID, pass);
}
wifiConnectionTimeout = millis();
return true;
}
} // namespace SlimeVR

View File

@@ -20,33 +20,75 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef SLIMEVR_WIFI_H_
#define SLIMEVR_WIFI_H_
#pragma once
#include "logging/Logger.h"
#ifdef ESP8266
#include <ESP8266WiFi.h>
#else
#include <WiFi.h>
#endif
namespace WiFiNetwork {
bool isConnected();
void setUp();
void upkeep();
void setWiFiCredentials(const char* SSID, const char* pass);
IPAddress getAddress();
uint8_t getWiFiState();
} // namespace WiFiNetwork
namespace SlimeVR {
class WiFiNetwork {
public:
enum class WiFiReconnectionStatus {
NotSetup = 0,
SavedAttempt,
HardcodeAttempt,
ServerCredAttempt,
Failed,
Success
};
enum class WiFiFailureReason {
Timeout = 0,
SSIDNotFound = 1,
WrongPassword = 2,
Unknown = 3,
};
[[nodiscard]] bool isConnected() const;
void setUp();
void upkeep();
void setWiFiCredentials(const char* SSID, const char* pass);
static IPAddress getAddress();
WiFiReconnectionStatus getWiFiState();
private:
static constexpr float WiFiTimeoutSeconds = 11;
void reportWifiProgress();
void setStaticIPIfDefined();
void onConnected();
bool trySavedCredentials();
bool tryHardcodedCredentials();
bool tryServerCredentials();
bool tryConnecting(
bool phyModeG = false,
const char* SSID = nullptr,
const char* pass = nullptr
);
void showConnectionAttemptFailed(const char* type) const;
static const char* statusToReasonString(wl_status_t status);
static WiFiFailureReason statusToFailure(wl_status_t status);
unsigned long lastWifiReportTime = 0;
unsigned long wifiConnectionTimeout = millis();
bool isWifiConnected = false;
WiFiReconnectionStatus wifiState = WiFiReconnectionStatus::NotSetup;
bool retriedOnG = false;
bool hadWifi = false;
unsigned long lastRssiSample = 0;
uint8_t lastFailStatus = 0;
SlimeVR::Logging::Logger wifiHandlerLogger{"WiFiHandler"};
};
/** Wifi Reconnection Statuses **/
typedef enum {
SLIME_WIFI_NOT_SETUP = 0,
SLIME_WIFI_SAVED_ATTEMPT,
SLIME_WIFI_SAVED_G_ATTEMPT,
SLIME_WIFI_HARDCODE_ATTEMPT,
SLIME_WIFI_HARDCODE_G_ATTEMPT,
SLIME_WIFI_SERVER_CRED_ATTEMPT,
SLIME_WIFI_SERVER_CRED_G_ATTEMPT
} wifi_reconnection_statuses;
#endif // SLIMEVR_WIFI_H_
} // namespace SlimeVR

View File

@@ -29,29 +29,31 @@
// it sucks.
// TODO: New implementation: https://github.com/SlimeVR/SlimeVR-Tracker-ESP/issues/71
// TODO: Cleanup with proper classes
SlimeVR::Logging::Logger wifiProvisioningLogger("WiFiProvisioning");
bool provisioning = false;
namespace SlimeVR {
void WiFiNetwork::upkeepProvisioning() {
void WifiProvisioning::upkeepProvisioning() {
// Called even when not provisioning to do things like provide neighbours or other
// upkeep
}
void WiFiNetwork::startProvisioning() {
void WifiProvisioning::startProvisioning() {
if (WiFi.beginSmartConfig()) {
provisioning = true;
wifiProvisioningLogger.info("SmartConfig started");
}
}
void WiFiNetwork::stopProvisioning() {
void WifiProvisioning::stopProvisioning() {
WiFi.stopSmartConfig();
provisioning = false;
}
void WiFiNetwork::provideNeighbours() {
void WifiProvisioning::provideNeighbours() {
// TODO: SmartConfig can't do this, created for future
}
bool WiFiNetwork::isProvisioning() { return provisioning && !WiFi.smartConfigDone(); }
bool WifiProvisioning::isProvisioning() const {
return provisioning && !WiFi.smartConfigDone();
}
} // namespace SlimeVR

View File

@@ -23,12 +23,23 @@
#ifndef SLIMEVR_WIFIPROVISIONING_H_
#define SLIMEVR_WIFIPROVISIONING_H_
namespace WiFiNetwork {
void upkeepProvisioning();
void startProvisioning();
void stopProvisioning();
bool isProvisioning();
void provideNeighbours();
} // namespace WiFiNetwork
#include "logging/Logger.h"
namespace SlimeVR {
class WifiProvisioning {
public:
void upkeepProvisioning();
void startProvisioning();
void stopProvisioning();
bool isProvisioning() const;
void provideNeighbours();
private:
SlimeVR::Logging::Logger wifiProvisioningLogger{"WiFiProvisioning"};
bool provisioning = false;
};
} // namespace SlimeVR
#endif // SLIMEVR_WIFIPROVISIONING_H_

View File

@@ -85,7 +85,7 @@ void cmdSet(CmdParser* parser) {
return;
}
WiFiNetwork::setWiFiCredentials(sc_ssid, sc_pw);
wifiNetwork.setWiFiCredentials(sc_ssid, sc_pw);
logger.info("CMD SET WIFI OK: New wifi credentials set, reconnecting");
}
} else if (parser->equalCmdParam(1, "BWIFI")) {
@@ -131,7 +131,7 @@ void cmdSet(CmdParser* parser) {
// set the pointer for pass to null for no password
ppass = NULL;
}
WiFiNetwork::setWiFiCredentials(ssid, ppass);
wifiNetwork.setWiFiCredentials(ssid, ppass);
logger.info("CMD SET BWIFI OK: New wifi credentials set, reconnecting");
}
} else {
@@ -150,10 +150,10 @@ void printState() {
HARDWARE_MCU,
PROTOCOL_VERSION,
FIRMWARE_VERSION,
WiFiNetwork::getAddress().toString().c_str(),
wifiNetwork.getAddress().toString().c_str(),
WiFi.macAddress().c_str(),
statusManager.getStatus(),
WiFiNetwork::getWiFiState()
wifiNetwork.getWiFiState()
);
char vendorBuffer[512];
@@ -311,10 +311,10 @@ void cmdGet(CmdParser* parser) {
HARDWARE_MCU,
PROTOCOL_VERSION,
FIRMWARE_VERSION,
WiFiNetwork::getAddress().toString().c_str(),
wifiNetwork.getAddress().toString().c_str(),
WiFi.macAddress().c_str(),
statusManager.getStatus(),
WiFiNetwork::getWiFiState()
wifiNetwork.getWiFiState()
);
auto& sensor0 = sensorManager.getSensors()[0];
sensor0->motionLoop();
@@ -347,8 +347,8 @@ void cmdGet(CmdParser* parser) {
if (WiFi.status() != WL_CONNECTED) {
WiFi.disconnect();
}
if (WiFiNetwork::isProvisioning()) {
WiFiNetwork::stopProvisioning();
if (wifiProvisioning.isProvisioning()) {
wifiProvisioning.stopProvisioning();
}
WiFi.scanNetworks();