version 1.4.0. See ChangeLog

This commit is contained in:
proddy
2019-01-27 21:03:12 +01:00
parent fabe0d8fca
commit 817c8a263c
15 changed files with 501 additions and 77 deletions

View File

@@ -5,6 +5,20 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.4.0] 2019-01-27
### Changed
- last will MQTT topic prefixed with a header like the rest of the topics
- All double and float numbers rendered to 2 decimal places (precision = 2)
- Default logging set to None when starting a telnet session
### Added
- Added support for external Dallas sensors (DS1822, DS18S20, DS18B20, DS1825). See readme
- Added UBAParametersMessage type to fetch boiler modulation min & max values
- Report shows system load average
## [1.3.2] 2019-01-23 ## [1.3.2] 2019-01-23
### Fixed ### Fixed

View File

@@ -2,10 +2,10 @@
EMS-ESP is a project to build an electronic controller circuit using an Espressif ESP8266 microcontroller to communicate with EMS (Energy Management System) based Boilers and Thermostats from the Bosch range and compatibles such as Buderus, Nefit, Junkers etc. EMS-ESP is a project to build an electronic controller circuit using an Espressif ESP8266 microcontroller 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, secondly 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. There are 3 parts to this project, first the design of the circuit, secondly the code for the ESP8266 microcontroller firmware with telnet and MQTT support, and lastly an example configuration for Home Assistant to monitor the data and issue direct commands via a MQTT broker.
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/b8880625bdf841d4adb2829732030887)](https://app.codacy.com/app/proddy/EMS-ESP?utm_source=github.com&utm_medium=referral&utm_content=proddy/EMS-ESP&utm_campaign=Badge_Grade_Settings) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/b8880625bdf841d4adb2829732030887)](https://app.codacy.com/app/proddy/EMS-ESP?utm_source=github.com&utm_medium=referral&utm_content=proddy/EMS-ESP&utm_campaign=Badge_Grade_Settings)
[![version](https://img.shields.io/badge/version-1.3.2-brightgreen.svg)](CHANGELOG.md) [![version](https://img.shields.io/badge/version-1.4.0-brightgreen.svg)](CHANGELOG.md)
- [EMS-ESP](#ems-esp) - [EMS-ESP](#ems-esp)
- [Introduction](#introduction) - [Introduction](#introduction)
@@ -15,6 +15,7 @@ There are 3 parts to this project, first the design of the circuit, secondly the
- [Monitoring The Output](#monitoring-the-output) - [Monitoring The Output](#monitoring-the-output)
- [Building The Circuit](#building-the-circuit) - [Building The Circuit](#building-the-circuit)
- [Powering The EMS Circuit](#powering-the-ems-circuit) - [Powering The EMS Circuit](#powering-the-ems-circuit)
- [Adding external temperature sensors](#adding-external-temperature-sensors)
- [How The EMS Bus Works](#how-the-ems-bus-works) - [How The EMS Bus Works](#how-the-ems-bus-works)
- [EMS IDs](#ems-ids) - [EMS IDs](#ems-ids)
- [EMS Polling](#ems-polling) - [EMS Polling](#ems-polling)
@@ -115,6 +116,10 @@ The EMS circuit will work with both 3.3V and 5V. It's easiest though to power di
| ------------------------------------------ | | ------------------------------------------ |
| ![Power circuit](doc/schematics/Schematic_EMS-ESP-supercap.png) | | ![Power circuit](doc/schematics/Schematic_EMS-ESP-supercap.png) |
## Adding external temperature sensors
The code supports auto-detection of Dallas type temperature sensors. The default gpio pin (DQ) to the ESP8266 is D5 but this can be configured in `my_config.h`. The dallas chips DS1822, DS18S20, DS18B20, DS1825 are supported including their parasite varieties.
## How The EMS Bus Works ## How The EMS Bus Works
Packages are streamed to the EMS "bus" from any other compatible connected device via serial TTL transmission using protocol 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 streamed to the EMS "bus" from any other compatible connected device via serial TTL transmission using protocol 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.
@@ -203,11 +208,11 @@ In `ems.cpp` you can add scheduled calls to specific EMS types in the functions
### Which thermostats are supported? ### Which thermostats are supported?
I am still working on adding more support to known thermostats. Any contributions here are welcome. The know types are listed in `ems_devices.h`. I am still working on adding more support to known thermostats. Any contributions here are welcome. The know types are listed in `ems_devices.h` and include
- RC20 and RC30 are fully supported - RC20 and RC30, both are fully supported
- RC35 only supports the 1st heating circuit (HC1) - RC35 with support for the 1st heating circuit (HC1)
- TC100/TC200/Easy has only support for reading the temperatures. There seems to be no way to set settings using EMS bus messages. One option is to send XMPP messages but a client is needed. - TC100/TC200/Easy but only with support for reading the temperatures. There seems to be no way to set settings using EMS bus messages that I know of. One option is to send XMPP messages but a special server is needed and out of scope for this project.
### Customizing The Code ### Customizing The Code
@@ -219,16 +224,18 @@ I am still working on adding more support to known thermostats. Any contribution
### Using MQTT ### Using MQTT
The boiler data is collected and sent as a single JSON object to MQTT TOPIC `home/ems-esp/boiler_data`. The `home` preifx is the MQTT topic prefix and can be customized in `my_config.h`. A hash is generated (CRC32 based) to determine if the payload has changed, otherwise don't send it. An example payload looks roughly like: The boiler data is collected and sent as a single JSON object to MQTT TOPIC `home/ems-esp/boiler_data`. The `home` preifx is the MQTT topic prefix and can be customized in `my_config.h`. A hash is generated (CRC32 based) to determine if the payload has changed, otherwise it will not be sent. An example payload looks like:
`{"wWSelTemp":"60","selFlowTemp":"5.0","outdoorTemp":"?","wWActivated":"on","wWComfort":"Comfort","wWCurTmp":"46.0","wWCurFlow":"0.0","wWHeat":"on","curFlowTemp":"54.2","retTemp":"51.5","burnGas":"off","heatPmp":"off","fanWork":"off","ignWork":"off","wWCirc":"off","selBurnPow":"0","curBurnPow":"0","sysPress":"1.2","boilTemp":"56.7","pumpMod":"0","ServiceCode":"0H"}` `{"wWSelTemp":"60","selFlowTemp":"5.0","outdoorTemp":"?","wWActivated":"on","wWComfort":"Comfort","wWCurTmp":"46.0","wWCurFlow":"0.0","wWHeat":"on","curFlowTemp":"54.2","retTemp":"51.5","burnGas":"off","heatPmp":"off","fanWork":"off","ignWork":"off","wWCirc":"off","selBurnPow":"0","curBurnPow":"0","sysPress":"1.2","boilTemp":"56.7","pumpMod":"0","ServiceCode":"0H"}`
Similarly the thermostat values are sent as a JSON package under the topic `home/ems-esp/thermostat_data` with the current mode, room temperature and set temperature like Similarly the thermostat values are also sent as a JSON package with the topic `home/ems-esp/thermostat_data` along with the current mode, room temperature and set temperature:
`{"thermostat_currtemp":"19.8","thermostat_seltemp":"16.0","thermostat_mode":"manual"}` `{"thermostat_currtemp":"19.8","thermostat_seltemp":"16.0","thermostat_mode":"manual"}`
If MQTT is not used set the MQTT_HOST to `NULL`. If MQTT is not used set the MQTT_HOST to `NULL`.
Some home automation systems such as Domoticz and OpenHab have special formats for their MQTT messages so I would advise to use [node-red](https://nodered.org/) as a parser.
### The Basic Shower Logic ### The Basic Shower Logic
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. 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.
@@ -284,7 +291,7 @@ Porting to the Arduino IDE can be a little tricky but it is possible.
- Add the ESP8266 boards (from Preferences add Additional Board URL `http://arduino.esp8266.com/stable/package_esp8266com_index.json`) - Add the ESP8266 boards (from Preferences add Additional Board URL `http://arduino.esp8266.com/stable/package_esp8266com_index.json`)
- Go to Boards Manager and install ESP8266 2.4.x platform - Go to Boards Manager and install ESP8266 2.4.x platform
- Select your ESP8266 from Tools->Boards and the correct port with Tools->Port - Select your ESP8266 from Tools->Boards and the correct port with Tools->Port
- From the Library Manager install the needed libraries from platformio.ini. Note make sure you pick ArduinoJson v5 and not v6. See https://arduinojson.org/v5/doc/ - From the Library Manager install the needed libraries from platformio.ini. Note make sure you pick ArduinoJson v5 (5.13.4 and above) and not v6. See https://arduinojson.org/v5/doc/
- Put all the files in a single sketch folder - Put all the files in a single sketch folder
- cross your fingers and hit CTRL-R to compile - cross your fingers and hit CTRL-R to compile
@@ -320,9 +327,8 @@ Some annoying issues that need fixing:
- https://github.com/patvdleer/nefit-client-python - https://github.com/patvdleer/nefit-client-python
- https://github.com/marconfus/ha-nefit - https://github.com/marconfus/ha-nefit
- https://github.com/robertklep/nefit-easy-core - https://github.com/robertklep/nefit-easy-core
- Add support for a temperature sensor on the circuit (DS18B20)
- Improve detection of Heating Off without checking for selFlowTemp (selected flow temperature) - Improve detection of Heating Off without checking for selFlowTemp (selected flow temperature)
- Split MQTT into smaller chunks. Now the messages can be up to 600 bytes which may cause issues. - Split MQTT into smaller chunks. Now the messages can be up to 600 bytes which may cause issues. Preferably make the items configurable.
## Your Comments and Feedback ## Your Comments and Feedback

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -13,7 +13,9 @@ MyESP::MyESP() {
_app_hostname = strdup("MyESP"); _app_hostname = strdup("MyESP");
_app_name = strdup("MyESP"); _app_name = strdup("MyESP");
_app_version = strdup("1.0.0"); _app_version = strdup("1.0.0");
_boottime = strdup("unknown"); _boottime = strdup("unknown");
_load_average = 100; // calculated load average
_telnetcommand_callback = NULL; _telnetcommand_callback = NULL;
_telnet_callback = NULL; _telnet_callback = NULL;
@@ -33,6 +35,7 @@ MyESP::MyESP() {
_mqtt_will_topic = NULL; _mqtt_will_topic = NULL;
_mqtt_will_payload = NULL; _mqtt_will_payload = NULL;
_mqtt_base = NULL; _mqtt_base = NULL;
_mqtt_topic = NULL;
_mqtt_qos = 0; _mqtt_qos = 0;
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN; _mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
@@ -180,10 +183,8 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
// to MQTT_BASE/app_hostname/topic // to MQTT_BASE/app_hostname/topic
void MyESP::mqttSubscribe(const char * topic) { void MyESP::mqttSubscribe(const char * topic) {
if (mqttClient.connected() && (strlen(topic) > 0)) { if (mqttClient.connected() && (strlen(topic) > 0)) {
char s[100]; unsigned int packetId = mqttClient.subscribe(_mqttTopic(topic), _mqtt_qos);
snprintf(s, sizeof(s), "%s/%s/%s", _mqtt_base, _app_hostname, topic); myDebug_P(PSTR("[MQTT] Subscribing to %s (PID %d)"), _mqttTopic(topic), packetId);
unsigned int packetId = mqttClient.subscribe(s, _mqtt_qos);
myDebug_P(PSTR("[MQTT] Subscribing to %s (PID %d)"), s, packetId);
} }
} }
@@ -191,19 +192,15 @@ void MyESP::mqttSubscribe(const char * topic) {
// to MQTT_BASE/app_hostname/topic // to MQTT_BASE/app_hostname/topic
void MyESP::mqttUnsubscribe(const char * topic) { void MyESP::mqttUnsubscribe(const char * topic) {
if (mqttClient.connected() && (strlen(topic) > 0)) { if (mqttClient.connected() && (strlen(topic) > 0)) {
char s[100]; unsigned int packetId = mqttClient.unsubscribe(_mqttTopic(topic));
snprintf(s, sizeof(s), "%s/%s/%s", _mqtt_base, _app_hostname, topic); myDebug_P(PSTR("[MQTT] Unsubscribing to %s (PID %d)"), _mqttTopic(topic), packetId);
unsigned int packetId = mqttClient.unsubscribe(s);
myDebug_P(PSTR("[MQTT] Unsubscribing to %s (PID %d)"), s, packetId);
} }
} }
// MQTT Publish // MQTT Publish
void MyESP::mqttPublish(const char * topic, const char * payload) { void MyESP::mqttPublish(const char * topic, const char * payload) {
char s[MQTT_MAX_SIZE]; // myDebug_P(PSTR("[MQTT] Sending pubish to %s with payload %s"), _mqttTopic(topic), payload);
snprintf(s, sizeof(s), "%s/%s/%s", _mqtt_base, _app_hostname, topic); mqttClient.publish(_mqttTopic(topic), _mqtt_qos, _mqtt_retain, payload);
// myDebug_P(PSTR("[MQTT] Sending pubish to %s with payload %s"), s, payload);
mqttClient.publish(s, _mqtt_qos, _mqtt_retain, payload);
} }
// MQTT onConnect - when a connect is established // MQTT onConnect - when a connect is established
@@ -227,7 +224,7 @@ void MyESP::_mqtt_setup() {
mqttClient.onDisconnect([this](AsyncMqttClientDisconnectReason reason) { mqttClient.onDisconnect([this](AsyncMqttClientDisconnectReason reason) {
if (reason == AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) { if (reason == AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) {
myDebug_P(PSTR("[MQTT] TCP Disconnected")); myDebug_P(PSTR("[MQTT] TCP Disconnected. Check mqtt logs."));
(_mqtt_callback)(MQTT_DISCONNECT_EVENT, NULL, NULL); // call callback with disconnect (_mqtt_callback)(MQTT_DISCONNECT_EVENT, NULL, NULL); // call callback with disconnect
} }
if (reason == AsyncMqttClientDisconnectReason::MQTT_IDENTIFIER_REJECTED) { if (reason == AsyncMqttClientDisconnectReason::MQTT_IDENTIFIER_REJECTED) {
@@ -394,8 +391,9 @@ void MyESP::_consoleShowHelp() {
SerialAndTelnet.printf("* Connected to WiFi SSID: %s\n\r", WiFi.SSID().c_str()); SerialAndTelnet.printf("* Connected to WiFi SSID: %s\n\r", WiFi.SSID().c_str());
SerialAndTelnet.printf("* Boot time: %s\n\r", _boottime); SerialAndTelnet.printf("* Boot time: %s\n\r", _boottime);
} }
SerialAndTelnet.printf("* Free RAM:%d KB, Load:%d%%\n\r", (ESP.getFreeHeap() / 1024), getSystemLoadAverage());
// for battery power is ESP.getVcc()
SerialAndTelnet.printf("* Free RAM: %d bytes\n\r", ESP.getFreeHeap());
#ifdef DEBUG_SUPPORT #ifdef DEBUG_SUPPORT
SerialAndTelnet.println("* Warning: in DEBUG_SUPPORT mode!"); SerialAndTelnet.println("* Warning: in DEBUG_SUPPORT mode!");
#endif #endif
@@ -425,6 +423,8 @@ void MyESP::_consoleShowHelp() {
SerialAndTelnet.println(FPSTR(_helpProjectCmds[i].description)); SerialAndTelnet.println(FPSTR(_helpProjectCmds[i].description));
} }
} }
SerialAndTelnet.println(); // newline
} }
// reset / restart // reset / restart
@@ -561,7 +561,7 @@ void MyESP::_telnetCommand(char * commandLine) {
// print custom settings // print custom settings
(_fs_settings_callback)(MYESP_FSACTION_LIST, 0, NULL, NULL); (_fs_settings_callback)(MYESP_FSACTION_LIST, 0, NULL, NULL);
SerialAndTelnet.println("\n\rUsage: set <setting> <value>"); SerialAndTelnet.println("\n\rUsage: set <setting> [value]");
} else if (wc == 2) { } else if (wc == 2) {
char * setting = _telnet_readWord(); char * setting = _telnet_readWord();
_changeSetting(1, setting, NULL); _changeSetting(1, setting, NULL);
@@ -647,10 +647,15 @@ void MyESP::_mqttConnect() {
mqttClient.setClientId(_app_hostname); mqttClient.setClientId(_app_hostname);
mqttClient.setKeepAlive(_mqtt_keepalive); mqttClient.setKeepAlive(_mqtt_keepalive);
mqttClient.setCleanSession(false); mqttClient.setCleanSession(false);
mqttClient.setWill(_mqtt_will_topic, _mqtt_qos, _mqtt_retain, _mqtt_will_payload);
// last will
if (_mqtt_will_topic) {
myDebug_P(PSTR("[MQTT] Setting last will topic %s"), _mqttTopic(_mqtt_will_topic));
mqttClient.setWill(_mqttTopic(_mqtt_will_topic), _mqtt_qos, _mqtt_retain, _mqtt_will_payload);
}
if (_mqtt_username && _mqtt_password) { if (_mqtt_username && _mqtt_password) {
myDebug_P(PSTR("[MQTT] Connecting to MQTT using user %s"), _mqtt_username); myDebug_P(PSTR("[MQTT] Connecting to MQTT using user %s..."), _mqtt_username);
mqttClient.setCredentials(_mqtt_username, _mqtt_password); mqttClient.setCredentials(_mqtt_username, _mqtt_password);
} else { } else {
myDebug_P(PSTR("[MQTT] Connecting to MQTT...")); myDebug_P(PSTR("[MQTT] Connecting to MQTT..."));
@@ -720,7 +725,7 @@ void MyESP::setMQTT(char * mqtt_host,
// callback // callback
_mqtt_callback = callback; _mqtt_callback = callback;
// other mqtt settings // various mqtt settings
_mqtt_keepalive = mqtt_keepalive; _mqtt_keepalive = mqtt_keepalive;
_mqtt_qos = mqtt_qos; _mqtt_qos = mqtt_qos;
_mqtt_retain = mqtt_retain; _mqtt_retain = mqtt_retain;
@@ -731,7 +736,6 @@ void MyESP::setMQTT(char * mqtt_host,
} else { } else {
_mqtt_will_topic = strdup(mqtt_will_topic); _mqtt_will_topic = strdup(mqtt_will_topic);
} }
if (!mqtt_will_payload || *mqtt_will_payload == 0x00) { if (!mqtt_will_payload || *mqtt_will_payload == 0x00) {
_mqtt_will_payload = NULL; _mqtt_will_payload = NULL;
} else { } else {
@@ -739,6 +743,27 @@ void MyESP::setMQTT(char * mqtt_host,
} }
} }
// builds up a topic by prefixing the base and hostname
char * MyESP::_mqttTopic(const char * topic) {
char buffer[MQTT_MAX_TOPIC_SIZE] = {0};
strlcpy(buffer, _mqtt_base, sizeof(buffer));
strlcat(buffer, "/", sizeof(buffer));
strlcat(buffer, _app_hostname, sizeof(buffer));
strlcat(buffer, "/", sizeof(buffer));
strlcat(buffer, topic, sizeof(buffer));
//snprintf(buffer, sizeof(buffer), "%s/%s/%s", _mqtt_base, _app_hostname, topic);
if (_mqtt_topic) {
free(_mqtt_topic);
}
_mqtt_topic = strdup(buffer);
return _mqtt_topic;
}
// print contents of file // print contents of file
void MyESP::_fs_printConfig() { void MyESP::_fs_printConfig() {
File configFile = SPIFFS.open("/config.json", "r"); File configFile = SPIFFS.open("/config.json", "r");
@@ -754,7 +779,7 @@ void MyESP::_fs_printConfig() {
// format File System // format File System
void MyESP::_fs_eraseConfig() { void MyESP::_fs_eraseConfig() {
myDebug_P(PSTR("[FS] Erasing settings. Please wait. ESP will automatically restart when finished.")); myDebug_P(PSTR("[FS] Erasing settings, please wait. ESP will automatically restart when finished."));
if (SPIFFS.format()) { if (SPIFFS.format()) {
resetESP(); resetESP();
@@ -855,6 +880,30 @@ void MyESP::_fs_setup() {
} }
} }
unsigned long MyESP::getSystemLoadAverage() {
return _load_average;
}
// calculate load average
void MyESP::_calculateLoad() {
static unsigned long last_loadcheck = 0;
static unsigned long load_counter_temp = 0;
load_counter_temp++;
if (millis() - last_loadcheck > LOADAVG_INTERVAL) {
static unsigned long load_counter = 0;
static unsigned long load_counter_max = 1;
load_counter = load_counter_temp;
load_counter_temp = 0;
if (load_counter > load_counter_max) {
load_counter_max = load_counter;
}
_load_average = 100 - (100 * load_counter / load_counter_max);
last_loadcheck = millis();
}
}
// register new instance // register new instance
void MyESP::begin(char * app_hostname, char * app_name, char * app_version) { void MyESP::begin(char * app_hostname, char * app_name, char * app_version) {
_app_hostname = strdup(app_hostname); _app_hostname = strdup(app_hostname);
@@ -874,9 +923,12 @@ void MyESP::begin(char * app_hostname, char * app_name, char * app_version) {
* Loop. This is called as often as possible and it handles wifi, telnet, mqtt etc * Loop. This is called as often as possible and it handles wifi, telnet, mqtt etc
*/ */
void MyESP::loop() { void MyESP::loop() {
_calculateLoad(); // calculate load
jw.loop(); // WiFi jw.loop(); // WiFi
_telnetHandle(); // Telnet/Debugger _telnetHandle(); // Telnet/Debugger
// do nothing else until we've got a wifi connection
if (WiFi.getMode() & WIFI_AP) { if (WiFi.getMode() & WIFI_AP) {
return; return;
} }

View File

@@ -25,6 +25,8 @@
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
#endif #endif
#define LOADAVG_INTERVAL 30000 // Interval between calculating load average (in ms)
// WIFI // WIFI
#define WIFI_CONNECT_TIMEOUT 10000 // Connecting timeout for WIFI in ms #define WIFI_CONNECT_TIMEOUT 10000 // Connecting timeout for WIFI in ms
#define WIFI_RECONNECT_INTERVAL 60000 // If could not connect to WIFI, retry after this time in ms #define WIFI_RECONNECT_INTERVAL 60000 // If could not connect to WIFI, retry after this time in ms
@@ -38,6 +40,8 @@
#define MQTT_RECONNECT_DELAY_STEP 5000 // Increase the reconnect delay in 5 seconds after each failed attempt #define MQTT_RECONNECT_DELAY_STEP 5000 // Increase the reconnect delay in 5 seconds after each failed attempt
#define MQTT_RECONNECT_DELAY_MAX 120000 // Set reconnect time to 2 minutes at most #define MQTT_RECONNECT_DELAY_MAX 120000 // Set reconnect time to 2 minutes at most
#define MQTT_MAX_SIZE 600 // max length of MQTT message #define MQTT_MAX_SIZE 600 // max length of MQTT message
#define MQTT_MAX_TOPIC_SIZE 50 // max length of MQTT message
// Internal MQTT events // Internal MQTT events
#define MQTT_CONNECT_EVENT 0 #define MQTT_CONNECT_EVENT 0
#define MQTT_DISCONNECT_EVENT 1 #define MQTT_DISCONNECT_EVENT 1
@@ -111,11 +115,12 @@ class MyESP {
void setSettings(fs_callback_f callback, fs_settings_callback_f fs_settings_callback); void setSettings(fs_callback_f callback, fs_settings_callback_f fs_settings_callback);
// general // general
void end(); void end();
void loop(); void loop();
void begin(char * app_hostname, char * app_name, char * app_version); void begin(char * app_hostname, char * app_name, char * app_version);
void setBoottime(const char * boottime); void setBoottime(const char * boottime);
void resetESP(); void resetESP();
unsigned long getSystemLoadAverage();
private: private:
// mqtt // mqtt
@@ -127,6 +132,7 @@ class MyESP {
mqtt_callback_f _mqtt_callback; mqtt_callback_f _mqtt_callback;
void _mqttOnConnect(); void _mqttOnConnect();
void _sendStart(); void _sendStart();
char * _mqttTopic(const char * topic);
char * _mqtt_host; char * _mqtt_host;
char * _mqtt_username; char * _mqtt_username;
char * _mqtt_password; char * _mqtt_password;
@@ -138,6 +144,7 @@ class MyESP {
bool _mqtt_retain; bool _mqtt_retain;
char * _mqtt_will_topic; char * _mqtt_will_topic;
char * _mqtt_will_payload; char * _mqtt_will_payload;
char * _mqtt_topic;
// wifi // wifi
DNSServer dnsServer; // For Access Point (AP) support DNSServer dnsServer; // For Access Point (AP) support
@@ -182,6 +189,10 @@ class MyESP {
char * _app_hostname; char * _app_hostname;
char * _app_name; char * _app_name;
char * _app_version; char * _app_version;
// load average (0..100)
void _calculateLoad();
unsigned short int _load_average;
}; };
extern MyESP myESP; extern MyESP myESP;

View File

@@ -15,6 +15,7 @@ lib_deps =
JustWifi JustWifi
AsyncMqttClient AsyncMqttClient
ArduinoJson ArduinoJson
OneWire
[env:nodemcuv2] [env:nodemcuv2]
board = nodemcuv2 board = nodemcuv2

221
src/ds18.cpp Normal file
View File

@@ -0,0 +1,221 @@
/*
* Dallas support for external settings
* Copied from Espurna - Copyright (C) 2017-2018 by Xose Pérez <xose dot perez at gmail dot com>
*
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
*
* See ChangeLog.md for history
* See README.md for Acknowledgments
*
*/
#include "ds18.h"
std::vector<ds_device_t> _devices;
DS18::DS18() {
_wire = NULL;
_count = 0;
_gpio = GPIO_NONE;
}
DS18::~DS18() {
if (_wire)
delete _wire;
}
// init
uint8_t DS18::setup(uint8_t gpio) {
uint8_t count;
_gpio = gpio;
// OneWire
if (_wire)
delete _wire;
_wire = new OneWire(_gpio);
// Search devices
count = loadDevices();
// If no devices found check again pulling up the line
if (count == 0) {
pinMode(_gpio, INPUT_PULLUP);
count = loadDevices();
}
_count = count;
return count;
}
// scan every 2 seconds
void DS18::loop() {
static unsigned long last = 0;
if (millis() - last < DS18_READ_INTERVAL)
return;
last = millis();
// Every second we either start a conversion or read the scratchpad
static bool conversion = true;
if (conversion) {
// Start conversion
_wire->reset();
_wire->skip();
_wire->write(DS18_CMD_START_CONVERSION, DS18_PARASITE);
} else {
// Read scratchpads
for (unsigned char index = 0; index < _devices.size(); index++) {
// Read scratchpad
if (_wire->reset() == 0) {
// Force a CRC check error
_devices[index].data[0] = _devices[index].data[0] + 1;
return;
}
_wire->select(_devices[index].address);
_wire->write(DS18_CMD_READ_SCRATCHPAD);
uint8_t data[DS18_DATA_SIZE];
for (unsigned char i = 0; i < DS18_DATA_SIZE; i++) {
data[i] = _wire->read();
}
if (_wire->reset() != 1) {
// Force a CRC check error
_devices[index].data[0] = _devices[index].data[0] + 1;
return;
}
memcpy(_devices[index].data, data, DS18_DATA_SIZE);
}
}
conversion = !conversion;
}
// return string of the device, with name and address
char * DS18::getDeviceString(char * buffer, unsigned char index) {
uint8_t size = 128;
if (index < _count) {
uint8_t * address = _devices[index].address;
unsigned char chip_id = chip(index);
if (chip_id == DS18_CHIP_DS18S20) {
strlcpy(buffer, "DS18S20", size);
} else if (chip_id == DS18_CHIP_DS18B20) {
strlcpy(buffer, "DS18B20", size);
} else if (chip_id == DS18_CHIP_DS1822) {
strlcpy(buffer, "DS1822", size);
} else if (chip_id == DS18_CHIP_DS1825) {
strlcpy(buffer, "DS1825", size);
} else {
strlcpy(buffer, "Unknown", size);
}
char a[30] = {0};
snprintf(a,
sizeof(a),
"(%02X%02X%02X%02X%02X%02X%02X%02X) @ GPIO%d",
address[0],
address[1],
address[2],
address[3],
address[4],
address[5],
address[6],
address[7],
_gpio);
strlcat(buffer, a, size);
} else {
strlcpy(buffer, "invalid", size);
}
return buffer;
}
/*
* Read sensor values
*
* Registers:
byte 0: temperature LSB
byte 1: temperature MSB
byte 2: high alarm temp
byte 3: low alarm temp
byte 4: DS18S20: store for crc
DS18B20 & DS1822: configuration register
byte 5: internal use & crc
byte 6: DS18S20: COUNT_REMAIN
DS18B20 & DS1822: store for crc
byte 7: DS18S20: COUNT_PER_C
DS18B20 & DS1822: store for crc
byte 8: SCRATCHPAD_CRC
*/
double DS18::getValue(unsigned char index) {
if (index >= _count)
return 0;
uint8_t * data = _devices[index].data;
if (OneWire::crc8(data, DS18_DATA_SIZE - 1) != data[DS18_DATA_SIZE - 1]) {
return 0;
}
int16_t raw = (data[1] << 8) | data[0];
if (chip(index) == DS18_CHIP_DS18S20) {
raw = raw << 3; // 9 bit resolution default
if (data[7] == 0x10) {
raw = (raw & 0xFFF0) + 12 - data[6]; // "count remain" gives full 12 bit resolution
}
} else {
byte cfg = (data[4] & 0x60);
if (cfg == 0x00)
raw = raw & ~7; // 9 bit res, 93.75 ms
else if (cfg == 0x20)
raw = raw & ~3; // 10 bit res, 187.5 ms
else if (cfg == 0x40)
raw = raw & ~1; // 11 bit res, 375 ms
// 12 bit res, 750 ms
}
double value = (float)raw / 16.0;
if (value == DS18_DISCONNECTED) {
return 0;
}
return value;
}
// check for a supported DS chip version
bool DS18::validateID(unsigned char id) {
return (id == DS18_CHIP_DS18S20) || (id == DS18_CHIP_DS18B20) || (id == DS18_CHIP_DS1822) || (id == DS18_CHIP_DS1825);
}
// return the type
unsigned char DS18::chip(unsigned char index) {
if (index < _count)
return _devices[index].address[0];
return 0;
}
// scan for DS sensors and load into the vector
uint8_t DS18::loadDevices() {
uint8_t address[8];
_wire->reset();
_wire->reset_search();
while (_wire->search(address)) {
// Check CRC
if (_wire->crc8(address, 7) == address[7]) {
// Check ID
if (validateID(address[0])) {
ds_device_t device;
memcpy(device.address, address, 8);
_devices.push_back(device);
}
}
}
return (_devices.size());
}

55
src/ds18.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* Dallas support for external temperature sensors
* Copyright (C) 2017-2018 by Xose Pérez <xose dot perez at gmail dot com>
*
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
*
* See ChangeLog.md for history
* See README.md for Acknowledgments
*
*/
#pragma once
#include <OneWire.h>
#include <vector>
#define DS18_CHIP_DS18S20 0x10
#define DS18_CHIP_DS1822 0x22
#define DS18_CHIP_DS18B20 0x28
#define DS18_CHIP_DS1825 0x3B
#define DS18_DATA_SIZE 9
#define DS18_PARASITE 1
#define DS18_DISCONNECTED -127
#define GPIO_NONE 0x99
#define DS18_READ_INTERVAL 2000 // Force sensor read & cache every 2 seconds
#define DS18_CMD_START_CONVERSION 0x44
#define DS18_CMD_READ_SCRATCHPAD 0xBE
typedef struct {
uint8_t address[8];
uint8_t data[DS18_DATA_SIZE];
} ds_device_t;
class DS18 {
public:
DS18();
~DS18();
uint8_t setup(uint8_t gpio);
void loop();
char * getDeviceString(char * s, unsigned char index);
double getValue(unsigned char index);
protected:
bool validateID(unsigned char id);
unsigned char chip(unsigned char index);
uint8_t loadDevices();
OneWire * _wire;
uint8_t _count; // # devices
uint8_t _gpio; // the sensor pin
};

View File

@@ -8,13 +8,17 @@
*/ */
// local libraries // local libraries
#include "ds18.h"
#include "ems.h" #include "ems.h"
#include "ems_devices.h" #include "ems_devices.h"
#include "emsuart.h" #include "emsuart.h"
#include "my_config.h" #include "my_config.h"
#include "version.h" #include "version.h"
// my libraries // Dallas external temp sensors
DS18 ds18;
// shared libraries
#include <MyESP.h> #include <MyESP.h>
// public libraries // public libraries
@@ -74,10 +78,11 @@ Ticker showerColdShotStopTimer;
#define SHOWER_COLDSHOT_DURATION 10 // in seconds. 10 seconds for cold water before turning back hot water #define SHOWER_COLDSHOT_DURATION 10 // in seconds. 10 seconds for cold water before turning back hot water
typedef struct { typedef struct {
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 alert of cold water bool shower_alert; // true if we want the alert of cold water
bool led_enabled; // LED on/off bool led_enabled; // LED on/off
unsigned long timestamp; // for internal timings, via millis() unsigned long timestamp; // for internal timings, via millis()
uint8_t dallas_sensors; // count of dallas sensors
} _EMSESP_Status; } _EMSESP_Status;
typedef struct { typedef struct {
@@ -122,7 +127,7 @@ void myDebugLog(const char * s) {
} }
// 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 = 2) {
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;
@@ -202,14 +207,20 @@ void _renderIntfractionalValue(const char * prefix, const char * postfix, uint8_
strlcpy(buffer, " ", sizeof(buffer)); strlcpy(buffer, " ", sizeof(buffer));
strlcat(buffer, prefix, sizeof(buffer)); strlcat(buffer, prefix, sizeof(buffer));
strlcat(buffer, ": ", sizeof(buffer)); strlcat(buffer, ": ", sizeof(buffer));
strlcat(buffer, _int_to_char(s, value / (decimals * 10)), sizeof(buffer));
strlcat(buffer, ".", sizeof(buffer)); if (value == EMS_VALUE_INT_NOTSET) {
strlcat(buffer, _int_to_char(s, value % (decimals * 10)), sizeof(buffer)); strlcat(buffer, "?", sizeof(buffer));
} else {
strlcat(buffer, _int_to_char(s, value / (decimals * 10)), sizeof(buffer));
strlcat(buffer, ".", sizeof(buffer));
strlcat(buffer, _int_to_char(s, value % (decimals * 10)), sizeof(buffer));
}
if (postfix != NULL) { if (postfix != NULL) {
strlcat(buffer, " ", sizeof(buffer)); strlcat(buffer, " ", sizeof(buffer));
strlcat(buffer, postfix, sizeof(buffer)); strlcat(buffer, postfix, sizeof(buffer));
} }
myDebug(buffer); myDebug(buffer);
} }
@@ -266,6 +277,8 @@ void showInfo() {
myDebug(" LED is %s", EMSESP_Status.led_enabled ? "on" : "off"); myDebug(" LED is %s", EMSESP_Status.led_enabled ? "on" : "off");
myDebug(" # connected Dallas temperature sensors = %d", EMSESP_Status.dallas_sensors);
myDebug(" Thermostat is %s, Boiler is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s", myDebug(" Thermostat is %s, Boiler is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s",
(ems_getThermostatEnabled() ? "enabled" : "disabled"), (ems_getThermostatEnabled() ? "enabled" : "disabled"),
(ems_getBoilerEnabled() ? "enabled" : "disabled"), (ems_getBoilerEnabled() ? "enabled" : "disabled"),
@@ -291,8 +304,10 @@ void showInfo() {
myDebug(" Boiler type: %s", ems_getBoilerDescription(buffer_type)); myDebug(" Boiler type: %s", ems_getBoilerDescription(buffer_type));
// active stats // active stats
myDebug(" Hot tap water is %s", (EMS_Boiler.tapwaterActive ? "running" : "off")); if (ems_getBusConnected()) {
myDebug(" Central Heating is %s", (EMS_Boiler.heatingActive ? "active" : "off")); myDebug(" Hot tap water is %s", (EMS_Boiler.tapwaterActive ? "running" : "off"));
myDebug(" Central Heating is %s", (EMS_Boiler.heatingActive ? "active" : "off"));
}
// UBAParameterWW // UBAParameterWW
_renderBoolValue("Warm Water activated", EMS_Boiler.wWActivated); _renderBoolValue("Warm Water activated", EMS_Boiler.wWActivated);
@@ -328,6 +343,11 @@ void showInfo() {
_renderFloatValue("System pressure", "bar", EMS_Boiler.sysPress); _renderFloatValue("System pressure", "bar", EMS_Boiler.sysPress);
myDebug(" Current System Service Code: %s", EMS_Boiler.serviceCodeChar); myDebug(" Current System Service Code: %s", EMS_Boiler.serviceCodeChar);
// UBAParametersMessage
_renderIntValue("Heating temperature setting on the boiler", "C", EMS_Boiler.heating_temp);
_renderIntValue("Boiler circuit pump modulation max. power", "%", EMS_Boiler.pump_mod_max);
_renderIntValue("Boiler circuit pump modulation min. power", "%", EMS_Boiler.pump_mod_min);
// UBAMonitorSlow // UBAMonitorSlow
_renderFloatValue("Outside temperature", "C", EMS_Boiler.extTemp); _renderFloatValue("Outside temperature", "C", EMS_Boiler.extTemp);
_renderFloatValue("Boiler temperature", "C", EMS_Boiler.boilTemp); _renderFloatValue("Boiler temperature", "C", EMS_Boiler.boilTemp);
@@ -383,9 +403,21 @@ void showInfo() {
myDebug(""); // newline myDebug(""); // newline
// Dallas
if (EMSESP_Status.dallas_sensors != 0) {
myDebug("%sExternal temperature sensors:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
for (uint8_t i = 0; i < EMSESP_Status.dallas_sensors; i++) {
char s[80] = {0};
snprintf(s, sizeof(s), "Sensor #%d", i + 1);
_renderFloatValue(s, "C", ds18.getValue(i));
}
}
myDebug(""); // newline
// show the Shower Info // show the Shower Info
if (EMSESP_Status.shower_timer) { if (EMSESP_Status.shower_timer) {
myDebug("%s Shower stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF); myDebug("%sShower stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
myDebug(" Shower Timer is %s", (EMSESP_Shower.showerOn ? "active" : "off")); myDebug(" Shower Timer is %s", (EMSESP_Shower.showerOn ? "active" : "off"));
} }
} }
@@ -597,7 +629,7 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c
// we set the logging here // we set the logging here
void TelnetCallback(uint8_t event) { void TelnetCallback(uint8_t event) {
if (event == TELNET_EVENT_CONNECT) { if (event == TELNET_EVENT_CONNECT) {
ems_setLogging(EMS_SYS_LOGGING_BASIC); ems_setLogging(EMS_SYS_LOGGING_NONE);
} else if (event == TELNET_EVENT_DISCONNECT) { } else if (event == TELNET_EVENT_DISCONNECT) {
ems_setLogging(EMS_SYS_LOGGING_NONE); ems_setLogging(EMS_SYS_LOGGING_NONE);
} }
@@ -838,10 +870,11 @@ void WIFICallback() {
// Initialize the boiler settings and shower settings // Initialize the boiler settings and shower settings
void initEMSESP() { void initEMSESP() {
// general settings // general settings
EMSESP_Status.shower_timer = BOILER_SHOWER_TIMER; EMSESP_Status.shower_timer = BOILER_SHOWER_TIMER;
EMSESP_Status.shower_alert = BOILER_SHOWER_ALERT; EMSESP_Status.shower_alert = BOILER_SHOWER_ALERT;
EMSESP_Status.led_enabled = false; EMSESP_Status.led_enabled = false;
EMSESP_Status.timestamp = millis(); EMSESP_Status.timestamp = millis();
EMSESP_Status.dallas_sensors = 0;
// shower settings // shower settings
EMSESP_Shower.timerStart = 0; EMSESP_Shower.timerStart = 0;
@@ -888,18 +921,22 @@ void do_systemCheck() {
// 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
// only if we have a EMS connection // only if we have a EMS connection
void do_regularUpdates() { void do_regularUpdates() {
myDebugLog("Calling scheduled data refresh from EMS devices.."); if (ems_getBusConnected()) {
ems_getThermostatValues(); myDebugLog("Calling scheduled data refresh from EMS devices..");
ems_getBoilerValues(); ems_getThermostatValues();
ems_getBoilerValues();
}
} }
// 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 water"); if (EMSESP_Status.shower_alert) {
ems_setWarmTapWaterActivated(false); myDebugLog("[Shower] doing a shot of cold water");
EMSESP_Shower.doingColdShot = true; ems_setWarmTapWaterActivated(false);
// start the timer for n seconds which will reset the water back to hot EMSESP_Shower.doingColdShot = true;
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop); // start the timer for n seconds which will reset the water back to hot
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
}
} }
// turn back on the hot water for the shower // turn back on the hot water for the shower
@@ -1006,6 +1043,9 @@ void setup() {
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off
ledcheckTimer.attach(LEDCHECK_TIME, do_ledcheck); // blink heartbeat LED ledcheckTimer.attach(LEDCHECK_TIME, do_ledcheck); // blink heartbeat LED
// check for Dallas sensors
EMSESP_Status.dallas_sensors = ds18.setup(TEMPERATURE_SENSOR_PIN);
// init the EMS bus // init the EMS bus
// call ems.cpp's init function to set all the internal params // call ems.cpp's init function to set all the internal params
ems_init(MY_THERMOSTAT_MODELID); ems_init(MY_THERMOSTAT_MODELID);
@@ -1020,6 +1060,11 @@ void loop() {
// the main loop // the main loop
myESP.loop(); myESP.loop();
// check Dallas sensors
if (EMSESP_Status.dallas_sensors != 0) {
ds18.loop();
}
// publish the values to MQTT, regardless if the values haven't changed // publish the values to MQTT, regardless if the values haven't changed
// we don't want to publish when doing a deep scan of the thermostat // we don't want to publish when doing a deep scan of the thermostat
if (ems_getEmsRefreshed() && (scanThermostat_count == 0)) { if (ems_getEmsRefreshed() && (scanThermostat_count == 0)) {

View File

@@ -22,16 +22,20 @@ _EMS_Sys_Status EMS_Sys_Status; // EMS Status
CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO queue for Tx send buffer CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO queue for Tx send buffer
// callbacks per type // callbacks per type
// Boiler and Buderus devices
// generic
void _process_Version(uint8_t * data, uint8_t length); void _process_Version(uint8_t * data, uint8_t length);
// Boiler and Buderus devices
void _process_UBAMonitorFast(uint8_t * data, uint8_t length); void _process_UBAMonitorFast(uint8_t * data, uint8_t length);
void _process_UBAMonitorSlow(uint8_t * data, uint8_t length); void _process_UBAMonitorSlow(uint8_t * data, uint8_t length);
void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length); void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length);
void _process_UBAParameterWW(uint8_t * data, uint8_t length); void _process_UBAParameterWW(uint8_t * data, uint8_t length);
void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length); void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length);
void _process_UBAParametersMessage(uint8_t * data, uint8_t length);
void _process_SetPoints(uint8_t * data, uint8_t length);
// Common for most thermostats // Common for most thermostats
void _process_SetPoints(uint8_t * data, uint8_t length);
void _process_RCTime(uint8_t * data, uint8_t length); void _process_RCTime(uint8_t * data, uint8_t length);
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length); void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length);
@@ -66,8 +70,7 @@ const _EMS_Type EMS_Types[] = {
{EMS_MODEL_UBA, EMS_TYPE_UBAParameterWW, "UBAParameterWW", _process_UBAParameterWW}, {EMS_MODEL_UBA, EMS_TYPE_UBAParameterWW, "UBAParameterWW", _process_UBAParameterWW},
{EMS_MODEL_UBA, EMS_TYPE_UBATotalUptimeMessage, "UBATotalUptimeMessage", _process_UBATotalUptimeMessage}, {EMS_MODEL_UBA, EMS_TYPE_UBATotalUptimeMessage, "UBATotalUptimeMessage", _process_UBATotalUptimeMessage},
{EMS_MODEL_UBA, EMS_TYPE_UBAMaintenanceSettingsMessage, "UBAMaintenanceSettingsMessage", NULL}, {EMS_MODEL_UBA, EMS_TYPE_UBAMaintenanceSettingsMessage, "UBAMaintenanceSettingsMessage", NULL},
{EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", NULL}, {EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", _process_UBAParametersMessage},
{EMS_MODEL_UBA, EMS_TYPE_UBAMaintenanceStatusMessage, "UBAMaintenanceStatusMessage", NULL},
{EMS_MODEL_UBA, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints}, {EMS_MODEL_UBA, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
// RC20 and RC20F // RC20 and RC20F
@@ -212,6 +215,12 @@ void ems_init(uint8_t thermostat_modelid) {
// UBATotalUptimeMessage // UBATotalUptimeMessage
EMS_Boiler.UBAuptime = EMS_VALUE_LONG_NOTSET; // Total UBA working hours EMS_Boiler.UBAuptime = EMS_VALUE_LONG_NOTSET; // Total UBA working hours
// UBAParametersMessage
EMS_Boiler.heating_temp = EMS_VALUE_INT_NOTSET; // Heating temperature setting on the boiler
EMS_Boiler.pump_mod_max = EMS_VALUE_INT_NOTSET; // Boiler circuit pump modulation max. power
EMS_Boiler.pump_mod_min = EMS_VALUE_INT_NOTSET; // Boiler circuit pump modulation min. power
// calculated values
EMS_Boiler.tapwaterActive = EMS_VALUE_INT_NOTSET; // Hot tap water is on/off EMS_Boiler.tapwaterActive = EMS_VALUE_INT_NOTSET; // Hot tap water is on/off
EMS_Boiler.heatingActive = EMS_VALUE_INT_NOTSET; // Central heating is on/off EMS_Boiler.heatingActive = EMS_VALUE_INT_NOTSET; // Central heating is on/off
@@ -569,11 +578,6 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
if (length == 1) { if (length == 1) {
uint8_t value = telegram[0]; // 1st byte of data package uint8_t value = telegram[0]; // 1st byte of data package
// TODO: remove, only for debugging why some people can't do a Tx
//if ((value & 0x80) == 0x80) {
// myDebug("Poll: %02X", value);
//}
// check first for a Poll for us // check first for a Poll for us
if (value == (EMS_ID_ME | 0x80)) { if (value == (EMS_ID_ME | 0x80)) {
// store when we received a last poll // store when we received a last poll
@@ -886,6 +890,15 @@ void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length) {
EMS_Sys_Status.emsRefreshed = true; // when we receieve this, lets force an MQTT publish EMS_Sys_Status.emsRefreshed = true; // when we receieve this, lets force an MQTT publish
} }
/*
* UBAParametersMessage - type 0x16
*/
void _process_UBAParametersMessage(uint8_t * data, uint8_t length) {
EMS_Boiler.heating_temp = data[1];
EMS_Boiler.pump_mod_max = data[9];
EMS_Boiler.pump_mod_min = data[10];
}
/** /**
* UBAMonitorWWMessage - type 0x34 - warm water monitor. 19 bytes long * UBAMonitorWWMessage - type 0x34 - warm water monitor. 19 bytes long
* received every 10 seconds * received every 10 seconds
@@ -1298,6 +1311,7 @@ void ems_getBoilerValues() {
ems_doReadCommand(EMS_TYPE_UBAMonitorFast, EMS_Boiler.type_id); // get boiler stats, instead of waiting 10secs for the broadcast ems_doReadCommand(EMS_TYPE_UBAMonitorFast, EMS_Boiler.type_id); // get boiler stats, instead of waiting 10secs for the broadcast
ems_doReadCommand(EMS_TYPE_UBAMonitorSlow, EMS_Boiler.type_id); // get more boiler stats, instead of waiting 60secs for the broadcast ems_doReadCommand(EMS_TYPE_UBAMonitorSlow, EMS_Boiler.type_id); // get more boiler stats, instead of waiting 60secs for the broadcast
ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_Boiler.type_id); // get Warm Water values ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_Boiler.type_id); // get Warm Water values
ems_doReadCommand(EMS_TYPE_UBAParametersMessage, EMS_Boiler.type_id); // get MC10 boiler values
ems_doReadCommand(EMS_TYPE_UBATotalUptimeMessage, EMS_Boiler.type_id); // get Warm Water values ems_doReadCommand(EMS_TYPE_UBATotalUptimeMessage, EMS_Boiler.type_id); // get Warm Water values
} }
@@ -1381,7 +1395,7 @@ char * ems_getBoilerDescription(char * buffer) {
* Find the versions of our connected devices * Find the versions of our connected devices
*/ */
void ems_scanDevices() { void ems_scanDevices() {
myDebug("Scanning EMS bus for devices. This may take a few seconds..."); myDebug("Scanning EMS bus for devices...");
// start refresh when scanning and forget anything devices we may have already set // start refresh when scanning and forget anything devices we may have already set
EMS_Thermostat.type_id = EMS_ID_NONE; // forget thermostat EMS_Thermostat.type_id = EMS_ID_NONE; // forget thermostat

View File

@@ -203,6 +203,11 @@ typedef struct { // UBAParameterWW
// UBATotalUptimeMessage // UBATotalUptimeMessage
uint32_t UBAuptime; // Total UBA working hours uint32_t UBAuptime; // Total UBA working hours
// UBAParametersMessage
uint8_t heating_temp; // Heating temperature setting on the boiler
uint8_t pump_mod_max; // Boiler circuit pump modulation max. power
uint8_t pump_mod_min; // Boiler circuit pump modulation min. power
// calculated values // calculated values
uint8_t tapwaterActive; // Hot tap water is on/off uint8_t tapwaterActive; // Hot tap water is on/off
uint8_t heatingActive; // Central heating is on/off uint8_t heatingActive; // Central heating is on/off

View File

@@ -97,7 +97,7 @@ const _Boiler_Type Boiler_Types[] = {
// various boilers and buderus type devices // various boilers and buderus type devices
{EMS_MODEL_UBA, 72, 0x08, "MC10"}, {EMS_MODEL_UBA, 72, 0x08, "MC10"},
{EMS_MODEL_UBA, 123, 0x08, "Nefit Trendline"}, {EMS_MODEL_UBA, 123, 0x08, "Buderus GB172/Nefit Trendline"},
{EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"}, {EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"},
{EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler"}, {EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler"},
{EMS_MODEL_UBA, 190, 0x09, "BC10 Base Controller"}, {EMS_MODEL_UBA, 190, 0x09, "BC10 Base Controller"},
@@ -115,11 +115,11 @@ const _Boiler_Type Boiler_Types[] = {
const _Thermostat_Type Thermostat_Types[] = { const _Thermostat_Type Thermostat_Types[] = {
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}, {EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20, 77, 0x17, "RC20 (e.g. Nefit Moduline 300)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}, {EMS_MODEL_RC20, 77, 0x17, "RC20/Nefit Moduline 300)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}, {EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC30, 78, 0x10, "RC30 (e.g. Nefit Moduline 400)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}, {EMS_MODEL_RC30, 78, 0x10, "RC30/Nefit Moduline 400)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC35, 86, 0x10, "RC35", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}, {EMS_MODEL_RC35, 86, 0x10, "RC35", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_EASY, 202, 0x18, "TC100 (e.g. Nefit Easy or CT100)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO}, {EMS_MODEL_EASY, 202, 0x18, "TC100/Nefit Easy", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_RC310, 158, 0x10, "RC310", EMS_THERMOSTAT_READ_NO, EMS_THERMOSTAT_WRITE_NO}, {EMS_MODEL_RC310, 158, 0x10, "RC310", EMS_THERMOSTAT_READ_NO, EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_CW100, 255, 0x18, "Bosch CW100", EMS_THERMOSTAT_READ_NO, EMS_THERMOSTAT_WRITE_NO} {EMS_MODEL_CW100, 255, 0x18, "Bosch CW100", EMS_THERMOSTAT_READ_NO, EMS_THERMOSTAT_WRITE_NO}

View File

@@ -6,5 +6,5 @@
#pragma once #pragma once
#define APP_NAME "EMS-ESP" #define APP_NAME "EMS-ESP"
#define APP_VERSION "1.3.2" #define APP_VERSION "1.4.0"
#define APP_HOSTNAME "ems-esp" #define APP_HOSTNAME "ems-esp"