version 1.2.3. See ChangeLog

This commit is contained in:
proddy
2019-01-03 15:10:52 +01:00
parent 64a5ebc25d
commit 23599fb0a9
8 changed files with 311 additions and 171 deletions

View File

@@ -5,6 +5,15 @@ 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).
## [1.2.3] 2019-01-03
### Fixed
- Can now hardcode Boiler and Thermostat types in my_config.h to bypass auto-detection
- Fixed MQTT subscribing to Heating and Hot Water active topics
- Fixed for listening to incoming MQTT topics (https://github.com/proddy/EMS-ESP-Boiler/issues/27)
- Fixed handling of current temperature on an RC35-type thermostat that doesn't have a sensor (https://github.com/proddy/EMS-ESP-Boiler/issues/18)
## [1.2.2] 2019-01-02
### Fixed

View File

@@ -5,7 +5,7 @@ EMS-ESP-Boiler is a project to build a controller circuit running with an ESP826
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.
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/b8880625bdf841d4adb2829732030887)](https://app.codacy.com/app/proddy/EMS-ESP-Boiler?utm_source=github.com&utm_medium=referral&utm_content=proddy/EMS-ESP-Boiler&utm_campaign=Badge_Grade_Settings)
[![version](https://img.shields.io/badge/version-1.2.0-brightgreen.svg)](CHANGELOG.md)
[![version](https://img.shields.io/badge/version-1.2.3-brightgreen.svg)](CHANGELOG.md)
- [EMS-ESP-Boiler](#ems-esp-boiler)
- [Introduction](#introduction)
@@ -46,29 +46,30 @@ My original intention for this home project was to build a custom smart thermost
Firstly, some acknowledgments and kudos to the following people who have open-sourced their projects which have helped me tremendously:
**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.
**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 for reading telegrams.
**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.
**bbqkees** - Kees built a working [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) (in German) for the EMS bus which is a little outdated, not 100% accurate and unfortunately no longer maintained.
## Supported Boilers Types
Most Bosch branded boilers that support the Logamatic EMS (and EMS+) bus protocols work with this design. This includes Nefit, Buderus, Worcester and Junkers (all copyrighted). Please make sure you read the **Disclaimer** carefully before sending ambiguous messages to your EMS bus as you cause serious damage to your boiler.
Most Bosch branded boilers that support the Logamatic EMS (and EMS+) bus protocols work with this design. This includes Nefit, Buderus, Worcester and Junkers (all copyrighted). Please make sure you read the **Disclaimer** carefully before sending ambiguous messages to your EMS bus as you could cause serious damage to your boiler or thermostat.
## 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 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.
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 dev boards. It will also work on bare ESP8266 chips such as the ESP-12E 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. Why is explained later.
## Getting Started
1. Either build the circuit below or purchase a ready built board from bbqkees via his [GitHub](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz) page or the [Domoticz forum](http://www.domoticz.com/forum/viewtopic.php?f=22&t=22079&start=20).
2. Get an ESP8266 dev board and connect the 2 EMS output lines from the boiler to the circuit and the Rx and Tx out to ESP pins D7 and D8 respectively. The EMS connection can either be the 12-15V AC direct from the thermostat bus line or from the 3.5" Service Jack at the front.
3. Optionally connect the three LEDs to show Rx and Tx traffic and Error codes to pins D1, D2, D3 respectively. I use 220 Ohm pull-down resistors. These pins are configurable in ``boiler.ino``. This is further explained in the **code** section below.
4. Build and upload the firmware to the ESP8266 device. I used Platformio with Visual Studio. Do make sure you set the MQTT and WiFi credentials correctly and if you're not using MQTT leave the MQTT_IP blank. The firmware supports OTA too with the default hostname as 'boiler' (or 'boiler.' depending on your OS and how the mdns resolves hostnames).
5. Power the ESP either via USB or direct into the 5v vin pin from an external power 5V volts supply with min 400mA.
6. Attach the 3v3 out on the ESP8266 to the DC power line on the EMS circuit as indicated in the schematics.
7. The WiFi connects via DHCP by default. Find the IP by from your router and then telnet (port 23) to it. Tip: to enable Telnet on Windows run `dism /online /Enable-Feature /FeatureName:TelnetClient` or install something like [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html). If everything is working you should see the messages appear in the window as shown in the next section. However if you're unable to locate the IP of the ESP then probably the WiFi failed to instantiate. In this case add -DUSE_SERIAL to the build options, connect at USB, build, upload and then use a terminal to connect to the serial port to see the debug messages. A word of warning, do not use both a USB and power from the EMS at the same time.
3. Optionally connect an external LED or decide to use the onboard ESP8266 LED. This will flash when there is an error on the EMS bus line or stay solid when it's connected.
4. Modify `my_custom.h`
5. Build and upload the firmware to the ESP8266 device. I used Platformio with Visual Studio Code but using Atom or a command-line is just as easy if you don't plan to make code changes. Do make sure you set the MQTT and WiFi credentials correctly in the build flags and if you're not using MQTT leave the MQTT_IP blank. The firmware supports OTA too with the default hostname as 'boiler'.
6. Power the ESP either via USB or direct into the 5v vin pin from an external power 5V volts supply with min 400mA.
7. Attach the 3v3 out on the ESP8266 to the DC power line on the EMS circuit as indicated in the schematics.
8. The WiFi connects via DHCP by default. Find the IP by from your router and then telnet (port 23) to it. If a connection can't be made it will go into Access Point mode. Tip: to enable Telnet on Windows run `dism /online /Enable-Feature /FeatureName:TelnetClient` or install something like [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html). If everything is working you should see the messages appear in the window as shown in the next section. However if you're unable to locate the IP of the ESP then something went wrong. Re-compile with the -DDEBUG_SUPPORT and connect via USB to a PC and check the Serial log for errors.
## Monitoring The Output
@@ -212,6 +213,8 @@ The code is built on the Arduino framework and is dependent on these external li
`boiler.ino` is the Arduino code for the ESP8266 that kicks it all off. This is where we have specific logic such as the code to monitor and alert on the Shower timer and light up the LEDs. LED support is enabled by default and can be switched off at compile time using the -DNO_LED build flag.
`my_config.h` all the custom settings
`MyESP.cpp` is my custom library to handle WiFi, MQTT, MDNS and Telnet. Uses a modified version of TelnetSpy (https://github.com/yasheena/telnetspy)
### Supported EMS Types
@@ -246,21 +249,24 @@ BC10 = Type 123 Version 04.05
#### RC20 (Moduline 300)
Read and write of setpoint temp and mode supported.
Read and write of setpoint temperature and mode are supported.
#### RC30 (Moduline 400)
Read and write of setpoint temp and mode supported.
Read and write of setpoint temperature and mode are 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.
Note I found 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
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.
Fetching the thermostats setpoint temperature 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
This is roughly supported but not fully tested.
#### 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.
@@ -276,11 +282,9 @@ There is limited support for an Nefit Easy TC100/TC200 type thermostat. The curr
When the ESP8266 boots it will send a start signal via MQTT. This is picked up by Home Assistant and sends a notification informing me that the device has booted. Useful for knowing when the ESP gets reset.
I'm using the standard PubSubClient client so make sure you set `-DMQTT_MAX_PACKET_SIZE=400` as the default package size is 128 and our JSON messages are around 300 bytes.
I run Mosquitto on my Raspberry PI 3 as the MQTT broker.
The boiler data is collected and sent as a single JSON object to MQTT TOPIC `home/boiler/boiler_data`. A hash is generated (CRC32 based) to determine if the payload has changed, otherwise don't send it. An example payload looks like:
The boiler data is collected and sent as a single JSON object to MQTT TOPIC `home/boiler/boiler_data`. A hash is generated (CRC32 based) to determine if the payload has changed, otherwise don't send it. An example payload looks roughly like:
`{"wWCurTmp":"43.0","wWHeat":"on","curFlowTemp":"51.7","retTemp":"48.0","burnGas":"off","heatPmp":"off","fanWork":"off","ignWork":"off","wWCirc":"off","selBurnPow":"0","curBurnPow":"0","sysPress":"1.6","boilTemp":"54.7","pumpMod":"4"}`
@@ -290,9 +294,9 @@ These topics can be configured in the `TOPIC_*` defines in `boiler.ino`. Make su
### The Basic Shower Logic
Checking whether the shower is running was tricky. We know when the warm water is on and being heated but need to distinguish between the central heating, shower, hot tap and bath. I found via trial and error the Selected Burner Max Power is between 80% and 115% when the shower is running and fixed at 75% if the central heating is on. Furthermore the Selected Flow Impulsion is 80 C for the heating.
Checking whether the shower is running is tricky. We know when the warm water is on and being heated but need to distinguish between the central heating, shower, hot tap and even a bath tap. So this code is a little experimental.
There is other logic in the code to compensate for ramp up and whether the shower is turned off and back on again quickly within a 10 second window.
There is other logic in the code to compensate for water heating up to shower temperature and whether the shower is turned off and back on again quickly within a 10 second window.
## Home Assistant Configuration
@@ -310,8 +314,6 @@ You can find the .yaml configuration files under `doc/ha`. See also https://comm
### Using PlatformIO Standalone
PlatformIO is my preferred way. The code uses a modified version [ESPHelper](https://github.com/ItKindaWorks/ESPHelper) which handles all the basic handling of the WiFi, MQTT, OTA and Telnet server. I switched from Atom to the marvelous Visual Studio Code, works on Windows, OSX and Linux.
**On Windows:**
- Download [Git](https://git-scm.com/download/win) (install using the default settings)

View File

@@ -118,7 +118,7 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
// call any final custom settings
if (_extern_WIFICallbackSet) {
myDebug_P(PSTR("[WIFI] calling custom wifi settings function"));
// myDebug_P(PSTR("[WIFI] calling custom wifi settings function"));
_extern_WIFICallback(); // call callback to set any custom things
}
}
@@ -137,6 +137,7 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
}
// received MQTT message
// we send this to the call back function. Important to parse are the event strings such as MQTT_MESSAGE_EVENT and MQTT_CONNECT_EVENT
void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
if (len == 0)
return;
@@ -153,9 +154,14 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
return;
}
char s[100];
snprintf(s, sizeof(s), "%s%s/%s", MQTT_BASE, _app_hostname, MQTT_TOPIC_START);
if (strcmp(topic, s) == 0) {
// topics are in format MQTT_BASE/HOSTNAME/TOPIC
char * topic_magnitude = strrchr(topic, '/'); // strip out everything until last /
if (topic_magnitude != nullptr) {
topic = topic_magnitude + 1;
}
// check for bootime, something specific I fetch as an acknolwegdemtn from Home Assistant
if (strcmp(topic, MQTT_TOPIC_START) == 0) {
myDebug_P(PSTR("[MQTT] boottime: %s"), message);
setBoottime(message);
return;
@@ -379,7 +385,7 @@ void MyESP::_consoleShowHelp() {
SerialAndTelnet.println("*********************************");
SerialAndTelnet.println("* Console and Log Monitoring *");
SerialAndTelnet.println("*********************************");
SerialAndTelnet.printf("* %s %s\n\r", _app_name, _app_version);
SerialAndTelnet.printf("* %s version %s\n\r", _app_name, _app_version);
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s\n\r",
hostname.c_str(),
WiFi.localIP().toString().c_str(),
@@ -543,7 +549,7 @@ void MyESP::_mqttConnect() {
mqttClient.setClientId(_app_hostname);
if (_mqtt_username && _mqtt_password) {
myDebug_P(PSTR("[MQTT] Connecting to MQTT using user %s & its password"), _mqtt_username);
myDebug_P(PSTR("[MQTT] Connecting to MQTT using user %s"), _mqtt_username);
mqttClient.setCredentials(_mqtt_username, _mqtt_password);
} else {
myDebug_P(PSTR("[MQTT] Connecting to MQTT..."));

View File

@@ -65,9 +65,6 @@ Ticker showerColdShotStopTimer;
#define TOPIC_SHOWER_COLDSHOT "shower_coldshot" // used to trigger a coldshot from HA publish
#define SHOWER_ALARM "shower_alarm" // for notifying HA that shower time has reached its limit
// logging - EMS_SYS_LOGGING_VERBOSE, EMS_SYS_LOGGING_NONE, EMS_SYS_LOGGING_BASIC (see ems.h)
#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_NONE
// shower settings for DEBUGGING only
#ifdef SHOWER_TEST
#undef SHOWER_PAUSE_TIME
@@ -99,7 +96,7 @@ command_t PROGMEM project_cmds[] = {
{"l [n]", "set logging (0=none, 1=raw, 2=basic, 3=thermostat only, 4=verbose)"},
{"s", "show statistics"},
{"D", "auto Detect EMS connected devices"},
{"D", "scan EMS connected Devices"},
{"h", "list supported EMS telegram type IDs"},
{"M", "publish to MQTT"},
{"Q", "print Tx Queue"},
@@ -133,9 +130,9 @@ unsigned long timestamp; // for internal timings, via millis()
uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
// logging messages with fixed strings (newline done automatically)
// logging messages with fixed strings
void myDebugLog(const char * s) {
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
if (ems_getLogging() >= EMS_SYS_LOGGING_BASIC) {
myDebug(s);
}
}
@@ -387,6 +384,7 @@ void showInfo() {
_renderFloatValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp);
_renderFloatValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp);
if (EMS_Thermostat.mode == 0) {
myDebug(" Mode is set to low");
} else if (EMS_Thermostat.mode == 1) {
@@ -482,13 +480,22 @@ void publishValues(bool force) {
rootThermostat[THERMOSTAT_CURRTEMP] = _float_to_char(s, EMS_Thermostat.curr_roomTemp);
rootThermostat[THERMOSTAT_SELTEMP] = _float_to_char(s, EMS_Thermostat.setpoint_roomTemp);
// send mode 0=low, 1=manual, 2=auto
if (EMS_Thermostat.mode == 0) {
rootThermostat[THERMOSTAT_MODE] = "low";
} else if (EMS_Thermostat.mode == 1) {
rootThermostat[THERMOSTAT_MODE] = "manual";
if (ems_getThermostatModel() == EMS_MODEL_RC20) {
if (EMS_Thermostat.mode == 0) {
rootThermostat[THERMOSTAT_MODE] = "low";
} else if (EMS_Thermostat.mode == 1) {
rootThermostat[THERMOSTAT_MODE] = "manual";
} else {
rootThermostat[THERMOSTAT_MODE] = "auto";
}
} else {
rootThermostat[THERMOSTAT_MODE] = "auto";
if (EMS_Thermostat.mode == 0) {
rootThermostat[THERMOSTAT_MODE] = "night";
} else if (EMS_Thermostat.mode == 1) {
rootThermostat[THERMOSTAT_MODE] = "day";
} else {
rootThermostat[THERMOSTAT_MODE] = "auto";
}
}
rlen = rootThermostat.measureLength();
@@ -562,7 +569,7 @@ void myDebugCallback() {
ems_printTxQueue();
break;
case 'D': // Auto detect EMS devices
ems_getAllVersions();
ems_scanDevices();
break;
default:
myDebug("Unknown command. Use ? for help.");
@@ -629,8 +636,6 @@ void MQTTcallback(unsigned int type, const char * topic, const char * message) {
myESP.mqttSubscribe(TOPIC_THERMOSTAT_CMD_MODE);
myESP.mqttSubscribe(TOPIC_SHOWER_TIMER);
myESP.mqttSubscribe(TOPIC_SHOWER_ALERT);
myESP.mqttSubscribe(TOPIC_BOILER_TAPWATER_ACTIVE);
myESP.mqttSubscribe(TOPIC_BOILER_HEATING_ACTIVE);
myESP.mqttSubscribe(TOPIC_SHOWER_COLDSHOT);
// publish to HA the status of the Shower parameters
@@ -689,7 +694,7 @@ void MQTTcallback(unsigned int type, const char * topic, const char * message) {
void WIFICallback() {
// when finally we're all set up, we can fire up the uart (this will enable the UART interrupts)
#ifdef DEBUG_SUPPORT
myDebug("Warning, in DEBUG mode. EMS bus is disabled. See -DDEBUG_SUPPORT build option.");
myDebug("Warning, in DEBUG mode. EMS bus has been disabled. See -DDEBUG_SUPPORT build option.");
#else
// Important! This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
// This is done after we have a WiFi signal to avoid any resource conflicts
@@ -697,26 +702,20 @@ void WIFICallback() {
myDebug("[UART] Opened Rx/Tx connection");
#endif
// now that we're connected, send a version request to see what things are on the EMS bus
myDebug("Starting up. Finding what devices are on the EMS bus...");
ems_getAllVersions();
// now that we're connected, check to see if we boiler and thermostat set
// otherwise this will initiate a self scan
ems_setModels();
}
// Initialize the boiler settings
void initBoiler() {
// default settings
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
// init shower
void initShower() {
// default showr settings
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
Boiler_Shower.timerStart = 0;
Boiler_Shower.timerPause = 0;
Boiler_Shower.duration = 0;
Boiler_Shower.doingColdShot = false;
ems_setLogging(BOILER_DEFAULT_LOGGING); // set default logging
ems_init(); // call ems.cpp's init function to set all the internal params
}
// call PublishValues without forcing, so using CRC to see if we really need to publish
@@ -728,7 +727,7 @@ void do_publishValues() {
void do_ledcheck() {
#ifndef NO_LED
if (ems_getBusConnected()) {
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard high=off
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
} else {
int state = digitalRead(BOILER_LED);
digitalWrite(BOILER_LED, !state);
@@ -761,7 +760,7 @@ void do_regularUpdates() {
// turn off hot water to send a shot of cold
void _showerColdShotStart() {
myDebugLog("Shower: doing a shot of cold");
myDebugLog("[Shower] doing a shot of cold water");
ems_setWarmTapWaterActivated(false);
Boiler_Shower.doingColdShot = true;
// start the timer for n seconds which will reset the water back to hot
@@ -771,11 +770,10 @@ void _showerColdShotStart() {
// turn back on the hot water for the shower
void _showerColdShotStop() {
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_setWarmTapWaterActivated(true);
Boiler_Shower.doingColdShot = false;
// disable the timer
showerColdShotStopTimer.detach();
showerColdShotStopTimer.detach(); // disable the timer
}
}
@@ -800,13 +798,13 @@ void showerCheck() {
// first check to see if hot water has been on long enough to be recognized as a Shower/Bath
if (!Boiler_Shower.showerOn && (timestamp - Boiler_Shower.timerStart) > SHOWER_MIN_DURATION) {
Boiler_Shower.showerOn = true;
myDebugLog("Shower: hot water still running, starting shower timer");
myDebugLog("[Shower] hot water still running, starting shower timer");
}
// check if the shower has been on too long
else if ((((timestamp - Boiler_Shower.timerStart) > SHOWER_MAX_DURATION) && !Boiler_Shower.doingColdShot)
&& Boiler_Status.shower_alert) {
myESP.sendHACommand(SHOWER_ALARM);
myDebugLog("Shower: exceeded max shower time");
myDebugLog("[Shower] exceeded max shower time");
_showerColdShotStart();
}
}
@@ -814,7 +812,6 @@ void showerCheck() {
// if it just turned off, record the time as it could be a short pause
if ((Boiler_Shower.timerStart != 0) && (Boiler_Shower.timerPause == 0)) {
Boiler_Shower.timerPause = timestamp;
myDebugLog("Shower: hot water turned off");
}
// if shower has been off for longer than the wait time
@@ -831,14 +828,13 @@ void showerCheck() {
strlcat(s, itoa((uint8_t)((Boiler_Shower.duration / 1000) % 60), buffer, 10), sizeof(s));
strlcat(s, " seconds", sizeof(s));
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
myDebug("Shower: finished with duration %s", s);
myDebug("[Shower] finished with duration %s", s);
}
myESP.mqttPublish(TOPIC_SHOWERTIME, s); // publish to HA
}
}
// reset everything
myDebugLog("Shower: resetting timers");
Boiler_Shower.timerStart = 0;
Boiler_Shower.timerPause = 0;
Boiler_Shower.showerOn = false;
@@ -865,13 +861,20 @@ void setup() {
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
// set up myESP for Wifi, MQTT, MDNS and Telnet
// with callbacks
myESP.setup(APP_HOSTNAME, APP_NAME, APP_VERSION, WIFI_SSID, WIFI_PASSWORD, MQTT_IP, MQTT_USER, MQTT_PASS);
myESP.consoleSetCallBackProjectCmds(project_cmds, ArraySize(project_cmds), myDebugCallback); // set up Telnet commands
myESP.setWIFICallback(WIFICallback);
myESP.setMQTTCallback(MQTTcallback);
// init Boiler specific parameters
initBoiler();
// init Shower specific parameters
initShower();
ems_setLogging(BOILER_DEFAULT_LOGGING); // set default logging. see my_config.h
// init the EMS bus
// call ems.cpp's init function to set all the internal params
ems_init(MY_BOILER_MODELID, MY_THERMOSTAT_MODELID);
}
//

View File

@@ -62,7 +62,7 @@ const _Model_Type Model_Types[] = {
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73"},
{EMS_MODEL_RC20, 77, 0x17, "RC20 (Nefit Moduline 300)"},
{EMS_MODEL_RC30, 78, 0x10, "RC30 (Nefit Moduline 400)"},
{EMS_MODEL_RC35, 86, 0x10, "RC35"},
{EMS_MODEL_RC35, 86, 0x10, "RC35 (or compatible"},
{EMS_MODEL_EASY, 202, 0x18, "TC100 (Nefit Easy/CT100)"}
};
@@ -165,7 +165,7 @@ uint8_t _last_TxTelgramCRC; // CRC of last Tx sent, for checking duplicates
// init stats and counters and buffers
// uses -255 or 255 for values that haven't been set yet (EMS_VALUE_INT_NOTSET and EMS_VALUE_FLOAT_NOTSET)
void ems_init() {
void ems_init(_EMS_MODEL_ID boiler_modelid, _EMS_MODEL_ID thermostat_modelid) {
// overall status
EMS_Sys_Status.emsRxPgks = 0;
EMS_Sys_Status.emsTxPkgs = 0;
@@ -177,12 +177,6 @@ void ems_init() {
EMS_Sys_Status.emsTxEnabled = true; // start up with Tx enabled
EMS_Sys_Status.emsBusConnected = false;
// no thermostat or boiler attached yet
EMS_Sys_Status.emsBoilerEnabled = false;
EMS_Sys_Status.emsThermostatEnabled = false;
EMS_Sys_Status.emsLogging = EMS_SYS_LOGGING_NONE; // Verbose logging is off
// thermostat
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_FLOAT_NOTSET;
EMS_Thermostat.curr_roomTemp = EMS_VALUE_FLOAT_NOTSET;
@@ -195,7 +189,6 @@ void ems_init() {
EMS_Thermostat.mode = 255; // dummy value
EMS_Thermostat.type_id = EMS_ID_NONE;
EMS_Thermostat.model_id = EMS_MODEL_NONE;
EMS_Thermostat.read_supported = false;
EMS_Thermostat.write_supported = false;
@@ -244,8 +237,11 @@ void ems_init() {
EMS_Boiler.tapwaterActive = EMS_VALUE_INT_NOTSET; // Hot tap water is on/off
EMS_Boiler.heatingActive = EMS_VALUE_INT_NOTSET; // Central heating is on/off
EMS_Boiler.type_id = EMS_ID_NONE;
EMS_Boiler.model_id = EMS_MODEL_NONE;
EMS_Boiler.type_id = EMS_ID_NONE;
// for lookup later
EMS_Boiler.model_id = boiler_modelid;
EMS_Thermostat.model_id = thermostat_modelid;
// counters
_ems_PollCount = 0;
@@ -281,16 +277,11 @@ void ems_setEmsRefreshed(bool b) {
}
bool ems_getBoilerEnabled() {
return EMS_Sys_Status.emsBoilerEnabled;
}
void ems_setBoilerEnabled(bool b) {
EMS_Sys_Status.emsBoilerEnabled = b;
myDebug("Boiler set to %s", EMS_Sys_Status.emsBoilerEnabled ? "enabled" : "disabled");
return (EMS_Boiler.model_id != EMS_MODEL_NONE);
}
bool ems_getThermostatEnabled() {
return EMS_Sys_Status.emsThermostatEnabled;
return (EMS_Thermostat.model_id != EMS_MODEL_NONE);
}
bool ems_getBusConnected() {
@@ -300,11 +291,6 @@ bool ems_getBusConnected() {
return EMS_Sys_Status.emsBusConnected;
}
void ems_setThermostatEnabled(bool b) {
EMS_Sys_Status.emsThermostatEnabled = b;
myDebug("Thermostat set to %s", EMS_Sys_Status.emsThermostatEnabled ? "enabled" : "disabled");
}
_EMS_SYS_LOGGING ems_getLogging() {
return EMS_Sys_Status.emsLogging;
}
@@ -330,6 +316,22 @@ void ems_setLogging(_EMS_SYS_LOGGING loglevel) {
}
}
// if the thermostat or boiler models have been provided, set them up
void ems_setModels() {
bool found = false;
if (ems_getThermostatModel() != EMS_MODEL_NONE) {
found = _ems_setModel(ems_getThermostatModel());
}
if (ems_getBoilerModel() != EMS_MODEL_NONE) {
found = found && _ems_setModel(ems_getBoilerModel());
}
if (!found) {
ems_scanDevices(); // initiate a scan
}
}
/**
* Calculate CRC checksum using lookup table for speed
* len is length of data in bytes (including the CRC byte at end)
@@ -482,6 +484,14 @@ void _ems_sendTelegram() {
}
// if Tx is disabled, don't do anything and ignore the request
// this could be because the boiler has yet to be found and the type_id is still empty
if (EMS_TxTelegram.dest == EMS_ID_NONE) {
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
EMS_TxQueue.shift(); // remove from queue
return;
}
// if there is no destination, also delete it from the queue
if (!EMS_Sys_Status.emsTxEnabled) {
myDebug("Tx is disabled. Ignoring %s request to 0x%02X.",
((EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) ? "write" : "read"),
@@ -953,54 +963,56 @@ void _process_UBAMonitorSlow(uint8_t * data, uint8_t length) {
}
/**
* RC20StatusMessage - type 0x91 - data from the RC20 thermostat (0x17) - 15 bytes long
* type 0x91 - data from the RC20 thermostat (0x17) - 15 bytes long
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC20StatusMessage(uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[1]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(2, data);
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC20StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC20StatusMessage_curr, data);
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
}
/**
* RC30StatusMessage - type 0x41 - data from the RC30 thermostat (0x10) - 14 bytes long
* type 0x41 - data from the RC30 thermostat (0x10) - 14 bytes long
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC30StatusMessage(uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[1]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(2, data);
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC30StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC30StatusMessage_curr, data);
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
}
/**
* RC35StatusMessage - type 0x3E - data from the RC35 thermostat (0x10)
* type 0x3E - data from the RC35 thermostat (0x10) - 16 bytes
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC35StatusMessage(uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[2]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(3, data);
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC35StatusMessage_setpoint]) / (float)2;
// There is no current room temperature sensor in this telegram
EMS_Thermostat.curr_roomTemp = EMS_VALUE_FLOAT_NOTSET;
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
}
/**
* EasyStatusMessage - type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
* type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
* The Easy has a digital precision of its floats to 2 decimal places, so values is divided by 100
*/
void _process_EasyStatusMessage(uint8_t * data, uint8_t length) {
EMS_Thermostat.curr_roomTemp = ((float)(((data[8] << 8) + data[9]))) / 100;
EMS_Thermostat.setpoint_roomTemp = ((float)(((data[10] << 8) + data[11]))) / 100;
EMS_Thermostat.curr_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_curr] << 8) + data[9]))) / 100;
EMS_Thermostat.setpoint_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_setpoint] << 8) + data[11]))) / 100;
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back to Home Assistant via MQTT
}
/**
* RC20Temperature - type 0xA8 - for reading the mode from the RC20 thermostat (0x17)
* type 0xA8 - for reading the mode from the RC20 thermostat (0x17)
* received only after requested
*/
void _process_RC20Set(uint8_t * data, uint8_t length) {
@@ -1008,7 +1020,7 @@ void _process_RC20Set(uint8_t * data, uint8_t length) {
}
/**
* RC30Temperature - type 0xA7 - for reading the mode from the RC30 thermostat (0x10)
* type 0xA7 - for reading the mode from the RC30 thermostat (0x10)
* received only after requested
*/
void _process_RC30Set(uint8_t * data, uint8_t length) {
@@ -1016,7 +1028,8 @@ void _process_RC30Set(uint8_t * data, uint8_t length) {
}
/**
* RC35Temperature - type 0x3D - for reading the mode from the RC35 thermostat (0x10)
* type 0x3D - for reading the mode from the RC35 thermostat (0x10)
* Working Mode Heating Circuit 1 (HC1)
* received only after requested
*/
void _process_RC35Set(uint8_t * data, uint8_t length) {
@@ -1024,14 +1037,14 @@ void _process_RC35Set(uint8_t * data, uint8_t length) {
}
/**
* RCOutdoorTempMessage - type 0xA3 - for external temp settings from the the RC* thermostats
* type 0xA3 - for external temp settings from the the RC* thermostats
*/
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length) {
// add support here if you're reading external sensors
}
/**
* Version - type 0x02 - get the firmware version and type of an EMS device
* type 0x02 - get the firmware version and type of an EMS device
*/
void _process_Version(uint8_t * data, uint8_t length) {
// ignore short messages that we can't interpret
@@ -1049,16 +1062,17 @@ void _process_Version(uint8_t * data, uint8_t length) {
char version[10] = {0};
snprintf(version, sizeof(version), "%02d.%02d", major, minor);
// use product ID to search
while (i < _Model_Types_max) {
if (Model_Types[i].product_id == product_id) {
typeFound = true; // we have a matching product id
typeFound = true; // we have a matching product id. i is the index.
break;
}
i++;
}
if (!typeFound) {
myDebug("Unknown device found. Product ID %d, Version %s", product_id, version);
myDebug("Unrecognized device found. Product ID %d, Version %s", product_id, version);
return;
}
@@ -1072,7 +1086,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
}
// set a thermostat
if ((isThermostat) && (!EMS_Sys_Status.emsThermostatEnabled)) {
if (isThermostat) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Found a Thermostat. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
Model_Types[i].model_string,
@@ -1080,19 +1094,21 @@ void _process_Version(uint8_t * data, uint8_t length) {
product_id,
version);
}
// set its capabilities
EMS_Thermostat.model_id = Model_Types[i].model_id;
EMS_Thermostat.type_id = Model_Types[i].type_id;
EMS_Thermostat.read_supported = Thermostat_Types[j].read_supported;
EMS_Thermostat.write_supported = Thermostat_Types[j].write_supported;
strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version));
ems_setThermostatEnabled(true);
ems_getThermostatValues(); // get Thermostat values (if supported)
}
// if we don't have a thermostat set, use this one
if (!ems_getThermostatEnabled()) {
myDebug("Setting the Thermostat to this one.");
// set its capabilities
EMS_Thermostat.model_id = Model_Types[i].model_id;
EMS_Thermostat.type_id = Model_Types[i].type_id;
EMS_Thermostat.read_supported = Thermostat_Types[j].read_supported;
EMS_Thermostat.write_supported = Thermostat_Types[j].write_supported;
strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version));
// otherwise assume its a boiler
if ((!isThermostat) && (!EMS_Sys_Status.emsBoilerEnabled)) {
ems_getThermostatValues(); // get Thermostat values (if supported)
}
} else {
// otherwise assume its a boiler
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Found a Boiler compatible device, model %s with TypeID 0x%02X, Product ID %d, Version %s",
Model_Types[i].model_string,
@@ -1100,13 +1116,93 @@ void _process_Version(uint8_t * data, uint8_t length) {
product_id,
version);
}
EMS_Boiler.type_id = Model_Types[i].type_id;
EMS_Boiler.model_id = Model_Types[i].model_id;
strlcpy(EMS_Boiler.version, version, sizeof(EMS_Boiler.version));
ems_setBoilerEnabled(true);
if (!ems_getBoilerEnabled()) {
myDebug("Setting the Boiler to this one.");
EMS_Boiler.type_id = Model_Types[i].type_id;
EMS_Boiler.model_id = Model_Types[i].model_id;
strlcpy(EMS_Boiler.version, version, sizeof(EMS_Boiler.version));
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
}
}
}
/**
* Given a MODEL_ID, look up its data and set either a Thermostat or Boiler
* return false if not found or no need to set
*/
bool _ems_setModel(_EMS_MODEL_ID model_id) {
if (model_id == EMS_MODEL_NONE) {
return false; // invalid model_id
}
// see if we have a valid model_id
uint8_t model_loc = 0;
bool found = false;
uint8_t i = 0;
const _Model_Type * model_type;
while (i < _Model_Types_max) {
model_type = &Model_Types[model_loc];
if (model_type->model_id == model_id) {
found = true; // we have a matching product id. i is the index.
break;
}
model_loc++;
}
if (!found) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Unknown model specified");
}
return false; // unknown model_id
}
// next check to see if its a known thermostat
// j will have pointer to the Thermostat details
bool isThermostat = false;
uint8_t j = 0;
while (j < _Thermostat_Types_max) {
if (Thermostat_Types[j].model_id == model_id) {
isThermostat = true; // we have a matching model
break;
}
j++;
}
// set a thermostat
if (isThermostat) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Setting Thermostat. Model %s with TypeID 0x%02X, Product ID %d",
model_type->model_string,
model_type->type_id,
model_type->product_id);
}
// set its capabilities
EMS_Thermostat.model_id = model_type->model_id;
EMS_Thermostat.type_id = model_type->type_id;
EMS_Thermostat.read_supported = Thermostat_Types[j].read_supported;
EMS_Thermostat.write_supported = Thermostat_Types[j].write_supported;
strlcpy(EMS_Thermostat.version, "unknown", sizeof(EMS_Thermostat.version));
ems_getThermostatValues(); // get Thermostat values (if supported)
} else {
// otherwise assume its a boiler
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Setting Boiler. Model %s with TypeID 0x%02X, Product ID %d",
model_type->model_string,
model_type->type_id,
model_type->product_id);
}
EMS_Boiler.model_id = model_type->model_id;
EMS_Boiler.type_id = model_type->type_id;
strlcpy(EMS_Boiler.version, "unknown", sizeof(EMS_Boiler.version));
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
}
return true;
}
/**
@@ -1203,7 +1299,7 @@ void ems_printTxQueue() {
* Generic function to return various settings from the thermostat
*/
void ems_getThermostatValues() {
if (!EMS_Sys_Status.emsThermostatEnabled) {
if (!ems_getThermostatEnabled()) {
return;
}
@@ -1283,7 +1379,7 @@ char * _ems_buildModelString(char * buffer, uint8_t size, _EMS_MODEL_ID model_id
*/
char * ems_getThermostatType(char * buffer) {
uint8_t size = 64;
if (!EMS_Sys_Status.emsThermostatEnabled) {
if (!ems_getThermostatEnabled()) {
strlcpy(buffer, "<not enabled>", size);
} else {
_ems_buildModelString(buffer, size, EMS_Thermostat.model_id);
@@ -1296,7 +1392,7 @@ char * ems_getThermostatType(char * buffer) {
*/
char * ems_getBoilerType(char * buffer) {
uint8_t size = 64;
if (!EMS_Sys_Status.emsBoilerEnabled) {
if (!ems_getBoilerEnabled()) {
strlcpy(buffer, "<not enabled>", size);
} else {
_ems_buildModelString(buffer, size, EMS_Boiler.model_id);
@@ -1306,20 +1402,19 @@ char * ems_getBoilerType(char * buffer) {
// returns the model type for a thermostat
_EMS_MODEL_ID ems_getThermostatModel() {
if (EMS_Sys_Status.emsThermostatEnabled) {
return (EMS_Thermostat.model_id);
} else {
return EMS_MODEL_NONE;
}
return (EMS_Thermostat.model_id);
}
// returns the model type for a boiler
_EMS_MODEL_ID ems_getBoilerModel() {
return (EMS_Boiler.model_id);
}
/*
* Find the versions of our connected devices
*/
void ems_getAllVersions() {
void ems_scanDevices() {
// send Version request to all known EMS devices
ems_setThermostatEnabled(false);
ems_setBoilerEnabled(false);
myDebug("Scanning EMS bus for devices. This may take a few seconds.");
for (int i = 0; i < _Model_Types_max; i++) {
if ((Model_Types[i].model_id != EMS_MODEL_NONE) && (Model_Types[i].model_id != EMS_MODEL_SERVICEKEY)) {
@@ -1448,7 +1543,7 @@ void ems_sendRawTelegram(char * telegram) {
* Set the temperature of the thermostat
*/
void ems_setThermostatTemp(float temperature) {
if (!EMS_Sys_Status.emsThermostatEnabled) {
if (!ems_getThermostatEnabled()) {
return;
}
@@ -1480,7 +1575,7 @@ void ems_setThermostatTemp(float temperature) {
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC30StatusMessage;
} else if ((model_id == EMS_MODEL_RC35) || (model_id == EMS_MODEL_ES73)) {
EMS_TxTelegram.type = EMS_TYPE_RC35Set;
EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp;
EMS_TxTelegram.offset = EMS_OFFSET_RC35Set_temp_day; // day mode only for now
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC35StatusMessage;
}
@@ -1499,7 +1594,7 @@ void ems_setThermostatTemp(float temperature) {
* 0xA8 on a RC20 and 0xA7 on RC30
*/
void ems_setThermostatMode(uint8_t mode) {
if (!EMS_Sys_Status.emsThermostatEnabled) {
if (!ems_getThermostatEnabled()) {
return;
}

View File

@@ -58,25 +58,34 @@
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
// RC20 specific
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
#define EMS_OFFSET_RC20Set_mode 23 // position of thermostat mode
#define EMS_OFFSET_RC20Set_temp 28 // position of thermostat setpoint temperature
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
#define EMS_OFFSET_RC20Set_mode 23 // position of thermostat mode
#define EMS_OFFSET_RC20Set_temp 28 // position of thermostat setpoint temperature
#define EMS_TYPE_RC20StatusMessage_setpoint 1 // setpoint temp
#define EMS_TYPE_RC20StatusMessage_curr 2 // current temp
// 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
#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
#define EMS_TYPE_RC30StatusMessage_setpoint 1 // setpoint temp
#define EMS_TYPE_RC30StatusMessage_curr 2 // current temp
// RC35 specific - not implemented yet
#define EMS_TYPE_RC35StatusMessage 0x3E // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC35Set 0x3D // for setting values like temp and mode
#define EMS_OFFSET_RC35Set_mode 7 // position of thermostat mode
#define EMS_OFFSET_RC35Set_temp 2 // position of thermostat setpoint temperature
// RC35 specific
#define EMS_TYPE_RC35StatusMessage 0x3E // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC35StatusMessage_setpoint 2 // desired temp
#define EMS_TYPE_RC35Set 0x3D // for setting values like temp and mode (Working mode HC1)
#define EMS_OFFSET_RC35Set_mode 6 // position of thermostat mode
#define EMS_OFFSET_RC35Set_temp_day 2 // position of thermostat setpoint temperature for day time
#define EMS_OFFSET_RC35Set_temp_night 1 // position of thermostat setpoint temperature for night time
// Easy specific
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
#define EMS_TYPE_EasyStatusMessage_setpoint 10 // setpoint temp
#define EMS_TYPE_EasyStatusMessage_curr 8 // current temp
// default values
#define EMS_VALUE_INT_ON 1 // boolean true
@@ -119,17 +128,15 @@ typedef enum {
typedef struct {
_EMS_RX_STATUS emsRxStatus;
_EMS_TX_STATUS emsTxStatus;
uint16_t emsRxPgks; // received
uint16_t emsTxPkgs; // sent
uint16_t emxCrcErr; // CRC errors
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 emsBoilerEnabled; // is the boiler online
_EMS_SYS_LOGGING emsLogging; // logging
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
bool emsBusConnected; // is there an active bus
unsigned long emsRxTimestamp; // timestamp of last EMS poll
uint16_t emsRxPgks; // received
uint16_t emsTxPkgs; // sent
uint16_t emxCrcErr; // CRC errors
bool emsPollEnabled; // flag enable the response to poll messages
bool emsTxEnabled; // flag if we're allowing sending of Tx packages
_EMS_SYS_LOGGING emsLogging; // logging
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
bool emsBusConnected; // is there an active bus
unsigned long emsRxTimestamp; // timestamp of last EMS poll
} _EMS_Sys_Status;
// The Tx send package
@@ -310,7 +317,7 @@ typedef struct {
// function definitions
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_init();
void ems_init(_EMS_MODEL_ID boiler_modelid, _EMS_MODEL_ID thermostat_modelid);
void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh = false);
void ems_sendRawTelegram(char * telegram);
@@ -322,12 +329,11 @@ void ems_setWarmTapWaterActivated(bool activated);
void ems_setExperimental(uint8_t value);
void ems_setPoll(bool b);
void ems_setTxEnabled(bool b);
void ems_setThermostatEnabled(bool b);
void ems_setBoilerEnabled(bool b);
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
void ems_setEmsRefreshed(bool b);
void ems_setWarmWaterModeComfort(bool comfort);
bool ems_checkEMSBUSAlive();
void ems_setModels();
void ems_getThermostatValues();
void ems_getBoilerValues();
@@ -339,9 +345,10 @@ bool ems_getBusConnected();
_EMS_SYS_LOGGING ems_getLogging();
uint8_t ems_getEmsTypesCount();
bool ems_getEmsRefreshed();
void ems_getAllVersions();
_EMS_MODEL_ID ems_getThermostatModel();
_EMS_MODEL_ID ems_getBoilerModel();
void ems_scanDevices();
void ems_printAllTypes();
char * ems_getThermostatType(char * buffer);
void ems_printTxQueue();
@@ -354,6 +361,8 @@ void _debugPrintPackage(const char * prefix, uint8_t * data, uint8_t len, con
void _ems_clearTxData();
int _ems_findModel(_EMS_MODEL_ID model_id);
char * _ems_buildModelString(char * buffer, uint8_t size, _EMS_MODEL_ID model_id);
bool _ems_setModel(_EMS_MODEL_ID model_id);
// global so can referenced in other classes
extern _EMS_Sys_Status EMS_Sys_Status;

View File

@@ -42,3 +42,19 @@
// set this if using an external temperature sensor like a DS18B20
#define TEMPERATURE_SENSOR_PIN D7
// logging - EMS_SYS_LOGGING_VERBOSE, EMS_SYS_LOGGING_NONE, EMS_SYS_LOGGING_BASIC (see ems.h)
// this can be changed via the Telnet console using the 'l' command
#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_NONE
//#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_VERBOSE
//#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_BASIC
// By default the EMS bus will be scanned for known devices. You can override this here
// by fixing the Boiler and Thermostat types
// Options are in ems.h and include..
// boilers: EMS_MODEL_BK15, EMS_MODEL_UBA, EMS_MODEL_BC10, EMS_MODEL_MM10, EMS_MODEL_WM10
// thermostats: EMS_MODEL_ES73, EMS_MODEL_RC20, EMS_MODEL_RC30, EMS_MODEL_RC35, EMS_MODEL_EASY
#define MY_BOILER_MODELID EMS_MODEL_NONE
#define MY_THERMOSTAT_MODELID EMS_MODEL_NONE
//#define MY_BOILER_MODELID EMS_MODEL_UBA
//#define MY_THERMOSTAT_MODELID EMS_MODEL_RC20

View File

@@ -1,5 +1,5 @@
#pragma once
#define APP_NAME "EMS-ESP-Boiler"
#define APP_VERSION "1.2.2"
#define APP_VERSION "1.2.3"
#define APP_HOSTNAME "boiler"