mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 01:09:51 +03:00
version 1.3.0
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
/*
|
||||
* EMS-ESP-Boiler
|
||||
* Paul Derbyshire - May 2018 - https://github.com/proddy/EMS-ESP-Boiler
|
||||
* https://community.home-assistant.io/t/thermostat-and-boiler-controller-for-ems-based-boilers-nefit-buderus-bosch-using-esp/53382
|
||||
* EMS-ESP
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
* See README for Acknowledgments
|
||||
* 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"
|
||||
@@ -45,6 +47,8 @@ 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
|
||||
@@ -65,19 +69,11 @@ Ticker showerColdShotStopTimer;
|
||||
#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
|
||||
|
||||
// shower settings for DEBUGGING only
|
||||
#ifdef SHOWER_TEST
|
||||
#undef SHOWER_PAUSE_TIME
|
||||
#undef SHOWER_MIN_DURATION
|
||||
#undef SHOWER_MAX_DURATION
|
||||
#undef SHOWER_COLDSHOT_DURATION
|
||||
#undef SHOWER_OFFSET_TIME
|
||||
const unsigned long SHOWER_PAUSE_TIME = 15000; // 15 seconds, max time if water is switched off & on during a shower
|
||||
const unsigned long SHOWER_MIN_DURATION = 20000; // 20 secs, before recognizing its a shower
|
||||
const unsigned long SHOWER_MAX_DURATION = 25000; // 25 secs, before trigger a shot of cold water
|
||||
const unsigned long SHOWER_COLDSHOT_DURATION = 5; // in seconds! how long for cold water shot
|
||||
const unsigned long SHOWER_OFFSET_TIME = 0; // 0 seconds grace time, to calibrate actual time under the shower
|
||||
#endif
|
||||
// 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
|
||||
@@ -94,24 +90,23 @@ typedef struct {
|
||||
|
||||
command_t PROGMEM project_cmds[] = {
|
||||
|
||||
{"l [n]", "set logging (0=none, 1=raw, 2=basic, 3=thermostat only, 4=verbose)"},
|
||||
{"s", "show statistics"},
|
||||
{"D", "scan EMS connected Devices"},
|
||||
{"h", "list supported EMS telegram type IDs"},
|
||||
{"M", "publish to MQTT"},
|
||||
{"Q", "print Tx Queue"},
|
||||
{"U [n]", "do a deep scan of all thermostat message types, starting at n"},
|
||||
{"P", "toggle EMS Poll response on/off"},
|
||||
{"X", "toggle EMS Tx transmission on/off"},
|
||||
{"S", "toggle Shower timer on/off"},
|
||||
{"A", "toggle shower Alert on/off"},
|
||||
{"r [s]", "send raw telegram in hex to EMS (s=XX XX XX...)"},
|
||||
{"b [xx]", "send boiler read request (xx=telegram type ID in hex)"},
|
||||
{"t [xx]", "send thermostat read request (xx=telegram type ID in hex)"},
|
||||
{"w [n]", "set boiler warm water temperature (min 30)"},
|
||||
{"a [n]", "set boiler warm tap water (0=off, 1=on)"},
|
||||
{"T [n]", "set thermostat temperature"},
|
||||
{"m [n]", "set thermostat mode (0=low/night, 1=manual/day, 2=auto)"}
|
||||
{"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"}
|
||||
|
||||
};
|
||||
|
||||
@@ -127,8 +122,6 @@ uint32_t previousThermostatPublishCRC = 0;
|
||||
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
|
||||
|
||||
unsigned long timestamp; // for internal timings, via millis()
|
||||
|
||||
uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
|
||||
|
||||
// logging messages with fixed strings
|
||||
@@ -269,7 +262,7 @@ void _renderBoolValue(const char * prefix, uint8_t value) {
|
||||
void showInfo() {
|
||||
// General stats from EMS bus
|
||||
|
||||
myDebug("%sEMS-ESP-Boiler system stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
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");
|
||||
@@ -519,116 +512,203 @@ void publishValues(bool force) {
|
||||
// sets the shower timer on/off
|
||||
void set_showerTimer() {
|
||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||
myDebug("Shower timer is %s", Boiler_Status.shower_timer ? "enabled" : "disabled");
|
||||
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 is %s", Boiler_Status.shower_alert ? "enabled" : "disabled");
|
||||
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
|
||||
void myDebugCallback() {
|
||||
char * cmd = myESP.consoleGetLastCommand();
|
||||
uint8_t len = strlen(cmd);
|
||||
bool b;
|
||||
// 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");
|
||||
|
||||
// look for single letter commands
|
||||
if (len == 1) {
|
||||
switch (cmd[0]) {
|
||||
case 's':
|
||||
showInfo();
|
||||
break;
|
||||
case 'P': // toggle Poll
|
||||
b = !ems_getPoll();
|
||||
ems_setPoll(b);
|
||||
break;
|
||||
case 'X': // toggle Tx
|
||||
b = !ems_getTxEnabled();
|
||||
ems_setTxEnabled(b);
|
||||
break;
|
||||
case 'M':
|
||||
publishValues(true);
|
||||
break;
|
||||
case 'h': // show type handlers
|
||||
ems_printAllTypes();
|
||||
break;
|
||||
case 'S': // toggle Shower timer support
|
||||
Boiler_Status.shower_timer = !Boiler_Status.shower_timer;
|
||||
myESP.mqttPublish(TOPIC_SHOWER_TIMER, Boiler_Status.shower_timer ? "1" : "0");
|
||||
break;
|
||||
case 'A': // toggle Shower alert
|
||||
Boiler_Status.shower_alert = !Boiler_Status.shower_alert;
|
||||
myESP.mqttPublish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
||||
break;
|
||||
case 'Q': // print Tx Queue
|
||||
ems_printTxQueue();
|
||||
break;
|
||||
case 'D': // Auto detect EMS devices
|
||||
ems_scanDevices();
|
||||
break;
|
||||
default:
|
||||
myDebug("Unknown command. Use ? for help.");
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// for commands with parameters, assume command is just a single letter followed by a space
|
||||
switch (cmd[0]) {
|
||||
case 'T': // set thermostat temp
|
||||
ems_setThermostatTemp(strtof(&cmd[2], 0));
|
||||
break;
|
||||
case 'm': // set thermostat mode
|
||||
ems_setThermostatMode(cmd[2] - '0');
|
||||
break;
|
||||
case 'w': // set warm water temp
|
||||
ems_setWarmWaterTemp((uint8_t)strtol(&cmd[2], 0, 10));
|
||||
break;
|
||||
case 'l': // logging
|
||||
ems_setLogging((_EMS_SYS_LOGGING)(cmd[2] - '0'));
|
||||
break;
|
||||
case 'a': // set ww activate on or off
|
||||
if ((cmd[2] - '0') == 1)
|
||||
ems_setWarmTapWaterActivated(true);
|
||||
else if ((cmd[2] - '0') == 0)
|
||||
ems_setWarmTapWaterActivated(false);
|
||||
break;
|
||||
case 'b': // boiler read command
|
||||
ems_doReadCommand((uint8_t)strtol(&cmd[2], 0, 16), EMS_Boiler.type_id);
|
||||
break;
|
||||
case 't': // thermostat command
|
||||
ems_doReadCommand((uint8_t)strtol(&cmd[2], 0, 16), EMS_Thermostat.type_id);
|
||||
break;
|
||||
case 'r': // send raw data
|
||||
ems_sendRawTelegram(&cmd[2]);
|
||||
break;
|
||||
case 'x': // experimental, not displayed!
|
||||
myDebug("Calling experimental...");
|
||||
ems_setLogging(EMS_SYS_LOGGING_VERBOSE);
|
||||
ems_setExperimental((uint8_t)strtol(&cmd[2], 0, 16)); // takes HEX param
|
||||
break;
|
||||
case 'U': // thermostat scan
|
||||
ems_setLogging(EMS_SYS_LOGGING_THERMOSTAT);
|
||||
publishValuesTimer.detach();
|
||||
systemCheckTimer.detach();
|
||||
regularUpdatesTimer.detach();
|
||||
scanThermostat_count = (uint8_t)strtol(&cmd[2], 0, 16);
|
||||
myDebug("Starting a deep message scan on thermometer");
|
||||
scanThermostat.attach(SCANTHERMOSTAT_TIME, do_scanThermostat);
|
||||
break;
|
||||
default:
|
||||
myDebug("Unknown command. Use ? for help.");
|
||||
break;
|
||||
// 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.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// MQTT Callback to handle incoming/outgoing changes
|
||||
void MQTTcallback(unsigned int type, const char * topic, const char * message) {
|
||||
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);
|
||||
@@ -708,7 +788,7 @@ void WIFICallback() {
|
||||
|
||||
// Initialize the boiler settings
|
||||
void initShower() {
|
||||
// default showr settings
|
||||
// default shower settings
|
||||
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
|
||||
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
|
||||
Boiler_Shower.timerStart = 0;
|
||||
@@ -719,7 +799,10 @@ void initShower() {
|
||||
|
||||
// call PublishValues without forcing, so using CRC to see if we really need to publish
|
||||
void do_publishValues() {
|
||||
publishValues(false);
|
||||
// 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
|
||||
@@ -745,16 +828,20 @@ void do_scanThermostat() {
|
||||
// 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. Please make sure you're not in DEBUG_SUPPORT mode. Retrying in %d seconds...",
|
||||
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() {
|
||||
myDebugLog("Calling scheduled data refresh from EMS devices..");
|
||||
ems_getThermostatValues(); // get Thermostat temps (if supported)
|
||||
ems_getBoilerValues();
|
||||
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
|
||||
@@ -860,16 +947,20 @@ void setup() {
|
||||
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
|
||||
|
||||
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
||||
// with callbacks
|
||||
myESP.setup(APP_HOSTNAME, APP_NAME, APP_VERSION, WIFI_SSID, WIFI_PASSWORD, MQTT_IP, MQTT_USER, MQTT_PASS);
|
||||
myESP.consoleSetCallBackProjectCmds(project_cmds, ArraySize(project_cmds), myDebugCallback); // set up Telnet commands
|
||||
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.setMQTTCallback(MQTTCallback);
|
||||
myESP.setTelnetCallback(TelnetCallback);
|
||||
|
||||
// init Shower specific parameters
|
||||
initShower();
|
||||
|
||||
ems_setLogging(BOILER_DEFAULT_LOGGING); // set default logging. see my_config.h
|
||||
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
|
||||
@@ -892,7 +983,7 @@ void loop() {
|
||||
ems_setEmsRefreshed(false);
|
||||
}
|
||||
|
||||
// do shower logic if it is enabled
|
||||
// do shower logic, if enabled
|
||||
if (Boiler_Status.shower_timer) {
|
||||
showerCheck();
|
||||
}
|
||||
123
src/ems.cpp
123
src/ems.cpp
@@ -1,10 +1,18 @@
|
||||
/**
|
||||
* ems.cpp
|
||||
*
|
||||
* handles all the processing of the EMS messages
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP-Boiler
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
|
||||
#include "ems.h"
|
||||
#include "ems_devices.h"
|
||||
#include "emsuart.h"
|
||||
#include <Arduino.h>
|
||||
#include <CircularBuffer.h> // https://github.com/rlogiacco/CircularBuffer
|
||||
#include <MyESP.h>
|
||||
#include <list> // std::list
|
||||
|
||||
// myESP
|
||||
#define myDebug(...) myESP.myDebug(__VA_ARGS__)
|
||||
@@ -42,42 +50,9 @@ void _process_RC35StatusMessage(uint8_t * data, uint8_t length);
|
||||
// Easy
|
||||
void _process_EasyStatusMessage(uint8_t * data, uint8_t length);
|
||||
|
||||
// EMS types for known Buderus devices
|
||||
// Note: This is still incomplete
|
||||
const _Model_Type Model_Types[] = {
|
||||
|
||||
// me
|
||||
{EMS_MODEL_SERVICEKEY, 999, 0x0B, "Service Key"},
|
||||
|
||||
// various boilers and buderus type devices
|
||||
{EMS_MODEL_UBA, 123, 0x08, "MC10/UBA3 Boiler"}, // verified
|
||||
{EMS_MODEL_BK15, 64, 0x08, "Sieger BK15 Boiler"}, // verified
|
||||
{EMS_MODEL_BC10, 190, 0x09, "BC10 Base Controller"}, // verified
|
||||
{EMS_MODEL_MM10, 125, 0x21, "MM10 Mixer Module"}, // warning, fake product id!
|
||||
{EMS_MODEL_WM10, 126, 0x11, "WM10 Switch Module"}, // warning, fake product id!
|
||||
|
||||
// controllers and thermostats
|
||||
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73"},
|
||||
{EMS_MODEL_RC20, 77, 0x17, "RC20 (Nefit Moduline 300)"},
|
||||
{EMS_MODEL_RC30, 78, 0x10, "RC30 (Nefit Moduline 400)"},
|
||||
{EMS_MODEL_RC35, 86, 0x10, "RC35 (or compatible"},
|
||||
{EMS_MODEL_EASY, 202, 0x18, "TC100 (Nefit Easy/CT100)"}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Known thermostat types and their abilities
|
||||
* Recognized EMS types and the functions they call to process the telegrams
|
||||
*/
|
||||
const _Thermostat_Type Thermostat_Types[] = {
|
||||
|
||||
{EMS_MODEL_RC20, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC30, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC35, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_EASY, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO},
|
||||
{EMS_MODEL_ES73, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}
|
||||
|
||||
};
|
||||
|
||||
const _EMS_Type EMS_Types[] = {
|
||||
|
||||
// common
|
||||
@@ -93,13 +68,19 @@ const _EMS_Type EMS_Types[] = {
|
||||
{EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", NULL},
|
||||
{EMS_MODEL_UBA, EMS_TYPE_UBAMaintenanceStatusMessage, "UBAMaintenanceStatusMessage", NULL},
|
||||
|
||||
// RC20
|
||||
// RC20 and RC20F
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RC20Set, "RC20Set", _process_RC20Set},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RC20StatusMessage, "RC20StatusMessage", _process_RC20StatusMessage},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RC20Set, "RC20Set", _process_RC20Set},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RC20StatusMessage, "RC20StatusMessage", _process_RC20StatusMessage},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
// RC30
|
||||
{EMS_MODEL_RC30, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC30, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
@@ -125,7 +106,6 @@ const _EMS_Type EMS_Types[] = {
|
||||
{EMS_MODEL_EASY, EMS_TYPE_EasyStatusMessage, "EasyStatusMessage", _process_EasyStatusMessage},
|
||||
{EMS_MODEL_EASY, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints}
|
||||
|
||||
|
||||
};
|
||||
|
||||
// calculate sizes of arrays
|
||||
@@ -163,7 +143,7 @@ uint8_t _last_TxTelgramCRC; // CRC of last Tx sent, for checking duplicates
|
||||
|
||||
// init stats and counters and buffers
|
||||
// uses -255 or 255 for values that haven't been set yet (EMS_VALUE_INT_NOTSET and EMS_VALUE_FLOAT_NOTSET)
|
||||
void ems_init(_EMS_MODEL_ID boiler_modelid, _EMS_MODEL_ID thermostat_modelid) {
|
||||
void ems_init(uint8_t boiler_modelid, uint8_t thermostat_modelid) {
|
||||
// overall status
|
||||
EMS_Sys_Status.emsRxPgks = 0;
|
||||
EMS_Sys_Status.emsTxPkgs = 0;
|
||||
@@ -816,7 +796,7 @@ void _processType(uint8_t * telegram, uint8_t length) {
|
||||
// there is a match, so write must have been successful
|
||||
EMS_TxQueue.shift(); // remove validate from queue
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Write to 0x%02X successful.", EMS_TxTelegram.dest);
|
||||
myDebug("Write to 0x%02X was successful", EMS_TxTelegram.dest);
|
||||
}
|
||||
ems_doReadCommand(EMS_TxTelegram.comparisonPostRead,
|
||||
EMS_TxTelegram.dest,
|
||||
@@ -1102,7 +1082,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
ems_getThermostatValues(); // get Thermostat values (if supported)
|
||||
}
|
||||
} else {
|
||||
// otherwise assume its a boiler
|
||||
// otherwise assume its a boiler or some other EMS device
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Boiler recognized. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
||||
Model_Types[i].model_string,
|
||||
@@ -1125,7 +1105,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
* Given a MODEL_ID, look up its data and set either a Thermostat or Boiler
|
||||
* return false if not found or no need to set
|
||||
*/
|
||||
bool _ems_setModel(_EMS_MODEL_ID model_id) {
|
||||
bool _ems_setModel(uint8_t model_id) {
|
||||
if (model_id == EMS_MODEL_NONE) {
|
||||
return false; // invalid model_id
|
||||
}
|
||||
@@ -1301,8 +1281,8 @@ void ems_getThermostatValues() {
|
||||
return;
|
||||
}
|
||||
|
||||
_EMS_MODEL_ID model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
uint8_t model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
|
||||
if (model_id == EMS_MODEL_RC20) {
|
||||
ems_doReadCommand(EMS_TYPE_RC20StatusMessage, type); // to get the setpoint temp
|
||||
@@ -1332,7 +1312,7 @@ void ems_getBoilerValues() {
|
||||
|
||||
|
||||
// return pointer to Model details
|
||||
int _ems_findModel(_EMS_MODEL_ID model_id) {
|
||||
int _ems_findModel(uint8_t model_id) {
|
||||
uint8_t i = 0;
|
||||
bool found = false;
|
||||
|
||||
@@ -1351,7 +1331,7 @@ int _ems_findModel(_EMS_MODEL_ID model_id) {
|
||||
return i;
|
||||
}
|
||||
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, _EMS_MODEL_ID model_id) {
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, uint8_t model_id) {
|
||||
int i = _ems_findModel(model_id);
|
||||
if (i != -1) {
|
||||
char tmp[6] = {0};
|
||||
@@ -1394,12 +1374,12 @@ char * ems_getBoilerType(char * buffer) {
|
||||
}
|
||||
|
||||
// returns the model type for a thermostat
|
||||
_EMS_MODEL_ID ems_getThermostatModel() {
|
||||
uint8_t ems_getThermostatModel() {
|
||||
return (EMS_Thermostat.model_id);
|
||||
}
|
||||
|
||||
// returns the model type for a boiler
|
||||
_EMS_MODEL_ID ems_getBoilerModel() {
|
||||
uint8_t ems_getBoilerModel() {
|
||||
return (EMS_Boiler.model_id);
|
||||
}
|
||||
|
||||
@@ -1407,7 +1387,11 @@ _EMS_MODEL_ID ems_getBoilerModel() {
|
||||
* Find the versions of our connected devices
|
||||
*/
|
||||
void ems_scanDevices() {
|
||||
myDebug("Scanning EMS bus for devices. This may take a few seconds.");
|
||||
if (!ems_getBusConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
myDebug("Scanning EMS bus for devices. This may take a few seconds...");
|
||||
|
||||
// copy over the IDs from Model-type to a list
|
||||
std::list<uint8_t> Device_Ids;
|
||||
@@ -1430,7 +1414,7 @@ void ems_scanDevices() {
|
||||
* Print out all handled types
|
||||
*/
|
||||
void ems_printAllTypes() {
|
||||
myDebug("These %d telegram TypeIDs are recognized currently:", _EMS_Types_max);
|
||||
myDebug("These %d telegram TypeIDs are recognized:", _EMS_Types_max);
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < _EMS_Types_max; i++) {
|
||||
@@ -1444,7 +1428,7 @@ void ems_printAllTypes() {
|
||||
}
|
||||
}
|
||||
|
||||
myDebug("\nThese %d thermostats are natively supported:", _Thermostat_Types_max);
|
||||
myDebug("\nThese %d thermostats models are supported:", _Thermostat_Types_max);
|
||||
for (i = 0; i < _Thermostat_Types_max; i++) {
|
||||
// find the model's details
|
||||
for (int j = 0; j < _Model_Types_max; j++) {
|
||||
@@ -1503,6 +1487,7 @@ void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh) {
|
||||
|
||||
/**
|
||||
* Send a raw telegram to the bus
|
||||
* telegram is a string of hex values
|
||||
*/
|
||||
void ems_sendRawTelegram(char * telegram) {
|
||||
uint8_t count = 0;
|
||||
@@ -1558,8 +1543,8 @@ void ems_setThermostatTemp(float temperature) {
|
||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||
|
||||
_EMS_MODEL_ID model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
uint8_t model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
|
||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||
EMS_TxTelegram.dest = type;
|
||||
@@ -1606,8 +1591,8 @@ void ems_setThermostatMode(uint8_t mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
_EMS_MODEL_ID model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
uint8_t model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
|
||||
myDebug("Setting thermostat mode to %d", mode);
|
||||
|
||||
@@ -1756,33 +1741,3 @@ void ems_setWarmTapWaterActivated(bool activated) {
|
||||
|
||||
EMS_TxQueue.push(EMS_TxTelegram); // add to queue
|
||||
}
|
||||
|
||||
/**
|
||||
* experimental code for debugging - not in production
|
||||
*/
|
||||
void ems_setExperimental(uint8_t value) {
|
||||
/*
|
||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||
|
||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_READ; // read command
|
||||
EMS_TxTelegram.dest = EMS_Thermostat.type; // set 8th bit to indicate a read
|
||||
EMS_TxTelegram.offset = 0; // 0 for all data
|
||||
EMS_TxTelegram.length = 8;
|
||||
EMS_TxTelegram.type = 0xF0;
|
||||
EMS_TxTelegram.type_validate = EMS_ID_NONE;
|
||||
|
||||
// EMS Plus test
|
||||
// Sending read to 0x18: telegram: 0B 98 F0 00 01 B9 63 DB (len 8)
|
||||
EMS_TxTelegram.data[0] = EMS_ID_ME; // src
|
||||
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest
|
||||
EMS_TxTelegram.data[2] = 0xF0; // marker
|
||||
EMS_TxTelegram.data[3] = EMS_TxTelegram.offset; // offset
|
||||
EMS_TxTelegram.data[4] = 0x01; // hi byte
|
||||
EMS_TxTelegram.data[5] = 0xB9; // low byte
|
||||
|
||||
EMS_TxTelegram.data[6] = 99; // max length
|
||||
|
||||
EMS_TxQueue.push(EMS_TxTelegram);
|
||||
*/
|
||||
}
|
||||
|
||||
211
src/ems.h
211
src/ems.h
@@ -1,25 +1,15 @@
|
||||
/*
|
||||
* Header file for EMS.cpp
|
||||
* Header file for ems.cpp
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "emsuart.h"
|
||||
#include "my_config.h" // include custom configuration settings
|
||||
#include <Arduino.h>
|
||||
#include <CircularBuffer.h> // https://github.com/rlogiacco/CircularBuffer
|
||||
#include <MyESP.h>
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
// EMS IDs
|
||||
#define EMS_ID_NONE 0x00 // Fixed - used as a dest in broadcast messages and empty type IDs
|
||||
#define EMS_ID_ME 0x0B // Fixed - our device, hardcoded as "Service Key"
|
||||
|
||||
//#define EMS_ID_THERMOSTAT 0xFF // Fixed - to recognize a Thermostat
|
||||
//#define EMS_ID_BOILER 0x08 // Fixed - also known as MC10.
|
||||
#define EMS_ID_ME 0x0B // Fixed - our device, hardcoded as the "Service Key"
|
||||
|
||||
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
|
||||
|
||||
@@ -27,70 +17,9 @@
|
||||
// This can differs per firmware version and typically 32 is the max
|
||||
#define EMS_MAX_TELEGRAM_LENGTH 99
|
||||
|
||||
// define here the EMS telegram types you need
|
||||
|
||||
// Common for all EMS devices
|
||||
#define EMS_TYPE_Version 0x02 // version of the UBA controller (boiler)
|
||||
|
||||
/*
|
||||
* Boiler...
|
||||
*/
|
||||
#define EMS_TYPE_UBAMonitorFast 0x18 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMonitorSlow 0x19 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMonitorWWMessage 0x34 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMaintenanceStatusMessage 0x1C // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAParameterWW 0x33
|
||||
#define EMS_TYPE_UBATotalUptimeMessage 0x14
|
||||
#define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15
|
||||
#define EMS_TYPE_UBAParametersMessage 0x16
|
||||
#define EMS_TYPE_UBASetPoints 0x1A
|
||||
#define EMS_TYPE_UBAFunctionTest 0x1D
|
||||
|
||||
#define EMS_OFFSET_UBAParameterWW_wwtemp 2 // WW Temperature
|
||||
#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated
|
||||
#define EMS_OFFSET_UBAParameterWW_wwComfort 9 // WW is in comfort or eco mode
|
||||
#define EMS_VALUE_UBAParameterWW_wwComfort_Comfort 0x00 // the value for comfort
|
||||
#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco
|
||||
|
||||
/*
|
||||
* Thermostat...
|
||||
*/
|
||||
|
||||
// Common for all thermostats
|
||||
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
|
||||
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
|
||||
|
||||
// RC20 specific
|
||||
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
|
||||
#define EMS_OFFSET_RC20Set_mode 23 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC20Set_temp 28 // position of thermostat setpoint temperature
|
||||
#define EMS_TYPE_RC20StatusMessage_setpoint 1 // setpoint temp
|
||||
#define EMS_TYPE_RC20StatusMessage_curr 2 // current temp
|
||||
|
||||
// RC30 specific
|
||||
#define EMS_TYPE_RC30StatusMessage 0x41 // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC30Set 0xA7 // for setting values like temp and mode
|
||||
#define EMS_OFFSET_RC30Set_mode 23 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature
|
||||
#define EMS_TYPE_RC30StatusMessage_setpoint 1 // setpoint temp
|
||||
#define EMS_TYPE_RC30StatusMessage_curr 2 // current temp
|
||||
|
||||
// RC35 specific
|
||||
#define EMS_TYPE_RC35StatusMessage 0x3E // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC35StatusMessage_setpoint 2 // desired temp
|
||||
#define EMS_TYPE_RC35StatusMessage_curr 3 // current temp
|
||||
#define EMS_TYPE_RC35Set 0x3D // for setting values like temp and mode (Working mode HC1)
|
||||
#define EMS_OFFSET_RC35Set_mode 7 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC35Set_temp_day 2 // position of thermostat setpoint temperature for day time
|
||||
#define EMS_OFFSET_RC35Set_temp_night 1 // position of thermostat setpoint temperature for night time
|
||||
|
||||
// Easy specific
|
||||
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
|
||||
#define EMS_TYPE_EasyStatusMessage_setpoint 10 // setpoint temp
|
||||
#define EMS_TYPE_EasyStatusMessage_curr 8 // current temp
|
||||
|
||||
|
||||
// default values
|
||||
#define EMS_VALUE_INT_ON 1 // boolean true
|
||||
#define EMS_VALUE_INT_OFF 0 // boolean false
|
||||
@@ -98,6 +27,34 @@
|
||||
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs
|
||||
#define EMS_VALUE_FLOAT_NOTSET -255 // float unset
|
||||
|
||||
#define EMS_THERMOSTAT_READ_YES true
|
||||
#define EMS_THERMOSTAT_READ_NO false
|
||||
#define EMS_THERMOSTAT_WRITE_YES true
|
||||
#define EMS_THERMOSTAT_WRITE_NO false
|
||||
|
||||
// ANSI Colors
|
||||
#define COLOR_RESET "\x1B[0m"
|
||||
#define COLOR_BLACK "\x1B[0;30m"
|
||||
#define COLOR_RED "\x1B[0;31m"
|
||||
#define COLOR_GREEN "\x1B[0;32m"
|
||||
#define COLOR_YELLOW "\x1B[0;33m"
|
||||
#define COLOR_BLUE "\x1B[0;34m"
|
||||
#define COLOR_MAGENTA "\x1B[0;35m"
|
||||
#define COLOR_CYAN "\x1B[0;36m"
|
||||
#define COLOR_WHITE "\x1B[0;37m"
|
||||
#define COLOR_BOLD_ON "\x1B[1m"
|
||||
#define COLOR_BOLD_OFF "\x1B[21m"
|
||||
|
||||
// trigger settings to determine if hot tap water or the heating is active
|
||||
#define EMS_BOILER_BURNPOWER_TAPWATER 100
|
||||
#define EMS_BOILER_SELFLOWTEMP_HEATING 70
|
||||
|
||||
//define maximum settable tapwater temperature, not every installation supports 90 degrees
|
||||
#define EMS_BOILER_TAPWATER_TEMPERATURE_MAX 60
|
||||
|
||||
#define EMS_TX_TELEGRAM_QUEUE_MAX 50 // max size of Tx FIFO queue
|
||||
|
||||
|
||||
/* EMS UART transfer status */
|
||||
typedef enum {
|
||||
EMS_RX_IDLE,
|
||||
@@ -160,8 +117,6 @@ typedef struct {
|
||||
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
||||
} _EMS_TxTelegram;
|
||||
|
||||
#define EMS_TX_TELEGRAM_QUEUE_MAX 20 // max size of Tx FIFO queue
|
||||
|
||||
// default empty Tx
|
||||
const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
||||
EMS_TX_TELEGRAM_INIT, // action
|
||||
@@ -179,35 +134,11 @@ const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
||||
{0x00} // data
|
||||
};
|
||||
|
||||
// Known Buderus non-Thermostat types
|
||||
typedef enum {
|
||||
EMS_MODEL_NONE,
|
||||
EMS_MODEL_ALL, // common for all devices
|
||||
|
||||
// service key
|
||||
EMS_MODEL_SERVICEKEY, // this is us
|
||||
|
||||
// main buderus boiler type devices
|
||||
EMS_MODEL_BK15,
|
||||
EMS_MODEL_UBA,
|
||||
EMS_MODEL_BC10,
|
||||
EMS_MODEL_MM10,
|
||||
EMS_MODEL_WM10,
|
||||
|
||||
// thermostats
|
||||
EMS_MODEL_ES73,
|
||||
EMS_MODEL_RC20,
|
||||
EMS_MODEL_RC30,
|
||||
EMS_MODEL_RC35,
|
||||
EMS_MODEL_EASY
|
||||
|
||||
} _EMS_MODEL_ID;
|
||||
|
||||
typedef struct {
|
||||
_EMS_MODEL_ID model_id;
|
||||
uint8_t product_id;
|
||||
uint8_t type_id;
|
||||
char model_string[50];
|
||||
uint8_t model_id;
|
||||
uint8_t product_id;
|
||||
uint8_t type_id;
|
||||
char model_string[50];
|
||||
} _Model_Type;
|
||||
|
||||
/*
|
||||
@@ -259,39 +190,34 @@ typedef struct { // UBAParameterWW
|
||||
uint8_t heatingActive; // Central heating is on/off
|
||||
|
||||
// settings
|
||||
char version[10];
|
||||
uint8_t type_id;
|
||||
_EMS_MODEL_ID model_id;
|
||||
char version[10];
|
||||
uint8_t type_id;
|
||||
uint8_t model_id;
|
||||
} _EMS_Boiler;
|
||||
|
||||
// Definition for thermostat type
|
||||
typedef struct {
|
||||
_EMS_MODEL_ID model_id;
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
uint8_t model_id;
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
} _Thermostat_Type;
|
||||
|
||||
#define EMS_THERMOSTAT_READ_YES true
|
||||
#define EMS_THERMOSTAT_READ_NO false
|
||||
#define EMS_THERMOSTAT_WRITE_YES true
|
||||
#define EMS_THERMOSTAT_WRITE_NO false
|
||||
|
||||
// Thermostat data
|
||||
typedef struct {
|
||||
uint8_t type_id; // the type ID of the thermostat
|
||||
_EMS_MODEL_ID model_id; // which Thermostat type
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
char version[10];
|
||||
float setpoint_roomTemp; // current set temp
|
||||
float curr_roomTemp; // current room temp
|
||||
uint8_t mode; // 0=low, 1=manual, 2=auto
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint8_t year;
|
||||
uint8_t type_id; // the type ID of the thermostat
|
||||
uint8_t model_id; // which Thermostat type
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
char version[10];
|
||||
float setpoint_roomTemp; // current set temp
|
||||
float curr_roomTemp; // current room temp
|
||||
uint8_t mode; // 0=low, 1=manual, 2=auto
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint8_t year;
|
||||
} _EMS_Thermostat;
|
||||
|
||||
// call back function signature
|
||||
@@ -299,28 +225,15 @@ typedef void (*EMS_processType_cb)(uint8_t * data, uint8_t length);
|
||||
|
||||
// Definition for each EMS type, including the relative callback function
|
||||
typedef struct {
|
||||
_EMS_MODEL_ID model_id;
|
||||
uint8_t model_id;
|
||||
uint8_t type;
|
||||
const char typeString[50];
|
||||
EMS_processType_cb processType_cb;
|
||||
} _EMS_Type;
|
||||
|
||||
// ANSI Colors
|
||||
#define COLOR_RESET "\x1B[0m"
|
||||
#define COLOR_BLACK "\x1B[0;30m"
|
||||
#define COLOR_RED "\x1B[0;31m"
|
||||
#define COLOR_GREEN "\x1B[0;32m"
|
||||
#define COLOR_YELLOW "\x1B[0;33m"
|
||||
#define COLOR_BLUE "\x1B[0;34m"
|
||||
#define COLOR_MAGENTA "\x1B[0;35m"
|
||||
#define COLOR_CYAN "\x1B[0;36m"
|
||||
#define COLOR_WHITE "\x1B[0;37m"
|
||||
#define COLOR_BOLD_ON "\x1B[1m"
|
||||
#define COLOR_BOLD_OFF "\x1B[21m"
|
||||
|
||||
// function definitions
|
||||
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
|
||||
void ems_init(_EMS_MODEL_ID boiler_modelid, _EMS_MODEL_ID thermostat_modelid);
|
||||
void ems_init(uint8_t boiler_modelid, uint8_t thermostat_modelid);
|
||||
void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh = false);
|
||||
void ems_sendRawTelegram(char * telegram);
|
||||
|
||||
@@ -329,7 +242,6 @@ void ems_setThermostatMode(uint8_t mode);
|
||||
void ems_setWarmWaterTemp(uint8_t temperature);
|
||||
void ems_setWarmWaterActivated(bool activated);
|
||||
void ems_setWarmTapWaterActivated(bool activated);
|
||||
void ems_setExperimental(uint8_t value);
|
||||
void ems_setPoll(bool b);
|
||||
void ems_setTxEnabled(bool b);
|
||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
|
||||
@@ -348,8 +260,8 @@ bool ems_getBusConnected();
|
||||
_EMS_SYS_LOGGING ems_getLogging();
|
||||
uint8_t ems_getEmsTypesCount();
|
||||
bool ems_getEmsRefreshed();
|
||||
_EMS_MODEL_ID ems_getThermostatModel();
|
||||
_EMS_MODEL_ID ems_getBoilerModel();
|
||||
uint8_t ems_getThermostatModel();
|
||||
uint8_t ems_getBoilerModel();
|
||||
|
||||
void ems_scanDevices();
|
||||
void ems_printAllTypes();
|
||||
@@ -362,10 +274,9 @@ uint8_t _crcCalculator(uint8_t * data, uint8_t len);
|
||||
void _processType(uint8_t * telegram, uint8_t length);
|
||||
void _debugPrintPackage(const char * prefix, uint8_t * data, uint8_t len, const char * color);
|
||||
void _ems_clearTxData();
|
||||
int _ems_findModel(_EMS_MODEL_ID model_id);
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, _EMS_MODEL_ID model_id);
|
||||
bool _ems_setModel(_EMS_MODEL_ID model_id);
|
||||
|
||||
int _ems_findModel(uint8_t model_id);
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, uint8_t model_id);
|
||||
bool _ems_setModel(uint8_t model_id);
|
||||
|
||||
// global so can referenced in other classes
|
||||
extern _EMS_Sys_Status EMS_Sys_Status;
|
||||
|
||||
132
src/ems_devices.h
Normal file
132
src/ems_devices.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* General information about known EMS devices
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ems.h"
|
||||
|
||||
/*
|
||||
* Boiler...
|
||||
*/
|
||||
#define EMS_TYPE_UBAMonitorFast 0x18 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMonitorSlow 0x19 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMonitorWWMessage 0x34 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMaintenanceStatusMessage 0x1C // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAParameterWW 0x33
|
||||
#define EMS_TYPE_UBATotalUptimeMessage 0x14
|
||||
#define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15
|
||||
#define EMS_TYPE_UBAParametersMessage 0x16
|
||||
#define EMS_TYPE_UBASetPoints 0x1A
|
||||
#define EMS_TYPE_UBAFunctionTest 0x1D
|
||||
|
||||
#define EMS_OFFSET_UBAParameterWW_wwtemp 2 // WW Temperature
|
||||
#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated
|
||||
#define EMS_OFFSET_UBAParameterWW_wwComfort 9 // WW is in comfort or eco mode
|
||||
#define EMS_VALUE_UBAParameterWW_wwComfort_Comfort 0x00 // the value for comfort
|
||||
#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco
|
||||
|
||||
/*
|
||||
* Thermostat...
|
||||
*/
|
||||
|
||||
// Common for all thermostats
|
||||
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
|
||||
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
|
||||
|
||||
// RC20 specific
|
||||
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
|
||||
#define EMS_OFFSET_RC20Set_mode 23 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC20Set_temp 28 // position of thermostat setpoint temperature
|
||||
#define EMS_TYPE_RC20StatusMessage_setpoint 1 // setpoint temp
|
||||
#define EMS_TYPE_RC20StatusMessage_curr 2 // current temp
|
||||
|
||||
// RC30 specific
|
||||
#define EMS_TYPE_RC30StatusMessage 0x41 // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC30Set 0xA7 // for setting values like temp and mode
|
||||
#define EMS_OFFSET_RC30Set_mode 23 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature
|
||||
#define EMS_TYPE_RC30StatusMessage_setpoint 1 // setpoint temp
|
||||
#define EMS_TYPE_RC30StatusMessage_curr 2 // current temp
|
||||
|
||||
// RC35 specific
|
||||
#define EMS_TYPE_RC35StatusMessage 0x3E // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC35StatusMessage_setpoint 2 // desired temp
|
||||
#define EMS_TYPE_RC35StatusMessage_curr 3 // current temp
|
||||
#define EMS_TYPE_RC35Set 0x3D // for setting values like temp and mode (Working mode HC1)
|
||||
#define EMS_OFFSET_RC35Set_mode 7 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC35Set_temp_day 2 // position of thermostat setpoint temperature for day time
|
||||
#define EMS_OFFSET_RC35Set_temp_night 1 // position of thermostat setpoint temperature for night time
|
||||
|
||||
// Easy specific
|
||||
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
|
||||
#define EMS_TYPE_EasyStatusMessage_setpoint 10 // setpoint temp
|
||||
#define EMS_TYPE_EasyStatusMessage_curr 8 // current temp
|
||||
|
||||
// Known EMS types
|
||||
typedef enum {
|
||||
EMS_MODEL_NONE,
|
||||
EMS_MODEL_ALL, // common for all devices
|
||||
|
||||
// service key
|
||||
EMS_MODEL_SERVICEKEY, // this is us
|
||||
|
||||
// main buderus boiler type devices
|
||||
EMS_MODEL_BK15,
|
||||
EMS_MODEL_UBA,
|
||||
EMS_MODEL_BC10,
|
||||
EMS_MODEL_BC25,
|
||||
EMS_MODEL_MM10,
|
||||
EMS_MODEL_WM10,
|
||||
EMS_MODEL_RFM20,
|
||||
|
||||
// thermostats
|
||||
EMS_MODEL_ES73,
|
||||
EMS_MODEL_RC20,
|
||||
EMS_MODEL_RC20F,
|
||||
EMS_MODEL_RC30,
|
||||
EMS_MODEL_RC35,
|
||||
EMS_MODEL_EASY
|
||||
|
||||
} _EMS_MODEL_ID;
|
||||
|
||||
// EMS types for known Buderus devices. This list will be extended when new devices are recognized.
|
||||
// format is MODEL_ID, PRODUCT ID, TYPE_ID, DESCRIPTION
|
||||
const _Model_Type Model_Types[] = {
|
||||
|
||||
// me
|
||||
{EMS_MODEL_SERVICEKEY, 999, 0x0B, "Service Key"},
|
||||
|
||||
// various boilers and buderus type devices
|
||||
{EMS_MODEL_UBA, 123, 0x08, "MC10/UBA3 Boiler"},
|
||||
{EMS_MODEL_BK15, 64, 0x08, "Sieger BK15 Boiler"},
|
||||
{EMS_MODEL_BC10, 190, 0x09, "BC10 Base Controller"},
|
||||
{EMS_MODEL_BC25, 125, 0x09, "BC25 Base Controller"},
|
||||
{EMS_MODEL_RFM20, 68, 0x09, "RFM20 RC20F Receiver"},
|
||||
{EMS_MODEL_MM10, 251, 0x21, "MM10 Mixer Module"}, // warning, fake product id!
|
||||
{EMS_MODEL_WM10, 250, 0x11, "WM10 Switch Module"}, // warning, fake product id!
|
||||
|
||||
// controllers and thermostats
|
||||
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73"},
|
||||
{EMS_MODEL_RC20, 77, 0x17, "RC20 (e.g. Nefit Moduline 300)"},
|
||||
{EMS_MODEL_RC20F, 93, 0x18, "RC20F"},
|
||||
{EMS_MODEL_RC30, 78, 0x10, "RC30 (e.g. Nefit Moduline 400)"},
|
||||
{EMS_MODEL_RC35, 86, 0x10, "RC35 (or compatible"},
|
||||
{EMS_MODEL_EASY, 202, 0x18, "TC100 (e.g. Nefit Easy or CT100)"}
|
||||
|
||||
};
|
||||
/*
|
||||
* Known thermostat types and their abilities
|
||||
*/
|
||||
const _Thermostat_Type Thermostat_Types[] = {
|
||||
|
||||
{EMS_MODEL_RC20, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC20F, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC30, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC35, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_EASY, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO},
|
||||
{EMS_MODEL_ES73, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}
|
||||
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
* emsuart.cpp
|
||||
*
|
||||
* The low level UART code for ESP8266 to read and write to the EMS bus via uart
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP-Boiler
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
|
||||
#include "emsuart.h"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* emsuart.h
|
||||
* Header file for emsuart.cpp
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP-Boiler
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,57 +1,43 @@
|
||||
/*
|
||||
* my_config.h
|
||||
*
|
||||
* All configurations and customization's go here
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP-Boiler
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// these are set as -D build flags during compilation
|
||||
// they can be set in platformio.ini or alternatively hard coded here
|
||||
/*
|
||||
#define WIFI_SSID "<my_ssid>"
|
||||
#define WIFI_PASSWORD "<my_password>"
|
||||
#define MQTT_IP "<broker_ip>"
|
||||
#define MQTT_USER "<broker_username>"
|
||||
#define MQTT_PASS "<broker_password>"
|
||||
*/
|
||||
#include "ems.h"
|
||||
|
||||
// Set your wifi and mqtt params
|
||||
// If set to NULL (default) you'll need to set them in AP mode using the 'set' command
|
||||
// as these values are stored in SPIFFs for persisted
|
||||
#define WIFI_SSID NULL
|
||||
#define WIFI_PASSWORD NULL
|
||||
#define MQTT_IP NULL
|
||||
#define MQTT_USER NULL
|
||||
#define MQTT_PASS NULL
|
||||
|
||||
// All MQTT topics are prefixed with the following string
|
||||
#define MQTT_BASE "home"
|
||||
|
||||
// default values for shower logic on/off
|
||||
#define BOILER_SHOWER_TIMER 1 // monitors how long a shower has taken
|
||||
#define BOILER_SHOWER_ALERT 0 // send alert if showetime exceeded
|
||||
#define BOILER_SHOWER_TIMER 1 // enable (1) to monitor shower time
|
||||
#define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold watewr when shower time limit has exceeded
|
||||
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
||||
|
||||
// trigger settings to determine if hot tap water or the heating is active
|
||||
#define EMS_BOILER_BURNPOWER_TAPWATER 100
|
||||
#define EMS_BOILER_SELFLOWTEMP_HEATING 70
|
||||
|
||||
//define maximum settable tapwater temperature, not every installation supports 90 degrees
|
||||
#define EMS_BOILER_TAPWATER_TEMPERATURE_MAX 60
|
||||
|
||||
// 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_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
||||
#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
|
||||
|
||||
// The LED for showing connection errors, either onboard or via an external pull-up LED
|
||||
// can be disabled using -DNO_LED build flag in platformio.ini
|
||||
// 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)
|
||||
// note: can be disabled completely using -DNO_LED build flag in platformio.ini
|
||||
#define BOILER_LED LED_BUILTIN // LED_BULLETIN for onboard LED
|
||||
//#define BOILER_LED D1 // for external LED like on the latest bbqkees boards
|
||||
|
||||
// set this if using an external temperature sensor like a DS18B20
|
||||
#define TEMPERATURE_SENSOR_PIN D7
|
||||
|
||||
// logging - EMS_SYS_LOGGING_VERBOSE, EMS_SYS_LOGGING_NONE, EMS_SYS_LOGGING_BASIC (see ems.h)
|
||||
// this can be changed via the Telnet console using the 'l' command
|
||||
#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_NONE
|
||||
//#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_VERBOSE
|
||||
//#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_BASIC
|
||||
|
||||
// By default the EMS bus will be scanned for known devices. You can override this here
|
||||
// by fixing the Boiler and Thermostat types
|
||||
// Options are in ems.h and include..
|
||||
// By default the EMS bus will be scanned for known devices (EMS_MODEL_NONE).
|
||||
// You can override this here by fixing the Boiler and Thermostat types.
|
||||
// See ems.h for the list of recognized types. For example:
|
||||
// boilers: EMS_MODEL_BK15, EMS_MODEL_UBA, EMS_MODEL_BC10, EMS_MODEL_MM10, EMS_MODEL_WM10
|
||||
// thermostats: EMS_MODEL_ES73, EMS_MODEL_RC20, EMS_MODEL_RC30, EMS_MODEL_RC35, EMS_MODEL_EASY
|
||||
#define MY_BOILER_MODELID EMS_MODEL_NONE
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define APP_NAME "EMS-ESP-Boiler"
|
||||
#define APP_VERSION "1.2.4"
|
||||
#define APP_HOSTNAME "boiler"
|
||||
#define APP_NAME "EMS-ESP Interface"
|
||||
#define APP_VERSION "1.3.0"
|
||||
#define APP_HOSTNAME "ems-esp"
|
||||
|
||||
Reference in New Issue
Block a user