mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
release 1.3.1
This commit is contained in:
15
CHANGELOG.md
15
CHANGELOG.md
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.3.1] 2019-01-12
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- telnet commands with set are no longer forced to lower case
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Custom settings (e.g set led) moved outside MyESP
|
||||||
|
- Moved all MQTT to my_config.h making it independent from Home Assistant
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- MQTT keep alive, last will testament and other settings all configurable in my_config.h
|
||||||
|
|
||||||
## [1.3.0] 2019-01-09
|
## [1.3.0] 2019-01-09
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ I've tested the code and circuit with a few ESP8266 development boards such as t
|
|||||||
2. Get an ESP8266 dev board and connect the 2 EMS output lines from the boiler to the circuit and the Rx and Tx out to ESP pins D7 and D8 respectively. The EMS connection can either be the 12-15V AC direct from the thermostat bus line or from the 3.5" Service Jack at the front.
|
2. Get an ESP8266 dev board and connect the 2 EMS output lines from the boiler to the circuit and the Rx and Tx out to ESP pins D7 and D8 respectively. The EMS connection can either be the 12-15V AC direct from the thermostat bus line or from the 3.5" Service Jack at the front.
|
||||||
3. Optionally connect an external LED or decide to use the onboard ESP8266 LED. This will flash when there is an error on the EMS bus line or stay solid when it's connected.
|
3. Optionally connect an external LED or decide to use the onboard ESP8266 LED. This will flash when there is an error on the EMS bus line or stay solid when it's connected.
|
||||||
4. Modify `my_custom.h`
|
4. Modify `my_custom.h`
|
||||||
5. Build and upload the firmware to the ESP8266 device. I used PlatformIO with Visual Studio Code but using Atom or a command-line is just as easy if you don't plan to make code changes. Do make sure you set the MQTT and WiFi credentials correctly in the build flags and if you're not using MQTT leave the MQTT_IP blank. The firmware supports OTA too with the default hostname as 'boiler'.
|
5. Build and upload the firmware to the ESP8266 device. I used PlatformIO with Visual Studio Code but using Atom or a command-line is just as easy if you don't plan to make code changes. Do make sure you set the MQTT and WiFi credentials correctly in the build flags and if you're not using MQTT leave the MQTT_HOST blank. The firmware supports OTA too with the default hostname as 'boiler'.
|
||||||
6. Power the ESP either via USB or direct into the 5v vin pin from an external power 5V volts supply with min 400mA.
|
6. Power the ESP either via USB or direct into the 5v vin pin from an external power 5V volts supply with min 400mA.
|
||||||
7. Attach the 3v3 out on the ESP8266 to the DC power line on the EMS circuit as indicated in the schematics.
|
7. Attach the 3v3 out on the ESP8266 to the DC power line on the EMS circuit as indicated in the schematics.
|
||||||
8. The WiFi connects via DHCP by default. Find the IP by from your router and then telnet (port 23) to it. If a connection can't be made it will go into Access Point mode. Tip: to enable Telnet on Windows run `dism /online /Enable-Feature /FeatureName:TelnetClient` or install something like [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html). If everything is working you should see the messages appear in the window as shown in the next section. However if you're unable to locate the IP of the ESP then something went wrong. Re-compile with the -DDEBUG_SUPPORT and connect via USB to a PC and check the Serial log for errors.
|
8. The WiFi connects via DHCP by default. Find the IP by from your router and then telnet (port 23) to it. If a connection can't be made it will go into Access Point mode. Tip: to enable Telnet on Windows run `dism /online /Enable-Feature /FeatureName:TelnetClient` or install something like [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html). If everything is working you should see the messages appear in the window as shown in the next section. However if you're unable to locate the IP of the ESP then something went wrong. Re-compile with the -DDEBUG_SUPPORT and connect via USB to a PC and check the Serial log for errors.
|
||||||
@@ -188,7 +188,7 @@ Every telegram sent is echo'd back to Rx.
|
|||||||
|
|
||||||
`ems.cpp` is the logic to read the EMS packets (telegrams), validates them and process them based on the type.
|
`ems.cpp` is the logic to read the EMS packets (telegrams), validates them and process them based on the type.
|
||||||
|
|
||||||
`ems-esp.ino` is the Arduino code for the ESP8266 that kicks it all off. This is where we have specific logic such as the code to monitor and alert on the Shower timer and light up the LEDs. LED support is enabled by default and can be switched off at compile time using the -DNO_LED build flag.
|
`ems-esp.ino` is the Arduino code for the ESP8266 that kicks it all off. This is where we have specific logic such as the code to monitor and alert on the Shower timer and light up the LEDs.
|
||||||
|
|
||||||
`my_config.h` all the custom settings
|
`my_config.h` all the custom settings
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ The ESP8266 will start in Access Point (AP) mode, so connect via WiFi to the SSI
|
|||||||
|
|
||||||
If the WiFi, MQTT, MDNS or something else fails to connect, re-build the firmware using the `-DDEBUG_SUPPORT` option, connect the ESP8266 to a USB in your computer and monitor the Serial output. A lot of detailed logging will be printed to help you pinpoint the cause of the error.
|
If the WiFi, MQTT, MDNS or something else fails to connect, re-build the firmware using the `-DDEBUG_SUPPORT` option, connect the ESP8266 to a USB in your computer and monitor the Serial output. A lot of detailed logging will be printed to help you pinpoint the cause of the error.
|
||||||
|
|
||||||
The onboard LED will flash if there is no connection with the EMS bus. You can disable LED support by adding -DNO_LED to the build options.
|
The onboard LED will flash if there is no connection with the EMS bus. You can disable LED support by the 'set led' command from the telnet client
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
|
|||||||
@@ -14,21 +14,32 @@ MyESP::MyESP() {
|
|||||||
_app_name = strdup("MyESP");
|
_app_name = strdup("MyESP");
|
||||||
_app_version = strdup("1.0.0");
|
_app_version = strdup("1.0.0");
|
||||||
_boottime = strdup("unknown");
|
_boottime = strdup("unknown");
|
||||||
_extern_WIFICallback = NULL;
|
|
||||||
_extern_WIFICallbackSet = false;
|
|
||||||
_telnetcommand_callback = NULL;
|
_telnetcommand_callback = NULL;
|
||||||
_telnet_callback = NULL;
|
_telnet_callback = NULL;
|
||||||
|
|
||||||
|
_fs_callback = NULL;
|
||||||
|
_fs_settings_callback = NULL;
|
||||||
|
|
||||||
_helpProjectCmds = NULL;
|
_helpProjectCmds = NULL;
|
||||||
_helpProjectCmds_count = 0;
|
_helpProjectCmds_count = 0;
|
||||||
|
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
|
||||||
|
|
||||||
_mqtt_host = NULL;
|
_mqtt_host = NULL;
|
||||||
_mqtt_password = NULL;
|
_mqtt_password = NULL;
|
||||||
_mqtt_username = NULL;
|
_mqtt_username = NULL;
|
||||||
|
_mqtt_retain = false;
|
||||||
|
_mqtt_keepalive = 300;
|
||||||
|
_mqtt_will = NULL;
|
||||||
|
_mqtt_base = NULL;
|
||||||
|
_mqtt_qos = 0;
|
||||||
|
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
|
||||||
|
|
||||||
_wifi_password = NULL;
|
_wifi_password = NULL;
|
||||||
_wifi_ssid = NULL;
|
_wifi_ssid = NULL;
|
||||||
_mqttbase = NULL;
|
_wifi_callback = NULL;
|
||||||
|
|
||||||
_suspendOutput = false;
|
_suspendOutput = false;
|
||||||
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
|
|
||||||
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MyESP::~MyESP() {
|
MyESP::~MyESP() {
|
||||||
@@ -113,8 +124,8 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// call any final custom settings
|
// call any final custom settings
|
||||||
if (_extern_WIFICallbackSet) {
|
if (_wifi_callback) {
|
||||||
_extern_WIFICallback(); // call callback to set any custom things
|
_wifi_callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,8 +136,8 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
|||||||
myDebug_P(PSTR("[WIFI] MAC %s"), WiFi.softAPmacAddress().c_str());
|
myDebug_P(PSTR("[WIFI] MAC %s"), WiFi.softAPmacAddress().c_str());
|
||||||
|
|
||||||
// call any final custom settings
|
// call any final custom settings
|
||||||
if (_extern_WIFICallbackSet) {
|
if (_wifi_callback) {
|
||||||
_extern_WIFICallback(); // call callback to set any custom things
|
_wifi_callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,14 +163,7 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
|||||||
char message[len + 1];
|
char message[len + 1];
|
||||||
strlcpy(message, (char *)payload, len + 1);
|
strlcpy(message, (char *)payload, len + 1);
|
||||||
|
|
||||||
// myDebug_P(PSTR("[MQTT] Received %s => %s"), topic, message);
|
// myDebug_P(PSTR("[MQTT] Received %s => %s"), topic, message); // enable for debugging
|
||||||
|
|
||||||
// check for our default ones
|
|
||||||
if ((strcmp(topic, MQTT_HA) == 0) && (strcmp(message, MQTT_TOPIC_START_PAYLOAD) == 0)) {
|
|
||||||
myDebug_P(PSTR("[MQTT] HA rebooted - restarting device"));
|
|
||||||
resetESP();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// topics are in format MQTT_BASE/HOSTNAME/TOPIC
|
// topics are in format MQTT_BASE/HOSTNAME/TOPIC
|
||||||
char * topic_magnitude = strrchr(topic, '/'); // strip out everything until last /
|
char * topic_magnitude = strrchr(topic, '/'); // strip out everything until last /
|
||||||
@@ -167,13 +171,6 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
|||||||
topic = topic_magnitude + 1;
|
topic = topic_magnitude + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for bootime, something specific I fetch as an acknowledgement from Home Assistant
|
|
||||||
if (strcmp(topic, MQTT_TOPIC_START) == 0) {
|
|
||||||
myDebug_P(PSTR("[MQTT] received boottime: %s"), message);
|
|
||||||
setBoottime(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send message event to custom service
|
// Send message event to custom service
|
||||||
(_mqtt_callback)(MQTT_MESSAGE_EVENT, topic, message);
|
(_mqtt_callback)(MQTT_MESSAGE_EVENT, topic, message);
|
||||||
}
|
}
|
||||||
@@ -183,8 +180,8 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
|||||||
void MyESP::mqttSubscribe(const char * topic) {
|
void MyESP::mqttSubscribe(const char * topic) {
|
||||||
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
||||||
char s[100];
|
char s[100];
|
||||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, topic);
|
snprintf(s, sizeof(s), "%s/%s/%s", _mqtt_base, _app_hostname, topic);
|
||||||
unsigned int packetId = mqttClient.subscribe(s, MQTT_QOS);
|
unsigned int packetId = mqttClient.subscribe(s, _mqtt_qos);
|
||||||
myDebug_P(PSTR("[MQTT] Subscribing to %s (PID %d)"), s, packetId);
|
myDebug_P(PSTR("[MQTT] Subscribing to %s (PID %d)"), s, packetId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +191,7 @@ void MyESP::mqttSubscribe(const char * topic) {
|
|||||||
void MyESP::mqttUnsubscribe(const char * topic) {
|
void MyESP::mqttUnsubscribe(const char * topic) {
|
||||||
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
||||||
char s[100];
|
char s[100];
|
||||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, topic);
|
snprintf(s, sizeof(s), "%s/%s/%s", _mqtt_base, _app_hostname, topic);
|
||||||
unsigned int packetId = mqttClient.unsubscribe(s);
|
unsigned int packetId = mqttClient.unsubscribe(s);
|
||||||
myDebug_P(PSTR("[MQTT] Unsubscribing to %s (PID %d)"), s, packetId);
|
myDebug_P(PSTR("[MQTT] Unsubscribing to %s (PID %d)"), s, packetId);
|
||||||
}
|
}
|
||||||
@@ -203,27 +200,16 @@ void MyESP::mqttUnsubscribe(const char * topic) {
|
|||||||
// MQTT Publish
|
// MQTT Publish
|
||||||
void MyESP::mqttPublish(const char * topic, const char * payload) {
|
void MyESP::mqttPublish(const char * topic, const char * payload) {
|
||||||
char s[MQTT_MAX_SIZE];
|
char s[MQTT_MAX_SIZE];
|
||||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, topic);
|
snprintf(s, sizeof(s), "%s/%s/%s", _mqtt_base, _app_hostname, topic);
|
||||||
// myDebug_P(PSTR("[MQTT] Sending pubish to %s with payload %s"), s, payload);
|
// myDebug_P(PSTR("[MQTT] Sending pubish to %s with payload %s"), s, payload);
|
||||||
mqttClient.publish(s, MQTT_QOS, false, payload);
|
mqttClient.publish(s, _mqtt_qos, _mqtt_retain, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MQTT onConnect - when a connect is established automatically subscribe to my HA topics
|
// MQTT onConnect - when a connect is established
|
||||||
void MyESP::_mqttOnConnect() {
|
void MyESP::_mqttOnConnect() {
|
||||||
myDebug_P(PSTR("[MQTT] Connected"));
|
myDebug_P(PSTR("[MQTT] Connected"));
|
||||||
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
|
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
|
||||||
|
|
||||||
#ifndef NO_HA
|
|
||||||
// standard subscribes for HA (Home Assistant)
|
|
||||||
mqttClient.subscribe(MQTT_HA, MQTT_QOS); // to "ha"
|
|
||||||
mqttSubscribe(MQTT_TOPIC_START); // to home/<hostname>/start
|
|
||||||
|
|
||||||
// send specific start command to HA via MQTT, which returns the boottime
|
|
||||||
char s[48];
|
|
||||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, MQTT_TOPIC_START);
|
|
||||||
mqttClient.publish(s, MQTT_QOS, false, MQTT_TOPIC_START_PAYLOAD);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// call custom function to handle mqtt receives
|
// call custom function to handle mqtt receives
|
||||||
(_mqtt_callback)(MQTT_CONNECT_EVENT, NULL, NULL);
|
(_mqtt_callback)(MQTT_CONNECT_EVENT, NULL, NULL);
|
||||||
}
|
}
|
||||||
@@ -342,29 +328,18 @@ void MyESP::_ota_setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sets boottime
|
// sets boottime
|
||||||
void MyESP::setBoottime(char * boottime) {
|
void MyESP::setBoottime(const char * boottime) {
|
||||||
if (_boottime) {
|
if (_boottime) {
|
||||||
free(_boottime);
|
free(_boottime);
|
||||||
}
|
}
|
||||||
_boottime = strdup(boottime);
|
_boottime = strdup(boottime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets boottime
|
|
||||||
void MyESP::setMQTTbase(char * mqttbase) {
|
|
||||||
if (_mqttbase) {
|
|
||||||
free(_mqttbase);
|
|
||||||
}
|
|
||||||
_mqttbase = strdup(mqttbase);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set callback of sketch function to process project messages
|
// Set callback of sketch function to process project messages
|
||||||
void MyESP::setTelnetCommands(command_t * cmds, uint8_t count, telnetcommand_callback_f callback) {
|
void MyESP::setTelnet(command_t * cmds, uint8_t count, telnetcommand_callback_f callback_cmd, telnet_callback_f callback) {
|
||||||
_helpProjectCmds = cmds; // command list
|
_helpProjectCmds = cmds; // command list
|
||||||
_helpProjectCmds_count = count; // number of commands
|
_helpProjectCmds_count = count; // number of commands
|
||||||
_telnetcommand_callback = callback; // external function to handle commands
|
_telnetcommand_callback = callback_cmd; // external function to handle commands
|
||||||
}
|
|
||||||
|
|
||||||
void MyESP::setTelnetCallback(telnet_callback_f callback) {
|
|
||||||
_telnet_callback = callback;
|
_telnet_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,6 +402,8 @@ void MyESP::_consoleShowHelp() {
|
|||||||
SerialAndTelnet.println("*\n\r* Commands:\n\r* ?=help, CTRL-D=quit, !=reboot");
|
SerialAndTelnet.println("*\n\r* Commands:\n\r* ?=help, CTRL-D=quit, !=reboot");
|
||||||
SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]"));
|
SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]"));
|
||||||
SerialAndTelnet.println(FPSTR("* set erase"));
|
SerialAndTelnet.println(FPSTR("* set erase"));
|
||||||
|
SerialAndTelnet.println(FPSTR("* set led <on | off>"));
|
||||||
|
|
||||||
SerialAndTelnet.println(FPSTR("*"));
|
SerialAndTelnet.println(FPSTR("*"));
|
||||||
|
|
||||||
// print custom commands if available. Take from progmem
|
// print custom commands if available. Take from progmem
|
||||||
@@ -453,7 +430,6 @@ void MyESP::_consoleShowHelp() {
|
|||||||
void MyESP::resetESP() {
|
void MyESP::resetESP() {
|
||||||
myDebug_P(PSTR("* Reboot ESP..."));
|
myDebug_P(PSTR("* Reboot ESP..."));
|
||||||
end();
|
end();
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
ESP.reset(); // for ESP8266 only
|
ESP.reset(); // for ESP8266 only
|
||||||
#else
|
#else
|
||||||
@@ -461,23 +437,7 @@ void MyESP::resetESP() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// sends a MQTT notification message to Home Assistant (HA)
|
// read next word from string buffer
|
||||||
void MyESP::sendHANotification(const char * message) {
|
|
||||||
char payload[48];
|
|
||||||
snprintf(payload, sizeof(payload), "%s : %s", _app_hostname, message);
|
|
||||||
myDebug_P(PSTR("[MQTT] Sending HA notification %s"), payload);
|
|
||||||
mqttClient.publish(MQTT_HA_NOTIFICATION, MQTT_QOS, false, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// send specific command to Home Assistant (HA) via MQTT
|
|
||||||
// format is: home/<hostname>/command with payload <cmd>
|
|
||||||
void MyESP::sendHACommand(const char * cmd) {
|
|
||||||
myDebug_P(PSTR("[MQTT] Sending HA command %s"), cmd);
|
|
||||||
char topic[48];
|
|
||||||
snprintf(topic, sizeof(topic), "%s/%s/%s", _mqttbase, _app_hostname, MQTT_TOPIC_COMMAND);
|
|
||||||
mqttClient.publish(topic, MQTT_QOS, false, cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
char * MyESP::_telnet_readWord() {
|
char * MyESP::_telnet_readWord() {
|
||||||
char * word = strtok(NULL, ", \n");
|
char * word = strtok(NULL, ", \n");
|
||||||
return word;
|
return word;
|
||||||
@@ -485,10 +445,10 @@ char * MyESP::_telnet_readWord() {
|
|||||||
|
|
||||||
// change settings - always as strings
|
// change settings - always as strings
|
||||||
// messy code but effective since we don't have too many settings
|
// messy code but effective since we don't have too many settings
|
||||||
void MyESP::_changeSetting(const char * setting, const char * value) {
|
void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value) {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
// validate 2nd argument
|
// check for our internal commands first
|
||||||
if (strcmp(setting, "erase") == 0) {
|
if (strcmp(setting, "erase") == 0) {
|
||||||
_fs_eraseConfig();
|
_fs_eraseConfig();
|
||||||
return;
|
return;
|
||||||
@@ -502,9 +462,7 @@ void MyESP::_changeSetting(const char * setting, const char * value) {
|
|||||||
_wifi_ssid = strdup(value);
|
_wifi_ssid = strdup(value);
|
||||||
}
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
} else if (strcmp(setting, "wifi_password") == 0) {
|
||||||
|
|
||||||
if (strcmp(setting, "wifi_password") == 0) {
|
|
||||||
if (_wifi_password)
|
if (_wifi_password)
|
||||||
free(_wifi_password);
|
free(_wifi_password);
|
||||||
_wifi_password = NULL; // just to be sure
|
_wifi_password = NULL; // just to be sure
|
||||||
@@ -512,9 +470,7 @@ void MyESP::_changeSetting(const char * setting, const char * value) {
|
|||||||
_wifi_password = strdup(value);
|
_wifi_password = strdup(value);
|
||||||
}
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
} else if (strcmp(setting, "mqtt_host") == 0) {
|
||||||
|
|
||||||
if (strcmp(setting, "mqtt_host") == 0) {
|
|
||||||
if (_mqtt_host)
|
if (_mqtt_host)
|
||||||
free(_mqtt_host);
|
free(_mqtt_host);
|
||||||
_mqtt_host = NULL; // just to be sure
|
_mqtt_host = NULL; // just to be sure
|
||||||
@@ -522,9 +478,7 @@ void MyESP::_changeSetting(const char * setting, const char * value) {
|
|||||||
_mqtt_host = strdup(value);
|
_mqtt_host = strdup(value);
|
||||||
}
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
} else if (strcmp(setting, "mqtt_username") == 0) {
|
||||||
|
|
||||||
if (strcmp(setting, "mqtt_username") == 0) {
|
|
||||||
if (_mqtt_username)
|
if (_mqtt_username)
|
||||||
free(_mqtt_username);
|
free(_mqtt_username);
|
||||||
_mqtt_username = NULL; // just to be sure
|
_mqtt_username = NULL; // just to be sure
|
||||||
@@ -532,9 +486,7 @@ void MyESP::_changeSetting(const char * setting, const char * value) {
|
|||||||
_mqtt_username = strdup(value);
|
_mqtt_username = strdup(value);
|
||||||
}
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
} else if (strcmp(setting, "mqtt_password") == 0) {
|
||||||
|
|
||||||
if (strcmp(setting, "mqtt_password") == 0) {
|
|
||||||
if (_mqtt_password)
|
if (_mqtt_password)
|
||||||
free(_mqtt_password);
|
free(_mqtt_password);
|
||||||
_mqtt_password = NULL; // just to be sure
|
_mqtt_password = NULL; // just to be sure
|
||||||
@@ -542,6 +494,9 @@ void MyESP::_changeSetting(const char * setting, const char * value) {
|
|||||||
_mqtt_password = strdup(value);
|
_mqtt_password = strdup(value);
|
||||||
}
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
|
} else {
|
||||||
|
// finally check for any custom commands
|
||||||
|
ok = (_fs_settings_callback)(MYESP_FSACTION_SET, wc, setting, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
@@ -558,7 +513,7 @@ void MyESP::_changeSetting(const char * setting, const char * value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_fs_saveConfig()) {
|
if (_fs_saveConfig()) {
|
||||||
SerialAndTelnet.println("Changes will have effect after the next restart. Please reboot using ! command");
|
SerialAndTelnet.println("Note, some changes will only have effect after a device reboot (use ! command)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,14 +555,19 @@ void MyESP::_telnetCommand(char * commandLine) {
|
|||||||
for (uint8_t i = 0; i < strlen(_mqtt_password); i++)
|
for (uint8_t i = 0; i < strlen(_mqtt_password); i++)
|
||||||
SerialAndTelnet.print("*");
|
SerialAndTelnet.print("*");
|
||||||
}
|
}
|
||||||
SerialAndTelnet.println("\n\r\n\rUsage: set <setting> <value>");
|
SerialAndTelnet.println();
|
||||||
|
|
||||||
|
// print custom settings
|
||||||
|
(_fs_settings_callback)(MYESP_FSACTION_LIST, 0, NULL, NULL);
|
||||||
|
|
||||||
|
SerialAndTelnet.println("\n\rUsage: set <setting> <value>");
|
||||||
} else if (wc == 2) {
|
} else if (wc == 2) {
|
||||||
char * setting = _telnet_readWord();
|
char * setting = _telnet_readWord();
|
||||||
_changeSetting(setting, NULL);
|
_changeSetting(1, setting, NULL);
|
||||||
} else if (wc == 3) {
|
} else if (wc == 3) {
|
||||||
char * setting = _telnet_readWord();
|
char * setting = _telnet_readWord();
|
||||||
char * value = _telnet_readWord();
|
char * value = _telnet_readWord();
|
||||||
_changeSetting(setting, value);
|
_changeSetting(2, setting, value);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -655,7 +615,6 @@ void MyESP::_telnetHandle() {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_suspendOutput = true;
|
_suspendOutput = true;
|
||||||
c = tolower(c);
|
|
||||||
if (charsRead < TELNET_MAX_COMMAND_LENGTH) {
|
if (charsRead < TELNET_MAX_COMMAND_LENGTH) {
|
||||||
_command[charsRead++] = c;
|
_command[charsRead++] = c;
|
||||||
}
|
}
|
||||||
@@ -665,17 +624,6 @@ void MyESP::_telnetHandle() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets a custom function to run when wifi is started
|
|
||||||
void MyESP::setWIFICallback(void (*callback)()) {
|
|
||||||
_extern_WIFICallback = callback;
|
|
||||||
_extern_WIFICallbackSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the mqtt callback for after connecting to subscribe and process incoming messages
|
|
||||||
void MyESP::setMQTTCallback(mqtt_callback_f callback) {
|
|
||||||
_mqtt_callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we have a connection to MQTT broker
|
// ensure we have a connection to MQTT broker
|
||||||
void MyESP::_mqttConnect() {
|
void MyESP::_mqttConnect() {
|
||||||
if (!_mqtt_host || mqttClient.connected() || (WiFi.status() != WL_CONNECTED)) {
|
if (!_mqtt_host || mqttClient.connected() || (WiFi.status() != WL_CONNECTED)) {
|
||||||
@@ -696,6 +644,9 @@ void MyESP::_mqttConnect() {
|
|||||||
|
|
||||||
mqttClient.setServer(_mqtt_host, MQTT_PORT);
|
mqttClient.setServer(_mqtt_host, MQTT_PORT);
|
||||||
mqttClient.setClientId(_app_hostname);
|
mqttClient.setClientId(_app_hostname);
|
||||||
|
mqttClient.setKeepAlive(_mqtt_keepalive);
|
||||||
|
mqttClient.setCleanSession(false);
|
||||||
|
mqttClient.setWill(_mqtt_will, _mqtt_qos, _mqtt_retain, "0"); // payload is 0
|
||||||
|
|
||||||
if (_mqtt_username && _mqtt_password) {
|
if (_mqtt_username && _mqtt_password) {
|
||||||
myDebug_P(PSTR("[MQTT] Connecting to MQTT using user %s"), _mqtt_username);
|
myDebug_P(PSTR("[MQTT] Connecting to MQTT using user %s"), _mqtt_username);
|
||||||
@@ -709,7 +660,7 @@ void MyESP::_mqttConnect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup everything we need
|
// Setup everything we need
|
||||||
void MyESP::setConnection(char * wifi_ssid, char * wifi_password, char * mqtt_host, char * mqtt_username, char * mqtt_password) {
|
void MyESP::setWIFI(char * wifi_ssid, char * wifi_password, wifi_callback_f callback) {
|
||||||
// Check SSID too long or missing
|
// Check SSID too long or missing
|
||||||
if (!wifi_ssid || *wifi_ssid == 0x00 || strlen(wifi_ssid) > 31) {
|
if (!wifi_ssid || *wifi_ssid == 0x00 || strlen(wifi_ssid) > 31) {
|
||||||
_wifi_ssid = NULL;
|
_wifi_ssid = NULL;
|
||||||
@@ -724,6 +675,19 @@ void MyESP::setConnection(char * wifi_ssid, char * wifi_password, char * mqtt_ho
|
|||||||
_wifi_password = strdup(wifi_password);
|
_wifi_password = strdup(wifi_password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// callback
|
||||||
|
_wifi_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyESP::setMQTT(char * mqtt_host,
|
||||||
|
char * mqtt_username,
|
||||||
|
char * mqtt_password,
|
||||||
|
char * mqtt_base,
|
||||||
|
unsigned long mqtt_keepalive,
|
||||||
|
unsigned char mqtt_qos,
|
||||||
|
bool mqtt_retain,
|
||||||
|
char * mqtt_will,
|
||||||
|
mqtt_callback_f callback) {
|
||||||
// can be empty
|
// can be empty
|
||||||
if (!mqtt_host || *mqtt_host == 0x00) {
|
if (!mqtt_host || *mqtt_host == 0x00) {
|
||||||
_mqtt_host = NULL;
|
_mqtt_host = NULL;
|
||||||
@@ -744,6 +708,26 @@ void MyESP::setConnection(char * wifi_ssid, char * wifi_password, char * mqtt_ho
|
|||||||
} else {
|
} else {
|
||||||
_mqtt_password = strdup(mqtt_password);
|
_mqtt_password = strdup(mqtt_password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// base
|
||||||
|
if (_mqtt_base) {
|
||||||
|
free(_mqtt_base);
|
||||||
|
}
|
||||||
|
_mqtt_base = strdup(mqtt_base);
|
||||||
|
|
||||||
|
// callback
|
||||||
|
_mqtt_callback = callback;
|
||||||
|
|
||||||
|
// other mqtt settings
|
||||||
|
_mqtt_keepalive = mqtt_keepalive;
|
||||||
|
_mqtt_qos = mqtt_qos;
|
||||||
|
_mqtt_retain = mqtt_retain;
|
||||||
|
|
||||||
|
// last will
|
||||||
|
if (_mqtt_will) {
|
||||||
|
free(_mqtt_will);
|
||||||
|
}
|
||||||
|
_mqtt_will = strdup(mqtt_will);
|
||||||
}
|
}
|
||||||
|
|
||||||
// print contents of file
|
// print contents of file
|
||||||
@@ -768,6 +752,11 @@ void MyESP::_fs_eraseConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyESP::setSettings(fs_callback_f callback_fs, fs_settings_callback_f callback_settings_fs) {
|
||||||
|
_fs_callback = callback_fs;
|
||||||
|
_fs_settings_callback = callback_settings_fs;
|
||||||
|
}
|
||||||
|
|
||||||
// load from spiffs
|
// load from spiffs
|
||||||
bool MyESP::_fs_loadConfig() {
|
bool MyESP::_fs_loadConfig() {
|
||||||
File configFile = SPIFFS.open("/config.json", "r");
|
File configFile = SPIFFS.open("/config.json", "r");
|
||||||
@@ -788,7 +777,7 @@ bool MyESP::_fs_loadConfig() {
|
|||||||
// use configFile.readString
|
// use configFile.readString
|
||||||
configFile.readBytes(buf.get(), size);
|
configFile.readBytes(buf.get(), size);
|
||||||
|
|
||||||
StaticJsonBuffer<300> jsonBuffer; // https://arduinojson.org/v5/assistant/
|
StaticJsonBuffer<500> jsonBuffer; // https://arduinojson.org/v5/assistant/
|
||||||
JsonObject & json = jsonBuffer.parseObject(buf.get());
|
JsonObject & json = jsonBuffer.parseObject(buf.get());
|
||||||
|
|
||||||
const char * value;
|
const char * value;
|
||||||
@@ -808,6 +797,9 @@ bool MyESP::_fs_loadConfig() {
|
|||||||
value = json["mqtt_password"];
|
value = json["mqtt_password"];
|
||||||
_mqtt_password = (value) ? strdup(value) : NULL;
|
_mqtt_password = (value) ? strdup(value) : NULL;
|
||||||
|
|
||||||
|
// callback for custom settings
|
||||||
|
(_fs_callback)(MYESP_FSACTION_LOAD, json);
|
||||||
|
|
||||||
configFile.close();
|
configFile.close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -815,7 +807,7 @@ bool MyESP::_fs_loadConfig() {
|
|||||||
|
|
||||||
// save settings to spiffs
|
// save settings to spiffs
|
||||||
bool MyESP::_fs_saveConfig() {
|
bool MyESP::_fs_saveConfig() {
|
||||||
StaticJsonBuffer<200> jsonBuffer;
|
StaticJsonBuffer<500> jsonBuffer; // https://arduinojson.org/v5/assistant/
|
||||||
JsonObject & json = jsonBuffer.createObject();
|
JsonObject & json = jsonBuffer.createObject();
|
||||||
|
|
||||||
json["wifi_ssid"] = _wifi_ssid;
|
json["wifi_ssid"] = _wifi_ssid;
|
||||||
@@ -824,6 +816,9 @@ bool MyESP::_fs_saveConfig() {
|
|||||||
json["mqtt_username"] = _mqtt_username;
|
json["mqtt_username"] = _mqtt_username;
|
||||||
json["mqtt_password"] = _mqtt_password;
|
json["mqtt_password"] = _mqtt_password;
|
||||||
|
|
||||||
|
// callback for custom settings
|
||||||
|
(_fs_callback)(MYESP_FSACTION_SAVE, json);
|
||||||
|
|
||||||
File configFile = SPIFFS.open("/config.json", "w");
|
File configFile = SPIFFS.open("/config.json", "w");
|
||||||
if (!configFile) {
|
if (!configFile) {
|
||||||
myDebug_P(PSTR("[FS] Failed to open config file for writing"));
|
myDebug_P(PSTR("[FS] Failed to open config file for writing"));
|
||||||
@@ -843,6 +838,8 @@ void MyESP::_fs_setup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _fs_printConfig(); // for debugging
|
||||||
|
|
||||||
// load the config file. if it doesn't exist create it with anything that was specified
|
// load the config file. if it doesn't exist create it with anything that was specified
|
||||||
if (!_fs_loadConfig()) {
|
if (!_fs_loadConfig()) {
|
||||||
_fs_saveConfig();
|
_fs_saveConfig();
|
||||||
|
|||||||
@@ -33,14 +33,7 @@
|
|||||||
#define OTA_PORT 8266 // OTA port
|
#define OTA_PORT 8266 // OTA port
|
||||||
|
|
||||||
// MQTT
|
// MQTT
|
||||||
#define MQTT_HA "/home/ha" // HA specific
|
|
||||||
#define MQTT_HA_NOTIFICATION "home/notification" // HA specific
|
|
||||||
#define MQTT_TOPIC_COMMAND "command" // HA specific
|
|
||||||
#define MQTT_TOPIC_START "start" // HA specific
|
|
||||||
#define MQTT_TOPIC_START_PAYLOAD "start" // HA specific
|
|
||||||
|
|
||||||
#define MQTT_PORT 1883 // MQTT port
|
#define MQTT_PORT 1883 // MQTT port
|
||||||
#define MQTT_QOS 1
|
|
||||||
#define MQTT_RECONNECT_DELAY_MIN 5000 // Try to reconnect in 5 seconds upon disconnection
|
#define MQTT_RECONNECT_DELAY_MIN 5000 // Try to reconnect in 5 seconds upon disconnection
|
||||||
#define MQTT_RECONNECT_DELAY_STEP 5000 // Increase the reconnect delay in 5 seconds after each failed attempt
|
#define MQTT_RECONNECT_DELAY_STEP 5000 // Increase the reconnect delay in 5 seconds after each failed attempt
|
||||||
#define MQTT_RECONNECT_DELAY_MAX 120000 // Set reconnect time to 2 minutes at most
|
#define MQTT_RECONNECT_DELAY_MAX 120000 // Set reconnect time to 2 minutes at most
|
||||||
@@ -69,11 +62,14 @@ typedef struct {
|
|||||||
char description[100];
|
char description[100];
|
||||||
} command_t;
|
} command_t;
|
||||||
|
|
||||||
|
typedef enum { MYESP_FSACTION_SET, MYESP_FSACTION_LIST, MYESP_FSACTION_SAVE, MYESP_FSACTION_LOAD } MYESP_FSACTION;
|
||||||
|
|
||||||
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_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(uint8_t, const char *)> telnetcommand_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<void(uint8_t)> telnet_callback_f;
|
||||||
|
typedef std::function<void(MYESP_FSACTION, JsonObject & json)> fs_callback_f;
|
||||||
|
typedef std::function<bool(MYESP_FSACTION, uint8_t, const char *, const char *)> fs_settings_callback_f;
|
||||||
|
|
||||||
// calculates size of an 2d array at compile time
|
// calculates size of an 2d array at compile time
|
||||||
template <typename T, size_t N>
|
template <typename T, size_t N>
|
||||||
@@ -89,30 +85,35 @@ class MyESP {
|
|||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
void setWIFICallback(void (*callback)());
|
void setWIFICallback(void (*callback)());
|
||||||
|
void setWIFI(char * wifi_ssid, char * wifi_password, wifi_callback_f callback);
|
||||||
// ha
|
|
||||||
void sendHACommand(const char * cmd);
|
|
||||||
void sendHANotification(const char * message);
|
|
||||||
|
|
||||||
// mqtt
|
// mqtt
|
||||||
void mqttSubscribe(const char * topic);
|
void mqttSubscribe(const char * topic);
|
||||||
void mqttUnsubscribe(const char * topic);
|
void mqttUnsubscribe(const char * topic);
|
||||||
void mqttPublish(const char * topic, const char * payload);
|
void mqttPublish(const char * topic, const char * payload);
|
||||||
void setMQTTbase(char * mqttbase);
|
void setMQTT(char * mqtt_host,
|
||||||
void setMQTTCallback(mqtt_callback_f callback);
|
char * mqtt_username,
|
||||||
|
char * mqtt_password,
|
||||||
|
char * mqtt_base,
|
||||||
|
unsigned long mqtt_keepalive,
|
||||||
|
unsigned char mqtt_qos,
|
||||||
|
bool mqtt_retain,
|
||||||
|
char * mqtt_will,
|
||||||
|
mqtt_callback_f callback);
|
||||||
|
|
||||||
// debug & telnet
|
// debug & telnet
|
||||||
void myDebug(const char * format, ...);
|
void myDebug(const char * format, ...);
|
||||||
void myDebug_P(PGM_P format_P, ...);
|
void myDebug_P(PGM_P format_P, ...);
|
||||||
void setTelnetCommands(command_t * cmds, uint8_t count, telnetcommand_callback_f callback);
|
void setTelnet(command_t * cmds, uint8_t count, telnetcommand_callback_f callback_cmd, telnet_callback_f callback);
|
||||||
void setTelnetCallback(telnet_callback_f callback);
|
|
||||||
|
// FS
|
||||||
|
void setSettings(fs_callback_f callback, fs_settings_callback_f fs_settings_callback);
|
||||||
|
|
||||||
// general
|
// general
|
||||||
void end();
|
void end();
|
||||||
void loop();
|
void loop();
|
||||||
void begin(char * app_hostname, char * app_name, char * app_version);
|
void begin(char * app_hostname, char * app_name, char * app_version);
|
||||||
void setConnection(char * wifi_ssid, char * wifi_password, char * mqtt_host, char * mqtt_username, char * mqtt_password);
|
void setBoottime(const char * boottime);
|
||||||
void setBoottime(char * boottime);
|
|
||||||
void resetESP();
|
void resetESP();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -130,14 +131,17 @@ class MyESP {
|
|||||||
char * _mqtt_password;
|
char * _mqtt_password;
|
||||||
char * _boottime;
|
char * _boottime;
|
||||||
bool _suspendOutput;
|
bool _suspendOutput;
|
||||||
char * _mqttbase;
|
char * _mqtt_base;
|
||||||
|
unsigned long _mqtt_keepalive;
|
||||||
|
unsigned char _mqtt_qos;
|
||||||
|
bool _mqtt_retain;
|
||||||
|
char * _mqtt_will;
|
||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
DNSServer dnsServer; // For Access Point (AP) support
|
DNSServer dnsServer; // For Access Point (AP) support
|
||||||
void _wifiCallback(justwifi_messages_t code, char * parameter);
|
void _wifiCallback(justwifi_messages_t code, char * parameter);
|
||||||
void _wifi_setup();
|
void _wifi_setup();
|
||||||
void (*_extern_WIFICallback)();
|
wifi_callback_f _wifi_callback;
|
||||||
bool _extern_WIFICallbackSet;
|
|
||||||
char * _wifi_ssid;
|
char * _wifi_ssid;
|
||||||
char * _wifi_password;
|
char * _wifi_password;
|
||||||
|
|
||||||
@@ -161,7 +165,7 @@ class MyESP {
|
|||||||
void _consoleShowHelp();
|
void _consoleShowHelp();
|
||||||
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
|
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
|
||||||
telnet_callback_f _telnet_callback; // callback for connect/disconnect
|
telnet_callback_f _telnet_callback; // callback for connect/disconnect
|
||||||
void _changeSetting(const char * setting, const char * value);
|
void _changeSetting(uint8_t wc, const char * setting, const char * value);
|
||||||
|
|
||||||
// fs
|
// fs
|
||||||
void _fs_setup();
|
void _fs_setup();
|
||||||
@@ -169,6 +173,8 @@ class MyESP {
|
|||||||
bool _fs_loadConfig();
|
bool _fs_loadConfig();
|
||||||
void _fs_printConfig();
|
void _fs_printConfig();
|
||||||
void _fs_eraseConfig();
|
void _fs_eraseConfig();
|
||||||
|
fs_callback_f _fs_callback;
|
||||||
|
fs_settings_callback_f _fs_settings_callback;
|
||||||
|
|
||||||
// general
|
// general
|
||||||
char * _app_hostname;
|
char * _app_hostname;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ env_default = d1_mini
|
|||||||
[common]
|
[common]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
flash_mode = dout
|
flash_mode = dout
|
||||||
; optional flags are -DNO_LED -DDEBUG_SUPPORT
|
; optional flags are -DDEBUG_SUPPORT
|
||||||
build_flags = -g -w
|
build_flags = -g -w
|
||||||
lib_deps =
|
lib_deps =
|
||||||
CRC32
|
CRC32
|
||||||
|
|||||||
224
src/ems-esp.ino
224
src/ems-esp.ino
@@ -28,7 +28,7 @@
|
|||||||
#define myDebug_P(...) myESP.myDebug_P(__VA_ARGS__)
|
#define myDebug_P(...) myESP.myDebug_P(__VA_ARGS__)
|
||||||
|
|
||||||
// timers, all values are in seconds
|
// timers, all values are in seconds
|
||||||
#define PUBLISHVALUES_TIME 120 // every 2 minutes post HA values
|
#define PUBLISHVALUES_TIME 120 // every 2 minutes post MQTT values
|
||||||
Ticker publishValuesTimer;
|
Ticker publishValuesTimer;
|
||||||
|
|
||||||
#define SYSTEMCHECK_TIME 10 // every 10 seconds check if Boiler is online and execute other requests
|
#define SYSTEMCHECK_TIME 10 // every 10 seconds check if Boiler is online and execute other requests
|
||||||
@@ -47,8 +47,6 @@ uint8_t scanThermostat_count = 0;
|
|||||||
|
|
||||||
Ticker showerColdShotStopTimer;
|
Ticker showerColdShotStopTimer;
|
||||||
|
|
||||||
static unsigned long timestamp; // for internal timings, via millis()
|
|
||||||
|
|
||||||
// thermostat
|
// thermostat
|
||||||
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values
|
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values
|
||||||
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes
|
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes
|
||||||
@@ -66,8 +64,8 @@ static unsigned long timestamp; // for internal timings, via millis()
|
|||||||
#define TOPIC_SHOWERTIME "showertime" // for sending shower time results
|
#define TOPIC_SHOWERTIME "showertime" // for sending shower time results
|
||||||
#define TOPIC_SHOWER_TIMER "shower_timer" // toggle switch for enabling the shower logic
|
#define TOPIC_SHOWER_TIMER "shower_timer" // toggle switch for enabling the shower logic
|
||||||
#define TOPIC_SHOWER_ALERT "shower_alert" // toggle switch for enabling the shower alarm logic
|
#define TOPIC_SHOWER_ALERT "shower_alert" // toggle switch for enabling the shower alarm logic
|
||||||
#define TOPIC_SHOWER_COLDSHOT "shower_coldshot" // used to trigger a coldshot from HA publish
|
#define TOPIC_SHOWER_COLDSHOT "shower_coldshot" // used to trigger a coldshot from an MQTT command
|
||||||
#define SHOWER_ALARM "shower_alarm" // for notifying HA that shower time has reached its limit
|
#define SHOWER_ALARM "shower_alarm" // for notifying that shower time has reached its limit
|
||||||
|
|
||||||
// if using the shower timer, change these settings
|
// if using the shower timer, change these settings
|
||||||
#define SHOWER_PAUSE_TIME 15000 // in ms. 15 seconds, max time if water is switched off & on during a shower
|
#define SHOWER_PAUSE_TIME 15000 // in ms. 15 seconds, max time if water is switched off & on during a shower
|
||||||
@@ -78,7 +76,9 @@ static unsigned long timestamp; // for internal timings, via millis()
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
bool shower_timer; // true if we want to report back on shower times
|
bool shower_timer; // true if we want to report back on shower times
|
||||||
bool shower_alert; // true if we want the alert of cold water
|
bool shower_alert; // true if we want the alert of cold water
|
||||||
} _Boiler_Status;
|
bool led_enabled; // LED on/off
|
||||||
|
unsigned long timestamp; // for internal timings, via millis()
|
||||||
|
} _EMSESP_Status;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool showerOn;
|
bool showerOn;
|
||||||
@@ -86,7 +86,7 @@ typedef struct {
|
|||||||
unsigned long timerPause; // ms
|
unsigned long timerPause; // ms
|
||||||
unsigned long duration; // ms
|
unsigned long duration; // ms
|
||||||
bool doingColdShot; // true if we've just sent a jolt of cold water
|
bool doingColdShot; // true if we've just sent a jolt of cold water
|
||||||
} _Boiler_Shower;
|
} _EMSESP_Shower;
|
||||||
|
|
||||||
command_t PROGMEM project_cmds[] = {
|
command_t PROGMEM project_cmds[] = {
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ command_t PROGMEM project_cmds[] = {
|
|||||||
{"thermostat read <hex type ID>", "send read request to thermostat"},
|
{"thermostat read <hex type ID>", "send read request to thermostat"},
|
||||||
{"thermostat temp <degrees>", "set current thermostat temperature"},
|
{"thermostat temp <degrees>", "set current thermostat temperature"},
|
||||||
{"thermostat mode <mode>", "set mode (0=low/night, 1=manual/day, 2=auto)"},
|
{"thermostat mode <mode>", "set mode (0=low/night, 1=manual/day, 2=auto)"},
|
||||||
{"thermostat scan <hex type ID>", "do a deep scan of all thermostat message types, starting at n"},
|
{"thermostat scan <hex type ID>", "do a force read on all type IDs starting at n"},
|
||||||
{"boiler read <hex type ID>", "send read request to boiler"},
|
{"boiler read <hex type ID>", "send read request to boiler"},
|
||||||
{"boiler wwtemp <degrees>", "set warm water temperature"},
|
{"boiler wwtemp <degrees>", "set warm water temperature"},
|
||||||
{"boiler tapwater <on | off>", "set warm tap water on or off"}
|
{"boiler tapwater <on | off>", "set warm tap water on or off"}
|
||||||
@@ -111,8 +111,8 @@ command_t PROGMEM project_cmds[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// store for overall system status
|
// store for overall system status
|
||||||
_Boiler_Status Boiler_Status;
|
_EMSESP_Status EMSESP_Status;
|
||||||
_Boiler_Shower Boiler_Shower;
|
_EMSESP_Shower EMSESP_Shower;
|
||||||
|
|
||||||
// logging messages with fixed strings
|
// logging messages with fixed strings
|
||||||
void myDebugLog(const char * s) {
|
void myDebugLog(const char * s) {
|
||||||
@@ -264,6 +264,8 @@ void showInfo() {
|
|||||||
myDebug(" System logging set to None");
|
myDebug(" System logging set to None");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
myDebug(" LED is %s", EMSESP_Status.led_enabled ? "on" : "off");
|
||||||
|
|
||||||
myDebug(" # EMS type handlers: %d", ems_getEmsTypesCount());
|
myDebug(" # EMS type handlers: %d", ems_getEmsTypesCount());
|
||||||
|
|
||||||
myDebug(" Thermostat is %s, Boiler is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s",
|
myDebug(" Thermostat is %s, Boiler is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s",
|
||||||
@@ -271,8 +273,8 @@ void showInfo() {
|
|||||||
(ems_getBoilerEnabled() ? "enabled" : "disabled"),
|
(ems_getBoilerEnabled() ? "enabled" : "disabled"),
|
||||||
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
||||||
((EMS_Sys_Status.emsTxEnabled) ? "enabled" : "disabled"),
|
((EMS_Sys_Status.emsTxEnabled) ? "enabled" : "disabled"),
|
||||||
((Boiler_Status.shower_timer) ? "enabled" : "disabled"),
|
((EMSESP_Status.shower_timer) ? "enabled" : "disabled"),
|
||||||
((Boiler_Status.shower_alert) ? "enabled" : "disabled"));
|
((EMSESP_Status.shower_alert) ? "enabled" : "disabled"));
|
||||||
|
|
||||||
myDebug(" EMS Bus Stats: Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
|
myDebug(" EMS Bus Stats: Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
|
||||||
(ems_getBusConnected() ? "yes" : "no"),
|
(ems_getBusConnected() ? "yes" : "no"),
|
||||||
@@ -382,13 +384,13 @@ void showInfo() {
|
|||||||
myDebug(""); // newline
|
myDebug(""); // newline
|
||||||
|
|
||||||
// show the Shower Info
|
// show the Shower Info
|
||||||
if (Boiler_Status.shower_timer) {
|
if (EMSESP_Status.shower_timer) {
|
||||||
myDebug("%s Shower stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
myDebug("%s Shower stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||||
myDebug(" Shower Timer is %s", (Boiler_Shower.showerOn ? "active" : "off"));
|
myDebug(" Shower Timer is %s", (EMSESP_Shower.showerOn ? "active" : "off"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// send values to HA via MQTT
|
// send values via MQTT
|
||||||
// a json object is created for the boiler and one for the thermostat
|
// a json object is created for the boiler and one for the thermostat
|
||||||
// CRC check is done to see if there are changes in the values since the last send to avoid too much wifi traffic
|
// CRC check is done to see if there are changes in the values since the last send to avoid too much wifi traffic
|
||||||
void publishValues(bool force) {
|
void publishValues(bool force) {
|
||||||
@@ -506,14 +508,14 @@ void publishValues(bool force) {
|
|||||||
// sets the shower timer on/off
|
// sets the shower timer on/off
|
||||||
void set_showerTimer() {
|
void set_showerTimer() {
|
||||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||||
myDebug("Shower timer has been set to %s", Boiler_Status.shower_timer ? "enabled" : "disabled");
|
myDebug("Shower timer has been set to %s", EMSESP_Status.shower_timer ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets the shower alert on/off
|
// sets the shower alert on/off
|
||||||
void set_showerAlert() {
|
void set_showerAlert() {
|
||||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||||
myDebug("Shower alert has been set to %s", Boiler_Status.shower_alert ? "enabled" : "disabled");
|
myDebug("Shower alert has been set to %s", EMSESP_Status.shower_alert ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,6 +555,43 @@ void startThermostatScan(uint8_t start) {
|
|||||||
scanThermostat.attach(SCANTHERMOSTAT_TIME, do_scanThermostat);
|
scanThermostat.attach(SCANTHERMOSTAT_TIME, do_scanThermostat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// callback for loading/saving settings to the file system (SPIFFS)
|
||||||
|
void FSCallback(MYESP_FSACTION action, JsonObject & json) {
|
||||||
|
if (action == MYESP_FSACTION_LOAD) {
|
||||||
|
EMSESP_Status.led_enabled = (bool)json["led"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == MYESP_FSACTION_SAVE) {
|
||||||
|
json["led"] = EMSESP_Status.led_enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback for custom settings
|
||||||
|
// wc is number of arguments after the 'set' command
|
||||||
|
bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) {
|
||||||
|
if (action == MYESP_FSACTION_SET) {
|
||||||
|
if ((strcmp(setting, "led") == 0) && (wc == 2)) {
|
||||||
|
if (strcmp(value, "on") == 0) {
|
||||||
|
EMSESP_Status.led_enabled = true;
|
||||||
|
} else if (strcmp(value, "off") == 0) {
|
||||||
|
EMSESP_Status.led_enabled = false;
|
||||||
|
// let's make sure LED is really off
|
||||||
|
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off
|
||||||
|
} else {
|
||||||
|
// unknown command
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == MYESP_FSACTION_LIST) {
|
||||||
|
myDebug(" led=%s", EMSESP_Status.led_enabled ? "on" : "off");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// call back when a telnet client connects or disconnects
|
// call back when a telnet client connects or disconnects
|
||||||
// we set the logging here
|
// we set the logging here
|
||||||
void TelnetCallback(uint8_t event) {
|
void TelnetCallback(uint8_t event) {
|
||||||
@@ -612,12 +651,12 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
|
|||||||
if (wc == 2) {
|
if (wc == 2) {
|
||||||
char * second_cmd = _readWord();
|
char * second_cmd = _readWord();
|
||||||
if (strcmp(second_cmd, "timer") == 0) {
|
if (strcmp(second_cmd, "timer") == 0) {
|
||||||
Boiler_Status.shower_timer = !Boiler_Status.shower_timer;
|
EMSESP_Status.shower_timer = !EMSESP_Status.shower_timer;
|
||||||
myESP.mqttPublish(TOPIC_SHOWER_TIMER, Boiler_Status.shower_timer ? "1" : "0");
|
myESP.mqttPublish(TOPIC_SHOWER_TIMER, EMSESP_Status.shower_timer ? "1" : "0");
|
||||||
ok = true;
|
ok = true;
|
||||||
} else if (strcmp(second_cmd, "alert") == 0) {
|
} else if (strcmp(second_cmd, "alert") == 0) {
|
||||||
Boiler_Status.shower_alert = !Boiler_Status.shower_alert;
|
EMSESP_Status.shower_alert = !EMSESP_Status.shower_alert;
|
||||||
myESP.mqttPublish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
myESP.mqttPublish(TOPIC_SHOWER_ALERT, EMSESP_Status.shower_alert ? "1" : "0");
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -711,19 +750,31 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
|||||||
myESP.mqttSubscribe(TOPIC_SHOWER_ALERT);
|
myESP.mqttSubscribe(TOPIC_SHOWER_ALERT);
|
||||||
myESP.mqttSubscribe(TOPIC_SHOWER_COLDSHOT);
|
myESP.mqttSubscribe(TOPIC_SHOWER_COLDSHOT);
|
||||||
|
|
||||||
// publish to HA the status of the Shower parameters
|
// subscribe to a start message and send the first publish
|
||||||
myESP.mqttPublish(TOPIC_SHOWER_TIMER, Boiler_Status.shower_timer ? "1" : "0");
|
myESP.mqttSubscribe(MQTT_TOPIC_START);
|
||||||
myESP.mqttPublish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
myESP.mqttPublish(MQTT_TOPIC_START, MQTT_TOPIC_START_PAYLOAD);
|
||||||
|
|
||||||
|
// publish the status of the Shower parameters
|
||||||
|
myESP.mqttPublish(TOPIC_SHOWER_TIMER, EMSESP_Status.shower_timer ? "1" : "0");
|
||||||
|
myESP.mqttPublish(TOPIC_SHOWER_ALERT, EMSESP_Status.shower_alert ? "1" : "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle incoming MQTT publish events
|
||||||
if (type == MQTT_MESSAGE_EVENT) {
|
if (type == MQTT_MESSAGE_EVENT) {
|
||||||
|
// handle response from a start message
|
||||||
|
// for HA, it gets sent the bootime
|
||||||
|
if (strcmp(topic, MQTT_TOPIC_START) == 0) {
|
||||||
|
myDebug("Received boottime: %s", message);
|
||||||
|
myESP.setBoottime(message);
|
||||||
|
}
|
||||||
|
|
||||||
// thermostat temp changes
|
// thermostat temp changes
|
||||||
if (strcmp(topic, TOPIC_THERMOSTAT_CMD_TEMP) == 0) {
|
if (strcmp(topic, TOPIC_THERMOSTAT_CMD_TEMP) == 0) {
|
||||||
float f = strtof((char *)message, 0);
|
float f = strtof((char *)message, 0);
|
||||||
char s[10] = {0};
|
char s[10] = {0};
|
||||||
myDebug("MQTT topic: thermostat temperature value %s", _float_to_char(s, f));
|
myDebug("MQTT topic: thermostat temperature value %s", _float_to_char(s, f));
|
||||||
ems_setThermostatTemp(f);
|
ems_setThermostatTemp(f);
|
||||||
publishValues(true); // publish back so HA is immediately updated
|
publishValues(true); // publish back immediately
|
||||||
}
|
}
|
||||||
|
|
||||||
// thermostat mode changes
|
// thermostat mode changes
|
||||||
@@ -739,9 +790,9 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
|||||||
// shower timer
|
// shower timer
|
||||||
if (strcmp(topic, TOPIC_SHOWER_TIMER) == 0) {
|
if (strcmp(topic, TOPIC_SHOWER_TIMER) == 0) {
|
||||||
if (message[0] == '1') {
|
if (message[0] == '1') {
|
||||||
Boiler_Status.shower_timer = true;
|
EMSESP_Status.shower_timer = true;
|
||||||
} else if (message[0] == '0') {
|
} else if (message[0] == '0') {
|
||||||
Boiler_Status.shower_timer = false;
|
EMSESP_Status.shower_timer = false;
|
||||||
}
|
}
|
||||||
set_showerTimer();
|
set_showerTimer();
|
||||||
}
|
}
|
||||||
@@ -749,9 +800,9 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
|||||||
// shower alert
|
// shower alert
|
||||||
if (strcmp(topic, TOPIC_SHOWER_ALERT) == 0) {
|
if (strcmp(topic, TOPIC_SHOWER_ALERT) == 0) {
|
||||||
if (message[0] == '1') {
|
if (message[0] == '1') {
|
||||||
Boiler_Status.shower_alert = true;
|
EMSESP_Status.shower_alert = true;
|
||||||
} else if (message[0] == '0') {
|
} else if (message[0] == '0') {
|
||||||
Boiler_Status.shower_alert = false;
|
EMSESP_Status.shower_alert = false;
|
||||||
}
|
}
|
||||||
set_showerAlert();
|
set_showerAlert();
|
||||||
}
|
}
|
||||||
@@ -780,14 +831,19 @@ void WIFICallback() {
|
|||||||
ems_setModels();
|
ems_setModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the boiler settings for the shower settings
|
// Initialize the boiler settings and shower settings
|
||||||
void initShower() {
|
void initEMSESP() {
|
||||||
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
|
// general settings
|
||||||
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
|
EMSESP_Status.shower_timer = BOILER_SHOWER_TIMER;
|
||||||
Boiler_Shower.timerStart = 0;
|
EMSESP_Status.shower_alert = BOILER_SHOWER_ALERT;
|
||||||
Boiler_Shower.timerPause = 0;
|
EMSESP_Status.led_enabled = false;
|
||||||
Boiler_Shower.duration = 0;
|
EMSESP_Status.timestamp = millis();
|
||||||
Boiler_Shower.doingColdShot = false;
|
|
||||||
|
// shower settings
|
||||||
|
EMSESP_Shower.timerStart = 0;
|
||||||
|
EMSESP_Shower.timerPause = 0;
|
||||||
|
EMSESP_Shower.duration = 0;
|
||||||
|
EMSESP_Shower.doingColdShot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// call PublishValues without forcing, so using CRC to see if we really need to publish
|
// call PublishValues without forcing, so using CRC to see if we really need to publish
|
||||||
@@ -799,16 +855,16 @@ void do_publishValues() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// callback to light up the LED, called via Ticker every second
|
// callback to light up the LED, called via Ticker every second
|
||||||
|
// fast way is to use WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + (state ? 4 : 8), (1 << BOILER_LED)); // 4 is on, 8 is off
|
||||||
void do_ledcheck() {
|
void do_ledcheck() {
|
||||||
#ifndef NO_LED
|
if (EMSESP_Status.led_enabled) {
|
||||||
if (ems_getBusConnected()) {
|
if (ems_getBusConnected()) {
|
||||||
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
|
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
|
||||||
} else {
|
} else {
|
||||||
int state = digitalRead(BOILER_LED);
|
int state = digitalRead(BOILER_LED);
|
||||||
digitalWrite(BOILER_LED, !state);
|
digitalWrite(BOILER_LED, !state);
|
||||||
}
|
}
|
||||||
//WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + (state ? 4 : 8), (1 << BOILER_LED)); // 4 is on, 8 is off
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thermostat scan
|
// Thermostat scan
|
||||||
@@ -841,17 +897,17 @@ void do_regularUpdates() {
|
|||||||
void _showerColdShotStart() {
|
void _showerColdShotStart() {
|
||||||
myDebugLog("[Shower] doing a shot of cold water");
|
myDebugLog("[Shower] doing a shot of cold water");
|
||||||
ems_setWarmTapWaterActivated(false);
|
ems_setWarmTapWaterActivated(false);
|
||||||
Boiler_Shower.doingColdShot = true;
|
EMSESP_Shower.doingColdShot = true;
|
||||||
// start the timer for n seconds which will reset the water back to hot
|
// start the timer for n seconds which will reset the water back to hot
|
||||||
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
|
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn back on the hot water for the shower
|
// turn back on the hot water for the shower
|
||||||
void _showerColdShotStop() {
|
void _showerColdShotStop() {
|
||||||
if (Boiler_Shower.doingColdShot) {
|
if (EMSESP_Shower.doingColdShot) {
|
||||||
myDebugLog("[Shower] finished shot of cold. hot water back on");
|
myDebugLog("[Shower] finished shot of cold. hot water back on");
|
||||||
ems_setWarmTapWaterActivated(true);
|
ems_setWarmTapWaterActivated(true);
|
||||||
Boiler_Shower.doingColdShot = false;
|
EMSESP_Shower.doingColdShot = false;
|
||||||
showerColdShotStopTimer.detach(); // disable the timer
|
showerColdShotStopTimer.detach(); // disable the timer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -861,62 +917,61 @@ void _showerColdShotStop() {
|
|||||||
*/
|
*/
|
||||||
void showerCheck() {
|
void showerCheck() {
|
||||||
// if already in cold mode, ignore all this logic until we're out of the cold blast
|
// if already in cold mode, ignore all this logic until we're out of the cold blast
|
||||||
if (!Boiler_Shower.doingColdShot) {
|
if (!EMSESP_Shower.doingColdShot) {
|
||||||
// is the hot water running?
|
// is the hot water running?
|
||||||
if (EMS_Boiler.tapwaterActive) {
|
if (EMS_Boiler.tapwaterActive) {
|
||||||
// if heater was previously off, start the timer
|
// if heater was previously off, start the timer
|
||||||
if (Boiler_Shower.timerStart == 0) {
|
if (EMSESP_Shower.timerStart == 0) {
|
||||||
// hot water just started...
|
// hot water just started...
|
||||||
Boiler_Shower.timerStart = timestamp;
|
EMSESP_Shower.timerStart = EMSESP_Status.timestamp;
|
||||||
Boiler_Shower.timerPause = 0; // remove any last pauses
|
EMSESP_Shower.timerPause = 0; // remove any last pauses
|
||||||
Boiler_Shower.doingColdShot = false;
|
EMSESP_Shower.doingColdShot = false;
|
||||||
Boiler_Shower.duration = 0;
|
EMSESP_Shower.duration = 0;
|
||||||
Boiler_Shower.showerOn = false;
|
EMSESP_Shower.showerOn = false;
|
||||||
} else {
|
} else {
|
||||||
// hot water has been on for a while
|
// hot water has been on for a while
|
||||||
// first check to see if hot water has been on long enough to be recognized as a Shower/Bath
|
// first check to see if hot water has been on long enough to be recognized as a Shower/Bath
|
||||||
if (!Boiler_Shower.showerOn && (timestamp - Boiler_Shower.timerStart) > SHOWER_MIN_DURATION) {
|
if (!EMSESP_Shower.showerOn && (EMSESP_Status.timestamp - EMSESP_Shower.timerStart) > SHOWER_MIN_DURATION) {
|
||||||
Boiler_Shower.showerOn = true;
|
EMSESP_Shower.showerOn = true;
|
||||||
myDebugLog("[Shower] hot water still running, starting shower timer");
|
myDebugLog("[Shower] hot water still running, starting shower timer");
|
||||||
}
|
}
|
||||||
// check if the shower has been on too long
|
// check if the shower has been on too long
|
||||||
else if ((((timestamp - Boiler_Shower.timerStart) > SHOWER_MAX_DURATION) && !Boiler_Shower.doingColdShot)
|
else if ((((EMSESP_Status.timestamp - EMSESP_Shower.timerStart) > SHOWER_MAX_DURATION) && !EMSESP_Shower.doingColdShot)
|
||||||
&& Boiler_Status.shower_alert) {
|
&& EMSESP_Status.shower_alert) {
|
||||||
myESP.sendHACommand(SHOWER_ALARM);
|
|
||||||
myDebugLog("[Shower] exceeded max shower time");
|
myDebugLog("[Shower] exceeded max shower time");
|
||||||
_showerColdShotStart();
|
_showerColdShotStart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // hot water is off
|
} else { // hot water is off
|
||||||
// if it just turned off, record the time as it could be a short pause
|
// if it just turned off, record the time as it could be a short pause
|
||||||
if ((Boiler_Shower.timerStart != 0) && (Boiler_Shower.timerPause == 0)) {
|
if ((EMSESP_Shower.timerStart != 0) && (EMSESP_Shower.timerPause == 0)) {
|
||||||
Boiler_Shower.timerPause = timestamp;
|
EMSESP_Shower.timerPause = EMSESP_Status.timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if shower has been off for longer than the wait time
|
// if shower has been off for longer than the wait time
|
||||||
if ((Boiler_Shower.timerPause != 0) && ((timestamp - Boiler_Shower.timerPause) > SHOWER_PAUSE_TIME)) {
|
if ((EMSESP_Shower.timerPause != 0) && ((EMSESP_Status.timestamp - EMSESP_Shower.timerPause) > SHOWER_PAUSE_TIME)) {
|
||||||
// it is over the wait period, so assume that the shower has finished and calculate the total time and publish
|
// it is over the wait period, so assume that the shower has finished and calculate the total time and publish
|
||||||
// because its unsigned long, can't have negative so check if length is less than OFFSET_TIME
|
// because its unsigned long, can't have negative so check if length is less than OFFSET_TIME
|
||||||
if ((Boiler_Shower.timerPause - Boiler_Shower.timerStart) > SHOWER_OFFSET_TIME) {
|
if ((EMSESP_Shower.timerPause - EMSESP_Shower.timerStart) > SHOWER_OFFSET_TIME) {
|
||||||
Boiler_Shower.duration = (Boiler_Shower.timerPause - Boiler_Shower.timerStart - SHOWER_OFFSET_TIME);
|
EMSESP_Shower.duration = (EMSESP_Shower.timerPause - EMSESP_Shower.timerStart - SHOWER_OFFSET_TIME);
|
||||||
if (Boiler_Shower.duration > SHOWER_MIN_DURATION) {
|
if (EMSESP_Shower.duration > SHOWER_MIN_DURATION) {
|
||||||
char s[50] = {0};
|
char s[50] = {0};
|
||||||
char buffer[16] = {0};
|
char buffer[16] = {0};
|
||||||
strlcpy(s, itoa((uint8_t)((Boiler_Shower.duration / (1000 * 60)) % 60), buffer, 10), sizeof(s));
|
strlcpy(s, itoa((uint8_t)((EMSESP_Shower.duration / (1000 * 60)) % 60), buffer, 10), sizeof(s));
|
||||||
strlcat(s, " minutes and ", sizeof(s));
|
strlcat(s, " minutes and ", sizeof(s));
|
||||||
strlcat(s, itoa((uint8_t)((Boiler_Shower.duration / 1000) % 60), buffer, 10), sizeof(s));
|
strlcat(s, itoa((uint8_t)((EMSESP_Shower.duration / 1000) % 60), buffer, 10), sizeof(s));
|
||||||
strlcat(s, " seconds", sizeof(s));
|
strlcat(s, " seconds", sizeof(s));
|
||||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||||
myDebug("[Shower] finished with duration %s", s);
|
myDebug("[Shower] finished with duration %s", s);
|
||||||
}
|
}
|
||||||
myESP.mqttPublish(TOPIC_SHOWERTIME, s); // publish to HA
|
myESP.mqttPublish(TOPIC_SHOWERTIME, s); // publish to MQTT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset everything
|
// reset everything
|
||||||
Boiler_Shower.timerStart = 0;
|
EMSESP_Shower.timerStart = 0;
|
||||||
Boiler_Shower.timerPause = 0;
|
EMSESP_Shower.timerPause = 0;
|
||||||
Boiler_Shower.showerOn = false;
|
EMSESP_Shower.showerOn = false;
|
||||||
_showerColdShotStop(); // turn hot water back on in case its off
|
_showerColdShotStop(); // turn hot water back on in case its off
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -927,35 +982,28 @@ void showerCheck() {
|
|||||||
// SETUP
|
// SETUP
|
||||||
//
|
//
|
||||||
void setup() {
|
void setup() {
|
||||||
#ifndef NO_LED
|
|
||||||
// set pin for LED
|
|
||||||
pinMode(BOILER_LED, OUTPUT);
|
|
||||||
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? HIGH : LOW); // light on. For onboard high=off
|
|
||||||
ledcheckTimer.attach(LEDCHECK_TIME, do_ledcheck); // blink heartbeat LED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef DEBUG_SUPPORT
|
#ifndef DEBUG_SUPPORT
|
||||||
// Timers using Ticker library
|
// Timers using Ticker library
|
||||||
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post HA values
|
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values
|
||||||
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
|
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
|
||||||
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
|
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
||||||
myESP.setTelnetCommands(project_cmds, ArraySize(project_cmds), TelnetCommandCallback); // set up Telnet commands
|
myESP.setTelnet(project_cmds, ArraySize(project_cmds), TelnetCommandCallback, TelnetCallback); // set up Telnet commands
|
||||||
myESP.setConnection(WIFI_SSID, WIFI_PASSWORD, MQTT_IP, MQTT_USER, MQTT_PASS); // optional
|
myESP.setWIFI(WIFI_SSID, WIFI_PASSWORD, WIFICallback);
|
||||||
myESP.begin(APP_HOSTNAME, APP_NAME, APP_VERSION);
|
myESP.setMQTT(MQTT_HOST, MQTT_USER, MQTT_PASS, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL, MQTTCallback);
|
||||||
myESP.setMQTTbase(MQTT_BASE);
|
myESP.setSettings(FSCallback, SettingsCallback);
|
||||||
|
|
||||||
// callbacks
|
myESP.begin(APP_HOSTNAME, APP_NAME, APP_VERSION); // start it all up
|
||||||
myESP.setWIFICallback(WIFICallback);
|
|
||||||
myESP.setMQTTCallback(MQTTCallback);
|
|
||||||
myESP.setTelnetCallback(TelnetCallback);
|
|
||||||
|
|
||||||
// init Shower specific parameters
|
// init our own parameters
|
||||||
initShower();
|
initEMSESP();
|
||||||
|
|
||||||
ems_setLogging(EMS_SYS_LOGGING_NONE); // set default logging to none
|
// set pin for LED
|
||||||
|
pinMode(BOILER_LED, OUTPUT);
|
||||||
|
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off
|
||||||
|
ledcheckTimer.attach(LEDCHECK_TIME, do_ledcheck); // blink heartbeat LED
|
||||||
|
|
||||||
// init the EMS bus
|
// init the EMS bus
|
||||||
// call ems.cpp's init function to set all the internal params
|
// call ems.cpp's init function to set all the internal params
|
||||||
@@ -966,7 +1014,7 @@ void setup() {
|
|||||||
// Main loop
|
// Main loop
|
||||||
//
|
//
|
||||||
void loop() {
|
void loop() {
|
||||||
timestamp = millis();
|
EMSESP_Status.timestamp = millis();
|
||||||
|
|
||||||
// the main loop
|
// the main loop
|
||||||
myESP.loop();
|
myESP.loop();
|
||||||
@@ -979,7 +1027,7 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do shower logic, if enabled
|
// do shower logic, if enabled
|
||||||
if (Boiler_Status.shower_timer) {
|
if (EMSESP_Status.shower_timer) {
|
||||||
showerCheck();
|
showerCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,6 +224,9 @@ void ems_init(uint8_t boiler_modelid, uint8_t thermostat_modelid) {
|
|||||||
_ems_PollCount = 0;
|
_ems_PollCount = 0;
|
||||||
_emsTxRetryCount = 0;
|
_emsTxRetryCount = 0;
|
||||||
_last_TxTelgramCRC = 0;
|
_last_TxTelgramCRC = 0;
|
||||||
|
|
||||||
|
// default logging is non
|
||||||
|
ems_setLogging(EMS_SYS_LOGGING_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters and Setters for parameters
|
// Getters and Setters for parameters
|
||||||
|
|||||||
@@ -15,12 +15,18 @@
|
|||||||
// as these values are stored in SPIFFs for persisted
|
// as these values are stored in SPIFFs for persisted
|
||||||
#define WIFI_SSID NULL
|
#define WIFI_SSID NULL
|
||||||
#define WIFI_PASSWORD NULL
|
#define WIFI_PASSWORD NULL
|
||||||
#define MQTT_IP NULL
|
#define MQTT_HOST NULL
|
||||||
#define MQTT_USER NULL
|
#define MQTT_USER NULL
|
||||||
#define MQTT_PASS NULL
|
#define MQTT_PASS NULL
|
||||||
|
|
||||||
// All MQTT topics are prefixed with the following string
|
// All MQTT topics are prefixed with the following string
|
||||||
#define MQTT_BASE "home"
|
#define MQTT_BASE "home"
|
||||||
|
#define MQTT_TOPIC_START "start"
|
||||||
|
#define MQTT_TOPIC_START_PAYLOAD "start"
|
||||||
|
#define MQTT_WILL "status" // for last will & testament
|
||||||
|
#define MQTT_RETAIN true
|
||||||
|
#define MQTT_KEEPALIVE 300
|
||||||
|
#define MQTT_QOS 1
|
||||||
|
|
||||||
// default values for shower logic on/off
|
// default values for shower logic on/off
|
||||||
#define BOILER_SHOWER_TIMER 1 // enable (1) to monitor shower time
|
#define BOILER_SHOWER_TIMER 1 // enable (1) to monitor shower time
|
||||||
@@ -29,7 +35,7 @@
|
|||||||
|
|
||||||
// Set LED pin used for showing ems bus connection status. Solid is connected, Flashing is error
|
// Set LED pin used for showing ems bus connection status. Solid is connected, Flashing is error
|
||||||
// can be either the onboard LED on the ESP8266 or external via an external pull-up LED (e.g. D1)
|
// can be either the onboard LED on the ESP8266 or external via an external pull-up LED (e.g. D1)
|
||||||
// note: can be disabled completely using -DNO_LED build flag in platformio.ini
|
// can be enabled and disabled via the 'set led' command
|
||||||
#define BOILER_LED LED_BUILTIN // LED_BULLETIN for onboard LED
|
#define BOILER_LED LED_BUILTIN // LED_BULLETIN for onboard LED
|
||||||
|
|
||||||
// set this if using an external temperature sensor like a DS18B20
|
// set this if using an external temperature sensor like a DS18B20
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define APP_NAME "EMS-ESP Interface"
|
#define APP_NAME "EMS-ESP Interface"
|
||||||
#define APP_VERSION "1.3.0"
|
#define APP_VERSION "1.3.1"
|
||||||
#define APP_HOSTNAME "ems-esp"
|
#define APP_HOSTNAME "ems-esp"
|
||||||
|
|||||||
Reference in New Issue
Block a user