mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
Version 1.1. See ChangeLog
This commit is contained in:
67
CHANGELOG.md
Normal file
67
CHANGELOG.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# EMS-ESP-Boiler Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Setting the mode and setpoint temperature on a RC35
|
||||||
|
|
||||||
|
## [1.1.0] 2018-12-22
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed handling of negative flaoting point values (like outdoor temp)
|
||||||
|
- Fixed handling of auto & manual mode on an RC30
|
||||||
|
- [Fixed condition where all telegram types were processed, instead of only broadcasts or our own reads](https://github.com/proddy/EMS-ESP-Boiler/issues/15)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Created this CHANGELOG.md file!
|
||||||
|
- [Added support for the Nefit Easy thermostat, reading of temperature values only](https://github.com/proddy/EMS-ESP-Boiler/issues/9) - note *read only* (big thanks @**kroon040** for lending me an Easy device)
|
||||||
|
- [Added support for RC35/Moduline 400](https://github.com/proddy/EMS-ESP-Boiler/issues/14) - *read only*
|
||||||
|
- [New raw logging mode for logging](https://github.com/proddy/EMS-ESP-Boiler/issues/11)
|
||||||
|
- [New 'r'command to send raw data to EMS](https://github.com/proddy/EMS-ESP-Boiler/issues/11)
|
||||||
|
- [Added MQTT messages for hot water on and heating on](https://github.com/proddy/EMS-ESP-Boiler/issues/10)
|
||||||
|
- Implemented FIFO circular buffer queue for up to 20 Tx messages (Q command to show queue)
|
||||||
|
- Toggle Tx transmission via telnet (use 'X' command)
|
||||||
|
- Show thermostat type in help stats (use 's' command)
|
||||||
|
- Show version is help stats (use '?' command)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved overall formatting of logging
|
||||||
|
- Include app name and version in telnet help
|
||||||
|
- Improved method to switch off hot tap water in Shower Alert
|
||||||
|
- Telnet P and M commands have changed
|
||||||
|
- Enabling Logging in telnet is now the 'l' command
|
||||||
|
- Logging is set back to None when telnet session closes
|
||||||
|
- Improved fetching of initial boiler values to post to MQTT at startup
|
||||||
|
- Improved handling and retrying of write/Tx commands
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Hid access from telnet to the Experimental custom function command 'x'
|
||||||
|
- Tx and Rx stats have gone from the stats page, as they were pretty meaningless
|
||||||
|
- Removed NO_TX define in platformio and replaced with system parameter 'command X'
|
||||||
|
- Removed -DDEBUG option in build
|
||||||
|
- Removed wwtemp MQTT messages to set the boiler temp. You'll never miss it.
|
||||||
|
- Removed LEDs for Tx, Rx and Err. Too many flashing lights and it drains the current.
|
||||||
|
- Removed capturing of last Rx and Tx times
|
||||||
|
- Support for older RC20 thermostats
|
||||||
|
|
||||||
|
### Known Issues
|
||||||
|
|
||||||
|
- There's a nasty memory leek when in telnet's verbose mode with sending which causes the EMS to reset when running for a while.
|
||||||
|
|
||||||
|
## [1.0.1] 2018-09-24
|
||||||
|
|
||||||
|
- Initial stable version
|
||||||
|
|
||||||
|
## [0.1.0] 2018-05-14
|
||||||
|
|
||||||
|
- Initial development version
|
||||||
113
README.md
113
README.md
@@ -1,10 +1,10 @@
|
|||||||
# EMS-ESP-Boiler
|
# EMS-ESP-Boiler
|
||||||
|
|
||||||
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.
|
EMS-ESP-Boiler is a project to build a controller circuit running with an ESP8266 to communicate with EMS (Energy Management System) based Boilers and Thermostats from the Bosch range and compatibles such as Buderus, Nefit, Junkers etc.
|
||||||
|
|
||||||
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.
|
There are 3 parts to this project, first the design of the circuit, second the code for the ESP8266 microcontroller firmware and lastly an example configuration for Home Assistant to monitor the data and issue direct commands via MQTT.
|
||||||
|
|
||||||
[](CHANGELOG.md)
|
[](CHANGELOG.md)
|
||||||
[](https://github.org/xoseperez/espurna/tree/dev/)
|
[](https://github.org/xoseperez/espurna/tree/dev/)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
@@ -23,7 +23,11 @@ There are 3 parts to this project, first the design of the circuit, second the c
|
|||||||
- [EMS Reading and Writing](#ems-reading-and-writing)
|
- [EMS Reading and Writing](#ems-reading-and-writing)
|
||||||
- [The ESP8266 Source Code](#the-esp8266-source-code)
|
- [The ESP8266 Source Code](#the-esp8266-source-code)
|
||||||
- [Supported EMS Types](#supported-ems-types)
|
- [Supported EMS Types](#supported-ems-types)
|
||||||
- [Supporting other Thermostats types](#supporting-other-thermostats-types)
|
- [Supported Thermostats](#supported-thermostats)
|
||||||
|
- [RC20 (Moduline 300)](#rc20-moduline-300)
|
||||||
|
- [RC30 (Moduline 400)](#rc30-moduline-400)
|
||||||
|
- [RC35](#rc35)
|
||||||
|
- [TC100/TC200 (Nefit Easy)](#tc100tc200-nefit-easy)
|
||||||
- [Customizing The Code](#customizing-the-code)
|
- [Customizing The Code](#customizing-the-code)
|
||||||
- [Using MQTT](#using-mqtt)
|
- [Using MQTT](#using-mqtt)
|
||||||
- [The Basic Shower Logic](#the-basic-shower-logic)
|
- [The Basic Shower Logic](#the-basic-shower-logic)
|
||||||
@@ -33,28 +37,30 @@ There are 3 parts to this project, first the design of the circuit, second the c
|
|||||||
- [Using ESPurna](#using-espurna)
|
- [Using ESPurna](#using-espurna)
|
||||||
- [Using Pre-built Firmware](#using-pre-built-firmware)
|
- [Using Pre-built Firmware](#using-pre-built-firmware)
|
||||||
- [Building Using Arduino IDE](#building-using-arduino-ide)
|
- [Building Using Arduino IDE](#building-using-arduino-ide)
|
||||||
- [Known Issues and ToDo's](#known-issues-and-todos)
|
- [Known Issues](#known-issues)
|
||||||
|
- [Wish List](#wish-list)
|
||||||
- [Your Comments and Feedback](#your-comments-and-feedback)
|
- [Your Comments and Feedback](#your-comments-and-feedback)
|
||||||
|
- [DISCLAIMER](#disclaimer)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
My original intention for this home project with to build my own smart thermostat for my Nefit Trendline boiler and then have it controlled via [Home Assistant](https://www.home-assistant.io/) on my mobile phone. I had a few ESP32s and ESP8266s lying around from previous IoT projects and building a specific circuit to decode the EMS messages was a nice challenge into more complex electronic circuits. I then 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.
|
My original intention for this home project with to build my own smart thermostat for my Nefit Trendline boiler and then have it controlled automatically via [Home Assistant](https://www.home-assistant.io/) on my mobile phone. I had a few ESP32s and ESP8266s lying around from previous IoT projects and building a specific circuit to decode the EMS messages was a nice challenge into designing complete end-to-end complex electronic circuits. I then began adding new features such as timing how long the shower would be running for and subsequently triggering an alarm (actually a shot of cold water) after a certain period.
|
||||||
|
|
||||||
Acknowledgments and kudos to the following people and their open-sourced projects:
|
Acknowledgments and kudos to the following people and their open-sourced projects that have helped me get this far:
|
||||||
|
|
||||||
**susisstrolch** - Probably the first working version of the EMS bridge circuit I could find designed for the ESP8266. I borrowed Juergen's [schematic](https://github.com/susisstrolch/EMS-ESP12) and parts of his code logic.
|
**susisstrolch** - Probably the first working version of the EMS bridge circuit I found designed for the ESP8266. I borrowed Juergen's [schematic](https://github.com/susisstrolch/EMS-ESP12) and parts of his code logic.
|
||||||
|
|
||||||
**bbqkees** - Kees built a [circuit](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz) and wrote some sample Arduino code to read from the EMS and push messages to Domoticz. His SMD board is also available to purchase from him directly.
|
**bbqkees** - Kees built a [circuit](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz) and some sample Arduino code to read from the EMS and push messages to Domoticz. His SMD board is also now available for purchase.
|
||||||
|
|
||||||
**EMS Wiki** - A comprehensive [reference](https://emswiki.thefischer.net/doku.php?id=wiki:ems:telegramme) for decoding the EMS telegrams, which I found not always to be 100% accurate. It's in German so use Google Translate if you need help.
|
**EMS Wiki** - A comprehensive [reference](https://emswiki.thefischer.net/doku.php?id=wiki:ems:telegramme) for decoding the EMS telegrams, which I found not always to be 100% accurate. It's in German so use Google Translate if you need help.
|
||||||
|
|
||||||
## Supported Boilers Types
|
## Supported Boilers Types
|
||||||
|
|
||||||
Most Bosch branded boilers that support the Logamatic EMS (and EMS+) bus protocols work with this design. Which are Nefit, Buderus, Worcester and Junkers and copyrighted.
|
Most Bosch branded boilers that support the Logamatic EMS (and EMS+) bus protocols work with this design. Which are Nefit, Buderus, Worcester and Junkers and copyrighted. Please make sure you read the **Disclaimer** carefully before sending ambigious messages to your EMS bus as you cause device damage.
|
||||||
|
|
||||||
## Supported ESP8266 devices
|
## Supported ESP8266 devices
|
||||||
|
|
||||||
I've tested the code and circuit with a few ESP8266 development boards such as the Wemos D1 Mini, Wemos D1 Mini Pro, Nodemcu0.9 and Nodemcu2 boards. It will also work on bare ESP8266 chips such as the 12s but do make sure you disabled the LED support and wire the UART correctly as this is switched (explained below).
|
I've tested the code and circuit with a few ESP8266 development boards such as the Wemos D1 Mini, Wemos D1 Mini Pro, Nodemcu0.9 and Nodemcu2 boards. It will also work on bare ESP8266 chips such as the E-12s but do make sure you disabled the LED support and wire the UART correctly as the code doesn't use the normal Rx and Tx pins. This is explained below.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
@@ -72,22 +78,23 @@ Use the telnet client to inform you of all activity and errors real-time. This i
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you type 'v 3' 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 type 'l 4' 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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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 the boiler. The 2nd parameter is the type. For example 'b 33' will request type UBAParameterWW and bring back the Warm Water temperatures from the Boiler.
|
- **b** to send a read command to the boiler. The 2nd parameter is the type. For example 'b 33' will request type UBAParameterWW and bring back the Warm Water temperatures from the Boiler.
|
||||||
- **t** is similar, but to send a read command to the thermostat.
|
- **t** is similar, but to send a read command to the thermostat.
|
||||||
- **T** set the thermostat temperature to the given celsius value
|
- **T** set the thermostat temperature to the given celsius 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 tap water on and off
|
||||||
- **h** to list all the recognized EMS types
|
- **h** to list all the recognized EMS types
|
||||||
- **p** to toggle the Polling response on/off (note it's not necessary to have Polling enabled to work)
|
- **P** to toggle the Polling response on/off (note it's not necessary to have Polling enabled to work)
|
||||||
- **m** to set the thermostat mode to manual or auto
|
- **m** to set the thermostat mode to manual or auto
|
||||||
- **S** to toggle the Shower Timer functionality on/off
|
- **S** to toggle the Shower Timer functionality on/off
|
||||||
- **A** to toggle the Shower Timer Alert functionality on/off
|
- **A** to toggle the Shower Timer Alert functionality on/off
|
||||||
@@ -118,7 +125,8 @@ And lastly if you don't fancy building the circuit, [bbqkees](http://www.domotic
|
|||||||
|
|
||||||
### Powering The EMS Circuit
|
### Powering The EMS Circuit
|
||||||
|
|
||||||
The EMS circuit will work with both 3.3V and 5V. It's easiest though to power directly from the ESP8266's 3V3 line and run a steady 5V into the microcontroller. Powering the ESP8266 microcontroller can be either
|
The EMS circuit will work with both 3.3V and 5V. It's easiest though to power directly from the ESP8266's 3V3 line and run a steady 5V into the microcontroller. Powering the ESP8266 microcontroller can be either:
|
||||||
|
|
||||||
- via the USB if your dev board has one
|
- via the USB if your dev board has one
|
||||||
- using an external 5V power supply into the 5V vin on the board
|
- using an external 5V power supply into the 5V vin on the board
|
||||||
- powering from the 3.5" service jack on the boiler. This will give you 8V so you need a buck converter (like a [Pololu D24C22F5](https://www.pololu.com/product/2858)) to step this down to 5V to provide enough power to the ESP8266 (250mA at least)
|
- powering from the 3.5" service jack on the boiler. This will give you 8V so you need a buck converter (like a [Pololu D24C22F5](https://www.pololu.com/product/2858)) to step this down to 5V to provide enough power to the ESP8266 (250mA at least)
|
||||||
@@ -128,7 +136,6 @@ The EMS circuit will work with both 3.3V and 5V. It's easiest though to power di
|
|||||||
| ------------------------------------------ |
|
| ------------------------------------------ |
|
||||||
|  |
|
|  |
|
||||||
|
|
||||||
|
|
||||||
## How The EMS Bus Works
|
## How The EMS Bus Works
|
||||||
|
|
||||||
Packages are sent to the EMS "bus" from the Boiler 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.
|
Packages are sent to the EMS "bus" from the Boiler 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.
|
||||||
@@ -151,7 +158,7 @@ Our circuit acts as a service key and thus uses an ID 0x0B. This ID is reserved
|
|||||||
|
|
||||||
### EMS Polling
|
### 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 the break signal. The ID always has its high 7th bit set so in the code we're looking for 1 byte messages matching the format `[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 8th bit (MSB) set so in the code we're looking for 1 byte messages matching 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. 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. Although I found this is not needed for normal operation so it's disabled as default in the code.
|
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. Although I found this is not needed for normal operation so it's disabled as default in the code.
|
||||||
|
|
||||||
@@ -175,7 +182,7 @@ The tables below shows which types are broadcasted regularly by the boiler (ID 0
|
|||||||
| Source (ID) | Type ID | Name | Description | Frequency |
|
| Source (ID) | Type ID | Name | Description | Frequency |
|
||||||
| ----------------- | ------- | ----------------- | --------------------------------------------------- | ---------- |
|
| ----------------- | ------- | ----------------- | --------------------------------------------------- | ---------- |
|
||||||
| Thermostat (0x17) | 0x06 | RCTime | returns time and date on the thermostat | 60 seconds |
|
| Thermostat (0x17) | 0x06 | RCTime | returns time and date on the thermostat | 60 seconds |
|
||||||
| Thermostat (0x17) | 0x91 | RC20StatusMessage | returns current and set temperatures | 60 seconds |
|
| Thermostat (0x17) | 0x91 | RC30StatusMessage | returns current and set temperatures | 60 seconds |
|
||||||
| Thermostat (0x17) | 0xA3 | RCTempMessage | returns temp values from external (outdoor) sensors | 60 seconds |
|
| Thermostat (0x17) | 0xA3 | RCTempMessage | returns temp values from external (outdoor) sensors | 60 seconds |
|
||||||
|
|
||||||
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.
|
||||||
@@ -184,15 +191,15 @@ Refer to the code in `ems.cpp` for further explanation on how to parse these mes
|
|||||||
|
|
||||||
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 (`<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 (`<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. Say we were requesting data from the thermostat we would use `[dest] = 0x97` since RC30 has an ID of 0x17.
|
When doing a request to read data the `[src]` is our device `(0x0B)` and the `[dest]` must have has it's MSB (8th bit) set. Say we were requesting data from the thermostat we would use `[dest] = 0x97` since RC30 has an ID of 0x17.
|
||||||
|
|
||||||
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.
|
Following a write request, the `[dest]` doesn't have the 8th bit set and after this write request the destination device will send either a single byte 0x01 for success or 0x04 for failure.
|
||||||
|
|
||||||
Every telegram sent is echo'd back to Rx.
|
Every telegram sent is echo'd back to Rx.
|
||||||
|
|
||||||
## The ESP8266 Source Code
|
## The ESP8266 Source Code
|
||||||
|
|
||||||
*Disclaimer*: This code here is really for reference only, I don't expect anyone to use as is since it's highly tailored to my environment and my needs. Most of the code however is self explanatory with comments here and there. If you wish to make some changes start with the `defines` and `const` sections at the top of `boiler.ino`.
|
*Disclaimer*: This code here is really for reference only, I don't expect anyone to use "as is" since it's highly tailored to my environment and my needs. Most of the code however is self explanatory with comments here and there in the code.
|
||||||
|
|
||||||
The code is built on the Arduino framework and is dependent on these external libraries:
|
The code is built on the Arduino framework and is dependent on these external libraries:
|
||||||
|
|
||||||
@@ -214,26 +221,44 @@ The code is built on the Arduino framework and is dependent on these external li
|
|||||||
`ems.cpp` defines callback functions that handle all the broadcast types listed above (e.g. 0x34, 0x18, 0x19 etc) plus these extra types:
|
`ems.cpp` defines callback functions that handle all the broadcast types listed above (e.g. 0x34, 0x18, 0x19 etc) plus these extra types:
|
||||||
|
|
||||||
| Source (ID) | Type ID | Name | Description |
|
| Source (ID) | Type ID | Name | Description |
|
||||||
| ----------------- | ------- | ----------------------------- | ---------------------------------------- |
|
| ----------------- | ---------------- | ----------------------------- | ---------------------------------------- |
|
||||||
| Boiler (0x08) | 0x33 | UBAParameterWW | reads selected & desired warm water temp |
|
| Boiler (0x08) | 0x33 | UBAParameterWW | reads selected & desired warm water temp |
|
||||||
| Boiler (0x08) | 0x14 | UBATotalUptimeMessage | |
|
| Boiler (0x08) | 0x14 | UBATotalUptimeMessage | |
|
||||||
| Boiler (0x08) | 0x15 | UBAMaintenanceSettingsMessage | |
|
| Boiler (0x08) | 0x15 | UBAMaintenanceSettingsMessage | |
|
||||||
| Boiler (0x08) | 0x16 | UBAParametersMessage | |
|
| Boiler (0x08) | 0x16 | UBAParametersMessage | |
|
||||||
| Thermostat (0x17) | 0xA8 | RC20Temperature | sets operating modes for a RC20 & RC30 |
|
| Thermostat (0x17) | 0xA8 | RC20Set | sets operating modes for an RC20 |
|
||||||
| Thermostat (0x17) | 0x02 | Version | reads Version major/minor |
|
| Thermostat (0x10) | 0xA7 | RC30Set | sets operating modes for an RC30 |
|
||||||
| Thermostat (0x18) | 0x0A | EasyTemperature | thermostat monitor for an TC100/Easy |
|
| Thermostat | 0x02 | Version | reads Version major/minor |
|
||||||
|
| Thermostat | 0x91, 0x41, 0x0A | Status Message | read monitor values |
|
||||||
|
|
||||||
In `boiler.ino` you can make calls to automatically send these read commands. See the function *regularUpdates()*
|
In `boiler.ino` you can make calls to automatically send these read commands. See the function *regularUpdates()*
|
||||||
|
|
||||||
#### Supporting other Thermostats types
|
### Supported Thermostats
|
||||||
|
|
||||||
The code is originally designed for a Moduline300 (RC30) thermostat.
|
Modify `EMS_ID_THERMOSTAT` in `myconfig.h` to the thermostat type you want to support.
|
||||||
|
|
||||||
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.
|
#### RC20 (Moduline 300)
|
||||||
|
|
||||||
There is limited support for an Nefit Easy TC100) thermostat. The current room temperature and setpoint temperature can be read. What still needs fixing is
|
Read and write of setpoint temp and mode supported.
|
||||||
- reading the mode (manual vs clock)
|
|
||||||
- setting the temperature
|
#### RC30 (Moduline 400)
|
||||||
|
|
||||||
|
Read and write of setpoint temp and mode supported.
|
||||||
|
|
||||||
|
Type's 3F, 49, 53, 5D are identical. So are 4B, 55, 5F and mostly zero's. Types 40, 4A, 54 and 5E are also the same.
|
||||||
|
|
||||||
|
#### RC35
|
||||||
|
|
||||||
|
***not implemented yet***!
|
||||||
|
|
||||||
|
An RC35 thermostat can support up to 4 heating circuits each controlled with their own Monitor and Working Mode IDs.
|
||||||
|
|
||||||
|
Fetching the thermostats setpoint temp us by requesting 0x3E and looking at the 3rd byte in the data telegram (data[2]) and dividing by 2.
|
||||||
|
The mode is on type 0x47 (or 0x3D) and the 8th byte (data[7]). 0=off, 1=on, 2=auto
|
||||||
|
|
||||||
|
#### TC100/TC200 (Nefit Easy)
|
||||||
|
|
||||||
|
There is limited support for an Nefit Easy TC100/TC200 type thermostat. The current room temperature and setpoint temperature can be read. What I'm still figuring out is how to read the mode and set the temperature values without sending http post commands to their web server.
|
||||||
|
|
||||||
### Customizing The Code
|
### Customizing The Code
|
||||||
|
|
||||||
@@ -286,7 +311,7 @@ PlatformIO is my preferred way. The code uses a modified version [ESPHelper](htt
|
|||||||
**On Windows:**
|
**On Windows:**
|
||||||
|
|
||||||
- Download [Git](https://git-scm.com/download/win) (install using the default settings)
|
- Download [Git](https://git-scm.com/download/win) (install using the default settings)
|
||||||
- Download and install [Visual Studio Code](https://code.visualstudio.com/docs/?dv=win) (VSC)
|
- Download and install [Visual Studio Code](https://code.visualstudio.com/docs/?dv=win) (VSC). It's like 40MB so don't confuse with the commercial Microsoft Visual Studio.
|
||||||
- Restart the PC (if using Windows) to apply the new PATH settings. It should now detect Git
|
- Restart the PC (if using Windows) to apply the new PATH settings. It should now detect Git
|
||||||
- Install these VSC extensions: PlatformIO IDE & GitLens, and then click reload to activate them
|
- Install these VSC extensions: PlatformIO IDE & GitLens, and then click reload to activate them
|
||||||
- Git clone this repo, eith using `git clone` from PlatformIO's terminal or the Git GUI interface
|
- Git clone this repo, eith using `git clone` from PlatformIO's terminal or the Git GUI interface
|
||||||
@@ -295,6 +320,8 @@ PlatformIO is my preferred way. The code uses a modified version [ESPHelper](htt
|
|||||||
**On Linux (e.g. Ubuntu under Windows10):**
|
**On Linux (e.g. Ubuntu under Windows10):**
|
||||||
|
|
||||||
- make sure Python 2.7 is installed
|
- make sure Python 2.7 is installed
|
||||||
|
- make sure you have a Linux distro installed (https://docs.microsoft.com/en-us/windows/wsl/install-win10)
|
||||||
|
- Do:
|
||||||
```python
|
```python
|
||||||
% pip install -U platformio
|
% pip install -U platformio
|
||||||
% sudo platformio upgrade
|
% sudo platformio upgrade
|
||||||
@@ -322,14 +349,12 @@ PlatformIO is my preferred way. The code uses a modified version [ESPHelper](htt
|
|||||||
7. Build the web interface: `node node_modules/gulp/bin/gulp.js`. This will create a compressed `code/espurna/static/index.html.gz.h`. If you get warnings about lf during the building edit `gulpfile.js` and change the line `'failOnError': true` to `false` as a temporary workaround.
|
7. Build the web interface: `node node_modules/gulp/bin/gulp.js`. This will create a compressed `code/espurna/static/index.html.gz.h`. If you get warnings about lf during the building edit `gulpfile.js` and change the line `'failOnError': true` to `false` as a temporary workaround.
|
||||||
8. Modify the platformio.ini file making sure you add `-DUSE_CUSTOM_H -DUSE_EXTRA` to the `debug_flags`
|
8. Modify the platformio.ini file making sure you add `-DUSE_CUSTOM_H -DUSE_EXTRA` to the `debug_flags`
|
||||||
9. Copy the following files from EMS-ESP-Boiler repo to where you installed ESPurna
|
9. Copy the following files from EMS-ESP-Boiler repo to where you installed ESPurna
|
||||||
|
|
||||||
```c
|
```c
|
||||||
espurna/index.html -> code/html/index.html
|
espurna/index.html -> code/html/index.html
|
||||||
espurna/custom.h -> code/config/custom.h
|
espurna/custom.h -> code/config/custom.h
|
||||||
espurna/boiler-espurna.ino -> code/espurna/boiler-espurna.ino
|
espurna/boiler-espurna.ino -> code/espurna/boiler-espurna.ino
|
||||||
ems*.* -> code/espurna/
|
ems*.* -> code/espurna/
|
||||||
```
|
```
|
||||||
|
|
||||||
10. Now build and upload as you usually would with PlatformIO (or ctrl-arl-t and choose the right build). Look at my version of platformio.ini as an example.
|
10. Now build and upload as you usually would with PlatformIO (or ctrl-arl-t and choose the right build). Look at my version of platformio.ini as an example.
|
||||||
11. When the firmware loads, use a wifi connected pc/mobile to connect to the Access Point called ESPURNA_XXXXXX. Use 'fibonacci' as the password. Navigate to `http://192.168.4.1` from a browser, set a new username and password when prompted, log off the wifi and reconnect to the AP using these new credentials. Again go to 192.168.4.1
|
11. When the firmware loads, use a wifi connected pc/mobile to connect to the Access Point called ESPURNA_XXXXXX. Use 'fibonacci' as the password. Navigate to `http://192.168.4.1` from a browser, set a new username and password when prompted, log off the wifi and reconnect to the AP using these new credentials. Again go to 192.168.4.1
|
||||||
12. In the ADMIN page enable Telnet and SAVE
|
12. In the ADMIN page enable Telnet and SAVE
|
||||||
@@ -378,18 +403,24 @@ Porting to the Arduino IDE can be a little tricky but it is possible.
|
|||||||
- Put all the files in a single sketch folder (`ESPHelper.*, boiler.ino, ems.*, emsuart.*`)
|
- Put all the files in a single sketch folder (`ESPHelper.*, boiler.ino, ems.*, emsuart.*`)
|
||||||
- cross your fingers and hit CTRL-R to compile...
|
- cross your fingers and hit CTRL-R to compile...
|
||||||
|
|
||||||
## Known Issues and ToDo's
|
## Known Issues
|
||||||
|
|
||||||
Some annoying issues that need fixing:
|
Some annoying issues that need fixing:
|
||||||
|
|
||||||
- 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 this for now.
|
- 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 this for now.
|
||||||
- I've seen a few duplicate telegrams being processed. Again, it's harmless and not a big issue, but still a bug.
|
|
||||||
|
|
||||||
Here's my top things I'm still working on:
|
## Wish List
|
||||||
|
|
||||||
- Complete the ESP32 version. It's surprisingly a lot easier doing the UART handling on an ESP32 with the ESP-IDF SDK. I have a first version beta that is working.
|
- Measure amount of gas in m3 per day for the hot water vs the central heating, and convert this into cost in Home Assistant
|
||||||
- Find a better way to control the 3-way valve to switch the warm water off quickly rather than deactivating the warm water heater each time. There is an unsupported call to do this, but I think its too risky and experimental. I'd love to get my hands on an Nefit Easy, sniff the packets being sent and reverse engineer the logic. Anyone help?
|
- Support changing temps on an Nefit Easy. To do this you must send XMPP messages directly to the thermostat. See this project: https://github.com/robertklep/nefit-easy-core
|
||||||
|
- Store custom params like wifi credentials, mqtt, thermostat type on ESP8266 using SPIFFS
|
||||||
|
- Automatic detection of thermostat type
|
||||||
|
- Add support for a temperature sensor on the circuit (DS18B20)
|
||||||
|
|
||||||
## Your Comments and Feedback
|
## Your Comments and Feedback
|
||||||
|
|
||||||
Any comments, suggestions or code changes are very welcome. Please post a GitHub issue.
|
Any comments, suggestions or code contributions are very welcome. Please post a GitHub issue.
|
||||||
|
|
||||||
|
## DISCLAIMER
|
||||||
|
|
||||||
|
This code and libraries were developed from information gathered on the internet and many hours of reverse engineering the communications between the EMS bus and thermostats. It is **not** based on any official documentation or supported libraries from Buderus/Junkers/Nefit (and associated companies) and therefore there are no guarantees whatsoever regarding the safety of your devices and/or their settings, or the accuracy of the information provided.
|
||||||
@@ -39,27 +39,5 @@
|
|||||||
payload: >
|
payload: >
|
||||||
{{ now().strftime("%H:%M:%S %-d/%b/%Y") }}
|
{{ now().strftime("%H:%M:%S %-d/%b/%Y") }}
|
||||||
|
|
||||||
# Boiler warm water temp
|
|
||||||
- id: boiler_wwtemp
|
|
||||||
trigger:
|
|
||||||
platform: state
|
|
||||||
entity_id: input_number.boiler_wwtemp
|
|
||||||
action:
|
|
||||||
service: mqtt.publish
|
|
||||||
data_template:
|
|
||||||
topic: 'home/boiler/boiler_wwtemp'
|
|
||||||
payload: >
|
|
||||||
{{ states.input_number.boiler_wwtemp.state }}
|
|
||||||
|
|
||||||
# See if wwTemp has changed in recent mqtt payload, then adjust input_number
|
|
||||||
- id: boiler_wwtemp_incoming
|
|
||||||
trigger:
|
|
||||||
platform: state
|
|
||||||
entity_id: sensor.warm_water_selected_temperature
|
|
||||||
action:
|
|
||||||
service: input_number.set_value
|
|
||||||
data_template:
|
|
||||||
entity_id: input_number.boiler_wwtemp
|
|
||||||
value: '{{ states.sensor.warm_water_selected_temperature.state }}'
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
boiler_wwtemp:
|
|
||||||
name: Warm Water temp
|
|
||||||
icon: mdi:temperature-celsius
|
|
||||||
min: 30
|
|
||||||
max: 60
|
|
||||||
step: 1
|
|
||||||
unit_of_measurement: "°C"
|
|
||||||
mode: slider
|
|
||||||
|
|
||||||
@@ -5,12 +5,12 @@ views:
|
|||||||
cards:
|
cards:
|
||||||
- type: entities
|
- type: entities
|
||||||
title: Boiler
|
title: Boiler
|
||||||
show_header_toggle: true
|
show_header_toggle: false
|
||||||
entities:
|
entities:
|
||||||
- sensor.boiler_boottime
|
- sensor.boiler_boottime
|
||||||
- sensor.boiler_updated
|
- sensor.boiler_updated
|
||||||
- type: divider
|
- type: divider
|
||||||
- input_number.boiler_wwtemp
|
- sensor.warm_water_selected_temperature
|
||||||
- sensor.warm_water_current_temperature
|
- sensor.warm_water_current_temperature
|
||||||
- sensor.warm_water_activated
|
- sensor.warm_water_activated
|
||||||
- sensor.warm_water_3way_valve
|
- sensor.warm_water_3way_valve
|
||||||
@@ -31,7 +31,7 @@ views:
|
|||||||
cards:
|
cards:
|
||||||
- type: entities
|
- type: entities
|
||||||
title: Shower Monitor
|
title: Shower Monitor
|
||||||
show_header_toggle: true
|
show_header_toggle: false
|
||||||
entities:
|
entities:
|
||||||
- switch.shower_timer
|
- switch.shower_timer
|
||||||
- switch.long_shower_alert
|
- switch.long_shower_alert
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 72 KiB |
@@ -166,9 +166,10 @@ void showInfo() {
|
|||||||
|
|
||||||
myDebug("\n # EMS type handlers: %d\n", ems_getEmsTypesCount());
|
myDebug("\n # EMS type handlers: %d\n", ems_getEmsTypesCount());
|
||||||
|
|
||||||
myDebug(" Thermostat is %s, Poll is %s, Shower timer is %s, Shower alert is %s\n",
|
myDebug(" Thermostat is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s\n",
|
||||||
((Boiler_Status.thermostat_enabled) ? "enabled" : "disabled"),
|
((Boiler_Status.thermostat_enabled) ? "enabled" : "disabled"),
|
||||||
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
||||||
|
((EMS_Sys_Status.emsTxEnabled) ? "enabled" : "disabled"),
|
||||||
((Boiler_Status.shower_timer) ? "enabled" : "disabled"),
|
((Boiler_Status.shower_timer) ? "enabled" : "disabled"),
|
||||||
((Boiler_Status.shower_alert) ? "enabled" : "disabled"));
|
((Boiler_Status.shower_alert) ? "enabled" : "disabled"));
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
[platformio]
|
[platformio]
|
||||||
|
; change this for your ESP8266 device
|
||||||
env_default = nodemcuv2
|
env_default = nodemcuv2
|
||||||
; env_default = d1_mini
|
; env_default = d1_mini
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
; optional flags are -DUSE_LED -DSHOWER_TEST -DDEBUG -DUSE_SERIAL -DNO_TX
|
; optional flags are -DUSE_LED -DSHOWER_TEST -DUSE_SERIAL
|
||||||
build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=400
|
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"'
|
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 =
|
lib_deps =
|
||||||
@@ -12,6 +13,7 @@ lib_deps =
|
|||||||
PubSubClient
|
PubSubClient
|
||||||
ArduinoJson
|
ArduinoJson
|
||||||
CRC32
|
CRC32
|
||||||
|
CircularBuffer
|
||||||
|
|
||||||
[env:nodemcuv2]
|
[env:nodemcuv2]
|
||||||
board = nodemcuv2
|
board = nodemcuv2
|
||||||
@@ -22,6 +24,10 @@ build_flags = ${common.build_flags} ${common.build_flags_custom}
|
|||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
; comment out next line if using USB and not OTA
|
; comment out next line if using USB and not OTA
|
||||||
upload_port = "boiler."
|
upload_port = "boiler."
|
||||||
|
; examples....
|
||||||
|
;upload_port = "boiler"
|
||||||
|
;upload_port = "boiler.local"
|
||||||
|
;upload_port = 10.10.10.6
|
||||||
|
|
||||||
[env:d1_mini]
|
[env:d1_mini]
|
||||||
board = d1_mini
|
board = d1_mini
|
||||||
@@ -32,6 +38,8 @@ build_flags = ${common.build_flags} ${common.build_flags_custom}
|
|||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
; comment out next line if using USB and not OTA
|
; comment out next line if using USB and not OTA
|
||||||
upload_port = "boiler."
|
upload_port = "boiler."
|
||||||
|
; examples....
|
||||||
;upload_port = "boiler"
|
;upload_port = "boiler"
|
||||||
;upload_port = "boiler.local"
|
;upload_port = "boiler.local"
|
||||||
|
;upload_port = 10.10.10.6
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ ESPHelper::ESPHelper(netInfo * startingNet) {
|
|||||||
//start the wifi & mqtt systems and attempt connection (currently blocking)
|
//start the wifi & mqtt systems and attempt connection (currently blocking)
|
||||||
//true on: parameter check validated
|
//true on: parameter check validated
|
||||||
//false on: parameter check failed
|
//false on: parameter check failed
|
||||||
bool ESPHelper::begin(const char * hostname) {
|
bool ESPHelper::begin(const char * hostname, const char * app_name, const char * app_version) {
|
||||||
#ifdef USE_SERIAL1
|
#ifdef USE_SERIAL1
|
||||||
Serial1.begin(115200);
|
Serial1.begin(115200);
|
||||||
Serial1.setDebugOutput(true);
|
Serial1.setDebugOutput(true);
|
||||||
@@ -85,6 +85,10 @@ bool ESPHelper::begin(const char * hostname) {
|
|||||||
strcpy(_hostname, hostname);
|
strcpy(_hostname, hostname);
|
||||||
OTA_enable();
|
OTA_enable();
|
||||||
|
|
||||||
|
strcpy(_app_name, app_name); // app name
|
||||||
|
strcpy(_app_version, app_version); // app version
|
||||||
|
|
||||||
|
|
||||||
setBoottime("<unknown>");
|
setBoottime("<unknown>");
|
||||||
|
|
||||||
if (_ssidSet) {
|
if (_ssidSet) {
|
||||||
@@ -176,7 +180,7 @@ bool ESPHelper::begin(const char * hostname) {
|
|||||||
|
|
||||||
consoleShowHelp(); // show this at bootup
|
consoleShowHelp(); // show this at bootup
|
||||||
|
|
||||||
//mark the system as started and return
|
// mark the system as started and return
|
||||||
_hasBegun = true;
|
_hasBegun = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -341,6 +345,12 @@ void ESPHelper::setWifiCallback(void (*callback)()) {
|
|||||||
_wifiCallbackSet = true;
|
_wifiCallbackSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//sets a custom function to run when telnet is started
|
||||||
|
void ESPHelper::setInitCallback(void (*callback)()) {
|
||||||
|
_initCallback = callback;
|
||||||
|
_initCallbackSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
//attempts to connect to wifi & mqtt server if not connected
|
//attempts to connect to wifi & mqtt server if not connected
|
||||||
void ESPHelper::reconnect() {
|
void ESPHelper::reconnect() {
|
||||||
static uint8_t tryCount = 0;
|
static uint8_t tryCount = 0;
|
||||||
@@ -614,6 +624,8 @@ void ESPHelper::consoleHandle() {
|
|||||||
// Show the initial message
|
// Show the initial message
|
||||||
consoleShowHelp();
|
consoleShowHelp();
|
||||||
|
|
||||||
|
_initCallback(); // call callback to set any custom things
|
||||||
|
|
||||||
// Empty buffer
|
// Empty buffer
|
||||||
while (telnetClient.available()) {
|
while (telnetClient.available()) {
|
||||||
telnetClient.read();
|
telnetClient.read();
|
||||||
@@ -655,7 +667,7 @@ void ESPHelper::consoleHandle() {
|
|||||||
|
|
||||||
// Inactivity - close connection if not received commands from user in telnet to reduce overheads
|
// Inactivity - close connection if not received commands from user in telnet to reduce overheads
|
||||||
if ((millis() - _lastTimeCommand) > MAX_TIME_INACTIVE) {
|
if ((millis() - _lastTimeCommand) > MAX_TIME_INACTIVE) {
|
||||||
telnetClient.println("* Closing session due to inactivity");
|
telnetClient.println("* Closing telnet session due to inactivity");
|
||||||
telnetClient.flush();
|
telnetClient.flush();
|
||||||
telnetClient.stop();
|
telnetClient.stop();
|
||||||
_telnetConnected = false;
|
_telnetConnected = false;
|
||||||
@@ -749,6 +761,10 @@ void ESPHelper::consoleShowHelp() {
|
|||||||
help += "* Connected to WiFi AP: " + WiFi.SSID() + "\n\r";
|
help += "* Connected to WiFi AP: " + WiFi.SSID() + "\n\r";
|
||||||
help += "* Boot time: ";
|
help += "* Boot time: ";
|
||||||
help.concat(_boottime);
|
help.concat(_boottime);
|
||||||
|
help += "\n\r* ";
|
||||||
|
help.concat(_app_name);
|
||||||
|
help += " Version ";
|
||||||
|
help.concat(_app_version);
|
||||||
help += "\n\r* Free RAM: ";
|
help += "\n\r* Free RAM: ";
|
||||||
help.concat(ESP.getFreeHeap());
|
help.concat(ESP.getFreeHeap());
|
||||||
help += " bytes\n\r";
|
help += " bytes\n\r";
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ class ESPHelper : public Print {
|
|||||||
|
|
||||||
ESPHelper(netInfo * startingNet);
|
ESPHelper(netInfo * startingNet);
|
||||||
|
|
||||||
bool begin(const char * hostname);
|
bool begin(const char * hostname, const char * app_name, const char * app_version);
|
||||||
|
|
||||||
void end();
|
void end();
|
||||||
|
|
||||||
void useSecureClient(const char * fingerprint);
|
void useSecureClient(const char * fingerprint);
|
||||||
@@ -110,6 +111,7 @@ class ESPHelper : public Print {
|
|||||||
void setMQTTCallback(MQTT_CALLBACK_SIGNATURE);
|
void setMQTTCallback(MQTT_CALLBACK_SIGNATURE);
|
||||||
|
|
||||||
void setWifiCallback(void (*callback)());
|
void setWifiCallback(void (*callback)());
|
||||||
|
void setInitCallback(void (*callback)());
|
||||||
|
|
||||||
void sendHACommand(const char * s);
|
void sendHACommand(const char * s);
|
||||||
void sendStart();
|
void sendStart();
|
||||||
@@ -162,6 +164,8 @@ class ESPHelper : public Print {
|
|||||||
char _clientName[40];
|
char _clientName[40];
|
||||||
void (*_wifiCallback)();
|
void (*_wifiCallback)();
|
||||||
bool _wifiCallbackSet = false;
|
bool _wifiCallbackSet = false;
|
||||||
|
void (*_initCallback)();
|
||||||
|
bool _initCallbackSet = false;
|
||||||
|
|
||||||
std::function<void(char *, uint8_t *, uint8_t)> _mqttCallback;
|
std::function<void(char *, uint8_t *, uint8_t)> _mqttCallback;
|
||||||
|
|
||||||
@@ -181,7 +185,7 @@ class ESPHelper : public Print {
|
|||||||
netInfo ** _netList;
|
netInfo ** _netList;
|
||||||
bool _verboseMessages = true;
|
bool _verboseMessages = true;
|
||||||
subscription _subscriptions[MAX_SUBSCRIPTIONS];
|
subscription _subscriptions[MAX_SUBSCRIPTIONS];
|
||||||
char _hostname[64];
|
char _hostname[24];
|
||||||
uint8_t _qos = DEFAULT_QOS;
|
uint8_t _qos = DEFAULT_QOS;
|
||||||
IPAddress _apIP = IPAddress(192, 168, 1, 254);
|
IPAddress _apIP = IPAddress(192, 168, 1, 254);
|
||||||
void changeNetwork();
|
void changeNetwork();
|
||||||
@@ -190,7 +194,9 @@ class ESPHelper : public Print {
|
|||||||
void resubscribe();
|
void resubscribe();
|
||||||
uint8_t setConnectionStatus();
|
uint8_t setConnectionStatus();
|
||||||
|
|
||||||
char _boottime[50];
|
char _boottime[24];
|
||||||
|
char _app_name[24];
|
||||||
|
char _app_version[10];
|
||||||
|
|
||||||
// console/telnet specific
|
// console/telnet specific
|
||||||
WiFiClient telnetClient;
|
WiFiClient telnetClient;
|
||||||
|
|||||||
359
src/boiler.ino
359
src/boiler.ino
@@ -11,6 +11,7 @@
|
|||||||
#include "ems.h"
|
#include "ems.h"
|
||||||
#include "emsuart.h"
|
#include "emsuart.h"
|
||||||
#include "my_config.h"
|
#include "my_config.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
// public libraries
|
// public libraries
|
||||||
#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson
|
#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson
|
||||||
@@ -20,13 +21,13 @@
|
|||||||
#include <Ticker.h> // https://github.com/esp8266/Arduino/tree/master/libraries/Ticker
|
#include <Ticker.h> // https://github.com/esp8266/Arduino/tree/master/libraries/Ticker
|
||||||
|
|
||||||
// timers, all values are in seconds
|
// timers, all values are in seconds
|
||||||
#define PUBLISHVALUES_TIME 300 // every 5 mins post HA values
|
#define PUBLISHVALUES_TIME 120 // every 2 minutes post HA values
|
||||||
Ticker publishValuesTimer;
|
Ticker publishValuesTimer;
|
||||||
|
|
||||||
#define SYSTEMCHECK_TIME 10 // every 10 seconds check if Boiler is online and execute other requests
|
#define SYSTEMCHECK_TIME 10 // every 10 seconds check if Boiler is online and execute other requests
|
||||||
Ticker systemCheckTimer;
|
Ticker systemCheckTimer;
|
||||||
|
|
||||||
#define REGULARUPDATES_TIME 60 // every minute a call is made, so for our 2 calls theres a write cmd every 30seconds
|
#define REGULARUPDATES_TIME 60 // every minute a call is made
|
||||||
Ticker regularUpdatesTimer;
|
Ticker regularUpdatesTimer;
|
||||||
|
|
||||||
#define HEARTBEAT_TIME 1 // every second blink heartbeat LED
|
#define HEARTBEAT_TIME 1 // every second blink heartbeat LED
|
||||||
@@ -38,10 +39,6 @@ Ticker scanThermostat;
|
|||||||
uint8_t scanThermostat_count;
|
uint8_t scanThermostat_count;
|
||||||
|
|
||||||
Ticker showerColdShotStopTimer;
|
Ticker showerColdShotStopTimer;
|
||||||
uint8_t regularUpdatesCount = 0;
|
|
||||||
|
|
||||||
#define MAX_MANUAL_CALLS 2 // number of ems reads we do during the fetch cycle (in regularUpdates)
|
|
||||||
|
|
||||||
|
|
||||||
// GPIOs
|
// GPIOs
|
||||||
#define LED_HEARTBEAT LED_BUILTIN // onboard LED
|
#define LED_HEARTBEAT LED_BUILTIN // onboard LED
|
||||||
@@ -63,8 +60,6 @@ uint8_t regularUpdatesCount = 0;
|
|||||||
|
|
||||||
// boiler
|
// boiler
|
||||||
#define TOPIC_BOILER_DATA MQTT_BOILER "boiler_data" // for sending boiler values
|
#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_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
|
#define TOPIC_BOILER_HEATING_ACTIVE MQTT_BOILER "heating_active" // if heating is on
|
||||||
|
|
||||||
@@ -94,8 +89,6 @@ const unsigned long SHOWER_OFFSET_TIME = 0; // 0 seconds grace time, to ca
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool wifi_connected;
|
bool wifi_connected;
|
||||||
bool boiler_online;
|
|
||||||
bool thermostat_enabled;
|
|
||||||
bool shower_timer; // true if we want to report back on shower times
|
bool shower_timer; // true if we want to report back on shower times
|
||||||
bool shower_alert; // true if we want the cold water reminder
|
bool shower_alert; // true if we want the cold water reminder
|
||||||
} _Boiler_Status;
|
} _Boiler_Status;
|
||||||
@@ -115,25 +108,27 @@ netInfo homeNet = {.mqttHost = MQTT_IP,
|
|||||||
.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);
|
||||||
|
|
||||||
command_t PROGMEM project_cmds[] = {
|
command_t PROGMEM project_cmds[] = {
|
||||||
|
|
||||||
{"v [n]", "set logging (0=none, 1=basic, 2=thermostat only, 3=verbose)"},
|
{"l [n]", "set logging (0=none, 1=raw, 2=basic, 3=thermostat only, 4=verbose)"},
|
||||||
{"s", "show statistics"},
|
{"s", "show statistics"},
|
||||||
{"h", "list supported EMS telegram type IDs"},
|
{"h", "list supported EMS telegram type IDs"},
|
||||||
{"P", "publish all stat to MQTT"},
|
{"M", "publish to MQTT"},
|
||||||
{"p", "toggle EMS Poll response on/off"},
|
{"Q", "print Tx Queue"},
|
||||||
|
{"P", "toggle EMS Poll response on/off"},
|
||||||
|
{"X", "toggle EMS Tx transmission on/off"},
|
||||||
{"S", "toggle Shower timer on/off"},
|
{"S", "toggle Shower timer on/off"},
|
||||||
{"A", "toggle shower Alert on/off"},
|
{"A", "toggle shower Alert on/off"},
|
||||||
{"b [xx]", "boiler request (xx=telegram type ID)"},
|
{"r [s]", "send raw telegram to EMS (s=XX XX XX...)"},
|
||||||
|
{"b [xx]", "send boiler read request (xx=telegram type ID in hex)"},
|
||||||
|
{"t [xx]", "send thermostat read request (xx=telegram type ID in hex)"},
|
||||||
{"w [nn]", "set boiler warm water temperature (min 30)"},
|
{"w [nn]", "set boiler warm water temperature (min 30)"},
|
||||||
{"a [n]", "boiler warm water (1=on, 2=off)"},
|
{"a [n]", "set boiler warm tap water (0=off, 1=on)"},
|
||||||
{"t [xx]", "thermostat request (xx=telegram type ID)"},
|
|
||||||
{"T [xx]", "set thermostat temperature"},
|
{"T [xx]", "set thermostat temperature"},
|
||||||
{"m [n]", "set thermostat mode (1=manual, 2=auto)"},
|
{"m [n]", "set thermostat mode (1=manual, 2=auto)"}
|
||||||
{"x [xx]", "experimental code for debugging."}
|
//{"U [c]", "do a thermostat scan on all ids (c=start id) for debugging only"}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -160,6 +155,7 @@ const unsigned long TX_HOLD_LED_TIME = 2000; // how long to hold the Tx LED bec
|
|||||||
|
|
||||||
unsigned long timestamp; // for internal timings, via millis()
|
unsigned long timestamp; // for internal timings, via millis()
|
||||||
static int connectionStatus = NO_CONNECTION;
|
static int connectionStatus = NO_CONNECTION;
|
||||||
|
int boilerStatus = false;
|
||||||
bool startMQTTsent = false;
|
bool startMQTTsent = false;
|
||||||
|
|
||||||
uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
|
uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
|
||||||
@@ -172,18 +168,14 @@ void myDebugLog(const char * s) {
|
|||||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||||
myDebug("%s\n", s);
|
myDebug("%s\n", s);
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
|
||||||
myESP.logger(LOG_HA, s);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert float to char
|
// convert float to char
|
||||||
//char * _float_to_char(char * a, float f, uint8_t precision = 1);
|
|
||||||
char * _float_to_char(char * a, float f, uint8_t precision = 1) {
|
char * _float_to_char(char * a, float f, uint8_t precision = 1) {
|
||||||
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), which has a -1 value
|
// check for 0x8000 (sensor missing)
|
||||||
if (f == EMS_VALUE_FLOAT_NOTSET) {
|
if (f == EMS_VALUE_FLOAT_NOTSET) {
|
||||||
strcpy(ret, "?");
|
strcpy(ret, "?");
|
||||||
} else {
|
} else {
|
||||||
@@ -272,58 +264,20 @@ void showInfo() {
|
|||||||
|
|
||||||
myDebug("\n # EMS type handlers: %d\n", ems_getEmsTypesCount());
|
myDebug("\n # EMS type handlers: %d\n", ems_getEmsTypesCount());
|
||||||
|
|
||||||
myDebug(" Thermostat is %s, Poll is %s, Shower timer is %s, Shower alert is %s\n",
|
myDebug(" Thermostat is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s\n",
|
||||||
((Boiler_Status.thermostat_enabled) ? "enabled" : "disabled"),
|
(ems_getThermostatEnabled() ? "enabled" : "disabled"),
|
||||||
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
||||||
|
((EMS_Sys_Status.emsTxEnabled) ? "enabled" : "disabled"),
|
||||||
((Boiler_Status.shower_timer) ? "enabled" : "disabled"),
|
((Boiler_Status.shower_timer) ? "enabled" : "disabled"),
|
||||||
((Boiler_Status.shower_alert) ? "enabled" : "disabled"));
|
((Boiler_Status.shower_alert) ? "enabled" : "disabled"));
|
||||||
|
|
||||||
myDebug(" EMS Bus Stats: Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d, ",
|
myDebug(" EMS Bus Stats: Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d\n",
|
||||||
(Boiler_Status.boiler_online ? "yes" : "no"),
|
(ems_getBoilerEnabled() ? "yes" : "no"),
|
||||||
EMS_Sys_Status.emsRxPgks,
|
EMS_Sys_Status.emsRxPgks,
|
||||||
EMS_Sys_Status.emsTxPkgs,
|
EMS_Sys_Status.emsTxPkgs,
|
||||||
EMS_Sys_Status.emxCrcErr);
|
EMS_Sys_Status.emxCrcErr);
|
||||||
|
|
||||||
myDebug("Rx Status=");
|
myDebug("\n%sBoiler stats:%s\n", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||||
switch (EMS_Sys_Status.emsRxStatus) {
|
|
||||||
case EMS_RX_IDLE:
|
|
||||||
myDebug("idle");
|
|
||||||
break;
|
|
||||||
case EMS_RX_ACTIVE:
|
|
||||||
myDebug("active");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
myDebug(", Tx Status=");
|
|
||||||
switch (EMS_Sys_Status.emsTxStatus) {
|
|
||||||
case EMS_TX_IDLE:
|
|
||||||
myDebug("idle");
|
|
||||||
break;
|
|
||||||
case EMS_TX_PENDING:
|
|
||||||
myDebug("pending");
|
|
||||||
break;
|
|
||||||
case EMS_TX_ACTIVE:
|
|
||||||
myDebug("active");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
myDebug(", Last Tx Action=");
|
|
||||||
switch (EMS_TxTelegram.action) {
|
|
||||||
case EMS_TX_READ:
|
|
||||||
myDebug("read");
|
|
||||||
break;
|
|
||||||
case EMS_TX_WRITE:
|
|
||||||
myDebug("write");
|
|
||||||
break;
|
|
||||||
case EMS_TX_VALIDATE:
|
|
||||||
myDebug("validate");
|
|
||||||
break;
|
|
||||||
case EMS_TX_NONE:
|
|
||||||
myDebug("none");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
myDebug("\n\n%sBoiler stats:%s\n", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
|
||||||
|
|
||||||
// active stats
|
// active stats
|
||||||
myDebug(" Hot tap water is %s\n", (EMS_Boiler.tapwaterActive ? "running" : "off"));
|
myDebug(" Hot tap water is %s\n", (EMS_Boiler.tapwaterActive ? "running" : "off"));
|
||||||
@@ -373,15 +327,22 @@ void showInfo() {
|
|||||||
EMS_Boiler.heatWorkMin % 60);
|
EMS_Boiler.heatWorkMin % 60);
|
||||||
|
|
||||||
// Thermostat stats
|
// Thermostat stats
|
||||||
if (Boiler_Status.thermostat_enabled) {
|
if (ems_getThermostatEnabled()) {
|
||||||
myDebug("\n%sThermostat stats:%s\n", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
myDebug("\n%sThermostat stats:%s\n", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||||
myDebug(" Thermostat time is %02d:%02d:%02d %d/%d/%d\n",
|
myDebug(" Thermostat type: ");
|
||||||
|
ems_printThermostatType();
|
||||||
|
myDebug("\n Thermostat time is ");
|
||||||
|
if (EMS_ID_THERMOSTAT != EMS_ID_THERMOSTAT_EASY) {
|
||||||
|
myDebug("%02d:%02d:%02d %d/%d/%d\n",
|
||||||
EMS_Thermostat.hour,
|
EMS_Thermostat.hour,
|
||||||
EMS_Thermostat.minute,
|
EMS_Thermostat.minute,
|
||||||
EMS_Thermostat.second,
|
EMS_Thermostat.second,
|
||||||
EMS_Thermostat.day,
|
EMS_Thermostat.day,
|
||||||
EMS_Thermostat.month,
|
EMS_Thermostat.month,
|
||||||
EMS_Thermostat.year + 2000);
|
EMS_Thermostat.year + 2000);
|
||||||
|
} else {
|
||||||
|
myDebug("<not supported>\n");
|
||||||
|
}
|
||||||
|
|
||||||
_renderFloatValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp);
|
_renderFloatValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp);
|
||||||
_renderFloatValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp);
|
_renderFloatValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp);
|
||||||
@@ -444,11 +405,9 @@ void publishValues(bool force) {
|
|||||||
crc.update(data[i]);
|
crc.update(data[i]);
|
||||||
}
|
}
|
||||||
uint32_t checksum = crc.finalize();
|
uint32_t checksum = crc.finalize();
|
||||||
//myDebug("Boiler HASH=%d %08x, len=%d, s=%s\n", checksum, checksum, len, data);
|
|
||||||
|
|
||||||
if ((previousBoilerPublishCRC != checksum) || force) {
|
if ((previousBoilerPublishCRC != checksum) || force) {
|
||||||
previousBoilerPublishCRC = checksum;
|
previousBoilerPublishCRC = checksum;
|
||||||
if (ems_getLogging() == EMS_SYS_LOGGING_VERBOSE) {
|
if (ems_getLogging() >= EMS_SYS_LOGGING_BASIC) {
|
||||||
myDebug("Publishing boiler data via MQTT\n");
|
myDebug("Publishing boiler data via MQTT\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,8 +417,8 @@ void publishValues(bool force) {
|
|||||||
|
|
||||||
// see if the heating or hot tap water has changed, if so send
|
// 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
|
// last_boilerActive stores heating in bit 1 and tap water in bit 2
|
||||||
if (last_boilerActive != ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive)) {
|
if ((last_boilerActive != ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive)) || force) {
|
||||||
if (ems_getLogging() == EMS_SYS_LOGGING_VERBOSE) {
|
if (ems_getLogging() >= EMS_SYS_LOGGING_BASIC) {
|
||||||
myDebug("Publishing hot water and heating state via MQTT\n");
|
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_TAPWATER_ACTIVE, EMS_Boiler.tapwaterActive == 1 ? "1" : "0");
|
||||||
@@ -468,9 +427,8 @@ void publishValues(bool force) {
|
|||||||
last_boilerActive = ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive); // remember last state
|
last_boilerActive = ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive); // remember last state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// handle the thermostat values separately
|
// handle the thermostat values separately
|
||||||
if (EMS_Sys_Status.emsThermostatEnabled) {
|
if (ems_getThermostatEnabled()) {
|
||||||
// only send thermostat values if we actually have them
|
// only send thermostat values if we actually have them
|
||||||
if (((int)EMS_Thermostat.curr_roomTemp == (int)0) || ((int)EMS_Thermostat.setpoint_roomTemp == (int)0))
|
if (((int)EMS_Thermostat.curr_roomTemp == (int)0) || ((int)EMS_Thermostat.setpoint_roomTemp == (int)0))
|
||||||
return;
|
return;
|
||||||
@@ -498,11 +456,9 @@ void publishValues(bool force) {
|
|||||||
crc.update(data[i]);
|
crc.update(data[i]);
|
||||||
}
|
}
|
||||||
uint32_t checksum = crc.finalize();
|
uint32_t checksum = crc.finalize();
|
||||||
//myDebug("Thermostat HASH=%d %08x, len=%d, s=%s\n", checksum, checksum, len, data);
|
|
||||||
|
|
||||||
if ((previousThermostatPublishCRC != checksum) || force) {
|
if ((previousThermostatPublishCRC != checksum) || force) {
|
||||||
previousThermostatPublishCRC = checksum;
|
previousThermostatPublishCRC = checksum;
|
||||||
if (ems_getLogging() == EMS_SYS_LOGGING_VERBOSE) {
|
if (ems_getLogging() >= EMS_SYS_LOGGING_BASIC) {
|
||||||
myDebug("Publishing thermostat data via MQTT\n");
|
myDebug("Publishing thermostat data via MQTT\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,11 +494,15 @@ void myDebugCallback() {
|
|||||||
case 's':
|
case 's':
|
||||||
showInfo();
|
showInfo();
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'P': // toggle Poll
|
||||||
b = !ems_getPoll();
|
b = !ems_getPoll();
|
||||||
ems_setPoll(b);
|
ems_setPoll(b);
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'X': // toggle Tx
|
||||||
|
b = !ems_getTxEnabled();
|
||||||
|
ems_setTxEnabled(b);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
//myESP.logger(LOG_HA, "Force publish values");
|
//myESP.logger(LOG_HA, "Force publish values");
|
||||||
publishValues(true);
|
publishValues(true);
|
||||||
break;
|
break;
|
||||||
@@ -557,6 +517,9 @@ void myDebugCallback() {
|
|||||||
Boiler_Status.shower_alert = !Boiler_Status.shower_alert;
|
Boiler_Status.shower_alert = !Boiler_Status.shower_alert;
|
||||||
myESP.publish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
myESP.publish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
||||||
break;
|
break;
|
||||||
|
case 'Q': //print Tx Queue
|
||||||
|
ems_printTxQueue();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
myDebug("Unknown command. Use ? for help.\n");
|
myDebug("Unknown command. Use ? for help.\n");
|
||||||
break;
|
break;
|
||||||
@@ -564,9 +527,6 @@ void myDebugCallback() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < 2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// for commands with parameters, assume command is just one letter
|
// for commands with parameters, assume command is just one letter
|
||||||
switch (cmd[0]) {
|
switch (cmd[0]) {
|
||||||
case 'T': // set thermostat temp
|
case 'T': // set thermostat temp
|
||||||
@@ -581,15 +541,15 @@ void myDebugCallback() {
|
|||||||
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;
|
||||||
case 'v': // verbose
|
case 'l': // logging
|
||||||
ems_setLogging((_EMS_SYS_LOGGING)(cmd[2] - '0'));
|
ems_setLogging((_EMS_SYS_LOGGING)(cmd[2] - '0'));
|
||||||
updateHeartbeat();
|
updateHeartbeat();
|
||||||
break;
|
break;
|
||||||
case 'a': // set ww activate on or off
|
case 'a': // set ww activate on or off
|
||||||
if ((cmd[2] - '0') == 1)
|
if ((cmd[2] - '0') == 1)
|
||||||
ems_setWarmWaterActivated(true);
|
ems_setWarmTapWaterActivated(true);
|
||||||
else if ((cmd[2] - '0') == 0)
|
else if ((cmd[2] - '0') == 0)
|
||||||
ems_setWarmWaterActivated(false);
|
ems_setWarmTapWaterActivated(false);
|
||||||
break;
|
break;
|
||||||
case 'b': // boiler read command
|
case 'b': // boiler read command
|
||||||
ems_doReadCommand((uint8_t)strtol(&cmd[2], 0, 16), EMS_ID_BOILER);
|
ems_doReadCommand((uint8_t)strtol(&cmd[2], 0, 16), EMS_ID_BOILER);
|
||||||
@@ -597,13 +557,16 @@ void myDebugCallback() {
|
|||||||
case 't': // thermostat command
|
case 't': // thermostat command
|
||||||
ems_doReadCommand((uint8_t)strtol(&cmd[2], 0, 16), EMS_ID_THERMOSTAT);
|
ems_doReadCommand((uint8_t)strtol(&cmd[2], 0, 16), EMS_ID_THERMOSTAT);
|
||||||
break;
|
break;
|
||||||
|
case 'r': // send raw data
|
||||||
|
ems_sendRawTelegram(&cmd[2]);
|
||||||
|
break;
|
||||||
case 'x': // experimental, not displayed!
|
case 'x': // experimental, not displayed!
|
||||||
myDebug("Calling experimental...\n");
|
myDebug("Calling experimental...\n");
|
||||||
ems_setLogging(EMS_SYS_LOGGING_VERBOSE);
|
ems_setLogging(EMS_SYS_LOGGING_VERBOSE);
|
||||||
ems_setExperimental((uint8_t)strtol(&cmd[2], 0, 16)); // takes HEX param
|
ems_setExperimental((uint8_t)strtol(&cmd[2], 0, 16)); // takes HEX param
|
||||||
break;
|
break;
|
||||||
case 'U': // thermostat scan
|
case 'U': // thermostat scan
|
||||||
myDebug("Doing a scan on thermostat IDs\n");
|
myDebug("Doing a type ID scan on thermostat...\n");
|
||||||
ems_setLogging(EMS_SYS_LOGGING_THERMOSTAT);
|
ems_setLogging(EMS_SYS_LOGGING_THERMOSTAT);
|
||||||
publishValuesTimer.detach();
|
publishValuesTimer.detach();
|
||||||
systemCheckTimer.detach();
|
systemCheckTimer.detach();
|
||||||
@@ -651,18 +614,6 @@ void MQTTcallback(char * topic, byte * payload, uint8_t length) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// boiler_warm_water_selected_temperature
|
|
||||||
if (strcmp(topic, TOPIC_BOILER_WARM_WATER_SELECTED_TEMPERATURE) == 0) {
|
|
||||||
uint8_t i = strtol((char *)payload, 0, 10);
|
|
||||||
myDebug("MQTT topic: boiler_warm_water_selected_temperature value %d\n", i);
|
|
||||||
#ifndef NO_TX
|
|
||||||
ems_setWarmWaterTemp(i);
|
|
||||||
#endif
|
|
||||||
// publish back so HA is immediately updated
|
|
||||||
publishValues(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// shower timer
|
// shower timer
|
||||||
if (strcmp(topic, TOPIC_SHOWER_TIMER) == 0) {
|
if (strcmp(topic, TOPIC_SHOWER_TIMER) == 0) {
|
||||||
if (payload[0] == '1') {
|
if (payload[0] == '1') {
|
||||||
@@ -687,9 +638,7 @@ void MQTTcallback(char * topic, byte * payload, uint8_t length) {
|
|||||||
|
|
||||||
// shower cold shot
|
// shower cold shot
|
||||||
if (strcmp(topic, TOPIC_SHOWER_COLDSHOT) == 0) {
|
if (strcmp(topic, TOPIC_SHOWER_COLDSHOT) == 0) {
|
||||||
if (Boiler_Status.shower_alert) {
|
|
||||||
_showerColdShotStart();
|
_showerColdShotStart();
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,15 +652,16 @@ void MQTTcallback(char * topic, byte * payload, uint8_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init callback, which is used to set functions and call methods when telnet has started
|
||||||
|
void InitCallback() {
|
||||||
|
ems_setLogging(BOILER_DEFAULT_LOGGING); // turn off logging as default startup
|
||||||
|
}
|
||||||
|
|
||||||
// WifiCallback, called when a WiFi connect has successfully been established
|
// WifiCallback, called when a WiFi connect has successfully been established
|
||||||
void WIFIcallback() {
|
void WIFIcallback() {
|
||||||
Boiler_Status.wifi_connected = true;
|
Boiler_Status.wifi_connected = true;
|
||||||
|
|
||||||
#ifdef USE_LED
|
#ifdef USE_LED
|
||||||
// turn off the LEDs since we've finished the boot loading
|
|
||||||
digitalWrite(LED_RX, LOW);
|
|
||||||
digitalWrite(LED_TX, LOW);
|
|
||||||
digitalWrite(LED_ERR, LOW);
|
|
||||||
digitalWrite(LED_HEARTBEAT, HIGH);
|
digitalWrite(LED_HEARTBEAT, HIGH);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -727,23 +677,20 @@ void updateHeartbeat() {
|
|||||||
} else {
|
} else {
|
||||||
heartbeatEnabled = false;
|
heartbeatEnabled = false;
|
||||||
#ifdef USE_LED
|
#ifdef USE_LED
|
||||||
// ...and turn off LED
|
digitalWrite(LED_HEARTBEAT, HIGH); // ...and turn off LED
|
||||||
digitalWrite(LED_HEARTBEAT, HIGH);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the boiler settings
|
// Initialize the boiler settings
|
||||||
void _initBoiler() {
|
void initBoiler() {
|
||||||
// default settings
|
// default settings
|
||||||
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
|
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
|
||||||
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
|
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
|
||||||
Boiler_Status.thermostat_enabled = BOILER_THERMOSTAT_ENABLED;
|
ems_setThermostatEnabled(BOILER_THERMOSTAT_ENABLED);
|
||||||
ems_setThermostatEnabled(Boiler_Status.thermostat_enabled);
|
|
||||||
|
|
||||||
// init boiler
|
// init boiler
|
||||||
Boiler_Status.wifi_connected = false;
|
Boiler_Status.wifi_connected = false;
|
||||||
Boiler_Status.boiler_online = false;
|
|
||||||
|
|
||||||
// init shower
|
// init shower
|
||||||
Boiler_Shower.timerStart = 0;
|
Boiler_Shower.timerStart = 0;
|
||||||
@@ -768,13 +715,7 @@ void do_publishValues() {
|
|||||||
void setup() {
|
void setup() {
|
||||||
#ifdef USE_LED
|
#ifdef USE_LED
|
||||||
// set pin for LEDs - start up with all lit up while we sort stuff out
|
// set pin for LEDs - start up with all lit up while we sort stuff out
|
||||||
pinMode(LED_RX, OUTPUT);
|
|
||||||
pinMode(LED_TX, OUTPUT);
|
|
||||||
pinMode(LED_ERR, OUTPUT);
|
|
||||||
pinMode(LED_HEARTBEAT, OUTPUT);
|
pinMode(LED_HEARTBEAT, OUTPUT);
|
||||||
digitalWrite(LED_RX, HIGH);
|
|
||||||
digitalWrite(LED_TX, HIGH);
|
|
||||||
digitalWrite(LED_ERR, HIGH);
|
|
||||||
digitalWrite(LED_HEARTBEAT, LOW); // onboard LED is on
|
digitalWrite(LED_HEARTBEAT, LOW); // onboard LED is on
|
||||||
heartbeatTimer.attach(HEARTBEAT_TIME, heartbeat); // blink heartbeat LED
|
heartbeatTimer.attach(HEARTBEAT_TIME, heartbeat); // blink heartbeat LED
|
||||||
#endif
|
#endif
|
||||||
@@ -782,10 +723,7 @@ void setup() {
|
|||||||
// Timers using Ticker library
|
// Timers using Ticker library
|
||||||
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post HA values
|
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post HA values
|
||||||
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
|
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
|
||||||
|
regularUpdatesTimer.attach(REGULARUPDATES_TIME, regularUpdates); // regular reads from the EMS
|
||||||
#ifndef NO_TX
|
|
||||||
regularUpdatesTimer.attach((REGULARUPDATES_TIME / MAX_MANUAL_CALLS), regularUpdates); // regular reads from the EMS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// set up WiFi
|
// set up WiFi
|
||||||
myESP.setWifiCallback(WIFIcallback);
|
myESP.setWifiCallback(WIFIcallback);
|
||||||
@@ -798,42 +736,20 @@ void setup() {
|
|||||||
myESP.addSubscription(TOPIC_THERMOSTAT_CMD_MODE);
|
myESP.addSubscription(TOPIC_THERMOSTAT_CMD_MODE);
|
||||||
myESP.addSubscription(TOPIC_SHOWER_TIMER);
|
myESP.addSubscription(TOPIC_SHOWER_TIMER);
|
||||||
myESP.addSubscription(TOPIC_SHOWER_ALERT);
|
myESP.addSubscription(TOPIC_SHOWER_ALERT);
|
||||||
myESP.addSubscription(TOPIC_BOILER_WARM_WATER_SELECTED_TEMPERATURE);
|
|
||||||
myESP.addSubscription(TOPIC_BOILER_TAPWATER_ACTIVE);
|
myESP.addSubscription(TOPIC_BOILER_TAPWATER_ACTIVE);
|
||||||
myESP.addSubscription(TOPIC_BOILER_HEATING_ACTIVE);
|
myESP.addSubscription(TOPIC_BOILER_HEATING_ACTIVE);
|
||||||
myESP.addSubscription(TOPIC_SHOWER_COLDSHOT);
|
myESP.addSubscription(TOPIC_SHOWER_COLDSHOT);
|
||||||
|
|
||||||
myESP.consoleSetCallBackProjectCmds(project_cmds, ArraySize(project_cmds), myDebugCallback); // set up Telnet commands
|
myESP.setInitCallback(InitCallback);
|
||||||
myESP.begin(HOSTNAME); // start wifi and mqtt services
|
|
||||||
|
|
||||||
// init ems stats
|
myESP.consoleSetCallBackProjectCmds(project_cmds, ArraySize(project_cmds), myDebugCallback); // set up Telnet commands
|
||||||
|
myESP.begin(HOSTNAME, APP_NAME, APP_VERSION); // start wifi and mqtt services
|
||||||
|
|
||||||
|
// init ems statisitcs
|
||||||
ems_init();
|
ems_init();
|
||||||
|
|
||||||
// init Boiler specific params
|
// init Boiler specific parameters
|
||||||
_initBoiler();
|
initBoiler();
|
||||||
}
|
|
||||||
|
|
||||||
// flash LEDs
|
|
||||||
// Using a faster way to write to pins as digitalWrite does a lot of overhead like pin checking & disabling interrupts
|
|
||||||
void showLEDs() {
|
|
||||||
#ifdef USE_LED
|
|
||||||
// ERR LED
|
|
||||||
if (!Boiler_Status.boiler_online) {
|
|
||||||
WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 4, (1 << LED_ERR)); // turn on
|
|
||||||
EMS_Sys_Status.emsRxStatus = EMS_RX_IDLE;
|
|
||||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE;
|
|
||||||
} else {
|
|
||||||
WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + 8, (1 << LED_ERR)); // turn off
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rx LED
|
|
||||||
WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + ((EMS_Sys_Status.emsRxStatus == EMS_RX_IDLE) ? 8 : 4), (1 << LED_RX));
|
|
||||||
|
|
||||||
// Tx LED
|
|
||||||
// because sends are quick, if we did a recent send show the LED for a short while
|
|
||||||
uint64_t t = (timestamp - EMS_Sys_Status.emsLastTx);
|
|
||||||
WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + ((t < TX_HOLD_LED_TIME) ? 4 : 8), (1 << LED_TX));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// heartbeat callback to light up the LED, called via Ticker
|
// heartbeat callback to light up the LED, called via Ticker
|
||||||
@@ -856,33 +772,37 @@ void do_scanThermostat() {
|
|||||||
// do a healthcheck every now and then to see if we connections
|
// do a healthcheck every now and then to see if we connections
|
||||||
void do_systemCheck() {
|
void do_systemCheck() {
|
||||||
// first do a system check to see if there is still a connection to the EMS
|
// first do a system check to see if there is still a connection to the EMS
|
||||||
Boiler_Status.boiler_online = ((timestamp - EMS_Sys_Status.emsLastPoll) < POLL_TIMEOUT_ERR);
|
if (!ems_getBoilerEnabled()) {
|
||||||
if (!Boiler_Status.boiler_online) {
|
myDebug("Error! Unable to connect to EMS bus. Please check connections. Retry in %d seconds...\n",
|
||||||
myDebug("Error! Unable to connect to EMS bus. Please check connections. Retry in 10 seconds...\n");
|
SYSTEMCHECK_TIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMS telegrams to send after startup
|
||||||
|
void firstTimeFetch() {
|
||||||
|
ems_doReadCommand(EMS_TYPE_UBAMonitorFast, EMS_ID_BOILER); // get boiler stats which usually comes every 10 sec
|
||||||
|
ems_doReadCommand(EMS_TYPE_UBAMonitorSlow, EMS_ID_BOILER); // get boiler stats which usually comes every 60 sec
|
||||||
|
ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_ID_BOILER); // get Warm Water values
|
||||||
|
|
||||||
|
if (ems_getThermostatEnabled()) {
|
||||||
|
ems_getThermostatValues(); // get Thermostat temps (if supported)
|
||||||
|
ems_doReadCommand(EMS_TYPE_RCTime, EMS_ID_THERMOSTAT); // get Thermostat time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// force calls to get data from EMS for the types that aren't sent as broadcasts
|
// force calls to get data from EMS for the types that aren't sent as broadcasts
|
||||||
// number of calls is defined in MAX_MANUAL_CALLS
|
|
||||||
// it's done as a cycle to prevent collisions, since we can only do 1 read command at a time
|
|
||||||
void regularUpdates() {
|
void regularUpdates() {
|
||||||
uint8_t cycle = (regularUpdatesCount++ % MAX_MANUAL_CALLS);
|
|
||||||
|
|
||||||
// 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 data which are not usually automatically broadcasted
|
|
||||||
ems_getThermostatTemps();
|
|
||||||
} else if (cycle == 1) {
|
|
||||||
ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_ID_BOILER); // get Warm Water values
|
ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_ID_BOILER); // get Warm Water values
|
||||||
}
|
|
||||||
|
if (ems_getThermostatEnabled()) {
|
||||||
|
ems_getThermostatValues(); // get Thermostat temps (if supported)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn off hot water to send a shot of cold
|
// turn off hot water to send a shot of cold
|
||||||
void _showerColdShotStart() {
|
void _showerColdShotStart() {
|
||||||
myDebugLog("Shower: doing a shot of cold");
|
myDebugLog("Shower: doing a shot of cold");
|
||||||
ems_setWarmWaterActivated(false);
|
ems_setWarmTapWaterActivated(false);
|
||||||
Boiler_Shower.doingColdShot = true;
|
Boiler_Shower.doingColdShot = true;
|
||||||
// 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
|
||||||
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
|
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
|
||||||
@@ -892,61 +812,16 @@ void _showerColdShotStart() {
|
|||||||
void _showerColdShotStop() {
|
void _showerColdShotStop() {
|
||||||
if (Boiler_Shower.doingColdShot) {
|
if (Boiler_Shower.doingColdShot) {
|
||||||
myDebugLog("Shower: finished shot of cold. hot water back on");
|
myDebugLog("Shower: finished shot of cold. hot water back on");
|
||||||
ems_setWarmWaterActivated(true);
|
ems_setWarmTapWaterActivated(true);
|
||||||
Boiler_Shower.doingColdShot = false;
|
Boiler_Shower.doingColdShot = false;
|
||||||
showerColdShotStopTimer.detach();
|
showerColdShotStopTimer.detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/*
|
||||||
// Main loop
|
|
||||||
//
|
|
||||||
void loop() {
|
|
||||||
connectionStatus = myESP.loop();
|
|
||||||
timestamp = millis();
|
|
||||||
|
|
||||||
// update the Rx Tx and ERR LEDs
|
|
||||||
#ifdef USE_LED
|
|
||||||
showLEDs();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// do not continue unless we have a wifi connection
|
|
||||||
if (connectionStatus < WIFI_ONLY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if first time connected to MQTT, send welcome start message
|
|
||||||
// which will send all the state values from HA back to the clock via MQTT and return the boottime
|
|
||||||
if ((!startMQTTsent) && (connectionStatus == FULL_CONNECTION)) {
|
|
||||||
myESP.sendStart();
|
|
||||||
startMQTTsent = true;
|
|
||||||
|
|
||||||
// publish to HA the status of the Shower parameters
|
|
||||||
myESP.publish(TOPIC_SHOWER_TIMER, Boiler_Status.shower_timer ? "1" : "0");
|
|
||||||
myESP.publish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
|
||||||
|
|
||||||
#ifndef NO_TX
|
|
||||||
if (Boiler_Status.boiler_online) {
|
|
||||||
// now that we're connected lets get some data from the EMS
|
|
||||||
ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_ID_BOILER);
|
|
||||||
ems_setWarmWaterActivated(true); // make sure warm water if activated, in case it got stuck with the shower alert
|
|
||||||
} else {
|
|
||||||
myDebugLog("Boot: can't connect to EMS.");
|
|
||||||
}
|
|
||||||
#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(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Shower Logic
|
* Shower Logic
|
||||||
*/
|
*/
|
||||||
if (Boiler_Status.shower_timer) {
|
void showerCheck() {
|
||||||
// if already in cold mode, ignore all this logic until we're out of the cold blast
|
// if already in cold mode, ignore all this logic until we're out of the cold blast
|
||||||
if (!Boiler_Shower.doingColdShot) {
|
if (!Boiler_Shower.doingColdShot) {
|
||||||
// is the hot water running?
|
// is the hot water running?
|
||||||
@@ -973,8 +848,7 @@ void loop() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
// check if the shower has been on too long
|
// check if the shower has been on too long
|
||||||
else if ((((timestamp - Boiler_Shower.timerStart) > SHOWER_MAX_DURATION)
|
else if ((((timestamp - Boiler_Shower.timerStart) > SHOWER_MAX_DURATION) && !Boiler_Shower.doingColdShot)
|
||||||
&& !Boiler_Shower.doingColdShot)
|
|
||||||
&& Boiler_Status.shower_alert) {
|
&& Boiler_Status.shower_alert) {
|
||||||
myESP.sendHACommand(TOPIC_SHOWER_ALARM);
|
myESP.sendHACommand(TOPIC_SHOWER_ALARM);
|
||||||
#ifdef SHOWER_TEST
|
#ifdef SHOWER_TEST
|
||||||
@@ -1005,8 +879,7 @@ void loop() {
|
|||||||
// it is over the wait period, so assume that the shower has finished and calculate the total time and publish
|
// it is over the wait period, so assume that the shower has finished and calculate the total time and publish
|
||||||
// because its unsigned long, can't have negative so check if length is less than OFFSET_TIME
|
// because its unsigned long, can't have negative so check if length is less than OFFSET_TIME
|
||||||
if ((Boiler_Shower.timerPause - Boiler_Shower.timerStart) > SHOWER_OFFSET_TIME) {
|
if ((Boiler_Shower.timerPause - Boiler_Shower.timerStart) > SHOWER_OFFSET_TIME) {
|
||||||
Boiler_Shower.duration =
|
Boiler_Shower.duration = (Boiler_Shower.timerPause - Boiler_Shower.timerStart - SHOWER_OFFSET_TIME);
|
||||||
(Boiler_Shower.timerPause - Boiler_Shower.timerStart - SHOWER_OFFSET_TIME);
|
|
||||||
if (Boiler_Shower.duration > SHOWER_MIN_DURATION) {
|
if (Boiler_Shower.duration > SHOWER_MIN_DURATION) {
|
||||||
char s[50];
|
char s[50];
|
||||||
sprintf(s,
|
sprintf(s,
|
||||||
@@ -1032,8 +905,52 @@ void loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Main loop
|
||||||
|
//
|
||||||
|
void loop() {
|
||||||
|
connectionStatus = myESP.loop();
|
||||||
|
timestamp = millis();
|
||||||
|
|
||||||
|
// update the Rx Tx and ERR LEDs
|
||||||
|
#ifdef USE_LED
|
||||||
|
showLEDs();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// do not continue unless we have a wifi connection
|
||||||
|
if (connectionStatus < WIFI_ONLY) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// yield to prevent watchdog from timing out
|
// if this is the first time we've connected to MQTT, send a welcome start message
|
||||||
yield();
|
// which will send all the state values from HA back to the clock via MQTT and return the boottime
|
||||||
|
if ((!startMQTTsent) && (connectionStatus == FULL_CONNECTION)) {
|
||||||
|
myESP.sendStart();
|
||||||
|
startMQTTsent = true;
|
||||||
|
|
||||||
|
// publish to HA the status of the Shower parameters
|
||||||
|
myESP.publish(TOPIC_SHOWER_TIMER, Boiler_Status.shower_timer ? "1" : "0");
|
||||||
|
myESP.publish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the EMS bus has just connected, send a request to fetch some initial values
|
||||||
|
if (ems_getBoilerEnabled() && boilerStatus == false) {
|
||||||
|
boilerStatus = true;
|
||||||
|
firstTimeFetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// publish the values to MQTT, regardless if the values haven't changed
|
||||||
|
if (ems_getEmsRefreshed()) {
|
||||||
|
publishValues(true);
|
||||||
|
ems_setEmsRefreshed(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do shower logic if its enabled
|
||||||
|
if (Boiler_Status.shower_timer) {
|
||||||
|
showerCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
yield(); // yield to prevent watchdog from timing out
|
||||||
}
|
}
|
||||||
|
|||||||
1190
src/ems.cpp
1190
src/ems.cpp
File diff suppressed because it is too large
Load Diff
137
src/ems.h
137
src/ems.h
@@ -9,26 +9,27 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
// EMS IDs
|
// EMS IDs
|
||||||
#define EMS_ID_NONE 0x00 // Fixed - used as a dest in broadcast messages
|
#define EMS_ID_NONE 0x00 // Fixed - used as a dest in broadcast messages and empty type IDs
|
||||||
#define EMS_ID_BOILER 0x08 // Fixed - also known as MC10.
|
#define EMS_ID_BOILER 0x08 // Fixed - also known as MC10.
|
||||||
#define EMS_ID_ME 0x0B // Fixed - our device, hardcoded as "Service Key"
|
#define EMS_ID_ME 0x0B // Fixed - our device, hardcoded as "Service Key"
|
||||||
|
|
||||||
// Special EMS Telegram Types
|
|
||||||
#define EMS_TYPE_NONE 0x00 // none
|
|
||||||
|
|
||||||
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
|
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
|
||||||
#define EMS_MAX_TELEGRAM_LENGTH 99 // max length of a telegram, including CRC
|
#define EMS_MAX_TELEGRAM_LENGTH 99 // max length of a 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
|
||||||
|
|
||||||
#define EMS_ID_THERMOSTAT_RC20 0x17 // RC20 (older Moduline 300)
|
#define EMS_ID_THERMOSTAT_RC20 0x17 // RC20 (e.g. Moduline 300)
|
||||||
#define EMS_ID_THERMOSTAT_RC30 0x10 // RC30 (Moduline 300)
|
#define EMS_ID_THERMOSTAT_RC30 0x10 // RC30 (e.g. Moduline 400)
|
||||||
#define EMS_ID_THERMOSTAT_RC35 0x10 // RC35 (Moduline 400)
|
#define EMS_ID_THERMOSTAT_EASY 0x18 // TC100 (Nefit Easy)
|
||||||
#define EMS_ID_THERMOSTAT_EASY 0x18 // Nefit Easy
|
|
||||||
|
|
||||||
// define here the EMS telegram types you need
|
// define here the EMS telegram types you need
|
||||||
|
|
||||||
// Boiler...
|
// Common for all EMS devices
|
||||||
|
#define EMS_TYPE_Version 0x02 // version of the UBA controller (boiler)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Boiler...
|
||||||
|
*/
|
||||||
#define EMS_TYPE_UBAMonitorFast 0x18 // is an automatic monitor broadcast
|
#define EMS_TYPE_UBAMonitorFast 0x18 // is an automatic monitor broadcast
|
||||||
#define EMS_TYPE_UBAMonitorSlow 0x19 // 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_UBAMonitorWWMessage 0x34 // is an automatic monitor broadcast
|
||||||
@@ -38,26 +39,39 @@
|
|||||||
#define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15
|
#define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15
|
||||||
#define EMS_TYPE_UBAParametersMessage 0x16
|
#define EMS_TYPE_UBAParametersMessage 0x16
|
||||||
#define EMS_TYPE_UBASetPoints 0x1A
|
#define EMS_TYPE_UBASetPoints 0x1A
|
||||||
|
#define EMS_TYPE_UBAFunctionTest 0x1D
|
||||||
|
|
||||||
// Thermostat...
|
#define EMS_OFFSET_UBAParameterWW_wwtemp 2 // WW Temperature
|
||||||
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast
|
#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Thermostat...
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Common for all thermostats
|
||||||
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
|
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
|
||||||
#define EMS_TYPE_RCTempMessage 0xA3 // is an automatic thermostat broadcast
|
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
|
||||||
#define EMS_TYPE_RC20Temperature 0xA8
|
|
||||||
#define EMS_TYPE_EasyTemperature 0x0A // reading values on an Easy Thermostat
|
|
||||||
#define EMS_TYPE_Version 0x02 // version of the UBA controller (boiler)
|
|
||||||
|
|
||||||
// Offsets for specific values in a telegram, per type, used for validation
|
// RC20 specific
|
||||||
#define EMS_OFFSET_RC20Temperature_temp 0x1C // thermostat set temp
|
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
|
||||||
#define EMS_OFFSET_RC20Temperature_mode 0x17 // thermostat mode
|
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
|
||||||
#define EMS_OFFSET_UBAParameterWW_wwtemp 0x02 // WW Temperature
|
#define EMS_OFFSET_RC20Set_mode 23 // position of thermostat mode
|
||||||
#define EMS_OFFSET_UBAParameterWW_wwactivated 0x01 // WW Activated
|
#define EMS_OFFSET_RC20Set_temp 28 // position of thermostat setpoint temperature
|
||||||
|
|
||||||
|
// RC30 specific
|
||||||
|
#define EMS_TYPE_RC30StatusMessage 0x41 // is an automatic thermostat broadcast giving us temps
|
||||||
|
#define EMS_TYPE_RC30Set 0xA7 // for setting values like temp and mode
|
||||||
|
#define EMS_OFFSET_RC30Set_mode 23 // position of thermostat mode
|
||||||
|
#define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature
|
||||||
|
|
||||||
|
// Easy specific
|
||||||
|
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
|
||||||
|
|
||||||
// default values
|
// default values
|
||||||
#define EMS_VALUE_INT_ON 1 // boolean true
|
#define EMS_VALUE_INT_ON 1 // boolean true
|
||||||
#define EMS_VALUE_INT_OFF 0 // boolean false
|
#define EMS_VALUE_INT_OFF 0 // boolean false
|
||||||
#define EMS_VALUE_INT_NOTSET 0xFF // for 8-bit ints
|
#define EMS_VALUE_INT_NOTSET 0xFF // for 8-bit ints
|
||||||
#define EMS_VALUE_FLOAT_NOTSET -1 // float unset
|
#define EMS_VALUE_FLOAT_NOTSET -255 // float unset
|
||||||
|
|
||||||
/* EMS UART transfer status */
|
/* EMS UART transfer status */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -67,20 +81,23 @@ typedef enum {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EMS_TX_IDLE,
|
EMS_TX_IDLE,
|
||||||
EMS_TX_PENDING, // got Tx package to send, waiting for next Poll to send
|
EMS_TX_ACTIVE, // Tx package being sent, no break sent
|
||||||
EMS_TX_ACTIVE // Tx package being sent, no break sent
|
EMS_TX_SUCCESS,
|
||||||
|
EMS_TX_ERROR
|
||||||
} _EMS_TX_STATUS;
|
} _EMS_TX_STATUS;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EMS_TX_NONE,
|
EMS_TX_TELEGRAM_INIT, // just initialized
|
||||||
EMS_TX_READ, // doing a read request
|
EMS_TX_TELEGRAM_READ, // doing a read request
|
||||||
EMS_TX_WRITE, // doing a write request
|
EMS_TX_TELEGRAM_WRITE, // doing a write request
|
||||||
EMS_TX_VALIDATE // do a validate after a write
|
EMS_TX_TELEGRAM_VALIDATE, // do a read but only to validate the last write
|
||||||
} _EMS_TX_ACTION;
|
EMS_TX_TELEGRAM_RAW // sending in raw mode
|
||||||
|
} _EMS_TX_TELEGRAM_ACTION;
|
||||||
|
|
||||||
/* EMS logging */
|
/* EMS logging */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EMS_SYS_LOGGING_NONE, // no messages
|
EMS_SYS_LOGGING_NONE, // no messages
|
||||||
|
EMS_SYS_LOGGING_RAW, // raw data mode
|
||||||
EMS_SYS_LOGGING_BASIC, // only basic read/write messages
|
EMS_SYS_LOGGING_BASIC, // only basic read/write messages
|
||||||
EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat
|
EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat
|
||||||
EMS_SYS_LOGGING_VERBOSE // everything
|
EMS_SYS_LOGGING_VERBOSE // everything
|
||||||
@@ -94,31 +111,51 @@ typedef struct {
|
|||||||
uint16_t emsTxPkgs; // sent
|
uint16_t emsTxPkgs; // sent
|
||||||
uint16_t emxCrcErr; // CRC errors
|
uint16_t emxCrcErr; // CRC errors
|
||||||
bool emsPollEnabled; // flag enable the response to poll messages
|
bool emsPollEnabled; // flag enable the response to poll messages
|
||||||
|
bool emsTxEnabled; // flag if we're allowing sending of Tx packages
|
||||||
bool emsThermostatEnabled; // if there is a RCxx thermostat active
|
bool emsThermostatEnabled; // if there is a RCxx thermostat active
|
||||||
|
bool emsBoilerEnabled; // is the boiler online
|
||||||
_EMS_SYS_LOGGING emsLogging; // logging
|
_EMS_SYS_LOGGING emsLogging; // logging
|
||||||
unsigned long emsLastPoll; // in ms, last time we received a poll
|
|
||||||
unsigned long emsLastRx; // timings
|
|
||||||
unsigned long emsLastTx; // timings
|
|
||||||
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
|
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
|
||||||
} _EMS_Sys_Status;
|
} _EMS_Sys_Status;
|
||||||
|
|
||||||
// The Tx send package
|
// The Tx send package
|
||||||
typedef struct {
|
typedef struct {
|
||||||
_EMS_TX_ACTION action; // read or write
|
_EMS_TX_TELEGRAM_ACTION action; // read or write
|
||||||
uint8_t dest;
|
uint8_t dest;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t offset;
|
uint8_t offset;
|
||||||
uint8_t length;
|
uint8_t length;
|
||||||
uint8_t checkValue; // value to validate against
|
uint8_t dataValue; // value to validate against
|
||||||
uint8_t type_validate; // type to call after a successful Write command
|
uint8_t type_validate; // type to call after a successful Write command
|
||||||
|
uint8_t comparisonValue; // value to compare against during a validate
|
||||||
|
uint8_t comparisonOffset; // offset of where the byte is we want to compare too later
|
||||||
|
uint8_t comparisonPostRead; // after a successful write call this to read
|
||||||
|
bool hasSent; // has been sent, just pending ack
|
||||||
|
bool forceRefresh; // should we send to MQTT after a successful Tx?
|
||||||
uint8_t data[EMS_TX_MAXBUFFERSIZE];
|
uint8_t data[EMS_TX_MAXBUFFERSIZE];
|
||||||
} _EMS_TxTelegram;
|
} _EMS_TxTelegram;
|
||||||
|
|
||||||
|
// default empty Tx
|
||||||
|
const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
||||||
|
EMS_TX_TELEGRAM_INIT, // action
|
||||||
|
EMS_ID_NONE, // dest
|
||||||
|
EMS_ID_NONE, // type
|
||||||
|
0, // offset
|
||||||
|
0, // length
|
||||||
|
0, // data value
|
||||||
|
EMS_ID_NONE, // type_validate
|
||||||
|
0, // comparisonValue
|
||||||
|
0, // comparisonOffset
|
||||||
|
EMS_ID_NONE, // comparisonPostRead
|
||||||
|
false, // hasSent
|
||||||
|
false, // forceRefresh
|
||||||
|
{0x00} // data
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Telegram package defintions
|
* Telegram package defintions
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct { // UBAParameterWW
|
||||||
// UBAParameterWW
|
|
||||||
uint8_t wWActivated; // Warm Water activated
|
uint8_t wWActivated; // Warm Water activated
|
||||||
uint8_t wWSelTemp; // Warm Water selected temperature
|
uint8_t wWSelTemp; // Warm Water selected temperature
|
||||||
uint8_t wWCircPump; // Warm Water circulation pump Available
|
uint8_t wWCircPump; // Warm Water circulation pump Available
|
||||||
@@ -161,7 +198,7 @@ typedef struct {
|
|||||||
|
|
||||||
// Thermostat data
|
// Thermostat data
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t type; // thermostat type (RC20, RC30, RC35 etc)
|
uint8_t type; // thermostat type (RC30, Easy etc)
|
||||||
float setpoint_roomTemp; // current set temp
|
float setpoint_roomTemp; // current set temp
|
||||||
float curr_roomTemp; // current room temp
|
float curr_roomTemp; // current room temp
|
||||||
uint8_t mode; // 0=low, 1=manual, 2=auto
|
uint8_t mode; // 0=low, 1=manual, 2=auto
|
||||||
@@ -174,9 +211,9 @@ typedef struct {
|
|||||||
} _EMS_Thermostat;
|
} _EMS_Thermostat;
|
||||||
|
|
||||||
// call back function signature
|
// call back function signature
|
||||||
typedef bool (*EMS_processType_cb)(uint8_t * data, uint8_t length);
|
typedef void (*EMS_processType_cb)(uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
// Definition for each type, including the relative callback function
|
// Definition for each EMS type, including the relative callback function
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t src;
|
uint8_t src;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
@@ -184,6 +221,12 @@ typedef struct {
|
|||||||
EMS_processType_cb processType_cb;
|
EMS_processType_cb processType_cb;
|
||||||
} _EMS_Types;
|
} _EMS_Types;
|
||||||
|
|
||||||
|
// Definition for thermostat type
|
||||||
|
typedef struct {
|
||||||
|
uint8_t id;
|
||||||
|
const char typeString[50];
|
||||||
|
} _Thermostat_Types;
|
||||||
|
|
||||||
// ANSI Colors
|
// ANSI Colors
|
||||||
#define COLOR_RESET "\x1B[0m"
|
#define COLOR_RESET "\x1B[0m"
|
||||||
#define COLOR_BLACK "\x1B[0;30m"
|
#define COLOR_BLACK "\x1B[0;30m"
|
||||||
@@ -200,34 +243,42 @@ typedef struct {
|
|||||||
// function definitions
|
// function definitions
|
||||||
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
|
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
|
||||||
void ems_init();
|
void ems_init();
|
||||||
void ems_doReadCommand(uint8_t type, uint8_t dest);
|
void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh = false);
|
||||||
|
void ems_sendRawTelegram(char * telegram);
|
||||||
|
|
||||||
void ems_setThermostatTemp(float temp);
|
void ems_setThermostatTemp(float temp);
|
||||||
void ems_setThermostatMode(uint8_t mode);
|
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);
|
||||||
|
void ems_setWarmTapWaterActivated(bool activated);
|
||||||
void ems_setExperimental(uint8_t value);
|
void ems_setExperimental(uint8_t value);
|
||||||
void ems_setPoll(bool b);
|
void ems_setPoll(bool b);
|
||||||
|
void ems_setTxEnabled(bool b);
|
||||||
void ems_setThermostatEnabled(bool b);
|
void ems_setThermostatEnabled(bool b);
|
||||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
|
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
|
||||||
|
void ems_setEmsRefreshed(bool b);
|
||||||
|
|
||||||
void ems_getThermostatTemps();
|
void ems_getThermostatValues();
|
||||||
bool ems_getPoll();
|
bool ems_getPoll();
|
||||||
|
bool ems_getTxEnabled();
|
||||||
bool ems_getThermostatEnabled();
|
bool ems_getThermostatEnabled();
|
||||||
|
bool ems_getBoilerEnabled();
|
||||||
_EMS_SYS_LOGGING ems_getLogging();
|
_EMS_SYS_LOGGING ems_getLogging();
|
||||||
uint8_t ems_getEmsTypesCount();
|
uint8_t ems_getEmsTypesCount();
|
||||||
|
uint8_t ems_getThermostatTypesCount();
|
||||||
|
bool ems_getEmsRefreshed();
|
||||||
|
|
||||||
void ems_printAllTypes();
|
void ems_printAllTypes();
|
||||||
|
void ems_printThermostatType();
|
||||||
|
void ems_printTxQueue();
|
||||||
|
|
||||||
// private functions
|
// private functions
|
||||||
uint8_t _crcCalculator(uint8_t * data, uint8_t len);
|
uint8_t _crcCalculator(uint8_t * data, uint8_t len);
|
||||||
void _processType(uint8_t * telegram, uint8_t length);
|
void _processType(uint8_t * telegram, uint8_t length);
|
||||||
void _initTxBuffer();
|
|
||||||
void _buildTxTelegram(uint8_t data_value);
|
|
||||||
void _debugPrintPackage(const char * prefix, uint8_t * data, uint8_t len, const char * color);
|
void _debugPrintPackage(const char * prefix, uint8_t * data, uint8_t len, const char * color);
|
||||||
|
void _ems_clearTxData();
|
||||||
|
|
||||||
// global so can referenced in other classes
|
// global so can referenced in other classes
|
||||||
extern _EMS_Sys_Status EMS_Sys_Status;
|
extern _EMS_Sys_Status EMS_Sys_Status;
|
||||||
extern _EMS_TxTelegram EMS_TxTelegram;
|
|
||||||
extern _EMS_Boiler EMS_Boiler;
|
extern _EMS_Boiler EMS_Boiler;
|
||||||
extern _EMS_Thermostat EMS_Thermostat;
|
extern _EMS_Thermostat EMS_Thermostat;
|
||||||
|
|||||||
@@ -25,20 +25,21 @@
|
|||||||
#define BOILER_SHOWER_TIMER 1 // monitors how long a shower has taken
|
#define BOILER_SHOWER_TIMER 1 // monitors how long a shower has taken
|
||||||
#define BOILER_SHOWER_ALERT 0 // send alert if showetime exceeded
|
#define BOILER_SHOWER_ALERT 0 // send alert if showetime exceeded
|
||||||
|
|
||||||
// define here the Thermostat type. see ems.h for options
|
// define here the Thermostat type. see ems.h for the supported types
|
||||||
//#define EMS_ID_THERMOSTAT EMS_ID_THERMOSTAT_RC20 // your thermostat ID
|
#define EMS_ID_THERMOSTAT EMS_ID_THERMOSTAT_RC20
|
||||||
#define EMS_ID_THERMOSTAT EMS_ID_THERMOSTAT_EASY
|
//#define EMS_ID_THERMOSTAT EMS_ID_THERMOSTAT_RC30
|
||||||
|
//#define EMS_ID_THERMOSTAT EMS_ID_THERMOSTAT_EASY
|
||||||
|
|
||||||
// trigger settings to determine if hot tap water or the heating is active
|
// trigger settings to determine if hot tap water or the heating is active
|
||||||
#define EMS_BOILER_BURNPOWER_TAPWATER 100
|
#define EMS_BOILER_BURNPOWER_TAPWATER 100
|
||||||
#define EMS_BOILER_SELFLOWTEMP_HEATING 70
|
#define EMS_BOILER_SELFLOWTEMP_HEATING 70
|
||||||
|
|
||||||
// if using the shower timer, change these settings (in ms)
|
// if using the shower timer, change these settings
|
||||||
#define SHOWER_PAUSE_TIME 15000 // 15 seconds, max time if water is switched off & on during a shower
|
#define SHOWER_PAUSE_TIME 15000 // in ms. 15 seconds, max time if water is switched off & on during a shower
|
||||||
#define SHOWER_MIN_DURATION 180000 // 3 minutes, before recognizing its a shower
|
#define SHOWER_MIN_DURATION 120000 // in ms. 2 minutes, before recognizing its a shower
|
||||||
#define SHOWER_MAX_DURATION 420000 // 7 minutes, before trigger a shot of cold water
|
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
||||||
#define SHOWER_COLDSHOT_DURATION 5 // 5 seconds for cold water - note, must be in seconds
|
#define SHOWER_OFFSET_TIME 5000 // in ms. 5 seconds grace time, to calibrate actual time under the shower
|
||||||
#define SHOWER_OFFSET_TIME 8000 // 8 seconds grace time, to calibrate actual time under the shower
|
#define SHOWER_COLDSHOT_DURATION 10 // in seconds. 10 seconds for cold water before turning back hot water
|
||||||
|
|
||||||
// if using LEDs to show traffic, configure the GPIOs here
|
// if using LEDs to show traffic, configure the GPIOs here
|
||||||
// only works if -DUSE_LED is set in platformio.ini
|
// only works if -DUSE_LED is set in platformio.ini
|
||||||
|
|||||||
2
src/version.h
Normal file
2
src/version.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define APP_NAME "EMS-ESP-Boiler"
|
||||||
|
#define APP_VERSION "1.1.0"
|
||||||
Reference in New Issue
Block a user