2018-11-19 17:07:25 +00:00
/*
2019-10-27 10:13:24 +00:00
support_wifi . ino - wifi support for Tasmota
2018-11-19 17:07:25 +00:00
2021-01-01 12:44:04 +00:00
Copyright ( C ) 2021 Theo Arends
2018-11-19 17:07:25 +00:00
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 < http : //www.gnu.org/licenses/>.
*/
/*********************************************************************************************\
* Wifi
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-02-26 12:45:46 +00:00
// Enable one of three below options for wifi re-connection debugging
//#define WIFI_FORCE_RF_CAL_ERASE // Erase rf calibration sector on restart only
2020-02-25 15:49:19 +00:00
//#define WIFI_RF_MODE_RF_CAL // Set RF_MODE to RF_CAL for restart and deepsleep during user_rf_pre_init
2020-02-25 15:52:07 +00:00
//#define WIFI_RF_PRE_INIT // Set RF_MODE to RF_CAL for restart, deepsleep and power on during user_rf_pre_init
2020-02-25 15:49:19 +00:00
2018-11-20 13:10:32 +00:00
# ifndef WIFI_RSSI_THRESHOLD
2020-02-20 10:24:35 +00:00
# define WIFI_RSSI_THRESHOLD 10 // Difference in dB between current network and scanned network
2018-11-20 13:10:32 +00:00
# endif
# ifndef WIFI_RESCAN_MINUTES
2020-02-20 10:24:35 +00:00
# define WIFI_RESCAN_MINUTES 44 // Number of minutes between wifi network rescan
2018-11-20 13:10:32 +00:00
# endif
2021-10-26 18:11:21 +01:00
# ifndef WIFI_RETRY_SECONDS
2022-06-10 13:49:35 +01:00
# define WIFI_RETRY_SECONDS 20 // Number of seconds connection to wifi network will retry
2021-10-26 18:11:21 +01:00
# endif
2018-11-20 13:10:32 +00:00
2019-03-31 10:59:04 +01:00
const uint8_t WIFI_CONFIG_SEC = 180 ; // seconds before restart
2020-02-20 10:24:35 +00:00
const uint8_t WIFI_CHECK_SEC = 20 ; // seconds
2021-10-26 18:11:21 +01:00
const uint8_t WIFI_RETRY_OFFSET_SEC = WIFI_RETRY_SECONDS ; // seconds
2018-11-19 17:07:25 +00:00
2019-08-18 12:23:43 +01:00
# include <ESP8266WiFi.h> // Wifi, MQTT, Ota, WifiManager
2022-12-27 20:59:34 +00:00
# include "lwip/dns.h"
2023-09-03 18:02:44 +01:00
# if ESP_IDF_VERSION_MAJOR >= 5
# include "esp_netif.h"
# endif
2018-11-19 17:07:25 +00:00
2022-08-07 14:52:23 +01:00
int WifiGetRssiAsQuality ( int rssi ) {
2018-11-19 17:07:25 +00:00
int quality = 0 ;
if ( rssi < = - 100 ) {
quality = 0 ;
} else if ( rssi > = - 50 ) {
quality = 100 ;
} else {
quality = 2 * ( rssi + 100 ) ;
}
return quality ;
}
2022-08-07 14:52:23 +01:00
// 0 1 2 3 4
const char kWifiEncryptionTypes [ ] PROGMEM = " OPEN|WEP|WPA/PSK|WPA2/PSK|WPA/WPA2/PSK "
# ifdef ESP32
2022-08-07 15:24:59 +01:00
// 5 6 7 8
" |WPA2-Enterprise|WPA3/PSK|WPA2/WPA3/PSK|WAPI/PSK "
2022-08-07 14:52:23 +01:00
# endif // ESP32
;
String WifiEncryptionType ( uint32_t i ) {
# ifdef ESP8266
// Reference. WiFi.encryptionType =
// 2 : ENC_TYPE_TKIP - WPA / PSK
// 4 : ENC_TYPE_CCMP - WPA2 / PSK
// 5 : ENC_TYPE_WEP - WEP
// 7 : ENC_TYPE_NONE - open network
// 8 : ENC_TYPE_AUTO - WPA / WPA2 / PSK
uint8_t typea [ ] = { 0 , 2 , 0 , 3 , 1 , 0 , 0 , 4 } ;
2022-08-07 15:24:59 +01:00
int type = typea [ WiFi . encryptionType ( i ) - 1 & 7 ] ;
2022-08-07 14:52:23 +01:00
# else
2022-08-07 15:24:59 +01:00
int type = WiFi . encryptionType ( i ) ;
2022-08-07 14:52:23 +01:00
# endif
2022-08-07 15:24:59 +01:00
if ( ( type < 0 ) | | ( type > 8 ) ) { type = 0 ; }
2022-08-07 14:52:23 +01:00
char stemp1 [ 20 ] ;
GetTextIndexed ( stemp1 , sizeof ( stemp1 ) , type , kWifiEncryptionTypes ) ;
return stemp1 ;
}
2019-01-28 13:08:33 +00:00
bool WifiConfigCounter ( void )
2018-11-19 17:07:25 +00:00
{
2019-08-17 16:13:09 +01:00
if ( Wifi . config_counter ) {
Wifi . config_counter = WIFI_CONFIG_SEC ;
2018-11-19 17:07:25 +00:00
}
2019-08-17 16:13:09 +01:00
return ( Wifi . config_counter ) ;
2018-11-19 17:07:25 +00:00
}
void WifiConfig ( uint8_t type )
{
2019-08-17 16:13:09 +01:00
if ( ! Wifi . config_type ) {
2018-11-19 17:07:25 +00:00
if ( ( WIFI_RETRY = = type ) | | ( WIFI_WAIT = = type ) ) { return ; }
2019-05-20 14:09:42 +01:00
# ifdef USE_EMULATION
2018-11-19 17:07:25 +00:00
UdpDisconnect ( ) ;
# endif // USE_EMULATION
WiFi . disconnect ( ) ; // Solve possible Wifi hangs
2019-08-17 16:13:09 +01:00
Wifi . config_type = type ;
2018-11-19 17:07:25 +00:00
# ifndef USE_WEBSERVER
2019-10-22 15:34:25 +01:00
if ( WIFI_MANAGER = = Wifi . config_type ) {
Wifi . config_type = WIFI_SERIAL ;
}
2018-11-19 17:07:25 +00:00
# endif // USE_WEBSERVER
2019-08-17 16:13:09 +01:00
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 ;
2020-10-29 11:39:44 +00:00
TasmotaGlobal . blinks = 255 ;
2019-08-17 16:13:09 +01:00
if ( WIFI_RESTART = = Wifi . config_type ) {
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ;
2018-11-19 17:07:25 +00:00
}
2019-08-17 16:13:09 +01:00
else if ( WIFI_SERIAL = = Wifi . config_type ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES ) ) ;
2018-11-19 17:07:25 +00:00
}
# ifdef USE_WEBSERVER
2019-08-17 16:13:09 +01:00
else if ( WIFI_MANAGER = = Wifi . config_type | | WIFI_MANAGER_RESET_ONLY = = Wifi . config_type ) {
WifiManagerBegin ( WIFI_MANAGER_RESET_ONLY = = Wifi . config_type ) ;
2018-11-19 17:07:25 +00:00
}
# endif // USE_WEBSERVER
}
}
2022-04-11 15:40:41 +01:00
# ifdef CONFIG_IDF_TARGET_ESP32C3
// https://github.com/espressif/arduino-esp32/issues/6264#issuecomment-1040147331
// There's an include for this but it doesn't define the function if it doesn't think it needs it, so manually declare the function
extern " C " void phy_bbpll_en_usb ( bool en ) ;
# endif // CONFIG_IDF_TARGET_ESP32C3
2021-07-05 13:50:33 +01:00
void WifiSetMode ( WiFiMode_t wifi_mode ) {
2022-04-11 15:40:41 +01:00
# ifdef CONFIG_IDF_TARGET_ESP32C3
// https://github.com/espressif/arduino-esp32/issues/6264#issuecomment-1094376906
// This brings the USB serial-jtag back to life. Suggest doing this immediately after wifi startup.
phy_bbpll_en_usb ( true ) ;
# endif // CONFIG_IDF_TARGET_ESP32C3
2019-11-08 12:00:32 +00:00
if ( WiFi . getMode ( ) = = wifi_mode ) { return ; }
if ( wifi_mode ! = WIFI_OFF ) {
2021-07-05 16:11:03 +01:00
WiFi . hostname ( TasmotaGlobal . hostname ) ; // ESP32 needs this here (before WiFi.mode) for core 2.0.0
2021-07-05 13:50:33 +01:00
2019-11-08 12:00:32 +00:00
// See: https://github.com/esp8266/Arduino/issues/6172#issuecomment-500457407
WiFi . forceSleepWake ( ) ; // Make sure WiFi is really active.
delay ( 100 ) ;
}
2019-12-02 10:56:40 +00:00
uint32_t retry = 2 ;
while ( ! WiFi . mode ( wifi_mode ) & & retry - - ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI " Retry set Mode... " ) ) ;
2019-12-02 10:56:40 +00:00
delay ( 100 ) ;
2019-11-09 13:26:17 +00:00
}
2019-11-08 12:00:32 +00:00
if ( wifi_mode = = WIFI_OFF ) {
delay ( 1000 ) ;
WiFi . forceSleepBegin ( ) ;
delay ( 1 ) ;
} else {
delay ( 30 ) ; // Must allow for some time to init.
}
}
2018-11-19 17:07:25 +00:00
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 250 ms to 3 s 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
2019-10-27 10:13:24 +00:00
* See https : //github.com/arendst/Tasmota/issues/2559
2018-11-19 17:07:25 +00:00
*/
// Sleep explanation: https://github.com/esp8266/Arduino/blob/3f0c601cfe81439ce17e9bd5d28994a7ed144482/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp#L255
2021-01-13 14:54:43 +00:00
/*
2021-06-11 17:14:12 +01:00
if ( TasmotaGlobal . sleep & & Settings - > flag3 . sleep_normal ) { // SetOption60 - Enable normal sleep instead of dynamic sleep
2020-04-10 17:24:08 +01:00
WiFi . setSleepMode ( WIFI_LIGHT_SLEEP ) ; // Allow light sleep during idle times
2018-11-19 17:07:25 +00:00
} else {
2020-04-10 17:24:08 +01:00
WiFi . setSleepMode ( WIFI_MODEM_SLEEP ) ; // Disable sleep (Esp8288/Arduino core and sdk default)
2018-11-19 17:07:25 +00:00
}
2021-01-13 14:54:43 +00:00
*/
2021-07-05 12:43:41 +01:00
bool wifi_no_sleep = Settings - > flag5 . wifi_no_sleep ;
# ifdef CONFIG_IDF_TARGET_ESP32C3
2021-07-05 13:50:33 +01:00
wifi_no_sleep = true ; // Temporary patch for IDF4.4, wifi sleeping may cause wifi drops
2021-07-05 12:43:41 +01:00
# endif
if ( 0 = = TasmotaGlobal . sleep | | wifi_no_sleep ) {
2021-01-14 16:03:01 +00:00
if ( ! TasmotaGlobal . wifi_stay_asleep ) {
2021-01-13 14:54:43 +00:00
WiFi . setSleepMode ( WIFI_NONE_SLEEP ) ; // Disable sleep
2021-01-14 16:03:01 +00:00
}
} else {
2021-07-05 11:32:58 +01:00
if ( Settings - > flag3 . sleep_normal ) { // SetOption60 - Enable normal sleep instead of dynamic sleep
2021-01-14 16:03:01 +00:00
WiFi . setSleepMode ( WIFI_LIGHT_SLEEP ) ; // Allow light sleep during idle times
} else {
2021-01-13 14:54:43 +00:00
WiFi . setSleepMode ( WIFI_MODEM_SLEEP ) ; // Sleep (Esp8288/Arduino core and sdk default)
}
}
2018-11-19 17:07:25 +00:00
}
2022-11-20 11:52:24 +00:00
void WifiBegin ( uint8_t flag , uint8_t channel ) {
2019-05-20 14:09:42 +01:00
# ifdef USE_EMULATION
2018-11-19 17:07:25 +00:00
UdpDisconnect ( ) ;
# endif // USE_EMULATION
WiFi . persistent ( false ) ; // Solve possible wifi init errors (re-add at 6.2.1.16 #4044, #4083)
2022-12-10 12:44:16 +00:00
# if defined(USE_IPV6) && defined(ESP32)
2022-12-04 15:57:34 +00:00
WiFi . IPv6 ( true ) ;
# endif
2022-11-17 11:01:09 +00:00
2022-11-18 13:31:49 +00:00
# ifdef USE_WIFI_RANGE_EXTENDER
2022-11-20 15:28:59 +00:00
if ( WiFi . getMode ( ) ! = WIFI_AP_STA | | ! RgxApUp ( ) ) { // Preserve range extender connections (#17103)
2022-11-17 11:01:09 +00:00
WiFi . disconnect ( true ) ; // Delete SDK wifi config
2022-11-16 16:28:03 +00:00
delay ( 200 ) ;
2022-11-17 11:01:09 +00:00
WifiSetMode ( WIFI_STA ) ; // Disable AP mode
2022-11-16 16:28:03 +00:00
}
2022-11-18 13:31:49 +00:00
# else
WiFi . disconnect ( true ) ; // Delete SDK wifi config
delay ( 200 ) ;
WifiSetMode ( WIFI_STA ) ; // Disable AP mode
# endif
2022-11-17 11:01:09 +00:00
2018-11-19 17:07:25 +00:00
WiFiSetSleepMode ( ) ;
2023-05-17 10:44:14 +01:00
WifiSetOutputPower ( ) ;
2019-02-18 14:41:41 +00:00
// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11N) { WiFi.setPhyMode(WIFI_PHY_MODE_11N); } // B/G/N
// if (WiFi.getPhyMode() != WIFI_PHY_MODE_11G) { WiFi.setPhyMode(WIFI_PHY_MODE_11G); } // B/G
2022-10-15 13:33:21 +01:00
# ifdef ESP32
if ( Wifi . phy_mode ) {
WiFi . setPhyMode ( WiFiPhyMode_t ( Wifi . phy_mode ) ) ; // 1-B/2-BG/3-BGN
}
# endif
2018-11-19 17:07:25 +00:00
if ( ! WiFi . getAutoConnect ( ) ) { WiFi . setAutoConnect ( true ) ; }
2020-02-20 10:24:35 +00:00
// WiFi.setAutoReconnect(true);
2018-11-19 17:07:25 +00:00
switch ( flag ) {
case 0 : // AP1
case 1 : // AP2
2021-06-11 17:14:12 +01:00
Settings - > sta_active = flag ;
2018-11-19 17:07:25 +00:00
break ;
case 2 : // Toggle
2021-06-11 17:14:12 +01:00
Settings - > sta_active ^ = 1 ;
2018-11-19 17:07:25 +00:00
} // 3: Current AP
2021-06-11 17:14:12 +01:00
if ( ! strlen ( SettingsText ( SET_STASSID1 + Settings - > sta_active ) ) ) {
Settings - > sta_active ^ = 1 ; // Skip empty SSID
2019-12-16 14:13:57 +00:00
}
2021-06-11 17:14:12 +01:00
if ( Settings - > ipv4_address [ 0 ] ) {
2021-07-29 15:57:04 +01:00
WiFi . config ( Settings - > ipv4_address [ 0 ] , Settings - > ipv4_address [ 1 ] , Settings - > ipv4_address [ 2 ] , Settings - > ipv4_address [ 3 ] , Settings - > ipv4_address [ 4 ] ) ; // Set static IP
2018-11-19 17:07:25 +00:00
}
2021-07-05 16:11:03 +01:00
WiFi . hostname ( TasmotaGlobal . hostname ) ; // ESP8266 needs this here (after WiFi.mode)
2020-01-26 14:52:26 +00:00
char stemp [ 40 ] = { 0 } ;
2018-11-19 22:06:42 +00:00
if ( channel ) {
2021-06-11 17:14:12 +01:00
WiFi . begin ( SettingsText ( SET_STASSID1 + Settings - > sta_active ) , SettingsText ( SET_STAPWD1 + Settings - > sta_active ) , channel , Wifi . bssid ) ;
2020-01-26 14:52:26 +00:00
// Add connected BSSID and channel for multi-AP installations
char hex_char [ 18 ] ;
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " Channel %d BSSId %s " ) , channel , ToHex_P ( ( unsigned char * ) Wifi . bssid , 6 , hex_char , sizeof ( hex_char ) , ' : ' ) ) ;
2018-11-19 17:07:25 +00:00
} else {
2021-06-11 17:14:12 +01:00
WiFi . begin ( SettingsText ( SET_STASSID1 + Settings - > sta_active ) , SettingsText ( SET_STAPWD1 + Settings - > sta_active ) ) ;
2018-11-19 17:07:25 +00:00
}
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI D_CONNECTING_TO_AP " %d %s%s " D_IN_MODE " 11%c " D_AS " %s... " ) ,
2021-06-11 17:14:12 +01:00
Settings - > sta_active + 1 , SettingsText ( SET_STASSID1 + Settings - > sta_active ) , stemp , pgm_read_byte ( & kWifiPhyMode [ WiFi . getPhyMode ( ) & 0x3 ] ) , TasmotaGlobal . hostname ) ;
2020-01-26 13:30:11 +00:00
2022-06-10 13:49:35 +01:00
if ( Settings - > flag5 . wait_for_wifi_result ) { // SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1)
WiFi . waitForConnectResult ( 1000 ) ; // https://github.com/arendst/Tasmota/issues/14985
}
2018-11-19 17:07:25 +00:00
}
2019-11-20 19:53:12 +00:00
void WifiBeginAfterScan ( void )
2018-11-19 22:06:42 +00:00
{
// Not active
2019-08-17 16:13:09 +01:00
if ( 0 = = Wifi . scan_state ) { return ; }
2018-11-19 22:06:42 +00:00
// Init scan when not connected
2019-08-17 16:13:09 +01:00
if ( 1 = = Wifi . scan_state ) {
memset ( ( void * ) & Wifi . bssid , 0 , sizeof ( Wifi . bssid ) ) ;
2020-01-26 14:52:26 +00:00
Wifi . best_network_db = - 127 ;
2019-08-17 16:13:09 +01:00
Wifi . scan_state = 3 ;
2018-11-19 22:06:42 +00:00
}
// Init scan when connected
2019-08-17 16:13:09 +01:00
if ( 2 = = Wifi . scan_state ) {
2018-11-20 11:03:42 +00:00
uint8_t * bssid = WiFi . BSSID ( ) ; // Get current bssid
2019-08-17 16:13:09 +01:00
memcpy ( ( void * ) & Wifi . bssid , ( void * ) bssid , sizeof ( Wifi . bssid ) ) ;
2020-02-20 10:24:35 +00:00
Wifi . best_network_db = WiFi . RSSI ( ) ; // Get current rssi and add threshold
2020-01-26 14:52:26 +00:00
if ( Wifi . best_network_db < - WIFI_RSSI_THRESHOLD ) {
Wifi . best_network_db + = WIFI_RSSI_THRESHOLD ;
}
2019-08-17 16:13:09 +01:00
Wifi . scan_state = 3 ;
2018-11-19 22:06:42 +00:00
}
// Init scan
2019-08-17 16:13:09 +01:00
if ( 3 = = Wifi . scan_state ) {
2018-11-19 22:06:42 +00:00
if ( WiFi . scanComplete ( ) ! = WIFI_SCAN_RUNNING ) {
WiFi . scanNetworks ( true ) ; // Start wifi scan async
2019-08-17 16:13:09 +01:00
Wifi . scan_state + + ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_WIFI " Network (re)scan started... " ) ) ;
2018-11-19 22:06:42 +00:00
return ;
}
}
int8_t wifi_scan_result = WiFi . scanComplete ( ) ;
// Check scan done
2019-08-17 16:13:09 +01:00
if ( 4 = = Wifi . scan_state ) {
2018-11-19 22:06:42 +00:00
if ( wifi_scan_result ! = WIFI_SCAN_RUNNING ) {
2019-08-17 16:13:09 +01:00
Wifi . scan_state + + ;
2018-11-19 22:06:42 +00:00
}
}
// Scan done
2019-08-17 16:13:09 +01:00
if ( 5 = = Wifi . scan_state ) {
2018-11-19 22:06:42 +00:00
int32_t channel = 0 ; // No scan result
int8_t ap = 3 ; // AP default if not found
2018-11-20 11:03:42 +00:00
uint8_t last_bssid [ 6 ] ; // Save last bssid
2019-08-17 16:13:09 +01:00
memcpy ( ( void * ) & last_bssid , ( void * ) & Wifi . bssid , sizeof ( last_bssid ) ) ;
2018-11-19 22:06:42 +00:00
if ( wifi_scan_result > 0 ) {
// Networks found
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < wifi_scan_result ; + + i ) {
2018-11-19 22:06:42 +00:00
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 ;
2019-06-30 15:44:36 +01:00
uint32_t j ;
2020-01-12 12:10:21 +00:00
for ( j = 0 ; j < MAX_SSIDS ; j + + ) {
2019-12-16 14:13:57 +00:00
if ( ssid_scan = = SettingsText ( SET_STASSID1 + j ) ) { // SSID match
2018-11-19 22:06:42 +00:00
known = true ;
2020-01-26 14:52:26 +00:00
if ( rssi_scan > Wifi . best_network_db ) { // Best network
2019-12-16 14:13:57 +00:00
if ( sec_scan = = ENC_TYPE_NONE | | SettingsText ( SET_STAPWD1 + j ) ) { // Check for passphrase if not open wlan
2020-02-20 10:24:35 +00:00
Wifi . best_network_db = ( int8_t ) rssi_scan ;
channel = chan_scan ;
ap = j ; // AP1 or AP2
memcpy ( ( void * ) & Wifi . bssid , ( void * ) bssid_scan , sizeof ( Wifi . bssid ) ) ;
2018-11-19 22:06:42 +00:00
}
}
break ;
}
}
2019-08-10 16:34:59 +01:00
char hex_char [ 18 ] ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_WIFI " Network %d, AP%c, SSId %s, Channel %d, BSSId %s, RSSI %d, Encryption %d " ) ,
2019-08-10 16:34:59 +01:00
i ,
( known ) ? ( j ) ? ' 2 ' : ' 1 ' : ' - ' ,
ssid_scan . c_str ( ) ,
chan_scan ,
2019-08-13 18:53:12 +01:00
ToHex_P ( ( unsigned char * ) bssid_scan , 6 , hex_char , sizeof ( hex_char ) , ' : ' ) ,
2019-08-10 16:34:59 +01:00
rssi_scan ,
( sec_scan = = ENC_TYPE_NONE ) ? 0 : 1 ) ;
2018-11-19 22:06:42 +00:00
delay ( 0 ) ;
}
WiFi . scanDelete ( ) ; // Clean up Ram
delay ( 0 ) ;
}
2019-08-17 16:13:09 +01:00
Wifi . scan_state = 0 ;
2018-11-20 11:03:42 +00:00
// If bssid changed then (re)connect wifi
2019-08-17 16:13:09 +01:00
for ( uint32_t i = 0 ; i < sizeof ( Wifi . bssid ) ; i + + ) {
if ( last_bssid [ i ] ! = Wifi . bssid [ i ] ) {
2018-11-19 22:06:42 +00:00
WifiBegin ( ap , channel ) ; // 0 (AP1), 1 (AP2) or 3 (default AP)
break ;
}
}
}
2022-08-03 06:31:29 +01:00
// Init scan for wifiscan command
if ( 6 = = Wifi . scan_state ) {
if ( wifi_scan_result ! = WIFI_SCAN_RUNNING ) {
WiFi . scanNetworks ( true ) ; // Start wifi scan async
Wifi . scan_state + + ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_WIFI " Network scan started... " ) ) ;
return ;
}
}
// Check scan done
if ( 7 = = Wifi . scan_state ) {
if ( wifi_scan_result ! = WIFI_SCAN_RUNNING ) {
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_WIFI " Network scan finished... " ) ) ;
Wifi . scan_state + + ;
return ;
}
}
// Scan done. Show SSId's scan result by MQTT and in console
if ( 7 < Wifi . scan_state ) {
Wifi . scan_state + + ;
ResponseClear ( ) ;
2022-08-07 13:23:27 +01:00
2022-08-03 06:31:29 +01:00
uint32_t initial_item = ( Wifi . scan_state - 9 ) * 10 ;
if ( wifi_scan_result > initial_item ) {
// Sort networks by RSSI
uint32_t indexes [ wifi_scan_result ] ;
for ( uint32_t i = 0 ; i < wifi_scan_result ; i + + ) {
indexes [ i ] = i ;
}
for ( uint32_t i = 0 ; i < wifi_scan_result ; i + + ) {
for ( uint32_t j = i + 1 ; j < wifi_scan_result ; j + + ) {
if ( WiFi . RSSI ( indexes [ j ] ) > WiFi . RSSI ( indexes [ i ] ) ) {
std : : swap ( indexes [ i ] , indexes [ j ] ) ;
}
}
}
delay ( 0 ) ;
// Publish the list
uint32_t end_item = ( wifi_scan_result > initial_item + 10 ) ? initial_item + 10 : wifi_scan_result ;
for ( uint32_t i = initial_item ; i < end_item ; i + + ) {
2022-08-07 13:23:27 +01:00
Response_P ( PSTR ( " { \" " D_CMND_WIFISCAN " \" :{ \" " D_STATUS5_NETWORK " %d \" :{ \" " D_SSID " \" : \" %s \" , \" " D_BSSID " \" : \" %s \" , \" " D_CHANNEL
" \" : \" %d \" , \" " D_JSON_SIGNAL " \" : \" %d \" , \" " D_RSSI " \" : \" %d \" , \" " D_JSON_ENCRYPTION " \" : \" %s \" }}} " ) ,
2022-08-03 06:31:29 +01:00
i + 1 ,
WiFi . SSID ( indexes [ i ] ) . c_str ( ) ,
WiFi . BSSIDstr ( indexes [ i ] ) . c_str ( ) ,
WiFi . channel ( indexes [ i ] ) ,
WiFi . RSSI ( indexes [ i ] ) ,
WifiGetRssiAsQuality ( WiFi . RSSI ( indexes [ i ] ) ) ,
2022-08-07 14:52:23 +01:00
WifiEncryptionType ( indexes [ i ] ) . c_str ( ) ) ;
2022-08-03 06:31:29 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_STAT , PSTR ( D_CMND_WIFISCAN ) ) ;
}
} else if ( 9 = = Wifi . scan_state ) {
Response_P ( PSTR ( " { \" " D_CMND_WIFISCAN " \" : \" " D_NO_NETWORKS_FOUND " \" } " ) ) ;
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_STAT , PSTR ( D_CMND_WIFISCAN ) ) ;
}
delay ( 0 ) ;
}
// Wait 1 minute before cleaning the results so the user can ask for the them using wifiscan command (HTTP use-case)
if ( 69 = = Wifi . scan_state ) {
//AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network scan results deleted..."));
Wifi . scan_state = 0 ;
WiFi . scanDelete ( ) ; // Clean up Ram
}
2018-11-19 22:06:42 +00:00
}
2019-11-20 19:53:12 +00:00
uint16_t WifiLinkCount ( void )
2019-02-18 14:41:41 +00:00
{
2019-08-17 16:13:09 +01:00
return Wifi . link_count ;
2019-02-18 14:41:41 +00:00
}
2019-11-20 19:53:12 +00:00
String WifiDowntime ( void )
2019-02-18 17:02:22 +00:00
{
2019-08-17 16:13:09 +01:00
return GetDuration ( Wifi . downtime ) ;
2019-02-18 17:02:22 +00:00
}
2018-11-19 17:07:25 +00:00
void WifiSetState ( uint8_t state )
{
2020-10-30 11:29:48 +00:00
if ( state = = TasmotaGlobal . global_state . wifi_down ) {
2018-11-19 17:07:25 +00:00
if ( state ) {
2020-10-29 12:58:50 +00:00
TasmotaGlobal . rules_flag . wifi_connected = 1 ;
2019-08-17 16:13:09 +01:00
Wifi . link_count + + ;
Wifi . downtime + = UpTime ( ) - Wifi . last_event ;
2018-11-19 17:07:25 +00:00
} else {
2020-10-29 12:58:50 +00:00
TasmotaGlobal . rules_flag . wifi_disconnected = 1 ;
2019-08-17 16:13:09 +01:00
Wifi . last_event = UpTime ( ) ;
2018-11-19 17:07:25 +00:00
}
}
2020-10-30 11:29:48 +00:00
TasmotaGlobal . global_state . wifi_down = state ^ 1 ;
if ( ! TasmotaGlobal . global_state . wifi_down ) {
TasmotaGlobal . global_state . network_down = 0 ;
2020-06-15 17:27:04 +01:00
}
2018-11-19 17:07:25 +00:00
}
2022-12-27 20:59:34 +00:00
/*****************************************************************************************************\
* IP detection revised for full IPv4 / IPv6 support
2023-03-01 09:58:02 +00:00
*
2022-12-27 20:59:34 +00:00
* In general , each interface ( Wifi / Eth ) can have 1 x IPv4 and
* 2 x IPv6 ( Global routable address and Link - Local starting witn fe80 : . . . )
2023-03-01 09:58:02 +00:00
*
2022-12-27 20:59:34 +00:00
* We always use an IPv4 address if one is assigned , and revert to
* IPv6 only on networks that are v6 only .
* Ethernet calls can be safely used even if the USE_ETHERNET is not enabled
2023-03-01 09:58:02 +00:00
*
2022-12-27 20:59:34 +00:00
* New APIs :
* - general form is :
* ` bool XXXGetIPYYY ( IPAddress * ) ` returns ` true ` if the address exists and copies the address
* if the pointer is non - null .
* ` bool XXXHasIPYYY ( ) ` same as above but only returns ` true ` or ` false `
* ` String XXXGetIPYYYStr ( ) ` returns the IP as a ` String ` or empty ` String ` if none
2023-03-01 09:58:02 +00:00
*
2022-12-27 20:59:34 +00:00
* ` XXX ` can be ` Wifi ` or ` Eth `
* ` YYY ` can be ` ` for any address , ` v6 ` for IPv6 global address or ` v6LinkLocal ` for Link - local
2023-03-01 09:58:02 +00:00
*
2022-12-27 20:59:34 +00:00
* - Legacy ` Wifi . localIP ( ) ` and ` ETH . localIP ( ) ` always return IPv4 and nothing on IPv6 only networks
*
* - v4 / v6 :
* ` WifiGetIP ` , ` WifiGetIPStr ` , ` WifiHasIP ` : get preferred v4 / v6 address for Wifi
* ` EthernetGetIP ` , ` EthernetGetIPStr ` , ` EthernetHasIP ` : get preferred v4 / v6 for Ethernet
2023-03-01 09:58:02 +00:00
*
2022-12-27 20:59:34 +00:00
* - Main IP to be used dual stack v4 / v6
* ` hasIP ` , ` IPGetListeningAddress ` , ` IPGetListeningAddressStr ` : any IP to listen to for Web Server
* IPv4 is always preferred , and Eth is preferred over Wifi .
* ` IPForUrl ` : converts v4 / v6 to use in URL , enclosing v6 in [ ]
2023-03-01 09:58:02 +00:00
*
2022-12-27 20:59:34 +00:00
* - v6 only :
* ` WifiGetIPv6 ` , ` WifiGetIPv6Str ` , ` WifiHasIPv6 `
* ` WifiGetIPv6LinkLocal ` , ` WifiGetIPv6LinkLocalStr `
* ` EthernetGetIPv6 , ` EthernetHasIPv6 ` , ` EthernetGetIPv6Str `
* ` EthernetGetIPv6LinkLocal ` , ` EthernetGetIPv6LinkLocalStr `
*
* - v4 only :
* ` WifiGetIPv4 ` , ` WifiGetIPv4Str ` , ` WifiHasIPv4 `
* ` EthernetGetIPv4 ` , ` EthernetGetIPv4Str ` , ` EthernetHasIPv4 `
2023-03-01 09:58:02 +00:00
*
2022-12-27 20:59:34 +00:00
* - DNS reporting actual values used ( not the Settings ) :
* ` DNSGetIP ( n ) ` , ` DNSGetIPStr ( n ) ` with n = ` 0 ` / ` 1 ` ( same dns for Wifi and Eth )
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2023-05-18 10:23:09 +01:00
bool WifiGetIP ( IPAddress * ip , bool exclude_ap = false ) ;
2022-12-27 20:59:34 +00:00
// IPv4 for Wifi
// Returns only IPv6 global address (no loopback and no link-local)
bool WifiGetIPv4 ( IPAddress * ip )
{
uint32_t wifi_uint = ( uint32_t ) WiFi . localIP ( ) ;
if ( ip ! = nullptr ) { * ip = wifi_uint ; }
return wifi_uint ! = 0 ;
}
bool WifiHasIPv4 ( void )
{
return WifiGetIPv4 ( nullptr ) ;
}
String WifiGetIPv4Str ( void )
{
IPAddress ip ;
return WifiGetIPv4 ( & ip ) ? ip . toString ( ) : String ( ) ;
}
bool EthernetGetIPv4 ( IPAddress * ip )
{
# if defined(ESP32) && CONFIG_IDF_TARGET_ESP32 && defined(USE_ETHERNET)
uint32_t wifi_uint = ( uint32_t ) EthernetLocalIP ( ) ;
if ( ip ! = nullptr ) { * ip = wifi_uint ; }
return wifi_uint ! = 0 ;
# else
if ( ip ! = nullptr ) { * ip = ( uint32_t ) 0 ; }
return false ;
# endif
}
bool EthernetHasIPv4 ( void )
{
return EthernetGetIPv4 ( nullptr ) ;
}
String EthernetGetIPv4Str ( void )
{
IPAddress ip ;
return EthernetGetIPv4 ( & ip ) ? ip . toString ( ) : String ( ) ;
}
2022-12-10 12:44:16 +00:00
# ifdef USE_IPV6
2022-12-08 18:06:51 +00:00
//
// Scan through all interfaces to find a global or local IPv6 address
// Arg:
// is_local: is the address Link-Local (true) or Global (false)
// if_type: possible values are "st" for Wifi STA, "en" for Ethernet, "lo" for localhost (not useful)
2022-12-27 20:59:34 +00:00
// Returns `true` if found
bool WifiFindIPv6 ( IPAddress * ip , bool is_local , const char * if_type = " st " ) {
2022-12-08 18:06:51 +00:00
for ( netif * intf = netif_list ; intf ! = nullptr ; intf = intf - > next ) {
if ( intf - > name [ 0 ] = = if_type [ 0 ] & & intf - > name [ 1 ] = = if_type [ 1 ] ) {
for ( uint32_t i = 0 ; i < LWIP_IPV6_NUM_ADDRESSES ; i + + ) {
ip_addr_t * ipv6 = & intf - > ip6_addr [ i ] ;
2022-12-10 12:44:16 +00:00
if ( IP_IS_V6_VAL ( * ipv6 ) & & ! ip_addr_isloopback ( ipv6 ) & & ! ip_addr_isany ( ipv6 ) & & ( ( bool ) ip_addr_islinklocal ( ipv6 ) = = is_local ) ) {
2022-12-27 20:59:34 +00:00
if ( ip ! = nullptr ) { * ip = * ipv6 ; }
return true ;
2022-12-08 18:06:51 +00:00
}
}
}
}
2022-12-27 20:59:34 +00:00
return false ;
}
2022-12-08 18:06:51 +00:00
2022-12-27 20:59:34 +00:00
2022-11-30 18:40:58 +00:00
// Returns only IPv6 global address (no loopback and no link-local)
2022-12-27 20:59:34 +00:00
bool WifiGetIPv6 ( IPAddress * ip )
{
return WifiFindIPv6 ( ip , false , " st " ) ;
}
bool WifiHasIPv6 ( void )
{
return WifiGetIPv6 ( nullptr ) ;
}
String WifiGetIPv6Str ( void )
2019-12-11 14:53:19 +00:00
{
2022-12-27 20:59:34 +00:00
IPAddress ip ;
return WifiGetIPv6 ( & ip ) ? ip . toString ( ) : String ( ) ;
2019-12-11 14:53:19 +00:00
}
2022-11-30 18:40:58 +00:00
2022-12-27 20:59:34 +00:00
bool WifiGetIPv6LinkLocal ( IPAddress * ip )
2022-11-30 18:40:58 +00:00
{
2022-12-27 20:59:34 +00:00
return WifiFindIPv6 ( ip , true , " st " ) ;
}
String WifiGetIPv6LinkLocalStr ( void )
{
IPAddress ip ;
return WifiGetIPv6LinkLocal ( & ip ) ? ip . toString ( ) : String ( ) ;
2022-11-30 18:40:58 +00:00
}
2022-12-27 20:59:34 +00:00
// Returns only IPv6 global address (no loopback and no link-local)
bool EthernetGetIPv6 ( IPAddress * ip )
{
return WifiFindIPv6 ( ip , false , " en " ) ;
}
bool EthernetHasIPv6 ( void )
{
return EthernetGetIPv6 ( nullptr ) ;
}
String EthernetGetIPv6Str ( void )
{
IPAddress ip ;
return EthernetGetIPv6 ( & ip ) ? ip . toString ( ) : String ( ) ;
}
bool EthernetGetIPv6LinkLocal ( IPAddress * ip )
{
return WifiFindIPv6 ( ip , true , " en " ) ;
}
bool EthernetHasIPv6LinkLocal ( void )
{
return EthernetGetIPv6LinkLocal ( nullptr ) ;
}
String EthernetGetIPv6LinkLocalStr ( void )
{
IPAddress ip ;
return EthernetGetIPv6LinkLocal ( & ip ) ? ip . toString ( ) : String ( ) ;
}
bool DNSGetIP ( IPAddress * ip , uint32_t idx )
2022-11-30 18:40:58 +00:00
{
# ifdef ESP32
2022-12-27 20:59:34 +00:00
WiFi . scrubDNS ( ) ; // internal calls to reconnect can zero the DNS servers, restore the previous values
# endif
const ip_addr_t * ip_dns = dns_getserver ( idx ) ;
if ( ! ip_addr_isany ( ip_dns ) ) {
if ( ip ! = nullptr ) { * ip = * ip_dns ; }
return true ;
2022-11-30 18:40:58 +00:00
}
2023-01-03 08:33:31 +00:00
if ( ip ! = nullptr ) { * ip = * IP4_ADDR_ANY ; }
2022-12-27 20:59:34 +00:00
return false ;
}
String DNSGetIPStr ( uint32_t idx )
{
IPAddress ip ;
return DNSGetIP ( & ip , idx ) ? ip . toString ( ) : String ( F ( " 0.0.0.0 " ) ) ;
2022-11-30 18:40:58 +00:00
}
2023-03-01 09:58:02 +00:00
//
2022-12-17 09:08:35 +00:00
# include "lwip/dns.h"
2022-11-30 18:40:58 +00:00
void WifiDumpAddressesIPv6 ( void )
{
2022-12-08 18:06:51 +00:00
for ( netif * intf = netif_list ; intf ! = nullptr ; intf = intf - > next ) {
2023-11-04 10:46:25 +00:00
if ( ! ip_addr_isany_val ( intf - > ip_addr ) ) AddLog ( LOG_LEVEL_DEBUG , " WIF: '%c%c%i' IPv4 %s " , intf - > name [ 0 ] , intf - > name [ 1 ] , intf - > num , IPAddress ( intf - > ip_addr ) . toString ( ) . c_str ( ) ) ;
2022-12-08 18:06:51 +00:00
for ( uint32_t i = 0 ; i < LWIP_IPV6_NUM_ADDRESSES ; i + + ) {
if ( ! ip_addr_isany_val ( intf - > ip6_addr [ i ] ) )
2023-11-04 10:46:25 +00:00
AddLog ( LOG_LEVEL_DEBUG , " IP : '%c%c%i' IPv6 %s %s " , intf - > name [ 0 ] , intf - > name [ 1 ] , intf - > num ,
2022-12-10 12:44:16 +00:00
IPAddress ( intf - > ip6_addr [ i ] ) . toString ( ) . c_str ( ) ,
2022-12-08 18:06:51 +00:00
ip_addr_islinklocal ( & intf - > ip6_addr [ i ] ) ? " local " : " " ) ;
}
}
2022-12-27 20:59:34 +00:00
AddLog ( LOG_LEVEL_DEBUG , " IP : DNS: %s %s " , IPAddress ( dns_getserver ( 0 ) ) . toString ( ) . c_str ( ) , IPAddress ( dns_getserver ( 1 ) ) . toString ( ) . c_str ( ) ) ;
AddLog ( LOG_LEVEL_DEBUG , " WIF: v4IP: %_I v6IP: %s mainIP: %s " , ( uint32_t ) WiFi . localIP ( ) , WifiGetIPv6Str ( ) . c_str ( ) , WifiGetIPStr ( ) . c_str ( ) ) ;
# if defined(ESP32) && CONFIG_IDF_TARGET_ESP32 && defined(USE_ETHERNET)
AddLog ( LOG_LEVEL_DEBUG , " ETH: v4IP %_I v6IP: %s mainIP: %s " , ( uint32_t ) EthernetLocalIP ( ) , EthernetGetIPv6Str ( ) . c_str ( ) , EthernetGetIPStr ( ) . c_str ( ) ) ;
# endif
AddLog ( LOG_LEVEL_DEBUG , " IP : ListeningIP %s " , IPGetListeningAddressStr ( ) . c_str ( ) ) ;
2022-11-30 18:40:58 +00:00
}
2022-12-10 12:44:16 +00:00
# endif // USE_IPV6
2019-12-11 14:53:19 +00:00
2022-12-27 20:59:34 +00:00
// Returns the IP address on which we listen (used for Web UI mainly)
//
// If IPv4 is set, it is preferred.
// If only IPv6, return the routable global address
bool IPGetListeningAddress ( IPAddress * ip )
{
if ( ip = = nullptr ) return HasIP ( ) ; // no value added for this method if no parameter
# ifdef USE_IPV6
// collect both Wifi and Eth IPs and choose an IPv4 if any (Eth has priority)
IPAddress ip_wifi ;
bool has_wifi = WifiGetIP ( & ip_wifi ) ;
# if defined(ESP32) && CONFIG_IDF_TARGET_ESP32 && defined(USE_ETHERNET)
IPAddress ip_eth ;
bool has_eth = EthernetGetIP ( & ip_eth ) ;
if ( has_wifi & & has_eth ) {
if ( ip_eth . isV4 ( ) ) { * ip = ip_eth ; return true ; }
if ( ip_wifi . isV4 ( ) ) { * ip = ip_wifi ; return true ; }
// both addresses are v6, return ETH
* ip = ip_eth ;
return true ;
}
// from here only wifi or eth may be valid
if ( has_eth ) { * ip = ip_eth ; return true ; }
# endif
if ( has_wifi ) { * ip = ip_wifi ; return true ; }
* ip = IPAddress ( ) ;
return false ;
# else // USE_IPV6
# if defined(ESP32) && CONFIG_IDF_TARGET_ESP32 && defined(USE_ETHERNET)
if ( EthernetGetIP ( ip ) ) { return true ; }
# endif
if ( WifiGetIP ( ip ) ) { return true ; }
* ip = IPAddress ( ) ;
return false ;
# endif // USE_IPV6
}
String IPGetListeningAddressStr ( void )
{
IPAddress ip ;
if ( IPGetListeningAddress ( & ip ) ) {
return ip . toString ( ) ;
} else {
return String ( ) ;
}
}
// Because of IPv6, we can't test an IP address agains (uint32_t)0L anymore
// This test would work only for IPv4 assigned addresses.
// We must now use the following instead
inline bool IPIsValid ( const IPAddress & ip )
{
# ifdef USE_IPV6
return ! ip_addr_isany_val ( ( const ip_addr_t & ) ip ) ;
# else
return static_cast < uint32_t > ( ip ) ! = 0 ;
# endif
}
// Because of IPv6, URL encoding of IP address needs to be adapted
// IPv4: address is "x.x.x.x"
// IPv6: address is enclosed in brackets "[x.x::x.x...]"
String IPForUrl ( const IPAddress & ip )
{
# ifdef USE_IPV6
if ( ip . isV4 ( ) ) {
return ip . toString ( ) . c_str ( ) ;
} else {
String s ( ' [ ' ) ;
s + = ip . toString ( ) . c_str ( ) ;
s + = ' ] ' ;
return s ;
}
# else
return ip . toString ( ) . c_str ( ) ;
# endif
}
2021-01-18 15:32:58 +00:00
// Check to see if we have any routable IP address
2022-12-27 20:59:34 +00:00
// IPv4 has always priority
// Copy the value of the IP if pointer provided (optional)
2023-05-18 10:23:09 +01:00
// `exclude_ap` allows to exlude AP IP address and focus only on local STA
bool WifiGetIP ( IPAddress * ip , bool exclude_ap ) {
# ifdef ESP32
wifi_mode_t mode = WiFi . getMode ( ) ;
if ( ( mode = = WIFI_MODE_STA | | mode = = WIFI_MODE_APSTA ) & & ( uint32_t ) WiFi . localIP ( ) ! = 0 ) {
2022-12-27 20:59:34 +00:00
if ( ip ! = nullptr ) { * ip = WiFi . localIP ( ) ; }
return true ;
}
2023-05-18 10:23:09 +01:00
if ( ! exclude_ap & & ( mode = = WIFI_MODE_AP | | mode = = WIFI_MODE_APSTA ) & & ( uint32_t ) WiFi . softAPIP ( ) ! = 0 ) {
2023-01-06 13:03:16 +00:00
if ( ip ! = nullptr ) { * ip = WiFi . softAPIP ( ) ; }
return true ;
}
2023-05-18 10:23:09 +01:00
# else
WiFiMode_t mode = WiFi . getMode ( ) ;
if ( ( mode = = WIFI_STA | | mode = = WIFI_AP_STA ) & & ( uint32_t ) WiFi . localIP ( ) ! = 0 ) {
if ( ip ! = nullptr ) { * ip = WiFi . localIP ( ) ; }
return true ;
}
if ( ! exclude_ap & & ( mode = = WIFI_AP | | mode = = WIFI_AP_STA ) & & ( uint32_t ) WiFi . softAPIP ( ) ! = 0 ) {
if ( ip ! = nullptr ) { * ip = WiFi . softAPIP ( ) ; }
return true ;
}
# endif
2023-01-06 18:03:07 +00:00
# ifdef USE_IPV6
2022-12-27 20:59:34 +00:00
IPAddress lip ;
if ( WifiGetIPv6 ( & lip ) ) {
if ( ip ! = nullptr ) { * ip = lip ; }
return true ;
}
if ( ip ! = nullptr ) { * ip = IPAddress ( ) ; }
2022-12-10 12:44:16 +00:00
# endif // USE_IPV6
2023-01-06 18:03:07 +00:00
return false ;
2019-10-24 12:13:16 +01:00
}
2019-10-13 13:15:32 +01:00
2022-12-27 20:59:34 +00:00
bool WifiHasIP ( void ) {
return WifiGetIP ( nullptr ) ;
}
String WifiGetIPStr ( void )
{
IPAddress ip ;
return WifiGetIP ( & ip ) ? ip . toString ( ) : String ( ) ;
}
// Has a routable IP, whether IPv4 or IPv6, Wifi or Ethernet
bool HasIP ( void ) {
if ( WifiHasIP ( ) ) return true ;
# if defined(ESP32) && CONFIG_IDF_TARGET_ESP32 && defined(USE_ETHERNET)
if ( EthernetHasIP ( ) ) return true ;
# endif
return false ;
}
2022-04-03 15:45:08 +01:00
void WifiCheckIp ( void ) {
2022-12-10 12:44:16 +00:00
# ifdef USE_IPV6
2022-12-04 15:57:34 +00:00
if ( WL_CONNECTED = = WiFi . status ( ) ) {
2022-12-13 08:31:43 +00:00
# ifdef ESP32
2022-12-04 15:57:34 +00:00
if ( ! Wifi . ipv6_local_link_called ) {
WiFi . enableIpV6 ( ) ;
Wifi . ipv6_local_link_called = true ;
// AddLog(LOG_LEVEL_DEBUG, PSTR("WIF: calling enableIpV6"));
}
2022-12-13 08:31:43 +00:00
# endif
2022-12-04 15:57:34 +00:00
}
2022-12-10 12:44:16 +00:00
# endif // USE_IPV6
2022-12-04 15:57:34 +00:00
2022-04-03 15:45:08 +01:00
if ( ( WL_CONNECTED = = WiFi . status ( ) ) & & WifiHasIP ( ) ) {
2018-11-19 17:07:25 +00:00
WifiSetState ( 1 ) ;
2019-08-17 16:13:09 +01:00
Wifi . counter = WIFI_CHECK_SEC ;
Wifi . retry = Wifi . retry_init ;
2021-07-29 14:10:30 +01:00
Wifi . max_retry = 0 ;
2020-02-13 11:46:06 +00:00
if ( Wifi . status ! = WL_CONNECTED ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI D_CONNECTED ) ) ;
// AddLog(LOG_LEVEL_INFO, PSTR("Wifi: Set IP addresses"));
2021-06-11 17:14:12 +01:00
Settings - > ipv4_address [ 1 ] = ( uint32_t ) WiFi . gatewayIP ( ) ;
Settings - > ipv4_address [ 2 ] = ( uint32_t ) WiFi . subnetMask ( ) ;
Settings - > ipv4_address [ 3 ] = ( uint32_t ) WiFi . dnsIP ( ) ;
2021-07-29 15:57:04 +01:00
Settings - > ipv4_address [ 4 ] = ( uint32_t ) WiFi . dnsIP ( 1 ) ;
2020-04-07 11:19:54 +01:00
// Save current AP parameters for quick reconnect
2021-06-11 17:14:12 +01:00
Settings - > wifi_channel = WiFi . channel ( ) ;
2020-04-07 11:19:54 +01:00
uint8_t * bssid = WiFi . BSSID ( ) ;
2021-06-11 17:14:12 +01:00
memcpy ( ( void * ) & Settings - > wifi_bssid , ( void * ) bssid , sizeof ( Settings - > wifi_bssid ) ) ;
2020-02-13 11:46:06 +00:00
}
Wifi . status = WL_CONNECTED ;
2018-11-19 17:07:25 +00:00
} else {
WifiSetState ( 0 ) ;
2021-06-11 17:14:12 +01:00
uint8_t wifi_config_tool = Settings - > sta_config ;
2022-06-10 13:49:35 +01:00
Wifi . status = ( Wifi . retry & 1 ) ? WiFi . status ( ) : 0 ; // Skip every second to reset result WiFi.status()
2019-08-17 16:13:09 +01:00
switch ( Wifi . status ) {
2018-11-19 17:07:25 +00:00
case WL_CONNECTED :
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI D_CONNECT_FAILED_NO_IP_ADDRESS ) ) ;
2020-02-20 10:24:35 +00:00
Wifi . status = 0 ;
Wifi . retry = Wifi . retry_init ;
2018-11-19 17:07:25 +00:00
break ;
case WL_NO_SSID_AVAIL :
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI D_CONNECT_FAILED_AP_NOT_REACHED ) ) ;
2021-06-11 17:14:12 +01:00
Settings - > wifi_channel = 0 ; // Disable stored AP
if ( WIFI_WAIT = = Settings - > sta_config ) {
2020-02-13 11:46:06 +00:00
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 ;
}
}
2018-11-19 17:07:25 +00:00
break ;
case WL_CONNECT_FAILED :
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI D_CONNECT_FAILED_WRONG_PASSWORD ) ) ;
2021-06-11 17:14:12 +01:00
Settings - > wifi_channel = 0 ; // Disable stored AP
2020-02-13 11:46:06 +00:00
if ( Wifi . retry > ( Wifi . retry_init / 2 ) ) {
Wifi . retry = Wifi . retry_init / 2 ;
}
else if ( Wifi . retry ) {
Wifi . retry = 0 ;
}
2018-11-19 17:07:25 +00:00
break ;
default : // WL_IDLE_STATUS and WL_DISCONNECTED
2019-08-17 16:13:09 +01:00
if ( ! Wifi . retry | | ( ( Wifi . retry_init / 2 ) = = Wifi . retry ) ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI D_CONNECT_FAILED_AP_TIMEOUT ) ) ;
2021-06-11 17:14:12 +01:00
Settings - > wifi_channel = 0 ; // Disable stored AP
2021-07-29 14:10:30 +01:00
Wifi . max_retry + + ;
if ( 100 = = Wifi . max_retry ) { // Restart after 100 * (WIFI_RETRY_OFFSET_SEC + MAC) / 2 seconds
TasmotaGlobal . restart_flag = 2 ;
}
2018-11-19 17:07:25 +00:00
} else {
2019-12-21 16:57:54 +00:00
if ( ! strlen ( SettingsText ( SET_STASSID1 ) ) & & ! strlen ( SettingsText ( SET_STASSID2 ) ) ) {
2021-06-11 17:14:12 +01:00
Settings - > wifi_channel = 0 ; // Disable stored AP
2019-10-22 15:34:25 +01:00
wifi_config_tool = WIFI_MANAGER ; // Skip empty SSIDs and start Wifi config tool
2019-08-17 16:13:09 +01:00
Wifi . retry = 0 ;
2018-11-19 17:07:25 +00:00
} else {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_WIFI D_ATTEMPTING_CONNECTION ) ) ;
2018-11-19 17:07:25 +00:00
}
}
}
2019-08-17 16:13:09 +01:00
if ( Wifi . retry ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > flag3 . use_wifi_scan ) { // SetOption56 - Scan wifi network at restart for configured AP's
2020-02-20 10:24:35 +00:00
if ( Wifi . retry_init = = Wifi . retry ) {
2019-08-17 16:13:09 +01:00
Wifi . scan_state = 1 ; // Select scanned SSID
2018-11-20 13:10:32 +00:00
}
} else {
2019-08-17 16:13:09 +01:00
if ( Wifi . retry_init = = Wifi . retry ) {
2021-06-11 17:14:12 +01:00
WifiBegin ( 3 , Settings - > wifi_channel ) ; // Select default SSID
2018-11-19 17:07:25 +00:00
}
2021-06-11 17:14:12 +01:00
if ( ( Settings - > sta_config ! = WIFI_WAIT ) & & ( ( Wifi . retry_init / 2 ) = = Wifi . retry ) ) {
2018-11-19 22:06:42 +00:00
WifiBegin ( 2 , 0 ) ; // Select alternate SSID
2018-11-19 17:07:25 +00:00
}
}
2019-08-17 16:13:09 +01:00
Wifi . retry - - ;
2018-11-19 17:07:25 +00:00
} else {
WifiConfig ( wifi_config_tool ) ;
2019-08-17 16:13:09 +01:00
Wifi . retry = Wifi . retry_init ;
2018-11-19 17:07:25 +00:00
}
2022-06-10 13:49:35 +01:00
Wifi . counter = 1 ; // Re-check in 1 second
2018-11-19 17:07:25 +00:00
}
}
void WifiCheck ( uint8_t param )
{
2019-08-17 16:13:09 +01:00
Wifi . counter - - ;
2018-11-19 17:07:25 +00:00
switch ( param ) {
case WIFI_SERIAL :
case WIFI_MANAGER :
WifiConfig ( param ) ;
break ;
default :
2019-08-17 16:13:09 +01:00
if ( Wifi . config_counter ) {
Wifi . config_counter - - ;
Wifi . counter = Wifi . config_counter + 5 ;
if ( Wifi . config_counter ) {
if ( ! Wifi . config_counter ) {
2018-11-19 17:07:25 +00:00
if ( strlen ( WiFi . SSID ( ) . c_str ( ) ) ) {
2019-12-16 14:13:57 +00:00
SettingsUpdateText ( SET_STASSID1 , WiFi . SSID ( ) . c_str ( ) ) ;
2018-11-19 17:07:25 +00:00
}
if ( strlen ( WiFi . psk ( ) . c_str ( ) ) ) {
2019-12-16 14:13:57 +00:00
SettingsUpdateText ( SET_STAPWD1 , WiFi . psk ( ) . c_str ( ) ) ;
2018-11-19 17:07:25 +00:00
}
2021-06-11 17:14:12 +01:00
Settings - > sta_active = 0 ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID " 1 %s " ) , SettingsText ( SET_STASSID1 ) ) ;
2018-11-19 17:07:25 +00:00
}
}
2019-08-17 16:13:09 +01:00
if ( ! Wifi . config_counter ) {
2018-11-19 17:07:25 +00:00
// SettingsSdkErase(); // Disabled v6.1.0b due to possible bad wifi connects
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ;
2018-11-19 17:07:25 +00:00
}
} else {
2019-08-17 16:13:09 +01:00
if ( Wifi . counter < = 0 ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( D_LOG_WIFI D_CHECKING_CONNECTION ) ) ;
2019-08-17 16:13:09 +01:00
Wifi . counter = WIFI_CHECK_SEC ;
2018-11-19 17:07:25 +00:00
WifiCheckIp ( ) ;
}
2022-04-03 15:45:08 +01:00
if ( ( WL_CONNECTED = = WiFi . status ( ) ) & & WifiHasIP ( ) & & ! Wifi . config_type ) {
2018-11-19 17:07:25 +00:00
WifiSetState ( 1 ) ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag3 . use_wifi_rescan ) { // SetOption57 - Scan wifi network every 44 minutes for configured AP's
2020-10-28 16:32:07 +00:00
if ( ! ( TasmotaGlobal . uptime % ( 60 * WIFI_RESCAN_MINUTES ) ) ) {
2022-08-03 06:31:29 +01:00
if ( ! Wifi . scan_state ) { Wifi . scan_state = 2 ; } // If wifi scan routine is free, use it. Otherwise, wait for next RESCAN TIME
2018-11-19 22:06:42 +00:00
}
}
2018-11-19 17:07:25 +00:00
} else {
WifiSetState ( 0 ) ;
2020-06-15 21:10:49 +01:00
Mdns . begun = 0 ;
2018-11-19 17:07:25 +00:00
}
}
2022-08-03 06:31:29 +01:00
if ( Wifi . scan_state ) { WifiBeginAfterScan ( ) ; }
2018-11-19 17:07:25 +00:00
}
}
int WifiState ( void )
{
int state = - 1 ;
2020-10-30 11:29:48 +00:00
if ( ! TasmotaGlobal . global_state . wifi_down ) { state = WIFI_RESTART ; }
2019-08-17 16:13:09 +01:00
if ( Wifi . config_type ) { state = Wifi . config_type ; }
2018-11-19 17:07:25 +00:00
return state ;
}
2023-05-19 16:41:18 +01:00
float WifiGetOutputPower ( void ) {
2023-05-17 09:45:00 +01:00
if ( Settings - > wifi_output_power ) {
2023-05-19 16:41:18 +01:00
Wifi . last_tx_pwr = Settings - > wifi_output_power ;
2023-05-17 09:45:00 +01:00
}
2023-05-19 16:41:18 +01:00
return ( float ) ( Wifi . last_tx_pwr ) / 10 ;
2019-12-27 10:13:22 +00:00
}
2020-02-20 10:24:35 +00:00
2023-05-16 17:55:55 +01:00
void WifiSetOutputPower ( void ) {
if ( Settings - > wifi_output_power ) {
WiFi . setOutputPower ( ( float ) ( Settings - > wifi_output_power ) / 10 ) ;
2023-05-17 10:44:14 +01:00
} else {
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " WIF: Dynamic Tx power enabled " ) ) ; // WifiPower 0
2023-05-16 17:55:55 +01:00
}
}
void WiFiSetTXpowerBasedOnRssi ( void ) {
2023-05-17 10:44:14 +01:00
// Dynamic WiFi transmit power based on RSSI lowering overall DC power usage.
// Original idea by ESPEasy (@TD-er)
if ( ! Settings - > flag4 . network_wifi | | Settings - > wifi_output_power ) { return ; }
2023-05-16 17:55:55 +01:00
const WiFiMode_t cur_mode = WiFi . getMode ( ) ;
if ( cur_mode = = WIFI_OFF ) { return ; }
// Range ESP32 : 2dBm - 20dBm
// Range ESP8266: 0dBm - 20.5dBm
2023-05-17 09:45:00 +01:00
int max_tx_pwr = MAX_TX_PWR_DBM_11b ;
2023-05-16 17:55:55 +01:00
int threshold = WIFI_SENSITIVITY_n ;
int phy_mode = WiFi . getPhyMode ( ) ;
switch ( phy_mode ) {
case 1 : // 11b (WIFI_PHY_MODE_11B)
threshold = WIFI_SENSITIVITY_11b ;
2023-05-17 09:45:00 +01:00
if ( max_tx_pwr > MAX_TX_PWR_DBM_11b ) max_tx_pwr = MAX_TX_PWR_DBM_11b ;
2023-05-16 17:55:55 +01:00
break ;
case 2 : // 11bg (WIFI_PHY_MODE_11G)
threshold = WIFI_SENSITIVITY_54g ;
2023-05-17 09:45:00 +01:00
if ( max_tx_pwr > MAX_TX_PWR_DBM_54g ) max_tx_pwr = MAX_TX_PWR_DBM_54g ;
2023-05-16 17:55:55 +01:00
break ;
case 3 : // 11bgn (WIFI_PHY_MODE_11N)
threshold = WIFI_SENSITIVITY_n ;
2023-05-17 09:45:00 +01:00
if ( max_tx_pwr > MAX_TX_PWR_DBM_n ) max_tx_pwr = MAX_TX_PWR_DBM_n ;
2023-05-16 17:55:55 +01:00
break ;
}
threshold + = 30 ; // Margin in dBm * 10 on top of threshold
// Assume AP sends with max set by ETSI standard.
// 2.4 GHz: 100 mWatt (20 dBm)
// US and some other countries allow 1000 mW (30 dBm)
int rssi = WiFi . RSSI ( ) * 10 ;
int newrssi = rssi - 200 ; // We cannot send with over 20 dBm, thus it makes no sense to force higher TX power all the time.
2023-05-17 09:45:00 +01:00
int min_tx_pwr = 0 ;
2023-05-16 17:55:55 +01:00
if ( newrssi < threshold ) {
2023-05-17 09:45:00 +01:00
min_tx_pwr = threshold - newrssi ;
2023-05-16 17:55:55 +01:00
}
2023-05-17 09:45:00 +01:00
if ( min_tx_pwr > max_tx_pwr ) {
min_tx_pwr = max_tx_pwr ;
2023-05-16 17:55:55 +01:00
}
2023-05-17 09:45:00 +01:00
WiFi . setOutputPower ( ( float ) min_tx_pwr / 10 ) ;
delay ( Wifi . last_tx_pwr < min_tx_pwr ) ; // If increase the TX power, give power supply of the unit some rest
2023-05-16 17:55:55 +01:00
/*
2023-05-17 09:45:00 +01:00
if ( Wifi . last_tx_pwr ! = min_tx_pwr ) {
2023-05-17 10:44:14 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " WIF: TX power %d, Sensitivity %d, RSSI %d " ) , min_tx_pwr / 10 , threshold / 10 , rssi / 10 ) ;
2023-05-16 17:55:55 +01:00
}
*/
2023-05-17 09:45:00 +01:00
Wifi . last_tx_pwr = min_tx_pwr ;
2019-11-08 12:00:32 +00:00
}
2020-02-25 15:49:19 +00:00
/*
See Esp . h , core_esp8266_phy . cpp and test_overrides . ino
RF_DEFAULT = 0 , // RF_CAL or not after deep-sleep wake up, depends on init data byte 108.
RF_CAL = 1 , // RF_CAL after deep-sleep wake up, there will be large current.
RF_NO_CAL = 2 , // no RF_CAL after deep-sleep wake up, there will only be small current.
RF_DISABLED = 4 // disable RF after deep-sleep wake up, just like modem sleep, there will be the smallest current.
*/
# ifdef WIFI_RF_MODE_RF_CAL
# ifndef USE_DEEPSLEEP
RF_MODE ( RF_CAL ) ;
# endif // USE_DEEPSLEEP
# endif // WIFI_RF_MODE_RF_CAL
# ifdef WIFI_RF_PRE_INIT
bool rf_pre_init_flag = false ;
RF_PRE_INIT ( )
{
# ifndef USE_DEEPSLEEP
system_deep_sleep_set_option ( 1 ) ; // The option is 1 by default.
system_phy_set_rfoption ( RF_CAL ) ;
# endif // USE_DEEPSLEEP
system_phy_set_powerup_option ( 3 ) ; // 3: RF initialization will do the whole RF calibration which will take about 200ms; this increases the current consumption.
rf_pre_init_flag = true ;
}
# endif // WIFI_RF_PRE_INIT
2021-04-22 17:10:26 +01:00
void WifiEnable ( void ) {
Wifi . counter = 1 ;
}
2022-08-21 10:46:43 +01:00
//#ifdef ESP8266
//#include <sntp.h> // sntp_servermode_dhcp()
//#endif // ESP8266
2022-12-04 15:57:34 +00:00
# ifdef ESP32
void WifiEvents ( arduino_event_t * event ) ;
# endif
2018-11-19 17:07:25 +00:00
void WifiConnect ( void )
{
2021-06-11 17:14:12 +01:00
if ( ! Settings - > flag4 . network_wifi ) { return ; }
2020-06-15 17:27:04 +01:00
2022-12-08 18:06:51 +00:00
# if defined(ESP32) && !defined(FIRMWARE_MINIMAL)
2022-12-04 15:57:34 +00:00
static bool wifi_event_registered = false ;
if ( ! wifi_event_registered ) {
WiFi . onEvent ( WifiEvents ) ; // register event listener only once
wifi_event_registered = true ;
}
# endif // ESP32
2018-11-19 17:07:25 +00:00
WifiSetState ( 0 ) ;
2023-05-17 10:44:14 +01:00
// WifiSetOutputPower();
2022-08-21 10:46:43 +01:00
//#ifdef ESP8266
// https://github.com/arendst/Tasmota/issues/16061#issuecomment-1216970170
// sntp_servermode_dhcp(0);
//#endif // ESP8266
2018-11-19 17:07:25 +00:00
WiFi . persistent ( false ) ; // Solve possible wifi init errors
2019-08-17 16:13:09 +01:00
Wifi . status = 0 ;
2020-04-13 16:45:06 +01:00
Wifi . retry_init = WIFI_RETRY_OFFSET_SEC + ( ESP_getChipId ( ) & 0xF ) ; // Add extra delay to stop overrun by simultanous re-connects
2019-08-17 16:13:09 +01:00
Wifi . retry = Wifi . retry_init ;
2021-07-29 14:10:30 +01:00
Wifi . max_retry = 0 ;
2019-08-17 16:13:09 +01:00
Wifi . counter = 1 ;
2018-11-19 17:07:25 +00:00
2021-06-11 17:14:12 +01:00
memcpy ( ( void * ) & Wifi . bssid , ( void * ) Settings - > wifi_bssid , sizeof ( Wifi . bssid ) ) ;
2020-04-07 11:19:54 +01:00
2020-02-25 15:49:19 +00:00
# ifdef WIFI_RF_PRE_INIT
if ( rf_pre_init_flag ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_WIFI " Pre-init done " ) ) ;
2020-02-25 15:49:19 +00:00
}
# endif // WIFI_RF_PRE_INIT
2018-11-19 17:07:25 +00:00
}
2022-05-01 15:12:54 +01:00
void WifiShutdown ( bool option ) {
2020-02-25 15:49:19 +00:00
// option = false - Legacy disconnect also used by DeepSleep
2020-02-26 12:45:46 +00:00
// option = true - Disconnect with SDK wifi calibrate sector erase when WIFI_FORCE_RF_CAL_ERASE enabled
2018-11-19 17:07:25 +00:00
delay ( 100 ) ; // Allow time for message xfer - disabled v6.1.0b
2020-02-25 15:49:19 +00:00
# ifdef USE_EMULATION
UdpDisconnect ( ) ;
2020-02-26 12:45:46 +00:00
delay ( 100 ) ; // Flush anything in the network buffers.
2020-02-25 15:49:19 +00:00
# endif // USE_EMULATION
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . mqtt_enabled ) { // SetOption3 - Enable MQTT
2019-11-03 11:33:36 +00:00
MqttDisconnect ( ) ;
2020-02-26 12:45:46 +00:00
delay ( 100 ) ; // Flush anything in the network buffers.
2019-11-03 11:33:36 +00:00
}
2020-02-25 15:49:19 +00:00
2020-02-26 12:45:46 +00:00
# ifdef WIFI_FORCE_RF_CAL_ERASE
if ( option ) {
2020-02-25 15:49:19 +00:00
WiFi . disconnect ( false ) ; // Disconnect wifi
SettingsErase ( 4 ) ; // Delete SDK wifi config and calibrate data
2020-02-26 12:45:46 +00:00
} else
# endif // WIFI_FORCE_RF_CAL_ERASE
{
2020-02-25 15:49:19 +00:00
// 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
// Courtesy of EspEasy
2020-02-26 20:43:46 +00:00
// WiFi.persistent(true); // use SDK storage of SSID/WPA parameters
2020-02-25 15:49:19 +00:00
ETS_UART_INTR_DISABLE ( ) ;
wifi_station_disconnect ( ) ; // this will store empty ssid/wpa into sdk storage
ETS_UART_INTR_ENABLE ( ) ;
2020-02-26 20:43:46 +00:00
// WiFi.persistent(false); // Do not use SDK storage of SSID/WPA parameters
2020-02-25 15:49:19 +00:00
}
delay ( 100 ) ; // Flush anything in the network buffers.
2019-11-12 21:30:44 +00:00
}
2021-04-22 17:10:26 +01:00
void WifiDisable ( void ) {
if ( ! TasmotaGlobal . global_state . wifi_down ) {
WifiShutdown ( ) ;
WifiSetMode ( WIFI_OFF ) ;
}
TasmotaGlobal . global_state . wifi_down = 1 ;
}
2023-11-17 12:02:19 +00:00
void EspRestart ( void ) {
2020-03-22 16:42:32 +00:00
ResetPwm ( ) ;
2020-02-25 15:49:19 +00:00
WifiShutdown ( true ) ;
2019-12-07 17:32:39 +00:00
CrashDumpClear ( ) ; // Clear the stack dump in RTC
2020-08-10 15:19:44 +01:00
2023-11-17 12:02:19 +00:00
# ifdef CONFIG_IDF_TARGET_ESP32C3
GpioForceHoldRelay ( ) ; // Retain the state when the chip or system is reset, for example, when watchdog time-out or Deep-sleep
# endif // CONFIG_IDF_TARGET_ESP32C3
2023-07-03 20:07:45 +01:00
if ( TasmotaGlobal . restart_halt ) { // Restart 2
2020-08-10 15:19:44 +01:00
while ( 1 ) {
OsWatchLoop ( ) ; // Feed OsWatch timer to prevent restart
SetLedLink ( 1 ) ; // Wifi led on
delay ( 200 ) ; // Satisfy SDK
SetLedLink ( 0 ) ; // Wifi led off
delay ( 800 ) ; // Satisfy SDK
}
2023-07-03 20:07:45 +01:00
}
else if ( TasmotaGlobal . restart_deepsleep ) { // Restart 9
2023-12-05 12:47:53 +00:00
# ifdef USE_DEEPSLEEP
DeepSleepStart ( ) ;
// should never come to this line....
# endif
2023-07-03 20:07:45 +01:00
ESP . deepSleep ( 0 ) ; // Deep sleep mode with only hardware triggered wake up
2023-12-05 12:47:53 +00:00
2023-07-03 20:07:45 +01:00
}
else {
2020-08-10 15:19:44 +01:00
ESP_Restart ( ) ;
}
2018-11-19 17:07:25 +00:00
}
2020-04-07 13:07:00 +01:00
2021-11-22 15:20:26 +00:00
# ifdef ESP8266
2020-04-07 13:07:00 +01:00
//
// Gratuitous ARP, backported from https://github.com/esp8266/Arduino/pull/6889
//
extern " C " {
# if LWIP_VERSION_MAJOR == 1
# include "netif/wlan_lwip_if.h" // eagle_lwip_getif()
# include "netif/etharp.h" // gratuitous arp
# else
# include "lwip/etharp.h" // gratuitous arp
# endif
}
void stationKeepAliveNow ( void ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( D_LOG_WIFI " Sending Gratuitous ARP " ) ) ;
2020-04-07 13:07:00 +01:00
for ( netif * interface = netif_list ; interface ! = nullptr ; interface = interface - > next )
if (
( interface - > flags & NETIF_FLAG_LINK_UP )
& & ( interface - > flags & NETIF_FLAG_UP )
# if LWIP_VERSION_MAJOR == 1
& & interface = = eagle_lwip_getif ( STATION_IF ) /* lwip1 does not set if->num properly */
& & ( ! ip_addr_isany ( & interface - > ip_addr ) )
# else
& & interface - > num = = STATION_IF
& & ( ! ip4_addr_isany_val ( * netif_ip4_addr ( interface ) ) )
# endif
)
{
etharp_gratuitous ( interface ) ;
break ;
}
}
void wifiKeepAlive ( void ) {
2020-11-27 13:43:45 +00:00
static uint32_t wifi_timer = millis ( ) ; // Wifi keepalive timer
2020-10-28 11:40:52 +00:00
2021-06-11 17:14:12 +01:00
uint32_t wifiTimerSec = Settings - > param [ P_ARP_GRATUITOUS ] ; // 8-bits number of seconds, or minutes if > 100
2020-04-07 13:07:00 +01:00
if ( ( WL_CONNECTED ! = Wifi . status ) | | ( 0 = = wifiTimerSec ) ) { return ; } // quick exit if wifi not connected or feature disabled
2020-10-28 10:48:57 +00:00
if ( TimeReached ( wifi_timer ) ) {
2020-04-07 13:07:00 +01:00
stationKeepAliveNow ( ) ;
2020-04-07 13:21:17 +01:00
if ( wifiTimerSec > 100 ) {
2020-10-28 11:40:52 +00:00
wifiTimerSec = ( wifiTimerSec - 100 ) * 60 ; // convert >100 as minutes, ex: 105 = 5 minutes, 110 = 10 minutes
2020-04-07 13:21:17 +01:00
}
2020-10-28 10:48:57 +00:00
SetNextTimeInterval ( wifi_timer , wifiTimerSec * 1000 ) ;
2020-04-07 13:07:00 +01:00
}
}
2021-11-22 15:20:26 +00:00
# endif // ESP8266
2020-11-06 14:22:03 +00:00
2022-12-17 09:08:35 +00:00
// expose a function to be called by WiFi32
int32_t WifiDNSGetTimeout ( void ) {
return Settings - > dns_timeout ;
}
// read Settings for DNS IPv6 priority
bool WifiDNSGetIPv6Priority ( void ) {
# ifdef USE_IPV6
// we prioritize IPv6 only if a global IPv6 address is available, otherwise revert to IPv4 if we have one as well
// Any change in logic needs to clear the DNS cache
static bool had_v6prio = false ;
2022-12-27 20:59:34 +00:00
bool has_v4 = WifiHasIPv4 ( ) | | EthernetHasIPv4 ( ) ;
bool has_v6 = WifiHasIPv6 ( ) | | EthernetHasIPv6 ( ) ;
2022-12-17 09:08:35 +00:00
bool v6prio = Settings - > flag6 . dns_ipv6_priority ;
2022-12-27 20:59:34 +00:00
if ( has_v4 & & ! has_v6 ) {
2022-12-17 09:08:35 +00:00
v6prio = false ; // revert to IPv4 first
2022-12-27 20:59:34 +00:00
} else if ( has_v6 & & ! has_v4 ) {
v6prio = true ; // only IPv6 is available
2022-12-17 09:08:35 +00:00
}
// any change of state requires a dns cache clear
if ( had_v6prio ! = v6prio ) {
2022-12-17 21:41:21 +00:00
# ifdef ESP32
dns_clear_cache ( ) ; // this function doesn't exist in LWIP used by ESP8266
# endif
2022-12-17 09:08:35 +00:00
had_v6prio = v6prio ;
}
return v6prio ;
# endif // USE_IPV6
return false ;
}
2022-06-19 16:57:43 +01:00
bool WifiHostByName ( const char * aHostname , IPAddress & aResult ) {
2023-09-06 13:48:12 +01:00
# ifdef USE_IPV6
# if ESP_IDF_VERSION_MAJOR >= 5
// try converting directly to IP
if ( aResult . fromString ( aHostname ) ) {
return true ; // we're done
}
# endif
# endif // USE_IPV6
2022-12-17 09:08:35 +00:00
uint32_t dns_start = millis ( ) ;
bool success = WiFi . hostByName ( aHostname , aResult , Settings - > dns_timeout ) ;
uint32_t dns_end = millis ( ) ;
if ( success ) {
2022-08-29 13:31:05 +01:00
// Host name resolved
2023-09-06 13:48:12 +01:00
# ifdef USE_IPV6
# if ESP_IDF_VERSION_MAJOR >= 5
// check if there is a zone-id
// look for '%' in string
const char * s = aHostname ;
while ( * s & & * s ! = ' % ' ) { s + + ; }
if ( * s = = ' % ' ) {
// we have a zone id
aResult . setZone ( netif_name_to_index ( s + 1 ) ) ;
}
# endif
# endif // USE_IPV6
2022-08-29 13:31:05 +01:00
if ( 0xFFFFFFFF ! = ( uint32_t ) aResult ) {
2023-06-12 18:45:18 +01:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( D_LOG_WIFI " DNS resolved '%s' (%s) in %i ms " ) , aHostname , aResult . toString ( ) . c_str ( ) , dns_end - dns_start ) ;
2022-08-29 13:31:05 +01:00
return true ;
}
2022-08-24 04:38:11 +01:00
}
2022-12-17 09:08:35 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_WIFI " DNS failed for %s after %i ms " ) , aHostname , dns_end - dns_start ) ;
2022-08-21 10:46:43 +01:00
return false ;
2022-06-13 10:45:09 +01:00
}
2022-06-20 13:20:35 +01:00
bool WifiDnsPresent ( const char * aHostname ) {
IPAddress aResult ;
return WifiHostByName ( aHostname , aResult ) ;
}
2020-11-06 14:22:03 +00:00
void WifiPollNtp ( ) {
static uint8_t ntp_sync_minute = 0 ;
2021-08-08 14:22:44 +01:00
static uint32_t ntp_run_time = 0 ;
2020-11-06 14:22:03 +00:00
2020-11-07 11:42:39 +00:00
if ( TasmotaGlobal . global_state . network_down | | Rtc . user_time_entry ) { return ; }
2020-11-06 14:22:03 +00:00
uint8_t uptime_minute = ( TasmotaGlobal . uptime / 60 ) % 60 ; // 0 .. 59
if ( ( ntp_sync_minute > 59 ) & & ( uptime_minute > 2 ) ) {
ntp_sync_minute = 1 ; // If sync prepare for a new cycle
}
// First try ASAP to sync. If fails try once every 60 seconds based on chip id
2021-08-08 14:22:44 +01:00
uint8_t offset = ( TasmotaGlobal . uptime < 30 ) ? RtcTime . second + ntp_run_time : ( ( ( ESP_getChipId ( ) & 0xF ) * 3 ) + 3 ) ;
2020-11-06 14:22:03 +00:00
if ( ( ( ( offset = = RtcTime . second ) & & ( ( RtcTime . year < 2016 ) | | // Never synced
( ntp_sync_minute = = uptime_minute ) ) ) | | // Re-sync every hour
TasmotaGlobal . ntp_force_sync ) ) { // Forced sync
TasmotaGlobal . ntp_force_sync = false ;
2021-08-08 14:22:44 +01:00
2022-02-27 15:09:32 +00:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " NTP: Sync time... " ) ) ;
2021-08-08 14:22:44 +01:00
ntp_run_time = millis ( ) ;
2022-10-16 16:56:58 +01:00
uint64_t ntp_nanos = WifiGetNtp ( ) ;
uint32_t ntp_time = ntp_nanos / 1000000000 ;
2021-08-08 14:22:44 +01:00
ntp_run_time = ( millis ( ) - ntp_run_time ) / 1000 ;
// AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: Runtime %d"), ntp_run_time);
if ( ntp_run_time < 5 ) { ntp_run_time = 0 ; } // DNS timeout is around 10s
2020-11-06 14:22:03 +00:00
if ( ntp_time > START_VALID_TIME ) {
Rtc . utc_time = ntp_time ;
2022-10-16 16:56:58 +01:00
Rtc . nanos = ntp_nanos % 1000000000 ;
2020-11-06 14:22:03 +00:00
ntp_sync_minute = 60 ; // Sync so block further requests
2022-02-27 15:09:32 +00:00
RtcSync ( " NTP " ) ;
2020-11-06 14:22:03 +00:00
} else {
ntp_sync_minute + + ; // Try again in next minute
}
}
}
2022-10-16 16:56:58 +01:00
uint64_t WifiGetNtp ( void ) {
2020-11-06 14:22:03 +00:00
static uint8_t ntp_server_id = 0 ;
2022-06-14 17:34:51 +01:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: Start NTP Sync %d ..."), ntp_server_id);
2020-11-06 14:22:03 +00:00
IPAddress time_server_ip ;
2023-03-01 09:58:02 +00:00
char fallback_ntp_server [ 2 ] [ 32 ] ;
ext_snprintf_P ( fallback_ntp_server [ 0 ] , sizeof ( fallback_ntp_server [ 0 ] ) , PSTR ( " %_I " ) , Settings - > ipv4_address [ 1 ] ) ; // #17984
ext_snprintf_P ( fallback_ntp_server [ 1 ] , sizeof ( fallback_ntp_server [ 1 ] ) , PSTR ( " %d.pool.ntp.org " ) , random ( 0 , 3 ) ) ;
2020-11-27 17:22:44 +00:00
2020-11-06 14:22:03 +00:00
char * ntp_server ;
2023-03-01 09:58:02 +00:00
for ( uint32_t i = 0 ; i < MAX_NTP_SERVERS + 2 ; i + + ) {
if ( ntp_server_id > = MAX_NTP_SERVERS + 2 ) { ntp_server_id = 0 ; }
ntp_server = ( ntp_server_id < MAX_NTP_SERVERS ) ? SettingsText ( SET_NTPSERVER1 + ntp_server_id ) : fallback_ntp_server [ ntp_server_id - MAX_NTP_SERVERS ] ;
2022-06-14 17:34:51 +01:00
if ( strlen ( ntp_server ) ) {
break ;
2020-11-06 14:22:03 +00:00
}
2022-06-14 17:34:51 +01:00
ntp_server_id + + ;
2020-11-06 14:22:03 +00:00
}
2022-06-14 17:34:51 +01:00
if ( ! WifiHostByName ( ntp_server , time_server_ip ) ) {
ntp_server_id + + ;
2022-06-20 09:49:23 +01:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: Unable to resolve '%s'"), ntp_server);
2020-11-06 14:22:03 +00:00
return 0 ;
}
2022-06-14 17:34:51 +01:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: NtpServer '%s' IP %_I"), ntp_server, (uint32_t)time_server_ip);
2020-11-06 14:22:03 +00:00
WiFiUDP udp ;
uint32_t attempts = 3 ;
while ( attempts > 0 ) {
uint32_t port = random ( 1025 , 65535 ) ; // Create a random port for the UDP connection.
if ( udp . begin ( port ) ! = 0 ) {
break ;
}
attempts - - ;
}
if ( 0 = = attempts ) { return 0 ; }
while ( udp . parsePacket ( ) > 0 ) { // Discard any previously received packets
yield ( ) ;
}
const uint32_t NTP_PACKET_SIZE = 48 ; // NTP time is in the first 48 bytes of message
uint8_t packet_buffer [ NTP_PACKET_SIZE ] ; // Buffer to hold incoming & outgoing packets
memset ( packet_buffer , 0 , NTP_PACKET_SIZE ) ;
packet_buffer [ 0 ] = 0 b11100011 ; // LI, Version, Mode
packet_buffer [ 1 ] = 0 ; // Stratum, or type of clock
packet_buffer [ 2 ] = 6 ; // Polling Interval
packet_buffer [ 3 ] = 0xEC ; // Peer Clock Precision
packet_buffer [ 12 ] = 49 ;
packet_buffer [ 13 ] = 0x4E ;
packet_buffer [ 14 ] = 49 ;
packet_buffer [ 15 ] = 52 ;
if ( udp . beginPacket ( time_server_ip , 123 ) = = 0 ) { // NTP requests are to port 123
2021-01-15 09:43:58 +00:00
ntp_server_id + + ; // Next server next time
2020-11-06 14:22:03 +00:00
udp . stop ( ) ;
return 0 ;
}
udp . write ( packet_buffer , NTP_PACKET_SIZE ) ;
udp . endPacket ( ) ;
uint32_t begin_wait = millis ( ) ;
while ( ! TimeReached ( begin_wait + 1000 ) ) { // Wait up to one second
uint32_t size = udp . parsePacket ( ) ;
uint32_t remote_port = udp . remotePort ( ) ;
if ( ( size > = NTP_PACKET_SIZE ) & & ( remote_port = = 123 ) ) {
udp . read ( packet_buffer , NTP_PACKET_SIZE ) ; // Read packet into the buffer
udp . stop ( ) ;
if ( ( packet_buffer [ 0 ] & 0 b11000000 ) = = 0 b11000000 ) {
// Leap-Indicator: unknown (clock unsynchronized)
// See: https://github.com/letscontrolit/ESPEasy/issues/2886#issuecomment-586656384
2022-02-27 15:09:32 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NTP: IP %_I unsynced " ) , ( uint32_t ) time_server_ip ) ;
2021-01-15 09:43:58 +00:00
ntp_server_id + + ; // Next server next time
2020-11-06 14:22:03 +00:00
return 0 ;
}
// convert four bytes starting at location 40 to a long integer
// TX time is used here.
uint32_t secs_since_1900 = ( uint32_t ) packet_buffer [ 40 ] < < 24 ;
secs_since_1900 | = ( uint32_t ) packet_buffer [ 41 ] < < 16 ;
secs_since_1900 | = ( uint32_t ) packet_buffer [ 42 ] < < 8 ;
secs_since_1900 | = ( uint32_t ) packet_buffer [ 43 ] ;
if ( 0 = = secs_since_1900 ) { // No time stamp received
2021-01-15 09:43:58 +00:00
ntp_server_id + + ; // Next server next time
2020-11-06 14:22:03 +00:00
return 0 ;
}
2022-10-17 16:59:18 +01:00
uint32_t tmp_fraction = ( uint32_t ) packet_buffer [ 44 ] < < 24 ;
tmp_fraction | = ( uint32_t ) packet_buffer [ 45 ] < < 16 ;
tmp_fraction | = ( uint32_t ) packet_buffer [ 46 ] < < 8 ;
tmp_fraction | = ( uint32_t ) packet_buffer [ 47 ] ;
uint32_t fraction = ( ( ( uint64_t ) tmp_fraction ) * 1000000000 ) > > 32 ;
return ( ( ( uint64_t ) secs_since_1900 ) - 2208988800UL ) * 1000000000 + fraction ;
2020-11-06 14:22:03 +00:00
}
delay ( 10 ) ;
}
// Timeout.
2022-06-14 17:34:51 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NTP: No reply from %_I " ) , ( uint32_t ) time_server_ip ) ;
2020-11-06 14:22:03 +00:00
udp . stop ( ) ;
2021-01-15 09:43:58 +00:00
ntp_server_id + + ; // Next server next time
2020-11-06 14:22:03 +00:00
return 0 ;
}
2022-12-04 15:57:34 +00:00
// --------------------------------------------------------------------------------
// Respond to some Arduino/esp-idf events for better IPv6 support
// --------------------------------------------------------------------------------
# ifdef ESP32
// typedef void (*WiFiEventSysCb)(arduino_event_t *event);
void WifiEvents ( arduino_event_t * event ) {
switch ( event - > event_id ) {
2022-12-10 12:44:16 +00:00
# ifdef USE_IPV6
2022-12-04 15:57:34 +00:00
case ARDUINO_EVENT_WIFI_STA_GOT_IP6 :
{
ip_addr_t ip_addr6 ;
ip_addr_copy_from_ip6 ( ip_addr6 , event - > event_info . got_ip6 . ip6_info . ip ) ;
2022-12-10 12:44:16 +00:00
IPAddress addr ( ip_addr6 ) ;
2022-12-08 18:06:51 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " %s: IPv6 %s %s " ) ,
event - > event_id = = ARDUINO_EVENT_ETH_GOT_IP6 ? " ETH " : " WIF " ,
addr . isLocal ( ) ? PSTR ( " Local " ) : PSTR ( " Global " ) , addr . toString ( ) . c_str ( ) ) ;
2022-12-04 15:57:34 +00:00
}
break ;
2022-12-10 12:44:16 +00:00
# endif // USE_IPV6
2022-12-04 15:57:34 +00:00
case ARDUINO_EVENT_WIFI_STA_GOT_IP :
{
ip_addr_t ip_addr4 ;
ip_addr_copy_from_ip4 ( ip_addr4 , event - > event_info . got_ip . ip_info . ip ) ;
2022-12-27 20:59:34 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " WIF: IPv4 %_I, mask %_I, gateway %_I " ) ,
2022-12-04 15:57:34 +00:00
event - > event_info . got_ip . ip_info . ip . addr ,
event - > event_info . got_ip . ip_info . netmask . addr ,
event - > event_info . got_ip . ip_info . gw . addr ) ;
}
break ;
case ARDUINO_EVENT_WIFI_STA_CONNECTED :
// AddLog(LOG_LEVEL_DEBUG, PSTR("WIF: Received ARDUINO_EVENT_WIFI_STA_CONNECTED"));
Wifi . ipv6_local_link_called = false ; // not sure if this is needed, make sure link-local is restored at each reconnect
break ;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED :
case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE :
Wifi . ipv6_local_link_called = false ;
break ;
default :
break ;
}
2022-12-27 20:59:34 +00:00
WiFi . scrubDNS ( ) ; // internal calls to reconnect can zero the DNS servers, restore the previous values
2022-12-04 15:57:34 +00:00
}
# endif // ESP32