mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 08:19:52 +03:00
version 1.3.2. See changelog
This commit is contained in:
21
CHANGELOG.md
21
CHANGELOG.md
@@ -5,6 +5,27 @@ 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.3.2] 2019-01-23
|
||||
|
||||
### Fixed
|
||||
|
||||
- Handle thermostats that don't have builtin temperature sensors when showing current temperature (https://github.com/proddy/EMS-ESP/issues/18#issuecomment-451012963)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved way to identify if the EMS bus is connected
|
||||
- Improved 'types' command to show more details
|
||||
- Improved auto detect of thermostat types
|
||||
|
||||
### Added
|
||||
|
||||
- Some more devices like the Nefit Topline & RC310 thermostat recognition
|
||||
- Added a check to see Tx is possible. See 'Tx Capable' under the 'info' screen
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed `MY_BOILER_MODELID` from `my_config.h`. It's always hardcoded.
|
||||
|
||||
## [1.3.1] 2019-01-12
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#define PUBLISHVALUES_TIME 120 // every 2 minutes post MQTT values
|
||||
Ticker publishValuesTimer;
|
||||
|
||||
#define SYSTEMCHECK_TIME 10 // every 10 seconds check if Boiler is online and execute other requests
|
||||
#define SYSTEMCHECK_TIME 20 // every 20 seconds check if Boiler is online
|
||||
Ticker systemCheckTimer;
|
||||
|
||||
#define REGULARUPDATES_TIME 60 // every minute a call is made to fetch data from EMS devices manually
|
||||
@@ -252,7 +252,7 @@ void _renderBoolValue(const char * prefix, uint8_t value) {
|
||||
void showInfo() {
|
||||
// General stats from EMS bus
|
||||
|
||||
myDebug("%sEMS-ESP System setstats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
myDebug("%sEMS-ESP System stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
_EMS_SYS_LOGGING sysLog = ems_getLogging();
|
||||
if (sysLog == EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug(" System logging set to Basic");
|
||||
@@ -266,8 +266,6 @@ void showInfo() {
|
||||
|
||||
myDebug(" LED is %s", EMSESP_Status.led_enabled ? "on" : "off");
|
||||
|
||||
myDebug(" # EMS type handlers: %d", ems_getEmsTypesCount());
|
||||
|
||||
myDebug(" Thermostat is %s, Boiler is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s",
|
||||
(ems_getThermostatEnabled() ? "enabled" : "disabled"),
|
||||
(ems_getBoilerEnabled() ? "enabled" : "disabled"),
|
||||
@@ -276,7 +274,9 @@ void showInfo() {
|
||||
((EMSESP_Status.shower_timer) ? "enabled" : "disabled"),
|
||||
((EMSESP_Status.shower_alert) ? "enabled" : "disabled"));
|
||||
|
||||
myDebug(" EMS Bus Stats: Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
|
||||
myDebug("\n%sEMS Bus Stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
myDebug(" Bus Connected=%s, Tx Capable=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
|
||||
(ems_getTxCapable() ? "yes" : "no"),
|
||||
(ems_getBusConnected() ? "yes" : "no"),
|
||||
EMS_Sys_Status.emsRxPgks,
|
||||
EMS_Sys_Status.emsTxPkgs,
|
||||
@@ -288,7 +288,7 @@ void showInfo() {
|
||||
|
||||
// version details
|
||||
char buffer_type[64];
|
||||
myDebug(" Boiler type: %s", ems_getBoilerType(buffer_type));
|
||||
myDebug(" Boiler type: %s", ems_getBoilerDescription(buffer_type));
|
||||
|
||||
// active stats
|
||||
myDebug(" Hot tap water is %s", (EMS_Boiler.tapwaterActive ? "running" : "off"));
|
||||
@@ -357,7 +357,7 @@ void showInfo() {
|
||||
// Thermostat stats
|
||||
if (ems_getThermostatEnabled()) {
|
||||
myDebug("%sThermostat stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
myDebug(" Thermostat type: %s", ems_getThermostatType(buffer_type));
|
||||
myDebug(" Thermostat type: %s", ems_getThermostatDescription(buffer_type));
|
||||
_renderFloatValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp);
|
||||
_renderFloatValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp);
|
||||
if (ems_getThermostatModel() != EMS_MODEL_EASY) {
|
||||
@@ -448,7 +448,7 @@ void publishValues(bool force) {
|
||||
// see if the heating or hot tap water has changed, if so send
|
||||
// last_boilerActive stores heating in bit 1 and tap water in bit 2
|
||||
if ((last_boilerActive != ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive)) || force) {
|
||||
myDebugLog("Publishing hot water and heating state via MQTT");
|
||||
myDebugLog("Publishing hot water and heating states via MQTT");
|
||||
myESP.mqttPublish(TOPIC_BOILER_TAPWATER_ACTIVE, EMS_Boiler.tapwaterActive == 1 ? "1" : "0");
|
||||
myESP.mqttPublish(TOPIC_BOILER_HEATING_ACTIVE, EMS_Boiler.heatingActive == 1 ? "1" : "0");
|
||||
|
||||
@@ -570,17 +570,18 @@ void FSCallback(MYESP_FSACTION action, JsonObject & json) {
|
||||
// callback for custom settings
|
||||
// wc is number of arguments after the 'set' command
|
||||
bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) {
|
||||
bool ok = false;
|
||||
|
||||
if (action == MYESP_FSACTION_SET) {
|
||||
if ((strcmp(setting, "led") == 0) && (wc == 2)) {
|
||||
if (strcmp(value, "on") == 0) {
|
||||
EMSESP_Status.led_enabled = true;
|
||||
ok = true;
|
||||
} else if (strcmp(value, "off") == 0) {
|
||||
EMSESP_Status.led_enabled = false;
|
||||
ok = true;
|
||||
// let's make sure LED is really off
|
||||
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off
|
||||
} else {
|
||||
// unknown command
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -589,7 +590,7 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c
|
||||
myDebug(" led=%s", EMSESP_Status.led_enabled ? "on" : "off");
|
||||
}
|
||||
|
||||
return true;
|
||||
return ok;
|
||||
}
|
||||
|
||||
// call back when a telnet client connects or disconnects
|
||||
@@ -762,7 +763,7 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
||||
// handle incoming MQTT publish events
|
||||
if (type == MQTT_MESSAGE_EVENT) {
|
||||
// handle response from a start message
|
||||
// for HA, it gets sent the bootime
|
||||
// for example with HA it sends the system time from the server
|
||||
if (strcmp(topic, MQTT_TOPIC_START) == 0) {
|
||||
myDebug("Received boottime: %s", message);
|
||||
myESP.setBoottime(message);
|
||||
@@ -828,9 +829,10 @@ void WIFICallback() {
|
||||
myDebug("[UART] Opened Rx/Tx connection");
|
||||
#endif
|
||||
|
||||
// now that we're connected, check to see if we boiler and thermostat set
|
||||
// otherwise this will initiate a self scan
|
||||
ems_setModels();
|
||||
// now that we're connected, set up the boiler and thermostat
|
||||
// the boiler's version will be requested and if there is no thermostat hardcoded it will try
|
||||
// and find the first one
|
||||
ems_discoverModels();
|
||||
}
|
||||
|
||||
// Initialize the boiler settings and shower settings
|
||||
@@ -879,20 +881,16 @@ void do_scanThermostat() {
|
||||
// do a system health check every now and then to see if we all connections
|
||||
void do_systemCheck() {
|
||||
if (!ems_getBusConnected()) {
|
||||
myDebug("Error! Unable to connect to EMS bus. Check connection and make sure you're not in DEBUG_SUPPORT mode. Retrying in %d "
|
||||
"seconds...",
|
||||
SYSTEMCHECK_TIME);
|
||||
myDebug("Error! Unable to read from EMS bus. Retrying in %d seconds...", SYSTEMCHECK_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
// force calls to get data from EMS for the types that aren't sent as broadcasts
|
||||
// only if we have a EMS connection
|
||||
void do_regularUpdates() {
|
||||
if (ems_getBusConnected()) {
|
||||
myDebugLog("Calling scheduled data refresh from EMS devices..");
|
||||
ems_getThermostatValues();
|
||||
ems_getBoilerValues();
|
||||
}
|
||||
myDebugLog("Calling scheduled data refresh from EMS devices..");
|
||||
ems_getThermostatValues();
|
||||
ems_getBoilerValues();
|
||||
}
|
||||
|
||||
// turn off hot water to send a shot of cold
|
||||
@@ -1010,7 +1008,7 @@ void setup() {
|
||||
|
||||
// init the EMS bus
|
||||
// call ems.cpp's init function to set all the internal params
|
||||
ems_init(MY_BOILER_MODELID, MY_THERMOSTAT_MODELID);
|
||||
ems_init(MY_THERMOSTAT_MODELID);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
513
src/ems.cpp
513
src/ems.cpp
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* ems.cpp
|
||||
*
|
||||
* handles all the processing of the EMS messages
|
||||
* Handles all the processing of the EMS messages
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
@@ -52,6 +52,7 @@ void _process_EasyStatusMessage(uint8_t * data, uint8_t length);
|
||||
|
||||
/*
|
||||
* Recognized EMS types and the functions they call to process the telegrams
|
||||
* Format: MODEL ID, TYPE ID, Description, function
|
||||
*/
|
||||
const _EMS_Type EMS_Types[] = {
|
||||
|
||||
@@ -67,50 +68,45 @@ const _EMS_Type EMS_Types[] = {
|
||||
{EMS_MODEL_UBA, EMS_TYPE_UBAMaintenanceSettingsMessage, "UBAMaintenanceSettingsMessage", NULL},
|
||||
{EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", NULL},
|
||||
{EMS_MODEL_UBA, EMS_TYPE_UBAMaintenanceStatusMessage, "UBAMaintenanceStatusMessage", NULL},
|
||||
{EMS_MODEL_UBA, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
// RC20 and RC20F
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RC20Set, "RC20Set", _process_RC20Set},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RC20StatusMessage, "RC20StatusMessage", _process_RC20StatusMessage},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RC20Set, "RC20Set", _process_RC20Set},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RC20StatusMessage, "RC20StatusMessage", _process_RC20StatusMessage},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
// RC30
|
||||
{EMS_MODEL_RC30, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC30, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
{EMS_MODEL_RC30, EMS_TYPE_RC30Set, "RC30Set", _process_RC30Set},
|
||||
{EMS_MODEL_RC30, EMS_TYPE_RC30StatusMessage, "RC30StatusMessage", _process_RC30StatusMessage},
|
||||
{EMS_MODEL_RC30, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
// RC35
|
||||
{EMS_MODEL_RC35, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC35, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
{EMS_MODEL_RC35, EMS_TYPE_RC35Set, "RC35Set", _process_RC35Set},
|
||||
{EMS_MODEL_RC35, EMS_TYPE_RC35StatusMessage, "RC35StatusMessage", _process_RC35StatusMessage},
|
||||
{EMS_MODEL_RC35, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
// ES73
|
||||
{EMS_MODEL_ES73, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_ES73, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
{EMS_MODEL_ES73, EMS_TYPE_RC35Set, "RC35Set", _process_RC35Set},
|
||||
{EMS_MODEL_ES73, EMS_TYPE_RC35StatusMessage, "RC35StatusMessage", _process_RC35StatusMessage},
|
||||
{EMS_MODEL_ES73, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
// Easy
|
||||
{EMS_MODEL_EASY, EMS_TYPE_EasyStatusMessage, "EasyStatusMessage", _process_EasyStatusMessage},
|
||||
{EMS_MODEL_EASY, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints}
|
||||
|
||||
};
|
||||
|
||||
// calculate sizes of arrays
|
||||
uint8_t _EMS_Types_max = ArraySize(EMS_Types); // number of defined types
|
||||
uint8_t _Model_Types_max = ArraySize(Model_Types); // number of models
|
||||
uint8_t _Boiler_Types_max = ArraySize(Boiler_Types); // number of models
|
||||
uint8_t _Thermostat_Types_max = ArraySize(Thermostat_Types); // number of defined thermostat types
|
||||
|
||||
// these structs contain the data we store from the Boiler and Thermostat
|
||||
@@ -134,8 +130,9 @@ const uint8_t ems_crc_table[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E,
|
||||
0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7, 0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5, 0xF7, 0xE9, 0xEB, 0xED, 0xEF,
|
||||
0xE1, 0xE3, 0xE5, 0xE7};
|
||||
|
||||
const uint8_t TX_WRITE_TIMEOUT_COUNT = 3; // 3 retries before timeout
|
||||
const unsigned long EMS_BUS_TIMEOUT = 5000; // timeout in ms before recognizing the ems bus is offline (5 seconds)
|
||||
const uint8_t TX_WRITE_TIMEOUT_COUNT = 3; // 3 retries before timeout
|
||||
const unsigned long EMS_BUS_TIMEOUT = 15000; // timeout in ms before recognizing the ems bus is offline (15 seconds)
|
||||
const unsigned long EMS_POLL_TIMEOUT = 5000; // timeout in ms before recognizing the ems bus is offline (5 seconds)
|
||||
|
||||
uint8_t _emsTxRetryCount; // used for retries when sending failed
|
||||
uint8_t _ems_PollCount; // not used, but can be used to slow down sending on faster chips
|
||||
@@ -143,17 +140,20 @@ 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(uint8_t boiler_modelid, uint8_t thermostat_modelid) {
|
||||
void ems_init(uint8_t thermostat_modelid) {
|
||||
// overall status
|
||||
EMS_Sys_Status.emsRxPgks = 0;
|
||||
EMS_Sys_Status.emsTxPkgs = 0;
|
||||
EMS_Sys_Status.emxCrcErr = 0;
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_IDLE;
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE;
|
||||
EMS_Sys_Status.emsRefreshed = false;
|
||||
EMS_Sys_Status.emsPollEnabled = false; // start up with Poll disabled
|
||||
EMS_Sys_Status.emsTxEnabled = true; // start up with Tx enabled
|
||||
EMS_Sys_Status.emsBusConnected = false;
|
||||
EMS_Sys_Status.emsRxPgks = 0;
|
||||
EMS_Sys_Status.emsTxPkgs = 0;
|
||||
EMS_Sys_Status.emxCrcErr = 0;
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_IDLE;
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE;
|
||||
EMS_Sys_Status.emsRefreshed = false;
|
||||
EMS_Sys_Status.emsPollEnabled = false; // start up with Poll disabled
|
||||
EMS_Sys_Status.emsTxEnabled = true; // start up with Tx enabled
|
||||
EMS_Sys_Status.emsBusConnected = false;
|
||||
EMS_Sys_Status.emsRxTimestamp = 0;
|
||||
EMS_Sys_Status.emsTxCapable = false;
|
||||
EMS_Sys_Status.emsPollTimestamp = 0;
|
||||
|
||||
// thermostat
|
||||
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_FLOAT_NOTSET;
|
||||
@@ -215,11 +215,16 @@ void ems_init(uint8_t boiler_modelid, uint8_t thermostat_modelid) {
|
||||
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;
|
||||
// boiler is hardcoded
|
||||
EMS_Boiler.type_id = EMS_ID_BOILER; // fixed at 0x08
|
||||
EMS_Boiler.product_id = 0;
|
||||
strlcpy(EMS_Boiler.version, "not set", sizeof(EMS_Boiler.version));
|
||||
|
||||
// for lookup later
|
||||
EMS_Boiler.model_id = boiler_modelid;
|
||||
EMS_Thermostat.model_id = thermostat_modelid;
|
||||
// set thermostat model
|
||||
EMS_Thermostat.type_id = EMS_ID_NONE; // fixed at 0x08
|
||||
EMS_Thermostat.product_id = 0;
|
||||
_ems_setThermostatModel(thermostat_modelid);
|
||||
strlcpy(EMS_Thermostat.version, "not set", sizeof(EMS_Thermostat.version));
|
||||
|
||||
// counters
|
||||
_ems_PollCount = 0;
|
||||
@@ -258,11 +263,22 @@ void ems_setEmsRefreshed(bool b) {
|
||||
}
|
||||
|
||||
bool ems_getBoilerEnabled() {
|
||||
return (EMS_Boiler.model_id != EMS_MODEL_NONE);
|
||||
return (EMS_Boiler.type_id != EMS_ID_NONE);
|
||||
}
|
||||
|
||||
bool ems_getThermostatEnabled() {
|
||||
return (EMS_Thermostat.model_id != EMS_MODEL_NONE);
|
||||
return (EMS_Thermostat.type_id != EMS_ID_NONE);
|
||||
}
|
||||
|
||||
uint8_t ems_getThermostatModel() {
|
||||
return (EMS_Thermostat.model_id);
|
||||
}
|
||||
|
||||
bool ems_getTxCapable() {
|
||||
if ((millis() - EMS_Sys_Status.emsPollTimestamp) > EMS_POLL_TIMEOUT) {
|
||||
EMS_Sys_Status.emsTxCapable = false;
|
||||
}
|
||||
return EMS_Sys_Status.emsTxCapable;
|
||||
}
|
||||
|
||||
bool ems_getBusConnected() {
|
||||
@@ -276,10 +292,6 @@ _EMS_SYS_LOGGING ems_getLogging() {
|
||||
return EMS_Sys_Status.emsLogging;
|
||||
}
|
||||
|
||||
uint8_t ems_getEmsTypesCount() {
|
||||
return _EMS_Types_max;
|
||||
}
|
||||
|
||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel) {
|
||||
if (loglevel <= EMS_SYS_LOGGING_VERBOSE) {
|
||||
EMS_Sys_Status.emsLogging = loglevel;
|
||||
@@ -297,22 +309,6 @@ 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)
|
||||
@@ -568,17 +564,21 @@ void _ems_sendTelegram() {
|
||||
*/
|
||||
void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||
// check if we just received a single byte
|
||||
// it could well be a Poll request from the boiler which has an ID 0x8B (0x0B | 0x80 to set 8th bit)
|
||||
// or either a return code like 0x01 or 0x04 from the last Write command issued
|
||||
// it could well be a Poll request from the boiler to us which will have a value of 0x8B (0x0B | 0x80)
|
||||
// or either a return code like 0x01 or 0x04 from the last Write command
|
||||
if (length == 1) {
|
||||
uint8_t value = telegram[0]; // 1st byte of data package
|
||||
|
||||
// TODO: remove, only for debugging why some people can't do a Tx
|
||||
//if ((value & 0x80) == 0x80) {
|
||||
// myDebug("Poll: %02X", value);
|
||||
//}
|
||||
|
||||
// check first for a Poll for us
|
||||
if (value == (EMS_ID_ME | 0x80)) {
|
||||
// we use this to see if we always have a connection to the boiler, in case of drop outs
|
||||
EMS_Sys_Status.emsRxTimestamp = millis(); // timestamp of last read
|
||||
EMS_Sys_Status.emsBusConnected = true;
|
||||
|
||||
// store when we received a last poll
|
||||
EMS_Sys_Status.emsPollTimestamp = millis();
|
||||
EMS_Sys_Status.emsTxCapable = true;
|
||||
|
||||
// do we have something to send thats waiting in the Tx queue? if so send it
|
||||
if (!EMS_TxQueue.isEmpty()) {
|
||||
@@ -652,7 +652,11 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||
}
|
||||
|
||||
// here we know its a valid incoming telegram of at least 6 bytes
|
||||
// lets process it and see what to do next
|
||||
// we use this to see if we always have a connection to the boiler, in case of drop outs
|
||||
EMS_Sys_Status.emsRxTimestamp = millis(); // timestamp of last read
|
||||
EMS_Sys_Status.emsBusConnected = true;
|
||||
|
||||
// now lets process it and see what to do next
|
||||
_processType(telegram, length);
|
||||
}
|
||||
|
||||
@@ -974,9 +978,15 @@ void _process_RC30StatusMessage(uint8_t * data, uint8_t length) {
|
||||
*/
|
||||
void _process_RC35StatusMessage(uint8_t * data, uint8_t length) {
|
||||
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC35StatusMessage_setpoint]) / (float)2;
|
||||
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC35StatusMessage_curr, data);
|
||||
EMS_Thermostat.day_mode = bitRead(data[EMS_OFFSET_RC35Get_mode_day], 1); //get day mode flag
|
||||
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
|
||||
|
||||
// check if temp sensor is unavailable
|
||||
if ((data[0] == 0x7D) && (data[1] = 0x00)) {
|
||||
EMS_Thermostat.curr_roomTemp = EMS_VALUE_FLOAT_NOTSET;
|
||||
} else {
|
||||
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC35StatusMessage_curr, data);
|
||||
}
|
||||
EMS_Thermostat.day_mode = bitRead(data[EMS_OFFSET_RC35Get_mode_day], 1); //get day mode flag
|
||||
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1024,6 +1034,7 @@ void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length) {
|
||||
|
||||
/**
|
||||
* type 0x02 - get the firmware version and type of an EMS device
|
||||
* look up known devices via the product id and setup if not already set
|
||||
*/
|
||||
void _process_Version(uint8_t * data, uint8_t length) {
|
||||
// ignore short messages that we can't interpret
|
||||
@@ -1031,155 +1042,142 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t product_id = data[0];
|
||||
uint8_t major = data[1];
|
||||
uint8_t minor = data[2];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
bool typeFound = false;
|
||||
bool isThermostat = false;
|
||||
char version[10] = {0};
|
||||
snprintf(version, sizeof(version), "%02d.%02d", major, minor);
|
||||
uint8_t product_id = data[0];
|
||||
char version[10] = {0};
|
||||
snprintf(version, sizeof(version), "%02d.%02d", data[1], data[2]);
|
||||
|
||||
// use product ID to search
|
||||
while (i < _Model_Types_max) {
|
||||
if (Model_Types[i].product_id == product_id) {
|
||||
// see if its a known boiler
|
||||
int i = 0;
|
||||
bool typeFound = false;
|
||||
while (i < _Boiler_Types_max) {
|
||||
if (Boiler_Types[i].product_id == product_id) {
|
||||
typeFound = true; // we have a matching product id. i is the index.
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!typeFound) {
|
||||
myDebug("Unrecognized device found. Product ID %d, Version %s", product_id, version);
|
||||
if (typeFound) {
|
||||
// its a boiler
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Boiler found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
||||
Boiler_Types[i].model_string,
|
||||
Boiler_Types[i].type_id,
|
||||
product_id,
|
||||
version);
|
||||
}
|
||||
|
||||
// if its a boiler set it (even if it was already set)
|
||||
if (Boiler_Types[i].type_id == EMS_ID_BOILER) {
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("* Setting boiler to this new type");
|
||||
}
|
||||
EMS_Boiler.type_id = Boiler_Types[i].type_id;
|
||||
EMS_Boiler.product_id = Boiler_Types[i].product_id;
|
||||
strlcpy(EMS_Boiler.version, version, sizeof(EMS_Boiler.version));
|
||||
|
||||
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// check to see if its a known thermostat
|
||||
while (j < _Thermostat_Types_max) {
|
||||
if (Thermostat_Types[j].model_id == Model_Types[i].model_id) {
|
||||
isThermostat = true; // we have a matching model
|
||||
// its not a boiler, maybe its a known thermostat
|
||||
i = 0;
|
||||
while (i < _Thermostat_Types_max) {
|
||||
if (Thermostat_Types[i].product_id == product_id) {
|
||||
typeFound = true; // we have a matching product id. i is the index.
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
i++;
|
||||
}
|
||||
|
||||
// set a thermostat
|
||||
if (isThermostat) {
|
||||
if (typeFound) {
|
||||
// its a known thermostat
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Thermostat recognized. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
||||
Model_Types[i].model_string,
|
||||
Model_Types[i].type_id,
|
||||
myDebug("Thermostat found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
||||
Thermostat_Types[i].model_string,
|
||||
Thermostat_Types[i].type_id,
|
||||
product_id,
|
||||
version);
|
||||
}
|
||||
|
||||
// if we don't have a thermostat set, use this one
|
||||
if (!ems_getThermostatEnabled()) {
|
||||
// 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;
|
||||
// if its the one we hard coded, refresh the data anyway
|
||||
if ((EMS_Thermostat.model_id == EMS_MODEL_NONE) || (EMS_Thermostat.model_id == Thermostat_Types[i].model_id)) {
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("* Setting thermostat to this new type");
|
||||
}
|
||||
EMS_Thermostat.model_id = Thermostat_Types[i].model_id;
|
||||
EMS_Thermostat.type_id = Thermostat_Types[i].type_id;
|
||||
EMS_Thermostat.read_supported = Thermostat_Types[i].read_supported;
|
||||
EMS_Thermostat.write_supported = Thermostat_Types[i].write_supported;
|
||||
EMS_Thermostat.product_id = product_id;
|
||||
strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version));
|
||||
|
||||
ems_getThermostatValues(); // get Thermostat values (if supported)
|
||||
// get Thermostat values (if supported)
|
||||
ems_getThermostatValues();
|
||||
}
|
||||
} else {
|
||||
// otherwise assume its a boiler or some other EMS device
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Boiler recognized. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
||||
Model_Types[i].model_string,
|
||||
Model_Types[i].type_id,
|
||||
product_id,
|
||||
version);
|
||||
}
|
||||
|
||||
if (!ems_getBoilerEnabled()) {
|
||||
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
|
||||
}
|
||||
myDebug("Unrecognized device found. Product ID %d, Version %s", product_id, version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
/*
|
||||
* Figure out the boiler and thermostat types
|
||||
*/
|
||||
bool _ems_setModel(uint8_t model_id) {
|
||||
if (model_id == EMS_MODEL_NONE) {
|
||||
return false; // invalid model_id
|
||||
}
|
||||
void ems_discoverModels() {
|
||||
// boiler
|
||||
ems_doReadCommand(EMS_TYPE_Version, EMS_ID_BOILER); // get version details of boiler
|
||||
ems_getBoilerValues(); // fetch values from boiler, instead of waiting for broadcasts
|
||||
|
||||
// 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.
|
||||
// thermostat
|
||||
if (EMS_Thermostat.model_id == EMS_MODEL_NONE) {
|
||||
ems_scanDevices(); // auto-discover it
|
||||
} else {
|
||||
// set the model as hardcoded (see my_devices.h) and fetch the version and product id
|
||||
_ems_setThermostatModel(EMS_Thermostat.model_id);
|
||||
ems_doReadCommand(EMS_TYPE_Version, EMS_Thermostat.type_id);
|
||||
ems_getThermostatValues();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a thermostat model ID go and fetch its characteristics
|
||||
*/
|
||||
void _ems_setThermostatModel(uint8_t thermostat_modelid) {
|
||||
bool found = false;
|
||||
uint8_t i = 0;
|
||||
const _Thermostat_Type * thermostat_type;
|
||||
while (i < _Thermostat_Types_max) {
|
||||
thermostat_type = &Thermostat_Types[i];
|
||||
if (thermostat_type->model_id == thermostat_modelid) {
|
||||
found = true; // we have a matching product id
|
||||
break;
|
||||
}
|
||||
model_loc++;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Unknown model specified");
|
||||
myDebug("Unknown thermostat model specified. Trying a scan...");
|
||||
}
|
||||
return false; // unknown model_id
|
||||
ems_scanDevices(); // auto-discover it
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 the thermostat
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Setting Thermostat. Model %s with TypeID 0x%02X, Product ID %d",
|
||||
thermostat_type->model_string,
|
||||
thermostat_type->type_id,
|
||||
thermostat_type->product_id);
|
||||
}
|
||||
|
||||
// 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;
|
||||
// set its capabilities
|
||||
EMS_Thermostat.model_id = thermostat_type->model_id;
|
||||
EMS_Thermostat.type_id = thermostat_type->type_id;
|
||||
EMS_Thermostat.read_supported = thermostat_type->read_supported;
|
||||
EMS_Thermostat.write_supported = thermostat_type->write_supported;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1211,17 +1209,6 @@ void _process_RCTime(uint8_t * data, uint8_t length) {
|
||||
EMS_Thermostat.day = data[3];
|
||||
EMS_Thermostat.month = data[1];
|
||||
EMS_Thermostat.year = data[0];
|
||||
|
||||
// we can optional set the time based on the thermostat's time if we want.
|
||||
// make sure you include Time library and TimeLib.h if enabling this
|
||||
/*
|
||||
setTime(EMS_Thermostat.hour,
|
||||
EMS_Thermostat.minute,
|
||||
EMS_Thermostat.second,
|
||||
EMS_Thermostat.day,
|
||||
EMS_Thermostat.month,
|
||||
EMS_Thermostat.year + 2000);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1281,7 +1268,7 @@ void ems_getThermostatValues() {
|
||||
}
|
||||
|
||||
if (!EMS_Thermostat.read_supported) {
|
||||
myDebug("Read operations not yet supported for this model Thermostat");
|
||||
myDebug("Read operations not yet supported for this model thermostat");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1314,99 +1301,107 @@ void ems_getBoilerValues() {
|
||||
ems_doReadCommand(EMS_TYPE_UBATotalUptimeMessage, EMS_Boiler.type_id); // get Warm Water values
|
||||
}
|
||||
|
||||
|
||||
// return pointer to Model details
|
||||
int _ems_findModel(uint8_t model_id) {
|
||||
uint8_t i = 0;
|
||||
bool found = false;
|
||||
|
||||
// scan through known ID types
|
||||
while (i < _Model_Types_max) {
|
||||
if (Model_Types[i].model_id == model_id) {
|
||||
found = true; // we have a match
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!found) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, uint8_t model_id) {
|
||||
int i = _ems_findModel(model_id);
|
||||
if (i != -1) {
|
||||
char tmp[6] = {0};
|
||||
strlcpy(buffer, Model_Types[i].model_string, size);
|
||||
strlcat(buffer, " [TypeID 0x", size);
|
||||
strlcat(buffer, _hextoa(Model_Types[i].type_id, tmp), size);
|
||||
strlcat(buffer, "] Product ID:", size);
|
||||
strlcat(buffer, itoa(Model_Types[i].product_id, tmp, 10), size);
|
||||
} else {
|
||||
strlcpy(buffer, "<unknown>", sizeof(buffer));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns current thermostat type as a string
|
||||
*/
|
||||
char * ems_getThermostatType(char * buffer) {
|
||||
uint8_t size = 64;
|
||||
char * ems_getThermostatDescription(char * buffer) {
|
||||
uint8_t size = 128;
|
||||
if (!ems_getThermostatEnabled()) {
|
||||
strlcpy(buffer, "<not enabled>", size);
|
||||
} else {
|
||||
_ems_buildModelString(buffer, size, EMS_Thermostat.model_id);
|
||||
// find the boiler details
|
||||
int i = 0;
|
||||
bool found = false;
|
||||
|
||||
// scan through known ID types
|
||||
while (i < _Thermostat_Types_max) {
|
||||
if (Thermostat_Types[i].product_id == EMS_Thermostat.product_id) {
|
||||
found = true; // we have a match
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (found) {
|
||||
strlcpy(buffer, Thermostat_Types[i].model_string, size);
|
||||
} else {
|
||||
strlcpy(buffer, "Generic Type", size);
|
||||
}
|
||||
|
||||
char tmp[6] = {0};
|
||||
strlcat(buffer, " [Type ID: 0x", size);
|
||||
strlcat(buffer, _hextoa(EMS_Thermostat.type_id, tmp), size);
|
||||
strlcat(buffer, "] Product ID:", size);
|
||||
strlcat(buffer, itoa(EMS_Thermostat.product_id, tmp, 10), size);
|
||||
strlcat(buffer, " Version:", size);
|
||||
strlcat(buffer, EMS_Thermostat.version, size);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns current boiler type as a string
|
||||
*/
|
||||
char * ems_getBoilerType(char * buffer) {
|
||||
uint8_t size = 64;
|
||||
char * ems_getBoilerDescription(char * buffer) {
|
||||
uint8_t size = 128;
|
||||
if (!ems_getBoilerEnabled()) {
|
||||
strlcpy(buffer, "<not enabled>", size);
|
||||
} else {
|
||||
_ems_buildModelString(buffer, size, EMS_Boiler.model_id);
|
||||
// find the boiler details
|
||||
int i = 0;
|
||||
bool found = false;
|
||||
|
||||
// scan through known ID types
|
||||
while (i < _Boiler_Types_max) {
|
||||
if (Boiler_Types[i].product_id == EMS_Boiler.product_id) {
|
||||
found = true; // we have a match
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (found) {
|
||||
strlcpy(buffer, Boiler_Types[i].model_string, size);
|
||||
} else {
|
||||
strlcpy(buffer, "Generic Type", size);
|
||||
}
|
||||
|
||||
char tmp[6] = {0};
|
||||
strlcat(buffer, " [Type ID: 0x", size);
|
||||
strlcat(buffer, _hextoa(EMS_Boiler.type_id, tmp), size);
|
||||
strlcat(buffer, "] Product ID:", size);
|
||||
strlcat(buffer, itoa(EMS_Boiler.product_id, tmp, 10), size);
|
||||
strlcat(buffer, " Version:", size);
|
||||
strlcat(buffer, EMS_Boiler.version, size);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// returns the model type for a thermostat
|
||||
uint8_t ems_getThermostatModel() {
|
||||
return (EMS_Thermostat.model_id);
|
||||
}
|
||||
|
||||
// returns the model type for a boiler
|
||||
uint8_t ems_getBoilerModel() {
|
||||
return (EMS_Boiler.model_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the versions of our connected devices
|
||||
*/
|
||||
void ems_scanDevices() {
|
||||
if (!ems_getBusConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
myDebug("Scanning EMS bus for devices. This may take a few seconds...");
|
||||
|
||||
// copy over the IDs from Model-type to a list
|
||||
std::list<uint8_t> Device_Ids;
|
||||
for (_Model_Type mt : Model_Types) {
|
||||
Device_Ids.push_back(mt.type_id);
|
||||
// start refresh when scanning and forget anything devices we may have already set
|
||||
EMS_Thermostat.type_id = EMS_ID_NONE; // forget thermostat
|
||||
EMS_Thermostat.model_id = EMS_MODEL_NONE;
|
||||
|
||||
std::list<uint8_t> Device_Ids; // new list
|
||||
|
||||
// copy over boilers
|
||||
for (_Boiler_Type bt : Boiler_Types) {
|
||||
Device_Ids.push_back(bt.type_id);
|
||||
}
|
||||
|
||||
// copy over thermostats
|
||||
for (_Thermostat_Type tt : Thermostat_Types) {
|
||||
Device_Ids.push_back(tt.type_id);
|
||||
}
|
||||
// remove duplicates and reserved IDs (like our own device)
|
||||
Device_Ids.sort();
|
||||
Device_Ids.unique();
|
||||
Device_Ids.remove(EMS_MODEL_NONE);
|
||||
Device_Ids.remove(EMS_MODEL_SERVICEKEY);
|
||||
|
||||
// send the read command with Version command
|
||||
for (uint8_t type_id : Device_Ids) {
|
||||
@@ -1418,36 +1413,30 @@ void ems_scanDevices() {
|
||||
* Print out all handled types
|
||||
*/
|
||||
void ems_printAllTypes() {
|
||||
myDebug("These %d telegram TypeIDs are recognized:", _EMS_Types_max);
|
||||
myDebug("\nThese %d boiler type devices are supported:", _Boiler_Types_max);
|
||||
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < _Boiler_Types_max; i++) {
|
||||
myDebug(" %s : type ID:0x%02X (%s)", Boiler_Types[i].model_string, EMS_Types[i].type, EMS_Types[i].typeString);
|
||||
}
|
||||
|
||||
myDebug("\nThese telegram type IDs are recognized for the selected boiler:");
|
||||
|
||||
for (i = 0; i < _EMS_Types_max; i++) {
|
||||
if (EMS_Types[i].model_id == EMS_MODEL_NONE) {
|
||||
myDebug(" (all devices) : type %02X (%s)", EMS_Types[i].type, EMS_Types[i].typeString);
|
||||
} else {
|
||||
int index = _ems_findModel(EMS_Types[i].model_id);
|
||||
if (index != -1) {
|
||||
myDebug(" %s : type %02X (%s)", Model_Types[index].model_string, EMS_Types[i].type, EMS_Types[i].typeString);
|
||||
}
|
||||
if ((EMS_Types[i].model_id == EMS_MODEL_ALL) || (EMS_Types[i].model_id == EMS_MODEL_UBA)) {
|
||||
myDebug(" type %02X (%s)", EMS_Types[i].type, EMS_Types[i].typeString);
|
||||
}
|
||||
}
|
||||
|
||||
myDebug("\nThese %d thermostats models are supported:", _Thermostat_Types_max);
|
||||
for (i = 0; i < _Thermostat_Types_max; i++) {
|
||||
// find the model's details
|
||||
for (int j = 0; j < _Model_Types_max; j++) {
|
||||
if (Model_Types[j].model_id == Thermostat_Types[i].model_id) {
|
||||
int index = _ems_findModel(Model_Types[j].model_id);
|
||||
if (index != -1) {
|
||||
myDebug(" %s [ID 0x%02X] Product ID:%d Read supported:%s Write supported:%s",
|
||||
Model_Types[index].model_string,
|
||||
Model_Types[index].type_id,
|
||||
Model_Types[index].product_id,
|
||||
(Thermostat_Types[i].read_supported) ? "yes" : "no",
|
||||
(Thermostat_Types[i].write_supported) ? "yes" : "no");
|
||||
}
|
||||
}
|
||||
}
|
||||
myDebug(" %s type ID:0x%02X Product ID:%d Read supported:%s Write supported:%s",
|
||||
Thermostat_Types[i].model_string,
|
||||
Thermostat_Types[i].type_id,
|
||||
Thermostat_Types[i].product_id,
|
||||
(Thermostat_Types[i].read_supported) ? "yes" : "no",
|
||||
(Thermostat_Types[i].write_supported) ? "yes" : "no");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
70
src/ems.h
70
src/ems.h
@@ -1,5 +1,10 @@
|
||||
/*
|
||||
* Header file for ems.cpp
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
* See ChangeLog.md for history
|
||||
* See README.md for Acknowledgments
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -8,8 +13,9 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
// EMS IDs
|
||||
#define EMS_ID_NONE 0x00 // Fixed - used as a dest in broadcast messages and empty type IDs
|
||||
#define EMS_ID_ME 0x0B // Fixed - our device, hardcoded as the "Service Key"
|
||||
#define EMS_ID_NONE 0x00 // Fixed - used as a dest in broadcast messages and empty type IDs
|
||||
#define EMS_ID_ME 0x0B // Fixed - our device, hardcoded as the "Service Key"
|
||||
#define EMS_ID_BOILER 0x08 // Fixed - boilers are always 0x08. I think.
|
||||
|
||||
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
|
||||
|
||||
@@ -25,7 +31,7 @@
|
||||
#define EMS_VALUE_INT_OFF 0 // boolean false
|
||||
#define EMS_VALUE_INT_NOTSET 0xFF // for 8-bit ints
|
||||
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs
|
||||
#define EMS_VALUE_FLOAT_NOTSET -255 // float unset
|
||||
#define EMS_VALUE_FLOAT_NOTSET -255 // float
|
||||
|
||||
#define EMS_THERMOSTAT_READ_YES true
|
||||
#define EMS_THERMOSTAT_READ_NO false
|
||||
@@ -89,15 +95,17 @@ 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
|
||||
_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 message received
|
||||
unsigned long emsPollTimestamp; // timestamp of last EMS poll
|
||||
bool emsTxCapable; // able to send via Tx
|
||||
} _EMS_Sys_Status;
|
||||
|
||||
// The Tx send package
|
||||
@@ -139,7 +147,17 @@ typedef struct {
|
||||
uint8_t product_id;
|
||||
uint8_t type_id;
|
||||
char model_string[50];
|
||||
} _Model_Type;
|
||||
} _Boiler_Type;
|
||||
|
||||
// Definition for thermostat type
|
||||
typedef struct {
|
||||
uint8_t model_id;
|
||||
uint8_t product_id;
|
||||
uint8_t type_id;
|
||||
char model_string[50];
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
} _Thermostat_Type;
|
||||
|
||||
/*
|
||||
* Telegram package defintions
|
||||
@@ -191,21 +209,15 @@ typedef struct { // UBAParameterWW
|
||||
|
||||
// settings
|
||||
char version[10];
|
||||
uint8_t type_id;
|
||||
uint8_t model_id;
|
||||
uint8_t type_id; // this is typically always 0x08
|
||||
uint8_t product_id;
|
||||
} _EMS_Boiler;
|
||||
|
||||
// Definition for thermostat type
|
||||
typedef struct {
|
||||
uint8_t model_id;
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
} _Thermostat_Type;
|
||||
|
||||
// Thermostat data
|
||||
typedef struct {
|
||||
uint8_t type_id; // the type ID of the thermostat
|
||||
uint8_t model_id; // which Thermostat type
|
||||
uint8_t product_id;
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
char version[10];
|
||||
@@ -234,7 +246,7 @@ typedef struct {
|
||||
|
||||
// function definitions
|
||||
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
|
||||
void ems_init(uint8_t boiler_modelid, uint8_t thermostat_modelid);
|
||||
void ems_init(uint8_t thermostat_modelid);
|
||||
void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh = false);
|
||||
void ems_sendRawTelegram(char * telegram);
|
||||
|
||||
@@ -259,25 +271,25 @@ bool ems_getThermostatEnabled();
|
||||
bool ems_getBoilerEnabled();
|
||||
bool ems_getBusConnected();
|
||||
_EMS_SYS_LOGGING ems_getLogging();
|
||||
uint8_t ems_getEmsTypesCount();
|
||||
bool ems_getEmsRefreshed();
|
||||
uint8_t ems_getThermostatModel();
|
||||
uint8_t ems_getBoilerModel();
|
||||
void ems_discoverModels();
|
||||
bool ems_getTxCapable();
|
||||
|
||||
void ems_scanDevices();
|
||||
void ems_printAllTypes();
|
||||
char * ems_getThermostatType(char * buffer);
|
||||
char * ems_getThermostatDescription(char * buffer);
|
||||
void ems_printTxQueue();
|
||||
char * ems_getBoilerType(char * buffer);
|
||||
char * ems_getBoilerDescription(char * buffer);
|
||||
|
||||
// private functions
|
||||
uint8_t _crcCalculator(uint8_t * data, uint8_t len);
|
||||
void _processType(uint8_t * telegram, uint8_t length);
|
||||
void _debugPrintPackage(const char * prefix, uint8_t * data, uint8_t len, const char * color);
|
||||
void _ems_clearTxData();
|
||||
int _ems_findModel(uint8_t model_id);
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, uint8_t model_id);
|
||||
int _ems_findBoilerModel(uint8_t model_id);
|
||||
bool _ems_setModel(uint8_t model_id);
|
||||
void _ems_setThermostatModel(uint8_t thermostat_modelid);
|
||||
|
||||
// global so can referenced in other classes
|
||||
extern _EMS_Sys_Status EMS_Sys_Status;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
/*
|
||||
* General information about known EMS devices
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
* See ChangeLog.md for history
|
||||
* See README.md for Acknowledgments
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -28,7 +33,7 @@
|
||||
#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco
|
||||
|
||||
/*
|
||||
* Thermostat...
|
||||
* Thermostats...
|
||||
*/
|
||||
|
||||
// Common for all thermostats
|
||||
@@ -71,17 +76,8 @@ typedef enum {
|
||||
EMS_MODEL_NONE,
|
||||
EMS_MODEL_ALL, // common for all devices
|
||||
|
||||
// service key
|
||||
EMS_MODEL_SERVICEKEY, // this is us
|
||||
|
||||
// main buderus boiler type devices
|
||||
EMS_MODEL_BK15,
|
||||
// generic ID for the boiler
|
||||
EMS_MODEL_UBA,
|
||||
EMS_MODEL_BC10,
|
||||
EMS_MODEL_BC25,
|
||||
EMS_MODEL_MM10,
|
||||
EMS_MODEL_WM10,
|
||||
EMS_MODEL_RFM20,
|
||||
|
||||
// thermostats
|
||||
EMS_MODEL_ES73,
|
||||
@@ -89,45 +85,42 @@ typedef enum {
|
||||
EMS_MODEL_RC20F,
|
||||
EMS_MODEL_RC30,
|
||||
EMS_MODEL_RC35,
|
||||
EMS_MODEL_EASY
|
||||
EMS_MODEL_EASY,
|
||||
EMS_MODEL_RC310,
|
||||
EMS_MODEL_CW100
|
||||
|
||||
} _EMS_MODEL_ID;
|
||||
|
||||
// EMS types for known Buderus devices. This list will be extended when new devices are recognized.
|
||||
// format is MODEL_ID, PRODUCT ID, TYPE_ID, DESCRIPTION
|
||||
const _Model_Type Model_Types[] = {
|
||||
|
||||
// me
|
||||
{EMS_MODEL_SERVICEKEY, 999, 0x0B, "Service Key"},
|
||||
const _Boiler_Type Boiler_Types[] = {
|
||||
|
||||
// various boilers and buderus type devices
|
||||
{EMS_MODEL_UBA, 123, 0x08, "MC10/UBA3 Boiler"},
|
||||
{EMS_MODEL_BK15, 64, 0x08, "Sieger BK15 Boiler"},
|
||||
{EMS_MODEL_BC10, 190, 0x09, "BC10 Base Controller"},
|
||||
{EMS_MODEL_BC25, 125, 0x09, "BC25 Base Controller"},
|
||||
{EMS_MODEL_RFM20, 68, 0x09, "RFM20 RC20F Receiver"},
|
||||
{EMS_MODEL_MM10, 251, 0x21, "MM10 Mixer Module"}, // warning, fake product id!
|
||||
{EMS_MODEL_WM10, 250, 0x11, "WM10 Switch Module"}, // warning, fake product id!
|
||||
|
||||
// controllers and thermostats
|
||||
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73"},
|
||||
{EMS_MODEL_RC20, 77, 0x17, "RC20 (e.g. Nefit Moduline 300)"},
|
||||
{EMS_MODEL_RC20F, 93, 0x18, "RC20F"},
|
||||
{EMS_MODEL_RC30, 78, 0x10, "RC30 (e.g. Nefit Moduline 400)"},
|
||||
{EMS_MODEL_RC35, 86, 0x10, "RC35 (or compatible"},
|
||||
{EMS_MODEL_EASY, 202, 0x18, "TC100 (e.g. Nefit Easy or CT100)"}
|
||||
{EMS_MODEL_UBA, 72, 0x08, "MC10"},
|
||||
{EMS_MODEL_UBA, 123, 0x08, "Nefit Trendline"},
|
||||
{EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"},
|
||||
{EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler"},
|
||||
{EMS_MODEL_UBA, 190, 0x09, "BC10 Base Controller"},
|
||||
{EMS_MODEL_UBA, 125, 0x09, "BC25 Base Controller"},
|
||||
{EMS_MODEL_UBA, 68, 0x09, "RFM20 Receiver"},
|
||||
{EMS_MODEL_UBA, 95, 0x08, "Bosch Condens 2500"},
|
||||
{EMS_MODEL_UBA, 251, 0x21, "MM10 Mixer Module"}, // warning, fake product id!
|
||||
{EMS_MODEL_UBA, 250, 0x11, "WM10 Switch Module"}, // warning, fake product id!
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Known thermostat types and their abilities
|
||||
* Known thermostat types and their capabilities
|
||||
*/
|
||||
const _Thermostat_Type Thermostat_Types[] = {
|
||||
|
||||
{EMS_MODEL_RC20, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC20F, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC30, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC35, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_EASY, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO},
|
||||
{EMS_MODEL_ES73, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}
|
||||
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC20, 77, 0x17, "RC20 (e.g. Nefit Moduline 300)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC30, 78, 0x10, "RC30 (e.g. Nefit Moduline 400)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC35, 86, 0x10, "RC35", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_EASY, 202, 0x18, "TC100 (e.g. Nefit Easy or CT100)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO},
|
||||
{EMS_MODEL_RC310, 158, 0x10, "RC310", EMS_THERMOSTAT_READ_NO, EMS_THERMOSTAT_WRITE_NO},
|
||||
{EMS_MODEL_CW100, 255, 0x18, "Bosch CW100", EMS_THERMOSTAT_READ_NO, EMS_THERMOSTAT_WRITE_NO}
|
||||
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/*
|
||||
* emsuart.h
|
||||
*
|
||||
* Header file for emsuart.cpp
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
@@ -31,23 +31,19 @@
|
||||
|
||||
// default values for shower logic on/off
|
||||
#define BOILER_SHOWER_TIMER 1 // enable (1) to monitor shower time
|
||||
#define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold watewr when shower time limit has exceeded
|
||||
#define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold water when shower time limit has exceeded
|
||||
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
||||
|
||||
// Set LED pin used for showing ems bus connection status. Solid is connected, Flashing is error
|
||||
// can be either the onboard LED on the ESP8266 or external via an external pull-up LED (e.g. D1)
|
||||
// can be either the onboard LED on the ESP8266 or external via an external pull-up LED (e.g. D1 on bbqkees' board)
|
||||
// can be enabled and disabled via the 'set led' command
|
||||
#define BOILER_LED LED_BUILTIN // LED_BULLETIN for onboard LED
|
||||
|
||||
// set this if using an external temperature sensor like a DS18B20
|
||||
#define TEMPERATURE_SENSOR_PIN D7
|
||||
// D5 is the default on bbqkees' board
|
||||
#define TEMPERATURE_SENSOR_PIN D5
|
||||
|
||||
// By default the EMS bus will be scanned for known devices (EMS_MODEL_NONE).
|
||||
// You can override this here by fixing the Boiler and Thermostat types.
|
||||
// See ems.h for the list of recognized types. For example:
|
||||
// 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
|
||||
// By default the EMS bus will be scanned for known devices based on product ids in ems_devices.h
|
||||
// You can override the Thermostat model by hardcoding the model type
|
||||
#define MY_THERMOSTAT_MODELID EMS_MODEL_NONE
|
||||
//#define MY_BOILER_MODELID EMS_MODEL_UBA
|
||||
//#define MY_THERMOSTAT_MODELID EMS_MODEL_RC20
|
||||
//#define MY_THERMOSTAT_MODELID EMS_MODEL_RC35
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
/**
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define APP_NAME "EMS-ESP Interface"
|
||||
#define APP_VERSION "1.3.1"
|
||||
#define APP_NAME "EMS-ESP"
|
||||
#define APP_VERSION "1.3.2"
|
||||
#define APP_HOSTNAME "ems-esp"
|
||||
|
||||
Reference in New Issue
Block a user