From 72b82a23261d5b924e8fbc7d9d9fdc37e95ca0ba Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 6 Apr 2019 11:50:43 +0200 Subject: [PATCH] merged in lobo's code --- CHANGELOG.md | 1 + src/ems-esp.cpp | 122 ++++++++++++---- src/ems.cpp | 364 ++++++++++++++++++++++++---------------------- src/ems.h | 9 +- src/ems_devices.h | 9 +- src/my_config.h | 22 ++- src/version.h | 2 +- 7 files changed, 318 insertions(+), 211 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87fb4ca19..14d16f93b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Buderus Logamax plus - EMS+ support (thanks @GlennArens, @gl3nni3) - MQTT 'restart' topic to reboot ESP (thanks @balk77) +- Support for multiple thermostat heating circuits like the HC1/HC2 on a RC35, also via MQTT (thanks @lobocobra) ## [1.6.0] 2019-03-24 diff --git a/src/ems-esp.cpp b/src/ems-esp.cpp index b74988bb8..169eda506 100644 --- a/src/ems-esp.cpp +++ b/src/ems-esp.cpp @@ -34,6 +34,8 @@ DS18 ds18; // set to value >0 if the ESP is overheating or there are timing issues. Recommend a value of 1. #define EMSESP_DELAY 1 // initially set to 0 for no delay +#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_PUBLISHWAIT 120 // every 2 minutes publish MQTT values, including Dallas sensors Ticker publishValuesTimer; @@ -67,14 +69,15 @@ typedef struct { uint8_t dallas_sensors; // count of dallas sensors // custom params - bool shower_timer; // true if we want to report back on shower times - bool shower_alert; // true if we want the alert of cold water - bool led; // LED on/off - bool silent_mode; // stop automatic Tx on/off - uint16_t publish_wait; // frequency of MQTT publish in seconds - uint8_t led_gpio; - uint8_t dallas_gpio; - uint8_t dallas_parasite; + bool shower_timer; // true if we want to report back on shower times + bool shower_alert; // true if we want the alert of cold water + bool led; // LED on/off + bool silent_mode; // stop automatic Tx on/off + uint16_t publish_wait; // frequency of MQTT publish in seconds + 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 } _EMSESP_Status; typedef struct { @@ -97,6 +100,7 @@ command_t PROGMEM project_cmds[] = { {true, "shower_timer ", "notify via MQTT all shower durations"}, {true, "shower_alert ", "send a warning of cold water after shower time is exceeded"}, {true, "publish_wait ", "set frequency for publishing to MQTT"}, + {true, "heating_circuit <1 | 2>", "set the thermostat HC to work with if using multiple heating circuits"}, {false, "info", "show data captured on the EMS bus"}, {false, "log ", "set logging mode to none, basic, thermostat only, raw or verbose"}, @@ -615,12 +619,20 @@ void publishValues(bool force) { doc.clear(); JsonObject rootThermostat = doc.to(); + rootThermostat[THERMOSTAT_HC] = _int_to_char(s, EMSESP_Status.heating_circuit); + if ((ems_getThermostatModel() == EMS_MODEL_EASY) || (ems_getThermostatModel() == EMS_MODEL_BOSCHEASY)) { rootThermostat[THERMOSTAT_SELTEMP] = _short_to_char(s, EMS_Thermostat.setpoint_roomTemp, 10); rootThermostat[THERMOSTAT_CURRTEMP] = _short_to_char(s, EMS_Thermostat.curr_roomTemp, 10); } else { rootThermostat[THERMOSTAT_SELTEMP] = _int_to_char(s, EMS_Thermostat.setpoint_roomTemp, 2); rootThermostat[THERMOSTAT_CURRTEMP] = _int_to_char(s, EMS_Thermostat.curr_roomTemp, 10); + + rootThermostat[THERMOSTAT_DAYTEMP] = _int_to_char(s, EMS_Thermostat.daytemp, 2); + rootThermostat[THERMOSTAT_NIGHTTEMP] = _int_to_char(s, EMS_Thermostat.nighttemp, 2); + rootThermostat[THERMOSTAT_HOLIDAYTEMP] = _int_to_char(s, EMS_Thermostat.holidaytemp, 2); + rootThermostat[THERMOSTAT_HEATINGTYPE] = _int_to_char(s, EMS_Thermostat.heatingtype); + rootThermostat[THERMOSTAT_CIRCUITCALCTEMP] = _int_to_char(s, EMS_Thermostat.circuitcalctemp); } // RC20 has different mode settings @@ -749,7 +761,7 @@ void do_publishSensorValues() { void do_publishValues() { // don't publish if we're not connected to the EMS bus if ((ems_getBusConnected()) && (!myESP.getUseSerial()) && myESP.isMQTTConnected()) { - publishValues(false); + publishValues(true); // force publish } } @@ -845,9 +857,7 @@ bool FSCallback(MYESP_FSACTION action, const JsonObject json) { } // dallas_parasite - if (!(EMSESP_Status.dallas_parasite = json["dallas_parasite"])) { - EMSESP_Status.dallas_parasite = EMSESP_DALLAS_PARASITE; // default value - } + EMSESP_Status.dallas_parasite = json["dallas_parasite"]; // thermostat_type if (!(EMS_Thermostat.type_id = json["thermostat_type"])) { @@ -874,20 +884,28 @@ bool FSCallback(MYESP_FSACTION action, const JsonObject json) { EMSESP_Status.publish_wait = DEFAULT_PUBLISHWAIT; // default value } + // heating_circuit + if (!(EMSESP_Status.heating_circuit = json["heating_circuit"])) { + EMSESP_Status.heating_circuit = DEFAULT_HEATINGCIRCUIT; // default value + } + ems_setThermostatHC(EMSESP_Status.heating_circuit); + return recreate_config; // return false if some settings are missing and we need to rebuild the file } if (action == MYESP_FSACTION_SAVE) { + json["thermostat_type"] = EMS_Thermostat.type_id; + json["boiler_type"] = EMS_Boiler.type_id; + json["led"] = EMSESP_Status.led; json["led_gpio"] = EMSESP_Status.led_gpio; json["dallas_gpio"] = EMSESP_Status.dallas_gpio; json["dallas_parasite"] = EMSESP_Status.dallas_parasite; - json["thermostat_type"] = EMS_Thermostat.type_id; - json["boiler_type"] = EMS_Boiler.type_id; json["silent_mode"] = EMSESP_Status.silent_mode; json["shower_timer"] = EMSESP_Status.shower_timer; json["shower_alert"] = EMSESP_Status.shower_alert; json["publish_wait"] = EMSESP_Status.publish_wait; + json["heating_circuit"] = EMSESP_Status.heating_circuit; return true; } @@ -1005,6 +1023,18 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c EMSESP_Status.publish_wait = atoi(value); ok = true; } + + // heating_circuit + if ((strcmp(setting, "heating_circuit") == 0) && (wc == 2)) { + uint8_t hc = atoi(value); + if ((hc >= 1) && (hc <= 2)) { + EMSESP_Status.heating_circuit = hc; + ems_setThermostatHC(hc); + ok = true; + } else { + myDebug("Error. Usage: set heating_circuit <1 | 2>"); + } + } } if (action == MYESP_FSACTION_LIST) { @@ -1015,14 +1045,14 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c if (EMS_Thermostat.type_id == EMS_ID_NONE) { myDebug(" thermostat_type="); - } else { myDebug(" thermostat_type=%02X", EMS_Thermostat.type_id); } + myDebug(" heating_circuit=%d", EMSESP_Status.heating_circuit); + if (EMS_Boiler.type_id == EMS_ID_NONE) { myDebug(" boiler_type="); - } else { myDebug(" boiler_type=%02X", EMS_Boiler.type_id); } @@ -1225,7 +1255,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { char s[10] = {0}; myDebug("MQTT topic: thermostat temperature value %s", _float_to_char(s, f)); ems_setThermostatTemp(f); - publishValues(true); // publish back immediately + publishValues(true); // publish back immediately, can't remember why I do this?! } // thermostat mode changes @@ -1240,6 +1270,41 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { } } + // thermostat heating circuit change + if (strcmp(topic, TOPIC_THERMOSTAT_CMD_HC) == 0) { + myDebug("MQTT topic: thermostat heating circuit value %s", message); + uint8_t hc = atoi((char *)message); + if ((hc >= 1) && (hc <= 2)) { + EMSESP_Status.heating_circuit = hc; + ems_setThermostatHC(hc); + // TODO: save setting to SPIFFS?? + } + } + + // set night temp value + if (strcmp(topic, TOPIC_THERMOSTAT_CMD_NIGHTTEMP) == 0) { + float f = strtof((char *)message, 0); + char s[10] = {0}; + myDebug("MQTT topic: new thermostat night temperature value %s", _float_to_char(s, f)); + ems_setThermostatTemp(f,1); + } + + // set daytemp value + if (strcmp(topic, TOPIC_THERMOSTAT_CMD_DAYTEMP) == 0) { + float f = strtof((char *)message, 0); + char s[10] = {0}; + myDebug("MQTT topic: new thermostat day temperature value %s", _float_to_char(s, f)); + ems_setThermostatTemp(f,2); + } + + // set holiday value + if (strcmp(topic, TOPIC_THERMOSTAT_CMD_HOLIDAYTEMP) == 0) { + float f = strtof((char *)message, 0); + char s[10] = {0}; + myDebug("MQTT topic: new thermostat holiday temperature value %s", _float_to_char(s, f)); + ems_setThermostatTemp(f,3); + } + // wwActivated if (strcmp(topic, TOPIC_BOILER_WWACTIVATED) == 0) { if (message[0] == '1' || strcmp(message, "on") == 0) { @@ -1254,7 +1319,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) { uint8_t t = atoi((char *)message); myDebug("MQTT topic: boiler warm water temperature value %d", t); ems_setWarmWaterTemp(t); - publishValues(true); // publish back immediately + publishValues(true); // publish back immediately, can't remember why I do this?! } // boiler ww comfort setting @@ -1317,17 +1382,16 @@ void WIFICallback() { // Most of these will be overwritten after the SPIFFS config file is loaded void initEMSESP() { // general settings - EMSESP_Status.shower_timer = false; - EMSESP_Status.shower_alert = false; - EMSESP_Status.led = true; // LED is on by default - EMSESP_Status.silent_mode = false; - EMSESP_Status.publish_wait = DEFAULT_PUBLISHWAIT; - - EMSESP_Status.timestamp = millis(); - EMSESP_Status.dallas_sensors = 0; - - EMSESP_Status.led_gpio = EMSESP_LED_GPIO; - EMSESP_Status.dallas_gpio = EMSESP_DALLAS_GPIO; + EMSESP_Status.shower_timer = false; + EMSESP_Status.shower_alert = false; + EMSESP_Status.led = true; // LED is on by default + EMSESP_Status.silent_mode = false; + EMSESP_Status.publish_wait = DEFAULT_PUBLISHWAIT; + EMSESP_Status.timestamp = millis(); + EMSESP_Status.dallas_sensors = 0; + EMSESP_Status.led_gpio = EMSESP_LED_GPIO; + EMSESP_Status.dallas_gpio = EMSESP_DALLAS_GPIO; + EMSESP_Status.heating_circuit = 1; // default heating circuit // shower settings EMSESP_Shower.timerStart = 0; diff --git a/src/ems.cpp b/src/ems.cpp index 91ed18fe9..673ccc346 100644 --- a/src/ems.cpp +++ b/src/ems.cpp @@ -27,8 +27,6 @@ CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO #define _toLong(i) ((data[i] << 16) + (data[i + 1] << 8) + (data[i + 2])) #define _bitRead(i, bit) (((data[i]) >> (bit)) & 0x01) -void _printMessage(_EMS_RxTelegram * EMS_RxTelegram); - // // process callbacks per type // @@ -120,14 +118,16 @@ const _EMS_Type EMS_Types[] = { // RC35 {EMS_MODEL_RC35, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage, false}, {EMS_MODEL_RC35, EMS_TYPE_RCTime, "RCTime", _process_RCTime, false}, - {EMS_MODEL_RC35, EMS_TYPE_RC35Set, "RC35Set", _process_RC35Set, false}, - {EMS_MODEL_RC35, EMS_TYPE_RC35StatusMessage, "RC35StatusMessage", _process_RC35StatusMessage, false}, + {EMS_MODEL_RC35, EMS_TYPE_RC35Set_HC1, "RC35Set_HC1", _process_RC35Set, false}, + {EMS_MODEL_RC35, EMS_TYPE_RC35StatusMessage_HC1, "RC35StatusMessage_HC1", _process_RC35StatusMessage, false}, + {EMS_MODEL_RC35, EMS_TYPE_RC35Set_HC2, "RC35Set_HC2", _process_RC35Set, false}, + {EMS_MODEL_RC35, EMS_TYPE_RC35StatusMessage_HC2, "RC35StatusMessage_HC2", _process_RC35StatusMessage, false}, // ES73 {EMS_MODEL_ES73, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage, false}, {EMS_MODEL_ES73, EMS_TYPE_RCTime, "RCTime", _process_RCTime, false}, - {EMS_MODEL_ES73, EMS_TYPE_RC35Set, "RC35Set", _process_RC35Set, false}, - {EMS_MODEL_ES73, EMS_TYPE_RC35StatusMessage, "RC35StatusMessage", _process_RC35StatusMessage, false}, + {EMS_MODEL_ES73, EMS_TYPE_RC35Set_HC1, "RC35Set", _process_RC35Set, false}, + {EMS_MODEL_ES73, EMS_TYPE_RC35StatusMessage_HC1, "RC35StatusMessage", _process_RC35StatusMessage, false}, // Easy {EMS_MODEL_EASY, EMS_TYPE_EasyStatusMessage, "EasyStatusMessage", _process_EasyStatusMessage, false}, @@ -201,10 +201,16 @@ void ems_init() { EMS_Thermostat.year = 0; EMS_Thermostat.mode = 255; // dummy value EMS_Thermostat.day_mode = 255; // dummy value + EMS_Thermostat.type_id = EMS_ID_NONE; + EMS_Thermostat.read_supported = false; + 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 - EMS_Thermostat.type_id = EMS_ID_NONE; - EMS_Thermostat.read_supported = false; - EMS_Thermostat.write_supported = false; // UBAParameterWW EMS_Boiler.wWActivated = EMS_VALUE_INT_NOTSET; // Warm Water activated @@ -297,6 +303,10 @@ 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.type_id != EMS_ID_NONE); } @@ -701,6 +711,110 @@ void _ems_readTelegram(uint8_t * telegram, uint8_t length) { _processType(&EMS_RxTelegram); } +/* + * print the telegram + */ +void _printMessage(_EMS_RxTelegram * EMS_RxTelegram) { + uint8_t * telegram = EMS_RxTelegram->telegram; + + // header info + uint8_t src = telegram[0] & 0x7F; + uint8_t dest = telegram[1] & 0x7F; // remove 8th bit to handle both reads and writes + uint8_t type; + bool emsp; + + // check if EMS or EMS+ by checking 3rd byte of telegram + if (telegram[2] >= 0xF0) { + // EMS+ + type = telegram[3]; + emsp = true; + } else { + type = telegram[2]; + emsp = false; + } + + char output_str[200] = {0}; + char buffer[16] = {0}; + char color_s[20] = {0}; + + // source + if (src == EMS_Boiler.type_id) { + strlcpy(output_str, "Boiler", sizeof(output_str)); + } else if (src == EMS_Thermostat.type_id) { + if (emsp) + strlcpy(output_str, "Thermostat+", sizeof(output_str)); + else + strlcpy(output_str, "Thermostat", sizeof(output_str)); + } else { + strlcpy(output_str, "0x", sizeof(output_str)); + strlcat(output_str, _hextoa(src, buffer), sizeof(output_str)); + } + + strlcat(output_str, " -> ", sizeof(output_str)); + + // destination + if (dest == EMS_ID_ME) { + if (emsp) { + strlcat(output_str, "me", sizeof(output_str)); + strlcpy(color_s, COLOR_BRIGHT_YELLOW, sizeof(color_s)); + } else { + strlcat(output_str, "me", sizeof(output_str)); + strlcpy(color_s, COLOR_YELLOW, sizeof(color_s)); + } + } else if (dest == EMS_ID_NONE) { + if (emsp) { + strlcat(output_str, "all", sizeof(output_str)); + strlcpy(color_s, COLOR_BRIGHT_GREEN, sizeof(color_s)); + } else { + strlcat(output_str, "all", sizeof(output_str)); + strlcpy(color_s, COLOR_GREEN, sizeof(color_s)); + } + } else if (dest == EMS_Boiler.type_id) { + if (emsp) { + strlcat(output_str, "Boiler+", sizeof(output_str)); + strlcpy(color_s, COLOR_BRIGHT_MAGENTA, sizeof(color_s)); + } else { + strlcat(output_str, "Boiler", sizeof(output_str)); + strlcpy(color_s, COLOR_MAGENTA, sizeof(color_s)); + } + } else if (dest == EMS_ID_SM10) { + strlcat(output_str, "SM10", sizeof(output_str)); + strlcpy(color_s, COLOR_MAGENTA, sizeof(color_s)); + } else if (dest == EMS_Thermostat.type_id) { + if (emsp) { + strlcat(output_str, "Thermostat+", sizeof(output_str)); + strlcpy(color_s, COLOR_BRIGHT_MAGENTA, sizeof(color_s)); + } else { + strlcat(output_str, "Thermostat", sizeof(output_str)); + strlcpy(color_s, COLOR_MAGENTA, sizeof(color_s)); + } + } else { + if (emsp) { + strlcat(output_str, "0x", sizeof(output_str)); + strlcat(output_str, _hextoa(dest, buffer), sizeof(output_str)); + strlcpy(color_s, COLOR_BRIGHT_MAGENTA, sizeof(color_s)); + } else { + strlcat(output_str, "0x", sizeof(output_str)); + strlcat(output_str, _hextoa(dest, buffer), sizeof(output_str)); + strlcpy(color_s, COLOR_MAGENTA, sizeof(color_s)); + } + } + + // type + strlcat(output_str, ", type 0x", sizeof(output_str)); + strlcat(output_str, _hextoa(type, buffer), sizeof(output_str)); + + if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_THERMOSTAT) { + // only print ones to/from thermostat if logging is set to thermostat only + if ((src == EMS_Thermostat.type_id) || (dest == EMS_Thermostat.type_id)) { + _debugPrintTelegram(output_str, EMS_RxTelegram, color_s); + } + } else { + // always print + _debugPrintTelegram(output_str, EMS_RxTelegram, color_s); + } +} + /** * print detailed telegram * and then call its callback if there is one defined @@ -719,149 +833,9 @@ void _ems_processTelegram(_EMS_RxTelegram * EMS_RxTelegram) { uint8_t poffset = telegram[4]; uint8_t * pdata = &telegram[5 + poffset]; // data block starts at position 5 plus the offset - _printMessage(EMS_RxTelegram); - - // see if we recognize the type first by scanning our known EMS types list - // trying to match the type ID - bool commonType = false; - bool typeFound = false; - bool forUs = false; - int i = 0; - - while (i < _EMS_Types_max) { - if ((EMS_Types[i].type == type) || (EMS_Types[i].emsplus && type >= 240 && EMS_Types[i].type == ptype)) { - typeFound = true; - commonType = (EMS_Types[i].model_id == EMS_MODEL_ALL); // is it common type for everyone? - forUs = (src == EMS_Boiler.type_id) || (src == EMS_Thermostat.type_id); // is it for us? So the src must match - break; - } - i++; - } - - // if it's a common type (across ems devices) or something specifically for us process it. - // dest will be EMS_ID_NONE and offset 0x00 for a broadcast message - if (typeFound && (commonType || forUs)) { - if ((EMS_Types[i].processType_cb) != (void *)NULL) { - // print non-verbose message - if ((EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_BASIC) || (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE)) { - if (EMS_Types[i].emsplus) - myDebug("<--- %s(0x%02X) received", EMS_Types[i].typeString, ptype); - else - myDebug("<--- %s(0x%02X) received", EMS_Types[i].typeString, type); - } - // call callback function to process it - if (EMS_Types[i].emsplus && poffset == EMS_PLUS_ID_NONE) - (void)EMS_Types[i].processType_cb(ptype, pdata, length - 6 - poffset); - // as we only handle complete telegrams (not partial) check that the offset is 0 - else if (offset == EMS_ID_NONE && !EMS_Types[i].emsplus) { - (void)EMS_Types[i].processType_cb(type, data, length - 5); - } - } - } -} - -/* - * print the telegram - */ -void _printMessage(_EMS_RxTelegram * EMS_RxTelegram) { - uint8_t * telegram = EMS_RxTelegram->telegram; - - bool emsp = false; - uint8_t src = telegram[0] & 0x7F; - uint8_t dest = telegram[1] & 0x7F; // remove 8th bit to handle both reads and writes - uint8_t type = telegram[2]; - uint8_t offset = telegram[3]; - uint8_t * data = &telegram[4]; // data block starts at position 5 - - if (type >= 0xF0) { - type = telegram[3]; - offset = telegram[4]; - data = &telegram[5 + offset]; - emsp = true; - } - - // print detailed telegram data + // print out the telegram if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_THERMOSTAT) { - char output_str[200] = {0}; - char buffer[16] = {0}; - char color_s[20] = {0}; - - // source - if (src == EMS_Boiler.type_id) { - strlcpy(output_str, "Boiler", sizeof(output_str)); - } else if (src == EMS_Thermostat.type_id) { - if (emsp) - strlcpy(output_str, "Thermostat+", sizeof(output_str)); - else - strlcpy(output_str, "Thermostat", sizeof(output_str)); - } else { - strlcpy(output_str, "0x", sizeof(output_str)); - strlcat(output_str, _hextoa(src, buffer), sizeof(output_str)); - } - - strlcat(output_str, " -> ", sizeof(output_str)); - - // destination - if (dest == EMS_ID_ME) { - if (emsp) { - strlcat(output_str, "me", sizeof(output_str)); - strlcpy(color_s, COLOR_BRIGHT_YELLOW, sizeof(color_s)); - } else { - strlcat(output_str, "me", sizeof(output_str)); - strlcpy(color_s, COLOR_YELLOW, sizeof(color_s)); - } - } else if (dest == EMS_ID_NONE) { - if (emsp) { - strlcat(output_str, "all", sizeof(output_str)); - strlcpy(color_s, COLOR_BRIGHT_GREEN, sizeof(color_s)); - } else { - strlcat(output_str, "all", sizeof(output_str)); - strlcpy(color_s, COLOR_GREEN, sizeof(color_s)); - } - } else if (dest == EMS_Boiler.type_id) { - if (emsp) { - strlcat(output_str, "Boiler+", sizeof(output_str)); - strlcpy(color_s, COLOR_BRIGHT_MAGENTA, sizeof(color_s)); - } else { - strlcat(output_str, "Boiler", sizeof(output_str)); - strlcpy(color_s, COLOR_MAGENTA, sizeof(color_s)); - } - } else if (dest == EMS_ID_SM10) { - strlcat(output_str, "SM10", sizeof(output_str)); - strlcpy(color_s, COLOR_MAGENTA, sizeof(color_s)); - } else if (dest == EMS_Thermostat.type_id) { - if (emsp) { - strlcat(output_str, "Thermostat+", sizeof(output_str)); - strlcpy(color_s, COLOR_BRIGHT_MAGENTA, sizeof(color_s)); - } else { - strlcat(output_str, "Thermostat", sizeof(output_str)); - strlcpy(color_s, COLOR_MAGENTA, sizeof(color_s)); - } - } else { - if (emsp) { - strlcat(output_str, "0x", sizeof(output_str)); - strlcat(output_str, _hextoa(dest, buffer), sizeof(output_str)); - strlcpy(color_s, COLOR_BRIGHT_MAGENTA, sizeof(color_s)); - } else { - strlcat(output_str, "0x", sizeof(output_str)); - strlcat(output_str, _hextoa(dest, buffer), sizeof(output_str)); - strlcpy(color_s, COLOR_MAGENTA, sizeof(color_s)); - } - } - - // type - strlcat(output_str, ", type 0x", sizeof(output_str)); - strlcat(output_str, _hextoa(type, buffer), sizeof(output_str)); - - if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_THERMOSTAT) { - // only print ones to/from thermostat if logging is set to thermostat only - if ((src == EMS_Thermostat.type_id) || (dest == EMS_Thermostat.type_id)) { - _debugPrintTelegram(output_str, EMS_RxTelegram, color_s); - } - } else { - // always print - _debugPrintTelegram(output_str, EMS_RxTelegram, color_s); - } + _printMessage(EMS_RxTelegram); } // see if we recognize the type first by scanning our known EMS types list @@ -886,16 +860,22 @@ void _printMessage(_EMS_RxTelegram * EMS_RxTelegram) { if (typeFound) { if ((EMS_Types[i].processType_cb) != (void *)NULL) { // print non-verbose message - if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_BASIC) { - myDebug("<--- %s(0x%02X) received", EMS_Types[i].typeString, type); + if ((EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_BASIC) || (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE)) { + if (EMS_Types[i].emsplus) + myDebug("<--- %s(0x%02X) received", EMS_Types[i].typeString, ptype); + else + myDebug("<--- %s(0x%02X) received", EMS_Types[i].typeString, type); } // call callback function to process it + if (EMS_Types[i].emsplus && poffset == EMS_PLUS_ID_NONE) + (void)EMS_Types[i].processType_cb(ptype, pdata, length - 6 - poffset); // as we only handle complete telegrams (not partial) check that the offset is 0 - if (offset == 0) { - (void)EMS_Types[i].processType_cb(src, data, EMS_RxTelegram->length - 5); + else if (offset == EMS_ID_NONE && !EMS_Types[i].emsplus) { + (void)EMS_Types[i].processType_cb(type, data, length - 5); } } } + EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE; } @@ -1177,7 +1157,7 @@ void _process_RC30StatusMessage(uint8_t src, uint8_t * data, uint8_t length) { } /** - * type 0x3E - data from the RC35 thermostat (0x10) - 16 bytes + * type 0x3E and 0x48 - data from the RC35 thermostat (0x10) - 16 bytes * For reading the temp values only * received every 60 seconds */ @@ -1185,13 +1165,16 @@ void _process_RC35StatusMessage(uint8_t src, uint8_t * data, uint8_t length) { EMS_Thermostat.setpoint_roomTemp = _toByte(EMS_OFFSET_RC35StatusMessage_setpoint); // is * 2 // check if temp sensor is unavailable - if ((data[0] == 0x7D) && (data[1] = 0x00)) { + if (data[3] == 0x7D) { EMS_Thermostat.curr_roomTemp = EMS_VALUE_SHORT_NOTSET; } else { EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_RC35StatusMessage_curr); } - EMS_Thermostat.day_mode = bitRead(data[EMS_OFFSET_RC35Get_mode_day], 1); // get day mode flag - EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT + EMS_Thermostat.day_mode = bitRead(data[EMS_OFFSET_RC35Get_mode_day], 1); // get day mode flag + + EMS_Thermostat.circuitcalctemp = data[EMS_OFFSET_RC35Set_circuitcalctemp]; // 0x48 calculated temperature Vorlauf bit 14 + + EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } /** @@ -1216,7 +1199,7 @@ void _process_RC1010StatusMessage(uint8_t type, uint8_t * data, uint8_t length) } void _process_RC1010SetMessage(uint8_t type, uint8_t * data, uint8_t length) { - // to complete + // to complete } /** @@ -1244,12 +1227,19 @@ void _process_RC30Set(uint8_t src, uint8_t * data, uint8_t length) { } /** - * type 0x3D - for reading the mode from the RC35 thermostat (0x10) - * Working Mode Heating Circuit 1 (HC1) + * type 0x3D and 0x47 - for reading the mode from the RC35 thermostat (0x10) + * Working Mode Heating Circuit 1 & 2 (HC1, HC2) * received only after requested */ void _process_RC35Set(uint8_t src, uint8_t * data, uint8_t length) { EMS_Thermostat.mode = _toByte(EMS_OFFSET_RC35Set_mode); + + 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_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT } /** @@ -1524,6 +1514,7 @@ void ems_getThermostatValues() { uint8_t model_id = EMS_Thermostat.model_id; uint8_t type = EMS_Thermostat.type_id; + uint8_t hc = EMS_Thermostat.hc; if (model_id == EMS_MODEL_RC20) { ems_doReadCommand(EMS_TYPE_RC20StatusMessage, type); // to get the setpoint temp @@ -1532,8 +1523,13 @@ void ems_getThermostatValues() { ems_doReadCommand(EMS_TYPE_RC30StatusMessage, type); // to get the setpoint temp ems_doReadCommand(EMS_TYPE_RC30Set, type); // to get the mode } else if ((model_id == EMS_MODEL_RC35) || (model_id == EMS_MODEL_ES73)) { - ems_doReadCommand(EMS_TYPE_RC35StatusMessage, type); // to get the setpoint temp - ems_doReadCommand(EMS_TYPE_RC35Set, type); // to get the mode + if (hc == 1) { + ems_doReadCommand(EMS_TYPE_RC35StatusMessage_HC1, type); // to get the setpoint temp + ems_doReadCommand(EMS_TYPE_RC35Set_HC1, type); // to get the mode + } else if (hc == 2) { + ems_doReadCommand(EMS_TYPE_RC35StatusMessage_HC2, type); // to get the setpoint temp + ems_doReadCommand(EMS_TYPE_RC35Set_HC2, type); // to get the mode + } } else if ((model_id == EMS_MODEL_EASY) || (model_id == EMS_MODEL_BOSCHEASY)) { ems_doReadCommand(EMS_TYPE_EasyStatusMessage, type); } @@ -1640,7 +1636,7 @@ char * ems_getBoilerDescription(char * buffer) { * Find the versions of our connected devices */ void ems_scanDevices() { - myDebug("Started scan of EMS bus for known devices"); + myDebug("Started scan on EMS bus for known devices"); std::list Device_Ids; // create a new list @@ -1814,8 +1810,9 @@ 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) { +void ems_setThermostatTemp(float temperature, uint8_t temptype) { if (!ems_getThermostatEnabled()) { return; } @@ -1831,6 +1828,7 @@ void ems_setThermostatTemp(float temperature) { uint8_t model_id = EMS_Thermostat.model_id; uint8_t type = EMS_Thermostat.type_id; + uint8_t hc = EMS_Thermostat.hc; // heating circuit EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE; EMS_TxTelegram.dest = type; @@ -1852,14 +1850,35 @@ void ems_setThermostatTemp(float temperature) { EMS_TxTelegram.offset = EMS_OFFSET_RC30Set_temp; EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC30StatusMessage; } else if ((model_id == EMS_MODEL_RC35) || (model_id == EMS_MODEL_ES73)) { - EMS_TxTelegram.type = EMS_TYPE_RC35Set; - if (EMS_Thermostat.day_mode == 0) { + switch (temptype) { + case 1: // change the night temp EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp_night; - } else if (EMS_Thermostat.day_mode == 1) { + break; + case 2: // change the day temp EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp_day; + break; + case 3: // change the holiday temp + EMS_TxTelegram.offset = 3; //holiday on RC35 + break; + default: + case 0: // automatic selection, if no type is defined, we use the standard code + if (EMS_Thermostat.day_mode == 0) { + EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp_night; + } else if (EMS_Thermostat.day_mode == 1) { + EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp_day; + } + break; } - EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage; + if (hc == 1) { + // heating circuit 1 + EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC1; + EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC1; + } else { + // heating circuit 2 + EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC2; + EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC2; + } } EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; @@ -1868,7 +1887,7 @@ void ems_setThermostatTemp(float temperature) { EMS_TxTelegram.comparisonOffset = EMS_TxTelegram.offset; EMS_TxTelegram.comparisonValue = EMS_TxTelegram.dataValue; - EMS_TxTelegram.forceRefresh = false; // send to MQTT is done automatically in EMS_TYPE_RC30StatusMessage + EMS_TxTelegram.forceRefresh = false; // send to MQTT is done automatically in EMS_TYPE_RC*StatusMessage EMS_TxQueue.push(EMS_TxTelegram); } @@ -1888,6 +1907,7 @@ void ems_setThermostatMode(uint8_t mode) { uint8_t model_id = EMS_Thermostat.model_id; uint8_t type = EMS_Thermostat.type_id; + uint8_t hc = EMS_Thermostat.hc; myDebug("Setting thermostat mode to %d", mode); @@ -1908,7 +1928,7 @@ void ems_setThermostatMode(uint8_t mode) { EMS_TxTelegram.type = EMS_TYPE_RC30Set; EMS_TxTelegram.offset = EMS_OFFSET_RC30Set_mode; } else if ((model_id == EMS_MODEL_RC35) || (model_id == EMS_MODEL_ES73)) { - EMS_TxTelegram.type = EMS_TYPE_RC35Set; + EMS_TxTelegram.type = (hc == 2) ? EMS_TYPE_RC35Set_HC2 : EMS_TYPE_RC35Set_HC1; EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_mode; } diff --git a/src/ems.h b/src/ems.h index c1f0d3e4d..7533e7af3 100644 --- a/src/ems.h +++ b/src/ems.h @@ -241,6 +241,7 @@ typedef struct { uint8_t product_id; bool read_supported; bool write_supported; + uint8_t hc; // heating circuit 1 or 2 char version[10]; int16_t setpoint_roomTemp; // current set temp int16_t curr_roomTemp; // current room temp @@ -252,6 +253,11 @@ typedef struct { uint8_t day; uint8_t month; uint8_t year; + uint8_t daytemp; + uint8_t nighttemp; + uint8_t holidaytemp; + uint8_t heatingtype; + uint8_t circuitcalctemp; } _EMS_Thermostat; // call back function signature for processing telegram types @@ -272,8 +278,9 @@ void ems_init(); void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh = false); void ems_sendRawTelegram(char * telegram); -void ems_setThermostatTemp(float temp); +void ems_setThermostatTemp(float temperature, uint8_t temptype = 0); void ems_setThermostatMode(uint8_t mode); +void ems_setThermostatHC(uint8_t hc); void ems_setWarmWaterTemp(uint8_t temperature); void ems_setWarmWaterActivated(bool activated); void ems_setWarmTapWaterActivated(bool activated); diff --git a/src/ems_devices.h b/src/ems_devices.h index 28cef1d53..79d7eb69d 100644 --- a/src/ems_devices.h +++ b/src/ems_devices.h @@ -74,14 +74,19 @@ #define EMS_OFFSET_RC30StatusMessage_curr 2 // current temp // RC35 specific -#define EMS_TYPE_RC35StatusMessage 0x3E // is an automatic thermostat broadcast giving us temps -#define EMS_TYPE_RC35Set 0x3D // for setting values like temp and mode (Working mode HC1) +#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_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_OFFSET_RC35StatusMessage_setpoint 2 // desired temp #define EMS_OFFSET_RC35StatusMessage_curr 3 // current temp #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 #define EMS_OFFSET_RC35Get_mode_day 1 // position of thermostat day mode +#define EMS_OFFSET_RC35Set_temp_holiday 3 // temp during holiday 0x47 +#define EMS_OFFSET_RC35Set_heatingtype 0 // floor heating = 3 0x47 +#define EMS_OFFSET_RC35Set_circuitcalctemp 14 // calculated circuit temperature 0x48 // Easy specific #define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat diff --git a/src/my_config.h b/src/my_config.h index 98b8559f3..364fddb58 100644 --- a/src/my_config.h +++ b/src/my_config.h @@ -23,12 +23,22 @@ #define MQTT_MAX_SIZE 700 // max size of a JSON object. See https://arduinojson.org/v6/assistant/ // MQTT for thermostat -#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values to MQTT -#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes via MQTT -#define TOPIC_THERMOSTAT_CMD_MODE "thermostat_cmd_mode" // for received thermostat mode changes via MQTT -#define THERMOSTAT_CURRTEMP "thermostat_currtemp" // current temperature -#define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature -#define THERMOSTAT_MODE "thermostat_mode" // mode +#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values to MQTT +#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes via MQTT +#define TOPIC_THERMOSTAT_CMD_MODE "thermostat_cmd_mode" // for received thermostat mode changes via MQTT +#define TOPIC_THERMOSTAT_CMD_HC "thermostat_cmd_hc" // for received thermostat hc number changes via MQTT +#define TOPIC_THERMOSTAT_CMD_DAYTEMP "thermostat_daytemp" // RC35 specific +#define TOPIC_THERMOSTAT_CMD_NIGHTTEMP "thermostat_nighttemp" // RC35 specific +#define TOPIC_THERMOSTAT_CMD_HOLIDAYTEMP "thermostat_holidayttemp" // RC35 specific +#define THERMOSTAT_CURRTEMP "thermostat_currtemp" // current temperature +#define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature +#define THERMOSTAT_HC "thermostat_hc" // which heating circuit number +#define THERMOSTAT_MODE "thermostat_mode" // mode +#define THERMOSTAT_DAYTEMP "thermostat_daytemp" // RC35 specific +#define THERMOSTAT_NIGHTTEMP "thermostat_nighttemp" // RC35 specific +#define THERMOSTAT_HOLIDAYTEMP "thermostat_holidayttemp" // RC35 specific +#define THERMOSTAT_HEATINGTYPE "themrostat_heatingtype" // RC35 specific (3=floorheating) +#define THERMOSTAT_CIRCUITCALCTEMP "thermostat_circuitcalctemp" // RC35 specific // MQTT for boiler #define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values to MQTT diff --git a/src/version.h b/src/version.h index bd3a30fc0..c5146089c 100644 --- a/src/version.h +++ b/src/version.h @@ -6,5 +6,5 @@ #pragma once #define APP_NAME "EMS-ESP" -#define APP_VERSION "1.7.0b2" +#define APP_VERSION "1.7.0b3" #define APP_HOSTNAME "ems-esp"