From 2beb5cd5f3c23051ab7c3907cea3283ede160973 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 7 Sep 2019 18:59:32 +0200 Subject: [PATCH] support for multiple HCs - https://github.com/proddy/EMS-ESP/issues/162 --- CHANGELOG.md | 10 ++ src/ems-esp.cpp | 401 +++++++++++++++++++++++----------------------- src/ems.cpp | 300 +++++++++++++++++++++------------- src/ems.h | 101 ++++++------ src/ems_devices.h | 6 + 5 files changed, 467 insertions(+), 351 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 344699e4..6aa93839 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.9.1 beta] 2019-09-07 + +### Added + +- Support for multiple Heating Circuits (RC35 only for now and writing via telnet) - https://github.com/proddy/EMS-ESP/issues/162 + +### Removed + +- Removed `heating_circuit` parameter + ## [1.9.0] 2019-09-01 ### Changed diff --git a/src/ems-esp.cpp b/src/ems-esp.cpp index 8abc6186..8bf26f05 100644 --- a/src/ems-esp.cpp +++ b/src/ems-esp.cpp @@ -39,8 +39,6 @@ DS18 ds18; // set to value >0 if the ESP is overheating or there are timing issues. Recommend a value of 1. #define EMSESP_DELAY 0 // initially set to 0 for no delay. Change to 1 if getting WDT resets from wifi -#define DEFAULT_HEATINGCIRCUIT 1 // default to HC1 for thermostats that support multiple heating circuits like the RC35 - // timers, all values are in seconds #define DEFAULT_PUBLISHTIME 120 // every 2 minutes publish MQTT values, including Dallas sensors Ticker publishValuesTimer; @@ -102,7 +100,6 @@ typedef struct { uint8_t led_gpio; // pin for LED uint8_t dallas_gpio; // pin for attaching external dallas temperature sensors bool dallas_parasite; // on/off is using parasite - uint8_t heating_circuit; // number of heating circuit, 1 or 2 uint8_t tx_mode; // TX mode 1,2 or 3 } _EMSESP_Settings; @@ -141,9 +138,9 @@ static const command_t project_cmds[] PROGMEM = { {false, "autodetect [deep]", "detect EMS devices and attempt to automatically set boiler and thermostat types"}, {false, "shower ", "toggle either timer or alert on/off"}, {false, "send XX ...", "send raw telegram data as hex to EMS bus"}, - {false, "thermostat read ", "send read request to the thermostat"}, - {false, "thermostat temp ", "set current thermostat temperature"}, - {false, "thermostat mode ", "set mode (0=low/night, 1=manual/day, 2=auto)"}, + {false, "thermostat read ", "send read request to the thermostat for heating circuit hc 1-4"}, + {false, "thermostat temp [hc] ", "set current thermostat temperature"}, + {false, "thermostat mode [hc] ", "set mode (0=low/night, 1=manual/day, 2=auto) for heating circuit hc 1-4"}, {false, "thermostat scan ", "probe thermostat on all type id responses"}, {false, "boiler read ", "send read request to boiler"}, {false, "boiler wwtemp ", "set boiler warm water temperature"}, @@ -392,29 +389,33 @@ void _renderBoolValue(const char * prefix, uint8_t value) { // figures out the thermostat mode // returns 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day -uint8_t _getThermostatMode() { - int thermoMode = EMS_VALUE_INT_NOTSET; +// hc_num is 1 to 4 +uint8_t _getThermostatMode(uint8_t hc_num) { + int thermoMode = EMS_VALUE_INT_NOTSET; + uint8_t model = ems_getThermostatModel(); + + uint8_t mode = EMS_Thermostat.hc[hc_num - 1].mode; - if (ems_getThermostatModel() == EMS_MODEL_RC20) { - if (EMS_Thermostat.mode == 0) { + if (model == EMS_MODEL_RC20) { + if (mode == 0) { thermoMode = 0; // low - } else if (EMS_Thermostat.mode == 1) { + } else if (mode == 1) { thermoMode = 1; // manual - } else if (EMS_Thermostat.mode == 2) { + } else if (mode == 2) { thermoMode = 2; // auto } - } else if (ems_getThermostatModel() == EMS_MODEL_RC300) { - if (EMS_Thermostat.mode == 0) { + } else if (model == EMS_MODEL_RC300) { + if (mode == 0) { thermoMode = 1; // manual - } else if (EMS_Thermostat.mode == 1) { + } else if (mode == 1) { thermoMode = 2; // auto } - } else { // default for all thermostats - if (EMS_Thermostat.mode == 0) { + } else { // default for all other thermostats + if (mode == 0) { thermoMode = 3; // night - } else if (EMS_Thermostat.mode == 1) { + } else if (mode == 1) { thermoMode = 4; // day - } else if (EMS_Thermostat.mode == 2) { + } else if (mode == 2) { thermoMode = 2; // auto } } @@ -602,58 +603,64 @@ void showInfo() { myDebug_P(PSTR("%sThermostat stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF); myDebug_P(PSTR(" Thermostat: %s"), ems_getThermostatDescription(buffer_type, false)); - // Render Current & Setpoint Room Temperature - if (ems_getThermostatModel() == EMS_MODEL_EASY) { - // Temperatures are *100 - _renderShortValue("Set room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 10); // *100 - _renderShortValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 10); // *100 - } else if ((ems_getThermostatModel() == EMS_MODEL_FR10) || (ems_getThermostatModel() == EMS_MODEL_FW100) - || (ems_getThermostatModel() == EMS_MODEL_FW120)) { - // Temperatures are *10 - _renderShortValue("Set room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 1); // *10 - _renderShortValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 1); // *10 - } else { - // because we store in 2 bytes short, when converting to a single byte we'll loose the negative value if its unset - _renderShortValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 2); // convert to a single byte * 2 - _renderShortValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 1); // is *10 - } - - // Render Day/Night/Holiday Temperature - if ((EMS_Thermostat.holidaytemp > 0) && (EMSESP_Settings.heating_circuit == 2)) { // only if we are on a RC35 we show more info - _renderIntValue("Day temperature", "C", EMS_Thermostat.daytemp, 2); // convert to a single byte * 2 - _renderIntValue("Night temperature", "C", EMS_Thermostat.nighttemp, 2); // convert to a single byte * 2 - _renderIntValue("Vacation temperature", "C", EMS_Thermostat.holidaytemp, 2); // convert to a single byte * 2 + // Render Thermostat Date & Time + uint8_t model = ems_getThermostatModel(); + if ((model != EMS_MODEL_EASY)) { + myDebug_P(PSTR(" Thermostat time is %s"), EMS_Thermostat.datetime); } - // Render Thermostat Date & Time - // not for EASY - if ((ems_getThermostatModel() != EMS_MODEL_EASY)) { - myDebug_P(PSTR(" Thermostat time is %02d:%02d:%02d %d/%d/%d"), - EMS_Thermostat.hour, - EMS_Thermostat.minute, - EMS_Thermostat.second, - EMS_Thermostat.day, - EMS_Thermostat.month, - EMS_Thermostat.year + 2000); + uint8_t _m_setpoint, _m_curr; + switch (model) { + case EMS_MODEL_EASY: + _m_setpoint = 10; // *100 + _m_curr = 10; // *100 + break; + case EMS_MODEL_FR10: + case EMS_MODEL_FW100: + case EMS_MODEL_FW120: + _m_setpoint = 1; // *10 + _m_curr = 1; // *10 + break; + default: // RC30,RC35 etc... + _m_setpoint = 2; // *2 + _m_curr = 1; // *10 + break; } - // Render Termostat Mode, if we have a mode - uint8_t thermoMode = _getThermostatMode(); // 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day + // go through all Heating Circuits + for (uint8_t hc_num = 1; hc_num <= EMS_THERMOSTAT_MAXHC; hc_num++) { + // only show if we have data for the Heating Circuit + if (EMS_Thermostat.hc[hc_num - 1].active) { + myDebug_P(PSTR(" Heating Circuit %d"), hc_num); + // Render Current & Setpoint Room Temperature for each thermostat type + _renderShortValue(" Setpoint room temperature", "C", EMS_Thermostat.hc[hc_num - 1].setpoint_roomTemp, _m_setpoint); + _renderShortValue(" Current room temperature", "C", EMS_Thermostat.hc[hc_num - 1].curr_roomTemp, _m_curr); + + // Render Day/Night/Holiday Temperature on RC35s + if (model == EMS_MODEL_RC35) { + _renderIntValue(" Day temperature", "C", EMS_Thermostat.hc[hc_num - 1].daytemp, 2); // convert to a single byte * 2 + _renderIntValue(" Night temperature", "C", EMS_Thermostat.hc[hc_num - 1].nighttemp, 2); // convert to a single byte * 2 + _renderIntValue(" Vacation temperature", "C", EMS_Thermostat.hc[hc_num - 1].holidaytemp, 2); // convert to a single byte * 2 + } - if (thermoMode == 0) { - myDebug_P(PSTR(" Mode is set to low")); - } else if (thermoMode == 1) { - myDebug_P(PSTR(" Mode is set to manual")); - } else if (thermoMode == 2) { - myDebug_P(PSTR(" Mode is set to auto")); - } else if (thermoMode == 3) { - myDebug_P(PSTR(" Mode is set to night")); - } else if (thermoMode == 4) { - myDebug_P(PSTR(" Mode is set to day")); + // Render Termostat Mode, if we have a mode + uint8_t thermoMode = _getThermostatMode(hc_num); // 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day + if (thermoMode == 0) { + myDebug_P(PSTR(" Mode is set to low")); + } else if (thermoMode == 1) { + myDebug_P(PSTR(" Mode is set to manual")); + } else if (thermoMode == 2) { + myDebug_P(PSTR(" Mode is set to auto")); + } else if (thermoMode == 3) { + myDebug_P(PSTR(" Mode is set to night")); + } else if (thermoMode == 4) { + myDebug_P(PSTR(" Mode is set to day")); + } + } } } - // Dallas + // Dallas external temp sensors if (EMSESP_Settings.dallas_sensors != 0) { myDebug_P(PSTR("")); // newline char buffer[128] = {0}; @@ -826,80 +833,97 @@ void publishValues(bool force) { last_boilerActive = ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive); // remember last state } - // handle the thermostat values separately - // only send thermostat values if we actually have them - if (ems_getThermostatEnabled() && ((EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET) && (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET))) { - // build new json object - doc.clear(); - JsonObject rootThermostat = doc.to(); - - rootThermostat[THERMOSTAT_HC] = _int_to_char(s, EMSESP_Settings.heating_circuit); - - // different logic depending on thermostat types - if (ems_getThermostatModel() == EMS_MODEL_EASY) { - if (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) - rootThermostat[THERMOSTAT_SELTEMP] = (double)EMS_Thermostat.setpoint_roomTemp / 100; - if (EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET) - rootThermostat[THERMOSTAT_CURRTEMP] = (double)EMS_Thermostat.curr_roomTemp / 100; - } else if ((ems_getThermostatModel() == EMS_MODEL_FR10) || (ems_getThermostatModel() == EMS_MODEL_FW100) - || (ems_getThermostatModel() == EMS_MODEL_FW120)) { - if (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) - rootThermostat[THERMOSTAT_SELTEMP] = (double)EMS_Thermostat.setpoint_roomTemp / 10; - if (EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET) - rootThermostat[THERMOSTAT_CURRTEMP] = (double)EMS_Thermostat.curr_roomTemp / 10; - } else { - if (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) - rootThermostat[THERMOSTAT_SELTEMP] = (double)EMS_Thermostat.setpoint_roomTemp / 2; - if (EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET) - rootThermostat[THERMOSTAT_CURRTEMP] = (double)EMS_Thermostat.curr_roomTemp / 10; - - if (EMS_Thermostat.daytemp != EMS_VALUE_INT_NOTSET) - rootThermostat[THERMOSTAT_DAYTEMP] = (double)EMS_Thermostat.daytemp / 2; - if (EMS_Thermostat.nighttemp != EMS_VALUE_INT_NOTSET) - rootThermostat[THERMOSTAT_NIGHTTEMP] = (double)EMS_Thermostat.nighttemp / 2; - if (EMS_Thermostat.holidaytemp != EMS_VALUE_INT_NOTSET) - rootThermostat[THERMOSTAT_HOLIDAYTEMP] = (double)EMS_Thermostat.holidaytemp / 2; - - if (EMS_Thermostat.heatingtype != EMS_VALUE_INT_NOTSET) - rootThermostat[THERMOSTAT_HEATINGTYPE] = EMS_Thermostat.heatingtype; - - if (EMS_Thermostat.circuitcalctemp != EMS_VALUE_INT_NOTSET) - rootThermostat[THERMOSTAT_CIRCUITCALCTEMP] = EMS_Thermostat.circuitcalctemp; - } - - uint8_t thermoMode = _getThermostatMode(); // 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day - - // Termostat Mode - if (thermoMode == 0) { - rootThermostat[THERMOSTAT_MODE] = "low"; - } else if (thermoMode == 1) { - rootThermostat[THERMOSTAT_MODE] = "manual"; - } else if (thermoMode == 2) { - rootThermostat[THERMOSTAT_MODE] = "auto"; - } else if (thermoMode == 3) { - rootThermostat[THERMOSTAT_MODE] = "night"; - } else if (thermoMode == 4) { - rootThermostat[THERMOSTAT_MODE] = "day"; - } + // handle the thermostat values + if (ems_getThermostatEnabled()) { + uint8_t total_active_hc = 0; // number of HCs + for (uint8_t hc_v = 0; hc_v < EMS_THERMOSTAT_MAXHC; hc_v++) { + _EMS_Thermostat_HC * thermostat = &EMS_Thermostat.hc[hc_v]; + + total_active_hc++; // increase count for #HCs we encounter + + // only send if we have an active Heating Circuit with real data + if ((thermostat->active) && (thermostat->curr_roomTemp != EMS_VALUE_SHORT_NOTSET) && (thermostat->setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET)) { + // build new json object + doc.clear(); + JsonObject rootThermostat = doc.to(); + + rootThermostat[THERMOSTAT_HC] = _int_to_char(s, (thermostat->hc) + 1); // heating circuit 1..4 + + // different logic depending on thermostat types + if (ems_getThermostatModel() == EMS_MODEL_EASY) { + if (thermostat->setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) + rootThermostat[THERMOSTAT_SELTEMP] = (double)thermostat->setpoint_roomTemp / 100; + if (thermostat->curr_roomTemp != EMS_VALUE_SHORT_NOTSET) + rootThermostat[THERMOSTAT_CURRTEMP] = (double)thermostat->curr_roomTemp / 100; + } else if ((ems_getThermostatModel() == EMS_MODEL_FR10) || (ems_getThermostatModel() == EMS_MODEL_FW100) + || (ems_getThermostatModel() == EMS_MODEL_FW120)) { + if (thermostat->setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) + rootThermostat[THERMOSTAT_SELTEMP] = (double)thermostat->setpoint_roomTemp / 10; + if (thermostat->curr_roomTemp != EMS_VALUE_SHORT_NOTSET) + rootThermostat[THERMOSTAT_CURRTEMP] = (double)thermostat->curr_roomTemp / 10; + } else { + if (thermostat->setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) + rootThermostat[THERMOSTAT_SELTEMP] = (double)thermostat->setpoint_roomTemp / 2; + if (thermostat->curr_roomTemp != EMS_VALUE_SHORT_NOTSET) + rootThermostat[THERMOSTAT_CURRTEMP] = (double)thermostat->curr_roomTemp / 10; + + if (thermostat->daytemp != EMS_VALUE_INT_NOTSET) + rootThermostat[THERMOSTAT_DAYTEMP] = (double)thermostat->daytemp / 2; + if (thermostat->nighttemp != EMS_VALUE_INT_NOTSET) + rootThermostat[THERMOSTAT_NIGHTTEMP] = (double)thermostat->nighttemp / 2; + if (thermostat->holidaytemp != EMS_VALUE_INT_NOTSET) + rootThermostat[THERMOSTAT_HOLIDAYTEMP] = (double)thermostat->holidaytemp / 2; + + if (thermostat->heatingtype != EMS_VALUE_INT_NOTSET) + rootThermostat[THERMOSTAT_HEATINGTYPE] = thermostat->heatingtype; + + if (thermostat->circuitcalctemp != EMS_VALUE_INT_NOTSET) + rootThermostat[THERMOSTAT_CIRCUITCALCTEMP] = thermostat->circuitcalctemp; + } - data[0] = '\0'; // reset data for next package - serializeJson(doc, data, sizeof(data)); + uint8_t thermoMode = _getThermostatMode(hc_v + 1); // 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day + + // Termostat Mode + if (thermoMode == 0) { + rootThermostat[THERMOSTAT_MODE] = "low"; + } else if (thermoMode == 1) { + rootThermostat[THERMOSTAT_MODE] = "manual"; + } else if (thermoMode == 2) { + rootThermostat[THERMOSTAT_MODE] = "auto"; + } else if (thermoMode == 3) { + rootThermostat[THERMOSTAT_MODE] = "night"; + } else if (thermoMode == 4) { + rootThermostat[THERMOSTAT_MODE] = "day"; + } - // check for empty json - jsonSize = measureJson(doc); - if (jsonSize > 2) { - // calculate new CRC - crc.reset(); - for (uint8_t i = 0; i < (jsonSize - 1); i++) { - crc.update(data[i]); - } - fchecksum = crc.finalize(); - if ((previousThermostatPublishCRC != fchecksum) || force) { - previousThermostatPublishCRC = fchecksum; - myDebugLog("Publishing thermostat data via MQTT"); + data[0] = '\0'; // reset data for next package + serializeJson(doc, data, sizeof(data)); - // send values via MQTT - myESP.mqttPublish(TOPIC_THERMOSTAT_DATA, data); + // check for empty json + jsonSize = measureJson(doc); + if (jsonSize > 2) { + // calculate new CRC + crc.reset(); + for (uint8_t i = 0; i < (jsonSize - 1); i++) { + crc.update(data[i]); + } + fchecksum = crc.finalize(); + if ((previousThermostatPublishCRC != fchecksum) || force) { + previousThermostatPublishCRC = fchecksum; + myDebugLog("Publishing thermostat data via MQTT"); + + // send values via MQTT + + char thermostat_topicname[20]; + strlcpy(thermostat_topicname, TOPIC_THERMOSTAT_DATA, sizeof(thermostat_topicname)); // "thermostat_data" + // if we have more than 1 active Heating Circuit, postfix with the HC number + if (total_active_hc > 1) { + char buffer[4]; + strlcat(thermostat_topicname, itoa(total_active_hc, buffer, 10), sizeof(thermostat_topicname)); + } + myESP.mqttPublish(thermostat_topicname, data); + } + } } } } @@ -1066,7 +1090,7 @@ void do_scanThermostat() { // do a system health check every now and then to see if we all connections void do_systemCheck() { - if (!ems_getBusConnected()) { + if (!ems_getBusConnected() && !myESP.getUseSerial()) { myDebug_P(PSTR("Error! Unable to read the EMS bus.")); } } @@ -1180,9 +1204,6 @@ bool LoadSaveCallback(MYESP_FSACTION action, JsonObject json) { EMSESP_Settings.listen_mode = settings["listen_mode"]; ems_setTxDisabled(EMSESP_Settings.listen_mode); - EMSESP_Settings.heating_circuit = settings["heating_circuit"] | DEFAULT_HEATINGCIRCUIT; - ems_setThermostatHC(EMSESP_Settings.heating_circuit); - EMSESP_Settings.tx_mode = settings["tx_mode"] | 1; // default to 1 (generic) ems_setTxMode(EMSESP_Settings.tx_mode); @@ -1200,7 +1221,6 @@ bool LoadSaveCallback(MYESP_FSACTION action, JsonObject json) { settings["shower_timer"] = EMSESP_Settings.shower_timer; settings["shower_alert"] = EMSESP_Settings.shower_alert; settings["publish_time"] = EMSESP_Settings.publish_time; - settings["heating_circuit"] = EMSESP_Settings.heating_circuit; settings["tx_mode"] = EMSESP_Settings.tx_mode; return true; @@ -1308,18 +1328,6 @@ bool SetListCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, co ok = true; } - // heating_circuit - if ((strcmp(setting, "heating_circuit") == 0) && (wc == 2)) { - uint8_t hc = atoi(value); - if ((hc >= 1) && (hc <= 2)) { - EMSESP_Settings.heating_circuit = hc; - ems_setThermostatHC(hc); - ok = true; - } else { - myDebug_P(PSTR("Error. Usage: set heating_circuit <1 | 2>")); - } - } - // tx_mode if ((strcmp(setting, "tx_mode") == 0) && (wc == 2)) { uint8_t mode = atoi(value); @@ -1338,7 +1346,6 @@ bool SetListCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, co myDebug_P(PSTR(" led_gpio=%d"), EMSESP_Settings.led_gpio); myDebug_P(PSTR(" dallas_gpio=%d"), EMSESP_Settings.dallas_gpio); myDebug_P(PSTR(" dallas_parasite=%s"), EMSESP_Settings.dallas_parasite ? "on" : "off"); - myDebug_P(PSTR(" heating_circuit=%d"), EMSESP_Settings.heating_circuit); myDebug_P(PSTR(" tx_mode=%d"), EMSESP_Settings.tx_mode); myDebug_P(PSTR(" listen_mode=%s"), EMSESP_Settings.listen_mode ? "on" : "off"); myDebug_P(PSTR(" shower_timer=%s"), EMSESP_Settings.shower_timer ? "on" : "off"); @@ -1487,13 +1494,21 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) { } // thermostat commands - if ((strcmp(first_cmd, "thermostat") == 0) && (wc == 3)) { - char * second_cmd = _readWord(); + if ((strcmp(first_cmd, "thermostat") == 0) && (wc >= 3)) { + char * second_cmd = _readWord(); + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC; + if (strcmp(second_cmd, "temp") == 0) { - ems_setThermostatTemp(_readFloatNumber()); + if (wc == 4) { + hc = _readIntNumber(); // next parameter is the heating circuit + } + ems_setThermostatTemp(_readFloatNumber(), hc); ok = true; } else if (strcmp(second_cmd, "mode") == 0) { - ems_setThermostatMode(_readIntNumber()); + if (wc == 4) { + hc = _readIntNumber(); // next parameter is the heating circuit + } + ems_setThermostatMode(_readIntNumber(), hc); ok = true; } else if (strcmp(second_cmd, "read") == 0) { ems_doReadCommand(_readHexNumber(), EMS_Thermostat.device_id); @@ -1602,7 +1617,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { float f = strtof((char *)message, 0); char s[10] = {0}; myDebug_P(PSTR("MQTT topic: thermostat temperature value %s"), _float_to_char(s, f)); - ems_setThermostatTemp(f); + ems_setThermostatTemp(f, EMS_THERMOSTAT_DEFAULTHC); publishValues(true); // publish back immediately, can't remember why I do this?! } @@ -1610,21 +1625,11 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { if (strcmp(topic, TOPIC_THERMOSTAT_CMD_MODE) == 0) { myDebug_P(PSTR("MQTT topic: thermostat mode value %s"), message); if (strcmp((char *)message, "auto") == 0) { - ems_setThermostatMode(2); + ems_setThermostatMode(2, EMS_THERMOSTAT_DEFAULTHC); } else if (strcmp((char *)message, "day") == 0 || strcmp((char *)message, "manual") == 0) { - ems_setThermostatMode(1); + ems_setThermostatMode(1, EMS_THERMOSTAT_DEFAULTHC); } else if (strcmp((char *)message, "night") == 0 || strcmp((char *)message, "off") == 0) { - ems_setThermostatMode(0); - } - } - - // thermostat heating circuit change - if (strcmp(topic, TOPIC_THERMOSTAT_CMD_HC) == 0) { - myDebug_P(PSTR("MQTT topic: thermostat heating circuit value %s"), message); - uint8_t hc = atoi((char *)message); - if ((hc >= 1) && (hc <= 2)) { - EMSESP_Settings.heating_circuit = hc; - ems_setThermostatHC(hc); + ems_setThermostatMode(0, EMS_THERMOSTAT_DEFAULTHC); } } @@ -1633,7 +1638,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { float f = strtof((char *)message, 0); char s[10] = {0}; myDebug_P(PSTR("MQTT topic: new thermostat night temperature value %s"), _float_to_char(s, f)); - ems_setThermostatTemp(f, 1); + ems_setThermostatTemp(f, EMS_THERMOSTAT_DEFAULTHC, 1); } // set daytemp value @@ -1641,7 +1646,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { float f = strtof((char *)message, 0); char s[10] = {0}; myDebug_P(PSTR("MQTT topic: new thermostat day temperature value %s"), _float_to_char(s, f)); - ems_setThermostatTemp(f, 2); + ems_setThermostatTemp(f, EMS_THERMOSTAT_DEFAULTHC, 2); } // set holiday value @@ -1649,7 +1654,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { float f = strtof((char *)message, 0); char s[10] = {0}; myDebug_P(PSTR("MQTT topic: new thermostat holiday temperature value %s"), _float_to_char(s, f)); - ems_setThermostatTemp(f, 3); + ems_setThermostatTemp(f, EMS_THERMOSTAT_DEFAULTHC, 3); } // wwActivated @@ -1770,27 +1775,30 @@ void WebCallback(JsonObject root) { char buffer[200]; thermostat["tm"] = ems_getThermostatDescription(buffer, true); + // TODO: enable support multiple HCs + uint8_t hc_num = EMS_THERMOSTAT_DEFAULTHC; // default to HC1 + // Render Current & Setpoint Room Temperature if (ems_getThermostatModel() == EMS_MODEL_EASY) { - if (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) - thermostat["ts"] = (double)EMS_Thermostat.setpoint_roomTemp / 100; - if (EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET) - thermostat["tc"] = (double)EMS_Thermostat.curr_roomTemp / 100; + if (EMS_Thermostat.hc[hc_num - 1].setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) + thermostat["ts"] = (double)EMS_Thermostat.hc[hc_num - 1].setpoint_roomTemp / 100; + if (EMS_Thermostat.hc[hc_num - 1].curr_roomTemp != EMS_VALUE_SHORT_NOTSET) + thermostat["tc"] = (double)EMS_Thermostat.hc[hc_num - 1].curr_roomTemp / 100; } else if ((ems_getThermostatModel() == EMS_MODEL_FR10) || (ems_getThermostatModel() == EMS_MODEL_FW100) || (ems_getThermostatModel() == EMS_MODEL_FW120)) { - if (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) - thermostat["ts"] = (double)EMS_Thermostat.setpoint_roomTemp / 10; - if (EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET) - thermostat["tc"] = (double)EMS_Thermostat.curr_roomTemp / 10; + if (EMS_Thermostat.hc[hc_num - 1].setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) + thermostat["ts"] = (double)EMS_Thermostat.hc[hc_num - 1].setpoint_roomTemp / 10; + if (EMS_Thermostat.hc[hc_num - 1].curr_roomTemp != EMS_VALUE_SHORT_NOTSET) + thermostat["tc"] = (double)EMS_Thermostat.hc[hc_num - 1].curr_roomTemp / 10; } else { - if (EMS_Thermostat.setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) - thermostat["ts"] = (double)EMS_Thermostat.setpoint_roomTemp / 2; - if (EMS_Thermostat.curr_roomTemp != EMS_VALUE_SHORT_NOTSET) - thermostat["tc"] = (double)EMS_Thermostat.curr_roomTemp / 10; + if (EMS_Thermostat.hc[hc_num - 1].setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) + thermostat["ts"] = (double)EMS_Thermostat.hc[hc_num - 1].setpoint_roomTemp / 2; + if (EMS_Thermostat.hc[hc_num - 1].curr_roomTemp != EMS_VALUE_SHORT_NOTSET) + thermostat["tc"] = (double)EMS_Thermostat.hc[hc_num - 1].curr_roomTemp / 10; } // Render Termostat Mode, if we have a mode - uint8_t thermoMode = _getThermostatMode(); // 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day + uint8_t thermoMode = _getThermostatMode(hc_num); // 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day if (thermoMode == 0) { thermostat["tmode"] = "low"; } else if (thermoMode == 1) { @@ -1890,17 +1898,16 @@ void WebCallback(JsonObject root) { // Most of these will be overwritten after the SPIFFS config file is loaded void initEMSESP() { // general settings - EMSESP_Settings.shower_timer = false; - EMSESP_Settings.shower_alert = false; - EMSESP_Settings.led = true; // LED is on by default - EMSESP_Settings.listen_mode = false; - EMSESP_Settings.publish_time = DEFAULT_PUBLISHTIME; - EMSESP_Settings.timestamp = millis(); - EMSESP_Settings.dallas_sensors = 0; - EMSESP_Settings.led_gpio = EMSESP_LED_GPIO; - EMSESP_Settings.dallas_gpio = EMSESP_DALLAS_GPIO; - EMSESP_Settings.heating_circuit = 1; // default heating circuit to HC1 - EMSESP_Settings.tx_mode = 1; // default tx mode + EMSESP_Settings.shower_timer = false; + EMSESP_Settings.shower_alert = false; + EMSESP_Settings.led = true; // LED is on by default + EMSESP_Settings.listen_mode = false; + EMSESP_Settings.publish_time = DEFAULT_PUBLISHTIME; + EMSESP_Settings.timestamp = millis(); + EMSESP_Settings.dallas_sensors = 0; + EMSESP_Settings.led_gpio = EMSESP_LED_GPIO; + EMSESP_Settings.dallas_gpio = EMSESP_DALLAS_GPIO; + EMSESP_Settings.tx_mode = 1; // default tx mode // shower settings EMSESP_Shower.timerStart = 0; diff --git a/src/ems.cpp b/src/ems.cpp index b4e96f99..b8f5b03d 100644 --- a/src/ems.cpp +++ b/src/ems.cpp @@ -159,6 +159,10 @@ const _EMS_Type EMS_Types[] = { {EMS_MODEL_RC35, EMS_TYPE_RC35StatusMessage_HC1, "RC35StatusMessage_HC1", _process_RC35StatusMessage}, {EMS_MODEL_RC35, EMS_TYPE_RC35Set_HC2, "RC35Set_HC2", _process_RC35Set}, {EMS_MODEL_RC35, EMS_TYPE_RC35StatusMessage_HC2, "RC35StatusMessage_HC2", _process_RC35StatusMessage}, + {EMS_MODEL_RC35, EMS_TYPE_RC35Set_HC3, "RC35Set_HC2", _process_RC35Set}, + {EMS_MODEL_RC35, EMS_TYPE_RC35StatusMessage_HC3, "RC35StatusMessage_HC3", _process_RC35StatusMessage}, + {EMS_MODEL_RC35, EMS_TYPE_RC35Set_HC4, "RC35Set_HC4", _process_RC35Set}, + {EMS_MODEL_RC35, EMS_TYPE_RC35StatusMessage_HC4, "RC35StatusMessage_HC4", _process_RC35StatusMessage}, // ES73 {EMS_MODEL_ES73, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage}, @@ -234,25 +238,24 @@ void ems_init() { EMS_Sys_Status.emsPollAck[0] = EMS_ID_ME; // thermostat - EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_SHORT_NOTSET; - EMS_Thermostat.curr_roomTemp = EMS_VALUE_SHORT_NOTSET; - EMS_Thermostat.hour = 0; - EMS_Thermostat.minute = 0; - EMS_Thermostat.second = 0; - EMS_Thermostat.day = 0; - EMS_Thermostat.month = 0; - EMS_Thermostat.year = 0; - EMS_Thermostat.mode = EMS_VALUE_INT_NOTSET; - EMS_Thermostat.day_mode = EMS_VALUE_INT_NOTSET; - EMS_Thermostat.device_id = EMS_ID_NONE; - EMS_Thermostat.write_supported = false; - EMS_Thermostat.hc = 1; // default heating circuit is 1 - EMS_Thermostat.daytemp = EMS_VALUE_INT_NOTSET; // 0x47 byte - EMS_Thermostat.nighttemp = EMS_VALUE_INT_NOTSET; // 0x47 byte - EMS_Thermostat.holidaytemp = EMS_VALUE_INT_NOTSET; // 0x47 byte - EMS_Thermostat.heatingtype = EMS_VALUE_INT_NOTSET; // 0x47 byte floor heating = 3 - EMS_Thermostat.circuitcalctemp = EMS_VALUE_INT_NOTSET; // 0x48 byte 14 - + strlcpy(EMS_Thermostat.datetime, "?", sizeof(EMS_Thermostat.datetime)); + EMS_Thermostat.write_supported = false; + EMS_Thermostat.device_id = EMS_ID_NONE; + + // init all heating circuits + for (uint8_t i = 0; i < EMS_THERMOSTAT_MAXHC; i++) { + EMS_Thermostat.hc[i].hc = i; + EMS_Thermostat.hc[i].active = false; + EMS_Thermostat.hc[i].mode = EMS_VALUE_INT_NOTSET; + EMS_Thermostat.hc[i].day_mode = EMS_VALUE_INT_NOTSET; + EMS_Thermostat.hc[i].daytemp = EMS_VALUE_INT_NOTSET; + EMS_Thermostat.hc[i].nighttemp = EMS_VALUE_INT_NOTSET; + EMS_Thermostat.hc[i].holidaytemp = EMS_VALUE_INT_NOTSET; + EMS_Thermostat.hc[i].heatingtype = EMS_VALUE_INT_NOTSET; // floor heating = 3 + EMS_Thermostat.hc[i].circuitcalctemp = EMS_VALUE_INT_NOTSET; + EMS_Thermostat.hc[i].setpoint_roomTemp = EMS_VALUE_SHORT_NOTSET; + EMS_Thermostat.hc[i].curr_roomTemp = EMS_VALUE_SHORT_NOTSET; + } // UBAParameterWW EMS_Boiler.wWActivated = EMS_VALUE_INT_NOTSET; // Warm Water activated @@ -357,10 +360,6 @@ void ems_setEmsRefreshed(bool b) { EMS_Sys_Status.emsRefreshed = b; } -void ems_setThermostatHC(uint8_t hc) { - EMS_Thermostat.hc = hc; -} - bool ems_getBoilerEnabled() { return (EMS_Boiler.device_id != EMS_ID_NONE); } @@ -1344,8 +1343,11 @@ void _process_UBAMonitorSlow(_EMS_RxTelegram * EMS_RxTelegram) { * e.g. 17 0B 91 00 80 1E 00 CB 27 00 00 00 00 05 01 00 CB 00 (CRC=47), #data=14 */ void _process_RC10StatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { - EMS_Thermostat.setpoint_roomTemp = _toByte(EMS_OFFSET_RC10StatusMessage_setpoint); // is * 2 - EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_RC10StatusMessage_curr); // is * 10 + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1 + + EMS_Thermostat.hc[hc].active = true; + EMS_Thermostat.hc[hc].setpoint_roomTemp = _toByte(EMS_OFFSET_RC10StatusMessage_setpoint); // is * 2 + EMS_Thermostat.hc[hc].curr_roomTemp = _toShort(EMS_OFFSET_RC10StatusMessage_curr); // is * 10 EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } @@ -1356,8 +1358,11 @@ void _process_RC10StatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { * received every 60 seconds */ void _process_RC20StatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { - EMS_Thermostat.setpoint_roomTemp = _toByte(EMS_OFFSET_RC20StatusMessage_setpoint); // is * 2 - EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_RC20StatusMessage_curr); // is * 10 + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1 + + EMS_Thermostat.hc[hc].active = true; + EMS_Thermostat.hc[hc].setpoint_roomTemp = _toByte(EMS_OFFSET_RC20StatusMessage_setpoint); // is * 2 + EMS_Thermostat.hc[hc].curr_roomTemp = _toShort(EMS_OFFSET_RC20StatusMessage_curr); // is * 10 EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } @@ -1367,29 +1372,35 @@ void _process_RC20StatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { * For reading the temp values only * received every 60 seconds */ void _process_RC30StatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { - EMS_Thermostat.setpoint_roomTemp = _toByte(EMS_OFFSET_RC30StatusMessage_setpoint); // is * 2 - EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_RC30StatusMessage_curr); // note, its 2 bytes here + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1 + + EMS_Thermostat.hc[hc].active = true; + EMS_Thermostat.hc[hc].setpoint_roomTemp = _toByte(EMS_OFFSET_RC30StatusMessage_setpoint); // is * 2 + EMS_Thermostat.hc[hc].curr_roomTemp = _toShort(EMS_OFFSET_RC30StatusMessage_curr); // note, its 2 bytes here EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } /** - * type 0x3E and 0x48 - data from the RC35 thermostat (0x10) - 16 bytes + * type 0x3E (HC1), 0x48 (HC2), 0x52 (HC3), 0x5C (HC4) - HK1MonitorMessage - data from the RC35 thermostat (0x10) - 16 bytes + * * For reading the temp values only * received every 60 seconds */ void _process_RC35StatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { - EMS_Thermostat.setpoint_roomTemp = _toByte(EMS_OFFSET_RC35StatusMessage_setpoint); // is * 2 + uint8_t hc_num = _getHeatingCircuit(EMS_RxTelegram); // which HC is it? + + EMS_Thermostat.hc[hc_num].setpoint_roomTemp = _toByte(EMS_OFFSET_RC35StatusMessage_setpoint); // is * 2 // check if temp sensor is unavailable if (EMS_RxTelegram->data[3] == 0x7D) { - EMS_Thermostat.curr_roomTemp = EMS_VALUE_SHORT_NOTSET; + EMS_Thermostat.hc[hc_num].curr_roomTemp = EMS_VALUE_SHORT_NOTSET; } else { - EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_RC35StatusMessage_curr); + EMS_Thermostat.hc[hc_num].curr_roomTemp = _toShort(EMS_OFFSET_RC35StatusMessage_curr); } - EMS_Thermostat.day_mode = _bitRead(EMS_OFFSET_RC35StatusMessage_mode, 1); // get day mode flag + EMS_Thermostat.hc[hc_num].day_mode = _bitRead(EMS_OFFSET_RC35StatusMessage_mode, 1); // get day mode flag - EMS_Thermostat.circuitcalctemp = _toByte(EMS_OFFSET_RC35Set_circuitcalctemp); // 0x48 calculated temperature + EMS_Thermostat.hc[hc_num].circuitcalctemp = _toByte(EMS_OFFSET_RC35Set_circuitcalctemp); // 0x48 calculated temperature EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } @@ -1399,8 +1410,11 @@ void _process_RC35StatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { * The Easy has a digital precision of its floats to 2 decimal places, so values must be divided by 100 */ void _process_EasyStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { - EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_EasyStatusMessage_curr); // is *100 - EMS_Thermostat.setpoint_roomTemp = _toShort(EMS_OFFSET_EasyStatusMessage_setpoint); // is *100 + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1 + + EMS_Thermostat.hc[hc].active = true; + EMS_Thermostat.hc[hc].curr_roomTemp = _toShort(EMS_OFFSET_EasyStatusMessage_curr); // is *100 + EMS_Thermostat.hc[hc].setpoint_roomTemp = _toShort(EMS_OFFSET_EasyStatusMessage_setpoint); // is *100 EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } @@ -1410,23 +1424,26 @@ void _process_EasyStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { * EMS+ messages may come in with different offsets so handle them here */ void _process_RCPLUSStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1 + EMS_Thermostat.hc[hc].active = true; + // handle single data values if (EMS_RxTelegram->data_length == 1) { switch (EMS_RxTelegram->offset) { - case EMS_OFFSET_RCPLUSStatusMessage_curr: // setpoint target temp - EMS_Thermostat.curr_roomTemp = _toShort(0); // value is * 10 + case EMS_OFFSET_RCPLUSStatusMessage_curr: // setpoint target temp + EMS_Thermostat.hc[hc].curr_roomTemp = _toShort(0); // value is * 10 break; - case EMS_OFFSET_RCPLUSStatusMessage_setpoint: // current target temp - EMS_Thermostat.setpoint_roomTemp = _toByte(0); // value is * 2 + case EMS_OFFSET_RCPLUSStatusMessage_setpoint: // current target temp + EMS_Thermostat.hc[hc].setpoint_roomTemp = _toByte(0); // value is * 2 break; - case EMS_OFFSET_RCPLUSStatusMessage_currsetpoint: // current setpoint temp, e.g. Thermostat -> all, telegram: 10 00 FF 06 01 A5 22 - EMS_Thermostat.setpoint_roomTemp = _toByte(0); // value is * 2 + case EMS_OFFSET_RCPLUSStatusMessage_currsetpoint: // current setpoint temp, e.g. Thermostat -> all, telegram: 10 00 FF 06 01 A5 22 + EMS_Thermostat.hc[hc].setpoint_roomTemp = _toByte(0); // value is * 2 break; - case EMS_OFFSET_RCPLUSStatusMessage_mode: // thermostat mode auto/manual - // manual : 10 00 FF 0A 01 A5 02 - // auto : Thermostat -> all, type 0x01A5 telegram: 10 00 FF 0A 01 A5 03 - EMS_Thermostat.mode = _bitRead(0, 0); // bit 1, mode (auto=1 or manual=0) - EMS_Thermostat.day_mode = _bitRead(0, 1); // get day mode flag + case EMS_OFFSET_RCPLUSStatusMessage_mode: // thermostat mode auto/manual + // manual : 10 00 FF 0A 01 A5 02 + // auto : Thermostat -> all, type 0x01A5 telegram: 10 00 FF 0A 01 A5 03 + EMS_Thermostat.hc[hc].mode = _bitRead(0, 0); // bit 1, mode (auto=1 or manual=0) + EMS_Thermostat.hc[EMS_TYPE_RC35StatusMessage_HC3].day_mode = _bitRead(0, 1); // get day mode flag break; } @@ -1434,10 +1451,10 @@ void _process_RCPLUSStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { // the whole telegram // e.g. Thermostat -> all, telegram: 10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00 // 10 00 FF 00 01 A5 80 00 01 30 28 00 30 28 01 54 03 03 01 01 54 02 A8 00 00 11 01 03 FF FF 00 - EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_RCPLUSStatusMessage_curr); // value is * 10 - EMS_Thermostat.setpoint_roomTemp = _toByte(EMS_OFFSET_RCPLUSStatusMessage_setpoint); // value is * 2 - EMS_Thermostat.day_mode = _bitRead(EMS_OFFSET_RCPLUSStatusMessage_mode, 1); // get day mode flag - EMS_Thermostat.mode = _bitRead(EMS_OFFSET_RCPLUSStatusMessage_mode, 0); // bit 1, mode (auto=1 or manual=0) + EMS_Thermostat.hc[hc].curr_roomTemp = _toShort(EMS_OFFSET_RCPLUSStatusMessage_curr); // value is * 10 + EMS_Thermostat.hc[hc].setpoint_roomTemp = _toByte(EMS_OFFSET_RCPLUSStatusMessage_setpoint); // value is * 2 + EMS_Thermostat.hc[hc].day_mode = _bitRead(EMS_OFFSET_RCPLUSStatusMessage_mode, 1); // get day mode flag + EMS_Thermostat.hc[hc].mode = _bitRead(EMS_OFFSET_RCPLUSStatusMessage_mode, 0); // bit 1, mode (auto=1 or manual=0) } EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT @@ -1455,10 +1472,13 @@ void _process_RCPLUSStatusMode(_EMS_RxTelegram * EMS_RxTelegram) { */ void _process_JunkersStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { if (EMS_RxTelegram->offset == 0) { + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1 + EMS_Thermostat.hc[hc].active = true; + // e.g. for FR10: 90 00 FF 00 00 6F 03 01 00 BE 00 BF // e.g. for FW100: 90 00 FF 00 00 6F 03 02 00 D7 00 DA F3 34 00 C4 - EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_JunkersStatusMessage_curr); // value is * 10 - EMS_Thermostat.setpoint_roomTemp = _toShort(EMS_OFFSET_JunkersStatusMessage_setpoint); // value is * 10 + EMS_Thermostat.hc[hc].curr_roomTemp = _toShort(EMS_OFFSET_JunkersStatusMessage_curr); // value is * 10 + EMS_Thermostat.hc[hc].setpoint_roomTemp = _toShort(EMS_OFFSET_JunkersStatusMessage_setpoint); // value is * 10 } } @@ -1471,18 +1491,20 @@ void _process_RCPLUSSetMessage(_EMS_RxTelegram * EMS_RxTelegram) { return; } + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1 + EMS_Thermostat.hc[hc].active = true; + // check for single values // but ignore values of 0xFF, e.g. 10 00 FF 08 01 B9 FF if ((EMS_RxTelegram->data_length == 1) && (_toByte(0) != 0xFF)) { // check for setpoint temps, e.g. Thermostat -> all, type 0x01B9, telegram: 10 00 FF 08 01 B9 26 (CRC=1A) #data=1 - if ((EMS_RxTelegram->offset == EMS_OFFSET_RCPLUSSet_temp_setpoint) || (EMS_RxTelegram->offset == EMS_OFFSET_RCPLUSSet_manual_setpoint)) { - EMS_Thermostat.setpoint_roomTemp = _toByte(0); // value is * 2 - EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT + EMS_Thermostat.hc[hc].setpoint_roomTemp = _toByte(0); // value is * 2 + EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT // check for mode, eg. 10 00 FF 08 01 B9 FF } else if (EMS_RxTelegram->offset == EMS_OFFSET_RCPLUSSet_mode) { - EMS_Thermostat.mode = (_toByte(0) == 0xFF); // Auto = xFF, Manual = x00 (auto=1 or manual=0) + EMS_Thermostat.hc[hc].mode = (_toByte(0) == 0xFF); // Auto = xFF, Manual = x00 (auto=1 or manual=0) EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } @@ -1491,10 +1513,10 @@ void _process_RCPLUSSetMessage(_EMS_RxTelegram * EMS_RxTelegram) { // check for long broadcasts if (EMS_RxTelegram->offset == 0) { - EMS_Thermostat.mode = _toByte(EMS_OFFSET_RCPLUSSet_mode); - EMS_Thermostat.daytemp = _toByte(EMS_OFFSET_RCPLUSSet_temp_comfort2); // is * 2 - EMS_Thermostat.nighttemp = _toByte(EMS_OFFSET_RCPLUSSet_temp_eco); // is * 2 - EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT + EMS_Thermostat.hc[hc].mode = _toByte(EMS_OFFSET_RCPLUSSet_mode); + EMS_Thermostat.hc[hc].daytemp = _toByte(EMS_OFFSET_RCPLUSSet_temp_comfort2); // is * 2 + EMS_Thermostat.hc[hc].nighttemp = _toByte(EMS_OFFSET_RCPLUSSet_temp_eco); // is * 2 + EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } } @@ -1511,7 +1533,10 @@ void _process_RC10Set(_EMS_RxTelegram * EMS_RxTelegram) { * received only after requested */ void _process_RC20Set(_EMS_RxTelegram * EMS_RxTelegram) { - EMS_Thermostat.mode = _toByte(EMS_OFFSET_RC20Set_mode); + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1 + + EMS_Thermostat.hc[hc].active = true; + EMS_Thermostat.hc[hc].mode = _toByte(EMS_OFFSET_RC20Set_mode); // fixed for HC1 } /** @@ -1519,21 +1544,50 @@ void _process_RC20Set(_EMS_RxTelegram * EMS_RxTelegram) { * received only after requested */ void _process_RC30Set(_EMS_RxTelegram * EMS_RxTelegram) { - EMS_Thermostat.mode = _toByte(EMS_OFFSET_RC30Set_mode); + uint8_t hc = EMS_THERMOSTAT_DEFAULTHC - 1; // use HC1 + + EMS_Thermostat.hc[hc].active = true; + EMS_Thermostat.hc[hc].mode = _toByte(EMS_OFFSET_RC30Set_mode); // fixed for HC1 +} + +// return which heating circuit it is, 1-4 +uint8_t _getHeatingCircuit(_EMS_RxTelegram * EMS_RxTelegram) { + uint8_t hc_num; + switch (EMS_RxTelegram->type) { + case EMS_TYPE_RC35StatusMessage_HC1: + case EMS_TYPE_RC35Set_HC1: + default: + hc_num = 1; + break; + case EMS_TYPE_RC35StatusMessage_HC2: + case EMS_TYPE_RC35Set_HC2: + hc_num = 2; + break; + case EMS_TYPE_RC35StatusMessage_HC3: + case EMS_TYPE_RC35Set_HC3: + hc_num = 3; + break; + case EMS_TYPE_RC35StatusMessage_HC4: + case EMS_TYPE_RC35Set_HC4: + hc_num = 4; + break; + } + + return (hc_num); } /** - * type 0x3D and 0x47 - for reading the mode from the RC35 thermostat (0x10) - * Working Mode Heating Circuit 1 & 2 (HC1, HC2) + * type 0x3D (HC1), 0x47 (HC2), 0x51 (HC3), 0x5B (HC4) - Working Mode Heating - for reading the mode from the RC35 thermostat (0x10) * received only after requested */ void _process_RC35Set(_EMS_RxTelegram * EMS_RxTelegram) { - EMS_Thermostat.mode = _toByte(EMS_OFFSET_RC35Set_mode); + uint8_t hc_num = _getHeatingCircuit(EMS_RxTelegram); // which HC is it? - EMS_Thermostat.daytemp = _toByte(EMS_OFFSET_RC35Set_temp_day); // is * 2 - EMS_Thermostat.nighttemp = _toByte(EMS_OFFSET_RC35Set_temp_night); // is * 2 - EMS_Thermostat.holidaytemp = _toByte(EMS_OFFSET_RC35Set_temp_holiday); // is * 2 - EMS_Thermostat.heatingtype = _toByte(EMS_OFFSET_RC35Set_heatingtype); // byte 0 bit floor heating = 3 0x47 + EMS_Thermostat.hc[hc_num].mode = _toByte(EMS_OFFSET_RC35Set_mode); + EMS_Thermostat.hc[hc_num].daytemp = _toByte(EMS_OFFSET_RC35Set_temp_day); // is * 2 + EMS_Thermostat.hc[hc_num].nighttemp = _toByte(EMS_OFFSET_RC35Set_temp_night); // is * 2 + EMS_Thermostat.hc[hc_num].holidaytemp = _toByte(EMS_OFFSET_RC35Set_temp_holiday); // is * 2 + EMS_Thermostat.hc[hc_num].heatingtype = _toByte(EMS_OFFSET_RC35Set_heatingtype); // byte 0 bit floor heating = 3 EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } @@ -1703,12 +1757,23 @@ void _process_RCTime(_EMS_RxTelegram * EMS_RxTelegram) { return; // not supported } - EMS_Thermostat.hour = _toByte(2); - EMS_Thermostat.minute = _toByte(4); - EMS_Thermostat.second = _toByte(5); - EMS_Thermostat.day = _toByte(3); - EMS_Thermostat.month = _toByte(1); - EMS_Thermostat.year = _toByte(0); + // render time to HH:MM:SS DD/MM/YYYY + char time_sp[25]; + char buffer[4]; + + strlcpy(time_sp, _smallitoa(_toByte(2), buffer), sizeof(time_sp)); // hour + strlcat(time_sp, ":", sizeof(time_sp)); + strlcat(time_sp, _smallitoa(_toByte(4), buffer), sizeof(time_sp)); // minute + strlcat(time_sp, ":", sizeof(time_sp)); + strlcat(time_sp, _smallitoa(_toByte(5), buffer), sizeof(time_sp)); // second + strlcat(time_sp, " ", sizeof(time_sp)); + strlcat(time_sp, _smallitoa(_toByte(3), buffer), sizeof(time_sp)); // day + strlcat(time_sp, "/", sizeof(time_sp)); + strlcat(time_sp, _smallitoa(_toByte(1), buffer), sizeof(time_sp)); // month + strlcat(time_sp, "/", sizeof(time_sp)); + strlcat(time_sp, itoa(_toByte(0) + 2000, buffer, 10), sizeof(time_sp)); // year + + strlcpy(EMS_Thermostat.datetime, time_sp, sizeof(time_sp)); // store } /* @@ -2027,7 +2092,7 @@ void ems_getThermostatValues() { uint8_t model_id = EMS_Thermostat.model_id; uint8_t device_id = EMS_Thermostat.device_id; - uint8_t hc = EMS_Thermostat.hc; + uint8_t statusMsg, opMode; switch (model_id) { case EMS_MODEL_RC20: @@ -2043,12 +2108,22 @@ void ems_getThermostatValues() { break; case EMS_MODEL_RC35: case EMS_MODEL_ES73: - if (hc == 1) { - ems_doReadCommand(EMS_TYPE_RC35StatusMessage_HC1, device_id); // to get the temps - ems_doReadCommand(EMS_TYPE_RC35Set_HC1, device_id); // to get the mode - } else if (hc == 2) { - ems_doReadCommand(EMS_TYPE_RC35StatusMessage_HC2, device_id); // to get the temps - ems_doReadCommand(EMS_TYPE_RC35Set_HC2, device_id); // to get the mode + for (uint8_t hc_num = 1; hc_num <= EMS_THERMOSTAT_MAXHC; hc_num++) { + if (hc_num == 1) { + statusMsg = EMS_TYPE_RC35StatusMessage_HC1; + opMode = EMS_TYPE_RC35Set_HC1; + } else if (hc_num == 2) { + statusMsg = EMS_TYPE_RC35StatusMessage_HC2; + opMode = EMS_TYPE_RC35Set_HC2; + } else if (hc_num == 3) { + statusMsg = EMS_TYPE_RC35StatusMessage_HC3; + opMode = EMS_TYPE_RC35Set_HC3; + } else if (hc_num == 4) { + statusMsg = EMS_TYPE_RC35StatusMessage_HC4; + opMode = EMS_TYPE_RC35Set_HC4; + } + ems_doReadCommand(statusMsg, device_id); // to get the temps + ems_doReadCommand(opMode, device_id); // to get the mode } break; case EMS_MODEL_RC300: @@ -2482,13 +2557,13 @@ void ems_sendRawTelegram(char * telegram) { * Set the temperature of the thermostat * temptype 0 = normal, 1=night temp, 2=day temp, 3=holiday temp */ -void ems_setThermostatTemp(float temperature, uint8_t temptype) { +void ems_setThermostatTemp(float temperature, uint8_t hc_num, uint8_t temptype) { if (!ems_getThermostatEnabled()) { return; } if (!EMS_Thermostat.write_supported) { - myDebug_P(PSTR("Write not supported for this Thermostat model")); + myDebug_P(PSTR("Write not supported yet for this Thermostat model")); return; } @@ -2498,12 +2573,11 @@ void ems_setThermostatTemp(float temperature, uint8_t temptype) { uint8_t model_id = EMS_Thermostat.model_id; uint8_t device_id = EMS_Thermostat.device_id; - uint8_t hc = EMS_Thermostat.hc; // heating circuit EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE; EMS_TxTelegram.dest = device_id; - myDebug_P(PSTR("Setting new thermostat temperature")); + myDebug_P(PSTR("Setting new thermostat temperature for heating circuit %d"), hc_num); if (model_id == EMS_MODEL_RC20) { EMS_TxTelegram.type = EMS_TYPE_RC20Set; @@ -2526,10 +2600,10 @@ void ems_setThermostatTemp(float temperature, uint8_t temptype) { } else if (model_id == EMS_MODEL_RC300) { EMS_TxTelegram.type = EMS_TYPE_RCPLUSSet; // for 3000 and 1010, e.g. 0B 10 FF (0A | 08) 01 89 2B // check mode - if (EMS_Thermostat.mode == 1) { // auto - EMS_TxTelegram.offset = 0x08; // auto offset - } else if (EMS_Thermostat.mode == 0) { // manuaL - EMS_TxTelegram.offset = 0x0A; // manual offset + if (EMS_Thermostat.hc[hc_num].mode == 1) { // auto + EMS_TxTelegram.offset = 0x08; // auto offset + } else if (EMS_Thermostat.hc[hc_num].mode == 0) { // manuaL + EMS_TxTelegram.offset = 0x0A; // manual offset } // EMS_TxTelegram.type_validate = EMS_TYPE_RCPLUSStatusMessage; // validate by reading from a different telegram EMS_TxTelegram.type_validate = EMS_ID_NONE; // validate by reading from a different telegram @@ -2549,24 +2623,28 @@ void ems_setThermostatTemp(float temperature, uint8_t temptype) { break; default: case 0: // automatic selection, if no type is defined, we use the standard code - if (EMS_Thermostat.day_mode == 0) { + if (EMS_Thermostat.hc[hc_num].day_mode == 0) { EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp_night; - } else if (EMS_Thermostat.day_mode == 1) { + } else if (EMS_Thermostat.hc[hc_num].day_mode == 1) { EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp_day; } break; } - if (hc == 1) { - // heating circuit 1 + + if (hc_num == 1) { EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC1; EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC1; - EMS_TxTelegram.type_validate = EMS_TxTelegram.type; - } else { - // heating circuit 2 + } else if (hc_num == 2) { EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC2; EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC2; - EMS_TxTelegram.type_validate = EMS_TxTelegram.type; + } else if (hc_num == 3) { + EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC3; + EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC3; + } else if (hc_num == 4) { + EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC4; + EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC4; } + EMS_TxTelegram.type_validate = EMS_TxTelegram.type; } EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; @@ -2583,7 +2661,7 @@ void ems_setThermostatTemp(float temperature, uint8_t temptype) { * (0=low/night, 1=manual/day, 2=auto/clock), 0xA8 on a RC20 and 0xA7 on RC30 * 0x01B9 for EMS+ 300/1000/3000, Auto=0xFF Manual=0x00. See https://github.com/proddy/EMS-ESP/wiki/RC3xx-Thermostats */ -void ems_setThermostatMode(uint8_t mode) { +void ems_setThermostatMode(uint8_t mode, uint8_t hc_num) { if (!ems_getThermostatEnabled()) { return; } @@ -2595,7 +2673,6 @@ void ems_setThermostatMode(uint8_t mode) { uint8_t model_id = EMS_Thermostat.model_id; uint8_t device_id = EMS_Thermostat.device_id; - uint8_t hc = EMS_Thermostat.hc; // RC300/1000/3000 have different settings if (model_id == EMS_MODEL_RC300) { @@ -2606,7 +2683,7 @@ void ems_setThermostatMode(uint8_t mode) { } } - myDebug_P(PSTR("Setting thermostat mode to %d"), mode); + myDebug_P(PSTR("Setting thermostat mode to %d for heating circuit"), mode, hc_num); _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx EMS_TxTelegram.timestamp = millis(); // set timestamp @@ -2631,14 +2708,21 @@ void ems_setThermostatMode(uint8_t mode) { EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC30StatusMessage; } else if ((model_id == EMS_MODEL_RC35) || (model_id == EMS_MODEL_ES73)) { - EMS_TxTelegram.type = (hc == 2) ? EMS_TYPE_RC35Set_HC2 : EMS_TYPE_RC35Set_HC1; - EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_mode; - EMS_TxTelegram.type_validate = EMS_TxTelegram.type; - if (hc == 1) { + if (hc_num == 1) { + EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC1; EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC1; - } else { + } else if (hc_num == 2) { + EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC2; EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC2; + } else if (hc_num == 3) { + EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC3; + EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC3; + } else if (hc_num == 4) { + EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC4; + EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC4; } + EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_mode; + EMS_TxTelegram.type_validate = EMS_TxTelegram.type; } else if (model_id == EMS_MODEL_RC300) { EMS_TxTelegram.type = EMS_TYPE_RCPLUSSet; // for 3000 and 1010, e.g. 48 10 FF 00 01 B9 00 for manual diff --git a/src/ems.h b/src/ems.h index d1e5a12a..2b802104 100644 --- a/src/ems.h +++ b/src/ems.h @@ -81,16 +81,14 @@ #define EMS_ID_HP 0x38 // HeatPump #define EMS_ID_GATEWAY 0x48 // KM200 Web Gateway -#define EMS_PRODUCTID_HEATRONICS 95 // ProductID for a Junkers Heatronic3 device +// Product IDs +#define EMS_PRODUCTID_HEATRONICS 95 // Junkers Heatronic3 device +#define EMS_PRODUCTID_SM10 73 // SM10 solar module +#define EMS_PRODUCTID_SM100 163 // SM100 solar module +#define EMS_PRODUCTID_ISM1 101 // Junkers ISM1 solar module -#define EMS_PRODUCTID_SM10 73 // ProductID for SM10 solar module -#define EMS_PRODUCTID_SM100 163 // ProductID for SM10 solar module -#define EMS_PRODUCTID_ISM1 101 // ProductID for SM10 solar module - -#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC - -// max length of a telegram, including CRC, for Rx and Tx. -#define EMS_MAX_TELEGRAM_LENGTH 32 +#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC +#define EMS_MAX_TELEGRAM_LENGTH 32 // max length of a telegram, including CRC, for Rx and Tx. // default values for null values #define EMS_VALUE_INT_ON 1 // boolean true @@ -100,6 +98,8 @@ #define EMS_VALUE_USHORT_NOTSET 0x8000 // for 2-byte unsigned shorts #define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs +#define EMS_THERMOSTAT_MAXHC 4 // max number of heating circuits +#define EMS_THERMOSTAT_DEFAULTHC 1 // default heating circuit is 1 #define EMS_THERMOSTAT_WRITE_YES true #define EMS_THERMOSTAT_WRITE_NO false @@ -116,11 +116,11 @@ #define EMS_SYS_LOGGING_DEFAULT EMS_SYS_LOGGING_NONE // define the model types which get rendered to html colors in the web interface -#define EMS_MODELTYPE_BOILER 1 // success color +#define EMS_MODELTYPE_BOILER 1 // success color #define EMS_MODELTYPE_THERMOSTAT 2 // info color -#define EMS_MODELTYPE_SM 3 // warning color -#define EMS_MODELTYPE_HP 4 // success color -#define EMS_MODELTYPE_OTHER 5 // no color +#define EMS_MODELTYPE_SM 3 // warning color +#define EMS_MODELTYPE_HP 4 // success color +#define EMS_MODELTYPE_OTHER 5 // no color /* EMS UART transfer status */ typedef enum { @@ -273,7 +273,13 @@ typedef struct { /* * Telegram package defintions */ -typedef struct { // UBAParameterWW +typedef struct { + // settings + uint8_t device_id; // this is typically always 0x08 + uint8_t product_id; + char version[10]; + + // UBAParameterWW uint8_t wWActivated; // Warm Water activated uint8_t wWSelTemp; // Warm Water selected temperature uint8_t wWCircPump; // Warm Water circulation pump Available @@ -324,23 +330,19 @@ typedef struct { // UBAParameterWW uint8_t tapwaterActive; // Hot tap water is on/off uint8_t heatingActive; // Central heating is on/off - // settings - char version[10]; - uint8_t device_id; // this is typically always 0x08 - uint8_t product_id; } _EMS_Boiler; /* * Telegram package defintions for Other EMS devices */ - typedef struct { - uint8_t HPModulation; // heatpump modulation in % - uint8_t HPSpeed; // speed 0-100 % - uint8_t device_id; // the device ID of the Heat Pump (e.g. 0x30) - uint8_t model_id; // Solar Module / Heat Pump model (e.g. 3 > EMS_MODEL_OTHER ) + uint8_t device_id; // the device ID of the Heat Pump (e.g. 0x30) + uint8_t model_id; // Solar Module / Heat Pump model uint8_t product_id; char version[10]; + + uint8_t HPModulation; // heatpump modulation in % + uint8_t HPSpeed; // speed 0-100 % } _EMS_HeatPump; typedef struct { @@ -352,6 +354,11 @@ typedef struct { // SM Solar Module - SM10/SM100/ISM1 typedef struct { + uint8_t device_id; // the device ID of the Solar Module + uint8_t model_id; // Solar Module + uint8_t product_id; + char version[10]; + int16_t collectorTemp; // collector temp int16_t bottomTemp; // bottom temp uint8_t pumpModulation; // modulation solar pump @@ -361,35 +368,35 @@ typedef struct { uint16_t EnergyToday; uint16_t EnergyTotal; uint32_t pumpWorkMin; // Total solar pump operating time - uint8_t device_id; // the device ID of the Solar Module - uint8_t model_id; // Solar Module - uint8_t product_id; - char version[10]; } _EMS_SolarModule; -// Thermostat data +// heating circuit typedef struct { - uint8_t device_id; // the device ID of the thermostat - uint8_t model_id; // thermostat model - uint8_t product_id; - bool write_supported; - uint8_t hc; // heating circuit 1 or 2 - char version[10]; + uint8_t hc; // heating circuit 1,2, 3 or 4 + bool active; // true if there is data for this HC int16_t setpoint_roomTemp; // current set temp int16_t curr_roomTemp; // current room temp uint8_t mode; // 0=low, 1=manual, 2=auto bool day_mode; // 0=night, 1=day - uint8_t hour; - uint8_t minute; - uint8_t second; - uint8_t day; - uint8_t month; - uint8_t year; uint8_t daytemp; uint8_t nighttemp; uint8_t holidaytemp; - uint8_t heatingtype; - uint8_t circuitcalctemp; + uint8_t heatingtype; // type of heating: 1 radiator, 2 convectors, 3 floors, 4 room supply + uint8_t circuitcalctemp; // calculated setpoint flow temperature +} _EMS_Thermostat_HC; + +// Thermostat data +typedef struct { + uint8_t device_id; // the device ID of the thermostat + uint8_t model_id; // thermostat model + uint8_t product_id; + char version[10]; + + char datetime[25]; // HH:MM:SS DD/MM/YYYY + bool write_supported; + + _EMS_Thermostat_HC hc[EMS_THERMOSTAT_MAXHC - 1]; // array for the 4 heating circuits + } _EMS_Thermostat; // call back function signature for processing telegram types @@ -419,9 +426,8 @@ void ems_startupTelegrams(); bool ems_checkEMSBUSAlive(); void ems_clearDeviceList(); -void ems_setThermostatTemp(float temperature, uint8_t temptype = 0); -void ems_setThermostatMode(uint8_t mode); -void ems_setThermostatHC(uint8_t hc); +void ems_setThermostatTemp(float temperature, uint8_t hc, uint8_t temptype = 0); +void ems_setThermostatMode(uint8_t mode, uint8_t hc); void ems_setWarmWaterTemp(uint8_t temperature); void ems_setFlowTemp(uint8_t temperature); void ems_setWarmWaterActivated(bool activated); @@ -432,9 +438,10 @@ void ems_setEmsRefreshed(bool b); void ems_setWarmWaterModeComfort(uint8_t comfort); void ems_setModels(); void ems_setTxDisabled(bool b); -bool ems_getTxDisabled(); void ems_setTxMode(uint8_t mode); +uint8_t _getHeatingCircuit(_EMS_RxTelegram * EMS_RxTelegram); + char * ems_getThermostatDescription(char * buffer, bool name_only = false); char * ems_getBoilerDescription(char * buffer, bool name_only = false); char * ems_getSolarModuleDescription(char * buffer, bool name_only = false); @@ -456,6 +463,8 @@ uint8_t ems_getSolarModuleModel(); void ems_discoverModels(); bool ems_getTxCapable(); uint32_t ems_getPollFrequency(); +bool ems_getTxDisabled(); + // private functions uint8_t _crcCalculator(uint8_t * data, uint8_t len); diff --git a/src/ems_devices.h b/src/ems_devices.h index 85273de0..af34e0f9 100644 --- a/src/ems_devices.h +++ b/src/ems_devices.h @@ -91,12 +91,18 @@ // RC35 specific #define EMS_TYPE_RC35StatusMessage_HC1 0x3E // is an automatic thermostat broadcast giving us temps on HC1 #define EMS_TYPE_RC35StatusMessage_HC2 0x48 // is an automatic thermostat broadcast giving us temps on HC2 +#define EMS_TYPE_RC35StatusMessage_HC3 0x52 // is an automatic thermostat broadcast giving us temps on HC3 +#define EMS_TYPE_RC35StatusMessage_HC4 0x5C // is an automatic thermostat broadcast giving us temps on HC4 + #define EMS_OFFSET_RC35StatusMessage_setpoint 2 // desired temp #define EMS_OFFSET_RC35StatusMessage_curr 3 // current temp #define EMS_OFFSET_RC35StatusMessage_mode 1 //day mode #define EMS_TYPE_RC35Set_HC1 0x3D // for setting values like temp and mode (Working mode HC1) #define EMS_TYPE_RC35Set_HC2 0x47 // for setting values like temp and mode (Working mode HC2) +#define EMS_TYPE_RC35Set_HC3 0x51 // for setting values like temp and mode (Working mode HC3) +#define EMS_TYPE_RC35Set_HC4 0x5B // for setting values like temp and mode (Working mode HC4) + #define EMS_OFFSET_RC35Set_mode 7 // position of thermostat mode #define EMS_OFFSET_RC35Set_temp_day 2 // position of thermostat setpoint temperature for day time #define EMS_OFFSET_RC35Set_temp_night 1 // position of thermostat setpoint temperature for night time