Fixed NTP fallback server functionality

Fixed NTP fallback server functionality (#9739)
This commit is contained in:
Theo Arends 2020-11-06 15:22:03 +01:00
parent 9026455891
commit 488a360d5b
7 changed files with 172 additions and 77 deletions

View File

@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file.
## [9.1.0.1]
### Changed
- platformio compiler option `no target align` enabled for stage
- Core library from v2.7.4.5 to v2.7.4.7
- Platformio compiler option `no target align` enabled (#9749)
### Fixed
- NTP fallback server functionality (#9739)
## [Released]

View File

@ -25,7 +25,7 @@ While fallback or downgrading is common practice it was never supported due to S
## Supported Core versions
This release will be supported from ESP8266/Arduino library Core version **2.7.4.5** due to reported security and stability issues on previous Core version. This will also support gzipped binaries.
This release will be supported from ESP8266/Arduino library Core version **2.7.4.7** due to reported security and stability issues on previous Core version. This will also support gzipped binaries.
Support of Core versions before 2.7.1 has been removed.
@ -39,7 +39,7 @@ For initial configuration this release supports Webserver based **WifiManager**
## Provided Binary Downloads
The following binary downloads have been compiled with ESP8266/Arduino library core version **2.7.4.5**.
The following binary downloads have been compiled with ESP8266/Arduino library core version **2.7.4.7**.
- **tasmota.bin** = The Tasmota version with most drivers. **RECOMMENDED RELEASE BINARY**
- **tasmota-BG.bin** to **tasmota-TW.bin** = The Tasmota version in different languages.
@ -58,4 +58,9 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
[Complete list](BUILDS.md) of available feature and sensors.
## Changelog v9.1.0.1
- platformio compiler option `no target align` enabled for stage
### Changed
- Core library from v2.7.4.5 to v2.7.4.7
- Platformio compiler option `no target align` enabled (#9749)
### Fixed
- NTP fallback server functionality (#9739)

View File

@ -2076,6 +2076,10 @@ void PrepLog_P2(uint32_t loglevel, PGM_P formatP, ...)
void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...)
{
if (TasmotaGlobal.prepped_loglevel) {
AddLog(TasmotaGlobal.prepped_loglevel);
}
va_list arg;
va_start(arg, formatP);
vsnprintf_P(TasmotaGlobal.log_data, sizeof(TasmotaGlobal.log_data), formatP, arg);

View File

@ -157,29 +157,6 @@ void ZigbeeWrite(const void *pSettings, unsigned nSettingsLen) {
NvmSave("zb", "zigbee", pSettings, nSettingsLen);
}
//
// sntp emulation
//
static bool bNetIsTimeSync = false;
//
void SntpInit() {
bNetIsTimeSync = true;
}
uint32_t SntpGetCurrentTimestamp(void) {
time_t now = 0;
if (bNetIsTimeSync || TasmotaGlobal.ntp_force_sync)
{
//Serial_DebugX(("timesync configTime %d\n", TasmotaGlobal.ntp_force_sync, bNetIsTimeSync));
// init to UTC Time
configTime(0, 0, SettingsText(SET_NTPSERVER1), SettingsText(SET_NTPSERVER2), SettingsText(SET_NTPSERVER3));
bNetIsTimeSync = false;
TasmotaGlobal.ntp_force_sync = false;
}
time(&now);
return now;
}
//
// Crash stuff
//

View File

@ -29,9 +29,6 @@ const uint32_t MINS_PER_HOUR = 60UL;
#define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400)))
extern "C" {
#include "sntp.h"
}
#include <Ticker.h>
Ticker TickerRtc;
@ -44,13 +41,12 @@ struct RTC {
uint32_t local_time = 0;
uint32_t daylight_saving_time = 0;
uint32_t standard_time = 0;
uint32_t ntp_time = 0;
uint32_t midnight = 0;
uint32_t restart_time = 0;
uint32_t millis = 0;
uint32_t last_sync = 0;
// uint32_t last_sync = 0;
int32_t time_timezone = 0;
uint8_t ntp_sync_minute = 0;
bool time_synced = false;
bool midnight_now = false;
bool user_time_entry = false; // Override NTP by user setting
} Rtc;
@ -374,52 +370,39 @@ uint32_t RuleToTime(TimeRule r, int yr)
void RtcSecond(void)
{
TIME_T tmpTime;
static uint32_t last_sync = 0;
Rtc.millis = millis();
if (!Rtc.user_time_entry) {
if (!TasmotaGlobal.global_state.network_down) {
uint8_t uptime_minute = (TasmotaGlobal.uptime / 60) % 60; // 0 .. 59
if ((Rtc.ntp_sync_minute > 59) && (uptime_minute > 2)) {
Rtc.ntp_sync_minute = 1; // If sync prepare for a new cycle
if (Rtc.time_synced) {
Rtc.time_synced = false;
last_sync = Rtc.utc_time;
if (Rtc.restart_time == 0) {
Rtc.restart_time = Rtc.utc_time - TasmotaGlobal.uptime; // save first synced time as restart time
}
uint8_t offset = (TasmotaGlobal.uptime < 30) ? RtcTime.second : (((ESP_getChipId() & 0xF) * 3) + 3) ; // First try ASAP to sync. If fails try once every 60 seconds based on chip id
if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || // Never synced
(Rtc.ntp_sync_minute == uptime_minute))) || // Re-sync every hour
TasmotaGlobal.ntp_force_sync ) ) { // Forced sync
Rtc.ntp_time = sntp_get_current_timestamp();
if (Rtc.ntp_time > START_VALID_TIME) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on)
TasmotaGlobal.ntp_force_sync = false;
Rtc.utc_time = Rtc.ntp_time;
Rtc.last_sync = Rtc.ntp_time;
Rtc.ntp_sync_minute = 60; // Sync so block further requests
if (Rtc.restart_time == 0) {
Rtc.restart_time = Rtc.utc_time - TasmotaGlobal.uptime; // save first ntp time as restart time
}
BreakTime(Rtc.utc_time, tmpTime);
RtcTime.year = tmpTime.year + 1970;
Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year);
Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year);
// Do not use AddLog_P2 here (interrupt routine) if syslog or mqttlog is enabled. UDP/TCP will force exception 9
PrepLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: " D_UTC_TIME " %s, " D_DST_TIME " %s, " D_STD_TIME " %s"),
GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str());
TIME_T tmpTime;
BreakTime(Rtc.utc_time, tmpTime);
RtcTime.year = tmpTime.year + 1970;
Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year);
Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year);
if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01
TasmotaGlobal.rules_flag.time_init = 1;
} else {
TasmotaGlobal.rules_flag.time_set = 1;
}
} else {
Rtc.ntp_sync_minute++; // Try again in next minute
}
// Do not use AddLog_P2 here (interrupt routine) if syslog or mqttlog is enabled. UDP/TCP will force exception 9
PrepLog_P2(LOG_LEVEL_DEBUG, PSTR("RTC: " D_UTC_TIME " %s, " D_DST_TIME " %s, " D_STD_TIME " %s"),
GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str());
if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01
TasmotaGlobal.rules_flag.time_init = 1;
} else {
TasmotaGlobal.rules_flag.time_set = 1;
}
}
if ((Rtc.utc_time > (2 * 60 * 60)) && (Rtc.last_sync < Rtc.utc_time - (2 * 60 * 60))) { // Every two hours a warning
if ((Rtc.utc_time > (2 * 60 * 60)) && (last_sync < Rtc.utc_time - (2 * 60 * 60))) { // Every two hours a warning
// Do not use AddLog_P2 here (interrupt routine) if syslog or mqttlog is enabled. UDP/TCP will force exception 9
PrepLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: Not synced"));
Rtc.last_sync = Rtc.utc_time;
PrepLog_P2(LOG_LEVEL_DEBUG, PSTR("RTC: Not synced"));
last_sync = Rtc.utc_time;
}
}
@ -477,9 +460,7 @@ void RtcSetTime(uint32_t epoch)
if (epoch < START_VALID_TIME) { // 2016-01-01
Rtc.user_time_entry = false;
TasmotaGlobal.ntp_force_sync = true;
sntp_init();
} else {
sntp_stop();
Rtc.user_time_entry = true;
Rtc.utc_time = epoch -1; // Will be corrected by RtcSecond
}
@ -487,12 +468,6 @@ void RtcSetTime(uint32_t epoch)
void RtcInit(void)
{
sntp_setservername(0, SettingsText(SET_NTPSERVER1));
sntp_setservername(1, SettingsText(SET_NTPSERVER2));
sntp_setservername(2, SettingsText(SET_NTPSERVER3));
sntp_stop();
sntp_set_timezone(0); // UTC time
sntp_init();
Rtc.utc_time = 0;
BreakTime(Rtc.utc_time, RtcTime);
TickerRtc.attach(1, RtcSecond);

View File

@ -876,6 +876,8 @@ void PerformEverySecond(void)
// Wifi keep alive to send Gratuitous ARP
wifiKeepAlive();
WifiPollNtp();
#ifdef ESP32
if (11 == TasmotaGlobal.uptime) { // Perform one-time ESP32 houskeeping
ESP_getSketchSize(); // Init sketchsize as it can take up to 2 seconds

View File

@ -701,3 +701,131 @@ void wifiKeepAlive(void) {
SetNextTimeInterval(wifi_timer, wifiTimerSec * 1000);
}
}
void WifiPollNtp() {
static uint8_t ntp_sync_minute = 0;
if (TasmotaGlobal.global_state.network_down) { return; }
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
uint8_t offset = (TasmotaGlobal.uptime < 30) ? RtcTime.second : (((ESP_getChipId() & 0xF) * 3) + 3) ;
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;
uint32_t ntp_time = WifiGetNtp();
if (ntp_time > START_VALID_TIME) {
Rtc.utc_time = ntp_time;
ntp_sync_minute = 60; // Sync so block further requests
Rtc.time_synced = true;
RtcSecond();
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: Synced"));
} else {
ntp_sync_minute++; // Try again in next minute
}
}
}
uint32_t WifiGetNtp(void) {
static uint8_t ntp_server_id = 0;
IPAddress time_server_ip;
char* ntp_server;
bool resolved_ip = false;
for (uint32_t i = 0; i < MAX_NTP_SERVERS; i++) {
ntp_server = SettingsText(SET_NTPSERVER1 + ntp_server_id);
if (strlen(ntp_server)) {
resolved_ip = (WiFi.hostByName(ntp_server, time_server_ip) == 1);
if (255 == time_server_ip[0]) { resolved_ip = false; }
yield();
if (resolved_ip) { break; }
}
ntp_server_id++;
if (ntp_server_id > 2) { ntp_server_id = 0; }
}
if (!resolved_ip) {
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: No server found"));
return 0;
}
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: Name %s, IP %s"), ntp_server, time_server_ip.toString().c_str());
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] = 0b11100011; // 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
ntp_server_id++;
if (ntp_server_id > 2) { ntp_server_id = 0; } // Next server next time
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] & 0b11000000) == 0b11000000) {
// Leap-Indicator: unknown (clock unsynchronized)
// See: https://github.com/letscontrolit/ESPEasy/issues/2886#issuecomment-586656384
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: IP %s unsynched"), time_server_ip.toString().c_str());
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
return 0;
}
return secs_since_1900 - 2208988800UL;
}
delay(10);
}
// Timeout.
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: No reply"));
udp.stop();
return 0;
}