This commit is contained in:
proddy
2018-05-17 21:57:46 +02:00
parent 73c0c2a145
commit 615c5417de
9 changed files with 123 additions and 113 deletions

139
README.md
View File

@@ -1,6 +1,8 @@
# EMS-ESP-Boiler
EMS-ESP-Boiler is an implementation of the EMS bridge protocol (Energy Management System) used in Buderus/Bosch/Nefit boilers running on an ESP8266 microcontroller. With the code and circuit design you'll be able to read values and write commands to the Boiler and any connected devices such as a thermostat. The data is collected and sent via MQTT to home automation server such as Home Assistant or Domoticz.
EMS-ESP-Boiler is an controller running on an ESP8266 to communicate with EMS (Energy Management System) based Boilers from the Bosch range. This includes the Buderus and Nefit ranger of boilers, heaters and thermostats.
There are 3 parts to this project, first the design of the circuit, second the code to deploy to an ESP8266 based microcontroller and lastly settings for Home Assistant to monitor data and issue direct commands via MQTT.
[![version](https://img.shields.io/badge/version-1.0-brightgreen.svg)](CHANGELOG.md)
[![branch](https://img.shields.io/badge/branch-dev-orange.svg)](https://github.org/xoseperez/espurna/tree/dev/)
@@ -8,7 +10,7 @@ EMS-ESP-Boiler is an implementation of the EMS bridge protocol (Energy Managemen
- [EMS-ESP-Boiler](#ems-esp-boiler)
- [Introduction](#introduction)
- [Which Boilers are supported?](#which-boilers-are-supported)
- [Supported boilers](#supported-boilers)
- [Acknowledgments](#acknowledgments)
- [ESP8266 device compatibility](#esp8266-device-compatibility)
- [Getting Started](#getting-started)
@@ -17,10 +19,12 @@ EMS-ESP-Boiler is an implementation of the EMS bridge protocol (Energy Managemen
- [Known Issues](#known-issues)
- [To Do](#to-do)
- [How the EMS works](#how-the-ems-works)
- [**IDs**](#ids)
- [1. EMS Polling](#1-ems-polling)
- [2. EMS Broadcasting](#2-ems-broadcasting)
- [3. EMS Sending](#3-ems-sending)
- [The Code](#the-code)
- [Supported EMS Types](#supported-ems-types)
- [Customizing](#customizing)
- [MQTT](#mqtt)
- [Home Assistant Configuration](#home-assistant-configuration)
@@ -33,75 +37,74 @@ 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](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 :-)
I originally started this project with the intention to build my own smart thermostat for my Nefit Trendline boiler and then have it controlled it via [Home Assistant](https://www.home-assistant.io/) on my phone. 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 of course to the delight of my two teenage daughters :-)
## Which Boilers are supported?
## Supported boilers
Most Bosch branded boilers that support the Logamatic EMS (and EMS+) bus protocols which are Nefit, Buderus, Worcester and Junkers.
## Acknowledgments
First, a big thanks and appreciation to the following people and their projects for giving me inspiration and code snippets:
First, a big thanks and appreciation to the following people and their own similar projects for giving me inspiration and code snippets:
**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
**bbqkees** - Kees built a circuit and sample Arduino code to read from the EMS and push to Domoticz. His SMD circuit is actually 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 Juergen's schematic and code logic. https://github.com/susisstrolch/EMS-ESP12
**susisstrolch** - Probably the first working version of the EMS bridge circuit I could find 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.
**EMS Wiki** - A comprehensive reference for decoding the EMS telegrams, which unfortunately I found not always 100% accurate. https://emswiki.thefischer.net/doku.php?id=wiki:ems:telegramme. (Google Translate is handy).
## ESP8266 device compatibility
I've tested the code and circuit with a Wemos D1 Mini, Wemos D1 Mini Pro, Nodemcu0.9 and Nodemcu2 development boards.
I've tested the code and circuit with a few ESP8266 development boards such as a Wemos D1 Mini, Wemos D1 Mini Pro, Nodemcu0.9 and the latest Nodemcu2 boards.
## Getting Started
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.
4. Power the ESP from an external 5V supply, either via USB or direct into the 5v vin pin.
5. Power the EMS circuit using the 3v3 out from the ESP8266. It will also work with 5v.
6. When it has booted, telnet (port 23) to the IP of the ESP8266. If everything is working you should see the messages appear in the window as shown below. I use Telnet client that comes with Linux distro on Windows 10 but you can also use [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html).
1. Build the circuit below, 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 lines from the boiler to the circuit and the Rx/Tx out to pins D7 and D8 on the ESP. 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 3.5" 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. These 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 your OS and how the mdns resolves hostnames.
4. Power the ESP from an external 5V supply, either via USB or direct into the 5v vin pin from a power supply.
5. Attach the 3v3 on the ESP8266 to the DC power line. It will also work with 5v.
6. When it has booted, telnet (port 23) to the IP of the ESP8266. If everything is working you should see the messages appear in the window as shown in the next section. I use a Telnet client that comes with Linux distro on Windows 10 but you can also use [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) for example.
## Debugging
Because the Rx and Tx of the MCU is occupied with communicating to the EMS
Use the telnet client to inform you of all activity and errors if they happen. Note, if you're unable to to connect start the ESP with serial mode and look for connection errors in the serial out window.
Because the Rx and Tx of the MCU are occupied with communicating to the EMS serial out will not work in the IDE terminal. Instead use a telnet client to inform you of all activity and errors real-time. Note, if you're unable to to connect fist time probably the WiFi is dodgy. In this case do connect to a serial terminal and look at the messages without the EMS attached.
For example:
This is an example of the telnet output:
![Telnet](doc/telnet/telnet_example.JPG)
If you hit 'q' and Enter, it will toggle verbose logging and you will see more details. I use ANSI colors with white text for info messages, green for well formatted telegram packages which the correct CRC, 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)
To see the current values of the Boiler and its parameters type 's' and hit Enter. Here it will tell you how many unsuccessful telegram packets it has received (#CrcErrors).
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)
You can issue commands directly to the bus using 'r' and some other examples I programmed such as:
* **r** to send a read command to a device to fetch values
* **t** set the thermostat temperature to the given value. This is also what Home Assistance uses via MQTT
You can issue commands directly to the EMS bus typing in a letter, an optional paramete followed by 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
* **t** set the thermostat temperature to the given value
* **w** to adjust the temperature of the warm water from the boiler
* **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, but its the proper way
* **p** to toggle the Polling response on/off. It's not necessary to have Polling enabled to work, but its the proper way
* **T** to toggle thermostat reading on/off
* **S** to toggle the Shower Timer functionality on/off
*Disclaimer: be careful when sending values to the boiler. If in doubt you can always reset the boiler to its original factory settings by following the instructions in the user guide. On my **Nefit Trendline HRC30** that is done by pressing the Home and Menu buttons simultaneously, selecting factory settings from the scroll menu and pressing the Reset button.
*Disclaimer: be careful when sending values to the boiler. If in doubt you can always reset the boiler to its original factory settings by following the instructions in the user guide. On my **Nefit Trendline HRC30** that is done by pressing the Home and Menu buttons simultaneously, selecting factory settings from the scroll menu and lastly pressing the Reset button.
## Building the Circuit
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.
I've included a prototype boards you can build yourself on a breadboard. One part for only reading values from the Boiler and an extension with the write logic so you can send commands.
We need the RX/TX of the ESP8266 for flashing, so the code in ``emsuart.cpp`` switches the UART pins to use RX1 and TX1 (GPIO13/D7 and GPIO15/D8 respectively). This also prevents any bogus stack data being sent to EMS bus when the ESP8266 decides to crash.
We need the Rx/Tx of the ESP8266 for flashing, so the code in ``emsuart.cpp`` switches the UART pins to use RX1 and TX1 (GPIO13/D7 and GPIO15/D8 respectively). This also prevents any bogus stack data being sent to EMS bus when the ESP8266 decides to crash after a Watch Dog Reset.
The breadboard layout was done using [DIY Layout Creator](https://github.com/bancika/diy-layout-creator) and sources files are included. It looks like:
The breadboard layout was done using [DIY Layout Creator](https://github.com/bancika/diy-layout-creator) and sources files are included in the repo.
Read Only | Both Read and Write
--- | ---
@@ -111,40 +114,37 @@ The schematic from Juergen which this is based off is:
![Schematic](doc/schematics/circuit.png)
**Notes:**\
*Optionally I've also added 2 polyfuses between the EMS and the Inductors which are not shown in the layout or schematics above.*
Below is a prototype circuit using a NodeMcu2 with the additional LEDs and 5v buck converter. The inputs from the EMS are not shown but there are at J60 and J58 at the bottom left. If you don't want to build the circuit [bbqkees](http://www.domoticz.com/forum/memberlist.php?mode=viewprofile&u=1736) has one you can purchase, shown here using a Wemos D1 Mini:
Below is an early messy prototype circuit using a NodeMcu2 with the additional LEDs and 5v buck converter. The inputs from the EMS are not shown but there are at J60 and J58 at the bottom left. If you don't want to build the circuit [bbqkees](http://www.domoticz.com/forum/memberlist.php?mode=viewprofile&u=1736) can sell you one which looks like the photo below running on a Wemos D1 Mini:
![Breadboard](doc/schematics/breadboard.png) | ![WemosD1](doc/schematics/wemos_kees.png)
## Known Issues
In the code:
In the source code:
* Very infrequently an EMS write command is not sent, probably due to a collision somewhere in the UART code waiting for a Poll. The retries in the code fix that but it is annoying nevertheless and needs fixing.
* I've seen a few duplicate telegrams being processed. Again, it's harmless and not a big issue.
* Very infrequently an EMS write command is not sent, probably due to a collision somewhere in the UART between an incoming Rx and a Poll. The retries in the code fix that but it is annoying nevertheless and does need fixing.
* I've seen a few duplicate telegrams being processed. Again, it's harmless and not a big issue. But a bug.
In the circuit:
* Powering the circuit of the ESP's 3v3 line is stable when there is a steady 5v going to VIN. There is stability issues and noise when using a buck step-down converter to power the ESP from the EMS bus line (which is around 15V AC)
* Powering the circuit of the ESP's 3v3 line is very stable when there is a steady 5v going to VIN. There are stability issues and some noise when using a buck step-down converter to power the ESP from the EMS bus line (which is around 15V AC).
## To Do
Here's my top things I'm still working on:
* Make an ESPurna version. ESPurna takes care of the WiFi, MQTT, web server, telnet & debugging and does a better job that my modified ESPHelper code.
* Make an ESPurna version. ESPurna is a lovely framework that takes care of the WiFi, MQTT, web server, telnet & debugging.
* Complete the ESP32 version. It's surprisingly a lot easier doing the UART code on an ESP32 with the ESP-IDF framework. The first beta version is working.
* Find a better way to control the 3-way valve to switch the warm water off quickly rather than adjusting the temperature.
## How the EMS works
Packages are sent to the EMS "bus" from the Boiler and any other compatible connected device via serial transmission. The protocol is 9600 baud, 8N1 (8 bytes, no parity, 1 stop bit). Each package is terminated with a break signal `<BRK>` which is a 11-bit long low signal of zeros.
Packages are sent to the EMS "bus" from the Boiler (or UBA as its also called) and any other compatible connected devices via serial TTL transmission. The protocol is 9600 baud, 8N1 (8 bytes, no parity, 1 stop bit). Each package is terminated with a break signal `<BRK>`, a 11-bit long low signal of zeros.
A package can be a single byte (see Polling below) or a string of 6 or more bytes making up an actual data telegram. A telegram is always in the format:
@@ -152,24 +152,24 @@ A package can be a single byte (see Polling below) or a string of 6 or more byte
I reference the first 4 bytes as the *header* in this document.
**IDs**
### **IDs**
Each device has a unique ID.
The Boiler (MC10) has an ID of 0x08 and also referred to as the Bus Master.
The Boiler has an ID of 0x08 (type MC10) 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. 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).
My thermostat, which is a* Moduline 300* uses the RC20 protocol and has an ID 0x17. If you're using an RC30 or RC35 type thermostat such as the newer Moduline 300s or 400s 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).
Our circuit acts as a service device (called a service key in the official doc) and uses the reserved ID 0x0B.
### 1. EMS Polling
The bus master (boiler) sends out a poll request every second by sending out a sequential list of all possible IDs as a single byte followed by a break signal. The ID always has its high 7th bit set so in the code we're looking for `[dest|0x80] <BRK>`.
The bus master (boiler) sends out a poll request every second by sending out a sequential list of all possible IDs as a single byte followed by the break signal. The ID always has its high 7th bit set so in the code we're looking for 1 byte messages with the format `[dest|0x80] <BRK>`.
Any connected device can respond to a Polling call with an acknowledging by sending back a single byte with its own ID. For example, in our case we would listen for a `[0x8B] <BRK>` (meaning us) and then send back `[0x0B] <BRK>` to say we're alive and ready.
Any connected device can respond to a Polling call with an acknowledging by sending back a single byte with its own ID. In our case we would listen for a `[0x8B] <BRK>` (meaning us) and then send back `[0x0B] <BRK>` to say we're alive and ready.
Polling is also the trigger to start transmitting any packages queued for sending. It must be done within 200ms or the bus master will time out.
## 2. EMS Broadcasting
### 2. EMS Broadcasting
When a device is broadcasting to everyone there is no specific destination needed. `[dest]` is always 0x00.
@@ -188,19 +188,18 @@ And a thermostat (ID 0x17 for a RC20) would broadcast these messages regularly:
Type | Description
--- | --- |
0x06 | time on thermostat Y,M,H,D,M,S,wd
0xA8 | setting low, manual, clock, overrule clock setting, manual setpoint temperature
0xA3 | thermostat temperatures
0x91 | set point room temperature x 2, room temperature x 10
Refer to the code in ``ems.cpp`` for further explanation on how to parse these message types and also reference the EMS Wiki.
### 3. EMS Sending
Telegram packets can only be sent after the Boiler sends a poll to the sending device. The response can be a read command to request data or a send command to return data. At the end of the transmission a poll response is sent from the client (e.g. ``<ID> <BRK>``) to say we're all done and free up the bus for other clients.
Telegram packets can only be sent after the Boiler sends a poll to the sending device. The response can be a read command to request data or a write command to send data. At the end of the transmission a poll response is sent from the client (e.g. ``<ID> <BRK>``) to say we're all done and free up the bus for other clients.
When doing a request to read data the ``[src]`` is our device (0x0B) and the ``[dest]`` has it's 7-bit set. For example to request data from the Thermostat use ``[dest] = 0x97`` as RC20 has an ID 0x17.
When doing a request to read data the ``[src]`` is our device (0x0B) and the ``[dest]`` has it's 7-bit set. Say we were requesting data from the thermostat we would use ``[dest] = 0x97`` since RC20 has an ID of 0x17.
When doing a write request, the 7th bit is masked in the ``[dest]``. After a write request the destination device will send either a single byte 0x01 for success or 0x04 for fail.
When doing a write request, the 7th bit is masked in the ``[dest]``. After this write request the destination device will send either a single byte 0x01 for success or 0x04 for fail back.
Every telegram sent is echo'd back to Rx.
## The Code
@@ -216,12 +215,26 @@ When doing a write request, the 7th bit is masked in the ``[dest]``. After a wri
`src\emsuart.cpp` handles the low level UART read and write logic. You shouldn't need to touch this. All receive commands from the EMS bus are handled asynchronously using a circular buffer via an interrupt. A separate function processes the buffer and extracts the telegrams. Since we don't send many Write commands this is done sequentially. I couldn't use the standard Arduino Serial implementation because of the 11-bit break signal causes a frame-error which gets ignored.
`src\ems.cpp` is the logic to read the EMS packets (telegrams), validates them and process them based on the type. If you have another thermostat this is where you will configure it.
`src\ems.cpp` is the logic to read the EMS packets (telegrams), validates them and process them based on the type.
`src\boiler.ino` is the Arduino code for the ESP8266 that kicks it all off. This is where we have specific logic such as the code to monitor and alert on the Shower timer and light up the LEDs.
`lib\ESPHelper` is my customized version of [ESPHelper](https://github.com/ItKindaWorks/ESPHelper) with added Telnet support and some other minor tweaking.
### Supported EMS Types
`ems.cpp` defines callback functions that handle all the broadcast types plus these extra types:
Device | Type | Description | What
- | - | - | - |
Boiler (0x08) | 0x33 | UBAParameterWW | selected & desired warm water temp
Boiler (0x08) | 0x14 | UBATotalUptimeMessage | -
Boiler (0x08) | 0x15 | UBAMaintenanceSettingsMessage | -
Boiler (0x08) | 0x16 | UBAParametersMessage | -
Thermostat (0x17) | 0xA8 | RC20Temperature | setting temperature and operating modes
Thermostat (0x17) | 0xA3 | RCOutdoorTempMessage | -
Thermostat (0x17) | 0x91 | RC20StatusMessage | set & current room temperatures
### Customizing
Most of the changes will be done in `boiler.ino` and `ems.cpp`.
@@ -233,14 +246,20 @@ 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'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 run Mosquitto on my Raspberry PI 3.
The temperature values of the thermostat are sent as a JSON object using
`home/boiler/thermostat` and payload for example of `{"currtemp":"22.30","seltemp":"20.00"}`
The boiler data is collected and sent as a single JSON object to MQTT TOPIC `home/boiler/boiler_data`. Example payload:
The topics can be configured in the `TOPIC_*` defines in `boiler.ino`. Make sure you change the HA configuration too.
`{"wWCurTmp":"43.0","wWHeat":"on","curFlowTemp":"51.7","retTemp":"48.0","burnGas":"off","heatPmp":"off","fanWork":"off","ignWork":"off","wWCirc":"off","selBurnPow":"0","curBurnPow":"0","sysPress":"1.6","boilTemp":"54.7","pumpMod":"4"}`
home/boiler/boiler_data
The temperature values of the thermostat as two seperate TOPICS `home/boiler/thermostat_currtemp` and `home/boiler/thermostat_seltemp`
Values sent from HA to set the temperature come in via the subscribed topic `home/boiler/thermostat_temp`
These topics can be configured in the `TOPIC_*` defines in `boiler.ino`. Make sure you change the HA configuration too to match.
## Home Assistant Configuration
@@ -408,9 +427,9 @@ upload_port = "boiler."
### Using ESPurna
*Note: This is still work in progress. The ESPurna code for the HTML config is still to be added*
*Note: This is still work in progress. The ESPurna code for the HTML config is still to be added.*
[ESPurna](https://github.com/xoseperez/espurna/wiki) is lovely framework that handles most of the tedious tasks of building IoT devices so you can focus on the functionality you need.
[ESPurna](https://github.com/xoseperez/espurna/wiki) is framework that handles most of the tedious tasks of building IoT devices so you can focus on the functionality you need.
If you're using Windows follow these steps. We'll be using the free Visual Studio Code and PlatformIO. Similar steps also work on Linux distributions.
@@ -434,6 +453,8 @@ PlatformIO should detect and set some things up for you. Build and Deploy as you
If you run into issues refer to official ESPurnas setup instructions [here](https://github.com/xoseperez/espurna/wiki/Build-and-update-from-Visual-Studio-Code-using-PlatformIO).
Next copy the files custom.h, index.html, boiler.ino and the esp*.cpp/h files from the espurna directory to the code directory and build.
### Your comments and feedback
Any comments or suggestions are very welcome. You can contact me at **dev** at **derbyshire** dot **nl** or via an *Issue* in GitHub

0
espurna/in progress.txt Normal file
View File

Binary file not shown.

Binary file not shown.

View File

@@ -58,8 +58,7 @@ void _showerColdShotStop();
#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
// thermostat support, shower timing and shower alert all enabled
#define BOILER_THERMOSTAT_ENABLED 1
#define BOILER_SHOWER_ENABLED 1
#define BOILER_SHOWER_TIMER 0
@@ -71,10 +70,6 @@ const unsigned long SHOWER_MAX_DURATION = 420000; // 7 minutes, before trigger
const unsigned long SHOWER_OFF_DURATION = 3000; // 3 seconds long for cold water
const uint8_t SHOWER_BURNPOWER_MIN = 80;
// for debugging...
//const unsigned long SHOWER_MIN_DURATION = 10000; // 10 seconds
//const unsigned long SHOWER_MAX_DURATION = 15000; // 15 seconds
typedef struct {
bool wifi_connected;
bool boiler_online;
@@ -96,8 +91,7 @@ netInfo homeNet = {.mqttHost = MQTT_IP,
.mqttPass = MQTT_PASS,
.mqttPort = 1883,
.ssid = WIFI_SSID,
.pass = WIFI_PASSWORD
};
.pass = WIFI_PASSWORD};
ESPHelper myESP(&homeNet);
@@ -176,6 +170,7 @@ void showInfo() {
}
myDebug("\nBoiler stats:\n");
// UBAMonitorWWMessage & UBAParameterWW
myDebug(" Warm Water activated: %s\n", (EMS_Boiler.wWActivated ? "yes" : "no"));
myDebug(" Warm Water selected temperature: %d C\n", EMS_Boiler.wWSelTemp);
@@ -195,7 +190,7 @@ void showInfo() {
myDebug(" Return temperature: %s C\n", _float_to_char(s, EMS_Boiler.retTemp));
myDebug(" Gas: %s\n", EMS_Boiler.burnGas ? "on" : "off");
myDebug(" Circulating pump: %s\n", EMS_Boiler.heatPmp ? "on" : "off");
myDebug(" Boiler 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");
@@ -233,8 +228,8 @@ void showInfo() {
_float_to_char(s, EMS_Thermostat.curr_roomTemp));
}
if (Boiler_Status.shower_enabled) {
// show the Shower Info
if (Boiler_Status.shower_enabled) {
myDebug("Shower stats:\n Shower is %s\n", (Boiler_Shower.showerOn ? "on" : "off"));
char s[70];
uint8_t sec = (uint8_t)((Boiler_Shower.duration / 1000) % 60);
@@ -250,10 +245,11 @@ void showInfo() {
void publishValues() {
myDebug("Publishing data to MQTT topics\n");
char s[20]; // for formatting strings
// Boiler values as one JSON object
StaticJsonBuffer<512> jsonBuffer;
char data[512];
char s[20];
JsonObject & root = jsonBuffer.createObject();
root["wWCurTmp"] = _float_to_char(s, EMS_Boiler.wWCurTmp);
root["wWHeat"] = EMS_Boiler.wWHeat ? "on" : "off";
@@ -284,7 +280,6 @@ void publishValues() {
}
}
// extra commands options for telnet debug window
void myDebugCallback() {
char * cmd = myESP.consoleGetLastCommand();
@@ -369,14 +364,13 @@ void WIFIcallback() {
emsuart_init();
}
// Initialize the boiler settings
void _initBoiler() {
// default settings
ems_setThermostatEnabled(BOILER_THERMOSTAT_ENABLED);
Boiler_Status.shower_enabled = BOILER_SHOWER_ENABLED;
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
// init boiler
Boiler_Status.wifi_connected = false;
Boiler_Status.boiler_online = true; // assume we have a connection, it will be checked in the loop() anyway
@@ -427,7 +421,7 @@ void setup() {
enableHeartbeat(ems_getLogVerbose());
}
// flash ERR LEDs
// flash LEDs
// Using a faster way to write to pins as digitalWrite does a lot of overhead like pin checking & disabling interrupts
void showLEDs() {
// update Ticker
@@ -466,6 +460,7 @@ void heartbeat() {
}
// enables or disables the heartbeat LED
// using the Ticker library
void enableHeartbeat(bool on) {
heartbeat_state = (on) ? LOW : HIGH;
heartbeat();
@@ -503,8 +498,6 @@ void _showerColdShotStop() {
// Main loop
//
void loop() {
// my myESP to maintain the wifi, mqtt and debugging
yield();
connectionStatus = myESP.loop();
timestamp = millis();

View File

@@ -1,7 +1,7 @@
/*
* ems.cpp
* handles all the EMS messages
* handles all the processing of the EMS messages
* Paul Derbyshire - https://github.com/proddy/EMS-ESP-Boiler
*/
@@ -32,7 +32,6 @@ const _EMS_Types EMS_Types[MAX_TYPECALLBACK] =
{EMS_ID_THERMOSTAT, EMS_TYPE_RC20Temperature, "RC20Temperature", 10, _process_RC20Temperature}
};
// reserve space for the data we collect from the Boiler and Thermostat
_EMS_Boiler EMS_Boiler;
_EMS_Thermostat EMS_Thermostat;
@@ -55,8 +54,7 @@ const uint8_t ems_crc_table[] =
0xF7, 0xE9, 0xEB, 0xED, 0xEF, 0xE1, 0xE3, 0xE5, 0xE7
};
extern ESPHelper myESP;
extern ESPHelper myESP; // needed for the DEBUG statements below
#define myDebug(x, ...) myESP.printf(x, ##__VA_ARGS__);
// constants timers
@@ -79,7 +77,7 @@ void ems_init() {
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
EMS_Sys_Status.emsThermostatEnabled = true; // there is a RCxx thermostat active as default
EMS_Sys_Status.emsLogVerbose = false; // Verbose logging is off
EMS_Thermostat.hour = 0;
@@ -89,9 +87,10 @@ void ems_init() {
EMS_Thermostat.month = 0;
EMS_Thermostat.year = 0;
// UBAParameterWW
EMS_Boiler.wWActivated = false; // Warm Water activated
EMS_Boiler.wWSelTemp = 0; // Warm Water selected temperature
EMS_Boiler.wWCircPump = false; // Warm Water circulation pump Available
EMS_Boiler.wWCircPump = false; // Warm Water circulation pump available
EMS_Boiler.wWDesiredTemp = 0; // Warm Water desired temperature
// UBAMonitorFast
@@ -101,7 +100,7 @@ void ems_init() {
EMS_Boiler.burnGas = false; // Gas on/off
EMS_Boiler.fanWork = false; // Fan on/off
EMS_Boiler.ignWork = false; // Ignition on/off
EMS_Boiler.heatPmp = false; // Circulating pump on/off
EMS_Boiler.heatPmp = false; // Boiler pump on/off
EMS_Boiler.wWHeat = false; // 3-way valve on WW
EMS_Boiler.wWCirc = false; // Circulation on/off
EMS_Boiler.selBurnPow = 0; // Burner max power

View File

@@ -16,14 +16,16 @@
// EMS Telegram Types
#define EMS_TYPE_NONE 0x00 // none
#define EMS_TYPE_UBAMonitorFast 0x18 // is an automatic monitor broadcast
#define EMS_TYPE_UBAMonitorSlow 0x19 // is an automatic monitor broadcast
#define EMS_TYPE_UBAMonitorWWMessage 0x34 // is an automatic monitor broadcast
#define EMS_TYPE_UBAParameterWW 0x33 // is an automatic monitor broadcast
#define EMS_TYPE_UBAMaintenanceStatusMessage 0x1c // is an automatic monitor broadcast
#define EMS_TYPE_UBAParameterWW 0x33
#define EMS_TYPE_UBATotalUptimeMessage 0x14
#define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15
#define EMS_TYPE_UBAParametersMessage 0x16
#define EMS_TYPE_UBAMaintenanceStatusMessage 0x1c
// EMS Telegram types from Thermostat
// types 1A and 35 and used for errors from Thermostat

View File

@@ -16,8 +16,7 @@ _EMSRxBuf * pEMSRxBuf;
_EMSRxBuf * paEMSRxBuf[EMS_MAXBUFFERS];
uint8_t emsRxBufIdx = 0;
// queues
os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen];
os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen]; // our Rx queue
//
// Main interrupt handler
@@ -27,15 +26,14 @@ static void emsuart_rx_intr_handler(void * para) {
static uint16_t length;
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE];
// is a new buffer? if so init the thing
// 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
length = 0;
}
// fill IRQ buffer, by emptying FIFO
// fill IRQ buffer, by emptying Rx FIFO
if (U0IS & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) {
// get data from Rx
while ((USS(EMSUART_UART) >> USRXC) & 0xFF) {
uart_buffer[length++] = USF(EMSUART_UART);
}
@@ -46,10 +44,8 @@ static void emsuart_rx_intr_handler(void * para) {
}
// BREAK detection = End of EMS data block
if (USIS(EMSUART_UART) & ((1 << UIBD))) {
// disable all interrupts and clear them
ETS_UART_INTR_DISABLE();
ETS_UART_INTR_DISABLE(); // disable all interrupts and clear them
U0IC = (1 << UIBD); // INT clear the BREAK detect interrupt
@@ -126,13 +122,11 @@ void ICACHE_FLASH_ATTR emsuart_init() {
USIE(EMSUART_UART) = 0; // disable all interrupts
// enable rx break, fifo full and timeout.
// not frame error UIFR or overflow UIOF. Frame errors are too frequent
// and overflow never happens because our buffer is only max 32 bytes
// not frame error UIFR (because they are too frequent) or overflow UIOF because our buffer is only max 32 bytes
USIE(EMSUART_UART) = (1 << UIBD) | (1 << UIFF) | (1 << UITO);
// set up interrupt callbacks for Rx and Tx
// set up interrupt callbacks for Rx
system_os_task(emsuart_recvTask, EMSUART_recvTaskPrio, recvTaskQueue, EMSUART_recvTaskQueueLen);
//system_os_task(emsuart_sendTask, sendTaskPrio, sendTaskQueue, sendTaskQueueLen);
// disable esp debug which will go to Tx and mess up the line
// system_set_os_print(0); // https://github.com/espruino/Espruino/issues/655
@@ -140,7 +134,7 @@ void ICACHE_FLASH_ATTR emsuart_init() {
ETS_UART_INTR_ATTACH(emsuart_rx_intr_handler, NULL);
ETS_UART_INTR_ENABLE();
// when all ready swap RX and TX (to use GPIO13 (D7) and GPIO15 (D8) )
// when all ready swap Rx and Tx pins (to use GPIO13 (D7) and GPIO15 (D8) )
system_uart_swap();
}
@@ -157,14 +151,15 @@ void ICACHE_FLASH_ATTR emsuart_tx_brk() {
USC0(EMSUART_UART) |= (tmp); // set bits
USC0(EMSUART_UART) &= ~(tmp); // clear bits
// To create a 11-bit <BRK> we set TXD_BRK bit so the break signal will automatically be sent when the tx fifo is empty
// To create a 11-bit <BRK> we set TXD_BRK bit so the break signal will
// automatically be sent when the tx fifo is empty
USC0(EMSUART_UART) |= (1 << UCBRK); // set bit
delayMicroseconds(EMS_TX_BRK_WAIT);
delayMicroseconds(EMS_TX_BRK_WAIT); // 2070 - based on trial and error using an oscilloscope
USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear bit
}
/*
* Send to tx, ending with a BRK
* Send to Tx, ending with a BRK
*/
void ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
for (uint8_t i = 0; i < len; i++) {
@@ -174,7 +169,7 @@ void ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
}
/*
* Send the Poll (our ID) to Tx as a single byte and ending with a <BRK>
* Send the Poll (our own ID) to Tx as a single byte and end with a <BRK>
*/
void ICACHE_FLASH_ATTR emsaurt_tx_poll() {
USF(EMSUART_UART) = EMS_ID_ME;

View File

@@ -13,11 +13,11 @@
#define EMSUART_BAUD 9600 // uart baud rate for the EMS circuit
#define EMS_MAXBUFFERS 4 // 4 buffers for circular filling to avoid collisions
#define EMS_MAXBUFFERSIZE 128 // max size of the buffer. packets are 32 bits
#define EMS_MAXBUFFERSIZE 128 // max size of the buffer. packets are max 32 bytes
// for how long we drop the Tx signal to create a 11-bit Break of zeros
// this is how long we drop the Tx signal to create a 11-bit Break of zeros
// At 9600 baud, 11 bits will be 1144 microseconds
// the BRK from UBA is roughly 1.039ms, so accounting for hardware lag use 2078 (for half-duplex) - 8 (lag)
// the BRK from Boiler is roughly 1.039ms, so accounting for hardware lag using around 2078 (for half-duplex) - 8 (lag)
#define EMS_TX_BRK_WAIT 2070
#define EMSUART_recvTaskPrio 1