470 lines
14 KiB
C++
470 lines
14 KiB
C++
#include "esp_wifi.h"
|
|
#include <WiFi.h>
|
|
#include "WifiDevice.h"
|
|
#include "../PreferencesKeys.h"
|
|
#include "../Logger.h"
|
|
#ifndef NUKI_HUB_UPDATER
|
|
#include "../MqttTopics.h"
|
|
#include "espMqttClient.h"
|
|
#endif
|
|
#include "../RestartReason.h"
|
|
|
|
WifiDevice::WifiDevice(const String& hostname, Preferences* preferences, const IPConfiguration* ipConfiguration)
|
|
: NetworkDevice(hostname, ipConfiguration),
|
|
_preferences(preferences)
|
|
{
|
|
#ifndef NUKI_HUB_UPDATER
|
|
size_t caLength = preferences->getString(preference_mqtt_ca, _ca, TLS_CA_MAX_SIZE);
|
|
size_t crtLength = preferences->getString(preference_mqtt_crt, _cert, TLS_CERT_MAX_SIZE);
|
|
size_t keyLength = preferences->getString(preference_mqtt_key, _key, TLS_KEY_MAX_SIZE);
|
|
|
|
_useEncryption = caLength > 1; // length is 1 when empty
|
|
|
|
if(_useEncryption)
|
|
{
|
|
Log->println(F("MQTT over TLS."));
|
|
_mqttClientSecure = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO);
|
|
_mqttClientSecure->setCACert(_ca);
|
|
if(crtLength > 1 && keyLength > 1) // length is 1 when empty
|
|
{
|
|
Log->println(F("MQTT with client certificate."));
|
|
_mqttClientSecure->setCertificate(_cert);
|
|
_mqttClientSecure->setPrivateKey(_key);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log->println(F("MQTT without TLS."));
|
|
_mqttClient = new espMqttClient(espMqttClientTypes::UseInternalTask::NO);
|
|
}
|
|
|
|
if(preferences->getBool(preference_mqtt_log_enabled, false) || preferences->getBool(preference_webserial_enabled, false))
|
|
{
|
|
MqttLoggerMode mode;
|
|
|
|
if(preferences->getBool(preference_mqtt_log_enabled, false) && preferences->getBool(preference_webserial_enabled, false)) mode = MqttLoggerMode::MqttAndSerialAndWeb;
|
|
else if (preferences->getBool(preference_webserial_enabled, false)) mode = MqttLoggerMode::SerialAndWeb;
|
|
else mode = MqttLoggerMode::MqttAndSerial;
|
|
_path = new char[200];
|
|
memset(_path, 0, sizeof(_path));
|
|
String pathStr = preferences->getString(preference_mqtt_lock_path);
|
|
pathStr.concat(mqtt_topic_log);
|
|
strcpy(_path, pathStr.c_str());
|
|
Log = new MqttLogger(*getMqttClient(), _path, mode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
const String WifiDevice::deviceName() const
|
|
{
|
|
return "Built-in Wi-Fi";
|
|
}
|
|
|
|
void WifiDevice::initialize()
|
|
{
|
|
String ssid = _preferences->getString(preference_wifi_ssid, "");
|
|
String pass = _preferences->getString(preference_wifi_pass, "");
|
|
WiFi.setHostname(_hostname.c_str());
|
|
|
|
WiFi.onEvent([&](WiFiEvent_t event, WiFiEventInfo_t info)
|
|
{
|
|
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();
|
|
}
|
|
else if(event == ARDUINO_EVENT_WIFI_SCAN_DONE)
|
|
{
|
|
Log->println(F("Wi-Fi scan done"));
|
|
_foundNetworks = WiFi.scanComplete();
|
|
|
|
for (int i = 0; i < _foundNetworks; i++)
|
|
{
|
|
Log->println(String(F("SSID ")) + WiFi.SSID(i) + String(F(" found with RSSI: ")) +
|
|
String(WiFi.RSSI(i)) + String(F("(")) +
|
|
String(constrain((100.0 + WiFi.RSSI(i)) * 2, 0, 100)) +
|
|
String(F(" %) and BSSID: ")) + WiFi.BSSIDstr(i) +
|
|
String(F(" and channel: ")) + String(WiFi.channel(i)));
|
|
}
|
|
|
|
if (_connectOnScanDone && _foundNetworks > 0)
|
|
{
|
|
connect();
|
|
}
|
|
else if (_connectOnScanDone)
|
|
{
|
|
Log->println("No networks found, restarting scan");
|
|
scan(false, true);
|
|
}
|
|
else if (_openAP)
|
|
{
|
|
openAP();
|
|
}
|
|
else if(_convertOldWiFi)
|
|
{
|
|
Log->println("Trying to convert old WiFi settings");
|
|
_convertOldWiFi = false;
|
|
_preferences->putBool(preference_wifi_converted, 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");
|
|
}
|
|
|
|
if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) != ESP_OK)
|
|
{
|
|
Log->println("Failed to set storage Wi-Fi");
|
|
}
|
|
|
|
String tempSSID = String(reinterpret_cast<const char*>(wifi_cfg.sta.ssid));
|
|
String tempPass = String(reinterpret_cast<const char*>(wifi_cfg.sta.password));
|
|
tempSSID.trim();
|
|
tempPass.trim();
|
|
bool found = false;
|
|
|
|
for (int i = 0; i < _foundNetworks; i++)
|
|
{
|
|
if(tempSSID.length() > 0 && tempSSID == WiFi.SSID(i) && tempPass.length() > 0)
|
|
{
|
|
_preferences->putString(preference_wifi_ssid, tempSSID);
|
|
_preferences->putString(preference_wifi_pass, tempPass);
|
|
Log->println("Succesfully converted old WiFi settings");
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 clear NVS Wi-Fi configuration");
|
|
}
|
|
|
|
if(found)
|
|
{
|
|
Log->println(String("Attempting to connect to saved SSID ") + String(ssid));
|
|
_connectOnScanDone = true;
|
|
_openAP = false;
|
|
scan(false, true);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
restartEsp(RestartReason::ReconfigureWifi);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
ssid.trim();
|
|
pass.trim();
|
|
|
|
if(ssid.length() > 0 && pass.length() > 0)
|
|
{
|
|
Log->println(String("Attempting to connect to saved SSID ") + String(ssid));
|
|
_connectOnScanDone = true;
|
|
_openAP = false;
|
|
scan(false, true);
|
|
return;
|
|
}
|
|
else if(!_preferences->getBool(preference_wifi_converted, false))
|
|
{
|
|
_connectOnScanDone = false;
|
|
_openAP = false;
|
|
_convertOldWiFi = true;
|
|
scan(false, true);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Log->println("No SSID or Wifi password saved, opening AP");
|
|
_connectOnScanDone = false;
|
|
_openAP = true;
|
|
scan(false, true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void WifiDevice::scan(bool passive, bool async)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WifiDevice::openAP()
|
|
{
|
|
if(_startAP)
|
|
{
|
|
WiFi.mode(WIFI_AP_STA);
|
|
WiFi.softAPsetHostname(_hostname.c_str());
|
|
WiFi.softAP("NukiHub", "NukiHubESP32");
|
|
_startAP = false;
|
|
}
|
|
}
|
|
|
|
bool WifiDevice::connect()
|
|
{
|
|
bool ret = false;
|
|
String ssid = _preferences->getString(preference_wifi_ssid, "");
|
|
String pass = _preferences->getString(preference_wifi_pass, "");
|
|
WiFi.mode(WIFI_STA);
|
|
WiFi.setHostname(_hostname.c_str());
|
|
delay(500);
|
|
|
|
int bestConnection = -1;
|
|
for (int i = 0; i < _foundNetworks; i++)
|
|
{
|
|
if (ssid == WiFi.SSID(i))
|
|
{
|
|
Log->println(String(F("Saved SSID ")) + ssid + String(F(" found with RSSI: ")) +
|
|
String(WiFi.RSSI(i)) + String(F("(")) +
|
|
String(constrain((100.0 + WiFi.RSSI(i)) * 2, 0, 100)) +
|
|
String(F(" %) and BSSID: ")) + WiFi.BSSIDstr(i) +
|
|
String(F(" and channel: ")) + String(WiFi.channel(i)));
|
|
if (bestConnection == -1)
|
|
{
|
|
bestConnection = i;
|
|
}
|
|
else
|
|
{
|
|
if (WiFi.RSSI(i) > WiFi.RSSI(bestConnection))
|
|
{
|
|
bestConnection = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestConnection == -1)
|
|
{
|
|
Log->print("No network found with SSID: ");
|
|
Log->println(ssid);
|
|
if(_preferences->getBool(preference_restart_on_disconnect, false) && (espMillis() > 60000))
|
|
{
|
|
restartEsp(RestartReason::RestartOnDisconnectWatchdog);
|
|
}
|
|
_connectOnScanDone = true;
|
|
_openAP = false;
|
|
scan(false, true);
|
|
return false;
|
|
}
|
|
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)));
|
|
|
|
|
|
if(!_ipConfiguration->dhcpEnabled())
|
|
{
|
|
WiFi.config(_ipConfiguration->ipAddress(), _ipConfiguration->dnsServer(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet());
|
|
}
|
|
|
|
WiFi.begin(ssid, pass);
|
|
auto status = WiFi.waitForConnectResult(10000);
|
|
|
|
switch (status)
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (status != WL_CONNECTED)
|
|
{
|
|
if(_preferences->getBool(preference_restart_on_disconnect, false) && (espMillis() > 60000))
|
|
{
|
|
restartEsp(RestartReason::RestartOnDisconnectWatchdog);
|
|
_connecting = false;
|
|
return false;
|
|
}
|
|
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 false;
|
|
}
|
|
|
|
void WifiDevice::reconfigure()
|
|
{
|
|
_preferences->putString(preference_wifi_ssid, "");
|
|
_preferences->putString(preference_wifi_pass, "");
|
|
delay(200);
|
|
restartEsp(RestartReason::ReconfigureWifi);
|
|
}
|
|
|
|
bool WifiDevice::isConnected()
|
|
{
|
|
if (WiFi.status() != WL_CONNECTED)
|
|
{
|
|
return false;
|
|
}
|
|
if (!_hasIP)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WifiDevice::onConnected()
|
|
{
|
|
Log->println(F("Wi-Fi connected"));
|
|
_connectedChannel = WiFi.channel();
|
|
_connectedBSSID = WiFi.BSSID();
|
|
_connected = true;
|
|
}
|
|
|
|
void WifiDevice::onDisconnected()
|
|
{
|
|
if(_connected)
|
|
{
|
|
_connected = false;
|
|
_disconnectTs = espMillis();
|
|
Log->println(F("Wi-Fi disconnected"));
|
|
|
|
//QUICK RECONNECT
|
|
_connecting = true;
|
|
String ssid = _preferences->getString(preference_wifi_ssid, "");
|
|
String pass = _preferences->getString(preference_wifi_pass, "");
|
|
|
|
if(!_ipConfiguration->dhcpEnabled())
|
|
{
|
|
WiFi.config(_ipConfiguration->ipAddress(), _ipConfiguration->dnsServer(), _ipConfiguration->defaultGateway(), _ipConfiguration->subnet());
|
|
}
|
|
|
|
WiFi.begin(ssid, pass);
|
|
|
|
int loop = 0;
|
|
|
|
while(!isConnected() && loop < 200)
|
|
{
|
|
loop++;
|
|
delay(100);
|
|
}
|
|
|
|
_connecting = false;
|
|
//END QUICK RECONNECT
|
|
|
|
if(!isConnected())
|
|
{
|
|
if(_preferences->getBool(preference_restart_on_disconnect, false) && (espMillis() > 60000))
|
|
{
|
|
restartEsp(RestartReason::RestartOnDisconnectWatchdog);
|
|
}
|
|
|
|
WiFi.disconnect(true);
|
|
WiFi.mode(WIFI_STA);
|
|
WiFi.disconnect();
|
|
delay(500);
|
|
|
|
wifi_mode_t wifiMode;
|
|
esp_wifi_get_mode(&wifiMode);
|
|
|
|
while (wifiMode != WIFI_MODE_STA || WiFi.status() == WL_CONNECTED)
|
|
{
|
|
delay(500);
|
|
Log->println(F("Waiting for WiFi mode change or disconnection."));
|
|
esp_wifi_get_mode(&wifiMode);
|
|
}
|
|
|
|
_connectOnScanDone = true;
|
|
_openAP = false;
|
|
scan(false, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
int8_t WifiDevice::signalStrength()
|
|
{
|
|
return WiFi.RSSI();
|
|
}
|
|
|
|
String WifiDevice::localIP()
|
|
{
|
|
return WiFi.localIP().toString();
|
|
}
|
|
|
|
String WifiDevice::BSSIDstr()
|
|
{
|
|
return WiFi.BSSIDstr();
|
|
}
|
|
|
|
bool WifiDevice::isApOpen()
|
|
{
|
|
return _openAP;
|
|
} |