mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
version 1.3.0
This commit is contained in:
990
src/ems-esp.ino
Normal file
990
src/ems-esp.ino
Normal file
@@ -0,0 +1,990 @@
|
||||
/*
|
||||
* EMS-ESP
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
* See ChangeLog.md for history
|
||||
* See README.md for Acknowledgments
|
||||
*/
|
||||
|
||||
// local libraries
|
||||
#include "ems.h"
|
||||
#include "ems_devices.h"
|
||||
#include "emsuart.h"
|
||||
#include "my_config.h"
|
||||
#include "version.h"
|
||||
|
||||
// my libraries
|
||||
#include <MyESP.h>
|
||||
|
||||
// public libraries
|
||||
#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson
|
||||
#include <CRC32.h> // https://github.com/bakercp/CRC32
|
||||
|
||||
// standard arduino libs
|
||||
#include <Ticker.h> // https://github.com/esp8266/Arduino/tree/master/libraries/Ticker
|
||||
|
||||
#define myDebug(...) myESP.myDebug(__VA_ARGS__)
|
||||
#define myDebug_P(...) myESP.myDebug_P(__VA_ARGS__)
|
||||
|
||||
// timers, all values are in seconds
|
||||
#define PUBLISHVALUES_TIME 120 // every 2 minutes post HA values
|
||||
Ticker publishValuesTimer;
|
||||
|
||||
#define SYSTEMCHECK_TIME 10 // every 10 seconds check if Boiler is online and execute other requests
|
||||
Ticker systemCheckTimer;
|
||||
|
||||
#define REGULARUPDATES_TIME 60 // every minute a call is made to fetch data from EMS devices manually
|
||||
Ticker regularUpdatesTimer;
|
||||
|
||||
#define LEDCHECK_TIME 1 // every second blink heartbeat LED
|
||||
Ticker ledcheckTimer;
|
||||
|
||||
// thermostat scan - for debugging
|
||||
Ticker scanThermostat;
|
||||
#define SCANTHERMOSTAT_TIME 1
|
||||
uint8_t scanThermostat_count = 0;
|
||||
|
||||
Ticker showerColdShotStopTimer;
|
||||
|
||||
static unsigned long timestamp; // for internal timings, via millis()
|
||||
|
||||
// thermostat
|
||||
#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_MODE "thermostat_cmd_mode" // for received thermostat mode changes
|
||||
#define THERMOSTAT_CURRTEMP "thermostat_currtemp" // current temperature
|
||||
#define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature
|
||||
#define THERMOSTAT_MODE "thermostat_mode" // mode
|
||||
|
||||
// boiler
|
||||
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values
|
||||
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
|
||||
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
|
||||
|
||||
// shower time
|
||||
#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_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 SHOWER_ALARM "shower_alarm" // for notifying HA that shower time has reached its limit
|
||||
|
||||
// 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_MIN_DURATION 120000 // in ms. 2 minutes, before recognizing its a shower
|
||||
#define SHOWER_OFFSET_TIME 5000 // in ms. 5 seconds grace time, to calibrate actual time under the shower
|
||||
#define SHOWER_COLDSHOT_DURATION 10 // in seconds. 10 seconds for cold water before turning back hot water
|
||||
|
||||
typedef struct {
|
||||
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
|
||||
} _Boiler_Status;
|
||||
|
||||
typedef struct {
|
||||
bool showerOn;
|
||||
unsigned long timerStart; // ms
|
||||
unsigned long timerPause; // ms
|
||||
unsigned long duration; // ms
|
||||
bool doingColdShot; // true if we've just sent a jolt of cold water
|
||||
} _Boiler_Shower;
|
||||
|
||||
command_t PROGMEM project_cmds[] = {
|
||||
|
||||
{"info", "show the values"},
|
||||
{"log <n | b | t | r | v>", "set logging mode to none, basic, thermostat only, raw or verbose"},
|
||||
{"poll", "toggle EMS poll request on/off"},
|
||||
{"tx", "toggle EMX Tx on/off"},
|
||||
{"publish", "publish values to MQTT"},
|
||||
{"types", "list supported EMS telegram type IDs"},
|
||||
{"queue", "print the Tx queue"},
|
||||
{"autodetect", "discover EMS devices and set boiler and thermostat automatically"},
|
||||
{"shower <timer | alert>", "toggle either timer or alert on/off"},
|
||||
{"send XX...", "send raw telegram data in hex to EMS bus"},
|
||||
{"thermostat read <hex type ID>", "send read request to thermostat"},
|
||||
{"thermostat temp <degrees>", "set current thermostat temperature"},
|
||||
{"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"},
|
||||
{"boiler read <hex type ID>", "send read request to boiler"},
|
||||
{"boiler wwtemp <degrees>", "set warm water temperature"},
|
||||
{"boiler tapwater <on | off>", "set warm tap water on or off"}
|
||||
|
||||
};
|
||||
|
||||
// store for overall system status
|
||||
_Boiler_Status Boiler_Status;
|
||||
_Boiler_Shower Boiler_Shower;
|
||||
|
||||
// CRC checks
|
||||
uint32_t previousBoilerPublishCRC = 0;
|
||||
uint32_t previousThermostatPublishCRC = 0;
|
||||
|
||||
// Times
|
||||
const unsigned long POLL_TIMEOUT_ERR = 10000; // if no signal from boiler for last 10 seconds, assume its offline
|
||||
const unsigned long TX_HOLD_LED_TIME = 2000; // how long to hold the Tx LED because its so quick
|
||||
|
||||
uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
|
||||
|
||||
// logging messages with fixed strings
|
||||
void myDebugLog(const char * s) {
|
||||
if (ems_getLogging() >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug(s);
|
||||
}
|
||||
}
|
||||
|
||||
// convert float to char
|
||||
char * _float_to_char(char * a, float f, uint8_t precision = 1) {
|
||||
long p[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
|
||||
|
||||
char * ret = a;
|
||||
// check for 0x8000 (sensor missing)
|
||||
if (f == EMS_VALUE_FLOAT_NOTSET) {
|
||||
strlcpy(ret, "?", sizeof(ret));
|
||||
} else {
|
||||
long whole = (long)f;
|
||||
itoa(whole, a, 10);
|
||||
while (*a != '\0')
|
||||
a++;
|
||||
*a++ = '.';
|
||||
long decimal = abs((long)((f - whole) * p[precision]));
|
||||
itoa(decimal, a, 10);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// convert bool to text
|
||||
char * _bool_to_char(char * s, uint8_t value) {
|
||||
if (value == EMS_VALUE_INT_ON) {
|
||||
strlcpy(s, "on", sizeof(s));
|
||||
} else if (value == EMS_VALUE_INT_OFF) {
|
||||
strlcpy(s, "off", sizeof(s));
|
||||
} else {
|
||||
strlcpy(s, "?", sizeof(s));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// convert int (single byte) to text value
|
||||
char * _int_to_char(char * s, uint8_t value) {
|
||||
if (value == EMS_VALUE_INT_NOTSET) {
|
||||
strlcpy(s, "?", sizeof(s));
|
||||
} else {
|
||||
itoa(value, s, 10);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// takes a float value at prints it to debug log
|
||||
void _renderFloatValue(const char * prefix, const char * postfix, float value) {
|
||||
char buffer[200] = {0};
|
||||
char s[20] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
strlcat(buffer, _float_to_char(s, value), sizeof(buffer));
|
||||
|
||||
if (postfix != NULL) {
|
||||
strlcat(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, postfix, sizeof(buffer));
|
||||
}
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// takes an int (single byte) value at prints it to debug log
|
||||
void _renderIntValue(const char * prefix, const char * postfix, uint8_t value) {
|
||||
char buffer[200] = {0};
|
||||
char s[20] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
strlcat(buffer, _int_to_char(s, value), sizeof(buffer));
|
||||
|
||||
if (postfix != NULL) {
|
||||
strlcat(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, postfix, sizeof(buffer));
|
||||
}
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// takes an int value, converts to a fraction
|
||||
void _renderIntfractionalValue(const char * prefix, const char * postfix, uint8_t value, uint8_t decimals) {
|
||||
char buffer[200] = {0};
|
||||
char s[20] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
strlcat(buffer, _int_to_char(s, value / (decimals * 10)), sizeof(buffer));
|
||||
strlcat(buffer, ".", sizeof(buffer));
|
||||
strlcat(buffer, _int_to_char(s, value % (decimals * 10)), sizeof(buffer));
|
||||
|
||||
if (postfix != NULL) {
|
||||
strlcat(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, postfix, sizeof(buffer));
|
||||
}
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// takes a long value at prints it to debug log
|
||||
void _renderLongValue(const char * prefix, const char * postfix, uint32_t value) {
|
||||
char buffer[200] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
|
||||
if (value == EMS_VALUE_LONG_NOTSET) {
|
||||
strlcat(buffer, "?", sizeof(buffer));
|
||||
} else {
|
||||
char s[20] = {0};
|
||||
strlcat(buffer, ltoa(value, s, 10), sizeof(buffer));
|
||||
}
|
||||
|
||||
if (postfix != NULL) {
|
||||
strlcat(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, postfix, sizeof(buffer));
|
||||
}
|
||||
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// takes a bool value at prints it to debug log
|
||||
void _renderBoolValue(const char * prefix, uint8_t value) {
|
||||
char buffer[200] = {0};
|
||||
char s[20] = {0};
|
||||
strlcpy(buffer, " ", sizeof(buffer));
|
||||
strlcat(buffer, prefix, sizeof(buffer));
|
||||
strlcat(buffer, ": ", sizeof(buffer));
|
||||
|
||||
strlcat(buffer, _bool_to_char(s, value), sizeof(buffer));
|
||||
|
||||
myDebug(buffer);
|
||||
}
|
||||
|
||||
// Show command - display stats on an 's' command
|
||||
void showInfo() {
|
||||
// General stats from EMS bus
|
||||
|
||||
myDebug("%sEMS-ESP System setstats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
_EMS_SYS_LOGGING sysLog = ems_getLogging();
|
||||
if (sysLog == EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug(" System logging set to Basic");
|
||||
} else if (sysLog == EMS_SYS_LOGGING_VERBOSE) {
|
||||
myDebug(" System logging set to Verbose");
|
||||
} else if (sysLog == EMS_SYS_LOGGING_THERMOSTAT) {
|
||||
myDebug(" System logging set to Thermostat only");
|
||||
} else {
|
||||
myDebug(" System logging set to None");
|
||||
}
|
||||
|
||||
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",
|
||||
(ems_getThermostatEnabled() ? "enabled" : "disabled"),
|
||||
(ems_getBoilerEnabled() ? "enabled" : "disabled"),
|
||||
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
||||
((EMS_Sys_Status.emsTxEnabled) ? "enabled" : "disabled"),
|
||||
((Boiler_Status.shower_timer) ? "enabled" : "disabled"),
|
||||
((Boiler_Status.shower_alert) ? "enabled" : "disabled"));
|
||||
|
||||
myDebug(" EMS Bus Stats: Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
|
||||
(ems_getBusConnected() ? "yes" : "no"),
|
||||
EMS_Sys_Status.emsRxPgks,
|
||||
EMS_Sys_Status.emsTxPkgs,
|
||||
EMS_Sys_Status.emxCrcErr);
|
||||
|
||||
myDebug("");
|
||||
|
||||
myDebug("%sBoiler stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
|
||||
// version details
|
||||
char buffer_type[64];
|
||||
myDebug(" Boiler type: %s", ems_getBoilerType(buffer_type));
|
||||
|
||||
// active stats
|
||||
myDebug(" Hot tap water is %s", (EMS_Boiler.tapwaterActive ? "running" : "off"));
|
||||
myDebug(" Central Heating is %s", (EMS_Boiler.heatingActive ? "active" : "off"));
|
||||
|
||||
// UBAParameterWW
|
||||
_renderBoolValue("Warm Water activated", EMS_Boiler.wWActivated);
|
||||
_renderBoolValue("Warm Water circulation pump available", EMS_Boiler.wWCircPump);
|
||||
myDebug(" Warm Water is set to %s", (EMS_Boiler.wWComfort ? "Comfort" : "ECO"));
|
||||
_renderIntValue("Warm Water selected temperature", "C", EMS_Boiler.wWSelTemp);
|
||||
_renderIntValue("Warm Water desired temperature", "C", EMS_Boiler.wWDesiredTemp);
|
||||
|
||||
// UBAMonitorWWMessage
|
||||
_renderFloatValue("Warm Water current temperature", "C", EMS_Boiler.wWCurTmp);
|
||||
_renderIntfractionalValue("Warm Water current tap water flow", "l/min", EMS_Boiler.wWCurFlow, 1);
|
||||
_renderLongValue("Warm Water # starts", "times", EMS_Boiler.wWStarts);
|
||||
if (EMS_Boiler.wWWorkM != EMS_VALUE_LONG_NOTSET) {
|
||||
myDebug(" Warm Water active time: %d days %d hours %d minutes",
|
||||
EMS_Boiler.wWWorkM / 1440,
|
||||
(EMS_Boiler.wWWorkM % 1440) / 60,
|
||||
EMS_Boiler.wWWorkM % 60);
|
||||
}
|
||||
_renderBoolValue("Warm Water 3-way valve", EMS_Boiler.wWHeat);
|
||||
|
||||
// UBAMonitorFast
|
||||
_renderIntValue("Selected flow temperature", "C", EMS_Boiler.selFlowTemp);
|
||||
_renderFloatValue("Current flow temperature", "C", EMS_Boiler.curFlowTemp);
|
||||
_renderFloatValue("Return temperature", "C", EMS_Boiler.retTemp);
|
||||
_renderBoolValue("Gas", EMS_Boiler.burnGas);
|
||||
_renderBoolValue("Boiler pump", EMS_Boiler.heatPmp);
|
||||
_renderBoolValue("Fan", EMS_Boiler.fanWork);
|
||||
_renderBoolValue("Ignition", EMS_Boiler.ignWork);
|
||||
_renderBoolValue("Circulation pump", EMS_Boiler.wWCirc);
|
||||
_renderIntValue("Burner selected max power", "%", EMS_Boiler.selBurnPow);
|
||||
_renderIntValue("Burner current power", "%", EMS_Boiler.curBurnPow);
|
||||
_renderFloatValue("Flame current", "uA", EMS_Boiler.flameCurr);
|
||||
_renderFloatValue("System pressure", "bar", EMS_Boiler.sysPress);
|
||||
myDebug(" Current System Service Code: %s", EMS_Boiler.serviceCodeChar);
|
||||
|
||||
// UBAMonitorSlow
|
||||
_renderFloatValue("Outside temperature", "C", EMS_Boiler.extTemp);
|
||||
_renderFloatValue("Boiler temperature", "C", EMS_Boiler.boilTemp);
|
||||
_renderIntValue("Pump modulation", "%", EMS_Boiler.pumpMod);
|
||||
_renderLongValue("Burner # restarts", "times", EMS_Boiler.burnStarts);
|
||||
if (EMS_Boiler.burnWorkMin != EMS_VALUE_LONG_NOTSET) {
|
||||
myDebug(" Total burner operating time: %d days %d hours %d minutes",
|
||||
EMS_Boiler.burnWorkMin / 1440,
|
||||
(EMS_Boiler.burnWorkMin % 1440) / 60,
|
||||
EMS_Boiler.burnWorkMin % 60);
|
||||
}
|
||||
if (EMS_Boiler.heatWorkMin != EMS_VALUE_LONG_NOTSET) {
|
||||
myDebug(" Total heat operating time: %d days %d hours %d minutes",
|
||||
EMS_Boiler.heatWorkMin / 1440,
|
||||
(EMS_Boiler.heatWorkMin % 1440) / 60,
|
||||
EMS_Boiler.heatWorkMin % 60);
|
||||
}
|
||||
if (EMS_Boiler.UBAuptime != EMS_VALUE_LONG_NOTSET) {
|
||||
myDebug(" Total UBA working time: %d days %d hours %d minutes",
|
||||
EMS_Boiler.UBAuptime / 1440,
|
||||
(EMS_Boiler.UBAuptime % 1440) / 60,
|
||||
EMS_Boiler.UBAuptime % 60);
|
||||
}
|
||||
|
||||
myDebug(""); // newline
|
||||
|
||||
// Thermostat stats
|
||||
if (ems_getThermostatEnabled()) {
|
||||
myDebug("%sThermostat stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
myDebug(" Thermostat type: %s", ems_getThermostatType(buffer_type));
|
||||
_renderFloatValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp);
|
||||
_renderFloatValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp);
|
||||
if (ems_getThermostatModel() != EMS_MODEL_EASY) {
|
||||
myDebug(" Thermostat time is %02d:%02d:%02d %d/%d/%d",
|
||||
EMS_Thermostat.hour,
|
||||
EMS_Thermostat.minute,
|
||||
EMS_Thermostat.second,
|
||||
EMS_Thermostat.day,
|
||||
EMS_Thermostat.month,
|
||||
EMS_Thermostat.year + 2000);
|
||||
|
||||
if (EMS_Thermostat.mode == 0) {
|
||||
myDebug(" Mode is set to low");
|
||||
} else if (EMS_Thermostat.mode == 1) {
|
||||
myDebug(" Mode is set to manual");
|
||||
} else if (EMS_Thermostat.mode == 2) {
|
||||
myDebug(" Mode is set to auto");
|
||||
} else {
|
||||
myDebug(" Mode is set to ?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myDebug(""); // newline
|
||||
|
||||
// show the Shower Info
|
||||
if (Boiler_Status.shower_timer) {
|
||||
myDebug("%s Shower stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
myDebug(" Shower Timer is %s", (Boiler_Shower.showerOn ? "active" : "off"));
|
||||
}
|
||||
}
|
||||
|
||||
// send values to HA via MQTT
|
||||
// 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
|
||||
void publishValues(bool force) {
|
||||
char s[20] = {0}; // for formatting strings
|
||||
StaticJsonBuffer<MQTT_MAX_SIZE> jsonBuffer;
|
||||
char data[MQTT_MAX_SIZE] = {0};
|
||||
JsonObject & rootBoiler = jsonBuffer.createObject();
|
||||
size_t rlen;
|
||||
CRC32 crc;
|
||||
uint32_t fchecksum;
|
||||
|
||||
rootBoiler["wWSelTemp"] = _int_to_char(s, EMS_Boiler.wWSelTemp);
|
||||
rootBoiler["selFlowTemp"] = _float_to_char(s, EMS_Boiler.selFlowTemp);
|
||||
rootBoiler["outdoorTemp"] = _float_to_char(s, EMS_Boiler.extTemp);
|
||||
rootBoiler["wWActivated"] = _bool_to_char(s, EMS_Boiler.wWActivated);
|
||||
rootBoiler["wWComfort"] = EMS_Boiler.wWComfort ? "Comfort" : "ECO";
|
||||
rootBoiler["wWCurTmp"] = _float_to_char(s, EMS_Boiler.wWCurTmp);
|
||||
snprintf(s, sizeof(s), "%i.%i", EMS_Boiler.wWCurFlow / 10, EMS_Boiler.wWCurFlow % 10);
|
||||
rootBoiler["wWCurFlow"] = s;
|
||||
rootBoiler["wWHeat"] = _bool_to_char(s, EMS_Boiler.wWHeat);
|
||||
rootBoiler["curFlowTemp"] = _float_to_char(s, EMS_Boiler.curFlowTemp);
|
||||
rootBoiler["retTemp"] = _float_to_char(s, EMS_Boiler.retTemp);
|
||||
rootBoiler["burnGas"] = _bool_to_char(s, EMS_Boiler.burnGas);
|
||||
rootBoiler["heatPmp"] = _bool_to_char(s, EMS_Boiler.heatPmp);
|
||||
rootBoiler["fanWork"] = _bool_to_char(s, EMS_Boiler.fanWork);
|
||||
rootBoiler["ignWork"] = _bool_to_char(s, EMS_Boiler.ignWork);
|
||||
rootBoiler["wWCirc"] = _bool_to_char(s, EMS_Boiler.wWCirc);
|
||||
rootBoiler["selBurnPow"] = _int_to_char(s, EMS_Boiler.selBurnPow);
|
||||
rootBoiler["curBurnPow"] = _int_to_char(s, EMS_Boiler.curBurnPow);
|
||||
rootBoiler["sysPress"] = _float_to_char(s, EMS_Boiler.sysPress);
|
||||
rootBoiler["boilTemp"] = _float_to_char(s, EMS_Boiler.boilTemp);
|
||||
rootBoiler["pumpMod"] = _int_to_char(s, EMS_Boiler.pumpMod);
|
||||
rootBoiler["ServiceCode"] = EMS_Boiler.serviceCodeChar;
|
||||
|
||||
rlen = rootBoiler.measureLength();
|
||||
rootBoiler.printTo(data, rlen + 1); // form the json string
|
||||
|
||||
// calculate hash and send values if something has changed, to save unnecessary wifi traffic
|
||||
for (size_t i = 0; i < rlen - 1; i++) {
|
||||
crc.update(data[i]);
|
||||
}
|
||||
fchecksum = crc.finalize();
|
||||
if ((previousBoilerPublishCRC != fchecksum) || force) {
|
||||
previousBoilerPublishCRC = fchecksum;
|
||||
myDebugLog("Publishing boiler data via MQTT");
|
||||
|
||||
// send values via MQTT
|
||||
myESP.mqttPublish(TOPIC_BOILER_DATA, data);
|
||||
}
|
||||
|
||||
// see if the heating or hot tap water has changed, if so send
|
||||
// last_boilerActive stores heating in bit 1 and tap water in bit 2
|
||||
if ((last_boilerActive != ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive)) || force) {
|
||||
myDebugLog("Publishing hot water and heating state via MQTT");
|
||||
myESP.mqttPublish(TOPIC_BOILER_TAPWATER_ACTIVE, EMS_Boiler.tapwaterActive == 1 ? "1" : "0");
|
||||
myESP.mqttPublish(TOPIC_BOILER_HEATING_ACTIVE, EMS_Boiler.heatingActive == 1 ? "1" : "0");
|
||||
|
||||
last_boilerActive = ((EMS_Boiler.tapwaterActive << 1) + EMS_Boiler.heatingActive); // remember last state
|
||||
}
|
||||
|
||||
// handle the thermostat values separately
|
||||
if (ems_getThermostatEnabled()) {
|
||||
// only send thermostat values if we actually have them
|
||||
if (((int)EMS_Thermostat.curr_roomTemp == (int)0) || ((int)EMS_Thermostat.setpoint_roomTemp == (int)0))
|
||||
return;
|
||||
|
||||
// build json object
|
||||
JsonObject & rootThermostat = jsonBuffer.createObject();
|
||||
rootThermostat[THERMOSTAT_CURRTEMP] = _float_to_char(s, EMS_Thermostat.curr_roomTemp);
|
||||
rootThermostat[THERMOSTAT_SELTEMP] = _float_to_char(s, EMS_Thermostat.setpoint_roomTemp);
|
||||
|
||||
// RC20 has different mode settings
|
||||
if (ems_getThermostatModel() == EMS_MODEL_RC20) {
|
||||
if (EMS_Thermostat.mode == 0) {
|
||||
rootThermostat[THERMOSTAT_MODE] = "low";
|
||||
} else if (EMS_Thermostat.mode == 1) {
|
||||
rootThermostat[THERMOSTAT_MODE] = "manual";
|
||||
} else {
|
||||
rootThermostat[THERMOSTAT_MODE] = "auto";
|
||||
}
|
||||
} else {
|
||||
if (EMS_Thermostat.mode == 0) {
|
||||
rootThermostat[THERMOSTAT_MODE] = "night";
|
||||
} else if (EMS_Thermostat.mode == 1) {
|
||||
rootThermostat[THERMOSTAT_MODE] = "day";
|
||||
} else {
|
||||
rootThermostat[THERMOSTAT_MODE] = "auto";
|
||||
}
|
||||
}
|
||||
|
||||
data[0] = '\0'; // reset data for next package
|
||||
rlen = rootThermostat.measureLength();
|
||||
rootThermostat.printTo(data, rlen + 1); // form the json string
|
||||
|
||||
// calculate new CRC
|
||||
crc.reset();
|
||||
for (size_t i = 0; i < rlen - 1; i++) {
|
||||
crc.update(data[i]);
|
||||
}
|
||||
uint32_t checksum = crc.finalize();
|
||||
if ((previousThermostatPublishCRC != checksum) || force) {
|
||||
previousThermostatPublishCRC = checksum;
|
||||
myDebugLog("Publishing thermostat data via MQTT");
|
||||
|
||||
// send values via MQTT
|
||||
myESP.mqttPublish(TOPIC_THERMOSTAT_DATA, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sets the shower timer on/off
|
||||
void set_showerTimer() {
|
||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||
myDebug("Shower timer has been set to %s", Boiler_Status.shower_timer ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
// sets the shower alert on/off
|
||||
void set_showerAlert() {
|
||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||
myDebug("Shower alert has been set to %s", Boiler_Status.shower_alert ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer and convert to an 8 bit int
|
||||
uint8_t _readIntNumber() {
|
||||
char * numTextPtr = strtok(NULL, ", \n");
|
||||
if (numTextPtr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return atoi(numTextPtr);
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer as a hex value and convert to an 8 bit int
|
||||
uint8_t _readHexNumber() {
|
||||
char * numTextPtr = strtok(NULL, ", \n");
|
||||
if (numTextPtr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return (uint8_t)strtol(numTextPtr, 0, 16);
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer
|
||||
char * _readWord() {
|
||||
char * word = strtok(NULL, ", \n");
|
||||
return word;
|
||||
}
|
||||
|
||||
// initiate a force scan by sending type read requests from 0 to FF to the thermostat
|
||||
// used to analyze repsonses for debugging
|
||||
void startThermostatScan(uint8_t start) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_THERMOSTAT);
|
||||
publishValuesTimer.detach();
|
||||
systemCheckTimer.detach();
|
||||
regularUpdatesTimer.detach();
|
||||
scanThermostat_count = start;
|
||||
myDebug("Starting a deep message scan on thermostat");
|
||||
scanThermostat.attach(SCANTHERMOSTAT_TIME, do_scanThermostat);
|
||||
}
|
||||
|
||||
// call back when a telnet client connects or disconnects
|
||||
// we set the logging here
|
||||
void TelnetCallback(uint8_t event) {
|
||||
if (event == TELNET_EVENT_CONNECT) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_BASIC);
|
||||
} else if (event == TELNET_EVENT_DISCONNECT) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
// extra commands options for telnet debug window
|
||||
// wc is the word count, i.e. number of arguments. Everything is in lower case.
|
||||
void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
|
||||
bool ok = false;
|
||||
// get first command argument
|
||||
char * first_cmd = strtok((char *)commandLine, ", \n");
|
||||
|
||||
if (strcmp(first_cmd, "info") == 0) {
|
||||
showInfo();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "poll") == 0) {
|
||||
bool b = !ems_getPoll();
|
||||
ems_setPoll(b);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "tx") == 0) {
|
||||
bool b = !ems_getTxEnabled();
|
||||
ems_setTxEnabled(b);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "publish") == 0) {
|
||||
publishValues(true);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "types") == 0) {
|
||||
ems_printAllTypes();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "queue") == 0) {
|
||||
ems_printTxQueue();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "autodetect") == 0) {
|
||||
ems_scanDevices();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// shower settings
|
||||
if (strcmp(first_cmd, "shower") == 0) {
|
||||
if (wc == 2) {
|
||||
char * second_cmd = _readWord();
|
||||
if (strcmp(second_cmd, "timer") == 0) {
|
||||
Boiler_Status.shower_timer = !Boiler_Status.shower_timer;
|
||||
myESP.mqttPublish(TOPIC_SHOWER_TIMER, Boiler_Status.shower_timer ? "1" : "0");
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "alert") == 0) {
|
||||
Boiler_Status.shower_alert = !Boiler_Status.shower_alert;
|
||||
myESP.mqttPublish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logging
|
||||
if (strcmp(first_cmd, "log") == 0) {
|
||||
if (wc == 2) {
|
||||
char * second_cmd = _readWord();
|
||||
if (strcmp(second_cmd, "v") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_VERBOSE);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "b") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_BASIC);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "t") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_THERMOSTAT);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "r") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_RAW);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "n") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// thermostat commands
|
||||
if (strcmp(first_cmd, "thermostat") == 0) {
|
||||
if (wc == 3) {
|
||||
char * second_cmd = _readWord();
|
||||
if (strcmp(second_cmd, "temp") == 0) {
|
||||
ems_setThermostatTemp(_readIntNumber());
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "mode") == 0) {
|
||||
ems_setThermostatMode(_readIntNumber());
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "read") == 0) {
|
||||
ems_doReadCommand(_readHexNumber(), EMS_Thermostat.type_id);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "scan") == 0) {
|
||||
startThermostatScan(_readIntNumber());
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// boiler commands
|
||||
if (strcmp(first_cmd, "boiler") == 0) {
|
||||
if (wc == 3) {
|
||||
char * second_cmd = _readWord();
|
||||
if (strcmp(second_cmd, "wwtemp") == 0) {
|
||||
ems_setWarmWaterTemp(_readIntNumber());
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "read") == 0) {
|
||||
ems_doReadCommand(_readHexNumber(), EMS_Boiler.type_id);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "tapwater") == 0) {
|
||||
char * third_cmd = _readWord();
|
||||
if (strcmp(third_cmd, "on") == 0) {
|
||||
ems_setWarmTapWaterActivated(true);
|
||||
ok = true;
|
||||
} else if (strcmp(third_cmd, "off") == 0) {
|
||||
ems_setWarmTapWaterActivated(false);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send raw
|
||||
if (strcmp(first_cmd, "send") == 0) {
|
||||
ems_sendRawTelegram((char *)&commandLine[5]);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// check for invalid command
|
||||
if (!ok) {
|
||||
myDebug("Unknown command. Use ? for help.");
|
||||
}
|
||||
}
|
||||
|
||||
// MQTT Callback to handle incoming/outgoing changes
|
||||
void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
||||
// we're connected. lets subscribe to some topics
|
||||
if (type == MQTT_CONNECT_EVENT) {
|
||||
myESP.mqttSubscribe(TOPIC_THERMOSTAT_CMD_TEMP);
|
||||
myESP.mqttSubscribe(TOPIC_THERMOSTAT_CMD_MODE);
|
||||
myESP.mqttSubscribe(TOPIC_SHOWER_TIMER);
|
||||
myESP.mqttSubscribe(TOPIC_SHOWER_ALERT);
|
||||
myESP.mqttSubscribe(TOPIC_SHOWER_COLDSHOT);
|
||||
|
||||
// publish to HA the status of the Shower parameters
|
||||
myESP.mqttPublish(TOPIC_SHOWER_TIMER, Boiler_Status.shower_timer ? "1" : "0");
|
||||
myESP.mqttPublish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
||||
}
|
||||
|
||||
if (type == MQTT_MESSAGE_EVENT) {
|
||||
// thermostat temp changes
|
||||
if (strcmp(topic, TOPIC_THERMOSTAT_CMD_TEMP) == 0) {
|
||||
float f = strtof((char *)message, 0);
|
||||
char s[10] = {0};
|
||||
myDebug("MQTT topic: thermostat temperature value %s", _float_to_char(s, f));
|
||||
ems_setThermostatTemp(f);
|
||||
publishValues(true); // publish back so HA is immediately updated
|
||||
}
|
||||
|
||||
// thermostat mode changes
|
||||
if (strcmp(topic, TOPIC_THERMOSTAT_CMD_MODE) == 0) {
|
||||
myDebug("MQTT topic: thermostat mode value %s", message);
|
||||
if (strcmp((char *)message, "auto") == 0) {
|
||||
ems_setThermostatMode(2);
|
||||
} else if (strcmp((char *)message, "manual") == 0) {
|
||||
ems_setThermostatMode(1);
|
||||
}
|
||||
}
|
||||
|
||||
// shower timer
|
||||
if (strcmp(topic, TOPIC_SHOWER_TIMER) == 0) {
|
||||
if (message[0] == '1') {
|
||||
Boiler_Status.shower_timer = true;
|
||||
} else if (message[0] == '0') {
|
||||
Boiler_Status.shower_timer = false;
|
||||
}
|
||||
set_showerTimer();
|
||||
}
|
||||
|
||||
// shower alert
|
||||
if (strcmp(topic, TOPIC_SHOWER_ALERT) == 0) {
|
||||
if (message[0] == '1') {
|
||||
Boiler_Status.shower_alert = true;
|
||||
} else if (message[0] == '0') {
|
||||
Boiler_Status.shower_alert = false;
|
||||
}
|
||||
set_showerAlert();
|
||||
}
|
||||
|
||||
// shower cold shot
|
||||
if (strcmp(topic, TOPIC_SHOWER_COLDSHOT) == 0) {
|
||||
_showerColdShotStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init callback, which is used to set functions and call methods after a wifi connection has been established
|
||||
void WIFICallback() {
|
||||
// when finally we're all set up, we can fire up the uart (this will enable the UART interrupts)
|
||||
#ifdef DEBUG_SUPPORT
|
||||
myDebug("Warning, in DEBUG mode. EMS bus has been disabled. See -DDEBUG_SUPPORT build option.");
|
||||
#else
|
||||
// Important! This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
|
||||
// This is done after we have a WiFi signal to avoid any resource conflicts
|
||||
emsuart_init();
|
||||
myDebug("[UART] Opened Rx/Tx connection");
|
||||
#endif
|
||||
|
||||
// now that we're connected, check to see if we boiler and thermostat set
|
||||
// otherwise this will initiate a self scan
|
||||
ems_setModels();
|
||||
}
|
||||
|
||||
// Initialize the boiler settings
|
||||
void initShower() {
|
||||
// default shower settings
|
||||
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
|
||||
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
|
||||
Boiler_Shower.timerStart = 0;
|
||||
Boiler_Shower.timerPause = 0;
|
||||
Boiler_Shower.duration = 0;
|
||||
Boiler_Shower.doingColdShot = false;
|
||||
}
|
||||
|
||||
// call PublishValues without forcing, so using CRC to see if we really need to publish
|
||||
void do_publishValues() {
|
||||
// don't publish if we're not connected to the EMS bus
|
||||
if (ems_getBusConnected()) {
|
||||
publishValues(false);
|
||||
}
|
||||
}
|
||||
|
||||
// callback to light up the LED, called via Ticker every second
|
||||
void do_ledcheck() {
|
||||
#ifndef NO_LED
|
||||
if (ems_getBusConnected()) {
|
||||
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
|
||||
} else {
|
||||
int state = digitalRead(BOILER_LED);
|
||||
digitalWrite(BOILER_LED, !state);
|
||||
}
|
||||
//WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + (state ? 4 : 8), (1 << BOILER_LED)); // 4 is on, 8 is off
|
||||
#endif
|
||||
}
|
||||
|
||||
// Thermostat scan
|
||||
void do_scanThermostat() {
|
||||
myDebug("> Scanning thermostat message type #0x%02X..", scanThermostat_count);
|
||||
ems_doReadCommand(scanThermostat_count, EMS_Thermostat.type_id);
|
||||
scanThermostat_count++;
|
||||
}
|
||||
|
||||
// do a system health check every now and then to see if we all connections
|
||||
void do_systemCheck() {
|
||||
if (!ems_getBusConnected()) {
|
||||
myDebug("Error! Unable to connect to EMS bus. Check connection and make sure you're not in DEBUG_SUPPORT mode. Retrying in %d "
|
||||
"seconds...",
|
||||
SYSTEMCHECK_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
// force calls to get data from EMS for the types that aren't sent as broadcasts
|
||||
// only if we have a EMS connection
|
||||
void do_regularUpdates() {
|
||||
if (ems_getBusConnected()) {
|
||||
myDebugLog("Calling scheduled data refresh from EMS devices..");
|
||||
ems_getThermostatValues(); // get Thermostat temps (if supported)
|
||||
ems_getBoilerValues();
|
||||
}
|
||||
}
|
||||
|
||||
// turn off hot water to send a shot of cold
|
||||
void _showerColdShotStart() {
|
||||
myDebugLog("[Shower] doing a shot of cold water");
|
||||
ems_setWarmTapWaterActivated(false);
|
||||
Boiler_Shower.doingColdShot = true;
|
||||
// start the timer for n seconds which will reset the water back to hot
|
||||
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
|
||||
}
|
||||
|
||||
// turn back on the hot water for the shower
|
||||
void _showerColdShotStop() {
|
||||
if (Boiler_Shower.doingColdShot) {
|
||||
myDebugLog("[Shower] finished shot of cold. hot water back on");
|
||||
ems_setWarmTapWaterActivated(true);
|
||||
Boiler_Shower.doingColdShot = false;
|
||||
showerColdShotStopTimer.detach(); // disable the timer
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Shower Logic
|
||||
*/
|
||||
void showerCheck() {
|
||||
// if already in cold mode, ignore all this logic until we're out of the cold blast
|
||||
if (!Boiler_Shower.doingColdShot) {
|
||||
// is the hot water running?
|
||||
if (EMS_Boiler.tapwaterActive) {
|
||||
// if heater was previously off, start the timer
|
||||
if (Boiler_Shower.timerStart == 0) {
|
||||
// hot water just started...
|
||||
Boiler_Shower.timerStart = timestamp;
|
||||
Boiler_Shower.timerPause = 0; // remove any last pauses
|
||||
Boiler_Shower.doingColdShot = false;
|
||||
Boiler_Shower.duration = 0;
|
||||
Boiler_Shower.showerOn = false;
|
||||
} else {
|
||||
// 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
|
||||
if (!Boiler_Shower.showerOn && (timestamp - Boiler_Shower.timerStart) > SHOWER_MIN_DURATION) {
|
||||
Boiler_Shower.showerOn = true;
|
||||
myDebugLog("[Shower] hot water still running, starting shower timer");
|
||||
}
|
||||
// check if the shower has been on too long
|
||||
else if ((((timestamp - Boiler_Shower.timerStart) > SHOWER_MAX_DURATION) && !Boiler_Shower.doingColdShot)
|
||||
&& Boiler_Status.shower_alert) {
|
||||
myESP.sendHACommand(SHOWER_ALARM);
|
||||
myDebugLog("[Shower] exceeded max shower time");
|
||||
_showerColdShotStart();
|
||||
}
|
||||
}
|
||||
} else { // hot water is off
|
||||
// if it just turned off, record the time as it could be a short pause
|
||||
if ((Boiler_Shower.timerStart != 0) && (Boiler_Shower.timerPause == 0)) {
|
||||
Boiler_Shower.timerPause = timestamp;
|
||||
}
|
||||
|
||||
// if shower has been off for longer than the wait time
|
||||
if ((Boiler_Shower.timerPause != 0) && ((timestamp - Boiler_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
|
||||
// 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) {
|
||||
Boiler_Shower.duration = (Boiler_Shower.timerPause - Boiler_Shower.timerStart - SHOWER_OFFSET_TIME);
|
||||
if (Boiler_Shower.duration > SHOWER_MIN_DURATION) {
|
||||
char s[50] = {0};
|
||||
char buffer[16] = {0};
|
||||
strlcpy(s, itoa((uint8_t)((Boiler_Shower.duration / (1000 * 60)) % 60), buffer, 10), sizeof(s));
|
||||
strlcat(s, " minutes and ", sizeof(s));
|
||||
strlcat(s, itoa((uint8_t)((Boiler_Shower.duration / 1000) % 60), buffer, 10), sizeof(s));
|
||||
strlcat(s, " seconds", sizeof(s));
|
||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||
myDebug("[Shower] finished with duration %s", s);
|
||||
}
|
||||
myESP.mqttPublish(TOPIC_SHOWERTIME, s); // publish to HA
|
||||
}
|
||||
}
|
||||
|
||||
// reset everything
|
||||
Boiler_Shower.timerStart = 0;
|
||||
Boiler_Shower.timerPause = 0;
|
||||
Boiler_Shower.showerOn = false;
|
||||
_showerColdShotStop(); // turn hot water back on in case its off
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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
|
||||
|
||||
// Timers using Ticker library
|
||||
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post HA values
|
||||
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
|
||||
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
|
||||
|
||||
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
||||
myESP.setTelnetCommands(project_cmds, ArraySize(project_cmds), TelnetCommandCallback); // set up Telnet commands
|
||||
myESP.setConnection(WIFI_SSID, WIFI_PASSWORD, MQTT_IP, MQTT_USER, MQTT_PASS); // optional
|
||||
myESP.begin(APP_HOSTNAME, APP_NAME, APP_VERSION);
|
||||
myESP.setMQTTbase(MQTT_BASE);
|
||||
|
||||
// callbacks
|
||||
myESP.setWIFICallback(WIFICallback);
|
||||
myESP.setMQTTCallback(MQTTCallback);
|
||||
myESP.setTelnetCallback(TelnetCallback);
|
||||
|
||||
// init Shower specific parameters
|
||||
initShower();
|
||||
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE); // set default logging to none
|
||||
|
||||
// init the EMS bus
|
||||
// call ems.cpp's init function to set all the internal params
|
||||
ems_init(MY_BOILER_MODELID, MY_THERMOSTAT_MODELID);
|
||||
}
|
||||
|
||||
//
|
||||
// Main loop
|
||||
//
|
||||
void loop() {
|
||||
timestamp = millis();
|
||||
|
||||
// the main loop
|
||||
myESP.loop();
|
||||
|
||||
// publish the values to MQTT, regardless if the values haven't changed
|
||||
// we don't want to publish when doing a deep scan of the thermostat
|
||||
if (ems_getEmsRefreshed() && (scanThermostat_count == 0)) {
|
||||
publishValues(true);
|
||||
ems_setEmsRefreshed(false);
|
||||
}
|
||||
|
||||
// do shower logic, if enabled
|
||||
if (Boiler_Status.shower_timer) {
|
||||
showerCheck();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user