From 5e90d264bc00de2c1704ea871737ac21948cdcca Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Thu, 4 Jun 2020 14:35:39 +0200 Subject: [PATCH] add remote room controller, add mqtt-set for thermostat-settings --- src/emsesp.cpp | 6 +++ src/emsesp.h | 1 + src/roomcontrol.cpp | 128 ++++++++++++++++++++++++++++++++++++++++++++ src/roomcontrol.h | 45 ++++++++++++++++ src/thermostat.cpp | 101 ++++++++++++++++++++++++++++------ src/thermostat.h | 6 ++- 6 files changed, 271 insertions(+), 16 deletions(-) create mode 100644 src/roomcontrol.cpp create mode 100644 src/roomcontrol.h diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 61791328e..a5ddb715f 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -563,6 +563,8 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) { //LOG_TRACE(F("Rx: %s"), Helpers::data_to_hex(data, length).c_str()); uint8_t first_value = data[0]; if (((first_value & 0x7F) == txservice_.ems_bus_id()) && (length > 1)) { + // if we ask ourself at roomcontrol for version e.g. 0B 98 02 ... + Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data, length); rxservice_.add(data, length); // just for logging return; // it's an echo } @@ -614,8 +616,12 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) { EMSbus::last_bus_activity(uuid::get_uptime()); // set the flag indication the EMS bus is active txservice_.send(); } + // send remote room temperature if active + Roomctrl::send(first_value ^ 0x80 ^ rxservice_.ems_mask()); return; } else { + // check if there is a message for the roomcontroller + Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data, length); // add to RxQueue, what ever it is. rxservice_.add(data, length); } diff --git a/src/emsesp.h b/src/emsesp.h index b93a5803a..5d61ea418 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -44,6 +44,7 @@ #include "console.h" #include "boiler.h" #include "shower.h" +#include "roomcontrol.h" #define LOG_TRACE_WATCH_NONE 0 // no watch set #define ESP_DELAY 1 diff --git a/src/roomcontrol.cpp b/src/roomcontrol.cpp new file mode 100644 index 000000000..ec17eae7f --- /dev/null +++ b/src/roomcontrol.cpp @@ -0,0 +1,128 @@ +/* + * EMS-ESP - https://github.com/proddy/EMS-ESP + * Copyright 2019 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 . + */ + +#include "roomcontrol.h" + +MAKE_PSTR(logger_name, "roomctrl") + +namespace emsesp { + +uint32_t rc_time_ = 0; +uint16_t hc_ = EMS_VALUE_USHORT_NOTSET; +int16_t remotetemp[4] = { + EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET +}; +/** + * set the temperature, + */ +void Roomctrl::set_remotetemp(uint8_t hc, int16_t temp){ + remotetemp[hc] = temp; +} + +/** + * if remote control is active send the temperature every minute + */ +void Roomctrl::send(uint8_t addr) { + uint8_t hc_ = addr - ADDR; + // check address, reply only on addresses 0x18..0x1B + if (hc_ > 3) { + return; + } + // no reply if the temperature is not set + if (remotetemp[hc_] == EMS_VALUE_SHORT_NOTSET) { + return; + } + if (millis() - rc_time_ > 60000) { // send every minute + rc_time_ = millis(); + temperature(addr, 0x00); // send to all + } else { + // acknowledge every poll, otherwise the master shows error A11-822 + EMSuart::send_poll(addr); + } +} + +/** + * check if there is a message for the remote room controller + */ +void Roomctrl::check(uint8_t addr, uint8_t * data, const uint8_t length) { + uint8_t hc_num = addr - ADDR; + // check address, reply only on addresses 0x18..0x1B + if (hc_num > 3) { + return; + } + // no reply if the temperature is not set + if (remotetemp[hc_num] == EMS_VALUE_SHORT_NOTSET) { + return; + } + // for now we only reply to version and remote temperature + if (data[2] == 0x02) { + version(addr, data[0]); + } else if (data[2] == 0xAF && data[3] == 0) { + temperature(addr, data[0]); + } else { + unknown(addr, data[0], data[2], data[3]); + } +} + +/** + * send version info RC20 (Prod. 113, Ver. 02.01) or RC20RF (Prod. 93, Ver. 02.00) + */ +void Roomctrl::version(uint8_t addr, uint8_t dst) { + uint8_t data[10]; + data[0] = addr; + data[1] = dst; + data[2] = 0x02; + data[3] = 0; + data[4] = 113; // set RC20 id 113, Ver 02.01 + data[5] = 0x02; + data[6] = 0x01; + data[7] = EMSbus::calculate_crc(data, 7); // apppend CRC + EMSuart::transmit(data, 8); +} + +/** + * unknown message id, we reply with empty message + */ +void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset) { + uint8_t data[10]; + data[0] = addr; + data[1] = dst; + data[2] = type; + data[3] = offset; + data[4] = EMSbus::calculate_crc(data, 4); // apppend CRC + EMSuart::transmit(data, 5); +} + +/** + * send the room temperature in message 0xAF + */ +void Roomctrl::temperature(uint8_t addr, uint8_t dst) { + uint8_t data[10]; + uint8_t hc_ = addr - ADDR; + data[0] = addr; + data[1] = dst; + data[2] = 0xAF; + data[3] = 0; + data[4] = (uint8_t)(remotetemp[hc_] >> 8); + data[5] = (uint8_t)(remotetemp[hc_] & 0xFF); + data[6] = 0; + data[7] = EMSbus::calculate_crc(data, 7); // apppend CRC + EMSuart::transmit(data, 8); +} + +} // namespace emsesp diff --git a/src/roomcontrol.h b/src/roomcontrol.h new file mode 100644 index 000000000..14c68552e --- /dev/null +++ b/src/roomcontrol.h @@ -0,0 +1,45 @@ +/* + * EMS-ESP - https://github.com/proddy/EMS-ESP + * Copyright 2019 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 . + */ + +#ifndef EMSESP_ROOMCTRL_H +#define EMSESP_ROOMCTRL_H + +#include "emsesp.h" +#include "telegram.h" +#include "uart/emsuart_esp8266.h" +#include "uart/emsuart_esp32.h" + +namespace emsesp { + +class Roomctrl { + public: + static void send(uint8_t addr); + static void check(uint8_t addr, uint8_t * data, const uint8_t length); + static void set_remotetemp(uint8_t hc, int16_t temp); + + private: + #define ADDR 0x18 + static void version(uint8_t addr, uint8_t dst); + static void unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset); + static void temperature(uint8_t addr, uint8_t dst); + +}; + +} // namespace emsesp + +#endif diff --git a/src/thermostat.cpp b/src/thermostat.cpp index 1fafff48b..a25984303 100644 --- a/src/thermostat.cpp +++ b/src/thermostat.cpp @@ -238,6 +238,7 @@ void Thermostat::thermostat_cmd(const char * message) { strlcpy(hc_name, "hc", 6); uint8_t hc_num = hc->hc_num(); strlcat(hc_name, Helpers::itoa(s, hc_num), 6); + if (nullptr != doc[hc_name]["mode"]) { std::string mode = doc[hc_name]["mode"]; // first check mode set_mode(mode, hc_num); @@ -268,11 +269,38 @@ void Thermostat::thermostat_cmd(const char * message) { } if (float f = doc[hc_name]["remotetemp"]) { if (f > 100 || f < 0) { - hc->remotetemp = EMS_VALUE_SHORT_NOTSET; + Roomctrl::set_remotetemp(hc_num - 1, EMS_VALUE_SHORT_NOTSET); } else { - hc->remotetemp = (uint16_t)(f * 10); + Roomctrl::set_remotetemp(hc_num - 1, (int16_t)(f * 10)); } } + if (nullptr != doc[hc_name]["control"]) { + uint8_t ctrl = doc[hc_name]["control"]; + set_control(ctrl, hc_num); + } + if (float ct = doc["calinttemp"]) { + set_settings_calinttemp((int8_t)(ct *10)); + } + if (nullptr != doc["minexttemp"]) { + int8_t mt = doc["minexttemp"]; + set_settings_minexttemp(mt); + } + if (nullptr != doc["building"]) { + uint8_t bd = doc["building"]; + set_settings_building(bd); + } + if (nullptr != doc["language"]) { + uint8_t lg = doc["language"]; + set_settings_language(lg); + } + if (nullptr != doc["display"]) { + uint8_t dp = doc["display"]; + set_settings_display(dp); + } + if (nullptr != doc["clockoffset"]) { + int8_t co = doc["clockoffset"]; + set_settings_clockoffset(co); + } } const char * command = doc["cmd"]; @@ -301,7 +329,6 @@ void Thermostat::thermostat_cmd(const char * message) { return; } - // set night temp value if (strcmp(command, "nighttemp") == 0) { float f = doc["data"]; if (f) { @@ -310,7 +337,6 @@ void Thermostat::thermostat_cmd(const char * message) { } } - // set daytemp value if (strcmp(command, "daytemp") == 0) { float f = doc["data"]; if (f) { @@ -319,7 +345,6 @@ void Thermostat::thermostat_cmd(const char * message) { return; } - // set holiday value if (strcmp(command, "holidaytemp") == 0) { float f = doc["data"]; if (f) { @@ -328,7 +353,6 @@ void Thermostat::thermostat_cmd(const char * message) { return; } - // set eco value if (strcmp(command, "ecotemp") == 0) { float f = doc["data"]; if (f) { @@ -337,7 +361,6 @@ void Thermostat::thermostat_cmd(const char * message) { return; } - // set heat value if (strcmp(command, "heattemp") == 0) { float f = doc["data"]; if (f) { @@ -346,7 +369,6 @@ void Thermostat::thermostat_cmd(const char * message) { return; } - // set nofrost value if (strcmp(command, "nofrosttemp") == 0) { float f = doc["data"]; if (f) { @@ -354,6 +376,7 @@ void Thermostat::thermostat_cmd(const char * message) { } return; } + if (strcmp(command, "summertemp") == 0) { float f = doc["data"]; if (f) { @@ -361,6 +384,7 @@ void Thermostat::thermostat_cmd(const char * message) { } return; } + if (strcmp(command, "designtemp") == 0) { float f = doc["data"]; if (f) { @@ -368,7 +392,8 @@ void Thermostat::thermostat_cmd(const char * message) { } return; } - if (strcmp(command, "offettemp") == 0) { + + if (strcmp(command, "offsettemp") == 0) { float f = doc["data"]; if (f) { set_temperature(f, HeatingCircuit::Mode::OFFSET, hc_num); @@ -1114,22 +1139,68 @@ void Thermostat::process_RCTime(std::shared_ptr telegram) { ); } +// 0xA5 - Set minimum external temperature +void Thermostat::set_settings_minexttemp(const int8_t mt) { + if (((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) || ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35)) { + LOG_INFO(F("Setting min external temperature to %d"), mt); + write_command(EMS_TYPE_IBASettings, 5, mt); + } +} + +// 0xA5 - Clock offset +void Thermostat::set_settings_clockoffset(const int8_t co) { + if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) { + LOG_INFO(F("Setting clock offset to %d"), co); + write_command(EMS_TYPE_IBASettings, 12, co); + } +} +// 0xA5 - Calibrate internal temperature +void Thermostat::set_settings_calinttemp(const int8_t ct) { + if (((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) || ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35)) { + LOG_INFO(F("Calibrating internal temperature to %d.%d"), ct / 10, ct < 0 ? -ct % 10 : ct % 10); + write_command(EMS_TYPE_IBASettings, 2, ct); + } +} + // 0xA5 - Set the display settings void Thermostat::set_settings_display(const uint8_t ds) { - LOG_INFO(F("Setting display to %d"), ds); - write_command(EMS_TYPE_IBASettings, 0, ds); + if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) { + LOG_INFO(F("Setting display to %d"), ds); + write_command(EMS_TYPE_IBASettings, 0, ds); + } } // 0xA5 - Set the building settings void Thermostat::set_settings_building(const uint8_t bg) { - LOG_INFO(F("Setting building to %d"), bg); - write_command(EMS_TYPE_IBASettings, 6, bg); + if (((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) || ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35)) { + LOG_INFO(F("Setting building to %d"), bg); + write_command(EMS_TYPE_IBASettings, 6, bg); + } } // 0xA5 Set the language settings void Thermostat::set_settings_language(const uint8_t lg) { - LOG_INFO(F("Setting building to %d"), lg); - write_command(EMS_TYPE_IBASettings, 1, lg); + if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) { + LOG_INFO(F("Setting building to %d"), lg); + write_command(EMS_TYPE_IBASettings, 1, lg); + } +} + +// Set the control-mode for hc 0-off, 1-RC20, 2-RC3x +void Thermostat::set_control(const uint8_t ctrl,const uint8_t hc_num) { + std::shared_ptr hc = heating_circuit(hc_num); + if (hc == nullptr) { + LOG_WARNING(F("set control: Heating Circuit %d not found or activated"), hc_num); + return; + } + if (ctrl > 2) { + LOG_WARNING(F("set control: Invalid control mode: %d"), ctrl); + return; + } + if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35 || (flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) { + LOG_INFO(F("Setting Circuit-control for hc%d to %d"), hc_num, ctrl); + write_command(set_typeids[hc->hc_num() - 1], 26, ctrl); + } } // sets the thermostat working mode, where mode is a string diff --git a/src/thermostat.h b/src/thermostat.h index 34c996682..65852f345 100644 --- a/src/thermostat.h +++ b/src/thermostat.h @@ -29,6 +29,7 @@ #include "emsesp.h" #include "helpers.h" #include "mqtt.h" +#include "roomcontrol.h" #include @@ -61,7 +62,6 @@ class Thermostat : public EMSdevice { uint8_t nofrosttemp = EMS_VALUE_UINT_NOTSET; uint8_t designtemp = EMS_VALUE_UINT_NOTSET; // heatingcurve design temp at MinExtTemp int8_t offsettemp = EMS_VALUE_INT_NOTSET; // heatingcurve offest temp at roomtemp signed! - uint16_t remotetemp = EMS_VALUE_SHORT_NOTSET; // for simulating a RC20 remote uint8_t hc_num() const { @@ -231,9 +231,13 @@ class Thermostat : public EMSdevice { void process_EasyMonitor(std::shared_ptr telegram); // set functions + void set_settings_minexttemp(const int8_t mt); + void set_settings_calinttemp(const int8_t ct); + void set_settings_clockoffset(const int8_t co); void set_settings_display(const uint8_t ds); void set_settings_building(const uint8_t bg); void set_settings_language(const uint8_t lg); + void set_control(const uint8_t ctrl, const uint8_t hc_num); void set_mode(const uint8_t mode, const uint8_t hc_num); void set_mode(const std::string & mode, const uint8_t hc_num); void set_temperature(const float temperature, const std::string & mode, const uint8_t hc_num);