mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-10 01:39:54 +03:00
alpha 0.2 - add solar, mixing, heatpump
This commit is contained in:
185
src/boiler.cpp
185
src/boiler.cpp
@@ -49,21 +49,24 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
DEBUG_LOG(F("Registering new Boiler with device ID 0x%02X"), device_id);
|
||||
|
||||
// the telegram handlers...
|
||||
register_telegram_type(0x18, F("UBAMonitorFast"), true, std::bind(&Boiler::process_UBAMonitorFast, this, _1)); // 0x18
|
||||
register_telegram_type(0x19, F("UBAMonitorSlow"), true, std::bind(&Boiler::process_UBAMonitorSlow, this, _1)); // 0x19
|
||||
register_telegram_type(0x34, F("UBAMonitorWW"), false, std::bind(&Boiler::process_UBAMonitorWW, this, _1)); // 0x34
|
||||
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, std::bind(&Boiler::process_UBAMaintenanceStatus, this, _1)); // 0x1C
|
||||
register_telegram_type(0x2A, F("MC10Status"), false, std::bind(&Boiler::process_MC10Status, this, _1)); // 0x2A
|
||||
register_telegram_type(0x33, F("UBAParameterWW"), true, std::bind(&Boiler::process_UBAParameterWW, this, _1)); // 0x33
|
||||
register_telegram_type(0x14, F("UBATotalUptime"), true, std::bind(&Boiler::process_UBATotalUptime, this, _1)); // 0x14
|
||||
register_telegram_type(0x35, F("UBAFlags"), false, std::bind(&Boiler::process_UBAFlags, this, _1)); // 0x35
|
||||
register_telegram_type(0x15, F("UBAMaintenanceSettings"), false, std::bind(&Boiler::process_UBAMaintenanceSettings, this, _1)); // 0x15
|
||||
register_telegram_type(0x16, F("UBAParameters"), true, std::bind(&Boiler::process_UBAParameters, this, _1)); // 0x16
|
||||
register_telegram_type(0x1A, F("UBASetPoints"), false, std::bind(&Boiler::process_UBASetPoints, this, _1)); // 0x1A
|
||||
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, std::bind(&Boiler::process_UBAOutdoorTemp, this, _1)); // 0xD1
|
||||
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, std::bind(&Boiler::process_UBAMonitorFastPlus, this, _1)); // 0xE4
|
||||
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, std::bind(&Boiler::process_UBAMonitorSlowPlus, this, _1)); // 0xE5
|
||||
register_telegram_type(0xE9, F("UBADHWStatus"), false, std::bind(&Boiler::process_UBADHWStatus, this, _1)); // 0xE9
|
||||
register_telegram_type(0x18, F("UBAMonitorFast"), true, std::bind(&Boiler::process_UBAMonitorFast, this, _1));
|
||||
register_telegram_type(0x19, F("UBAMonitorSlow"), true, std::bind(&Boiler::process_UBAMonitorSlow, this, _1));
|
||||
register_telegram_type(0x34, F("UBAMonitorWW"), false, std::bind(&Boiler::process_UBAMonitorWW, this, _1));
|
||||
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, std::bind(&Boiler::process_UBAMaintenanceStatus, this, _1));
|
||||
register_telegram_type(0x2A, F("MC10Status"), false, std::bind(&Boiler::process_MC10Status, this, _1));
|
||||
register_telegram_type(0x33, F("UBAParameterWW"), true, std::bind(&Boiler::process_UBAParameterWW, this, _1));
|
||||
register_telegram_type(0x14, F("UBATotalUptime"), true, std::bind(&Boiler::process_UBATotalUptime, this, _1));
|
||||
register_telegram_type(0x35, F("UBAFlags"), false, std::bind(&Boiler::process_UBAFlags, this, _1));
|
||||
register_telegram_type(0x15, F("UBAMaintenanceSettings"), false, std::bind(&Boiler::process_UBAMaintenanceSettings, this, _1));
|
||||
register_telegram_type(0x16, F("UBAParameters"), true, std::bind(&Boiler::process_UBAParameters, this, _1));
|
||||
register_telegram_type(0x1A, F("UBASetPoints"), false, std::bind(&Boiler::process_UBASetPoints, this, _1));
|
||||
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, std::bind(&Boiler::process_UBAOutdoorTemp, this, _1));
|
||||
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, std::bind(&Boiler::process_UBAMonitorFastPlus, this, _1));
|
||||
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, std::bind(&Boiler::process_UBAMonitorSlowPlus, this, _1));
|
||||
|
||||
register_telegram_type(0xE9, F("UBADHWStatus"), false, std::bind(&Boiler::process_UBADHWStatus, this, _1));
|
||||
register_telegram_type(0xE3, F("HeatPumpMonitor1"), true, std::bind(&Boiler::process_HPMonitor1, this, _1));
|
||||
register_telegram_type(0xE5, F("HeatPumpMonitor2"), true, std::bind(&Boiler::process_HPMonitor2, this, _1));
|
||||
|
||||
// MQTT callbacks
|
||||
register_mqtt_topic("boiler_cmd", std::bind(&Boiler::boiler_cmd, this, _1));
|
||||
@@ -73,6 +76,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_mqtt_topic("boiler_cmd_wwtemp", std::bind(&Boiler::boiler_cmd_wwtemp, this, _1));
|
||||
}
|
||||
|
||||
// add submenu context
|
||||
void Boiler::add_context_menu() {
|
||||
EMSESPShell::commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
@@ -83,8 +87,8 @@ void Boiler::add_context_menu() {
|
||||
});
|
||||
}
|
||||
|
||||
// boiler_cmd topic
|
||||
void Boiler::boiler_cmd(const char * message) {
|
||||
// convert JSON and get the command
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
DeserializationError error = deserializeJson(doc, message);
|
||||
if (error) {
|
||||
@@ -157,148 +161,155 @@ void Boiler::boiler_cmd_wwtemp(const char * message) {
|
||||
void Boiler::publish_values() {
|
||||
const size_t capacity = JSON_OBJECT_SIZE(47); // must recalculate if more objects addded https://arduinojson.org/v6/assistant/
|
||||
DynamicJsonDocument doc(capacity);
|
||||
JsonObject rootBoiler = doc.to<JsonObject>();
|
||||
|
||||
char s[10]; // for formatting strings
|
||||
|
||||
if (wWComfort_ == 0x00) {
|
||||
rootBoiler["wWComfort"] = "Hot";
|
||||
doc["wWComfort"] = "Hot";
|
||||
} else if (wWComfort_ == 0xD8) {
|
||||
rootBoiler["wWComfort"] = "Eco";
|
||||
doc["wWComfort"] = "Eco";
|
||||
} else if (wWComfort_ == 0xEC) {
|
||||
rootBoiler["wWComfort"] = "Intelligent";
|
||||
doc["wWComfort"] = "Intelligent";
|
||||
}
|
||||
|
||||
if (wWSelTemp_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["wWSelTemp"] = wWSelTemp_;
|
||||
doc["wWSelTemp"] = wWSelTemp_;
|
||||
}
|
||||
if (wWDisinfectTemp_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["wWDisinfectionTemp"] = wWDisinfectTemp_;
|
||||
doc["wWDisinfectionTemp"] = wWDisinfectTemp_;
|
||||
}
|
||||
if (selFlowTemp_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["selFlowTemp"] = selFlowTemp_;
|
||||
doc["selFlowTemp"] = selFlowTemp_;
|
||||
}
|
||||
if (selBurnPow_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["selBurnPow"] = selBurnPow_;
|
||||
doc["selBurnPow"] = selBurnPow_;
|
||||
}
|
||||
if (curBurnPow_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["curBurnPow"] = curBurnPow_;
|
||||
doc["curBurnPow"] = curBurnPow_;
|
||||
}
|
||||
if (pumpMod_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["pumpMod"] = pumpMod_;
|
||||
doc["pumpMod"] = pumpMod_;
|
||||
}
|
||||
if (wWCircPump_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWCircPump"] = wWCircPump_;
|
||||
doc["wWCircPump"] = wWCircPump_;
|
||||
}
|
||||
if (wWCircPumpType_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWCiPuType"] = wWCircPumpType_;
|
||||
doc["wWCiPuType"] = wWCircPumpType_;
|
||||
}
|
||||
if (wWCircPumpMode_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["wWCiPuMode"] = wWCircPumpMode_;
|
||||
doc["wWCiPuMode"] = wWCircPumpMode_;
|
||||
}
|
||||
if (extTemp_ != EMS_VALUE_SHORT_NOTSET) {
|
||||
rootBoiler["outdoorTemp"] = (float)extTemp_ / 10;
|
||||
doc["outdoorTemp"] = (float)extTemp_ / 10;
|
||||
}
|
||||
if (wWCurTmp_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["wWCurTmp"] = (float)wWCurTmp_ / 10;
|
||||
doc["wWCurTmp"] = (float)wWCurTmp_ / 10;
|
||||
}
|
||||
if (wWCurFlow_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["wWCurFlow"] = (float)wWCurFlow_ / 10;
|
||||
doc["wWCurFlow"] = (float)wWCurFlow_ / 10;
|
||||
}
|
||||
if (curFlowTemp_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["curFlowTemp"] = (float)curFlowTemp_ / 10;
|
||||
doc["curFlowTemp"] = (float)curFlowTemp_ / 10;
|
||||
}
|
||||
if (retTemp_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["retTemp"] = (float)retTemp_ / 10;
|
||||
doc["retTemp"] = (float)retTemp_ / 10;
|
||||
}
|
||||
if (switchTemp_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["switchTemp"] = (float)switchTemp_ / 10;
|
||||
doc["switchTemp"] = (float)switchTemp_ / 10;
|
||||
}
|
||||
if (sysPress_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["sysPress"] = (float)sysPress_ / 10;
|
||||
doc["sysPress"] = (float)sysPress_ / 10;
|
||||
}
|
||||
if (boilTemp_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["boilTemp"] = (float)boilTemp_ / 10;
|
||||
doc["boilTemp"] = (float)boilTemp_ / 10;
|
||||
}
|
||||
if (wwStorageTemp1_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["wwStorageTemp1"] = (float)wwStorageTemp1_ / 10;
|
||||
doc["wwStorageTemp1"] = (float)wwStorageTemp1_ / 10;
|
||||
}
|
||||
if (wwStorageTemp2_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["wwStorageTemp2"] = (float)wwStorageTemp2_ / 10;
|
||||
doc["wwStorageTemp2"] = (float)wwStorageTemp2_ / 10;
|
||||
}
|
||||
if (exhaustTemp_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["exhaustTemp"] = (float)exhaustTemp_ / 10;
|
||||
doc["exhaustTemp"] = (float)exhaustTemp_ / 10;
|
||||
}
|
||||
if (wWActivated_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWActivated"] = Helpers::render_value(s, wWActivated_, EMS_VALUE_BOOL);
|
||||
doc["wWActivated"] = Helpers::render_value(s, wWActivated_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (wWOneTime_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWOnetime"] = Helpers::render_value(s, wWOneTime_, EMS_VALUE_BOOL);
|
||||
doc["wWOnetime"] = Helpers::render_value(s, wWOneTime_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (wWDesinfecting_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWDesinfecting"] = Helpers::render_value(s, wWDesinfecting_, EMS_VALUE_BOOL);
|
||||
doc["wWDesinfecting"] = Helpers::render_value(s, wWDesinfecting_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (wWReadiness_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWReady"] = Helpers::render_value(s, wWReadiness_, EMS_VALUE_BOOL);
|
||||
doc["wWReady"] = Helpers::render_value(s, wWReadiness_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (wWRecharging_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWRecharge"] = Helpers::render_value(s, wWRecharging_, EMS_VALUE_BOOL);
|
||||
doc["wWRecharge"] = Helpers::render_value(s, wWRecharging_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (wWTemperatureOK_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWTempOK"] = Helpers::render_value(s, wWTemperatureOK_, EMS_VALUE_BOOL);
|
||||
doc["wWTempOK"] = Helpers::render_value(s, wWTemperatureOK_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (wWCirc_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
|
||||
doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (burnGas_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["burnGas"] = Helpers::render_value(s, burnGas_, EMS_VALUE_BOOL);
|
||||
doc["burnGas"] = Helpers::render_value(s, burnGas_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (flameCurr_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["flameCurr"] = (float)(int16_t)flameCurr_ / 10;
|
||||
doc["flameCurr"] = (float)(int16_t)flameCurr_ / 10;
|
||||
}
|
||||
if (heatPmp_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["heatPmp"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL);
|
||||
doc["heatPmp"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (fanWork_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["fanWork"] = Helpers::render_value(s, fanWork_, EMS_VALUE_BOOL);
|
||||
doc["fanWork"] = Helpers::render_value(s, fanWork_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (ignWork_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["ignWork"] = Helpers::render_value(s, ignWork_, EMS_VALUE_BOOL);
|
||||
doc["ignWork"] = Helpers::render_value(s, ignWork_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (wWHeat_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
rootBoiler["wWHeat"] = Helpers::render_value(s, wWHeat_, EMS_VALUE_BOOL);
|
||||
doc["wWHeat"] = Helpers::render_value(s, wWHeat_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (heating_temp_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["heating_temp"] = heating_temp_;
|
||||
doc["heating_temp"] = heating_temp_;
|
||||
}
|
||||
if (pump_mod_max_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["pump_mod_max"] = pump_mod_max_;
|
||||
doc["pump_mod_max"] = pump_mod_max_;
|
||||
}
|
||||
if (pump_mod_min_ != EMS_VALUE_UINT_NOTSET) {
|
||||
rootBoiler["pump_mod_min"] = pump_mod_min_;
|
||||
doc["pump_mod_min"] = pump_mod_min_;
|
||||
}
|
||||
if (wWStarts_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
rootBoiler["wWStarts"] = wWStarts_;
|
||||
doc["wWStarts"] = wWStarts_;
|
||||
}
|
||||
if (wWWorkM_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
rootBoiler["wWWorkM"] = wWWorkM_;
|
||||
doc["wWWorkM"] = wWWorkM_;
|
||||
}
|
||||
if (UBAuptime_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
rootBoiler["UBAuptime"] = UBAuptime_;
|
||||
doc["UBAuptime"] = UBAuptime_;
|
||||
}
|
||||
if (burnStarts_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
rootBoiler["burnStarts"] = burnStarts_;
|
||||
doc["burnStarts"] = burnStarts_;
|
||||
}
|
||||
if (burnWorkMin_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
rootBoiler["burnWorkMin"] = burnWorkMin_;
|
||||
doc["burnWorkMin"] = burnWorkMin_;
|
||||
}
|
||||
if (heatWorkMin_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
rootBoiler["heatWorkMin"] = heatWorkMin_;
|
||||
doc["heatWorkMin"] = heatWorkMin_;
|
||||
}
|
||||
|
||||
if (serviceCode_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootBoiler["ServiceCode"] = serviceCodeChar_;
|
||||
rootBoiler["ServiceCodeNumber"] = serviceCode_;
|
||||
doc["serviceCode"] = serviceCodeChar_;
|
||||
doc["serviceCodeNumber"] = serviceCode_;
|
||||
}
|
||||
|
||||
// heatpump specific
|
||||
if (hpModulation_ != EMS_VALUE_UINT_NOTSET) {
|
||||
doc["pumpmodulation"] = hpModulation_;
|
||||
}
|
||||
if (hpSpeed_ != EMS_VALUE_UINT_NOTSET) {
|
||||
doc["pumpspeed"] = hpSpeed_;
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
@@ -410,6 +421,14 @@ void Boiler::show_values(uuid::console::Shell & shell) {
|
||||
shell.printfln(F(" Total UBA working time: %d days %d hours %d minutes"), UBAuptime_ / 1440, (UBAuptime_ % 1440) / 60, UBAuptime_ % 60);
|
||||
}
|
||||
|
||||
if (hpModulation_ != EMS_VALUE_UINT_NOTSET) {
|
||||
print_value(shell, F("Heat Pump modulation"), F_(percent), Helpers::render_value(buffer, hpModulation_, 1));
|
||||
}
|
||||
|
||||
if (hpSpeed_ != EMS_VALUE_UINT_NOTSET) {
|
||||
print_value(shell, F("Heat Pump speed"), F_(percent), Helpers::render_value(buffer, hpSpeed_, 1));
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
@@ -583,6 +602,39 @@ void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr<const Telegram> telegram
|
||||
telegram->read_value(pumpMod_, 25); // or is it switchTemp ?
|
||||
}
|
||||
|
||||
/*
|
||||
* Type 0xE3 - HeatPump Monitor 1
|
||||
*/
|
||||
void Boiler::process_HPMonitor1(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(hpModulation_, 13);
|
||||
}
|
||||
|
||||
/*
|
||||
* Type 0xE5 - HeatPump Monitor 2
|
||||
*/
|
||||
void Boiler::process_HPMonitor2(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(hpSpeed_, 25);
|
||||
}
|
||||
|
||||
// 0xE9 - DHW Status
|
||||
// e.g. 08 00 E9 00 37 01 F6 01 ED 00 00 00 00 41 3C 00 00 00 00 00 00 00 00 00 00 00 00 37 00 00 00 (CRC=77) #data=27
|
||||
void Boiler::process_UBADHWStatus(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(wWSetTmp_, 0);
|
||||
telegram->read_value(wWCurTmp_, 1);
|
||||
telegram->read_value(wWCurTmp2_, 3);
|
||||
telegram->read_value(wWWorkM_, 17);
|
||||
telegram->read_value(wWStarts_, 14);
|
||||
telegram->read_value(wWOneTime_, 12, 2);
|
||||
telegram->read_value(wWDesinfecting_, 12, 3);
|
||||
telegram->read_value(wWReadiness_, 12, 4);
|
||||
telegram->read_value(wWRecharging_, 13, 4);
|
||||
telegram->read_value(wWTemperatureOK_, 13, 5);
|
||||
telegram->read_value(wWActivated_, 20);
|
||||
telegram->read_value(wWCircPump_, 13, 2);
|
||||
telegram->read_value(wWSelTemp_, 10);
|
||||
telegram->read_value(wWDisinfectTemp_, 9);
|
||||
}
|
||||
|
||||
/**
|
||||
* UBAOutdoorTemp - type 0xD1 - external temperature EMS+
|
||||
*/
|
||||
@@ -593,11 +645,6 @@ void Boiler::process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram) {
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
// 0xE9 - DHW Status
|
||||
void Boiler::process_UBADHWStatus(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
|
||||
|
||||
// UBASetPoint 0x1A
|
||||
// not yet implemented
|
||||
void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
18
src/boiler.h
18
src/boiler.h
@@ -32,9 +32,6 @@
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
MAKE_PSTR(logger_name2, "boiler")
|
||||
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Boiler : public EMSdevice {
|
||||
@@ -53,10 +50,10 @@ class Boiler : public EMSdevice {
|
||||
|
||||
uint8_t last_boilerState = 0xFF; // remember last state of heating and warm water on/off
|
||||
|
||||
static constexpr uint8_t EMS_TYPE_UBAParameterWW = 0x33;
|
||||
static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D;
|
||||
static constexpr uint8_t EMS_TYPE_UBAFlags = 0x35;
|
||||
static constexpr uint8_t EMS_TYPE_UBASetPoints = 0x1A;
|
||||
static constexpr uint8_t EMS_TYPE_UBAParameterWW = 0x33;
|
||||
static constexpr uint8_t EMS_TYPE_UBAFunctionTest = 0x1D;
|
||||
static constexpr uint8_t EMS_TYPE_UBAFlags = 0x35;
|
||||
static constexpr uint8_t EMS_TYPE_UBASetPoints = 0x1A;
|
||||
|
||||
static constexpr uint8_t EMS_BOILER_SELFLOWTEMP_HEATING = 20; // was originally 70, changed to 30 for issue #193, then to 20 with issue #344
|
||||
|
||||
@@ -122,6 +119,10 @@ class Boiler : public EMSdevice {
|
||||
uint8_t tap_water_active_ = EMS_VALUE_BOOL_NOTSET; // Hot tap water is on/off
|
||||
uint8_t heating_active_ = EMS_VALUE_BOOL_NOTSET; // Central heating is on/off
|
||||
|
||||
// heatpump boilers
|
||||
uint8_t hpModulation_ = EMS_VALUE_UINT_NOTSET; // heatpump modulation in %
|
||||
uint8_t hpSpeed_ = EMS_VALUE_UINT_NOTSET; // speed 0-100 %
|
||||
|
||||
void process_UBAParameterWW(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAMonitorFast(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBATotalUptime(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -136,7 +137,10 @@ class Boiler : public EMSdevice {
|
||||
void process_MC10Status(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAMaintenanceStatus(std::shared_ptr<const Telegram> telegram);
|
||||
void process_UBAMaintenanceSettings(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void process_UBADHWStatus(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPMonitor1(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPMonitor2(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void check_active();
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ void EMSESPShell::stopped() {
|
||||
logger().log(LogLevel::INFO, LogFacility::AUTH, F("Admin session closed on console %s"), console_name().c_str());
|
||||
}
|
||||
logger().log(LogLevel::INFO, LogFacility::CONSOLE, F("User session closed on console %s"), console_name().c_str());
|
||||
|
||||
// remove all custom contexts
|
||||
commands->remove_all_commands();
|
||||
_console_commands_loaded = false; // make sure they got loaded next time a console is opened
|
||||
}
|
||||
|
||||
// show welcome banner
|
||||
@@ -87,7 +91,9 @@ void EMSESPShell::add_console_commands() {
|
||||
return;
|
||||
}
|
||||
|
||||
commands->remove_context_commands(ShellContext::MAIN); // just in case, remove everything
|
||||
// just in case, remove everything
|
||||
// commands->remove_context_commands(ShellContext::MAIN);
|
||||
commands->remove_all_commands();
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
@@ -96,7 +102,7 @@ void EMSESPShell::add_console_commands() {
|
||||
shell.printfln(F("Refreshing console and fetching device data"));
|
||||
_console_commands_loaded = false;
|
||||
add_console_commands();
|
||||
EMSESP::fetch_all_values();
|
||||
EMSESP::fetch_device_values();
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
@@ -119,7 +125,7 @@ void EMSESPShell::add_console_commands() {
|
||||
});
|
||||
|
||||
/*
|
||||
* add the submenus...
|
||||
* add the submenu contexts...
|
||||
*/
|
||||
|
||||
// MQTT
|
||||
@@ -150,7 +156,7 @@ void EMSESPShell::add_console_commands() {
|
||||
});
|
||||
|
||||
// add all the context menus for the connected devices
|
||||
// this assumes they have been loaded
|
||||
// this assumes they devices have been detected and registered
|
||||
EMSESP::add_context_menu();
|
||||
|
||||
enter_custom_context(ShellContext::MAIN); // add su, exit and help
|
||||
@@ -223,7 +229,7 @@ void EMSESPShell::enter_custom_context(unsigned int context) {
|
||||
uint16_t watch_id = 0; // no watch ID set
|
||||
if ((arguments.size() == 2) && (level == uuid::log::Level::TRACE)) {
|
||||
watch_id = Helpers::hextoint(arguments[1].c_str());
|
||||
shell.printfln(("Tracing only telegrams that match a device/telegram type ID of 0x%02X"), watch_id);
|
||||
shell.printfln(("Tracing only telegrams that match a device ID/telegram type ID of 0x%02X"), watch_id);
|
||||
}
|
||||
emsesp::EMSESP::trace_watch_id(watch_id);
|
||||
}
|
||||
@@ -405,10 +411,10 @@ void Console::start() {
|
||||
}
|
||||
|
||||
// always start the telnet service
|
||||
// default idle is 10 minutes, default write timeout is 0 (automatic)
|
||||
// note, this must be started after the network/wifi for ESP32 otherwise it'll crash
|
||||
#ifndef EMSESP_STANDALONE
|
||||
telnet_.start();
|
||||
// default idle is 10 minutes, default write timeout is 0 (automatic)
|
||||
// telnet_.default_write_timeout(1000); // in ms, socket timeout 1 second
|
||||
#endif
|
||||
}
|
||||
@@ -422,6 +428,8 @@ void Console::loop() {
|
||||
#endif
|
||||
|
||||
Shell::loop_all();
|
||||
|
||||
// delay(0); // in EMS-ESP 1.9.5 this helped with stability
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -52,7 +52,9 @@ using uuid::log::Level;
|
||||
// clang-format on
|
||||
|
||||
// common words
|
||||
#ifdef EMSESP_DEBUG
|
||||
MAKE_PSTR_WORD(test)
|
||||
#endif
|
||||
MAKE_PSTR_WORD(exit)
|
||||
MAKE_PSTR_WORD(help)
|
||||
MAKE_PSTR_WORD(settings)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
MAKE_PSTR_WORD(controller)
|
||||
// MAKE_PSTR_WORD(controller)
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
|
||||
@@ -38,27 +38,27 @@
|
||||
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Solar Modules - type 0x30
|
||||
// Solar Modules - 0x30
|
||||
{ 73, DeviceType::SOLAR, F("SM10"), DeviceFlags::EMS_DEVICE_FLAG_SM10},
|
||||
{163, DeviceType::SOLAR, F("SM100"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
{164, DeviceType::SOLAR, F("SM200"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
{101, DeviceType::SOLAR, F("ISM1"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
{162, DeviceType::SOLAR, F("SM50"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
|
||||
// Mixing Modules - type 0x20 or 0x21
|
||||
// Mixing Modules - 0x20 / 0x21
|
||||
{160, DeviceType::MIXING, F("MM100"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{161, DeviceType::MIXING, F("MM200"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{ 69, DeviceType::MIXING, F("MM10"), DeviceFlags::EMS_DEVICE_FLAG_MM10},
|
||||
{159, DeviceType::MIXING, F("MM50"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
|
||||
// Heat Pumps - type 0x38
|
||||
// Heat Pumps - 0x38
|
||||
{252, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{200, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Switches - type 0x11
|
||||
// Switches - 0x11
|
||||
{ 71, DeviceType::SWITCH, F("WM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x11
|
||||
|
||||
// Controllers - type 0x09 and 0x10
|
||||
// Controllers - 0x09 / 0x10
|
||||
{ 68, DeviceType::CONTROLLER, F("BC10/RFM20"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
|
||||
{190, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},// 0x09
|
||||
@@ -71,21 +71,20 @@
|
||||
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{207, DeviceType::CONTROLLER, F("Sense II/CS200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10
|
||||
|
||||
// Connect devices - type 0x02
|
||||
// Connect devices - 0x02
|
||||
{205, DeviceType::CONNECT, F("Moduline Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
|
||||
{206, DeviceType::CONNECT, F("Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
|
||||
{171, DeviceType::CONNECT, F("OpenTherm Converter"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
|
||||
|
||||
// Gateways - type 0x48 and sometimes 0x18?
|
||||
// Gateways - 0x48 / 0x18
|
||||
{189, DeviceType::GATEWAY, F("KM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x48
|
||||
{ 94, DeviceType::GATEWAY, F("RC"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
|
||||
|
||||
// Thermostat - not currently supporting write operations
|
||||
// Thermostat - not currently supporting write operations, like the Easy/100 types - 0x18
|
||||
{202, DeviceType::THERMOSTAT, F("Logamatic TC100/Moduline Easy"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
|
||||
{203, DeviceType::THERMOSTAT, F("EasyControl CT200"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
|
||||
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
|
||||
|
||||
// Thermostat - Buderus/Nefit specific
|
||||
// Thermostat - Common for Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18
|
||||
{ 79, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC10},// 0x17
|
||||
{ 80, DeviceType::THERMOSTAT, F("Moduline 200"), DeviceFlags::EMS_DEVICE_FLAG_RC10}, // 0x17
|
||||
{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
|
||||
@@ -93,14 +92,15 @@
|
||||
{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
|
||||
{ 86, DeviceType::THERMOSTAT, F("RC35"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
|
||||
{ 93, DeviceType::THERMOSTAT, F("RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
|
||||
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
|
||||
{158, DeviceType::THERMOSTAT, F("RC300/RC310/Moduline 3000/CW400/Sense II"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||
{165, DeviceType::THERMOSTAT, F("RC100/Moduline 1000/1010"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
|
||||
|
||||
// Thermostat - Sieger
|
||||
// Thermostat - Sieger - 0x10 / 0x17
|
||||
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
|
||||
{113, DeviceType::THERMOSTAT, F("ES72"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17
|
||||
|
||||
// Thermostat - Junkers - all 0x10
|
||||
// Thermostat - Junkers - 0x10
|
||||
{105, DeviceType::THERMOSTAT, F("FW100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{106, DeviceType::THERMOSTAT, F("FW200"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{107, DeviceType::THERMOSTAT, F("FR100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_2}, // older model
|
||||
|
||||
@@ -23,7 +23,6 @@ MAKE_PSTR_WORD(tx_mode)
|
||||
MAKE_PSTR_WORD(read_only)
|
||||
MAKE_PSTR_WORD(emsbus)
|
||||
MAKE_PSTR_WORD(devices)
|
||||
MAKE_PSTR_WORD(deep)
|
||||
MAKE_PSTR_WORD(send)
|
||||
MAKE_PSTR_WORD(telegram)
|
||||
|
||||
@@ -56,9 +55,9 @@ Network EMSESP::network_; // WiFi
|
||||
Shower EMSESP::shower_; // Shower logic
|
||||
|
||||
// static/common variables
|
||||
uint8_t EMSESP::actual_master_thermostat_ = EMSESP_DEFAULT_NOTSET; // which thermostat leads when multiple found
|
||||
uint16_t EMSESP::trace_watch_id_ = 0; // for when log is TRACE
|
||||
bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
|
||||
uint8_t EMSESP::actual_master_thermostat_ = EMSESP_DEFAULT_MASTER_THERMOSTAT; // which thermostat leads when multiple found
|
||||
uint16_t EMSESP::trace_watch_id_ = 0; // for when log is TRACE
|
||||
bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
|
||||
bool EMSESP::ems_read_only_;
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
@@ -66,19 +65,16 @@ bool EMSESP::ems_read_only_;
|
||||
#endif
|
||||
|
||||
// for each associated EMS device go and request data values
|
||||
void EMSESP::fetch_all_values() {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
emsdevice->fetch_values();
|
||||
}
|
||||
}
|
||||
void EMSESP::fetch_device_values() {
|
||||
fetch_device_values(0); // fetch all
|
||||
}
|
||||
|
||||
// for a specific EMS device go and request data values
|
||||
// or if device_id is 0 it will fetch from all known devices
|
||||
void EMSESP::fetch_device_values(const uint8_t device_id) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->is_device_id(device_id)) {
|
||||
if ((device_id == 0) || emsdevice->is_device_id(device_id)) {
|
||||
emsdevice->fetch_values();
|
||||
return;
|
||||
}
|
||||
@@ -478,7 +474,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
|
||||
shell.println();
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
shell.printf(F("%s%s%s"), COLOR_BOLD_ON, emsdevice->to_string().c_str(), COLOR_BOLD_OFF);
|
||||
shell.printf(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
|
||||
if ((emsdevice->device_type() == EMSdevice::DeviceType::THERMOSTAT) && (emsdevice->device_id() == actual_master_thermostat())) {
|
||||
shell.printf(F(" ** master device **"));
|
||||
}
|
||||
@@ -584,7 +580,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
txservice_.send_poll(); // close the bus
|
||||
} else {
|
||||
#ifdef EMSESP_DEBUG
|
||||
logger_.err(F("Waiting for Tx ACK (1 or 4) but got 0x%02X"), first_value);
|
||||
logger_.err(F("Expecting Tx ACK (1/4) but got 0x%02X. Tx:%s"), first_value, txservice_.last_tx_to_string().c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -611,7 +607,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
}
|
||||
|
||||
// check for poll, if so, send Tx from the queue immediately
|
||||
// if ht3 poll must be ems_bus_id else if buderus poll must be (ems_bus_id | 0x80)
|
||||
// if ht3 poll must be ems_bus_id else if Buderus poll must be (ems_bus_id | 0x80)
|
||||
if ((length == 1) && ((first_value ^ 0x80 ^ rxservice_.ems_mask()) == txservice_.ems_bus_id())) {
|
||||
EMSbus::last_bus_activity(millis()); // set the flag indication the EMS bus is active
|
||||
txservice_.send();
|
||||
@@ -729,7 +725,9 @@ void EMSESP::console_commands() {
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(send), F_(telegram)},
|
||||
flash_string_vector{F_(data_mandatory)},
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) { EMSESP::send_raw_telegram(arguments.front().c_str()); });
|
||||
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
EMSESP::send_raw_telegram(arguments.front().c_str());
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::EMS,
|
||||
CommandFlags::USER,
|
||||
@@ -784,7 +782,6 @@ void EMSESP::start() {
|
||||
system_.start();
|
||||
network_.start();
|
||||
console_.start();
|
||||
mqtt_.start();
|
||||
sensors_.start();
|
||||
rxservice_.start();
|
||||
txservice_.start();
|
||||
|
||||
@@ -54,7 +54,6 @@ class EMSESP {
|
||||
static void start();
|
||||
static void loop();
|
||||
|
||||
static void fetch_all_values();
|
||||
static void publish_all_values();
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
@@ -114,13 +113,14 @@ class EMSESP {
|
||||
|
||||
static void console_commands();
|
||||
|
||||
static void fetch_device_values(const uint8_t device_id);
|
||||
static void fetch_device_values();
|
||||
|
||||
private:
|
||||
EMSESP() = delete;
|
||||
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static void fetch_device_values(const uint8_t device_id);
|
||||
static bool add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand);
|
||||
static std::string device_tostring(const uint8_t device_id);
|
||||
|
||||
|
||||
@@ -18,7 +18,14 @@
|
||||
|
||||
#include "heatpump.h"
|
||||
|
||||
MAKE_PSTR_WORD(heatpump)
|
||||
// MAKE_PSTR_WORD(heatpump)
|
||||
|
||||
/*
|
||||
example telegrams 0x32B, 0x37B
|
||||
"38 10 FF 00 03 7B 08 24 00 4B",
|
||||
"38 10 FF 00 03 2B 00 C7 07 C3 01",
|
||||
"38 10 FF 00 03 2B 00 D1 08 2A 01",
|
||||
*/
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -28,20 +35,25 @@ uuid::log::Logger Heatpump::logger_{F_(logger_name), uuid::log::Facility::CONSOL
|
||||
|
||||
Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
DEBUG_LOG(F("Registering new Heat Pump module with device ID 0x%02X"), device_id);
|
||||
|
||||
// telegram handlers
|
||||
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Heatpump::process_XX, this, _1));
|
||||
register_telegram_type(0x047B, F("HP1"), true, std::bind(&Heatpump::process_HPMonitor1, this, _1));
|
||||
register_telegram_type(0x042B, F("HP2"), true, std::bind(&Heatpump::process_HPMonitor2, this, _1));
|
||||
|
||||
// MQTT callbacks
|
||||
// register_mqtt_topic("cmd", std::bind(&Heatpump::cmd, this, _1));
|
||||
|
||||
}
|
||||
|
||||
// context submenu
|
||||
void Heatpump::add_context_menu() {
|
||||
}
|
||||
|
||||
// display all values into the shell console
|
||||
void Heatpump::show_values(uuid::console::Shell & shell) {
|
||||
EMSdevice::show_values(shell); // always call this to show header
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// publish values via MQTT
|
||||
@@ -57,4 +69,25 @@ bool Heatpump::updated_values() {
|
||||
void Heatpump::console_commands() {
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
/*
|
||||
* Type 0x42B- HeatPump Monitor 1
|
||||
* e.g. "38 10 FF 00 03 2B 00 D1 08 2A 01"
|
||||
*/
|
||||
void Heatpump::process_HPMonitor1(std::shared_ptr<const Telegram> telegram) {
|
||||
// still to implement
|
||||
}
|
||||
|
||||
/*
|
||||
* Type 0x47B - HeatPump Monitor 2
|
||||
* e.g. "38 10 FF 00 03 7B 08 24 00 4B"
|
||||
*/
|
||||
void Heatpump::process_HPMonitor2(std::shared_ptr<const Telegram> telegram) {
|
||||
// still to implement
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -45,6 +45,9 @@ class Heatpump : public EMSdevice {
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void console_commands();
|
||||
|
||||
void process_HPMonitor1(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPMonitor2(std::shared_ptr<const Telegram> telegram);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "mixing.h"
|
||||
|
||||
MAKE_PSTR_WORD(mixing)
|
||||
// MAKE_PSTR_WORD(mixing)
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -28,24 +28,74 @@ uuid::log::Logger Mixing::logger_{F_(logger_name), uuid::log::Facility::CONSOLE}
|
||||
|
||||
Mixing::Mixing(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
// telegram handlers
|
||||
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Mixing::process_XX, this, _1));
|
||||
DEBUG_LOG(F("Registering new Mixing module with device ID 0x%02X"), device_id);
|
||||
|
||||
// telegram handlers 0x20 - 0x27 for HC
|
||||
register_telegram_type(0x02D7, F("MMPLUSStatusMessage_HC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_HC, this, _1));
|
||||
register_telegram_type(0x02D8, F("MMPLUSStatusMessage_HC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_HC, this, _1));
|
||||
register_telegram_type(0x02D9, F("MMPLUSStatusMessage_HC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_HC, this, _1));
|
||||
register_telegram_type(0x02DA, F("MMPLUSStatusMessage_HC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_HC, this, _1));
|
||||
|
||||
// telegram handlers for warm water/DHW 0x28, 0x29
|
||||
register_telegram_type(0x0331, F("MMPLUSStatusMessage_WWC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_WWC, this, _1));
|
||||
register_telegram_type(0x0332, F("MMPLUSStatusMessage_WWC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_WWC, this, _1));
|
||||
|
||||
// EMS 1.0
|
||||
register_telegram_type(0x00AB, F("MMStatusMessage"), true, std::bind(&Mixing::process_MMStatusMessage, this, _1));
|
||||
|
||||
// MQTT callbacks
|
||||
// register_mqtt_topic("cmd", std::bind(&Mixing::cmd, this, _1));
|
||||
|
||||
}
|
||||
|
||||
// add context submenu
|
||||
void Mixing::add_context_menu() {
|
||||
}
|
||||
|
||||
// display all values into the shell console
|
||||
void Mixing::show_values(uuid::console::Shell & shell) {
|
||||
EMSdevice::show_values(shell); // always call this to show header
|
||||
|
||||
char buffer[10]; // used for formatting
|
||||
|
||||
shell.printfln(F(" Circuit #: %d"), hc_);
|
||||
print_value(shell, F("Current flow temperature"), F_(degrees), Helpers::render_value(buffer, flowTemp_, 10));
|
||||
print_value(shell, F("Setpoint flow temperature"), F_(degrees), Helpers::render_value(buffer, flowSetTemp_, 1));
|
||||
print_value(shell, F("Current pump modulation"), Helpers::render_value(buffer, pumpMod_, 1));
|
||||
print_value(shell, F("Current valve status"), Helpers::render_value(buffer, valveStatus_, 1));
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// publish values via MQTT
|
||||
// ideally we should group up all the mixing units together into a nested JSON but for now we'll send them individually
|
||||
void Mixing::publish_values() {
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_SMALL);
|
||||
|
||||
if (flowTemp_ != EMS_VALUE_USHORT_NOTSET) {
|
||||
doc["flowTemp"] = (float)flowTemp_ / 10;
|
||||
}
|
||||
|
||||
if (pumpMod_ != EMS_VALUE_UINT_NOTSET) {
|
||||
doc["pumpMod"] = pumpMod_;
|
||||
}
|
||||
|
||||
if (valveStatus_ != EMS_VALUE_UINT_NOTSET) {
|
||||
doc["valveStatus_"] = valveStatus_;
|
||||
}
|
||||
|
||||
if (flowSetTemp_ != EMS_VALUE_UINT_NOTSET) {
|
||||
doc["flowSetTemp_"] = flowSetTemp_;
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
DEBUG_LOG(F("[DEBUG] Performing a mixing module publish"));
|
||||
#endif
|
||||
|
||||
char topic[30];
|
||||
char s[3]; // for formatting strings
|
||||
strlcpy(topic, "mixing_data", 30);
|
||||
strlcat(topic, Helpers::itoa(s, hc_), 30); // append hc to topic
|
||||
Mqtt::publish(topic, doc);
|
||||
}
|
||||
|
||||
// check to see if values have been updated
|
||||
@@ -57,4 +107,27 @@ bool Mixing::updated_values() {
|
||||
void Mixing::console_commands() {
|
||||
}
|
||||
|
||||
// 0x02D7, 0x02D8 etc...
|
||||
void Mixing::process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram) {
|
||||
hc_ = 0x02D8 - telegram->type_id; // determine which circuit this is
|
||||
telegram->read_value(flowTemp_, 3); // isd * 10
|
||||
telegram->read_value(pumpMod_, 5);
|
||||
telegram->read_value(valveStatus_, 2);
|
||||
}
|
||||
|
||||
// Mixing module warm water loading - 0x0331, 0x0332
|
||||
void Mixing::process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram) {
|
||||
hc_ = 0x0332 - telegram->type_id; // determine which circuit this is. There are max 2.
|
||||
telegram->read_value(flowTemp_, 0); // isd * 10
|
||||
telegram->read_value(pumpMod_, 2);
|
||||
telegram->read_value(valveStatus_, 11);
|
||||
}
|
||||
|
||||
void Mixing::process_MMStatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
hc_ = 1; // fixed
|
||||
telegram->read_value(flowTemp_, 1); // isd * 10
|
||||
telegram->read_value(pumpMod_, 3);
|
||||
telegram->read_value(valveStatus_, 0);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
11
src/mixing.h
11
src/mixing.h
@@ -45,6 +45,17 @@ class Mixing : public EMSdevice {
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void console_commands();
|
||||
|
||||
void process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MMStatusMessage(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
private:
|
||||
uint16_t hc_ = EMS_VALUE_USHORT_NOTSET;
|
||||
uint16_t flowTemp_ = EMS_VALUE_USHORT_NOTSET;
|
||||
uint8_t pumpMod_ = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t valveStatus_ = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t flowSetTemp_ = EMS_VALUE_UINT_NOTSET;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
105
src/mqtt.cpp
105
src/mqtt.cpp
@@ -47,11 +47,9 @@ MAKE_PSTR(logger_name, "mqtt")
|
||||
namespace emsesp {
|
||||
|
||||
// exposing static stuff to compiler/linker
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
AsyncMqttClient Mqtt::mqttClient_;
|
||||
#endif
|
||||
|
||||
std::vector<Mqtt::MQTTFunction> Mqtt::mqtt_functions_;
|
||||
bool Mqtt::mqtt_retain_;
|
||||
uint8_t Mqtt::mqtt_qos_;
|
||||
@@ -59,6 +57,7 @@ std::string Mqtt::mqtt_hostname_; // copy of hostname
|
||||
std::string Mqtt::mqtt_base_;
|
||||
uint16_t Mqtt::mqtt_publish_fails_ = 0;
|
||||
size_t Mqtt::maximum_mqtt_messages_ = Mqtt::MAX_MQTT_MESSAGES;
|
||||
bool Mqtt::force_publish_ = false;
|
||||
uint16_t Mqtt::mqtt_message_id_ = 0;
|
||||
std::deque<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
|
||||
char will_topic_[Mqtt::MQTT_TOPIC_MAX_SIZE]; // because MQTT library keeps only char pointer
|
||||
@@ -87,20 +86,22 @@ void Mqtt::flush_message_queue() {
|
||||
}
|
||||
|
||||
// restart MQTT services
|
||||
void Mqtt ::reconnect() {
|
||||
void Mqtt::reconnect() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
mqttClient_.disconnect();
|
||||
#endif
|
||||
|
||||
DEBUG_LOG(F("Reconnecting..."));
|
||||
|
||||
// flush_message_queue(); // clean the queues
|
||||
// start();
|
||||
}
|
||||
|
||||
// MQTT setup
|
||||
void Mqtt::start() {
|
||||
// get some settings and store them locally. This is also because the asyncmqtt library uses references
|
||||
// exit if already initialized
|
||||
if (mqtt_start_) {
|
||||
return;
|
||||
}
|
||||
mqtt_start_ = true;
|
||||
|
||||
// get some settings and store them locally. This is also because the asyncmqtt library uses references for char *
|
||||
Settings settings;
|
||||
mqtt_enabled_ = settings.mqtt_enabled();
|
||||
mqtt_hostname_ = settings.hostname();
|
||||
@@ -119,9 +120,7 @@ void Mqtt::start() {
|
||||
mqtt_enabled_ = false;
|
||||
}
|
||||
|
||||
if (!mqtt_init_) {
|
||||
init(); // set up call backs. only done once.
|
||||
}
|
||||
init(); // set up call backs. only done once.
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
mqtt_enabled_ = true; // force it on for debugging standalone
|
||||
@@ -141,17 +140,24 @@ void Mqtt::start() {
|
||||
mqtt_connecting_ = false;
|
||||
mqtt_last_connection_ = millis();
|
||||
mqtt_reconnect_delay_ = Mqtt::MQTT_RECONNECT_DELAY_MIN;
|
||||
|
||||
DEBUG_LOG(F("Configuring MQTT service..."));
|
||||
}
|
||||
|
||||
// MQTT init callbacks
|
||||
// This should only be executed once
|
||||
void Mqtt::init() {
|
||||
if (mqtt_init_) {
|
||||
return;
|
||||
}
|
||||
mqtt_init_ = true;
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
mqttClient_.onConnect([this](bool sessionPresent) { on_connect(); });
|
||||
|
||||
mqttClient_.onDisconnect([this](AsyncMqttClientDisconnectReason reason) {
|
||||
if (reason == AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) {
|
||||
logger_.err(F("Cannot connect to server"));
|
||||
logger_.err(F("Disconnected from server"));
|
||||
}
|
||||
if (reason == AsyncMqttClientDisconnectReason::MQTT_IDENTIFIER_REJECTED) {
|
||||
logger_.err(F("Server identifier Rejected"));
|
||||
@@ -169,6 +175,7 @@ void Mqtt::init() {
|
||||
// Reset reconnection delay
|
||||
mqtt_last_connection_ = millis();
|
||||
mqtt_connecting_ = false;
|
||||
mqtt_start_ = false; // will force a new start()
|
||||
});
|
||||
|
||||
// mqttClient.onSubscribe([this](uint16_t packetId, uint8_t qos) { DEBUG_LOG(F("Subscribe ACK for PID %d"), packetId); });
|
||||
@@ -179,8 +186,6 @@ void Mqtt::init() {
|
||||
on_message(topic, payload, len);
|
||||
});
|
||||
#endif
|
||||
|
||||
mqtt_init_ = true;
|
||||
}
|
||||
|
||||
Mqtt::MQTTFunction::MQTTFunction(uint8_t device_id, const std::string && topic, mqtt_function_p mqtt_function)
|
||||
@@ -192,8 +197,7 @@ Mqtt::MQTTFunction::MQTTFunction(uint8_t device_id, const std::string && topic,
|
||||
// subscribe to an MQTT topic, and store the associated callback function
|
||||
void Mqtt::subscribe(const uint8_t device_id, const std::string & topic, mqtt_function_p cb) {
|
||||
mqtt_functions_.emplace_back(device_id, std::move(topic), cb); // register a call back function for a specific telegram type
|
||||
|
||||
queue_subscribe_message(topic); // add subscription to queue
|
||||
queue_subscribe_message(topic); // add subscription to queue
|
||||
}
|
||||
|
||||
// subscribe to an MQTT topic, and store the associated callback function. For generic functions not tied to a device
|
||||
@@ -201,7 +205,9 @@ void Mqtt::subscribe(const std::string & topic, mqtt_function_p cb) {
|
||||
subscribe(0, topic, cb); // no device_id needed, if generic to EMS-ESP
|
||||
}
|
||||
|
||||
// Main loops. Checks for connection, sends out top item on publish queue
|
||||
// Main MQTT loop
|
||||
// Checks for connection, establishes a connection if not
|
||||
// sends out top item on publish queue
|
||||
void Mqtt::loop() {
|
||||
// exit if MQTT is not enabled, there is no WIFI or we're still in the MQTT connection process
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -214,6 +220,13 @@ void Mqtt::loop() {
|
||||
|
||||
// if we're already connected....
|
||||
if (connected()) {
|
||||
if (force_publish_) {
|
||||
force_publish_ = false;
|
||||
send_heartbeat(); // create a heartbeat payload
|
||||
EMSESP::publish_all_values(); // add sensors and mqtt to queue
|
||||
publish_all_queue(); // publish everything on queue
|
||||
}
|
||||
|
||||
// send out heartbeat
|
||||
uint32_t currentMillis = millis();
|
||||
if ((currentMillis - last_heartbeat_ > MQTT_HEARTBEAT_INTERVAL)) {
|
||||
@@ -227,7 +240,7 @@ void Mqtt::loop() {
|
||||
EMSESP::publish_all_values();
|
||||
}
|
||||
|
||||
// publish top item from MQTT queue - this happens every 200ms to stop flooding
|
||||
// publish top item from MQTT queue to stop flooding
|
||||
if ((uint32_t)(currentMillis - last_mqtt_poll_) > MQTT_PUBLISH_WAIT) {
|
||||
last_mqtt_poll_ = currentMillis;
|
||||
publish_queue();
|
||||
@@ -236,24 +249,23 @@ void Mqtt::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to reconnect. Check when was the last time we tried.
|
||||
if (millis() - mqtt_last_connection_ < mqtt_reconnect_delay_) {
|
||||
// We need to reconnect. Check when was the last time we tried this
|
||||
if (mqtt_last_connection_ && (millis() - mqtt_last_connection_ < mqtt_reconnect_delay_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mqtt_connecting_ = true; // we're doing a connection
|
||||
mqtt_connecting_ = true; // we're doing a connection now
|
||||
|
||||
// Increase the reconnect delay
|
||||
// Increase the reconnect delay for next time
|
||||
mqtt_reconnect_delay_ += MQTT_RECONNECT_DELAY_STEP;
|
||||
if (mqtt_reconnect_delay_ > MQTT_RECONNECT_DELAY_MAX) {
|
||||
mqtt_reconnect_delay_ = MQTT_RECONNECT_DELAY_MAX;
|
||||
}
|
||||
|
||||
// Connect to the MQTT broker
|
||||
logger_.info(F("Connecting to MQTT server..."));
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
mqttClient_.connect();
|
||||
start();
|
||||
logger_.info(F("Connecting to MQTT server..."));
|
||||
mqttClient_.connect(); // Connect to the MQTT broker
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -270,7 +282,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
return;
|
||||
}
|
||||
|
||||
shell.printfln(F("MQTT queue (%d items):"), mqtt_messages_.size());
|
||||
shell.printfln(F("MQTT queue (%d messages):"), mqtt_messages_.size());
|
||||
|
||||
for (const auto & message : mqtt_messages_) {
|
||||
auto content = message.content_;
|
||||
@@ -400,14 +412,11 @@ char * Mqtt::make_topic(char * result, const std::string & topic) {
|
||||
|
||||
// send online appended with the version information as JSON
|
||||
void Mqtt::send_start_topic() {
|
||||
Settings settings;
|
||||
|
||||
StaticJsonDocument<200> doc; // length is actually about 60
|
||||
JsonObject payload = doc.to<JsonObject>();
|
||||
payload["event"] = "start";
|
||||
payload["version"] = settings.app_version();
|
||||
StaticJsonDocument<90> doc;
|
||||
doc["event"] = "start";
|
||||
doc["version"] = Settings().app_version();
|
||||
#ifndef EMSESP_STANDALONE
|
||||
payload["IP"] = WiFi.localIP().toString();
|
||||
doc["IP"] = WiFi.localIP().toString();
|
||||
#endif
|
||||
|
||||
publish("info", doc, false); // send with retain off
|
||||
@@ -434,13 +443,12 @@ void Mqtt::send_heartbeat() {
|
||||
return;
|
||||
}
|
||||
|
||||
StaticJsonDocument<100> doc; // length is 76
|
||||
JsonObject rootHeartbeat = doc.to<JsonObject>();
|
||||
StaticJsonDocument<90> doc;
|
||||
|
||||
rootHeartbeat["rssid"] = Network::wifi_quality();
|
||||
rootHeartbeat["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
rootHeartbeat["freemem"] = System::free_mem();
|
||||
rootHeartbeat["mqttpublishfails"] = mqtt_publish_fails_;
|
||||
doc["rssid"] = Network::wifi_quality();
|
||||
doc["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
doc["freemem"] = System::free_mem();
|
||||
doc["mqttpublishfails"] = mqtt_publish_fails_;
|
||||
|
||||
publish("heartbeat", doc, false); // send to MQTT with retain off
|
||||
}
|
||||
@@ -529,6 +537,13 @@ void Mqtt::publish(const char * topic, const bool value) {
|
||||
queue_publish_message(topic, value ? "1" : "0", mqtt_retain_);
|
||||
}
|
||||
|
||||
// publish all queued messages to MQTT
|
||||
void Mqtt::publish_all_queue() {
|
||||
while (!mqtt_messages_.empty()) {
|
||||
publish_queue();
|
||||
}
|
||||
}
|
||||
|
||||
// take top from queue and try and publish it
|
||||
// assumes there is an MQTT connection
|
||||
void Mqtt::publish_queue() {
|
||||
@@ -576,12 +591,14 @@ void Mqtt::publish_queue() {
|
||||
if (packet_id == 0) {
|
||||
// it failed. if we retried n times, give up. remove from queue
|
||||
if (mqtt_message.retry_count_ == (MQTT_PUBLISH_MAX_RETRY - 1)) {
|
||||
DEBUG_LOG(F("Failed to publish to %s after %d attemps, payload %s"), full_topic, mqtt_message.retry_count_ + 1, message->payload.c_str());
|
||||
logger_.err(F("Failed to publish to %s after %d attempts"), full_topic, mqtt_message.retry_count_ + 1);
|
||||
mqtt_publish_fails_++; // increment failure counter
|
||||
mqtt_messages_.pop_front(); // delete
|
||||
return;
|
||||
} else {
|
||||
mqtt_messages_.front().retry_count_++;
|
||||
// logger_.err(F("Failed to publish to %s. Trying again, #%d"), full_topic, mqtt_message.retry_count_ + 1);
|
||||
DEBUG_LOG(F("Failed to publish to %s. Trying again, #%d"), full_topic, mqtt_message.retry_count_ + 1);
|
||||
return; // leave on queue for next time so it gets republished
|
||||
}
|
||||
}
|
||||
@@ -728,11 +745,11 @@ void Mqtt::console_commands() {
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
CommandFlags::ADMIN,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F_(publish)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) {
|
||||
shell.printfln(F("Publishing all values to MQTT"));
|
||||
EMSESP::publish_all_values();
|
||||
force_publish_ = true;
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::MQTT,
|
||||
|
||||
39
src/mqtt.h
39
src/mqtt.h
@@ -39,9 +39,9 @@
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
#define EMSESP_MAX_JSON_SIZE 800 // for json docs from ems devices
|
||||
#define EMSESP_MAX_JSON_SIZE_SMALL 200 // for smaller json docs from ems devices
|
||||
#define EMSESP_MAX_JSON_SIZE_MEDIUM 200 // for smaller json docs from ems devices
|
||||
#define EMSESP_MAX_JSON_SIZE_SMALL 200 // for smaller json docs
|
||||
#define EMSESP_MAX_JSON_SIZE_MEDIUM 800 // for smaller json docs from ems devices
|
||||
#define EMSESP_MAX_JSON_SIZE_LARGE 1500 // for large json docs from ems devices, like boiler or thermostat data
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -61,7 +61,6 @@ struct MqttMessage {
|
||||
|
||||
class Mqtt {
|
||||
public:
|
||||
void start();
|
||||
void loop();
|
||||
void send_heartbeat();
|
||||
|
||||
@@ -108,6 +107,7 @@ class Mqtt {
|
||||
};
|
||||
static std::deque<QueuedMqttMessage> mqtt_messages_;
|
||||
|
||||
void start();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static AsyncMqttClient mqttClient_;
|
||||
@@ -120,8 +120,8 @@ class Mqtt {
|
||||
static uint16_t mqtt_message_id_;
|
||||
|
||||
static constexpr uint8_t MQTT_QUEUE_MAX_SIZE = 50;
|
||||
static constexpr uint32_t MQTT_PUBLISH_WAIT = 200; // delay between sending publishes, although it should be asynchronous!
|
||||
static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 4; // max retries for giving up on publishing
|
||||
static constexpr uint32_t MQTT_PUBLISH_WAIT = 750; // delay between sending publishes, although it should be asynchronous!
|
||||
static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing
|
||||
static constexpr uint8_t MQTT_KEEP_ALIVE = 60;
|
||||
static constexpr uint32_t MQTT_RECONNECT_DELAY_MIN = 2000; // Try to reconnect in 2 seconds upon disconnection
|
||||
static constexpr uint32_t MQTT_RECONNECT_DELAY_STEP = 3000; // Increase the reconnect delay in 3 seconds after each failed attempt
|
||||
@@ -140,6 +140,7 @@ class Mqtt {
|
||||
void on_connect();
|
||||
static char * make_topic(char * result, const std::string & topic);
|
||||
void publish_queue();
|
||||
void publish_all_queue();
|
||||
void send_start_topic();
|
||||
static void reconnect();
|
||||
void init();
|
||||
@@ -157,29 +158,31 @@ class Mqtt {
|
||||
};
|
||||
static std::vector<MQTTFunction> mqtt_functions_; // list of mqtt callbacks for all devices
|
||||
|
||||
static std::string mqtt_hostname_;
|
||||
static std::string mqtt_base_;
|
||||
static uint8_t mqtt_qos_;
|
||||
|
||||
static uint16_t mqtt_publish_fails_;
|
||||
|
||||
uint32_t mqtt_last_connection_;
|
||||
uint32_t mqtt_last_connection_ = 0;
|
||||
uint32_t mqtt_reconnect_delay_ = MQTT_RECONNECT_DELAY_MIN;
|
||||
bool mqtt_init_ = false;
|
||||
bool mqtt_connecting_;
|
||||
bool mqtt_start_ = false;
|
||||
bool mqtt_connecting_ = false;
|
||||
uint16_t mqtt_publish_time_;
|
||||
|
||||
uint32_t last_heartbeat_ = 0;
|
||||
uint32_t last_mqtt_poll_ = 0;
|
||||
uint32_t last_publish_ = 0;
|
||||
|
||||
static bool force_publish_;
|
||||
|
||||
// settings
|
||||
std::string mqtt_ip_;
|
||||
std::string mqtt_user_;
|
||||
std::string mqtt_password_;
|
||||
bool mqtt_enabled_;
|
||||
bool mqtt_heartbeat_;
|
||||
uint16_t mqtt_port_;
|
||||
static std::string mqtt_hostname_;
|
||||
static std::string mqtt_base_;
|
||||
static uint8_t mqtt_qos_;
|
||||
std::string mqtt_ip_;
|
||||
std::string mqtt_user_;
|
||||
std::string mqtt_password_;
|
||||
bool mqtt_enabled_ = true; // start off assuming we want to connect
|
||||
bool mqtt_heartbeat_;
|
||||
uint16_t mqtt_port_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -219,25 +219,31 @@ void Sensors::publish_values() {
|
||||
|
||||
// if we're not using nested JSON, send each sensor out seperately
|
||||
// sensor1, sensor2 etc...
|
||||
// e.g. {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":"21.81"}}
|
||||
// e.g. sensor_1 = {"temp":20.2}
|
||||
if (!mqtt_nestedjson_) {
|
||||
StaticJsonDocument<100> doc; // length of payload is 60 bytes
|
||||
JsonObject sensors = doc.to<JsonObject>();
|
||||
StaticJsonDocument<20> doc;
|
||||
for (const auto & device : devices_) {
|
||||
char s[5];
|
||||
sensors["temp"] = Helpers::render_value(s, device.temperature_c_, 2);
|
||||
doc["temp"] = Helpers::render_value(s, device.temperature_c_, 2);
|
||||
char topic[60]; // sensors{1-n}
|
||||
strlcpy(topic, "sensor_", 50); // create topic
|
||||
strlcat(topic, device.to_string().c_str(), 50);
|
||||
Mqtt::publish(topic, doc);
|
||||
doc.clear(); // clear json doc so we can reuse the buffer again
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// group all sensors together - https://github.com/proddy/EMS-ESP/issues/327
|
||||
const size_t capacity = JSON_OBJECT_SIZE(num_devices * 60); // must recalculate if more objects addded https://arduinojson.org/v6/assistant/
|
||||
DynamicJsonDocument doc(capacity);
|
||||
JsonObject sensors = doc.to<JsonObject>();
|
||||
// https://arduinojson.org/v6/assistant/
|
||||
// sensors = {
|
||||
// "sensor1":{"id":"28-EA41-9497-0E03-5F","temp":"23.25"},
|
||||
// "sensor2":{"id":"28-EA41-9497-0E03-5F","temp":"23.25"},
|
||||
// "sensor3":{"id":"28-EA41-9497-0E03-5F","temp":"23.25"},
|
||||
// "sensor4":{"id":"28-EA41-9497-0E03-5F","temp":"23.25"}
|
||||
// }
|
||||
// const size_t capacity = num_devices * JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(num_devices);
|
||||
DynamicJsonDocument doc(100 * num_devices);
|
||||
|
||||
uint8_t i = 1;
|
||||
for (const auto & device : devices_) {
|
||||
@@ -245,7 +251,7 @@ void Sensors::publish_values() {
|
||||
strlcpy(sensorID, "sensor", 10);
|
||||
char s[5];
|
||||
strlcat(sensorID, Helpers::itoa(s, i++), 10);
|
||||
JsonObject dataSensor = sensors.createNestedObject(sensorID);
|
||||
JsonObject dataSensor = doc.createNestedObject(sensorID);
|
||||
dataSensor["id"] = device.to_string();
|
||||
dataSensor["temp"] = Helpers::render_value(s, device.temperature_c_, 2);
|
||||
}
|
||||
|
||||
@@ -34,13 +34,13 @@ namespace emsesp {
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", wifi_password, "", (), "") \
|
||||
EMSESP_SETTINGS_CUSTOM(std::string, "", syslog_host, "", (), "") \
|
||||
EMSESP_SETTINGS_ENUM(uuid::log::Level, "", syslog_level, "", (), uuid::log::Level::OFF) \
|
||||
EMSESP_SETTINGS_SIMPLE(unsigned long, "", syslog_mark_interval, "", (), 0) \
|
||||
EMSESP_SETTINGS_SIMPLE(unsigned long, "", syslog_mark_interval, "", (), EMSESP_DEFAULT_SYSLOG_INTERVAL) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint8_t, "", ems_bus_id, "", (), EMSESP_DEFAULT_BUS_ID) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint8_t, "", ems_tx_mode, "", (), EMSESP_DEFAULT_TX_MODE) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", ems_read_only, "", (), false) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", shower_timer, "", (), false) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", shower_alert, "", (), false) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint8_t, "", master_thermostat, "", (), EMSESP_DEFAULT_NOTSET) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", ems_read_only, "", (), EMSESP_DEFAULT_EMS_READ_ONLY) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", shower_timer, "", (), EMSESP_DEFAULT_SHOWER_TIMER) \
|
||||
EMSESP_SETTINGS_SIMPLE(bool, "", shower_alert, "", (), EMSESP_DEFAULT_SHOWER_ALERT) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint8_t, "", master_thermostat, "", (), EMSESP_DEFAULT_MASTER_THERMOSTAT) \
|
||||
EMSESP_SETTINGS_SIMPLE(uint16_t, "", mqtt_publish_time, "", (), EMSESP_DEFAULT_MQTT_PUBLISH_TIME) \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", mqtt_ip, "", (), "") \
|
||||
EMSESP_SETTINGS_SIMPLE(std::string, "", mqtt_user, "", (), "") \
|
||||
|
||||
@@ -41,13 +41,11 @@
|
||||
#include "version.h"
|
||||
#include "console.h"
|
||||
|
||||
// defaults
|
||||
#define EMSESP_DEFAULT_HOSTNAME "ems-esp2" // TODO rename to ems-esp
|
||||
// default settings - these can be customized from within the application
|
||||
#define EMSESP_DEFAULT_HOSTNAME "ems-esp"
|
||||
#define EMSESP_DEFAULT_ADMIN_PASSWORD "neo"
|
||||
#define EMSESP_DEFAULT_BUS_ID 0x0B
|
||||
#define EMSESP_DEFAULT_TX_MODE 1
|
||||
#define EMSESP_DEFAULT_NOTSET 0
|
||||
|
||||
#define EMSESP_DEFAULT_MQTT_ENABLED true
|
||||
#define EMSESP_DEFAULT_MQTT_BASE "home"
|
||||
#define EMSESP_DEFAULT_MQTT_PORT 1883
|
||||
@@ -55,6 +53,11 @@
|
||||
#define EMSESP_DEFAULT_MQTT_RETAIN false
|
||||
#define EMSESP_DEFAULT_MQTT_NESTEDJSON true
|
||||
#define EMSESP_DEFAULT_MQTT_HEARTBEAT true
|
||||
#define EMSESP_DEFAULT_EMS_READ_ONLY false
|
||||
#define EMSESP_DEFAULT_SHOWER_TIMER false
|
||||
#define EMSESP_DEFAULT_SHOWER_ALERT false
|
||||
#define EMSESP_DEFAULT_SYSLOG_INTERVAL 0
|
||||
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // not set
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#define EMSESP_DEFAULT_MQTT_PUBLISH_TIME 10
|
||||
|
||||
@@ -115,10 +115,9 @@ void Shower::shower_alert_start() {
|
||||
// Publish shower data
|
||||
// returns true if added to MQTT queue went ok
|
||||
void Shower::publish_values() {
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
JsonObject rootShower = doc.to<JsonObject>();
|
||||
rootShower["shower_timer"] = shower_timer_ ? "1" : "0";
|
||||
rootShower["shower_alert"] = shower_alert_ ? "1" : "0";
|
||||
StaticJsonDocument<90> doc;
|
||||
doc["shower_timer"] = shower_timer_ ? "1" : "0";
|
||||
doc["shower_alert"] = shower_alert_ ? "1" : "0";
|
||||
|
||||
// only publish shower duration if there is a value
|
||||
char s[50];
|
||||
@@ -126,9 +125,9 @@ void Shower::publish_values() {
|
||||
char buffer[16] = {0};
|
||||
strlcpy(s, Helpers::itoa(buffer, (uint8_t)((duration_ / (1000 * 60)) % 60), 10), 50);
|
||||
strlcat(s, " minutes and ", 50);
|
||||
strlcat(s, Helpers::itoa(buffer,(uint8_t)((duration_ / 1000) % 60), 10), 50);
|
||||
strlcat(s, Helpers::itoa(buffer, (uint8_t)((duration_ / 1000) % 60), 10), 50);
|
||||
strlcat(s, " seconds", 50);
|
||||
rootShower["duration"] = s;
|
||||
doc["duration"] = s;
|
||||
}
|
||||
|
||||
Mqtt::publish("shower_data", doc);
|
||||
|
||||
149
src/solar.cpp
149
src/solar.cpp
@@ -18,7 +18,9 @@
|
||||
|
||||
#include "solar.h"
|
||||
|
||||
MAKE_PSTR_WORD(solar)
|
||||
// MAKE_PSTR_WORD(solar)
|
||||
MAKE_PSTR(kwh, "kWh")
|
||||
MAKE_PSTR(wh, "Wh")
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -28,24 +30,91 @@ uuid::log::Logger Solar::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
|
||||
|
||||
Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
DEBUG_LOG(F("Registering new Solar module with device ID 0x%02X"), device_id);
|
||||
|
||||
// telegram handlers
|
||||
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Solar::process_XX, this, _1));
|
||||
register_telegram_type(0x0097, F("SM10Monitor"), true, std::bind(&Solar::process_SM10Monitor, this, _1));
|
||||
register_telegram_type(0x0362, F("SM100Monitor"), true, std::bind(&Solar::process_SM100Monitor, this, _1));
|
||||
register_telegram_type(0x0364, F("SM100Status"), false, std::bind(&Solar::process_SM100Status, this, _1));
|
||||
register_telegram_type(0x036A, F("SM100Status2"), false, std::bind(&Solar::process_SM100Status2, this, _1));
|
||||
register_telegram_type(0x038E, F("SM100Energy"), false, std::bind(&Solar::process_SM100Energy, this, _1));
|
||||
register_telegram_type(0x0003, F("ISM1StatusMessage"), true, std::bind(&Solar::process_ISM1StatusMessage, this, _1));
|
||||
register_telegram_type(0x0001, F("ISM1Set"), false, std::bind(&Solar::process_ISM1Set, this, _1));
|
||||
|
||||
// MQTT callbacks
|
||||
// register_mqtt_topic("cmd", std::bind(&Solar::cmd, this, _1));
|
||||
|
||||
}
|
||||
|
||||
// context submenu
|
||||
void Solar::add_context_menu() {
|
||||
}
|
||||
|
||||
// display all values into the shell console
|
||||
void Solar::show_values(uuid::console::Shell & shell) {
|
||||
EMSdevice::show_values(shell); // always call this to show header
|
||||
|
||||
char buffer[10]; // used for formatting
|
||||
|
||||
print_value(shell, F("Collector temperature (TS1)"), F_(degrees), Helpers::render_value(buffer, collectorTemp_, 10));
|
||||
print_value(shell, F("Bottom temperature (TS2)"), F_(degrees), Helpers::render_value(buffer, bottomTemp_, 10));
|
||||
print_value(shell, F("Bottom temperature (TS5)"), F_(degrees), Helpers::render_value(buffer, bottomTemp2_, 10));
|
||||
print_value(shell, F("Pump modulation"), F_(percent), Helpers::render_value(buffer, pumpModulation_, 1));
|
||||
print_value(shell, F("Valve (VS2) status"), Helpers::render_value(buffer, valveStatus_, EMS_VALUE_BOOL));
|
||||
print_value(shell, F("Pump (PS1) active"), Helpers::render_value(buffer, pump_, EMS_VALUE_BOOL));
|
||||
|
||||
if (pumpWorkMin_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
shell.printfln(F(" Pump working time: %d days %d hours %d minutes"), pumpWorkMin_ / 1440, (pumpWorkMin_ % 1440) / 60, pumpWorkMin_ % 60);
|
||||
}
|
||||
|
||||
print_value(shell, F("Energy last hour"), F_(wh), Helpers::render_value(buffer, energyLastHour_, 10));
|
||||
print_value(shell, F("Energy today"), F_(wh), Helpers::render_value(buffer, energyToday_, 0)); // no division
|
||||
print_value(shell, F("Energy total"), F_(kwh), Helpers::render_value(buffer, energyTotal_, 10));
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// publish values via MQTT
|
||||
void Solar::publish_values() {
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_MEDIUM);
|
||||
|
||||
char s[10]; // for formatting strings
|
||||
|
||||
if (collectorTemp_ != EMS_VALUE_SHORT_NOTSET) {
|
||||
doc["collectortemp"] = (float)collectorTemp_ / 10;
|
||||
}
|
||||
if (bottomTemp_ != EMS_VALUE_SHORT_NOTSET) {
|
||||
doc["bottomtemp"] = (float)bottomTemp_ / 10;
|
||||
}
|
||||
if (bottomTemp2_ != EMS_VALUE_SHORT_NOTSET) {
|
||||
doc["bottomtemp2"] = (float)bottomTemp2_ / 10;
|
||||
}
|
||||
if (pumpModulation_ != EMS_VALUE_INT_NOTSET) {
|
||||
doc["pumpmodulation"] = pumpModulation_;
|
||||
}
|
||||
if (pump_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
doc["pump"] = Helpers::render_value(s, pump_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (valveStatus_ != EMS_VALUE_BOOL_NOTSET) {
|
||||
doc["valvestatus"] = Helpers::render_value(s, valveStatus_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (pumpWorkMin_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
doc["pumpWorkMin"] = (float)pumpWorkMin_;
|
||||
}
|
||||
if (energyLastHour_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
doc["energylasthour"] = (float)energyLastHour_ / 10;
|
||||
}
|
||||
if (energyToday_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
doc["energytoday"] = energyToday_;
|
||||
}
|
||||
if (energyTotal_ != EMS_VALUE_ULONG_NOTSET) {
|
||||
doc["energytotal"] = (float)energyTotal_ / 10;
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
DEBUG_LOG(F("[DEBUG] Performing a solar module publish"));
|
||||
#endif
|
||||
|
||||
Mqtt::publish("sm_data", doc);
|
||||
}
|
||||
|
||||
// check to see if values have been updated
|
||||
@@ -57,4 +126,78 @@ bool Solar::updated_values() {
|
||||
void Solar::console_commands() {
|
||||
}
|
||||
|
||||
// SM10Monitor - type 0x97
|
||||
void Solar::process_SM10Monitor(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(collectorTemp_, 2); // collector temp from SM10, is *10
|
||||
telegram->read_value(bottomTemp_, 5); // bottom temp from SM10, is *10
|
||||
telegram->read_value(pumpModulation_, 4); // modulation solar pump
|
||||
telegram->read_value(pump_, 7, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* SM100Monitor - type 0x0162 EMS+ - for SM100 and SM200
|
||||
* e.g. B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
|
||||
* e.g, 30 00 FF 00 02 62 01 AC
|
||||
* 30 00 FF 18 02 62 80 00
|
||||
* 30 00 FF 00 02 62 01 A1 - for bottom temps
|
||||
* bytes 0+1 = TS1 Temperature sensor for collector
|
||||
* bytes 2+3 = TS2 Temperature sensor bottom cylinder 1
|
||||
* bytes 16+17 = TS5 Temperature sensor bottom cylinder 2
|
||||
*/
|
||||
void Solar::process_SM100Monitor(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(collectorTemp_, 0); // is *10
|
||||
telegram->read_value(bottomTemp_, 2); // is *10
|
||||
telegram->read_value(bottomTemp2_, 16); // is *10
|
||||
}
|
||||
|
||||
/*
|
||||
* SM100Status - type 0x0264 EMS+ for pump modulation - for SM100 and SM200
|
||||
* e.g. 30 00 FF 09 02 64 64 = 100%
|
||||
* 30 00 FF 09 02 64 1E = 30%
|
||||
*/
|
||||
void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(pumpModulation_, 9);
|
||||
}
|
||||
|
||||
/*
|
||||
* SM100Status2 - type 0x026A EMS+ for pump on/off at offset 0x0A - for SM100 and SM200
|
||||
* e.g. B0 00 FF 00 02 6A 03 03 03 03 01 03 03 03 03 03 01 03
|
||||
* byte 4 = VS2 3-way valve for cylinder 2 : test=01, on=04 and off=03
|
||||
* byte 10 = PS1 Solar circuit pump for collector array 1: test=01, on=04 and off=03
|
||||
*/
|
||||
void Solar::process_SM100Status2(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(valveStatus_, 4, 2); // on if bit 2 set
|
||||
telegram->read_value(pump_, 10, 2); // on if bit 2 set
|
||||
}
|
||||
|
||||
/*
|
||||
* SM100Energy - type 0x028E EMS+ for energy readings
|
||||
* e.g. 30 00 FF 00 02 8E 00 00 00 00 00 00 06 C5 00 00 76 35
|
||||
*/
|
||||
void Solar::process_SM100Energy(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(energyLastHour_, 0); // last hour / 10 in Wh
|
||||
telegram->read_value(energyToday_, 4); // todays in Wh
|
||||
telegram->read_value(energyTotal_, 8); // total / 10 in kWh
|
||||
}
|
||||
|
||||
/*
|
||||
* Junkers ISM1 Solar Module - type 0x0003 EMS+ for energy readings
|
||||
* e.g. B0 00 FF 00 00 03 32 00 00 00 00 13 00 D6 00 00 00 FB D0 F0
|
||||
*/
|
||||
void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(collectorTemp_, 4); // Collector Temperature
|
||||
telegram->read_value(bottomTemp_, 6); // Temperature Bottom of Solar Boiler
|
||||
telegram->read_value(energyLastHour_, 2); // Solar Energy produced in last hour - is * 10 and handled in ems-esp.cpp
|
||||
telegram->read_value(pump_, 8, 0); // Solar pump on (1) or off (0)
|
||||
telegram->read_value(pumpWorkMin_, 10);
|
||||
}
|
||||
|
||||
/*
|
||||
* Junkers ISM1 Solar Module - type 0x0001 EMS+ for setting values
|
||||
* e.g. 90 30 FF 06 00 01 50
|
||||
*/
|
||||
void Solar::process_ISM1Set(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(setpoint_maxBottomTemp_, 6);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
20
src/solar.h
20
src/solar.h
@@ -45,6 +45,26 @@ class Solar : public EMSdevice {
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void console_commands();
|
||||
|
||||
int16_t collectorTemp_ = EMS_VALUE_SHORT_NOTSET; // collector temp (TS1)
|
||||
int16_t bottomTemp_ = EMS_VALUE_SHORT_NOTSET; // bottom temp (TS2)
|
||||
int16_t bottomTemp2_ = EMS_VALUE_SHORT_NOTSET; // bottom temp cylinder 2 (TS5)
|
||||
uint8_t pumpModulation_ = EMS_VALUE_UINT_NOTSET; // modulation solar pump
|
||||
uint8_t pump_ = EMS_VALUE_BOOL_NOTSET; // pump active
|
||||
uint8_t valveStatus_ = EMS_VALUE_BOOL_NOTSET; // valve status (VS2)
|
||||
int16_t setpoint_maxBottomTemp_ = EMS_VALUE_SHORT_NOTSET; // setpoint for maximum collector temp
|
||||
uint32_t energyLastHour_ = EMS_VALUE_ULONG_NOTSET;
|
||||
uint32_t energyToday_ = EMS_VALUE_ULONG_NOTSET;
|
||||
uint32_t energyTotal_ = EMS_VALUE_ULONG_NOTSET;
|
||||
uint32_t pumpWorkMin_ = EMS_VALUE_ULONG_NOTSET; // Total solar pump operating time
|
||||
|
||||
void process_SM10Monitor(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Monitor(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Status(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Status2(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Energy(std::shared_ptr<const Telegram> telegram);
|
||||
void process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_ISM1Set(std::shared_ptr<const Telegram> telegram);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -58,7 +58,6 @@ int System::reset_counter_;
|
||||
|
||||
// handle generic system related MQTT commands
|
||||
void System::mqtt_commands(const char * message) {
|
||||
// convert JSON and get the command
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
DeserializationError error = deserializeJson(doc, message);
|
||||
if (error) {
|
||||
|
||||
@@ -602,4 +602,9 @@ void TxService::post_send_query() {
|
||||
}
|
||||
}
|
||||
|
||||
// returns details of the last Tx message that was sent (for debugging)
|
||||
std::string TxService::last_tx_to_string() const {
|
||||
return Helpers::data_to_hex(telegram_last_, telegram_last_length_);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -82,7 +82,6 @@ class Telegram {
|
||||
void read_value8(int16_t & param, const uint8_t index) const;
|
||||
void read_value(int8_t & param, const uint8_t index) const;
|
||||
|
||||
|
||||
private:
|
||||
int8_t _getDataPosition(const uint8_t index) const;
|
||||
};
|
||||
@@ -288,6 +287,9 @@ class TxService : public EMSbus {
|
||||
return tx_telegrams_;
|
||||
}
|
||||
|
||||
std::string last_tx_to_string() const;
|
||||
|
||||
|
||||
private:
|
||||
static constexpr uint8_t MAXIMUM_TX_RETRIES = 3;
|
||||
|
||||
|
||||
@@ -37,6 +37,28 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == "solar") {
|
||||
shell.printfln(F("Testing Solar"));
|
||||
|
||||
rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
|
||||
std::string version("1.2.3");
|
||||
add_device(0x30, 163, version, EMSdevice::Brand::BUDERUS); // SM100
|
||||
|
||||
rxservice_.loop();
|
||||
|
||||
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
|
||||
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
|
||||
uint8_t s1[] = {0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
|
||||
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x89};
|
||||
rxservice_.add(s1, sizeof(s1));
|
||||
rxservice_.loop();
|
||||
|
||||
shell.loop_all();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == "cr100") {
|
||||
shell.printfln(F("Testing CR100"));
|
||||
|
||||
@@ -259,7 +281,7 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
|
||||
strcpy(payload, "{\"cmd\":\"temp\",\"hc\":2,\"data\":22}");
|
||||
mqtt_.incoming(topic, payload);
|
||||
|
||||
strcpy(topic, "home/ems-esp2/cmd");
|
||||
strcpy(topic, "home/ems-esp/cmd");
|
||||
strcpy(payload, "restart");
|
||||
mqtt_.incoming(topic, payload);
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
|
||||
// if we're on auto mode, register this first one we find as we may find multiple
|
||||
// or if its the master thermostat we defined
|
||||
if (((num_devices == 1) && (actual_master_thermostat == EMSESP_DEFAULT_NOTSET)) || (master_thermostat == device_id)) {
|
||||
if (((num_devices == 1) && (actual_master_thermostat == EMSESP_DEFAULT_MASTER_THERMOSTAT)) || (master_thermostat == device_id)) {
|
||||
EMSESP::actual_master_thermostat(device_id);
|
||||
DEBUG_LOG(F("Registering new thermostat with device ID 0x%02X (as the master)"), device_id);
|
||||
|
||||
@@ -150,7 +150,6 @@ void Thermostat::add_context_menu() {
|
||||
// general MQTT command for controlling thermostat
|
||||
// e.g. { "cmd":"daytemp2", "data": 20 }
|
||||
void Thermostat::thermostat_cmd(const char * message) {
|
||||
// convert JSON and get the command
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_SMALL> doc;
|
||||
DeserializationError error = deserializeJson(doc, message);
|
||||
if (error) {
|
||||
@@ -296,18 +295,22 @@ void Thermostat::publish_values() {
|
||||
// optional, add external temp
|
||||
if (flags == EMS_DEVICE_FLAG_RC35) {
|
||||
if (dampedoutdoortemp != EMS_VALUE_INT_NOTSET) {
|
||||
rootThermostat["dampedtemp"] = dampedoutdoortemp;
|
||||
doc["dampedtemp"] = dampedoutdoortemp;
|
||||
}
|
||||
if (tempsensor1 != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootThermostat["tempsensor1"] = (float)tempsensor1 / 10;
|
||||
doc["tempsensor1"] = (float)tempsensor1 / 10;
|
||||
}
|
||||
if (tempsensor2 != EMS_VALUE_USHORT_NOTSET) {
|
||||
rootThermostat["tempsensor1"] = (float)tempsensor2 / 10;
|
||||
doc["tempsensor1"] = (float)tempsensor2 / 10;
|
||||
}
|
||||
}
|
||||
|
||||
// go through all the heating circuits
|
||||
for (const auto & hc : heating_circuits_) {
|
||||
// only send if we have an actual setpoint temp temperature values
|
||||
if ((hc->setpoint_roomTemp == EMS_VALUE_SHORT_NOTSET) || (hc->curr_roomTemp == EMS_VALUE_SHORT_NOTSET)) {
|
||||
break; // skip this HC as we don't have the temperature values yet
|
||||
}
|
||||
|
||||
has_data = true;
|
||||
if (mqtt_nested_json_) {
|
||||
// create nested json for each HC
|
||||
@@ -367,8 +370,12 @@ void Thermostat::publish_values() {
|
||||
dataThermostat["designtemp"] = hc->designtemp;
|
||||
}
|
||||
|
||||
dataThermostat["mode"] = mode_tostring(hc->get_mode(flags));
|
||||
dataThermostat["modetype"] = mode_tostring(hc->get_mode_type(flags));
|
||||
if (hc->mode != EMS_VALUE_UINT_NOTSET) {
|
||||
dataThermostat["mode"] = mode_tostring(hc->get_mode(flags));
|
||||
}
|
||||
if (hc->mode_type != EMS_VALUE_UINT_NOTSET) {
|
||||
dataThermostat["modetype"] = mode_tostring(hc->get_mode_type(flags));
|
||||
}
|
||||
|
||||
// if its not nested, send immediately
|
||||
if (!mqtt_nested_json_) {
|
||||
@@ -376,18 +383,14 @@ void Thermostat::publish_values() {
|
||||
char s[3]; // for formatting strings
|
||||
strlcpy(topic, "thermostat_data", 30);
|
||||
strlcat(topic, Helpers::itoa(s, hc->hc_num()), 30); // append hc to topic
|
||||
char data[EMSESP_MAX_JSON_SIZE];
|
||||
serializeJson(doc, data);
|
||||
Mqtt::publish(topic, data);
|
||||
Mqtt::publish(topic, doc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're using nested json, send all in one go
|
||||
if (mqtt_nested_json_ && has_data) {
|
||||
char data[EMSESP_MAX_JSON_SIZE];
|
||||
serializeJson(doc, data);
|
||||
Mqtt::publish("thermostat_data", data);
|
||||
Mqtt::publish("thermostat_data", doc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1164,7 +1167,7 @@ void Thermostat::console_commands() {
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t value;
|
||||
if (arguments.empty()) {
|
||||
value = EMSESP_DEFAULT_NOTSET;
|
||||
value = EMSESP_DEFAULT_MASTER_THERMOSTAT;
|
||||
} else {
|
||||
value = Helpers::hextoint(arguments.front().c_str());
|
||||
}
|
||||
|
||||
@@ -37,17 +37,15 @@ uint8_t tx_mode_ = EMS_TXMODE_DEFAULT;
|
||||
// Important: must not use ICACHE_FLASH_ATTR
|
||||
//
|
||||
void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) {
|
||||
static uint8_t length;
|
||||
static uint8_t length = 0;
|
||||
static bool rx_idle_ = true;
|
||||
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE + 2];
|
||||
|
||||
// TODO check if need UART Rx idle/busy
|
||||
/*
|
||||
// is a new buffer? if so init the thing for a new telegram
|
||||
if (EMS_Sys_Status.emsRxStatus == EMS_RX_STATUS_IDLE) {
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_BUSY; // status set to busy
|
||||
length = 0;
|
||||
if (rx_idle_) {
|
||||
rx_idle_ = false; // status set to busy
|
||||
length = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
// fill IRQ buffer, by emptying Rx FIFO
|
||||
if (USIS(EMSUART_UART) & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) {
|
||||
@@ -68,8 +66,7 @@ void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) {
|
||||
|
||||
pEMSRxBuf->length = (length > EMS_MAXBUFFERSIZE) ? EMS_MAXBUFFERSIZE : length;
|
||||
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, pEMSRxBuf->length); // copy data into transfer buffer, including the BRK 0x00 at the end
|
||||
length = 0;
|
||||
// EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE; // TODO check set the status flag stating BRK has been received and we can start a new package
|
||||
rx_idle_ = true; // check set the status flag stating BRK has been received and we can start a new package
|
||||
ETS_UART_INTR_ENABLE(); // re-enable UART interrupts
|
||||
|
||||
system_os_post(EMSUART_recvTaskPrio, 0, 0); // call emsuart_recvTask() at next opportunity
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "2.0.0a1"
|
||||
#define EMSESP_APP_VERSION "2.0.0a2"
|
||||
|
||||
Reference in New Issue
Block a user