diff --git a/CHANGELOG.md b/CHANGELOG.md index 10628b887..eaaf72843 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,51 @@ 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). +## [2.2.0] December 28 2020 + +### Added +- function keys in editor: cursor, del, home, end. F1=help, F2=show, and other shortcuts +- SM100 pump working time and energy units +- heating curve parameters and commands for RC300 +- `wwonetime` for RC300 thermostat +- expose test framework via api (#611) +- SysLog has enable/disable flag in WebUI +- Add solar configuration telegrams (#616) [thanks @hpanther] +- `log trace` shows decoded or optional raw telegrams, `watch unknown` for only unknown telegrams +- WM10 switch telegrams +- boiler information (#633), pumpmod min/max commands +- maintenance message and command +- thermostat program, reducemode, controlmode +- optional delayed start for sending tx-telegrams to prevent conflicts with KM200 +- RC35 holiday setting with `+` for 'at home' + +### Fixed +- mixer IPM pumpstatus +- mixer devices in HA were incorrectly named +- Prevent HA MQTT config messages for thermostat that has no 'currtemp' (#582) +- serviceCodeNumber, 3-char serviceCode, exhausttemp and heating_active for newer ems+ boilers +- prevent MQTT publish messages from sending twice +- repeated output on read commands +- heating_active for ems+ + +### Changed +- optimized MQTT for HA to reduce heap fragmentation issues +- change syslog settings without reboot +- HA-config split in smaller blocks +- commands `fetch` and `publish [ha]` as call +- mqtt json package sizes +- renamed the command system info (which showed settings) to `settings` +- renamed the command system report (Which dumped debug info) to `info` +- Changing settings via web restarts only selected services +- renamed pio targets (esp8266-ci and esp32-ci for GitHub CI) +- telnet default settings `log info`, timeout 60 min +- `log debug` not showing telegram names, use `log trace` or `watch on` to show the telegrams +- optimized how console and web display device data ([#632](https://github.com/proddy/EMS-ESP/issues/632)) + +### Removed +- old shell and python build scripts + + ## [2.1.0] October 31 2020 ### Added @@ -39,7 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix writing to the Junkers FR120 thermostat - support for changing summermode - added missing `heatingtype` to thermostat data -- handle incomming ems+ read requests, ignore F7 telegrams with 3byte-id +- handle incoming ems+ read requests, ignore F7 telegrams with 3byte-id - fix month for setting clock from NTP ### Changed @@ -675,4 +720,4 @@ There are breaking changes in this release. See `publish_time` below and make su ## [0.1.0] 2018-05-14 -- Initial development version +- Initial development version \ No newline at end of file diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index a6f80b134..41f082e80 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -1,40 +1,10 @@ # Changelog ### Added -- function keys in editor: cursor, del, home, end. F1=help, F2=show, and other shortcuts -- SM100 pump working time and energy units -- heating curve parameters and commands for RC300 -- `wwonetime` for RC300 thermostat -- expose test framework via api (#611) -- SysLog has enable/disable flag in WebUI -- Add solar configuration telegrams (#616) [thanks @hpanther] -- `log trace` shows decoded telegrams, `watch unknown` for only unknown telegrams -- WM10 switch telegrams -- boiler information (#633), pumpmod min/max commands -- maintenance message and command -- thermostat program, reducemode, controlmode + - See https://github.com/proddy/EMS-ESP/issues/632 ### Fixed -- mixer IPM pumpstatus -- mixer devices in HA were incorrectly named -- Prevent HA MQTT config messages for thermostat that has no 'currtemp' (#582) -- serviceCodeNumber, 3-char serviceCode, exhausttemp and heating_active for newer ems+ boilers -- prevent MQTT publish messages from sending twice -- repeated output on read commands ### Changed -- optimized MQTT for HA to reduce heap fragmentation issues -- change syslog settings without reboot -- HA-config split in smaller blocks -- commands `fetch` and `publish [ha]` as call -- mqtt json package sizes -- renamed the command system info (which showed settings) to `settings` -- renamed the command system report (Which dumped debug info) to `info` -- Changing settings via web restarts only selected services -- renamed pio targets (esp8266-ci and esp32-ci for GitHub CI) -- telnet default settings `log info`, timeout 60 min -- `log debug` not showing telegram names, use `log trace` or `watch on` to show the telegrams -- optimized how console and web display device data ([#632](https://github.com/proddy/EMS-ESP/issues/632)) -### Removed -- old shell and python build scripts +### Removed \ No newline at end of file diff --git a/interface/src/system/SystemStatusForm.tsx b/interface/src/system/SystemStatusForm.tsx index d85702bb2..64b9f1bbd 100644 --- a/interface/src/system/SystemStatusForm.tsx +++ b/interface/src/system/SystemStatusForm.tsx @@ -94,17 +94,6 @@ class SystemStatusForm extends Component ) } - - - - - - - - diff --git a/lib/framework/APSettingsService.cpp b/lib/framework/APSettingsService.cpp index 41350f2f9..b847748f3 100644 --- a/lib/framework/APSettingsService.cpp +++ b/lib/framework/APSettingsService.cpp @@ -21,7 +21,7 @@ void APSettingsService::reconfigureAP() { void APSettingsService::loop() { unsigned long currentMillis = uuid::get_uptime(); - unsigned long manageElapsed = (unsigned long)(currentMillis - _lastManaged); + unsigned long manageElapsed = (uint32_t)(currentMillis - _lastManaged); if (manageElapsed >= MANAGE_NETWORK_DELAY) { _lastManaged = currentMillis; manageAP(); diff --git a/lib/framework/ESPUtils.h b/lib/framework/ESPUtils.h index e24952ce6..5e6170729 100644 --- a/lib/framework/ESPUtils.h +++ b/lib/framework/ESPUtils.h @@ -7,7 +7,7 @@ class ESPUtils { public: static String defaultDeviceValue(String prefix = "") { #ifdef ESP32 - return prefix + String((unsigned long)ESP.getEfuseMac(), HEX); + return prefix + String((uint32_t)ESP.getEfuseMac(), HEX); #elif defined(ESP8266) return prefix + String(ESP.getChipId(), HEX); #endif diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp index 0fceef01d..8dc3e10fb 100644 --- a/lib/framework/MqttSettingsService.cpp +++ b/lib/framework/MqttSettingsService.cpp @@ -61,7 +61,7 @@ void MqttSettingsService::begin() { } void MqttSettingsService::loop() { - if (_reconfigureMqtt || (_disconnectedAt && (unsigned long)(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) { + if (_reconfigureMqtt || (_disconnectedAt && (uint32_t)(uuid::get_uptime() - _disconnectedAt) >= MQTT_RECONNECTION_DELAY)) { // reconfigure MQTT client configureMqtt(); diff --git a/lib/framework/NTPSettingsService.cpp b/lib/framework/NTPSettingsService.cpp index 202a82c8c..0a2605d4f 100644 --- a/lib/framework/NTPSettingsService.cpp +++ b/lib/framework/NTPSettingsService.cpp @@ -70,7 +70,11 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari if (!sntp_enabled() && json.is()) { String timeUtc = json["time_utc"]; struct tm tm = {0}; - char * s = strptime(timeUtc.c_str(), "%Y-%m-%dT%H:%M:%SZ", &tm); + + // TODO fix strptime, which is not included in xtensa + // char * s = strptime(timeUtc.c_str(), "%Y-%m-%dT%H:%M:%SZ", &tm); + + char * s = nullptr; if (s != nullptr) { time_t time = mktime(&tm); struct timeval now = {.tv_sec = time}; @@ -80,6 +84,7 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari return; } } + AsyncWebServerResponse * response = request->beginResponse(400); request->send(response); } diff --git a/lib/framework/SystemStatus.cpp b/lib/framework/SystemStatus.cpp index 017e5c723..0bedb9ce0 100644 --- a/lib/framework/SystemStatus.cpp +++ b/lib/framework/SystemStatus.cpp @@ -7,8 +7,6 @@ SystemStatus::SystemStatus(AsyncWebServer * server, SecurityManager * securityMa } void SystemStatus::systemStatus(AsyncWebServerRequest * request) { - uint8_t free_mem_percent = emsesp::System::free_mem(); // added by proddy - AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE); JsonObject root = response->getRoot(); #ifdef ESP32 @@ -40,8 +38,7 @@ void SystemStatus::systemStatus(AsyncWebServerRequest * request) { root["fs_used"] = fs_info.usedBytes; #endif - root["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); // proddy added - root["free_mem"] = free_mem_percent; // proddy added + root["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); // proddy added response->setLength(); request->send(response); diff --git a/lib/framework/WiFiSettingsService.cpp b/lib/framework/WiFiSettingsService.cpp index 54226a550..588a7c2aa 100644 --- a/lib/framework/WiFiSettingsService.cpp +++ b/lib/framework/WiFiSettingsService.cpp @@ -56,7 +56,7 @@ void WiFiSettingsService::reconfigureWiFiConnection() { void WiFiSettingsService::loop() { unsigned long currentMillis = millis(); - if (!_lastConnectionAttempt || (unsigned long)(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) { + if (!_lastConnectionAttempt || (uint32_t)(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) { _lastConnectionAttempt = currentMillis; manageSTA(); } diff --git a/lib/uuid-console/src/shell_log.cpp b/lib/uuid-console/src/shell_log.cpp index 8556f18a7..93d5f1104 100644 --- a/lib/uuid-console/src/shell_log.cpp +++ b/lib/uuid-console/src/shell_log.cpp @@ -30,7 +30,7 @@ namespace uuid { namespace console { -static const char __pstr__logger_name[] __attribute__((__aligned__(sizeof(int)))) PROGMEM = "shell"; +static const char __pstr__logger_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "shell"; const uuid::log::Logger Shell::logger_{reinterpret_cast(__pstr__logger_name), uuid::log::Facility::LPR}; Shell::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ptr && content) diff --git a/lib/uuid-log/src/format_level_lowercase.cpp b/lib/uuid-log/src/format_level_lowercase.cpp index 61dc124fb..03b386a13 100644 --- a/lib/uuid-log/src/format_level_lowercase.cpp +++ b/lib/uuid-log/src/format_level_lowercase.cpp @@ -26,19 +26,19 @@ namespace uuid { namespace log { -static constexpr const char * pstr_level_lowercase_off __attribute__((__aligned__(sizeof(int)))) PROGMEM = "off"; -static constexpr const char * pstr_level_lowercase_emerg __attribute__((__aligned__(sizeof(int)))) PROGMEM = "emerg"; -static constexpr const char * pstr_level_lowercase_crit __attribute__((__aligned__(sizeof(int)))) PROGMEM = "crit"; -static constexpr const char * pstr_level_lowercase_alert __attribute__((__aligned__(sizeof(int)))) PROGMEM = "alert"; -static constexpr const char * pstr_level_lowercase_err __attribute__((__aligned__(sizeof(int)))) PROGMEM = "err"; -static constexpr const char * pstr_level_lowercase_warning __attribute__((__aligned__(sizeof(int)))) PROGMEM = "warning"; -static constexpr const char * pstr_level_lowercase_notice __attribute__((__aligned__(sizeof(int)))) PROGMEM = "notice"; -static constexpr const char * pstr_level_lowercase_info __attribute__((__aligned__(sizeof(int)))) PROGMEM = "info"; -static constexpr const char * pstr_level_lowercase_debug __attribute__((__aligned__(sizeof(int)))) PROGMEM = "debug"; -static constexpr const char * pstr_level_lowercase_trace __attribute__((__aligned__(sizeof(int)))) PROGMEM = "trace"; -static constexpr const char * pstr_level_lowercase_all __attribute__((__aligned__(sizeof(int)))) PROGMEM = "all"; +static constexpr const char * pstr_level_lowercase_off __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "off"; +static constexpr const char * pstr_level_lowercase_emerg __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "emerg"; +static constexpr const char * pstr_level_lowercase_crit __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "crit"; +static constexpr const char * pstr_level_lowercase_alert __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "alert"; +static constexpr const char * pstr_level_lowercase_err __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "err"; +static constexpr const char * pstr_level_lowercase_warning __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "warning"; +static constexpr const char * pstr_level_lowercase_notice __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "notice"; +static constexpr const char * pstr_level_lowercase_info __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "info"; +static constexpr const char * pstr_level_lowercase_debug __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "debug"; +static constexpr const char * pstr_level_lowercase_trace __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "trace"; +static constexpr const char * pstr_level_lowercase_all __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "all"; -static const __FlashStringHelper * log_level_lowercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(int)))) +static const __FlashStringHelper * log_level_lowercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = {reinterpret_cast(pstr_level_lowercase_off), reinterpret_cast(pstr_level_lowercase_emerg), reinterpret_cast(pstr_level_lowercase_crit), diff --git a/lib/uuid-log/src/format_level_uppercase.cpp b/lib/uuid-log/src/format_level_uppercase.cpp index 480e2e7a8..ca0d66677 100644 --- a/lib/uuid-log/src/format_level_uppercase.cpp +++ b/lib/uuid-log/src/format_level_uppercase.cpp @@ -26,19 +26,19 @@ namespace uuid { namespace log { -static constexpr const char * pstr_level_uppercase_off __attribute__((__aligned__(sizeof(int)))) PROGMEM = "OFF"; -static constexpr const char * pstr_level_uppercase_emerg __attribute__((__aligned__(sizeof(int)))) PROGMEM = "EMERG"; -static constexpr const char * pstr_level_uppercase_crit __attribute__((__aligned__(sizeof(int)))) PROGMEM = "CRIT"; -static constexpr const char * pstr_level_uppercase_alert __attribute__((__aligned__(sizeof(int)))) PROGMEM = "ALERT"; -static constexpr const char * pstr_level_uppercase_err __attribute__((__aligned__(sizeof(int)))) PROGMEM = "ERR"; -static constexpr const char * pstr_level_uppercase_warning __attribute__((__aligned__(sizeof(int)))) PROGMEM = "WARNING"; -static constexpr const char * pstr_level_uppercase_notice __attribute__((__aligned__(sizeof(int)))) PROGMEM = "NOTICE"; -static constexpr const char * pstr_level_uppercase_info __attribute__((__aligned__(sizeof(int)))) PROGMEM = "INFO"; -static constexpr const char * pstr_level_uppercase_debug __attribute__((__aligned__(sizeof(int)))) PROGMEM = "DEBUG"; -static constexpr const char * pstr_level_uppercase_trace __attribute__((__aligned__(sizeof(int)))) PROGMEM = "TRACE"; -static constexpr const char * pstr_level_uppercase_all __attribute__((__aligned__(sizeof(int)))) PROGMEM = "ALL"; +static constexpr const char * pstr_level_uppercase_off __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "OFF"; +static constexpr const char * pstr_level_uppercase_emerg __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "EMERG"; +static constexpr const char * pstr_level_uppercase_crit __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "CRIT"; +static constexpr const char * pstr_level_uppercase_alert __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "ALERT"; +static constexpr const char * pstr_level_uppercase_err __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "ERR"; +static constexpr const char * pstr_level_uppercase_warning __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "WARNING"; +static constexpr const char * pstr_level_uppercase_notice __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "NOTICE"; +static constexpr const char * pstr_level_uppercase_info __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "INFO"; +static constexpr const char * pstr_level_uppercase_debug __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "DEBUG"; +static constexpr const char * pstr_level_uppercase_trace __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "TRACE"; +static constexpr const char * pstr_level_uppercase_all __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "ALL"; -static const __FlashStringHelper * log_level_uppercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(int)))) +static const __FlashStringHelper * log_level_uppercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = {reinterpret_cast(pstr_level_uppercase_off), reinterpret_cast(pstr_level_uppercase_emerg), reinterpret_cast(pstr_level_uppercase_crit), diff --git a/lib/uuid-syslog/src/syslog.cpp b/lib/uuid-syslog/src/syslog.cpp index ec59d9395..55d2fd992 100644 --- a/lib/uuid-syslog/src/syslog.cpp +++ b/lib/uuid-syslog/src/syslog.cpp @@ -87,7 +87,7 @@ #include #include -static const char __pstr__logger_name[] __attribute__((__aligned__(sizeof(int)))) PROGMEM = "syslog"; +static const char __pstr__logger_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "syslog"; namespace uuid { @@ -413,7 +413,7 @@ bool SyslogService::transmit(const QueuedLogMessage & message) { tm.tm_hour, tm.tm_min, tm.tm_sec, - (unsigned long)message.time_.tv_usec); + (uint32_t)message.time_.tv_usec); } else { udp_.print('-'); } diff --git a/lib/uuid-telnet/src/telnet.cpp b/lib/uuid-telnet/src/telnet.cpp index e6571c560..9e8150b43 100644 --- a/lib/uuid-telnet/src/telnet.cpp +++ b/lib/uuid-telnet/src/telnet.cpp @@ -59,7 +59,7 @@ #endif #endif -static const char __pstr__logger_name[] __attribute__((__aligned__(sizeof(int)))) PROGMEM = "telnet"; +static const char __pstr__logger_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = "telnet"; namespace uuid { diff --git a/makefile b/makefile index 2a4e46b1b..4f01c490a 100644 --- a/makefile +++ b/makefile @@ -18,7 +18,7 @@ MAKEFLAGS+="j " TARGET := emsesp BUILD := build SOURCES := src lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src src/devices lib/ArduinoJson/src src/test -INCLUDES := lib/ArduinoJson/src lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src src/devices src +INCLUDES := lib/ArduinoJson/src lib_standalone lib/uuid-common/src lib/uuid-console/src lib/uuid-log/src lib/uuid-telnet/src lib/uuid-syslog/src src/devices lib src LIBRARIES := CPPCHECK = cppcheck diff --git a/platformio.ini b/platformio.ini index 520a34125..71ddab7ca 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,6 +10,22 @@ extra_configs = pio_local.ini [common] +; default platformio compile flags are: -fno-rtti -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-functions=4 -ffunction-sections -fdata-sections -fno-exceptions -Wall +core_build_flags = -Wno-deprecated-declarations + -mtarget-align + -free + -fipa-pta + -Wreturn-type + -DCORE_DEBUG_LEVEL=0 + -DNDEBUG + -DFP_IN_IROM + -DBEARSSL_SSL_BASIC + -DVTABLES_IN_FLASH + -DPSTR_ALIGN=1 ; remove the 4-bytes alignment for PSTR() + -std=c17 + -std=c++17 + -std=gnu++17 + debug_flags = ; -D EMSESP_DEBUG ; -D EMSESP_UART_DEBUG @@ -17,8 +33,8 @@ debug_flags = ; -D EMSESP_FORCE_SERIAL ; -D ENABLE_CORS -; default platformio compile flags are: -fno-rtti -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-functions=4 -ffunction-sections -fdata-sections -fno-exceptions -Wall build_flags = + ${common.core_build_flags} ${factory_settings.build_flags} -D FT_PROJECT=1 -D FT_SECURITY=1 @@ -31,6 +47,10 @@ build_flags = -D ARDUINOJSON_ENABLE_STD_STRING=1 -D PROGMEM_WWW -D CORS_ORIGIN=\"http://localhost:3000\" + +build_unflags = -Wall + -Wdeprecated-declarations + -std=gnu++11 [env] framework = arduino @@ -38,6 +58,7 @@ monitor_speed = 115200 upload_speed = 921600 build_type = release lib_ldf_mode = chain+ +; lib_compat_mode = strict check_tool = cppcheck, clangtidy check_severity = high, medium @@ -72,12 +93,20 @@ extra_scripts = scripts/rename_fw.py board = esp12e ; https://github.com/platformio/platform-espressif8266/tree/master/boards platform = espressif8266 ; https://github.com/platformio/platform-espressif8266/releases +platform_packages = platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git + ; toolchain-xtensa @ ~2.100100.200706 + ; toolchain-xtensa @ 2.40802.200502 + ; toolchain-xtensa @ https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-linux-gnu.xtensa-lx106-elf-0474ae9.200706.tar.gz + ; platformio/tool-esptool @ 1.413.0 + ; platformio/tool-esptoolpy @ ~1.30000.0 + mcspr/toolchain-xtensa @ 5.100200.201223 board_build.filesystem = littlefs board_build.f_cpu = 160000000L ; 160MHz ; eagle.flash.4m1m.ld = 1019 KB sketch, 1000 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 2052 KB OTA & buffer ; eagle.flash.4m2m.ld = 1019 KB sketch, 2024 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 1028 KB OTA & buffer ; board_build.ldscript = eagle.flash.4m2m.ld build_flags = ${common.build_flags} ${common.debug_flags} +build_unflags = ${common.build_unflags} lib_ignore = AsyncTCP diff --git a/src/WebAPIService.cpp b/src/WebAPIService.cpp index 8e96efcee..4388681f5 100644 --- a/src/WebAPIService.cpp +++ b/src/WebAPIService.cpp @@ -16,11 +16,7 @@ * along with this program. If not, see . */ -#include "WebAPIService.h" #include "emsesp.h" -#include "command.h" -#include "emsdevice.h" -#include "system.h" namespace emsesp { diff --git a/src/WebDevicesService.cpp b/src/WebDevicesService.cpp index 7e2a289d9..3fc637b54 100644 --- a/src/WebDevicesService.cpp +++ b/src/WebDevicesService.cpp @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -#include "WebDevicesService.h" #include "emsesp.h" namespace emsesp { diff --git a/src/WebSettingsService.cpp b/src/WebSettingsService.cpp index bf5c56cff..ee0e6d618 100644 --- a/src/WebSettingsService.cpp +++ b/src/WebSettingsService.cpp @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -#include "WebSettingsService.h" #include "emsesp.h" namespace emsesp { diff --git a/src/WebStatusService.cpp b/src/WebStatusService.cpp index 9fa8caa3e..d71efff31 100644 --- a/src/WebStatusService.cpp +++ b/src/WebStatusService.cpp @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -#include "WebStatusService.h" #include "emsesp.h" namespace emsesp { diff --git a/src/command.cpp b/src/command.cpp index 077116865..77477c16a 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -24,7 +24,11 @@ namespace emsesp { uuid::log::Logger Command::logger_{F_(command), uuid::log::Facility::DAEMON}; -std::vector Command::cmdfunctions_; +static emsesp::array cmdfunctions_(90, 255, 16); // reserve space for 90 commands + +emsesp::array * Command::commands() { + return &cmdfunctions_; +} // calls a command // id may be used to represent a heating circuit for example @@ -85,12 +89,19 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val } // add a command to the list, which does not return json -void Command::add(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cb) { +void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb) { // if the command already exists for that device type don't add it if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) { return; } - cmdfunctions_.emplace_back(device_type, cmd, cb, nullptr); + + CmdFunction cf; + cf.cmd_ = cmd; + cf.device_type_ = device_type; + cf.cmdfunction_json_ = nullptr; // empty + cf.cmdfunction_ = cb; + cmdfunctions_.push(cf); + // cmdfunctions_.emplace_back(device_type, cmd, cb, nullptr); // see if we need to subscribe if (Mqtt::enabled()) { @@ -105,7 +116,14 @@ void Command::add_with_json(const uint8_t device_type, const __FlashStringHelper return; } - cmdfunctions_.emplace_back(device_type, cmd, nullptr, cb); // add command + CmdFunction cf; + cf.cmd_ = cmd; + cf.device_type_ = device_type; + cf.cmdfunction_json_ = cb; + cf.cmdfunction_ = nullptr; // empty + cmdfunctions_.push(cf); + + // cmdfunctions_.emplace_back(device_type, cmd, nullptr, cb); // add command } // see if a command exists for that device type @@ -132,11 +150,11 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, const ch // output list of all commands to console for a specific DeviceType void Command::show(uuid::console::Shell & shell, uint8_t device_type) { - if (commands().empty()) { + if (cmdfunctions_.empty()) { shell.println(F("No commands")); } - for (const auto & cf : Command::commands()) { + for (const auto & cf : cmdfunctions_) { if (cf.device_type_ == device_type) { shell.printf("%s ", uuid::read_flash_string(cf.cmd_).c_str()); } @@ -162,7 +180,7 @@ bool Command::device_has_commands(const uint8_t device_type) { for (const auto & emsdevice : EMSESP::emsdevices) { if ((emsdevice) && (emsdevice->device_type() == device_type)) { // device found, now see if it has any commands - for (const auto & cf : Command::commands()) { + for (const auto & cf : cmdfunctions_) { if (cf.device_type_ == device_type) { return true; } diff --git a/src/command.h b/src/command.h index eeb142b59..dda7cd9b2 100644 --- a/src/command.h +++ b/src/command.h @@ -26,6 +26,8 @@ #include #include +#include "containers.h" + #include "console.h" #include @@ -44,22 +46,13 @@ class Command { const __FlashStringHelper * cmd_; cmdfunction_p cmdfunction_; cmdfunction_json_p cmdfunction_json_; - - CmdFunction(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cmdfunction, cmdfunction_json_p cmdfunction_json) - : device_type_(device_type) - , cmd_(cmd) - , cmdfunction_(cmdfunction) - , cmdfunction_json_(cmdfunction_json) { - } }; - static std::vector commands() { - return cmdfunctions_; - } + static emsesp::array * commands(); static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json); static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id); - static void add(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, cmdfunction_p cb); + static void add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb); static void add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb); static void show_all(uuid::console::Shell & shell); static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd); @@ -68,8 +61,6 @@ class Command { static void show_devices(uuid::console::Shell & shell); static bool device_has_commands(const uint8_t device_type); - static std::vector cmdfunctions_; // list of commands - private: static uuid::log::Logger logger_; }; diff --git a/src/console.cpp b/src/console.cpp index 7658f3ea5..210b1f00f 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -422,7 +422,7 @@ void EMSESPShell::add_console_commands() { std::vector command_list; uint8_t device_type = EMSdevice::device_name_2_device_type(arguments[0].c_str()); if (Command::device_has_commands(device_type)) { - for (const auto & cf : Command::commands()) { + for (const auto & cf : *Command::commands()) { if (cf.device_type_ == device_type) { command_list.emplace_back(uuid::read_flash_string(cf.cmd_)); } diff --git a/src/console.h b/src/console.h index a97bac804..64ab1cb93 100644 --- a/src/console.h +++ b/src/console.h @@ -46,9 +46,11 @@ using uuid::log::Level; // clang-format off // strings stored 32 bit aligned on ESP8266/ESP32 -#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] __attribute__((__aligned__(sizeof(int)))) PROGMEM = string_literal; +#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = string_literal; #define MAKE_PSTR_WORD(string_name) MAKE_PSTR(string_name, #string_name) #define F_(string_name) FPSTR(__pstr__##string_name) +#define MAKE_PSTR_LIST(list_name, ...) static const __FlashStringHelper * const __pstr__##list_name[] PROGMEM = {__VA_ARGS__, nullptr}; +#define FL_(list_name) (__pstr__##list_name) // clang-format on // localizations @@ -58,7 +60,7 @@ using uuid::log::Level; #undef LOCAL #endif -static constexpr uint32_t INVALID_PASSWORD_DELAY_MS = 3000; +static constexpr uint32_t INVALID_PASSWORD_DELAY_MS = 2000; namespace emsesp { diff --git a/src/containers.h b/src/containers.h new file mode 100644 index 000000000..40a96a4e2 --- /dev/null +++ b/src/containers.h @@ -0,0 +1,448 @@ +/* + * EMS-ESP - https://github.com/proddy/EMS-ESP + * Copyright 2020 Paul Derbyshire + * + * 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 . + */ + +/* + * Lightweight queue & array + * Based ideas from https://github.com/muwerk/ustd + * Limits to max 255 entries + */ + +#ifndef EMSESP_CONTAINERS_H +#define EMSESP_CONTAINERS_H + +#include + +#if defined EMSESP_ASSERT +#include +#endif + +namespace emsesp { + +template +class queueIterator { + public: + queueIterator(T * values_ptr, uint8_t p) + : values_ptr_{values_ptr} + , position_{p} { + } + + bool operator!=(const queueIterator & other) const { + return !(*this == other); + } + + bool operator==(const queueIterator & other) const { + return position_ == other.position_; + } + + queueIterator & operator++() { + ++position_; + return *this; + } + + T & operator*() const { + return *(values_ptr_ + position_); + } + + private: + T * values_ptr_; + uint8_t position_; +}; + +template +class queue { + private: + T * que_; + uint8_t peakSize_; + uint8_t maxSize_; + uint8_t size_; + uint8_t quePtrFront_; // back + uint8_t quePtrBack_; // front + T bad_; + + public: + // Constructs a queue object with the maximum number of pointer entries + queue(uint8_t maxQueueSize) + : maxSize_(maxQueueSize) { + memset(&bad_, 0, sizeof(bad_)); + quePtrFront_ = 0; + quePtrBack_ = 0; + size_ = 0; + peakSize_ = 0; + que_ = (T *)malloc(sizeof(T) * maxSize_); + if (que_ == nullptr) + maxSize_ = 0; + } + + // Deallocate the queue structure + ~queue() { + if (que_ != nullptr) { + free(que_); + que_ = nullptr; + } + } + + // Push a new entry into the queue. + // true on success, false if queue is full, then the element is relaced with the front one + bool push(T ent) { + // Serial.print("quePtrFront_: "); + // Serial.print(quePtrFront_); + // Serial.print(" quePtrBack_: "); + // Serial.print(quePtrBack_); + // Serial.println(); + if (size_ >= maxSize_) { + // que_[quePtrFront_] = ent; + return false; + } + que_[quePtrBack_] = ent; + quePtrBack_ = (quePtrBack_ + 1) % maxSize_; + ++size_; + if (size_ > peakSize_) { + peakSize_ = size_; + } + return true; + } + + bool push_back(T ent) { + return push(ent); + } + + // Push a new entry into the front of queue, moving the rest down + // true on success, false if queue is full + // there are no good checks for overflow + bool push_front(T ent) { + if (size_ >= maxSize_) { + return false; + } + // Serial.print("quePtrFront_: "); + // Serial.print(quePtrFront_); + // Serial.print(" quePtrBack_: "); + // Serial.print(quePtrBack_); + // Serial.println(); + + for (uint8_t i = 0; i <= size_; i++) { + que_[quePtrBack_ - i + 1] = que_[quePtrBack_ - i]; // move the rest up 1 + } + que_[quePtrFront_] = ent; // add the new one + quePtrBack_ = (quePtrBack_ + 1) % maxSize_; + ++size_; + if (size_ > peakSize_) { + peakSize_ = size_; + } + return true; + } + + T & operator[](uint8_t i) { + return que_[i + quePtrFront_]; + } + + // Pop the oldest entry from the queue + T pop() { + if (size_ == 0) + return bad_; + T ent = que_[quePtrFront_]; + quePtrFront_ = (quePtrFront_ + 1) % maxSize_; + --size_; + return ent; + } + + // alias pop_front to keep backwards compatibility with std::list/queue + T pop_front() { + return pop(); + } + + T front() { + return que_[quePtrFront_]; + } + + uint8_t front_i() { + return quePtrFront_; + } + + T * front_p() { + return &que_[quePtrFront_]; + } + + T back() { + return que_[quePtrBack_]; + } + + // Set the value for entry that's given back, if read from an empty + // queue is requested. By default, an entry all memset to zero is given + // back. Using this function, the value of an invalid read can be configured + void setInvalidValue(T & entryInvalidValue) { + bad_ = entryInvalidValue; + } + + // returns true: queue empty, false: not empty + bool empty() { + if (size_ == 0) + return true; + else + return false; + } + + // returns number of entries in the queue + uint8_t size() { + return (size_); + } + + // max number of queue entries that have been in the queue + uint8_t peak() { + return (peakSize_); + } + + // iterators + queueIterator begin() { + return queueIterator(que_, quePtrFront_); + } + queueIterator end() { + return queueIterator(que_, quePtrFront_ + size_); + } + + queueIterator begin() const { + return queueIterator(que_, quePtrFront_); + } + + queueIterator end() const { + return queueIterator(que_, quePtrFront_ + size_); + } +}; + + +template +class arrayIterator { + public: + arrayIterator(T * values_ptr) + : values_ptr_{values_ptr} + , position_{0} { + } + + arrayIterator(T * values_ptr, size_t size_) + : values_ptr_{values_ptr} + , position_{size_} { + } + + bool operator!=(const arrayIterator & other) const { + return !(*this == other); + } + + bool operator==(const arrayIterator & other) const { + return position_ == other.position_; + } + + arrayIterator & operator++() { + ++position_; + return *this; + } + + T & operator*() const { + return *(values_ptr_ + position_); + } + + private: + T * values_ptr_; + size_t position_; +}; + +#define ARRAY_INIT_SIZE 16 +#define ARRAY_MAX_SIZE 255 // fixed for uint8_t +#define ARRAY_INC_SIZE 16 + +template +class array { + private: + T * arr_; + uint8_t startSize_; + uint8_t maxSize_; + uint8_t incSize_ = ARRAY_INC_SIZE; + uint8_t allocSize_; + uint8_t size_; + T bad_; + + public: + // Constructs an array object. All allocation-hints are optional, the + // array class will allocate memory as needed during writes, if + // startSize_!=maxSize_. + // @param startSize_ The number of array entries that are allocated + // during object creation + // @param maxSize_ The maximal limit of records that will be allocated. + // If startSize_ < maxSize_, the array size_ will grow automatically as + // needed. + // @param incSize_ The number of array entries that are allocated as a + // chunk if the array needs to grow + array(uint8_t startSize_ = ARRAY_INIT_SIZE, uint8_t maxSize_ = ARRAY_MAX_SIZE, uint8_t incSize_ = ARRAY_INC_SIZE) + : startSize_(startSize_) + , maxSize_(maxSize_) + , incSize_(incSize_) { + size_ = 0; + memset(&bad_, 0, sizeof(bad_)); + if (maxSize_ < startSize_) + maxSize_ = startSize_; + allocSize_ = startSize_; + arr_ = new T[allocSize_]; + } + + ~array() { + /*! Free resources */ + if (arr_ != nullptr) { + delete[] arr_; + arr_ = nullptr; + } + } + + // Change the array allocation size_. the new number of array entries, corresponding memory is allocated/free'd as necessary. + bool resize(uint8_t newSize) { + uint8_t mv = newSize; + if (newSize > maxSize_) { + if (maxSize_ == allocSize_) + return false; + else + newSize = maxSize_; + } + if (newSize <= allocSize_) + return true; + T * arrn = new T[newSize]; + if (arrn == nullptr) + return false; + for (uint8_t i = 0; i < mv; i++) { + arrn[i] = arr_[i]; + } + delete[] arr_; + arr_ = arrn; + allocSize_ = newSize; + return true; + } + + // Set the value for entry that's given back, + // if read of an invalid index is requested. + // By default, an entry all memset to zero is given + // back. Using this function, the value of an invalid read can be configured. + // returns the value that is given back in case an invalid operation (e.g. read out of bounds) is tried + void setInvalidValue(T & entryInvalidValue) { + bad_ = entryInvalidValue; + } + + // Append an array element after the current end of the array + // takes entry array element that is appended after the last current + // entry. The new array size_ must be smaller than maxSize_ as defined + // during array creation. New array memory is automatically allocated if + // within maxSize_ boundaries + int push(T & entry) { + if (size_ >= allocSize_) { + if (incSize_ == 0) + return -1; + if (!resize(allocSize_ + incSize_)) + return -1; + } + arr_[size_] = entry; + ++size_; + return size_ - 1; + } + + // Assign content of array element at i for const's + T operator[](uint8_t i) const { + if (i >= allocSize_) { + if (incSize_ == 0) { +#ifdef EMSESP_ASSERT + assert(i < allocSize_); +#endif + } + if (!resize(allocSize_ + incSize_)) { +#ifdef EMSESP_ASSERT + assert(i < allocSize_); +#endif + } + } + if (i >= size_ && i <= allocSize_) + size_ = i + 1; + if (i >= allocSize_) { + return bad_; + } + return arr_[i]; + } + + // Assign content of array element at i + T & operator[](uint8_t i) { + if (i >= allocSize_) { + if (incSize_ == 0) { +#ifdef EMSESP_ASSERT + assert(i < allocSize_); +#endif + } + if (!resize(allocSize_ + incSize_)) { +#ifdef EMSESP_ASSERT + assert(i < allocSize_); +#endif + } + } + if (i >= size_ && i <= allocSize_) + size_ = i + 1; + if (i >= allocSize_) { + return bad_; + } + return arr_[i]; + } + + // true if array empty, false otherwise + bool empty() const { + if (size_ == 0) + return true; + else + return false; + } + + // return number of array elements + uint8_t size() const { + return (size_); + } + + // returns number of allocated entries which can be larger than the length of the array + uint8_t alloclen() const { + return (allocSize_); + } + + // emplace + // template + // void emplace1(Args... args) { + // add(args...); + // }; + // template + // void emplace(Args &&... args) { + // add(T(std::forward(args)...)); + // } + + // iterators + arrayIterator begin() { + return arrayIterator(arr_); + } + arrayIterator end() { + return arrayIterator(arr_, size_); + } + + arrayIterator begin() const { + return arrayIterator(arr_); + } + + arrayIterator end() const { + return arrayIterator(arr_, size_); + } +}; + +} // namespace emsesp + +#endif diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 153ff3c1b..a9a55f0e0 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -26,11 +26,11 @@ uuid::log::Logger Boiler::logger_{F_(boiler), uuid::log::Facility::CONSOLE}; Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand) : EMSdevice(device_type, device_id, product_id, version, name, flags, brand) { - reserve_mem(20); // reserve some space for the telegram registries, to avoid memory fragmentation - LOG_DEBUG(F("Adding new Boiler with device ID 0x%02X"), device_id); - System::show_mem("starting boiler regs"); // TODO remove debug + reserve_telgram_functions(25); // reserve some space for the telegram registries, to avoid memory fragmentation + + System::show_mem("starting boiler regs"); // the telegram handlers... register_telegram_type(0x10, F("UBAErrorMessage1"), false, [&](std::shared_ptr t) { process_UBAErrorMessage(t); }); @@ -60,7 +60,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const EMSESP::send_read_request(0x11, device_id); // read last errorcode on start (only published on errors) EMSESP::send_read_request(0x15, device_id); // read maintenace data on start (only published on change) - System::show_mem("after telegram type reg"); // TODO remove debug + System::show_mem("after telegram type reg"); // MQTT commands for boiler topic register_mqtt_cmd(F("comfort"), [&](const char * value, const int8_t id) { return set_warmwater_mode(value, id); }); @@ -80,407 +80,438 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_mqtt_cmd(F("boilhystoff"), [&](const char * value, const int8_t id) { return set_hyst_off(value, id); }); register_mqtt_cmd(F("burnperiod"), [&](const char * value, const int8_t id) { return set_burn_period(value, id); }); register_mqtt_cmd(F("pumpdelay"), [&](const char * value, const int8_t id) { return set_pump_delay(value, id); }); - // register_mqtt_cmd(F("reset"), [&](const char * value, const int8_t id) { return set_reset(value, id); }); register_mqtt_cmd(F("maintenance"), [&](const char * value, const int8_t id) { return set_maintenance(value, id); }); register_mqtt_cmd(F("pumpmodmax"), [&](const char * value, const int8_t id) { return set_max_pump(value, id); }); register_mqtt_cmd(F("pumpmodmin"), [&](const char * value, const int8_t id) { return set_min_pump(value, id); }); + // register_mqtt_cmd(F("reset"), [&](const char * value, const int8_t id) { return set_reset(value, id); }); - System::show_mem("after mqtt cmd reg"); // TODO remove debug + System::show_mem("after mqtt cmd reg"); // add values - - // init_devicevalues(30); // TODO reserve mem + reserve_device_values(50); // main - boiler_data topic - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, {}, F("heatingActive"), F("Heating active"), DeviceValueUOM::NONE); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, {}, F("tapwaterActive"), F("Warm water/DHW active"), DeviceValueUOM::NONE); - register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &selFlowTemp_, DeviceValueType::UINT, {}, F("selFlowTemp"), F("Selected flow temperature"), DeviceValueUOM::DEGREES); - register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &selBurnPow_, DeviceValueType::UINT, {}, F("selBurnPow"), F("Burner selected max power"), DeviceValueUOM::PERCENT); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &pumpMod_, DeviceValueType::UINT, {}, F("pumpMod"), F("Pump modulation"), DeviceValueUOM::PERCENT); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &pumpMod2_, DeviceValueType::UINT, {}, F("pumpMod2"), F("Heat pump modulation"), DeviceValueUOM::PERCENT); - - register_device_value(TAG_BOILER_DATA, - &outdoorTemp_, - DeviceValueType::SHORT, - flash_string_vector{F("10")}, - F("outdoorTemp"), - F("Outside temperature"), + DeviceValueTAG::TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, F("heatingActive"), F("Heating active"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, + &tapwaterActive_, + DeviceValueType::BOOL, + nullptr, + F("tapwaterActive"), + F("Warm water/DHW active"), + DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, + &selFlowTemp_, + DeviceValueType::UINT, + nullptr, + F("selFlowTemp"), + F("Selected flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, + &selBurnPow_, + DeviceValueType::UINT, + nullptr, + F("selBurnPow"), + F("Burner selected max power"), + DeviceValueUOM::PERCENT); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &pumpMod_, DeviceValueType::UINT, nullptr, F("pumpMod"), F("Pump modulation"), DeviceValueUOM::PERCENT); + register_device_value( + DeviceValueTAG::TAG_BOILER_DATA, &pumpMod2_, DeviceValueType::UINT, nullptr, F("pumpMod2"), F("Heat pump modulation"), DeviceValueUOM::PERCENT); + + register_device_value(TAG_BOILER_DATA, &outdoorTemp_, DeviceValueType::SHORT, FL_(div10), F("outdoorTemp"), F("Outside temperature"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &curFlowTemp_, DeviceValueType::USHORT, - flash_string_vector{F("10")}, + FL_(div10), F("curFlowTemp"), F("Current flow temperature"), DeviceValueUOM::DEGREES); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, - &retTemp_, - DeviceValueType::USHORT, - flash_string_vector{F("10")}, - F("retTemp"), - F("Return temperature"), - DeviceValueUOM::DEGREES); + register_device_value( + DeviceValueTAG::TAG_BOILER_DATA, &retTemp_, DeviceValueType::USHORT, FL_(div10), F("retTemp"), F("Return temperature"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &switchTemp_, DeviceValueType::USHORT, - flash_string_vector{F("10")}, + FL_(div10), F("switchTemp"), F("Mixing switch temperature"), DeviceValueUOM::DEGREES); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, - &sysPress_, - DeviceValueType::UINT, - flash_string_vector{F("10")}, - F("sysPress"), - F("System pressure"), - DeviceValueUOM::BAR); - register_device_value(TAG_BOILER_DATA, - &boilTemp_, - DeviceValueType::USHORT, - flash_string_vector{F("10")}, - F("boilTemp"), - F("Max boiler temperature"), - DeviceValueUOM::DEGREES); - register_device_value(TAG_BOILER_DATA, - &exhaustTemp_, - DeviceValueType::USHORT, - flash_string_vector{F("10")}, - F("exhaustTemp"), - F("Exhaust temperature"), - DeviceValueUOM::DEGREES); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &sysPress_, DeviceValueType::UINT, FL_(div10), F("sysPress"), F("System pressure"), DeviceValueUOM::BAR); + register_device_value(TAG_BOILER_DATA, &boilTemp_, DeviceValueType::USHORT, FL_(div10), F("boilTemp"), F("Max boiler temperature"), DeviceValueUOM::DEGREES); + register_device_value(TAG_BOILER_DATA, &exhaustTemp_, DeviceValueType::USHORT, FL_(div10), F("exhaustTemp"), F("Exhaust temperature"), DeviceValueUOM::DEGREES); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, {}, F("burnGas"), F("Gas"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &flameCurr_, DeviceValueType::USHORT, {F("10")}, F("flameCurr"), F("Flame current"), DeviceValueUOM::UA); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &heatPump_, DeviceValueType::BOOL, {}, F("heatPump"), F("Heat pump"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, {}, F("fanWork"), F("Fan"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, {}, F("ignWork"), F("Ignition"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &burnGas_, DeviceValueType::BOOL, nullptr, F("burnGas"), F("Gas"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &flameCurr_, DeviceValueType::USHORT, FL_(div10), F("flameCurr"), F("Flame current"), DeviceValueUOM::UA); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &heatPump_, DeviceValueType::BOOL, nullptr, F("heatPump"), F("Heat pump"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &fanWork_, DeviceValueType::BOOL, nullptr, F("fanWork"), F("Fan"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &ignWork_, DeviceValueType::BOOL, nullptr, F("ignWork"), F("Ignition"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, + &heatingActivated_, + DeviceValueType::BOOL, + nullptr, + F("heatingActivated"), + F("Heating activated"), + DeviceValueUOM::NONE); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &heatingActivated_, DeviceValueType::BOOL, {}, F("heatingActivated"), F("Heating activated"), DeviceValueUOM::NONE); + DeviceValueTAG::TAG_BOILER_DATA, &heatingTemp_, DeviceValueType::UINT, nullptr, F("heatingTemp"), F("Heating temperature"), DeviceValueUOM::DEGREES); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &heatingTemp_, DeviceValueType::UINT, {}, F("heatingTemp"), F("Heating temperature"), DeviceValueUOM::DEGREES); + DeviceValueTAG::TAG_BOILER_DATA, &pumpModMax_, DeviceValueType::UINT, nullptr, F("pumpModMax"), F("Burner pump max power"), DeviceValueUOM::PERCENT); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &pumpModMax_, DeviceValueType::UINT, {}, F("pumpModMax"), F("Burner pump max power"), DeviceValueUOM::PERCENT); + DeviceValueTAG::TAG_BOILER_DATA, &pumpModMin_, DeviceValueType::UINT, nullptr, F("pumpModMin"), F("Burner pump min power"), DeviceValueUOM::PERCENT); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &pumpDelay_, DeviceValueType::UINT, nullptr, F("pumpDelay"), F("Pump delay"), DeviceValueUOM::MINUTES); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &pumpModMin_, DeviceValueType::UINT, {}, F("pumpModMin"), F("Burner pump min power"), DeviceValueUOM::PERCENT); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &pumpDelay_, DeviceValueType::UINT, {}, F("pumpDelay"), F("Pump delay"), DeviceValueUOM::MINUTES); + DeviceValueTAG::TAG_BOILER_DATA, &burnMinPeriod_, DeviceValueType::UINT, nullptr, F("burnMinPeriod"), F("Burner min period"), DeviceValueUOM::MINUTES); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &burnMinPeriod_, DeviceValueType::UINT, {}, F("burnMinPeriod"), F("Burner min period"), DeviceValueUOM::MINUTES); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &burnMinPower_, DeviceValueType::UINT, {}, F("burnMinPower"), F("Burner min power"), DeviceValueUOM::PERCENT); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &burnMaxPower_, DeviceValueType::UINT, {}, F("burnMaxPower"), F("Burner max power"), DeviceValueUOM::PERCENT); + DeviceValueTAG::TAG_BOILER_DATA, &burnMinPower_, DeviceValueType::UINT, nullptr, F("burnMinPower"), F("Burner min power"), DeviceValueUOM::PERCENT); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &boilHystOn_, DeviceValueType::INT, {}, F("boilHystOn"), F("Hysteresis on temperature"), DeviceValueUOM::DEGREES); + DeviceValueTAG::TAG_BOILER_DATA, &burnMaxPower_, DeviceValueType::UINT, nullptr, F("burnMaxPower"), F("Burner max power"), DeviceValueUOM::PERCENT); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &boilHystOff_, DeviceValueType::INT, {}, F("boilHystOff"), F("Hysteresis off temperature"), DeviceValueUOM::DEGREES); + DeviceValueTAG::TAG_BOILER_DATA, &boilHystOn_, DeviceValueType::INT, nullptr, F("boilHystOn"), F("Hysteresis on temperature"), DeviceValueUOM::DEGREES); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, + &boilHystOff_, + DeviceValueType::INT, + nullptr, + F("boilHystOff"), + F("Hysteresis off temperature"), + DeviceValueUOM::DEGREES); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &setFlowTemp_, DeviceValueType::UINT, {}, F("setFlowTemp"), F("Set flow temperature"), DeviceValueUOM::DEGREES); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &setBurnPow_, DeviceValueType::UINT, {}, F("setBurnPow"), F("Burner set power"), DeviceValueUOM::PERCENT); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &curBurnPow_, DeviceValueType::UINT, {}, F("curBurnPow"), F("Burner current power"), DeviceValueUOM::PERCENT); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, {}, F("burnStarts"), F("Burner # starts"), DeviceValueUOM::NONE); + DeviceValueTAG::TAG_BOILER_DATA, &setFlowTemp_, DeviceValueType::UINT, nullptr, F("setFlowTemp"), F("Set flow temperature"), DeviceValueUOM::DEGREES); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &burnWorkMin_, DeviceValueType::TIME, {}, F("burnWorkMin"), F("Total burner operating time"), DeviceValueUOM::MINUTES); + DeviceValueTAG::TAG_BOILER_DATA, &setBurnPow_, DeviceValueType::UINT, nullptr, F("setBurnPow"), F("Burner set power"), DeviceValueUOM::PERCENT); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &heatWorkMin_, DeviceValueType::TIME, {}, F("heatWorkMin"), F("Total heat operating time"), DeviceValueUOM::MINUTES); + DeviceValueTAG::TAG_BOILER_DATA, &curBurnPow_, DeviceValueType::UINT, nullptr, F("curBurnPow"), F("Burner current power"), DeviceValueUOM::PERCENT); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &burnStarts_, DeviceValueType::ULONG, nullptr, F("burnStarts"), F("Burner # starts"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, + &burnWorkMin_, + DeviceValueType::TIME, + nullptr, + F("burnWorkMin"), + F("Total burner operating time"), + DeviceValueUOM::MINUTES); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, + &heatWorkMin_, + DeviceValueType::TIME, + nullptr, + F("heatWorkMin"), + F("Total heat operating time"), + DeviceValueUOM::MINUTES); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA, &UBAuptime_, DeviceValueType::TIME, {}, F("UBAuptime"), F("Total UBA operating time"), DeviceValueUOM::MINUTES); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &lastCode_, DeviceValueType::TEXT, {}, F("lastCode"), F("Last error code"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &serviceCode_, DeviceValueType::TEXT, {}, F("serviceCode"), F("Service code"), DeviceValueUOM::NONE); + DeviceValueTAG::TAG_BOILER_DATA, &UBAuptime_, DeviceValueType::TIME, nullptr, F("UBAuptime"), F("Total UBA operating time"), DeviceValueUOM::MINUTES); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &lastCode_, DeviceValueType::TEXT, nullptr, F("lastCode"), F("Last error code"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &serviceCode_, DeviceValueType::TEXT, nullptr, F("serviceCode"), F("Service code"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA, &serviceCodeNumber_, DeviceValueType::USHORT, - {}, + nullptr, F("serviceCodeNumber"), F("Service code number"), DeviceValueUOM::NONE); - System::show_mem("after device value reg"); // TODO remove debug + System::show_mem("after device value reg"); -#ifdef EMSESP_FORCE_SERIAL - return; // TODO remove early exit -#endif + return; // TODO early exit for memory profiling on ESP8266 // ww - boiler_data_ww topic register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWSelTemp_, DeviceValueType::UINT, - {}, + nullptr, F("wWSelTemp"), F("Warm Water selected temperature"), DeviceValueUOM::DEGREES); - register_device_value( - DeviceValueTAG::TAG_BOILER_DATA_WW, &wWSetTemp_, DeviceValueType::UINT, {}, F("wWSetTemp"), F("Warm water set temperature"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, - &wWType_, - DeviceValueType::ENUM, - flash_string_vector{F("off"), F("flow"), F("buffered flow"), F("buffer"), F("layered buffer")}, - F("wWType"), - F("Warm water type"), - DeviceValueUOM::NONE); + &wWSetTemp_, + DeviceValueType::UINT, + nullptr, + F("wWSetTemp"), + F("Warm water set temperature"), + DeviceValueUOM::DEGREES); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWType_, DeviceValueType::ENUM, FL_(enum_flow), F("wWType"), F("Warm water type"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWComfort_, DeviceValueType::ENUM, - flash_string_vector{F("hot"), F("eco"), F("intelligent")}, + FL_(enum_comfort), F("wWComfort"), F("Warm water comfort"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, - {}, + nullptr, F("wWCircPump"), F("Warm water circulation pump available"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWChargeType_, DeviceValueType::BOOL, - flash_string_vector{F("3-way valve"), F("charge pump")}, + FL_(enum_charge), F("wWChargeType"), F("Warm Water charging type"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, - {}, + nullptr, F("wWDisinfectionTemp"), F("Warm Water disinfection temperature"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWCircPumpMode_, DeviceValueType::ENUM, - flash_string_vector{F("off"), F("1x3min"), F("2x3min"), F("3x3min"), F("4x3min"), F("5x3min"), F("6x3min"), F("continuos")}, + FL_(enum_freq), F("wWCircPumpMode"), F("Warm water circulation pump freq"), DeviceValueUOM::NONE); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA_WW, &wWCirc_, DeviceValueType::BOOL, {}, F("wWCirc"), F("Warm Water circulation active"), DeviceValueUOM::NONE); + DeviceValueTAG::TAG_BOILER_DATA_WW, &wWCirc_, DeviceValueType::BOOL, nullptr, F("wWCirc"), F("Warm Water circulation active"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWCurTemp_, DeviceValueType::USHORT, - {F("10")}, + FL_(div10), F("wWCurTemp"), F("Warm Water current temperature (intern)"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWCurTemp2_, DeviceValueType::USHORT, - flash_string_vector{F("10")}, + FL_(div10), F("wWCurTemp2"), F("Warm Water current temperature (extern)"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWCurFlow_, DeviceValueType::UINT, - {F("10")}, + FL_(div10), F("wWCurFlow"), F("Warm Water current tap water flow"), DeviceValueUOM::LMIN); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wwStorageTemp1_, DeviceValueType::USHORT, - flash_string_vector{F("10")}, + FL_(div10), F("wwStorageTemp1"), F("Warm water storage temperature (intern)"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wwStorageTemp2_, DeviceValueType::USHORT, - flash_string_vector{F("10")}, + FL_(div10), F("wwStorageTemp2"), F("Warm water storage temperature (extern)"), DeviceValueUOM::DEGREES); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA_WW, &wWActivated_, DeviceValueType::BOOL, {}, F("wWActivated"), F("Warm Water activated"), DeviceValueUOM::NONE); - register_device_value( - DeviceValueTAG::TAG_BOILER_DATA_WW, &wWOneTime_, DeviceValueType::BOOL, {}, F("wWOneTime"), F("Warm Water one time charging"), DeviceValueUOM::NONE); + DeviceValueTAG::TAG_BOILER_DATA_WW, &wWActivated_, DeviceValueType::BOOL, nullptr, F("wWActivated"), F("Warm Water activated"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, + &wWOneTime_, + DeviceValueType::BOOL, + nullptr, + F("wWOneTime"), + F("Warm Water one time charging"), + DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWDisinfecting_, DeviceValueType::BOOL, - {}, + nullptr, F("wWDisinfecting"), F("Warm Water disinfecting"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWCharging_, DeviceValueType::BOOL, {}, F("wWCharging"), F("Warm Water charging"), DeviceValueUOM::NONE); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA_WW, &wWRecharging_, DeviceValueType::BOOL, {}, F("wWRecharging"), F("Warm Water recharging"), DeviceValueUOM::NONE); + DeviceValueTAG::TAG_BOILER_DATA_WW, &wWCharging_, DeviceValueType::BOOL, nullptr, F("wWCharging"), F("Warm Water charging"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, + &wWRecharging_, + DeviceValueType::BOOL, + nullptr, + F("wWRecharging"), + F("Warm Water recharging"), + DeviceValueUOM::NONE); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, {}, F("wWTempOK"), F("Warm Water temperature ok"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWActive_, DeviceValueType::BOOL, {}, F("wWActive"), F("Warm Water active"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWHeat_, DeviceValueType::BOOL, {}, F("wWHeat"), F("Warm Water heating"), DeviceValueUOM::NONE); + DeviceValueTAG::TAG_BOILER_DATA_WW, &wWTempOK_, DeviceValueType::BOOL, nullptr, F("wWTempOK"), F("Warm Water temperature ok"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWActive_, DeviceValueType::BOOL, nullptr, F("wWActive"), F("Warm Water active"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWHeat_, DeviceValueType::BOOL, nullptr, F("wWHeat"), F("Warm Water heating"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWSetPumpPower_, DeviceValueType::UINT, - {}, + nullptr, F("wWSetPumpPower"), F("Warm Water pump set power"), DeviceValueUOM::PERCENT); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wwMixTemperature_, DeviceValueType::USHORT, - {}, + nullptr, F("wwMixTemperature"), F("Warm Water mix temperature"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wwBufferTemperature_, DeviceValueType::USHORT, - {}, + nullptr, F("wwBufferTemperature"), F("Warm Water buffer boiler temperature"), DeviceValueUOM::DEGREES); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWStarts_, DeviceValueType::ULONG, {}, F("wWStarts"), F("Warm Water # starts"), DeviceValueUOM::NONE); register_device_value( - DeviceValueTAG::TAG_BOILER_DATA_WW, &wWStarts2_, DeviceValueType::ULONG, {}, F("wWStarts2"), F("Warm Water # starts (control)"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, &wWWorkM_, DeviceValueType::TIME, {}, F("wWWorkM"), F("Warm Water active time"), DeviceValueUOM::MINUTES); + DeviceValueTAG::TAG_BOILER_DATA_WW, &wWStarts_, DeviceValueType::ULONG, nullptr, F("wWStarts"), F("Warm Water # starts"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA_WW, + &wWStarts2_, + DeviceValueType::ULONG, + nullptr, + F("wWStarts2"), + F("Warm Water # starts (control)"), + DeviceValueUOM::NONE); + register_device_value( + DeviceValueTAG::TAG_BOILER_DATA_WW, &wWWorkM_, DeviceValueType::TIME, nullptr, F("wWWorkM"), F("Warm Water active time"), DeviceValueUOM::MINUTES); // info - boiler_data_info topic register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &upTimeControl_, DeviceValueType::TIME, - flash_string_vector{F("60")}, + FL_(div60), F("upTimeControl"), F("Operating time total heat"), DeviceValueUOM::MINUTES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &upTimeCompHeating_, DeviceValueType::TIME, - flash_string_vector{F("60")}, + FL_(div60), F("upTimeCompHeating"), F("Operating time compressor heating"), DeviceValueUOM::MINUTES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &upTimeCompCooling_, DeviceValueType::TIME, - flash_string_vector{F("60")}, + FL_(div60), F("upTimeCompCooling"), F("Operating time compressor cooling"), DeviceValueUOM::MINUTES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &upTimeCompWw_, DeviceValueType::TIME, - flash_string_vector{F("60")}, + FL_(div60), F("upTimeCompWw"), F("Operating time compressor warm water"), DeviceValueUOM::MINUTES); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &heatingStarts_, DeviceValueType::ULONG, - {}, + nullptr, F("heatingStarts"), F("# heating starts (control)"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &coolingStarts_, DeviceValueType::ULONG, - {}, + nullptr, F("coolingStarts"), F("# cooling starts (control)"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &nrgConsTotal_, DeviceValueType::ULONG, - {}, + nullptr, F("nrgConsTotal"), F("Total energy consumption"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &nrgConsCompTotal_, DeviceValueType::ULONG, - {}, + nullptr, F("nrgConsCompTotal"), F("Energy consumption compressor total"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &nrgConsCompHeating_, DeviceValueType::ULONG, - {}, + nullptr, F("nrgConsCompHeating"), F("Energy consumption compressor heating"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &nrgConsCompWw_, DeviceValueType::ULONG, - {}, + nullptr, F("nrgConsCompWw"), F("Energy consumption compressor warm water"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &nrgConsCompCooling_, DeviceValueType::ULONG, - {}, + nullptr, F("nrgConsCompCooling"), F("Energy consumption compressor cooling"), DeviceValueUOM::NONE); - register_device_value( - DeviceValueTAG::TAG_BOILER_DATA_INFO, &nrgSuppTotal_, DeviceValueType::ULONG, {}, F("nrgSuppTotal"), F("Total energy supplied"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, + &nrgSuppTotal_, + DeviceValueType::ULONG, + nullptr, + F("nrgSuppTotal"), + F("Total energy supplied"), + DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &nrgSuppHeating_, DeviceValueType::ULONG, - {}, + nullptr, F("nrgSuppHeating"), F("Total energy supplied heating"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &nrgSuppWw_, DeviceValueType::ULONG, - {}, + nullptr, F("nrgSuppWw"), F("Total energy warm supplied warm water"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &nrgSuppCooling_, DeviceValueType::ULONG, - {}, + nullptr, F("nrgSuppCooling"), F("Total energy supplied cooling"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &auxElecHeatNrgConsTotal_, DeviceValueType::ULONG, - {}, + nullptr, F("auxElecHeatNrgConsTotal"), F("Auxiliary electrical heater energy consumption total"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &auxElecHeatNrgConsHeating_, DeviceValueType::ULONG, - {}, + nullptr, F("auxElecHeatNrgConsHeating"), F("Auxiliary electrical heater energy consumption heating"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &auxElecHeatNrgConsDHW_, DeviceValueType::ULONG, - {}, + nullptr, F("auxElecHeatNrgConsDHW"), F("Auxiliary electrical heater energy consumption DHW"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &maintenanceMessage_, DeviceValueType::TEXT, - {}, + nullptr, F("maintenanceMessage"), F("Maintenance message"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &maintenanceDate_, DeviceValueType::TEXT, - {}, + nullptr, F("maintenanceDate"), F("Maintenance set date"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &maintenanceType_, DeviceValueType::ENUM, - flash_string_vector{F("off"), F("time"), F("date")}, + FL_(enum_off_time_date), F("maintenanceType"), F("Scheduled maintenance"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_BOILER_DATA_INFO, &maintenanceTime_, DeviceValueType::UINT, - {}, + nullptr, F("maintenanceTime"), F("Maintenance set time"), DeviceValueUOM::NONE); @@ -680,11 +711,12 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr telegram has_update(telegram->read_value(serviceCodeNumber_, 4)); // at this point do a quick check to see if the hot water or heating is active - uint8_t state = 0; - telegram->read_value(state, 11); - boilerState_ = state & 0x01 ? 0x08 : 0; - boilerState_ |= state & 0x02 ? 0x01 : 0; - boilerState_ |= state & 0x04 ? 0x02 : 0; + uint8_t state = EMS_VALUE_UINT_NOTSET; + if (telegram->read_value(state, 11)) { + boilerState_ = state & 0x01 ? 0x08 : 0; + boilerState_ |= state & 0x02 ? 0x01 : 0; + boilerState_ |= state & 0x04 ? 0x02 : 0; + } check_active(); // do a quick check to see if the hot water or heating is active } @@ -870,6 +902,9 @@ void Boiler::process_UBAMaintenanceStatus(std::shared_ptr telegr // 0x10, 0x11 void Boiler::process_UBAErrorMessage(std::shared_ptr telegram) { + if (telegram->offset > 0 || telegram->message_length < 9) { + return; + } // data: displaycode(2), errornumber(2), year, month, hour, day, minute, duration(2), src-addr if (telegram->message_data[4] & 0x80) { // valid date @@ -896,6 +931,9 @@ void Boiler::process_UBAErrorMessage(std::shared_ptr telegram) { // 0x15 void Boiler::process_UBAMaintenanceData(std::shared_ptr telegram) { + if (telegram->offset > 0 || telegram->message_length < 5) { + return; + } // first byte: Maintenance messages (0 = none, 1 = by operating hours, 2 = by date) telegram->read_value(maintenanceType_, 0); diff --git a/src/devices/boiler.h b/src/devices/boiler.h index 9b816e040..9250ae906 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -19,18 +19,7 @@ #ifndef EMSESP_BOILER_H #define EMSESP_BOILER_H -#include -#include - -#include - -#include - -#include "emsdevice.h" -#include "telegram.h" #include "emsesp.h" -#include "helpers.h" -#include "mqtt.h" namespace emsesp { diff --git a/src/devices/connect.h b/src/devices/connect.h index edf536a33..9baaba7f6 100644 --- a/src/devices/connect.h +++ b/src/devices/connect.h @@ -19,15 +19,7 @@ #ifndef EMSESP_CONNECT_H #define EMSESP_CONNECT_H -#include -#include - -#include - -#include "emsdevice.h" -#include "telegram.h" -#include "helpers.h" -#include "mqtt.h" +#include "emsesp.h" namespace emsesp { diff --git a/src/devices/controller.h b/src/devices/controller.h index 6dcf24f00..7aba2d0ab 100644 --- a/src/devices/controller.h +++ b/src/devices/controller.h @@ -19,15 +19,7 @@ #ifndef EMSESP_CONTROLLER_H #define EMSESP_CONTROLLER_H -#include -#include - -#include - -#include "emsdevice.h" -#include "telegram.h" -#include "helpers.h" -#include "mqtt.h" +#include "emsesp.h" namespace emsesp { diff --git a/src/devices/gateway.h b/src/devices/gateway.h index 69c9631ac..0b3c26629 100644 --- a/src/devices/gateway.h +++ b/src/devices/gateway.h @@ -19,15 +19,7 @@ #ifndef EMSESP_GATEWAY_H #define EMSESP_GATEWAY_H -#include -#include - -#include - -#include "emsdevice.h" -#include "telegram.h" -#include "helpers.h" -#include "mqtt.h" +#include "emsesp.h" namespace emsesp { diff --git a/src/devices/generic.h b/src/devices/generic.h index aa0a5a718..dd9dd0946 100644 --- a/src/devices/generic.h +++ b/src/devices/generic.h @@ -19,15 +19,7 @@ #ifndef EMSESP_GENERIC_H #define EMSESP_GENERIC_H -#include -#include - -#include - -#include "emsdevice.h" -#include "telegram.h" -#include "helpers.h" -#include "mqtt.h" +#include "emsesp.h" namespace emsesp { diff --git a/src/devices/heatpump.cpp b/src/devices/heatpump.cpp index c4e978069..d07cc55a8 100644 --- a/src/devices/heatpump.cpp +++ b/src/devices/heatpump.cpp @@ -32,8 +32,11 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c register_telegram_type(0x042B, F("HP1"), true, [&](std::shared_ptr t) { process_HPMonitor1(t); }); register_telegram_type(0x047B, F("HP2"), true, [&](std::shared_ptr t) { process_HPMonitor2(t); }); - register_device_value(DeviceValueTAG::TAG_NONE, &airHumidity_, DeviceValueType::UINT, flash_string_vector{F("2")}, F("airHumidity"), F("Relative air humidity"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_NONE, &dewTemperature_, DeviceValueType::UINT, {}, F("dewTemperature"), F("Dew point temperature"), DeviceValueUOM::NONE); + // device values + register_device_value( + DeviceValueTAG::TAG_NONE, &airHumidity_, DeviceValueType::UINT, FL_(div2), F("airHumidity"), F("Relative air humidity"), DeviceValueUOM::NONE); + register_device_value( + DeviceValueTAG::TAG_NONE, &dewTemperature_, DeviceValueType::UINT, nullptr, F("dewTemperature"), F("Dew point temperature"), DeviceValueUOM::NONE); } // publish HA config diff --git a/src/devices/heatpump.h b/src/devices/heatpump.h index 9634f3a15..c7dcf0559 100644 --- a/src/devices/heatpump.h +++ b/src/devices/heatpump.h @@ -19,16 +19,7 @@ #ifndef EMSESP_HEATPUMP_H #define EMSESP_HEATPUMP_H -#include -#include - -#include - -#include "emsdevice.h" #include "emsesp.h" -#include "telegram.h" -#include "helpers.h" -#include "mqtt.h" namespace emsesp { diff --git a/src/devices/mixer.cpp b/src/devices/mixer.cpp index f5cee8c5c..4c38bc8f5 100644 --- a/src/devices/mixer.cpp +++ b/src/devices/mixer.cpp @@ -53,6 +53,7 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s if (flags == EMSdevice::EMS_DEVICE_FLAG_IPM) { register_telegram_type(0x010C, F("IPMSetMessage"), false, [&](std::shared_ptr t) { process_IPMStatusMessage(t); }); } + } @@ -74,10 +75,10 @@ void Mixer::register_values(const Type type, uint16_t hc) { tag = DeviceValueTAG::TAG_WWC1 + hc - 1; } - register_device_value(tag, &flowTemp_, DeviceValueType::USHORT, flash_string_vector{F("10")}, F("flowTemp"), F("Current flow temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, {}, F("flowSetTemp"), F("Setpoint flow temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, {}, F("pumpStatus"), F("Pump/Valve status"), DeviceValueUOM::NONE); - register_device_value(tag, &status_, DeviceValueType::INT, {}, F("status"), F("Current status"), DeviceValueUOM::NONE); + register_device_value(tag, &flowTemp_, DeviceValueType::USHORT, FL_(div10), F("flowTemp"), F("Current flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, F("flowSetTemp"), F("Setpoint flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, F("pumpStatus"), F("Pump/Valve status"), DeviceValueUOM::NONE); + register_device_value(tag, &status_, DeviceValueType::INT, nullptr, F("status"), F("Current status"), DeviceValueUOM::NONE); } diff --git a/src/devices/mixer.h b/src/devices/mixer.h index 66a475636..64f035218 100644 --- a/src/devices/mixer.h +++ b/src/devices/mixer.h @@ -19,16 +19,7 @@ #ifndef EMSESP_MIXER_H #define EMSESP_MIXER_H -#include -#include - -#include - -#include "emsdevice.h" #include "emsesp.h" -#include "telegram.h" -#include "helpers.h" -#include "mqtt.h" namespace emsesp { diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index b6685224d..3c340379f 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -60,37 +60,39 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s register_telegram_type(0x0101, F("ISM1Set"), false, [&](std::shared_ptr t) { process_ISM1Set(t); }); } + // device values... + // special case for a device_id with 0x2A where it's not actual a solar module if (device_id == 0x2A) { - register_device_value(DeviceValueTAG::TAG_NONE, &type_, DeviceValueType::TEXT, {}, F("type"), F("Type"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_NONE, &type_, DeviceValueType::TEXT, nullptr, F("type"), F("Type"), DeviceValueUOM::NONE); strncpy(type_, "warm water circuit", sizeof(type_)); } register_device_value(DeviceValueTAG::TAG_NONE, &collectorTemp_, DeviceValueType::SHORT, - flash_string_vector{F("10")}, + FL_(div10), F("collectorTemp"), F("Collector temperature (TS1)"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_NONE, &tankBottomTemp_, DeviceValueType::SHORT, - flash_string_vector{F("10")}, + FL_(div10), F("tankBottomTemp"), F("Bottom temperature (TS2)"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_NONE, &tankBottomTemp2_, DeviceValueType::SHORT, - flash_string_vector{F("10")}, + FL_(div10), F("tankBottomTemp2"), F("Bottom temperature (TS5)"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_NONE, &heatExchangerTemp_, DeviceValueType::SHORT, - {F("10")}, + FL_(div10), F("heatExchangerTemp"), F("Heat exchanger temperature (TS6)"), DeviceValueUOM::DEGREES); @@ -98,43 +100,37 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s register_device_value(DeviceValueTAG::TAG_NONE, &tank1MaxTempCurrent_, DeviceValueType::UINT, - {}, + nullptr, F("tank1MaxTempCurrent"), F("Maximum Tank temperature"), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_NONE, &solarPumpModulation_, DeviceValueType::UINT, - {}, + nullptr, F("solarPumpModulation"), F("Solar pump modulation (PS1)"), DeviceValueUOM::PERCENT); - register_device_value( - DeviceValueTAG::TAG_NONE, &cylinderPumpModulation_, DeviceValueType::UINT, {}, F("cylinderPumpModulation"), F("Cylinder pump modulation (PS5)"), DeviceValueUOM::PERCENT); - - register_device_value(DeviceValueTAG::TAG_NONE, &solarPump_, DeviceValueType::BOOL, {}, F("solarPump"), F("Solar pump (PS1) active"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_NONE, &valveStatus_, DeviceValueType::BOOL, {}, F("valveStatus"), F("Valve status"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_NONE, &tankHeated_, DeviceValueType::BOOL, {}, F("tankHeated"), F("Tank heated"), DeviceValueUOM::NONE); - register_device_value( - DeviceValueTAG::TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, {}, F("collectorShutdown"), F("Collector shutdown"), DeviceValueUOM::NONE); - - register_device_value(DeviceValueTAG::TAG_NONE, &pumpWorkMin_, DeviceValueType::TIME, {}, F("pumpWorkMin"), F("Pump working time"), DeviceValueUOM::MINUTES); - register_device_value(DeviceValueTAG::TAG_NONE, - &energyLastHour_, - DeviceValueType::ULONG, - flash_string_vector{F("10")}, - F("energyLastHour"), - F("Energy last hour"), - DeviceValueUOM::WH); - register_device_value(DeviceValueTAG::TAG_NONE, - &energyTotal_, - DeviceValueType::ULONG, - flash_string_vector{F("10")}, - F("energyTotal"), - F("Energy total"), - DeviceValueUOM::KWH); - register_device_value(DeviceValueTAG::TAG_NONE, &energyToday_, DeviceValueType::ULONG, {}, F("energyToday"), F("Energy today"), DeviceValueUOM::WH); + &cylinderPumpModulation_, + DeviceValueType::UINT, + nullptr, + F("cylinderPumpModulation"), + F("Cylinder pump modulation (PS5)"), + DeviceValueUOM::PERCENT); + + register_device_value(DeviceValueTAG::TAG_NONE, &solarPump_, DeviceValueType::BOOL, nullptr, F("solarPump"), F("Solar pump (PS1) active"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_NONE, &valveStatus_, DeviceValueType::BOOL, nullptr, F("valveStatus"), F("Valve status"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, F("tankHeated"), F("Tank heated"), DeviceValueUOM::NONE); + register_device_value( + DeviceValueTAG::TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, F("collectorShutdown"), F("Collector shutdown"), DeviceValueUOM::NONE); + + register_device_value(DeviceValueTAG::TAG_NONE, &pumpWorkMin_, DeviceValueType::TIME, nullptr, F("pumpWorkMin"), F("Pump working time"), DeviceValueUOM::MINUTES); + + register_device_value( + DeviceValueTAG::TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), F("energyLastHour"), F("Energy last hour"), DeviceValueUOM::WH); + register_device_value(DeviceValueTAG::TAG_NONE, &energyTotal_, DeviceValueType::ULONG, FL_(div10), F("energyTotal"), F("Energy total"), DeviceValueUOM::KWH); + register_device_value(DeviceValueTAG::TAG_NONE, &energyToday_, DeviceValueType::ULONG, nullptr, F("energyToday"), F("Energy today"), DeviceValueUOM::WH); } // publish HA config diff --git a/src/devices/solar.h b/src/devices/solar.h index 5febdccf4..4a3bbca84 100644 --- a/src/devices/solar.h +++ b/src/devices/solar.h @@ -19,16 +19,7 @@ #ifndef EMSESP_SOLAR_H #define EMSESP_SOLAR_H -#include -#include - -#include - -#include "emsdevice.h" #include "emsesp.h" -#include "telegram.h" -#include "helpers.h" -#include "mqtt.h" namespace emsesp { diff --git a/src/devices/switch.cpp b/src/devices/switch.cpp index 6bf5c5f0d..2a5709b60 100644 --- a/src/devices/switch.cpp +++ b/src/devices/switch.cpp @@ -34,15 +34,10 @@ Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const register_telegram_type(0x9D, F("WM10SetMessage"), false, [&](std::shared_ptr t) { process_WM10SetMessage(t); }); register_telegram_type(0x1E, F("WM10TempMessage"), false, [&](std::shared_ptr t) { process_WM10TempMessage(t); }); - register_device_value(DeviceValueTAG::TAG_NONE, &activated_, DeviceValueType::BOOL, {}, F("activated"), F("Activated"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_NONE, - &flowTemp_, - DeviceValueType::USHORT, - flash_string_vector{F("10")}, - F("flowTemp"), - F("Current flow temperature"), - DeviceValueUOM::DEGREES); - register_device_value(DeviceValueTAG::TAG_NONE, &status_, DeviceValueType::INT, {}, F("status"), F("Status"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, F("activated"), F("Activated"), DeviceValueUOM::NONE); + register_device_value( + DeviceValueTAG::TAG_NONE, &flowTemp_, DeviceValueType::USHORT, FL_(div10), F("flowTemp"), F("Current flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(DeviceValueTAG::TAG_NONE, &status_, DeviceValueType::INT, nullptr, F("status"), F("Status"), DeviceValueUOM::NONE); } // publish HA config diff --git a/src/devices/switch.h b/src/devices/switch.h index 2f5759143..10b636334 100644 --- a/src/devices/switch.h +++ b/src/devices/switch.h @@ -19,16 +19,7 @@ #ifndef EMSESP_SWITCH_H #define EMSESP_SWITCH_H -#include -#include - -#include - -#include "emsdevice.h" #include "emsesp.h" -#include "telegram.h" -#include "helpers.h" -#include "mqtt.h" namespace emsesp { diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 9989d6960..cf9c66531 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -42,7 +42,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i && ((actual_master_thermostat == EMSESP_DEFAULT_MASTER_THERMOSTAT) || (device_id < actual_master_thermostat)))) { EMSESP::actual_master_thermostat(device_id); actual_master_thermostat = device_id; - reserve_mem(25); // reserve some space for the telegram registries, to avoid memory fragmentation + reserve_telgram_functions(25); // reserve some space for the telegram registries, to avoid memory fragmentation // common telegram handlers register_telegram_type(EMS_TYPE_RCOutdoorTemp, F("RCOutdoorTemp"), false, [&](std::shared_ptr t) { process_RCOutdoorTemp(t); }); @@ -868,6 +868,10 @@ void Thermostat::process_RC35Monitor(std::shared_ptr telegram) { // exit if the 15th byte (second from last) is 0x00, which I think is calculated flow setpoint temperature // with weather controlled RC35s this value is >=5, otherwise can be zero and our setpoint temps will be incorrect // see https://github.com/proddy/EMS-ESP/issues/373#issuecomment-627907301 + if (telegram->offset > 0 || telegram->message_length < 15) { + return; + } + if (telegram->message_data[14] == 0x00) { return; } @@ -890,7 +894,7 @@ void Thermostat::process_RC35Monitor(std::shared_ptr telegram) { // type 0x3D (HC1), 0x47 (HC2), 0x51 (HC3), 0x5B (HC4) - Working Mode Heating - for reading the mode from the RC35 thermostat (0x10) void Thermostat::process_RC35Set(std::shared_ptr telegram) { // check to see we have a valid type. heating: 1 radiator, 2 convectors, 3 floors, 4 room supply - if (telegram->message_data[0] == 0x00) { + if (telegram->offset == 0 && telegram->message_data[0] == 0x00) { return; } @@ -935,6 +939,10 @@ void Thermostat::process_RC35Timer(std::shared_ptr telegram) { // process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long void Thermostat::process_RCTime(std::shared_ptr telegram) { + if (telegram->offset > 0 || telegram->message_length < 5) { + return; + } + if (flags() == EMS_DEVICE_FLAG_EASY) { return; // not supported } @@ -975,6 +983,10 @@ void Thermostat::process_RCTime(std::shared_ptr telegram) { // 10 00 A2 00 41 32 32 03 30 00 02 00 00 00 00 00 00 02 CRC // A 2 2 816 void Thermostat::process_RCError(std::shared_ptr telegram) { + if (telegram->offset > 0 || telegram->message_length < 5) { + return; + } + char buf[4]; buf[0] = telegram->message_data[0]; buf[1] = telegram->message_data[1]; @@ -986,6 +998,10 @@ void Thermostat::process_RCError(std::shared_ptr telegram) { // 0x12 error log void Thermostat::process_RCErrorMessage(std::shared_ptr telegram) { + if (telegram->offset > 0 || telegram->message_length < 12) { + return; + } + // data: displaycode(2), errornumber(2), year, month, hour, day, minute, duration(2), src-addr if (telegram->message_data[4] & 0x80) { // valid date char code[3]; @@ -1011,7 +1027,7 @@ bool Thermostat::set_minexttemp(const char * value, const int8_t id) { return false; } - LOG_INFO(F("Setting min external temperature to %d"), mt); + LOG_INFO(F("Setting min external temperature to %d C"), mt); if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) { write_command(0x240, 10, mt, 0x240); } else { @@ -1128,7 +1144,7 @@ bool Thermostat::set_control(const char * value, const int8_t id) { uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id; std::shared_ptr hc = heating_circuit(hc_num); - if (hc == nullptr || ctrl > 2) { + if (hc == nullptr) { return false; } @@ -1238,12 +1254,20 @@ bool Thermostat::set_holiday(const char * value, const int8_t id) { data[0] = (hd[0] - '0') * 10 + (hd[1] - '0'); data[1] = (hd[3] - '0') * 10 + (hd[4] - '0'); data[2] = (hd[7] - '0') * 100 + (hd[8] - '0') * 10 + (hd[9] - '0'); - data[3] = (hd[11] - '0') * 10 + (hd[11] - '0'); + data[3] = (hd[11] - '0') * 10 + (hd[12] - '0'); data[4] = (hd[14] - '0') * 10 + (hd[15] - '0'); data[5] = (hd[18] - '0') * 100 + (hd[19] - '0') * 10 + (hd[20] - '0'); - LOG_INFO(F("Setting holiday for hc %d"), hc->hc_num()); - write_command(timer_typeids[hc->hc_num() - 1], 87, data, 6, 0); + if (hd[10] == '-') { + LOG_INFO(F("Setting holiday away from home for hc %d"), hc->hc_num()); + write_command(timer_typeids[hc->hc_num() - 1], 87, data, 6, 0); + } else if (hd[10] == '+') { + LOG_INFO(F("Setting holiday at home for hc %d"), hc->hc_num()); + write_command(timer_typeids[hc->hc_num() - 1], 93, data, 6, 0); + } else { + LOG_WARNING(F("Set holiday: Invalid")); + return false; + } return true; } @@ -2004,6 +2028,7 @@ void Thermostat::add_commands() { register_mqtt_cmd(F("clockoffset"), [&](const char * value, const int8_t id) { return set_clockoffset(value, id); }); register_mqtt_cmd(F("language"), [&](const char * value, const int8_t id) { return set_language(value, id); }); register_mqtt_cmd(F("display"), [&](const char * value, const int8_t id) { return set_display(value, id); }); + break; case EMS_DEVICE_FLAG_RC35: // RC30 and RC35 register_mqtt_cmd(F("nighttemp"), [&](const char * value, const int8_t id) { return set_nighttemp(value, id); }); register_mqtt_cmd(F("daytemp"), [&](const char * value, const int8_t id) { return set_daytemp(value, id); }); @@ -2042,41 +2067,43 @@ void Thermostat::add_commands() { // register main device values, top level for all thermostats (non heating circuit) void Thermostat::register_device_values() { + /* + uint8_t model = this->model(); // Common for all thermostats - register_device_value(DeviceValueTAG::TAG_NONE, &dateTime_, DeviceValueType::TEXT, {}, F("dateTime"), F("Date/Time"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_NONE, &errorCode_, DeviceValueType::TEXT, {}, F("errorCode"), F("Error code"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_NONE, &lastCode_, DeviceValueType::TEXT, {}, F("lastCode"), F("Last error"), DeviceValueUOM::NONE); - register_device_value(DeviceValueTAG::TAG_NONE, &wwTemp_, DeviceValueType::UINT, {}, F("wwTemp"), F("Warm water high temperature"), DeviceValueUOM::DEGREES); - register_device_value(DeviceValueTAG::TAG_NONE, &wwTempLow_, DeviceValueType::UINT, {}, F("wwTempLow"), F("Warm water low temperature"), DeviceValueUOM::DEGREES); - register_device_value(DeviceValueTAG::TAG_NONE, &wwExtra1_, DeviceValueType::UINT, {}, F("wwExtra1"), F("Warm water circuit 1 extra"), DeviceValueUOM::DEGREES); - register_device_value(DeviceValueTAG::TAG_NONE, &wwExtra2_, DeviceValueType::UINT, {}, F("wwExtra2"), F("Warm water circuit 2 extra"), DeviceValueUOM::DEGREES); + register_device_value(DeviceValueTAG::TAG_NONE, &dateTime_, DeviceValueType::TEXT,nullptr, F("dateTime"), F("Date/Time"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_NONE, &errorCode_, DeviceValueType::TEXT,nullptr, F("errorCode"), F("Error code"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_NONE, &lastCode_, DeviceValueType::TEXT,nullptr, F("lastCode"), F("Last error"), DeviceValueUOM::NONE); + register_device_value(DeviceValueTAG::TAG_NONE, &wwTemp_, DeviceValueType::UINT,nullptr, F("wwTemp"), F("Warm water high temperature"), DeviceValueUOM::DEGREES); + register_device_value(DeviceValueTAG::TAG_NONE, &wwTempLow_, DeviceValueType::UINT,nullptr, F("wwTempLow"), F("Warm water low temperature"), DeviceValueUOM::DEGREES); + register_device_value(DeviceValueTAG::TAG_NONE, &wwExtra1_, DeviceValueType::UINT,nullptr, F("wwExtra1"), F("Warm water circuit 1 extra"), DeviceValueUOM::DEGREES); + register_device_value(DeviceValueTAG::TAG_NONE, &wwExtra2_, DeviceValueType::UINT,nullptr, F("wwExtra2"), F("Warm water circuit 2 extra"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_NONE, &tempsensor1_, DeviceValueType::USHORT, - flash_string_vector{F("10")}, + FL_(div10), F("inttemp1"), F("Temperature sensor 1"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_NONE, &tempsensor2_, DeviceValueType::USHORT, - flash_string_vector{F("10")}, + FL_(div10), F("inttemp2"), F("Temperature sensor 2"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_NONE, &ibaCalIntTemperature_, DeviceValueType::INT, - flash_string_vector{F("2")}, + FL_(div2), F("intoffset"), F("Offset int. temperature"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_NONE, &ibaMinExtTemperature_, DeviceValueType::INT, - {}, + nullptr, F("minexttemp"), F("Min ext. temperature"), DeviceValueUOM::DEGREES); // min ext temp for heating curve, in deg. @@ -2108,7 +2135,7 @@ void Thermostat::register_device_values() { register_device_value(DeviceValueTAG::TAG_NONE, &ibaClockOffset_, DeviceValueType::UINT, - {}, + nullptr, F("ibaClockOffset"), F("Clock offset"), DeviceValueUOM::NONE); // offset (in sec) to clock, 0xff=-1s, 0x02=2s @@ -2126,12 +2153,12 @@ void Thermostat::register_device_values() { register_device_value(DeviceValueTAG::TAG_NONE, &dampedoutdoortemp2_, DeviceValueType::SHORT, - flash_string_vector{F("10")}, + FL_(div10), F("dampedtemp"), F("Damped outdoor temperature"), DeviceValueUOM::DEGREES); register_device_value( - DeviceValueTAG::TAG_NONE, &floordrytemp_, DeviceValueType::UINT, {}, F("floordrytemp"), F("Floor drying temperature"), DeviceValueUOM::DEGREES); + DeviceValueTAG::TAG_NONE, &floordrytemp_, DeviceValueType::UINT,nullptr, F("floordrytemp"), F("Floor drying temperature"), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_NONE, &ibaBuildingType_, DeviceValueType::ENUM, @@ -2160,7 +2187,7 @@ void Thermostat::register_device_values() { register_device_value(DeviceValueTAG::TAG_NONE, &dampedoutdoortemp_, DeviceValueType::SHORT, - {}, + nullptr, F("dampedtemp"), F("Damped outdoor temperature"), DeviceValueUOM::DEGREES); @@ -2186,10 +2213,12 @@ void Thermostat::register_device_values() { F("Warm water circulation mode"), DeviceValueUOM::NONE); } + */ } // registers the values for a heating circuit void Thermostat::register_device_values_hc(std::shared_ptr hc) { + /* uint8_t model = hc->get_model(); // heating circuit @@ -2198,14 +2227,14 @@ void Thermostat::register_device_values_hc(std::shared_ptrsetpoint_roomTemp, DeviceValueType::SHORT, setpoint_temp_divider, F("seltemp"), F("Setpoint room temperature"), DeviceValueUOM::DEGREES); @@ -2226,7 +2255,7 @@ void Thermostat::register_device_values_hc(std::shared_ptrmanualtemp, DeviceValueType::UINT, {F("2")}, F("manualtemp"), F("Manual temperature"), DeviceValueUOM::DEGREES); register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, {F("2")}, F("comforttemp"), F("Comfort temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, {}, F("summertemp"), F("Summer temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, {}, F("designtemp"), F("Design temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, {}, F("offsettemp"), F("Offset temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, {}, F("minflowtemp"), F("Min flow temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, {}, F("maxflowtemp"), F("Max flow temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, {}, F("roominfluence"), F("Room influence"), DeviceValueUOM::NONE); - register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, {}, F("nofrosttemp"), F("Nofrost temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, {}, F("targetflowtemp"), F("Target flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->summertemp, DeviceValueType::UINT,nullptr, F("summertemp"), F("Summer temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->designtemp, DeviceValueType::UINT,nullptr, F("designtemp"), F("Design temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->offsettemp, DeviceValueType::INT,nullptr, F("offsettemp"), F("Offset temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT,nullptr, F("minflowtemp"), F("Min flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT,nullptr, F("maxflowtemp"), F("Max flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT,nullptr, F("roominfluence"), F("Room influence"), DeviceValueUOM::NONE); + register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT,nullptr, F("nofrosttemp"), F("Nofrost temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT,nullptr, F("targetflowtemp"), F("Target flow temperature"), DeviceValueUOM::DEGREES); register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, @@ -2279,7 +2308,7 @@ void Thermostat::register_device_values_hc(std::shared_ptrprogram, DeviceValueType::UINT, {}, F("program"), F("Program"), DeviceValueUOM::NONE); + register_device_value(tag, &hc->program, DeviceValueType::UINT,nullptr, F("program"), F("Program"), DeviceValueUOM::NONE); } if (model == EMS_DEVICE_FLAG_RC20) { @@ -2290,9 +2319,9 @@ void Thermostat::register_device_values_hc(std::shared_ptrmode, DeviceValueType::ENUM, flash_string_vector{F("off"), F("manual"), F("auto")}, F("mode"), F("Mode"), DeviceValueUOM::NONE); register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, flash_string_vector{F("day")}, F("modetype"), F("Mode type"), DeviceValueUOM::NONE); - register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, flash_string_vector{F("2")}, F("daytemp"), F("Day temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, flash_string_vector{F("2")}, F("nighttemp"), F("Night temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->program, DeviceValueType::UINT, {}, F("program"), F("Program"), DeviceValueUOM::NONE); + register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("daytemp"), F("Day temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("nighttemp"), F("Night temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->program, DeviceValueType::UINT,nullptr, F("program"), F("Program"), DeviceValueUOM::NONE); } if (model == EMS_DEVICE_FLAG_RC35 || model == EMS_DEVICE_FLAG_RC30_1) { @@ -2300,18 +2329,18 @@ void Thermostat::register_device_values_hc(std::shared_ptrmodetype, DeviceValueType::ENUM, {F("night"), F("day")}, F("modetype"), F("Mode type"), DeviceValueUOM::NONE); register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, {F("2")}, F("daytemp"), F("Day temperature"), DeviceValueUOM::DEGREES); register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, {F("2")}, F("nighttemp"), F("Night temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, {}, F("designtemp"), F("Design temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->designtemp, DeviceValueType::UINT,nullptr, F("designtemp"), F("Design temperature"), DeviceValueUOM::DEGREES); register_device_value(tag, &hc->offsettemp, DeviceValueType::INT, {F("2")}, F("offsettemp"), F("Offset temperature"), DeviceValueUOM::DEGREES); register_device_value(tag, &hc->holidaytemp, DeviceValueType::UINT, {F("2")}, F("holidaytemp"), F("Holiday temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, {}, F("targetflowtemp"), F("Target flow temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->summertemp, DeviceValueType::UINT, {}, F("summertemp"), F("Summer temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, {}, F("summermode"), F("Summer mode"), DeviceValueUOM::NONE); - register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL, {}, F("holidaymode"), F("Holiday mode"), DeviceValueUOM::NONE); - register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, {}, F("nofrosttemp"), F("Nofrost temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT, {}, F("roominfluence"), F("Room influence"), DeviceValueUOM::NONE); - register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT, {}, F("minflowtemp"), F("Min flow temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, {}, F("maxflowtemp"), F("Max flow temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT, {}, F("flowtempoffset"), F("Flow temperature offset"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT,nullptr, F("targetflowtemp"), F("Target flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->summertemp, DeviceValueType::UINT,nullptr, F("summertemp"), F("Summer temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->summermode, DeviceValueType::BOOL,nullptr, F("summermode"), F("Summer mode"), DeviceValueUOM::NONE); + register_device_value(tag, &hc->holidaymode, DeviceValueType::BOOL,nullptr, F("holidaymode"), F("Holiday mode"), DeviceValueUOM::NONE); + register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT,nullptr, F("nofrosttemp"), F("Nofrost temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT,nullptr, F("roominfluence"), F("Room influence"), DeviceValueUOM::NONE); + register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT,nullptr, F("minflowtemp"), F("Min flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT,nullptr, F("maxflowtemp"), F("Max flow temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT,nullptr, F("flowtempoffset"), F("Flow temperature offset"), DeviceValueUOM::DEGREES); register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, @@ -2333,7 +2362,7 @@ void Thermostat::register_device_values_hc(std::shared_ptrprogram, DeviceValueType::UINT, {}, F("program"), F("Program"), DeviceValueUOM::NONE); + register_device_value(tag, &hc->program, DeviceValueType::UINT,nullptr, F("program"), F("Program"), DeviceValueUOM::NONE); } if (model == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) { @@ -2346,11 +2375,12 @@ void Thermostat::register_device_values_hc(std::shared_ptrdaytemp, DeviceValueType::UINT, flash_string_vector{F("2")}, F("heattemp"), F("Heat temperature"), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, flash_string_vector{F("2")}, F("ecotemp"), F("Eco temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), F("heattemp"), F("Heat temperature"), DeviceValueUOM::DEGREES); + register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), F("ecotemp"), F("Eco temperature"), DeviceValueUOM::DEGREES); register_device_value( - tag, &hc->nofrosttemp, DeviceValueType::INT, flash_string_vector{F("2")}, F("nofrosttemp"), F("Nofrost temperature"), DeviceValueUOM::DEGREES); + tag, &hc->nofrosttemp, DeviceValueType::INT, FL_(div2), F("nofrosttemp"), F("Nofrost temperature"), DeviceValueUOM::DEGREES); } + */ } } // namespace emsesp diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index d62eda831..89b8571af 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -19,19 +19,7 @@ #ifndef EMSESP_THERMOSTAT_H #define EMSESP_THERMOSTAT_H -#include -#include - -#include - -#include "emsdevice.h" -#include "telegram.h" #include "emsesp.h" -#include "helpers.h" -#include "mqtt.h" - -#include -#include namespace emsesp { diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index eef5b6316..73194510e 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -23,7 +23,7 @@ namespace emsesp { // mapping of UOM, to match order in DeviceValueUOM enum emsdevice.h // must be an int of 4 bytes, 32bit aligned -static const __FlashStringHelper * DeviceValueUOM_s[] __attribute__((__aligned__(sizeof(int)))) PROGMEM = { +static const __FlashStringHelper * DeviceValueUOM_s[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = { F_(degrees), F_(percent), @@ -69,10 +69,6 @@ const std::string EMSdevice::tag_to_string(uint8_t tag) { return uuid::read_flash_string(DeviceValueTAG_s[tag - 1]); // offset by 1 to account for NONE } -const std::vector EMSdevice::devicevalues() const { - return devicevalues_; -} - std::string EMSdevice::brand_to_string() const { switch (brand_) { case EMSdevice::Brand::BOSCH: @@ -264,7 +260,7 @@ std::string EMSdevice::to_string_short() const { void EMSdevice::fetch_values() { EMSESP::logger().debug(F("Fetching values for device ID 0x%02X"), device_id()); - for (const auto & tf : telegram_functions_) { + for (const auto & tf : *telegram_functions_) { if (tf.fetch_) { read_command(tf.telegram_type_id_); } @@ -275,7 +271,7 @@ void EMSdevice::fetch_values() { void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) { EMSESP::logger().debug(F("Toggling fetch for device ID 0x%02X, telegram ID 0x%02X to %d"), device_id(), telegram_id, toggle); - for (auto & tf : telegram_functions_) { + for (auto & tf : *telegram_functions_) { if (tf.telegram_type_id_ == telegram_id) { tf.fetch_ = toggle; } @@ -284,7 +280,7 @@ void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) { // get status of automatic fetch for a telegram id bool EMSdevice::get_toggle_fetch(uint16_t telegram_id) { - for (auto & tf : telegram_functions_) { + for (auto & tf : *telegram_functions_) { if (tf.telegram_type_id_ == telegram_id) { return tf.fetch_; } @@ -292,15 +288,33 @@ bool EMSdevice::get_toggle_fetch(uint16_t telegram_id) { return false; } +// list device values +void EMSdevice::show_device_values(uuid::console::Shell & shell) { + size_t total_s = 0; + uint8_t count = 0; + for (const auto & dv : *devicevalues_) { + size_t s = sizeof(dv); + if (dv.full_name) { + shell.printfln("[%s] %d", uuid::read_flash_string(dv.full_name).c_str(), s); + } else { + shell.printfln("[%s]* %d", uuid::read_flash_string(dv.short_name).c_str(), s); + } + total_s += s; + count++; + } + shell.printfln("Total size of %d elements: %d", count, total_s); + shell.println(); +} + // list all the telegram type IDs for this device void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) { - if (telegram_functions_.size() == 0) { + if (telegram_functions_->size() == 0) { return; } shell.printf(F(" This %s will respond to telegram type IDs: "), device_type_name().c_str()); - for (const auto & tf : telegram_functions_) { + for (const auto & tf : *telegram_functions_) { shell.printf(F("0x%02X "), tf.telegram_type_id_); } shell.println(); @@ -308,16 +322,17 @@ void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) { // list all the telegram type IDs for this device, outputting to a string (max size 200) char * EMSdevice::show_telegram_handlers(char * result) { + uint8_t size = telegram_functions_->size(); + strlcpy(result, "", 200); - if (telegram_functions_.size() == 0) { + if (!size) { return result; } char str[10]; - uint8_t i = 0; - size_t size = telegram_functions_.size(); - for (const auto & tf : telegram_functions_) { + uint8_t i = 0; + for (const auto & tf : *telegram_functions_) { snprintf_P(str, sizeof(str), PSTR("0x%02X"), tf.telegram_type_id_); strlcat(result, str, 200); if (++i < size) { @@ -339,12 +354,26 @@ void EMSdevice::register_mqtt_topic(const std::string & topic, mqtt_subfunction_ // add command to library void EMSdevice::register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f) { - Command::add(device_type_, device_id_, cmd, f); + Command::add(device_type_, cmd, f); } // register a call back function for a specific telegram type void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p f) { - telegram_functions_.emplace_back(telegram_type_id, telegram_type_name, fetch, f); + TelegramFunction tf; + tf.fetch_ = fetch; + tf.process_function_ = f; + tf.telegram_type_id_ = telegram_type_id; + tf.telegram_type_name_ = telegram_type_name; + telegram_functions_->push(tf); + + // TelegramFunction(uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p process_function) + // : telegram_type_id_(telegram_type_id) + // , telegram_type_name_(telegram_type_name) + // , fetch_(fetch) + // , process_function_(process_function) { + // } + + // telegram_functions_.emplace_back(telegram_type_id, telegram_type_name, fetch, f); } // add to device value library @@ -357,14 +386,13 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __ // full name: used in Web and Console // uom: unit of measure from DeviceValueUOM // icon (optional): the HA mdi icon to use, from locale_*.h file -void EMSdevice::register_device_value(uint8_t tag, - void * value_p, - uint8_t type, - const flash_string_vector & options, - const __FlashStringHelper * short_name, - const __FlashStringHelper * full_name, - uint8_t uom, - const __FlashStringHelper * icon) { +void EMSdevice::register_device_value(uint8_t tag, + void * value_p, + uint8_t type, + const __FlashStringHelper * const * options, + const __FlashStringHelper * short_name, + const __FlashStringHelper * full_name, + uint8_t uom) { // init the value depending on it's type if (type == DeviceValueType::TEXT) { *(char *)(value_p) = {'\0'}; @@ -382,7 +410,26 @@ void EMSdevice::register_device_value(uint8_t tag, } // add to our library - devicevalues_.emplace_back(device_type_, tag, value_p, type, options, short_name, full_name, uom, icon); + DeviceValue dv; + dv.device_type = device_type_; + dv.tag = tag; + dv.value_p = value_p; + dv.type = type; + dv.short_name = short_name; + dv.full_name = full_name; + dv.uom = uom; + + dv.options = options; + dv.options_size = 0; + // count #options + if (options != nullptr) { + uint8_t i = 0; + while (options[i++]) { + dv.options_size++; + }; + } + + devicevalues_->push(dv); } // looks up the uom (suffix) for a given key from the device value table @@ -397,7 +444,10 @@ std::string EMSdevice::get_value_uom(const char * key) { p++; } - for (const auto & dv : devicevalues_) { + // find the key (p) in the name + // because the new container is not multi-threaded can't use the iterator + for (uint8_t i = 0; i < devicevalues_->size(); i++) { + auto dv = (*devicevalues_)[i]; if (dv.full_name != nullptr) { if (uuid::read_flash_string(dv.full_name) == p) { // ignore TIME since "minutes" is already included @@ -419,13 +469,16 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) { JsonArray data = json.createNestedArray("data"); uint8_t num_elements = 0; - for (const auto & dv : devicevalues_) { + for (uint8_t i = 0; i < devicevalues_->size(); i++) { + auto dv = (*devicevalues_)[i]; + + // for (const auto & dv : devicevalues()) { // ignore if full_name empty if (dv.full_name != nullptr) { // handle Booleans (true, false) if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { // see if we have options for the bool's - if (dv.options.size() == 2) { + if (dv.options_size == 2) { data.add(*(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1]); } else { // see how to render the value depending on the setting @@ -449,7 +502,7 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) { // handle ENUMs else if ((dv.type == DeviceValueType::ENUM) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) { - if (*(uint8_t *)(dv.value_p) < dv.options.size()) { + if (*(uint8_t *)(dv.value_p) < dv.options_size) { data.add(dv.options[*(uint8_t *)(dv.value_p)]); } } @@ -459,7 +512,7 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) { // If a divider is specified, do the division to 2 decimals places and send back as double/float // otherwise force as an integer whole // the nested if's is necessary due to the way the ArduinoJson templates are pre-processed by the compiler - uint8_t divider = (dv.options.size() == 1) ? Helpers::atoint(uuid::read_flash_string(dv.options[0]).c_str()) : 0; + uint8_t divider = ((dv.options_size) == 1) ? Helpers::atoint(uuid::read_flash_string(dv.options[0]).c_str()) : 0; // INT if ((dv.type == DeviceValueType::INT) && Helpers::hasValue(*(int8_t *)(dv.value_p))) { @@ -536,7 +589,9 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter uint8_t old_tag = 255; JsonObject json = root; - for (const auto & dv : devicevalues_) { + for (uint8_t i = 0; i < devicevalues_->size(); i++) { + auto dv = (*devicevalues_)[i]; + // for (const auto & dv : devicevalues()) { // only show if tag is either empty or matches a value, and don't show if full_name is empty unless we're outputing for mqtt payloads if (((tag_filter == DeviceValueTAG::TAG_NONE) || (tag_filter == dv.tag)) && (dv.full_name != nullptr || !verbose)) { bool have_tag = ((dv.tag != DeviceValueTAG::TAG_NONE) && (dv.device_type != DeviceType::BOILER)); @@ -561,7 +616,7 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter // handle Booleans (true, false) if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { // see if we have options for the bool's - if (dv.options.size() == 2) { + if (dv.options_size == 2) { json[name] = *(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1]; has_value = true; } else { @@ -590,7 +645,7 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter // handle ENUMs else if ((dv.type == DeviceValueType::ENUM) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) { - if (*(uint8_t *)(dv.value_p) < dv.options.size()) { + if (*(uint8_t *)(dv.value_p) < dv.options_size) { json[name] = dv.options[*(uint8_t *)(dv.value_p)]; has_value = true; } @@ -601,7 +656,7 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter // If a divider is specified, do the division to 2 decimals places and send back as double/float // otherwise force as an integer whole // the nested if's is necessary due to the way the ArduinoJson templates are pre-processed by the compiler - uint8_t divider = (dv.options.size() == 1) ? Helpers::atoint(uuid::read_flash_string(dv.options[0]).c_str()) : 0; + uint8_t divider = (dv.options_size == 1) ? Helpers::atoint(uuid::read_flash_string(dv.options[0]).c_str()) : 0; // INT if ((dv.type == DeviceValueType::INT) && Helpers::hasValue(*(int8_t *)(dv.value_p))) { @@ -658,6 +713,17 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter return has_value; } +// create the Home Assistant configs for each value as a sensor +void EMSdevice::publish_mqtt_ha_sensor() { + for (const auto & dv : *devicevalues_) { + Mqtt::register_mqtt_ha_sensor(dv.type, dv.tag, dv.full_name, device_type_, dv.short_name, dv.uom); + } + + // publish it + bool ok = publish_ha_config(); + ha_config_done(ok); // see if it worked +} + // return the name of the telegram type std::string EMSdevice::telegram_type_name(std::shared_ptr telegram) { // see if it's one of the common ones, like Version @@ -667,7 +733,7 @@ std::string EMSdevice::telegram_type_name(std::shared_ptr telegr return read_flash_string(F("UBADevices")); } - for (const auto & tf : telegram_functions_) { + for (const auto & tf : *telegram_functions_) { if ((tf.telegram_type_id_ == telegram->type_id) && (telegram->type_id != 0xFF)) { return uuid::read_flash_string(tf.telegram_type_name_); } @@ -679,7 +745,7 @@ std::string EMSdevice::telegram_type_name(std::shared_ptr telegr // take a telegram_type_id and call the matching handler // return true if match found bool EMSdevice::handle_telegram(std::shared_ptr telegram) { - for (const auto & tf : telegram_functions_) { + for (const auto & tf : *telegram_functions_) { if (tf.telegram_type_id_ == telegram->type_id) { // if the data block is empty, assume that this telegram is not recognized by the bus master // so remove it from the automatic fetch list @@ -689,7 +755,10 @@ bool EMSdevice::handle_telegram(std::shared_ptr telegram) { return false; } - tf.process_function_(telegram); + if (telegram->message_length > 0) { + tf.process_function_(telegram); + } + return true; } } diff --git a/src/emsdevice.h b/src/emsdevice.h index f998091cd..793d744de 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -23,6 +23,7 @@ #include #include +#include "containers.h" #include "emsfactory.h" #include "telegram.h" #include "mqtt.h" @@ -212,10 +213,13 @@ class EMSdevice { std::string to_string_short() const; void show_telegram_handlers(uuid::console::Shell & shell); + void show_device_values(uuid::console::Shell & shell); char * show_telegram_handlers(char * result); void show_mqtt_handlers(uuid::console::Shell & shell); using process_function_p = std::function)>; + // using process_function_p = void (*)(std::shared_ptr); + void register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p cb); bool handle_telegram(std::shared_ptr telegram); @@ -223,14 +227,13 @@ class EMSdevice { bool generate_values_json(JsonObject & json, const uint8_t tag_filter, const bool verbose = false); bool generate_values_json_web(JsonObject & json); - void register_device_value(uint8_t tag, - void * value_p, - uint8_t type, - const flash_string_vector & options, - const __FlashStringHelper * short_name, - const __FlashStringHelper * full_name, - uint8_t uom, - const __FlashStringHelper * icon = nullptr); + void register_device_value(uint8_t tag, + void * value_p, + uint8_t type, + const __FlashStringHelper * const * options, + const __FlashStringHelper * short_name, + const __FlashStringHelper * full_name, + uint8_t uom); void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid); void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid); @@ -240,16 +243,14 @@ class EMSdevice { void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f); void register_mqtt_cmd(const __FlashStringHelper * cmd, cmdfunction_p f); + void publish_mqtt_ha_sensor(); + std::string telegram_type_name(std::shared_ptr telegram); void fetch_values(); void toggle_fetch(uint16_t telegram_id, bool toggle); bool get_toggle_fetch(uint16_t telegram_id); - void reserve_mem(size_t n) { - telegram_functions_.reserve(n); - } - bool ha_config_done() const { return ha_config_done_; } @@ -318,41 +319,14 @@ class EMSdevice { static constexpr uint8_t EMS_DEVICE_FLAG_RC100 = 9; static constexpr uint8_t EMS_DEVICE_FLAG_JUNKERS = 10; - struct DeviceValue { - uint8_t device_type; // EMSdevice::DeviceType - uint8_t tag; // DeviceValueTAG::* - void * value_p; // pointer to variable of any type - uint8_t type; // DeviceValueType::* - const flash_string_vector options; // list of options for ENUM, or divider - const __FlashStringHelper * short_name; // used in MQTT - const __FlashStringHelper * full_name; // used in Web and Console - uint8_t uom; // DeviceValueUOM::* - const __FlashStringHelper * icon; // HA icon + void reserve_device_values(uint8_t elements) { + static auto dv_ = emsesp::array(elements, 255, 16); + devicevalues_ = &dv_; + } - DeviceValue(uint8_t device_type, - uint8_t tag, - void * value_p, - uint8_t type, - const flash_string_vector options, - const __FlashStringHelper * short_name, - const __FlashStringHelper * full_name, - uint8_t uom, - const __FlashStringHelper * icon) - : device_type(device_type) - , tag(tag) - , value_p(value_p) - , type(type) - , options(options) - , short_name(short_name) - , full_name(full_name) - , uom(uom) - , icon(icon) { - } - }; - const std::vector devicevalues() const; - - void init_devicevalues(uint8_t size) { - devicevalues_.reserve(size); + void reserve_telgram_functions(uint8_t elements) { + static auto tf_ = emsesp::array(elements, 255, 16); + telegram_functions_ = &tf_; } private: @@ -373,17 +347,21 @@ class EMSdevice { const __FlashStringHelper * telegram_type_name_; // e.g. RC20Message bool fetch_; // if this type_id be queried automatically process_function_p process_function_; - - TelegramFunction(uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p process_function) - : telegram_type_id_(telegram_type_id) - , telegram_type_name_(telegram_type_name) - , fetch_(fetch) - , process_function_(process_function) { - } }; - std::vector telegram_functions_; // each EMS device has its own set of registered telegram types + emsesp::array * telegram_functions_; // each EMS device has its own set of registered telegram types - std::vector devicevalues_; + struct DeviceValue { + uint8_t device_type; // EMSdevice::DeviceType + uint8_t tag; // DeviceValueTAG::* + void * value_p; // pointer to variable of any type + uint8_t type; // DeviceValueType::* + const __FlashStringHelper * const * options; // options as a flash char array + uint8_t options_size; // # options in the char array, calculated + const __FlashStringHelper * short_name; // used in MQTT + const __FlashStringHelper * full_name; // used in Web and Console + uint8_t uom; // DeviceValueUOM::* + }; + emsesp::array * devicevalues_; }; } // namespace emsesp diff --git a/src/emsesp.cpp b/src/emsesp.cpp index ff9d7e2ab..f3dd25722 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -20,6 +20,12 @@ namespace emsesp { +#if defined(EMSESP_STANDALONE) +uint32_t heap_start = 0; +#else +uint32_t heap_start = ESP.getFreeHeap(); // get initial available heap memory +#endif + AsyncWebServer webServer(80); #if defined(ESP32) @@ -38,10 +44,11 @@ WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSES WebDevicesService EMSESP::webDevicesService = WebDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); WebAPIService EMSESP::webAPIService = WebAPIService(&webServer); -using DeviceFlags = emsesp::EMSdevice; -using DeviceType = emsesp::EMSdevice::DeviceType; +using DeviceFlags = EMSdevice; +using DeviceType = EMSdevice::DeviceType; + std::vector> EMSESP::emsdevices; // array of all the detected EMS devices -std::vector EMSESP::device_library_; // library of all our known EMS devices so far +std::vector EMSESP::device_library_; // library of all our known EMS devices, in heap uuid::log::Logger EMSESP::logger_{F_(emsesp), uuid::log::Facility::KERN}; @@ -410,16 +417,7 @@ void EMSESP::publish_device_values(uint8_t device_type) { if (emsdevice && (emsdevice->device_type() == device_type)) { // if we're using HA and it's not already done, send the config topics first. only do this once if (Mqtt::ha_enabled() && (!emsdevice->ha_config_done())) { - // create the configs for each value as a sensor - for (const auto & dv : emsdevice->devicevalues()) { - if (dv.device_type == device_type) { - Mqtt::register_mqtt_ha_sensor(dv.type, dv.tag, dv.full_name, device_type, dv.short_name, dv.uom, dv.icon); - } - } - - // create HA device - // if this is done early, it may fail for some reason - emsdevice->ha_config_done(emsdevice->publish_ha_config()); + emsdevice->publish_mqtt_ha_sensor(); // create the configs for each value as a sensor } // if its a boiler, generate json for each group and publish it @@ -771,26 +769,14 @@ void EMSESP::show_devices(uuid::console::Shell & shell) { } shell.println(); emsdevice->show_telegram_handlers(shell); - // emsdevice->show_mqtt_handlers(shell); - shell.println(); -#if defined(EMSESP_DEBUG) - // TODO debug stuff - count size of objects - size_t total_s = 0; - uint8_t count = 0; - for (const auto & dv : emsdevice->devicevalues()) { - size_t s = sizeof(dv); - if (dv.full_name) { - shell.printfln("[%s] %d", uuid::read_flash_string(dv.full_name).c_str(), s); - } else { - shell.printfln("[%s]* %d", uuid::read_flash_string(dv.short_name).c_str(), s); - } - total_s += s; - count++; - } - shell.printfln("Total size of %d elements: %d", count, total_s); +#if defined EMSESP_DEBUG + emsdevice->show_mqtt_handlers(shell); shell.println(); + emsdevice->show_device_values(shell); #endif + + shell.println(); } } } @@ -1039,33 +1025,55 @@ void EMSESP::send_raw_telegram(const char * data) { // start all the core services // the services must be loaded in the correct order void EMSESP::start() { - // see if we need to migrate from previous versions - if (!system_.check_upgrade()) { + // start the file system. We use LittleFS for ESP8266. #ifdef ESP32 - SPIFFS.begin(true); + SPIFFS.begin(true); #elif defined(ESP8266) - LittleFS.begin(); + LittleFS.begin(); #endif - esp8266React.begin(); // loads system settings (wifi, mqtt, etc) - webSettingsService.begin(); // load EMS-ESP specific settings - } + esp8266React.begin(); // loads system settings (wifi, mqtt, etc) + webSettingsService.begin(); // load EMS-ESP specific settings - // Load our library of known devices into stack mem. Names are stored in Flash mem. - // device_library_.reserve(80); + system_.check_upgrade(); // do any upgrades + +#if defined(EMSESP_DEBUG) +#ifndef EMSESP_STANDALONE + uint32_t tbefore = ESP.getFreeHeap(); +#endif +#endif + + // Load our library of known devices into stack mem. Names are stored in Flash memory + // Still it takes up about 960bytes + device_library_.reserve(80); device_library_ = { #include "device_library.h" }; - console_.start(); // telnet and serial console - mqtt_.start(); // mqtt init - system_.start(); // starts syslog, uart, sets version, initializes LED. Requires pre-loaded settings. - shower_.start(); // initialize shower timer and shower alert - dallassensor_.start(); // dallas external sensors - webServer.begin(); // start web server +#if defined(EMSESP_DEBUG) +#ifndef EMSESP_STANDALONE + uint32_t tafter = ESP.getFreeHeap(); +#endif +#endif + + console_.start(); // telnet and serial console + mqtt_.start(); // mqtt init + system_.start(heap_start); // starts syslog, uart, sets version, initializes LED. Requires pre-loaded settings. + shower_.start(); // initialize shower timer and shower alert + dallassensor_.start(); // dallas external sensors + webServer.begin(); // start web server + + // emsdevices.reserve(5); // reserve space for initially 5 devices to avoid mem frag issues - emsdevices.reserve(5); // reserve space for initially 5 devices to avoid mem frag issues LOG_INFO(F("EMS Device library loaded with %d records"), device_library_.size()); + +#if defined(EMSESP_DEBUG) +#ifndef EMSESP_STANDALONE + LOG_INFO(F("Used %d mem for devices"), tbefore - tafter); + System::show_mem("after start()"); +#endif +#endif + } // main loop calling all services diff --git a/src/emsesp.h b/src/emsesp.h index a5ecd3dde..ae777b1cc 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -22,9 +22,10 @@ #include #include -#include #include +#include + #include #include #include @@ -39,6 +40,7 @@ #include "WebSettingsService.h" #include "WebAPIService.h" +#include "containers.h" #include "emsdevice.h" #include "emsfactory.h" #include "telegram.h" diff --git a/src/helpers.cpp b/src/helpers.cpp index f1852735a..c1a88a516 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -355,8 +355,9 @@ double Helpers::round2(double value, const uint8_t divider) { return (int)((value / divider) * 100 + 0.5) / 100.0; } -bool Helpers::check_abs(const int32_t i) { - return ((i < 0 ? -i : i) != 0xFFFFFF); +// abs of a signed 32-bit integer +uint32_t Helpers::abs(const int32_t i) { + return (i < 0 ? -i : i); } // for booleans, use isBool true (EMS_VALUE_BOOL) @@ -467,6 +468,4 @@ bool Helpers::value2enum(const char * v, uint8_t & value, const flash_string_vec } - - } // namespace emsesp diff --git a/src/helpers.h b/src/helpers.h index 9590f1cac..2e9365364 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -51,6 +51,7 @@ class Helpers { static uint32_t hextoint(const char * hex); static uint16_t atoint(const char * value); static bool check_abs(const int32_t i); + static uint32_t abs(const int32_t i); static double round2(double value, const uint8_t divider); static std::string toLower(std::string const & s); diff --git a/src/locale_EN.h b/src/locale_EN.h index 06fcddada..803bde3cf 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#pragma once + // common words MAKE_PSTR_WORD(exit) MAKE_PSTR_WORD(help) @@ -125,3 +127,38 @@ MAKE_PSTR(new_password_prompt1, "Enter new password: ") MAKE_PSTR(new_password_prompt2, "Retype new password: ") MAKE_PSTR(password_prompt, "Password: ") MAKE_PSTR(unset, "") + +MAKE_PSTR_WORD(2); +MAKE_PSTR_WORD(10); +MAKE_PSTR_WORD(100); +MAKE_PSTR_WORD(60); + +MAKE_PSTR_LIST(div2, F_(2)) +MAKE_PSTR_LIST(div10, F_(10)) +MAKE_PSTR_LIST(div100, F_(100)) +MAKE_PSTR_LIST(div60, F_(60)) + +MAKE_PSTR_WORD(time) +MAKE_PSTR_WORD(date) +MAKE_PSTR_WORD(1x3min) +MAKE_PSTR_WORD(2x3min) +MAKE_PSTR_WORD(3x3min) +MAKE_PSTR_WORD(4x3min) +MAKE_PSTR_WORD(5x3min) +MAKE_PSTR_WORD(6x3min) +MAKE_PSTR_WORD(continuos); +MAKE_PSTR(3wayvalve, "3-way valve") +MAKE_PSTR(chargepump, "charge pump") +MAKE_PSTR_WORD(hot) +MAKE_PSTR_WORD(eco) +MAKE_PSTR_WORD(intelligent) +MAKE_PSTR_WORD(flow) +MAKE_PSTR_WORD(buffer) +MAKE_PSTR(bufferedflow, "buffered flow") +MAKE_PSTR(layeredbuffer, "layered buffer") + +MAKE_PSTR_LIST(enum_off_time_date, F_(off), F_(time), F_(date)) +MAKE_PSTR_LIST(enum_freq, F_(off), F_(1x3min), F_(2x3min), F_(3x3min), F_(4x3min), F_(5x3min), F_(6x3min), F_(continuos)) +MAKE_PSTR_LIST(enum_charge, F_(3wayvalve), F_(chargepump)) +MAKE_PSTR_LIST(enum_comfort, F_(hot), F_(eco), F_(intelligent)) +MAKE_PSTR_LIST(enum_flow, F_(off), F_(flow), F_(bufferedflow), F_(buffer), F_(layeredbuffer)) diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 957009e1c..8626f1034 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -24,6 +24,7 @@ namespace emsesp { AsyncMqttClient * Mqtt::mqttClient_; + // static parameters we make global std::string Mqtt::hostname_; uint8_t Mqtt::mqtt_qos_; @@ -39,15 +40,16 @@ uint8_t Mqtt::dallas_format_; uint8_t Mqtt::ha_climate_format_; bool Mqtt::ha_enabled_; +static emsesp::queue mqtt_messages_ = emsesp::queue(MAX_MQTT_MESSAGES); + std::vector Mqtt::mqtt_subfunctions_; -uint16_t Mqtt::mqtt_publish_fails_ = 0; -bool Mqtt::connecting_ = false; -bool Mqtt::initialized_ = false; -uint8_t Mqtt::connectcount_ = 0; -uint16_t Mqtt::mqtt_message_id_ = 0; -std::list Mqtt::mqtt_messages_; -char will_topic_[Mqtt::MQTT_TOPIC_MAX_SIZE]; // because MQTT library keeps only char pointer +uint16_t Mqtt::mqtt_publish_fails_ = 0; +bool Mqtt::connecting_ = false; +bool Mqtt::initialized_ = false; +uint8_t Mqtt::connectcount_ = 0; +uint16_t Mqtt::mqtt_message_id_ = 0; +char will_topic_[Mqtt::MQTT_TOPIC_MAX_SIZE]; // because MQTT library keeps only char pointer uuid::log::Logger Mqtt::logger_{F_(mqtt), uuid::log::Facility::DAEMON}; @@ -506,17 +508,17 @@ void Mqtt::ha_status() { doc["uniq_id"] = FJSON("status"); doc["~"] = System::hostname(); // default ems-esp - // doc["avty_t"] = FJSON("~/status"); + // doc["avty_t"] = FJSON("~/status"); // commented out, as it causes errors in HA sometimes doc["json_attr_t"] = FJSON("~/heartbeat"); doc["stat_t"] = FJSON("~/heartbeat"); doc["name"] = FJSON("EMS-ESP status"); doc["val_tpl"] = FJSON("{{value_json['status']}}"); JsonObject dev = doc.createNestedObject("dev"); - dev["name"] = FJSON("EMS-ESP"); + dev["name"] = F_(EMSESP); // "EMS-ESP" dev["sw"] = EMSESP_APP_VERSION; dev["mf"] = FJSON("proddy"); - dev["mdl"] = FJSON("EMS-ESP"); + dev["mdl"] = F_(EMSESP); // "EMS-ESP" JsonArray ids = dev.createNestedArray("ids"); ids.add("ems-esp"); @@ -545,11 +547,12 @@ std::shared_ptr Mqtt::queue_message(const uint8_t operation, message = std::make_shared(operation, full_topic, payload, retain); } - // if the queue is full, make room but removing the last one - if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES) { - mqtt_messages_.pop_front(); - } - mqtt_messages_.emplace_back(mqtt_message_id_++, std::move(message)); + QueuedMqttMessage qmm; + qmm.content_ = std::move(message); + qmm.retry_count_ = 0; + qmm.packet_id_ = 0; + qmm.id_ = mqtt_message_id_++; + mqtt_messages_.push_back(qmm); return mqtt_messages_.back().content_; // this is because the message has been moved } @@ -706,7 +709,8 @@ void Mqtt::process_queue() { mqtt_messages_.pop_front(); // delete return; } else { - mqtt_messages_.front().retry_count_++; + // update the record + mqtt_messages_.front_p()->retry_count_++; LOG_DEBUG(F("Failed to publish to %s. Trying again, #%d"), message->topic.c_str(), mqtt_message.retry_count_ + 1); return; // leave on queue for next time so it gets republished } @@ -715,7 +719,7 @@ void Mqtt::process_queue() { // if we have ACK set with QOS 1 or 2, leave on queue and let the ACK process remove it // but add the packet_id so we can check it later if (mqtt_qos_ != 0) { - mqtt_messages_.front().packet_id_ = packet_id; + mqtt_messages_.front_p()->packet_id_ = packet_id; #if defined(EMSESP_DEBUG) LOG_DEBUG(F("[DEBUG] Setting packetID for ACK to %d"), packet_id); #endif @@ -733,15 +737,13 @@ void Mqtt::register_mqtt_ha_sensor(uint8_t type, // device v const __FlashStringHelper * name, const uint8_t device_type, const __FlashStringHelper * entity, - const uint8_t uom, - const __FlashStringHelper * icon) { + const uint8_t uom) { // ignore if name (fullname) is empty if (name == nullptr) { return; } - // DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG); - StaticJsonDocument doc; // TODO see if this crashes ESP8266? + DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG); bool have_prefix = ((tag != DeviceValueTAG::TAG_NONE) && (device_type != EMSdevice::DeviceType::BOILER)); @@ -791,7 +793,7 @@ void Mqtt::register_mqtt_ha_sensor(uint8_t type, // device v // look at the device value type if (type != DeviceValueType::BOOL) { // - // normal HA sensor + // normal HA sensor, not a boolean one // // topic @@ -807,21 +809,17 @@ void Mqtt::register_mqtt_ha_sensor(uint8_t type, // device v doc["unit_of_meas"] = EMSdevice::uom_to_string(uom); } - // if there was no icon supplied, resort to the default one - if (icon == nullptr) { - switch (uom) { - case DeviceValueUOM::DEGREES: - doc["ic"] = F_(icontemperature); - break; - case DeviceValueUOM::PERCENT: - doc["ic"] = F_(iconpercent); - break; - case DeviceValueUOM::NONE: - default: - break; - } - } else { - doc["ic"] = icon; // must be prefixed with mdi: + // map the HA icon + switch (uom) { + case DeviceValueUOM::DEGREES: + doc["ic"] = F_(icontemperature); + break; + case DeviceValueUOM::PERCENT: + doc["ic"] = F_(iconpercent); + break; + case DeviceValueUOM::NONE: + default: + break; } } else { // diff --git a/src/mqtt.h b/src/mqtt.h index 7d56e7ef1..643c2844d 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -38,8 +38,18 @@ using uuid::console::Shell; +// time between HA publishes #define MQTT_HA_PUBLISH_DELAY 50 +// size of queue +#if defined(EMSESP_STANDALONE) +#define MAX_MQTT_MESSAGES 70 +#elif defined(ESP32) +#define MAX_MQTT_MESSAGES 100 +#else +#define MAX_MQTT_MESSAGES 20 +#endif + namespace emsesp { using mqtt_subfunction_p = std::function; @@ -103,8 +113,7 @@ class Mqtt { const __FlashStringHelper * name, const uint8_t device_type, const __FlashStringHelper * entity, - const uint8_t uom, - const __FlashStringHelper * icon); + const uint8_t uom); static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb); static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type); @@ -174,37 +183,19 @@ class Mqtt { mqtt_retain_ = mqtt_retain; } + struct QueuedMqttMessage { + uint16_t id_; + std::shared_ptr content_; + uint8_t retry_count_; + uint16_t packet_id_; + }; + private: static uuid::log::Logger logger_; - class QueuedMqttMessage { - public: - const uint16_t id_; - const std::shared_ptr content_; - uint8_t retry_count_; - uint16_t packet_id_; - - ~QueuedMqttMessage() = default; - QueuedMqttMessage(uint16_t id, std::shared_ptr && content) - : id_(id) - , content_(std::move(content)) { - retry_count_ = 0; - packet_id_ = 0; - } - }; - static std::list mqtt_messages_; - static AsyncMqttClient * mqttClient_; static uint16_t mqtt_message_id_; -#if defined(EMSESP_STANDALONE) - static constexpr size_t MAX_MQTT_MESSAGES = 70; // size of queue -#elif defined(ESP32) - static constexpr size_t MAX_MQTT_MESSAGES = 100; // size of queue -#else - static constexpr size_t MAX_MQTT_MESSAGES = 20; // size of queue -#endif - static constexpr uint32_t MQTT_PUBLISH_WAIT = 200; // delay between sending publishes, to account for large payloads static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing @@ -261,7 +252,7 @@ class Mqtt { static uint8_t dallas_format_; static uint8_t ha_climate_format_; static bool ha_enabled_; -}; +}; // namespace emsesp } // namespace emsesp diff --git a/src/shower.h b/src/shower.h index 6983f2292..75b0c0c89 100644 --- a/src/shower.h +++ b/src/shower.h @@ -19,17 +19,8 @@ #ifndef EMSESP_SHOWER_H #define EMSESP_SHOWER_H -#include -#include - -#include "helpers.h" -#include "console.h" -#include "mqtt.h" -#include "telegram.h" #include "emsesp.h" -#include - namespace emsesp { class Shower { diff --git a/src/system.cpp b/src/system.cpp index f8d032ffd..900034021 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -130,17 +130,6 @@ void System::format(uuid::console::Shell & shell) { System::restart(); } -// return free heap mem as a percentage -uint8_t System::free_mem() { -#ifndef EMSESP_STANDALONE - uint32_t free_memory = ESP.getFreeHeap(); -#else - uint32_t free_memory = 1000; -#endif - - return (100 * free_memory / heap_start_); -} - void System::syslog_init() { int8_t syslog_level_; uint32_t syslog_mark_interval_; @@ -182,14 +171,10 @@ void System::syslog_init() { } // first call. Sets memory and starts up the UART Serial bridge -void System::start() { - // set the inital free mem +void System::start(uint32_t heap_start) { + // set the inital free mem, only on first boot if (heap_start_ < 2) { -#ifndef EMSESP_STANDALONE - heap_start_ = ESP.getFreeHeap(); -#else - heap_start_ = 2000; -#endif + heap_start_ = heap_start; } #if defined(EMSESP_DEBUG) @@ -202,15 +187,15 @@ void System::start() { // these commands respond to the topic "system" and take a payload like {cmd:"", data:"", id:""} EMSESP::webSettingsService.read([&](WebSettings & settings) { - Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(pin), System::command_pin); - Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(send), System::command_send); - Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(publish), System::command_publish); - Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(fetch), System::command_fetch); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch); Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info); Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings); #if defined(EMSESP_TEST) - Command::add(EMSdevice::DeviceType::SYSTEM, settings.ems_bus_id, F_(test), System::command_test); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(test), System::command_test); #endif }); @@ -308,13 +293,12 @@ void System::show_mem(const char * note) { static uint8_t old_heap_frag = 0; uint32_t free_heap = ESP.getFreeHeap(); uint8_t heap_frag = ESP.getHeapFragmentation(); - LOG_INFO(F("(%s) Free heap: %d%% (%lu) (~%lu), frag:%d%% (~%d)"), + LOG_INFO(F("(%s) Free heap: %lu (~%lu), frag:%d%% (~%d)"), note, - free_mem(), free_heap, - (uint32_t)abs(free_heap - old_free_heap), + (uint32_t)Helpers::abs(free_heap - old_free_heap), heap_frag, - (uint8_t)abs(heap_frag - old_heap_frag)); + (uint8_t)Helpers::abs(heap_frag - old_heap_frag)); old_free_heap = free_heap; old_heap_frag = heap_frag; #endif @@ -329,8 +313,6 @@ void System::send_heartbeat() { return; } - uint32_t free_memory = free_mem(); - #if defined(ESP8266) uint8_t frag_memory = ESP.getHeapFragmentation(); #endif @@ -352,8 +334,8 @@ void System::send_heartbeat() { doc["mqttfails"] = Mqtt::publish_fails(); doc["txfails"] = EMSESP::txservice_.telegram_fail_count(); doc["rxfails"] = EMSESP::rxservice_.telegram_error_count(); - doc["freemem"] = free_memory; #if defined(ESP8266) + doc["freemem"] = ESP.getFreeHeap(); doc["fragmem"] = frag_memory; #endif if (analog_enabled_) { @@ -508,12 +490,11 @@ void System::show_system(uuid::console::Shell & shell) { shell.printfln(F("CPU frequency: %u MHz"), ESP.getCpuFreqMHz()); #endif shell.printfln(F("Sketch size: %u bytes (%u bytes free)"), ESP.getSketchSize(), ESP.getFreeSketchSpace()); - shell.printfln(F("Free heap: %lu bytes"), (unsigned long)ESP.getFreeHeap()); - shell.printfln(F("Free mem: %d %%"), free_mem()); + shell.printfln(F("Free heap: %lu bytes"), (uint32_t)ESP.getFreeHeap()); #if defined(ESP8266) shell.printfln(F("Heap fragmentation: %u %%"), ESP.getHeapFragmentation()); - shell.printfln(F("Maximum free block size: %lu bytes"), (unsigned long)ESP.getMaxFreeBlockSize()); - shell.printfln(F("Free continuations stack: %lu bytes"), (unsigned long)ESP.getFreeContStack()); + shell.printfln(F("Maximum free block size: %lu bytes"), (uint32_t)ESP.getMaxFreeBlockSize()); + shell.printfln(F("Free continuations stack: %lu bytes"), (uint32_t)ESP.getFreeContStack()); #endif shell.println(); @@ -734,10 +715,10 @@ void System::console_commands(Shell & shell, unsigned int context) { Console::enter_custom_context(shell, context); } -// upgrade from previous versions of EMS-ESP, based on SPIFFS on an ESP8266 +// upgrade from previous versions of EMS-ESP // returns true if an upgrade was done -// the logic is bit abnormal (loading both filesystems and testing) but this was the only way I could get it to work reliably bool System::check_upgrade() { + /* #if defined(ESP8266) LittleFSConfig l_cfg; l_cfg.setAutoFormat(false); @@ -923,6 +904,8 @@ bool System::check_upgrade() { #else return false; #endif +*/ + return false; } // export all settings to JSON text @@ -1033,8 +1016,8 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json node["version"] = EMSESP_APP_VERSION; node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); - node["freemem"] = free_mem(); #if defined(ESP8266) + node["freemem"] = ESP.getFreeHeap(); node["fragmem"] = ESP.getHeapFragmentation(); #endif diff --git a/src/system.h b/src/system.h index f01d8afe2..e380613e9 100644 --- a/src/system.h +++ b/src/system.h @@ -39,7 +39,7 @@ namespace emsesp { class System { public: - void start(); + void start(uint32_t heap_start); void loop(); // commands @@ -59,7 +59,6 @@ class System { static bool command_test(const char * value, const int8_t id); #endif - static uint8_t free_mem(); static void upload_status(bool in_progress); static bool upload_status(); static void show_mem(const char * note); diff --git a/src/telegram.cpp b/src/telegram.cpp index c25388d67..513a4af5e 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -127,10 +127,10 @@ std::string Telegram::to_string_message() const { // checks if we have an Rx telegram that needs processing void RxService::loop() { while (!rx_telegrams_.empty()) { - auto telegram = rx_telegrams_.front().telegram_; + auto telegram = rx_telegrams_.pop().telegram_; (void)EMSESP::process_telegram(telegram); // further process the telegram increment_telegram_count(); // increase rx count - rx_telegrams_.pop_front(); // remove it from the queue + // rx_telegrams_.pop_front(); // remove it from the queue } } @@ -217,25 +217,10 @@ void RxService::add(uint8_t * data, uint8_t length) { // if we receive a hc2.. telegram from 0x19.. match it to master_thermostat if master is 0x18 src = EMSESP::check_master_device(src, type_id, true); - // create the telegram - auto telegram = std::make_shared(operation, src, dest, type_id, offset, message_data, message_length); - - // check if queue is full, if so remove top item to make space - if (rx_telegrams_.size() >= MAX_RX_TELEGRAMS) { - rx_telegrams_.pop_front(); - } - - rx_telegrams_.emplace_back(rx_telegram_id_++, std::move(telegram)); // add to queue -} - -// -// Tx CODE starts here... -// - -// empty queue, don't process -void TxService::flush_tx_queue() { - tx_telegrams_.clear(); - tx_telegram_id_ = 0; + QueuedRxTelegram qrxt; + qrxt.telegram_ = std::make_shared(operation, src, dest, type_id, offset, message_data, message_length); + qrxt.id_ = rx_telegram_id_++; + rx_telegrams_.push(qrxt); } // start and initialize Tx @@ -279,13 +264,13 @@ void TxService::send() { return; } + // get the Telegram, also removes from queue + auto telegram = tx_telegrams_.pop(); + // if we're in read-only mode (tx_mode 0) forget the Tx call if (tx_mode() != 0) { - send_telegram(tx_telegrams_.front()); + send_telegram(telegram); } - - - tx_telegrams_.pop_front(); // remove the telegram from the queue } // process a Tx telegram @@ -411,21 +396,19 @@ void TxService::add(const uint8_t operation, uint8_t * message_data, const uint8_t message_length, const bool front) { - auto telegram = std::make_shared(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length); - #ifdef EMSESP_DEBUG LOG_DEBUG(F("[DEBUG] New Tx [#%d] telegram, length %d"), tx_telegram_id_, message_length); #endif - // if the queue is full, make room but removing the last one - if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) { - tx_telegrams_.pop_front(); - } + QueuedTxTelegram qtxt; + qtxt.id_ = tx_telegram_id_++; + qtxt.retry_ = false; + qtxt.telegram_ = std::make_shared(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length); if (front) { - tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram), false); // add to back of queue + tx_telegrams_.push_front(qtxt); // add to front of queue } else { - tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false); // add to back of queue + tx_telegrams_.push_back(qtxt); // add to back of queue } } @@ -484,21 +467,19 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt EMSESP::set_read_id(type_id); } - auto telegram = std::make_shared(operation, src, dest, type_id, offset, message_data, message_length); // operation is TX_WRITE or TX_READ - - // if the queue is full, make room but removing the last one - if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) { - tx_telegrams_.pop_front(); - } - #ifdef EMSESP_DEBUG LOG_DEBUG(F("[DEBUG] New Tx [#%d] telegram, length %d"), tx_telegram_id_, message_length); #endif + QueuedTxTelegram qtxt; + qtxt.id_ = tx_telegram_id_++; + qtxt.retry_ = false; + qtxt.telegram_ = std::make_shared(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length); + if (front) { - tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram), false); // add to back of queue + tx_telegrams_.push_front(qtxt); // add to front of queue } else { - tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false); // add to back of queue + tx_telegrams_.push_back(qtxt); // add to back of queue } } @@ -548,7 +529,7 @@ void TxService::send_raw(const char * telegram_data) { return; // nothing to send } - add(Telegram::Operation::TX_RAW, data, count + 1, true); // add to front of Tx queue + add(Telegram::Operation::TX_RAW, data, count + 1, true); // add to top/front of Tx queue } // add last Tx to tx queue and increment count @@ -573,16 +554,15 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui Helpers::data_to_hex(data, length).c_str()); #endif - // add to the top of the queue - if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) { - tx_telegrams_.pop_back(); - } - - tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram_last_), true); + QueuedTxTelegram qtxt; + qtxt.id_ = tx_telegram_id_++; + qtxt.retry_ = true; // this time it is a retry + qtxt.telegram_ = telegram_last_; + tx_telegrams_.push_front(qtxt); // add to front of queue } uint16_t TxService::read_next_tx() { - // add to the top of the queue + // add to the top/front of the queue uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes add(Telegram::Operation::TX_READ, telegram_last_->dest, telegram_last_->type_id, telegram_last_->offset + 25, message_data, 1, true); return telegram_last_->type_id; @@ -606,8 +586,8 @@ uint16_t TxService::post_send_query() { uint8_t dest = (this->telegram_last_->dest & 0x7F); // when set a value with large offset before and validate on same type, we have to add offset 0, 26, 52, ... uint8_t offset = (this->telegram_last_->type_id == post_typeid) ? ((this->telegram_last_->offset / 26) * 26) : 0; - uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes - this->add(Telegram::Operation::TX_READ, dest, post_typeid, offset, message_data, 1, true); + uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes + this->add(Telegram::Operation::TX_READ, dest, post_typeid, offset, message_data, 1, true); // add to top/front of queue // read_request(telegram_last_post_send_query_, dest, 0); // no offset LOG_DEBUG(F("Sending post validate read, type ID 0x%02X to dest 0x%02X"), post_typeid, dest); set_post_send_query(0); // reset diff --git a/src/telegram.h b/src/telegram.h index 0d8185180..4a845f0fe 100644 --- a/src/telegram.h +++ b/src/telegram.h @@ -20,7 +20,6 @@ #define EMSESP_TELEGRAM_H #include -#include // UART drivers #if defined(ESP8266) @@ -33,8 +32,12 @@ #include +#include "containers.h" #include "helpers.h" +#define MAX_RX_TELEGRAMS 10 // size of Rx queue +#define MAX_TX_TELEGRAMS 30 // size of Tx queue + // default values for null values static constexpr uint8_t EMS_VALUE_BOOL = 0xFF; // used to mark that something is a boolean static constexpr uint8_t EMS_VALUE_BOOL_OFF = 0x00; // boolean false @@ -200,8 +203,6 @@ class EMSbus { }; class RxService : public EMSbus { - static constexpr size_t MAX_RX_TELEGRAMS = 10; - public: RxService() = default; ~RxService() = default; @@ -229,37 +230,29 @@ class RxService : public EMSbus { return (q <= EMS_BUS_QUALITY_RX_THRESHOLD ? 100 : 100 - q); } - class QueuedRxTelegram { - public: - const uint16_t id_; - const std::shared_ptr telegram_; - - ~QueuedRxTelegram() = default; - QueuedRxTelegram(uint16_t id, std::shared_ptr && telegram) - : id_(id) - , telegram_(std::move(telegram)) { - } + struct QueuedRxTelegram { + uint16_t id_; + std::shared_ptr telegram_; }; - const std::list queue() const { + const emsesp::queue queue() const { return rx_telegrams_; } private: static constexpr uint8_t EMS_BUS_QUALITY_RX_THRESHOLD = 5; // % threshold before reporting quality issues - uint8_t rx_telegram_id_ = 0; // queue counter - uint32_t telegram_count_ = 0; // # Rx received - uint32_t telegram_error_count_ = 0; // # Rx CRC errors - std::shared_ptr rx_telegram; // the incoming Rx telegram - std::list rx_telegrams_; // the Rx Queue + uint8_t rx_telegram_id_ = 0; // queue counter + uint32_t telegram_count_ = 0; // # Rx received + uint32_t telegram_error_count_ = 0; // # Rx CRC errors + std::shared_ptr rx_telegram; // the incoming Rx telegram + emsesp::queue rx_telegrams_ = emsesp::queue(MAX_RX_TELEGRAMS); // the Rx Queue }; class TxService : public EMSbus { public: - static constexpr size_t MAX_TX_TELEGRAMS = 30; // size of Tx queue - static constexpr uint8_t TX_WRITE_FAIL = 4; // EMS return code for fail - static constexpr uint8_t TX_WRITE_SUCCESS = 1; // EMS return code for success + static constexpr uint8_t TX_WRITE_FAIL = 4; // EMS return code for fail + static constexpr uint8_t TX_WRITE_SUCCESS = 1; // EMS return code for success TxService() = default; ~TxService() = default; @@ -277,7 +270,6 @@ class TxService : public EMSbus { void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0); void send_raw(const char * telegram_data); void send_poll(); - void flush_tx_queue(); void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length); bool is_last_tx(const uint8_t src, const uint8_t dest) const; uint16_t post_send_query(); @@ -342,21 +334,13 @@ class TxService : public EMSbus { telegram_write_count_++; } - class QueuedTxTelegram { - public: - const uint16_t id_; - const std::shared_ptr telegram_; - const bool retry_; // is a retry - - ~QueuedTxTelegram() = default; - QueuedTxTelegram(uint16_t id, std::shared_ptr && telegram, bool retry) - : id_(id) - , telegram_(std::move(telegram)) - , retry_(retry) { - } + struct QueuedTxTelegram { + uint16_t id_; + std::shared_ptr telegram_; + bool retry_; // true if its a retry }; - const std::list queue() const { + const emsesp::queue queue() const { return tx_telegrams_; } @@ -367,7 +351,7 @@ class TxService : public EMSbus { #endif private: - std::list tx_telegrams_; // the Tx queue + emsesp::queue tx_telegrams_ = emsesp::queue(MAX_TX_TELEGRAMS); // the Tx Queue uint32_t telegram_read_count_ = 0; // # Tx successful reads uint32_t telegram_write_count_ = 0; // # Tx successful writes diff --git a/src/test/test.cpp b/src/test/test.cpp index f538050ae..5eb7cf105 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -401,10 +401,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { if (command == "boiler") { shell.printfln(F("Testing boiler...")); run_test("boiler"); + shell.invoke_command("show devices"); shell.invoke_command("show"); - shell.invoke_command("call boiler info"); - shell.invoke_command("call system publish"); - shell.invoke_command("show mqtt"); + // shell.invoke_command("call boiler info"); + // shell.invoke_command("call system publish"); + // shell.invoke_command("show mqtt"); } if (command == "fr120") { @@ -558,7 +559,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { uart_telegram("98 00 FF 00 01 A5 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03"); // without CRC uart_telegram_withCRC("98 00 FF 00 01 A6 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03 6B"); // with CRC - EMSESP::txservice_.flush_tx_queue(); shell.loop_all(); EMSESP::show_device_values(shell); @@ -638,8 +638,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { if (command == "tx") { shell.printfln(F("Testing Tx...")); - EMSESP::txservice_.flush_tx_queue(); - // TX queue example - Me -> Thermostat, (0x91), telegram: 0B 17 91 05 44 45 46 47 (#data=4) uint8_t t11[] = {0x44, 0x45, 0x46, 0x47}; EMSESP::txservice_.add(Telegram::Operation::TX_RAW, 0x17, 0x91, 0x05, t11, sizeof(t11)); @@ -672,16 +670,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { for (uint8_t i = 0; i < 10; i++) { EMSESP::txservice_.send(); // send it to UART } - - EMSESP::txservice_.flush_tx_queue(); } if (command == "poll") { shell.printfln(F("Testing Poll...")); - // check if sending works when a poll comes in, with retries - EMSESP::txservice_.flush_tx_queue(); - // simulate sending a read request // uint8_t t16[] = {0x44, 0x45, 0x46, 0x47}; // Me -> Thermostat, (0x91), telegram: 0B 17 91 05 44 45 46 47 (#data=4) // EMSESP::txservice_.add(Telegram::Operation::TX_RAW, 0x17, 0x91, 0x05, t16, sizeof(t16)); @@ -702,8 +695,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { uint8_t t2[] = {0x21, 0x22}; EMSESP::send_write_request(0x91, 0x17, 0x00, t2, sizeof(t2), 0); EMSESP::show_ems(shell); - - EMSESP::txservice_.flush_tx_queue(); } if (command == "cmd") { @@ -759,10 +750,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { char boiler_topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char thermostat_topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char system_topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; - - // test publish and adding to queue - EMSESP::txservice_.flush_tx_queue(); - EMSESP::EMSESP::mqtt_.publish("boiler", "test me"); Mqtt::show_mqtt(shell); // show queue @@ -816,7 +803,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { EMSESP::incoming_telegram(poll, 1); EMSESP::show_ems(shell); - EMSESP::txservice_.flush_tx_queue(); } if (command == "rx2") { diff --git a/src/test/test.h b/src/test/test.h index 1ed36f6eb..15a93336b 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -21,21 +21,7 @@ #ifndef EMSESP_TEST_H #define EMSESP_TEST_H -#include - -#include -#include - -#include -#include -#include - -#include "emsdevice.h" -#include "emsfactory.h" -#include "telegram.h" -#include "mqtt.h" #include "emsesp.h" -#include "command.h" namespace emsesp {