From a5103513f2fe6ad7a3ada85eff97c079abf2fee1 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 16 Mar 2019 12:51:33 +0100 Subject: [PATCH] warm water modes --- src/ems-esp.ino | 183 ++++++++++++++++++++++++++++++---------------- src/ems.cpp | 31 ++++---- src/ems.h | 2 +- src/ems_devices.h | 11 +-- 4 files changed, 146 insertions(+), 81 deletions(-) diff --git a/src/ems-esp.ino b/src/ems-esp.ino index 43c521f3a..7f1bc9b21 100644 --- a/src/ems-esp.ino +++ b/src/ems-esp.ino @@ -59,18 +59,20 @@ Ticker showerColdShotStopTimer; #define SHOWER_MIN_DURATION 120000 // in ms. 2 minutes, before recognizing its a shower #define SHOWER_OFFSET_TIME 5000 // in ms. 5 seconds grace time, to calibrate actual time under the shower #define SHOWER_COLDSHOT_DURATION 10 // in seconds. 10 seconds for cold water before turning back hot water +#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water typedef struct { - 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_enabled; // LED on/off - bool test_mode; // test mode to stop automatic Tx on/off - unsigned long timestamp; // for internal timings, via millis() uint8_t dallas_sensors; // count of dallas sensors - uint8_t led_gpio; - uint8_t dallas_gpio; - uint8_t dallas_parasite; + + // 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_enabled; // LED on/off + bool test_mode; // test mode to stop automatic Tx on/off + uint8_t led_gpio; + uint8_t dallas_gpio; + uint8_t dallas_parasite; } _EMSESP_Status; typedef struct { @@ -83,27 +85,30 @@ typedef struct { command_t PROGMEM project_cmds[] = { - {"set led ", "toggle status LED on/off"}, - {"set led_gpio ", "set the LED pin. Default is the onboard LED (D1=5)"}, - {"set dallas_gpio ", "set the pin for external Dallas temperature sensors (D5=14)"}, - {"set thermostat_type ", "set the thermostat type id (e.g. 10 for 0x10)"}, - {"set boiler_type ", "set the boiler type id (e.g. 8 for 0x08)"}, - {"set test_mode ", "test_mode turns off all automatic reads"}, - {"info", "show data captured on the EMS bus"}, - {"log ", "set logging mode to none, basic, thermostat only, raw or verbose"}, - {"publish", "publish all values to MQTT"}, - {"types", "list supported EMS telegram type IDs"}, - {"queue", "show current Tx queue"}, - {"autodetect", "discover EMS devices and attempt to automatically set boiler and thermostat"}, - {"shower ", "toggle either timer or alert on/off"}, - {"send XX ...", "send raw telegram data as hex to EMS bus"}, - {"thermostat read ", "send read request to the thermostat"}, - {"thermostat temp ", "set current thermostat temperature"}, - {"thermostat mode ", "set mode (0=low/night, 1=manual/day, 2=auto)"}, - {"thermostat scan ", "do a read on all type IDs"}, - {"boiler read ", "send read request to boiler"}, - {"boiler wwtemp ", "set boiler warm water temperature"}, - {"boiler tapwater ", "set boiler warm tap water on/off"} + {true, "led ", "toggle status LED on/off"}, + {true, "led_gpio ", "set the LED pin. Default is the onboard LED (D1=5)"}, + {true, "dallas_gpio ", "set the pin for external Dallas temperature sensors (D5=14)"}, + {true, "dallas_parasite ", "set to on if powering Dallas via parasite"}, + {true, "thermostat_type ", "set the thermostat type id (e.g. 10 for 0x10)"}, + {true, "boiler_type ", "set the boiler type id (e.g. 8 for 0x08)"}, + {true, "test_mode ", "test_mode turns off all automatic reads"}, + {true, "shower_timer ", "notify on shower durations"}, + {true, "shower_alert ", "send a warning of cold water after shower time is exceeded"}, + {false, "info", "show data captured on the EMS bus"}, + {false, "log ", "set logging mode to none, basic, thermostat only, raw or verbose"}, + {false, "publish", "publish all values to MQTT"}, + {false, "types", "list supported EMS telegram type IDs"}, + {false, "queue", "show current Tx queue"}, + {false, "autodetect", "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 scan ", "do a read on all type IDs"}, + {false, "boiler read ", "send read request to boiler"}, + {false, "boiler wwtemp ", "set boiler warm water temperature"}, + {false, "boiler tapwater ", "set boiler warm tap water on/off"} }; @@ -306,9 +311,14 @@ void showInfo() { // UBAParameterWW _renderBoolValue("Warm Water activated", EMS_Boiler.wWActivated); _renderBoolValue("Warm Water circulation pump available", EMS_Boiler.wWCircPump); - if (EMS_Boiler.wWComfort != EMS_VALUE_INT_NOTSET) { - myDebug(" Warm Water is set to %s", (EMS_Boiler.wWComfort ? "Comfort" : "ECO")); + if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Hot) { + myDebug(" Warm Water comfort is set to Hot"); + } else if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Eco) { + myDebug(" Warm Water comfort is set to Eco"); + } else if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Intelligent) { + myDebug(" Warm Water comfort is set to Intelligent"); } + _renderIntValue("Warm Water selected temperature", "C", EMS_Boiler.wWSelTemp); _renderIntValue("Warm Water desired temperature", "C", EMS_Boiler.wWDesiredTemp); @@ -337,7 +347,11 @@ void showInfo() { _renderIntValue("Burner current power", "%", EMS_Boiler.curBurnPow); _renderFloatValue("Flame current", "uA", EMS_Boiler.flameCurr); _renderFloatValue("System pressure", "bar", EMS_Boiler.sysPress); - myDebug(" System Service Code: %s (%d)", EMS_Boiler.serviceCodeChar, EMS_Boiler.serviceCode); + if (EMS_Boiler.serviceCode == EMS_VALUE_SHORT_NOTSET) { + myDebug(" System service code: %s", EMS_Boiler.serviceCodeChar); + } else { + myDebug(" System service code: %s (%d)", EMS_Boiler.serviceCodeChar, EMS_Boiler.serviceCode); + } // UBAParametersMessage _renderIntValue("Heating temperature setting on the boiler", "C", EMS_Boiler.heating_temp); @@ -402,10 +416,11 @@ void showInfo() { // Dallas if (EMSESP_Status.dallas_sensors != 0) { - char s[80] = {0}; + char s[80] = {0}; + char buffer[128] = {0}; myDebug("%sExternal temperature sensors:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF); - for (uint8_t i = 0; i < EMSESP_Status.dallas_sensors; i++) { - snprintf(s, sizeof(s), "Sensor #%d", i + 1); + for (uint8_t i = 0; i < EMSESP_Status.dallas_sensors; i++) { + snprintf(s, sizeof(s), "Sensor #%d %s", i + 1, ds18.getDeviceString(buffer, i)); _renderFloatValue(s, "C", ds18.getValue(i)); } myDebug(""); // newline @@ -421,19 +436,18 @@ void showInfo() { // send all dallas sensor values as a JSON package to MQTT void publishSensorValues() { StaticJsonDocument doc; - bool hasdata = false; + bool hasdata = false; - // see if the sensor values have changed, if so send - //JsonObject & sensors = jsonBuffer.createObject(); + // see if the sensor values have changed, if so send JsonObject sensors = doc.to(); for (uint8_t i = 0; i < EMSESP_Status.dallas_sensors; i++) { double sensorValue = ds18.getValue(i); - if(sensorValue != DS18_DISCONNECTED && sensorValue != DS18_CRC_ERROR) { - char label[8] = {0}; - char valuestr[8] = {0}; // for formatting temp - sprintf(label,"temp_%d",(i+1)); + if (sensorValue != DS18_DISCONNECTED && sensorValue != DS18_CRC_ERROR) { + char label[8] = {0}; + char valuestr[8] = {0}; // for formatting temp + sprintf(label, "temp_%d", (i + 1)); sensors[label] = _float_to_char(valuestr, sensorValue); - hasdata = true; + hasdata = true; } } @@ -455,8 +469,8 @@ void publishValues(bool force) { uint32_t fchecksum; static uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off - static uint32_t previousBoilerPublishCRC = 0; // CRC check - static uint32_t previousThermostatPublishCRC = 0; // CRC check + static uint32_t previousBoilerPublishCRC = 0; // CRC check for boiler values + static uint32_t previousThermostatPublishCRC = 0; // CRC check for thermostat values JsonObject rootBoiler = doc.to(); @@ -464,8 +478,16 @@ void publishValues(bool force) { rootBoiler["selFlowTemp"] = _float_to_char(s, EMS_Boiler.selFlowTemp); rootBoiler["outdoorTemp"] = _float_to_char(s, EMS_Boiler.extTemp); rootBoiler["wWActivated"] = _bool_to_char(s, EMS_Boiler.wWActivated); - rootBoiler["wWComfort"] = EMS_Boiler.wWComfort ? "Comfort" : "ECO"; - rootBoiler["wWCurTmp"] = _float_to_char(s, EMS_Boiler.wWCurTmp); + + if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Hot) { + rootBoiler["wWComfort"] = "Hot"; + } else if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Eco) { + rootBoiler["wWComfort"] = "Eco"; + } else if (EMS_Boiler.wWComfort == EMS_VALUE_UBAParameterWW_wwComfort_Intelligent) { + rootBoiler["wWComfort"] = "Intelligent"; + } + + rootBoiler["wWCurTmp"] = _float_to_char(s, EMS_Boiler.wWCurTmp); snprintf(s, sizeof(s), "%i.%i", EMS_Boiler.wWCurFlow / 10, EMS_Boiler.wWCurFlow % 10); rootBoiler["wWCurFlow"] = s; rootBoiler["wWHeat"] = _bool_to_char(s, EMS_Boiler.wWHeat); @@ -637,8 +659,8 @@ bool FSCallback(MYESP_FSACTION action, const JsonObject json) { EMSESP_Status.dallas_gpio = EMSESP_DALLAS_GPIO; // default value } - // dallas_gpio - if (!(EMSESP_Status.dallas_gpio = json["dallas_parasite"])) { + // dallas_parasite + if (!(EMSESP_Status.dallas_parasite = json["dallas_parasite"])) { EMSESP_Status.dallas_parasite = EMSESP_DALLAS_PARASITE; // default value } @@ -657,6 +679,16 @@ bool FSCallback(MYESP_FSACTION action, const JsonObject json) { EMSESP_Status.test_mode = false; // default value } + // shower_timer + if (!(EMSESP_Status.shower_timer = json["shower_timer"])) { + EMSESP_Status.shower_timer = false; // default value + } + + // shower_alert + if (!(EMSESP_Status.shower_alert = json["shower_alert"])) { + EMSESP_Status.shower_alert = false; // default value + } + return false; // always save the settings } @@ -668,6 +700,8 @@ bool FSCallback(MYESP_FSACTION action, const JsonObject json) { json["thermostat_type"] = EMS_Thermostat.type_id; json["boiler_type"] = EMS_Boiler.type_id; json["test_mode"] = EMSESP_Status.test_mode; + json["shower_timer"] = EMSESP_Status.shower_timer; + json["shower_alert"] = EMSESP_Status.shower_alert; return true; } @@ -691,7 +725,8 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c EMSESP_Status.led_enabled = false; ok = true; // let's make sure LED is really off - digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off + digitalWrite(EMSESP_Status.led_gpio, + (EMSESP_Status.led_gpio == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off } } @@ -724,12 +759,12 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c // dallas_parasite if ((strcmp(setting, "dallas_parasite") == 0) && (wc == 2)) { - if (strcmp(value, "true") == 0) { + if (strcmp(value, "on") == 0) { EMSESP_Status.dallas_parasite = true; - ok = true; - } else if (strcmp(value, "false") == 0) { + ok = true; + } else if (strcmp(value, "off") == 0) { EMSESP_Status.dallas_parasite = false; - ok = true; + ok = true; } } @@ -744,10 +779,31 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c EMS_Boiler.type_id = ((wc == 2) ? (uint8_t)strtol(value, 0, 16) : EMS_ID_NONE); ok = true; } + + // shower timer + if ((strcmp(setting, "shower_timer") == 0) && (wc == 2)) { + if (strcmp(value, "on") == 0) { + EMSESP_Status.shower_timer = true; + ok = true; + } else if (strcmp(value, "off") == 0) { + EMSESP_Status.shower_timer = false; + ok = true; + } + } + + // shower alert + if ((strcmp(setting, "shower_alert") == 0) && (wc == 2)) { + if (strcmp(value, "on") == 0) { + EMSESP_Status.shower_alert = true; + ok = true; + } else if (strcmp(value, "off") == 0) { + EMSESP_Status.shower_alert = false; + ok = true; + } + } } if (action == MYESP_FSACTION_LIST) { - myDebug(" test_mode=%s", EMSESP_Status.test_mode ? "on" : "off"); myDebug(" led=%s", EMSESP_Status.led_enabled ? "on" : "off"); myDebug(" led_gpio=%d", EMSESP_Status.led_gpio); myDebug(" dallas_gpio=%d", EMSESP_Status.dallas_gpio); @@ -766,6 +822,10 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c } else { myDebug(" boiler_type=%02X", EMS_Boiler.type_id); } + + myDebug(" test_mode=%s", EMSESP_Status.test_mode ? "on" : "off"); + myDebug(" shower_timer=%s", EMSESP_Status.shower_timer ? "on" : "off"); + myDebug(" shower_alert=%s", EMSESP_Status.shower_alert ? "on" : "off"); } return ok; @@ -1015,7 +1075,7 @@ void WIFICallback() { // This is done after we have a WiFi signal to avoid any resource conflicts if (myESP.getUseSerial()) { - myDebug("Warning! EMS bus disabled when in Serial mode. Use 'set serial off' to enable."); + myDebug("Warning! EMS bus disabled when in Serial mode. Use 'set serial off' to start EMS."); } else { emsuart_init(); myDebug("[UART] Opened Rx/Tx connection"); @@ -1027,8 +1087,8 @@ void WIFICallback() { // Initialize the boiler settings and shower settings void initEMSESP() { // general settings - EMSESP_Status.shower_timer = BOILER_SHOWER_TIMER; - EMSESP_Status.shower_alert = BOILER_SHOWER_ALERT; + EMSESP_Status.shower_timer = false; + EMSESP_Status.shower_alert = false; EMSESP_Status.led_enabled = true; // LED is on by default EMSESP_Status.test_mode = false; @@ -1230,9 +1290,9 @@ void setup() { // enable regular checks if not in test mode if (!EMSESP_Status.test_mode) { - publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values - regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS - publishSensorValuesTimer.attach(PUBLISHSENSORVALUES_TIME, do_publishSensorValues); // post MQTT sensor values + publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values + regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS + publishSensorValuesTimer.attach(PUBLISHSENSORVALUES_TIME, do_publishSensorValues); // post MQTT sensor values } // set pin for LED @@ -1244,7 +1304,6 @@ void setup() { // check for Dallas sensors EMSESP_Status.dallas_sensors = ds18.setup(EMSESP_Status.dallas_gpio, EMSESP_Status.dallas_parasite); // returns #sensors - } // diff --git a/src/ems.cpp b/src/ems.cpp index 8d7d93f74..7e68e3d51 100644 --- a/src/ems.cpp +++ b/src/ems.cpp @@ -892,7 +892,7 @@ void _process_UBAParameterWW(uint8_t type, uint8_t * data, uint8_t length) { EMS_Boiler.wWSelTemp = data[2]; EMS_Boiler.wWCircPump = (data[6] == 0xFF); // 0xFF means on EMS_Boiler.wWDesiredTemp = data[8]; - EMS_Boiler.wWComfort = (data[EMS_OFFSET_UBAParameterWW_wwComfort] == 0x00); + EMS_Boiler.wWComfort = data[EMS_OFFSET_UBAParameterWW_wwComfort]; EMS_Sys_Status.emsRefreshed = true; // when we receieve this, lets force an MQTT publish } @@ -1098,7 +1098,6 @@ void _process_Version(uint8_t type, uint8_t * data, uint8_t length) { return; } - bool do_save = false; uint8_t product_id = data[0]; char version[10] = {0}; snprintf(version, sizeof(version), "%02d.%02d", data[1], data[2]); @@ -1135,7 +1134,7 @@ void _process_Version(uint8_t type, uint8_t * data, uint8_t length) { EMS_Boiler.product_id = Boiler_Types[i].product_id; strlcpy(EMS_Boiler.version, version, sizeof(EMS_Boiler.version)); - do_save = true; + myESP.fs_saveConfig(); // save config to SPIFFS ems_getBoilerValues(); // get Boiler values that we would usually have to wait for } @@ -1178,7 +1177,7 @@ void _process_Version(uint8_t type, uint8_t * data, uint8_t length) { EMS_Thermostat.product_id = product_id; strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version)); - do_save = true; + myESP.fs_saveConfig(); // save config to SPIFFS // get Thermostat values (if supported) ems_getThermostatValues(); @@ -1187,10 +1186,6 @@ void _process_Version(uint8_t type, uint8_t * data, uint8_t length) { myDebug("Unrecognized device found. TypeID 0x%02X, Product ID %d, Version %s", type, product_id, version); } - // if the boiler or thermostat values have changed, save them to SPIFFS - if (do_save) { - myESP.fs_saveConfig(); - } } /* @@ -1737,22 +1732,32 @@ void ems_setWarmWaterTemp(uint8_t temperature) { /** * Set the warm water mode to comfort to Eco/Comfort + * 1 = Hot, 2 = Eco, 3 = Intelligent */ -void ems_setWarmWaterModeComfort(bool comfort) { - myDebug("Setting boiler warm water to comfort mode %s\n", comfort ? "Comfort" : "Eco"); - +void ems_setWarmWaterModeComfort(uint8_t comfort) { _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx EMS_TxTelegram.timestamp = millis(); // set timestamp EMS_Sys_Status.txRetryCount = 0; // reset retry counter + if (comfort == 1) { + myDebug("Setting boiler warm water comfort mode to Comfort"); + EMS_TxTelegram.dataValue = EMS_VALUE_UBAParameterWW_wwComfort_Hot; + } else if (comfort == 2) { + myDebug("Setting boiler warm water comfort mode to Eco"); + EMS_TxTelegram.dataValue = EMS_VALUE_UBAParameterWW_wwComfort_Eco; + } else if (comfort == 3) { + myDebug("Setting boiler warm water comfort mode to Intelligent"); + EMS_TxTelegram.dataValue = EMS_VALUE_UBAParameterWW_wwComfort_Intelligent; + } else { + return; // invalid comfort value + } + EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE; EMS_TxTelegram.dest = EMS_Boiler.type_id; EMS_TxTelegram.type = EMS_TYPE_UBAParameterWW; EMS_TxTelegram.offset = EMS_OFFSET_UBAParameterWW_wwComfort; EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate - EMS_TxTelegram.dataValue = - (comfort ? EMS_VALUE_UBAParameterWW_wwComfort_Comfort : EMS_VALUE_UBAParameterWW_wwComfort_Eco); // 0x00 is on, 0xD8 is off EMS_TxQueue.push(EMS_TxTelegram); } diff --git a/src/ems.h b/src/ems.h index 91e8c3070..e5e4af523 100644 --- a/src/ems.h +++ b/src/ems.h @@ -254,7 +254,7 @@ void ems_setPoll(bool b); void ems_setTxEnabled(bool b); void ems_setLogging(_EMS_SYS_LOGGING loglevel); void ems_setEmsRefreshed(bool b); -void ems_setWarmWaterModeComfort(bool comfort); +void ems_setWarmWaterModeComfort(uint8_t comfort); bool ems_checkEMSBUSAlive(); void ems_setModels(); diff --git a/src/ems_devices.h b/src/ems_devices.h index bae98b500..9a27f957a 100644 --- a/src/ems_devices.h +++ b/src/ems_devices.h @@ -32,11 +32,12 @@ #define EMS_TYPE_UBASetPoints 0x1A #define EMS_TYPE_UBAFunctionTest 0x1D -#define EMS_OFFSET_UBAParameterWW_wwtemp 2 // WW Temperature -#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated -#define EMS_OFFSET_UBAParameterWW_wwComfort 9 // WW is in comfort or eco mode -#define EMS_VALUE_UBAParameterWW_wwComfort_Comfort 0x00 // the value for comfort -#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco +#define EMS_OFFSET_UBAParameterWW_wwtemp 2 // WW Temperature +#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated +#define EMS_OFFSET_UBAParameterWW_wwComfort 9 // WW is in comfort or eco mode +#define EMS_VALUE_UBAParameterWW_wwComfort_Hot 0x00 // the value for hot +#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco +#define EMS_VALUE_UBAParameterWW_wwComfort_Intelligent 0xEC // the value for intelligent /* * Thermostats...