mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
updated to latest ESPurna
This commit is contained in:
47
README.md
47
README.md
@@ -82,12 +82,13 @@ To see the current values of the Boiler and its parameters type 's' and hit Ente
|
||||
Commands can be issued directly to the EMS bus typing in a letter followed by an optional parameter and pressing Enter. Supported commands are:
|
||||
|
||||
- **r** to send a read command to all devices to fetch values. The 2nd parameter is the type. For example 'r 33' will request type UBAParameterWW and bring back the Warm Water temperatures (not the heating) from the Boiler. You can issue any type here.
|
||||
- **t** set the thermostat temperature to the given value
|
||||
- **t** set the thermostat temperature to the given celsius value
|
||||
- **w** to adjust the temperature of the warm water from the boiler
|
||||
- **a** to turn the warm water on and off
|
||||
- **h** to list all the recognized EMS types
|
||||
- **p** to toggle the Polling response on/off. It's not necessary to have Polling enabled to work. I use this for debugging purposes.
|
||||
- **m** to set the thermostat mode from low, manual and clock/auto.
|
||||
- **T** to toggle thermostat reading on/off
|
||||
- **T** to toggle thermostat readings on/off
|
||||
- **S** to toggle the Shower Timer functionality on/off
|
||||
|
||||
**Disclaimer: be careful when sending values to the boiler. If in doubt you can always reset the boiler to its original factory settings by following the instructions in the user guide. On my **Nefit Trendline HRC30** that is done by holding down the Home and Menu buttons simultaneously for a few seconds, selecting factory settings from the scroll menu and lastly pressing the Reset button.**
|
||||
@@ -220,13 +221,13 @@ The code is built on the Arduino framework and is dependent on these external li
|
||||
|
||||
In `boiler.ino` you can make calls to automatically send these read commands. See the function *regularUpdates()*
|
||||
|
||||
#### Support for RC35 type Thermostats
|
||||
#### Supporting RC35 model Thermostats
|
||||
|
||||
An RC35 thermostat has 4 heating circuits. To read the values use the Monitor type IDs (e.g. 0x3E, 0x48, etc). The mode (0=night, 1=day, 2=holiday) is the first byte of the telegram and the temperature is the value of the 2nd byte divided by 2.
|
||||
The code is designed for a Moduline300 (RC20) thermostat. To adjust for a RC35 first change `EMS_ID_THERMOSTAT` in `ems.h`. A RC35 thermostat has 4 heating circuits and to read the values use different Monitor type IDs (e.g. 0x3E, 0x48, etc). The mode (0=night, 1=day, 2=holiday) is the first byte of the telegram and the temperature is the value of the 2nd byte divided by 2.
|
||||
|
||||
Then to set temperature values use the Working Mode with type IDs (0x3D, 0x47,0x51 and 0x5B) respectively. Set the offset (byte 4 of the header) to determine which temperature you're changing; 1 for night, 2 for day and 3 for holiday. The data value is the desired temperature multiplied by 2 as a single byte.
|
||||
|
||||
Consult the wiki documentation for the data format.
|
||||
Consult the wiki documentation for the actual data format.
|
||||
|
||||
### Customizing the code
|
||||
|
||||
@@ -244,7 +245,7 @@ I'm using the standard PubSubClient client so make sure you set `-DMQTT_MAX_PACK
|
||||
|
||||
I run Mosquitto on my Raspberry PI 3 as the MQTT broker.
|
||||
|
||||
The boiler data is collected and sent as a single JSON object to MQTT TOPIC `home/boiler/boiler_data`. A hash is generated (CRC32 based) to determine if the payload has changed, otherwise don't send it. An example payload looks like:
|
||||
The boiler data is collected and sent as a single JSON object to MQTT TOPIC `home/boiler/boiler_data` (or `{hostname}/boiler_data` for ESPurna). A hash is generated (CRC32 based) to determine if the payload has changed, otherwise don't send it. An example payload looks like:
|
||||
|
||||
`{"wWCurTmp":"43.0","wWHeat":"on","curFlowTemp":"51.7","retTemp":"48.0","burnGas":"off","heatPmp":"off","fanWork":"off","ignWork":"off","wWCirc":"off","selBurnPow":"0","curBurnPow":"0","sysPress":"1.6","boilTemp":"54.7","pumpMod":"4"}`
|
||||
|
||||
@@ -290,30 +291,34 @@ Roughly these are the steps needed when running Windows:
|
||||
|
||||
### Using ESPurna
|
||||
|
||||
[ESPurna](https://github.com/xoseperez/espurna/wiki) is framework that handles most of the tedious tasks of building IoT devices so you can focus on the functionality you need. This replaces my ESPHelper code in the standalone version above. ESPurna is natively built on PlatformIO and Visual Studio Code too which is nice. So if you're brave, follow these steps:
|
||||
[ESPurna](https://github.com/xoseperez/espurna/wiki) is framework that handles most of the tedious tasks of building IoT devices so you can focus on the functionality you need. This replaces my ESPHelper code in the standalone version above. ESPurna is natively built on PlatformIO and Visual Studio Code too which is nice. The latest version supported is 1.13.3. So if you're brave, follow these steps:
|
||||
|
||||
1. Download and install [NodeJS](https://nodejs.org/en/download). This gives you npm. Choose the LTS version
|
||||
2. Download ESPurna by cloning the ESPurna git repository from `https://github.com/xoseperez/espurna.git`
|
||||
3. Restart VSC. PlatformIO should detect and set some things up for you automagically
|
||||
4. From VSC open the folder `espurna\code`
|
||||
2. Download ESPurna by cloning the ESPurna git repository (command palette, git clone, https://github.com/xoseperez/espurna.git)
|
||||
3. Restart VSC.
|
||||
4. From VSC open the folder `espurna\code`. PlatformIO should detect and set some things up for you automagically
|
||||
5. open a terminal window (*ctrl-`*)
|
||||
6. Install the node modules: `npm install --only=dev`
|
||||
7. Build the web interface: `node node_modules/gulp/bin/gulp.js`. This will create a compressed `code/espurna/static/index.html.gz.h`
|
||||
8. First time users build the filesystem by *ctrl-alt-t* and run the task 'uploadfs'
|
||||
9. Copy these files from this repo's *espurna* directory to where you installed ESPurna
|
||||
7. Build the web interface: `node node_modules/gulp/bin/gulp.js`. This will create a compressed `code/espurna/static/index.html.gz.h`. If you get warnings about lf during the building edit `gulpfile.js` and change the line `'failOnError': true` to `false` as a temporary workaround.
|
||||
8. Modify the platformio.ini file making sure you add `-DUSE_CUSTOM_H -DUSE_EXTRA` to the `debug_flags`
|
||||
9. Copy the following files from EMS-ESP-Boiler repo to where you installed ESPurna
|
||||
|
||||
```
|
||||
code/html/index.html
|
||||
code/config/custom.h
|
||||
code/espurna/boiler-espurna.ino
|
||||
code/espurna/ems*.*
|
||||
espurna/index.html -> code/html/index.html
|
||||
espurna/custom.h -> code/config/custom.h
|
||||
espurna/boiler-espurna.ino -> code/espurna/boiler-espurna.ino
|
||||
ems*.* -> code/espurna/
|
||||
```
|
||||
|
||||
10. Now build and upload as you usually would. Look at my version of platformio.ini as an example.
|
||||
10. Now build and upload as you usually would with PlatformIO (or ctrl-arl-t and choose the right build). Look at my version of platformio.ini as an example.
|
||||
11. When the firmware loads, use a wifi connected pc/mobile to connect to the Access Point called ESPURNA_XXXXXX. Use 'fibonacci' as the password. Navigate to `http://192.168.4.1` from a browser, set a new username and password when prompted, log off the wifi and reconnect to the AP using these new credentials. Again go to 192.168.4.1
|
||||
12. In the ADMIN page enable Telnet and SAVE
|
||||
13. In the WIFI page add your home wifi details, click SAVE and reboot, and go to the new IP
|
||||
14. Configure MQTT
|
||||
|
||||
The Telnet functions are `BOILER.READ`, `BOILER.INFO` and a few others for reference. `HELP` will list them. Add your own functions to expand the functionality by calling the EMS* functions as in the examples.
|
||||
|
||||
If you run into issues refer to ESPurna's official setup instructions [here](https://github.com/xoseperez/espurna/wiki/Build-and-update-from-Visual-Studio-Code-using-PlatformIO).
|
||||
If you run into issues refer to ESPurna's official setup instructions [here](https://github.com/xoseperez/espurna/wiki/Build-and-update-from-Visual-Studio-Code-using-PlatformIO) and [here](https://github.com/xoseperez/espurna/wiki/Configuration).
|
||||
|
||||
|
||||
This is what ESPurna looks like with the custom boiler code:
|
||||
@@ -328,10 +333,8 @@ pre-baked firmwares for some ESP8266 devices based on ESPurna are available in t
|
||||
2. Install the ESPTool by running `pip install esptool` from a command prompt
|
||||
3. Connect the ESP via USB, figure out the COM port
|
||||
4. run `esptool.py -p <com> write_flash 0x00000 <firmware>` where firmware is the `.bin` file and \<com\> is the COM port, e.g. `COM3`
|
||||
5. Connect using WiFi from a phone or PC to the "**ESPURNA_XXXXXX**" network. Password is '**fibonacci**'
|
||||
6. Once connected browse to "http://192.168.4.1" and setup your wifi, mqtt etc
|
||||
|
||||
Again, if you run into problems read [this](https://github.com/xoseperez/espurna/wiki/Configuration) from ESPurna's configuration help page.
|
||||
now follow the steps in ESPurna section above from #10 on to configure the device.
|
||||
|
||||
### Using the Arduino IDE
|
||||
|
||||
|
||||
@@ -13,20 +13,31 @@
|
||||
#define BOILER_POLLING_ENABLED 0
|
||||
#define BOILER_LOGGING_NONE 1
|
||||
|
||||
// home/boiler/
|
||||
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values
|
||||
#define TOPIC_THERMOSTAT_TEMP "thermostat_temp" // for received thermostat temp changes
|
||||
#define TOPIC_THERMOSTAT_CURRTEMP "thermostat_currtemp" // current temperature
|
||||
#define TOPIC_THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature
|
||||
#define TOPIC_THERMOSTAT_MODE "thermostat_mode" // selected temperature
|
||||
#define TOPIC_BOILER_WARM_WATER_SELECTED_TEMPERATURE "boiler_wwtemp" // warm water selected temp
|
||||
|
||||
#define BOILERSEND_INTERVAL 60000 // send every minute to HA
|
||||
|
||||
typedef struct {
|
||||
bool wifi_connected;
|
||||
bool boiler_online;
|
||||
bool thermostat_enabled;
|
||||
bool shower_enabled; // true if we want to report back on shower times
|
||||
bool shower_timer; // true if we want the cold water reminder
|
||||
bool shower_timer; // true if we want to report back on shower times
|
||||
bool shower_alert; // true if we want the cold water reminder
|
||||
} _Boiler_Status;
|
||||
|
||||
typedef struct {
|
||||
bool showerOn;
|
||||
unsigned long timerStart; // ms
|
||||
unsigned long timerPause; // ms
|
||||
unsigned long duration; // ms
|
||||
bool isColdShot; // true if we've just sent a jolt of cold water
|
||||
bool hotWaterOn;
|
||||
unsigned long timerStart; // ms
|
||||
unsigned long timerPause; // ms
|
||||
unsigned long duration; // ms
|
||||
bool doingColdShot; // true if we've just sent a jolt of cold water
|
||||
} _Boiler_Shower;
|
||||
|
||||
// store for overall system status
|
||||
@@ -44,7 +55,7 @@ void _boilerConfigure() {
|
||||
uint8_t _boilerLogging = getSetting("boilerLogging", BOILER_LOGGING_NONE).toInt();
|
||||
ems_setLogging((_EMS_SYS_LOGGING)_boilerLogging);
|
||||
|
||||
Boiler_Status.shower_enabled = getSetting("boilerShower", BOILER_SHOWER_ENABLED).toInt() == 1;
|
||||
Boiler_Status.shower_timer = getSetting("boilerShower", BOILER_SHOWER_ENABLED).toInt() == 1;
|
||||
}
|
||||
|
||||
// WEB callbacks
|
||||
@@ -63,38 +74,111 @@ void _boilerWebSocketOnSend(JsonObject & root) {
|
||||
void _boilerWebSocketOnAction(uint32_t client_id, const char * action, JsonObject & data) {
|
||||
}
|
||||
|
||||
// send to HA
|
||||
void sendHA() {
|
||||
String topic = getSetting("haPrefix", HOMEASSISTANT_PREFIX);
|
||||
String output;
|
||||
// assume ha is enabled
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject & config = jsonBuffer.createObject();
|
||||
String name = getSetting("hostname");
|
||||
config.set("name", name);
|
||||
config.set("platform", "mqtt");
|
||||
config.printTo(output);
|
||||
// convert float to char
|
||||
//char * _float_to_char(char * a, float f, uint8_t precision = 1);
|
||||
char * _float_to_char(char * a, float f, uint8_t precision = 1) {
|
||||
long p[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
|
||||
|
||||
mqttSendRaw(topic.c_str(), output.c_str());
|
||||
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
|
||||
char * ret = a;
|
||||
// check for 0x8000 (sensor missing), which has a -1 value
|
||||
if (f == EMS_VALUE_FLOAT_NOTSET) {
|
||||
strcpy(ret, "?");
|
||||
} else {
|
||||
long whole = (long)f;
|
||||
itoa(whole, a, 10);
|
||||
while (*a != '\0')
|
||||
a++;
|
||||
*a++ = '.';
|
||||
long decimal = abs((long)((f - whole) * p[precision]));
|
||||
itoa(decimal, a, 10);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// convert bool to text
|
||||
char * _bool_to_char(char * s, uint8_t value) {
|
||||
if (value == EMS_VALUE_INT_ON) {
|
||||
strcpy(s, "on");
|
||||
} else if (value == EMS_VALUE_INT_OFF) {
|
||||
strcpy(s, "off");
|
||||
} else {
|
||||
strcpy(s, "?");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// convert int to text value
|
||||
char * _int_to_char(char * s, uint8_t value) {
|
||||
if (value == EMS_VALUE_INT_NOTSET) {
|
||||
strcpy(s, "?");
|
||||
} else {
|
||||
itoa(value, s, 10);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// takes a float value at prints it to debug log
|
||||
void _renderFloatValue(const char * prefix, const char * postfix, float value) {
|
||||
myDebug(" %s: ", prefix);
|
||||
char s[20];
|
||||
myDebug("%s", _float_to_char(s, value));
|
||||
|
||||
if (postfix != NULL) {
|
||||
myDebug(" %s", postfix);
|
||||
}
|
||||
|
||||
myDebug("\n");
|
||||
}
|
||||
|
||||
// takes an int value at prints it to debug log
|
||||
void _renderIntValue(const char * prefix, const char * postfix, uint8_t value) {
|
||||
myDebug(" %s: ", prefix);
|
||||
char s[20];
|
||||
myDebug("%s", _int_to_char(s, value));
|
||||
|
||||
if (postfix != NULL) {
|
||||
myDebug(" %s", postfix);
|
||||
}
|
||||
|
||||
myDebug("\n");
|
||||
}
|
||||
|
||||
// takes a bool value at prints it to debug log
|
||||
void _renderBoolValue(const char * prefix, uint8_t value) {
|
||||
myDebug(" %s: ", prefix);
|
||||
char s[20];
|
||||
myDebug("%s\n", _bool_to_char(s, value));
|
||||
}
|
||||
|
||||
// Show command - display stats on an 's' command
|
||||
void showInfo() {
|
||||
char s[10]; // for formatting floats using the _float_to_char() function
|
||||
|
||||
// General stats from EMS bus
|
||||
myDebug("EMS Bus stats:\n");
|
||||
myDebug(" Thermostat is %s, Poll is %s, Shower is %s, Shower timer is %s, RxPgks=%d, TxPkgs=%d, #CrcErrors=%d",
|
||||
myDebug("%sEMS-ESP-Boiler system stats:%s\n", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
myDebug(" System Logging is set to ");
|
||||
_EMS_SYS_LOGGING sysLog = ems_getLogging();
|
||||
if (sysLog == EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Basic");
|
||||
} else if (sysLog == EMS_SYS_LOGGING_VERBOSE) {
|
||||
myDebug("Verbose");
|
||||
} else {
|
||||
myDebug("None");
|
||||
}
|
||||
|
||||
myDebug("\n # EMS type handlers: %d\n", ems_getEmsTypesCount());
|
||||
|
||||
myDebug(" Thermostat is %s, Poll is %s, Shower timer is %s, Shower alert is %s\n",
|
||||
((Boiler_Status.thermostat_enabled) ? "enabled" : "disabled"),
|
||||
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
||||
((Boiler_Status.shower_enabled) ? "enabled" : "disabled"),
|
||||
((Boiler_Status.shower_timer) ? "enabled" : "disabled"),
|
||||
((Boiler_Status.shower_alert) ? "enabled" : "disabled"));
|
||||
|
||||
myDebug(" EMS Bus Stats: Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d, ",
|
||||
(Boiler_Status.boiler_online ? "yes" : "no"),
|
||||
EMS_Sys_Status.emsRxPgks,
|
||||
EMS_Sys_Status.emsTxPkgs,
|
||||
EMS_Sys_Status.emxCrcErr);
|
||||
|
||||
myDebug(", RxStatus=");
|
||||
myDebug("Rx Status=");
|
||||
switch (EMS_Sys_Status.emsRxStatus) {
|
||||
case EMS_RX_IDLE:
|
||||
myDebug("idle");
|
||||
@@ -104,7 +188,7 @@ void showInfo() {
|
||||
break;
|
||||
}
|
||||
|
||||
myDebug(", TxStatus=");
|
||||
myDebug(", Tx Status=");
|
||||
switch (EMS_Sys_Status.emsTxStatus) {
|
||||
case EMS_TX_IDLE:
|
||||
myDebug("idle");
|
||||
@@ -117,7 +201,7 @@ void showInfo() {
|
||||
break;
|
||||
}
|
||||
|
||||
myDebug(", TxAction=");
|
||||
myDebug(", Last Tx Action=");
|
||||
switch (EMS_TxTelegram.action) {
|
||||
case EMS_TX_READ:
|
||||
myDebug("read");
|
||||
@@ -133,41 +217,42 @@ void showInfo() {
|
||||
break;
|
||||
}
|
||||
|
||||
myDebug("\nBoiler stats:\n");
|
||||
myDebug("\n\n%sBoiler stats:%s\n", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
|
||||
// UBAMonitorWWMessage & UBAParameterWW
|
||||
myDebug(" Warm Water activated: %s\n", (EMS_Boiler.wWActivated ? "yes" : "no"));
|
||||
myDebug(" Warm Water selected temperature: %d C\n", EMS_Boiler.wWSelTemp);
|
||||
myDebug(" Warm Water circulation pump available: %s\n", (EMS_Boiler.wWCircPump ? "yes" : "no"));
|
||||
myDebug(" Warm Water desired temperature: %d C\n", EMS_Boiler.wWDesiredTemp);
|
||||
myDebug(" Warm Water current temperature: %s C\n", _float_to_char(s, EMS_Boiler.wWCurTmp));
|
||||
myDebug(" Warm Water # starts: %d times\n", EMS_Boiler.wWStarts);
|
||||
// UBAParameterWW
|
||||
_renderBoolValue("Warm Water activated", EMS_Boiler.wWActivated);
|
||||
_renderBoolValue("Warm Water circulation pump available", EMS_Boiler.wWCircPump);
|
||||
_renderIntValue("Warm Water selected temperature", "C", EMS_Boiler.wWSelTemp);
|
||||
_renderIntValue("Warm Water desired temperature", "C", EMS_Boiler.wWDesiredTemp);
|
||||
|
||||
// UBAMonitorWWMessage
|
||||
_renderFloatValue("Warm Water current temperature", "C", EMS_Boiler.wWCurTmp);
|
||||
_renderIntValue("Warm Water # starts", "times", EMS_Boiler.wWStarts);
|
||||
myDebug(" Warm Water active time: %d days %d hours %d minutes\n",
|
||||
EMS_Boiler.wWWorkM / 1440,
|
||||
(EMS_Boiler.wWWorkM % 1440) / 60,
|
||||
EMS_Boiler.wWWorkM % 60);
|
||||
myDebug(" Warm Water 3-way valve: %s\n", EMS_Boiler.wWHeat ? "on" : "off");
|
||||
_renderBoolValue("Warm Water 3-way valve", EMS_Boiler.wWHeat);
|
||||
|
||||
// UBAMonitorFast
|
||||
myDebug(" Selected flow temperature: %d C\n", EMS_Boiler.selFlowTemp);
|
||||
myDebug(" Current flow temperature: %s C\n", _float_to_char(s, EMS_Boiler.curFlowTemp));
|
||||
myDebug(" Return temperature: %s C\n", _float_to_char(s, EMS_Boiler.retTemp));
|
||||
|
||||
myDebug(" Gas: %s\n", EMS_Boiler.burnGas ? "on" : "off");
|
||||
myDebug(" Boiler pump: %s\n", EMS_Boiler.heatPmp ? "on" : "off");
|
||||
myDebug(" Fan: %s\n", EMS_Boiler.fanWork ? "on" : "off");
|
||||
myDebug(" Ignition: %s\n", EMS_Boiler.ignWork ? "on" : "off");
|
||||
myDebug(" Circulation pump: %s\n", EMS_Boiler.wWCirc ? "on" : "off");
|
||||
myDebug(" Burner max power: %d %%\n", EMS_Boiler.selBurnPow);
|
||||
myDebug(" Burner current power: %d %%\n", EMS_Boiler.curBurnPow);
|
||||
myDebug(" Flame current: %s uA\n", _float_to_char(s, EMS_Boiler.flameCurr));
|
||||
myDebug(" System pressure: %s bar\n", _float_to_char(s, EMS_Boiler.sysPress));
|
||||
_renderIntValue("Selected flow temperature", "C", EMS_Boiler.selFlowTemp);
|
||||
_renderFloatValue("Current flow temperature", "C", EMS_Boiler.curFlowTemp);
|
||||
_renderFloatValue("Return temperature", "C", EMS_Boiler.retTemp);
|
||||
_renderBoolValue("Gas", EMS_Boiler.burnGas);
|
||||
_renderBoolValue("Boiler pump", EMS_Boiler.heatPmp);
|
||||
_renderBoolValue("Fan", EMS_Boiler.fanWork);
|
||||
_renderBoolValue("Ignition", EMS_Boiler.ignWork);
|
||||
_renderBoolValue("Circulation pump", EMS_Boiler.wWCirc);
|
||||
_renderIntValue("Burner selected max power", "%", EMS_Boiler.selBurnPow);
|
||||
_renderIntValue("Burner current power", "%", EMS_Boiler.curBurnPow);
|
||||
_renderFloatValue("Flame current", "uA", EMS_Boiler.flameCurr);
|
||||
_renderFloatValue("System pressure", "bar", EMS_Boiler.sysPress);
|
||||
|
||||
// UBAMonitorSlow
|
||||
myDebug(" Outside temperature: %s C\n", _float_to_char(s, EMS_Boiler.extTemp));
|
||||
myDebug(" Boiler temperature: %s C\n", _float_to_char(s, EMS_Boiler.boilTemp));
|
||||
myDebug(" Pump modulation: %d %%\n", EMS_Boiler.pumpMod);
|
||||
myDebug(" # burner restarts: %d\n", EMS_Boiler.burnStarts);
|
||||
_renderFloatValue("Outside temperature", "C", EMS_Boiler.extTemp);
|
||||
_renderFloatValue("Boiler temperature", "C", EMS_Boiler.boilTemp);
|
||||
_renderIntValue("Pump modulation", "%", EMS_Boiler.pumpMod);
|
||||
_renderIntValue("Burner # restarts", "times", EMS_Boiler.burnStarts);
|
||||
myDebug(" Total burner operating time: %d days %d hours %d minutes\n",
|
||||
EMS_Boiler.burnWorkMin / 1440,
|
||||
(EMS_Boiler.burnWorkMin % 1440) / 60,
|
||||
@@ -179,7 +264,8 @@ void showInfo() {
|
||||
|
||||
// Thermostat stats
|
||||
if (Boiler_Status.thermostat_enabled) {
|
||||
myDebug("Thermostat stats:\n Thermostat time is %02d:%02d:%02d %d/%d/%d\n",
|
||||
myDebug("\n%sThermostat stats:%s\n", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
myDebug(" Thermostat time is %02d:%02d:%02d %d/%d/%d\n",
|
||||
EMS_Thermostat.hour,
|
||||
EMS_Thermostat.minute,
|
||||
EMS_Thermostat.second,
|
||||
@@ -187,8 +273,8 @@ void showInfo() {
|
||||
EMS_Thermostat.month,
|
||||
EMS_Thermostat.year + 2000);
|
||||
|
||||
myDebug(" Setpoint room temperature is %s C\n", _float_to_char(s, EMS_Thermostat.setpoint_roomTemp));
|
||||
myDebug(" Current room temperature is %s C\n", _float_to_char(s, EMS_Thermostat.curr_roomTemp));
|
||||
_renderFloatValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp);
|
||||
_renderFloatValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp);
|
||||
myDebug(" Mode is set to ");
|
||||
if (EMS_Thermostat.mode == 0) {
|
||||
myDebug("low\n");
|
||||
@@ -197,20 +283,10 @@ void showInfo() {
|
||||
} else if (EMS_Thermostat.mode == 2) {
|
||||
myDebug("clock/auto\n");
|
||||
} else {
|
||||
myDebug("<unknown>\n");
|
||||
myDebug("?\n");
|
||||
}
|
||||
}
|
||||
|
||||
// show the Shower Info
|
||||
if (Boiler_Status.shower_enabled) {
|
||||
myDebug("Shower stats:\n Shower is %s\n", (Boiler_Shower.showerOn ? "on" : "off"));
|
||||
char s[70];
|
||||
uint8_t sec = (uint8_t)((Boiler_Shower.duration / 1000) % 60);
|
||||
uint8_t min = (uint8_t)((Boiler_Shower.duration / (1000 * 60)) % 60);
|
||||
sprintf(s, " Last shower duration was %d minutes and %d %s\n", min, sec, (sec == 1) ? "second" : "seconds");
|
||||
myDebug(s);
|
||||
}
|
||||
|
||||
myDebug("\n");
|
||||
}
|
||||
|
||||
@@ -242,7 +318,7 @@ void _boilerInitCommands() {
|
||||
|
||||
settingsRegisterCommand(F("BOILER.LOGGING"), [](Embedis * e) {
|
||||
if (e->argc < 2) {
|
||||
DEBUG_MSG_P(PSTR("-ERROR: arg is 0 or 1\n"));
|
||||
DEBUG_MSG_P(PSTR("-ERROR: arg is 0, 1 or 2\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -269,43 +345,60 @@ void _boilerInitCommands() {
|
||||
});
|
||||
}
|
||||
|
||||
void _boilerMQTTCallback(unsigned int type, const char * topic, const char * payload) {
|
||||
/*
|
||||
|
||||
if (type == MQTT_CONNECT_EVENT) {
|
||||
char buffer[strlen(MQTT_TOPIC_LED) + 3];
|
||||
snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_LED);
|
||||
mqttSubscribe(buffer);
|
||||
}
|
||||
// send values to HA via MQTT
|
||||
void publishValues() {
|
||||
char s[20]; // for formatting strings
|
||||
|
||||
if (type == MQTT_MESSAGE_EVENT) {
|
||||
// Boiler values as one JSON object
|
||||
StaticJsonBuffer<512> jsonBuffer;
|
||||
char data[512];
|
||||
JsonObject & root = jsonBuffer.createObject();
|
||||
|
||||
// Match topic
|
||||
String t = mqttMagnitude((char *) topic);
|
||||
if (!t.startsWith(MQTT_TOPIC_LED)) return;
|
||||
root["wWSelTemp"] = _int_to_char(s, EMS_Boiler.wWSelTemp);
|
||||
root["wWActivated"] = _bool_to_char(s, EMS_Boiler.wWActivated);
|
||||
root["wWCurTmp"] = _float_to_char(s, EMS_Boiler.wWCurTmp);
|
||||
root["wWHeat"] = _bool_to_char(s, EMS_Boiler.wWHeat);
|
||||
root["curFlowTemp"] = _float_to_char(s, EMS_Boiler.curFlowTemp);
|
||||
root["retTemp"] = _float_to_char(s, EMS_Boiler.retTemp);
|
||||
root["burnGas"] = _bool_to_char(s, EMS_Boiler.burnGas);
|
||||
root["heatPmp"] = _bool_to_char(s, EMS_Boiler.heatPmp);
|
||||
root["fanWork"] = _bool_to_char(s, EMS_Boiler.fanWork);
|
||||
root["ignWork"] = _bool_to_char(s, EMS_Boiler.ignWork);
|
||||
root["wWCirc"] = _bool_to_char(s, EMS_Boiler.wWCirc);
|
||||
root["selBurnPow"] = _int_to_char(s, EMS_Boiler.selBurnPow);
|
||||
root["curBurnPow"] = _int_to_char(s, EMS_Boiler.curBurnPow);
|
||||
root["sysPress"] = _float_to_char(s, EMS_Boiler.sysPress);
|
||||
root["boilTemp"] = _float_to_char(s, EMS_Boiler.boilTemp);
|
||||
root["pumpMod"] = _int_to_char(s, EMS_Boiler.pumpMod);
|
||||
|
||||
// Get led ID
|
||||
unsigned int ledID = t.substring(strlen(MQTT_TOPIC_LED)+1).toInt();
|
||||
if (ledID >= _ledCount()) {
|
||||
DEBUG_MSG_P(PSTR("[LED] Wrong ledID (%d)\n"), ledID);
|
||||
size_t len = root.measureLength();
|
||||
root.printTo(data, len + 1); // form the json string
|
||||
mqttSend(TOPIC_BOILER_DATA, data);
|
||||
|
||||
// handle the thermostat values separately
|
||||
if (EMS_Sys_Status.emsThermostatEnabled) {
|
||||
// only send thermostat values if we actually have them
|
||||
if (((int)EMS_Thermostat.curr_roomTemp == (int)0) || ((int)EMS_Thermostat.setpoint_roomTemp == (int)0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if LED is managed
|
||||
if (_ledMode(ledID) != LED_MODE_MQTT) return;
|
||||
mqttSend(TOPIC_THERMOSTAT_CURRTEMP, _float_to_char(s, EMS_Thermostat.curr_roomTemp));
|
||||
mqttSend(TOPIC_THERMOSTAT_SELTEMP, _float_to_char(s, EMS_Thermostat.setpoint_roomTemp));
|
||||
|
||||
// get value
|
||||
unsigned char value = relayParsePayload(payload);
|
||||
|
||||
// Action to perform
|
||||
if (value == 2) {
|
||||
_ledToggle(ledID);
|
||||
// send mode 0=low, 1=manual, 2=clock/auto
|
||||
if (EMS_Thermostat.mode == 0) {
|
||||
mqttSend(TOPIC_THERMOSTAT_MODE, "low");
|
||||
} else if (EMS_Thermostat.mode == 1) {
|
||||
mqttSend(TOPIC_THERMOSTAT_MODE, "manual");
|
||||
} else {
|
||||
_ledStatus(ledID, value == 1);
|
||||
mqttSend(TOPIC_THERMOSTAT_MODE, "auto"); // must be auto
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
void _boilerMQTTCallback(unsigned int type, const char * topic, const char * payload) {
|
||||
}
|
||||
|
||||
// TELNET commands
|
||||
@@ -330,19 +423,18 @@ void boilerSetup() {
|
||||
_boilerConfigure();
|
||||
|
||||
wsOnSendRegister(_boilerWebSocketOnSend);
|
||||
wsOnAfterParseRegister(_boilerConfigure);
|
||||
wsOnActionRegister(_boilerWebSocketOnAction);
|
||||
wsOnReceiveRegister(_boilerWebSocketOnReceive);
|
||||
|
||||
mqttRegister(_boilerMQTTCallback);
|
||||
|
||||
_boilerInitCommands();
|
||||
_boilerInitCommands(); // telnet
|
||||
|
||||
// init shower
|
||||
Boiler_Shower.timerStart = 0;
|
||||
Boiler_Shower.timerPause = 0;
|
||||
Boiler_Shower.duration = 0;
|
||||
Boiler_Shower.isColdShot = false;
|
||||
Boiler_Shower.timerStart = 0;
|
||||
Boiler_Shower.timerPause = 0;
|
||||
Boiler_Shower.duration = 0;
|
||||
Boiler_Shower.doingColdShot = false;
|
||||
|
||||
// ems init values
|
||||
ems_init();
|
||||
@@ -352,8 +444,22 @@ void boilerSetup() {
|
||||
|
||||
// Register loop
|
||||
espurnaRegisterLoop(_boilerLoop);
|
||||
|
||||
espurnaRegisterReload([]() { _boilerConfigure(); });
|
||||
}
|
||||
|
||||
// LOOP
|
||||
void _boilerLoop() {
|
||||
static unsigned long last_boilersend = 0;
|
||||
if ((last_boilersend == 0) || (millis() - last_boilersend > BOILERSEND_INTERVAL)) {
|
||||
last_boilersend = millis();
|
||||
|
||||
// get 0x33 WW values manually
|
||||
ems_doReadCommand(EMS_TYPE_UBAParameterWW);
|
||||
|
||||
#if MQTT_SUPPORT
|
||||
// send MQTT
|
||||
publishValues();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
#undef EMBEDDED_WEB
|
||||
#define EMBEDDED_WEB 1
|
||||
|
||||
#undef NTP_SERVER
|
||||
#define NTP_SERVER "nl.pool.ntp.org"
|
||||
|
||||
//#undef MQTT_TOPIC
|
||||
//#define MQTT_TOPIC "/{identifier}"
|
||||
// default is "{hostname}"
|
||||
|
||||
1268
espurna/index.html
1268
espurna/index.html
File diff suppressed because it is too large
Load Diff
Binary file not shown.
BIN
firmware/nodemcu2.bin
Normal file
BIN
firmware/nodemcu2.bin
Normal file
Binary file not shown.
Binary file not shown.
BIN
firmware/wemos-d1mini.bin
Normal file
BIN
firmware/wemos-d1mini.bin
Normal file
Binary file not shown.
Reference in New Issue
Block a user