From 2a6b436b2ef5657d02df3d3869badb45e76a839f Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 25 Jun 2026 21:23:49 +0200 Subject: [PATCH] fix: negative dew point calculation (emulated RC100H) #3134 --- src/core/roomcontrol.cpp | 870 ++++++++++++++++++++------------------- 1 file changed, 437 insertions(+), 433 deletions(-) diff --git a/src/core/roomcontrol.cpp b/src/core/roomcontrol.cpp index a2c0c5098..2eb59fbbd 100644 --- a/src/core/roomcontrol.cpp +++ b/src/core/roomcontrol.cpp @@ -16,437 +16,441 @@ * along with this program. If not, see . */ -#include "roomcontrol.h" + #include "roomcontrol.h" -namespace emsesp { - -// init statics -bool Roomctrl::switch_off_[HCS] = {false, false, false, false}; -uint32_t Roomctrl::send_time_[HCS] = {0, 0, 0, 0}; -uint32_t Roomctrl::receive_time_[HCS] = {0, 0, 0, 0}; -int16_t Roomctrl::remotetemp_[HCS] = {EMS_VALUE_INT16_NOTSET, EMS_VALUE_INT16_NOTSET, EMS_VALUE_INT16_NOTSET, EMS_VALUE_INT16_NOTSET}; -uint8_t Roomctrl::remotehum_[HCS] = {EMS_VALUE_UINT8_NOTSET, EMS_VALUE_UINT8_NOTSET, EMS_VALUE_UINT8_NOTSET, EMS_VALUE_UINT8_NOTSET}; -uint8_t Roomctrl::sendtype_[HCS] = {SendType::TEMP, SendType::TEMP, SendType::TEMP, SendType::TEMP}; -uint8_t Roomctrl::type_[HCS] = {RemoteType::NONE, RemoteType::NONE, RemoteType::NONE, RemoteType::NONE}; -uint32_t Roomctrl::timeout_ = 0; - -/** - * set the temperature, - */ -void Roomctrl::set_timeout(uint8_t t) { - timeout_ = t * 3600000; // ms -} -void Roomctrl::set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp) { - if (!type_[hc] && !type) { - return; - } - if (remotetemp_[hc] != EMS_VALUE_INT16_NOTSET && temp == EMS_VALUE_INT16_NOTSET) { // switch remote off - remotetemp_[hc] = EMS_VALUE_INT16_NOTSET; - switch_off_[hc] = true; - send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now - sendtype_[hc] = SendType::TEMP; - return; - } - if (hc >= HCS || !type) { - return; - } - if (remotetemp_[hc] != temp) { - send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now - sendtype_[hc] = SendType::TEMP; - } - type_[hc] = type; - remotetemp_[hc] = temp; - receive_time_[hc] = uuid::get_uptime(); -} - -// set humidity for RC100H emulation -void Roomctrl::set_remotehum(const uint8_t type, const uint8_t hc, const int8_t hum) { - if (hc >= HCS || type != type_[hc]) { - return; - } - if (remotehum_[hc] != hum) { - send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now - sendtype_[hc] = SendType::HUMI; - } - remotehum_[hc] = hum; -} - -uint8_t Roomctrl::get_hc(uint8_t addr) { - addr &= 0x7F; - if (addr >= 0x40 && addr <= 0x44 && type_[addr - 0x40] == SENSOR) { - return addr - 0x40; // SENSOR - } else if (addr >= 0x38 && addr <= 0x3B - && (type_[addr - 0x38] == RC100H || type_[addr - 0x38] == RC200 || type_[addr - 0x38] == RC100 || type_[addr - 0x38] == RT800 - || type_[addr - 0x38] == RC120RF)) { - return addr - 0x38; // RC100H, RC200 - } else if (addr >= 0x18 && addr <= 0x1B && (type_[addr - 0x18] == RC20 || type_[addr - 0x18] == FB10)) { - return addr - 0x18; // RC20, FB10 - } - return 0xFF; // invalid -} - -/** - * if remote control is active send the temperature every 15 seconds - */ -void Roomctrl::send(uint8_t addr) { - if ((addr & 0x80) || EMSESP::system_.readonly_mode()) { - return; - } - uint8_t hc = get_hc(addr); - // check address, reply only on addresses 0x18..0x1B or 0x40..0x43 - if (hc >= HCS) { - return; - } - // no reply if the temperature is not set - if (!switch_off_[hc] && remotetemp_[hc] == EMS_VALUE_INT16_NOTSET && remotehum_[hc] == EMS_VALUE_UINT8_NOTSET) { - return; - } - - if (!switch_off_[hc] && timeout_ && (uuid::get_uptime() - receive_time_[hc]) > timeout_) { - remotetemp_[hc] = EMS_VALUE_INT16_NOTSET; - switch_off_[hc] = true; - sendtype_[hc] = SendType::TEMP; - EMSESP::logger().warning("remotetemp timeout hc%d, stop sending roomtemperature to thermostat", hc); - } - if (switch_off_[hc] || (uuid::get_uptime() - send_time_[hc]) > SEND_INTERVAL) { // check interval - if (type_[hc] == RC100H || type_[hc] == RT800 || type_[hc] == RC120RF) { - if (sendtype_[hc] == SendType::HUMI) { // send humidity - if (switch_off_[hc]) { - remotehum_[hc] = EMS_VALUE_UINT8_NOTSET; - } - send_time_[hc] = uuid::get_uptime(); - humidity(addr, 0x10, hc); - sendtype_[hc] = SendType::TEMP; - } else { // temperature telegram - if (remotehum_[hc] != EMS_VALUE_UINT8_NOTSET) { - sendtype_[hc] = SendType::HUMI; - } else { - send_time_[hc] = uuid::get_uptime(); - } - temperature(addr, 0x10, hc); // send to master-thermostat - } - } else if (type_[hc] == RC200 || type_[hc] == RC100 || type_[hc] == FB10) { - send_time_[hc] = uuid::get_uptime(); - temperature(addr, 0x10, hc); // send to master-thermostat (https://github.com/emsesp/EMS-ESP32/issues/336) - } else { // type==RC20 or SENSOR - send_time_[hc] = uuid::get_uptime(); - temperature(addr, 0x00, hc); // send to all - } - if (remotehum_[hc] == EMS_VALUE_UINT8_NOTSET && switch_off_[hc]) { - switch_off_[hc] = false; - type_[hc] = RemoteType::NONE; - } - } else { - // acknowledge every poll - EMSuart::send_poll(addr | EMSbus::ems_mask()); - } -} - -/** - * check if there is a message for the remote room controller - */ -void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) { - if (EMSESP::system_.readonly_mode()) { - return; - } - uint8_t hc = get_hc(addr); - if (hc >= HCS || length < 5) { - return; - } - if (type_[hc] == SENSOR) { - return; - } - // no reply if the temperature is not set - if (remotetemp_[hc] == EMS_VALUE_INT16_NOTSET) { - return; - } - // reply to writes with write nack byte - if ((addr & 0x80) == 0) { // it's a write to us - ack_write(); // accept writes, don't care. - return; - } - addr &= 0x7F; - // 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] == EMSdevice::EMS_TYPE_VERSION) { - version(addr, data[0], hc); - } else if (data[2] == 0xAF && data[3] == 0) { - temperature(addr, data[0], hc); - } else if (length == 6) { // all other ems queries - unknown(addr, data[0], data[2], data[3]); - } else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 0 && data[6] == 0x23) { // Junkers - temperature(addr, data[0], hc); - } else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x2B + hc) { // EMS+ temperature - temperature(addr, data[0], hc); - } else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x7B + hc && remotehum_[hc] != EMS_VALUE_UINT8_NOTSET) { // EMS+ humidity - humidity(addr, data[0], hc); - } else if (length == 8 && data[2] == 0xFF) { // ems+ query - unknown(addr, data[0], data[3], data[5], data[6]); - } else if (data[2] == 0xF7) { // ems+ query with 3 bytes type src dst 7F offset len=FF FF HIGH LOW - replyF7(addr, data[0], data[3], data[5], data[6], data[7], hc); - } else if (length == 8) { - unknown(addr, data[0], data[3], data[5], data[6]); - } -} - -/** - * send version info - */ -void Roomctrl::version(uint8_t addr, uint8_t dst, uint8_t hc) { - uint8_t data[20]; - data[0] = addr | EMSbus::ems_mask(); - data[1] = dst & 0x7F; - data[2] = 0x02; - data[3] = 0; - data[4] = type_[hc]; // set RC20 id 113, Ver 02.01 or Junkers FB10 id 109, Ver 16.05, RC100H id 200 ver 40.04 - if (type_[hc] == RC20) { - data[5] = 2; // version 2.01 - data[6] = 1; - data[7] = EMSbus::calculate_crc(data, 7); // append CRC - EMSuart::transmit(data, 8); - return; - } else if (type_[hc] == FB10) { - data[5] = 16; // version 16.05 - data[6] = 5; - data[7] = 0; - data[8] = 0; - data[9] = 0; - data[10] = 0; - data[11] = 0; - data[12] = 0; - data[13] = 0; - data[14] = EMSbus::calculate_crc(data, 14); // append CRC - EMSuart::transmit(data, 15); - return; - } else if (type_[hc] == RC200) { - data[5] = 32; // version 32.02 see #1611 - data[6] = 2; - data[7] = 0; - data[8] = 0xFF; - data[9] = 0; - data[10] = 0; - data[11] = 0; - data[12] = 0; - data[13] = 0; - data[14] = EMSbus::calculate_crc(data, 14); // append CRC - EMSuart::transmit(data, 15); - return; - } else if (type_[hc] == RC100H) { - data[5] = 40; // version 40.04 - data[6] = 4; - data[7] = 0; - data[8] = 0xFF; - data[9] = EMSbus::calculate_crc(data, 9); // append CRC - EMSuart::transmit(data, 10); - return; - } else if (type_[hc] == RC100) { - data[5] = 40; // version 40.03 - data[6] = 3; - data[7] = 0; - data[8] = 0xFF; - data[9] = EMSbus::calculate_crc(data, 9); // append CRC - EMSuart::transmit(data, 10); - return; - } else if (type_[hc] == RT800) { - data[5] = 21; // version 21.03 - data[6] = 3; - data[7] = EMSbus::calculate_crc(data, 7); // append CRC - EMSuart::transmit(data, 8); - return; - } else if (type_[hc] == RC120RF) { - data[5] = 16; // version 16.02 - data[6] = 2; - data[7] = 0; - data[8] = 0xFF; - data[9] = EMSbus::calculate_crc(data, 9); // append CRC - EMSuart::transmit(data, 10); - return; - } -} - -/** - * 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 | EMSbus::ems_mask(); - data[1] = dst & 0x7F; - data[2] = type; - data[3] = offset; - data[4] = EMSbus::calculate_crc(data, 4); // append CRC - EMSuart::transmit(data, 5); -} - -void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typeh, uint8_t typel) { - uint8_t data[10]; - data[0] = addr | EMSbus::ems_mask(); - data[1] = dst & 0x7F; - data[2] = 0xFF; - data[3] = offset; - data[4] = typeh; - data[5] = typel; - data[6] = EMSbus::calculate_crc(data, 6); // append CRC - EMSuart::transmit(data, 7); -} - -/** - * send the room temperature in message 0xAF - */ -void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) { - uint8_t data[14]; - data[0] = addr | EMSbus::ems_mask(); - data[1] = dst & 0x7F; - if (type_[hc] == 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); // append CRC - EMSuart::transmit(data, 8); - } else if (type_[hc] == FB10) { // Junkers FB10, telegram 0x0123 - data[2] = 0xFF; - data[3] = 0; - data[4] = 0; - data[5] = 0x23; // fixed for all hc - data[6] = (uint8_t)(remotetemp_[hc] >> 8); - data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); - data[8] = EMSbus::calculate_crc(data, 8); // append CRC - EMSuart::transmit(data, 9); - } else if (type_[hc] == RC200) { // RC200, telegram 42B, ff - data[2] = 0xFF; - data[3] = 0; - data[4] = 3; - data[5] = 0x2B + hc; - data[6] = (uint8_t)(remotetemp_[hc] >> 8); - data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); - uint16_t t1 = remotetemp_[hc] * 10; - data[8] = (uint8_t)(t1 >> 8); - data[9] = (uint8_t)(t1 & 0xFF); - data[10] = 1; // not sure what this is and if we need it, maybe mode? - data[11] = EMSbus::calculate_crc(data, 11); // append CRC - EMSuart::transmit(data, 12); - } else if (type_[hc] == RC100H || type_[hc] == RC100) { // RC100H, telegram 42B, ff - data[2] = 0xFF; - data[3] = 0; - data[4] = 3; - data[5] = 0x2B + hc; - data[6] = (uint8_t)(remotetemp_[hc] >> 8); - data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); - data[8] = EMSbus::calculate_crc(data, 8); // append CRC - EMSuart::transmit(data, 9); - } else if (type_[hc] == SENSOR) { // wireless sensor, broadcast id 435 - data[2] = 0xFF; - data[3] = 0; - data[4] = 3; - data[5] = 0x35 + hc; - data[6] = (uint8_t)(remotetemp_[hc] >> 8); - data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); - data[8] = EMSbus::calculate_crc(data, 8); // append CRC - EMSuart::transmit(data, 9); - } else if (type_[hc] == RT800) { // RC200, telegram 42B, ff - data[2] = 0xFF; - data[3] = 0; - data[4] = 3; - data[5] = 0x2B + hc; - data[6] = (uint8_t)(remotetemp_[hc] >> 8); - data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); - uint16_t t1 = remotetemp_[hc] * 10; - data[8] = (uint8_t)(t1 >> 8); - data[9] = (uint8_t)(t1 & 0xFF); - data[10] = 1; // not sure what this is and if we need it, maybe mode? - data[11] = 9; // not sure what this is and if we need it, maybe mode? - data[12] = EMSbus::calculate_crc(data, 12); // append CRC - EMSuart::transmit(data, 13); - } else if (type_[hc] == RC120RF) { - data[2] = 0xFF; - data[3] = 0; - data[4] = 3; - data[5] = 0x2B + hc; - data[6] = (uint8_t)(remotetemp_[hc] >> 8); - data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); - uint16_t t1 = remotetemp_[hc] * 10; - data[8] = (uint8_t)(t1 >> 8); - data[9] = (uint8_t)(t1 & 0xFF); - data[10] = 0; // not sure what this is and if we need it - data[11] = 0; // not sure what this is and if we need it - data[12] = EMSbus::calculate_crc(data, 12); // append CRC - EMSuart::transmit(data, 13); - } -} - -// send telegram 0x047B only for RC100H -void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) { - uint8_t data[11]; - data[0] = addr | EMSbus::ems_mask(); - data[1] = dst & 0x7F; - uint16_t dew = calc_dew(remotetemp_[hc], remotehum_[hc]); - data[2] = 0xFF; - data[3] = 0; - data[4] = 3; - data[5] = 0x7B + hc; - data[6] = dew == EMS_VALUE_INT16_NOTSET ? EMS_VALUE_INT8_NOTSET : (uint8_t)((dew + 5) / 10); - data[7] = remotehum_[hc]; - data[8] = (uint8_t)(dew << 8); - data[9] = (uint8_t)(dew & 0xFF); - data[10] = EMSbus::calculate_crc(data, 10); // append CRC - EMSuart::transmit(data, 11); -} - -/** - * send a nack if someone want to write to us. - */ -void Roomctrl::nack_write() { - uint8_t data[1]; - data[0] = TxService::TX_WRITE_FAIL; - EMSuart::transmit(data, 1); -} - -/** - * send a ack if someone want to write to us. - */ -void Roomctrl::ack_write() { - uint8_t data[1]; - data[0] = TxService::TX_WRITE_SUCCESS; - EMSuart::transmit(data, 1); -} - -void Roomctrl::replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh, uint8_t typeh, uint8_t typel, uint8_t hc) { - uint8_t data[12]; - data[0] = addr | EMSbus::ems_mask(); - data[1] = dst & 0x7F; - data[2] = 0xF7; - data[3] = offset; - data[4] = typehh; - data[5] = typeh; - data[6] = typel; - if (typehh == 0x02) { - if (type_[hc] == RC200 || type_[hc] == FB10) { - data[7] = 0xFF; - data[8] = 0x01; - } else { - data[7] = 0x0F; - data[8] = 0x00; - } - } else { - data[7] = 0; - data[8] = 0; - } - data[9] = EMSbus::calculate_crc(data, 9); // append CRC - EMSuart::transmit(data, 10); -} - -int16_t Roomctrl::calc_dew(int16_t temp, uint8_t humi) { - if (humi == EMS_VALUE_UINT8_NOTSET || temp == EMS_VALUE_INT16_NOTSET) { - return EMS_VALUE_INT16_NOTSET; - } - const float k2 = 17.62; - const float k3 = 243.12; - const float t = (float)temp / 10; - const float h = (float)humi / 100; - int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h))); - return dt; -} - - -} // namespace emsesp + namespace emsesp { + + // init statics + bool Roomctrl::switch_off_[HCS] = {false, false, false, false}; + uint32_t Roomctrl::send_time_[HCS] = {0, 0, 0, 0}; + uint32_t Roomctrl::receive_time_[HCS] = {0, 0, 0, 0}; + int16_t Roomctrl::remotetemp_[HCS] = {EMS_VALUE_INT16_NOTSET, EMS_VALUE_INT16_NOTSET, EMS_VALUE_INT16_NOTSET, EMS_VALUE_INT16_NOTSET}; + uint8_t Roomctrl::remotehum_[HCS] = {EMS_VALUE_UINT8_NOTSET, EMS_VALUE_UINT8_NOTSET, EMS_VALUE_UINT8_NOTSET, EMS_VALUE_UINT8_NOTSET}; + uint8_t Roomctrl::sendtype_[HCS] = {SendType::TEMP, SendType::TEMP, SendType::TEMP, SendType::TEMP}; + uint8_t Roomctrl::type_[HCS] = {RemoteType::NONE, RemoteType::NONE, RemoteType::NONE, RemoteType::NONE}; + uint32_t Roomctrl::timeout_ = 0; + + /** + * set the temperature, + */ + void Roomctrl::set_timeout(uint8_t t) { + timeout_ = t * 3600000; // ms + } + void Roomctrl::set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp) { + if (!type_[hc] && !type) { + return; + } + if (remotetemp_[hc] != EMS_VALUE_INT16_NOTSET && temp == EMS_VALUE_INT16_NOTSET) { // switch remote off + remotetemp_[hc] = EMS_VALUE_INT16_NOTSET; + switch_off_[hc] = true; + send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now + sendtype_[hc] = SendType::TEMP; + return; + } + if (hc >= HCS || !type) { + return; + } + if (remotetemp_[hc] != temp) { + send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now + sendtype_[hc] = SendType::TEMP; + } + type_[hc] = type; + remotetemp_[hc] = temp; + receive_time_[hc] = uuid::get_uptime(); + } + + // set humidity for RC100H emulation + void Roomctrl::set_remotehum(const uint8_t type, const uint8_t hc, const int8_t hum) { + if (hc >= HCS || type != type_[hc]) { + return; + } + if (remotehum_[hc] != hum) { + send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now + sendtype_[hc] = SendType::HUMI; + } + remotehum_[hc] = hum; + } + + uint8_t Roomctrl::get_hc(uint8_t addr) { + addr &= 0x7F; + if (addr >= 0x40 && addr <= 0x44 && type_[addr - 0x40] == SENSOR) { + return addr - 0x40; // SENSOR + } else if (addr >= 0x38 && addr <= 0x3B + && (type_[addr - 0x38] == RC100H || type_[addr - 0x38] == RC200 || type_[addr - 0x38] == RC100 || type_[addr - 0x38] == RT800 + || type_[addr - 0x38] == RC120RF)) { + return addr - 0x38; // RC100H, RC200 + } else if (addr >= 0x18 && addr <= 0x1B && (type_[addr - 0x18] == RC20 || type_[addr - 0x18] == FB10)) { + return addr - 0x18; // RC20, FB10 + } + return 0xFF; // invalid + } + + /** + * if remote control is active send the temperature every 15 seconds + */ + void Roomctrl::send(uint8_t addr) { + if ((addr & 0x80) || EMSESP::system_.readonly_mode()) { + return; + } + uint8_t hc = get_hc(addr); + // check address, reply only on addresses 0x18..0x1B or 0x40..0x43 + if (hc >= HCS) { + return; + } + // no reply if the temperature is not set + if (!switch_off_[hc] && remotetemp_[hc] == EMS_VALUE_INT16_NOTSET && remotehum_[hc] == EMS_VALUE_UINT8_NOTSET) { + return; + } + + if (!switch_off_[hc] && timeout_ && (uuid::get_uptime() - receive_time_[hc]) > timeout_) { + remotetemp_[hc] = EMS_VALUE_INT16_NOTSET; + switch_off_[hc] = true; + sendtype_[hc] = SendType::TEMP; + EMSESP::logger().warning("remotetemp timeout hc%d, stop sending roomtemperature to thermostat", hc); + } + if (switch_off_[hc] || (uuid::get_uptime() - send_time_[hc]) > SEND_INTERVAL) { // check interval + if (type_[hc] == RC100H || type_[hc] == RT800 || type_[hc] == RC120RF) { + if (sendtype_[hc] == SendType::HUMI) { // send humidity + if (switch_off_[hc]) { + remotehum_[hc] = EMS_VALUE_UINT8_NOTSET; + } + send_time_[hc] = uuid::get_uptime(); + humidity(addr, 0x10, hc); + sendtype_[hc] = SendType::TEMP; + } else { // temperature telegram + if (remotehum_[hc] != EMS_VALUE_UINT8_NOTSET) { + sendtype_[hc] = SendType::HUMI; + } else { + send_time_[hc] = uuid::get_uptime(); + } + temperature(addr, 0x10, hc); // send to master-thermostat + } + } else if (type_[hc] == RC200 || type_[hc] == RC100 || type_[hc] == FB10) { + send_time_[hc] = uuid::get_uptime(); + temperature(addr, 0x10, hc); // send to master-thermostat (https://github.com/emsesp/EMS-ESP32/issues/336) + } else { // type==RC20 or SENSOR + send_time_[hc] = uuid::get_uptime(); + temperature(addr, 0x00, hc); // send to all + } + if (remotehum_[hc] == EMS_VALUE_UINT8_NOTSET && switch_off_[hc]) { + switch_off_[hc] = false; + type_[hc] = RemoteType::NONE; + } + } else { + // acknowledge every poll + EMSuart::send_poll(addr | EMSbus::ems_mask()); + } + } + + /** + * check if there is a message for the remote room controller + */ + void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) { + if (EMSESP::system_.readonly_mode()) { + return; + } + uint8_t hc = get_hc(addr); + if (hc >= HCS || length < 5) { + return; + } + if (type_[hc] == SENSOR) { + return; + } + // no reply if the temperature is not set + if (remotetemp_[hc] == EMS_VALUE_INT16_NOTSET) { + return; + } + // reply to writes with write nack byte + if ((addr & 0x80) == 0) { // it's a write to us + ack_write(); // accept writes, don't care. + return; + } + addr &= 0x7F; + // 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] == EMSdevice::EMS_TYPE_VERSION) { + version(addr, data[0], hc); + } else if (data[2] == 0xAF && data[3] == 0) { + temperature(addr, data[0], hc); + } else if (length == 6) { // all other ems queries + unknown(addr, data[0], data[2], data[3]); + } else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 0 && data[6] == 0x23) { // Junkers + temperature(addr, data[0], hc); + } else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x2B + hc) { // EMS+ temperature + temperature(addr, data[0], hc); + } else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x7B + hc && remotehum_[hc] != EMS_VALUE_UINT8_NOTSET) { // EMS+ humidity + humidity(addr, data[0], hc); + } else if (length == 8 && data[2] == 0xFF) { // ems+ query + unknown(addr, data[0], data[3], data[5], data[6]); + } else if (data[2] == 0xF7) { // ems+ query with 3 bytes type src dst 7F offset len=FF FF HIGH LOW + replyF7(addr, data[0], data[3], data[5], data[6], data[7], hc); + } else if (length == 8) { + unknown(addr, data[0], data[3], data[5], data[6]); + } + } + + /** + * send version info + */ + void Roomctrl::version(uint8_t addr, uint8_t dst, uint8_t hc) { + uint8_t data[20]; + data[0] = addr | EMSbus::ems_mask(); + data[1] = dst & 0x7F; + data[2] = 0x02; + data[3] = 0; + data[4] = type_[hc]; // set RC20 id 113, Ver 02.01 or Junkers FB10 id 109, Ver 16.05, RC100H id 200 ver 40.04 + if (type_[hc] == RC20) { + data[5] = 2; // version 2.01 + data[6] = 1; + data[7] = EMSbus::calculate_crc(data, 7); // append CRC + EMSuart::transmit(data, 8); + return; + } else if (type_[hc] == FB10) { + data[5] = 16; // version 16.05 + data[6] = 5; + data[7] = 0; + data[8] = 0; + data[9] = 0; + data[10] = 0; + data[11] = 0; + data[12] = 0; + data[13] = 0; + data[14] = EMSbus::calculate_crc(data, 14); // append CRC + EMSuart::transmit(data, 15); + return; + } else if (type_[hc] == RC200) { + data[5] = 32; // version 32.02 see #1611 + data[6] = 2; + data[7] = 0; + data[8] = 0xFF; + data[9] = 0; + data[10] = 0; + data[11] = 0; + data[12] = 0; + data[13] = 0; + data[14] = EMSbus::calculate_crc(data, 14); // append CRC + EMSuart::transmit(data, 15); + return; + } else if (type_[hc] == RC100H) { + data[5] = 40; // version 40.04 + data[6] = 4; + data[7] = 0; + data[8] = 0xFF; + data[9] = EMSbus::calculate_crc(data, 9); // append CRC + EMSuart::transmit(data, 10); + return; + } else if (type_[hc] == RC100) { + data[5] = 40; // version 40.03 + data[6] = 3; + data[7] = 0; + data[8] = 0xFF; + data[9] = EMSbus::calculate_crc(data, 9); // append CRC + EMSuart::transmit(data, 10); + return; + } else if (type_[hc] == RT800) { + data[5] = 21; // version 21.03 + data[6] = 3; + data[7] = EMSbus::calculate_crc(data, 7); // append CRC + EMSuart::transmit(data, 8); + return; + } else if (type_[hc] == RC120RF) { + data[5] = 16; // version 16.02 + data[6] = 2; + data[7] = 0; + data[8] = 0xFF; + data[9] = EMSbus::calculate_crc(data, 9); // append CRC + EMSuart::transmit(data, 10); + return; + } + } + + /** + * 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 | EMSbus::ems_mask(); + data[1] = dst & 0x7F; + data[2] = type; + data[3] = offset; + data[4] = EMSbus::calculate_crc(data, 4); // append CRC + EMSuart::transmit(data, 5); + } + + void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typeh, uint8_t typel) { + uint8_t data[10]; + data[0] = addr | EMSbus::ems_mask(); + data[1] = dst & 0x7F; + data[2] = 0xFF; + data[3] = offset; + data[4] = typeh; + data[5] = typel; + data[6] = EMSbus::calculate_crc(data, 6); // append CRC + EMSuart::transmit(data, 7); + } + + /** + * send the room temperature in message 0xAF + */ + void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) { + uint8_t data[14]; + data[0] = addr | EMSbus::ems_mask(); + data[1] = dst & 0x7F; + if (type_[hc] == 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); // append CRC + EMSuart::transmit(data, 8); + } else if (type_[hc] == FB10) { // Junkers FB10, telegram 0x0123 + data[2] = 0xFF; + data[3] = 0; + data[4] = 0; + data[5] = 0x23; // fixed for all hc + data[6] = (uint8_t)(remotetemp_[hc] >> 8); + data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); + data[8] = EMSbus::calculate_crc(data, 8); // append CRC + EMSuart::transmit(data, 9); + } else if (type_[hc] == RC200) { // RC200, telegram 42B, ff + data[2] = 0xFF; + data[3] = 0; + data[4] = 3; + data[5] = 0x2B + hc; + data[6] = (uint8_t)(remotetemp_[hc] >> 8); + data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); + uint16_t t1 = remotetemp_[hc] * 10; + data[8] = (uint8_t)(t1 >> 8); + data[9] = (uint8_t)(t1 & 0xFF); + data[10] = 1; // not sure what this is and if we need it, maybe mode? + data[11] = EMSbus::calculate_crc(data, 11); // append CRC + EMSuart::transmit(data, 12); + } else if (type_[hc] == RC100H || type_[hc] == RC100) { // RC100H, telegram 42B, ff + data[2] = 0xFF; + data[3] = 0; + data[4] = 3; + data[5] = 0x2B + hc; + data[6] = (uint8_t)(remotetemp_[hc] >> 8); + data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); + data[8] = EMSbus::calculate_crc(data, 8); // append CRC + EMSuart::transmit(data, 9); + } else if (type_[hc] == SENSOR) { // wireless sensor, broadcast id 435 + data[2] = 0xFF; + data[3] = 0; + data[4] = 3; + data[5] = 0x35 + hc; + data[6] = (uint8_t)(remotetemp_[hc] >> 8); + data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); + data[8] = EMSbus::calculate_crc(data, 8); // append CRC + EMSuart::transmit(data, 9); + } else if (type_[hc] == RT800) { // RC200, telegram 42B, ff + data[2] = 0xFF; + data[3] = 0; + data[4] = 3; + data[5] = 0x2B + hc; + data[6] = (uint8_t)(remotetemp_[hc] >> 8); + data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); + uint16_t t1 = remotetemp_[hc] * 10; + data[8] = (uint8_t)(t1 >> 8); + data[9] = (uint8_t)(t1 & 0xFF); + data[10] = 1; // not sure what this is and if we need it, maybe mode? + data[11] = 9; // not sure what this is and if we need it, maybe mode? + data[12] = EMSbus::calculate_crc(data, 12); // append CRC + EMSuart::transmit(data, 13); + } else if (type_[hc] == RC120RF) { + data[2] = 0xFF; + data[3] = 0; + data[4] = 3; + data[5] = 0x2B + hc; + data[6] = (uint8_t)(remotetemp_[hc] >> 8); + data[7] = (uint8_t)(remotetemp_[hc] & 0xFF); + uint16_t t1 = remotetemp_[hc] * 10; + data[8] = (uint8_t)(t1 >> 8); + data[9] = (uint8_t)(t1 & 0xFF); + data[10] = 0; // not sure what this is and if we need it + data[11] = 0; // not sure what this is and if we need it + data[12] = EMSbus::calculate_crc(data, 12); // append CRC + EMSuart::transmit(data, 13); + } + } + + // send telegram 0x047B only for RC100H + void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) { + int16_t dew = calc_dew(remotetemp_[hc], remotehum_[hc]); + int8_t dew8 = EMS_VALUE_INT8_NOTSET; + if (dew != EMS_VALUE_INT16_NOTSET) { + dew8 = static_cast((dew >= 0 ? dew + 5 : dew - 5) / 10); + } + uint8_t data[11]; + data[0] = addr | EMSbus::ems_mask(); + data[1] = dst & 0x7F; + data[2] = 0xFF; + data[3] = 0; + data[4] = 3; + data[5] = 0x7B + hc; + data[6] = static_cast(dew8); + data[7] = remotehum_[hc]; + data[8] = static_cast(static_cast(dew) >> 8); + data[9] = static_cast(static_cast(dew) & 0xFF); + data[10] = EMSbus::calculate_crc(data, 10); // append CRC + EMSuart::transmit(data, 11); + } + + /** + * send a nack if someone want to write to us. + */ + void Roomctrl::nack_write() { + uint8_t data[1]; + data[0] = TxService::TX_WRITE_FAIL; + EMSuart::transmit(data, 1); + } + + /** + * send a ack if someone want to write to us. + */ + void Roomctrl::ack_write() { + uint8_t data[1]; + data[0] = TxService::TX_WRITE_SUCCESS; + EMSuart::transmit(data, 1); + } + + void Roomctrl::replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh, uint8_t typeh, uint8_t typel, uint8_t hc) { + uint8_t data[12]; + data[0] = addr | EMSbus::ems_mask(); + data[1] = dst & 0x7F; + data[2] = 0xF7; + data[3] = offset; + data[4] = typehh; + data[5] = typeh; + data[6] = typel; + if (typehh == 0x02) { + if (type_[hc] == RC200 || type_[hc] == FB10) { + data[7] = 0xFF; + data[8] = 0x01; + } else { + data[7] = 0x0F; + data[8] = 0x00; + } + } else { + data[7] = 0; + data[8] = 0; + } + data[9] = EMSbus::calculate_crc(data, 9); // append CRC + EMSuart::transmit(data, 10); + } + + int16_t Roomctrl::calc_dew(int16_t temp, uint8_t humi) { + if (humi == EMS_VALUE_UINT8_NOTSET || temp == EMS_VALUE_INT16_NOTSET) { + return EMS_VALUE_INT16_NOTSET; + } + const float k2 = 17.62; + const float k3 = 243.12; + const float t = (float)temp / 10; + const float h = (float)humi / 100; + int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h))); + return dt; + } + + + } // namespace emsesp \ No newline at end of file