This commit is contained in:
Paul
2019-09-07 18:59:32 +02:00
parent 9fa10aaf8f
commit 2beb5cd5f3
5 changed files with 463 additions and 347 deletions

View File

@@ -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

View File

@@ -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 <timer | alert>", "toggle either timer or alert on/off"},
{false, "send XX ...", "send raw telegram data as hex to EMS bus"},
{false, "thermostat read <type ID>", "send read request to the thermostat"},
{false, "thermostat temp <degrees>", "set current thermostat temperature"},
{false, "thermostat mode <mode>", "set mode (0=low/night, 1=manual/day, 2=auto)"},
{false, "thermostat read <type ID>", "send read request to the thermostat for heating circuit hc 1-4"},
{false, "thermostat temp [hc] <degrees>", "set current thermostat temperature"},
{false, "thermostat mode [hc] <mode>", "set mode (0=low/night, 1=manual/day, 2=auto) for heating circuit hc 1-4"},
{false, "thermostat scan <type ID>", "probe thermostat on all type id responses"},
{false, "boiler read <type ID>", "send read request to boiler"},
{false, "boiler wwtemp <degrees>", "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() {
// hc_num is 1 to 4
uint8_t _getThermostatMode(uint8_t hc_num) {
int thermoMode = EMS_VALUE_INT_NOTSET;
uint8_t model = ems_getThermostatModel();
if (ems_getThermostatModel() == EMS_MODEL_RC20) {
if (EMS_Thermostat.mode == 0) {
uint8_t mode = EMS_Thermostat.hc[hc_num - 1].mode;
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,44 +603,48 @@ 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
// 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 model = ems_getThermostatModel();
if ((model != EMS_MODEL_EASY)) {
myDebug_P(PSTR(" Thermostat time is %s"), EMS_Thermostat.datetime);
}
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;
}
// 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
}
// 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) {
myDebug_P(PSTR(" Mode is set to low"));
} else if (thermoMode == 1) {
@@ -652,8 +657,10 @@ void showInfo() {
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,48 +833,55 @@ 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))) {
// 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<JsonObject>();
rootThermostat[THERMOSTAT_HC] = _int_to_char(s, EMSESP_Settings.heating_circuit);
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 (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;
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 (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;
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 (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 (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 (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 (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 (EMS_Thermostat.heatingtype != EMS_VALUE_INT_NOTSET)
rootThermostat[THERMOSTAT_HEATINGTYPE] = EMS_Thermostat.heatingtype;
if (thermostat->heatingtype != EMS_VALUE_INT_NOTSET)
rootThermostat[THERMOSTAT_HEATINGTYPE] = thermostat->heatingtype;
if (EMS_Thermostat.circuitcalctemp != EMS_VALUE_INT_NOTSET)
rootThermostat[THERMOSTAT_CIRCUITCALCTEMP] = EMS_Thermostat.circuitcalctemp;
if (thermostat->circuitcalctemp != EMS_VALUE_INT_NOTSET)
rootThermostat[THERMOSTAT_CIRCUITCALCTEMP] = thermostat->circuitcalctemp;
}
uint8_t thermoMode = _getThermostatMode(); // 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day
uint8_t thermoMode = _getThermostatMode(hc_v + 1); // 0xFF=unknown, 0=low, 1=manual, 2=auto, 3=night, 4=day
// Termostat Mode
if (thermoMode == 0) {
@@ -899,7 +913,17 @@ void publishValues(bool force) {
myDebugLog("Publishing thermostat data via MQTT");
// send values via MQTT
myESP.mqttPublish(TOPIC_THERMOSTAT_DATA, data);
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)) {
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) {
@@ -1899,7 +1907,6 @@ void initEMSESP() {
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
// shower settings

View File

@@ -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;
strlcpy(EMS_Thermostat.datetime, "?", sizeof(EMS_Thermostat.datetime));
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.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
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
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
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
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_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,9 +1513,9 @@ 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_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,9 +2600,9 @@ 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
if (EMS_Thermostat.hc[hc_num].mode == 1) { // auto
EMS_TxTelegram.offset = 0x08; // auto offset
} else if (EMS_Thermostat.mode == 0) { // manuaL
} 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
@@ -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;
if (hc_num == 1) {
EMS_TxTelegram.type = EMS_TYPE_RC35Set_HC1;
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC1;
} 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;
if (hc == 1) {
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC1;
} else {
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage_HC2;
}
} 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

View File

@@ -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
#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
// 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_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_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
@@ -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 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;
// heating circuit
typedef struct {
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 daytemp;
uint8_t nighttemp;
uint8_t holidaytemp;
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;
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
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;
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);

View File

@@ -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