From 87ce1a6d9bf8510f1c4a750ad3a7114b340347b9 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 8 Oct 2023 12:06:06 +0200 Subject: [PATCH] add remotetemp to RC3xx --- CHANGELOG_LATEST.md | 1 + src/devices/thermostat.cpp | 55 +++++++++++++- src/devices/thermostat.h | 2 + src/roomcontrol.cpp | 152 +++++++++++++++++++++++++++++++------ src/roomcontrol.h | 13 +++- src/version.h | 2 +- 6 files changed, 195 insertions(+), 30 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index cc8ecfb8d..1db1716b9 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -10,6 +10,7 @@ - telegrams for RC100H, hc2 (seen on discord) - names for BC400, GB192i, read temperatures for low loss header and heatblock [#1317](https://github.com/emsesp/EMS-ESP32/discussions/1317) - option for `forceheatingoff` [#1262](https://github.com/emsesp/EMS-ESP32/issues/1262) +- remote thermostat emulation for RC3xx ## Fixed diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index e96637353..98d537ee7 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -1752,11 +1752,42 @@ bool Thermostat::set_remotetemp(const char * value, const int8_t id) { hc->remotetemp = (int16_t)(f * 10); } - Roomctrl::set_remotetemp(hc->hc(), hc->remotetemp); + if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) { + Roomctrl::set_remotetemp(Roomctrl::FB10, hc->hc(), hc->remotetemp); // FB10 + } else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC35 || model() == EMSdevice::EMS_DEVICE_FLAG_RC30_N) { + Roomctrl::set_remotetemp(Roomctrl::RC20, hc->hc(), hc->remotetemp); // RC20 + } else if (model() == EMSdevice::EMS_DEVICE_FLAG_RC300) { + Roomctrl::set_remotetemp(Roomctrl::RC100H, hc->hc(), hc->remotetemp); // RC100H + } return true; } +bool Thermostat::set_remotehum(const char * value, const int8_t id) { + float f; + if (!Helpers::value2float(value, f)) { + return false; + } + + uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id; + std::shared_ptr hc = heating_circuit(hc_num); + if (hc == nullptr) { + return false; + } + + if (f > 100 || f < 0) { + hc->remotehum = EMS_VALUE_SHORT_NOTSET; + } else { + hc->remotehum = (int16_t)(f * 10); + } + + if (model() == EMSdevice::EMS_DEVICE_FLAG_RC300) { + Roomctrl::set_remotehum(Roomctrl::RC100H, hc->hc(), hc->remotehum); // RC100H + return true; + } + return false; +} + // 0xA5/0xA7 - Set the building settings bool Thermostat::set_building(const char * value, const int8_t id) { uint8_t bd; @@ -4211,6 +4242,24 @@ void Thermostat::register_device_values_hc(std::shared_ptrdewoffset, DeviceValueType::UINT, FL_(dewoffset), DeviceValueUOM::K, MAKE_CF_CB(set_dewoffset), 2, 10); register_device_value(tag, &hc->roomtempdiff, DeviceValueType::UINT, FL_(roomtempdiff), DeviceValueUOM::K, MAKE_CF_CB(set_roomtempdiff)); register_device_value(tag, &hc->hpminflowtemp, DeviceValueType::UINT, FL_(hpminflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_hpminflowtemp)); + register_device_value(tag, + &hc->remotetemp, + DeviceValueType::SHORT, + DeviceValueNumOp::DV_NUMOP_DIV10, + FL_(remotetemp), + DeviceValueUOM::DEGREES, + MAKE_CF_CB(set_remotetemp), + -1, + 101); + register_device_value(tag, + &hc->remotehum, + DeviceValueType::SHORT, + DeviceValueNumOp::DV_NUMOP_DIV10, + FL_(remotehum), + DeviceValueUOM::DEGREES, + MAKE_CF_CB(set_remotehum), + -1, + 101); break; case EMS_DEVICE_FLAG_CRF: @@ -4389,7 +4438,9 @@ void Thermostat::register_device_values_hc(std::shared_ptrwwprio, DeviceValueType::BOOL, FL_(wwprio), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwprio)); register_device_value( tag, &hc->switchtime1, DeviceValueType::STRING, FL_(tpl_switchtime), FL_(switchtime1), DeviceValueUOM::NONE, MAKE_CF_CB(set_switchtime1)); diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index 47c552ebf..0c27b2e67 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -37,6 +37,7 @@ class Thermostat : public EMSdevice { int16_t selTemp; int16_t roomTemp; int16_t remotetemp; // for readback + int16_t remotehum; // for readback uint8_t tempautotemp; int8_t remoteseltemp; uint8_t mode; @@ -426,6 +427,7 @@ class Thermostat : public EMSdevice { bool set_vacreducemode(const char * value, const int8_t id); bool set_nofrostmode(const char * value, const int8_t id); bool set_remotetemp(const char * value, const int8_t id); + bool set_remotehum(const char * value, const int8_t id); bool set_roominfl_factor(const char * value, const int8_t id); bool set_reducemode(const char * value, const int8_t id); bool set_switchtime1(const char * value, const int8_t id); diff --git a/src/roomcontrol.cpp b/src/roomcontrol.cpp index 331082561..61c8678a5 100644 --- a/src/roomcontrol.cpp +++ b/src/roomcontrol.cpp @@ -24,26 +24,62 @@ namespace emsesp { bool Roomctrl::switch_off_[HCS] = {false, false, false, false}; uint32_t Roomctrl::rc_time_[HCS] = {0, 0, 0, 0}; int16_t Roomctrl::remotetemp_[HCS] = {EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET}; +int16_t Roomctrl::remotehum_[HCS] = {EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET}; +uint8_t Roomctrl::sendcnt[] = {0, 0, 0, 0}; +uint8_t Roomctrl::type_ = RC20; /** * set the temperature, */ -void Roomctrl::set_remotetemp(const uint8_t hc, const int16_t temp) { - if (hc >= HCS) { +void Roomctrl::set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp) { + if (hc >= HCS || (type != RC20 && type != FB10 && type != RC100H && type != SENSOR)) { return; } + type_ = type; if (remotetemp_[hc] != EMS_VALUE_SHORT_NOTSET && temp == EMS_VALUE_SHORT_NOTSET) { switch_off_[hc] = true; } + if (remotetemp_[hc] != temp) { + rc_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now + sendcnt[hc] = 0; + } remotetemp_[hc] = temp; } +void Roomctrl::set_remotehum(const uint8_t type, const uint8_t hc, const int16_t hum) { + if (hc >= HCS || (type != RC20 && type != FB10 && type != RC100H && type != SENSOR)) { + return; + } + type_ = type; + if (remotehum_[hc] != EMS_VALUE_SHORT_NOTSET && hum == EMS_VALUE_SHORT_NOTSET) { + switch_off_[hc] = true; + } + if (remotehum_[hc] != hum) { + rc_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now + sendcnt[hc] = 1; + } + remotehum_[hc] = hum; +} + +uint8_t Roomctrl::get_hc(uint8_t addr) { + switch (type_) { + case SENSOR: + return addr - 0x40; + case RC100H: + return addr - 0x38; + case FB10: + case RC20: + default: + return addr - 0x18; + } +} + /** * if remote control is active send the temperature every minute */ void Roomctrl::send(const uint8_t addr) { - uint8_t hc = addr - ADDR; - // check address, reply only on addresses 0x18..0x1B + uint8_t hc = get_hc(addr); + // check address, reply only on addresses 0x18..0x1B or 0x40..0x43 if (hc >= HCS) { return; } @@ -53,9 +89,26 @@ void Roomctrl::send(const uint8_t addr) { } if (uuid::get_uptime() - rc_time_[hc] > SEND_INTERVAL) { // send every minute - rc_time_[hc] = uuid::get_uptime(); // use EMS-ESP's millis() to prevent overhead - temperature(addr, 0x00); // send to all - switch_off_[hc] = false; + if (type_ == FB10 || type_ == RC100H) { + if (sendcnt[hc] == 1) { + rc_time_[hc] = uuid::get_uptime(); + humidity(addr, 0x10, hc); + sendcnt[hc] = 0; + } else { + if (remotehum_[hc] != EMS_VALUE_SHORT_NOTSET) { + sendcnt[hc] = 1; + } else { + rc_time_[hc] = uuid::get_uptime(); + } + temperature(addr, 0x10, hc); // send to master-thermostat (https://github.com/emsesp/EMS-ESP32/issues/336) + } + } else { + rc_time_[hc] = uuid::get_uptime(); + temperature(addr, 0x00, hc); // send to all + } + if (remotehum_[hc] == EMS_VALUE_SHORT_NOTSET) { + switch_off_[hc] = false; + } } else { // acknowledge every poll, otherwise the master shows error A22-816 EMSuart::send_poll(addr); @@ -66,7 +119,7 @@ void Roomctrl::send(const uint8_t addr) { * check if there is a message for the remote room controller */ void Roomctrl::check(const uint8_t addr, const uint8_t * data) { - uint8_t hc = (addr & 0x7F) - ADDR; + uint8_t hc = get_hc(addr & 0x7F); // check address, reply only on addresses 0x18..0x1B if (hc >= HCS) { @@ -83,12 +136,18 @@ void Roomctrl::check(const uint8_t addr, const uint8_t * data) { } // reads: for now we only reply to version and remote temperature // empty message back if temperature not set or unknown message type - if (data[2] == 0x02) { + if (data[2] == EMSdevice::EMS_TYPE_VERSION) { version(addr, data[0]); } else if (remotetemp_[hc] == EMS_VALUE_SHORT_NOTSET) { unknown(addr, data[0], data[2], data[3]); } else if (data[2] == 0xAF && data[3] == 0) { - temperature(addr, data[0]); + temperature(addr, data[0], hc); + } else if (data[2] == 0xFF && data[3] == 0 && data[5] == 0 && data[6] == 0x23) { // Junkers + temperature(addr, data[0], hc); + } else if (data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x2B) { // EMS+ temperature + temperature(addr, data[0], hc); + } else if (data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x7B && remotehum_[hc] != EMS_VALUE_SHORT_NOTSET) { // EMS+ humidity + humidity(addr, data[0], hc); } else { unknown(addr, data[0], data[2], data[3]); } @@ -103,9 +162,9 @@ void Roomctrl::version(uint8_t addr, uint8_t dst) { 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[4] = type_; // set RC20 id 113, Ver 02.01 or Junkers FB10 id 109, Ver 16.05, RC100H id 200 ver 40.04 + data[5] = type_ == RC20 ? 2 : type_ == FB10 ? 16 : 40; + data[6] = type_ == RC20 ? 1 : type_ == FB10 ? 5 : 4; data[7] = EMSbus::calculate_crc(data, 7); // apppend CRC EMSuart::transmit(data, 8); } @@ -126,19 +185,64 @@ void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset) /** * send the room temperature in message 0xAF */ -void Roomctrl::temperature(uint8_t addr, uint8_t dst) { +void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) { 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); + data[0] = addr; + data[1] = dst; + if (type_ == RC20) { // RC20, telegram 0xAF + 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); + } else if (type_ == FB10) { // Junkers FB10, telegram 0x0123 + data[2] = 0xFF; + data[3] = 0; + data[4] = 0; + data[5] = 0x23; // is this always 0x23 or count with hc? + data[6] = (uint8_t)(remotetemp_[hc] >> 8); + data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); + data[8] = EMSbus::calculate_crc(data, 8); // apppend CRC + EMSuart::transmit(data, 9); + } else if (type_ == RC100H) { // RC100H, telegram 42B + data[2] = 0xFF; + data[3] = 0; + data[4] = 3; + data[5] = 0x2B; + data[6] = (uint8_t)(remotetemp_[hc] >> 8); + data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); + data[8] = EMSbus::calculate_crc(data, 8); // apppend CRC + EMSuart::transmit(data, 9); + } else if (type_ == SENSOR) { // wireless sensor, broadcast id 435 + data[2] = 0xFF; + data[3] = 0; + data[4] = 3; + data[5] = 0x35; + data[6] = (uint8_t)(remotetemp_[hc] >> 8); + data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); + data[8] = EMSbus::calculate_crc(data, 8); // apppend CRC + EMSuart::transmit(data, 9); + } } + +void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) { + uint8_t data[10]; + data[0] = addr; + data[1] = dst; + if (type_ == RC100H) { // RC100H, telegram 47B + data[2] = 0xFF; + data[3] = 0; + data[4] = 3; + data[5] = 0x7B; + data[6] = (uint8_t)(remotehum_[hc] >> 8); + data[7] = (uint8_t)(remotehum_[hc] & 0xFF); + data[8] = EMSbus::calculate_crc(data, 8); // apppend CRC + EMSuart::transmit(data, 9); + } +} + /** * send a nack if someone want to write to us. */ diff --git a/src/roomcontrol.h b/src/roomcontrol.h index 838c65cbb..9eeedba56 100644 --- a/src/roomcontrol.h +++ b/src/roomcontrol.h @@ -26,21 +26,28 @@ class Roomctrl { public: static void send(const uint8_t addr); static void check(const uint8_t addr, const uint8_t * data); - static void set_remotetemp(const uint8_t hc, const int16_t temp); + static void set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp); + static void set_remotehum(const uint8_t type, const uint8_t hc, const int16_t hum); + enum : uint8_t { RC20 = 113, FB10 = 109, RC100H = 200, SENSOR = 0x40 }; private: - static constexpr uint8_t ADDR = 0x18; + static constexpr uint8_t ADDR = 0x18; // address for hc1 static constexpr uint32_t SEND_INTERVAL = 60000; // 1 minute static constexpr uint8_t HCS = 4; // max 4 heating circuits + static uint8_t get_hc(const uint8_t addr); 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); + static void temperature(uint8_t addr, uint8_t dst, uint8_t hc); + static void humidity(uint8_t addr, uint8_t dst, uint8_t hc); static void nack_write(); static bool switch_off_[HCS]; static uint32_t rc_time_[HCS]; static int16_t remotetemp_[HCS]; + static int16_t remotehum_[HCS]; + static uint8_t sendcnt[HCS]; + static uint8_t type_; // type is product-id 113 for RC20 or 109 for Junkers FB10 }; } // namespace emsesp diff --git a/src/version.h b/src/version.h index 80fdaf1bb..3dc626a20 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.6.3-dev.1" +#define EMSESP_APP_VERSION "3.6.3-dev.2a"