add hanlding for thermostat mode

This commit is contained in:
proddy
2018-05-31 22:05:21 +02:00
parent e1b06328ad
commit ecd02fdc5f
11 changed files with 178 additions and 127 deletions

View File

@@ -76,22 +76,23 @@ I've tested the code and circuit with a few ESP8266 development boards such as t
Use the telnet client to inform you of all activity and errors real-time. This is an example of the telnet output: Use the telnet client to inform you of all activity and errors real-time. This is an example of the telnet output:
![Telnet](doc/telnet/telnet_example.JPG) ![Telnet](doc/telnet/telnet_example.PNG)
If you hit 'q' and Enter, it will toggle verbose logging showing you more detailed messages. I use ANSI colors with white text for info messages, green for well formatted telegram packages (which have validated CRC checks), red for corrupt packages and yellow for send responses. If you hit 'q' and Enter, it will toggle verbose logging showing you more detailed messages. I use ANSI colors with white text for info messages, green for well formatted telegram packages (which have validated CRC checks), red for corrupt packages and yellow for send responses.
![Telnet](doc/telnet/telnet_verbose.JPG) ![Telnet](doc/telnet/telnet_verbose.PNG)
To see the current values of the Boiler and its parameters type 's' and hit Enter. Watch out for unsuccessful telegram packets in the #CrcErrors line. To see the current values of the Boiler and its parameters type 's' and hit Enter. Watch out for unsuccessful telegram packets in the #CrcErrors line.
![Telnet](doc/telnet/telnet_stats.JPG) ![Telnet](doc/telnet/telnet_stats.PNG)
Commands can be issued directly to the EMS bus typing in a letter followed by an optional parameter and pressing Enter. Supported commands are: Commands can be issued directly to the EMS bus typing in a letter followed by an optional parameter and pressing Enter. Supported commands are:
* **r** to send a read command to all devices to fetch values. The 2nd parameter is the type. For example 'r 33' will request type UBAParameterWW and bring back the Warm Water temperatures (not the heating) from the Boiler * **r** to send a read command to all devices to fetch values. The 2nd parameter is the type. For example 'r 33' will request type UBAParameterWW and bring back the Warm Water temperatures (not the heating) from the Boiler
* **t** set the thermostat temperature to the given value * **t** set the thermostat temperature to the given value
* **w** to adjust the temperature of the warm water from the boiler * **w** to adjust the temperature of the warm water from the boiler
* **a** to turn the warm water on and off * **a** to turn the warm water on and off
* **p** to toggle the Polling response on/off. It's not necessary to have Polling enabled to work * **p** to toggle the Polling response on/off. It's not necessary to have Polling enabled to work. I use this for debugging purposes.
* **m** to set the thermostat mode from low, manual and clock/auto.
* **T** to toggle thermostat reading on/off * **T** to toggle thermostat reading on/off
* **S** to toggle the Shower Timer functionality on/off * **S** to toggle the Shower Timer functionality on/off
@@ -187,7 +188,7 @@ The Boiler (ID 0x08) will send out these broadcast telegrams regularly:
And a thermostat (ID 0x17 for a RC20) would broadcast these messages regularly: And a thermostat (ID 0x17 for a RC20) would broadcast these messages regularly:
| Type | Description | | Type | Description |
| ---- | ----------- | undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined | | ---- | ----------- | undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |undefined |
| 0x06 | time on thermostat Y,M,H,D,M,S,wd | | 0x06 | time on thermostat Y,M,H,D,M,S,wd |
Refer to the code in `ems.cpp` for further explanation on how to parse these message types and also reference the EMS Wiki. Refer to the code in `ems.cpp` for further explanation on how to parse these message types and also reference the EMS Wiki.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

BIN
doc/telnet/telnet_stats.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -159,7 +159,7 @@ bool ESPHelper::begin(const char * hostname) {
//initially attempt to connect to wifi when we begin (but only block for 2 seconds before timing out) //initially attempt to connect to wifi when we begin (but only block for 2 seconds before timing out)
uint8_t timeout = 0; //counter for begin connection attempts uint8_t timeout = 0; //counter for begin connection attempts
while (((!client.connected() && _mqttSet) || WiFi.status() != WL_CONNECTED) while (((!client.connected() && _mqttSet) || WiFi.status() != WL_CONNECTED)
&& timeout < 200) { //max 2 sec before timeout && timeout < 200) { //max 2 sec before timeout
reconnect(); reconnect();
timeout++; timeout++;
} }
@@ -445,7 +445,7 @@ uint8_t ESPHelper::setConnectionStatus() {
//if connected to wifi set the mode to wifi only and run the callback if needed //if connected to wifi set the mode to wifi only and run the callback if needed
if (WiFi.status() == WL_CONNECTED) { if (WiFi.status() == WL_CONNECTED) {
if (_connectionStatus < WIFI_ONLY if (_connectionStatus < WIFI_ONLY
&& _wifiCallbackSet) { //if the wifi previously wasn't connected but now is, run the callback && _wifiCallbackSet) { //if the wifi previously wasn't connected but now is, run the callback
_wifiCallback(); _wifiCallback();
} }
returnVal = WIFI_ONLY; returnVal = WIFI_ONLY;

View File

@@ -46,14 +46,15 @@ Ticker showerResetTimer;
#define PROJECT_CMDS \ #define PROJECT_CMDS \
"s=show statistics\n\r" \ "s=show statistics\n\r" \
"* q=toggle Verbose telegram logging\n\r" \ "* q=toggle Verbose telegram logging\n\r" \
"* m=publish stats to MQTT\n\r" \ "* P=publish stats to MQTT\n\r" \
"* p=toggle Poll response\n\r" \ "* p=toggle Poll response (for debugging)\n\r" \
"* T=toggle Thermostat suport on/off\n\r" \ "* T=toggle Thermostat suport on/off\n\r" \
"* S=toggle Shower Timer on/off\n\r" \ "* S=toggle Shower Timer on/off\n\r" \
"* r [n] to request for data from EMS " \ "* r [n] to request for data from EMS, some examples:\n\r" \
"(33=UBAParameterWW, 18=UBAMonitorFast, 19=UBAMonitorSlow, " \ "* from Boiler: 33=UBAParameterWW, 18=UBAMonitorFast, 19=UBAMonitorSlow, 34=UBAMonitorWWMessage\n\r" \
"34=UBAMonitorWWMessage, 91=RC20StatusMessage, 6=RC20Time, 2=Version)\n\r" \ "* from Thermostat: 91=RC20StatusMessage, A8=RC20Temperature, 6=RC20Time, 2=Version\n\r" \
"* t [n] set thermostat temperature to n\n\r" \ "* t [n] set thermostat temperature to n\n\r" \
"* m [n] set thermostat mode (0=low, 1=manual, 2=clock)\n\r" \
"* w [n] set boiler warm water temperature to n (min 30)\n\r" \ "* w [n] set boiler warm water temperature to n (min 30)\n\r" \
"* a [n] activate boiler warm water on (n=1) or off (n=0)" "* a [n] activate boiler warm water on (n=1) or off (n=0)"
@@ -106,7 +107,8 @@ netInfo homeNet = {.mqttHost = MQTT_IP,
.mqttPass = MQTT_PASS, .mqttPass = MQTT_PASS,
.mqttPort = 1883, // this is the default, change if using another port .mqttPort = 1883, // this is the default, change if using another port
.ssid = WIFI_SSID, .ssid = WIFI_SSID,
.pass = WIFI_PASSWORD}; .pass = WIFI_PASSWORD
};
ESPHelper myESP(&homeNet); ESPHelper myESP(&homeNet);
@@ -236,6 +238,16 @@ void showInfo() {
myDebug(" Setpoint room temperature is %s C\n", _float_to_char(s, EMS_Thermostat.setpoint_roomTemp)); myDebug(" Setpoint room temperature is %s C\n", _float_to_char(s, EMS_Thermostat.setpoint_roomTemp));
myDebug(" Current room temperature is %s C\n", _float_to_char(s, EMS_Thermostat.curr_roomTemp)); myDebug(" Current room temperature is %s C\n", _float_to_char(s, EMS_Thermostat.curr_roomTemp));
myDebug(" Mode is set to ");
if (EMS_Thermostat.mode == 0) {
myDebug("low\n");
} else if (EMS_Thermostat.mode == 1) {
myDebug("manual\n");
} else if (EMS_Thermostat.mode == 2) {
myDebug("clock/auto\n");
} else {
myDebug("<unknown>\n");
}
} }
// show the Shower Info // show the Shower Info
@@ -303,7 +315,7 @@ void myDebugCallback() {
b = !ems_getPoll(); b = !ems_getPoll();
ems_setPoll(b); ems_setPoll(b);
break; break;
case 'm': case 'P':
publishValues(); publishValues();
break; break;
case 'r': // read command for Boiler or Thermostat case 'r': // read command for Boiler or Thermostat
@@ -312,6 +324,9 @@ void myDebugCallback() {
case 't': // set thermostat temp case 't': // set thermostat temp
ems_setThermostatTemp(strtof(&cmd[2], 0)); ems_setThermostatTemp(strtof(&cmd[2], 0));
break; break;
case 'm': // set thermostat mode
ems_setThermostatMode(cmd[2] - '0');
break;
case 'w': // set warm water temp case 'w': // set warm water temp
ems_setWarmWaterTemp((uint8_t)strtol(&cmd[2], 0, 10)); ems_setWarmWaterTemp((uint8_t)strtol(&cmd[2], 0, 10));
break; break;
@@ -545,7 +560,7 @@ void loop() {
} else { } else {
// check if the shower has been on too long // check if the shower has been on too long
if ((((timestamp - Boiler_Shower.timerStart) > SHOWER_MAX_DURATION) && !Boiler_Shower.isColdShot) if ((((timestamp - Boiler_Shower.timerStart) > SHOWER_MAX_DURATION) && !Boiler_Shower.isColdShot)
&& Boiler_Status.shower_timer) { && Boiler_Status.shower_timer) {
_showerColdShotStart(); _showerColdShotStart();
// start the timer for n seconds which will reset the water back to hot // start the timer for n seconds which will reset the water back to hot
showerResetTimer.attach(SHOWER_OFF_DURATION, _showerColdShotStop); showerResetTimer.attach(SHOWER_OFF_DURATION, _showerColdShotStop);

View File

@@ -37,6 +37,12 @@ _EMS_TxTelegram EMS_TxTelegram; // Empty buffer for sending telegrams
#define EMS_TYPE_RCOutdoorTempMessage 0xa3 // we can ignore #define EMS_TYPE_RCOutdoorTempMessage 0xa3 // we can ignore
#define EMS_TYPE_Version 0x02 // version of the controller #define EMS_TYPE_Version 0x02 // version of the controller
// Offsets for specific values in a telegram, per type, used for validation
#define EMS_OFFSET_RC20Temperature_temp 0x1C // thermostat set temp
#define EMS_OFFSET_RC20Temperature_mode 0x17 // thermostat mode
#define EMS_OFFSET_UBAParameterWW_wwtemp 0x02 // WW Temperature
#define EMS_OFFSET_UBAParameterWW_wwactivated 0x01 // WW Activated
// and call backs // and call backs
#define MAX_TYPECALLBACK 12 // make sure it matches the #types you have #define MAX_TYPECALLBACK 12 // make sure it matches the #types you have
// callbacks per type // callbacks per type
@@ -47,24 +53,22 @@ bool _process_UBAParameterWW(uint8_t * data, uint8_t length);
bool _process_RC20StatusMessage(uint8_t * data, uint8_t length); bool _process_RC20StatusMessage(uint8_t * data, uint8_t length);
bool _process_RC20Time(uint8_t * data, uint8_t length); bool _process_RC20Time(uint8_t * data, uint8_t length);
bool _process_RC20Temperature(uint8_t * data, uint8_t length); bool _process_RC20Temperature(uint8_t * data, uint8_t length);
bool _process_RC20Temperature(uint8_t * data, uint8_t length);
bool _process_Version(uint8_t * data, uint8_t length); bool _process_Version(uint8_t * data, uint8_t length);
const _EMS_Types EMS_Types[MAX_TYPECALLBACK] = const _EMS_Types EMS_Types[MAX_TYPECALLBACK] =
{ {EMS_ID_BOILER, EMS_TYPE_UBAMonitorFast, "UBAMonitorFast", 36, _process_UBAMonitorFast}, {{EMS_ID_BOILER, EMS_TYPE_UBAMonitorFast, "UBAMonitorFast", 36, _process_UBAMonitorFast},
{EMS_ID_BOILER, EMS_TYPE_UBAMonitorSlow, "UBAMonitorSlow", 28, _process_UBAMonitorSlow}, {EMS_ID_BOILER, EMS_TYPE_UBAMonitorSlow, "UBAMonitorSlow", 28, _process_UBAMonitorSlow},
{EMS_ID_BOILER, EMS_TYPE_UBAMonitorWWMessage, "UBAMonitorWWMessage", 10, _process_UBAMonitorWWMessage}, {EMS_ID_BOILER, EMS_TYPE_UBAMonitorWWMessage, "UBAMonitorWWMessage", 10, _process_UBAMonitorWWMessage},
{EMS_ID_BOILER, EMS_TYPE_UBAParameterWW, "UBAParameterWW", 10, _process_UBAParameterWW}, {EMS_ID_BOILER, EMS_TYPE_UBAParameterWW, "UBAParameterWW", 10, _process_UBAParameterWW},
{EMS_ID_BOILER, EMS_TYPE_UBATotalUptimeMessage, "UBATotalUptimeMessage", 30, NULL}, {EMS_ID_BOILER, EMS_TYPE_UBATotalUptimeMessage, "UBATotalUptimeMessage", 30, NULL},
{EMS_ID_BOILER, EMS_TYPE_UBAMaintenanceSettingsMessage, "UBAMaintenanceSettingsMessage", 30, NULL}, {EMS_ID_BOILER, EMS_TYPE_UBAMaintenanceSettingsMessage, "UBAMaintenanceSettingsMessage", 30, NULL},
{EMS_ID_BOILER, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", 30, NULL}, {EMS_ID_BOILER, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", 30, NULL},
{EMS_ID_BOILER, EMS_TYPE_UBAMaintenanceStatusMessage, "UBAMaintenanceStatusMessage", 30, NULL}, {EMS_ID_BOILER, EMS_TYPE_UBAMaintenanceStatusMessage, "UBAMaintenanceStatusMessage", 30, NULL},
{EMS_ID_THERMOSTAT, EMS_TYPE_RC20StatusMessage, "RC20StatusMessage", 3, _process_RC20StatusMessage}, {EMS_ID_THERMOSTAT, EMS_TYPE_RC20StatusMessage, "RC20StatusMessage", 3, _process_RC20StatusMessage},
{EMS_ID_THERMOSTAT, EMS_TYPE_RC20Time, "RC20Time", 20, _process_RC20Time}, {EMS_ID_THERMOSTAT, EMS_TYPE_RC20Time, "RC20Time", 20, _process_RC20Time},
{EMS_ID_THERMOSTAT, EMS_TYPE_RC20Temperature, "RC20Temperature", 10, _process_RC20Temperature}, {EMS_ID_THERMOSTAT, EMS_TYPE_RC20Temperature, "RC20Temperature", 27, _process_RC20Temperature},
{EMS_ID_THERMOSTAT, EMS_TYPE_Version, "Version", 2, _process_Version} {EMS_ID_THERMOSTAT, EMS_TYPE_Version, "Version", 2, _process_Version}};
};
// reserve space for the data we collect from the Boiler and Thermostat // reserve space for the data we collect from the Boiler and Thermostat
_EMS_Boiler EMS_Boiler; _EMS_Boiler EMS_Boiler;
@@ -72,21 +76,20 @@ _EMS_Thermostat EMS_Thermostat;
// CRC lookup table with poly 12 for faster checking // CRC lookup table with poly 12 for faster checking
const uint8_t ems_crc_table[] = const uint8_t ems_crc_table[] =
{ 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24, {0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24,
0x26, 0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x26, 0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A,
0x4C, 0x4E, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5A, 0x5C, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x4C, 0x4E, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5A, 0x5C, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70,
0x72, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96, 0x72, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96,
0x98, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC, 0x98, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC,
0xBE, 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, 0xE0, 0xE2, 0xBE, 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, 0xE0, 0xE2,
0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, 0xF0, 0xF2, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFE, 0x19, 0x1B, 0x1D, 0x1F, 0x11, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, 0xF0, 0xF2, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFE, 0x19, 0x1B, 0x1D, 0x1F, 0x11,
0x13, 0x15, 0x17, 0x09, 0x0B, 0x0D, 0x0F, 0x01, 0x03, 0x05, 0x07, 0x39, 0x3B, 0x3D, 0x3F, 0x31, 0x33, 0x35, 0x37, 0x13, 0x15, 0x17, 0x09, 0x0B, 0x0D, 0x0F, 0x01, 0x03, 0x05, 0x07, 0x39, 0x3B, 0x3D, 0x3F, 0x31, 0x33, 0x35, 0x37,
0x29, 0x2B, 0x2D, 0x2F, 0x21, 0x23, 0x25, 0x27, 0x59, 0x5B, 0x5D, 0x5F, 0x51, 0x53, 0x55, 0x57, 0x49, 0x4B, 0x4D, 0x29, 0x2B, 0x2D, 0x2F, 0x21, 0x23, 0x25, 0x27, 0x59, 0x5B, 0x5D, 0x5F, 0x51, 0x53, 0x55, 0x57, 0x49, 0x4B, 0x4D,
0x4F, 0x41, 0x43, 0x45, 0x47, 0x79, 0x7B, 0x7D, 0x7F, 0x71, 0x73, 0x75, 0x77, 0x69, 0x6B, 0x6D, 0x6F, 0x61, 0x63, 0x4F, 0x41, 0x43, 0x45, 0x47, 0x79, 0x7B, 0x7D, 0x7F, 0x71, 0x73, 0x75, 0x77, 0x69, 0x6B, 0x6D, 0x6F, 0x61, 0x63,
0x65, 0x67, 0x99, 0x9B, 0x9D, 0x9F, 0x91, 0x93, 0x95, 0x97, 0x89, 0x8B, 0x8D, 0x8F, 0x81, 0x83, 0x85, 0x87, 0xB9, 0x65, 0x67, 0x99, 0x9B, 0x9D, 0x9F, 0x91, 0x93, 0x95, 0x97, 0x89, 0x8B, 0x8D, 0x8F, 0x81, 0x83, 0x85, 0x87, 0xB9,
0xBB, 0xBD, 0xBF, 0xB1, 0xB3, 0xB5, 0xB7, 0xA9, 0xAB, 0xAD, 0xAF, 0xA1, 0xA3, 0xA5, 0xA7, 0xD9, 0xDB, 0xDD, 0xDF, 0xBB, 0xBD, 0xBF, 0xB1, 0xB3, 0xB5, 0xB7, 0xA9, 0xAB, 0xAD, 0xAF, 0xA1, 0xA3, 0xA5, 0xA7, 0xD9, 0xDB, 0xDD, 0xDF,
0xD1, 0xD3, 0xD5, 0xD7, 0xC9, 0xCB, 0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7, 0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5, 0xD1, 0xD3, 0xD5, 0xD7, 0xC9, 0xCB, 0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7, 0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5,
0xF7, 0xE9, 0xEB, 0xED, 0xEF, 0xE1, 0xE3, 0xE5, 0xE7 0xF7, 0xE9, 0xEB, 0xED, 0xEF, 0xE1, 0xE3, 0xE5, 0xE7};
};
extern ESPHelper myESP; // needed for the DEBUG statements below extern ESPHelper myESP; // needed for the DEBUG statements below
#define myDebug(x, ...) myESP.printf(x, ##__VA_ARGS__); #define myDebug(x, ...) myESP.printf(x, ##__VA_ARGS__);
@@ -95,9 +98,10 @@ extern ESPHelper myESP; // needed for the DEBUG statements below
const uint64_t RX_READ_TIMEOUT = 5000; // in ms. 5 seconds timeout for read replies const uint64_t RX_READ_TIMEOUT = 5000; // in ms. 5 seconds timeout for read replies
const uint8_t RX_READ_TIMEOUT_COUNT = 4; // 4 retries before timeout const uint8_t RX_READ_TIMEOUT_COUNT = 4; // 4 retries before timeout
uint8_t emsLastRxCount = 0; uint8_t emsLastRxCount; // used for retries when sending failed
// init stats and counters and buffers // init stats and counters and buffers
// uses -1 or 255 for values that haven't been set yet
void ems_init() { void ems_init() {
// overall status // overall status
EMS_Sys_Status.emsRxPgks = 0; EMS_Sys_Status.emsRxPgks = 0;
@@ -120,6 +124,7 @@ void ems_init() {
EMS_Thermostat.day = 0; EMS_Thermostat.day = 0;
EMS_Thermostat.month = 0; EMS_Thermostat.month = 0;
EMS_Thermostat.year = 0; EMS_Thermostat.year = 0;
EMS_Thermostat.mode = 255; // dummy value
// UBAParameterWW // UBAParameterWW
EMS_Boiler.wWActivated = false; // Warm Water activated EMS_Boiler.wWActivated = false; // Warm Water activated
@@ -274,8 +279,8 @@ void _ems_sendTelegram() {
void ems_parseTelegram(uint8_t * telegram, uint8_t length) { void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
// if we're waiting on a response from a previous read and it hasn't come, try again // if we're waiting on a response from a previous read and it hasn't come, try again
if ((EMS_Sys_Status.emsTxStatus != EMS_TX_PENDING) if ((EMS_Sys_Status.emsTxStatus != EMS_TX_PENDING)
&& ((EMS_TxTelegram.action == EMS_TX_READ) || (EMS_TxTelegram.action == EMS_TX_VALIDATE)) && ((EMS_TxTelegram.action == EMS_TX_READ) || (EMS_TxTelegram.action == EMS_TX_VALIDATE))
&& ((millis() - EMS_Sys_Status.emsLastTx) > RX_READ_TIMEOUT)) { && ((millis() - EMS_Sys_Status.emsLastTx) > RX_READ_TIMEOUT)) {
if (emsLastRxCount++ >= RX_READ_TIMEOUT_COUNT) { if (emsLastRxCount++ >= RX_READ_TIMEOUT_COUNT) {
// give up // give up
myDebug("Error! no send acknowledgement. Giving up.\n"); myDebug("Error! no send acknowledgement. Giving up.\n");
@@ -460,6 +465,18 @@ void _processType(uint8_t * telegram, uint8_t length) {
} }
} }
/*
* Report back true if there is a package pending a write in the queue
*/
bool _checkWriteQueueFull() {
if (EMS_Sys_Status.emsTxStatus == EMS_TX_PENDING) { // send is already pending
myDebug("Cannot write - already a telegram pending send.\n");
return true;
}
return false; // nothing queue, we can do a write command
}
/* /*
* UBAParameterWW - type 0x33 - warm water parameters * UBAParameterWW - type 0x33 - warm water parameters
*/ */
@@ -544,35 +561,45 @@ bool _process_RC20StatusMessage(uint8_t * data, uint8_t length) {
} }
/* /*
* RC20Temperature - type 0xa8 - set temp value from the RC20 thermostat (0x17) * RC20Temperature - type 0xa8 - for set temp value and mode from the RC20 thermostat (0x17)
* Special case as we only want to store the value after a write command
*/ */
bool _process_RC20Temperature(uint8_t * data, uint8_t length) { bool _process_RC20Temperature(uint8_t * data, uint8_t length) {
// only interested in the single byte response we send to validate a temp change // check if this was called specifically to validate a single value
if (length == 6) { // which is always stored in data[0] because we request only 1 byte
EMS_Thermostat.setpoint_roomTemp = ((float)data[0]) / (float)2; if (length == EMS_MIN_TELEGRAM_LENGTH) {
if (EMS_TxTelegram.type_validate == EMS_OFFSET_RC20Temperature_temp) {
// validate the set temp
EMS_Thermostat.setpoint_roomTemp = ((float)data[0]) / (float)2;
} else if (EMS_TxTelegram.type_validate == EMS_OFFSET_RC20Temperature_mode) {
// validate the mode
EMS_Thermostat.mode = data[0];
}
EMS_Sys_Status.emsRefreshed = true; // set the updated flag to trigger a send back to HA // and send the values back to HA (Home Assistant MQTT)
EMS_Sys_Status.emsRefreshed = true;
} else {
// Process the whole telegram package
EMS_Thermostat.mode = data[EMS_OFFSET_RC20Temperature_mode]; // get the mode
} }
return true; return true;
} }
/* /*
* Version - type 0x02 - get the version of the Thermostat * Version - type 0x02 - get the version of the Thermostat firmware
*/ */
bool _process_Version(uint8_t * data, uint8_t length) { bool _process_Version(uint8_t * data, uint8_t length) {
uint8_t major = data[1]; uint8_t major = data[1];
uint8_t minor = data[2]; uint8_t minor = data[2];
// TODO: finish this myDebug("Version %d.%d\n", major, minor); // TODO: finish this
return true; return true;
} }
/* /*
* process_RC20Time - type 0x06 - date and time from the RC20 thermostat (0x17) - 14 bytes long * process_RC20Time - type 0x06 - date and time from the RC20 thermostat (0x17) - 14 bytes long
*/ */
bool _process_RC20Time(uint8_t * data, uint8_t length) { bool _process_RC20Time(uint8_t * data, uint8_t length) {
EMS_Thermostat.hour = data[2]; EMS_Thermostat.hour = data[2];
EMS_Thermostat.minute = data[4]; EMS_Thermostat.minute = data[4];
@@ -581,7 +608,7 @@ bool _process_RC20Time(uint8_t * data, uint8_t length) {
EMS_Thermostat.month = data[1]; EMS_Thermostat.month = data[1];
EMS_Thermostat.year = data[0]; EMS_Thermostat.year = data[0];
// we can optional set the time based on the Thermostat's time if we want // we can optional set the time based on the thermostat's time if we want
setTime(EMS_Thermostat.hour, setTime(EMS_Thermostat.hour,
EMS_Thermostat.minute, EMS_Thermostat.minute,
EMS_Thermostat.second, EMS_Thermostat.second,
@@ -593,8 +620,8 @@ bool _process_RC20Time(uint8_t * data, uint8_t length) {
} }
/* /*
* Build the telegram, which includes a single byte followed by the CRC at the end * Build the telegram, which includes a single byte followed by the CRC at the end
*/ */
void _buildTxTelegram(uint8_t data_value) { void _buildTxTelegram(uint8_t data_value) {
// header // header
EMS_TxTelegram.data[0] = EMS_ID_ME; // src EMS_TxTelegram.data[0] = EMS_ID_ME; // src
@@ -613,19 +640,16 @@ void _buildTxTelegram(uint8_t data_value) {
/* /*
* Send a command to Tx to Read from another device * Send a command to Tx to Read from another device
* Read commands when sent must to responded too by the destination (target) immediately * Read commands when sent must to responded too by the destination (target) immediately
* usually within a 10ms window * usually within a 10ms window
*/ */
void ems_doReadCommand(uint8_t type) { void ems_doReadCommand(uint8_t type) {
if (type == EMS_TYPE_NONE) if (type == EMS_TYPE_NONE)
return; // not a valid type, quit return; // not a valid type, quit
// check if there is already something in the queue if (_checkWriteQueueFull())
if (EMS_Sys_Status.emsTxStatus == EMS_TX_PENDING) { // send is already pending return; // check if there is already something in the queue
myDebug("Cannot write - already a telegram pending send.\n");
return;
}
uint8_t dest, size; uint8_t dest, size;
@@ -652,102 +676,109 @@ void ems_doReadCommand(uint8_t type) {
myDebug("Requesting type %s(0x%02x) from dest 0x%02x for %d bytes\n", EMS_Types[i].typeString, type, dest, size); myDebug("Requesting type %s(0x%02x) from dest 0x%02x for %d bytes\n", EMS_Types[i].typeString, type, dest, size);
} }
EMS_TxTelegram.action = EMS_TX_READ; // read command EMS_TxTelegram.action = EMS_TX_READ; // read command
EMS_TxTelegram.dest = dest | 0x80; // set 7th bit to indicate a read EMS_TxTelegram.dest = dest | 0x80; // set 7th bit to indicate a read
EMS_TxTelegram.offset = 0; // 0 for all data EMS_TxTelegram.offset = 0; // 0 for all data
EMS_TxTelegram.length = 6; // is always 6 bytes long (including CRC at end) EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; // is always 6 bytes long (including CRC at end)
EMS_TxTelegram.type = type; EMS_TxTelegram.type = type;
_buildTxTelegram(size); // we send the # bytes we want back _buildTxTelegram(size); // we send the # bytes we want back
} }
/* /*
* Set the temperature of the thermostat * Set the temperature of the thermostat
*/ */
void ems_setThermostatTemp(float temperature) { void ems_setThermostatTemp(float temperature) {
// check if there is already something in the queue if (_checkWriteQueueFull())
if (EMS_Sys_Status.emsTxStatus == EMS_TX_PENDING) { // send is already pending return; // check if there is already something in the queue
myDebug("Cannot write - already a telegram pending send.\n");
return;
}
char s[10]; char s[10];
myDebug("Setting thermostat temperature to %s C\n", _float_to_char(s, temperature)); myDebug("Setting thermostat temperature to %s C\n", _float_to_char(s, temperature));
EMS_TxTelegram.action = EMS_TX_WRITE; // write command EMS_TxTelegram.action = EMS_TX_WRITE;
EMS_TxTelegram.dest = EMS_ID_THERMOSTAT; EMS_TxTelegram.dest = EMS_ID_THERMOSTAT;
EMS_TxTelegram.type = EMS_TYPE_RC20Temperature; EMS_TxTelegram.type = EMS_TYPE_RC20Temperature;
EMS_TxTelegram.offset = 0x1C; // manual setpoint temperature EMS_TxTelegram.offset = EMS_OFFSET_RC20Temperature_temp;
EMS_TxTelegram.length = 6; // includes CRC EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
EMS_TxTelegram.checkValue = (uint8_t)((float)temperature * (float)2); // value to compare against. must be a single int EMS_TxTelegram.checkValue = (uint8_t)((float)temperature * (float)2); // value to compare against. must be a single int
// post call is RC20StatusMessage to fetch temps and send to HA // post call is back to EMS_TYPE_RC20Temperature to fetch temps and send to HA
EMS_TxTelegram.type_validate = EMS_TYPE_RC20Temperature; EMS_TxTelegram.type_validate = EMS_OFFSET_RC20Temperature_temp;
_buildTxTelegram(EMS_TxTelegram.checkValue); _buildTxTelegram(EMS_TxTelegram.checkValue);
} }
/* /*
* Set the warm water temperature * Set the thermostat working mode (0=low, 1=manual, 2=clock/auto)
*/ */
void ems_setThermostatMode(uint8_t mode) {
if (_checkWriteQueueFull())
return; // check if there is already something in the queue
myDebug("Setting thermostat mode to %d\n", mode);
EMS_TxTelegram.action = EMS_TX_WRITE;
EMS_TxTelegram.dest = EMS_ID_THERMOSTAT;
EMS_TxTelegram.type = EMS_TYPE_RC20Temperature;
EMS_TxTelegram.offset = EMS_OFFSET_RC20Temperature_mode;
EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
EMS_TxTelegram.checkValue = mode; // value to compare against. must be a single int
// post call is back to EMS_TYPE_RC20Temperature to fetch temps and send to HA
EMS_TxTelegram.type_validate = EMS_OFFSET_RC20Temperature_mode;
_buildTxTelegram(EMS_TxTelegram.checkValue);
}
/*
* Set the warm water temperature
*/
void ems_setWarmWaterTemp(uint8_t temperature) { void ems_setWarmWaterTemp(uint8_t temperature) {
// check if there is already something in the queue if (_checkWriteQueueFull())
if (EMS_Sys_Status.emsTxStatus == EMS_TX_PENDING) { // send is already pending return; // check if there is already something in the queue
myDebug("Cannot write - already a telegram pending send.\n");
return;
}
myDebug("Setting boiler warm water temperature to %d C\n", temperature); myDebug("Setting boiler warm water temperature to %d C\n", temperature);
EMS_TxTelegram.action = EMS_TX_WRITE; // write command EMS_TxTelegram.action = EMS_TX_WRITE;
EMS_TxTelegram.dest = EMS_ID_BOILER; EMS_TxTelegram.dest = EMS_ID_BOILER;
EMS_TxTelegram.type = EMS_TYPE_UBAParameterWW; EMS_TxTelegram.type = EMS_TYPE_UBAParameterWW;
EMS_TxTelegram.offset = 0x02; // Temperature EMS_TxTelegram.offset = EMS_OFFSET_UBAParameterWW_wwtemp;
EMS_TxTelegram.length = 6; // includes CRC EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
EMS_TxTelegram.checkValue = temperature; // value to compare against. must be a single int EMS_TxTelegram.checkValue = temperature; // value to compare against. must be a single int
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't force a send to check the value but do it during next broadcast
EMS_TxTelegram.type_validate = EMS_ID_NONE; // this means don't send and we'll pick up the data from the next broadcast
_buildTxTelegram(temperature); _buildTxTelegram(temperature);
} }
/* /*
* Activate / De-activate the Warm Water * Activate / De-activate the Warm Water
* true = on, false = off * true = on, false = off
*/ */
void ems_setWarmWaterActivated(bool activated) { void ems_setWarmWaterActivated(bool activated) {
// check if there is already something in the queue if (_checkWriteQueueFull())
if (EMS_Sys_Status.emsTxStatus == EMS_TX_PENDING) { // send is already pending return; // check if there is already something in the queue
myDebug("Cannot write - already a telegram pending send.\n");
return;
}
myDebug("Setting boiler warm water to %s\n", activated ? "on" : "off"); myDebug("Setting boiler warm water to %s\n", activated ? "on" : "off");
EMS_TxTelegram.action = EMS_TX_WRITE; // write command EMS_TxTelegram.action = EMS_TX_WRITE;
EMS_TxTelegram.dest = EMS_ID_BOILER; EMS_TxTelegram.dest = EMS_ID_BOILER;
EMS_TxTelegram.type = EMS_TYPE_UBAParameterWW; EMS_TxTelegram.type = EMS_TYPE_UBAParameterWW;
EMS_TxTelegram.offset = 0x01; // WW activation EMS_TxTelegram.offset = EMS_OFFSET_UBAParameterWW_wwactivated;
EMS_TxTelegram.length = 6; // includes CRC EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
EMS_TxTelegram.type_validate = EMS_ID_NONE; // this means don't send and we'll pick up the data from the next broadcast EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't force a send to check the value but do it during next broadcast
if (activated) { // 0xFF is on, 0x00 is off
EMS_TxTelegram.checkValue = 0xFF; // the EMS value for on EMS_TxTelegram.checkValue = (activated ? 0xFF : 0x00);
_buildTxTelegram(0xFF); _buildTxTelegram(EMS_TxTelegram.checkValue);
} else {
EMS_TxTelegram.checkValue = 0x00; // the EMS value for off
_buildTxTelegram(0x00);
}
} }
/* /*
* Helper functions for formatting and converting floats * Helper functions for formatting and converting floats
*/ */
// function to turn a telegram int (2 bytes) to a float // function to turn a telegram int (2 bytes) to a float
float _toFloat(uint8_t i, uint8_t * data) { float _toFloat(uint8_t i, uint8_t * data) {
if ((data[i] == 0x80) && (data[i + 1] == 0)) // 0x8000 is used when sensor is missing if ((data[i] == 0x80) && (data[i + 1] == 0)) // 0x8000 is used when sensor is missing
return (float)-1; return (float)-1; // return -1 to indicate that is unknown
return ((float)(((data[i] << 8) + data[i + 1]))) / 10; return ((float)(((data[i] << 8) + data[i + 1]))) / 10;
} }
@@ -762,7 +793,7 @@ char * _float_to_char(char * a, float f, uint8_t precision) {
long p[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; long p[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
char * ret = a; char * ret = a;
// check for 0x8000 (sensor missing) // check for 0x8000 (sensor missing), which has a -1 value
if (f == -1) { if (f == -1) {
strcpy(ret, "<n/a>"); strcpy(ret, "<n/a>");
} else { } else {

View File

@@ -17,6 +17,8 @@
// Special EMS Telegram Types // Special EMS Telegram Types
#define EMS_TYPE_NONE 0x00 // none #define EMS_TYPE_NONE 0x00 // none
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
#define EMS_TX_MAXBUFFERSIZE 128 // max size of the buffer. packets are 32 bits #define EMS_TX_MAXBUFFERSIZE 128 // max size of the buffer. packets are 32 bits
/* EMS UART transfer status */ /* EMS UART transfer status */
@@ -108,8 +110,9 @@ typedef struct {
// RC20 data // RC20 data
typedef struct { typedef struct {
float setpoint_roomTemp; float setpoint_roomTemp; // current set temp
float curr_roomTemp; float curr_roomTemp; // current room temp
uint8_t mode; // 0=low, 1=manual, 2=clock/auto
uint8_t hour; uint8_t hour;
uint8_t minute; uint8_t minute;
uint8_t second; uint8_t second;
@@ -135,6 +138,7 @@ extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_init(); void ems_init();
void ems_doReadCommand(uint8_t type); void ems_doReadCommand(uint8_t type);
void ems_setThermostatTemp(float temp); void ems_setThermostatTemp(float temp);
void ems_setThermostatMode(uint8_t mode);
void ems_setWarmWaterTemp(uint8_t temperature); void ems_setWarmWaterTemp(uint8_t temperature);
void ems_setWarmWaterActivated(bool activated); void ems_setWarmWaterActivated(bool activated);