added mqtt for hot water & heating on/off

This commit is contained in:
proddy
2018-12-08 16:32:21 +01:00
parent 357d2a56ad
commit 30e73cbd0c
20 changed files with 213 additions and 172 deletions

View File

@@ -23,7 +23,7 @@ There are 3 parts to this project, first the design of the circuit, second the c
- [EMS Reading and Writing](#ems-reading-and-writing)
- [The ESP8266 Source Code](#the-esp8266-source-code)
- [Supported EMS Types](#supported-ems-types)
- [Supporting A Type RC35 Thermostat](#supporting-a-type-rc35-thermostat)
- [Supporting other Thermostats types](#supporting-other-thermostats-types)
- [Customizing The Code](#customizing-the-code)
- [Using MQTT](#using-mqtt)
- [The Basic Shower Logic](#the-basic-shower-logic)
@@ -223,13 +223,13 @@ The code is built on the Arduino framework and is dependent on these external li
In `boiler.ino` you can make calls to automatically send these read commands. See the function *regularUpdates()*
#### Supporting A Type RC35 Thermostat
#### Supporting other Thermostats types
The code is designed for a Moduline300 (RC20) thermostat. To adjust for a RC35 first change `EMS_ID_THERMOSTAT` in `ems.h`. A RC35 thermostat has 4 heating circuits and to read the values use different Monitor type IDs (e.g. 0x3E, 0x48, etc). The mode (0=night, 1=day, 2=holiday) is the first byte of the telegram and the temperature is the value of the 2nd byte divided by 2.
The code is originally designed for a Moduline300 (RC20) thermostat.
Then to set temperature values use the Working Mode with type IDs (0x3D, 0x47,0x51 and 0x5B) respectively. Set the offset (byte 4 of the header) to determine which temperature you're changing; 1 for night, 2 for day and 3 for holiday. The data value is the desired temperature multiplied by 2 as a single byte.
To adjust for a RC35 first change `EMS_ID_THERMOSTAT` in `ems.cpp`. A RC35 thermostat has 4 heating circuits and to read the values use different Monitor type IDs (e.g. 0x3E, 0x48, etc). The mode (0=night, 1=day, 2=holiday) is the first byte of the telegram and the temperature is the value of the 2nd byte divided by 2. Then to set temperature values use the Working Mode with type IDs (0x3D, 0x47,0x51 and 0x5B) respectively. Set the offset (byte 4 of the header) to determine which temperature you're changing; 1 for night, 2 for day and 3 for holiday. The data value is the desired temperature multiplied by 2 as a single byte.
Consult the wiki documentation for the actual data format.
I will add further support for the other thermostats (such as the Nefit Easy) as soon as I can get my hands on a physical device. I do however welcome contribtions to this code repository which is essentially the purpose of GitHub. By inspecting the telegram packets and looking up the codes in the German wiki (and with lots of trial and error) it is possible to easily extend the existing functions to support other EMS devices.
### Customizing The Code
@@ -243,7 +243,7 @@ Most of the changes will be done in `boiler.ino` and `ems.cpp`.
When the ESP8266 boots it will send a start signal via MQTT. This is picked up by Home Assistant and sends a notification informing me that the device has booted. Useful for knowing when the ESP gets reset.
I'm using the standard PubSubClient client so make sure you set `-DMQTT_MAX_PACKET_SIZE=300` as the default package size is 128 and our JSON messages are around 220 bytes.
I'm using the standard PubSubClient client so make sure you set `-DMQTT_MAX_PACKET_SIZE=400` as the default package size is 128 and our JSON messages are around 300 bytes.
I run Mosquitto on my Raspberry PI 3 as the MQTT broker.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

View File

@@ -1,18 +0,0 @@
thermostat_temp:
name: Set Temperature
icon: mdi:temperature-celsius
min: 10
max: 25
step: 0.5
unit_of_measurement: "°C"
mode: slider
boiler_wwtemp:
name: Warm Water temp
icon: mdi:temperature-celsius
min: 30
max: 60
step: 1
unit_of_measurement: "°C"
mode: slider

View File

@@ -1,7 +1,3 @@
##########################################################
## Boiler
##########################################################
- id: boiler_shower
alias: Alert shower time
trigger:
@@ -43,31 +39,6 @@
payload: >
{{ now().strftime("%H:%M:%S %-d/%b/%Y") }}
# Set thermostat temp
- id: thermostat_temp
alias: 'Adjust Theromostat Temperature'
trigger:
platform: state
entity_id: input_number.thermostat_temp
action:
service: mqtt.publish
data_template:
topic: 'home/boiler/thermostat_cmd_temp'
payload: >
{{ states.input_number.thermostat_temp.state }}
# See if thermostat_temp has changed in recent mqtt payload, then adjust input_number
- id: thermostat_temp_incoming
trigger:
platform: state
entity_id: sensor.current_set_temperature
action:
service: input_number.set_value
data_template:
entity_id: input_number.thermostat_temp
value: '{{ states.sensor.current_set_temperature.state }}'
# Boiler warm water temp
- id: boiler_wwtemp
trigger:

View File

@@ -0,0 +1,12 @@
- platform: mqtt
name: 'Tap Water'
state_topic: 'home/boiler/tapwater_active'
payload_on: "1"
payload_off: "0"
- platform: mqtt
name: 'Heating'
state_topic: 'home/boiler/heating_active'
payload_on: "1"
payload_off: "0"

BIN
doc/home assistant/ha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,9 @@
boiler_wwtemp:
name: Warm Water temp
icon: mdi:temperature-celsius
min: 30
max: 60
step: 1
unit_of_measurement: "°C"
mode: slider

View File

@@ -26,6 +26,16 @@
name: 'Last shower duration'
force_update: true
- platform: mqtt
state_topic: 'home/boiler/boiler_data'
name: 'Tap Water'
value_template: '{{ value_json.tapwaterActive }}'
- platform: mqtt
state_topic: 'home/boiler/boiler_data'
name: 'Heating'
value_template: '{{ value_json.heatingActive }}'
- platform: mqtt
state_topic: 'home/boiler/boiler_data'
name: 'Warm Water selected temperature'

View File

@@ -51,13 +51,6 @@ views:
- type: vertical-stack
cards:
- type: entities
title: Thermostat
show_header_toggle: true
entities:
- sensor.current_room_temperature
- sensor.current_mode
- input_number.thermostat_temp
- type: history-graph
entities:
- sensor.current_room_temperature

View File

@@ -245,7 +245,7 @@ void showInfo() {
_renderBoolValue("Circulation pump", EMS_Boiler.wWCirc);
_renderIntValue("Burner selected max power", "%", EMS_Boiler.selBurnPow);
_renderIntValue("Burner current power", "%", EMS_Boiler.curBurnPow);
_renderFloatValue("Flame current", "uA", EMS_Boiler.flameCurr);
_renderFloatValue("Flame current", "mA", EMS_Boiler.flameCurr);
_renderFloatValue("System pressure", "bar", EMS_Boiler.sysPress);
// UBAMonitorSlow

View File

@@ -5,7 +5,7 @@ env_default = nodemcuv2
[common]
platform = espressif8266
; optional flags are -DUSE_LED -DSHOWER_TEST -DDEBUG -DUSE_SERIAL -DNO_TX
build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=300
build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=400
build_flags_custom = '-DWIFI_SSID="my_ssid"' '-DWIFI_PASSWORD="my_password"' '-DMQTT_IP="my_broker_ip"' '-DMQTT_USER="my_broker_username"' '-DMQTT_PASS="my_broker_password"'
lib_deps =
Time

View File

@@ -61,7 +61,10 @@ uint8_t regularUpdatesCount = 0;
// boiler
#define TOPIC_BOILER_DATA MQTT_BOILER "boiler_data" // for sending boiler values
#define TOPIC_BOILER_ MQTT_BOILER "boiler_wwtemp" // warm water selected temp
#define TOPIC_BOILER_WARM_WATER_SELECTED_TEMPERATURE MQTT_BOILER "boiler_wwtemp" // warm water selected temp
#define TOPIC_BOILER_TAPWATER_ACTIVE MQTT_BOILER "tapwater_active" // if hot tap water is running
#define TOPIC_BOILER_HEATING_ACTIVE MQTT_BOILER "heating_active" // if heating is on
// shower time
#define TOPIC_SHOWERTIME MQTT_BOILER "showertime" // for sending shower time results
@@ -95,7 +98,6 @@ const unsigned long SHOWER_COLDSHOT_DURATION = 5; // in seconds! how long for co
const unsigned long SHOWER_OFFSET_TIME = 0; // 0 seconds grace time, to calibrate actual time under the shower
#endif
const uint8_t SHOWER_BURNPOWER_MIN = 80;
typedef struct {
bool wifi_connected;
bool boiler_online;
@@ -106,7 +108,6 @@ typedef struct {
typedef struct {
bool showerOn;
bool hotWaterOn;
unsigned long timerStart; // ms
unsigned long timerPause; // ms
unsigned long duration; // ms
@@ -124,18 +125,18 @@ netInfo homeNet = {.mqttHost = MQTT_IP,
ESPHelper myESP(&homeNet);
command_t PROGMEM project_cmds[] = {{"s", "show statistics"},
{"h", "list supported EMS telegram type ids"},
{"h", "list EMS telegram type ids with supported logic"},
{"P", "publish all stat to MQTT"},
{"v", "[n] set logging (0=none, 1=basic, 2=verbose)"},
{"p", "poll ems response on/off (default is off)"},
{"T", "thermostat monitoring on/off"},
{"S", "shower timer on/off"},
{"A", "shower alert on/off"},
{"r", "[n] send EMS request (n=telegram type id. Use 'h' for list)"},
{"t", "[n] set thermostat temperature to n"},
{"p", "toggle EMS Poll response on/off"},
{"T", "toggle Thermostat monitoring on/off"},
{"S", "toggle Shower timer on/off"},
{"A", "toggle shower Alert on/off"},
{"r", "[n] send EMS request (n=any telegram type id. Use 'h' for suppported types)"},
{"t", "[n] set thermostat temperature"},
{"m", "[n] set thermostat mode (1=manual, 2=auto)"},
{"w", "[n] set boiler warm water temperature to n (min 30)"},
{"a", "[n] boiler warm water on (n=1) or off (n=0)"},
{"w", "[n] set boiler warm water temperature (min 30)"},
{"a", "[n] boiler warm water (1=on, 2=off)"},
{"x", "[n] experimental (warning: for debugging only!)"}};
// calculates size of an 2d array at compile time
@@ -163,6 +164,8 @@ unsigned long timestamp; // for internal timings, via millis()
static int connectionStatus = NO_CONNECTION;
bool startMQTTsent = false;
uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
// toggle for heartbeat LED
bool heartbeatEnabled;
@@ -322,6 +325,10 @@ void showInfo() {
myDebug("\n\n%sBoiler stats:%s\n", COLOR_BOLD_ON, COLOR_BOLD_OFF);
// active stats
myDebug(" Hot tap water is %s\n", (EMS_Boiler.tapwaterActive ? "running" : "off"));
myDebug(" Central Heating is %s\n", (EMS_Boiler.heatingActive ? "active" : "off"));
// UBAParameterWW
_renderBoolValue("Warm Water activated", EMS_Boiler.wWActivated);
_renderBoolValue("Warm Water circulation pump available", EMS_Boiler.wWCircPump);
@@ -348,7 +355,7 @@ void showInfo() {
_renderBoolValue("Circulation pump", EMS_Boiler.wWCirc);
_renderIntValue("Burner selected max power", "%", EMS_Boiler.selBurnPow);
_renderIntValue("Burner current power", "%", EMS_Boiler.curBurnPow);
_renderFloatValue("Flame current", "uA", EMS_Boiler.flameCurr);
_renderFloatValue("Flame current", "mA", EMS_Boiler.flameCurr);
_renderFloatValue("System pressure", "bar", EMS_Boiler.sysPress);
// UBAMonitorSlow
@@ -393,14 +400,15 @@ void showInfo() {
// show the Shower Info
if (Boiler_Status.shower_timer) {
myDebug("\n%s Shower stats:%s\n", COLOR_BOLD_ON, COLOR_BOLD_OFF);
myDebug(" Hot water is %s\n", (Boiler_Shower.hotWaterOn ? "running" : "stopped"));
myDebug(" Shower is %s\n", (Boiler_Shower.showerOn ? "on" : "off"));
myDebug(" Shower Timer is %s\n", (Boiler_Shower.showerOn ? "active" : "off"));
}
myDebug("\n");
}
// send values to HA via MQTT
// a json object is created for the boiler and one for the thermostat
// CRC check is done to see if there are changes in the values since the last send to avoid too much wifi traffic
void publishValues(bool force) {
char s[20]; // for formatting strings
@@ -435,7 +443,7 @@ void publishValues(bool force) {
crc.update(data[i]);
}
uint32_t checksum = crc.finalize();
//myDebug("HASH=%d %08x, len=%d, s=%s\n", checksum, checksum, len, data);
//myDebug("Boiler HASH=%d %08x, len=%d, s=%s\n", checksum, checksum, len, data);
if ((previousBoilerPublishCRC != checksum) || force) {
previousBoilerPublishCRC = checksum;
@@ -447,6 +455,19 @@ void publishValues(bool force) {
myESP.publish(TOPIC_BOILER_DATA, data);
}
// see if the heating or hot tap water has changed, if so send
// last_boilerActive stores heating in bit 1 and tap water in bit 2
if (last_boilerActive != ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive)) {
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
myDebug("Publishing hot water and heating state via MQTT\n");
}
myESP.publish(TOPIC_BOILER_TAPWATER_ACTIVE, EMS_Boiler.tapwaterActive == 1 ? "1" : "0");
myESP.publish(TOPIC_BOILER_HEATING_ACTIVE, EMS_Boiler.heatingActive == 1 ? "1" : "0");
last_boilerActive = ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive); // remember last state
}
// handle the thermostat values separately
if (EMS_Sys_Status.emsThermostatEnabled) {
// only send thermostat values if we actually have them
@@ -476,6 +497,7 @@ void publishValues(bool force) {
crc.update(data[i]);
}
uint32_t checksum = crc.finalize();
//myDebug("Thermostat HASH=%d %08x, len=%d, s=%s\n", checksum, checksum, len, data);
if ((previousThermostatPublishCRC != checksum) || force) {
previousThermostatPublishCRC = checksum;
@@ -517,7 +539,7 @@ void myDebugCallback() {
ems_setPoll(b);
break;
case 'P':
myESP.logger(LOG_HA, "Force publish values");
//myESP.logger(LOG_HA, "Force publish values");
publishValues(true);
break;
case 'r': // read command for Boiler or Thermostat
@@ -751,6 +773,8 @@ void setup() {
myESP.addSubscription(TOPIC_SHOWER_TIMER);
myESP.addSubscription(TOPIC_SHOWER_ALERT);
myESP.addSubscription(TOPIC_BOILER_WARM_WATER_SELECTED_TEMPERATURE);
myESP.addSubscription(TOPIC_BOILER_TAPWATER_ACTIVE);
myESP.addSubscription(TOPIC_BOILER_HEATING_ACTIVE);
myESP.addSubscription(TOPIC_SHOWER_COLDSHOT);
myESP.consoleSetCallBackProjectCmds(project_cmds, ArraySize(project_cmds), myDebugCallback); // set up Telnet commands
@@ -812,11 +836,8 @@ void regularUpdates() {
// only do calls if the EMS is connected and alive
if (Boiler_Status.boiler_online) {
if ((cycle == 0) && Boiler_Status.thermostat_enabled) {
// force get the thermostat mode which is not broadcasted
// important! this is only configured for the RC20. Look up the correct telegram type for other thermostat versions
if (EMS_ID_THERMOSTAT == 0x17) { // RC20 is type 0x17
ems_doReadCommand(EMS_TYPE_RC20Temperature);
}
// force get the thermostat data which are not usually automatically broadcasted
ems_getThermostatTemps();
} else if (cycle == 1) {
ems_doReadCommand(EMS_TYPE_UBAParameterWW); // get Warm Water values
}
@@ -880,10 +901,11 @@ void loop() {
#endif
}
// publish the values to MQTT (only if there are changes)
// if we received new data and flagged for pushing, do it
if (EMS_Sys_Status.emsRefreshed) {
EMS_Sys_Status.emsRefreshed = false;
publishValues(true);
publishValues(false);
}
/*
@@ -892,13 +914,8 @@ void loop() {
if (Boiler_Status.shower_timer) {
// if already in cold mode, ignore all this logic until we're out of the cold blast
if (!Boiler_Shower.doingColdShot) {
// these values come from UBAMonitorFast - type 0x18) which is broadcasted every second so our timings are accurate enough
// and no need to fetch the values from the boiler
Boiler_Shower.hotWaterOn =
((EMS_Boiler.selBurnPow >= SHOWER_BURNPOWER_MIN) && (EMS_Boiler.selFlowTemp == 0) && EMS_Boiler.burnGas);
// is the hot water running?
if (Boiler_Shower.hotWaterOn) {
if (EMS_Boiler.tapwaterActive) {
// if heater was previously off, start the timer
if (Boiler_Shower.timerStart == 0) {
// hot water just started...

View File

@@ -10,6 +10,16 @@
#include <Arduino.h>
#include <TimeLib.h>
// add you custom setings here like thermostat IDs and thresholds
const uint8_t SHOWER_BURNPOWER_MIN = 80;
// define here the Thermostat type
#define EMS_ID_THERMOSTAT EMS_ID_THERMOSTAT_RC20 // your thermostat ID
// define here the boiler power settings (selBurnPow) when hot tap water is running and the heating is on
#define EMS_BOILER_BURNPOWER_TAPWATER 115
#define EMS_BOILER_BURNPOWER_HEATING 75
// Check for ESPurna vs ESPHelper (standalone)
#ifdef USE_CUSTOM_H
#include "debug.h"
@@ -41,8 +51,9 @@ bool _process_RC20Temperature(uint8_t * data, uint8_t length);
bool _process_RCTempMessage(uint8_t * data, uint8_t length);
bool _process_Version(uint8_t * data, uint8_t length);
const _EMS_Types EMS_Types[] =
{{EMS_ID_BOILER, EMS_TYPE_UBAMonitorFast, "UBAMonitorFast", _process_UBAMonitorFast},
const _EMS_Types EMS_Types[] = {
{EMS_ID_BOILER, EMS_TYPE_UBAMonitorFast, "UBAMonitorFast", _process_UBAMonitorFast},
{EMS_ID_BOILER, EMS_TYPE_UBAMonitorSlow, "UBAMonitorSlow", _process_UBAMonitorSlow},
{EMS_ID_BOILER, EMS_TYPE_UBAMonitorWWMessage, "UBAMonitorWWMessage", _process_UBAMonitorWWMessage},
{EMS_ID_BOILER, EMS_TYPE_UBAParameterWW, "UBAParameterWW", _process_UBAParameterWW},
@@ -55,7 +66,9 @@ const _EMS_Types EMS_Types[] =
{EMS_ID_THERMOSTAT, EMS_TYPE_RC20Time, "RC20Time", _process_RC20Time},
{EMS_ID_THERMOSTAT, EMS_TYPE_RC20Temperature, "RC20Temperature", _process_RC20Temperature},
{EMS_ID_THERMOSTAT, EMS_TYPE_RCTempMessage, "RCTempMessage", _process_RCTempMessage},
{EMS_ID_THERMOSTAT, EMS_TYPE_Version, "Version", _process_Version}};
{EMS_ID_THERMOSTAT, EMS_TYPE_Version, "Version", _process_Version}
};
uint8_t _EMS_Types_max = ArraySize(EMS_Types); // number of defined types
// reserve space for the data we collect from the Boiler and Thermostat
@@ -98,11 +111,12 @@ void ems_init() {
EMS_Sys_Status.emsLastRx = 0;
EMS_Sys_Status.emsLastTx = 0;
EMS_Sys_Status.emsRefreshed = false;
EMS_Sys_Status.emsPollEnabled = false; // start up with Poll disabled
EMS_Sys_Status.emsThermostatEnabled = true; // there is a RCxx thermostat active as default
EMS_Sys_Status.emsLogging = EMS_SYS_LOGGING_NONE; // Verbose logging is off
// thermostat
EMS_Thermostat.type = EMS_ID_THERMOSTAT; // type, see ems.h
EMS_Thermostat.hour = 0;
EMS_Thermostat.minute = 0;
EMS_Thermostat.second = 0;
@@ -146,6 +160,9 @@ void ems_init() {
EMS_Boiler.wWWorkM = EMS_VALUE_INT_NOTSET; // Warm Water # minutes
EMS_Boiler.wWOneTime = EMS_VALUE_INT_NOTSET; // Warm Water one time function on/off
EMS_Boiler.tapwaterActive = EMS_VALUE_INT_NOTSET; // Hot tap water is on/off
EMS_Boiler.heatingActive = EMS_VALUE_INT_NOTSET; // Central heating is on/off
// init the Tx package
_initTxBuffer();
}
@@ -316,13 +333,18 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
&& ((millis() - EMS_Sys_Status.emsLastTx) > RX_READ_TIMEOUT)) {
if (emsLastRxCount++ >= RX_READ_TIMEOUT_COUNT) {
// give up and reset tx
if (EMS_Sys_Status.emsLogging != EMS_SYS_LOGGING_NONE) {
myDebug("Error! Failed to send telegram, cancelling last write command.\n");
}
// re-initialise
_initTxBuffer();
} else {
if (EMS_Sys_Status.emsLogging != EMS_SYS_LOGGING_NONE) {
myDebug("Didn't receive acknowledgement from the 0x%02x, so resending (attempt #%d/%d)...\n",
EMS_TxTelegram.type,
emsLastRxCount,
RX_READ_TIMEOUT_COUNT);
}
EMS_Sys_Status.emsTxStatus = EMS_TX_PENDING; // set to pending will trigger sending the same package again
}
}
@@ -415,10 +437,11 @@ void _processType(uint8_t * telegram, uint8_t length) {
// we have a match
typeFound = true;
// call callback to fetch the values from the telegram
// ignoring the return value for now
// return value tells us if we need to force send values back to MQTT
if ((EMS_Types[i].processType_cb) != (void *)NULL) {
(void)EMS_Types[i].processType_cb(data, length);
EMS_Sys_Status.emsRefreshed = EMS_Types[i].processType_cb(data, length);
}
break;
}
i++;
@@ -530,7 +553,7 @@ bool _checkWriteQueueFull() {
myDebug("Delaying write command as there is already a telegram (type 0x%02x) in the queue\n",
EMS_TxTelegram.type);
}
return true;
return true; // something in queue
}
return false; // nothing queue, we can do a write command
@@ -538,7 +561,7 @@ bool _checkWriteQueueFull() {
/*
* UBAParameterWW - type 0x33 - warm water parameters
* received only after requested
* received only after requested (not broadcasted)
*/
bool _process_UBAParameterWW(uint8_t * data, uint8_t length) {
EMS_Boiler.wWSelTemp = data[2];
@@ -546,9 +569,7 @@ bool _process_UBAParameterWW(uint8_t * data, uint8_t length) {
EMS_Boiler.wWCircPump = (data[6] == 0xFF); // 0xFF means on
EMS_Boiler.wWDesiredTemp = data[8];
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
return true;
return true; // triggers a send the values back to Home Assistant via MQTT
}
/*
@@ -561,7 +582,7 @@ bool _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length) {
EMS_Boiler.wWWorkM = _toLong(10, data);
EMS_Boiler.wWOneTime = bitRead(data[5], 1);
return true;
return false; // no need to update mqtt
}
/*
@@ -580,9 +601,16 @@ bool _process_UBAMonitorFast(uint8_t * data, uint8_t length) {
EMS_Boiler.heatPmp = bitRead(v, 5);
EMS_Boiler.wWHeat = bitRead(v, 6);
EMS_Boiler.wWCirc = bitRead(v, 7);
EMS_Boiler.selBurnPow = data[3]; // max power
EMS_Boiler.curBurnPow = data[4];
EMS_Boiler.selBurnPow = data[3]; // burn power max setting
// check if the boiler is providing hot water to the tap or hot water to the central heating
// we use a quick hack:
// the heating on, if burner selected max power = 75 (UBAMonitorFast:EMS_Boiler.selBurnPow)
// hot tap water running, if burner selected max power=115 (UBAMonitorFast:EMS_Boiler.selBurnPow)
// we could also add (EMS_Boiler.selFlowTemp == 0) && EMS_Boiler.burnGas) for more precision
EMS_Boiler.tapwaterActive = ((EMS_Boiler.selBurnPow == EMS_BOILER_BURNPOWER_TAPWATER) ? 1 : 0);
EMS_Boiler.heatingActive = ((EMS_Boiler.selBurnPow == EMS_BOILER_BURNPOWER_HEATING) ? 1 : 0);
EMS_Boiler.flameCurr = _toFloat(15, data);
@@ -592,7 +620,7 @@ bool _process_UBAMonitorFast(uint8_t * data, uint8_t length) {
EMS_Boiler.sysPress = (((float)data[17]) / (float)10);
}
return true;
return false; // no need to update mqtt
}
/*
@@ -607,9 +635,7 @@ bool _process_UBAMonitorSlow(uint8_t * data, uint8_t length) {
EMS_Boiler.burnWorkMin = _toLong(13, data);
EMS_Boiler.heatWorkMin = _toLong(19, data);
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
return true;
return true; // triggers a send the values back to Home Assistant via MQTT
}
/*
@@ -620,9 +646,7 @@ bool _process_RC20StatusMessage(uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[1]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(2, data);
EMS_Sys_Status.emsRefreshed = true; // set the updated flag to trigger a send back to HA
return true;
return true; // triggers a send the values back to Home Assistant via MQTT
}
/*
@@ -642,13 +666,12 @@ bool _process_RC20Temperature(uint8_t * data, uint8_t length) {
}
// 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;
// Process the whole telegram package
EMS_Thermostat.mode = data[EMS_OFFSET_RC20Temperature_mode]; // get the mode
return false; // don't update mqtt
}
/*
@@ -657,21 +680,24 @@ bool _process_RC20Temperature(uint8_t * data, uint8_t length) {
bool _process_RCTempMessage(uint8_t * data, uint8_t length) {
// add support here if you're reading external sensors
return true;
return false; // don't update mqtt
}
/*
* Version - type 0x02 - get the version of the Thermostat firmware
* We don't bother storing these values anywhere
* When a thermostat is connecting it will send out 0x02 messages too, which we'll ignore
* We don't bother storing these values anywhere, just print
*/
bool _process_Version(uint8_t * data, uint8_t length) {
// ignore short messages
if (length == 8) {
uint8_t major = data[1];
uint8_t minor = data[2];
myDebug("Version %d.%d\n", major, minor);
}
return true;
return false; // don't update mqtt
}
/*
@@ -696,7 +722,7 @@ bool _process_RC20Time(uint8_t * data, uint8_t length) {
EMS_Thermostat.year + 2000);
*/
return true;
return false; // don't update mqtt
}
/*
@@ -720,6 +746,15 @@ void _buildTxTelegram(uint8_t data_value) {
EMS_Sys_Status.emsTxStatus = EMS_TX_PENDING; // armed and ready to send
}
/*
* Generic function to return temperature settings from the thermostat
*/
void ems_getThermostatTemps() {
if (EMS_Thermostat.type == EMS_ID_THERMOSTAT_RC20) {
ems_doReadCommand(EMS_TYPE_RC20Temperature);
}
}
/*
* Send a command to UART Tx to Read from another device
* Read commands when sent must respond by the destination (target) immediately (or within 10ms)

View File

@@ -21,8 +21,10 @@
#define EMS_TX_MAXBUFFERSIZE 128 // max size of the buffer. packets are 32 bits
// define here the Thermostat type
#define EMS_ID_THERMOSTAT 0x17 // x17=RC20 (Moduline300), x10=RC30/RC35 (Moduline 400)
#define EMS_ID_THERMOSTAT_RC20 0x17 // RC20 (older Moduline 300)
#define EMS_ID_THERMOSTAT_RC30 0x10 // RC30 (Moduline 300)
#define EMS_ID_THERMOSTAT_RC35 0x10 // RC35 (Moduline 400)
#define EMS_ID_THERMOSTAT_EASY 0x18 // Nefit Easy
// define here the EMS telegram types you need
@@ -143,10 +145,16 @@ typedef struct {
uint32_t wWStarts; // Warm Water # starts
uint32_t wWWorkM; // Warm Water # minutes
uint8_t wWOneTime; // Warm Water one time function on/off
// calculated values
uint8_t tapwaterActive; // Hot tap water is on/off
uint8_t heatingActive; // Central heating is on/off
} _EMS_Boiler;
// RC20 data
// Thermostat data
typedef struct {
uint8_t type; // thermostat type (RC20, RC30, RC35 etc)
float setpoint_roomTemp; // current set temp
float curr_roomTemp; // current room temp
uint8_t mode; // 0=low, 1=manual, 2=auto
@@ -186,18 +194,22 @@ typedef struct {
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_init();
void ems_doReadCommand(uint8_t type);
void ems_setThermostatTemp(float temp);
void ems_setThermostatMode(uint8_t mode);
void ems_setWarmWaterTemp(uint8_t temperature);
void ems_setWarmWaterActivated(bool activated);
void ems_setExperimental(uint8_t value);
void ems_setPoll(bool b);
bool ems_getPoll();
bool ems_getThermostatEnabled();
void ems_setThermostatEnabled(bool b);
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
void ems_getThermostatTemps();
bool ems_getPoll();
bool ems_getThermostatEnabled();
_EMS_SYS_LOGGING ems_getLogging();
uint8_t ems_getEmsTypesCount();
void ems_printAllTypes();
// private functions