mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
improvements to rendering floats
This commit is contained in:
@@ -30,7 +30,6 @@ There are 3 parts to this project, first the design of the circuit, secondly the
|
|||||||
- [Home Assistant Configuration](#home-assistant-configuration)
|
- [Home Assistant Configuration](#home-assistant-configuration)
|
||||||
- [Building The Firmware](#building-the-firmware)
|
- [Building The Firmware](#building-the-firmware)
|
||||||
- [Using PlatformIO Standalone](#using-platformio-standalone)
|
- [Using PlatformIO Standalone](#using-platformio-standalone)
|
||||||
- [Building Using Arduino IDE](#building-using-arduino-ide)
|
|
||||||
- [Using the Pre-built Firmware](#using-the-pre-built-firmware)
|
- [Using the Pre-built Firmware](#using-the-pre-built-firmware)
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Known Issues](#known-issues)
|
- [Known Issues](#known-issues)
|
||||||
@@ -83,7 +82,7 @@ The code and circuit has been tested with a few ESP8266 development boards such
|
|||||||
13. Connect the EMS lines to the ESP. This can be done via the two EMS wires or via the 3.5mm service jack if you have an bbqkees board.
|
13. Connect the EMS lines to the ESP. This can be done via the two EMS wires or via the 3.5mm service jack if you have an bbqkees board.
|
||||||
14. Reboot the ESP, either by the reset switch or pulling the power.
|
14. Reboot the ESP, either by the reset switch or pulling the power.
|
||||||
15. The ESP will first perform an autodetect to try and discover the EMS devices attached. If your boiler and thermostat are recognized it will set these types and store them for ever and ever. You can trace the output by telnet'ing to the board `telnet ems-esp.local`. Also use `info` to check the status.
|
15. The ESP will first perform an autodetect to try and discover the EMS devices attached. If your boiler and thermostat are recognized it will set these types and store them for ever and ever. You can trace the output by telnet'ing to the board `telnet ems-esp.local`. Also use `info` to check the status.
|
||||||
16. If your boiler/thermostat is not discovered create a GitHub issue stating the type and product ID. These will be added to the file `ems_devices.h` in a future release.
|
16. If your boiler/thermostat is not discovered create a GitHub issue stating the type and Product ID. These will be added to the file `ems_devices.h` in a future release.
|
||||||
17. If all is well and there is traffic on the EMS bus the onboard LED will stop blinking and be permanently on. If this is annoying you can disable with `set led off`. To see the EMS messages type `set log v` for verbose logging.
|
17. If all is well and there is traffic on the EMS bus the onboard LED will stop blinking and be permanently on. If this is annoying you can disable with `set led off`. To see the EMS messages type `set log v` for verbose logging.
|
||||||
18. And all is not well, check the wiring, make sure serial is off and look at the telnet session for errors. If in doubt, wipe the ESP with `pio run -t erase` and start again with step #3
|
18. And all is not well, check the wiring, make sure serial is off and look at the telnet session for errors. If in doubt, wipe the ESP with `pio run -t erase` and start again with step #3
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#ifndef MyEMS_h
|
#ifndef MyEMS_h
|
||||||
#define MyEMS_h
|
#define MyEMS_h
|
||||||
|
|
||||||
#define MYESP_VERSION "1.1.6b4"
|
#define MYESP_VERSION "1.1.6"
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ int16_t DS18::getRawValue(unsigned char index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return real value as a double
|
// return real value as a double
|
||||||
|
// The raw temperature data is in units of sixteenths of a degree, so the value must be divided by 16 in order to convert it to degrees.
|
||||||
double DS18::getValue(unsigned char index) {
|
double DS18::getValue(unsigned char index) {
|
||||||
double value = (float)getRawValue(index) / 16.0;
|
double value = (float)getRawValue(index) / 16.0;
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -161,56 +161,42 @@ char * _bool_to_char(char * s, uint8_t value) {
|
|||||||
|
|
||||||
// convert short (two bytes) to text value
|
// convert short (two bytes) to text value
|
||||||
// negative values are assumed stored as 1-compliment (https://medium.com/@LeeJulija/how-integers-are-stored-in-memory-using-twos-complement-5ba04d61a56c)
|
// negative values are assumed stored as 1-compliment (https://medium.com/@LeeJulija/how-integers-are-stored-in-memory-using-twos-complement-5ba04d61a56c)
|
||||||
char * _short_to_char(char * s, int16_t value, uint8_t div = 10) {
|
char * _short_to_char(char * s, int16_t value, uint8_t decimals = 1) {
|
||||||
// remove errors on invalid values
|
// remove errors on invalid values
|
||||||
if (abs(value) >= EMS_VALUE_SHORT_NOTSET) {
|
if (abs(value) >= EMS_VALUE_SHORT_NOTSET) {
|
||||||
strlcpy(s, "?", sizeof(s));
|
strlcpy(s, "?", sizeof(s));
|
||||||
return (s);
|
return (s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (div != 0) {
|
if (decimals == 0) {
|
||||||
|
itoa(value, s, 10);
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// floating point
|
||||||
char s2[5] = {0};
|
char s2[5] = {0};
|
||||||
// check for negative values
|
// check for negative values
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
strlcpy(s, "-", 2);
|
strlcpy(s, "-", 2);
|
||||||
strlcat(s, itoa(abs(value) / div, s2, 10), 5);
|
value = abs(value);
|
||||||
} else {
|
|
||||||
strlcpy(s, itoa(value / div, s2, 10), 5);
|
|
||||||
}
|
}
|
||||||
|
strlcpy(s, itoa(value / (decimals * 10), s2, 10), 5);
|
||||||
strlcat(s, ".", sizeof(s));
|
strlcat(s, ".", sizeof(s));
|
||||||
strlcat(s, itoa(abs(value) % div, s2, 10), 5);
|
strlcat(s, itoa(value % (decimals * 10), s2, 10), 5);
|
||||||
} else {
|
|
||||||
itoa(value, s, 10);
|
|
||||||
}
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert int (single byte) to text value
|
// takes a short value (2 bytes), converts to a fraction
|
||||||
char * _int_to_char(char * s, uint8_t value, uint8_t div = 0) {
|
// most values stored a s short are either *10 or *100
|
||||||
if (value == EMS_VALUE_INT_NOTSET) {
|
void _renderShortValue(const char * prefix, const char * postfix, int16_t value, uint8_t decimals = 1) {
|
||||||
strlcpy(s, "?", sizeof(s));
|
|
||||||
} else {
|
|
||||||
if (div != 0) {
|
|
||||||
char s2[5] = {0};
|
|
||||||
strlcpy(s, itoa(value / div, s2, 10), 5);
|
|
||||||
strlcat(s, ".", sizeof(s));
|
|
||||||
strlcat(s, itoa(value % div, s2, 10), 5);
|
|
||||||
} else {
|
|
||||||
itoa(value, s, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// takes an int value (1 byte), converts to a fraction
|
|
||||||
void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, uint8_t div = 0) {
|
|
||||||
char buffer[200] = {0};
|
char buffer[200] = {0};
|
||||||
char s[20] = {0};
|
char s[20] = {0};
|
||||||
strlcpy(buffer, " ", sizeof(buffer));
|
strlcpy(buffer, " ", sizeof(buffer));
|
||||||
strlcat(buffer, prefix, sizeof(buffer));
|
strlcat(buffer, prefix, sizeof(buffer));
|
||||||
strlcat(buffer, ": ", sizeof(buffer));
|
strlcat(buffer, ": ", sizeof(buffer));
|
||||||
|
|
||||||
strlcat(buffer, _int_to_char(s, value, div), sizeof(buffer));
|
strlcat(buffer, _short_to_char(s, value, decimals), sizeof(buffer));
|
||||||
|
|
||||||
if (postfix != NULL) {
|
if (postfix != NULL) {
|
||||||
strlcat(buffer, " ", sizeof(buffer));
|
strlcat(buffer, " ", sizeof(buffer));
|
||||||
@@ -220,15 +206,49 @@ void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, u
|
|||||||
myDebug(buffer);
|
myDebug(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// takes a short value (2 bytes), converts to a fraction
|
// convert int (single byte) to text value
|
||||||
void _renderShortValue(const char * prefix, const char * postfix, int16_t value, uint8_t div = 10) {
|
char * _int_to_char(char * s, uint8_t value, uint8_t div = 1) {
|
||||||
|
if (value == EMS_VALUE_INT_NOTSET) {
|
||||||
|
strlcpy(s, "?", sizeof(s));
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
char s2[5] = {0};
|
||||||
|
|
||||||
|
switch (div) {
|
||||||
|
case 1:
|
||||||
|
itoa(value, s, 10);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
strlcpy(s, itoa(value >> 1, s2, 10), 5);
|
||||||
|
strlcat(s, ".", sizeof(s));
|
||||||
|
strlcat(s, ((value & 0x01) ? "5" : "0"), 5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 10:
|
||||||
|
strlcpy(s, itoa(value / 10, s2, 10), 5);
|
||||||
|
strlcat(s, ".", sizeof(s));
|
||||||
|
strlcat(s, itoa(value % 10, s2, 10), 5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
itoa(value, s, 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes an int value (1 byte), converts to a fraction
|
||||||
|
void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, uint8_t div = 1) {
|
||||||
char buffer[200] = {0};
|
char buffer[200] = {0};
|
||||||
char s[20] = {0};
|
char s[20] = {0};
|
||||||
strlcpy(buffer, " ", sizeof(buffer));
|
strlcpy(buffer, " ", sizeof(buffer));
|
||||||
strlcat(buffer, prefix, sizeof(buffer));
|
strlcat(buffer, prefix, sizeof(buffer));
|
||||||
strlcat(buffer, ": ", sizeof(buffer));
|
strlcat(buffer, ": ", sizeof(buffer));
|
||||||
|
|
||||||
strlcat(buffer, _short_to_char(s, value, div), sizeof(buffer));
|
strlcat(buffer, _int_to_char(s, value, div), sizeof(buffer));
|
||||||
|
|
||||||
if (postfix != NULL) {
|
if (postfix != NULL) {
|
||||||
strlcat(buffer, " ", sizeof(buffer));
|
strlcat(buffer, " ", sizeof(buffer));
|
||||||
@@ -422,11 +442,16 @@ void showInfo() {
|
|||||||
if ((ems_getThermostatModel() == EMS_MODEL_EASY) || (ems_getThermostatModel() == EMS_MODEL_BOSCHEASY)) {
|
if ((ems_getThermostatModel() == EMS_MODEL_EASY) || (ems_getThermostatModel() == EMS_MODEL_BOSCHEASY)) {
|
||||||
// for easy temps are * 100
|
// for easy temps are * 100
|
||||||
// also we don't have the time or mode
|
// also we don't have the time or mode
|
||||||
_renderShortValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 100);
|
_renderShortValue("Set room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 10);
|
||||||
_renderShortValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 100);
|
|
||||||
} else {
|
|
||||||
_renderShortValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 2);
|
|
||||||
_renderShortValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 10);
|
_renderShortValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 10);
|
||||||
|
} else {
|
||||||
|
// because we store in 2 bytes short, when converting to a single byte we'll loose the negative value if its unset
|
||||||
|
if ((EMS_Thermostat.setpoint_roomTemp <= 0) || (EMS_Thermostat.curr_roomTemp <= 0)) {
|
||||||
|
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_INT_NOTSET;
|
||||||
|
EMS_Thermostat.curr_roomTemp = EMS_VALUE_INT_NOTSET;
|
||||||
|
}
|
||||||
|
_renderIntValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 2); // convert to a single byte * 2
|
||||||
|
_renderIntValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 10); // is *10
|
||||||
|
|
||||||
myDebug(" Thermostat time is %02d:%02d:%02d %d/%d/%d",
|
myDebug(" Thermostat time is %02d:%02d:%02d %d/%d/%d",
|
||||||
EMS_Thermostat.hour,
|
EMS_Thermostat.hour,
|
||||||
@@ -451,12 +476,12 @@ void showInfo() {
|
|||||||
|
|
||||||
// Dallas
|
// Dallas
|
||||||
if (EMSESP_Status.dallas_sensors != 0) {
|
if (EMSESP_Status.dallas_sensors != 0) {
|
||||||
char s[80] = {0};
|
//char s[80] = {0};
|
||||||
char buffer[128] = {0};
|
char buffer[128] = {0};
|
||||||
|
char valuestr[8] = {0}; // for formatting temp
|
||||||
myDebug("%sExternal temperature sensors:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
myDebug("%sExternal temperature sensors:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||||
for (uint8_t i = 0; i < EMSESP_Status.dallas_sensors; i++) {
|
for (uint8_t i = 0; i < EMSESP_Status.dallas_sensors; i++) {
|
||||||
snprintf(s, sizeof(s), "Sensor #%d %s", i + 1, ds18.getDeviceString(buffer, i));
|
myDebug(" Sensor #%d %s: %s C", i + 1, ds18.getDeviceString(buffer, i), _float_to_char(valuestr, ds18.getValue(i) ));
|
||||||
_renderShortValue(s, "C", ds18.getRawValue(i), 16); // divide by 16
|
|
||||||
}
|
}
|
||||||
myDebug(""); // newline
|
myDebug(""); // newline
|
||||||
}
|
}
|
||||||
@@ -525,8 +550,7 @@ void publishValues(bool force) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rootBoiler["wWCurTmp"] = _short_to_char(s, EMS_Boiler.wWCurTmp);
|
rootBoiler["wWCurTmp"] = _short_to_char(s, EMS_Boiler.wWCurTmp);
|
||||||
snprintf(s, sizeof(s), "%i.%i", EMS_Boiler.wWCurFlow / 10, EMS_Boiler.wWCurFlow % 10);
|
rootBoiler["wWCurFlow"] = _int_to_char(s, EMS_Boiler.wWCurFlow, 10);
|
||||||
rootBoiler["wWCurFlow"] = s;
|
|
||||||
rootBoiler["wWHeat"] = _bool_to_char(s, EMS_Boiler.wWHeat);
|
rootBoiler["wWHeat"] = _bool_to_char(s, EMS_Boiler.wWHeat);
|
||||||
rootBoiler["curFlowTemp"] = _short_to_char(s, EMS_Boiler.curFlowTemp);
|
rootBoiler["curFlowTemp"] = _short_to_char(s, EMS_Boiler.curFlowTemp);
|
||||||
rootBoiler["retTemp"] = _short_to_char(s, EMS_Boiler.retTemp);
|
rootBoiler["retTemp"] = _short_to_char(s, EMS_Boiler.retTemp);
|
||||||
@@ -571,7 +595,7 @@ void publishValues(bool force) {
|
|||||||
// handle the thermostat values separately
|
// handle the thermostat values separately
|
||||||
if (ems_getThermostatEnabled()) {
|
if (ems_getThermostatEnabled()) {
|
||||||
// only send thermostat values if we actually have them
|
// only send thermostat values if we actually have them
|
||||||
if ((EMS_Thermostat.curr_roomTemp == 0) || (EMS_Thermostat.setpoint_roomTemp == 0))
|
if ((EMS_Thermostat.curr_roomTemp <= 0) || (EMS_Thermostat.setpoint_roomTemp <= 0))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// build new json object
|
// build new json object
|
||||||
@@ -579,11 +603,11 @@ void publishValues(bool force) {
|
|||||||
JsonObject rootThermostat = doc.to<JsonObject>();
|
JsonObject rootThermostat = doc.to<JsonObject>();
|
||||||
|
|
||||||
if ((ems_getThermostatModel() == EMS_MODEL_EASY) || (ems_getThermostatModel() == EMS_MODEL_BOSCHEASY)) {
|
if ((ems_getThermostatModel() == EMS_MODEL_EASY) || (ems_getThermostatModel() == EMS_MODEL_BOSCHEASY)) {
|
||||||
rootThermostat[THERMOSTAT_CURRTEMP] = _short_to_char(s, EMS_Thermostat.curr_roomTemp, 100);
|
rootThermostat[THERMOSTAT_SELTEMP] = _short_to_char(s, EMS_Thermostat.setpoint_roomTemp, 10);
|
||||||
rootThermostat[THERMOSTAT_SELTEMP] = _short_to_char(s, EMS_Thermostat.setpoint_roomTemp, 100);
|
|
||||||
} else {
|
|
||||||
rootThermostat[THERMOSTAT_CURRTEMP] = _short_to_char(s, EMS_Thermostat.curr_roomTemp, 10);
|
rootThermostat[THERMOSTAT_CURRTEMP] = _short_to_char(s, EMS_Thermostat.curr_roomTemp, 10);
|
||||||
rootThermostat[THERMOSTAT_SELTEMP] = _short_to_char(s, EMS_Thermostat.setpoint_roomTemp, 2);
|
} else {
|
||||||
|
rootThermostat[THERMOSTAT_SELTEMP] = _int_to_char(s, EMS_Thermostat.setpoint_roomTemp, 2);
|
||||||
|
rootThermostat[THERMOSTAT_CURRTEMP] = _int_to_char(s, EMS_Thermostat.curr_roomTemp, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
// RC20 has different mode settings
|
// RC20 has different mode settings
|
||||||
@@ -701,6 +725,61 @@ char * _readWord() {
|
|||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// publish external dallas sensor temperature values to MQTT
|
||||||
|
void do_publishSensorValues() {
|
||||||
|
if (EMSESP_Status.dallas_sensors != 0) {
|
||||||
|
publishSensorValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()) && (!myESP.getUseSerial()) && myESP.isMQTTConnected()) {
|
||||||
|
publishValues(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 << EMSESP_Status.led_gpio)); // 4 is on, 8 is off
|
||||||
|
void do_ledcheck() {
|
||||||
|
if (EMSESP_Status.led) {
|
||||||
|
if (ems_getBusConnected()) {
|
||||||
|
digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
|
||||||
|
} else {
|
||||||
|
int state = digitalRead(EMSESP_Status.led_gpio);
|
||||||
|
digitalWrite(EMSESP_Status.led_gpio, !state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thermostat scan
|
||||||
|
void do_scanThermostat() {
|
||||||
|
if ((ems_getBusConnected()) && (!myESP.getUseSerial())) {
|
||||||
|
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()) && (!myESP.getUseSerial())) {
|
||||||
|
myDebug("Error! Unable to read from EMS bus. 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()) && (!myESP.getUseSerial())) {
|
||||||
|
myDebugLog("Calling scheduled data refresh from EMS devices...");
|
||||||
|
ems_getThermostatValues();
|
||||||
|
ems_getBoilerValues();
|
||||||
|
ems_getOtherValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// initiate a force scan by sending type read requests from 0 to FF to the thermostat
|
// initiate a force scan by sending type read requests from 0 to FF to the thermostat
|
||||||
// used to analyze responses for debugging
|
// used to analyze responses for debugging
|
||||||
void startThermostatScan(uint8_t start) {
|
void startThermostatScan(uint8_t start) {
|
||||||
@@ -713,6 +792,27 @@ void startThermostatScan(uint8_t start) {
|
|||||||
scanThermostat.attach(SCANTHERMOSTAT_TIME, do_scanThermostat);
|
scanThermostat.attach(SCANTHERMOSTAT_TIME, do_scanThermostat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// turn back on the hot water for the shower
|
||||||
|
void _showerColdShotStop() {
|
||||||
|
if (EMSESP_Shower.doingColdShot) {
|
||||||
|
myDebugLog("[Shower] finished shot of cold. hot water back on");
|
||||||
|
ems_setWarmTapWaterActivated(true);
|
||||||
|
EMSESP_Shower.doingColdShot = false;
|
||||||
|
showerColdShotStopTimer.detach(); // disable the timer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn off hot water to send a shot of cold
|
||||||
|
void _showerColdShotStart() {
|
||||||
|
if (EMSESP_Status.shower_alert) {
|
||||||
|
myDebugLog("[Shower] doing a shot of cold water");
|
||||||
|
ems_setWarmTapWaterActivated(false);
|
||||||
|
EMSESP_Shower.doingColdShot = true;
|
||||||
|
// start the timer for n seconds which will reset the water back to hot
|
||||||
|
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// callback for loading/saving settings to the file system (SPIFFS)
|
// callback for loading/saving settings to the file system (SPIFFS)
|
||||||
bool FSCallback(MYESP_FSACTION action, const JsonObject json) {
|
bool FSCallback(MYESP_FSACTION action, const JsonObject json) {
|
||||||
bool recreate_config = true;
|
bool recreate_config = true;
|
||||||
@@ -1235,82 +1335,6 @@ void initEMSESP() {
|
|||||||
EMSESP_Shower.doingColdShot = false;
|
EMSESP_Shower.doingColdShot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish external dallas sensor temperature values to MQTT
|
|
||||||
void do_publishSensorValues() {
|
|
||||||
if (EMSESP_Status.dallas_sensors != 0) {
|
|
||||||
publishSensorValues();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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()) && (!myESP.getUseSerial()) && myESP.isMQTTConnected()) {
|
|
||||||
publishValues(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 << EMSESP_Status.led_gpio)); // 4 is on, 8 is off
|
|
||||||
void do_ledcheck() {
|
|
||||||
if (EMSESP_Status.led) {
|
|
||||||
if (ems_getBusConnected()) {
|
|
||||||
digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
|
|
||||||
} else {
|
|
||||||
int state = digitalRead(EMSESP_Status.led_gpio);
|
|
||||||
digitalWrite(EMSESP_Status.led_gpio, !state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thermostat scan
|
|
||||||
void do_scanThermostat() {
|
|
||||||
if ((ems_getBusConnected()) && (!myESP.getUseSerial())) {
|
|
||||||
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()) && (!myESP.getUseSerial())) {
|
|
||||||
myDebug("Error! Unable to read from EMS bus. 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()) && (!myESP.getUseSerial())) {
|
|
||||||
myDebugLog("Calling scheduled data refresh from EMS devices...");
|
|
||||||
ems_getThermostatValues();
|
|
||||||
ems_getBoilerValues();
|
|
||||||
ems_getOtherValues();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// turn off hot water to send a shot of cold
|
|
||||||
void _showerColdShotStart() {
|
|
||||||
if (EMSESP_Status.shower_alert) {
|
|
||||||
myDebugLog("[Shower] doing a shot of cold water");
|
|
||||||
ems_setWarmTapWaterActivated(false);
|
|
||||||
EMSESP_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 (EMSESP_Shower.doingColdShot) {
|
|
||||||
myDebugLog("[Shower] finished shot of cold. hot water back on");
|
|
||||||
ems_setWarmTapWaterActivated(true);
|
|
||||||
EMSESP_Shower.doingColdShot = false;
|
|
||||||
showerColdShotStopTimer.detach(); // disable the timer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Shower Logic
|
* Shower Logic
|
||||||
*/
|
*/
|
||||||
41
src/ems.cpp
41
src/ems.cpp
@@ -14,11 +14,6 @@
|
|||||||
#include <MyESP.h>
|
#include <MyESP.h>
|
||||||
#include <list> // std::list
|
#include <list> // std::list
|
||||||
|
|
||||||
#define _toByte(i) (data[i])
|
|
||||||
#define _toShort(i) ((data[i] << 8) + data[i + 1])
|
|
||||||
#define _toLong(i) ((data[i] << 16) + (data[i + 1] << 8) + (data[i + 2]))
|
|
||||||
#define _bitRead(i, bit) (((data[i]) >> (bit)) & 0x01)
|
|
||||||
|
|
||||||
// myESP for logging to telnet and serial
|
// myESP for logging to telnet and serial
|
||||||
#define myDebug(...) myESP.myDebug(__VA_ARGS__)
|
#define myDebug(...) myESP.myDebug(__VA_ARGS__)
|
||||||
|
|
||||||
@@ -26,7 +21,15 @@ _EMS_Sys_Status EMS_Sys_Status; // EMS Status
|
|||||||
|
|
||||||
CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO queue for Tx send buffer
|
CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO queue for Tx send buffer
|
||||||
|
|
||||||
// callbacks per type
|
//
|
||||||
|
// process callbacks per type
|
||||||
|
//
|
||||||
|
|
||||||
|
// macros used in the _process* functions
|
||||||
|
#define _toByte(i) (data[i])
|
||||||
|
#define _toShort(i) ((data[i] << 8) + data[i + 1])
|
||||||
|
#define _toLong(i) ((data[i] << 16) + (data[i + 1] << 8) + (data[i + 2]))
|
||||||
|
#define _bitRead(i, bit) (((data[i]) >> (bit)) & 0x01)
|
||||||
|
|
||||||
// generic
|
// generic
|
||||||
void _process_Version(uint8_t src, uint8_t * data, uint8_t length);
|
void _process_Version(uint8_t src, uint8_t * data, uint8_t length);
|
||||||
@@ -1191,7 +1194,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
|
|||||||
|
|
||||||
if (typeFound) {
|
if (typeFound) {
|
||||||
// its a boiler
|
// its a boiler
|
||||||
myDebug("Boiler found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
myDebug("Boiler found. Model %s with TypeID 0x%02X, ProductID %d, Version %s",
|
||||||
Boiler_Types[i].model_string,
|
Boiler_Types[i].model_string,
|
||||||
Boiler_Types[i].type_id,
|
Boiler_Types[i].type_id,
|
||||||
product_id,
|
product_id,
|
||||||
@@ -1200,7 +1203,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
|
|||||||
// if its a boiler set it
|
// if its a boiler set it
|
||||||
// it will take the first one found in the list
|
// it will take the first one found in the list
|
||||||
if ((EMS_Boiler.type_id == EMS_ID_NONE) || (EMS_Boiler.type_id == Boiler_Types[i].type_id)) {
|
if ((EMS_Boiler.type_id == EMS_ID_NONE) || (EMS_Boiler.type_id == Boiler_Types[i].type_id)) {
|
||||||
myDebug("* Setting Boiler type to Model %s, TypeID 0x%02X, Product ID %d, Version %s",
|
myDebug("* Setting Boiler type to Model %s, TypeID 0x%02X, ProductID %d, Version %s",
|
||||||
Boiler_Types[i].model_string,
|
Boiler_Types[i].model_string,
|
||||||
Boiler_Types[i].type_id,
|
Boiler_Types[i].type_id,
|
||||||
product_id,
|
product_id,
|
||||||
@@ -1230,7 +1233,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
|
|||||||
if (typeFound) {
|
if (typeFound) {
|
||||||
// its a known thermostat
|
// its a known thermostat
|
||||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||||
myDebug("Thermostat found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
myDebug("Thermostat found. Model %s with TypeID 0x%02X, ProductID %d, Version %s",
|
||||||
Thermostat_Types[i].model_string,
|
Thermostat_Types[i].model_string,
|
||||||
Thermostat_Types[i].type_id,
|
Thermostat_Types[i].type_id,
|
||||||
product_id,
|
product_id,
|
||||||
@@ -1240,7 +1243,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
|
|||||||
// if we don't have a thermostat set, use this one
|
// if we don't have a thermostat set, use this one
|
||||||
if ((EMS_Thermostat.type_id == EMS_ID_NONE) || (EMS_Thermostat.model_id == EMS_MODEL_NONE)
|
if ((EMS_Thermostat.type_id == EMS_ID_NONE) || (EMS_Thermostat.model_id == EMS_MODEL_NONE)
|
||||||
|| (EMS_Thermostat.type_id == Thermostat_Types[i].type_id)) {
|
|| (EMS_Thermostat.type_id == Thermostat_Types[i].type_id)) {
|
||||||
myDebug("* Setting Thermostat type to Model %s, TypeID 0x%02X, Product ID %d, Version %s",
|
myDebug("* Setting Thermostat type to Model %s, TypeID 0x%02X, ProductID %d, Version %s",
|
||||||
Thermostat_Types[i].model_string,
|
Thermostat_Types[i].model_string,
|
||||||
Thermostat_Types[i].type_id,
|
Thermostat_Types[i].type_id,
|
||||||
product_id,
|
product_id,
|
||||||
@@ -1272,7 +1275,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeFound) {
|
if (typeFound) {
|
||||||
myDebug("Device found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
myDebug("Device found. Model %s with TypeID 0x%02X, ProductID %d, Version %s",
|
||||||
Other_Types[i].model_string,
|
Other_Types[i].model_string,
|
||||||
Other_Types[i].type_id,
|
Other_Types[i].type_id,
|
||||||
product_id,
|
product_id,
|
||||||
@@ -1289,7 +1292,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
myDebug("Unrecognized device found. TypeID 0x%02X, Product ID %d, Version %s", src, product_id, version);
|
myDebug("Unrecognized device found. TypeID 0x%02X, ProductID %d, Version %s", src, product_id, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1339,7 +1342,7 @@ void _ems_setThermostatModel(uint8_t thermostat_modelid) {
|
|||||||
|
|
||||||
// set the thermostat
|
// set the thermostat
|
||||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||||
myDebug("Setting Thermostat. Model %s with TypeID 0x%02X, Product ID %d",
|
myDebug("Setting Thermostat. Model %s with TypeID 0x%02X, ProductID %d",
|
||||||
thermostat_type->model_string,
|
thermostat_type->model_string,
|
||||||
thermostat_type->type_id,
|
thermostat_type->type_id,
|
||||||
thermostat_type->product_id);
|
thermostat_type->product_id);
|
||||||
@@ -1485,7 +1488,7 @@ char * ems_getThermostatDescription(char * buffer) {
|
|||||||
strlcat(buffer, _hextoa(EMS_Thermostat.type_id, tmp), size);
|
strlcat(buffer, _hextoa(EMS_Thermostat.type_id, tmp), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
strlcat(buffer, " (Product ID:", size);
|
strlcat(buffer, " (ProductID:", size);
|
||||||
strlcat(buffer, itoa(EMS_Thermostat.product_id, tmp, 10), size);
|
strlcat(buffer, itoa(EMS_Thermostat.product_id, tmp, 10), size);
|
||||||
strlcat(buffer, " Version:", size);
|
strlcat(buffer, " Version:", size);
|
||||||
strlcat(buffer, EMS_Thermostat.version, size);
|
strlcat(buffer, EMS_Thermostat.version, size);
|
||||||
@@ -1522,7 +1525,7 @@ char * ems_getBoilerDescription(char * buffer) {
|
|||||||
strlcat(buffer, _hextoa(EMS_Boiler.type_id, tmp), size);
|
strlcat(buffer, _hextoa(EMS_Boiler.type_id, tmp), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
strlcat(buffer, " (Product ID:", size);
|
strlcat(buffer, " (ProductID:", size);
|
||||||
strlcat(buffer, itoa(EMS_Boiler.product_id, tmp, 10), size);
|
strlcat(buffer, itoa(EMS_Boiler.product_id, tmp, 10), size);
|
||||||
strlcat(buffer, " Version:", size);
|
strlcat(buffer, " Version:", size);
|
||||||
strlcat(buffer, EMS_Boiler.version, size);
|
strlcat(buffer, EMS_Boiler.version, size);
|
||||||
@@ -1574,12 +1577,12 @@ void ems_printAllTypes() {
|
|||||||
|
|
||||||
myDebug("\nThese %d boiler type devices are in the library:", _Boiler_Types_max);
|
myDebug("\nThese %d boiler type devices are in the library:", _Boiler_Types_max);
|
||||||
for (i = 0; i < _Boiler_Types_max; i++) {
|
for (i = 0; i < _Boiler_Types_max; i++) {
|
||||||
myDebug(" %s, type ID:0x%02X Product ID:%d", Boiler_Types[i].model_string, Boiler_Types[i].type_id, Boiler_Types[i].product_id);
|
myDebug(" %s, type ID:0x%02X ProductID:%d", Boiler_Types[i].model_string, Boiler_Types[i].type_id, Boiler_Types[i].product_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
myDebug("\nThese %d EMS devices are in the library:", _Other_Types_max);
|
myDebug("\nThese %d EMS devices are in the library:", _Other_Types_max);
|
||||||
for (i = 0; i < _Other_Types_max; i++) {
|
for (i = 0; i < _Other_Types_max; i++) {
|
||||||
myDebug(" %s, type ID:0x%02X Product ID:%d", Other_Types[i].model_string, Other_Types[i].type_id, Other_Types[i].product_id);
|
myDebug(" %s, type ID:0x%02X ProductID:%d", Other_Types[i].model_string, Other_Types[i].type_id, Other_Types[i].product_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
myDebug("\nThese telegram type IDs are recognized for the selected boiler:");
|
myDebug("\nThese telegram type IDs are recognized for the selected boiler:");
|
||||||
@@ -1591,7 +1594,7 @@ void ems_printAllTypes() {
|
|||||||
|
|
||||||
myDebug("\nThese %d thermostats models are supported:", _Thermostat_Types_max);
|
myDebug("\nThese %d thermostats models are supported:", _Thermostat_Types_max);
|
||||||
for (i = 0; i < _Thermostat_Types_max; i++) {
|
for (i = 0; i < _Thermostat_Types_max; i++) {
|
||||||
myDebug(" %s, type ID:0x%02X Product ID:%d Read/Write support:%c%c",
|
myDebug(" %s, type ID:0x%02X ProductID:%d Read/Write support:%c%c",
|
||||||
Thermostat_Types[i].model_string,
|
Thermostat_Types[i].model_string,
|
||||||
Thermostat_Types[i].type_id,
|
Thermostat_Types[i].type_id,
|
||||||
Thermostat_Types[i].product_id,
|
Thermostat_Types[i].product_id,
|
||||||
@@ -1940,7 +1943,7 @@ void ems_setWarmTapWaterActivated(bool activated) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start up sequence for UBA Master
|
* Start up sequence for UBA Master, hopefully to initialize a handshake
|
||||||
* Still experimental
|
* Still experimental
|
||||||
*/
|
*/
|
||||||
void ems_startupTelegrams() {
|
void ems_startupTelegrams() {
|
||||||
|
|||||||
@@ -6,5 +6,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define APP_NAME "EMS-ESP"
|
#define APP_NAME "EMS-ESP"
|
||||||
#define APP_VERSION "1.6.0b5"
|
#define APP_VERSION "1.6.0b6"
|
||||||
#define APP_HOSTNAME "ems-esp"
|
#define APP_HOSTNAME "ems-esp"
|
||||||
|
|||||||
Reference in New Issue
Block a user