diff --git a/CHANGELOG.md b/CHANGELOG.md index bcf1747a7..05b50720b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.5.4] 2019-03-03 + +### Changed + +- MQTT keep alive changed from 5 minutes to 1 minute + +### Added +- Callback for OTA. This is used to disable EMS bus during a firmware OTA update, which caused problems with the latest ESP89266 core libraries +- Added rough estimate of WiFi signal strength to info page +- Added the build time & date to the info page (optional in platformio.ini) + + + ## [1.5.3] 2019-02-22 ### Changed diff --git a/firmware/firmware_d1_mini.bin b/firmware/firmware_d1_mini.bin index 447c28c30..15ca8a6fc 100644 Binary files a/firmware/firmware_d1_mini.bin and b/firmware/firmware_d1_mini.bin differ diff --git a/lib/myESP/MyESP.cpp b/lib/myESP/MyESP.cpp index e08fb4973..93398633b 100644 --- a/lib/myESP/MyESP.cpp +++ b/lib/myESP/MyESP.cpp @@ -2,7 +2,8 @@ * MyESP - my ESP helper class to handle Wifi, MQTT and Telnet * * Paul Derbyshire - December 2018 - * Version 1.1 - Feb 22 2019. Added support for ESP32 + * Version 1.1 - Feb 22 2019. Added support for ESP32 + * Version 1.1.1 - March 3 2019. Added OTA callback * * Ideas borrowed from Espurna https://github.com/xoseperez/espurna */ @@ -13,7 +14,7 @@ MyESP::MyESP() { _app_hostname = strdup("MyESP"); _app_name = strdup("MyESP"); - _app_version = strdup("1.0.0"); + _app_version = strdup("1.1.1"); _boottime = strdup("unknown"); _load_average = 100; // calculated load average @@ -47,6 +48,8 @@ MyESP::MyESP() { _wifi_callback = NULL; _wifi_connected = false; + _ota_callback = NULL; + _suspendOutput = false; } @@ -297,6 +300,20 @@ void MyESP::_wifi_setup() { jw.addNetwork(_wifi_ssid, _wifi_password); // Add a network } +// set the callback function for the OTA onstart +void MyESP::setOTA(ota_callback_f OTACallback) { + _ota_callback = OTACallback; +} + +// OTA callback when the upload process starts +void MyESP::_OTACallback() { + myDebug_P(PSTR("[OTA] Start")); + SerialAndTelnet.handle(); // force flush + if (_ota_callback) { + (_ota_callback)(); // call custom function to handle mqtt receives + } +} + // OTA Setup void MyESP::_ota_setup() { if (!_wifi_ssid) { @@ -306,7 +323,7 @@ void MyESP::_ota_setup() { //ArduinoOTA.setPort(OTA_PORT); ArduinoOTA.setHostname(_app_hostname); - ArduinoOTA.onStart([this]() { myDebug_P(PSTR("[OTA] Start")); }); + ArduinoOTA.onStart([this]() { _OTACallback(); }); ArduinoOTA.onEnd([this]() { myDebug_P(PSTR("[OTA] Done, restarting...")); }); ArduinoOTA.onProgress([this](unsigned int progress, unsigned int total) { static unsigned int _progOld; @@ -374,6 +391,70 @@ void MyESP::_telnet_setup() { memset(_command, 0, TELNET_MAX_COMMAND_LENGTH); } + +#define RTC_LEAP_YEAR(year) ((((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0)) + +/* Days in a month */ +static uint8_t RTC_Months[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, /* Not leap year */ + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} /* Leap year */ +}; + +// https://stackoverflow.com/questions/43063071/the-arduino-ntp-i-want-print-out-datadd-mm-yyyy +void MyESP::_printBuildTime(unsigned long unix) { + // compensate for summer/winter time and CET. Can't be bothered to work out DST. + // add 3600 to the UNIX time during winter, (3600 s = 1 h), and 7200 during summer (DST). + unix += 3600; // add 1 hour + + uint8_t Day, Month; + + uint8_t Seconds = unix % 60; /* Get seconds from unix */ + unix /= 60; /* Go to minutes */ + uint8_t Minutes = unix % 60; /* Get minutes */ + unix /= 60; /* Go to hours */ + uint8_t Hours = unix % 24; /* Get hours */ + unix /= 24; /* Go to days */ + uint8_t WeekDay = (unix + 3) % 7 + 1; /* Get week day, monday is first day */ + + uint16_t year = 1970; /* Process year */ + while (1) { + if (RTC_LEAP_YEAR(year)) { + if (unix >= 366) { + unix -= 366; + } else { + break; + } + } else if (unix >= 365) { + unix -= 365; + } else { + break; + } + year++; + } + + /* Get year in xx format */ + uint8_t Year = (uint8_t)(year - 2000); + /* Get month */ + for (Month = 0; Month < 12; Month++) { + if (RTC_LEAP_YEAR(year)) { + if (unix >= (uint32_t)RTC_Months[1][Month]) { + unix -= RTC_Months[1][Month]; + } else { + break; + } + } else if (unix >= (uint32_t)RTC_Months[0][Month]) { + unix -= RTC_Months[0][Month]; + } else { + break; + } + } + + Month++; /* Month starts with 1 */ + Day = unix + 1; /* Date starts with 1 */ + + SerialAndTelnet.printf("%02d:%02d:%02d %d/%d/%d", Hours, Minutes, Seconds, Day, Month, Year); +} + // Show help of commands void MyESP::_consoleShowHelp() { SerialAndTelnet.println(); @@ -389,15 +470,21 @@ void MyESP::_consoleShowHelp() { #else String hostname = WiFi.hostname(); #endif - SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s", + SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s", hostname.c_str(), WiFi.localIP().toString().c_str(), WiFi.macAddress().c_str()); #ifdef ARDUINO_BOARD SerialAndTelnet.printf(" Board: %s", ARDUINO_BOARD); #endif + +#ifdef BUILD_TIME + SerialAndTelnet.print(" (Build "); + _printBuildTime(BUILD_TIME); + SerialAndTelnet.print(")"); +#endif SerialAndTelnet.println(); - SerialAndTelnet.printf("* Connected to WiFi SSID: %s", WiFi.SSID().c_str()); + SerialAndTelnet.printf("* Connected to WiFi SSID: %s (signal %d%%)", WiFi.SSID().c_str(), getWifiQuality()); SerialAndTelnet.println(); SerialAndTelnet.printf("* Boot time: %s", _boottime); SerialAndTelnet.println(); @@ -991,8 +1078,7 @@ void MyESP::_calculateLoad() { } } -// return true if wifi is connected -// WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library +// return true if wifi is connected: // WL_IDLE_STATUS = 0, // WL_NO_SSID_AVAIL = 1, // WL_SCAN_COMPLETED = 2, @@ -1004,6 +1090,25 @@ bool MyESP::isWifiConnected() { return (_wifi_connected); } +/* + * Return the quality (Received Signal Strength Indicator) of the WiFi network. + * Returns -1 if WiFi is disconnected. + * High quality: 90% ~= -55dBm + * Medium quality: 50% ~= -75dBm + * Low quality: 30% ~= -85dBm + * Unusable quality: 8% ~= -96dBm + */ +int MyESP::getWifiQuality() { + if (WiFi.status() != WL_CONNECTED) + return -1; + int dBm = WiFi.RSSI(); + if (dBm <= -100) + return 0; + if (dBm >= -50) + return 100; + return 2 * (dBm + 100); +} + // register new instance void MyESP::begin(const char * app_hostname, const char * app_name, const char * app_version) { _app_hostname = strdup(app_hostname); diff --git a/lib/myESP/MyESP.h b/lib/myESP/MyESP.h index 2fd7f78df..2753add6f 100644 --- a/lib/myESP/MyESP.h +++ b/lib/myESP/MyESP.h @@ -81,6 +81,7 @@ typedef enum { MYESP_FSACTION_SET, MYESP_FSACTION_LIST, MYESP_FSACTION_SAVE, MYE typedef std::function mqtt_callback_f; typedef std::function wifi_callback_f; +typedef std::function ota_callback_f; typedef std::function telnetcommand_callback_f; typedef std::function telnet_callback_f; typedef std::function fs_callback_f; @@ -107,18 +108,21 @@ class MyESP { void mqttSubscribe(const char * topic); void mqttUnsubscribe(const char * topic); void mqttPublish(const char * topic, const char * payload); - void setMQTT(const char * mqtt_host, - const char * mqtt_username, - const char * mqtt_password, - const char * mqtt_base, - unsigned long mqtt_keepalive, - unsigned char mqtt_qos, - bool mqtt_retain, - const char * mqtt_will_topic, - const char * mqtt_will_online_payload, - const char * mqtt_will_offline_payload, + void setMQTT(const char * mqtt_host, + const char * mqtt_username, + const char * mqtt_password, + const char * mqtt_base, + unsigned long mqtt_keepalive, + unsigned char mqtt_qos, + bool mqtt_retain, + const char * mqtt_will_topic, + const char * mqtt_will_online_payload, + const char * mqtt_will_offline_payload, mqtt_callback_f callback); + // OTA + void setOTA(ota_callback_f OTACallback); + // debug & telnet void myDebug(const char * format, ...); void myDebug_P(PGM_P format_P, ...); @@ -136,6 +140,8 @@ class MyESP { void setBoottime(const char * boottime); void resetESP(); uint16_t getSystemLoadAverage(); + int getWifiQuality(); + private: // mqtt @@ -170,7 +176,9 @@ class MyESP { bool _wifi_connected; // ota - void _ota_setup(); + ota_callback_f _ota_callback; + void _ota_setup(); + void _OTACallback(); // telnet & debug TelnetSpy SerialAndTelnet; @@ -205,6 +213,7 @@ class MyESP { char * _boottime; bool _suspendOutput; bool _use_serial; + void _printBuildTime(unsigned long rawTime); // load average (0..100) void _calculateLoad(); diff --git a/platformio.ini-example b/platformio.ini-example index 616a52e45..39e122318 100644 --- a/platformio.ini-example +++ b/platformio.ini-example @@ -7,6 +7,7 @@ env_default = d1_mini platform = espressif8266 flash_mode = dout build_flags = -g -w +;build_flags = -g -w -DBUILD_TIME=$UNIX_TIME wifi_settings = ; hard code if you prefer. Recommendation is to set from within the app when in Serial or AP mode diff --git a/src/ems-esp.ino b/src/ems-esp.ino index 9478fdb71..d6c51965a 100644 --- a/src/ems-esp.ino +++ b/src/ems-esp.ino @@ -660,7 +660,7 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c // reset pin pinMode(EMSESP_Status.led_gpio, OUTPUT); digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off - ok = true; + ok = true; } // dallas_gpio @@ -841,6 +841,12 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) { } } +// OTA callback when the OTA process starts +// so we can disable the EMS to avoid any noise +void OTACallback() { + emsuart_stop(); +} + // MQTT Callback to handle incoming/outgoing changes void MQTTCallback(unsigned int type, const char * topic, const char * message) { // we're connected. lets subscribe to some topics @@ -900,7 +906,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { ems_setWarmWaterActivated(false); } } - + // boiler wwtemp changes if (strcmp(topic, TOPIC_BOILER_CMD_WWTEMP) == 0) { float f = strtof((char *)message, 0); @@ -941,7 +947,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { void WIFICallback() { // This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals // This is done after we have a WiFi signal to avoid any resource conflicts - + if (myESP.getUseSerial()) { myDebug("EMS UART disabled when in Serial mode. Use 'set serial off' to change."); } else { @@ -950,7 +956,6 @@ void WIFICallback() { // go and find the boiler and thermostat types ems_discoverModels(); } - } // Initialize the boiler settings and shower settings @@ -1130,8 +1135,20 @@ void setup() { #endif // MQTT host, username and password taken from the SPIFFS settings - myESP.setMQTT(NULL, NULL, NULL, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, - MQTT_WILL_ONLINE_PAYLOAD, MQTT_WILL_OFFLINE_PAYLOAD, MQTTCallback); + myESP.setMQTT(NULL, + NULL, + NULL, + MQTT_BASE, + MQTT_KEEPALIVE, + MQTT_QOS, + MQTT_RETAIN, + MQTT_WILL_TOPIC, + MQTT_WILL_ONLINE_PAYLOAD, + MQTT_WILL_OFFLINE_PAYLOAD, + MQTTCallback); + + // OTA callback which is called when OTA is starting + myESP.setOTA(OTACallback); // custom settings in SPIFFS myESP.setSettings(FSCallback, SettingsCallback); @@ -1143,7 +1160,7 @@ void setup() { if (EMSESP_Status.led_gpio != EMS_VALUE_INT_NOTSET) { pinMode(EMSESP_Status.led_gpio, OUTPUT); digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off - ledcheckTimer.attach_ms(LEDCHECK_TIME, do_ledcheck); // blink heartbeat LED + ledcheckTimer.attach_ms(LEDCHECK_TIME, do_ledcheck); // blink heartbeat LED } // check for Dallas sensors diff --git a/src/my_config.h b/src/my_config.h index a5581c2d0..c0a1ef781 100644 --- a/src/my_config.h +++ b/src/my_config.h @@ -20,7 +20,7 @@ #define MQTT_WILL_ONLINE_PAYLOAD "online" // for last will & testament payload #define MQTT_WILL_OFFLINE_PAYLOAD "offline" // for last will & testament payload #define MQTT_RETAIN false -#define MQTT_KEEPALIVE 300 +#define MQTT_KEEPALIVE 60 // 1 minute #define MQTT_QOS 1 // MQTT for thermostat diff --git a/src/version.h b/src/version.h index 9a2580edc..77cc3d6c5 100644 --- a/src/version.h +++ b/src/version.h @@ -6,5 +6,5 @@ #pragma once #define APP_NAME "EMS-ESP" -#define APP_VERSION "1.5.3" +#define APP_VERSION "1.5.4" #define APP_HOSTNAME "ems-esp"