mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
improvements to reduce heap and fragmentation
This commit is contained in:
@@ -29,39 +29,39 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
LOG_DEBUG(F("Adding new Boiler with device ID 0x%02X"), device_id);
|
||||
|
||||
// the telegram handlers...
|
||||
register_telegram_type(0x10, F("UBAErrorMessage1"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1));
|
||||
register_telegram_type(0x11, F("UBAErrorMessage2"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1));
|
||||
register_telegram_type(0x18, F("UBAMonitorFast"), false, 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"), false, 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("UBAMaintenanceData"), false, std::bind(&Boiler::process_UBAMaintenanceData, 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(0xE3, F("UBAMonitorSlowPlus"), false, std::bind(&Boiler::process_UBAMonitorSlowPlus2, 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(0x10, F("UBAErrorMessage1"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAErrorMessage(t); });
|
||||
register_telegram_type(0x11, F("UBAErrorMessage2"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAErrorMessage(t); });
|
||||
register_telegram_type(0x18, F("UBAMonitorFast"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAMonitorFast(t); });
|
||||
register_telegram_type(0x19, F("UBAMonitorSlow"), true, [&](std::shared_ptr<const Telegram> t) { process_UBAMonitorSlow(t); });
|
||||
register_telegram_type(0x34, F("UBAMonitorWW"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAMonitorWW(t); });
|
||||
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAMaintenanceStatus(t); });
|
||||
register_telegram_type(0x2A, F("MC10Status"), false, [&](std::shared_ptr<const Telegram> t) { process_MC10Status(t); });
|
||||
register_telegram_type(0x33, F("UBAParameterWW"), true, [&](std::shared_ptr<const Telegram> t) { process_UBAParameterWW(t); });
|
||||
register_telegram_type(0x14, F("UBATotalUptime"), false, [&](std::shared_ptr<const Telegram> t) { process_UBATotalUptime(t); });
|
||||
register_telegram_type(0x35, F("UBAFlags"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAFlags(t); });
|
||||
register_telegram_type(0x15, F("UBAMaintenanceData"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAMaintenanceData(t); });
|
||||
register_telegram_type(0x16, F("UBAParameters"), true, [&](std::shared_ptr<const Telegram> t) { process_UBAParameters(t); });
|
||||
register_telegram_type(0x1A, F("UBASetPoints"), false, [&](std::shared_ptr<const Telegram> t) { process_UBASetPoints(t); });
|
||||
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAOutdoorTemp(t); });
|
||||
register_telegram_type(0xE3, F("UBAMonitorSlowPlus"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAMonitorSlowPlus2(t); });
|
||||
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAMonitorFastPlus(t); });
|
||||
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, [&](std::shared_ptr<const Telegram> t) { process_UBAMonitorSlowPlus(t); });
|
||||
register_telegram_type(0xE9, F("UBADHWStatus"), false, [&](std::shared_ptr<const Telegram> t) { process_UBADHWStatus(t); });
|
||||
|
||||
// MQTT commands for boiler_cmd topic
|
||||
register_mqtt_cmd(F("comfort"), std::bind(&Boiler::set_warmwater_mode, this, _1, _2));
|
||||
register_mqtt_cmd(F("wwactivated"), std::bind(&Boiler::set_warmwater_activated, this, _1, _2));
|
||||
register_mqtt_cmd(F("wwtapactivated"), std::bind(&Boiler::set_tapwarmwater_activated, this, _1, _2));
|
||||
register_mqtt_cmd(F("wwonetime"), std::bind(&Boiler::set_warmwater_onetime, this, _1, _2));
|
||||
register_mqtt_cmd(F("wwcirculation"), std::bind(&Boiler::set_warmwater_circulation, this, _1, _2));
|
||||
register_mqtt_cmd(F("flowtemp"), std::bind(&Boiler::set_flow_temp, this, _1, _2));
|
||||
register_mqtt_cmd(F("wwtemp"), std::bind(&Boiler::set_warmwater_temp, this, _1, _2));
|
||||
register_mqtt_cmd(F("burnmaxpower"), std::bind(&Boiler::set_max_power, this, _1, _2));
|
||||
register_mqtt_cmd(F("burnminpower"), std::bind(&Boiler::set_min_power, this, _1, _2));
|
||||
register_mqtt_cmd(F("boilhyston"), std::bind(&Boiler::set_hyst_on, this, _1, _2));
|
||||
register_mqtt_cmd(F("boilhystoff"), std::bind(&Boiler::set_hyst_off, this, _1, _2));
|
||||
register_mqtt_cmd(F("burnperiod"), std::bind(&Boiler::set_burn_period, this, _1, _2));
|
||||
register_mqtt_cmd(F("pumpdelay"), std::bind(&Boiler::set_pump_delay, this, _1, _2));
|
||||
register_mqtt_cmd(F("comfort"), [&](const char * value, const int8_t id) { set_warmwater_mode(value, id); });
|
||||
register_mqtt_cmd(F("wwactivated"), [&](const char * value, const int8_t id) { set_warmwater_activated(value, id); });
|
||||
register_mqtt_cmd(F("wwtapactivated"), [&](const char * value, const int8_t id) { set_tapwarmwater_activated(value, id); });
|
||||
register_mqtt_cmd(F("wwonetime"), [&](const char * value, const int8_t id) { set_warmwater_onetime(value, id); });
|
||||
register_mqtt_cmd(F("wwcirculation"), [&](const char * value, const int8_t id) { set_warmwater_circulation(value, id); });
|
||||
register_mqtt_cmd(F("flowtemp"), [&](const char * value, const int8_t id) { set_flow_temp(value, id); });
|
||||
register_mqtt_cmd(F("wwtemp"), [&](const char * value, const int8_t id) { set_warmwater_temp(value, id); });
|
||||
register_mqtt_cmd(F("burnmaxpower"), [&](const char * value, const int8_t id) { set_max_power(value, id); });
|
||||
register_mqtt_cmd(F("burnminpower"), [&](const char * value, const int8_t id) { set_min_power(value, id); });
|
||||
register_mqtt_cmd(F("boilhyston"), [&](const char * value, const int8_t id) { set_hyst_on(value, id); });
|
||||
register_mqtt_cmd(F("boilhystoff"), [&](const char * value, const int8_t id) { set_hyst_off(value, id); });
|
||||
register_mqtt_cmd(F("burnperiod"), [&](const char * value, const int8_t id) { set_burn_period(value, id); });
|
||||
register_mqtt_cmd(F("pumpdelay"), [&](const char * value, const int8_t id) { set_pump_delay(value, id); });
|
||||
}
|
||||
|
||||
// add submenu context
|
||||
@@ -99,8 +99,9 @@ void Boiler::device_info(JsonArray & root) {
|
||||
|
||||
// publish values via MQTT
|
||||
void Boiler::publish_values() {
|
||||
const size_t capacity = JSON_OBJECT_SIZE(56); // must recalculate if more objects addded https://arduinojson.org/v6/assistant/
|
||||
DynamicJsonDocument doc(capacity);
|
||||
// const size_t capacity = JSON_OBJECT_SIZE(56); // must recalculate if more objects addded https://arduinojson.org/v6/assistant/
|
||||
// DynamicJsonDocument doc(capacity);
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_LARGE> doc;
|
||||
|
||||
char s[10]; // for formatting strings
|
||||
|
||||
@@ -862,7 +863,7 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
flash_string_vector{F_(typeid_mandatory)},
|
||||
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
uint16_t type_id = Helpers::hextoint(arguments.front().c_str());
|
||||
EMSESP::send_read_request(type_id, device_id());
|
||||
EMSESP::send_read_request(type_id, get_device_id());
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::BOILER,
|
||||
|
||||
@@ -29,8 +29,8 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
|
||||
LOG_DEBUG(F("Adding new Heat Pump module with device ID 0x%02X"), device_id);
|
||||
|
||||
// telegram handlers
|
||||
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));
|
||||
register_telegram_type(0x047B, F("HP1"), true, [&](std::shared_ptr<const Telegram> t) { process_HPMonitor1(t); });
|
||||
register_telegram_type(0x042B, F("HP2"), true, [&](std::shared_ptr<const Telegram> t) { process_HPMonitor2(t); });
|
||||
}
|
||||
|
||||
// context submenu
|
||||
|
||||
@@ -31,21 +31,27 @@ Mixing::Mixing(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
if (device_id <= 0x27) {
|
||||
// telegram handlers 0x20 - 0x27 for HC
|
||||
register_telegram_type(device_id - 0x20 + 0x02D7, F("MMPLUSStatusMessage_HC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_HC, this, _1));
|
||||
register_telegram_type(device_id - 0x20 + 0x02D7, F("MMPLUSStatusMessage_HC"), true, [&](std::shared_ptr<const Telegram> t) {
|
||||
process_MMPLUSStatusMessage_HC(t);
|
||||
});
|
||||
} else {
|
||||
// telegram handlers for warm water/DHW 0x28, 0x29
|
||||
register_telegram_type(device_id - 0x28 + 0x0331, F("MMPLUSStatusMessage_WWC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_WWC, this, _1));
|
||||
register_telegram_type(device_id - 0x28 + 0x0331, F("MMPLUSStatusMessage_WWC"), true, [&](std::shared_ptr<const Telegram> t) {
|
||||
process_MMPLUSStatusMessage_WWC(t);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// EMS 1.0
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
register_telegram_type(0x00AA, F("MMConfigMessage"), false, std::bind(&Mixing::process_MMConfigMessage, this, _1));
|
||||
register_telegram_type(0x00AB, F("MMStatusMessage"), true, std::bind(&Mixing::process_MMStatusMessage, this, _1));
|
||||
register_telegram_type(0x00AC, F("MMSetMessage"), false, std::bind(&Mixing::process_MMSetMessage, this, _1));
|
||||
register_telegram_type(0x00AA, F("MMConfigMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_MMConfigMessage(t); });
|
||||
register_telegram_type(0x00AB, F("MMStatusMessage"), true, [&](std::shared_ptr<const Telegram> t) { process_MMStatusMessage(t); });
|
||||
register_telegram_type(0x00AC, F("MMSetMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_MMSetMessage(t); });
|
||||
}
|
||||
|
||||
// HT3
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
register_telegram_type(0x010C, F("IPMSetMessage"), false, std::bind(&Mixing::process_IPMStatusMessage, this, _1));
|
||||
register_telegram_type(0x010C, F("IPMSetMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_IPMStatusMessage(t); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +70,7 @@ void Mixing::device_info(JsonArray & root) {
|
||||
} else {
|
||||
render_value_json(root, "", F("Heating Circuit"), hc_, nullptr);
|
||||
}
|
||||
|
||||
render_value_json(root, "", F("Current flow temperature"), flowTemp_, F_(degrees), 10);
|
||||
render_value_json(root, "", F("Setpoint flow temperature"), flowSetTemp_, F_(degrees));
|
||||
render_value_json(root, "", F("Current pump modulation"), pumpMod_, F_(percent));
|
||||
@@ -92,6 +99,7 @@ void Mixing::show_values(uuid::console::Shell & shell) {
|
||||
} else {
|
||||
print_value(shell, 2, F("Heating Circuit"), hc_, nullptr);
|
||||
}
|
||||
|
||||
print_value(shell, 4, F("Current flow temperature"), flowTemp_, F_(degrees), 10);
|
||||
print_value(shell, 4, F("Setpoint flow temperature"), flowSetTemp_, F_(degrees));
|
||||
print_value(shell, 4, F("Current pump modulation"), pumpMod_, F_(percent));
|
||||
@@ -136,7 +144,7 @@ void Mixing::publish_values() {
|
||||
char topic[30];
|
||||
char s[3]; // for formatting strings
|
||||
strlcpy(topic, "mixing_data", 30);
|
||||
strlcat(topic, Helpers::itoa(s, device_id() - 0x20 + 1), 30); // append hc to topic
|
||||
strlcat(topic, Helpers::itoa(s, get_device_id() - 0x20 + 1), 30); // append hc to topic
|
||||
Mqtt::publish(topic, doc);
|
||||
}
|
||||
|
||||
@@ -168,7 +176,7 @@ void Mixing::process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> tel
|
||||
// A1 00 FF 00 00 0C 02 04 00 01 1D 00 82
|
||||
void Mixing::process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
type_ = Type::HC;
|
||||
hc_ = device_id() - 0x20 + 1;
|
||||
hc_ = get_device_id() - 0x20 + 1;
|
||||
uint8_t ismixed = 0;
|
||||
telegram->read_value(ismixed, 0); // check if circuit is active, 0-off, 1-unmixed, 2-mixed
|
||||
if (ismixed == 0) {
|
||||
@@ -195,7 +203,7 @@ void Mixing::process_MMStatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
// the heating circuit is determine by which device_id it is, 0x20 - 0x23
|
||||
// 0x21 is position 2. 0x20 is typically reserved for the WM10 switch module
|
||||
// see https://github.com/proddy/EMS-ESP/issues/270 and https://github.com/proddy/EMS-ESP/issues/386#issuecomment-629610918
|
||||
hc_ = device_id() - 0x20 + 1;
|
||||
hc_ = get_device_id() - 0x20 + 1;
|
||||
telegram->read_value(flowTemp_, 1); // is * 10
|
||||
telegram->read_value(pumpMod_, 3);
|
||||
telegram->read_value(flowSetTemp_, 0);
|
||||
@@ -207,7 +215,7 @@ void Mixing::process_MMStatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
// Mixing on a MM10 - 0xAA
|
||||
// e.g. Thermostat -> Mixing Module, type 0xAA, telegram: 10 21 AA 00 FF 0C 0A 11 0A 32 xx
|
||||
void Mixing::process_MMConfigMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
hc_ = device_id() - 0x20 + 1;
|
||||
hc_ = get_device_id() - 0x20 + 1;
|
||||
// pos 0: active FF = on
|
||||
// pos 1: valve runtime 0C = 120 sec in units of 10 sec
|
||||
}
|
||||
@@ -215,7 +223,7 @@ void Mixing::process_MMConfigMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
// Mixing on a MM10 - 0xAC
|
||||
// e.g. Thermostat -> Mixing Module, type 0xAC, telegram: 10 21 AC 00 1E 64 01 AB
|
||||
void Mixing::process_MMSetMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
hc_ = device_id() - 0x20 + 1;
|
||||
hc_ = get_device_id() - 0x20 + 1;
|
||||
// pos 0: flowtemp setpoint 1E = 30°C
|
||||
// pos 1: position in %
|
||||
}
|
||||
|
||||
@@ -30,20 +30,22 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
|
||||
// telegram handlers
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM10) {
|
||||
register_telegram_type(0x0097, F("SM10Monitor"), true, std::bind(&Solar::process_SM10Monitor, this, _1));
|
||||
register_telegram_type(0x0097, F("SM10Monitor"), true, [&](std::shared_ptr<const Telegram> t) { process_SM10Monitor(t); });
|
||||
}
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
|
||||
register_telegram_type(0x0362, F("SM100Monitor"), true, std::bind(&Solar::process_SM100Monitor, this, _1));
|
||||
register_telegram_type(0x0363, F("SM100Monitor2"), true, std::bind(&Solar::process_SM100Monitor2, this, _1));
|
||||
register_telegram_type(0x0366, F("SM100Config"), true, std::bind(&Solar::process_SM100Config, 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"), true, std::bind(&Solar::process_SM100Energy, this, _1));
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_SM100) {
|
||||
register_telegram_type(0x0362, F("SM100Monitor"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100Monitor(t); });
|
||||
register_telegram_type(0x0363, F("SM100Monitor2"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100Monitor2(t); });
|
||||
register_telegram_type(0x0366, F("SM100Config"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100Config(t); });
|
||||
|
||||
register_telegram_type(0x0364, F("SM100Status"), false, [&](std::shared_ptr<const Telegram> t) { process_SM100Status(t); });
|
||||
register_telegram_type(0x036A, F("SM100Status2"), false, [&](std::shared_ptr<const Telegram> t) { process_SM100Status2(t); });
|
||||
register_telegram_type(0x038E, F("SM100Energy"), true, [&](std::shared_ptr<const Telegram> t) { process_SM100Energy(t); });
|
||||
}
|
||||
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_ISM) {
|
||||
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, std::bind(&Solar::process_ISM1StatusMessage, this, _1));
|
||||
register_telegram_type(0x0101, F("ISM1Set"), false, std::bind(&Solar::process_ISM1Set, this, _1));
|
||||
register_telegram_type(0x0103, F("ISM1StatusMessage"), true, [&](std::shared_ptr<const Telegram> t) { process_ISM1StatusMessage(t); });
|
||||
register_telegram_type(0x0101, F("ISM1Set"), false, [&](std::shared_ptr<const Telegram> t) { process_ISM1Set(t); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +108,7 @@ void Solar::show_values(uuid::console::Shell & shell) {
|
||||
|
||||
// publish values via MQTT
|
||||
void Solar::publish_values() {
|
||||
DynamicJsonDocument doc(EMSESP_MAX_JSON_SIZE_MEDIUM);
|
||||
StaticJsonDocument<EMSESP_MAX_JSON_SIZE_MEDIUM> doc;
|
||||
|
||||
char s[10]; // for formatting strings
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace emsesp {
|
||||
|
||||
uuid::log::Logger EMSdevice::logger_{F_(emsesp), uuid::log::Facility::CONSOLE};
|
||||
|
||||
std::vector<EMSdevice::TelegramFunction> EMSdevice::telegram_functions_;
|
||||
|
||||
std::string EMSdevice::brand_to_string() const {
|
||||
switch (brand_) {
|
||||
case EMSdevice::Brand::BOSCH:
|
||||
@@ -198,7 +200,7 @@ void EMSdevice::show_values(uuid::console::Shell & shell) {
|
||||
|
||||
// for each telegram that has the fetch value set (true) do a read request
|
||||
void EMSdevice::fetch_values() {
|
||||
LOG_DEBUG(F("Fetching values for device ID 0x%02X"), device_id());
|
||||
LOG_DEBUG(F("Fetching values for device ID 0x%02X"), get_device_id());
|
||||
|
||||
for (const auto & tf : telegram_functions_) {
|
||||
if (tf.fetch_) {
|
||||
@@ -209,7 +211,7 @@ void EMSdevice::fetch_values() {
|
||||
|
||||
// toggle on/off automatic fetch for a telegram id
|
||||
void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) {
|
||||
LOG_DEBUG(F("Toggling fetch for device ID 0x%02X, telegram ID 0x%02X to %d"), device_id(), telegram_id, toggle);
|
||||
LOG_DEBUG(F("Toggling fetch for device ID 0x%02X, telegram ID 0x%02X to %d"), get_device_id(), telegram_id, toggle);
|
||||
|
||||
for (auto & tf : telegram_functions_) {
|
||||
if (tf.telegram_type_id_ == telegram_id) {
|
||||
@@ -243,22 +245,12 @@ void EMSdevice::register_mqtt_topic(const std::string & topic, mqtt_subfunction_
|
||||
|
||||
void EMSdevice::register_mqtt_cmd(const __FlashStringHelper * cmd, mqtt_cmdfunction_p f) {
|
||||
LOG_DEBUG(F("Registering MQTT cmd %s for device type %s"), uuid::read_flash_string(cmd).c_str(), this->device_type_name().c_str());
|
||||
Mqtt::add_command(this->device_type_, cmd, f);
|
||||
}
|
||||
|
||||
EMSdevice::TelegramFunction::TelegramFunction(uint16_t telegram_type_id,
|
||||
const __FlashStringHelper * telegram_type_name,
|
||||
bool fetch,
|
||||
process_function_p process_function)
|
||||
: telegram_type_id_(telegram_type_id)
|
||||
, telegram_type_name_(telegram_type_name)
|
||||
, fetch_(fetch)
|
||||
, process_function_(process_function) {
|
||||
Mqtt::add_command(this->device_type_, this->device_id_, cmd, f);
|
||||
}
|
||||
|
||||
// register a call back function for a specific telegram type
|
||||
void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p f) {
|
||||
telegram_functions_.emplace_back(telegram_type_id, std::move(telegram_type_name), fetch, std::move(f));
|
||||
telegram_functions_.emplace_back(telegram_type_id, telegram_type_name, fetch, f);
|
||||
}
|
||||
|
||||
// return the name of the telegram type
|
||||
@@ -302,24 +294,22 @@ bool EMSdevice::handle_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// send Tx write with a data block
|
||||
void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid) {
|
||||
EMSESP::send_write_request(type_id, device_id(), offset, message_data, message_length, validate_typeid);
|
||||
EMSESP::send_write_request(type_id, this->get_device_id(), offset, message_data, message_length, validate_typeid);
|
||||
}
|
||||
|
||||
// send Tx write with a single value
|
||||
void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) {
|
||||
uint8_t message_data[1];
|
||||
message_data[0] = value;
|
||||
EMSESP::send_write_request(type_id, device_id(), offset, message_data, 1, validate_typeid);
|
||||
EMSESP::send_write_request(type_id, this->get_device_id(), offset, value, validate_typeid);
|
||||
}
|
||||
|
||||
// send Tx write with a single value, with no post validation
|
||||
void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value) {
|
||||
write_command(type_id, offset, value, 0);
|
||||
EMSESP::send_write_request(type_id, this->get_device_id(), offset, value, 0);
|
||||
}
|
||||
|
||||
// send Tx read command to the device
|
||||
void EMSdevice::read_command(const uint16_t type_id) {
|
||||
EMSESP::send_read_request(type_id, device_id());
|
||||
EMSESP::send_read_request(type_id, get_device_id());
|
||||
}
|
||||
|
||||
// prints a string value to the console
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using namespace std::placeholders; // for `_1`
|
||||
|
||||
class EMSdevice {
|
||||
public:
|
||||
static constexpr uint8_t EMS_DEVICES_MAX_TELEGRAMS = 20;
|
||||
|
||||
// device_type defines which derived class to use, e.g. BOILER, THERMOSTAT etc..
|
||||
EMSdevice(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)
|
||||
: device_type_(device_type)
|
||||
@@ -47,17 +47,7 @@ class EMSdevice {
|
||||
|
||||
virtual ~EMSdevice() = default; // destructor of base class must always be virtual because it's a polymorphic class
|
||||
|
||||
/*
|
||||
// https://github.com/proddy/EMS-ESP/issues/434#issuecomment-667840531
|
||||
inline uint8_t device_id(uint8_t hc = 0) const {
|
||||
if (((device_id_ & 0x7F) >= 0x18) && ((device_id_ & 0x7F) <= 0x1B)) {
|
||||
return ((device_id_ & 0x80) + 0x18 + hc);
|
||||
}
|
||||
return device_id_;
|
||||
}
|
||||
*/
|
||||
|
||||
inline uint8_t device_id() const {
|
||||
inline uint8_t get_device_id() const {
|
||||
return device_id_;
|
||||
}
|
||||
|
||||
@@ -157,6 +147,10 @@ class EMSdevice {
|
||||
void fetch_values();
|
||||
void toggle_fetch(uint16_t telegram_id, bool toggle);
|
||||
|
||||
static void reserve_mem(size_t n) {
|
||||
telegram_functions_.reserve(n);
|
||||
}
|
||||
|
||||
// prints a ems device value to the console, handling the correct rendering of the type
|
||||
// padding is # white space
|
||||
// name is the name of the parameter
|
||||
@@ -296,17 +290,21 @@ class EMSdevice {
|
||||
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
class TelegramFunction {
|
||||
public:
|
||||
TelegramFunction(uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p process_function);
|
||||
~TelegramFunction() = default;
|
||||
|
||||
struct TelegramFunction {
|
||||
uint16_t telegram_type_id_; // it's type_id
|
||||
const __FlashStringHelper * telegram_type_name_; // e.g. RC20Message
|
||||
bool fetch_; // if this type_id be queried automatically
|
||||
process_function_p process_function_;
|
||||
|
||||
process_function_p process_function_;
|
||||
|
||||
TelegramFunction(uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p process_function)
|
||||
: telegram_type_id_(telegram_type_id)
|
||||
, telegram_type_name_(telegram_type_name)
|
||||
, fetch_(fetch)
|
||||
, process_function_(process_function) {
|
||||
}
|
||||
};
|
||||
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
|
||||
static std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using DeviceFlags = emsesp::EMSdevice;
|
||||
using DeviceType = emsesp::EMSdevice::DeviceType;
|
||||
|
||||
AsyncWebServer webServer(80);
|
||||
|
||||
#if defined(ESP32)
|
||||
@@ -40,6 +37,8 @@ EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&web
|
||||
EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
|
||||
using DeviceFlags = emsesp::EMSdevice;
|
||||
using DeviceType = emsesp::EMSdevice::DeviceType;
|
||||
std::vector<std::unique_ptr<EMSdevice>> EMSESP::emsdevices; // array of all the detected EMS devices
|
||||
std::vector<emsesp::EMSESP::Device_record> EMSESP::device_library_; // libary of all our known EMS devices so far
|
||||
|
||||
@@ -77,6 +76,11 @@ void EMSESP::fetch_device_values(const uint8_t device_id) {
|
||||
}
|
||||
}
|
||||
|
||||
// clears list of recognized devices
|
||||
void EMSESP::clear_all_devices() {
|
||||
emsdevices.clear(); // or use empty to release memory too
|
||||
}
|
||||
|
||||
// return number of devices of a known type
|
||||
uint8_t EMSESP::count_devices(const uint8_t device_type) {
|
||||
uint8_t count = 0;
|
||||
@@ -88,6 +92,33 @@ uint8_t EMSESP::count_devices(const uint8_t device_type) {
|
||||
return count;
|
||||
}
|
||||
|
||||
// scans for new devices
|
||||
void EMSESP::scan_devices() {
|
||||
EMSESP::clear_all_devices();
|
||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
|
||||
}
|
||||
|
||||
/**
|
||||
* if thermostat master is 0x18 it handles only ww and hc1, hc2..hc4 handled by devices 0x19..0x1B
|
||||
* we send to right device and match all reads to 0x18
|
||||
*/
|
||||
uint8_t EMSESP::check_master_device(const uint8_t device_id, const uint16_t type_id, const bool read) {
|
||||
uint16_t mon_id[4] = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
|
||||
uint16_t set_id[4] = {0x02B9, 0x02BA, 0x02BB, 0x02BC};
|
||||
if (actual_master_thermostat_ == 0x18) {
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (type_id == mon_id[i] || type_id == set_id[i]) {
|
||||
if (read) {
|
||||
return 0x18;
|
||||
} else {
|
||||
return 0x18 + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return device_id;
|
||||
}
|
||||
|
||||
void EMSESP::actual_master_thermostat(const uint8_t device_id) {
|
||||
actual_master_thermostat_ = device_id;
|
||||
}
|
||||
@@ -215,11 +246,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
} else if ((it.telegram_->operation) == Telegram::Operation::TX_WRITE) {
|
||||
op = read_flash_string(F("WRITE"));
|
||||
}
|
||||
shell.printfln(F(" [%02d%c] %s %s"),
|
||||
it.id_,
|
||||
((it.retry_) ? '*' : ' '),
|
||||
op.c_str(),
|
||||
pretty_telegram(it.telegram_).c_str());
|
||||
shell.printfln(F(" [%02d%c] %s %s"), it.id_, ((it.retry_) ? '*' : ' '), op.c_str(), pretty_telegram(it.telegram_).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,7 +568,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) {
|
||||
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())) {
|
||||
if ((emsdevice->device_type() == EMSdevice::DeviceType::THERMOSTAT) && (emsdevice->get_device_id() == actual_master_thermostat())) {
|
||||
shell.printf(F(" ** master device **"));
|
||||
}
|
||||
shell.println();
|
||||
@@ -561,6 +588,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// first check to see if we already have it, if so update the record
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
@@ -585,6 +613,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// look up the rest of the details using the product_id and create the new device object
|
||||
Device_record * device_p = nullptr;
|
||||
for (auto & device : device_library_) {
|
||||
@@ -636,6 +665,17 @@ void EMSESP::send_write_request(const uint16_t type_id,
|
||||
txservice_.set_post_send_query(validate_typeid); // store which type_id to send Tx read after a write
|
||||
}
|
||||
|
||||
void EMSESP::send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value) {
|
||||
send_write_request(type_id, dest, offset, value, 0);
|
||||
}
|
||||
|
||||
// send Tx write with a single value
|
||||
void EMSESP::send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) {
|
||||
uint8_t message_data[1];
|
||||
message_data[0] = value;
|
||||
EMSESP::send_write_request(type_id, dest, offset, message_data, 1, validate_typeid);
|
||||
}
|
||||
|
||||
// this is main entry point when data is received on the Rx line, via emsuart library
|
||||
// we check if its a complete telegram or just a single byte (which could be a poll or a return status)
|
||||
// the CRC check is not done here, only when it's added to the Rx queue with add()
|
||||
@@ -735,9 +775,11 @@ void EMSESP::send_raw_telegram(const char * data) {
|
||||
txservice_.send_raw(data);
|
||||
}
|
||||
|
||||
// kick off the party, start all the services
|
||||
// start all the core services
|
||||
// the services must be loaded in the correct order
|
||||
void EMSESP::start() {
|
||||
// Load our library of known devices
|
||||
// Load our library of known devices. Names are stored in Flash mem.
|
||||
device_library_.reserve(100);
|
||||
device_library_ = {
|
||||
#include "device_library.h"
|
||||
};
|
||||
@@ -758,6 +800,12 @@ void EMSESP::start() {
|
||||
txservice_.start(); // sets bus ID, sends out request for EMS devices
|
||||
sensors_.start(); // dallas external sensors
|
||||
webServer.begin(); // start web server
|
||||
|
||||
// reserve some space for the telegram registries, to avoid memory fragmentation
|
||||
EMSdevice::reserve_mem(EMSdevice::EMS_DEVICES_MAX_TELEGRAMS); // space for 20 telegram handlers
|
||||
emsdevices.reserve(5); // reserve space for initially 5 devices
|
||||
|
||||
LOG_INFO("EMS Device library loaded with %d records", device_library_.size());
|
||||
}
|
||||
|
||||
// main loop calling all services
|
||||
|
||||
@@ -78,6 +78,9 @@ class EMSESP {
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const uint16_t validate_typeid);
|
||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value);
|
||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
||||
|
||||
static void send_raw_telegram(const char * data);
|
||||
static bool device_exists(const uint8_t device_id);
|
||||
|
||||
@@ -87,6 +90,7 @@ class EMSESP {
|
||||
|
||||
static uint8_t actual_master_thermostat();
|
||||
static void actual_master_thermostat(const uint8_t device_id);
|
||||
static uint8_t check_master_device(const uint8_t device_id, const uint16_t type_id, const bool read);
|
||||
|
||||
static void show_device_values(uuid::console::Shell & shell);
|
||||
static void show_sensor_values(uuid::console::Shell & shell);
|
||||
@@ -134,6 +138,8 @@ class EMSESP {
|
||||
static void fetch_device_values(const uint8_t device_id = 0);
|
||||
|
||||
static bool add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand);
|
||||
static void scan_devices();
|
||||
static void clear_all_devices();
|
||||
|
||||
static std::vector<std::unique_ptr<EMSdevice>> emsdevices;
|
||||
|
||||
|
||||
46
src/mqtt.cpp
46
src/mqtt.cpp
@@ -28,6 +28,7 @@ AsyncMqttClient * Mqtt::mqttClient_;
|
||||
std::string Mqtt::hostname_;
|
||||
uint8_t Mqtt::mqtt_qos_;
|
||||
uint16_t Mqtt::publish_time_;
|
||||
uint8_t Mqtt::bus_id_;
|
||||
|
||||
std::vector<Mqtt::MQTTSubFunction> Mqtt::mqtt_subfunctions_;
|
||||
std::vector<Mqtt::MQTTCmdFunction> Mqtt::mqtt_cmdfunctions_;
|
||||
@@ -35,38 +36,11 @@ std::vector<Mqtt::MQTTCmdFunction> Mqtt::mqtt_cmdfunctions_;
|
||||
uint16_t Mqtt::mqtt_publish_fails_ = 0;
|
||||
size_t Mqtt::maximum_mqtt_messages_ = Mqtt::MAX_MQTT_MESSAGES;
|
||||
uint16_t Mqtt::mqtt_message_id_ = 0;
|
||||
std::deque<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
|
||||
std::list<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
|
||||
char will_topic_[Mqtt::MQTT_TOPIC_MAX_SIZE]; // because MQTT library keeps only char pointer
|
||||
|
||||
uuid::log::Logger Mqtt::logger_{F_(mqtt), uuid::log::Facility::DAEMON};
|
||||
|
||||
Mqtt::QueuedMqttMessage::QueuedMqttMessage(uint16_t id, std::shared_ptr<MqttMessage> && content)
|
||||
: id_(id)
|
||||
, content_(std::move(content)) {
|
||||
retry_count_ = 0;
|
||||
packet_id_ = 0;
|
||||
}
|
||||
|
||||
MqttMessage::MqttMessage(const uint8_t operation, const std::string & topic, const std::string && payload, bool retain)
|
||||
: operation(operation)
|
||||
, topic(topic)
|
||||
, payload(std::move(payload))
|
||||
, retain(retain) {
|
||||
}
|
||||
|
||||
Mqtt::MQTTSubFunction::MQTTSubFunction(const uint8_t device_type, const std::string && topic, const std::string && full_topic, mqtt_subfunction_p mqtt_subfunction)
|
||||
: device_type_(device_type)
|
||||
, topic_(topic)
|
||||
, full_topic_(full_topic)
|
||||
, mqtt_subfunction_(mqtt_subfunction) {
|
||||
}
|
||||
|
||||
Mqtt::MQTTCmdFunction::MQTTCmdFunction(const uint8_t device_type, const __FlashStringHelper * cmd, mqtt_cmdfunction_p mqtt_cmdfunction)
|
||||
: device_type_(device_type)
|
||||
, cmd_(cmd)
|
||||
, mqtt_cmdfunction_(mqtt_cmdfunction) {
|
||||
}
|
||||
|
||||
// subscribe to an MQTT topic, and store the associated callback function
|
||||
// only if it already hasn't been added
|
||||
void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_subfunction_p cb) {
|
||||
@@ -88,10 +62,11 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_
|
||||
}
|
||||
|
||||
// adds a command and callback function for a specific device
|
||||
void Mqtt::add_command(const uint8_t device_type, const __FlashStringHelper * cmd, mqtt_cmdfunction_p cb) {
|
||||
void Mqtt::add_command(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, mqtt_cmdfunction_p cb) {
|
||||
// subscribe to the command topic if it doesn't exist yet
|
||||
// create the cmd topic for a device like "<device_type>_cmd" e.g. "boiler_cmd"
|
||||
// unless its a system MQTT command, then its system_cmd
|
||||
|
||||
std::string cmd_topic(40, '\0');
|
||||
if (device_type == EMSdevice::DeviceType::SERVICEKEY) {
|
||||
cmd_topic = MQTT_SYSTEM_CMD; // hard-coded system
|
||||
@@ -111,8 +86,7 @@ void Mqtt::add_command(const uint8_t device_type, const __FlashStringHelper * cm
|
||||
Mqtt::subscribe(device_type, cmd_topic, nullptr); // use an empty function handler to signal this is a command function
|
||||
}
|
||||
|
||||
// add the function to our list
|
||||
mqtt_cmdfunctions_.emplace_back(device_type, std::move(cmd), std::move(cb));
|
||||
mqtt_cmdfunctions_.emplace_back(device_type, device_id, cmd, cb);
|
||||
}
|
||||
|
||||
// subscribe to an MQTT topic, and store the associated callback function. For generic functions not tied to a specific device
|
||||
@@ -366,6 +340,8 @@ void Mqtt::start() {
|
||||
mqtt_qos_ = mqttSettings.mqtt_qos;
|
||||
});
|
||||
|
||||
EMSESP::emsespSettingsService.read([&](EMSESPSettings & settings) { bus_id_ = settings.ems_bus_id; });
|
||||
|
||||
mqttClient_->onConnect([this](bool sessionPresent) { on_connect(); });
|
||||
|
||||
mqttClient_->onDisconnect([this](AsyncMqttClientDisconnectReason reason) {
|
||||
@@ -402,6 +378,10 @@ void Mqtt::start() {
|
||||
// publish
|
||||
on_publish(packetId);
|
||||
});
|
||||
|
||||
// create space for command buffer, to avoid heap memory fragmentation
|
||||
mqtt_cmdfunctions_.reserve(40); // current count with boiler+thermostat is 37
|
||||
mqtt_subfunctions_.reserve(10);
|
||||
}
|
||||
|
||||
void Mqtt::set_publish_time(uint16_t publish_time) {
|
||||
@@ -432,8 +412,8 @@ void Mqtt::on_connect() {
|
||||
// add the system MQTT subscriptions, only if its a fresh start with no previous subscriptions
|
||||
// these commands respond to the topic "system_cmd" and take a payload like {cmd:"", data:"", id:""}
|
||||
if (mqtt_subfunctions_.empty()) {
|
||||
add_command(EMSdevice::DeviceType::SERVICEKEY, F("pin"), System::mqtt_command_pin);
|
||||
add_command(EMSdevice::DeviceType::SERVICEKEY, F("send"), System::mqtt_command_send);
|
||||
add_command(EMSdevice::DeviceType::SERVICEKEY, bus_id_, F("pin"), System::mqtt_command_pin);
|
||||
add_command(EMSdevice::DeviceType::SERVICEKEY, bus_id_, F("send"), System::mqtt_command_send);
|
||||
}
|
||||
|
||||
LOG_INFO(F("MQTT connected"));
|
||||
|
||||
68
src/mqtt.h
68
src/mqtt.h
@@ -24,7 +24,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
|
||||
#include <AsyncMqttClient.h>
|
||||
@@ -46,16 +46,20 @@ namespace emsesp {
|
||||
using mqtt_subfunction_p = std::function<void(const char * message)>;
|
||||
using mqtt_cmdfunction_p = std::function<void(const char * data, const int8_t id)>;
|
||||
|
||||
using namespace std::placeholders; // for `_1`
|
||||
|
||||
struct MqttMessage {
|
||||
MqttMessage(const uint8_t operation, const std::string & topic, const std::string && payload, bool retain);
|
||||
~MqttMessage() = default;
|
||||
|
||||
const uint8_t operation;
|
||||
const std::string topic;
|
||||
const std::string payload;
|
||||
const bool retain;
|
||||
|
||||
MqttMessage(const uint8_t operation, const std::string & topic, const std::string && payload, bool retain)
|
||||
: operation(operation)
|
||||
, topic(topic)
|
||||
, payload(std::move(payload))
|
||||
, retain(retain) {
|
||||
}
|
||||
};
|
||||
|
||||
class Mqtt {
|
||||
@@ -74,7 +78,7 @@ class Mqtt {
|
||||
static void subscribe(const std::string & topic, mqtt_subfunction_p cb);
|
||||
static void resubscribe();
|
||||
|
||||
static void add_command(const uint8_t device_type, const __FlashStringHelper * cmd, mqtt_cmdfunction_p cb);
|
||||
static void add_command(const uint8_t device_type, const uint8_t device_id, const __FlashStringHelper * cmd, mqtt_cmdfunction_p cb);
|
||||
|
||||
static void publish(const std::string & topic, const std::string & payload, bool retain = false);
|
||||
static void publish(const std::string & topic, const JsonDocument & payload, bool retain = false);
|
||||
@@ -106,16 +110,18 @@ class Mqtt {
|
||||
mqtt_publish_fails_ = 0;
|
||||
}
|
||||
|
||||
static std::string hostname_;
|
||||
|
||||
class MQTTCmdFunction {
|
||||
public:
|
||||
MQTTCmdFunction(const uint8_t device_type, const __FlashStringHelper * cmd, mqtt_cmdfunction_p mqtt_cmdfunction);
|
||||
~MQTTCmdFunction() = default;
|
||||
|
||||
const uint8_t device_type_;
|
||||
struct MQTTCmdFunction {
|
||||
uint8_t device_type_;
|
||||
uint8_t device_id_;
|
||||
const __FlashStringHelper * cmd_;
|
||||
mqtt_cmdfunction_p mqtt_cmdfunction_;
|
||||
|
||||
MQTTCmdFunction(uint8_t device_type, uint8_t device_id, const __FlashStringHelper * cmd, mqtt_cmdfunction_p mqtt_cmdfunction)
|
||||
: device_type_(device_type)
|
||||
, device_id_(device_id)
|
||||
, cmd_(cmd)
|
||||
, mqtt_cmdfunction_(mqtt_cmdfunction) {
|
||||
}
|
||||
};
|
||||
|
||||
static std::vector<MQTTCmdFunction> commands() {
|
||||
@@ -127,15 +133,20 @@ class Mqtt {
|
||||
|
||||
class QueuedMqttMessage {
|
||||
public:
|
||||
QueuedMqttMessage(uint16_t id, std::shared_ptr<MqttMessage> && content);
|
||||
~QueuedMqttMessage() = default;
|
||||
|
||||
const uint16_t id_;
|
||||
const std::shared_ptr<const MqttMessage> content_;
|
||||
uint8_t retry_count_;
|
||||
uint16_t packet_id_;
|
||||
|
||||
~QueuedMqttMessage() = default;
|
||||
QueuedMqttMessage(uint16_t id, std::shared_ptr<MqttMessage> && content)
|
||||
: id_(id)
|
||||
, content_(std::move(content)) {
|
||||
retry_count_ = 0;
|
||||
packet_id_ = 0;
|
||||
}
|
||||
};
|
||||
static std::deque<QueuedMqttMessage> mqtt_messages_;
|
||||
static std::list<QueuedMqttMessage> mqtt_messages_;
|
||||
|
||||
static AsyncMqttClient * mqttClient_;
|
||||
|
||||
@@ -143,7 +154,7 @@ class Mqtt {
|
||||
static uint16_t mqtt_message_id_;
|
||||
static bool mqtt_retain_;
|
||||
|
||||
static constexpr size_t MAX_MQTT_MESSAGES = 30; // size of queue
|
||||
static constexpr size_t MAX_MQTT_MESSAGES = 20; // size of queue
|
||||
static constexpr uint32_t MQTT_PUBLISH_WAIT = 200; // delay between sending publishes, to account for large payloads
|
||||
static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing
|
||||
|
||||
@@ -159,15 +170,18 @@ class Mqtt {
|
||||
static uint16_t mqtt_publish_fails_;
|
||||
|
||||
// function handlers for MQTT subscriptions
|
||||
class MQTTSubFunction {
|
||||
public:
|
||||
MQTTSubFunction(const uint8_t device_type, const std::string && topic, const std::string && full_topic, mqtt_subfunction_p mqtt_subfunction);
|
||||
~MQTTSubFunction() = default;
|
||||
|
||||
const uint8_t device_type_; // which device type, from DeviceType::
|
||||
struct MQTTSubFunction {
|
||||
uint8_t device_type_; // which device type, from DeviceType::
|
||||
const std::string topic_;
|
||||
const std::string full_topic_; // the fully qualified topic name, usually with the hostname prefixed
|
||||
mqtt_subfunction_p mqtt_subfunction_; // can be empty
|
||||
|
||||
MQTTSubFunction(uint8_t device_type, const std::string && topic, const std::string && full_topic, mqtt_subfunction_p mqtt_subfunction)
|
||||
: device_type_(device_type)
|
||||
, topic_(topic)
|
||||
, full_topic_(full_topic)
|
||||
, mqtt_subfunction_(mqtt_subfunction) {
|
||||
}
|
||||
};
|
||||
|
||||
static std::vector<MQTTSubFunction> mqtt_subfunctions_; // list of mqtt subscribe callbacks for all devices
|
||||
@@ -177,8 +191,10 @@ class Mqtt {
|
||||
uint32_t last_publish_ = 0;
|
||||
|
||||
// settings, copied over
|
||||
static uint8_t mqtt_qos_;
|
||||
static uint16_t publish_time_;
|
||||
static std::string hostname_;
|
||||
static uint8_t mqtt_qos_;
|
||||
static uint16_t publish_time_;
|
||||
static uint8_t bus_id_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -127,11 +127,6 @@ std::string Telegram::to_string_message() const {
|
||||
return Helpers::data_to_hex(this->message_data, this->message_length);
|
||||
}
|
||||
|
||||
RxService::QueuedRxTelegram::QueuedRxTelegram(uint16_t id, std::shared_ptr<Telegram> && telegram)
|
||||
: id_(id)
|
||||
, telegram_(std::move(telegram)) {
|
||||
}
|
||||
|
||||
// empty queue, don't process them
|
||||
void RxService::flush_rx_queue() {
|
||||
rx_telegrams_.clear();
|
||||
@@ -143,12 +138,9 @@ void RxService::flush_rx_queue() {
|
||||
void RxService::loop() {
|
||||
while (!rx_telegrams_.empty()) {
|
||||
auto telegram = rx_telegrams_.front().telegram_;
|
||||
|
||||
(void)EMSESP::process_telegram(telegram); // further process the telegram
|
||||
|
||||
increment_telegram_count(); // increase count
|
||||
|
||||
rx_telegrams_.pop_front(); // remove it from the queue
|
||||
increment_telegram_count(); // increase count
|
||||
rx_telegrams_.pop_front(); // remove it from the queue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,11 +213,15 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
LOG_DEBUG(F("[DEBUG] New Rx [#%d] telegram, message length %d"), rx_telegram_id_, message_length);
|
||||
#endif
|
||||
|
||||
// if we don't have a type_id or empty data block, exit
|
||||
if ((type_id == 0) || (message_length == 0)) {
|
||||
// if we don't have a type_id exit,
|
||||
// do not exit on empty message, it is checked for toggle fetch
|
||||
if (type_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we receive a hc2.. telegram from 0x19.. match it to master_thermostat if master is 0x18
|
||||
src = EMSESP::check_master_device(src, type_id, true);
|
||||
|
||||
// create the telegram
|
||||
auto telegram = std::make_shared<Telegram>(Telegram::Operation::RX, src, dest, type_id, offset, message_data, message_length);
|
||||
|
||||
@@ -241,12 +237,6 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
// Tx CODE starts here...
|
||||
//
|
||||
|
||||
TxService::QueuedTxTelegram::QueuedTxTelegram(uint16_t id, std::shared_ptr<Telegram> && telegram, bool retry)
|
||||
: id_(id)
|
||||
, telegram_(std::move(telegram))
|
||||
, retry_(retry) {
|
||||
}
|
||||
|
||||
// empty queue, don't process
|
||||
void TxService::flush_tx_queue() {
|
||||
tx_telegrams_.clear();
|
||||
@@ -295,16 +285,12 @@ void TxService::send() {
|
||||
}
|
||||
|
||||
// if we're in read-only mode (tx_mode 0) forget the Tx call
|
||||
if (tx_mode() == 0) {
|
||||
tx_telegrams_.pop_front();
|
||||
return;
|
||||
if (tx_mode() != 0) {
|
||||
send_telegram(tx_telegrams_.front());
|
||||
}
|
||||
|
||||
// send next telegram in the queue (which is actually a list!)
|
||||
send_telegram(tx_telegrams_.front());
|
||||
|
||||
// remove the telegram from the queue
|
||||
tx_telegrams_.pop_front();
|
||||
tx_telegrams_.pop_front(); // remove the telegram from the queue
|
||||
}
|
||||
|
||||
// process a Tx telegram
|
||||
@@ -324,6 +310,10 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
// dest - for READ the MSB must be set
|
||||
// fix the READ or WRITE depending on the operation
|
||||
uint8_t dest = telegram->dest;
|
||||
|
||||
// check if we have to manipulate the id for thermostats > 0x18
|
||||
dest = EMSESP::check_master_device(dest, telegram->type_id, false);
|
||||
|
||||
if (telegram->operation == Telegram::Operation::TX_READ) {
|
||||
dest |= 0x80; // read has 8th bit set for the destination
|
||||
}
|
||||
@@ -570,7 +560,7 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DENUG
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG(F("[DEBUG] Last Tx %s operation failed. Retry #%d. sent message: %s, received: %s"),
|
||||
(operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"),
|
||||
retry_count_,
|
||||
|
||||
@@ -20,9 +20,7 @@
|
||||
#define EMSESP_TELEGRAM_H
|
||||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <memory> // for unique ptrs
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
// UART drivers
|
||||
#if defined(ESP8266)
|
||||
@@ -197,7 +195,7 @@ class EMSbus {
|
||||
|
||||
class RxService : public EMSbus {
|
||||
public:
|
||||
static constexpr size_t MAX_RX_TELEGRAMS = 20;
|
||||
static constexpr size_t MAX_RX_TELEGRAMS = 10;
|
||||
|
||||
RxService() = default;
|
||||
~RxService() = default;
|
||||
@@ -226,31 +224,32 @@ class RxService : public EMSbus {
|
||||
|
||||
class QueuedRxTelegram {
|
||||
public:
|
||||
QueuedRxTelegram(uint16_t id, std::shared_ptr<Telegram> && telegram);
|
||||
~QueuedRxTelegram() = default;
|
||||
|
||||
uint16_t id_; // sequential identifier
|
||||
const uint16_t id_;
|
||||
const std::shared_ptr<const Telegram> telegram_;
|
||||
|
||||
~QueuedRxTelegram() = default;
|
||||
QueuedRxTelegram(uint16_t id, std::shared_ptr<Telegram> && telegram)
|
||||
: id_(id)
|
||||
, telegram_(std::move(telegram)) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<QueuedRxTelegram> queue() const {
|
||||
const std::list<QueuedRxTelegram> queue() const {
|
||||
return rx_telegrams_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t last_rx_check_ = 0;
|
||||
|
||||
uint8_t rx_telegram_id_ = 0; // queue counter
|
||||
|
||||
uint32_t last_rx_check_ = 0;
|
||||
uint8_t rx_telegram_id_ = 0; // queue counter
|
||||
uint16_t telegram_count_ = 0; // # Rx received
|
||||
uint16_t telegram_error_count_ = 0; // # Rx CRC errors
|
||||
|
||||
std::deque<QueuedRxTelegram> rx_telegrams_;
|
||||
std::list<QueuedRxTelegram> rx_telegrams_;
|
||||
};
|
||||
|
||||
class TxService : public EMSbus {
|
||||
public:
|
||||
static constexpr size_t MAX_TX_TELEGRAMS = 30; // size of Tx queue
|
||||
static constexpr size_t MAX_TX_TELEGRAMS = 20; // size of Tx queue
|
||||
|
||||
static constexpr uint8_t TX_WRITE_FAIL = 4;
|
||||
static constexpr uint8_t TX_WRITE_SUCCESS = 1;
|
||||
@@ -336,26 +335,29 @@ class TxService : public EMSbus {
|
||||
|
||||
class QueuedTxTelegram {
|
||||
public:
|
||||
QueuedTxTelegram(uint16_t id, std::shared_ptr<Telegram> && telegram, bool retry);
|
||||
~QueuedTxTelegram() = default;
|
||||
|
||||
uint16_t id_; // sequential identifier
|
||||
const uint16_t id_;
|
||||
const std::shared_ptr<const Telegram> telegram_;
|
||||
bool retry_; // is a retry
|
||||
const bool retry_; // is a retry
|
||||
|
||||
~QueuedTxTelegram() = default;
|
||||
QueuedTxTelegram(uint16_t id, std::shared_ptr<Telegram> && telegram, bool retry)
|
||||
: id_(id)
|
||||
, telegram_(std::move(telegram))
|
||||
, retry_(retry) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<QueuedTxTelegram> queue() const {
|
||||
const std::list<QueuedTxTelegram> queue() const {
|
||||
return tx_telegrams_;
|
||||
}
|
||||
|
||||
static constexpr uint8_t MAXIMUM_TX_RETRIES = 3;
|
||||
|
||||
private:
|
||||
uint8_t tx_telegram_id_ = 0; // queue counter
|
||||
uint8_t tx_telegram_id_ = 0; // queue counter
|
||||
uint32_t last_tx_check_ = 0;
|
||||
|
||||
uint32_t last_tx_check_ = 0;
|
||||
|
||||
std::deque<QueuedTxTelegram> tx_telegrams_;
|
||||
std::list<QueuedTxTelegram> tx_telegrams_;
|
||||
|
||||
uint16_t telegram_read_count_ = 0; // # Tx successful reads
|
||||
uint16_t telegram_write_count_ = 0; // # Tx successful writes
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "2.0.0b12-1"
|
||||
#define EMSESP_APP_VERSION "2.0.0b12-2"
|
||||
|
||||
Reference in New Issue
Block a user