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
# define WIFI_RETRY_SECONDS 12 // Number of seconds connection to wifi network will retry
# 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
2019-10-11 06:40:55 +01:00
# if LWIP_IPV6
# include <AddrList.h> // IPv6 DualStack
2019-10-14 11:34:01 +01:00
# endif // LWIP_IPV6=1
2018-11-19 17:07:25 +00:00
2019-08-17 16:13:09 +01:00
struct WIFI {
2019-08-18 12:23:43 +01:00
uint32_t last_event = 0 ; // Last wifi connection event
uint32_t downtime = 0 ; // Wifi down duration
uint16_t link_count = 0 ; // Number of wifi re-connect
2019-08-17 16:13:09 +01:00
uint8_t counter ;
uint8_t retry_init ;
uint8_t retry ;
2021-07-29 14:10:30 +01:00
uint8_t max_retry ;
2019-08-17 16:13:09 +01:00
uint8_t status ;
uint8_t config_type = 0 ;
uint8_t config_counter = 0 ;
uint8_t scan_state ;
2020-02-20 10:24:35 +00:00
uint8_t bssid [ 6 ] ;
2020-01-26 14:52:26 +00:00
int8_t best_network_db ;
2019-08-17 16:13:09 +01:00
} Wifi ;
2018-11-19 17:07:25 +00:00
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 ;
}
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
}
}
2021-07-05 13:50:33 +01:00
void WifiSetMode ( WiFiMode_t wifi_mode ) {
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)
}
}
2019-11-11 16:39:24 +00:00
WifiSetOutputPower ( ) ;
2018-11-19 17:07:25 +00:00
}
2018-11-19 22:06:42 +00:00
void WifiBegin ( uint8_t flag , uint8_t channel )
2018-11-19 17:07:25 +00:00
{
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)
WiFi . disconnect ( true ) ; // Delete SDK wifi config
delay ( 200 ) ;
2021-07-05 16:11:03 +01:00
2021-07-05 13:50:33 +01:00
WifiSetMode ( WIFI_STA ) ; // Disable AP mode
2018-11-19 17:07:25 +00:00
WiFiSetSleepMode ( ) ;
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
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-02-27 17:21:13 +00:00
WiFi . waitForConnectResult ( 1000 ) ;
2019-10-11 06:40:55 +01:00
# if LWIP_IPV6
for ( bool configured = false ; ! configured ; ) {
2019-10-11 13:39:58 +01:00
uint16_t cfgcnt = 0 ;
for ( auto addr : addrList ) {
if ( ( configured = ! addr . isLocal ( ) & & addr . isV6 ( ) ) | | cfgcnt = = 30 ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_WIFI " Got IPv6 global address %s " ) , addr . toString ( ) . c_str ( ) ) ;
2019-10-11 13:39:58 +01:00
break ; // IPv6 is mandatory but stop after 15 seconds
}
2021-01-20 09:44:10 +00:00
delay ( 500 ) ; // Loop until real IPv6 address is aquired or too many tries failed
2021-01-23 15:26:23 +00:00
cfgcnt + + ;
2019-10-11 06:40:55 +01:00
}
}
2019-10-14 11:34:01 +01:00
# endif // LWIP_IPV6=1
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 ;
}
}
}
}
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
}
2019-10-14 11:34:01 +01:00
# if LWIP_IPV6
2019-12-11 14:53:19 +00:00
String WifiGetIPv6 ( void )
{
for ( auto a : addrList ) {
if ( ! a . isLocal ( ) & & a . isV6 ( ) ) return a . toString ( ) ;
}
return " " ;
}
2021-01-18 15:32:58 +00:00
# endif // LWIP_IPV6=1
2019-12-11 14:53:19 +00:00
2021-01-18 15:32:58 +00:00
// Check to see if we have any routable IP address
inline bool WifiCheck_hasIP ( IPAddress const & ip_address )
2019-10-24 12:13:16 +01:00
{
2021-01-23 15:26:23 +00:00
# ifdef LWIP2_IPV6
2021-01-18 15:32:58 +00:00
return ! a . isLocal ( ) ;
# else
return static_cast < uint32_t > ( ip_address ) ! = 0 ;
2021-01-23 15:26:23 +00:00
# endif
2019-10-24 12:13:16 +01:00
}
2019-10-13 13:15:32 +01:00
2018-11-19 17:07:25 +00:00
void WifiCheckIp ( void )
{
2021-01-18 15:32:58 +00:00
if ( ( WL_CONNECTED = = WiFi . status ( ) ) & & WifiCheck_hasIP ( WiFi . localIP ( ) ) ) {
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 ;
2019-08-17 16:13:09 +01:00
Wifi . status = WiFi . status ( ) ;
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 . counter = 1 ;
Wifi . retry - - ;
2018-11-19 17:07:25 +00:00
} else {
WifiConfig ( wifi_config_tool ) ;
2019-08-17 16:13:09 +01:00
Wifi . counter = 1 ;
Wifi . retry = Wifi . retry_init ;
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 . scan_state ) { WifiBeginAfterScan ( ) ; }
2018-11-19 22:06:42 +00:00
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 ( ) ;
}
2021-01-18 15:32:58 +00:00
if ( ( WL_CONNECTED = = WiFi . status ( ) ) & & WifiCheck_hasIP ( WiFi . localIP ( ) ) & & ! 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 ) ) ) {
2019-08-17 16:13:09 +01:00
Wifi . scan_state = 2 ;
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
}
}
}
}
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 ;
}
2019-12-27 10:13:22 +00:00
String WifiGetOutputPower ( void )
{
char stemp1 [ TOPSZ ] ;
2021-06-11 17:14:12 +01:00
dtostrfd ( ( float ) ( Settings - > wifi_output_power ) / 10 , 1 , stemp1 ) ;
2019-12-27 10:13:22 +00:00
return String ( stemp1 ) ;
}
2020-02-20 10:24:35 +00:00
2019-11-08 12:00:32 +00:00
void WifiSetOutputPower ( void )
{
2021-06-11 17:14:12 +01:00
WiFi . setOutputPower ( ( float ) ( Settings - > wifi_output_power ) / 10 ) ;
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 ;
}
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
2018-11-19 17:07:25 +00:00
WifiSetState ( 0 ) ;
2019-11-08 12:00:32 +00:00
WifiSetOutputPower ( ) ;
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
}
2020-02-25 15:49:19 +00:00
void WifiShutdown ( bool option = false )
2018-11-19 17:07:25 +00:00
{
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 ;
}
2019-11-12 21:30:44 +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
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . restart_halt ) {
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
}
} else {
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
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 ( ) ;
2020-11-06 14:22:03 +00:00
uint32_t ntp_time = WifiGetNtp ( ) ;
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 ;
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
}
}
}
uint32_t WifiGetNtp ( void ) {
static uint8_t ntp_server_id = 0 ;
IPAddress time_server_ip ;
2020-11-27 17:22:44 +00:00
char fallback_ntp_server [ 16 ] ;
snprintf_P ( fallback_ntp_server , sizeof ( fallback_ntp_server ) , PSTR ( " %d.pool.ntp.org " ) , random ( 0 , 3 ) ) ;
2020-11-06 14:22:03 +00:00
char * ntp_server ;
bool resolved_ip = false ;
2020-11-27 17:22:44 +00:00
for ( uint32_t i = 0 ; i < = MAX_NTP_SERVERS ; i + + ) {
2021-01-15 09:43:58 +00:00
if ( ntp_server_id > 2 ) { ntp_server_id = 0 ; }
2020-11-27 17:22:44 +00:00
if ( i < MAX_NTP_SERVERS ) {
ntp_server = SettingsText ( SET_NTPSERVER1 + ntp_server_id ) ;
} else {
ntp_server = fallback_ntp_server ;
}
2020-11-06 14:22:03 +00:00
if ( strlen ( ntp_server ) ) {
2021-08-08 14:22:44 +01:00
resolved_ip = ( WiFi . hostByName ( ntp_server , time_server_ip ) = = 1 ) ; // DNS timeout set to (ESP8266) 10s / (ESP32) 14s
if ( ( 255 = = time_server_ip [ 0 ] ) | | // No valid name resolved (255.255.255.255)
( ( 255 = = time_server_ip [ 1 ] ) & & ( 255 = = time_server_ip [ 2 ] ) & & ( 255 = = time_server_ip [ 3 ] ) ) ) { // No valid name resolved (x.255.255.255)
resolved_ip = false ;
}
2020-11-06 14:22:03 +00:00
yield ( ) ;
if ( resolved_ip ) { break ; }
2021-08-08 14:22:44 +01:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: Unable to resolve '%s'"), ntp_server);
2020-11-06 14:22:03 +00:00
}
ntp_server_id + + ;
}
if ( ! resolved_ip ) {
2021-08-08 15:08:08 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NTP: Unable to resolve IP address " ) ) ;
2020-11-06 14:22:03 +00:00
return 0 ;
}
2021-08-08 14:22:44 +01:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: Host %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 ;
}
return secs_since_1900 - 2208988800UL ;
}
delay ( 10 ) ;
}
// Timeout.
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " NTP: No reply " ) ) ;
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 ;
}