version 1.2.3. See ChangeLog

This commit is contained in:
proddy
2019-01-03 15:10:52 +01:00
parent 64a5ebc25d
commit 23599fb0a9
8 changed files with 311 additions and 171 deletions

View File

@@ -65,9 +65,6 @@ Ticker showerColdShotStopTimer;
#define TOPIC_SHOWER_COLDSHOT "shower_coldshot" // used to trigger a coldshot from HA publish
#define SHOWER_ALARM "shower_alarm" // for notifying HA that shower time has reached its limit
// logging - EMS_SYS_LOGGING_VERBOSE, EMS_SYS_LOGGING_NONE, EMS_SYS_LOGGING_BASIC (see ems.h)
#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_NONE
// shower settings for DEBUGGING only
#ifdef SHOWER_TEST
#undef SHOWER_PAUSE_TIME
@@ -99,7 +96,7 @@ command_t PROGMEM project_cmds[] = {
{"l [n]", "set logging (0=none, 1=raw, 2=basic, 3=thermostat only, 4=verbose)"},
{"s", "show statistics"},
{"D", "auto Detect EMS connected devices"},
{"D", "scan EMS connected Devices"},
{"h", "list supported EMS telegram type IDs"},
{"M", "publish to MQTT"},
{"Q", "print Tx Queue"},
@@ -133,9 +130,9 @@ unsigned long timestamp; // for internal timings, via millis()
uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
// logging messages with fixed strings (newline done automatically)
// logging messages with fixed strings
void myDebugLog(const char * s) {
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
if (ems_getLogging() >= EMS_SYS_LOGGING_BASIC) {
myDebug(s);
}
}
@@ -387,6 +384,7 @@ void showInfo() {
_renderFloatValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp);
_renderFloatValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp);
if (EMS_Thermostat.mode == 0) {
myDebug(" Mode is set to low");
} else if (EMS_Thermostat.mode == 1) {
@@ -482,13 +480,22 @@ void publishValues(bool force) {
rootThermostat[THERMOSTAT_CURRTEMP] = _float_to_char(s, EMS_Thermostat.curr_roomTemp);
rootThermostat[THERMOSTAT_SELTEMP] = _float_to_char(s, EMS_Thermostat.setpoint_roomTemp);
// send mode 0=low, 1=manual, 2=auto
if (EMS_Thermostat.mode == 0) {
rootThermostat[THERMOSTAT_MODE] = "low";
} else if (EMS_Thermostat.mode == 1) {
rootThermostat[THERMOSTAT_MODE] = "manual";
if (ems_getThermostatModel() == EMS_MODEL_RC20) {
if (EMS_Thermostat.mode == 0) {
rootThermostat[THERMOSTAT_MODE] = "low";
} else if (EMS_Thermostat.mode == 1) {
rootThermostat[THERMOSTAT_MODE] = "manual";
} else {
rootThermostat[THERMOSTAT_MODE] = "auto";
}
} else {
rootThermostat[THERMOSTAT_MODE] = "auto";
if (EMS_Thermostat.mode == 0) {
rootThermostat[THERMOSTAT_MODE] = "night";
} else if (EMS_Thermostat.mode == 1) {
rootThermostat[THERMOSTAT_MODE] = "day";
} else {
rootThermostat[THERMOSTAT_MODE] = "auto";
}
}
rlen = rootThermostat.measureLength();
@@ -562,7 +569,7 @@ void myDebugCallback() {
ems_printTxQueue();
break;
case 'D': // Auto detect EMS devices
ems_getAllVersions();
ems_scanDevices();
break;
default:
myDebug("Unknown command. Use ? for help.");
@@ -629,8 +636,6 @@ void MQTTcallback(unsigned int type, const char * topic, const char * message) {
myESP.mqttSubscribe(TOPIC_THERMOSTAT_CMD_MODE);
myESP.mqttSubscribe(TOPIC_SHOWER_TIMER);
myESP.mqttSubscribe(TOPIC_SHOWER_ALERT);
myESP.mqttSubscribe(TOPIC_BOILER_TAPWATER_ACTIVE);
myESP.mqttSubscribe(TOPIC_BOILER_HEATING_ACTIVE);
myESP.mqttSubscribe(TOPIC_SHOWER_COLDSHOT);
// publish to HA the status of the Shower parameters
@@ -689,7 +694,7 @@ void MQTTcallback(unsigned int type, const char * topic, const char * message) {
void WIFICallback() {
// when finally we're all set up, we can fire up the uart (this will enable the UART interrupts)
#ifdef DEBUG_SUPPORT
myDebug("Warning, in DEBUG mode. EMS bus is disabled. See -DDEBUG_SUPPORT build option.");
myDebug("Warning, in DEBUG mode. EMS bus has been disabled. See -DDEBUG_SUPPORT build option.");
#else
// Important! This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
// This is done after we have a WiFi signal to avoid any resource conflicts
@@ -697,26 +702,20 @@ void WIFICallback() {
myDebug("[UART] Opened Rx/Tx connection");
#endif
// now that we're connected, send a version request to see what things are on the EMS bus
myDebug("Starting up. Finding what devices are on the EMS bus...");
ems_getAllVersions();
// now that we're connected, check to see if we boiler and thermostat set
// otherwise this will initiate a self scan
ems_setModels();
}
// Initialize the boiler settings
void initBoiler() {
// default settings
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
// init shower
void initShower() {
// default showr settings
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
Boiler_Shower.timerStart = 0;
Boiler_Shower.timerPause = 0;
Boiler_Shower.duration = 0;
Boiler_Shower.doingColdShot = false;
ems_setLogging(BOILER_DEFAULT_LOGGING); // set default logging
ems_init(); // call ems.cpp's init function to set all the internal params
}
// call PublishValues without forcing, so using CRC to see if we really need to publish
@@ -728,7 +727,7 @@ void do_publishValues() {
void do_ledcheck() {
#ifndef NO_LED
if (ems_getBusConnected()) {
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard high=off
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
} else {
int state = digitalRead(BOILER_LED);
digitalWrite(BOILER_LED, !state);
@@ -761,7 +760,7 @@ void do_regularUpdates() {
// turn off hot water to send a shot of cold
void _showerColdShotStart() {
myDebugLog("Shower: doing a shot of cold");
myDebugLog("[Shower] doing a shot of cold water");
ems_setWarmTapWaterActivated(false);
Boiler_Shower.doingColdShot = true;
// start the timer for n seconds which will reset the water back to hot
@@ -771,11 +770,10 @@ void _showerColdShotStart() {
// turn back on the hot water for the shower
void _showerColdShotStop() {
if (Boiler_Shower.doingColdShot) {
myDebugLog("Shower: finished shot of cold. hot water back on");
myDebugLog("[Shower] finished shot of cold. hot water back on");
ems_setWarmTapWaterActivated(true);
Boiler_Shower.doingColdShot = false;
// disable the timer
showerColdShotStopTimer.detach();
showerColdShotStopTimer.detach(); // disable the timer
}
}
@@ -800,13 +798,13 @@ void showerCheck() {
// first check to see if hot water has been on long enough to be recognized as a Shower/Bath
if (!Boiler_Shower.showerOn && (timestamp - Boiler_Shower.timerStart) > SHOWER_MIN_DURATION) {
Boiler_Shower.showerOn = true;
myDebugLog("Shower: hot water still running, starting shower timer");
myDebugLog("[Shower] hot water still running, starting shower timer");
}
// check if the shower has been on too long
else if ((((timestamp - Boiler_Shower.timerStart) > SHOWER_MAX_DURATION) && !Boiler_Shower.doingColdShot)
&& Boiler_Status.shower_alert) {
myESP.sendHACommand(SHOWER_ALARM);
myDebugLog("Shower: exceeded max shower time");
myDebugLog("[Shower] exceeded max shower time");
_showerColdShotStart();
}
}
@@ -814,7 +812,6 @@ void showerCheck() {
// if it just turned off, record the time as it could be a short pause
if ((Boiler_Shower.timerStart != 0) && (Boiler_Shower.timerPause == 0)) {
Boiler_Shower.timerPause = timestamp;
myDebugLog("Shower: hot water turned off");
}
// if shower has been off for longer than the wait time
@@ -831,14 +828,13 @@ void showerCheck() {
strlcat(s, itoa((uint8_t)((Boiler_Shower.duration / 1000) % 60), buffer, 10), sizeof(s));
strlcat(s, " seconds", sizeof(s));
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
myDebug("Shower: finished with duration %s", s);
myDebug("[Shower] finished with duration %s", s);
}
myESP.mqttPublish(TOPIC_SHOWERTIME, s); // publish to HA
}
}
// reset everything
myDebugLog("Shower: resetting timers");
Boiler_Shower.timerStart = 0;
Boiler_Shower.timerPause = 0;
Boiler_Shower.showerOn = false;
@@ -865,13 +861,20 @@ void setup() {
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
// set up myESP for Wifi, MQTT, MDNS and Telnet
// with callbacks
myESP.setup(APP_HOSTNAME, APP_NAME, APP_VERSION, WIFI_SSID, WIFI_PASSWORD, MQTT_IP, MQTT_USER, MQTT_PASS);
myESP.consoleSetCallBackProjectCmds(project_cmds, ArraySize(project_cmds), myDebugCallback); // set up Telnet commands
myESP.setWIFICallback(WIFICallback);
myESP.setMQTTCallback(MQTTcallback);
// init Boiler specific parameters
initBoiler();
// init Shower specific parameters
initShower();
ems_setLogging(BOILER_DEFAULT_LOGGING); // set default logging. see my_config.h
// init the EMS bus
// call ems.cpp's init function to set all the internal params
ems_init(MY_BOILER_MODELID, MY_THERMOSTAT_MODELID);
}
//

View File

@@ -62,7 +62,7 @@ const _Model_Type Model_Types[] = {
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73"},
{EMS_MODEL_RC20, 77, 0x17, "RC20 (Nefit Moduline 300)"},
{EMS_MODEL_RC30, 78, 0x10, "RC30 (Nefit Moduline 400)"},
{EMS_MODEL_RC35, 86, 0x10, "RC35"},
{EMS_MODEL_RC35, 86, 0x10, "RC35 (or compatible"},
{EMS_MODEL_EASY, 202, 0x18, "TC100 (Nefit Easy/CT100)"}
};
@@ -165,7 +165,7 @@ uint8_t _last_TxTelgramCRC; // CRC of last Tx sent, for checking duplicates
// init stats and counters and buffers
// uses -255 or 255 for values that haven't been set yet (EMS_VALUE_INT_NOTSET and EMS_VALUE_FLOAT_NOTSET)
void ems_init() {
void ems_init(_EMS_MODEL_ID boiler_modelid, _EMS_MODEL_ID thermostat_modelid) {
// overall status
EMS_Sys_Status.emsRxPgks = 0;
EMS_Sys_Status.emsTxPkgs = 0;
@@ -177,12 +177,6 @@ void ems_init() {
EMS_Sys_Status.emsTxEnabled = true; // start up with Tx enabled
EMS_Sys_Status.emsBusConnected = false;
// no thermostat or boiler attached yet
EMS_Sys_Status.emsBoilerEnabled = false;
EMS_Sys_Status.emsThermostatEnabled = false;
EMS_Sys_Status.emsLogging = EMS_SYS_LOGGING_NONE; // Verbose logging is off
// thermostat
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_FLOAT_NOTSET;
EMS_Thermostat.curr_roomTemp = EMS_VALUE_FLOAT_NOTSET;
@@ -195,7 +189,6 @@ void ems_init() {
EMS_Thermostat.mode = 255; // dummy value
EMS_Thermostat.type_id = EMS_ID_NONE;
EMS_Thermostat.model_id = EMS_MODEL_NONE;
EMS_Thermostat.read_supported = false;
EMS_Thermostat.write_supported = false;
@@ -244,8 +237,11 @@ void ems_init() {
EMS_Boiler.tapwaterActive = EMS_VALUE_INT_NOTSET; // Hot tap water is on/off
EMS_Boiler.heatingActive = EMS_VALUE_INT_NOTSET; // Central heating is on/off
EMS_Boiler.type_id = EMS_ID_NONE;
EMS_Boiler.model_id = EMS_MODEL_NONE;
EMS_Boiler.type_id = EMS_ID_NONE;
// for lookup later
EMS_Boiler.model_id = boiler_modelid;
EMS_Thermostat.model_id = thermostat_modelid;
// counters
_ems_PollCount = 0;
@@ -281,16 +277,11 @@ void ems_setEmsRefreshed(bool b) {
}
bool ems_getBoilerEnabled() {
return EMS_Sys_Status.emsBoilerEnabled;
}
void ems_setBoilerEnabled(bool b) {
EMS_Sys_Status.emsBoilerEnabled = b;
myDebug("Boiler set to %s", EMS_Sys_Status.emsBoilerEnabled ? "enabled" : "disabled");
return (EMS_Boiler.model_id != EMS_MODEL_NONE);
}
bool ems_getThermostatEnabled() {
return EMS_Sys_Status.emsThermostatEnabled;
return (EMS_Thermostat.model_id != EMS_MODEL_NONE);
}
bool ems_getBusConnected() {
@@ -300,11 +291,6 @@ bool ems_getBusConnected() {
return EMS_Sys_Status.emsBusConnected;
}
void ems_setThermostatEnabled(bool b) {
EMS_Sys_Status.emsThermostatEnabled = b;
myDebug("Thermostat set to %s", EMS_Sys_Status.emsThermostatEnabled ? "enabled" : "disabled");
}
_EMS_SYS_LOGGING ems_getLogging() {
return EMS_Sys_Status.emsLogging;
}
@@ -330,6 +316,22 @@ void ems_setLogging(_EMS_SYS_LOGGING loglevel) {
}
}
// if the thermostat or boiler models have been provided, set them up
void ems_setModels() {
bool found = false;
if (ems_getThermostatModel() != EMS_MODEL_NONE) {
found = _ems_setModel(ems_getThermostatModel());
}
if (ems_getBoilerModel() != EMS_MODEL_NONE) {
found = found && _ems_setModel(ems_getBoilerModel());
}
if (!found) {
ems_scanDevices(); // initiate a scan
}
}
/**
* Calculate CRC checksum using lookup table for speed
* len is length of data in bytes (including the CRC byte at end)
@@ -482,6 +484,14 @@ void _ems_sendTelegram() {
}
// if Tx is disabled, don't do anything and ignore the request
// this could be because the boiler has yet to be found and the type_id is still empty
if (EMS_TxTelegram.dest == EMS_ID_NONE) {
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
EMS_TxQueue.shift(); // remove from queue
return;
}
// if there is no destination, also delete it from the queue
if (!EMS_Sys_Status.emsTxEnabled) {
myDebug("Tx is disabled. Ignoring %s request to 0x%02X.",
((EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) ? "write" : "read"),
@@ -953,54 +963,56 @@ void _process_UBAMonitorSlow(uint8_t * data, uint8_t length) {
}
/**
* RC20StatusMessage - type 0x91 - data from the RC20 thermostat (0x17) - 15 bytes long
* type 0x91 - data from the RC20 thermostat (0x17) - 15 bytes long
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC20StatusMessage(uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[1]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(2, data);
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC20StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC20StatusMessage_curr, data);
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
}
/**
* RC30StatusMessage - type 0x41 - data from the RC30 thermostat (0x10) - 14 bytes long
* type 0x41 - data from the RC30 thermostat (0x10) - 14 bytes long
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC30StatusMessage(uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[1]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(2, data);
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC30StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC30StatusMessage_curr, data);
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
}
/**
* RC35StatusMessage - type 0x3E - data from the RC35 thermostat (0x10)
* type 0x3E - data from the RC35 thermostat (0x10) - 16 bytes
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC35StatusMessage(uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[2]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(3, data);
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC35StatusMessage_setpoint]) / (float)2;
// There is no current room temperature sensor in this telegram
EMS_Thermostat.curr_roomTemp = EMS_VALUE_FLOAT_NOTSET;
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
}
/**
* EasyStatusMessage - type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
* type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
* The Easy has a digital precision of its floats to 2 decimal places, so values is divided by 100
*/
void _process_EasyStatusMessage(uint8_t * data, uint8_t length) {
EMS_Thermostat.curr_roomTemp = ((float)(((data[8] << 8) + data[9]))) / 100;
EMS_Thermostat.setpoint_roomTemp = ((float)(((data[10] << 8) + data[11]))) / 100;
EMS_Thermostat.curr_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_curr] << 8) + data[9]))) / 100;
EMS_Thermostat.setpoint_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_setpoint] << 8) + data[11]))) / 100;
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
}
/**
* RC20Temperature - type 0xA8 - for reading the mode from the RC20 thermostat (0x17)
* type 0xA8 - for reading the mode from the RC20 thermostat (0x17)
* received only after requested
*/
void _process_RC20Set(uint8_t * data, uint8_t length) {
@@ -1008,7 +1020,7 @@ void _process_RC20Set(uint8_t * data, uint8_t length) {
}
/**
* RC30Temperature - type 0xA7 - for reading the mode from the RC30 thermostat (0x10)
* type 0xA7 - for reading the mode from the RC30 thermostat (0x10)
* received only after requested
*/
void _process_RC30Set(uint8_t * data, uint8_t length) {
@@ -1016,7 +1028,8 @@ void _process_RC30Set(uint8_t * data, uint8_t length) {
}
/**
* RC35Temperature - type 0x3D - for reading the mode from the RC35 thermostat (0x10)
* type 0x3D - for reading the mode from the RC35 thermostat (0x10)
* Working Mode Heating Circuit 1 (HC1)
* received only after requested
*/
void _process_RC35Set(uint8_t * data, uint8_t length) {
@@ -1024,14 +1037,14 @@ void _process_RC35Set(uint8_t * data, uint8_t length) {
}
/**
* RCOutdoorTempMessage - type 0xA3 - for external temp settings from the the RC* thermostats
* type 0xA3 - for external temp settings from the the RC* thermostats
*/
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length) {
// add support here if you're reading external sensors
}
/**
* Version - type 0x02 - get the firmware version and type of an EMS device
* type 0x02 - get the firmware version and type of an EMS device
*/
void _process_Version(uint8_t * data, uint8_t length) {
// ignore short messages that we can't interpret
@@ -1049,16 +1062,17 @@ void _process_Version(uint8_t * data, uint8_t length) {
char version[10] = {0};
snprintf(version, sizeof(version), "%02d.%02d", major, minor);
// use product ID to search
while (i < _Model_Types_max) {
if (Model_Types[i].product_id == product_id) {
typeFound = true; // we have a matching product id
typeFound = true; // we have a matching product id. i is the index.
break;
}
i++;
}
if (!typeFound) {
myDebug("Unknown device found. Product ID %d, Version %s", product_id, version);
myDebug("Unrecognized device found. Product ID %d, Version %s", product_id, version);
return;
}
@@ -1072,7 +1086,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
}
// set a thermostat
if ((isThermostat) && (!EMS_Sys_Status.emsThermostatEnabled)) {
if (isThermostat) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Found a Thermostat. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
Model_Types[i].model_string,
@@ -1080,19 +1094,21 @@ void _process_Version(uint8_t * data, uint8_t length) {
product_id,
version);
}
// set its capabilities
EMS_Thermostat.model_id = Model_Types[i].model_id;
EMS_Thermostat.type_id = Model_Types[i].type_id;
EMS_Thermostat.read_supported = Thermostat_Types[j].read_supported;
EMS_Thermostat.write_supported = Thermostat_Types[j].write_supported;
strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version));
ems_setThermostatEnabled(true);
ems_getThermostatValues(); // get Thermostat values (if supported)
}
// if we don't have a thermostat set, use this one
if (!ems_getThermostatEnabled()) {
myDebug("Setting the Thermostat to this one.");
// set its capabilities
EMS_Thermostat.model_id = Model_Types[i].model_id;
EMS_Thermostat.type_id = Model_Types[i].type_id;
EMS_Thermostat.read_supported = Thermostat_Types[j].read_supported;
EMS_Thermostat.write_supported = Thermostat_Types[j].write_supported;
strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version));
// otherwise assume its a boiler
if ((!isThermostat) && (!EMS_Sys_Status.emsBoilerEnabled)) {
ems_getThermostatValues(); // get Thermostat values (if supported)
}
} else {
// otherwise assume its a boiler
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Found a Boiler compatible device, model %s with TypeID 0x%02X, Product ID %d, Version %s",
Model_Types[i].model_string,
@@ -1100,13 +1116,93 @@ void _process_Version(uint8_t * data, uint8_t length) {
product_id,
version);
}
EMS_Boiler.type_id = Model_Types[i].type_id;
EMS_Boiler.model_id = Model_Types[i].model_id;
strlcpy(EMS_Boiler.version, version, sizeof(EMS_Boiler.version));
ems_setBoilerEnabled(true);
if (!ems_getBoilerEnabled()) {
myDebug("Setting the Boiler to this one.");
EMS_Boiler.type_id = Model_Types[i].type_id;
EMS_Boiler.model_id = Model_Types[i].model_id;
strlcpy(EMS_Boiler.version, version, sizeof(EMS_Boiler.version));
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
}
}
}
/**
* Given a MODEL_ID, look up its data and set either a Thermostat or Boiler
* return false if not found or no need to set
*/
bool _ems_setModel(_EMS_MODEL_ID model_id) {
if (model_id == EMS_MODEL_NONE) {
return false; // invalid model_id
}
// see if we have a valid model_id
uint8_t model_loc = 0;
bool found = false;
uint8_t i = 0;
const _Model_Type * model_type;
while (i < _Model_Types_max) {
model_type = &Model_Types[model_loc];
if (model_type->model_id == model_id) {
found = true; // we have a matching product id. i is the index.
break;
}
model_loc++;
}
if (!found) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Unknown model specified");
}
return false; // unknown model_id
}
// next check to see if its a known thermostat
// j will have pointer to the Thermostat details
bool isThermostat = false;
uint8_t j = 0;
while (j < _Thermostat_Types_max) {
if (Thermostat_Types[j].model_id == model_id) {
isThermostat = true; // we have a matching model
break;
}
j++;
}
// set a thermostat
if (isThermostat) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Setting Thermostat. Model %s with TypeID 0x%02X, Product ID %d",
model_type->model_string,
model_type->type_id,
model_type->product_id);
}
// set its capabilities
EMS_Thermostat.model_id = model_type->model_id;
EMS_Thermostat.type_id = model_type->type_id;
EMS_Thermostat.read_supported = Thermostat_Types[j].read_supported;
EMS_Thermostat.write_supported = Thermostat_Types[j].write_supported;
strlcpy(EMS_Thermostat.version, "unknown", sizeof(EMS_Thermostat.version));
ems_getThermostatValues(); // get Thermostat values (if supported)
} else {
// otherwise assume its a boiler
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Setting Boiler. Model %s with TypeID 0x%02X, Product ID %d",
model_type->model_string,
model_type->type_id,
model_type->product_id);
}
EMS_Boiler.model_id = model_type->model_id;
EMS_Boiler.type_id = model_type->type_id;
strlcpy(EMS_Boiler.version, "unknown", sizeof(EMS_Boiler.version));
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
}
return true;
}
/**
@@ -1203,7 +1299,7 @@ void ems_printTxQueue() {
* Generic function to return various settings from the thermostat
*/
void ems_getThermostatValues() {
if (!EMS_Sys_Status.emsThermostatEnabled) {
if (!ems_getThermostatEnabled()) {
return;
}
@@ -1283,7 +1379,7 @@ char * _ems_buildModelString(char * buffer, uint8_t size, _EMS_MODEL_ID model_id
*/
char * ems_getThermostatType(char * buffer) {
uint8_t size = 64;
if (!EMS_Sys_Status.emsThermostatEnabled) {
if (!ems_getThermostatEnabled()) {
strlcpy(buffer, "<not enabled>", size);
} else {
_ems_buildModelString(buffer, size, EMS_Thermostat.model_id);
@@ -1296,7 +1392,7 @@ char * ems_getThermostatType(char * buffer) {
*/
char * ems_getBoilerType(char * buffer) {
uint8_t size = 64;
if (!EMS_Sys_Status.emsBoilerEnabled) {
if (!ems_getBoilerEnabled()) {
strlcpy(buffer, "<not enabled>", size);
} else {
_ems_buildModelString(buffer, size, EMS_Boiler.model_id);
@@ -1306,20 +1402,19 @@ char * ems_getBoilerType(char * buffer) {
// returns the model type for a thermostat
_EMS_MODEL_ID ems_getThermostatModel() {
if (EMS_Sys_Status.emsThermostatEnabled) {
return (EMS_Thermostat.model_id);
} else {
return EMS_MODEL_NONE;
}
return (EMS_Thermostat.model_id);
}
// returns the model type for a boiler
_EMS_MODEL_ID ems_getBoilerModel() {
return (EMS_Boiler.model_id);
}
/*
* Find the versions of our connected devices
*/
void ems_getAllVersions() {
void ems_scanDevices() {
// send Version request to all known EMS devices
ems_setThermostatEnabled(false);
ems_setBoilerEnabled(false);
myDebug("Scanning EMS bus for devices. This may take a few seconds.");
for (int i = 0; i < _Model_Types_max; i++) {
if ((Model_Types[i].model_id != EMS_MODEL_NONE) && (Model_Types[i].model_id != EMS_MODEL_SERVICEKEY)) {
@@ -1448,7 +1543,7 @@ void ems_sendRawTelegram(char * telegram) {
* Set the temperature of the thermostat
*/
void ems_setThermostatTemp(float temperature) {
if (!EMS_Sys_Status.emsThermostatEnabled) {
if (!ems_getThermostatEnabled()) {
return;
}
@@ -1480,7 +1575,7 @@ void ems_setThermostatTemp(float temperature) {
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC30StatusMessage;
} else if ((model_id == EMS_MODEL_RC35) || (model_id == EMS_MODEL_ES73)) {
EMS_TxTelegram.type = EMS_TYPE_RC35Set;
EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp;
EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp_day; // day mode only for now
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage;
}
@@ -1499,7 +1594,7 @@ void ems_setThermostatTemp(float temperature) {
* 0xA8 on a RC20 and 0xA7 on RC30
*/
void ems_setThermostatMode(uint8_t mode) {
if (!EMS_Sys_Status.emsThermostatEnabled) {
if (!ems_getThermostatEnabled()) {
return;
}

View File

@@ -58,25 +58,34 @@
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
// RC20 specific
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
#define EMS_OFFSET_RC20Set_mode 23 // position of thermostat mode
#define EMS_OFFSET_RC20Set_temp 28 // position of thermostat setpoint temperature
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
#define EMS_OFFSET_RC20Set_mode 23 // position of thermostat mode
#define EMS_OFFSET_RC20Set_temp 28 // position of thermostat setpoint temperature
#define EMS_TYPE_RC20StatusMessage_setpoint 1 // setpoint temp
#define EMS_TYPE_RC20StatusMessage_curr 2 // current temp
// RC30 specific
#define EMS_TYPE_RC30StatusMessage 0x41 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC30Set 0xA7 // for setting values like temp and mode
#define EMS_OFFSET_RC30Set_mode 23 // position of thermostat mode
#define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature
#define EMS_TYPE_RC30StatusMessage 0x41 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC30Set 0xA7 // for setting values like temp and mode
#define EMS_OFFSET_RC30Set_mode 23 // position of thermostat mode
#define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature
#define EMS_TYPE_RC30StatusMessage_setpoint 1 // setpoint temp
#define EMS_TYPE_RC30StatusMessage_curr 2 // current temp
// RC35 specific - not implemented yet
#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
#define EMS_OFFSET_RC35Set_mode 7 // position of thermostat mode
#define EMS_OFFSET_RC35Set_temp 2 // position of thermostat setpoint temperature
// RC35 specific
#define EMS_TYPE_RC35StatusMessage 0x3E // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC35StatusMessage_setpoint 2 // desired temp
#define EMS_TYPE_RC35Set 0x3D // for setting values like temp and mode (Working mode HC1)
#define EMS_OFFSET_RC35Set_mode 6 // 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
// Easy specific
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
#define EMS_TYPE_EasyStatusMessage_setpoint 10 // setpoint temp
#define EMS_TYPE_EasyStatusMessage_curr 8 // current temp
// default values
#define EMS_VALUE_INT_ON 1 // boolean true
@@ -119,17 +128,15 @@ typedef enum {
typedef struct {
_EMS_RX_STATUS emsRxStatus;
_EMS_TX_STATUS emsTxStatus;
uint16_t emsRxPgks; // received
uint16_t emsTxPkgs; // sent
uint16_t emxCrcErr; // CRC errors
bool emsPollEnabled; // flag enable the response to poll messages
bool emsTxEnabled; // flag if we're allowing sending of Tx packages
bool emsThermostatEnabled; // if there is a RCxx thermostat active
bool emsBoilerEnabled; // is the boiler online
_EMS_SYS_LOGGING emsLogging; // logging
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
bool emsBusConnected; // is there an active bus
unsigned long emsRxTimestamp; // timestamp of last EMS poll
uint16_t emsRxPgks; // received
uint16_t emsTxPkgs; // sent
uint16_t emxCrcErr; // CRC errors
bool emsPollEnabled; // flag enable the response to poll messages
bool emsTxEnabled; // flag if we're allowing sending of Tx packages
_EMS_SYS_LOGGING emsLogging; // logging
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
bool emsBusConnected; // is there an active bus
unsigned long emsRxTimestamp; // timestamp of last EMS poll
} _EMS_Sys_Status;
// The Tx send package
@@ -310,7 +317,7 @@ typedef struct {
// function definitions
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_init();
void ems_init(_EMS_MODEL_ID boiler_modelid, _EMS_MODEL_ID thermostat_modelid);
void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh = false);
void ems_sendRawTelegram(char * telegram);
@@ -322,12 +329,11 @@ void ems_setWarmTapWaterActivated(bool activated);
void ems_setExperimental(uint8_t value);
void ems_setPoll(bool b);
void ems_setTxEnabled(bool b);
void ems_setThermostatEnabled(bool b);
void ems_setBoilerEnabled(bool b);
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
void ems_setEmsRefreshed(bool b);
void ems_setWarmWaterModeComfort(bool comfort);
bool ems_checkEMSBUSAlive();
void ems_setModels();
void ems_getThermostatValues();
void ems_getBoilerValues();
@@ -339,9 +345,10 @@ bool ems_getBusConnected();
_EMS_SYS_LOGGING ems_getLogging();
uint8_t ems_getEmsTypesCount();
bool ems_getEmsRefreshed();
void ems_getAllVersions();
_EMS_MODEL_ID ems_getThermostatModel();
_EMS_MODEL_ID ems_getBoilerModel();
void ems_scanDevices();
void ems_printAllTypes();
char * ems_getThermostatType(char * buffer);
void ems_printTxQueue();
@@ -354,6 +361,8 @@ void _debugPrintPackage(const char * prefix, uint8_t * data, uint8_t len, con
void _ems_clearTxData();
int _ems_findModel(_EMS_MODEL_ID model_id);
char * _ems_buildModelString(char * buffer, uint8_t size, _EMS_MODEL_ID model_id);
bool _ems_setModel(_EMS_MODEL_ID model_id);
// global so can referenced in other classes
extern _EMS_Sys_Status EMS_Sys_Status;

View File

@@ -42,3 +42,19 @@
// set this if using an external temperature sensor like a DS18B20
#define TEMPERATURE_SENSOR_PIN D7
// logging - EMS_SYS_LOGGING_VERBOSE, EMS_SYS_LOGGING_NONE, EMS_SYS_LOGGING_BASIC (see ems.h)
// this can be changed via the Telnet console using the 'l' command
#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_NONE
//#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_VERBOSE
//#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_BASIC
// By default the EMS bus will be scanned for known devices. You can override this here
// by fixing the Boiler and Thermostat types
// Options are in ems.h and include..
// boilers: EMS_MODEL_BK15, EMS_MODEL_UBA, EMS_MODEL_BC10, EMS_MODEL_MM10, EMS_MODEL_WM10
// thermostats: EMS_MODEL_ES73, EMS_MODEL_RC20, EMS_MODEL_RC30, EMS_MODEL_RC35, EMS_MODEL_EASY
#define MY_BOILER_MODELID EMS_MODEL_NONE
#define MY_THERMOSTAT_MODELID EMS_MODEL_NONE
//#define MY_BOILER_MODELID EMS_MODEL_UBA
//#define MY_THERMOSTAT_MODELID EMS_MODEL_RC20

View File

@@ -1,5 +1,5 @@
#pragma once
#define APP_NAME "EMS-ESP-Boiler"
#define APP_VERSION "1.2.2"
#define APP_VERSION "1.2.3"
#define APP_HOSTNAME "boiler"