mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 08:19:52 +03:00
multiple thermostat choice - #238
This commit is contained in:
@@ -78,6 +78,7 @@ typedef struct {
|
||||
uint8_t dallas_gpio; // pin for attaching external dallas temperature sensors
|
||||
bool dallas_parasite; // on/off is using parasite
|
||||
uint8_t tx_mode; // TX mode 1,2 or 3
|
||||
uint8_t master_thermostat; // Product ID of master thermostat to use
|
||||
} _EMSESP_Settings;
|
||||
|
||||
typedef struct {
|
||||
@@ -99,6 +100,7 @@ static const command_t project_cmds[] PROGMEM = {
|
||||
{true, "shower_alert <on | off>", "stop hot water to send 3 cold burst warnings after max shower time is exceeded"},
|
||||
{true, "publish_time <seconds>", "set frequency for publishing data to MQTT (0=automatic)"},
|
||||
{true, "tx_mode <n>", "changes Tx logic. 1=EMS generic, 2=EMS+, 3=HT3"},
|
||||
{true, "master_thermostat [product id]", "product id to use as the master thermostat. Use no args for list."},
|
||||
|
||||
{false, "info", "show current values deciphered from the EMS messages"},
|
||||
{false, "log <n | b | t | s | r | j | v | w [type ID]", "set logging to none, basic, thermostat, solar module, raw, jabber, verbose or watch a specific type"},
|
||||
@@ -252,7 +254,7 @@ void showInfo() {
|
||||
myDebug_P(PSTR("\n%sEMS Bus stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
|
||||
if (ems_getBusConnected()) {
|
||||
myDebug_P(PSTR(" Bus is connected, protocol: %s"), ((EMS_Sys_Status.emsIDMask == 0x80) ? "HT3" : "Buderus"));
|
||||
myDebug_P(PSTR(" Bus is connected, protocol: %s"), (ems_isHT3() ? "HT3" : "Buderus"));
|
||||
myDebug_P(PSTR(" Rx: # successful read requests=%d, # CRC errors=%d"), EMS_Sys_Status.emsRxPgks, EMS_Sys_Status.emxCrcErr);
|
||||
|
||||
if (ems_getTxCapable()) {
|
||||
@@ -360,7 +362,7 @@ void showInfo() {
|
||||
EMS_Boiler.UBAuptime % 60);
|
||||
}
|
||||
|
||||
// For SM10/SM100 Solar Module
|
||||
// For SM10/SM100/SM200 Solar Module
|
||||
if (ems_getSolarModuleEnabled()) {
|
||||
myDebug_P(PSTR("")); // newline
|
||||
myDebug_P(PSTR("%sSolar Module stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
@@ -764,7 +766,7 @@ void publishEMSValues(bool force) {
|
||||
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_MIXING); // unset flag
|
||||
}
|
||||
|
||||
// For SM10 and SM100 Solar Modules
|
||||
// For SM10 and SM100/SM200 Solar Modules
|
||||
if (ems_getSolarModuleEnabled() && (ems_Device_has_flags(EMS_DEVICE_UPDATE_FLAG_SOLAR) || force)) {
|
||||
// build new json object
|
||||
doc.clear();
|
||||
@@ -855,7 +857,6 @@ void do_publishValues() {
|
||||
myDebugLog("Starting scheduled MQTT publish...");
|
||||
publishEMSValues(false);
|
||||
publishSensorValues();
|
||||
do_publishShowerData();
|
||||
}
|
||||
|
||||
// callback to light up the LED, called via Ticker every second
|
||||
@@ -946,6 +947,9 @@ bool LoadSaveCallback(MYESP_FSACTION_t action, JsonObject settings) {
|
||||
EMSESP_Settings.tx_mode = settings["tx_mode"] | EMS_TXMODE_DEFAULT; // default to 1 (generic)
|
||||
ems_setTxMode(EMSESP_Settings.tx_mode);
|
||||
|
||||
EMSESP_Settings.master_thermostat = settings["master_thermostat"] | 0; // default to 0 (none)
|
||||
ems_setMasterThermostat(EMSESP_Settings.master_thermostat);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -959,6 +963,7 @@ bool LoadSaveCallback(MYESP_FSACTION_t action, JsonObject settings) {
|
||||
settings["shower_alert"] = EMSESP_Settings.shower_alert;
|
||||
settings["publish_time"] = EMSESP_Settings.publish_time;
|
||||
settings["tx_mode"] = EMSESP_Settings.tx_mode;
|
||||
settings["master_thermostat"] = EMSESP_Settings.master_thermostat;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1076,6 +1081,27 @@ bool SetListCallback(MYESP_FSACTION_t action, uint8_t wc, const char * setting,
|
||||
myDebug_P(PSTR("Error. Usage: set tx_mode <1 | 2 | 3>"));
|
||||
}
|
||||
}
|
||||
|
||||
// master_thermostat
|
||||
if (strcmp(setting, "master_thermostat") == 0) {
|
||||
if (wc == 1) {
|
||||
// show list
|
||||
char device_string[100];
|
||||
myDebug_P(PSTR("Available thermostat Product ids:"));
|
||||
for (std::list<_Detected_Device>::iterator it = Devices.begin(); it != Devices.end(); ++it) {
|
||||
if (it->device_type == EMS_DEVICE_TYPE_THERMOSTAT) {
|
||||
strlcpy(device_string, (it)->device_desc_p, sizeof(device_string));
|
||||
myDebug_P(PSTR(" %d = %s"), (it)->product_id, device_string);
|
||||
}
|
||||
}
|
||||
myDebug_P(PSTR("Usage: set master_thermostat <product id>"));
|
||||
} else if (wc == 2) {
|
||||
uint8_t pid = atoi(value);
|
||||
EMSESP_Settings.master_thermostat = pid;
|
||||
ems_setMasterThermostat(pid);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action == MYESP_FSACTION_LIST) {
|
||||
@@ -1087,11 +1113,18 @@ bool SetListCallback(MYESP_FSACTION_t action, uint8_t wc, const char * setting,
|
||||
myDebug_P(PSTR(" listen_mode=%s"), EMSESP_Settings.listen_mode ? "on" : "off");
|
||||
myDebug_P(PSTR(" shower_timer=%s"), EMSESP_Settings.shower_timer ? "on" : "off");
|
||||
myDebug_P(PSTR(" shower_alert=%s"), EMSESP_Settings.shower_alert ? "on" : "off");
|
||||
|
||||
if (EMSESP_Settings.publish_time) {
|
||||
myDebug_P(PSTR(" publish_time=%d"), EMSESP_Settings.publish_time);
|
||||
} else {
|
||||
myDebug_P(PSTR(" publish_time=0 (always publish on data received)"), EMSESP_Settings.publish_time);
|
||||
}
|
||||
|
||||
if (EMSESP_Settings.master_thermostat) {
|
||||
myDebug_P(PSTR(" master_thermostat=%d"), EMSESP_Settings.master_thermostat);
|
||||
} else {
|
||||
myDebug_P(PSTR(" master_thermostat=0 (use first one detected)"), EMSESP_Settings.master_thermostat);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
@@ -1360,7 +1393,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
||||
// this is used for example for comfort, flowtemp
|
||||
myESP.mqttSubscribe(TOPIC_BOILER_CMD);
|
||||
|
||||
// these three need to be unqiue topics
|
||||
// these three need to be unique topics
|
||||
myESP.mqttSubscribe(TOPIC_BOILER_CMD_WWACTIVATED);
|
||||
myESP.mqttSubscribe(TOPIC_BOILER_CMD_WWONETIME);
|
||||
myESP.mqttSubscribe(TOPIC_BOILER_CMD_WWTEMP);
|
||||
@@ -1396,6 +1429,8 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
||||
const char * command = doc["cmd"];
|
||||
|
||||
// Check whatever the command is and act accordingly
|
||||
|
||||
// we only have one now, this is for the shower coldshot
|
||||
if (strcmp(command, TOPIC_SHOWER_COLDSHOT) == 0) {
|
||||
_showerColdShotStart();
|
||||
return;
|
||||
@@ -1708,7 +1743,7 @@ void WebCallback(JsonObject root) {
|
||||
boiler["ok"] = false;
|
||||
}
|
||||
|
||||
// For SM10/SM100 Solar Module
|
||||
// For SM10/SM100/SM200 Solar Module
|
||||
JsonObject sm = root.createNestedObject("sm");
|
||||
if (ems_getSolarModuleEnabled()) {
|
||||
sm["ok"] = true;
|
||||
@@ -1775,6 +1810,7 @@ void initEMSESP() {
|
||||
EMSESP_Settings.led_gpio = EMSESP_LED_GPIO;
|
||||
EMSESP_Settings.dallas_gpio = EMSESP_DALLAS_GPIO;
|
||||
EMSESP_Settings.tx_mode = EMS_TXMODE_DEFAULT; // default tx mode
|
||||
EMSESP_Settings.master_thermostat = 0;
|
||||
|
||||
// shower settings
|
||||
EMSESP_Shower.timerStart = 0;
|
||||
|
||||
40
src/ems.cpp
40
src/ems.cpp
@@ -71,6 +71,11 @@ void ems_Device_remove_flags(unsigned int flags) {
|
||||
EMS_Sys_Status.emsRefreshedFlags &= ~flags;
|
||||
}
|
||||
|
||||
// returns true if HT3, other Buderus protocol
|
||||
bool ems_isHT3() {
|
||||
return (EMS_Sys_Status.emsIDMask == 0x80);
|
||||
}
|
||||
|
||||
// init stats and counters and buffers
|
||||
void ems_init() {
|
||||
ems_clearDeviceList(); // init the device map
|
||||
@@ -171,9 +176,9 @@ void ems_init() {
|
||||
EMS_Boiler.pump_mod_min = EMS_VALUE_INT_NOTSET; // Boiler circuit pump modulation min. power %
|
||||
|
||||
// Solar Module values
|
||||
EMS_SolarModule.collectorTemp = EMS_VALUE_SHORT_NOTSET; // collector temp from SM10/SM100
|
||||
EMS_SolarModule.bottomTemp = EMS_VALUE_SHORT_NOTSET; // bottom temp from SM10/SM100
|
||||
EMS_SolarModule.pumpModulation = EMS_VALUE_INT_NOTSET; // modulation solar pump SM10/SM100
|
||||
EMS_SolarModule.collectorTemp = EMS_VALUE_SHORT_NOTSET; // collector temp from SM10/SM100/SM200
|
||||
EMS_SolarModule.bottomTemp = EMS_VALUE_SHORT_NOTSET; // bottom temp from SM10/SM100/SM200
|
||||
EMS_SolarModule.pumpModulation = EMS_VALUE_INT_NOTSET; // modulation solar pump SM10/SM100/SM200
|
||||
EMS_SolarModule.pump = EMS_VALUE_BOOL_NOTSET; // pump active
|
||||
EMS_SolarModule.EnergyLastHour = EMS_VALUE_USHORT_NOTSET;
|
||||
EMS_SolarModule.EnergyToday = EMS_VALUE_USHORT_NOTSET;
|
||||
@@ -410,6 +415,10 @@ void ems_setTxMode(uint8_t mode) {
|
||||
EMS_Sys_Status.emsTxMode = mode;
|
||||
}
|
||||
|
||||
void ems_setMasterThermostat(uint8_t product_id) {
|
||||
EMS_Sys_Status.emsMasterThermostat = product_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* debug print a telegram to telnet/serial including the CRC
|
||||
*/
|
||||
@@ -1241,7 +1250,7 @@ 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
|
||||
// or prtial, e.g. for modes:
|
||||
// or partial, e.g. for modes:
|
||||
// manual : 10 00 FF 0A 01 A5 02
|
||||
// auto : 10 00 FF 0A 01 A5 03
|
||||
_setValue(EMS_RxTelegram, &EMS_Thermostat.hc[hc].curr_roomTemp, EMS_OFFSET_RCPLUSStatusMessage_curr); // value is * 10
|
||||
@@ -1665,6 +1674,17 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
strlcat(version, ".", sizeof(version));
|
||||
strlcat(version, _smallitoa(EMS_RxTelegram->data[offset + 2], buf), sizeof(version));
|
||||
|
||||
// some devices store the protocol type (HT3, Buderus) in tbe last byte
|
||||
// we don't do anything with this yet.
|
||||
if (EMS_RxTelegram->data_length >= 10) {
|
||||
uint8_t protocol_type = EMS_RxTelegram->data[9];
|
||||
if (protocol_type == 2) {
|
||||
// it's a junkers
|
||||
} else if (protocol_type >= 3) {
|
||||
// it's buderus
|
||||
}
|
||||
}
|
||||
|
||||
// scan through known devices matching the productid
|
||||
uint8_t product_id = EMS_RxTelegram->data[offset];
|
||||
uint8_t i = 0;
|
||||
@@ -1677,7 +1697,7 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
i++;
|
||||
}
|
||||
|
||||
// if not found, just add it
|
||||
// if not found, just add it as an unknown device
|
||||
if (!typeFound) {
|
||||
(void)_addDevice(EMS_DEVICE_TYPE_UNKNOWN, product_id, device_id, nullptr, version);
|
||||
return;
|
||||
@@ -1701,6 +1721,9 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
strlcpy(EMS_Boiler.version, version, sizeof(EMS_Boiler.version));
|
||||
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
|
||||
} else if (type == EMS_DEVICE_TYPE_THERMOSTAT) {
|
||||
// we can only support a single thermostat currently, so check which product_id we may have chosen
|
||||
// to be the master - see https://github.com/proddy/EMS-ESP/issues/238
|
||||
if ((EMS_Sys_Status.emsMasterThermostat == 0) || (EMS_Sys_Status.emsMasterThermostat == product_id)) {
|
||||
EMS_Thermostat.device_id = device_id;
|
||||
EMS_Thermostat.device_flags = (flags & 0x7F); // remove 7th bit
|
||||
EMS_Thermostat.write_supported = (flags & EMS_DEVICE_FLAG_NO_WRITE) == 0;
|
||||
@@ -1708,6 +1731,7 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
EMS_Thermostat.device_desc_p = device_desc_p;
|
||||
strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version));
|
||||
ems_getThermostatValues(); // get Thermostat values
|
||||
}
|
||||
} else if (type == EMS_DEVICE_TYPE_SOLAR) {
|
||||
EMS_SolarModule.device_id = device_id;
|
||||
EMS_SolarModule.product_id = product_id;
|
||||
@@ -2460,7 +2484,13 @@ void ems_setWarmWaterActivated(bool activated) {
|
||||
EMS_TxTelegram.offset = EMS_OFFSET_UBAParameterWW_wwactivated;
|
||||
EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
|
||||
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
|
||||
|
||||
// https://github.com/proddy/EMS-ESP/issues/268
|
||||
if (ems_isHT3()) {
|
||||
EMS_TxTelegram.dataValue = (activated ? 0x08 : 0x00); // 0x08 is on, 0x00 is off
|
||||
} else {
|
||||
EMS_TxTelegram.dataValue = (activated ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
|
||||
}
|
||||
|
||||
EMS_TxQueue.push(EMS_TxTelegram);
|
||||
}
|
||||
|
||||
@@ -137,6 +137,7 @@ typedef struct {
|
||||
uint8_t emsIDMask; // Buderus: 0x00, Junkers: 0x80
|
||||
uint8_t emsPollAck[1]; // acknowledge buffer for Poll
|
||||
uint8_t emsTxMode; // Tx mode 1, 2 or 3
|
||||
uint8_t emsMasterThermostat; // product ID for the default thermostat to use
|
||||
char emsDeviceMap[EMS_SYS_DEVICEMAP_LENGTH]; // contents of 0x07 telegram with bitmasks for all active EMS devices
|
||||
} _EMS_Sys_Status;
|
||||
|
||||
@@ -336,7 +337,7 @@ typedef struct {
|
||||
_EMS_Mixing_HC hc[EMS_THERMOSTAT_MAXHC]; // array for the 4 heating circuits
|
||||
} _EMS_Mixing;
|
||||
|
||||
// Solar Module - SM10/SM100/ISM1
|
||||
// Solar Module - SM10/SM100/SM200/ISM1
|
||||
typedef struct {
|
||||
uint8_t device_id; // the device ID of the Solar Module
|
||||
uint8_t device_flags; // Solar Module flags
|
||||
@@ -421,6 +422,7 @@ void ems_setWarmWaterModeComfort(uint8_t comfort);
|
||||
void ems_setModels();
|
||||
void ems_setTxDisabled(bool b);
|
||||
void ems_setTxMode(uint8_t mode);
|
||||
void ems_setMasterThermostat(uint8_t product_id);
|
||||
char * ems_getDeviceDescription(_EMS_DEVICE_TYPE device_type, char * buffer, bool name_only = false);
|
||||
bool ems_getDeviceTypeDescription(uint8_t device_id, char * buffer);
|
||||
void ems_getThermostatValues();
|
||||
@@ -444,6 +446,7 @@ bool ems_getTxDisabled();
|
||||
void ems_Device_add_flags(unsigned int flags);
|
||||
bool ems_Device_has_flags(unsigned int flags);
|
||||
void ems_Device_remove_flags(unsigned int flags);
|
||||
bool ems_isHT3();
|
||||
|
||||
// private functions
|
||||
uint8_t _crcCalculator(uint8_t * data, uint8_t len);
|
||||
|
||||
Reference in New Issue
Block a user