mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
added boiler stats to MQTT
This commit is contained in:
64
README.md
64
README.md
@@ -33,7 +33,7 @@ EMS-ESP-Boiler is an implementation of the EMS bridge protocol (Energy Managemen
|
||||
|
||||
## Introduction
|
||||
|
||||
I originally started this project with the intention to build my own smart thermostat for my Nefit Trendline boiler and control it via Home Assistant. When I started deciphering the boiler EMS codes I began adding new features such as timing how long the shower was running for and triggering an alarm (actually a shot of cold water!) after a certain duration. This to the delight of my two teenage daughters :-)
|
||||
I originally started this project with the intention to build my own smart thermostat for my Nefit Trendline boiler and control it via [Home Assistant](https://www.home-assistant.io/). When I started deciphering the boiler EMS codes I began adding new features such as timing how long the shower was running for and triggering an alarm (actually a shot of cold water!) after a certain duration. This to the delight of my two teenage daughters :-)
|
||||
|
||||
## Which Boilers are supported?
|
||||
|
||||
@@ -45,7 +45,7 @@ First, a big thanks and appreciation to the following people and their projects
|
||||
|
||||
**bbqkees** - Kees built a circuit and sample Arduino code to read from the EMS and push to Domoticz. His SMD circuit is available for purchase. Check it out at https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz
|
||||
|
||||
**susisstrolch** - Probably the first working version of the EMS bridge circuit designed for the ESP8266. I borrowed his schematic and code logic. https://github.com/susisstrolch/EMS-ESP12
|
||||
**susisstrolch** - Probably the first working version of the EMS bridge circuit designed for the ESP8266. I borrowed Juergen's schematic and code logic. https://github.com/susisstrolch/EMS-ESP12
|
||||
|
||||
**EMS Wiki** - A reference for decoding the EMS telegrams (which I found not always 100% accurate). https://emswiki.thefischer.net/doku.php?id=wiki:ems:telegramme. Use Google Translate if you can't read German.
|
||||
|
||||
@@ -57,7 +57,7 @@ I've tested the code and circuit with a Wemos D1 Mini, Wemos D1 Mini Pro, Nodemc
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Build the circuit (or purchase a ready built one from Kees via his [GitHub](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz) page or the [Domoticz forum](http://www.domoticz.com/forum/viewtopic.php?f=22&t=22079&start=20)).
|
||||
1. Build the circuit (or purchase a ready built one from bbqkees via his [GitHub](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz) page or the [Domoticz forum](http://www.domoticz.com/forum/viewtopic.php?f=22&t=22079&start=20)).
|
||||
2. Connect the EMS to the circuit and the RX/TX to the ESP8266 on pins D7 and D8. The EMS connection can either be the 12-15V AC direct from the EMS (split from the Thermostat if you have one) or from the Service Jack at the front. Again bbqkees has a nice explanation [here](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz/tree/master/Documentation).
|
||||
3. Optionally connect the three LEDs to show RX and TX traffic and Error codes to pins D1, D2, D3 respectively. I use 220 Ohm pull-down resistors. The pins are configurable in ``boiler.ino``. See the explanation below in the **code** section.
|
||||
3. Build and upload the firmware to an ESP8266 device. Make sure you set the MQTT and WiFi credentials. If you're not using MQTT leave the MQTT_IP blank. The firmware supports OTA too and the default hostname is 'boiler' or 'boiler.' depending on the mdns resolve.
|
||||
@@ -95,7 +95,7 @@ You can issue commands directly to the bus using 'r' and some other examples I p
|
||||
|
||||
## Building the Circuit
|
||||
|
||||
The EMS circuit is really all credit to the hard work many people have done before me, noticeably Juergen with his ESP8266 [version](https://github.com/susisstrolch/EMS-ESP8266_12-PCB/tree/newmaster/Schematics/EMS-ESP8266-12).
|
||||
The EMS circuit is really all credit to the hard work many people have done before me, noticeably *susisstrolch* with his ESP8266 [version](https://github.com/susisstrolch/EMS-ESP8266_12-PCB/tree/newmaster/Schematics/EMS-ESP8266-12).
|
||||
|
||||
I've included a prototype boards you can build yourself on a breadboard. One for only Reading values from the Boiler and a second with the inclusion of the Write logic to send commands.
|
||||
|
||||
@@ -118,7 +118,7 @@ Here's a pretty rough example circuit using a NodeMcu2 with the additional LEDs
|
||||
|
||||

|
||||
|
||||
The nicest solution ultimately is to purchase a ready made circuit from [Kees](http://www.domoticz.com/forum/memberlist.php?mode=viewprofile&u=1736). Here's an example of one working with a Wemos D1 Mini:
|
||||
The nicest solution ultimately is to purchase a ready made circuit from [bbqkees](http://www.domoticz.com/forum/memberlist.php?mode=viewprofile&u=1736). Here's an example of one working with a Wemos D1 Mini:
|
||||
|
||||

|
||||
|
||||
@@ -153,7 +153,7 @@ Each device has a unique ID.
|
||||
|
||||
The Boiler (MC10) has an ID of 0x08 and also referred to as the Bus Master.
|
||||
|
||||
My thermostat, which is a Moduline 300 uses the RC20 format and has an ID 0x17. If you're using an RC30 or RC35 type thermostat use 0x10 and make adjustments in the code as appropriate. Kees did a nice write-up on his github page [here](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz/blob/master/README.md).
|
||||
My thermostat, which is a Moduline 300 uses the RC20 format and has an ID 0x17. If you're using an RC30 or RC35 type thermostat use 0x10 and make adjustments in the code as appropriate. bbqkees did a nice write-up on his github page [here](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz/blob/master/README.md).
|
||||
|
||||
Our circuit acts as a service device and uses a special reserved ID of 0x0B (called a service key).
|
||||
|
||||
@@ -228,6 +228,8 @@ When doing a write request, the 7th bit is masked in the ``[dest]``. After a wri
|
||||
|
||||
When the ESP8266 boots it will send a start signal via MQTT. This is picked up by Home Assistant it sends me a notification informing me that the device has booted. Useful for knowing when the ESP gets reset - it can happen.
|
||||
|
||||
I'm using the standard PubSubClient client so make sure you set -DMQTT_MAX_PACKET_SIZE=512 as the default package size is 128 and our JSON messages are around 220 bytes.
|
||||
|
||||
I run Mosquitto on my Raspberry PI 3.
|
||||
|
||||
The temperature values of the thermostat are sent as a JSON object using
|
||||
@@ -251,16 +253,14 @@ Assuming you've setup up MQTT as I did, this is what my HA configuration looks l
|
||||
**sensors.yaml**
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/thermostat'
|
||||
state_topic: 'home/boiler/thermostat_currtemp'
|
||||
name: 'Boiler Thermostat Current Temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: '{{ value_json.currtemp }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/thermostat'
|
||||
state_topic: 'home/boiler/thermostat_seltemp'
|
||||
name: 'Boiler Thermostat Set Temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: '{{ value_json.seltemp }}'
|
||||
|
||||
- platform: template
|
||||
sensors:
|
||||
@@ -277,6 +277,12 @@ Assuming you've setup up MQTT as I did, this is what my HA configuration looks l
|
||||
showertime_time:
|
||||
value_template: '{{ as_timestamp(states.sensor.last_shower_duration.last_updated) | int | timestamp_custom("%-I:%M %P on %a %-d %b") }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
name: 'Warm Water current temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: '{{ value_json.wWCurTmp }}'
|
||||
|
||||
**automations.yaml**
|
||||
|
||||
- id: thermostat_temp
|
||||
@@ -319,33 +325,33 @@ Assuming you've setup up MQTT as I did, this is what my HA configuration looks l
|
||||
|
||||
**groups.yaml**
|
||||
|
||||
boiler:
|
||||
name: Boiler
|
||||
view: no
|
||||
entities:
|
||||
- sensor.boiler_boottime
|
||||
- sensor.warm_water_current_temperature
|
||||
|
||||
shower:
|
||||
name: Shower
|
||||
view: no
|
||||
entities:
|
||||
- sensor.last_shower_duration
|
||||
- sensor.showertime_time
|
||||
|
||||
thermostat:
|
||||
name: Thermostat
|
||||
view: no
|
||||
entities:
|
||||
- sensor.boiler_thermostat_current_temperature
|
||||
- sensor.boiler_thermostat_set_temperature
|
||||
- sensor.thermostat_current_temperature
|
||||
- sensor.thermostat_set_temperature
|
||||
- input_number.thermostat_temp
|
||||
|
||||
**customize.yaml**
|
||||
|
||||
sensor.boiler_boottime:
|
||||
friendly_name: Boot time
|
||||
icon: mdi:clock-start
|
||||
|
||||
sensor.showertime_time:
|
||||
friendly_name: 'Last shower'
|
||||
icon: mdi:timelapse
|
||||
|
||||
sensor.boiler_thermostat_current_temperature:
|
||||
friendly_name: 'Current room temperature'
|
||||
|
||||
sensor.boiler_thermostat_set_temperature:
|
||||
friendly_name: 'Set thermostat temperature'
|
||||
|
||||
And in Home Assistant looks like:
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
# Building the Firmware
|
||||
|
||||
BIN
doc/ha/ha.JPG
BIN
doc/ha/ha.JPG
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB |
BIN
doc/ha/ha.png
Normal file
BIN
doc/ha/ha.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
BIN
doc/ha/ha_notify.jpg
Normal file
BIN
doc/ha/ha_notify.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
@@ -51,9 +51,13 @@ void _showerColdShotStop();
|
||||
#define MQTT_BOILER MQTT_BASE HOSTNAME "/"
|
||||
#define TOPIC_START MQTT_BOILER MQTT_TOPIC_START
|
||||
|
||||
#define TOPIC_THERMOSTAT MQTT_BOILER "thermostat"
|
||||
#define TOPIC_SHOWERTIME MQTT_BOILER "showertime"
|
||||
#define TOPIC_THERMOSTAT_TEMP MQTT_BOILER "thermostat_temp"
|
||||
#define TOPIC_THERMOSTAT_TEMP MQTT_BOILER "thermostat_temp" // for received thermostat temp changes
|
||||
#define TOPIC_THERMOSTAT_CURRTEMP MQTT_BOILER "thermostat_currtemp" // current temperature
|
||||
#define TOPIC_THERMOSTAT_SELTEMP MQTT_BOILER "thermostat_seltemp" // selected temperature
|
||||
|
||||
#define TOPIC_BOILER_DATA MQTT_BOILER "boiler_data" // for sending boiler values
|
||||
#define TOPIC_SHOWERTIME MQTT_BOILER "showertime" // for sending shower time results
|
||||
|
||||
|
||||
// all on
|
||||
#define BOILER_THERMOSTAT_ENABLED 1
|
||||
@@ -190,11 +194,11 @@ void showInfo() {
|
||||
myDebug(" Current flow temperature: %s C\n", _float_to_char(s, EMS_Boiler.curFlowTemp));
|
||||
myDebug(" Return temperature: %s C\n", _float_to_char(s, EMS_Boiler.retTemp));
|
||||
|
||||
myDebug(" Gas: %s\n", EMS_Boiler.burnGas ? "on" : "off"); // 0 -gas on
|
||||
myDebug(" Circulating pump: %s\n", EMS_Boiler.heatPmp ? "on" : "off"); // 5 - boiler circuit pump on
|
||||
myDebug(" Fan: %s\n", EMS_Boiler.fanWork ? "on" : "off"); // 2
|
||||
myDebug(" Ignition: %s\n", EMS_Boiler.ignWork ? "on" : "off"); // 3
|
||||
myDebug(" Circulation pump: %s\n", EMS_Boiler.wWCirc ? "on" : "off"); // 7
|
||||
myDebug(" Gas: %s\n", EMS_Boiler.burnGas ? "on" : "off");
|
||||
myDebug(" Circulating pump: %s\n", EMS_Boiler.heatPmp ? "on" : "off");
|
||||
myDebug(" Fan: %s\n", EMS_Boiler.fanWork ? "on" : "off");
|
||||
myDebug(" Ignition: %s\n", EMS_Boiler.ignWork ? "on" : "off");
|
||||
myDebug(" Circulation pump: %s\n", EMS_Boiler.wWCirc ? "on" : "off");
|
||||
myDebug(" Burner max power: %d %%\n", EMS_Boiler.selBurnPow);
|
||||
myDebug(" Burner current power: %d %%\n", EMS_Boiler.curBurnPow);
|
||||
myDebug(" Flame current: %s uA\n", _float_to_char(s, EMS_Boiler.flameCurr));
|
||||
@@ -244,22 +248,40 @@ void showInfo() {
|
||||
|
||||
// send values to HA via MQTT
|
||||
void publishValues() {
|
||||
// only send values if we actually have them
|
||||
if (((int)EMS_Thermostat.curr_roomTemp == (int)0) || ((int)EMS_Thermostat.setpoint_roomTemp == (int)0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
myDebug("Publishing data to MQTT topics\n");
|
||||
|
||||
// build a JSON with the current temp and selected temp from the Thermostat
|
||||
StaticJsonBuffer<200> jsonBuffer;
|
||||
// Boiler values as one JSON object
|
||||
StaticJsonBuffer<512> jsonBuffer;
|
||||
char data[512];
|
||||
char s[20];
|
||||
JsonObject & root = jsonBuffer.createObject();
|
||||
root["currtemp"] = (String)EMS_Thermostat.curr_roomTemp;
|
||||
root["seltemp"] = (String)EMS_Thermostat.setpoint_roomTemp;
|
||||
root["wWCurTmp"] = _float_to_char(s, EMS_Boiler.wWCurTmp);
|
||||
root["wWHeat"] = EMS_Boiler.wWHeat ? "on" : "off";
|
||||
root["curFlowTemp"] = _float_to_char(s, EMS_Boiler.curFlowTemp);
|
||||
root["retTemp"] = _float_to_char(s, EMS_Boiler.retTemp);
|
||||
root["burnGas"] = EMS_Boiler.burnGas ? "on" : "off";
|
||||
root["heatPmp"] = EMS_Boiler.heatPmp ? "on" : "off";
|
||||
root["fanWork"] = EMS_Boiler.fanWork ? "on" : "off";
|
||||
root["ignWork"] = EMS_Boiler.ignWork ? "on" : "off";
|
||||
root["wWCirc"] = EMS_Boiler.wWCirc ? "on" : "off";
|
||||
root["selBurnPow"] = (String)EMS_Boiler.selBurnPow;
|
||||
root["curBurnPow"] = (String)EMS_Boiler.curBurnPow;
|
||||
root["sysPress"] = _float_to_char(s, EMS_Boiler.sysPress);
|
||||
root["boilTemp"] = _float_to_char(s, EMS_Boiler.boilTemp);
|
||||
root["pumpMod"] = (String)EMS_Boiler.pumpMod;
|
||||
|
||||
char data[100];
|
||||
root.printTo(data, root.measureLength() + 1);
|
||||
myESP.publish(TOPIC_THERMOSTAT, data);
|
||||
myESP.publish(TOPIC_BOILER_DATA, data);
|
||||
|
||||
if (EMS_Sys_Status.emsThermostatEnabled) {
|
||||
// only send thermostat values if we actually have them
|
||||
if (((int)EMS_Thermostat.curr_roomTemp == (int)0) || ((int)EMS_Thermostat.setpoint_roomTemp == (int)0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
myESP.publish(TOPIC_THERMOSTAT_CURRTEMP, _float_to_char(s, EMS_Thermostat.curr_roomTemp));
|
||||
myESP.publish(TOPIC_THERMOSTAT_SELTEMP, _float_to_char(s, EMS_Thermostat.setpoint_roomTemp));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -386,11 +408,11 @@ void setup() {
|
||||
|
||||
// set up Wifi, MQTT, Telnet
|
||||
myESP.setWifiCallback(WIFIcallback);
|
||||
|
||||
myESP.setMQTTCallback(MQTTcallback);
|
||||
myESP.addSubscription(TOPIC_START);
|
||||
myESP.addSubscription(TOPIC_THERMOSTAT);
|
||||
myESP.addSubscription(TOPIC_THERMOSTAT_TEMP);
|
||||
myESP.addSubscription(TOPIC_SHOWERTIME);
|
||||
|
||||
myESP.consoleSetHelpProjectsCmds(PROJECT_CMDS);
|
||||
myESP.consoleSetCallBackProjectCmds(myDebugCallback);
|
||||
myESP.begin(HOSTNAME);
|
||||
|
||||
@@ -428,8 +428,8 @@ void _processType(uint8_t * telegram, uint8_t length) {
|
||||
*/
|
||||
bool _process_UBAParameterWW(uint8_t * data, uint8_t length) {
|
||||
EMS_Boiler.wWSelTemp = data[2];
|
||||
EMS_Boiler.wWActivated = (data[1] == 0xFF);
|
||||
EMS_Boiler.wWCircPump = (data[6] == 0xFF);
|
||||
EMS_Boiler.wWActivated = (data[1] == 0xFF); // 0xFF means on
|
||||
EMS_Boiler.wWCircPump = (data[6] == 0xFF); // 0xFF means on
|
||||
EMS_Boiler.wWDesiredTemp = data[8];
|
||||
|
||||
return true;
|
||||
@@ -488,6 +488,8 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user