diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index e67d4b1bb..162fbc2a9 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -603,13 +603,18 @@ void WifiDisconnect(void) WiFi.persistent(false); // Do not use SDK storage of SSID/WPA parameters } -void EspRestart(void) +void WifiShutdown(void) { delay(100); // Allow time for message xfer - disabled v6.1.0b if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT MqttDisconnect(); } WifiDisconnect(); +} + +void EspRestart(void) +{ + WifiShutdown(); // ESP.restart(); // This results in exception 3 on restarts on core 2.3.0 ESP.reset(); } diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 607afefd1..33bed67aa 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -116,6 +116,8 @@ const uint16_t SYSLOG_TIMER = 600; // Seconds to restore syslog_level const uint16_t SERIALLOG_TIMER = 600; // Seconds to disable SerialLog const uint8_t OTA_ATTEMPTS = 5; // Number of times to try fetching the new firmware +const uint16_t TELE_PERIOD_START = 10000; // Initial tele_period until wifi connected + const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in (serial and http) command buffer const uint16_t FLOATSZ = 16; // Max number of characters in float result from dtostrfd (max 32) const uint16_t CMDSZ = 24; // Max number of characters in command diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 61528bd80..0135f3dde 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -104,7 +104,6 @@ int ota_state_flag = 0; // OTA state flag int ota_result = 0; // OTA result int restart_flag = 0; // Tasmota restart flag int wifi_state_flag = WIFI_RESTART; // Wifi state flag -int tele_period = 1; // Tele period timer int blinks = 201; // Number of LED blinks uint32_t uptime = 0; // Counting every second until 4294967295 = 130 year uint32_t loop_load_avg = 0; // Indicative loop load average @@ -114,6 +113,7 @@ float global_temperature = 9999; // Provide a global temperature to b float global_humidity = 0; // Provide a global humidity to be used by some sensors float global_pressure = 0; // Provide a global pressure to be used by some sensors char *ota_url; // OTA url string pointer +uint16_t tele_period = TELE_PERIOD_START; // Tele period timer uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command uint16_t blink_counter = 0; // Number of blink cycles uint16_t seriallog_timer = 0; // Timer to disable Seriallog @@ -846,8 +846,13 @@ void PerformEverySecond(void) ResetGlobalValues(); if (Settings.tele_period) { + if (tele_period >= TELE_PERIOD_START) { + if (!global_state.wifi_down) { + tele_period = 0; // Allow teleperiod once wifi is connected + } + } tele_period++; - if (tele_period >= Settings.tele_period) { + if (tele_period == Settings.tele_period) { tele_period = 0; MqttPublishTeleState(); diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 3663ed497..c8250afbb 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -558,7 +558,9 @@ void MqttConnected(void) Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"), (GetResetReason() == "Exception") ? ESP.getResetInfo().c_str() : GetResetReason().c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); MqttPublishAllPowerState(); - if (Settings.tele_period) { tele_period = Settings.tele_period -9; } // Enable TelePeriod in 9 seconds + if (Settings.tele_period) { + tele_period = Settings.tele_period -5; // Enable TelePeriod in 5 seconds + } rules_flag.system_boot = 1; XdrvCall(FUNC_MQTT_INIT); } diff --git a/tasmota/xdrv_29_deepsleep.ino b/tasmota/xdrv_29_deepsleep.ino index 08f0755ef..fc9f0dd0e 100644 --- a/tasmota/xdrv_29_deepsleep.ino +++ b/tasmota/xdrv_29_deepsleep.ino @@ -42,9 +42,12 @@ const char kDeepsleepCommands[] PROGMEM = D_PRFX_DEEPSLEEP "|" void (* const DeepsleepCommand[])(void) PROGMEM = { &CmndDeepsleepTime }; +uint32_t deepsleep_sleeptime = 0; +uint8_t deepsleep_flag = 0; + bool DeepSleepEnabled(void) { - if (0 == Settings.deepsleep) { + if (Settings.deepsleep < 10) { return false; } @@ -77,59 +80,81 @@ void DeepSleepInit(void) void DeepSleepCheck(void) { - // new function AFTER_TELEPERIOD can take some time therefore <2 - if (DeepSleepEnabled() && (Settings.deepsleep > 10) && (Settings.deepsleep < 4294967295)) { - SettingsSaveAll(); - // deepsleep_slip is ideally 10.000 == 100% - // typically the device has up to 4% slip. Anything else is a wrong setting in the deepsleep_slip - // therefore all values >110% or <90% will be resetted to 100% to avoid crazy sleep times. - // This should normally never executed, but can happen an manual wakeup and problems during wakeup - if ((RtcSettings.nextwakeup == 0) || (RtcSettings.deepsleep_slip < 9000) || (RtcSettings.deepsleep_slip > 11000) || (RtcSettings.nextwakeup > (UtcTime() + Settings.deepsleep))) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); - RtcSettings.nextwakeup = 0; - RtcSettings.deepsleep_slip = 10000; -// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("new settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); - } - // timeslip in 0.1 seconds between the real wakeup and the calculated wakeup - // because deepsleep is in second and timeslip in 0.1 sec the compare always check if the slip is in the 10% range - int16_t timeslip = (int16_t)(RtcSettings.nextwakeup+millis()/1000-UtcTime())*10; -// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: Timeslip 0.1 sec:? %d < %d < %ld"), -Settings.deepsleep, timeslip, Settings.deepsleep ); - //allow 10% of deepsleep error to count as valid deepsleep; expecting 3-4% - // if more then 10% timeslip = 0 == non valid wakeup; maybe manual - timeslip = (timeslip < -(int32_t)Settings.deepsleep) ? 0 : (timeslip > (int32_t)Settings.deepsleep) ? 0 : 1; -// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: Normal deepsleep? %d"), timeslip ); - if (timeslip) { - RtcSettings.deepsleep_slip = (Settings.deepsleep + RtcSettings.nextwakeup-UtcTime()) * RtcSettings.deepsleep_slip / (Settings.deepsleep - (millis() / 1000)); - //Avoid crazy numbers. Again maximum 10% deviation. -// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: %% calculate drift %ld"), RtcSettings.deepsleep_slip ); - RtcSettings.deepsleep_slip = tmin(tmax(RtcSettings.deepsleep_slip, 9000),11000); + // Deepsleep_slip is ideally 10.000 == 100% + // Typically the device has up to 4% slip. Anything else is a wrong setting in the deepsleep_slip + // Therefore all values >110% or <90% will be resetted to 100% to avoid crazy sleep times. + // This should normally never executed, but can happen an manual wakeup and problems during wakeup + if ((RtcSettings.nextwakeup == 0) || + (RtcSettings.deepsleep_slip < 9000) || + (RtcSettings.deepsleep_slip > 11000) || + (RtcSettings.nextwakeup > (UtcTime() + Settings.deepsleep))) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), + RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); + RtcSettings.nextwakeup = 0; + RtcSettings.deepsleep_slip = 10000; + } -// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: %% new drift %ld"), RtcSettings.deepsleep_slip ); - RtcSettings.nextwakeup += Settings.deepsleep; + // Timeslip in 0.1 seconds between the real wakeup and the calculated wakeup + // Because deepsleep is in second and timeslip in 0.1 sec the compare always check if the slip is in the 10% range + int16_t timeslip = (int16_t)(RtcSettings.nextwakeup + millis() / 1000 - UtcTime()) * 10; + + // Allow 10% of deepsleep error to count as valid deepsleep; expecting 3-4% + // if more then 10% timeslip = 0 == non valid wakeup; maybe manual + timeslip = (timeslip < -(int32_t)Settings.deepsleep) ? 0 : (timeslip > (int32_t)Settings.deepsleep) ? 0 : 1; + if (timeslip) { + RtcSettings.deepsleep_slip = (Settings.deepsleep + RtcSettings.nextwakeup - UtcTime()) * RtcSettings.deepsleep_slip / (Settings.deepsleep - (millis() / 1000)); + // Avoid crazy numbers. Again maximum 10% deviation. + RtcSettings.deepsleep_slip = tmin(tmax(RtcSettings.deepsleep_slip, 9000), 11000); + RtcSettings.nextwakeup += Settings.deepsleep; + } + + // It may happen that wakeup in just <5 seconds in future + // In this case also add deepsleep to nextwakeup + if (RtcSettings.nextwakeup <= (UtcTime() - MIN_DEEPSLEEP_TIME)) { + // ensure nextwakeup is at least in the future + RtcSettings.nextwakeup += (((UtcTime() + MIN_DEEPSLEEP_TIME - RtcSettings.nextwakeup) / Settings.deepsleep) + 1) * Settings.deepsleep; + } + + String dt = GetDT(RtcSettings.nextwakeup + LocalTime() - UtcTime()); // 2017-03-07T11:08:02 + // Limit sleeptime to MAX_DEEPSLEEP_CYCLE + // uint32_t deepsleep_sleeptime = MAX_DEEPSLEEP_CYCLE < (RtcSettings.nextwakeup - UtcTime()) ? (uint32_t)MAX_DEEPSLEEP_CYCLE : RtcSettings.nextwakeup - UtcTime(); + deepsleep_sleeptime = tmin((uint32_t)MAX_DEEPSLEEP_CYCLE ,RtcSettings.nextwakeup - UtcTime()); + + // stat/tasmota/STATUS = {"DeepSleep":{"Time":"2019-11-12T21:33:45","Epoch":1573590825}} + Response_P(PSTR("{\"" D_PRFX_DEEPSLEEP "\":{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%d}}"), (char*)dt.c_str(), RtcSettings.nextwakeup); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_STATUS)); + +// Response_P(S_OFFLINE); +// MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic +} + +void DeepSleepStart(void) +{ + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Sleeping")); // Won't show in GUI + + WifiShutdown(); + RtcSettings.ultradeepsleep = RtcSettings.nextwakeup - UtcTime(); + RtcSettingsSave(); + + ESP.deepSleep(100 * RtcSettings.deepsleep_slip * deepsleep_sleeptime); + yield(); +} + +void DeepSleepEverySecond(void) +{ + if (!deepsleep_flag) { return; } + + if (DeepSleepEnabled()) { + if (4 == deepsleep_flag) { // Allow 4 seconds to update web console before deepsleep + SettingsSaveAll(); + DeepSleepCheck(); } - // it may happen that wakeup in just <5 seconds in future - // in this case also add deepsleep to nextwakeup - if (RtcSettings.nextwakeup <= (UtcTime() - MIN_DEEPSLEEP_TIME)) { - // ensure nextwakeup is at least in the future - RtcSettings.nextwakeup += (((UtcTime() + MIN_DEEPSLEEP_TIME - RtcSettings.nextwakeup) / Settings.deepsleep) + 1) * Settings.deepsleep; + deepsleep_flag--; + if (deepsleep_flag <= 0) { + DeepSleepStart(); } - String dt = GetDT(RtcSettings.nextwakeup + LocalTime() - UtcTime()); // 2017-03-07T11:08:02 -// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: Next wakeup %s"), (char*)dt.c_str()); - //limit sleeptime to MAX_DEEPSLEEP_CYCLE - //uint32_t sleeptime = MAX_DEEPSLEEP_CYCLE < (RtcSettings.nextwakeup - UtcTime()) ? (uint32_t)MAX_DEEPSLEEP_CYCLE : RtcSettings.nextwakeup - UtcTime(); - uint32_t sleeptime = tmin((uint32_t)MAX_DEEPSLEEP_CYCLE , RtcSettings.nextwakeup - UtcTime()); - Response_P(PSTR("{\"" D_PRFX_DEEPSLEEP "\":{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%d}}"), (char*)dt.c_str(), RtcSettings.nextwakeup); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_STATUS "1"), false); - Response_P(S_OFFLINE); - MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); // Offline or remove previous retained topic - yield(); - MqttDisconnect(); - RtcSettings.ultradeepsleep = RtcSettings.nextwakeup - UtcTime(); -// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("DSL: Sleeptime %d sec, deepsleep_slip %ld"), sleeptime, RtcSettings.deepsleep_slip); - RtcSettingsSave(); - ESP.deepSleep(100 * RtcSettings.deepsleep_slip * sleeptime); - yield(); - // Sleeping + } else { + deepsleep_flag = 0; } } @@ -139,11 +164,11 @@ void DeepSleepCheck(void) void CmndDeepsleepTime(void) { - if ((XdrvMailbox.payload == 0) || + if ((0 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 10) && (XdrvMailbox.payload < (10 * 366 * 24 * 60 * 60)))) { // Allow max 10 years sleep Settings.deepsleep = XdrvMailbox.payload; RtcSettings.nextwakeup = 0; - tele_period = Settings.tele_period -1; // Initiate start DeepSleep on next finish of forced TelePeriod + deepsleep_flag = (0 == XdrvMailbox.payload) ? 0 : 4; } Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, Settings.deepsleep); } @@ -157,8 +182,13 @@ bool Xdrv29(uint8_t function) bool result = false; switch (function) { + case FUNC_EVERY_SECOND: + DeepSleepEverySecond(); + break; case FUNC_AFTER_TELEPERIOD: - DeepSleepCheck(); + if (!deepsleep_flag) { + deepsleep_flag = 4; // Start deepsleep in 4 seconds + } break; case FUNC_COMMAND: result = DecodeCommand(kDeepsleepCommands, DeepsleepCommand);