EMS+ updates

This commit is contained in:
proddy
2019-05-03 12:47:25 +02:00
parent 851e9579fe
commit fbb3a46b85
7 changed files with 84 additions and 76 deletions

View File

@@ -5,7 +5,7 @@ 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.7.0 dev] 2019-04-17
## [1.7.0 dev] 2019-05
### Added
@@ -15,16 +15,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support for multiple thermostat heating circuits like the HC1/HC2 on a RC35, also via MQTT (thanks @lobocobra)
- `boiler flowtemp` command to set the flow temperature [(issue 59)](https://github.com/proddy/EMS-ESP/issues/59)
- `tx_delay` setting for circuits where we needed to slow down Tx transmission
- nefit proline hrc 24 cw4 thermostat
- support for Buderus RC300 and RC310 thermostats [(issue 37)](https://github.com/proddy/EMS-ESP/issues/37)
- new boiler: Nefit proline hrc 24 cw4 thermostat
- new thermostats: Buderus RC300 and RC310 thermostats [(issue 37)](https://github.com/proddy/EMS-ESP/issues/37)
- added a test harness to try out response to various telegrams
- EMS_ID_GATEWAY for Buderus Web Gateway KM200
- new devices: Buderus Web Gateway KM200, Solar Module SM100
### Changed
- `types` renamed to `devices` to also show all detected devices
- EMS Plus logic optimized
- `silent_mode` to `listen_mode`
- increased Tx queue to 100
## [1.6.0] 2019-03-24

View File

@@ -57,7 +57,7 @@ uint8_t scanThermostat_count = 0;
// ems bus scan
Ticker scanDevices;
#define SCANDEVICES_TIME 250 // ms
#define SCANDEVICES_TIME 350 // ms
uint8_t scanDevices_count;
Ticker showerColdShotStopTimer;
@@ -324,7 +324,7 @@ void showInfo() {
myDebug(" System logging set to None");
}
myDebug(" LED is %s, Silent mode is %s", EMSESP_Status.led ? "on" : "off", EMSESP_Status.listen_mode ? "on" : "off");
myDebug(" LED is %s, Listen mode is %s", EMSESP_Status.led ? "on" : "off", EMSESP_Status.listen_mode ? "on" : "off");
if (EMSESP_Status.dallas_sensors > 0) {
myDebug(" %d external temperature sensor%s found", EMSESP_Status.dallas_sensors, (EMSESP_Status.dallas_sensors == 1) ? "" : "s");
}
@@ -893,7 +893,7 @@ void startDeviceScan() {
publishSensorValuesTimer.detach();
scanDevices_count = 1; // starts at 1
ems_setLogging(EMS_SYS_LOGGING_NONE);
myDebug("Starting a deep EMS device scan. This will take about 1 minute. Please wait...");
myDebug("Starting a deep EMS device scan. This can take up to 2 minutes. Please wait...");
scanThermostat.attach_ms(SCANDEVICES_TIME, do_scanDevices);
}
@@ -971,7 +971,7 @@ bool FSCallback(MYESP_FSACTION action, const JsonObject json) {
EMS_Boiler.device_id = EMSESP_BOILER_TYPE; // set default
}
// silent mode
// listen mode
EMSESP_Status.listen_mode = json["listen_mode"];
ems_setTxDisabled(EMSESP_Status.listen_mode);
@@ -1512,7 +1512,7 @@ void WIFICallback() {
emsuart_init();
myDebug("[UART] Opened Rx/Tx connection");
if (!EMSESP_Status.listen_mode) {
// go and find the boiler and thermostat types, if not in silent mode
// go and find the boiler and thermostat types, if not in listen mode
ems_discoverModels();
}
}

View File

@@ -15,7 +15,7 @@
#include <list> // std::list
#ifdef TESTS
#include "unit_tests.h"
#include "test_data.h"
uint8_t _TEST_DATA_max = ArraySize(TEST_DATA);
#endif
@@ -382,8 +382,7 @@ void ems_setLogging(_EMS_SYS_LOGGING loglevel) {
/**
* Calculate CRC checksum using lookup table for speed
* len is length of data in bytes (including the CRC byte at end)
* So its the complete telegram with the header. CRC is calculated only over the data bytes
* len is length of all the data in bytes (including the header & CRC byte at end)
*/
uint8_t _crcCalculator(uint8_t * data, uint8_t len) {
uint8_t crc = 0;
@@ -456,8 +455,8 @@ void _debugPrintTelegram(const char * prefix, _EMS_RxTelegram * EMS_RxTelegram,
char output_str[200] = {0};
char buffer[16] = {0};
uint8_t * data = EMS_RxTelegram->telegram;
uint8_t len = EMS_RxTelegram->data_length; // length of data block
uint8_t full_len = EMS_RxTelegram->length - 1; // no CRC
uint8_t data_len = EMS_RxTelegram->data_length; // length of data block
uint8_t length = EMS_RxTelegram->length; // includes CRC
strlcpy(output_str, "(", sizeof(output_str));
strlcat(output_str, COLOR_CYAN, sizeof(output_str));
@@ -475,19 +474,19 @@ void _debugPrintTelegram(const char * prefix, _EMS_RxTelegram * EMS_RxTelegram,
strlcat(output_str, prefix, sizeof(output_str));
strlcat(output_str, " telegram: ", sizeof(output_str));
for (int i = 0; i < full_len; i++) {
for (int i = 0; i < (length - 1); i++) {
strlcat(output_str, _hextoa(data[i], buffer), sizeof(output_str));
strlcat(output_str, " ", sizeof(output_str)); // add space
}
strlcat(output_str, "(CRC=", sizeof(output_str));
strlcat(output_str, _hextoa(data[full_len], buffer), sizeof(output_str));
strlcat(output_str, _hextoa(data[length - 1], buffer), sizeof(output_str));
strlcat(output_str, ")", sizeof(output_str));
// print number of data bytes only if its a valid telegram
if (len > 5) {
strlcat(output_str, ", #data=", sizeof(output_str));
strlcat(output_str, itoa(len, buffer, 10), sizeof(output_str));
if (data_len) {
strlcat(output_str, " #data=", sizeof(output_str));
strlcat(output_str, itoa(data_len, buffer, 10), sizeof(output_str));
}
strlcat(output_str, COLOR_RESET, sizeof(output_str));
@@ -638,7 +637,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
/**
* the main logic that parses the telegram message
* When we receive a Poll Request we need to send any Tx packages quickly within a 200ms window
* length is total number of bytes of the telegram including the CRC byte at the end if it exists
* length is total number of bytes of the telegram including the CRC byte at the end (if it exists)
*/
void _ems_readTelegram(uint8_t * telegram, uint8_t length) {
// create the Rx package
@@ -712,7 +711,7 @@ void _ems_readTelegram(uint8_t * telegram, uint8_t length) {
if (length <= 7) {
EMS_RxTelegram.data_length = 0; // special broadcast on ems+ have no data values
} else {
EMS_RxTelegram.data_length = length - 8; // remove 5 bytes header plus CRC + length byte + 0x00 at end
EMS_RxTelegram.data_length = length - 7; // remove 6 byte header plus CRC
}
} else {
EMS_RxTelegram.emsplus = false;
@@ -832,16 +831,21 @@ void _printMessage(_EMS_RxTelegram * EMS_RxTelegram) {
* and then call its callback if there is one defined
*/
void _ems_processTelegram(_EMS_RxTelegram * EMS_RxTelegram) {
// header
uint8_t src = EMS_RxTelegram->src;
uint16_t type = EMS_RxTelegram->type;
uint8_t offset = EMS_RxTelegram->offset;
// print out the telegram
// print out the telegram for verbose mode
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_THERMOSTAT) {
_printMessage(EMS_RxTelegram);
}
// ignore telegrams that don't have any data
if (EMS_RxTelegram->data_length == 0) {
return;
}
// header
uint8_t src = EMS_RxTelegram->src;
uint16_t type = EMS_RxTelegram->type;
// see if we recognize the type first by scanning our known EMS types list
bool typeFound = false;
uint8_t i = 0;
@@ -866,14 +870,13 @@ void _ems_processTelegram(_EMS_RxTelegram * EMS_RxTelegram) {
if ((EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_BASIC) || (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE)) {
myDebug("<--- %s(0x%02X) received", EMS_Types[i].typeString, type);
}
// call callback function to process it
// as we only handle complete telegrams (not partial) check that the offset is 0
// if EMS+ always proces it
// call callback function to process the telegram, only if there is data
if (EMS_RxTelegram->emsplus) {
// if EMS+ always proces it
(void)EMS_Types[i].processType_cb(EMS_RxTelegram);
} else {
// only if the offset is 0 as we want to handle full telegrams and not partial
if (offset == EMS_ID_NONE) {
if (EMS_RxTelegram->offset == EMS_ID_NONE) {
(void)EMS_Types[i].processType_cb(EMS_RxTelegram);
}
}
@@ -1193,7 +1196,8 @@ void _process_EasyStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) {
void _process_RCPLUSStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) {
if (EMS_RxTelegram->offset == 0) {
// the whole telegram
// e.g. Thermostat -> all, telegram: 10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00 (CRC=CC), #data=27
// e.g. Thermostat -> all, telegram: 10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00
// 10 00 FF 00 01 A5 80 00 01 30 28 00 30 28 01 54 03 03 01 01 54 02 A8 00 00 11 01 03 FF FF 00
EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_RCPLUSStatusMessage_curr); // value is * 10
EMS_Thermostat.setpoint_roomTemp = _toByte(EMS_OFFSET_RCPLUSStatusMessage_setpoint); // value is * 2
@@ -1202,14 +1206,14 @@ void _process_RCPLUSStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) {
// day night is byte(8), 0x01 for night, 0x00 for day
}
// this is a temp value but not sure which one
// e.g. Thermostat -> all, telegram: 10 00 FF 07 01 A5 32 (CRC=4E), #data=3
// actual set point
// e.g. Thermostat -> all, telegram: 10 00 FF 07 01 A5 32
if (EMS_RxTelegram->offset == 7) {
// to add...
}
// this is a temp value but not sure which one
// e.g. Thermostat -> all, telegram: 18 00 FF 06 01 A5 22 (CRC=64), #data=3
// next set point
// e.g. Thermostat -> all, telegram: 18 00 FF 06 01 A5 22
if (EMS_RxTelegram->offset == 6) {
// to add...
}
@@ -1286,8 +1290,13 @@ void _process_SM10Monitor(_EMS_RxTelegram * EMS_RxTelegram) {
* SM100Monitor - type 0x0262 EMS+
*/
void _process_SM100Monitor(_EMS_RxTelegram * EMS_RxTelegram) {
EMS_Other.SMcollectorTemp = _toShort(0); // collector temp from SM100, is *10
EMS_Other.SMbottomTemp = _toShort(2); // bottom temp from SM100, is *10
if (EMS_RxTelegram->data_length <= 2) {
EMS_Other.SMcollectorTemp = _toShort(0); // collector temp from SM100, is *10
}
if (EMS_RxTelegram->data_length > 2) {
EMS_Other.SMbottomTemp = _toShort(2); // bottom temp from SM100, is *10
}
EMS_Other.SM = true;
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
@@ -1819,7 +1828,7 @@ void ems_doReadCommand(uint16_t type, uint8_t dest, bool forceRefresh) {
// if we're preventing all outbound traffic, quit
if (EMS_Sys_Status.emsTxDisabled) {
myDebug("in Silent Mode. All Tx is disabled.");
myDebug("in Listen Mode. All Tx is disabled.");
return;
}
@@ -2202,18 +2211,16 @@ void ems_setWarmTapWaterActivated(bool activated) {
*/
void ems_startupTelegrams() {
if ((EMS_Sys_Status.emsTxDisabled) || (!EMS_Sys_Status.emsBusConnected)) {
myDebug("Unable to send startup sequence when in silent mode or bus is disabled");
myDebug("Unable to send startup sequence when in listen mode or the bus is disabled");
}
myDebug("Sending startup sequence...");
char s[20] = {0};
// (00:07:27.512) Telegram echo: telegram: 0B 08 1D 00 00 (CRC=84), #data=1
// Write type 0x1D to get out of function test mode
snprintf(s, sizeof(s), "%02X %02X 1D 00 00", EMS_ID_ME, EMS_Boiler.device_id);
ems_sendRawTelegram(s);
// (00:07:35.555) Telegram echo: telegram: 0B 88 01 00 1B (CRC=8B), #data=1
// Read type 0x01
snprintf(s, sizeof(s), "%02X %02X 01 00 1B", EMS_ID_ME, EMS_Boiler.device_id | 0x80);
ems_sendRawTelegram(s);
@@ -2262,11 +2269,11 @@ void ems_testTelegram(uint8_t test_num) {
length++; // this is the total amount of bytes
telegram[length] = _crcCalculator(telegram, length + 1); // add the CRC
myDebug("[TEST %d] Injecting telegram %s (data length %d)", test_num, TEST_DATA[test_num - 1], length);
myDebug("[TEST %d] Injecting telegram %s", test_num, TEST_DATA[test_num - 1]);
// go an parse it
_ems_readTelegram(telegram, length + 1); // include CRC in length
#else
myDebug("Firmware not compiled with tests");
myDebug("Firmware not compiled with test data set");
#endif
}

View File

@@ -20,7 +20,6 @@
#define EMS_ID_SM 0x30 // Solar Module SM10 and SM100
#define EMS_ID_GATEWAY 0x48 // KM200 Web Gateway
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
// max length of a telegram, including CRC, for Rx and Tx.
@@ -40,12 +39,12 @@
#define EMS_BOILER_BURNPOWER_TAPWATER 100
#define EMS_BOILER_SELFLOWTEMP_HEATING 70
//define maximum settable tapwater temperature, not every installation supports 90 degrees
// define maximum settable tapwater temperature
#define EMS_BOILER_TAPWATER_TEMPERATURE_MAX 60
#define EMS_TX_TELEGRAM_QUEUE_MAX 50 // max size of Tx FIFO queue
#define EMS_TX_TELEGRAM_QUEUE_MAX 100 // max size of Tx FIFO queue
//#define EMS_SYS_LOGGING_DEFAULT EMS_SYS_LOGGING_VERBOSE
// #define EMS_SYS_LOGGING_DEFAULT EMS_SYS_LOGGING_VERBOSE
#define EMS_SYS_LOGGING_DEFAULT EMS_SYS_LOGGING_NONE
/* EMS UART transfer status */

View File

@@ -99,7 +99,7 @@
// RC1010,RC310 and RC300 specific (EMS Plus)
#define EMS_TYPE_RCPLUSStatusMessage 0x01A5 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RCPLUSSet 0x03 // setpoint temp message - this is incorrect!
#define EMS_OFFSET_RCPLUSStatusMessage_setpoint 2 // setpoint temp
#define EMS_OFFSET_RCPLUSStatusMessage_setpoint 3 // setpoint temp
#define EMS_OFFSET_RCPLUSStatusMessage_curr 0 // current temp
// Known EMS types

29
src/test_data.h Normal file
View File

@@ -0,0 +1,29 @@
#ifdef TESTS
static const char * TEST_DATA[] = {
"08 00 34 00 3E 02 1D 80 00 31 00 00 01 00 01 0B AE 02", // test 1 - EMS
"10 00 FF 00 01 A5 80 00 01 30 28 00 30 28 01 54 03 03 01 01 54 02 A8 00 00 11 01 03 FF FF 00", // test 2 - RC310 ems+
"10 00 FF 19 01 A5 06 04 00 00 00 00 FF 64 37 00 3C 01 FF 01", // test 3 - RC310 ems+
"30 00 FF 00 02 62 00 A1 01 3F 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00", // test 4 - SM100
"10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00", // test 5 - RC1010
"18 00 FF 00 01 A5 00 DD 21 23 00 00 23 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00", // test 6 - RC300
"90 00 FF 00 00 6F 01 01 00 46 00 B9", // test 7 - FR10
"30 00 FF 00 02 62 01 FB 01 9E 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 2B", // test 8 - SM100
"30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0C 20 64 00 00 00 00 E9", // test 9 - SM100
"30 09 FF 00 00 01", // test 10 - EMS+
"30 0B 97 00", // test 11 - SM100
"30 00 FF 00 02 62 1 CA", // test 12 - SM100
"30 00 FF 00 02 8E 00 00 00 00 00 00 05 19 00 00 75 D3", // test 13 - SM100
"30 00 FF 00 02 63 80 00 80 00 00 00 80 00 80 00 80 00 00", // test 14 - SM100
"30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0B 09 64 00 00 00 00", // test 15 - SM100
"30 00 FF 00 02 62 01 CA 01 93 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00", // test 16 - SM100
"30 00 FF 00 02 6A 03 03 03 00 03 03 03 03 03 00 03 03", // test 17 - SM100
"30 00 FF 00 02 6A 03 03 03 00 03 03 03 03 03 00 04 03", // test 18 - SM100
"30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 09 08 64 00 00 00 00", // test 19 - SM100
"10 00 FF 07 01 A5 32" // test 20 - RC EMS+
};
#endif

View File

@@ -1,28 +0,0 @@
#ifdef TESTS
static const char * TEST_DATA[] = {
"08 00 34 00 3E 02 1D 80 00 31 00 00 01 00 01 0B AE 02", // test 1 - EMS
"10 00 FF 00 01 A5 80 00 01 30 28 00 30 28 01 54 03 03 01 01 54 02 A8 00 00 11 01 03 FF FF 00", // test 2 - RC310 ems+
"10 00 FF 19 01 A5 06 04 00 00 00 00 FF 64 37 00 3C 01 FF 01", // test 3 - RC310 ems+
"30 00 FF 00 02 62 00 A1 01 3F 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00", // test 4 - SM100
"10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00", // test 5 - RC1010
"18 00 FF 00 01 A5 00 DD 21 23 00 00 23 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00", // test 6 - RC300
"90 00 FF 00 00 6F 01 01 00 46 00 B9", // test 7 - FR10
"30 00 FF 00 02 62 01 FB 01 9E 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 2B", // test 8 - SM100
"30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0C 20 64 00 00 00 00 E9", // test 9 - SM100
"30 09 FF 00 00 01", // test 10 - EMS+
"30 0B 97 00", // test 11 - SM100
"30 00 FF 00 02 62 1 CA", // test 12 - SM100
"30 00 FF 00 02 8E 00 00 00 00 00 00 05 19 00 00 75 D3", // test 13 - SM100
"30 00 FF 00 02 63 80 00 80 00 00 00 80 00 80 00 80 00 00", // test 14 - SM100
"30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0B 09 64 00 00 00 00", // test 15 - SM100
"30 00 FF 00 02 62 01 CA 01 93 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00 80 00", // test 16 - SM100
"30 00 FF 00 02 6A 03 03 03 00 03 03 03 03 03 00 03 03", // test 17 - SM100
"30 00 FF 00 02 6A 03 03 03 00 03 03 03 03 03 00 04 03", // test 18 - SM100
"30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 09 08 64 00 00 00 00" // test 19 - SM100
};
#endif