From e34a1f0719be4e27ddcc3d2a275c342b013f9e7c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 19 Nov 2018 18:07:25 +0100 Subject: [PATCH] Add wifi network scan * Add command SetOption56 0/1 to enable wifi network scan and select highest RSSI (#3173) --- sonoff/_changelog.ino | 2 + sonoff/my_user_config.h | 6 +- sonoff/settings.h | 4 +- sonoff/sonoff.ino | 11 - sonoff/support.ino | 444 ---------------------------- sonoff/support_wifi.ino | 548 +++++++++++++++++++++++++++++++++++ sonoff/xdrv_01_webserver.ino | 3 + 7 files changed, 558 insertions(+), 460 deletions(-) create mode 100644 sonoff/support_wifi.ino diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index b60ebf9f0..6d13b0c17 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,5 +1,7 @@ /* 6.3.0.10 20181118 * Add command SetOption36 0..255 milliseconds (50 default) to tune main loop dynamic delay + * Add support for LG HVac and IrRemote (#4377) + * Add command SetOption56 0/1 to enable wifi network scan and select highest RSSI (#3173) * * 6.3.0.9 20181118 * Moved command SetSensorXX to debugging driver freeing user code space diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 3e0efdef4..004e93a09 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -365,9 +365,9 @@ #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) // -- Low level interface devices ----------------- -#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) -// #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) - #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) +#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k3 code, 0k3 mem, 48 iram) +// #define USE_IR_HVAC // Support for HVAC (Toshiba, Mitsubishi and LG) system using IR (+3k5 code) + #define USE_IR_RECEIVE // Support for IR receiver (+6k5 code, 264 iram) #define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // #define USE_WS2812_CTYPE NEO_GRB // WS2812 Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) diff --git a/sonoff/settings.h b/sonoff/settings.h index 124314f0a..cc4d9840f 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -68,8 +68,8 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t time_append_timezone : 1; // bit 2 (v6.2.1.2) uint32_t gui_hostname_ip : 1; // bit 3 (v6.2.1.20) uint32_t tuya_apply_o20 : 1; // bit 4 (v6.3.0.4) - uint32_t hass_short_discovery_msg : 1; // bit 5 (vTBD) - uint32_t spare06 : 1; + uint32_t hass_short_discovery_msg : 1; // bit 5 (v6.3.0.7) + uint32_t use_wifi_scan : 1; // bit 6 (v6.3.0.10) uint32_t spare07 : 1; uint32_t spare08 : 1; uint32_t spare09 : 1; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 4930fdab4..486a3ba93 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -46,15 +46,10 @@ // Libraries #include // RTC, Energy, OSWatch -#include // MQTT, Ota, WifiManager #include // MQTT, Ota #include // Ota #include // Webserver, Updater #include // WemoHue, IRremote, Domoticz -#ifdef USE_WEBSERVER - #include // WifiManager, Webserver - #include // WifiManager -#endif // USE_WEBSERVER #ifdef USE_ARDUINO_OTA #include // Arduino OTA #ifndef USE_DISCOVERY @@ -100,12 +95,6 @@ const uint8_t kIFan02Speed[4][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}}; // Global variables SerialConfig serial_config = SERIAL_8N1; // Serial interface configuration 8 data bits, No parity, 1 stop bit -#ifdef USE_MQTT_TLS - WiFiClientSecure EspClient; // Wifi Secure Client -#else - WiFiClient EspClient; // Wifi Client -#endif - WiFiUDP PortUdp; // UDP Syslog and Alexa unsigned long feature_drv1; // Compiled driver feature map diff --git a/sonoff/support.ino b/sonoff/support.ino index 02e8ad74f..64af2e472 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -1178,450 +1178,6 @@ void GetFeatures(void) } -/*********************************************************************************************\ - * Wifi -\*********************************************************************************************/ - -#define WIFI_CONFIG_SEC 180 // seconds before restart -#define WIFI_CHECK_SEC 20 // seconds -#define WIFI_RETRY_OFFSET_SEC 20 // seconds - -uint8_t wifi_counter; -uint8_t wifi_retry_init; -uint8_t wifi_retry; -uint8_t wifi_status; -uint8_t wps_result; -uint8_t wifi_config_type = 0; -uint8_t wifi_config_counter = 0; - -int WifiGetRssiAsQuality(int rssi) -{ - int quality = 0; - - if (rssi <= -100) { - quality = 0; - } else if (rssi >= -50) { - quality = 100; - } else { - quality = 2 * (rssi + 100); - } - return quality; -} - -boolean WifiConfigCounter(void) -{ - if (wifi_config_counter) { - wifi_config_counter = WIFI_CONFIG_SEC; - } - return (wifi_config_counter); -} - -extern "C" { -#include "user_interface.h" -} - -void WifiWpsStatusCallback(wps_cb_status status); - -void WifiWpsStatusCallback(wps_cb_status status) -{ -/* from user_interface.h: - enum wps_cb_status { - WPS_CB_ST_SUCCESS = 0, - WPS_CB_ST_FAILED, - WPS_CB_ST_TIMEOUT, - WPS_CB_ST_WEP, // WPS failed because that WEP is not supported - WPS_CB_ST_SCAN_ERR, // can not find the target WPS AP - }; -*/ - wps_result = status; - if (WPS_CB_ST_SUCCESS == wps_result) { - wifi_wps_disable(); - } else { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_WPS_FAILED_WITH_STATUS " %d"), wps_result); - AddLog(LOG_LEVEL_DEBUG); - wifi_config_counter = 2; - } -} - -boolean WifiWpsConfigDone(void) -{ - return (!wps_result); -} - -boolean WifiWpsConfigBegin(void) -{ - wps_result = 99; - if (!wifi_wps_disable()) { return false; } - if (!wifi_wps_enable(WPS_TYPE_PBC)) { return false; } // so far only WPS_TYPE_PBC is supported (SDK 2.0.0) - if (!wifi_set_wps_cb((wps_st_cb_t) &WifiWpsStatusCallback)) { return false; } - if (!wifi_wps_start()) { return false; } - return true; -} - -void WifiConfig(uint8_t type) -{ - if (!wifi_config_type) { - if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; } -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) - UdpDisconnect(); -#endif // USE_EMULATION - WiFi.disconnect(); // Solve possible Wifi hangs - wifi_config_type = type; - -#ifndef USE_WPS - if (WIFI_WPSCONFIG == wifi_config_type) { wifi_config_type = WIFI_MANAGER; } -#endif // USE_WPS -#ifndef USE_WEBSERVER - if (WIFI_MANAGER == wifi_config_type) { wifi_config_type = WIFI_SMARTCONFIG; } -#endif // USE_WEBSERVER -#ifndef USE_SMARTCONFIG - if (WIFI_SMARTCONFIG == wifi_config_type) { wifi_config_type = WIFI_SERIAL; } -#endif // USE_SMARTCONFIG - - wifi_config_counter = WIFI_CONFIG_SEC; // Allow up to WIFI_CONFIG_SECS seconds for phone to provide ssid/pswd - wifi_counter = wifi_config_counter +5; - blinks = 1999; - if (WIFI_RESTART == wifi_config_type) { - restart_flag = 2; - } - else if (WIFI_SERIAL == wifi_config_type) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES)); - } -#ifdef USE_SMARTCONFIG - else if (WIFI_SMARTCONFIG == wifi_config_type) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_1_SMARTCONFIG " " D_ACTIVE_FOR_3_MINUTES)); - WiFi.beginSmartConfig(); - } -#endif // USE_SMARTCONFIG -#ifdef USE_WPS - else if (WIFI_WPSCONFIG == wifi_config_type) { - if (WifiWpsConfigBegin()) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_3_WPSCONFIG " " D_ACTIVE_FOR_3_MINUTES)); - } else { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_3_WPSCONFIG " " D_FAILED_TO_START)); - wifi_config_counter = 3; - } - } -#endif // USE_WPS -#ifdef USE_WEBSERVER - else if (WIFI_MANAGER == wifi_config_type) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES)); - WifiManagerBegin(); - } -#endif // USE_WEBSERVER - } -} - -void WiFiSetSleepMode(void) -{ -/* Excerpt from the esp8266 non os sdk api reference (v2.2.1): - * Sets sleep type for power saving. Set WIFI_NONE_SLEEP to disable power saving. - * - Default mode: WIFI_MODEM_SLEEP. - * - In order to lower the power comsumption, ESP8266 changes the TCP timer - * tick from 250ms to 3s in WIFI_LIGHT_SLEEP mode, which leads to increased timeout for - * TCP timer. Therefore, the WIFI_MODEM_SLEEP or deep-sleep mode should be used - * where there is a requirement for the accurancy of the TCP timer. - * - * Sleep is disabled in core 2.4.1 and 2.4.2 as there are bugs in their SDKs - * See https://github.com/arendst/Sonoff-Tasmota/issues/2559 - */ - -// Sleep explanation: https://github.com/esp8266/Arduino/blob/3f0c601cfe81439ce17e9bd5d28994a7ed144482/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp#L255 -#if defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) -#else // Enabled in 2.3.0, 2.4.0 and stage - if (sleep) { - WiFi.setSleepMode(WIFI_LIGHT_SLEEP); // Allow light sleep during idle times - } else { - WiFi.setSleepMode(WIFI_MODEM_SLEEP); // Disable sleep (Esp8288/Arduino core and sdk default) - } -#endif -} - -void WifiBegin(uint8_t flag) -{ - const char kWifiPhyMode[] = " BGN"; - -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) - UdpDisconnect(); -#endif // USE_EMULATION - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 // (!strncmp_P(ESP.getSdkVersion(),PSTR("1.5.3"),5)) - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_PATCH_ISSUE_2186)); - WiFi.mode(WIFI_OFF); // See https://github.com/esp8266/Arduino/issues/2186 -#endif - - WiFi.persistent(false); // Solve possible wifi init errors (re-add at 6.2.1.16 #4044, #4083) - WiFi.disconnect(true); // Delete SDK wifi config - delay(200); - WiFi.mode(WIFI_STA); // Disable AP mode - WiFiSetSleepMode(); -// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11N) { WiFi.setPhyMode(WIFI_PHY_MODE_11N); } - if (!WiFi.getAutoConnect()) { WiFi.setAutoConnect(true); } -// WiFi.setAutoReconnect(true); - switch (flag) { - case 0: // AP1 - case 1: // AP2 - Settings.sta_active = flag; - break; - case 2: // Toggle - Settings.sta_active ^= 1; - } // 3: Current AP - if ('\0' == Settings.sta_ssid[Settings.sta_active][0]) { Settings.sta_active ^= 1; } // Skip empty SSID - if (Settings.ip_address[0]) { - WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); // Set static IP - } - WiFi.hostname(my_hostname); - WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active]); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s " D_IN_MODE " 11%c " D_AS " %s..."), - Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); - AddLog(LOG_LEVEL_INFO); -} - -void WifiSetState(uint8_t state) -{ - if (state == global_state.wifi_down) { - if (state) { - rules_flag.wifi_connected = 1; - } else { - rules_flag.wifi_disconnected = 1; - } - } - global_state.wifi_down = state ^1; -} - -void WifiCheckIp(void) -{ - if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0)) { - WifiSetState(1); - wifi_counter = WIFI_CHECK_SEC; - wifi_retry = wifi_retry_init; - AddLog_P((wifi_status != WL_CONNECTED) ? LOG_LEVEL_INFO : LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CONNECTED)); - if (wifi_status != WL_CONNECTED) { -// AddLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Set IP addresses")); - Settings.ip_address[1] = (uint32_t)WiFi.gatewayIP(); - Settings.ip_address[2] = (uint32_t)WiFi.subnetMask(); - Settings.ip_address[3] = (uint32_t)WiFi.dnsIP(); - } - wifi_status = WL_CONNECTED; - } else { - WifiSetState(0); - uint8_t wifi_config_tool = Settings.sta_config; - wifi_status = WiFi.status(); - switch (wifi_status) { - case WL_CONNECTED: - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_NO_IP_ADDRESS)); - wifi_status = 0; - wifi_retry = wifi_retry_init; - break; - case WL_NO_SSID_AVAIL: - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED)); - if (WIFI_WAIT == Settings.sta_config) { - wifi_retry = wifi_retry_init; - } else { - if (wifi_retry > (wifi_retry_init / 2)) { - wifi_retry = wifi_retry_init / 2; - } - else if (wifi_retry) { - wifi_retry = 0; - } - } - break; - case WL_CONNECT_FAILED: - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD)); - if (wifi_retry > (wifi_retry_init / 2)) { - wifi_retry = wifi_retry_init / 2; - } - else if (wifi_retry) { - wifi_retry = 0; - } - break; - default: // WL_IDLE_STATUS and WL_DISCONNECTED - if (!wifi_retry || ((wifi_retry_init / 2) == wifi_retry)) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT)); - } else { - if (('\0' == Settings.sta_ssid[0][0]) && ('\0' == Settings.sta_ssid[1][0])) { - wifi_config_tool = WIFI_CONFIG_NO_SSID; // Skip empty SSIDs and start Wifi config tool - wifi_retry = 0; - } else { - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_ATTEMPTING_CONNECTION)); - } - } - } - if (wifi_retry) { - if (wifi_retry_init == wifi_retry) { - WifiBegin(3); // Select default SSID - } - if ((Settings.sta_config != WIFI_WAIT) && ((wifi_retry_init / 2) == wifi_retry)) { - WifiBegin(2); // Select alternate SSID - } - wifi_counter = 1; - wifi_retry--; - } else { - WifiConfig(wifi_config_tool); - wifi_counter = 1; - wifi_retry = wifi_retry_init; - } - } -} - -void WifiCheck(uint8_t param) -{ - wifi_counter--; - switch (param) { - case WIFI_SERIAL: - case WIFI_SMARTCONFIG: - case WIFI_MANAGER: - case WIFI_WPSCONFIG: - WifiConfig(param); - break; - default: - if (wifi_config_counter) { - wifi_config_counter--; - wifi_counter = wifi_config_counter +5; - if (wifi_config_counter) { -#ifdef USE_SMARTCONFIG - if ((WIFI_SMARTCONFIG == wifi_config_type) && WiFi.smartConfigDone()) { - wifi_config_counter = 0; - } -#endif // USE_SMARTCONFIG -#ifdef USE_WPS - if ((WIFI_WPSCONFIG == wifi_config_type) && WifiWpsConfigDone()) { - wifi_config_counter = 0; - } -#endif // USE_WPS - if (!wifi_config_counter) { - if (strlen(WiFi.SSID().c_str())) { - strlcpy(Settings.sta_ssid[0], WiFi.SSID().c_str(), sizeof(Settings.sta_ssid[0])); - } - if (strlen(WiFi.psk().c_str())) { - strlcpy(Settings.sta_pwd[0], WiFi.psk().c_str(), sizeof(Settings.sta_pwd[0])); - } - Settings.sta_active = 0; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_WCFG_1_SMARTCONFIG D_CMND_SSID "1 %s"), Settings.sta_ssid[0]); - AddLog(LOG_LEVEL_INFO); - } - } - if (!wifi_config_counter) { -#ifdef USE_SMARTCONFIG - if (WIFI_SMARTCONFIG == wifi_config_type) { WiFi.stopSmartConfig(); } -#endif // USE_SMARTCONFIG -// SettingsSdkErase(); // Disabled v6.1.0b due to possible bad wifi connects - restart_flag = 2; - } - } else { - if (wifi_counter <= 0) { - AddLog_P(LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CHECKING_CONNECTION)); - wifi_counter = WIFI_CHECK_SEC; - WifiCheckIp(); - } - if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !wifi_config_type) { - WifiSetState(1); -#ifdef BE_MINIMAL - if (1 == RtcSettings.ota_loader) { - RtcSettings.ota_loader = 0; - ota_state_flag = 3; - } -#endif // BE_MINIMAL - -#ifdef USE_DISCOVERY - if (!mdns_begun) { - if (mdns_delayed_start) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_ATTEMPTING_CONNECTION)); - mdns_delayed_start--; - } else { - mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START]; - mdns_begun = MDNS.begin(my_hostname); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MDNS "%s"), (mdns_begun) ? D_INITIALIZED : D_FAILED); - AddLog(LOG_LEVEL_INFO); - } - } -#endif // USE_DISCOVERY - -#ifdef USE_WEBSERVER - if (Settings.webserver) { - StartWebserver(Settings.webserver, WiFi.localIP()); -#ifdef USE_DISCOVERY -#ifdef WEBSERVER_ADVERTISE - if (mdns_begun) { - MDNS.addService("http", "tcp", WEB_PORT); - } -#endif // WEBSERVER_ADVERTISE -#endif // USE_DISCOVERY - } else { - StopWebserver(); - } -#ifdef USE_EMULATION - if (Settings.flag2.emulation) { UdpConnect(); } -#endif // USE_EMULATION -#endif // USE_WEBSERVER - -#ifdef USE_KNX - if (!knx_started && Settings.flag.knx_enabled) { - KNXStart(); - knx_started = true; - } -#endif // USE_KNX - - } else { - WifiSetState(0); -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) - UdpDisconnect(); -#endif // USE_EMULATION - mdns_begun = false; -#ifdef USE_KNX - knx_started = false; -#endif // USE_KNX - } - } - } -} - -int WifiState(void) -{ - int state = -1; - - if (!global_state.wifi_down) { state = WIFI_RESTART; } - if (wifi_config_type) { state = wifi_config_type; } - return state; -} - -void WifiConnect(void) -{ - WifiSetState(0); - WiFi.persistent(false); // Solve possible wifi init errors - wifi_status = 0; - wifi_retry_init = WIFI_RETRY_OFFSET_SEC + ((ESP.getChipId() & 0xF) * 2); - wifi_retry = wifi_retry_init; - wifi_counter = 1; -} - -// Enable from 6.0.0a until 6.1.0a - disabled due to possible cause of bad wifi connect on core 2.3.0 -// Re-enabled from 6.3.0.7 with ESP.restart replaced by ESP.reset -void WifiDisconnect(void) -{ - // Courtesy of EspEasy - WiFi.persistent(true); // use SDK storage of SSID/WPA parameters - ETS_UART_INTR_DISABLE(); - wifi_station_disconnect(); // this will store empty ssid/wpa into sdk storage - ETS_UART_INTR_ENABLE(); - WiFi.persistent(false); // Do not use SDK storage of SSID/WPA parameters -} - -void EspRestart(void) -{ - delay(100); // Allow time for message xfer - disabled v6.1.0b - WifiDisconnect(); -// ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 - ESP.reset(); -} - -/* -void EspRestart(void) -{ - ESP.restart(); -} -*/ - /*********************************************************************************************\ * Basic I2C routines \*********************************************************************************************/ diff --git a/sonoff/support_wifi.ino b/sonoff/support_wifi.ino new file mode 100644 index 000000000..dbd0f24a5 --- /dev/null +++ b/sonoff/support_wifi.ino @@ -0,0 +1,548 @@ +/* + support_wifi.ino - wifi support for Sonoff-Tasmota + + Copyright (C) 2018 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*********************************************************************************************\ + * Wifi +\*********************************************************************************************/ + +#define WIFI_CONFIG_SEC 180 // seconds before restart +#define WIFI_CHECK_SEC 20 // seconds +#define WIFI_RETRY_OFFSET_SEC 20 // seconds + +#include // Wifi, MQTT, Ota, WifiManager +#include + +#ifdef USE_MQTT_TLS + WiFiClientSecure EspClient; // Wifi Secure Client +#else + WiFiClient EspClient; // Wifi Client +#endif + +uint8_t wifi_counter; +uint8_t wifi_retry_init; +uint8_t wifi_retry; +uint8_t wifi_status; +uint8_t wps_result; +uint8_t wifi_config_type = 0; +uint8_t wifi_config_counter = 0; + +uint8_t wifi_scan_state; +int8_t wifi_best_ap = 3; +uint8_t wifi_best_bssid[6]; +int32_t wifi_best_channel; + +int WifiGetRssiAsQuality(int rssi) +{ + int quality = 0; + + if (rssi <= -100) { + quality = 0; + } else if (rssi >= -50) { + quality = 100; + } else { + quality = 2 * (rssi + 100); + } + return quality; +} + +boolean WifiConfigCounter(void) +{ + if (wifi_config_counter) { + wifi_config_counter = WIFI_CONFIG_SEC; + } + return (wifi_config_counter); +} + +extern "C" { +#include "user_interface.h" +} + +void WifiWpsStatusCallback(wps_cb_status status); + +void WifiWpsStatusCallback(wps_cb_status status) +{ +/* from user_interface.h: + enum wps_cb_status { + WPS_CB_ST_SUCCESS = 0, + WPS_CB_ST_FAILED, + WPS_CB_ST_TIMEOUT, + WPS_CB_ST_WEP, // WPS failed because that WEP is not supported + WPS_CB_ST_SCAN_ERR, // can not find the target WPS AP + }; +*/ + wps_result = status; + if (WPS_CB_ST_SUCCESS == wps_result) { + wifi_wps_disable(); + } else { + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_WPS_FAILED_WITH_STATUS " %d"), wps_result); + AddLog(LOG_LEVEL_DEBUG); + wifi_config_counter = 2; + } +} + +boolean WifiWpsConfigDone(void) +{ + return (!wps_result); +} + +boolean WifiWpsConfigBegin(void) +{ + wps_result = 99; + if (!wifi_wps_disable()) { return false; } + if (!wifi_wps_enable(WPS_TYPE_PBC)) { return false; } // so far only WPS_TYPE_PBC is supported (SDK 2.0.0) + if (!wifi_set_wps_cb((wps_st_cb_t) &WifiWpsStatusCallback)) { return false; } + if (!wifi_wps_start()) { return false; } + return true; +} + +void WifiConfig(uint8_t type) +{ + if (!wifi_config_type) { + if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; } +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) + UdpDisconnect(); +#endif // USE_EMULATION + WiFi.disconnect(); // Solve possible Wifi hangs + wifi_config_type = type; + +#ifndef USE_WPS + if (WIFI_WPSCONFIG == wifi_config_type) { wifi_config_type = WIFI_MANAGER; } +#endif // USE_WPS +#ifndef USE_WEBSERVER + if (WIFI_MANAGER == wifi_config_type) { wifi_config_type = WIFI_SMARTCONFIG; } +#endif // USE_WEBSERVER +#ifndef USE_SMARTCONFIG + if (WIFI_SMARTCONFIG == wifi_config_type) { wifi_config_type = WIFI_SERIAL; } +#endif // USE_SMARTCONFIG + + wifi_config_counter = WIFI_CONFIG_SEC; // Allow up to WIFI_CONFIG_SECS seconds for phone to provide ssid/pswd + wifi_counter = wifi_config_counter +5; + blinks = 1999; + if (WIFI_RESTART == wifi_config_type) { + restart_flag = 2; + } + else if (WIFI_SERIAL == wifi_config_type) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES)); + } +#ifdef USE_SMARTCONFIG + else if (WIFI_SMARTCONFIG == wifi_config_type) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_1_SMARTCONFIG " " D_ACTIVE_FOR_3_MINUTES)); + WiFi.beginSmartConfig(); + } +#endif // USE_SMARTCONFIG +#ifdef USE_WPS + else if (WIFI_WPSCONFIG == wifi_config_type) { + if (WifiWpsConfigBegin()) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_3_WPSCONFIG " " D_ACTIVE_FOR_3_MINUTES)); + } else { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_3_WPSCONFIG " " D_FAILED_TO_START)); + wifi_config_counter = 3; + } + } +#endif // USE_WPS +#ifdef USE_WEBSERVER + else if (WIFI_MANAGER == wifi_config_type) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES)); + WifiManagerBegin(); + } +#endif // USE_WEBSERVER + } +} + +void WiFiSetSleepMode(void) +{ +/* Excerpt from the esp8266 non os sdk api reference (v2.2.1): + * Sets sleep type for power saving. Set WIFI_NONE_SLEEP to disable power saving. + * - Default mode: WIFI_MODEM_SLEEP. + * - In order to lower the power comsumption, ESP8266 changes the TCP timer + * tick from 250ms to 3s in WIFI_LIGHT_SLEEP mode, which leads to increased timeout for + * TCP timer. Therefore, the WIFI_MODEM_SLEEP or deep-sleep mode should be used + * where there is a requirement for the accurancy of the TCP timer. + * + * Sleep is disabled in core 2.4.1 and 2.4.2 as there are bugs in their SDKs + * See https://github.com/arendst/Sonoff-Tasmota/issues/2559 + */ + +// Sleep explanation: https://github.com/esp8266/Arduino/blob/3f0c601cfe81439ce17e9bd5d28994a7ed144482/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp#L255 +#if defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) +#else // Enabled in 2.3.0, 2.4.0 and stage + if (sleep) { + WiFi.setSleepMode(WIFI_LIGHT_SLEEP); // Allow light sleep during idle times + } else { + WiFi.setSleepMode(WIFI_MODEM_SLEEP); // Disable sleep (Esp8288/Arduino core and sdk default) + } +#endif +} + +void WifiBegin(uint8_t flag) +{ + const char kWifiPhyMode[] = " BGN"; + +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) + UdpDisconnect(); +#endif // USE_EMULATION + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 // (!strncmp_P(ESP.getSdkVersion(),PSTR("1.5.3"),5)) + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_PATCH_ISSUE_2186)); + WiFi.mode(WIFI_OFF); // See https://github.com/esp8266/Arduino/issues/2186 +#endif + + WiFi.persistent(false); // Solve possible wifi init errors (re-add at 6.2.1.16 #4044, #4083) + WiFi.disconnect(true); // Delete SDK wifi config + delay(200); + WiFi.mode(WIFI_STA); // Disable AP mode + WiFiSetSleepMode(); +// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11N) { WiFi.setPhyMode(WIFI_PHY_MODE_11N); } + if (!WiFi.getAutoConnect()) { WiFi.setAutoConnect(true); } +// WiFi.setAutoReconnect(true); + switch (flag) { + case 0: // AP1 + case 1: // AP2 + Settings.sta_active = flag; + break; + case 2: // Toggle + Settings.sta_active ^= 1; + } // 3: Current AP + if ('\0' == Settings.sta_ssid[Settings.sta_active][0]) { Settings.sta_active ^= 1; } // Skip empty SSID + if (Settings.ip_address[0]) { + WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); // Set static IP + } + WiFi.hostname(my_hostname); + if (wifi_best_ap < 3) { + WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active], wifi_best_channel, wifi_best_bssid); + } else { + WiFi.begin(Settings.sta_ssid[Settings.sta_active], Settings.sta_pwd[Settings.sta_active]); + } + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s " D_IN_MODE " 11%c " D_AS " %s..."), + Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); + AddLog(LOG_LEVEL_INFO); +} + +void WifiSetState(uint8_t state) +{ + if (state == global_state.wifi_down) { + if (state) { + rules_flag.wifi_connected = 1; + } else { + rules_flag.wifi_disconnected = 1; + } + } + global_state.wifi_down = state ^1; +} + +void WifiCheckIp(void) +{ + if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0)) { + WifiSetState(1); + wifi_counter = WIFI_CHECK_SEC; + wifi_retry = wifi_retry_init; + AddLog_P((wifi_status != WL_CONNECTED) ? LOG_LEVEL_INFO : LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CONNECTED)); + if (wifi_status != WL_CONNECTED) { +// AddLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Set IP addresses")); + Settings.ip_address[1] = (uint32_t)WiFi.gatewayIP(); + Settings.ip_address[2] = (uint32_t)WiFi.subnetMask(); + Settings.ip_address[3] = (uint32_t)WiFi.dnsIP(); + } + wifi_status = WL_CONNECTED; + } else { + WifiSetState(0); + uint8_t wifi_config_tool = Settings.sta_config; + wifi_status = WiFi.status(); + switch (wifi_status) { + case WL_CONNECTED: + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_NO_IP_ADDRESS)); + wifi_status = 0; + wifi_retry = wifi_retry_init; + break; + case WL_NO_SSID_AVAIL: + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED)); + if (WIFI_WAIT == Settings.sta_config) { + wifi_retry = wifi_retry_init; + } else { + if (wifi_retry > (wifi_retry_init / 2)) { + wifi_retry = wifi_retry_init / 2; + } + else if (wifi_retry) { + wifi_retry = 0; + } + } + break; + case WL_CONNECT_FAILED: + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD)); + if (wifi_retry > (wifi_retry_init / 2)) { + wifi_retry = wifi_retry_init / 2; + } + else if (wifi_retry) { + wifi_retry = 0; + } + break; + default: // WL_IDLE_STATUS and WL_DISCONNECTED + if (!wifi_retry || ((wifi_retry_init / 2) == wifi_retry)) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT)); + } else { + if (('\0' == Settings.sta_ssid[0][0]) && ('\0' == Settings.sta_ssid[1][0])) { + wifi_config_tool = WIFI_CONFIG_NO_SSID; // Skip empty SSIDs and start Wifi config tool + wifi_retry = 0; + } else { + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_ATTEMPTING_CONNECTION)); + } + } + } + if (wifi_retry) { + if (!Settings.flag3.use_wifi_scan) { + if (wifi_retry_init == wifi_retry) { + WifiBegin(3); // Select default SSID + } + if ((Settings.sta_config != WIFI_WAIT) && ((wifi_retry_init / 2) == wifi_retry)) { + WifiBegin(2); // Select alternate SSID + } + } else { + if (wifi_retry_init == wifi_retry) { + wifi_best_ap = 3; + wifi_scan_state = 1; + } + if (1 == wifi_scan_state) { + if (WiFi.scanComplete() != WIFI_SCAN_RUNNING) { + WiFi.disconnect(); + WiFi.scanNetworks(true); // Start wifi scan async + wifi_scan_state = 2; + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR("Network scan started...")); + } + } + int8_t wifi_scan_result = WiFi.scanComplete(); + if (2 == wifi_scan_state) { // Scan started + if (wifi_scan_result != WIFI_SCAN_RUNNING) { + wifi_scan_state = 3; + } + } + if (3 == wifi_scan_state) { // Scan done + if (wifi_scan_result > 0) { + int best_network_db = INT_MIN; + for (int8_t i = 0; i < wifi_scan_result; ++i) { + String ssid_scan; + int32_t rssi_scan; + uint8_t sec_scan; + uint8_t* bssid_scan; + int32_t chan_scan; + bool hidden_scan; + + WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, bssid_scan, chan_scan, hidden_scan); + + bool known = false; + uint8_t j; + for (j = 0; j < 2; j++) { + if (ssid_scan == Settings.sta_ssid[j]) { // SSID match + known = true; + if (rssi_scan > best_network_db) { // Best network + if (sec_scan == ENC_TYPE_NONE || Settings.sta_pwd[j]) { // Check for passphrase if not open wlan + best_network_db = rssi_scan; + wifi_best_channel = chan_scan; + wifi_best_ap = j; + memcpy((void*) &wifi_best_bssid, (void*) bssid_scan, sizeof(wifi_best_bssid)); + } + } + break; + } + } + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %02X:%02X:%02X:%02X:%02X:%02X, RSSI %d, Encryption %d"), + i, (known) ? (j) ? '2' : '1' : '-', ssid_scan.c_str(), chan_scan, bssid_scan[0], bssid_scan[1], bssid_scan[2], bssid_scan[3], bssid_scan[4], bssid_scan[5], rssi_scan, (sec_scan == ENC_TYPE_NONE) ? 0 : 1); + AddLog(LOG_LEVEL_DEBUG); + delay(0); + } + WiFi.scanDelete(); // Clean up ram + delay(0); + } else { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR("No network found")); + } + wifi_scan_state = 4; + } + if (4 == wifi_scan_state) { // Strongest found + WifiBegin(wifi_best_ap); // Select strongest or default SSID + wifi_scan_state = 0; + } + } + + wifi_counter = 1; + wifi_retry--; + } else { + WifiConfig(wifi_config_tool); + wifi_counter = 1; + wifi_retry = wifi_retry_init; + } + } +} + +void WifiCheck(uint8_t param) +{ + wifi_counter--; + switch (param) { + case WIFI_SERIAL: + case WIFI_SMARTCONFIG: + case WIFI_MANAGER: + case WIFI_WPSCONFIG: + WifiConfig(param); + break; + default: + if (wifi_config_counter) { + wifi_config_counter--; + wifi_counter = wifi_config_counter +5; + if (wifi_config_counter) { +#ifdef USE_SMARTCONFIG + if ((WIFI_SMARTCONFIG == wifi_config_type) && WiFi.smartConfigDone()) { + wifi_config_counter = 0; + } +#endif // USE_SMARTCONFIG +#ifdef USE_WPS + if ((WIFI_WPSCONFIG == wifi_config_type) && WifiWpsConfigDone()) { + wifi_config_counter = 0; + } +#endif // USE_WPS + if (!wifi_config_counter) { + if (strlen(WiFi.SSID().c_str())) { + strlcpy(Settings.sta_ssid[0], WiFi.SSID().c_str(), sizeof(Settings.sta_ssid[0])); + } + if (strlen(WiFi.psk().c_str())) { + strlcpy(Settings.sta_pwd[0], WiFi.psk().c_str(), sizeof(Settings.sta_pwd[0])); + } + Settings.sta_active = 0; + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_WIFI D_WCFG_1_SMARTCONFIG D_CMND_SSID "1 %s"), Settings.sta_ssid[0]); + AddLog(LOG_LEVEL_INFO); + } + } + if (!wifi_config_counter) { +#ifdef USE_SMARTCONFIG + if (WIFI_SMARTCONFIG == wifi_config_type) { WiFi.stopSmartConfig(); } +#endif // USE_SMARTCONFIG +// SettingsSdkErase(); // Disabled v6.1.0b due to possible bad wifi connects + restart_flag = 2; + } + } else { + if (wifi_counter <= 0) { + AddLog_P(LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CHECKING_CONNECTION)); + wifi_counter = WIFI_CHECK_SEC; + WifiCheckIp(); + } + if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !wifi_config_type) { + WifiSetState(1); +#ifdef BE_MINIMAL + if (1 == RtcSettings.ota_loader) { + RtcSettings.ota_loader = 0; + ota_state_flag = 3; + } +#endif // BE_MINIMAL + +#ifdef USE_DISCOVERY + if (!mdns_begun) { + if (mdns_delayed_start) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_ATTEMPTING_CONNECTION)); + mdns_delayed_start--; + } else { + mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START]; + mdns_begun = MDNS.begin(my_hostname); + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MDNS "%s"), (mdns_begun) ? D_INITIALIZED : D_FAILED); + AddLog(LOG_LEVEL_INFO); + } + } +#endif // USE_DISCOVERY + +#ifdef USE_WEBSERVER + if (Settings.webserver) { + StartWebserver(Settings.webserver, WiFi.localIP()); +#ifdef USE_DISCOVERY +#ifdef WEBSERVER_ADVERTISE + if (mdns_begun) { + MDNS.addService("http", "tcp", WEB_PORT); + } +#endif // WEBSERVER_ADVERTISE +#endif // USE_DISCOVERY + } else { + StopWebserver(); + } +#ifdef USE_EMULATION + if (Settings.flag2.emulation) { UdpConnect(); } +#endif // USE_EMULATION +#endif // USE_WEBSERVER + +#ifdef USE_KNX + if (!knx_started && Settings.flag.knx_enabled) { + KNXStart(); + knx_started = true; + } +#endif // USE_KNX + + } else { + WifiSetState(0); +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) + UdpDisconnect(); +#endif // USE_EMULATION + mdns_begun = false; +#ifdef USE_KNX + knx_started = false; +#endif // USE_KNX + } + } + } +} + +int WifiState(void) +{ + int state = -1; + + if (!global_state.wifi_down) { state = WIFI_RESTART; } + if (wifi_config_type) { state = wifi_config_type; } + return state; +} + +void WifiConnect(void) +{ + WifiSetState(0); + WiFi.persistent(false); // Solve possible wifi init errors + wifi_status = 0; + wifi_retry_init = WIFI_RETRY_OFFSET_SEC + ((ESP.getChipId() & 0xF) * 2); + wifi_retry = wifi_retry_init; + wifi_counter = 1; +} + +// Enable from 6.0.0a until 6.1.0a - disabled due to possible cause of bad wifi connect on core 2.3.0 +// Re-enabled from 6.3.0.7 with ESP.restart replaced by ESP.reset +void WifiDisconnect(void) +{ + // Courtesy of EspEasy + WiFi.persistent(true); // use SDK storage of SSID/WPA parameters + ETS_UART_INTR_DISABLE(); + wifi_station_disconnect(); // this will store empty ssid/wpa into sdk storage + ETS_UART_INTR_ENABLE(); + WiFi.persistent(false); // Do not use SDK storage of SSID/WPA parameters +} + +void EspRestart(void) +{ + delay(100); // Allow time for message xfer - disabled v6.1.0b + WifiDisconnect(); +// ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 + ESP.reset(); +} + +/* +void EspRestart(void) +{ + ESP.restart(); +} +*/ + diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index a4cf8c137..85f1c71e2 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -29,6 +29,9 @@ #define HTTP_REFRESH_TIME 2345 // milliseconds +#include // WifiManager, Webserver +#include // WifiManager + #ifdef USE_RF_FLASH uint8_t *efm8bb1_update = NULL; #endif // USE_RF_FLASH