1.9.5 - Merge remote-tracking branch 'origin/dev'

This commit is contained in:
Paul
2020-04-30 10:47:55 +02:00
27 changed files with 3863 additions and 2067 deletions

View File

@@ -36,4 +36,6 @@ PointerAlignment: Middle
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInParentheses: false
DerivePointerAlignment: false
SortIncludes: false

3
.gitignore vendored
View File

@@ -18,3 +18,6 @@ node_modules
# project specfic
scripts/stackdmp.txt
firmware
# firmware
*.bin

View File

@@ -1,7 +1,8 @@
os: linux
dist: bionic
language: python
python:
- "2.7"
- "3.8"
cache:
directories:
@@ -55,6 +56,7 @@ before_deploy:
deploy:
provider: releases
edge:
# source: wenkokke/dpl
branch: master
token: ${GITHUB_TOKEN}
file_glob: true

View File

@@ -5,7 +5,46 @@ 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.9.4] 2019-12-15
## [1.9.5] 30-04-2020
### Added
- Solar Module SM200 support
- Support writing to Junkers FR100 thermostats
- Support writing to RC100, Moduline 1000/1010 thermostats
- MM10 Mixing module support (thanks @MichaelDvP)
- MM200 warm water circuits (https://github.com/proddy/EMS-ESP/pull/315)
- Support for Moduline 200 and Sieger ES72 thermostats
- First implementation of writing to generic Junker Thermostats (thanks @Neonox31)
- Added model type (Buderus, Sieger, Junkers, Nefit, Bosch, Worcester) to device names
- `set master_thermostat <product id>` to choose with thermostat is master when there are multiple on the bus
- `boiler wwonetime` command from Telnet
- `set bus_id <ID>` to support multiple EMS-ESP circuits. Default is 0x0B to mimic a service key.
- `mqtt_nestedjson` option to disable multiple data records being nested into a single JSON string
- MQTT publish messages are queued and gracefully published every second to avoid TCP blocks
- Added features to WW messages (0x33, 0x34) to improve WW monitoring. (PR#338 by @ypaindaveine)
- Added mixing log and stub for EMS type 0xAC (PR#338 by @ypaindaveine)
- Added Thermostat retrieving settings (0xA5) (validated on RC30N) with MQTT support (thanks Yves @ypaindaveine. See #352)
- Merged with PR https://github.com/proddy/EMS-ESP/pull/366 from @MichaelDvP fixing RC20 and MM50
### Fixed
- set boiler warm water temp on Junkers/Bosch HT3
- fixed detection of the Moduline 400 thermostat
- RC35 setting temperature also forces the current select temp to change, irrespective of the mode
### Changed
- improved MQTT publishing to stop network flooding. `publish_time` of -1 is no publish, 0 is automatic otherwise its a time interval
- External sensors (like Dallas DS18*) are sent as a nested MQTT topic including their unqiue identifier
- `mqttlog` console command renamed to `mqttqueue` to only show the current publish queue
- `status` payload on start-up shows the IP and Version of EMS-ESP
- `thermostat mode` takes a string like manual,auto,heat,day,night,eco,comfort,holiday,nofrost
- `thermostat temp` also takes a mode string, e.g. `thermostat temp 20 heat`
- `queue` renamed to `txqueue`
### Removed
- `autodetect scan`. Replaced with `devices scan` and `devices scan+` for deep scanning
- `mqttlog all` and showing MQTT log in the web interface - no point showing history of previous mqtt publishes in ESP's precious memory. For debugging I recommend using MQTT Explorer or another external tool.
## [1.9.4] 15-12-2019
There are breaking changes in this release. Make you sure you adjust the MQTT topics as described in the wiki.

View File

@@ -15,7 +15,7 @@ EMS-ESP is a open-source system built for the Espressif ESP8266 microcontroller
## Features
* Supporting more than [50 EMS devices](https://emsesp.github.io/docs/#/Supported-EMS-Devices) (EMS 1, EMS+/2.0 and Heatronics 3).
* Supporting more than [50 EMS devices](https://emsesp.github.io/docs/#/Supported-EMS-Devices) (EMS 1, EMS 2.0/Plus and Heatronics 3).
* A web interface for easy configuration and real-time monitoring of the EMS bus.
* Telnet for advanced configuration and verbose traffic logging.
* Configurable MQTT, with templates for Home Assistant and Domoticz.

View File

@@ -4,16 +4,23 @@
[platformio]
default_envs = release
;default_envs = debug
; default_envs = debug
[common]
# From https://github.com/esp8266/Arduino/blob/master/tools/sdk/ld
# eagle.flash.4m1m.ld = 1019 KB sketch, 1000 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 2052 KB OTA & buffer
# eagle.flash.4m2m.ld = same as above but with 2024 KB SPIFFS
# eagle.flash.4m.ld = same as above but with no SPIFFS storage
ldscript = eagle.flash.4m1m.ld
; custom build options are:
; -DMYESP_TIMESTAMP
; -DTESTS
; -DCRASH
; -DFORCE_SERIAL
; -DMYESP_DEBUG
;custom_flags = -DFORCE_SERIAL -DMYESP_DEBUG
; -DSENSOR_MQTT_USEID
; custom_flags = -DFORCE_SERIAL -DMYESP_DEBUG -DEMSESP_SIMULATE
custom_flags =
# Available lwIP variants (macros):
@@ -26,33 +33,28 @@ custom_flags =
# -DVTABLES_IN_FLASH
# -DNO_GLOBAL_EEPROM
# -DBEARSSL_SSL_BASIC
# general_flags = -DNO_GLOBAL_EEPROM -DVTABLES_IN_FLASH -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
general_flags = -DNO_GLOBAL_EEPROM
# From https://github.com/esp8266/Arduino/blob/master/tools/sdk/ld
# eagle.flash.4m1m.ld = 1019 KB sketch, 1000 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 2052 KB OTA & buffer
# eagle.flash.4m2m.ld = same as above but with 2024 KB SPIFFS
# eagle.flash.4m.ld = same as above but with no SPIFFS storage
build_flags_4m1m = -Wl,-Teagle.flash.4m1m.ld
build_flags = ${common.general_flags} ${common.build_flags_4m1m}
build_flags = ${common.general_flags} -std=c++11 -fno-exceptions
[env]
framework = arduino
;platform = espressif8266@2.2.2 ; arduino core 2.5.2
platform = espressif8266
;platform = https://github.com/platformio/platform-espressif8266#develop
;platform = https://github.com/platformio/platform-espressif8266#feature/stage
board_build.ldscript = ${common.ldscript}
lib_compat_mode = strict
lib_deps =
https://github.com/rlogiacco/CircularBuffer
https://github.com/PaulStoffregen/OneWire
https://github.com/me-no-dev/ESPAsyncWebServer
https://github.com/me-no-dev/ESPAsyncUDP
uuid-common@^1.1.0
uuid-log@^2.1.1
uuid-syslog@^2.0.4 ; https://github.com/nomis/mcu-uuid-syslog
uuid-log@^2.1.1
uuid-syslog@^2.0.4 ; https://github.com/nomis/mcu-uuid-syslog
JustWifi@2.0.2 ; https://github.com/xoseperez/justwifi
AsyncMqttClient@0.8.2 ; https://github.com/marvinroger/async-mqtt-client
EEPROM_Rotate@0.9.2 ; https://github.com/xoseperez/eeprom_rotate
ArduinoJson@6.13.0 ; https://github.com/bblanchon/ArduinoJson
ArduinoJson
ESPAsyncTCP@1.2.2 ; https://github.com/me-no-dev/ESPAsyncTCP
upload_speed = 921600
monitor_speed = 115200
@@ -112,3 +114,4 @@ build_flags = ${common.build_flags} ${common.custom_flags}
extra_scripts =
pre:scripts/pre_script.py
scripts/main_script.py

View File

@@ -25,7 +25,7 @@ def clr(color, text):
def remove_float_support():
flags = " ".join(env['LINKFLAGS'])
print(clr(Color.BLUE, "** LINKFLAGS = %ss" % flags))
print(clr(Color.BLUE, "LINKFLAGS = %ss" % flags))
flags = flags.replace("-u _printf_float", "")
flags = flags.replace("-u _scanf_float", "")
newflags = flags.split()

View File

@@ -7,7 +7,7 @@ Import("env")
def build_web():
print("** Building web...")
env.Execute(
"node ./tools/webfilesbuilder/node_modules/gulp/bin/gulp.js --cwd ./tools/webfilesbuilder")
"node ./tools/webfilesbuilder/node_modules/gulp/bin/gulp.js --silent --cwd ./tools/webfilesbuilder")
def code_check(source, target, env):
print("** Starting cppcheck...")

View File

@@ -23,6 +23,15 @@ union system_rtcmem_t {
uint32_t value;
};
struct mqtt_message_t {
uint16_t packetId = 0;
char * topic = nullptr;
char * payload = nullptr;
bool retain = false;
uint8_t retry_count = 0;
};
std::deque<mqtt_message_t> _mqtt_queue;
// nasty global variables that are called from internal ws functions
static char * _general_password = nullptr;
static bool _shouldRestart = false;
@@ -74,10 +83,12 @@ MyESP::MyESP() {
_mqtt_heartbeat = false;
_mqtt_keepalive = MQTT_KEEPALIVE;
_mqtt_qos = MQTT_QOS;
_mqtt_nestedjson = false;
_mqtt_retain = MQTT_RETAIN;
_mqtt_will_topic = strdup(MQTT_WILL_TOPIC);
_mqtt_will_online_payload = strdup(MQTT_WILL_ONLINE_PAYLOAD);
_mqtt_will_offline_payload = strdup(MQTT_WILL_OFFLINE_PAYLOAD);
_mqtt_publish_fails = 0; // count of number of failed MQTT topic publishes
// network
_network_password = nullptr;
@@ -110,14 +121,6 @@ MyESP::MyESP() {
// get the build time
_buildTime = _getBuildTime();
// MQTT log
for (uint8_t i = 0; i < MYESP_MQTTLOG_MAX; i++) {
MQTT_log[i].type = 0;
MQTT_log[i].timestamp = 0;
MQTT_log[i].topic = nullptr;
MQTT_log[i].payload = nullptr;
}
}
MyESP::~MyESP() {
@@ -382,21 +385,17 @@ bool MyESP::mqttSubscribe(const char * topic) {
if (mqttClient.connected() && (strlen(topic) > 0)) {
char * topic_s = _mqttTopic(topic);
uint16_t packet_id = mqttClient.subscribe(topic_s, _mqtt_qos);
#ifdef MYESP_DEBUG
myDebug_P(PSTR("[MQTT] Subscribing to %s"), topic_s);
#endif
if (packet_id) {
// add to mqtt log
_addMQTTLog(topic_s, "", MYESP_MQTTLOGTYPE_SUBSCRIBE); // Has an empty payload for now
return true;
} else {
uint16_t packet_id = mqttClient.subscribe(topic_s, _mqtt_qos);
if (!packet_id) {
myDebug_P(PSTR("[MQTT] Error subscribing to %s, error %d"), _mqttTopic(topic), packet_id);
return false;
}
}
return false; // didn't work
return true;
}
// MQTT unsubscribe
@@ -407,30 +406,218 @@ void MyESP::mqttUnsubscribe(const char * topic) {
}
}
// Publish using the user's custom retain flag
bool MyESP::mqttPublish(const char * topic, const char * payload) {
// use the custom MQTT retain flag
return mqttPublish(topic, payload, _mqtt_retain);
}
// print MQTT log
void MyESP::_printMQTTQueue() {
myDebug_P(PSTR("MQTT publish queue:"));
// MQTT Publish
// returns true if all good
bool MyESP::mqttPublish(const char * topic, const char * payload, bool retain) {
if (mqttClient.connected() && (strlen(topic) > 0)) {
#ifdef MYESP_DEBUG
myDebug_P(PSTR("[MQTT] Sending publish to %s with payload %s"), _mqttTopic(topic), payload);
#endif
uint16_t packet_id = mqttClient.publish(_mqttTopic(topic), _mqtt_qos, retain, payload);
if (_mqtt_queue.empty()) {
myDebug_P(PSTR(" queue is empty!"));
myDebug_P(PSTR("")); // newline
return;
}
if (packet_id) {
_addMQTTLog(topic, payload, MYESP_MQTTLOGTYPE_PUBLISH); // add to the log
return true;
for (mqtt_message_t it : _mqtt_queue) {
if (it.retry_count == 0) {
if (it.packetId == 0) {
myDebug_P(PSTR(" topic=%s payload=%s"), it.topic, it.payload);
} else {
myDebug_P(PSTR(" topic=%s payload=%s (pid %d)"), it.topic, it.payload, it.packetId);
}
} else {
myDebug_P(PSTR("[MQTT] Error publishing to %s with payload %s [error %d]"), _mqttTopic(topic), payload, packet_id);
myDebug_P(PSTR(" topic=%s payload=%s (pid %d, retry #%d)"), it.topic, it.payload, it.packetId, it.retry_count);
}
}
return false; // failed
myDebug_P(PSTR("")); // newline
}
// Publish using the user's custom retain flag
bool MyESP::mqttPublish(const char * topic, const char * payload) {
return (_mqttQueue(topic, payload, _mqtt_retain));
}
bool MyESP::mqttPublish(const char * topic, JsonDocument & payload) {
return (_mqttQueue(topic, payload, _mqtt_retain));
}
// MQTT Publish
bool MyESP::mqttPublish(const char * topic, const char * payload, bool retain) {
return (_mqttQueue(topic, payload, retain));
}
bool MyESP::mqttPublish(const char * topic, JsonDocument & payload, bool retain) {
return (_mqttQueue(topic, payload, retain));
}
// put a payload string into the queue
// can't have empty topic
// returns false if can't add to queue
bool MyESP::_mqttQueue(const char * topic, const char * payload, bool retain) {
if (!mqttClient.connected() || _mqtt_queue.size() >= MQTT_QUEUE_MAX_SIZE || !_hasValue(topic)) {
return false;
}
// create a new message
mqtt_message_t element;
element.topic = strdup(topic);
element.retain = retain;
element.packetId = 0;
element.retry_count = 0;
if (payload != NULL) {
element.payload = strdup(payload);
}
#ifdef MYESP_DEBUG
myDebug_P(PSTR("[MQTT] Adding to queue: #%d [%s] %s"), _mqtt_queue.size(), element.topic, element.payload);
#endif
_mqtt_queue.push_back(element);
return true;
}
// convert json doc to a string buffer and place on queue
// can't have empty payload or topic
// returns false if can't add to queue
bool MyESP::_mqttQueue(const char * topic, JsonDocument & payload, bool retain) {
if (!mqttClient.connected() || _mqtt_queue.size() >= MQTT_QUEUE_MAX_SIZE || !_hasValue(topic)) {
return false;
}
// check for empty JSON doc - we don't like those
size_t capacity = measureJson(payload);
if (!capacity) {
return false;
}
// create a new message
mqtt_message_t element;
element.topic = strdup(topic);
element.retain = retain;
element.packetId = 0;
element.retry_count = 0;
// reserve space for buffer and serialize json into it
capacity++; // add one more to cover the EOL
element.payload = (char *)malloc(capacity);
serializeJson(payload, (char *)element.payload, capacity);
#ifdef MYESP_DEBUG
myDebug_P(PSTR("[MQTT] Adding to queue: #%d [%s] %s"), _mqtt_queue.size(), element.topic, element.payload);
#endif
_mqtt_queue.push_back(element);
return true;
}
// called when an MQTT Publish ACK is received
// check if ACK matches the last Publish we sent, if not report an error
// and always remove from queue
void MyESP::_mqttOnPublish(uint16_t packetId) {
#ifdef MYESP_DEBUG
myDebug_P(PSTR("[MQTT] Publish ACK for PID %d"), packetId);
#endif
// find the MQTT message in the queue and remove it
if ((_mqtt_queue.empty()) || (_mqtt_qos == 0)) {
return;
}
mqtt_message_t element = _mqtt_queue.front(); // get top of list
// if the last published failed, don't bother checking it. wait for the re-try
if (element.packetId == 0) {
return;
}
if (element.packetId == packetId) {
#ifdef MYESP_DEBUG
myDebug_P(PSTR("[MQTT] Found PID %d. Removing from queue."), packetId);
#endif
} else {
#ifdef MYESP_DEBUG
myDebug_P(PSTR("[MQTT] Mismatch, expecting PID %d, got %d."), element.packetId, packetId);
_mqtt_publish_fails++; // increment error count
#endif
}
_mqttRemoveLastPublish(); // always remove
}
// removes top of queue
void MyESP::_mqttRemoveLastPublish() {
mqtt_message_t element = _mqtt_queue.front(); // get top of list
free(element.topic);
if (element.payload) {
free(element.payload);
}
_mqtt_queue.pop_front();
}
// take top from queue and try and publish it
void MyESP::_mqttPublishQueue() {
if ((!mqttClient.connected()) || (_mqtt_queue.empty())) {
return;
}
mqtt_message_t element = _mqtt_queue.front(); // fetch from queue
// try and publish it
uint16_t packet_id = mqttClient.publish(_mqttTopic(element.topic), _mqtt_qos, element.retain, element.payload);
#ifdef MYESP_DEBUG
myDebug_P(PSTR("[MQTT] Sent publish (attempt #%d, pid %d) [%s] [%s]"), element.retry_count, packet_id, _mqttTopic(element.topic), element.payload);
#endif
if (packet_id == 0) {
// it failed. if we retried n times, give up. remove from queue
if (element.retry_count == (MQTT_PUBLISH_MAX_RETRY - 1)) {
myDebug_P(PSTR("[MQTT] Failed to publish to %s with payload %s"), _mqttTopic(element.topic), element.payload);
_mqtt_publish_fails++; // increment failure counter
_mqttRemoveLastPublish();
} else {
_mqtt_queue[0].retry_count++;
}
return;
}
// if we have ACK set with QOS 1 or 2, leave on queue and let the ACK process remove it
// but add the packet_id so we can check it later
if (_mqtt_qos != 0) {
_mqtt_queue[0].packetId = packet_id;
#ifdef MYESP_DEBUG
myDebug_P(PSTR("[MQTT] Setting packetID %d"), packet_id);
#endif
return;
}
// delete it from queue
_mqttRemoveLastPublish();
}
// send online appended with the version information as JSON
void MyESP::_sendStartTopic() {
StaticJsonDocument<MYESP_JSON_MAXSIZE_SMALL> doc;
JsonObject payload = doc.to<JsonObject>();
payload["version"] = _app_version;
payload["IP"] = WiFi.localIP().toString();
// add time if we know it
if ((_ntp_enabled) && (NTP.tcr->abbrev != nullptr)) {
uint32_t real_time = getSystemTime();
// exclude millis() just in case
if (real_time > 10000L) {
char s[25];
// ISO 8601 describes an internationally accepted way to represent dates and times using numbers
snprintf_P(s,
25,
PSTR("%04u-%02u-%02uT%02u:%02u:%02u"),
to_year(real_time),
to_month(real_time),
to_day(real_time),
to_hour(real_time),
to_minute(real_time),
to_second(real_time)
);
payload["boottime"] = s;
}
}
mqttPublish(MQTT_TOPIC_START, doc, true); // send with retain on
}
// MQTT onConnect - when a connect is established
@@ -440,8 +627,8 @@ void MyESP::_mqttOnConnect() {
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
_mqtt_last_connection = millis();
// say we're alive to the Last Will topic
mqttPublish(_mqtt_will_topic, _mqtt_will_online_payload, true); // force retain on
// say we're alive to the Last Will topic, with retain on
mqttPublish(_mqtt_will_topic, _mqtt_will_online_payload, true);
// subscribe to general subs
mqttSubscribe(MQTT_TOPIC_RESTART);
@@ -449,10 +636,14 @@ void MyESP::_mqttOnConnect() {
// subscribe to a start message and send the first publish
// forcing retain to off since we only want to send this once
mqttSubscribe(MQTT_TOPIC_START);
mqttPublish(MQTT_TOPIC_START, MQTT_TOPIC_START_PAYLOAD, false);
// send start topic now unless NTP is enabled, otherwise wait for the time
if (!_ntp_enabled) {
_sendStartTopic();
}
// send heartbeat if enabled
_heartbeatCheck();
heartbeatCheck(true);
// call custom function to handle mqtt receives
(_mqtt_callback_f)(MQTT_CONNECT_EVENT, nullptr, nullptr);
@@ -489,7 +680,8 @@ void MyESP::_mqtt_setup() {
});
//mqttClient.onSubscribe([this](uint16_t packetId, uint8_t qos) { myDebug_P(PSTR("[MQTT] Subscribe ACK for PID %d"), packetId); });
//mqttClient.onPublish([this](uint16_t packetId) { myDebug_P(PSTR("[MQTT] Publish ACK for PID %d"), packetId); });
mqttClient.onPublish([this](uint16_t packetId) { _mqttOnPublish(packetId); });
mqttClient.onMessage([this](char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
_mqttOnMessage(topic, payload, len);
@@ -502,8 +694,7 @@ void MyESP::_mqtt_setup() {
// last will
if (_hasValue(_mqtt_will_topic)) {
mqttClient.setWill(_mqttTopic(_mqtt_will_topic), 1, true,
_mqtt_will_offline_payload); // retain always true
mqttClient.setWill(_mqttTopic(_mqtt_will_topic), 1, true, _mqtt_will_offline_payload); // retain always true
}
// set credentials if we have them
@@ -525,7 +716,7 @@ void MyESP::_wifi_setup() {
WiFi.setSleepMode(WIFI_NONE_SLEEP); // added to possibly fix wifi dropouts in arduino core 2.5.0
// ref: https://github.com/esp8266/Arduino/issues/6471
// ref: https://github.com/esp8266/Arduino/issues/6366
// high tx power causing weird behavior, slighly lowering from 20.5 to 20.0 may help stability
// high tx power causing weird behavior, slightly lowering from 20.5 to 20.0 may help stability
WiFi.setOutputPower(20.0); // in DBM
#endif
@@ -715,8 +906,8 @@ void MyESP::_consoleShowHelp() {
myDebug_P(PSTR("*"));
myDebug_P(PSTR("* Commands:"));
myDebug_P(PSTR("* ?/help=show commands, CTRL-D/quit=close telnet session"));
myDebug_P(PSTR("* set, system, restart, mqttlog, kick, save"));
myDebug_P(PSTR("* ?/help=show commands, CTRL-D/quit=end telnet session"));
myDebug_P(PSTR("* set, system, restart, mqttqueue, kick, save"));
#ifdef CRASH
myDebug_P(PSTR("* crash <dump | clear | test [n]>"));
@@ -749,16 +940,17 @@ bool MyESP::_hasValue(const char * s) {
void MyESP::_printSetCommands() {
myDebug_P(PSTR("\nset commands:\n"));
myDebug_P(PSTR(" set erase"));
myDebug_P(PSTR(" set <wifi_mode <ap | client>"));
myDebug_P(PSTR(" set wifi_mode <ap | client>"));
myDebug_P(PSTR(" set <wifi_ssid | wifi_password> [value]"));
myDebug_P(PSTR(" set mqtt_enabled <on | off>"));
myDebug_P(PSTR(" set mqtt_enabled [on | off]"));
myDebug_P(PSTR(" set <mqtt_ip | mqtt_username | mqtt_password> [value]"));
myDebug_P(PSTR(" set mqtt_heartbeat <on | off> (every 2 mins)"));
myDebug_P(PSTR(" set mqtt_base [string]"));
myDebug_P(PSTR(" set mqtt_port [number]"));
myDebug_P(PSTR(" set mqtt_base [prefix]"));
myDebug_P(PSTR(" set mqtt_port [n]"));
myDebug_P(PSTR(" set mqtt_qos [0-3]"));
myDebug_P(PSTR(" set mqtt_keepalive [seconds]"));
myDebug_P(PSTR(" set mqtt_retain [on | off]"));
myDebug_P(PSTR(" set mqtt_nestedjson [on | off]"));
myDebug_P(PSTR(" set ntp_enabled <on | off>"));
myDebug_P(PSTR(" set ntp_interval [minutes]"));
myDebug_P(PSTR(" set ntp_timezone [n]"));
@@ -821,6 +1013,7 @@ void MyESP::_printSetCommands() {
myDebug_P(PSTR(" mqtt_retain=%s"), (_mqtt_retain) ? "on" : "off");
myDebug_P(PSTR(" mqtt_qos=%d"), _mqtt_qos);
myDebug_P(PSTR(" mqtt_heartbeat=%s"), (_mqtt_heartbeat) ? "on" : "off");
myDebug_P(PSTR(" mqtt_nestedjson=%s"), (_mqtt_nestedjson) ? "on" : "off");
#ifdef FORCE_SERIAL
myDebug_P(PSTR(" serial=%s (this is always when compiled with -DFORCE_SERIAL)"), (_general_serial) ? "on" : "off");
@@ -873,9 +1066,10 @@ char * MyESP::_telnet_readWord(bool allow_all_chars) {
// messy code but effective since we don't have too many settings
// wc is word count, number of parameters after the 'set' command
bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value) {
bool save_config = false;
bool save_custom_config = false;
bool restart = false;
bool save_config = false;
bool restart = false;
MYESP_FSACTION_t save_custom_config = MYESP_FSACTION_ERR; // default
// check for our internal commands first
if (strcmp(setting, "erase") == 0) {
@@ -924,6 +1118,8 @@ bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
restart = save_config;
} else if (strcmp(setting, "mqtt_heartbeat") == 0) {
save_config = fs_setSettingValue(&_mqtt_heartbeat, value, false);
} else if (strcmp(setting, "mqtt_nestedjson") == 0) {
save_config = fs_setSettingValue(&_mqtt_nestedjson, value, false);
} else if (strcmp(setting, "ntp_enabled") == 0) {
save_config = fs_setSettingValue(&_ntp_enabled, value, false);
} else if (strcmp(setting, "ntp_interval") == 0) {
@@ -938,13 +1134,14 @@ bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
// finally check for any custom commands
if (_fs_setlist_callback_f) {
save_custom_config = (_fs_setlist_callback_f)(MYESP_FSACTION_SET, wc, setting, value);
restart = (save_custom_config == MYESP_FSACTION_RESTART);
}
}
bool ok = false;
// if we were able to recognize the set command, continue
if ((save_config || save_custom_config)) {
if ((save_config || save_custom_config != MYESP_FSACTION_ERR)) {
// check for 2 params
if (value == nullptr) {
myDebug_P(PSTR("%s has been reset to its default value."), setting);
@@ -960,7 +1157,7 @@ bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
}
// and see if we need to also save for custom config
if (save_custom_config) {
if (save_custom_config != MYESP_FSACTION_ERR) {
ok = _fs_createCustomConfig();
}
@@ -1042,12 +1239,12 @@ void MyESP::_telnetCommand(char * commandLine) {
}
// print mqtt log command
if ((strcmp(ptrToCommandName, "mqttlog") == 0) && (wc == 1)) {
_printMQTTLog();
if (strcmp(ptrToCommandName, "mqttqueue") == 0) {
_printMQTTQueue();
return;
}
// show system stats
// show system status
if ((strcmp(ptrToCommandName, "system") == 0) && (wc == 1)) {
showSystemStats();
return;
@@ -1055,12 +1252,11 @@ void MyESP::_telnetCommand(char * commandLine) {
// save everything
if ((strcmp(ptrToCommandName, "save") == 0) && (wc == 1)) {
_fs_writeConfig();
_fs_createCustomConfig();
saveSettings();
return;
}
// show system stats
// quit
if ((strcmp(ptrToCommandName, "quit") == 0) && (wc == 1)) {
myDebug_P(PSTR("[TELNET] exiting telnet session"));
SerialAndTelnet.disconnectClient();
@@ -1088,6 +1284,12 @@ void MyESP::_telnetCommand(char * commandLine) {
}
}
// public function so clients can save config
void MyESP::saveSettings() {
_fs_writeConfig();
_fs_createCustomConfig();
}
// returns WiFi hostname as a String object
String MyESP::_getESPhostname() {
String hostname;
@@ -1323,13 +1525,13 @@ void MyESP::_systemCheckLoop() {
}
}
// print out ESP system stats
// print out ESP system status
// for battery power is ESP.getVcc()
void MyESP::showSystemStats() {
#if defined(ESP8266)
myDebug_P(PSTR("%sESP8266 System stats:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
myDebug_P(PSTR("%sESP8266 System status:%s"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
#else
myDebug_P(PSTR("ESP32 System stats:"));
myDebug_P(PSTR("ESP32 System status:"));
#endif
myDebug_P(PSTR(""));
@@ -1373,13 +1575,14 @@ void MyESP::showSystemStats() {
if (isMQTTConnected()) {
myDebug_P(PSTR(" [MQTT] is connected (heartbeat %s)"), getHeartbeat() ? "enabled" : "disabled");
myDebug_P(PSTR(" [MQTT] # failed topic publishes: %d"), _mqtt_publish_fails);
} else {
myDebug_P(PSTR(" [MQTT] is disconnected"));
}
if (_have_ntp_time) {
if ((_have_ntp_time) && (NTP.tcr->abbrev != nullptr)) {
uint32_t real_time = getSystemTime();
myDebug_P(PSTR(" [NTP] Local Time is %02d:%02d:%02d %s (%d)"), to_hour(real_time), to_minute(real_time), to_second(real_time), NTP.tcr->abbrev, real_time);
myDebug_P(PSTR(" [NTP] Local Time is %02d:%02d:%02d %s"), to_hour(real_time), to_minute(real_time), to_second(real_time), NTP.tcr->abbrev);
}
#ifdef CRASH
@@ -1450,43 +1653,40 @@ void MyESP::showSystemStats() {
/*
* Send heartbeat via MQTT with all system data
*/
void MyESP::_heartbeatCheck(bool force) {
void MyESP::heartbeatCheck(bool force) {
static uint32_t last_heartbeat = 0;
if ((millis() - last_heartbeat > MYESP_HEARTBEAT_INTERVAL) || force) {
last_heartbeat = millis();
// print to log if force is set, so at bootup
if (force) {
_printHeap("[SYSTEM]");
}
#ifdef MYESP_DEBUG
_printHeap("[HEARTBEAT] ");
_printHeap("[HEARTBEAT]");
#endif
if (!isMQTTConnected() || !(_mqtt_heartbeat)) {
return;
}
// print to log if force is set
if (force) {
_printHeap("[SYSTEM]");
}
uint32_t total_memory = _getInitialFreeHeap();
uint32_t free_memory = ESP.getFreeHeap();
uint8_t mem_available = 100 * free_memory / total_memory; // as a %
StaticJsonDocument<MYESP_JSON_MAXSIZE_SMALL> doc;
JsonObject rootHeartbeat = doc.to<JsonObject>();
const size_t capacity = JSON_OBJECT_SIZE(6);
StaticJsonDocument<capacity> doc;
JsonObject rootHeartbeat = doc.to<JsonObject>();
rootHeartbeat["version"] = _app_version;
rootHeartbeat["IP"] = WiFi.localIP().toString();
rootHeartbeat["rssid"] = getWifiQuality();
rootHeartbeat["load"] = getSystemLoadAverage();
rootHeartbeat["uptime"] = _getUptime();
rootHeartbeat["freemem"] = mem_available;
rootHeartbeat["MQTTdisconnects"] = _getSystemDropoutCounter();
rootHeartbeat["rssid"] = getWifiQuality();
rootHeartbeat["load"] = getSystemLoadAverage();
rootHeartbeat["uptime"] = _getUptime();
rootHeartbeat["freemem"] = mem_available;
rootHeartbeat["tcpdrops"] = _getSystemDropoutCounter();
rootHeartbeat["mqttpublishfails"] = _mqtt_publish_fails;
char data[300] = {0};
serializeJson(doc, data, sizeof(data));
(void)mqttPublish(MQTT_TOPIC_HEARTBEAT, data, false); // send to MQTT with retain off
mqttPublish(MQTT_TOPIC_HEARTBEAT, doc, false); // send to MQTT with retain off
}
}
@@ -1499,13 +1699,13 @@ void MyESP::heartbeatPrint() {
uint32_t total_memory = _getInitialFreeHeap();
uint32_t free_memory = ESP.getFreeHeap();
myDebug("[%d] uptime:%d bytesfree:%d (%2u%%), load:%d, dropouts:%d",
i++,
_getUptime(),
free_memory,
100 * free_memory / total_memory,
getSystemLoadAverage(),
_getSystemDropoutCounter()
myDebug_P(PSTR("[%d] uptime:%d bytesfree:%d (%2u%%), load:%d, dropouts:%d"),
i++,
_getUptime(),
free_memory,
100 * free_memory / total_memory,
getSystemLoadAverage(),
_getSystemDropoutCounter()
);
}
@@ -1738,23 +1938,25 @@ bool MyESP::_fs_loadConfig() {
_general_serial = general["serial"];
#endif
JsonObject mqtt = doc["mqtt"];
_mqtt_enabled = mqtt["enabled"];
_mqtt_heartbeat = mqtt["heartbeat"];
_mqtt_ip = strdup(mqtt["ip"] | "");
_mqtt_user = strdup(mqtt["user"] | "");
_mqtt_port = mqtt["port"] | MQTT_PORT;
_mqtt_keepalive = mqtt["keepalive"] | MQTT_KEEPALIVE;
_mqtt_retain = mqtt["retain"];
_mqtt_qos = mqtt["qos"] | MQTT_QOS;
_mqtt_password = strdup(mqtt["password"] | "");
_mqtt_base = strdup(mqtt["base"] | MQTT_BASE_DEFAULT);
JsonObject mqtt = doc["mqtt"];
_mqtt_enabled = mqtt["enabled"];
_mqtt_heartbeat = mqtt["heartbeat"];
_mqtt_ip = strdup(mqtt["ip"] | "");
_mqtt_user = strdup(mqtt["user"] | "");
_mqtt_port = mqtt["port"] | MQTT_PORT;
_mqtt_keepalive = mqtt["keepalive"] | MQTT_KEEPALIVE;
_mqtt_retain = mqtt["retain"];
_mqtt_qos = mqtt["qos"] | MQTT_QOS;
_mqtt_nestedjson = mqtt["nestedjson"] | true; // default to on
_mqtt_password = strdup(mqtt["password"] | "");
_mqtt_base = strdup(mqtt["base"] | MQTT_BASE_DEFAULT);
JsonObject ntp = doc["ntp"];
_ntp_server = strdup(ntp["server"] | "");
_ntp_interval = ntp["interval"] | 60;
if (_ntp_interval < 2)
if (_ntp_interval < 2) {
_ntp_interval = NTP_INTERVAL_DEFAULT;
}
_ntp_enabled = ntp["enabled"];
_ntp_timezone = ntp["timezone"] | NTP_TIMEZONE_DEFAULT;
@@ -1951,17 +2153,18 @@ bool MyESP::_fs_writeConfig() {
general["log_ip"] = _general_log_ip;
general["version"] = _app_version;
JsonObject mqtt = doc.createNestedObject("mqtt");
mqtt["enabled"] = _mqtt_enabled;
mqtt["heartbeat"] = _mqtt_heartbeat;
mqtt["ip"] = _mqtt_ip;
mqtt["user"] = _mqtt_user;
mqtt["port"] = _mqtt_port;
mqtt["qos"] = _mqtt_qos;
mqtt["keepalive"] = _mqtt_keepalive;
mqtt["retain"] = _mqtt_retain;
mqtt["password"] = _mqtt_password;
mqtt["base"] = _mqtt_base;
JsonObject mqtt = doc.createNestedObject("mqtt");
mqtt["enabled"] = _mqtt_enabled;
mqtt["heartbeat"] = _mqtt_heartbeat;
mqtt["ip"] = _mqtt_ip;
mqtt["user"] = _mqtt_user;
mqtt["port"] = _mqtt_port;
mqtt["qos"] = _mqtt_qos;
mqtt["keepalive"] = _mqtt_keepalive;
mqtt["retain"] = _mqtt_retain;
mqtt["password"] = _mqtt_password;
mqtt["base"] = _mqtt_base;
mqtt["nestedjson"] = _mqtt_nestedjson;
JsonObject ntp = doc.createNestedObject("ntp");
ntp["server"] = _ntp_server;
@@ -2063,6 +2266,11 @@ void MyESP::_calculateLoad() {
}
}
// returns true if nested JSON setting is enabled
bool MyESP::mqttUseNestedJson() {
return _mqtt_nestedjson;
}
// returns true is MQTT is alive
bool MyESP::isMQTTConnected() {
return mqttClient.connected();
@@ -2278,7 +2486,7 @@ void MyESP::writeLogEvent(const uint8_t type, const char * msg) {
// Handles WebSocket Events
void MyESP::_onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t * data, size_t len) {
if (type == WS_EVT_ERROR) {
myDebug("[WEB] WebSocket[%s][%u] error(%u): %s\r\n", server->url(), client->id(), *((uint16_t *)arg), (char *)data);
myDebug_P(PSTR("[WEB] WebSocket[%s][%u] error(%u): %s\r\n"), server->url(), client->id(), *((uint16_t *)arg), (char *)data);
} else if (type == WS_EVT_DATA) {
AwsFrameInfo * info = (AwsFrameInfo *)arg;
uint64_t index = info->index;
@@ -2442,13 +2650,13 @@ void MyESP::_sendStatus() {
uint32_t total_memory = _getInitialFreeHeap();
uint32_t free_memory = ESP.getFreeHeap();
DynamicJsonDocument doc(MQTT_MAX_PAYLOAD_SIZE_LARGE);
DynamicJsonDocument doc(MYESP_JSON_MAXSIZE_MEDIUM);
JsonObject root = doc.to<JsonObject>();
root["command"] = "status";
FSInfo fsinfo;
if (!SPIFFS.info(fsinfo)) {
myDebug("[SYSTEM] Error getting info on SPIFFS");
myDebug_P(PSTR("[SYSTEM] Error getting info on SPIFFS"));
} else {
root["availspiffs"] = (fsinfo.totalBytes - fsinfo.usedBytes) / 1000;
root["spiffssize"] = (fsinfo.totalBytes / 1000);
@@ -2486,31 +2694,7 @@ void MyESP::_sendStatus() {
sprintf(uptime, "%d day%s %d hour%s %d minute%s %d second%s", d, (d == 1) ? "" : "s", h, (h == 1) ? "" : "s", m, (m == 1) ? "" : "s", sec, (sec == 1) ? "" : "s");
root["uptime"] = uptime;
char topic_s[MQTT_MAX_TOPIC_SIZE] = {0};
if (_hasValue(_mqtt_base)) {
strlcpy(topic_s, _mqtt_base, sizeof(topic_s));
strlcat(topic_s, "/", sizeof(topic_s));
strlcat(topic_s, _general_hostname, sizeof(topic_s));
} else {
strlcpy(topic_s, _general_hostname, sizeof(topic_s));
}
strlcat(topic_s, "/", sizeof(topic_s));
root["mqttloghdr"] = topic_s;
// create MQTT log
JsonArray list = root.createNestedArray("mqttlog");
// only send Publish
for (uint8_t i = 0; i < MYESP_MQTTLOG_MAX; i++) {
if ((MQTT_log[i].type == 1) && (MQTT_log[i].topic != nullptr)) {
JsonObject item = list.createNestedObject();
item["topic"] = MQTT_log[i].topic;
item["payload"] = MQTT_log[i].payload;
item["time"] = MQTT_log[i].timestamp;
}
}
char buffer[MQTT_MAX_PAYLOAD_SIZE_LARGE];
char buffer[MYESP_JSON_MAXSIZE_MEDIUM];
size_t len = serializeJson(root, buffer);
_ws->textAll(buffer, len);
@@ -2569,48 +2753,49 @@ void MyESP::_webserver_setup() {
request->send(response);
});
_webServer->on("/update",
HTTP_POST,
[](AsyncWebServerRequest * request) {
AsyncWebServerResponse * response = request->beginResponse(200, "text/plain", _shouldRestart ? "OK" : "FAIL");
response->addHeader("Connection", "close");
request->send(response);
},
[](AsyncWebServerRequest * request, String filename, size_t index, uint8_t * data, size_t len, bool final) {
if (!request->authenticate(MYESP_HTTP_USERNAME, _general_password)) {
return;
}
if (!index) {
ETS_UART_INTR_DISABLE(); // disable all UART interrupts to be safe
//_writeLogEvent(MYESP_SYSLOG_INFO, "Firmware update started");
Update.runAsync(true);
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
_webServer->on(
"/update",
HTTP_POST,
[](AsyncWebServerRequest * request) {
AsyncWebServerResponse * response = request->beginResponse(200, "text/plain", _shouldRestart ? "OK" : "FAIL");
response->addHeader("Connection", "close");
request->send(response);
},
[](AsyncWebServerRequest * request, String filename, size_t index, uint8_t * data, size_t len, bool final) {
if (!request->authenticate(MYESP_HTTP_USERNAME, _general_password)) {
return;
}
if (!index) {
ETS_UART_INTR_DISABLE(); // disable all UART interrupts to be safe
//_writeLogEvent(MYESP_SYSLOG_INFO, "Firmware update started");
Update.runAsync(true);
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
//_writeLogEvent(MYESP_SYSLOG_ERROR, "Not enough space to update");
#ifdef MYESP_DEBUG
Update.printError(Serial);
Update.printError(Serial);
#endif
}
}
if (!Update.hasError()) {
if (Update.write(data, len) != len) {
}
}
if (!Update.hasError()) {
if (Update.write(data, len) != len) {
//_writeLogEvent(MYESP_SYSLOG_ERROR, "Writing to flash failed");
#ifdef MYESP_DEBUG
Update.printError(Serial);
Update.printError(Serial);
#endif
}
}
if (final) {
if (Update.end(true)) {
//_writeLogEvent(MYESP_SYSLOG_INFO, "Firmware update finished");
_shouldRestart = !Update.hasError();
} else {
}
}
if (final) {
if (Update.end(true)) {
//_writeLogEvent(MYESP_SYSLOG_INFO, "Firmware update finished");
_shouldRestart = !Update.hasError();
} else {
//_writeLogEvent(MYESP_SYSLOG_ERROR, "Firmware update failed");
#ifdef MYESP_DEBUG
Update.printError(Serial);
Update.printError(Serial);
#endif
}
}
});
}
}
});
_webServer->on("/fonts/glyphicons-halflings-regular.woff", HTTP_GET, [](AsyncWebServerRequest * request) {
AsyncWebServerResponse * response =
@@ -2667,84 +2852,13 @@ void MyESP::_printHeap(const char * prefix) {
uint32_t total_memory = _getInitialFreeHeap();
uint32_t free_memory = ESP.getFreeHeap();
myDebug("%s Free Heap: %d bytes initially | %d bytes used (%2u%%) | %d bytes free (%2u%%)",
prefix,
total_memory,
total_memory - free_memory,
100 * (total_memory - free_memory) / total_memory,
free_memory,
100 * free_memory / total_memory);
}
// print MQTT log - everything that was published last per topic
void MyESP::_printMQTTLog() {
myDebug_P(PSTR("MQTT publish log:"));
uint8_t i;
for (i = 0; i < MYESP_MQTTLOG_MAX; i++) {
if ((MQTT_log[i].topic != nullptr) && (MQTT_log[i].type == MYESP_MQTTLOGTYPE_PUBLISH)) {
myDebug_P(PSTR(" Timestamp:%02d:%02d:%02d Topic:%s Payload:%s"),
to_hour(MQTT_log[i].timestamp),
to_minute(MQTT_log[i].timestamp),
to_second(MQTT_log[i].timestamp),
MQTT_log[i].topic,
MQTT_log[i].payload);
}
}
myDebug_P(PSTR("")); // newline
myDebug_P(PSTR("MQTT subscriptions:"));
for (i = 0; i < MYESP_MQTTLOG_MAX; i++) {
if ((MQTT_log[i].topic != nullptr) && (MQTT_log[i].type == MYESP_MQTTLOGTYPE_SUBSCRIBE)) {
myDebug_P(PSTR(" Topic:%s"), MQTT_log[i].topic);
}
}
myDebug_P(PSTR("")); // newline
}
// add an MQTT log entry to our buffer
void MyESP::_addMQTTLog(const char * topic, const char * payload, const MYESP_MQTTLOGTYPE_t type) {
static uint8_t logCount = 0;
uint8_t logPointer = 0;
bool found = false;
#ifdef MYESP_DEBUG
myDebug("_addMQTTLog [#%d] %s (%d) [%s] (%d)", logCount, topic, strlen(topic), payload, strlen(payload));
#endif
// find the topic
// topics must be unique for either publish or subscribe
while ((logPointer < MYESP_MQTTLOG_MAX) && (_hasValue(MQTT_log[logPointer].topic))) {
if ((strcmp(MQTT_log[logPointer].topic, topic) == 0) && (MQTT_log[logPointer].type == type)) {
found = true;
break;
}
logPointer++;
}
// if not found add it and increment next free space pointer
if (!found) {
logPointer = logCount;
if (++logCount == MYESP_MQTTLOG_MAX) {
logCount = 0; // rotate
}
}
// delete old record
if (MQTT_log[logPointer].topic) {
free(MQTT_log[logPointer].topic);
}
if (MQTT_log[logPointer].payload) {
free(MQTT_log[logPointer].payload);
}
// and add new record
MQTT_log[logPointer].type = type;
MQTT_log[logPointer].topic = strdup(topic);
MQTT_log[logPointer].payload = strdup(payload);
MQTT_log[logPointer].timestamp = now();
myDebug_P(PSTR("%s Free Heap: %d bytes initially | %d bytes used (%2u%%) | %d bytes free (%2u%%)"),
prefix,
total_memory,
total_memory - free_memory,
100 * (total_memory - free_memory) / total_memory,
free_memory,
100 * free_memory / total_memory);
}
// send UTC time via ws
@@ -2776,9 +2890,11 @@ void MyESP::_bootupSequence() {
// check if its booted
if (boot_status == MYESP_BOOTSTATUS_BOOTED) {
if ((_ntp_enabled) && (now() > 10000) && !_have_ntp_time) {
if ((_ntp_enabled) && (now() > 10000L) && !_have_ntp_time) {
_have_ntp_time = true;
writeLogEvent(MYESP_SYSLOG_INFO, "System booted");
// send start topic
_sendStartTopic();
}
return;
}
@@ -2871,7 +2987,6 @@ void MyESP::begin(const char * app_hostname, const char * app_name, const char *
_webserver_setup(); // init web server
_setSystemCheck(false); // reset system check
_heartbeatCheck(true); // force heartbeat, will send out message to log too
_setSystemDropoutCounter(0); // reset # TCP dropouts
@@ -2884,7 +2999,7 @@ void MyESP::begin(const char * app_hostname, const char * app_name, const char *
void MyESP::loop() {
_calculateLoad();
_systemCheckLoop();
_heartbeatCheck();
heartbeatCheck();
_bootupSequence(); // see if a reset was pressed during bootup
jw.loop(); // WiFi
@@ -2897,6 +3012,14 @@ void MyESP::loop() {
_mqttConnect(); // MQTT
// every second check MQTT queue for publishing
static unsigned long lastMqttPoll = 0;
unsigned long currentMillis = millis();
if ((unsigned long)(currentMillis - lastMqttPoll) >= MQTT_PUBLISH_WAIT) {
_mqttPublishQueue();
lastMqttPoll = currentMillis;
}
// SysLog
uuid::loop();
syslog.loop();
@@ -2907,7 +3030,7 @@ void MyESP::loop() {
}
if (_formatreq) {
myDebug("[SYSTEM] Factory reset initiated. Please wait. System will automatically restart when complete...");
myDebug_P(PSTR("[SYSTEM] Factory reset initiated. Please wait. System will automatically restart when complete..."));
SPIFFS.end();
_ws->enable(false);
SPIFFS.format();
@@ -2917,7 +3040,7 @@ void MyESP::loop() {
if (_shouldRestart) {
writeLogEvent(MYESP_SYSLOG_INFO, "System is restarting");
myDebug("[SYSTEM] Restarting...");
myDebug_P(PSTR("[SYSTEM] Restarting..."));
_deferredReset(500, CUSTOM_RESET_TERMINAL);
ESP.restart();
}

View File

@@ -9,7 +9,7 @@
#ifndef MyESP_h
#define MyESP_h
#define MYESP_VERSION "1.2.22"
#define MYESP_VERSION "1.2.37"
#include <ArduinoJson.h>
#include <ArduinoOTA.h>
@@ -17,6 +17,7 @@
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <JustWifi.h>
#include <deque> // for MQTT publish queue
// SysLog
#include <uuid/common.h>
@@ -85,7 +86,6 @@ extern struct rst_info resetInfo;
#define MQTT_RECONNECT_DELAY_MAX 120000 // Set reconnect time to 2 minutes at most
#define MQTT_TOPIC_START "start"
#define MQTT_TOPIC_HEARTBEAT "heartbeat"
#define MQTT_TOPIC_START_PAYLOAD "start"
#define MQTT_TOPIC_RESTART "restart"
#define MQTT_WILL_ONLINE_PAYLOAD "online" // for last will & testament payload
#define MQTT_WILL_OFFLINE_PAYLOAD "offline" // for last will & testament payload
@@ -95,20 +95,18 @@ extern struct rst_info resetInfo;
#define MQTT_QOS 0 // default qos 0
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name
#define MQTT_MAX_TOPIC_SIZE 50 // max length of MQTT topic
#define MQTT_MAX_PAYLOAD_SIZE 700 // max size of a JSON object. See https://arduinojson.org/v6/assistant/
#define MQTT_MAX_PAYLOAD_SIZE_LARGE 2000 // max size of a large JSON object, like for sending MQTT log
#define MQTT_QUEUE_MAX_SIZE 50 // Size of the MQTT queue
#define MQTT_PUBLISH_WAIT 750 // time in ms before sending MQTT messages
#define MQTT_PUBLISH_MAX_RETRY 4 // max retries for giving up on publishing
#define MYESP_JSON_MAXSIZE_LARGE 2000 // for large Dynamic json files - https://arduinojson.org/v6/assistant/
#define MYESP_JSON_MAXSIZE_MEDIUM 800 // for medium Dynamic json files - https://arduinojson.org/v6/assistant/
#define MYESP_JSON_MAXSIZE_SMALL 200 // for smaller Static json documents - https://arduinojson.org/v6/assistant/
// Internal MQTT events
#define MQTT_CONNECT_EVENT 0
#define MQTT_DISCONNECT_EVENT 1
#define MQTT_MESSAGE_EVENT 2
#define MYESP_JSON_MAXSIZE_LARGE 2000 // for large Dynamic json files
#define MYESP_JSON_MAXSIZE_MEDIUM 800 // for medium Dynamic json files
#define MYESP_JSON_MAXSIZE_SMALL 200 // for smaller Static json documents
#define MYESP_MQTTLOG_MAX 60 // max number of log entries for MQTT publishes and subscribes
#define MYESP_MQTT_PAYLOAD_ON '1' // for MQTT switch on
#define MYESP_MQTT_PAYLOAD_OFF '0' // for MQTT switch off
@@ -210,17 +208,25 @@ struct RtcmemData {
static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
#define MYESP_SYSTEM_CHECK_TIME 60000 // The system is considered stable after these many millis (1 minute)
#define MYESP_SYSTEM_CHECK_MAX 10 // After this many crashes on boot
#define MYESP_HEARTBEAT_INTERVAL 120000 // in milliseconds, how often the MQTT heartbeat is sent (2 mins)
#define MYESP_SYSTEM_CHECK_TIME 60000 // The system is considered stable after these many millis (1 min)
#define MYESP_SYSTEM_CHECK_MAX 10 // After this many crashes on boot
#define MYESP_HEARTBEAT_INTERVAL 60000 // in milliseconds, how often the MQTT heartbeat is sent (1 min)
typedef struct {
bool set; // is it a set command
char key[50];
char description[100];
bool set; // is it a set command?
char key[60];
char description[110];
} command_t;
typedef enum { MYESP_FSACTION_SET, MYESP_FSACTION_LIST, MYESP_FSACTION_SAVE, MYESP_FSACTION_LOAD } MYESP_FSACTION_t;
typedef enum {
MYESP_FSACTION_SET,
MYESP_FSACTION_LIST,
MYESP_FSACTION_SAVE,
MYESP_FSACTION_LOAD,
MYESP_FSACTION_ERR,
MYESP_FSACTION_OK,
MYESP_FSACTION_RESTART
} MYESP_FSACTION_t;
typedef enum {
MYESP_BOOTSTATUS_POWERON = 0,
@@ -229,24 +235,14 @@ typedef enum {
MYESP_BOOTSTATUS_RESETNEEDED = 3
} MYESP_BOOTSTATUS_t; // boot messages
typedef enum { MYESP_MQTTLOGTYPE_NONE, MYESP_MQTTLOGTYPE_PUBLISH, MYESP_MQTTLOGTYPE_SUBSCRIBE } MYESP_MQTTLOGTYPE_t;
// for storing all MQTT publish messages
typedef struct {
uint8_t type; // 0=none, 1=publish, 2=subscribe
char * topic;
char * payload;
time_t timestamp;
} _MQTT_Log_t;
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callback_f;
typedef std::function<void()> wifi_callback_f;
typedef std::function<void()> ota_callback_f;
typedef std::function<void(uint8_t, const char *)> telnetcommand_callback_f;
typedef std::function<void(uint8_t)> telnet_callback_f;
typedef std::function<bool(MYESP_FSACTION_t, JsonObject json)> fs_loadsave_callback_f;
typedef std::function<bool(MYESP_FSACTION_t, uint8_t, const char *, const char *)> fs_setlist_callback_f;
typedef std::function<void(JsonObject root)> web_callback_f;
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callback_f;
typedef std::function<void()> wifi_callback_f;
typedef std::function<void()> ota_callback_f;
typedef std::function<void(uint8_t, const char *)> telnetcommand_callback_f;
typedef std::function<void(uint8_t)> telnet_callback_f;
typedef std::function<bool(MYESP_FSACTION_t, JsonObject json)> fs_loadsave_callback_f;
typedef std::function<MYESP_FSACTION_t(MYESP_FSACTION_t, uint8_t, const char *, const char *)> fs_setlist_callback_f;
typedef std::function<void(JsonObject root)> web_callback_f;
// calculates size of an 2d array at compile time
template <typename T, size_t N>
@@ -285,7 +281,10 @@ class MyESP {
void mqttUnsubscribe(const char * topic);
bool mqttPublish(const char * topic, const char * payload);
bool mqttPublish(const char * topic, const char * payload, bool retain);
bool mqttPublish(const char * topic, JsonDocument & payload);
bool mqttPublish(const char * topic, JsonDocument & payload, bool retain);
void setMQTT(mqtt_callback_f callback);
bool mqttUseNestedJson();
// OTA
void setOTA(ota_callback_f OTACallback_pre, ota_callback_f OTACallback_post);
@@ -302,6 +301,7 @@ class MyESP {
// FS
void setSettings(fs_loadsave_callback_f loadsave, fs_setlist_callback_f setlist, bool useSerial = true);
void saveSettings();
bool fs_saveConfig(JsonObject root);
bool fs_saveCustomConfig(JsonObject root);
bool fs_setSettingValue(char ** setting, const char * value, const char * value_default);
@@ -329,25 +329,25 @@ class MyESP {
uint32_t getSystemLoadAverage();
uint32_t getSystemResetReason();
uint8_t getSystemBootStatus();
bool _have_ntp_time;
unsigned long getSystemTime();
void heartbeatPrint();
void heartbeatCheck(bool force = false);
private:
// mqtt
void _mqttOnMessage(char * topic, char * payload, size_t len);
void _mqttConnect();
void _mqtt_setup();
void _mqttOnConnect();
void _sendStart();
char * _mqttTopic(const char * topic);
// mqtt log
_MQTT_Log_t MQTT_log[MYESP_MQTTLOG_MAX]; // log for publish and subscribe messages
void _printMQTTLog();
void _addMQTTLog(const char * topic, const char * payload, const MYESP_MQTTLOGTYPE_t type);
void _mqttOnMessage(char * topic, char * payload, size_t len);
void _mqttOnPublish(uint16_t packetId);
void _mqttConnect();
void _mqtt_setup();
void _mqttOnConnect();
void _sendStart();
char * _mqttTopic(const char * topic);
bool _mqttQueue(const char * topic, const char * payload, bool retain);
bool _mqttQueue(const char * topic, JsonDocument & payload, bool retain);
void _printMQTTQueue();
void _mqttPublishQueue();
void _mqttRemoveLastPublish();
void _sendStartTopic();
AsyncMqttClient mqttClient; // the MQTT class
uint32_t _mqtt_reconnect_delay;
mqtt_callback_f _mqtt_callback_f;
@@ -366,6 +366,8 @@ class MyESP {
uint32_t _mqtt_last_connection;
bool _mqtt_connecting;
bool _mqtt_heartbeat;
bool _mqtt_nestedjson;
uint16_t _mqtt_publish_fails;
// wifi
void _wifiCallback(justwifi_messages_t code, char * parameter);
@@ -408,20 +410,18 @@ class MyESP {
void _syslog_setup();
// fs and settings
void _fs_setup();
bool _fs_loadConfig();
bool _fs_loadCustomConfig();
void _fs_eraseConfig();
bool _fs_writeConfig();
bool _fs_createCustomConfig();
bool _fs_sendConfig();
size_t _fs_validateConfigFile(const char * filename, size_t maxsize, JsonDocument & doc);
size_t _fs_validateLogFile(const char * filename);
void _fs_setup();
bool _fs_loadConfig();
bool _fs_loadCustomConfig();
void _fs_eraseConfig();
bool _fs_writeConfig();
bool _fs_createCustomConfig();
bool _fs_sendConfig();
size_t _fs_validateConfigFile(const char * filename, size_t maxsize, JsonDocument & doc);
size_t _fs_validateLogFile(const char * filename);
fs_loadsave_callback_f _fs_loadsave_callback_f;
fs_setlist_callback_f _fs_setlist_callback_f;
void _printSetCommands();
void _printSetCommands();
// general
char * _general_hostname;
@@ -444,34 +444,27 @@ class MyESP {
void _kick();
// reset reason and rtcmem
bool _rtcmem_status;
bool _rtcmemStatus();
bool _getRtcmemStatus();
void _rtcmemInit();
void _rtcmemSetup();
void _deferredReset(unsigned long delay, uint8_t reason);
bool _rtcmem_status;
bool _rtcmemStatus();
bool _getRtcmemStatus();
void _rtcmemInit();
void _rtcmemSetup();
void _deferredReset(unsigned long delay, uint8_t reason);
uint8_t _getSystemStabilityCounter();
void _setSystemStabilityCounter(uint8_t counter);
uint8_t _getSystemDropoutCounter();
void _setSystemDropoutCounter(uint8_t counter);
void _increaseSystemDropoutCounter();
void _setSystemResetReason(uint8_t reason);
uint8_t _getCustomResetReason();
void _setCustomResetReason(uint8_t reason);
uint8_t _getSystemResetReason();
void _setSystemBootStatus(uint8_t status);
bool _systemStable;
void _bootupSequence();
bool _getSystemCheck();
void _systemCheckLoop();
void _setSystemCheck(bool stable);
void _setSystemBootStatus(uint8_t status);
bool _systemStable;
void _bootupSequence();
bool _getSystemCheck();
void _systemCheckLoop();
void _setSystemCheck(bool stable);
// load average (0..100) and heap ram
void _calculateLoad();
@@ -479,9 +472,6 @@ class MyESP {
uint32_t _getInitialFreeHeap();
uint32_t _getUsedHeap();
// heartbeat
void _heartbeatCheck(bool force = false);
// web
web_callback_f _web_callback_f;
const char * _http_username;
@@ -500,6 +490,7 @@ class MyESP {
uint16_t _ntp_interval;
bool _ntp_enabled;
uint8_t _ntp_timezone;
bool _have_ntp_time;
};
extern MyESP myESP;

View File

@@ -110,16 +110,16 @@ time_t ICACHE_FLASH_ATTR NtpClient::getNtpTime() {
time_t UnixUTCtime = (highWord << 16 | lowWord) - 2208988800UL;
time_t adjustedtime = (*tz).toLocal(UnixUTCtime, &tcr);
myESP.myDebug("[NTP] Internet time: %02d:%02d:%02d UTC on %d/%d. Local time: %02d:%02d:%02d %s",
to_hour(UnixUTCtime),
to_minute(UnixUTCtime),
to_second(UnixUTCtime),
to_day(UnixUTCtime),
to_month(UnixUTCtime),
to_hour(adjustedtime),
to_minute(adjustedtime),
to_second(adjustedtime),
tcr->abbrev);
myESP.myDebug_P(PSTR("[NTP] Internet time: %02d:%02d:%02d UTC on %d/%d. Local time: %02d:%02d:%02d %s"),
to_hour(UnixUTCtime),
to_minute(UnixUTCtime),
to_second(UnixUTCtime),
to_day(UnixUTCtime),
to_month(UnixUTCtime),
to_hour(adjustedtime),
to_minute(adjustedtime),
to_second(adjustedtime),
tcr->abbrev);
setTime(adjustedtime);
});

View File

@@ -114,7 +114,7 @@
<div class="row form-group">
<label class="col-xs-3">Publish Time<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="How often to send MQTT topics with stats. 0 is automatic. (in seconds)"></i></label>
data-content="How often to send the MQTT topics in seconds. Must be at least 1"></i></label>
<span class="col-xs-9">
<input class="form-control input-sm" placeholder="0" value="" style="display:inline;max-width:185px"
id="publish_time" type="text">
@@ -192,10 +192,8 @@
<td id="b4"></td>
</tr>
<tr>
<th>Boiler Temperature:</th>
<th>DHW Temperature:</th>
<td id="b5"></td>
<th>Return Temperature:</th>
<td id="b6"></td>
</tr>
</table>
</div>

View File

@@ -8,7 +8,7 @@ var custom_config = {
"listen_mode": false,
"shower_timer": false,
"shower_alert": false,
"publish_time": 0,
"publish_time": 10,
"tx_mode": 1
}
};
@@ -99,7 +99,7 @@ function listCustomStats() {
var l = document.createElement("li");
var type = obj[i].type;
var color = "";
if (type === "UBAMaster") {
if (type === "Boiler") {
color = "list-group-item-success";
} else if (type === "Thermostat") {
color = "list-group-item-info";
@@ -108,7 +108,7 @@ function listCustomStats() {
} else if (type === "Heat Pump") {
color = "list-group-item-success";
}
l.innerHTML = obj[i].model + " (Version:" + obj[i].version + " ProductID:" + obj[i].productid + " DeviceID:0x" + obj[i].deviceid + ")";
l.innerHTML = obj[i].model + " (DeviceID: 0x" + obj[i].deviceid + ", ProductID: " + obj[i].productid + ", Version: " + obj[i].version + ")";
l.className = "list-group-item " + color;
list.appendChild(l);
}
@@ -122,7 +122,6 @@ function listCustomStats() {
document.getElementById("b3").innerHTML = ajaxobj.boiler.b3 + " &#8451;";
document.getElementById("b4").innerHTML = ajaxobj.boiler.b4 + " &#8451;";
document.getElementById("b5").innerHTML = ajaxobj.boiler.b5 + " &#8451;";
document.getElementById("b6").innerHTML = ajaxobj.boiler.b6 + " &#8451;";
} else {
document.getElementById("boiler_show").style.display = "none";
}

View File

@@ -18,24 +18,28 @@ DS18::DS18() {
}
DS18::~DS18() {
if (_wire)
if (_wire) {
delete _wire;
}
}
// init
uint8_t DS18::setup(uint8_t gpio, bool parasite) {
uint8_t count;
void DS18::setup(uint8_t gpio, bool parasite) {
_gpio = gpio;
_parasite = (parasite ? 1 : 0);
// OneWire
if (_wire)
if (_wire) {
delete _wire;
}
_wire = new OneWire(_gpio);
}
// Search devices
count = loadDevices();
// clear list and scan for devices
uint8_t DS18::scan() {
_devices.clear();
uint8_t count = loadDevices(); // start the search
// If no devices found check again pulling up the line
if (count == 0) {
@@ -48,30 +52,22 @@ uint8_t DS18::setup(uint8_t gpio, bool parasite) {
return count;
}
// scan every 2 seconds
void DS18::loop() {
static uint32_t last = 0;
if (millis() - last < DS18_READ_INTERVAL)
return;
last = millis();
// Every second we either start a conversion or read the scratchpad
// 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, _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;
_devices[index].data[0] = _devices[index].data[0] + 1; // Force a CRC check error
return;
}
// Read each scratchpad
_wire->select(_devices[index].address);
_wire->write(DS18_CMD_READ_SCRATCHPAD);
@@ -81,8 +77,7 @@ void DS18::loop() {
}
if (_wire->reset() != 1) {
// Force a CRC check error
_devices[index].data[0] = _devices[index].data[0] + 1;
_devices[index].data[0] = _devices[index].data[0] + 1; // Force a CRC check error
return;
}
@@ -94,7 +89,7 @@ void DS18::loop() {
}
// return string of the device, with name and address
char * DS18::getDeviceString(char * buffer, unsigned char index) {
char * DS18::getDeviceType(char * buffer, unsigned char index) {
uint8_t size = 128;
if (index < _count) {
unsigned char chip_id = chip(index);
@@ -109,25 +104,22 @@ char * DS18::getDeviceString(char * buffer, unsigned char index) {
} else {
strlcpy(buffer, "Unknown", size);
}
} else {
strlcpy(buffer, "invalid", size);
}
/*
return buffer;
}
// return string of the device, with name and address
char * DS18::getDeviceID(char * buffer, unsigned char index) {
uint8_t size = 128;
if (index < _count) {
uint8_t * address = _devices[index].address;
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);
char a[30] = {0};
snprintf(a, sizeof(a), "%02X%02X%02X%02X%02X%02X%02X%02X", address[0], address[1], address[2], address[3], address[4], address[5], address[6], address[7]);
strlcat(buffer, a, size);
*/
strlcpy(buffer, a, size);
} else {
strlcpy(buffer, "invalid", size);
}
@@ -153,8 +145,9 @@ char * DS18::getDeviceString(char * buffer, unsigned char index) {
byte 8: SCRATCHPAD_CRC
*/
int16_t DS18::getRawValue(unsigned char index) {
if (index >= _count)
if (index >= _count) {
return 0;
}
uint8_t * data = _devices[index].data;
@@ -170,23 +163,35 @@ int16_t DS18::getRawValue(unsigned char index) {
}
} else {
byte cfg = (data[4] & 0x60);
if (cfg == 0x00)
if (cfg == 0x00) {
raw = raw & ~7; // 9 bit res, 93.75 ms
else if (cfg == 0x20)
} else if (cfg == 0x20) {
raw = raw & ~3; // 10 bit res, 187.5 ms
else if (cfg == 0x40)
} else if (cfg == 0x40) {
raw = raw & ~1; // 11 bit res, 375 ms
// 12 bit res, 750 ms
}
}
return raw;
}
// return real value as a float
// The raw temperature data is in units of sixteenths of a degree, so the value must be divided by 16 in order to convert it to degrees.
// return real value as a float, rounded to 2 decimal places
float DS18::getValue(unsigned char index) {
float value = (float)getRawValue(index) / 16.0;
return value;
int16_t raw_value = getRawValue(index);
// check if valid
if ((raw_value == DS18_CRC_ERROR) || (raw_value == DS18_DISCONNECTED)) {
return (float)DS18_DISCONNECTED;
}
// The raw temperature data is in units of sixteenths of a degree,
// so the value must first be divided by 16 in order to convert it to degrees.
float new_value = (float)(raw_value / 16.0);
// round to 2 decimal places
// https://arduinojson.org/v6/faq/how-to-configure-the-serialization-of-floats/
return (int)(new_value * 100 + 0.5) / 100.0;
}
// check for a supported DS chip version
@@ -196,8 +201,9 @@ bool DS18::validateID(unsigned char id) {
// return the type
unsigned char DS18::chip(unsigned char index) {
if (index < _count)
if (index < _count) {
return _devices[index].address[0];
}
return 0;
}
@@ -206,6 +212,7 @@ 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]) {

View File

@@ -36,9 +36,11 @@ class DS18 {
DS18();
~DS18();
uint8_t setup(uint8_t gpio, bool parasite);
void setup(uint8_t gpio, bool parasite);
uint8_t scan();
void loop();
char * getDeviceString(char * s, unsigned char index);
char * getDeviceType(char * s, unsigned char index);
char * getDeviceID(char * buffer, unsigned char index);
float getValue(unsigned char index);
int16_t getRawValue(unsigned char index); // raw values, needs / 16

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

433
src/ems.h
View File

@@ -12,6 +12,9 @@
#include <Arduino.h>
#include <list> // std::list
// EMS bus IDs
#define EMS_BUSID_DEFAULT 0x0B // Default 0x0B (Service Key)
// EMS tx_mode types
#define EMS_TXMODE_DEFAULT 1 // Default (was previously known as tx_mode 2 in v1.8.x)
#define EMS_TXMODE_EMSPLUS 2 // EMS+
@@ -26,11 +29,12 @@
#define EMS_VALUE_BOOL_ON 0x01 // boolean true
#define EMS_VALUE_BOOL_ON2 0xFF // boolean true, EMS sometimes uses 0xFF for TRUE
#define EMS_VALUE_BOOL_OFF 0x00 // boolean false
#define EMS_VALUE_INT_NOTSET 0xFF // for 8-bit unsigned ints/bytes
#define EMS_VALUE_SHORT_NOTSET -32768 // for 2-byte signed shorts
#define EMS_VALUE_USHORT_NOTSET 0x8000 // for 2-byte unsigned shorts
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs
#define EMS_VALUE_BOOL_NOTSET 0xFE // random number that's not 0, 1 or FF
#define EMS_VALUE_INT_NOTSET 0xFF // for 8-bit unsigned ints/bytes
#define EMS_VALUE_SHORT_NOTSET -32000 // 0x8300 for 2-byte signed shorts
#define EMS_VALUE_USHORT_NOTSET 32000 // 0x7D00 (was 0x8000) for 2-byte unsigned shorts
#define EMS_VALUE_USHORT_NOTVALID 0x8000 // 0x8000 for 2-byte unsigned shorts
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs
// thermostat specific
#define EMS_THERMOSTAT_MAXHC 4 // max number of heating circuits
@@ -38,36 +42,40 @@
#define EMS_THERMOSTAT_WRITE_YES true
#define EMS_THERMOSTAT_WRITE_NO false
// mixing specific
#define EMS_MIXING_MAXHC 4 // max number of heating circuits
#define EMS_MIXING_MAXWWC 2 // max number of warm water circuits
// Device Flags
#define EMS_DEVICE_FLAG_NONE 0 // no flags set
#define EMS_DEVICE_FLAG_SM10 10 // solar module1
#define EMS_DEVICE_FLAG_SM100 11 // solar module2
// group flags specific for thermostats
#define EMS_DEVICE_FLAG_NO_WRITE 0x80 // top bit set if write not supported
#define EMS_DEVICE_FLAG_EASY 1
#define EMS_DEVICE_FLAG_RC10 2
#define EMS_DEVICE_FLAG_RC20 3
#define EMS_DEVICE_FLAG_RC30 4
#define EMS_DEVICE_FLAG_RC35 5
#define EMS_DEVICE_FLAG_RC300 6
#define EMS_DEVICE_FLAG_JUNKERS 7
typedef enum {
EMS_THERMOSTAT_MODE_UNKNOWN,
EMS_THERMOSTAT_MODE_OFF,
EMS_THERMOSTAT_MODE_MANUAL,
EMS_THERMOSTAT_MODE_AUTO,
EMS_THERMOSTAT_MODE_NIGHT,
EMS_THERMOSTAT_MODE_DAY
} _EMS_THERMOSTAT_MODE;
// They are unique to the model type (mixing, solar, thermostat etc)
enum EMS_DEVICE_FLAG_TYPES : uint8_t {
EMS_DEVICE_FLAG_NONE = 0,
EMS_DEVICE_FLAG_MMPLUS = 20, // mixing EMS+
EMS_DEVICE_FLAG_MM10 = 21, // mixing MM10
EMS_DEVICE_FLAG_SM10 = 10,
EMS_DEVICE_FLAG_SM100 = 11, // for SM100 and SM200
EMS_DEVICE_FLAG_EASY = 1,
EMS_DEVICE_FLAG_RC10 = 2,
EMS_DEVICE_FLAG_RC20 = 3,
EMS_DEVICE_FLAG_RC30 = 4,
EMS_DEVICE_FLAG_RC30N = 5, // newer type of RC30 with RC35 circuit
EMS_DEVICE_FLAG_RC35 = 6,
EMS_DEVICE_FLAG_RC100 = 7,
EMS_DEVICE_FLAG_RC300 = 8,
EMS_DEVICE_FLAG_RC20N = 9,
EMS_DEVICE_FLAG_JUNKERS1 = 31, // use 0x65 for HC
EMS_DEVICE_FLAG_JUNKERS2 = 32, // use 0x79 for HC, older models
EMS_DEVICE_FLAG_JUNKERS = (1 << 6), // 6th bit set if its junkers HT3
EMS_DEVICE_FLAG_NO_WRITE = (1 << 7) // top bit set if thermostat write not supported
};
// trigger settings to determine if hot tap water or the heating is active
#define EMS_BOILER_BURNPOWER_TAPWATER 100
#define EMS_BOILER_SELFLOWTEMP_HEATING 30 // was 70, changed to 30 for https://github.com/proddy/EMS-ESP/issues/193
#define EMS_BOILER_SELFLOWTEMP_HEATING 20 // was originally 70, changed to 30 for issue #193, then to 20 with issue #344
// define maximum setable tapwater temperature
// define min & maximum setable tapwater temperatures
#define EMS_BOILER_TAPWATER_TEMPERATURE_MAX 60
#define EMS_BOILER_TAPWATER_TEMPERATURE_MIN 30
#define EMS_TX_TELEGRAM_QUEUE_MAX 50 // max size of Tx FIFO queue. Number of Tx records to send.
@@ -106,55 +114,42 @@ typedef enum {
/* EMS logging */
typedef enum {
EMS_SYS_LOGGING_NONE, // no messages
EMS_SYS_LOGGING_RAW, // raw data mode
EMS_SYS_LOGGING_WATCH, // watch a specific type ID
EMS_SYS_LOGGING_BASIC, // only basic read/write messages
EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat
EMS_SYS_LOGGING_SOLARMODULE, // only telegrams sent from thermostat
EMS_SYS_LOGGING_VERBOSE, // everything
EMS_SYS_LOGGING_JABBER // lots of debug output...
EMS_SYS_LOGGING_NONE, // no messages
EMS_SYS_LOGGING_RAW, // raw data mode
EMS_SYS_LOGGING_WATCH, // watch a specific type ID
EMS_SYS_LOGGING_BASIC, // only basic read/write messages
EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat
EMS_SYS_LOGGING_SOLARMODULE, // only telegrams sent from solar module
EMS_SYS_LOGGING_MIXINGMODULE, // only telegrams sent from mixing module
EMS_SYS_LOGGING_VERBOSE, // everything
EMS_SYS_LOGGING_JABBER, // lots of debug output...
EMS_SYS_LOGGING_DEVICE // watch the device ID
} _EMS_SYS_LOGGING;
// status/counters since last power on
typedef struct {
_EMS_RX_STATUS emsRxStatus;
_EMS_TX_STATUS emsTxStatus;
uint16_t emsRxPgks; // # successfull received
uint16_t emsTxPkgs; // # successfull sent
uint16_t emxCrcErr; // CRC errors
bool emsPollEnabled; // flag enable the response to poll messages
_EMS_SYS_LOGGING emsLogging; // logging
uint16_t emsLogging_typeID; // the typeID to watch
uint8_t emsRefreshedFlags; // fresh data, needs to be pushed out to MQTT
bool emsBusConnected; // is there an active bus
uint32_t emsRxTimestamp; // timestamp of last EMS message received
uint32_t emsPollFrequency; // time between EMS polls
bool emsTxCapable; // able to send via Tx
bool emsTxDisabled; // true to prevent all Tx
uint8_t txRetryCount; // # times the last Tx was re-sent
uint8_t emsIDMask; // Buderus: 0x00, Junkers: 0x80
uint8_t emsPollAck[1]; // acknowledge buffer for Poll
uint8_t emsTxMode; // Tx mode 1, 2 or 3
char emsDeviceMap[EMS_SYS_DEVICEMAP_LENGTH]; // contents of 0x07 telegram with bitmasks for all active EMS devices
uint16_t emsRxPgks; // # successfull received
uint16_t emsTxPkgs; // # successfull sent
uint16_t emxCrcErr; // CRC errors
bool emsPollEnabled; // flag enable the response to poll messages
_EMS_SYS_LOGGING emsLogging; // logging
uint16_t emsLogging_ID; // the type or device ID to watch
uint8_t emsRefreshedFlags; // fresh data, needs to be pushed out to MQTT
bool emsBusConnected; // is there an active bus
uint32_t emsRxTimestamp; // timestamp of last EMS message received
uint32_t emsPollFrequency; // time between EMS polls
bool emsTxCapable; // able to send via Tx
bool emsTxDisabled; // true to prevent all Tx
uint8_t txRetryCount; // # times the last Tx was re-sent
uint8_t emsIDMask; // Buderus: 0x00, Junkers: 0x80
uint8_t emsPollAck[1]; // acknowledge buffer for Poll
uint8_t emsTxMode; // Tx mode 1, 2 or 3
uint8_t emsbusid; // EMS bus ID, default 0x0B for Service Key
uint8_t emsMasterThermostat; // product ID for the default thermostat to use
} _EMS_Sys_Status;
// The Tx send package
typedef struct {
_EMS_TX_TELEGRAM_ACTION action; // read, write, validate, init
uint8_t dest;
uint16_t type;
uint8_t offset;
uint8_t length; // full length of complete telegram, including CRC
uint8_t dataValue; // value to validate against
uint16_t type_validate; // type to call after a successful Write command
uint8_t comparisonValue; // value to compare against during a validate command
uint8_t comparisonOffset; // offset of where the byte is we want to compare too during validation
uint16_t comparisonPostRead; // after a successful write, do a read from this type ID
unsigned long timestamp; // when created
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
} _EMS_TxTelegram;
// The Rx receive package
typedef struct {
unsigned long timestamp; // timestamp from millis()
@@ -170,6 +165,23 @@ typedef struct {
uint8_t emsplus_type; // FF, F7 or F9
} _EMS_RxTelegram;
// The Tx send package
typedef struct {
_EMS_TX_TELEGRAM_ACTION action; // read, write, validate, init
uint8_t dest;
uint16_t type;
uint8_t offset;
uint8_t length; // full length of complete telegram, including CRC
bool emsplus; // true if ems+/ems 2.0
uint8_t dataValue; // value to validate against
uint16_t type_validate; // type to call after a successful Write command
uint8_t comparisonValue; // value to compare against during a validate command
uint8_t comparisonOffset; // offset of where the byte is we want to compare too during validation
uint16_t comparisonPostRead; // after a successful write, do a read from this type ID
unsigned long timestamp; // when created
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
} _EMS_TxTelegram;
// default empty Tx, must match struct
const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
EMS_TX_TELEGRAM_INIT, // action
@@ -177,7 +189,8 @@ const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
EMS_ID_NONE, // type
0, // offset
0, // length
0, // data value
false, // emsplus (no)
0, // dataValue
EMS_ID_NONE, // type_validate
0, // comparisonValue
0, // comparisonOffset
@@ -193,7 +206,8 @@ typedef enum : uint8_t {
EMS_DEVICE_UPDATE_FLAG_THERMOSTAT = (1 << 1),
EMS_DEVICE_UPDATE_FLAG_MIXING = (1 << 2),
EMS_DEVICE_UPDATE_FLAG_SOLAR = (1 << 3),
EMS_DEVICE_UPDATE_FLAG_HEATPUMP = (1 << 4)
EMS_DEVICE_UPDATE_FLAG_HEATPUMP = (1 << 4),
EMS_DEVICE_UPDATE_FLAG_SETTINGS = (1 << 5)
} _EMS_DEVICE_UPDATE_FLAG;
typedef enum : uint8_t {
@@ -205,13 +219,36 @@ typedef enum : uint8_t {
EMS_DEVICE_TYPE_SOLAR,
EMS_DEVICE_TYPE_HEATPUMP,
EMS_DEVICE_TYPE_GATEWAY,
EMS_DEVICE_TYPE_OTHER,
EMS_DEVICE_TYPE_SWITCH,
EMS_DEVICE_TYPE_CONTROLLER,
EMS_DEVICE_TYPE_CONNECT,
EMS_DEVICE_TYPE_UNKNOWN
} _EMS_DEVICE_TYPE;
// to store mapping of device_ids to their string name
typedef struct {
_EMS_DEVICE_TYPE device_type;
char device_type_string[30];
} _EMS_Device_Types;
// mapping for EMS_Devices_Type
const _EMS_Device_Types EMS_Devices_Types[] = {
{EMS_DEVICE_TYPE_UNKNOWN, EMS_MODELTYPE_UNKNOWN_STRING}, // the first, at index 0 is reserved for "unknown"
{EMS_DEVICE_TYPE_NONE, "All"},
{EMS_DEVICE_TYPE_SERVICEKEY, "Me"},
{EMS_DEVICE_TYPE_BOILER, "Boiler"},
{EMS_DEVICE_TYPE_THERMOSTAT, "Thermostat"},
{EMS_DEVICE_TYPE_MIXING, "Mixing Module"},
{EMS_DEVICE_TYPE_SOLAR, "Solar Module"},
{EMS_DEVICE_TYPE_HEATPUMP, "Heat Pump"},
{EMS_DEVICE_TYPE_GATEWAY, "Gateway"},
{EMS_DEVICE_TYPE_SWITCH, "Switching Module"},
{EMS_DEVICE_TYPE_CONTROLLER, "Controller"},
{EMS_DEVICE_TYPE_CONNECT, "Connect Module"}
};
// to store all known EMS devices to date
typedef struct {
uint8_t product_id;
@@ -220,13 +257,6 @@ typedef struct {
uint8_t flags;
} _EMS_Device;
// to store mapping of device_ids to their string name
typedef struct {
uint8_t device_id;
_EMS_DEVICE_TYPE device_type;
char device_type_string[30];
} _EMS_Device_Types;
// for storing all recognised EMS devices
typedef struct {
_EMS_DEVICE_TYPE device_type; // type (see above)
@@ -248,18 +278,25 @@ typedef struct {
uint8_t product_id;
char version[10];
uint8_t brand; // 0=unknown, 1=bosch, 2=junkers, 3=buderus, 4=nefit, 5=sieger, 11=worcester
// UBAParameterWW
uint8_t wWActivated; // Warm Water activated
uint8_t wWSelTemp; // Warm Water selected temperature
uint8_t wWCircPump; // Warm Water circulation pump Available
uint8_t wWDesiredTemp; // Warm Water desired temperature
uint8_t wWComfort; // Warm water comfort or ECO mode
uint8_t wWActivated; // Warm Water activated
uint8_t wWSelTemp; // Warm Water selected temperature
uint8_t wWCircPump; // Warm Water circulation pump Available
uint8_t wWCircPumpMode; // Warm Water circulation pump mode (1 = 1x3min, ..., 6=6x3min, 7 continuous)
uint8_t wWCircPumpType; // Warm Water circulation pump type (0 = charge pump, 0xff = 3-way pump)
uint8_t wWDesinfectTemp; // Warm Water desinfection temperature
uint8_t wWComfort; // Warm water comfort or ECO mode
// UBAMonitorFast
uint8_t selFlowTemp; // Selected flow temperature
uint16_t curFlowTemp; // Current flow temperature
uint16_t wwStorageTemp1; // warm water storage temp 1
uint16_t wwStorageTemp2; // warm water storage temp 2
uint16_t retTemp; // Return temperature
uint8_t burnGas; // Gas on/off
uint8_t wWMode; // warm water mode
uint8_t fanWork; // Fan on/off
uint8_t ignWork; // Ignition on/off
uint8_t heatPmp; // Circulating pump on/off
@@ -275,6 +312,7 @@ typedef struct {
// UBAMonitorSlow
int16_t extTemp; // Outside temperature
uint16_t boilTemp; // Boiler temperature
uint16_t exhaustTemp; // Exhaust temperature
uint8_t pumpMod; // Pump modulation
uint32_t burnStarts; // # burner starts
uint32_t burnWorkMin; // Total burner operating time
@@ -282,11 +320,17 @@ typedef struct {
uint16_t switchTemp; // Switch temperature
// UBAMonitorWWMessage
uint16_t wWCurTmp; // Warm Water current temperature
uint32_t wWStarts; // Warm Water # starts
uint32_t wWWorkM; // Warm Water # minutes
uint8_t wWOneTime; // Warm Water one time function on/off
uint8_t wWCurFlow; // Warm Water current flow in l/min
uint8_t wWSetTmp; // set temp WW (DHW)
uint16_t wWCurTmp; // Warm Water current temperature
uint16_t wWCurTmp2; // Warm Water current temperature storage
uint32_t wWStarts; // Warm Water # starts
uint32_t wWWorkM; // Warm Water # minutes
uint8_t wWOneTime; // Warm Water one time function on/off
uint8_t wWDesinfecting; // Warm Water desinfection on/off
uint8_t wWReadiness; // Warm Water readiness on/off
uint8_t wWRecharging; // Warm Water recharge on/off
uint8_t wWTemperatureOK; // Warm Water temperature ok on/off
uint8_t wWCurFlow; // Warm Water current flow in l/min
// UBATotalUptimeMessage
uint32_t UBAuptime; // Total UBA working hours
@@ -299,7 +343,6 @@ typedef struct {
// calculated values
uint8_t tapwaterActive; // Hot tap water is on/off
uint8_t heatingActive; // Central heating is on/off
} _EMS_Boiler;
/*
@@ -322,34 +365,46 @@ typedef struct {
uint16_t flowTemp;
uint8_t pumpMod;
uint8_t valveStatus;
} _EMS_Mixing_HC;
uint8_t flowSetTemp;
} _EMS_MixingModule_HC;
// Mixer data
// Mixing Module per WWC
typedef struct {
uint8_t device_id;
uint8_t device_flags;
const char * device_desc_p;
uint8_t product_id;
char version[10];
bool detected;
_EMS_Mixing_HC hc[EMS_THERMOSTAT_MAXHC]; // array for the 4 heating circuits
} _EMS_Mixing;
uint8_t wwc; // warm water circuit 1, 2
bool active; // true if there is data for this WWC
uint16_t flowTemp;
uint8_t pumpMod;
uint8_t tempStatus;
} _EMS_MixingModule_WWC;
// Solar Module - SM10/SM100/ISM1
// Mixing data
typedef struct {
uint8_t device_id;
uint8_t device_flags;
const char * device_desc_p;
uint8_t product_id;
char version[10];
_EMS_MixingModule_HC hc[EMS_MIXING_MAXHC]; // array for the 4 heating circuits
_EMS_MixingModule_WWC wwc[EMS_MIXING_MAXWWC]; // array for the 2 ww circuits
} _EMS_MixingModule;
// Solar Module - SM10/SM100/SM200/ISM1
typedef struct {
uint8_t device_id; // the device ID of the Solar Module
uint8_t device_flags; // Solar Module flags
const char * device_desc_p;
uint8_t product_id;
char version[10];
int16_t collectorTemp; // collector temp
int16_t bottomTemp; // bottom temp
int16_t collectorTemp; // collector temp (TS1)
int16_t bottomTemp; // bottom temp (TS2)
int16_t bottomTemp2; // bottom temp cylinder 2 (TS5)
uint8_t pumpModulation; // modulation solar pump
uint8_t pump; // pump active
uint8_t valveStatus; // valve status (VS2)
int16_t setpoint_maxBottomTemp; // setpoint for maximum collector temp
uint16_t EnergyLastHour;
uint16_t EnergyToday;
uint16_t EnergyTotal;
uint32_t EnergyLastHour;
uint32_t EnergyToday;
uint32_t EnergyTotal;
uint32_t pumpWorkMin; // Total solar pump operating time
} _EMS_SolarModule;
@@ -360,7 +415,7 @@ typedef struct {
int16_t setpoint_roomTemp; // current set temp
int16_t curr_roomTemp; // current room temp
uint8_t mode; // 0=low, 1=manual, 2=auto (or night, day on RC35s)
uint8_t day_mode; // 0=night, 1=day
uint8_t mode_type; // 0=night/eco, 1=day/comfort
uint8_t summer_mode;
uint8_t holiday_mode;
uint8_t daytemp;
@@ -372,13 +427,22 @@ typedef struct {
// Thermostat data
typedef struct {
uint8_t device_id; // the device ID of the thermostat
uint8_t device_flags; // thermostat model flags
const char * device_desc_p;
uint8_t product_id;
char version[10];
char datetime[25]; // HH:MM:SS DD/MM/YYYY
bool write_supported;
uint8_t device_id; // the device ID of the thermostat
uint8_t device_flags; // thermostat model flags
const char * device_desc_p;
uint8_t product_id;
char version[10];
char datetime[25]; // HH:MM:SS DD/MM/YYYY
bool write_supported;
// Installation parameters (tested on RC30)
uint8_t ibaMainDisplay; // 00, display on Thermostat: 0 int. temp, 1 int. setpoint, 2 ext. temp., 3 burner temp., 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
uint8_t ibaLanguage; // 01, language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian
uint8_t ibaCalIntTemperature; // 02, offset int. temperature sensor, by * 0.1 Kelvin
int16_t ibaMinExtTemperature; // 05, min ext temp for heating curve, in deg., 0xF6=-10, 0x0 = 0, 0xFF=-1 (actually a int8_t, coded as int16_t to benefit from negative value rendering)
uint8_t ibaBuildingType; // 06, building type: 0 = light, 1 = medium, 2 = heavy
uint8_t ibaClockOffset; // 12, offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s
_EMS_Thermostat_HC hc[EMS_THERMOSTAT_MAXHC]; // array for the 4 heating circuits
} _EMS_Thermostat;
@@ -393,49 +457,93 @@ typedef struct {
EMS_processType_cb processType_cb;
} _EMS_Type;
typedef enum : uint8_t {
EMS_THERMOSTAT_MODE_UNKNOWN = 0,
EMS_THERMOSTAT_MODE_OFF,
EMS_THERMOSTAT_MODE_MANUAL,
EMS_THERMOSTAT_MODE_AUTO,
EMS_THERMOSTAT_MODE_HEAT, // 'heizen'
EMS_THERMOSTAT_MODE_NIGHT,
EMS_THERMOSTAT_MODE_DAY,
EMS_THERMOSTAT_MODE_ECO, // 'sparen'
EMS_THERMOSTAT_MODE_COMFORT,
EMS_THERMOSTAT_MODE_HOLIDAY,
EMS_THERMOSTAT_MODE_NOFROST
} _EMS_THERMOSTAT_MODE;
#define EMS_THERMOSTAT_MODE_UNKNOWN_STR "unknown"
#define EMS_THERMOSTAT_MODE_OFF_STR "off"
#define EMS_THERMOSTAT_MODE_MANUAL_STR "manual"
#define EMS_THERMOSTAT_MODE_AUTO_STR "auto"
#define EMS_THERMOSTAT_MODE_HEAT_STR "heat"
#define EMS_THERMOSTAT_MODE_NIGHT_STR "night"
#define EMS_THERMOSTAT_MODE_DAY_STR "day"
#define EMS_THERMOSTAT_MODE_ECO_STR "eco"
#define EMS_THERMOSTAT_MODE_COMFORT_STR "comfort"
#define EMS_THERMOSTAT_MODE_HOLIDAY_STR "holiday"
#define EMS_THERMOSTAT_MODE_NOFROST_STR "nofrost"
// function definitions
void ems_dumpBuffer(const char * prefix, uint8_t * telegram, uint8_t length);
void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_init();
void ems_doReadCommand(uint16_t type, uint8_t dest);
void ems_sendRawTelegram(char * telegram);
void ems_scanDevices();
void ems_printDevices();
uint8_t ems_printDevices_s(char * buffer, uint16_t len);
void ems_printTxQueue();
void ems_testTelegram(uint8_t test_num);
void ems_startupTelegrams();
bool ems_checkEMSBUSAlive();
void ems_clearDeviceList();
void ems_setThermostatTemp(float temperature, uint8_t hc, uint8_t temptype = 0);
void ems_setThermostatMode(uint8_t mode, uint8_t hc);
void ems_setWarmWaterTemp(uint8_t temperature);
void ems_setFlowTemp(uint8_t temperature);
void ems_setWarmWaterActivated(bool activated);
void ems_setWarmWaterOnetime(bool activated);
void ems_setWarmTapWaterActivated(bool activated);
void ems_setPoll(bool b);
void ems_setLogging(_EMS_SYS_LOGGING loglevel, uint16_t type_id = 0);
void ems_setWarmWaterModeComfort(uint8_t comfort);
void ems_setModels();
void ems_setTxDisabled(bool b);
void ems_setTxMode(uint8_t mode);
char * ems_getDeviceDescription(_EMS_DEVICE_TYPE device_type, char * buffer, bool name_only = false);
bool ems_getDeviceTypeDescription(uint8_t device_id, char * buffer);
void ems_getThermostatValues();
void ems_getBoilerValues();
void ems_getSolarModuleValues();
bool ems_getPoll();
bool ems_getTxEnabled();
bool ems_getThermostatEnabled();
bool ems_getMixingDeviceEnabled();
bool ems_getBoilerEnabled();
bool ems_getSolarModuleEnabled();
bool ems_getHeatPumpEnabled();
void ems_dumpBuffer(const char * prefix, uint8_t * telegram, uint8_t length);
void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_init();
void ems_doReadCommand(uint16_t type, uint8_t dest, uint8_t offset = 0);
void ems_sendRawTelegram(char * telegram);
void ems_printDevices();
uint8_t ems_printDevices_s(char * buffer, uint16_t len);
void ems_printTxQueue();
void ems_testTelegram(uint8_t test_num);
void ems_startupTelegrams();
bool ems_checkEMSBUSAlive();
void ems_setSettingsLanguage(uint8_t lg);
void ems_setSettingsBuilding(uint8_t bg);
void ems_setSettingsDisplay(uint8_t ds);
void ems_setThermostatTemp(float temperature, uint8_t hc, _EMS_THERMOSTAT_MODE temptype);
void ems_setThermostatTemp(float temperature, uint8_t hc, const char * mode_s);
void ems_setThermostatMode(_EMS_THERMOSTAT_MODE mode, uint8_t hc);
void ems_setThermostatMode(const char * mode_s, uint8_t hc);
void ems_setWarmWaterTemp(uint8_t temperature);
void ems_setFlowTemp(uint8_t temperature);
void ems_setWarmWaterActivated(bool activated);
void ems_setWarmWaterOnetime(bool activated);
void ems_setWarmWaterCirculation(bool activated);
void ems_setWarmTapWaterActivated(bool activated);
void ems_setPoll(bool b);
void ems_setLogging(_EMS_SYS_LOGGING loglevel, uint16_t type_id);
void ems_setLogging(_EMS_SYS_LOGGING loglevel, bool quiet = false);
void ems_setWarmWaterModeComfort(uint8_t comfort);
void ems_setModels();
void ems_setTxDisabled(bool b);
void ems_setTxMode(uint8_t mode);
void ems_setEMSbusid(uint8_t id);
void ems_setMasterThermostat(uint8_t product_id);
char * ems_getDeviceDescription(_EMS_DEVICE_TYPE device_type, char * buffer, bool name_only = false);
bool ems_getDeviceTypeDescription(uint8_t device_id, char * buffer);
char * ems_getDeviceTypeName(_EMS_DEVICE_TYPE device_type, char * buffer);
_EMS_THERMOSTAT_MODE ems_getThermostatMode(const char * mode_s);
void ems_getThermostatValues();
void ems_getBoilerValues();
void ems_getSettingsValues();
void ems_getSolarModuleValues();
void ems_getMixingModuleValues();
char * ems_getThermostatModeString(_EMS_THERMOSTAT_MODE mode, char * mode_str);
bool ems_getPoll();
bool ems_getTxEnabled();
bool ems_getThermostatEnabled();
bool ems_getMixingModuleEnabled();
bool ems_getBoilerEnabled();
bool ems_getSolarModuleEnabled();
bool ems_getHeatPumpEnabled();
bool ems_getBusConnected();
_EMS_SYS_LOGGING ems_getLogging();
uint8_t ems_getThermostatModel();
uint8_t ems_getSolarModuleModel();
uint8_t ems_getThermostatFlags();
uint8_t ems_getSolarModuleFlags();
void ems_discoverModels();
bool ems_getTxCapable();
uint32_t ems_getPollFrequency();
@@ -443,6 +551,8 @@ bool ems_getTxDisabled();
void ems_Device_add_flags(unsigned int flags);
bool ems_Device_has_flags(unsigned int flags);
void ems_Device_remove_flags(unsigned int flags);
bool ems_isHT3();
void ems_scanDevices();
// private functions
uint8_t _crcCalculator(uint8_t * data, uint8_t len);
@@ -450,14 +560,15 @@ void _processType(_EMS_RxTelegram * EMS_RxTelegram);
void _debugPrintPackage(const char * prefix, _EMS_RxTelegram * EMS_RxTelegram, const char * color);
void _ems_clearTxData();
void _removeTxQueue();
uint8_t _getHeatingCircuit(_EMS_RxTelegram * EMS_RxTelegram);
int8_t _getHeatingCircuit(_EMS_RxTelegram * EMS_RxTelegram);
void _printMessage(_EMS_RxTelegram * EMS_RxTelegram, const int show_type = -1);
// global so can referenced in other classes
extern _EMS_Sys_Status EMS_Sys_Status;
extern _EMS_Boiler EMS_Boiler;
extern _EMS_Thermostat EMS_Thermostat;
extern _EMS_SolarModule EMS_SolarModule;
extern _EMS_HeatPump EMS_HeatPump;
extern _EMS_Mixing EMS_Mixing;
extern _EMS_Sys_Status EMS_Sys_Status;
extern _EMS_Boiler EMS_Boiler;
extern _EMS_Thermostat EMS_Thermostat;
extern _EMS_SolarModule EMS_SolarModule;
extern _EMS_HeatPump EMS_HeatPump;
extern _EMS_MixingModule EMS_MixingModule;
extern std::list<_Detected_Device> Devices;

View File

@@ -12,41 +12,7 @@
#include "ems.h"
// Fixed EMS Device IDs
#define EMS_ID_ME 0x0B // our device, hardcoded as the "Service Key"
#define EMS_ID_BOILER 0x08 // all UBA Boilers have 0x08
#define EMS_ID_SM 0x30 // Solar Module SM10, SM100 and ISM1
#define EMS_ID_HP 0x38 // HeatPump
#define EMS_ID_GATEWAY 0x48 // Gateway e.g. KM200 Web Gateway
#define EMS_ID_MIXING1 0x20 // Mixing
#define EMS_ID_MIXING2 0x21 // Mixing
#define EMS_ID_SWITCH 0x11 // Switch
#define EMS_ID_CONTROLLER 0x09 // Controller
#define EMS_ID_CONNECT1 0x02 // Connect
#define EMS_ID_CONNECT2 0x50 // Connect
#define EMS_ID_THERMOSTAT1 0x10 // Thermostat
#define EMS_ID_THERMOSTAT2 0x17 // Thermostat
#define EMS_ID_THERMOSTAT3 0x18 // Thermostat
// mapping for EMS_Devices_Type
const _EMS_Device_Types EMS_Devices_Types[] = {
{EMS_ID_BOILER, EMS_DEVICE_TYPE_BOILER, "UBAMaster"},
{EMS_ID_THERMOSTAT1, EMS_DEVICE_TYPE_THERMOSTAT, "Thermostat"},
{EMS_ID_THERMOSTAT2, EMS_DEVICE_TYPE_THERMOSTAT, "Thermostat"},
{EMS_ID_THERMOSTAT3, EMS_DEVICE_TYPE_THERMOSTAT, "Thermostat"},
{EMS_ID_SM, EMS_DEVICE_TYPE_SOLAR, "Solar Module"},
{EMS_ID_HP, EMS_DEVICE_TYPE_HEATPUMP, "Heat Pump"},
{EMS_ID_GATEWAY, EMS_DEVICE_TYPE_GATEWAY, "Gateway"},
{EMS_ID_ME, EMS_DEVICE_TYPE_SERVICEKEY, "Me"},
{EMS_ID_NONE, EMS_DEVICE_TYPE_NONE, "All"},
{EMS_ID_MIXING1, EMS_DEVICE_TYPE_MIXING, "Mixing Module"},
{EMS_ID_MIXING2, EMS_DEVICE_TYPE_MIXING, "Mixing Module"},
{EMS_ID_SWITCH, EMS_DEVICE_TYPE_SWITCH, "Switching Module"},
{EMS_ID_CONTROLLER, EMS_DEVICE_TYPE_CONTROLLER, "Controller"},
{EMS_ID_CONNECT1, EMS_DEVICE_TYPE_CONNECT, "Connect"},
{EMS_ID_CONNECT2, EMS_DEVICE_TYPE_CONNECT, "Connect"}
};
#define EMS_ID_BOILER 0x08 // all UBA Boilers have 0x08
/*
* Common Type
@@ -61,6 +27,7 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
#define EMS_TYPE_UBAMonitorSlow 0x19 // is an automatic monitor broadcast
#define EMS_TYPE_UBAMonitorWWMessage 0x34 // is an automatic monitor broadcast
#define EMS_TYPE_UBAMaintenanceStatusMessage 0x1C // is an automatic monitor broadcast
#define EMS_TYPE_MC10Status 0x2A
#define EMS_TYPE_UBAParameterWW 0x33
#define EMS_TYPE_UBATotalUptimeMessage 0x14
#define EMS_TYPE_UBAFlags 0x35
@@ -77,6 +44,7 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
#define EMS_OFFSET_UBAParameterWW_wwtemp 2 // WW Temperature
#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated
#define EMS_OFFSET_UBAParameterWW_wwOneTime 0x00 // WW OneTime loading
#define EMS_OFFSET_UBAParameterWW_wwCirulation 1 // WW circulation
#define EMS_OFFSET_UBAParameterWW_wwComfort 9 // WW is in comfort or eco mode
#define EMS_VALUE_UBAParameterWW_wwComfort_Hot 0x00 // the value for hot
#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco
@@ -84,19 +52,73 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
#define EMS_OFFSET_UBASetPoints_flowtemp 0 // flow temp
// SM and HP Types
// Installation settings
#define EMS_TYPE_IBASettingsMessage 0xA5 // installation settings
#define EMS_OFFSET_IBASettings_Display 0 // display
#define EMS_OFFSET_IBASettings_Language 1 // language
#define EMS_OFFSET_IBASettings_MinExtTemp 5 // min. ext. temperature
#define EMS_OFFSET_IBASettings_Building 6 // building
#define EMS_OFFSET_IBASettings_CalIntTemp 2 // cal. int. temperature
#define EMS_OFFSET_IBASettings_ClockOffset 12 // clock offset
#define EMS_VALUE_IBASettings_LANG_GERMAN 0
#define EMS_VALUE_IBASettings_LANG_DUTCH 1
#define EMS_VALUE_IBASettings_LANG_FRENCH 2
#define EMS_VALUE_IBASettings_LANG_ITALIAN 3
#define EMS_VALUE_IBASettings_BUILDING_LIGHT 0
#define EMS_VALUE_IBASettings_BUILDING_MEDIUM 1
#define EMS_VALUE_IBASettings_BUILDING_HEAVY 2
#define EMS_VALUE_IBASettings_DISPLAY_INTTEMP 0
#define EMS_VALUE_IBASettings_DISPLAY_INTSETPOINT 1
#define EMS_VALUE_IBASettings_DISPLAY_EXTTEMP 2
#define EMS_VALUE_IBASettings_DISPLAY_BURNERTEMP 3
#define EMS_VALUE_IBASettings_DISPLAY_WWTEMP 4
#define EMS_VALUE_IBASettings_DISPLAY_FUNCMODE 5
#define EMS_VALUE_IBASettings_DISPLAY_TIME 6
#define EMS_VALUE_IBASettings_DISPLAY_DATE 7
#define EMS_VALUE_IBASettings_DISPLAY_SMOKETEMP 9
// Mixing Modules
// MM100/MM200 (EMS Plus)
#define EMS_TYPE_MMPLUSStatusMessage_HC1 0x01D7 // mixing status HC1
#define EMS_TYPE_MMPLUSStatusMessage_HC2 0x01D8 // mixing status HC2
#define EMS_TYPE_MMPLUSStatusMessage_HC3 0x01D9 // mixing status HC3
#define EMS_TYPE_MMPLUSStatusMessage_HC4 0x01DA // mixing status HC4
#define EMS_TYPE_MMPLUSStatusMessage_WWC1 0x0231 // mixing status WWC1
#define EMS_TYPE_MMPLUSStatusMessage_WWC2 0x0232 // mixing status WWC2
#define EMS_OFFSET_MMPLUSStatusMessage_flow_temp 3 // flow temperature
#define EMS_OFFSET_MMPLUSStatusMessage_pump_mod 5 // pump modulation
#define EMS_OFFSET_MMPLUSStatusMessage_valve_status 2 // valve in percent
#define EMS_OFFSET_MMPLUSStatusMessage_WW_flow_temp 0 // flow temperature
#define EMS_OFFSET_MMPLUSStatusMessage_WW_pump_mod 2 // pump on 6, off 0
#define EMS_OFFSET_MMPLUSStatusMessage_WW_temp_status 11 // 0,1,2
// MM10
#define EMS_TYPE_MMStatusMessage 0xAB // mixing status
#define EMS_OFFSET_MMStatusMessage_flow_set 0 // flow setpoint
#define EMS_OFFSET_MMStatusMessage_flow_temp 1 // flow temperature
#define EMS_OFFSET_MMStatusMessage_pump_mod 3 // pump modulation in percent
#define EMS_OFFSET_MMStatusMessage_valve_status 4 // valve 0..255
#define EMS_TYPE_MM10ParameterMessage 0xAC // mixing parameters
// Solar Module
// Assuming here that the SM200 behaves like SM100
#define EMS_TYPE_SM10Monitor 0x97 // SM10Monitor
#define EMS_TYPE_SM100Monitor 0x0262 // SM100Monitor
#define EMS_TYPE_SM100Status 0x0264 // SM100Status
#define EMS_TYPE_SM100Status2 0x026A // SM100Status2
#define EMS_TYPE_SM100Energy 0x028E // SM100Energy
#define EMS_TYPE_HPMonitor1 0xE3 // HeatPump Monitor 1
#define EMS_TYPE_HPMonitor2 0xE5 // HeatPump Monitor 2
// ISPM solar module
#define EMS_TYPE_ISM1StatusMessage 0x0003 // Solar Module Junkers ISM1 Status
#define EMS_TYPE_ISM1Set 0x0001 // for setting values of the solar module like max boiler temp
#define EMS_OFFSET_ISM1Set_MaxBoilerTemp 6 // position of max boiler temp e.g. 50 in the following example: 90 30 FF 06 00 01 50 (CRC=2C)
// Heat Pump
#define EMS_TYPE_HPMonitor1 0xE3 // HeatPump Monitor 1
#define EMS_TYPE_HPMonitor2 0xE5 // HeatPump Monitor 2
/*
* Thermostat Types
*/
@@ -118,6 +140,16 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
#define EMS_OFFSET_RC20StatusMessage_setpoint 1 // setpoint temp
#define EMS_OFFSET_RC20StatusMessage_curr 2 // current temp
#define EMS_TYPE_RC20NStatusMessage 0xAE
#define EMS_OFFSET_RC20NStatusMessage_setpoint 2 // setpoint temp in AE
#define EMS_OFFSET_RC20NStatusMessage_curr 3 // current temp in AE
#define EMS_TYPE_RC20NSet 0xAD
#define EMS_OFFSET_RC20NSet_temp_day 2 // position of thermostat setpoint temperature for day time
#define EMS_OFFSET_RC20NSet_temp_night 1 // position of thermostat setpoint temperature for night time
#define EMS_OFFSET_RC20NSet_mode 3 // position mode
#define EMS_OFFSET_RC20NSet_heatingtype 0
#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
@@ -142,7 +174,6 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
#define EMS_OFFSET_RC35StatusMessage_mode 1 // day mode, also summer on RC3's
#define EMS_OFFSET_RC35StatusMessage_mode1 0 // for holiday mode
#define EMS_TYPE_RC35Set_HC1 0x3D // for setting values like temp and mode (Working mode HC1)
#define EMS_TYPE_RC35Set_HC2 0x47 // for setting values like temp and mode (Working mode HC2)
#define EMS_TYPE_RC35Set_HC3 0x51 // for setting values like temp and mode (Working mode HC3)
@@ -151,9 +182,10 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
#define EMS_OFFSET_RC35Set_mode 7 // 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
#define EMS_OFFSET_RC35Set_temp_holiday 3 // temp during holiday 0x47
#define EMS_OFFSET_RC35Set_heatingtype 0 // floor heating = 3 0x47
#define EMS_OFFSET_RC35Set_circuitcalctemp 14 // calculated circuit temperature 0x48
#define EMS_OFFSET_RC35Set_temp_holiday 3 // temp during holiday mode
#define EMS_OFFSET_RC35Set_heatingtype 0 // e.g. floor heating = 3
#define EMS_OFFSET_RC35Set_circuitcalctemp 14 // calculated circuit temperature
#define EMS_OFFSET_RC35Set_seltemp 37 // selected temp
// Easy specific
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
@@ -180,21 +212,38 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
#define EMS_OFFSET_RCPLUSSet_temp_setpoint 8 // temp setpoint, when changing of templevel (in auto) value is reset and set to FF
#define EMS_OFFSET_RCPLUSSet_manual_setpoint 10 // manual setpoint
// Junkers FR10, FR50, FW100 (EMS Plus)
#define EMS_TYPE_JunkersStatusMessage 0x6F // is an automatic thermostat broadcast giving us temps
// Junkers FR10, FR50, FW100, FW120 (EMS Plus)
// HC1 = 0x6F-0x72
#define EMS_TYPE_JunkersStatusMessage_HC1 0x6F
#define EMS_TYPE_JunkersStatusMessage_HC2 0x70
#define EMS_TYPE_JunkersStatusMessage_HC3 0x71
#define EMS_TYPE_JunkersStatusMessage_HC4 0x72
#define EMS_OFFSET_JunkersStatusMessage_daymode 0 // 3 = day, 2 = night
#define EMS_OFFSET_JunkersStatusMessage_mode 1 // current mode, 1 = manual, 2 = auto
#define EMS_OFFSET_JunkersStatusMessage_setpoint 2 // setpoint temp
#define EMS_OFFSET_JunkersStatusMessage_curr 4 // current temp
// MM100 (EMS Plus)
#define EMS_TYPE_MMPLUSStatusMessage_HC1 0x01D7 // mixer status HC1
#define EMS_TYPE_MMPLUSStatusMessage_HC2 0x01D8 // mixer status HC2
#define EMS_TYPE_MMPLUSStatusMessage_HC3 0x01D9 // mixer status HC3
#define EMS_TYPE_MMPLUSStatusMessage_HC4 0x01DA // mixer status HC4
#define EMS_OFFSET_MMPLUSStatusMessage_flow_temp 3 // flow temperature
#define EMS_OFFSET_MMPLUSStatusMessage_pump_mod 5 // pump modulation
#define EMS_OFFSET_MMPLUSStatusMessage_valve_status 2 // valve in percent
// HC1-4 0x65-0x68 - EMS_DEVICE_FLAG_JUNKERS1
// Junkers FR10, FR50, FW100, FW120
#define EMS_TYPE_JunkersSetMessage1_HC1 0x65
#define EMS_TYPE_JunkersSetMessage1_HC2 0x66
#define EMS_TYPE_JunkersSetMessage1_HC3 0x67
#define EMS_TYPE_JunkersSetMessage1_HC4 0x68
#define EMS_OFFSET_JunkersSetMessage_day_temp 0x11 // EMS offset to set temperature on thermostat for day mode
#define EMS_OFFSET_JunkersSetMessage_night_temp 0x10 // EMS offset to set temperature on thermostat for night mode
#define EMS_OFFSET_JunkersSetMessage_no_frost_temp 0x0F // EMS offset to set temperature on thermostat for no frost mode
#define EMS_OFFSET_JunkersSetMessage_set_mode 0x0E // EMS offset to set mode on thermostat
// HC1-4 0x79-0x7C - EMS_DEVICE_FLAG_JUNKERS2
// Junkers FR100
#define EMS_TYPE_JunkersSetMessage2_HC1 0x79
#define EMS_TYPE_JunkersSetMessage2_HC2 0x7A
#define EMS_TYPE_JunkersSetMessage2_HC3 0x7B
#define EMS_TYPE_JunkersSetMessage2_HC4 0x7C
#define EMS_OFFSET_JunkersSetMessage2_no_frost_temp 0x05
#define EMS_OFFSET_JunkersSetMessage2_eco_temp 0x06
#define EMS_OFFSET_JunkersSetMessage3_heat 0x07
/*
* Table of all known EMS Devices
@@ -203,7 +252,7 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
static const _EMS_Device EMS_Devices[] = {
//
// UBA Masters - typically with device_id of 0x08
// UBA Masters - must have device_id of 0x08
//
{72, EMS_DEVICE_TYPE_BOILER, "MC10 Module", EMS_DEVICE_FLAG_NONE},
{123, EMS_DEVICE_TYPE_BOILER, "Buderus GBx72/Nefit Trendline/Junkers Cerapur/Worcester Greenstar Si/27i", EMS_DEVICE_FLAG_NONE},
@@ -213,7 +262,8 @@ static const _EMS_Device EMS_Devices[] = {
{208, EMS_DEVICE_TYPE_BOILER, "Buderus Logamax plus/GB192/Bosch Condens GC9000", EMS_DEVICE_FLAG_NONE},
{64, EMS_DEVICE_TYPE_BOILER, "Sieger BK13,BK15/Nefit Smartline/Buderus GB1x2", EMS_DEVICE_FLAG_NONE},
{234, EMS_DEVICE_TYPE_BOILER, "Buderus Logamax Plus GB122", EMS_DEVICE_FLAG_NONE},
{95, EMS_DEVICE_TYPE_BOILER, "Bosch Condens 2500/Buderus Logamax GB062/Junkers Cerapur Top/Worcester Greenstar i/Generic HT3", EMS_DEVICE_FLAG_NONE},
{84, EMS_DEVICE_TYPE_BOILER, "Buderus Logamax Plus GB022", EMS_DEVICE_FLAG_NONE},
{95, EMS_DEVICE_TYPE_BOILER, "Bosch Condens 2500/Buderus Logamax,Logomatic/Junkers Cerapur Top/Worcester Greenstar i/Generic HT3", EMS_DEVICE_FLAG_NONE},
{122, EMS_DEVICE_TYPE_BOILER, "Nefit Proline", EMS_DEVICE_FLAG_NONE},
{170, EMS_DEVICE_TYPE_BOILER, "Buderus Logano GB212", EMS_DEVICE_FLAG_NONE},
{172, EMS_DEVICE_TYPE_BOILER, "Nefit Enviline", EMS_DEVICE_FLAG_NONE},
@@ -223,19 +273,17 @@ static const _EMS_Device EMS_Devices[] = {
//
{73, EMS_DEVICE_TYPE_SOLAR, "SM10 Solar Module", EMS_DEVICE_FLAG_SM10},
{163, EMS_DEVICE_TYPE_SOLAR, "SM100 Solar Module", EMS_DEVICE_FLAG_SM100},
{164, EMS_DEVICE_TYPE_SOLAR, "SM200 Solar Module", EMS_DEVICE_FLAG_SM100},
{101, EMS_DEVICE_TYPE_SOLAR, "Junkers ISM1 Solar Module", EMS_DEVICE_FLAG_SM100},
{162, EMS_DEVICE_TYPE_SOLAR, "SM50 Solar Module", EMS_DEVICE_FLAG_SM100},
//
// Mixing Devices - type 0x20 or 0x21
//
{160, EMS_DEVICE_TYPE_MIXING, "MM100 Mixing Module", EMS_DEVICE_FLAG_NONE},
{161, EMS_DEVICE_TYPE_MIXING, "MM200 Mixing Module", EMS_DEVICE_FLAG_NONE},
{69, EMS_DEVICE_TYPE_MIXING, "MM10 Mixer Module", EMS_DEVICE_FLAG_NONE},
{159, EMS_DEVICE_TYPE_MIXING, "MM50 Mixing Module", EMS_DEVICE_FLAG_NONE},
{79, EMS_DEVICE_TYPE_MIXING, "MM100 Mixer Module", EMS_DEVICE_FLAG_NONE},
{80, EMS_DEVICE_TYPE_MIXING, "MM200 Mixer Module", EMS_DEVICE_FLAG_NONE},
{78, EMS_DEVICE_TYPE_MIXING, "MM400 Mixer Module", EMS_DEVICE_FLAG_NONE},
{160, EMS_DEVICE_TYPE_MIXING, "MM100 Mixing Module", EMS_DEVICE_FLAG_MMPLUS},
{161, EMS_DEVICE_TYPE_MIXING, "MM200 Mixing Module", EMS_DEVICE_FLAG_MMPLUS},
{69, EMS_DEVICE_TYPE_MIXING, "MM10 Mixing Module", EMS_DEVICE_FLAG_MM10},
{159, EMS_DEVICE_TYPE_MIXING, "MM50 Mixing Module", EMS_DEVICE_FLAG_MMPLUS},
//
// HeatPump - type 0x38
@@ -244,25 +292,29 @@ static const _EMS_Device EMS_Devices[] = {
{200, EMS_DEVICE_TYPE_HEATPUMP, "HeatPump Module", EMS_DEVICE_FLAG_NONE},
//
// Other devices, like 0x11 for Switching, 0x09 for controllers, 0x02 for Connect, 0x48 for Gateway
// Other devices like controllers and modems
// such as 0x11 for Switching, 0x09 for controllers, 0x02 for Connect, 0x48 for Gateway
//
{71, EMS_DEVICE_TYPE_SWITCH, "WM10 Switch Module", EMS_DEVICE_FLAG_NONE}, // 0x11
{68, EMS_DEVICE_TYPE_CONTROLLER, "BC10/RFM20 Receiver", EMS_DEVICE_FLAG_NONE}, // 0x09
{218, EMS_DEVICE_TYPE_CONTROLLER, "Junkers M200/Buderus RFM200 Receiver", EMS_DEVICE_FLAG_NONE}, // 0x50
{190, EMS_DEVICE_TYPE_CONTROLLER, "BC10 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{114, EMS_DEVICE_TYPE_CONTROLLER, "BC10 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{125, EMS_DEVICE_TYPE_CONTROLLER, "BC25 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{169, EMS_DEVICE_TYPE_CONTROLLER, "BC40 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{152, EMS_DEVICE_TYPE_CONTROLLER, "Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{95, EMS_DEVICE_TYPE_CONTROLLER, "HT3 Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{230, EMS_DEVICE_TYPE_CONTROLLER, "BC Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{205, EMS_DEVICE_TYPE_CONNECT, "Nefit Moduline Easy Connect", EMS_DEVICE_FLAG_NONE}, // 0x02
{206, EMS_DEVICE_TYPE_CONNECT, "Bosch Easy Connect", EMS_DEVICE_FLAG_NONE}, // 0x02
{171, EMS_DEVICE_TYPE_CONNECT, "EMS-OT OpenTherm converter", EMS_DEVICE_FLAG_NONE}, // 0x02
{189, EMS_DEVICE_TYPE_GATEWAY, "Web Gateway KM200", EMS_DEVICE_FLAG_NONE}, // 0x48
{71, EMS_DEVICE_TYPE_SWITCH, "WM10 Switch Module", EMS_DEVICE_FLAG_NONE}, // 0x11
{68, EMS_DEVICE_TYPE_CONTROLLER, "BC10/RFM20 Receiver", EMS_DEVICE_FLAG_NONE}, // 0x09
{218, EMS_DEVICE_TYPE_CONTROLLER, "Junkers M200/Buderus RFM200 Receiver", EMS_DEVICE_FLAG_NONE}, // 0x50
{190, EMS_DEVICE_TYPE_CONTROLLER, "BC10 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{114, EMS_DEVICE_TYPE_CONTROLLER, "BC10 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{125, EMS_DEVICE_TYPE_CONTROLLER, "BC25 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{169, EMS_DEVICE_TYPE_CONTROLLER, "BC40 Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{152, EMS_DEVICE_TYPE_CONTROLLER, "Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{95, EMS_DEVICE_TYPE_CONTROLLER, "HT3 Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{209, EMS_DEVICE_TYPE_CONTROLLER, "W-B ErP Boiler Control Panel", EMS_DEVICE_FLAG_NONE}, // 0x09
{230, EMS_DEVICE_TYPE_CONTROLLER, "BC Base Controller", EMS_DEVICE_FLAG_NONE}, // 0x09
{205, EMS_DEVICE_TYPE_CONNECT, "Nefit Moduline Easy Connect", EMS_DEVICE_FLAG_NONE}, // 0x02
{206, EMS_DEVICE_TYPE_CONNECT, "Bosch Easy Connect", EMS_DEVICE_FLAG_NONE}, // 0x02
{171, EMS_DEVICE_TYPE_CONNECT, "EMS-OT OpenTherm converter", EMS_DEVICE_FLAG_NONE}, // 0x02
{189, EMS_DEVICE_TYPE_GATEWAY, "Web Gateway KM200", EMS_DEVICE_FLAG_NONE}, // 0x48
{94, EMS_DEVICE_TYPE_GATEWAY, "RC Remote Device", EMS_DEVICE_FLAG_NONE}, // 0x18
{207, EMS_DEVICE_TYPE_CONTROLLER, "Worcester Sense II/Bosch CS200 Solar Controller", EMS_DEVICE_FLAG_NONE}, // 0x10
//
// Thermostats, typically device id of 0x10, 0x17 and 0x18
// Thermostats, typically device id of 0x10, 0x17, 0x18, 0x38 (RC100), 0x39 (Easy)
//
// Easy devices - not currently supporting write operations
@@ -270,28 +322,30 @@ static const _EMS_Device EMS_Devices[] = {
{203, EMS_DEVICE_TYPE_THERMOSTAT, "Bosch EasyControl CT200", EMS_DEVICE_FLAG_EASY | EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{157, EMS_DEVICE_TYPE_THERMOSTAT, "Buderus RC200/Bosch CW100/Junkers CW100", EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Buderus/Nefit
// Buderus/Nefit specific
{79, EMS_DEVICE_TYPE_THERMOSTAT, "RC10/Moduline 100", EMS_DEVICE_FLAG_RC10}, // 0x17
{80, EMS_DEVICE_TYPE_THERMOSTAT, "Moduline 200", EMS_DEVICE_FLAG_RC10}, // 0x17
{77, EMS_DEVICE_TYPE_THERMOSTAT, "RC20/Moduline 300", EMS_DEVICE_FLAG_RC20}, // 0x17
{93, EMS_DEVICE_TYPE_THERMOSTAT, "RC20RF", EMS_DEVICE_FLAG_RC20}, // 0x18
{67, EMS_DEVICE_TYPE_THERMOSTAT, "RC30", EMS_DEVICE_FLAG_RC30}, // 0x10
{78, EMS_DEVICE_TYPE_THERMOSTAT, "RC30/Moduline 400", EMS_DEVICE_FLAG_RC30}, // 0x10
{67, EMS_DEVICE_TYPE_THERMOSTAT, "RC30", EMS_DEVICE_FLAG_RC30N}, // 0x10 - based on RC35
{78, EMS_DEVICE_TYPE_THERMOSTAT, "Moduline 400", EMS_DEVICE_FLAG_RC30}, // 0x10
{86, EMS_DEVICE_TYPE_THERMOSTAT, "RC35", EMS_DEVICE_FLAG_RC35}, // 0x10
{93, EMS_DEVICE_TYPE_THERMOSTAT, "RC20RF", EMS_DEVICE_FLAG_RC20}, // 0x19
{158, EMS_DEVICE_TYPE_THERMOSTAT, "RC300/RC310/Moduline 3000/Bosch CW400/W-B Sense II", EMS_DEVICE_FLAG_RC300}, // 0x10
{165, EMS_DEVICE_TYPE_THERMOSTAT, "RC100/Moduline 1010", EMS_DEVICE_FLAG_RC300 | EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{165, EMS_DEVICE_TYPE_THERMOSTAT, "RC100/Moduline 1000/1010", EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
// Sieger
{076, EMS_DEVICE_TYPE_THERMOSTAT, "Sieger ES73", EMS_DEVICE_FLAG_RC35}, // 0x10
{76, EMS_DEVICE_TYPE_THERMOSTAT, "Sieger ES73", EMS_DEVICE_FLAG_RC35}, // 0x10
{113, EMS_DEVICE_TYPE_THERMOSTAT, "Sieger ES72/Buderus RC20", EMS_DEVICE_FLAG_RC20N}, // 0x17
// Junkers
{105, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW100", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{106, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW200", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{107, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR100", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{108, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR110", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{111, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR10", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{191, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR120", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{192, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW120", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE}, // 0x10, cannot write
{147, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR50", EMS_DEVICE_FLAG_JUNKERS | EMS_DEVICE_FLAG_NO_WRITE} // 0x10, cannot write
// Junkers - all 0x10
{105, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW100", EMS_DEVICE_FLAG_JUNKERS1}, // 0x10
{106, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW200", EMS_DEVICE_FLAG_JUNKERS1}, // 0x10
{107, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR100", EMS_DEVICE_FLAG_JUNKERS2}, // 0x10
{108, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR110", EMS_DEVICE_FLAG_JUNKERS2}, // 0x10
{111, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR10", EMS_DEVICE_FLAG_JUNKERS1}, // 0x10
{147, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR50", EMS_DEVICE_FLAG_JUNKERS1}, // 0x10
{191, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FR120", EMS_DEVICE_FLAG_JUNKERS1}, // 0x10
{192, EMS_DEVICE_TYPE_THERMOSTAT, "Junkers FW120", EMS_DEVICE_FLAG_JUNKERS1} // 0x10
};

View File

@@ -76,11 +76,11 @@ char * _short_to_char(char * s, int16_t value, uint8_t decimals) {
return s;
}
// convert short (two bytes) to text string and prints it
// convert unsigned short (two bytes) to text string and prints it
// decimals: 0 = no division, 1=divide value by 10, 2=divide by 2, 10=divide value by 100
char * _ushort_to_char(char * s, uint16_t value, uint8_t decimals) {
// remove errors or invalid values
if (value == EMS_VALUE_USHORT_NOTSET) {
if (value == EMS_VALUE_USHORT_NOTSET) { // 0x7D00
strlcpy(s, "?", 10);
return (s);
}
@@ -149,6 +149,7 @@ void _renderUShortValue(const char * prefix, const char * postfix, uint16_t valu
// convert int (single byte) to text value and returns it
char * _int_to_char(char * s, uint8_t value, uint8_t div) {
s[0] = '\0'; // reset
if (value == EMS_VALUE_INT_NOTSET) {
strlcpy(s, "?", sizeof(s));
return (s);
@@ -200,7 +201,7 @@ void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, u
}
// takes a long value at prints it to debug log and prints
void _renderLongValue(const char * prefix, const char * postfix, uint32_t value) {
void _renderLongValue(const char * prefix, const char * postfix, uint32_t value, uint8_t div) {
static char buffer[200] = {0};
strlcpy(buffer, " ", sizeof(buffer));
strlcat(buffer, prefix, sizeof(buffer));
@@ -210,7 +211,13 @@ void _renderLongValue(const char * prefix, const char * postfix, uint32_t value)
strlcat(buffer, "?", sizeof(buffer));
} else {
char s[20] = {0};
strlcat(buffer, ltoa(value, s, 10), sizeof(buffer));
if (div == 0) {
strlcat(buffer, ltoa(value, s, 10), sizeof(buffer));
} else {
strlcat(buffer, ltoa(value / 10, s, 10), sizeof(buffer));
strlcat(buffer, ".", sizeof(buffer));
strlcat(buffer, ltoa(value % 10, s, 10), sizeof(buffer));
}
}
if (postfix != nullptr) {

View File

@@ -22,7 +22,7 @@ void _renderShortValue(const char * prefix, const char * postfix, int16_t va
void _renderUShortValue(const char * prefix, const char * postfix, uint16_t value, uint8_t decimals = 1);
char * _int_to_char(char * s, uint8_t value, uint8_t div = 1);
void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, uint8_t div = 1);
void _renderLongValue(const char * prefix, const char * postfix, uint32_t value);
void _renderLongValue(const char * prefix, const char * postfix, uint32_t value, uint8_t div = 0);
void _renderBoolValue(const char * prefix, uint8_t value);
char * _hextoa(uint8_t value, char * buffer);
char * _smallitoa(uint8_t value, char * buffer);

View File

@@ -224,27 +224,27 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
}
emsuart_tx_brk(); // send <BRK>
} else if (EMS_Sys_Status.emsTxMode == EMS_TXMODE_DEFAULT) {
/*
* based on code from https://github.com/proddy/EMS-ESP/issues/103 by @susisstrolch
* we emit the whole telegram, with Rx interrupt disabled, collecting busmaster response in FIFO.
* after sending the last char we poll the Rx status until either
* - size(Rx FIFO) == size(Tx-Telegram)
* - <BRK> is detected
* At end of receive we re-enable Rx-INT and send a Tx-BRK in loopback mode.
*
* EMS-Bus error handling
* 1. Busmaster stops echoing on Tx w/o permission
* 2. Busmaster cancel telegram by sending a BRK
*
* Case 1. is handled by a watchdog counter which is reset on each
* Tx attempt. The timeout should be 20x EMSUART_BIT_TIME plus
* some smart guess for processing time on targeted EMS device.
* We set EMS_Sys_Status.emsTxStatus to EMS_TX_WTD_TIMEOUT and return
*
* Case 2. is handled via a BRK chk during transmission.
* We set EMS_Sys_Status.emsTxStatus to EMS_TX_BRK_DETECT and return
*
*/
/*
* based on code from https://github.com/proddy/EMS-ESP/issues/103 by @susisstrolch
* we emit the whole telegram, with Rx interrupt disabled, collecting busmaster response in FIFO.
* after sending the last char we poll the Rx status until either
* - size(Rx FIFO) == size(Tx-Telegram)
* - <BRK> is detected
* At end of receive we re-enable Rx-INT and send a Tx-BRK in loopback mode.
*
* EMS-Bus error handling
* 1. Busmaster stops echoing on Tx w/o permission
* 2. Busmaster cancel telegram by sending a BRK
*
* Case 1. is handled by a watchdog counter which is reset on each
* Tx attempt. The timeout should be 20x EMSUART_BIT_TIME plus
* some smart guess for processing time on targeted EMS device.
* We set EMS_Sys_Status.emsTxStatus to EMS_TX_WTD_TIMEOUT and return
*
* Case 2. is handled via a BRK chk during transmission.
* We set EMS_Sys_Status.emsTxStatus to EMS_TX_BRK_DETECT and return
*
*/
uint16_t wdc = EMS_TX_TO_COUNT;
ETS_UART_INTR_DISABLE(); // disable rx interrupt

View File

@@ -10,8 +10,9 @@
#include "ems.h"
// TOPICS with _CMD_ are used for receiving commands from an MQTT Broker
// TOPICS with *_CMD_* are used for receiving commands from an MQTT Broker
// EMS-ESP will subscribe to these topics
#define TOPIC_GENERIC_CMD "generic_cmd" // for receiving generic system commands via MQTT
// MQTT for thermostat
@@ -26,38 +27,57 @@
#define TOPIC_THERMOSTAT_CMD_DAYTEMP "daytemp" // day temp (RC35 specific)
#define TOPIC_THERMOSTAT_CMD_NIGHTTEMP "nighttemp" // night temp (RC35 specific)
#define TOPIC_THERMOSTAT_CMD_HOLIDAYTEMP "holidayttemp" // holiday temp (RC35 specific)
#define TOPIC_THERMOSTAT_CMD_NOFROSTTEMP "nofrosttemp" // frost temp (Junkers specific)
#define TOPIC_THERMOSTAT_CMD_ECOTEMP "ecotemp" // eco temp (Junkers specific)
#define TOPIC_THERMOSTAT_CMD_HEATTEMP "heattemp" // heat temp (Junkers specific)
#define THERMOSTAT_CURRTEMP "currtemp" // current temperature
#define THERMOSTAT_SELTEMP "seltemp" // selected temperature
#define THERMOSTAT_HC "hc" // which heating circuit number
#define THERMOSTAT_MODE "mode" // mode
#define THERMOSTAT_MODETYPE "modetype" // mode type
#define THERMOSTAT_DAYTEMP "daytemp" // RC35 specific
#define THERMOSTAT_NIGHTTEMP "nighttemp" // RC35 specific
#define THERMOSTAT_HOLIDAYTEMP "holidayttemp" // RC35 specific
#define THERMOSTAT_HEATINGTYPE "heatingtype" // RC35 specific (3=floorheating)
#define THERMOSTAT_CIRCUITCALCTEMP "circuitcalctemp" // RC35 specific
// mixing module
#define MIXING_HC "hc" // which heating circuit number
#define MIXING_WWC "wwc" // which warm water circuit number
// MQTT for boiler
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values to MQTT
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
#define TOPIC_BOILER_CMD "boiler_cmd" // for receiving boiler commands via MQTT
#define TOPIC_BOILER_CMD_WWACTIVATED "boiler_cmd_wwactivated" // change water on/off
#define TOPIC_BOILER_CMD_WWONETIME "boiler_cmd_wwonetime" // warm warter one time loading
#define TOPIC_BOILER_CMD_WWTEMP "boiler_cmd_wwtemp" // wwtemp changes via MQTT
#define TOPIC_BOILER_CMD_COMFORT "comfort" // ww comfort setting via MQTT
#define TOPIC_BOILER_CMD_FLOWTEMP "flowtemp" // flowtemp value via MQTT
#define TOPIC_BOILER_CMD "boiler_cmd" // for receiving boiler commands via MQTT
#define TOPIC_BOILER_CMD_WWACTIVATED "boiler_cmd_wwactivated" // change water on/off
#define TOPIC_BOILER_CMD_WWONETIME "boiler_cmd_wwonetime" // warm warter one time loading
#define TOPIC_BOILER_CMD_WWCIRCULATION "boiler_cmd_wwcirculation" // start warm warter circulation
#define TOPIC_BOILER_CMD_WWTEMP "boiler_cmd_wwtemp" // wwtemp changes via MQTT
#define TOPIC_BOILER_CMD_COMFORT "comfort" // ww comfort setting via MQTT
#define TOPIC_BOILER_CMD_FLOWTEMP "flowtemp" // flowtemp value via MQTT
// MQTT for settings
#define TOPIC_SETTINGS_DATA "settings_data" // for sending settings values to MQTT
#define TOPIC_SETTINGS_CMD "settings_cmd" // for receiving settings commands via MQTT
#define TOPIC_SETTINGS_CMD_DISPLAY "display" // change display
#define TOPIC_SETTINGS_CMD_LANGUAGE "language" // change language
#define TOPIC_SETTINGS_CMD_BUILDING "building" // change building
#define TOPIC_SETTINGS_CMD_MINEXTTEMP "minextTemp" // change min. ext. temp.
// MQTT for mixing device
#define TOPIC_MIXING_DATA "mixing_data" // for sending mixing device values to MQTT
// MQTT for SM10/SM100 Solar Module
// MQTT for SM10/SM100/SM200 Solar Module
#define TOPIC_SM_DATA "sm_data" // topic name
#define SM_COLLECTORTEMP "collectortemp" // collector temp
#define SM_BOTTOMTEMP "bottomtemp" // bottom temp
#define SM_BOTTOMTEMP "bottomtemp" // bottom temp1
#define SM_BOTTOMTEMP2 "bottomtemp2" // bottom temp2
#define SM_PUMPMODULATION "pumpmodulation" // pump modulation
#define SM_PUMP "pump" // pump active
#define SM_VALVESTATUS "valvestatus" // valve status
#define SM_ENERGYLASTHOUR "energylasthour" // energy last hour
#define SM_ENERGYTODAY "energytoday" // energy today
#define SM_ENERGYTOTAL "energytotal" // energy total
@@ -76,5 +96,7 @@
#define TOPIC_SHOWER_DURATION "duration" // duration of the last shower
// MQTT for External Sensors
#define TOPIC_EXTERNAL_SENSORS "sensors" // for sending sensor values to MQTT
#define PAYLOAD_EXTERNAL_SENSORS "temp_%d" // for formatting the payload for each external dallas sensor
#define TOPIC_EXTERNAL_SENSORS "sensors" // topic for sending sensor values to MQTT
#define PAYLOAD_EXTERNAL_SENSOR_NUM "sensor" // which sensor #
#define PAYLOAD_EXTERNAL_SENSOR_ID "id"
#define PAYLOAD_EXTERNAL_SENSOR_TEMP "temp"

View File

@@ -1 +1 @@
#define APP_VERSION "1.9.4"
#define APP_VERSION "1.9.5"

View File

@@ -181,9 +181,9 @@
<div class="row form-group">
<label class="col-xs-3">Port<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="MQTT Server port number"></i></label>
data-content="MQTT Server port number (default 1883)"></i></label>
<span class="col-xs-9">
<input class="form-control input-sm" placeholder="1883" value="" style="display:inline;max-width:185px"
<input class="form-control input-sm" placeholder="" value="" style="display:inline;max-width:185px"
id="mqttport" type="text">
</span>
<br>
@@ -191,9 +191,9 @@
<div class="row form-group">
<label class="col-xs-3">QOS<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="MQTT QOS 0,1 or 2"></i></label>
data-content="MQTT QOS 0, 1 or 2 (default 0)"></i></label>
<span class="col-xs-9">
<input class="form-control input-sm" placeholder="1" value="" style="display:inline;max-width:185px"
<input class="form-control input-sm" placeholder="" value="" style="display:inline;max-width:185px"
id="mqttqos" type="text">
</span>
<br>
@@ -201,9 +201,9 @@
<div class="row form-group">
<label class="col-xs-3">Keep Alive<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="MQTT Keep Alive time in seconds"></i></label>
data-content="MQTT Keep Alive time in seconds (default 60)"></i></label>
<span class="col-xs-9">
<input class="form-control input-sm" placeholder="60" value="" style="display:inline;max-width:185px"
<input class="form-control input-sm" placeholder="" value="" style="display:inline;max-width:185px"
id="mqttkeepalive" type="text">
</span>
<br>
@@ -246,14 +246,14 @@
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="MQTT topic prefix (&lt;mqtt base&gt;/&lt;host name&gt;/)"></i></label>
<span class="col-xs-9">
<input class="form-control input-sm" placeholder="home" value="" style="display:inline;max-width:185px"
<input class="form-control input-sm" placeholder="" value="" style="display:inline;max-width:185px"
id="mqttbase" type="text">
</span>
</div>
<div class="row form-group">
<label class="col-xs-3">Heartbeat<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="Enable or Disable an automatic MQTT topic publish with system stats"></i></label>
data-content="Enable or Disable an automatic MQTT topic publish with system status"></i></label>
<div class="col-xs-9">
<form>
<label class="radio-inline">
@@ -508,16 +508,6 @@
<table class="table table-hover table-striped table-condensed" border=1>
<caption>MQTT&nbsp;&nbsp;<div id="mqttconnected"></div>&nbsp;<div id="mqttheartbeat"></div>
</caption>
<thead>
<tr>
<th>Time</th>
<th>Topic<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
id="mqttloghdr"></i></th>
<th>Payload</th>
</tr>
</thead>
<tbody id="mqttlog"></tbody>
</table>
</div>
</div>

View File

@@ -391,40 +391,6 @@ function listStats() {
document.getElementById("mqttheartbeat").className = "label label-primary";
}
document.getElementById("mqttloghdr").setAttribute('data-content', "Topics are prefixed with " + ajaxobj.mqttloghdr);
var mtable = document.getElementById("mqttlog");
var obj = ajaxobj.mqttlog;
var tr, td;
for (var i = 0; i < obj.length; i++) {
tr = document.createElement("tr");
td = document.createElement("td");
if (obj[i].time < 1563300000) {
td.innerHTML = "(" + obj[i].time + ")";
} else {
var vuepoch = new Date(obj[i].time * 1000);
td.innerHTML = vuepoch.getUTCFullYear() +
"-" + twoDigits(vuepoch.getUTCMonth() + 1) +
"-" + twoDigits(vuepoch.getUTCDate()) +
" " + twoDigits(vuepoch.getUTCHours()) +
":" + twoDigits(vuepoch.getUTCMinutes()) +
":" + twoDigits(vuepoch.getUTCSeconds());
}
tr.appendChild(td);
td = document.createElement("td");
td.innerHTML = obj[i].topic
tr.appendChild(td);
td = document.createElement("td");
td.innerHTML = obj[i].payload
tr.appendChild(td);
mtable.appendChild(tr);
}
}
function getContent(contentname) {

View File

@@ -97,8 +97,9 @@ var custom_configfile = {
"listen_mode": false,
"shower_timer": true,
"shower_alert": false,
"publish_time": 0,
"tx_mode": 1
"publish_time": 10,
"tx_mode": 1,
"bus_id": 11
}
};
@@ -118,17 +119,7 @@ function sendStatus() {
"systemload": 0,
"mqttconnected": true,
"mqttheartbeat": false,
"uptime": "0 days 0 hours 1 minute 45 seconds",
"mqttloghdr": "home/ems-esp/",
"mqttlog": [
{ "topic": "start", "payload": "start", "time": 1565956388 },
{ "topic": "shower_timer", "payload": "1", "time": 1565956388 },
{ "topic": "shower_alert", "payload": "0", "time": 1565956388 },
{ "topic": "boiler_data", "payload": "{\"wWComfort\":\"Hot\",\"wWSelTemp\":60,\"selFlowTemp\":5,\"selBurnPow\":0,\"curBurnPow\":0,\"pumpMod\":0,\"wWCurTmp\":48.4,\"wWCurFlow\":0,\"curFlowTemp\":49.3,\"retTemp\":49.3,\"sysPress\":1.8,\"boilTemp\":50.5,\"wWActivated\":\"on\",\"burnGas\":\"off\",\"heatPmp\":\"off\",\"fanWork\":\"off\",\"ignWork\":\"off\",\"wWCirc\":\"off\",\"wWHeat\":\"on\",\"burnStarts\":223397,\"burnWorkMin\":366019,\"heatWorkMin\":294036,\"ServiceCode\":\"0H\",\"ServiceCodeNumber\":203}", "time": 1565956463 },
{ "topic": "tapwater_active", "payload": "0", "time": 1565956408 },
{ "topic": "heating_active", "payload": "0", "time": 1565956408 },
{ "topic": "thermostat_data", "payload": "{\"thermostat_hc\":\"1\",\"thermostat_seltemp\":15,\"thermostat_currtemp\":23,\"thermostat_mode\":\"auto\"}", "time": 1565956444 }
]
"uptime": "0 days 0 hours 1 minute 45 seconds"
};
wss.broadcast(stats);
@@ -170,8 +161,7 @@ function sendCustomStatus() {
"b2": "off",
"b3": 0,
"b4": 53,
"b5": 54.4,
"b6": 53.3
"b5": 54.4
},
"sm": {