release 1.5.0

This commit is contained in:
proddy
2019-02-03 20:44:15 +01:00
parent 43ed0a30db
commit 83bad9dbaa
21 changed files with 466 additions and 979 deletions

View File

@@ -85,11 +85,9 @@ command_t PROGMEM project_cmds[] = {
{"info", "show the values"},
{"log <n | b | t | r | v>", "set logging mode to none, basic, thermostat only, raw or verbose"},
{"poll", "toggle EMS poll request on/off"},
{"tx", "toggle EMX Tx line on/off"},
{"publish", "publish values to MQTT"},
{"types", "list supported EMS telegram type IDs"},
{"queue", "print the Tx queue"},
{"queue", "list Tx queue"},
{"autodetect", "discover EMS devices and set boiler and thermostat automatically"},
{"shower <timer | alert>", "toggle either timer or alert on/off"},
{"send XX...", "send raw telegram data in hex to EMS bus"},
@@ -265,19 +263,16 @@ void showInfo() {
myDebug(" LED is %s", EMSESP_Status.led_enabled ? "on" : "off");
myDebug(" # connected Dallas temperature sensors = %d", EMSESP_Status.dallas_sensors);
myDebug(" # connected Dallas temperature sensors=%d", EMSESP_Status.dallas_sensors);
myDebug(" Thermostat is %s, Boiler is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s",
myDebug(" Thermostat is %s, Boiler is %s, Shower Timer is %s, Shower Alert is %s",
(ems_getThermostatEnabled() ? "enabled" : "disabled"),
(ems_getBoilerEnabled() ? "enabled" : "disabled"),
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
((EMS_Sys_Status.emsTxEnabled) ? "enabled" : "disabled"),
((EMSESP_Status.shower_timer) ? "enabled" : "disabled"),
((EMSESP_Status.shower_alert) ? "enabled" : "disabled"));
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"),
myDebug(" Bus Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
(ems_getBusConnected() ? "yes" : "no"),
EMS_Sys_Status.emsRxPgks,
EMS_Sys_Status.emsTxPkgs,
@@ -633,7 +628,7 @@ bool FSCallback(MYESP_FSACTION action, JsonObject & json) {
// callback for custom settings when showing Stored Settings
// wc is number of arguments after the 'set' command
// returns true if successful
// returns true if the setting was recognized and changed
bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) {
bool ok = false;
@@ -721,18 +716,6 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
ok = true;
}
if (strcmp(first_cmd, "poll") == 0) {
bool b = !ems_getPoll();
ems_setPoll(b);
ok = true;
}
if (strcmp(first_cmd, "tx") == 0) {
bool b = !ems_getTxEnabled();
ems_setTxEnabled(b);
ok = true;
}
if (strcmp(first_cmd, "publish") == 0) {
publishValues(true);
ok = true;
@@ -925,17 +908,12 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
// Init callback, which is used to set functions and call methods after a wifi connection has been established
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 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 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
emsuart_init();
myDebug("[UART] Opened Rx/Tx connection");
#endif
// now that we're connected, find out what boiler and thermostats we have specified
// go and find the boiler and thermostat types
ems_discoverModels();
}
@@ -1100,18 +1078,21 @@ void setup() {
// call ems.cpp's init function to set all the internal params
ems_init();
#ifndef DEBUG_SUPPORT
// Timers using Ticker library
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
#endif
// set up myESP for Wifi, MQTT, MDNS and Telnet
myESP.setTelnet(project_cmds, ArraySize(project_cmds), TelnetCommandCallback, TelnetCallback); // set up Telnet commands
#ifdef WIFI_SSID
myESP.setWIFI(WIFI_SSID, WIFI_PASSWORD, WIFICallback);
myESP.setMQTT(
MQTT_HOST, MQTT_USER, MQTT_PASS, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, MQTT_WILL_PAYLOAD, MQTTCallback);
#else
myESP.setWIFI(NULL, NULL, WIFICallback); // pull the wifi settings from the SPIFFS stored settings
#endif
// MQTT host, username and password taken from the SPIFFS settings
myESP.setMQTT(NULL, NULL, NULL, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, MQTT_WILL_PAYLOAD, MQTTCallback);
// custom settings in SPIFFS
myESP.setSettings(FSCallback, SettingsCallback);

View File

@@ -24,35 +24,39 @@ CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO
// callbacks per type
// generic
void _process_Version(uint8_t * data, uint8_t length);
void _process_Version(uint8_t type, uint8_t * data, uint8_t length);
// Boiler and Buderus devices
void _process_UBAMonitorFast(uint8_t * data, uint8_t length);
void _process_UBAMonitorSlow(uint8_t * data, uint8_t length);
void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length);
void _process_UBAParameterWW(uint8_t * data, uint8_t length);
void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length);
void _process_UBAParametersMessage(uint8_t * data, uint8_t length);
void _process_SetPoints(uint8_t * data, uint8_t length);
void _process_UBAMonitorFast(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBAMonitorSlow(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBAMonitorWWMessage(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBAParameterWW(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBATotalUptimeMessage(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBAParametersMessage(uint8_t type, uint8_t * data, uint8_t length);
void _process_SetPoints(uint8_t type, uint8_t * data, uint8_t length);
// Common for most thermostats
void _process_RCTime(uint8_t * data, uint8_t length);
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length);
void _process_RCTime(uint8_t type, uint8_t * data, uint8_t length);
void _process_RCOutdoorTempMessage(uint8_t type, uint8_t * data, uint8_t length);
// RC10
void _process_RC10Set(uint8_t type, uint8_t * data, uint8_t length);
void _process_RC10StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
// RC20
void _process_RC20Set(uint8_t * data, uint8_t length);
void _process_RC20StatusMessage(uint8_t * data, uint8_t length);
void _process_RC20Set(uint8_t type, uint8_t * data, uint8_t length);
void _process_RC20StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
// RC30
void _process_RC30Set(uint8_t * data, uint8_t length);
void _process_RC30StatusMessage(uint8_t * data, uint8_t length);
void _process_RC30Set(uint8_t type, uint8_t * data, uint8_t length);
void _process_RC30StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
// RC35
void _process_RC35Set(uint8_t * data, uint8_t length);
void _process_RC35StatusMessage(uint8_t * data, uint8_t length);
void _process_RC35Set(uint8_t type, uint8_t * data, uint8_t length);
void _process_RC35StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
// Easy
void _process_EasyStatusMessage(uint8_t * data, uint8_t length);
void _process_EasyStatusMessage(uint8_t type, uint8_t * data, uint8_t length);
/*
* Recognized EMS types and the functions they call to process the telegrams
@@ -73,6 +77,11 @@ const _EMS_Type EMS_Types[] = {
{EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", _process_UBAParametersMessage},
{EMS_MODEL_UBA, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
// RC10
{EMS_MODEL_RC10, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
{EMS_MODEL_RC10, EMS_TYPE_RC10Set, "RC10Set", _process_RC10Set},
{EMS_MODEL_RC10, EMS_TYPE_RC10StatusMessage, "RC10StatusMessage", _process_RC10StatusMessage},
// RC20 and RC20F
{EMS_MODEL_RC20, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
{EMS_MODEL_RC20, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
@@ -133,14 +142,10 @@ 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 uint8_t TX_WRITE_TIMEOUT_COUNT = 2; // 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
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() {
@@ -148,15 +153,15 @@ void ems_init() {
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.emsRxStatus = EMS_RX_STATUS_IDLE;
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_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;
EMS_Sys_Status.txRetryCount = 0;
// thermostat
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_FLOAT_NOTSET;
@@ -233,11 +238,6 @@ void ems_init() {
EMS_Thermostat.product_id = 0;
strlcpy(EMS_Thermostat.version, "not set", sizeof(EMS_Thermostat.version));
// counters
_ems_PollCount = 0;
_emsTxRetryCount = 0;
_last_TxTelgramCRC = 0;
// default logging is none
ems_setLogging(EMS_SYS_LOGGING_DEFAULT);
}
@@ -252,15 +252,6 @@ bool ems_getPoll() {
return EMS_Sys_Status.emsPollEnabled;
}
void ems_setTxEnabled(bool b) {
EMS_Sys_Status.emsTxEnabled = b;
myDebug("EMS Bus Tx is set to %s", EMS_Sys_Status.emsTxEnabled ? "enabled" : "disabled");
}
bool ems_getTxEnabled() {
return EMS_Sys_Status.emsTxEnabled;
}
bool ems_getEmsRefreshed() {
return EMS_Sys_Status.emsRefreshed;
}
@@ -462,26 +453,13 @@ void _ems_sendTelegram() {
EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length); // add the CRC
_debugPrintTelegram("Sending raw", EMS_TxTelegram.data, EMS_TxTelegram.length, COLOR_CYAN); // always show
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); // send the telegram to the UART Tx
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_TxTelegram.dest == EMS_ID_NONE) {
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
EMS_TxQueue.shift(); // remove from queue
return;
}
// 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_Sys_Status.emsTxEnabled) {
myDebug("Tx is disabled. Ignoring %s request to 0x%02X.",
((EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) ? "write" : "read"),
EMS_TxTelegram.dest & 0x7F);
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
EMS_TxQueue.shift(); // remove from queue
EMS_TxQueue.shift(); // remove from queue
return;
}
@@ -508,16 +486,6 @@ void _ems_sendTelegram() {
uint8_t crc = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length);
EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = crc;
// check if we already sent the same one, otherwise assume the last Tx hasn't been successful
// and remove from queue and exit
if (crc == _last_TxTelgramCRC) {
// myDebug("Duplicate message, just sent this one, so removing from queue!");
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
EMS_TxQueue.shift(); // remove the last Tx from the queue
return;
}
_last_TxTelgramCRC = crc;
// print debug info
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
char s[64] = {0};
@@ -533,42 +501,59 @@ void _ems_sendTelegram() {
}
// send the telegram to the UART Tx
EMS_Sys_Status.emsTxStatus = EMS_TX_ACTIVE;
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length);
EMS_Sys_Status.emsTxPkgs++;
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_WAIT;
}
// if it was a write command, check if we need to do a new read to validate the results
// we do this by turning the last write into a read
if ((EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) && (EMS_TxTelegram.type_validate != EMS_ID_NONE)) {
// create a new Telegram copying from the last write
_EMS_TxTelegram new_EMS_TxTelegram;
// copy details
new_EMS_TxTelegram.type_validate = EMS_TxTelegram.type_validate;
new_EMS_TxTelegram.dest = EMS_TxTelegram.dest;
new_EMS_TxTelegram.type = EMS_TxTelegram.type;
new_EMS_TxTelegram.action = EMS_TX_TELEGRAM_VALIDATE;
new_EMS_TxTelegram.offset = EMS_TxTelegram.comparisonOffset; // location of byte to fetch
new_EMS_TxTelegram.dataValue = 1; // fetch single byte
new_EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; // is always 6 bytes long (including CRC at end)
new_EMS_TxTelegram.comparisonValue = EMS_TxTelegram.comparisonValue;
new_EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.comparisonPostRead;
new_EMS_TxTelegram.comparisonOffset = EMS_TxTelegram.comparisonOffset;
// remove old telegram from queue and add this new read one
EMS_TxQueue.shift(); // remove from queue
EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it next in line
/**
* Takes the last write command and turns into a validate request
* placing it on the queue
*/
void _createValidate() {
if (EMS_TxQueue.isEmpty()) {
return;
}
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE;
// release the Tx lock
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
// get the first in the queue, which is at the head
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first();
// safety check: only do a validate after a write and when we have a type to validate
if ((EMS_TxTelegram.action != EMS_TX_TELEGRAM_WRITE) || (EMS_TxTelegram.type_validate == EMS_ID_NONE)) {
return;
}
// create a new Telegram copying from the last write
_EMS_TxTelegram new_EMS_TxTelegram;
new_EMS_TxTelegram.action = EMS_TX_TELEGRAM_VALIDATE;
// copy old Write record
new_EMS_TxTelegram.type_validate = EMS_TxTelegram.type_validate;
new_EMS_TxTelegram.dest = EMS_TxTelegram.dest;
new_EMS_TxTelegram.type = EMS_TxTelegram.type;
new_EMS_TxTelegram.comparisonValue = EMS_TxTelegram.comparisonValue;
new_EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.comparisonPostRead;
new_EMS_TxTelegram.comparisonOffset = EMS_TxTelegram.comparisonOffset;
// this is what is different
new_EMS_TxTelegram.offset = EMS_TxTelegram.comparisonOffset; // location of byte to fetch
new_EMS_TxTelegram.dataValue = 1; // fetch single byte
new_EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; // is always 6 bytes long (including CRC at end)
// remove old telegram from queue and add this new read one
EMS_TxQueue.shift(); // remove from queue
EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it first to be picked up next (FIFO)
}
/**
* the main logic that parses the telegram message, triggered by an interrupt in emsuart.cpp
* length is only data bytes, excluding the BRK
* Read commands are asynchronous as they're handled by the interrupt
* When we receive a Poll Request we need to send any Tx packages quickly
* When we receive a Poll Request we need to send any Tx packages quickly within a 200ms window
*/
void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
// check if we just received a single byte
@@ -579,65 +564,52 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
// check first for a Poll for us
if (value == (EMS_ID_ME | 0x80)) {
// store when we received a last poll
EMS_Sys_Status.emsPollTimestamp = millis();
EMS_Sys_Status.emsPollTimestamp = millis(); // store when we received a last poll
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()) {
// do we have something to send thats waiting in the Tx queue? if so send it if the Queue is not in a wait state
if ((!EMS_TxQueue.isEmpty()) && (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_IDLE)) {
_ems_sendTelegram(); // perform the read/write command immediately
/*
if ((_ems_PollCount++ % 2) == 0) {
_ems_sendTelegram(); // perform the read/write command, slowing it down a little
}
*/
} else {
// nothing to send so just send a poll acknowledgement back
if (EMS_Sys_Status.emsPollEnabled) {
emsaurt_tx_poll();
}
}
} else if ((value == EMS_TX_ERROR) || (value == EMS_TX_SUCCESS)) {
// if its a success (01) or failure (04), then see if its from one of our last writes
// a response from UBA after a write should be within a specific time period < 100ms
if (!EMS_TxQueue.isEmpty()) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first(); // get current Tx package we last sent
if ((EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) && (value == EMS_TX_ERROR)) {
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
myDebug("* Error: last write failed. removing write request from queue");
}
EMS_TxQueue.shift(); // write failed so remove from queue and forget it for now
} else if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_WAIT) {
// this may be a single byte 01 (success) or 04 (error) from a recent write command?
if (value == EMS_TX_SUCCESS) {
EMS_Sys_Status.emsTxPkgs++;
// got a success 01. Send a validate to check the value of the last write
emsaurt_tx_poll(); // send a poll to free the EMS bus
_createValidate(); // create a validate Tx request
} else if (value == EMS_TX_ERROR) {
// last write failed (04), delete it from queue and dont bother to retry
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
myDebug("** Write command failed from host");
}
emsaurt_tx_poll(); // send a poll to free the EMS bus
_removeTxQueue(); // remove from queue
}
}
return; // all done here, quit. if we haven't processes anything its a poll but not for us
return; // all done here
}
// ignore anything that doesn't resemble a proper telegram package
// minimal is 5 bytes, excluding CRC at the end
if (length < EMS_MIN_TELEGRAM_LENGTH) {
// _debugPrintTelegram("Noisy data:", telegram, length, COLOR_RED);
if (length <= 4) {
//_debugPrintTelegram("Noisy data:", telegram, length, COLOR_RED);
return;
}
// Assume at this point we have something that vaguely resembles a telegram
// see if we got a telegram as [src] [dest] [type] [offset] [data] [crc]
// so is at least 6 bytes long and the CRC checks out (which is last byte)
// Assume at this point we have something that vaguely resembles a telegram in the format [src] [dest] [type] [offset] [data] [crc]
// validate the CRC, if its bad ignore it
uint8_t crc = _crcCalculator(telegram, length);
if (telegram[length - 1] != crc) {
EMS_Sys_Status.emxCrcErr++;
_debugPrintTelegram("Corrupt telegram:", telegram, length, COLOR_RED);
// at this point something arrived on the bus, which means if we were waiting for something to arrive then we
// can forget it as it should have arrived in a 100ms window. So remove it.
if (!EMS_TxQueue.isEmpty()) {
/*
// commented out because too much chatter
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Corrupt telegram, so removing last read from Tx queue.");
}
*/
EMS_TxQueue.shift();
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
_debugPrintTelegram("Corrupt telegram:", telegram, length, COLOR_RED);
}
return;
}
@@ -755,125 +727,146 @@ void _ems_processTelegram(uint8_t * telegram, uint8_t length) {
// call callback function to process it
// as we only handle complete telegrams (not partial) check that the offset is 0
if (offset == EMS_ID_NONE) {
(void)EMS_Types[i].processType_cb(data, length - 5);
(void)EMS_Types[i].processType_cb(type, data, length - 5);
}
}
}
}
/**
* Remove current Tx telegram from queue and release lock on Tx
*/
void _removeTxQueue() {
if (!EMS_TxQueue.isEmpty()) {
EMS_TxQueue.shift(); // remove item from top of the queue
}
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
}
/**
* deciphers the telegram packet
* deciphers the telegram packet, which has already been checked for valid CRC and has a complete header (min of 5 bytes)
* length is only data bytes, excluding the BRK
* We only remove from the Tx queue if the read or write was successful
*/
void _processType(uint8_t * telegram, uint8_t length) {
// header
uint8_t src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes here
uint8_t dest = telegram[1] & 0x7F;
uint8_t type = telegram[2];
uint8_t * data = telegram + 4; // data block starts at position 5
uint8_t src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes here
// if its an echo of ourselves from the master, ignore
// if its an echo of ourselves from the master UBA, ignore
if (src == EMS_ID_ME) {
// _debugPrintTelegram("Telegram echo:", telegram, length, COLOR_BLUE);
//_debugPrintTelegram("Telegram echo:", telegram, length, COLOR_BLUE);
return;
}
// did we request this telegram? If so it would be either a read or a validate telegram and still
// on top of the Tx queue with the same type. because we don't remove a Tx from the queue after a send
// if its a validate check the value, or if its a read, update the Read counter
// then we can safely removed the read/validate from the queue
if ((dest == EMS_ID_ME) && (!EMS_TxQueue.isEmpty())) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first(); // get current Tx package we last sent
// if its a broadcast and we didn't just send anything, process it and exit
if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_IDLE) {
_ems_processTelegram(telegram, length);
return;
}
// do the types match? If so we were expecting this telegram response back to us
if (EMS_TxTelegram.type == type) {
emsaurt_tx_poll(); // send Acknowledgement back to free the EMS bus since we have the telegram
// release the lock on the TxQueue
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
// if last action was a read, go ahead and process it
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_READ) {
EMS_Sys_Status.emsRxPgks++; // increment rx counter
_emsTxRetryCount = 0; // reset retry count
//
// and process the telegram. This calls the main function to process each type
//
_ems_processTelegram(telegram, length);
if (EMS_TxTelegram.forceRefresh) {
ems_setEmsRefreshed(true); // set the MQTT refresh flag to force sending to MQTT
// at this point we can assume Txstatus is EMS_TX_STATUS_WAIT
// for READ, WRITE or VALIDATE the dest is always us, so check this
// if not just process and quit
if ((telegram[1] & 0x7F) != EMS_ID_ME) {
_ems_processTelegram(telegram, length);
return;
}
// first double check we actually have something in the queue
if (EMS_TxQueue.isEmpty()) {
_ems_processTelegram(telegram, length);
return;
}
// get the Tx telegram we just sent
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first();
// check action
// if READ, match the current inbound telegram to what we sent
// if WRITE, should not happen
// if VALIDATE, check the contents
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_READ) {
uint8_t type = telegram[2];
if ((src == EMS_TxTelegram.dest) && (type == EMS_TxTelegram.type)) {
// all checks out, read was successful, remove tx from queue and continue to process telegram
_removeTxQueue();
EMS_Sys_Status.emsRxPgks++; // increment counter
// myDebug("** Read from 0x%02X ok", type);
ems_setEmsRefreshed(EMS_TxTelegram.forceRefresh); // does mqtt need refreshing?
} else {
// read not OK, we didn't get back a telegram we expected
// leave on queue and try again, but continue to process what we received as it may be important
EMS_Sys_Status.txRetryCount++;
// if retried too many times, give up and remove it
if (EMS_Sys_Status.txRetryCount >= TX_WRITE_TIMEOUT_COUNT) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Read failed. Giving up, removing from queue");
}
EMS_TxQueue.shift(); // remove read from queue, all done now
return; // quit
}
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) {
// this read was for a validate. Do a compare on the 1 byte result from the last write
uint8_t dataReceived = data[0]; // only a single byte is returned after a read
if (EMS_TxTelegram.comparisonValue == dataReceived) {
// there is a match, so write must have been successful
EMS_TxQueue.shift(); // remove validate from queue
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Write to 0x%02X was successful", EMS_TxTelegram.dest);
}
ems_doReadCommand(EMS_TxTelegram.comparisonPostRead,
EMS_TxTelegram.dest,
true); // get values and force a refresh to MQTT
} else {
// write failed.
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Last write failed. Compared set value 0x%02X with received value 0x%02X",
EMS_TxTelegram.comparisonValue,
dataReceived);
}
if (_emsTxRetryCount++ >= TX_WRITE_TIMEOUT_COUNT) {
// give up
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("...Giving up!");
}
EMS_TxQueue.shift(); // remove from queue
} else {
// retry, turn the validate back into a write and try again
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("...Retrying attempt %d...", _emsTxRetryCount);
}
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dataValue = EMS_TxTelegram.comparisonValue; // restore old value
EMS_TxTelegram.offset = EMS_TxTelegram.comparisonOffset; // restore old value
EMS_TxQueue.shift(); // remove validate from queue
EMS_TxQueue.unshift(EMS_TxTelegram); // add back to queue making it next in line
}
_removeTxQueue();
} else {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("...Retrying read. Attempt %d/%d...", EMS_Sys_Status.txRetryCount, TX_WRITE_TIMEOUT_COUNT);
}
return;
}
}
// telegram was for us, but seems we didn't ask for it
// so kinda invalidates the last action on the queue remove from queue
myDebug("Telegram sent to us but we didn't ask for it! (src=0x%02X dest=0x%02X type=0x%02X)",
telegram[0] & 0x7F,
telegram[1] & 0x7F,
telegram[2]);
// ems_printTxQueue();
// EMS_TxQueue.shift(); // remove validate from queue
} else {
// we didn't request it, was for somebody else or a broadcast, process it anyway
_ems_processTelegram(telegram, length);
_ems_processTelegram(telegram, length); // process it always
}
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) {
// should not get here, since this is handled earlier receiving a 01 or 04
myDebug("** Error ! Write - should not be here");
}
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) {
// this is a read telegram which we use to validate the last write
uint8_t * data = telegram + 4; // data block starts at position 5
uint8_t dataReceived = data[0]; // only a single byte is returned after a read
if (EMS_TxTelegram.comparisonValue == dataReceived) {
// validate was successful, the write changed the value
_removeTxQueue(); // now we can remove the Tx validate command the queue
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Write to 0x%02X was successful", EMS_TxTelegram.dest);
}
// follow up with the post read command
ems_doReadCommand(EMS_TxTelegram.comparisonPostRead, EMS_TxTelegram.dest, true);
} else {
// write failed
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Last write failed. Compared set value 0x%02X with received value 0x%02X",
EMS_TxTelegram.comparisonValue,
dataReceived);
}
if (++EMS_Sys_Status.txRetryCount > TX_WRITE_TIMEOUT_COUNT) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Write failed. Giving up, removing from queue");
}
_removeTxQueue();
} else {
// retry, turn the validate back into a write and try again
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("...Retrying write. Attempt %d/%d...", EMS_Sys_Status.txRetryCount, TX_WRITE_TIMEOUT_COUNT);
}
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dataValue = EMS_TxTelegram.comparisonValue; // restore old value
EMS_TxTelegram.offset = EMS_TxTelegram.comparisonOffset; // restore old value
EMS_TxQueue.shift(); // remove validate from queue
EMS_TxQueue.unshift(EMS_TxTelegram); // add back to queue making it next in line
}
}
}
emsaurt_tx_poll(); // send Acknowledgement back to free the EMS bus since we have the telegram
}
/**
* Check if hot tap water or heating is active
* using a quick hack:
* heating is on if Selected Flow Temp >= 70 (in my case)
* tap water is on if Selected Flow Temp = 0 and Selected Burner Power >= 115
* using a quick hack for checking the heating. Selected Flow Temp >= 70
*/
bool _checkActive() {
/* old code
EMS_Boiler.tapwaterActive = ((EMS_Boiler.selFlowTemp == 0)
&& (EMS_Boiler.selBurnPow >= EMS_BOILER_BURNPOWER_TAPWATER) & (EMS_Boiler.burnGas == EMS_VALUE_INT_ON));
*/
// hot tap water, using flow to check insread of the burner power
EMS_Boiler.tapwaterActive = ((EMS_Boiler.wWCurFlow != 0) && (EMS_Boiler.burnGas == EMS_VALUE_INT_ON));
@@ -885,7 +878,7 @@ bool _checkActive() {
* UBAParameterWW - type 0x33 - warm water parameters
* received only after requested (not broadcasted)
*/
void _process_UBAParameterWW(uint8_t * data, uint8_t length) {
void _process_UBAParameterWW(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.wWActivated = (data[1] == 0xFF); // 0xFF means on
EMS_Boiler.wWSelTemp = data[2];
EMS_Boiler.wWCircPump = (data[6] == 0xFF); // 0xFF means on
@@ -899,7 +892,7 @@ void _process_UBAParameterWW(uint8_t * data, uint8_t length) {
* UBATotalUptimeMessage - type 0x14 - total uptime
* received only after requested (not broadcasted)
*/
void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length) {
void _process_UBATotalUptimeMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.UBAuptime = _toLong(0, data);
EMS_Sys_Status.emsRefreshed = true; // when we receieve this, lets force an MQTT publish
}
@@ -907,7 +900,7 @@ void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length) {
/*
* UBAParametersMessage - type 0x16
*/
void _process_UBAParametersMessage(uint8_t * data, uint8_t length) {
void _process_UBAParametersMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.heating_temp = data[1];
EMS_Boiler.pump_mod_max = data[9];
EMS_Boiler.pump_mod_min = data[10];
@@ -917,7 +910,7 @@ void _process_UBAParametersMessage(uint8_t * data, uint8_t length) {
* UBAMonitorWWMessage - type 0x34 - warm water monitor. 19 bytes long
* received every 10 seconds
*/
void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length) {
void _process_UBAMonitorWWMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.wWCurTmp = _toFloat(1, data);
EMS_Boiler.wWStarts = _toLong(13, data);
EMS_Boiler.wWWorkM = _toLong(10, data);
@@ -929,7 +922,7 @@ void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length) {
* UBAMonitorFast - type 0x18 - central heating monitor part 1 (25 bytes long)
* received every 10 seconds
*/
void _process_UBAMonitorFast(uint8_t * data, uint8_t length) {
void _process_UBAMonitorFast(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.selFlowTemp = data[0];
EMS_Boiler.curFlowTemp = _toFloat(1, data);
EMS_Boiler.retTemp = _toFloat(13, data);
@@ -965,7 +958,7 @@ void _process_UBAMonitorFast(uint8_t * data, uint8_t length) {
* UBAMonitorSlow - type 0x19 - central heating monitor part 2 (27 bytes long)
* received every 60 seconds
*/
void _process_UBAMonitorSlow(uint8_t * data, uint8_t length) {
void _process_UBAMonitorSlow(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.extTemp = _toFloat(0, data); // 0x8000 if not available
EMS_Boiler.boilTemp = _toFloat(2, data); // 0x8000 if not available
EMS_Boiler.pumpMod = data[9];
@@ -974,12 +967,25 @@ void _process_UBAMonitorSlow(uint8_t * data, uint8_t length) {
EMS_Boiler.heatWorkMin = _toLong(19, data);
}
/**
* type 0xB1 - data from the RC10 thermostat (0x17)
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC10StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC10StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC10StatusMessage_curr, data);
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
}
/**
* 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) {
void _process_RC20StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC20StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC20StatusMessage_curr, data);
@@ -991,7 +997,7 @@ void _process_RC20StatusMessage(uint8_t * data, uint8_t length) {
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC30StatusMessage(uint8_t * data, uint8_t length) {
void _process_RC30StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC30StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC30StatusMessage_curr, data);
@@ -1003,7 +1009,7 @@ void _process_RC30StatusMessage(uint8_t * data, uint8_t length) {
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC35StatusMessage(uint8_t * data, uint8_t length) {
void _process_RC35StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC35StatusMessage_setpoint]) / (float)2;
// check if temp sensor is unavailable
@@ -1020,18 +1026,26 @@ void _process_RC35StatusMessage(uint8_t * data, uint8_t length) {
* 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) {
void _process_EasyStatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
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 via MQTT
}
/**
* type 0xB0 - for reading the mode from the RC10 thermostat (0x17)
* received only after requested
*/
void _process_RC10Set(uint8_t type, uint8_t * data, uint8_t length) {
// mode not implemented yet
}
/**
* type 0xA8 - for reading the mode from the RC20 thermostat (0x17)
* received only after requested
*/
void _process_RC20Set(uint8_t * data, uint8_t length) {
void _process_RC20Set(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.mode = data[EMS_OFFSET_RC20Set_mode];
}
@@ -1039,7 +1053,7 @@ void _process_RC20Set(uint8_t * data, uint8_t length) {
* type 0xA7 - for reading the mode from the RC30 thermostat (0x10)
* received only after requested
*/
void _process_RC30Set(uint8_t * data, uint8_t length) {
void _process_RC30Set(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.mode = data[EMS_OFFSET_RC30Set_mode];
}
@@ -1048,14 +1062,14 @@ void _process_RC30Set(uint8_t * data, uint8_t length) {
* Working Mode Heating Circuit 1 (HC1)
* received only after requested
*/
void _process_RC35Set(uint8_t * data, uint8_t length) {
void _process_RC35Set(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.mode = data[EMS_OFFSET_RC35Set_mode];
}
/**
* type 0xA3 - for external temp settings from the the RC* thermostats
*/
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length) {
void _process_RCOutdoorTempMessage(uint8_t type, uint8_t * data, uint8_t length) {
// add support here if you're reading external sensors
}
@@ -1063,7 +1077,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) {
void _process_Version(uint8_t type, uint8_t * data, uint8_t length) {
// ignore short messages that we can't interpret
if (length < 3) {
return;
@@ -1155,7 +1169,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
ems_getThermostatValues();
}
} else {
myDebug("Unrecognized device found. Product ID %d, Version %s", product_id, version);
myDebug("Unrecognized device found. TypeID 0x%02X, Product ID %d, Version %s", type, product_id, version);
}
// if the boiler or thermostat values have changed, save them to SPIFFS
@@ -1170,7 +1184,6 @@ void _process_Version(uint8_t * data, uint8_t length) {
void ems_discoverModels() {
// boiler
ems_doReadCommand(EMS_TYPE_Version, EMS_Boiler.type_id); // get version details of boiler
ems_getBoilerValues(); // fetch values from boiler, instead of waiting for broadcasts
// thermostat
// if it hasn't been set, auto discover it
@@ -1179,7 +1192,6 @@ void ems_discoverModels() {
} else {
// set the model as hardcoded (see my_devices.h) and fetch the version and product id
ems_doReadCommand(EMS_TYPE_Version, EMS_Thermostat.type_id);
ems_getThermostatValues();
}
}
@@ -1223,24 +1235,26 @@ void _ems_setThermostatModel(uint8_t thermostat_modelid) {
}
/**
* UBASetPoint 0x1A, for RC20 and other thermostats. not really sure what to do with this data yet.
* UBASetPoint 0x1A
*/
void _process_SetPoints(uint8_t * data, uint8_t length) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_THERMOSTAT) {
void _process_SetPoints(uint8_t type, uint8_t * data, uint8_t length) {
/*
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
if (length != 0) {
uint8_t setpoint = data[0];
uint8_t hk_power = data[1];
uint8_t ww_power = data[2];
myDebug(" SetPoint=%d, hk_power=%d ww_power=%d", setpoint, hk_power, ww_power);
myDebug(" SetPoint=%d, hk_power=%d, ww_power=%d", setpoint, hk_power, ww_power);
}
}
*/
}
/**
* process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long
* common for all thermostats
*/
void _process_RCTime(uint8_t * data, uint8_t length) {
void _process_RCTime(uint8_t type, uint8_t * data, uint8_t length) {
if (EMS_Thermostat.model_id == EMS_MODEL_EASY) {
return; // not supported
}
@@ -1260,7 +1274,12 @@ void ems_printTxQueue() {
_EMS_TxTelegram EMS_TxTelegram;
char sType[20] = {0};
myDebug("Tx queue (%d/%d), 1=first to send", EMS_TxQueue.size(), EMS_TxQueue.capacity);
if (EMS_TxQueue.size() == 0) {
myDebug("Tx queue is empty.");
return;
}
myDebug("Tx queue (%d/%d)", EMS_TxQueue.size(), EMS_TxQueue.capacity);
for (byte i = 0; i < EMS_TxQueue.size(); i++) {
EMS_TxTelegram = EMS_TxQueue[i]; // retrieves the i-th element from the buffer without removing it
@@ -1341,7 +1360,7 @@ void ems_getBoilerValues() {
ems_doReadCommand(EMS_TYPE_UBAMonitorSlow, EMS_Boiler.type_id); // get more boiler stats, instead of waiting 60secs for the broadcast
ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_Boiler.type_id); // get Warm Water values
ems_doReadCommand(EMS_TYPE_UBAParametersMessage, EMS_Boiler.type_id); // get MC10 boiler values
ems_doReadCommand(EMS_TYPE_UBATotalUptimeMessage, EMS_Boiler.type_id); // get Warm Water values
ems_doReadCommand(EMS_TYPE_UBATotalUptimeMessage, EMS_Boiler.type_id); // get uptime from boiler
}
/**
@@ -1495,6 +1514,7 @@ void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
// see if its a known type
int i = _ems_findType(type);
@@ -1532,6 +1552,7 @@ void ems_sendRawTelegram(char * telegram) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
// get first value, which should be the src
if (p = strtok(telegram, " ,")) { // delimiter
@@ -1578,6 +1599,7 @@ void ems_setThermostatTemp(float temperature) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
uint8_t model_id = EMS_Thermostat.model_id;
uint8_t type = EMS_Thermostat.type_id;
@@ -1593,6 +1615,10 @@ void ems_setThermostatTemp(float temperature) {
EMS_TxTelegram.type = EMS_TYPE_RC20Set;
EMS_TxTelegram.offset = EMS_OFFSET_RC20Set_temp;
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC20StatusMessage;
} else if (model_id == EMS_MODEL_RC10) {
EMS_TxTelegram.type = EMS_TYPE_RC10Set;
EMS_TxTelegram.offset = EMS_OFFSET_RC10Set_temp;
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC10StatusMessage;
} else if (model_id == EMS_MODEL_RC30) {
EMS_TxTelegram.type = EMS_TYPE_RC30Set;
EMS_TxTelegram.offset = EMS_OFFSET_RC30Set_temp;
@@ -1639,6 +1665,7 @@ void ems_setThermostatMode(uint8_t mode) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dest = type;
@@ -1679,6 +1706,7 @@ void ems_setWarmWaterTemp(uint8_t temperature) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dest = EMS_Boiler.type_id;
@@ -1703,6 +1731,8 @@ void ems_setWarmWaterModeComfort(bool comfort) {
myDebug("Setting boiler warm water to comfort mode %s\n", comfort ? "Comfort" : "Eco");
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dest = EMS_Boiler.type_id;
@@ -1712,6 +1742,7 @@ void ems_setWarmWaterModeComfort(bool comfort) {
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
EMS_TxTelegram.dataValue =
(comfort ? EMS_VALUE_UBAParameterWW_wwComfort_Comfort : EMS_VALUE_UBAParameterWW_wwComfort_Eco); // 0x00 is on, 0xD8 is off
EMS_TxQueue.push(EMS_TxTelegram);
}
@@ -1723,6 +1754,8 @@ void ems_setWarmWaterActivated(bool activated) {
myDebug("Setting boiler warm water %s", activated ? "on" : "off");
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dest = EMS_Boiler.type_id;
@@ -1731,6 +1764,7 @@ void ems_setWarmWaterActivated(bool activated) {
EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
EMS_TxTelegram.dataValue = (activated ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
EMS_TxQueue.push(EMS_TxTelegram);
}
@@ -1744,6 +1778,7 @@ void ems_setWarmTapWaterActivated(bool activated) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
// clear Tx to make sure all data is set to 0x00
for (int i = 0; (i < EMS_MAX_TELEGRAM_LENGTH); i++) {
@@ -1762,7 +1797,6 @@ void ems_setWarmTapWaterActivated(bool activated) {
EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.type;
EMS_TxTelegram.forceRefresh = true; // send new value to MQTT after successful write
// create header
EMS_TxTelegram.data[0] = EMS_ID_ME; // src
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest

View File

@@ -61,17 +61,18 @@
/* EMS UART transfer status */
typedef enum {
EMS_RX_IDLE,
EMS_RX_ACTIVE // Rx package is being sent
EMS_RX_STATUS_IDLE,
EMS_RX_STATUS_BUSY // Rx package is being received
} _EMS_RX_STATUS;
typedef enum {
EMS_TX_IDLE,
EMS_TX_ACTIVE, // Tx package being sent, no break sent
EMS_TX_SUCCESS,
EMS_TX_ERROR
EMS_TX_STATUS_IDLE, // ready
EMS_TX_STATUS_WAIT // waiting for response from last Tx
} _EMS_TX_STATUS;
#define EMS_TX_SUCCESS 0x01 // EMS single byte after a Tx Write indicating a success
#define EMS_TX_ERROR 0x04 // EMS single byte after a Tx Write indicating an error
typedef enum {
EMS_TX_TELEGRAM_INIT, // just initialized
EMS_TX_TELEGRAM_READ, // doing a read request
@@ -97,18 +98,18 @@ typedef struct {
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
unsigned long emsPollTimestamp; // timestamp of last EMS poll sent to us
bool emsTxCapable; // able to send via Tx
uint8_t txRetryCount; // # times the last Tx was re-sent
} _EMS_Sys_Status;
// The Tx send package
typedef struct {
_EMS_TX_TELEGRAM_ACTION action; // read or write
_EMS_TX_TELEGRAM_ACTION action; // read, write, validate, init
uint8_t dest;
uint8_t type;
uint8_t offset;
@@ -123,6 +124,8 @@ typedef struct {
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
} _EMS_TxTelegram;
// default empty Tx
const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
EMS_TX_TELEGRAM_INIT, // action
@@ -236,8 +239,8 @@ typedef struct {
uint8_t year;
} _EMS_Thermostat;
// call back function signature
typedef void (*EMS_processType_cb)(uint8_t * data, uint8_t length);
// call back function signature for processing telegram types
typedef void (*EMS_processType_cb)(uint8_t type, uint8_t * data, uint8_t length);
// Definition for each EMS type, including the relative callback function
typedef struct {
@@ -293,6 +296,7 @@ void _ems_clearTxData();
int _ems_findBoilerModel(uint8_t model_id);
bool _ems_setModel(uint8_t model_id);
void _ems_setThermostatModel(uint8_t thermostat_modelid);
void _removeTxQueue();
// global so can referenced in other classes
extern _EMS_Sys_Status EMS_Sys_Status;

View File

@@ -46,6 +46,13 @@
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
// RC10 specific
#define EMS_TYPE_RC10StatusMessage 0xB1 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC10Set 0xB0 // for setting values like temp and mode
#define EMS_OFFSET_RC10Set_temp 4 // position of thermostat setpoint temperature
#define EMS_TYPE_RC10StatusMessage_setpoint 1 // setpoint temp
#define EMS_TYPE_RC10StatusMessage_curr 3 // current 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
@@ -87,6 +94,7 @@ typedef enum {
// thermostats
EMS_MODEL_ES73,
EMS_MODEL_RC10,
EMS_MODEL_RC20,
EMS_MODEL_RC20F,
EMS_MODEL_RC30,
@@ -104,8 +112,9 @@ const _Boiler_Type Boiler_Types[] = {
{EMS_MODEL_UBA, 72, 0x08, "MC10"},
{EMS_MODEL_UBA, 123, 0x08, "Buderus GB172/Nefit Trendline"},
{EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"},
{EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler"},
{EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler/Nefit Smartline"},
{EMS_MODEL_UBA, 190, 0x09, "BC10 Base Controller"},
{EMS_MODEL_UBA, 114, 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"},
@@ -120,6 +129,7 @@ const _Boiler_Type Boiler_Types[] = {
const _Thermostat_Type Thermostat_Types[] = {
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC10, 79, 0x17, "RC10/Nefit Moduline 100)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20, 77, 0x17, "RC20/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/Nefit Moduline 400)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},

View File

@@ -28,8 +28,8 @@ static void emsuart_rx_intr_handler(void * para) {
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE];
// is a new buffer? if so init the thing for a new telegram
if (EMS_Sys_Status.emsRxStatus == EMS_RX_IDLE) {
EMS_Sys_Status.emsRxStatus = EMS_RX_ACTIVE; // status set to active
if (EMS_Sys_Status.emsRxStatus == EMS_RX_STATUS_IDLE) {
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_BUSY; // status set to busy
length = 0;
}
@@ -55,7 +55,7 @@ static void emsuart_rx_intr_handler(void * para) {
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, length);
// set the status flag stating BRK has been received and we can start a new package
EMS_Sys_Status.emsRxStatus = EMS_RX_IDLE;
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE;
// call emsuart_recvTask() at next opportunity
system_os_post(EMSUART_recvTaskPrio, 0, 0);

View File

@@ -10,8 +10,10 @@
#include "ems.h"
// All MQTT topics are prefixed with the following string
#define MQTT_BASE "home"
// MQTT base name
#define MQTT_BASE "home" // all MQTT topics are prefix with this string, in the format <MQTT_BASE>/<app name>/<topic>
// MQTT general settings
#define MQTT_TOPIC_START "start"
#define MQTT_TOPIC_START_PAYLOAD "start"
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name
@@ -20,7 +22,7 @@
#define MQTT_KEEPALIVE 300
#define MQTT_QOS 1
// thermostat
// MQTT for thermostat
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes
#define TOPIC_THERMOSTAT_CMD_MODE "thermostat_cmd_mode" // for received thermostat mode changes
@@ -28,7 +30,7 @@
#define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature
#define THERMOSTAT_MODE "thermostat_mode" // mode
// boiler
// MQTT for boiler
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
@@ -44,17 +46,10 @@
#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
//////////////////////////////////////////////////////////////////////////////////////////////////
// THESE DEFAULT VALUES CAN ALSO BE SET AND STORED WITHTIN THE APPLICATION (see 'set' command) //
//////////////////////////////////////////////////////////////////////////////////////////////////
// Set your wifi and mqtt params
// If set to NULL (default) you'll need to set them in AP mode using the 'set' command
#define WIFI_SSID NULL
#define WIFI_PASSWORD NULL
#define MQTT_HOST NULL
#define MQTT_USER NULL
#define MQTT_PASS NULL
////////////////////////////////////////////////////////////////////////////////////////////////////
// THESE DEFAULT VALUES CAN ALSO BE SET AND STORED WITHTIN THE APPLICATION (see 'set' command) //
// ALTHOUGH YOU MAY ALSO HARDCODE THEM HERE BUT THEY WILL BE OVERWRITTEN WITH NEW RELEASE UPDATES //
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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 (LED_BULLETIN) or external via an external pull-up LED (e.g. D1 on bbqkees' board)

View File

@@ -6,5 +6,5 @@
#pragma once
#define APP_NAME "EMS-ESP"
#define APP_VERSION "1.4.1"
#define APP_VERSION "1.5.0"
#define APP_HOSTNAME "ems-esp"