From 93b1d47bdac908d6d750864bd36990ea3514af8f Mon Sep 17 00:00:00 2001 From: iranl Date: Fri, 18 Oct 2024 20:36:32 +0200 Subject: [PATCH] Improve Wifi connection and portal --- src/Config.h | 2 +- src/NukiNetwork.cpp | 17 +-- src/NukiNetwork.h | 2 +- src/WebCfgServer.cpp | 136 +++++++++++++-------- src/WebCfgServer.h | 2 +- src/networkDevices/WifiDevice.cpp | 195 ++++++++++++++---------------- src/networkDevices/WifiDevice.h | 1 + 7 files changed, 193 insertions(+), 162 deletions(-) diff --git a/src/Config.h b/src/Config.h index e302821..fd8bfc4 100644 --- a/src/Config.h +++ b/src/Config.h @@ -4,7 +4,7 @@ #define NUKI_HUB_VERSION "9.02" #define NUKI_HUB_BUILD "unknownbuildnr" -#define NUKI_HUB_DATE "unknowndate" +#define NUKI_HUB_DATE "2024-10-18" #define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest" #define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json" diff --git a/src/NukiNetwork.cpp b/src/NukiNetwork.cpp index 529bcde..b8498bf 100644 --- a/src/NukiNetwork.cpp +++ b/src/NukiNetwork.cpp @@ -170,6 +170,11 @@ NetworkDevice *NukiNetwork::device() return _device; } +bool NukiNetwork::isConnected() +{ + return _device->isConnected(); +} + #ifdef NUKI_HUB_UPDATER void NukiNetwork::initialize() { @@ -427,7 +432,7 @@ bool NukiNetwork::update() }); } - if(_logIp && device()->isConnected() && !_device->localIP().equals("0.0.0.0")) + if(_logIp && _device->isConnected() && !_device->localIP().equals("0.0.0.0")) { _logIp = false; Log->print(F("IP: ")); @@ -1083,11 +1088,12 @@ void NukiNetwork::publishHASSConfig(char* deviceType, const char* baseTopic, cha baseTopic, _lockPath + mqtt_topic_uptime, deviceType, - "", + "duration", "", "diagnostic", "", - { { (char*)"en", (char*)"true" }}); + { { (char*)"en", (char*)"true" }, + { (char*)"unit_of_meas", (char*)"min"}}); if(_preferences->getBool(preference_mqtt_log_enabled, false)) { @@ -3906,9 +3912,4 @@ String NukiNetwork::localIP() { return _device->localIP(); } - -bool NukiNetwork::isConnected() -{ - return _device->isConnected(); -} #endif \ No newline at end of file diff --git a/src/NukiNetwork.h b/src/NukiNetwork.h index 602d74b..79872d7 100644 --- a/src/NukiNetwork.h +++ b/src/NukiNetwork.h @@ -26,6 +26,7 @@ public: void reconfigureDevice(); void scan(bool passive = false, bool async = true); bool isApOpen(); + bool isConnected(); void clearWifiFallback(); const String networkDeviceName() const; @@ -44,7 +45,6 @@ public: void disableAutoRestarts(); // disable on OTA start void disableMqtt(); String localIP(); - bool isConnected(); void subscribe(const char* prefix, const char* path); void initTopic(const char* prefix, const char* path, const char* value); diff --git a/src/WebCfgServer.cpp b/src/WebCfgServer.cpp index eb72821..ed4d76a 100644 --- a/src/WebCfgServer.cpp +++ b/src/WebCfgServer.cpp @@ -302,6 +302,28 @@ void WebCfgServer::initialize() } } +void WebCfgServer::printCheckBox(PsychicStreamResponse *response, const char *token, const char *description, const bool value, const char *htmlClass) +{ + response->print(""); + response->print(description); + response->print(""); + + response->print("print(token); + response->print("\" value=\"0\""); + response->print("/>"); + + response->print("print(token); + + response->print("\" class=\""); + response->print(htmlClass); + + response->print("\" value=\"1\""); + response->print(value ? " checked=\"checked\"" : ""); + response->print("/>"); +} + #ifndef CONFIG_IDF_TARGET_ESP32H2 esp_err_t WebCfgServer::buildSSIDListHtml(PsychicRequest *request) { @@ -377,6 +399,14 @@ esp_err_t WebCfgServer::buildWifiConnectHtml(PsychicRequest *request) printInputField(&response, "WIFISSID", "SSID", "", 32, "id=\"inputssid\"", false, true); printInputField(&response, "WIFIPASS", "Secret key", "", 63, "id=\"inputpass\"", false, true); response.print(""); + response.print("

IP Address assignment

"); + response.print(""); + printCheckBox(&response, "DHCPENA", "Enable DHCP", _preferences->getBool(preference_ip_dhcp_enabled), ""); + printInputField(&response, "IPADDR", "Static IP address", _preferences->getString(preference_ip_address).c_str(), 15, ""); + printInputField(&response, "IPSUB", "Subnet", _preferences->getString(preference_ip_subnet).c_str(), 15, ""); + printInputField(&response, "IPGTW", "Default gateway", _preferences->getString(preference_ip_gateway).c_str(), 15, ""); + printInputField(&response, "DNSSRV", "DNS Server", _preferences->getString(preference_ip_dns_server).c_str(), 15, ""); + response.print("
"); response.print("
"); response.print(""); response.print("

"); @@ -412,6 +442,41 @@ bool WebCfgServer::processWiFi(PsychicRequest *request, String& message) { pass = value; } + else if(key == "DHCPENA") + { + if(_preferences->getBool(preference_ip_dhcp_enabled, true) != (value == "1")) + { + _preferences->putBool(preference_ip_dhcp_enabled, (value == "1")); + } + } + else if(key == "IPADDR") + { + if(_preferences->getString(preference_ip_address, "") != value) + { + _preferences->putString(preference_ip_address, value); + } + } + else if(key == "IPSUB") + { + if(_preferences->getString(preference_ip_subnet, "") != value) + { + _preferences->putString(preference_ip_subnet, value); + } + } + else if(key == "IPGTW") + { + if(_preferences->getString(preference_ip_gateway, "") != value) + { + _preferences->putString(preference_ip_gateway, value); + } + } + else if(key == "DNSSRV") + { + if(_preferences->getString(preference_ip_dns_server, "") != value) + { + _preferences->putString(preference_ip_dns_server, value); + } + } } ssid.trim(); @@ -419,55 +484,50 @@ bool WebCfgServer::processWiFi(PsychicRequest *request, String& message) if (ssid.length() > 0 && pass.length() > 0) { + if (_preferences->getBool(preference_ip_dhcp_enabled, true) && _preferences->getString(preference_ip_address, "").length() <= 0) + { + const IPConfiguration* _ipConfiguration = new IPConfiguration(_preferences); + + if(!_ipConfiguration->dhcpEnabled()) + { + WiFi.config(_ipConfiguration->ipAddress(), _ipConfiguration->dnsServer(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet()); + } + } + WiFi.begin(ssid, pass); int loop = 0; - while(WiFi.status() != WL_CONNECTED && loop < 150) + while(!_network->isConnected() && loop < 150) { delay(100); loop++; } - if (WiFi.status() != WL_CONNECTED) + if (!_network->isConnected()) { message = "Failed to connect to the given SSID with the given secret key, credentials not saved
"; + return res; } else { - message = "Connection successful. Rebooting Nuki Hub.
"; - if(WiFi.isConnected()) + if(_network->isConnected()) { - esp_wifi_disconnect(); + message = "Connection successful. Rebooting Nuki Hub.
"; + _preferences->putString(preference_wifi_ssid, ssid); + _preferences->putString(preference_wifi_pass, pass); + res = true; } - - wifi_config_t wifi_cfg; - if(esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { - Log->println("Failed to get Wi-Fi configuration in RAM"); + else + { + message = "Failed to connect to the given SSID, no IP received, credentials not saved
"; return res; } - - if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) { - Log->println("Failed to set storage Wi-Fi"); - return res; - } - - memset(wifi_cfg.sta.ssid, 0, sizeof(wifi_cfg.sta.ssid)); - memset(wifi_cfg.sta.password, 0, sizeof(wifi_cfg.sta.password)); - - if (esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { - Log->println("Failed to set Wi-Fi configuration"); - return res; - } - - _preferences->putString(preference_wifi_ssid, ssid); - _preferences->putString(preference_wifi_pass, pass); - - res = true; } } else { message = "No SSID or secret key entered, credentials not saved
"; + return res; } return res; @@ -4213,28 +4273,6 @@ esp_err_t WebCfgServer::processFactoryReset(PsychicRequest *request) return res; } -void WebCfgServer::printCheckBox(PsychicStreamResponse *response, const char *token, const char *description, const bool value, const char *htmlClass) -{ - response->print(""); - response->print(description); - response->print(""); - - response->print("print(token); - response->print("\" value=\"0\""); - response->print("/>"); - - response->print("print(token); - - response->print("\" class=\""); - response->print(htmlClass); - - response->print("\" value=\"1\""); - response->print(value ? " checked=\"checked\"" : ""); - response->print("/>"); -} - void WebCfgServer::printTextarea(PsychicStreamResponse *response, const char *token, const char *description, diff --git a/src/WebCfgServer.h b/src/WebCfgServer.h index 0a6e75d..e9be8b9 100644 --- a/src/WebCfgServer.h +++ b/src/WebCfgServer.h @@ -67,7 +67,6 @@ private: esp_err_t processUnpair(PsychicRequest *request, bool opener); esp_err_t processUpdate(PsychicRequest *request); esp_err_t processFactoryReset(PsychicRequest *request); - void printCheckBox(PsychicStreamResponse *response, const char* token, const char* description, const bool value, const char* htmlClass); void printTextarea(PsychicStreamResponse *response, const char *token, const char *description, const char *value, const size_t& maxLength, const bool& enabled = true, const bool& showLengthRestriction = false); void printDropDown(PsychicStreamResponse *response, const char *token, const char *description, const String preselectedValue, std::vector> options, const String className); void buildNavigationButton(PsychicStreamResponse *response, const char* caption, const char* targetPath, const char* labelText = ""); @@ -107,6 +106,7 @@ private: void buildHtmlHeader(PsychicStreamResponse *response, String additionalHeader = ""); void waitAndProcess(const bool blocking, const uint32_t duration); esp_err_t handleOtaUpload(PsychicRequest *request, const String& filename, uint64_t index, uint8_t *data, size_t len, bool final); + void printCheckBox(PsychicStreamResponse *response, const char* token, const char* description, const bool value, const char* htmlClass); void printProgress(size_t prg, size_t sz); #ifndef CONFIG_IDF_TARGET_ESP32H2 esp_err_t buildWifiConnectHtml(PsychicRequest *request); diff --git a/src/networkDevices/WifiDevice.cpp b/src/networkDevices/WifiDevice.cpp index 01e6e35..5c7e893 100644 --- a/src/networkDevices/WifiDevice.cpp +++ b/src/networkDevices/WifiDevice.cpp @@ -22,20 +22,24 @@ void WifiDevice::initialize() String pass = _preferences->getString(preference_wifi_pass, ""); WiFi.setHostname(_hostname.c_str()); - if(!_ipConfiguration->dhcpEnabled()) - { - WiFi.config(_ipConfiguration->ipAddress(), _ipConfiguration->dnsServer(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet()); - } - WiFi.onEvent([&](WiFiEvent_t event, WiFiEventInfo_t info) { - if(event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) + if(event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED || event == ARDUINO_EVENT_WIFI_STA_STOP) { if(!_openAP && !_connecting && _connected) { onDisconnected(); + _hasIP = false; } } + else if(event == ARDUINO_EVENT_WIFI_STA_GOT_IP) + { + _hasIP = true; + } + else if(event == ARDUINO_EVENT_WIFI_STA_LOST_IP) + { + _hasIP = false; + } else if(event == ARDUINO_EVENT_WIFI_STA_CONNECTED) { onConnected(); @@ -117,10 +121,7 @@ void WifiDevice::initialize() } else { - Log->println("No SSID or Wifi password saved, opening AP"); - _connectOnScanDone = false; - _openAP = true; - scan(false, true); + restartEsp(RestartReason::ReconfigureWifi); return; } } @@ -158,23 +159,28 @@ void WifiDevice::initialize() void WifiDevice::scan(bool passive, bool async) { - WiFi.scanDelete(); + if(!_connecting) + { + WiFi.scanDelete(); + WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); + WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); - if(async) - { - Log->println(F("Wi-Fi async scan started")); - } - else - { - Log->println(F("Wi-Fi sync scan started")); - } - if(passive) - { - WiFi.scanNetworks(async,false,true,75U); - } - else - { - WiFi.scanNetworks(async); + if(async) + { + Log->println(F("Wi-Fi async scan started")); + } + else + { + Log->println(F("Wi-Fi sync scan started")); + } + if(passive) + { + WiFi.scanNetworks(async,false,true,75U); + } + else + { + WiFi.scanNetworks(async); + } } } @@ -182,12 +188,9 @@ void WifiDevice::openAP() { if(_startAP) { - WiFi.persistent(false); WiFi.mode(WIFI_AP_STA); - WiFi.persistent(false); WiFi.softAPsetHostname(_hostname.c_str()); WiFi.softAP("NukiHub", "NukiHubESP32"); - WiFi.persistent(false); _startAP = false; } } @@ -197,7 +200,6 @@ bool WifiDevice::connect() bool ret = false; String ssid = _preferences->getString(preference_wifi_ssid, ""); String pass = _preferences->getString(preference_wifi_pass, ""); - WiFi.persistent(false); WiFi.mode(WIFI_STA); WiFi.setHostname(_hostname.c_str()); delay(500); @@ -239,99 +241,75 @@ bool WifiDevice::connect() else { _connecting = true; + esp_wifi_scan_stop(); Log->println(String(F("Trying to connect to SSID ")) + ssid + String(F(" found with RSSI: ")) + String(WiFi.RSSI(bestConnection)) + String(F("(")) + String(constrain((100.0 + WiFi.RSSI(bestConnection)) * 2, 0, 100)) + String(F(" %) and BSSID: ")) + WiFi.BSSIDstr(bestConnection) + String(F(" and channel: ")) + String(WiFi.channel(bestConnection))); - ret = WiFi.begin(ssid.c_str(), pass.c_str(), WiFi.channel(bestConnection), WiFi.BSSID(bestConnection), true); - WiFi.persistent(false); - _connecting = false; - } - - if(!ret) - { - int loop = 0; - - while(!isConnected() && loop < 200) + + + if(!_ipConfiguration->dhcpEnabled()) { - loop++; - delay(100); + WiFi.config(_ipConfiguration->ipAddress(), _ipConfiguration->dnsServer(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet()); } - - if(!isConnected()) + + WiFi.begin(ssid, pass); + auto status = WiFi.waitForConnectResult(10000); + + switch (status) { - esp_wifi_disconnect(); - esp_wifi_stop(); - esp_wifi_deinit(); - - Log->println(F("Failed to connect. Wait for ESP restart.")); - delay(1000); - restartEsp(RestartReason::WifiInitFailed); + case WL_CONNECTED: + Log->println("WiFi connected"); + break; + case WL_NO_SSID_AVAIL: + Log->println("WiFi SSID not available"); + break; + case WL_CONNECT_FAILED: + Log->println("WiFi connection failed"); + break; + case WL_IDLE_STATUS: + Log->println("WiFi changing status"); + break; + case WL_DISCONNECTED: + Log->println("WiFi disconnected"); + break; + default: + Log->println("WiFi timeout"); + break; } - } - else - { - if(!_preferences->getBool(preference_wifi_converted, false)) - { - _preferences->putBool(preference_wifi_converted, true); - } - - int loop = 0; - - while(!isConnected() && loop < 200) - { - loop++; - delay(100); - } - - if(!isConnected()) + + if (status != WL_CONNECTED) { if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) { restartEsp(RestartReason::RestartOnDisconnectWatchdog); + _connecting = false; return false; } - Log->print("Connection failed, retrying"); + Log->println("Retrying"); _connectOnScanDone = true; _openAP = false; scan(false, true); + _connecting = false; return false; } + else + { + if(!_preferences->getBool(preference_wifi_converted, false)) + { + _preferences->putBool(preference_wifi_converted, true); + } + _connecting = false; + return true; + } } - return ret; + return false; } void WifiDevice::reconfigure() { - bool changed = false; - wifi_config_t wifi_cfg; - if(esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { - Log->println("Failed to get Wi-Fi configuration in RAM"); - } - - if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK) { - Log->println("Failed to set storage Wi-Fi"); - } - - if(sizeof(wifi_cfg.sta.ssid) > 0) - { - memset(wifi_cfg.sta.ssid, 0, sizeof(wifi_cfg.sta.ssid)); - changed = true; - } - if(sizeof(wifi_cfg.sta.password) > 0) - { - memset(wifi_cfg.sta.password, 0, sizeof(wifi_cfg.sta.password)); - changed = true; - } - if(changed) - { - if (esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) { - Log->println("Failed to clear NVS Wi-Fi configuration"); - } - } - _preferences->putString(preference_wifi_ssid, ""); _preferences->putString(preference_wifi_pass, ""); delay(200); @@ -340,7 +318,16 @@ void WifiDevice::reconfigure() bool WifiDevice::isConnected() { - return (WiFi.status() == WL_CONNECTED); + if (WiFi.status() != WL_CONNECTED) + { + return false; + } + if (!_hasIP) + { + return false; + } + + return true; } void WifiDevice::onConnected() @@ -363,12 +350,17 @@ void WifiDevice::onDisconnected() _connecting = true; String ssid = _preferences->getString(preference_wifi_ssid, ""); String pass = _preferences->getString(preference_wifi_pass, ""); - WiFi.begin(ssid.c_str(), pass.c_str(), _connectedChannel, _connectedBSSID, true); - WiFi.persistent(false); + + if(!_ipConfiguration->dhcpEnabled()) + { + WiFi.config(_ipConfiguration->ipAddress(), _ipConfiguration->dnsServer(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet()); + } + + WiFi.begin(ssid, pass); int loop = 0; - while(!isConnected() && loop < 50) + while(!isConnected() && loop < 200) { loop++; delay(100); @@ -381,7 +373,6 @@ void WifiDevice::onDisconnected() { if(_preferences->getBool(preference_restart_on_disconnect, false) && ((esp_timer_get_time() / 1000) > 60000)) restartEsp(RestartReason::RestartOnDisconnectWatchdog); - WiFi.persistent(false); WiFi.disconnect(true); WiFi.mode(WIFI_STA); WiFi.disconnect(); diff --git a/src/networkDevices/WifiDevice.h b/src/networkDevices/WifiDevice.h index 55da63f..50e9d25 100644 --- a/src/networkDevices/WifiDevice.h +++ b/src/networkDevices/WifiDevice.h @@ -40,6 +40,7 @@ private: bool _startAP = true; bool _convertOldWiFi = false; bool _connected = false; + bool _hasIP = false; uint8_t _connectedChannel = 0; uint8_t* _connectedBSSID; int64_t _disconnectTs = 0;