mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
version 1.4.1
This commit is contained in:
11
CHANGELOG.md
11
CHANGELOG.md
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.4.1] 2019-01-29
|
||||
|
||||
### Added
|
||||
|
||||
- The led pin, dallas pin and both thermostat and boiler type IDs can be set with the application, and stored.
|
||||
|
||||
### Changed
|
||||
|
||||
- some minor improvements to autodetect
|
||||
|
||||
|
||||
## [1.4.0] 2019-01-27
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -402,10 +402,8 @@ void MyESP::_consoleShowHelp() {
|
||||
SerialAndTelnet.println(FPSTR("* set"));
|
||||
SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]"));
|
||||
SerialAndTelnet.println(FPSTR("* set erase"));
|
||||
SerialAndTelnet.println(FPSTR("* set led <on | off>"));
|
||||
SerialAndTelnet.println(FPSTR("*"));
|
||||
|
||||
// print custom commands if available. Take from progmem
|
||||
// print custom commands if available. Taken from progmem
|
||||
if (_telnetcommand_callback) {
|
||||
// find the longest key length so we can right align it
|
||||
uint8_t max_len = 0;
|
||||
@@ -513,8 +511,8 @@ void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
||||
SerialAndTelnet.printf("%s changed to %s\n\r", setting, value);
|
||||
}
|
||||
|
||||
if (_fs_saveConfig()) {
|
||||
SerialAndTelnet.println("Note, some changes will only have effect after a device reboot (use ! command)");
|
||||
if (fs_saveConfig()) {
|
||||
SerialAndTelnet.println("Note, some changes will only have effect after the ESP is restarted (use ! command)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -811,7 +809,7 @@ bool MyESP::_fs_loadConfig() {
|
||||
// use configFile.readString
|
||||
configFile.readBytes(buf.get(), size);
|
||||
|
||||
StaticJsonBuffer<500> jsonBuffer; // https://arduinojson.org/v5/assistant/
|
||||
StaticJsonBuffer<SPIFFS_MAXSIZE> jsonBuffer;
|
||||
JsonObject & json = jsonBuffer.parseObject(buf.get());
|
||||
|
||||
const char * value;
|
||||
@@ -831,17 +829,18 @@ bool MyESP::_fs_loadConfig() {
|
||||
value = json["mqtt_password"];
|
||||
_mqtt_password = (value) ? strdup(value) : NULL;
|
||||
|
||||
// callback for custom settings
|
||||
(_fs_callback)(MYESP_FSACTION_LOAD, json);
|
||||
// callback for loading custom settings
|
||||
// ok is false if there's a problem loading a custom setting (e.g. does not exist)
|
||||
bool ok = (_fs_callback)(MYESP_FSACTION_LOAD, json);
|
||||
|
||||
configFile.close();
|
||||
|
||||
return true;
|
||||
return ok;
|
||||
}
|
||||
|
||||
// save settings to spiffs
|
||||
bool MyESP::_fs_saveConfig() {
|
||||
StaticJsonBuffer<500> jsonBuffer; // https://arduinojson.org/v5/assistant/
|
||||
bool MyESP::fs_saveConfig() {
|
||||
StaticJsonBuffer<SPIFFS_MAXSIZE> jsonBuffer;
|
||||
JsonObject & json = jsonBuffer.createObject();
|
||||
|
||||
json["wifi_ssid"] = _wifi_ssid;
|
||||
@@ -850,8 +849,8 @@ bool MyESP::_fs_saveConfig() {
|
||||
json["mqtt_username"] = _mqtt_username;
|
||||
json["mqtt_password"] = _mqtt_password;
|
||||
|
||||
// callback for custom settings
|
||||
(_fs_callback)(MYESP_FSACTION_SAVE, json);
|
||||
// callback for saving custom settings
|
||||
(void)(_fs_callback)(MYESP_FSACTION_SAVE, json);
|
||||
|
||||
File configFile = SPIFFS.open("/config.json", "w");
|
||||
if (!configFile) {
|
||||
@@ -872,11 +871,12 @@ void MyESP::_fs_setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
// _fs_printConfig(); // for debugging
|
||||
//_fs_printConfig(); // for debugging
|
||||
|
||||
// load the config file. if it doesn't exist create it with anything that was specified
|
||||
// load the config file. if it doesn't exist create it
|
||||
if (!_fs_loadConfig()) {
|
||||
_fs_saveConfig();
|
||||
myDebug_P(PSTR("[FS] Re-creating config file"));
|
||||
fs_saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -939,4 +939,4 @@ void MyESP::loop() {
|
||||
yield(); // ...and breath
|
||||
}
|
||||
|
||||
MyESP myESP;
|
||||
MyESP myESP; // create instance
|
||||
|
||||
@@ -61,8 +61,11 @@
|
||||
#define COLOR_CYAN "\x1B[0;36m"
|
||||
#define COLOR_WHITE "\x1B[0;37m"
|
||||
|
||||
// SPIFFS
|
||||
#define SPIFFS_MAXSIZE 500 // https://arduinojson.org/v5/assistant/
|
||||
|
||||
typedef struct {
|
||||
char key[30];
|
||||
char key[40];
|
||||
char description[100];
|
||||
} command_t;
|
||||
|
||||
@@ -72,7 +75,7 @@ typedef std::function<void(unsigned int, const char *, const char *)>
|
||||
typedef std::function<void()> wifi_callback_f;
|
||||
typedef std::function<void(uint8_t, const char *)> telnetcommand_callback_f;
|
||||
typedef std::function<void(uint8_t)> telnet_callback_f;
|
||||
typedef std::function<void(MYESP_FSACTION, JsonObject & json)> fs_callback_f;
|
||||
typedef std::function<bool(MYESP_FSACTION, JsonObject & json)> fs_callback_f;
|
||||
typedef std::function<bool(MYESP_FSACTION, uint8_t, const char *, const char *)> fs_settings_callback_f;
|
||||
|
||||
// calculates size of an 2d array at compile time
|
||||
@@ -113,6 +116,7 @@ class MyESP {
|
||||
|
||||
// FS
|
||||
void setSettings(fs_callback_f callback, fs_settings_callback_f fs_settings_callback);
|
||||
bool fs_saveConfig();
|
||||
|
||||
// general
|
||||
void end();
|
||||
@@ -178,7 +182,6 @@ class MyESP {
|
||||
|
||||
// fs
|
||||
void _fs_setup();
|
||||
bool _fs_saveConfig();
|
||||
bool _fs_loadConfig();
|
||||
void _fs_printConfig();
|
||||
void _fs_eraseConfig();
|
||||
|
||||
171
src/ems-esp.ino
171
src/ems-esp.ino
@@ -32,7 +32,7 @@ DS18 ds18;
|
||||
#define myDebug_P(...) myESP.myDebug_P(__VA_ARGS__)
|
||||
|
||||
// timers, all values are in seconds
|
||||
#define PUBLISHVALUES_TIME 120 // every 2 minutes post MQTT values
|
||||
#define PUBLISHVALUES_TIME 120 // every 2 minutes publish MQTT values
|
||||
Ticker publishValuesTimer;
|
||||
|
||||
#define SYSTEMCHECK_TIME 20 // every 20 seconds check if Boiler is online
|
||||
@@ -41,7 +41,7 @@ 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
|
||||
#define LEDCHECK_TIME 1 // every second blink the heartbeat LED
|
||||
Ticker ledcheckTimer;
|
||||
|
||||
// thermostat scan - for debugging
|
||||
@@ -51,26 +51,6 @@ uint8_t scanThermostat_count = 0;
|
||||
|
||||
Ticker showerColdShotStopTimer;
|
||||
|
||||
// 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 an MQTT command
|
||||
#define SHOWER_ALARM "shower_alarm" // for notifying 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
|
||||
@@ -83,6 +63,8 @@ typedef struct {
|
||||
bool led_enabled; // LED on/off
|
||||
unsigned long timestamp; // for internal timings, via millis()
|
||||
uint8_t dallas_sensors; // count of dallas sensors
|
||||
uint8_t led_gpio;
|
||||
uint8_t dallas_gpio;
|
||||
} _EMSESP_Status;
|
||||
|
||||
typedef struct {
|
||||
@@ -95,10 +77,16 @@ typedef struct {
|
||||
|
||||
command_t PROGMEM project_cmds[] = {
|
||||
|
||||
{"set led <on | off>", "toggle status LED on/off"},
|
||||
{"set led_gpio <pin>", "set the LED pin (onboard=2)"},
|
||||
{"set dallas_gpio <pin>", "set the pin for the external Dallas temperature sensor (D5=14)"},
|
||||
{"set thermostat_type <hex type ID>", "set the thermostat type id (e.g. 10 for 0x10)"},
|
||||
{"set boiler_type <hex type ID>", "set the boiler type id (e.g. 8 for 0x08)"},
|
||||
|
||||
{"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"},
|
||||
{"tx", "toggle EMX Tx line on/off"},
|
||||
{"publish", "publish values to MQTT"},
|
||||
{"types", "list supported EMS telegram type IDs"},
|
||||
{"queue", "print the Tx queue"},
|
||||
@@ -349,7 +337,9 @@ void showInfo() {
|
||||
_renderIntValue("Boiler circuit pump modulation min. power", "%", EMS_Boiler.pump_mod_min);
|
||||
|
||||
// UBAMonitorSlow
|
||||
if (EMS_Boiler.extTemp != EMS_VALUE_FLOAT_NOTSET) {
|
||||
_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);
|
||||
@@ -576,7 +566,7 @@ char * _readWord() {
|
||||
}
|
||||
|
||||
// initiate a force scan by sending type read requests from 0 to FF to the thermostat
|
||||
// used to analyze repsonses for debugging
|
||||
// used to analyze responses for debugging
|
||||
void startThermostatScan(uint8_t start) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_THERMOSTAT);
|
||||
publishValuesTimer.detach();
|
||||
@@ -589,22 +579,66 @@ void startThermostatScan(uint8_t start) {
|
||||
|
||||
|
||||
// callback for loading/saving settings to the file system (SPIFFS)
|
||||
void FSCallback(MYESP_FSACTION action, JsonObject & json) {
|
||||
bool FSCallback(MYESP_FSACTION action, JsonObject & json) {
|
||||
bool ok = true;
|
||||
if (action == MYESP_FSACTION_LOAD) {
|
||||
// led
|
||||
if (json.containsKey("led")) {
|
||||
EMSESP_Status.led_enabled = (bool)json["led"];
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
// led_gpio
|
||||
if (json.containsKey("led_gpio")) {
|
||||
EMSESP_Status.led_gpio = json["led_gpio"];
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
// dallas_gpio
|
||||
if (json.containsKey("dallas_gpio")) {
|
||||
EMSESP_Status.dallas_gpio = json["dallas_gpio"];
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
// thermostat_type
|
||||
if (json.containsKey("thermostat_type")) {
|
||||
EMS_Thermostat.type_id = json["thermostat_type"];
|
||||
} else {
|
||||
EMS_Thermostat.type_id = EMSESP_THERMOSTAT_TYPE; // set default
|
||||
ok = false;
|
||||
}
|
||||
|
||||
// boiler_type
|
||||
if (json.containsKey("boiler_type")) {
|
||||
EMS_Boiler.type_id = json["boiler_type"];
|
||||
} else {
|
||||
EMS_Boiler.type_id = EMSESP_BOILER_TYPE; // set default
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == MYESP_FSACTION_SAVE) {
|
||||
json["led"] = EMSESP_Status.led_enabled;
|
||||
json["led_gpio"] = EMSESP_Status.led_gpio;
|
||||
json["dallas_gpio"] = EMSESP_Status.dallas_gpio;
|
||||
json["thermostat_type"] = EMS_Thermostat.type_id;
|
||||
json["boiler_type"] = EMS_Boiler.type_id;
|
||||
}
|
||||
|
||||
return ok; // all ok
|
||||
}
|
||||
|
||||
// callback for custom settings
|
||||
// callback for custom settings when showing Stored Settings
|
||||
// wc is number of arguments after the 'set' command
|
||||
// returns true if successful
|
||||
bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) {
|
||||
bool ok = false;
|
||||
|
||||
if (action == MYESP_FSACTION_SET) {
|
||||
// led
|
||||
if ((strcmp(setting, "led") == 0) && (wc == 2)) {
|
||||
if (strcmp(value, "on") == 0) {
|
||||
EMSESP_Status.led_enabled = true;
|
||||
@@ -613,13 +647,53 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c
|
||||
EMSESP_Status.led_enabled = false;
|
||||
ok = true;
|
||||
// let's make sure LED is really off
|
||||
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off
|
||||
digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off
|
||||
}
|
||||
}
|
||||
|
||||
// led_gpio
|
||||
if ((strcmp(setting, "led_gpio") == 0) && (wc == 2)) {
|
||||
EMSESP_Status.led_gpio = atoi(value);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// dallas_gpio
|
||||
if ((strcmp(setting, "dallas_gpio") == 0) && (wc == 2)) {
|
||||
EMSESP_Status.dallas_gpio = atoi(value);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// thermostat_type
|
||||
if (strcmp(setting, "thermostat_type") == 0) {
|
||||
EMS_Thermostat.type_id = ((wc == 2) ? (uint8_t)strtol(value, 0, 16) : EMS_ID_NONE);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// boiler_type
|
||||
if (strcmp(setting, "boiler_type") == 0) {
|
||||
EMS_Boiler.type_id = ((wc == 2) ? (uint8_t)strtol(value, 0, 16) : EMS_ID_NONE);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == MYESP_FSACTION_LIST) {
|
||||
myDebug(" led=%s", EMSESP_Status.led_enabled ? "on" : "off");
|
||||
myDebug(" led_gpio=%d", EMSESP_Status.led_gpio);
|
||||
myDebug(" dallas_gpio=%d", EMSESP_Status.dallas_gpio);
|
||||
|
||||
if (EMS_Thermostat.type_id == EMS_ID_NONE) {
|
||||
myDebug(" thermostat_type=<not set>");
|
||||
|
||||
} else {
|
||||
myDebug(" thermostat_type=%02X", EMS_Thermostat.type_id);
|
||||
}
|
||||
|
||||
if (EMS_Boiler.type_id == EMS_ID_NONE) {
|
||||
myDebug(" boiler_type=<not set>");
|
||||
|
||||
} else {
|
||||
myDebug(" boiler_type=%02X", EMS_Boiler.type_id);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
@@ -629,7 +703,7 @@ bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, c
|
||||
// we set the logging here
|
||||
void TelnetCallback(uint8_t event) {
|
||||
if (event == TELNET_EVENT_CONNECT) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE);
|
||||
ems_setLogging(EMS_SYS_LOGGING_DEFAULT);
|
||||
} else if (event == TELNET_EVENT_DISCONNECT) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE);
|
||||
}
|
||||
@@ -861,9 +935,7 @@ void WIFICallback() {
|
||||
myDebug("[UART] Opened Rx/Tx connection");
|
||||
#endif
|
||||
|
||||
// now that we're connected, set up the boiler and thermostat
|
||||
// the boiler's version will be requested and if there is no thermostat hardcoded it will try
|
||||
// and find the first one
|
||||
// now that we're connected, find out what boiler and thermostats we have specified
|
||||
ems_discoverModels();
|
||||
}
|
||||
|
||||
@@ -876,6 +948,9 @@ void initEMSESP() {
|
||||
EMSESP_Status.timestamp = millis();
|
||||
EMSESP_Status.dallas_sensors = 0;
|
||||
|
||||
EMSESP_Status.led_gpio = EMSESP_LED_GPIO;
|
||||
EMSESP_Status.dallas_gpio = EMSESP_DALLAS_GPIO;
|
||||
|
||||
// shower settings
|
||||
EMSESP_Shower.timerStart = 0;
|
||||
EMSESP_Shower.timerPause = 0;
|
||||
@@ -892,14 +967,14 @@ void do_publishValues() {
|
||||
}
|
||||
|
||||
// callback to light up the LED, called via Ticker every second
|
||||
// fast way is to use WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + (state ? 4 : 8), (1 << BOILER_LED)); // 4 is on, 8 is off
|
||||
// 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_enabled) {
|
||||
if (ems_getBusConnected()) {
|
||||
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
|
||||
digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
|
||||
} else {
|
||||
int state = digitalRead(BOILER_LED);
|
||||
digitalWrite(BOILER_LED, !state);
|
||||
int state = digitalRead(EMSESP_Status.led_gpio);
|
||||
digitalWrite(EMSESP_Status.led_gpio, !state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1019,6 +1094,12 @@ void showerCheck() {
|
||||
// SETUP
|
||||
//
|
||||
void setup() {
|
||||
// init our own parameters
|
||||
initEMSESP();
|
||||
|
||||
// call ems.cpp's init function to set all the internal params
|
||||
ems_init();
|
||||
|
||||
#ifndef DEBUG_SUPPORT
|
||||
// Timers using Ticker library
|
||||
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values
|
||||
@@ -1031,24 +1112,22 @@ void setup() {
|
||||
myESP.setWIFI(WIFI_SSID, WIFI_PASSWORD, WIFICallback);
|
||||
myESP.setMQTT(
|
||||
MQTT_HOST, MQTT_USER, MQTT_PASS, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, MQTT_WILL_PAYLOAD, MQTTCallback);
|
||||
|
||||
// custom settings in SPIFFS
|
||||
myESP.setSettings(FSCallback, SettingsCallback);
|
||||
|
||||
myESP.begin(APP_HOSTNAME, APP_NAME, APP_VERSION); // start it all up
|
||||
|
||||
// init our own parameters
|
||||
initEMSESP();
|
||||
// start up all the services
|
||||
myESP.begin(APP_HOSTNAME, APP_NAME, APP_VERSION);
|
||||
|
||||
// set pin for LED
|
||||
pinMode(BOILER_LED, OUTPUT);
|
||||
digitalWrite(BOILER_LED, (BOILER_LED == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off
|
||||
if (EMSESP_Status.led_gpio != EMS_VALUE_INT_NOTSET) {
|
||||
pinMode(EMSESP_Status.led_gpio, OUTPUT);
|
||||
digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? HIGH : LOW); // light off. For onboard high=off
|
||||
ledcheckTimer.attach(LEDCHECK_TIME, do_ledcheck); // blink heartbeat LED
|
||||
}
|
||||
|
||||
// check for Dallas sensors
|
||||
EMSESP_Status.dallas_sensors = ds18.setup(TEMPERATURE_SENSOR_PIN);
|
||||
|
||||
// init the EMS bus
|
||||
// call ems.cpp's init function to set all the internal params
|
||||
ems_init(MY_THERMOSTAT_MODELID);
|
||||
EMSESP_Status.dallas_sensors = ds18.setup(EMSESP_Status.dallas_gpio); // returns #sensors
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
103
src/ems.cpp
103
src/ems.cpp
@@ -143,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(uint8_t thermostat_modelid) {
|
||||
void ems_init() {
|
||||
// overall status
|
||||
EMS_Sys_Status.emsRxPgks = 0;
|
||||
EMS_Sys_Status.emsTxPkgs = 0;
|
||||
@@ -224,15 +224,13 @@ void ems_init(uint8_t thermostat_modelid) {
|
||||
EMS_Boiler.tapwaterActive = EMS_VALUE_INT_NOTSET; // Hot tap water is on/off
|
||||
EMS_Boiler.heatingActive = EMS_VALUE_INT_NOTSET; // Central heating is on/off
|
||||
|
||||
// boiler is hardcoded
|
||||
EMS_Boiler.type_id = EMS_ID_BOILER; // fixed at 0x08
|
||||
// set boiler type
|
||||
EMS_Boiler.product_id = 0;
|
||||
strlcpy(EMS_Boiler.version, "not set", sizeof(EMS_Boiler.version));
|
||||
|
||||
// set thermostat model
|
||||
EMS_Thermostat.type_id = EMS_ID_NONE; // fixed at 0x08
|
||||
EMS_Thermostat.model_id = EMS_MODEL_NONE;
|
||||
EMS_Thermostat.product_id = 0;
|
||||
_ems_setThermostatModel(thermostat_modelid);
|
||||
strlcpy(EMS_Thermostat.version, "not set", sizeof(EMS_Thermostat.version));
|
||||
|
||||
// counters
|
||||
@@ -240,8 +238,8 @@ void ems_init(uint8_t thermostat_modelid) {
|
||||
_emsTxRetryCount = 0;
|
||||
_last_TxTelgramCRC = 0;
|
||||
|
||||
// default logging is non
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE);
|
||||
// default logging is none
|
||||
ems_setLogging(EMS_SYS_LOGGING_DEFAULT);
|
||||
}
|
||||
|
||||
// Getters and Setters for parameters
|
||||
@@ -469,15 +467,15 @@ void _ems_sendTelegram() {
|
||||
return;
|
||||
}
|
||||
|
||||
// if Tx is disabled, don't do anything and ignore the request
|
||||
// this could be because the boiler has yet to be found and the type_id is still empty
|
||||
// if there is no destination, also delete it from the queue
|
||||
if (EMS_TxTelegram.dest == EMS_ID_NONE) {
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
|
||||
EMS_TxQueue.shift(); // remove from queue
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is no destination, also delete it from the queue
|
||||
// if Tx is disabled, don't do anything and ignore the request
|
||||
// this could be because the boiler has yet to be found and the type_id is still empty
|
||||
if (!EMS_Sys_Status.emsTxEnabled) {
|
||||
myDebug("Tx is disabled. Ignoring %s request to 0x%02X.",
|
||||
((EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) ? "write" : "read"),
|
||||
@@ -516,6 +514,7 @@ void _ems_sendTelegram() {
|
||||
// myDebug("Duplicate message, just sent this one, so removing from queue!");
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
|
||||
EMS_TxQueue.shift(); // remove the last Tx from the queue
|
||||
return;
|
||||
}
|
||||
_last_TxTelgramCRC = crc;
|
||||
|
||||
@@ -770,8 +769,8 @@ void _ems_processTelegram(uint8_t * telegram, uint8_t length) {
|
||||
*/
|
||||
void _processType(uint8_t * telegram, uint8_t length) {
|
||||
// header
|
||||
uint8_t src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes
|
||||
uint8_t dest = telegram[1] & 0x7F; // remove 8th bit to handle both reads and writes
|
||||
uint8_t src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes here
|
||||
uint8_t dest = telegram[1] & 0x7F;
|
||||
uint8_t type = telegram[2];
|
||||
uint8_t * data = telegram + 4; // data block starts at position 5
|
||||
|
||||
@@ -781,27 +780,33 @@ void _processType(uint8_t * telegram, uint8_t length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// did we request this telegram? If so it would be either a read or a validate telegram and still on the
|
||||
// Tx queue with the same type
|
||||
// did we request this telegram? If so it would be either a read or a validate telegram and still
|
||||
// on top of the Tx queue with the same type. because we don't remove a Tx from the queue after a send
|
||||
// if its a validate check the value, or if its a read, update the Read counter
|
||||
// then we can safely removed the read/validate from the queue
|
||||
if ((dest == EMS_ID_ME) && (!EMS_TxQueue.isEmpty())) {
|
||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first(); // get current Tx package we last sent
|
||||
|
||||
// do the types match? If so we were expecting this response back to us
|
||||
// do the types match? If so we were expecting this telegram response back to us
|
||||
if (EMS_TxTelegram.type == type) {
|
||||
emsaurt_tx_poll(); // send Acknowledgement back to free the EMS bus
|
||||
emsaurt_tx_poll(); // send Acknowledgement back to free the EMS bus since we have the telegram
|
||||
|
||||
// if last action was a read, we are just happy that we actually got a response back
|
||||
// if last action was a read, go ahead and process it
|
||||
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_READ) {
|
||||
EMS_Sys_Status.emsRxPgks++; // increment rx counter
|
||||
_emsTxRetryCount = 0; // reset retry count
|
||||
_ems_processTelegram(telegram, length); // and process the telegram
|
||||
//
|
||||
// and process the telegram. This calls the main function to process each type
|
||||
//
|
||||
_ems_processTelegram(telegram, length);
|
||||
if (EMS_TxTelegram.forceRefresh) {
|
||||
ems_setEmsRefreshed(true); // set the MQTT refresh flag to force sending to MQTT
|
||||
}
|
||||
EMS_TxQueue.shift(); // remove read from queue, all done now
|
||||
} else if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) {
|
||||
return; // quit
|
||||
}
|
||||
|
||||
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) {
|
||||
// this read was for a validate. Do a compare on the 1 byte result from the last write
|
||||
uint8_t dataReceived = data[0]; // only a single byte is returned after a read
|
||||
if (EMS_TxTelegram.comparisonValue == dataReceived) {
|
||||
@@ -816,7 +821,7 @@ void _processType(uint8_t * telegram, uint8_t length) {
|
||||
} else {
|
||||
// write failed.
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Last write failed. Compared set value 0x%02X with received value 0x%02X.",
|
||||
myDebug("Last write failed. Compared set value 0x%02X with received value 0x%02X",
|
||||
EMS_TxTelegram.comparisonValue,
|
||||
dataReceived);
|
||||
}
|
||||
@@ -838,9 +843,18 @@ void _processType(uint8_t * telegram, uint8_t length) {
|
||||
EMS_TxQueue.unshift(EMS_TxTelegram); // add back to queue making it next in line
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
// telegram was for us, but seems we didn't ask for it
|
||||
// so kinda invalidates the last action on the queue remove from queue
|
||||
myDebug("Telegram sent to us but we didn't ask for it! (src=0x%02X dest=0x%02X type=0x%02X)",
|
||||
telegram[0] & 0x7F,
|
||||
telegram[1] & 0x7F,
|
||||
telegram[2]);
|
||||
// ems_printTxQueue();
|
||||
// EMS_TxQueue.shift(); // remove validate from queue
|
||||
} else {
|
||||
// we didn't request it, was for somebody else or a broadcast, process it anyway
|
||||
_ems_processTelegram(telegram, length);
|
||||
@@ -1055,6 +1069,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool do_save = false;
|
||||
uint8_t product_id = data[0];
|
||||
char version[10] = {0};
|
||||
snprintf(version, sizeof(version), "%02d.%02d", data[1], data[2]);
|
||||
@@ -1072,29 +1087,33 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
|
||||
if (typeFound) {
|
||||
// its a boiler
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Boiler found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
||||
myDebug("Boiler type device found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
||||
Boiler_Types[i].model_string,
|
||||
Boiler_Types[i].type_id,
|
||||
product_id,
|
||||
version);
|
||||
|
||||
// if its a boiler set it
|
||||
// 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)) {
|
||||
myDebug("* Setting Boiler type to Model %s, TypeID 0x%02X, Product ID %d, Version %s",
|
||||
Boiler_Types[i].model_string,
|
||||
Boiler_Types[i].type_id,
|
||||
product_id,
|
||||
version);
|
||||
}
|
||||
|
||||
// if its a boiler set it (even if it was already set)
|
||||
if (Boiler_Types[i].type_id == EMS_ID_BOILER) {
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("* Setting boiler to this new type");
|
||||
}
|
||||
EMS_Boiler.type_id = Boiler_Types[i].type_id;
|
||||
EMS_Boiler.product_id = Boiler_Types[i].product_id;
|
||||
strlcpy(EMS_Boiler.version, version, sizeof(EMS_Boiler.version));
|
||||
|
||||
do_save = true;
|
||||
|
||||
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// its not a boiler, maybe its a known thermostat
|
||||
// its not a boiler, maybe its a known thermostat?
|
||||
i = 0;
|
||||
while (i < _Thermostat_Types_max) {
|
||||
if (Thermostat_Types[i].product_id == product_id) {
|
||||
@@ -1115,11 +1134,14 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
}
|
||||
|
||||
// if we don't have a thermostat set, use this one
|
||||
// if its the one we hard coded, refresh the data anyway
|
||||
if ((EMS_Thermostat.model_id == EMS_MODEL_NONE) || (EMS_Thermostat.model_id == Thermostat_Types[i].model_id)) {
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("* Setting thermostat to this new type");
|
||||
}
|
||||
if ((EMS_Thermostat.type_id == EMS_ID_NONE) || (EMS_Thermostat.model_id == EMS_MODEL_NONE)
|
||||
|| (EMS_Thermostat.type_id == Thermostat_Types[i].type_id)) {
|
||||
myDebug("* Setting Thermostat type to Model %s, TypeID 0x%02X, Product ID %d, Version %s",
|
||||
Thermostat_Types[i].model_string,
|
||||
Thermostat_Types[i].type_id,
|
||||
product_id,
|
||||
version);
|
||||
|
||||
EMS_Thermostat.model_id = Thermostat_Types[i].model_id;
|
||||
EMS_Thermostat.type_id = Thermostat_Types[i].type_id;
|
||||
EMS_Thermostat.read_supported = Thermostat_Types[i].read_supported;
|
||||
@@ -1127,12 +1149,19 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
EMS_Thermostat.product_id = product_id;
|
||||
strlcpy(EMS_Thermostat.version, version, sizeof(EMS_Thermostat.version));
|
||||
|
||||
do_save = true;
|
||||
|
||||
// get Thermostat values (if supported)
|
||||
ems_getThermostatValues();
|
||||
}
|
||||
} else {
|
||||
myDebug("Unrecognized device found. Product ID %d, Version %s", product_id, version);
|
||||
}
|
||||
|
||||
// if the boiler or thermostat values have changed, save them to SPIFFS
|
||||
if (do_save) {
|
||||
myESP.fs_saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1140,15 +1169,15 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
*/
|
||||
void ems_discoverModels() {
|
||||
// boiler
|
||||
ems_doReadCommand(EMS_TYPE_Version, EMS_ID_BOILER); // get version details of boiler
|
||||
ems_doReadCommand(EMS_TYPE_Version, EMS_Boiler.type_id); // get version details of boiler
|
||||
ems_getBoilerValues(); // fetch values from boiler, instead of waiting for broadcasts
|
||||
|
||||
// thermostat
|
||||
if (EMS_Thermostat.model_id == EMS_MODEL_NONE) {
|
||||
// if it hasn't been set, auto discover it
|
||||
if (EMS_Thermostat.type_id == EMS_ID_NONE) {
|
||||
ems_scanDevices(); // auto-discover it
|
||||
} else {
|
||||
// set the model as hardcoded (see my_devices.h) and fetch the version and product id
|
||||
_ems_setThermostatModel(EMS_Thermostat.model_id);
|
||||
ems_doReadCommand(EMS_TYPE_Version, EMS_Thermostat.type_id);
|
||||
ems_getThermostatValues();
|
||||
}
|
||||
|
||||
10
src/ems.h
10
src/ems.h
@@ -15,17 +15,13 @@
|
||||
// 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 the "Service Key"
|
||||
#define EMS_ID_BOILER 0x08 // Fixed - boilers are always 0x08. I think.
|
||||
#define EMS_ID_DEFAULT_BOILER 0x08
|
||||
|
||||
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
|
||||
|
||||
// max length of a telegram, including CRC, for Rx and Tx.
|
||||
// This can differs per firmware version and typically 32 is the max
|
||||
#define EMS_MAX_TELEGRAM_LENGTH 99
|
||||
|
||||
// Common for all EMS devices
|
||||
#define EMS_TYPE_Version 0x02 // version of the UBA controller (boiler)
|
||||
|
||||
// default values
|
||||
#define EMS_VALUE_INT_ON 1 // boolean true
|
||||
#define EMS_VALUE_INT_OFF 0 // boolean false
|
||||
@@ -60,6 +56,8 @@
|
||||
|
||||
#define EMS_TX_TELEGRAM_QUEUE_MAX 50 // max size of Tx FIFO queue
|
||||
|
||||
//#define EMS_SYS_LOGGING_DEFAULT EMS_SYS_LOGGING_VERBOSE
|
||||
#define EMS_SYS_LOGGING_DEFAULT EMS_SYS_LOGGING_NONE
|
||||
|
||||
/* EMS UART transfer status */
|
||||
typedef enum {
|
||||
@@ -251,7 +249,7 @@ typedef struct {
|
||||
|
||||
// function definitions
|
||||
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
|
||||
void ems_init(uint8_t thermostat_modelid);
|
||||
void ems_init();
|
||||
void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh = false);
|
||||
void ems_sendRawTelegram(char * telegram);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
* See ChangeLog.md for history
|
||||
* See ChangeLog.md for History
|
||||
* See README.md for Acknowledgments
|
||||
*
|
||||
*/
|
||||
@@ -12,6 +12,12 @@
|
||||
|
||||
#include "ems.h"
|
||||
|
||||
|
||||
/*
|
||||
* Common
|
||||
*/
|
||||
#define EMS_TYPE_Version 0x02
|
||||
|
||||
/*
|
||||
* Boiler...
|
||||
*/
|
||||
@@ -91,11 +97,10 @@ typedef enum {
|
||||
|
||||
} _EMS_MODEL_ID;
|
||||
|
||||
// EMS types for known Buderus devices. This list will be extended when new devices are recognized.
|
||||
// EMS types for known Buderus/Bosch devices. This list will be extended when new devices are recognized.
|
||||
// format is MODEL_ID, PRODUCT ID, TYPE_ID, DESCRIPTION
|
||||
const _Boiler_Type Boiler_Types[] = {
|
||||
|
||||
// various boilers and buderus type devices
|
||||
{EMS_MODEL_UBA, 72, 0x08, "MC10"},
|
||||
{EMS_MODEL_UBA, 123, 0x08, "Buderus GB172/Nefit Trendline"},
|
||||
{EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"},
|
||||
|
||||
@@ -10,15 +10,6 @@
|
||||
|
||||
#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_HOST NULL
|
||||
#define MQTT_USER NULL
|
||||
#define MQTT_PASS NULL
|
||||
|
||||
// All MQTT topics are prefixed with the following string
|
||||
#define MQTT_BASE "home"
|
||||
#define MQTT_TOPIC_START "start"
|
||||
@@ -29,21 +20,53 @@
|
||||
#define MQTT_KEEPALIVE 300
|
||||
#define MQTT_QOS 1
|
||||
|
||||
// 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 an MQTT command
|
||||
|
||||
// default values for shower logic on/off
|
||||
#define BOILER_SHOWER_TIMER 1 // enable (1) to monitor shower time
|
||||
#define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold water when shower time limit has exceeded
|
||||
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// THESE DEFAULT VALUES CAN ALSO BE SET AND STORED WITHTIN THE APPLICATION (see 'set' command) //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Set your wifi and mqtt params
|
||||
// If set to NULL (default) you'll need to set them in AP mode using the 'set' command
|
||||
#define WIFI_SSID NULL
|
||||
#define WIFI_PASSWORD NULL
|
||||
#define MQTT_HOST NULL
|
||||
#define MQTT_USER NULL
|
||||
#define MQTT_PASS NULL
|
||||
|
||||
// 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 on bbqkees' board)
|
||||
// can be either the onboard LED on the ESP8266 (LED_BULLETIN) or external via an external pull-up LED (e.g. D1 on bbqkees' board)
|
||||
// can be enabled and disabled via the 'set led' command
|
||||
#define BOILER_LED LED_BUILTIN // LED_BULLETIN for onboard LED
|
||||
// pin can be set by 'set led_gpio'
|
||||
#define EMSESP_LED_GPIO LED_BUILTIN
|
||||
|
||||
// set this if using an external temperature sensor like a DS18B20
|
||||
// D5 is the default on bbqkees' board
|
||||
#define TEMPERATURE_SENSOR_PIN D5
|
||||
#define EMSESP_DALLAS_GPIO D5
|
||||
|
||||
// By default the EMS bus will be scanned for known devices based on product ids in ems_devices.h
|
||||
// You can override the Thermostat model by hardcoding the model type
|
||||
#define MY_THERMOSTAT_MODELID EMS_MODEL_NONE
|
||||
//#define MY_THERMOSTAT_MODELID EMS_MODEL_RC35
|
||||
// You can override the Thermostat and Boiler types here
|
||||
#define EMSESP_BOILER_TYPE EMS_ID_NONE
|
||||
#define EMSESP_THERMOSTAT_TYPE EMS_ID_NONE
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
#pragma once
|
||||
|
||||
#define APP_NAME "EMS-ESP"
|
||||
#define APP_VERSION "1.4.0"
|
||||
#define APP_VERSION "1.4.1"
|
||||
#define APP_HOSTNAME "ems-esp"
|
||||
|
||||
Reference in New Issue
Block a user