diff --git a/src/console.h b/src/console.h index 337133426..27d8afe1e 100644 --- a/src/console.h +++ b/src/console.h @@ -112,6 +112,7 @@ MAKE_PSTR(deviceid_optional, "[device ID]") MAKE_PSTR(invalid_log_level, "Invalid log level") MAKE_PSTR(ip_address_optional, "[IP address]") MAKE_PSTR(ip_address_mandatory, "") +MAKE_PSTR(port_mandatory, "") MAKE_PSTR(log_level_fmt, "Log level = %s") MAKE_PSTR(log_level_optional, "[level]") MAKE_PSTR(name_mandatory, "") diff --git a/src/device_library.h b/src/device_library.h index dc0f76b86..2f1e9a492 100644 --- a/src/device_library.h +++ b/src/device_library.h @@ -24,36 +24,38 @@ */ // Boilers - 0x08 -{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, -{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, -{133, DeviceType::BOILER, F("GB125/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, -{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, -{208, DeviceType::BOILER, F("Logamax plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, { 64, DeviceType::BOILER, F("BK13,BK15/Smartline/GB1x2"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, -{234, DeviceType::BOILER, F("Logamax Plus GB122"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, +{ 72, DeviceType::BOILER, F("GB125/MC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, { 84, DeviceType::BOILER, F("Logamax Plus GB022"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, { 95, DeviceType::BOILER, F("Condens 2500/Logamax/Logomatic/Cerapur Top/Greenstar/Generic HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, +{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, {122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, +{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, +{133, DeviceType::BOILER, F("GB125/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, {170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, {172, DeviceType::BOILER, F("Enviline/Compress 6000AW"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, -{ 72, DeviceType::BOILER, F("GB125/MC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, +{195, DeviceType::BOILER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, +{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, +{208, DeviceType::BOILER, F("Logamax plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, +{234, DeviceType::BOILER, F("Logamax Plus GB122"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, + // 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}, +{163, DeviceType::SOLAR, F("SM100"), DeviceFlags::EMS_DEVICE_FLAG_SM100}, +{164, DeviceType::SOLAR, F("SM200"), DeviceFlags::EMS_DEVICE_FLAG_SM100}, // Mixing Modules - 0x20-0x27 for HC, 0x28-0x29 for WWC -{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}, +{160, DeviceType::MIXING, F("MM100"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS}, +{161, DeviceType::MIXING, F("MM200"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS}, // 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}, +{252, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // Switches - 0x11 { 71, DeviceType::SWITCH, F("WM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x11 @@ -61,36 +63,38 @@ // Controllers - 0x09 / 0x10 / 0x50 { 68, DeviceType::CONTROLLER, F("BC10/RFM20"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 { 89, DeviceType::CONTROLLER, F("BC10 GB142"), 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 +{ 95, DeviceType::CONTROLLER, F("HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 {114, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 {125, DeviceType::CONTROLLER, F("BC25"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 -{169, DeviceType::CONTROLLER, F("BC40"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 {152, DeviceType::CONTROLLER, F("Controller"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 -{ 95, DeviceType::CONTROLLER, F("HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 -{209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 -{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 +{169, DeviceType::CONTROLLER, F("BC40"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 +{190, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 {207, DeviceType::CONTROLLER, F("Sense II/CS200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10 +{209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 +{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50 +{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 +{241, DeviceType::CONTROLLER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 + // Connect devices - 0x02 +{171, DeviceType::CONNECT, F("OpenTherm Converter"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 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 - 0x48 / 0x18 -{189, DeviceType::GATEWAY, F("KM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x48 { 94, DeviceType::GATEWAY, F("RFM20 Remote Base for RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18 +{189, DeviceType::GATEWAY, F("KM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x48 // 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 // Thermostat - Common for Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 +{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_1},// 0x10 - based on RC35 +{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17 +{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10 { 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 -{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_1},// 0x10 - based on RC35 -{ 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 diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 1519cc4d8..567b12c4f 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -228,8 +228,7 @@ std::string EMSdevice::telegram_type_name(std::shared_ptr telegr } for (const auto & tf : telegram_functions_) { -// if ((tf.telegram_type_id_ & 0x7F) == (telegram->type_id & 0x7F)) { - if (tf.telegram_type_id_ == telegram->type_id && (telegram->type_id < 0xF0)) { + if (tf.telegram_type_id_ == (telegram->type_id && ((telegram->type_id & 0xF0) != 0xF0))) { return uuid::read_flash_string(tf.telegram_type_name_); } } diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 7f7130424..851b3626c 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -66,11 +66,6 @@ uint32_t EMSESP::last_fetch_ = 0; #include "test/test_data.h" // used with the 'test' command, under su/admin #endif -// for each associated EMS device go and request its data values -void EMSESP::fetch_device_values() { - fetch_device_values(0); // 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) { @@ -423,7 +418,7 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { if (emsdevice) { if (emsdevice->is_device_id(telegram->src)) { found = emsdevice->handle_telegram(telegram); - // check to see if we need to force an MQTT publish + // check to see if we need to follow up after the telegram has been processed if (found) { if (emsdevice->updated_values()) { emsdevice->publish_values(); // publish to MQTT if we explicitly have too diff --git a/src/emsesp.h b/src/emsesp.h index 02f27eb49..1f5a55861 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -123,8 +123,7 @@ class EMSESP { static void console_commands(Shell & shell, unsigned int context); - static void fetch_device_values(const uint8_t device_id); - static void fetch_device_values(); + static void fetch_device_values(const uint8_t device_id = 0); private: EMSESP() = delete; diff --git a/src/mixing.cpp b/src/mixing.cpp index 4589af2fa..2a94bc245 100644 --- a/src/mixing.cpp +++ b/src/mixing.cpp @@ -43,8 +43,6 @@ Mixing::Mixing(uint8_t device_type, uint8_t device_id, uint8_t product_id, const register_telegram_type(0x00AB, F("MMStatusMessage"), true, std::bind(&Mixing::process_MMStatusMessage, this, _1)); register_telegram_type(0x00AC, F("MMSetMessage"), false, nullptr); } - Settings settings; - mqtt_format_ = settings.mqtt_format(); // single, nested or ha // MQTT callbacks // register_mqtt_topic("cmd", std::bind(&Mixing::cmd, this, _1)); @@ -88,63 +86,43 @@ void Mixing::show_values(uuid::console::Shell & shell) { // 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); - JsonObject rootMixing = doc.to(); - JsonObject dataMixing; - if (mqtt_format_ == Settings::MQTT_format::SINGLE) { - switch (type_) { - case Type::HC: - rootMixing["type"] = "hc"; - break; - case Type::WWC: - rootMixing["type"] = "wwc"; - break; - case Type::NONE: - default: - return; - } - dataMixing = rootMixing; - } else { - char hc_name[10]; // hc{1-4} - if(type_ == Type::HC) { - strlcpy(hc_name, "hc", 10); - } else { - strlcpy(hc_name, "wwc", 10); - } - char s[3]; // for formatting strings - strlcat(hc_name, Helpers::itoa(s, hc_), 10); - dataMixing = rootMixing.createNestedObject(hc_name); + switch (type_) { + case Type::HC: + doc["type"] = "hc"; + break; + case Type::WWC: + doc["type"] = "wwc"; + break; + case Type::NONE: + default: + return; } if (flowTemp_ != EMS_VALUE_USHORT_NOTSET) { - dataMixing["flowTemp"] = (float)flowTemp_ / 10; + doc["flowTemp"] = (float)flowTemp_ / 10; } if (pumpMod_ != EMS_VALUE_UINT_NOTSET) { - dataMixing["pumpMod"] = pumpMod_; + doc["pumpMod"] = pumpMod_; } if (status_ != EMS_VALUE_UINT_NOTSET) { - dataMixing["status"] = status_; + doc["status"] = status_; } if (flowSetTemp_ != EMS_VALUE_UINT_NOTSET) { - dataMixing["flowSetTemp"] = flowSetTemp_; + doc["flowSetTemp"] = flowSetTemp_; } #ifdef EMSESP_DEBUG LOG_DEBUG(F("[DEBUG] Performing a mixing module publish")); #endif - // if format is single, send immediately and quit - if (mqtt_format_ == Settings::MQTT_format::SINGLE) { - 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 - Mqtt::publish(topic, doc); - return; - } - Mqtt::publish("mixing_data", doc); + 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 + Mqtt::publish(topic, doc); } // heating circuits 0x02D7, 0x02D8 etc... diff --git a/src/mixing.h b/src/mixing.h index a3d2ab36f..6b430f62e 100644 --- a/src/mixing.h +++ b/src/mixing.h @@ -63,7 +63,6 @@ class Mixing : public EMSdevice { uint8_t status_ = EMS_VALUE_UINT_NOTSET; uint8_t flowSetTemp_ = EMS_VALUE_UINT_NOTSET; Type type_ = Type::NONE; - uint8_t mqtt_format_; // single, nested or ha }; diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 4b7f6d41f..a082903de 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -24,10 +24,11 @@ MAKE_PSTR_WORD(qos) MAKE_PSTR_WORD(base) MAKE_PSTR_WORD(heartbeat) MAKE_PSTR_WORD(ip) +MAKE_PSTR_WORD(port) MAKE_PSTR_WORD(nested) MAKE_PSTR_WORD(single) MAKE_PSTR_WORD(ha) -MAKE_PSTR_WORD(my) +MAKE_PSTR_WORD(custom) MAKE_PSTR_WORD(publish_time) MAKE_PSTR_WORD(publish) MAKE_PSTR_WORD(connected) @@ -647,8 +648,8 @@ void Mqtt::console_commands(Shell & shell, unsigned int context) { value = Settings::MQTT_format::NESTED; } else if (arguments[0] == read_flash_string(F_(ha))) { value = Settings::MQTT_format::HA; - } else if (arguments[0] == read_flash_string(F_(my))) { - value = Settings::MQTT_format::MY; + } else if (arguments[0] == read_flash_string(F_(custom))) { + value = Settings::MQTT_format::CUSTOM; } else { shell.println(F("Must be single, nested or ha")); return; @@ -658,7 +659,7 @@ void Mqtt::console_commands(Shell & shell, unsigned int context) { shell.println(F("Please restart EMS-ESP")); }, [](Shell & shell __attribute__((unused)), const std::vector & arguments __attribute__((unused))) -> const std::vector { - return std::vector{read_flash_string(F_(single)), read_flash_string(F_(nested)), read_flash_string(F_(ha)), read_flash_string(F_(my))}; + return std::vector{read_flash_string(F_(single)), read_flash_string(F_(nested)), read_flash_string(F_(ha)), read_flash_string(F_(custom))}; }); EMSESPShell::commands->add_command(ShellContext::MQTT, @@ -725,6 +726,19 @@ void Mqtt::console_commands(Shell & shell, unsigned int context) { } reconnect(); }); + + EMSESPShell::commands->add_command(ShellContext::MQTT, + CommandFlags::ADMIN, + flash_string_vector{F_(set), F_(port)}, + flash_string_vector{F_(port_mandatory)}, + [](Shell & shell __attribute__((unused)), const std::vector & arguments) { + Settings settings; + if (!arguments.empty()) { + settings.mqtt_port(atoi(arguments.front().c_str())); + settings.commit(); + } + reconnect(); + }); EMSESPShell::commands->add_command(ShellContext::MQTT, CommandFlags::ADMIN, @@ -823,8 +837,8 @@ void Mqtt::console_commands(Shell & shell, unsigned int context) { shell.printfln(F_(mqtt_format_fmt), F_(nested)); } else if (settings.mqtt_format() == Settings::MQTT_format::HA) { shell.printfln(F_(mqtt_format_fmt), F_(ha)); - } else if (settings.mqtt_format() == Settings::MQTT_format::MY) { - shell.printfln(F_(mqtt_format_fmt), F_(my)); + } else if (settings.mqtt_format() == Settings::MQTT_format::CUSTOM) { + shell.printfln(F_(mqtt_format_fmt), F_(custom)); } shell.printfln(F_(mqtt_heartbeat_fmt), settings.mqtt_heartbeat() ? F_(enabled) : F_(disabled)); shell.printfln(F_(mqtt_publish_time_fmt), settings.mqtt_publish_time()); diff --git a/src/sensors.cpp b/src/sensors.cpp index 94b098406..7c502e482 100644 --- a/src/sensors.cpp +++ b/src/sensors.cpp @@ -274,7 +274,7 @@ void Sensors::publish_values() { uint8_t i = 1; for (const auto & device : devices_) { - if (mqtt_format_ == Settings::MQTT_format::MY) { + if (mqtt_format_ == Settings::MQTT_format::CUSTOM) { char s[5]; doc[device.to_string()] = Helpers::render_value(s, device.temperature_c_, 2); } else { diff --git a/src/settings.h b/src/settings.h index 06412a97c..795c41319 100644 --- a/src/settings.h +++ b/src/settings.h @@ -48,8 +48,8 @@ #define EMSESP_DEFAULT_TX_MODE 1 #define EMSESP_DEFAULT_MQTT_ENABLED true #define EMSESP_DEFAULT_MQTT_BASE "home" -#define EMSESP_DEFAULT_MQTT_PORT 1884 -#define EMSESP_DEFAULT_MQTT_QOS 0 +#define EMSESP_DEFAULT_MQTT_PORT 1883 +#define EMSESP_DEFAULT_MQTT_QOS 1 #define EMSESP_DEFAULT_MQTT_RETAIN false #define EMSESP_DEFAULT_MQTT_FORMAT 2 // 2=nested #define EMSESP_DEFAULT_MQTT_HEARTBEAT true @@ -148,7 +148,7 @@ class Settings { uint8_t master_thermostat() const; void master_thermostat(const uint8_t & master_thermostat); - enum MQTT_format : uint8_t { SINGLE = 1, NESTED, HA, MY }; + enum MQTT_format : uint8_t { SINGLE = 1, NESTED, HA, CUSTOM }; uint8_t mqtt_format() const; void mqtt_format(const uint8_t & mqtt_format); diff --git a/src/solar.cpp b/src/solar.cpp index 254e17539..22c4add94 100644 --- a/src/solar.cpp +++ b/src/solar.cpp @@ -172,9 +172,9 @@ void Solar::process_SM100Status2(std::shared_ptr telegram) { * 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 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 + telegram->read_value32(energyLastHour_, 0); // last hour / 10 in Wh + telegram->read_value32(energyToday_, 4); // todays in Wh + telegram->read_value32(energyTotal_, 8); // total / 10 in kWh } /* diff --git a/src/telegram.cpp b/src/telegram.cpp index 2461da50d..8fd6bf852 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -104,13 +104,13 @@ std::string Telegram::to_string(const uint8_t * telegram, uint8_t length) const // if offset is 0, it takes the whole telegram. if it's for example 1 it'll show the 2nd data item and // everything after it // returns -1 if out of bounds -int8_t Telegram::_getDataPosition(const uint8_t index) const { - return ((index - offset) >= message_length) ? -1 : (index - offset); +int8_t Telegram::_getDataPosition(const uint8_t index, const uint8_t size) const { + return ((index - offset + size - 1) >= message_length) ? -1 : (index - offset); } // unsigned byte void Telegram::read_value(uint8_t & param, const uint8_t index) const { - int8_t pos = _getDataPosition(index); + int8_t pos = _getDataPosition(index, sizeof(param)); if (pos < 0) { return; } @@ -119,7 +119,7 @@ void Telegram::read_value(uint8_t & param, const uint8_t index) const { // signed byte void Telegram::read_value(int8_t & param, const uint8_t index) const { - int8_t pos = _getDataPosition(index); + int8_t pos = _getDataPosition(index, sizeof(param)); if (pos < 0) { return; } @@ -128,7 +128,7 @@ void Telegram::read_value(int8_t & param, const uint8_t index) const { // unsigned short void Telegram::read_value(uint16_t & param, const uint8_t index) const { - int8_t pos = _getDataPosition(index); + int8_t pos = _getDataPosition(index, sizeof(param)); if (pos < 0) { return; } @@ -145,7 +145,7 @@ void Telegram::read_value(uint16_t & param, const uint8_t index) const { // signed short void Telegram::read_value(int16_t & param, const uint8_t index) const { - int8_t pos = _getDataPosition(index); + int8_t pos = _getDataPosition(index, sizeof(param)); if (pos < 0) { return; } @@ -160,9 +160,9 @@ void Telegram::read_value(int16_t & param, const uint8_t index) const { param = value; } -// Long +// Long 24 bit void Telegram::read_value(uint32_t & param, const uint8_t index) const { - int8_t pos = _getDataPosition(index); + int8_t pos = _getDataPosition(index, 3); if (pos < 0) { return; } @@ -170,9 +170,19 @@ void Telegram::read_value(uint32_t & param, const uint8_t index) const { param = (uint32_t)((message_data[pos] << 16) + (message_data[pos + 1] << 8) + (message_data[pos + 2])); } +// Long 32 bit +void Telegram::read_value32(uint32_t & param, const uint8_t index) const { + int8_t pos = _getDataPosition(index, sizeof(param)); + if (pos < 0) { + return; + } + + param = (uint32_t)((message_data[pos] << 24) + (message_data[pos] << 16) + (message_data[pos + 1] << 8) + (message_data[pos + 2])); +} + // bit from an unsigned byte void Telegram::read_value(uint8_t & param, const uint8_t index, const uint8_t bit) const { - int8_t pos = _getDataPosition(index); + int8_t pos = _getDataPosition(index, sizeof(param)); if (pos < 0) { return; } @@ -182,7 +192,7 @@ void Telegram::read_value(uint8_t & param, const uint8_t index, const uint8_t bi // convert signed short to single 8 byte, for setpoint thermostat temperatures that don't store their temps in 2 bytes void Telegram::read_value8(int16_t & param, const uint8_t index) const { - int8_t pos = _getDataPosition(index); + int8_t pos = _getDataPosition(index, 1); if (pos < 0) { return; } @@ -365,7 +375,7 @@ void TxService::loop() { // sends a 1 byte poll which is our own device ID void TxService::send_poll() { - //OG_TRACE(F("Ack %02X"),ems_bus_id() ^ ems_mask()); + //LOG_TRACE(F("Ack %02X"),ems_bus_id() ^ ems_mask()); EMSuart::send_poll(ems_bus_id() ^ ems_mask()); } diff --git a/src/telegram.h b/src/telegram.h index 8b6fa661f..ef6129b21 100644 --- a/src/telegram.h +++ b/src/telegram.h @@ -76,6 +76,7 @@ class Telegram { void read_value(uint16_t & param, const uint8_t index) const; void read_value(uint32_t & param, const uint8_t index) const; + void read_value32(uint32_t & param, const uint8_t index) const; void read_value(uint8_t & param, const uint8_t index, const uint8_t bit) const; void read_value(uint8_t & param, const uint8_t index) const; void read_value(int16_t & param, const uint8_t index) const; @@ -83,7 +84,7 @@ class Telegram { void read_value(int8_t & param, const uint8_t index) const; private: - int8_t _getDataPosition(const uint8_t index) const; + int8_t _getDataPosition(const uint8_t index, const uint8_t size) const; }; class EMSbus { diff --git a/src/thermostat.cpp b/src/thermostat.cpp index e69ac5e82..bb57ff86d 100644 --- a/src/thermostat.cpp +++ b/src/thermostat.cpp @@ -135,6 +135,16 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i } else { LOG_DEBUG(F("Registering new thermostat with device ID 0x%02X"), device_id); } + + + // for the thermostat, go a query all the heating circuits. This is only done once. The automatic fetch will from now on + // only update the active heating circuits + for (uint8_t i = 0; i < monitor_typeids.size(); i++) { + EMSESP::send_read_request(set_typeids[i], device_id); + } + for (uint8_t i = 0; i < monitor_typeids.size(); i++) { + EMSESP::send_read_request(monitor_typeids[i], device_id); + } } // for the master thermostat initialize the MQTT subscribes @@ -221,6 +231,7 @@ void Thermostat::thermostat_cmd(const char * message) { LOG_DEBUG(F("MQTT error: payload %s, error %s"), message, error.c_str()); return; } + for (const auto & hc : heating_circuits_) { char hc_name[6], s[3]; // hc{1-4} strlcpy(hc_name, "hc", 6); @@ -230,7 +241,7 @@ void Thermostat::thermostat_cmd(const char * message) { std::string mode = doc[hc_name]["mode"]; // first check mode set_mode(mode, hc_num); } - if (float f = doc[hc_name]["temp"]) { + if (float f = doc[hc_name]["temp"]) { set_temperature(f, HeatingCircuit::Mode::AUTO, hc_num); } if (float f = doc[hc_name]["nighttemp"]) { @@ -255,10 +266,10 @@ void Thermostat::thermostat_cmd(const char * message) { set_temperature(f, HeatingCircuit::Mode::HOLIDAY, hc_num); } if (float f = doc[hc_name]["remotetemp"]) { - if(f > 100 || f < 0) { + if (f > 100 || f < 0) { hc->remotetemp = EMS_VALUE_SHORT_NOTSET; } else { - hc->remotetemp = (uint16_t) (f * 10); + hc->remotetemp = (uint16_t)(f * 10); } } } @@ -376,7 +387,7 @@ void Thermostat::thermostat_cmd_mode(const char * message) { set_mode(s, DEFAULT_HEATING_CIRCUIT); } -// this function is called post and telegram process call +// this function is called post the telegram handler function has been executed // we check if any of the thermostat values have changed and then republish if necessary bool Thermostat::updated_values() { // only publish on the master thermostat @@ -420,8 +431,9 @@ void Thermostat::publish_values() { JsonObject rootThermostat = doc.to(); JsonObject dataThermostat; - // optional, add external temp. I don't think anyone (except MichaelDvP) actually is interested in this - if ((flags == EMS_DEVICE_FLAG_RC35 || flags == EMS_DEVICE_FLAG_RC30_1) && (mqtt_format_ == Settings::MQTT_format::SINGLE || mqtt_format_ == Settings::MQTT_format::MY)) { + // add external temp + if ((flags == EMS_DEVICE_FLAG_RC35 || flags == EMS_DEVICE_FLAG_RC30_1) + && (mqtt_format_ == Settings::MQTT_format::SINGLE || mqtt_format_ == Settings::MQTT_format::CUSTOM)) { if (datetime_.size()) { rootThermostat["time"] = datetime_.c_str(); } @@ -434,15 +446,12 @@ void Thermostat::publish_values() { if (tempsensor2 != EMS_VALUE_USHORT_NOTSET) { rootThermostat["inttemp2"] = (float)tempsensor2 / 10; } - if (ibaCalIntTemperature != EMS_VALUE_INT_NOTSET) { rootThermostat["intoffset"] = (float)ibaCalIntTemperature / 2; } - if (ibaMinExtTemperature != EMS_VALUE_INT_NOTSET) { rootThermostat["minexttemp"] = (float)ibaMinExtTemperature; // min ext temp for heating curve, in deg. } - if (ibaBuildingType != EMS_VALUE_UINT_NOTSET) { if (ibaBuildingType == 0) { rootThermostat["building"] = "light"; @@ -495,6 +504,7 @@ void Thermostat::publish_values() { if (hc->setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) { dataThermostat["seltemp"] = Helpers::round2((float)hc->setpoint_roomTemp / setpoint_temp_divider); } + if (hc->curr_roomTemp != EMS_VALUE_SHORT_NOTSET && hc->curr_roomTemp != EMS_VALUE_USHORT_NOTSET) { dataThermostat["currtemp"] = Helpers::round2((float)hc->curr_roomTemp / curr_temp_divider); } @@ -525,7 +535,8 @@ void Thermostat::publish_values() { dataThermostat["designtemp"] = hc->designtemp; } - if (hc->mode != EMS_VALUE_UINT_NOTSET) { + // when using HA always send the mode otherwise it'll break the component/widget and report an error + if ((hc->mode != EMS_VALUE_UINT_NOTSET) || (mqtt_format_ == Settings::MQTT_format::HA)) { uint8_t hc_mode = hc->get_mode(flags); // if we're sending to HA the only valid mode types are heat, auto and off if (mqtt_format_ == Settings::MQTT_format::HA) { @@ -574,7 +585,7 @@ void Thermostat::publish_values() { Mqtt::publish("thermostat_data", doc); } else if (mqtt_format_ == Settings::MQTT_format::HA) { Mqtt::publish("homeassistant/climate/ems-esp/state", doc); - } else if (mqtt_format_ == Settings::MQTT_format::MY) { + } else if (mqtt_format_ == Settings::MQTT_format::CUSTOM) { Mqtt::publish("thermostat_data", doc); } } @@ -1239,7 +1250,6 @@ void Thermostat::set_mode(const uint8_t mode, const uint8_t hc_num) { write_command(set_typeids[hc->hc_num() - 1], offset, set_mode_value, validate_typeid); } - // Set the temperature of the thermostat void Thermostat::set_temperature(const float temperature, const uint8_t mode, const uint8_t hc_num) { if (can_write()) { diff --git a/src/thermostat.h b/src/thermostat.h index c22c2cd0f..e38c2a835 100644 --- a/src/thermostat.h +++ b/src/thermostat.h @@ -246,4 +246,4 @@ class Thermostat : public EMSdevice { } // namespace emsesp -#endif +#endif \ No newline at end of file diff --git a/src/uart/emsuart_esp32.cpp b/src/uart/emsuart_esp32.cpp index 3d993eb26..9d939c0f2 100644 --- a/src/uart/emsuart_esp32.cpp +++ b/src/uart/emsuart_esp32.cpp @@ -81,7 +81,7 @@ void EMSuart::start(uint8_t tx_mode) { restart(); return; } - tx_mode_ = tx_mode; + tx_mode_ = tx_mode; uart_config_t uart_config = { .baud_rate = EMSUART_BAUD, .data_bits = UART_DATA_8_BITS, @@ -89,18 +89,18 @@ void EMSuart::start(uint8_t tx_mode) { .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, }; - + ESP_ERROR_CHECK(uart_param_config(EMSUART_UART, &uart_config)); ESP_ERROR_CHECK(uart_set_pin(EMSUART_UART, EMSUART_TXPIN, EMSUART_RXPIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); EMS_UART.int_ena.val = 0; // disable all intr. EMS_UART.int_clr.val = 0xFFFFFFFF; // clear all intr. flags EMS_UART.idle_conf.tx_brk_num = 11; // breaklength 11 bit - EMS_UART.idle_conf.rx_idle_thrhd = 256; + EMS_UART.idle_conf.rx_idle_thrhd = 256; drop_next_rx = true; buf_handle = xRingbufferCreate(128, RINGBUF_TYPE_NOSPLIT); ESP_ERROR_CHECK(uart_isr_register(EMSUART_UART, emsuart_rx_intr_handler, NULL, ESP_INTR_FLAG_IRAM, &uart_handle)); xTaskCreate(emsuart_recvTask, "emsuart_recvTask", 2048, NULL, 12, NULL); - EMS_UART.int_ena.brk_det = 1; // activate only break + EMS_UART.int_ena.brk_det = 1; // activate only break } /* @@ -114,9 +114,9 @@ void EMSuart::stop() { * Restart Interrupt */ void EMSuart::restart() { - if (EMS_UART.int_raw.brk_det) { + if (EMS_UART.int_raw.brk_det) { EMS_UART.int_clr.brk_det = 1; // clear flag - drop_next_rx = true; // and drop first frame + drop_next_rx = true; // and drop first frame } EMS_UART.int_ena.brk_det = 1; // activate only break }; diff --git a/src/uart/emsuart_esp32.h b/src/uart/emsuart_esp32.h index 38df2c212..f627a6d33 100644 --- a/src/uart/emsuart_esp32.h +++ b/src/uart/emsuart_esp32.h @@ -16,6 +16,11 @@ * along with this program. If not, see . */ + +/* + * ESP32 UART port by @ArwedL and improved by @MichaelDvP. See https://github.com/proddy/EMS-ESP/issues/380 + */ + #ifndef EMSESP_EMSUART_H #define EMSESP_EMSUART_H @@ -31,7 +36,7 @@ #define EMSUART_UART UART_NUM_2 // on the ESP32 we're using UART2 #define EMS_UART UART2 // for intr setting -#define EMSUART_BAUD 9600 // uart baud rate for the EMS circuit +#define EMSUART_BAUD 9600 // uart baud rate for the EMS circuit // customize the GPIO pins for RX and TX here #define EMSUART_RXPIN 23 // 17 is UART2 RX. Use 23 for D7 on a Wemos D1-32 mini for backwards compatabilty diff --git a/src/uart/emsuart_esp8266.cpp b/src/uart/emsuart_esp8266.cpp index f1fc623c7..f9de8613e 100644 --- a/src/uart/emsuart_esp8266.cpp +++ b/src/uart/emsuart_esp8266.cpp @@ -38,8 +38,8 @@ bool drop_next_rx = true; // Important: must not use ICACHE_FLASH_ATTR // void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) { - static uint8_t length = 0; // static bool rx_idle_ = true; + static uint8_t length = 0; static uint8_t uart_buffer[EMS_MAXBUFFERSIZE + 2]; /* @@ -62,7 +62,7 @@ void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) { */ // BREAK detection = End of EMS data block if (USIS(EMSUART_UART) & ((1 << UIBD))) { - length = 0; + length = 0; while ((USS(EMSUART_UART) >> USRXC) & 0xFF) { uint8_t rx = USF(EMSUART_UART); if (length < EMS_MAXBUFFERSIZE) { @@ -77,7 +77,7 @@ void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) { if (!drop_next_rx) { pEMSRxBuf->length = length; os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, pEMSRxBuf->length); // copy data into transfer buffer, including the BRK 0x00 at the end - // rx_idle_ = true; // 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 } drop_next_rx = false; ETS_UART_INTR_ENABLE(); // re-enable UART interrupts diff --git a/src/uart/emsuart_esp8266.h b/src/uart/emsuart_esp8266.h index 810d1bcb1..ecf1c3574 100644 --- a/src/uart/emsuart_esp8266.h +++ b/src/uart/emsuart_esp8266.h @@ -36,7 +36,7 @@ #define EMS_TXMODE_DEFAULT 1 #define EMS_TXMODE_EMSPLUS 2 #define EMS_TXMODE_HT3 3 -#define EMS_TXMODE_NEW 4 // for michael +#define EMS_TXMODE_NEW 4 // for michael's testing // LEGACY #define EMSUART_BIT_TIME 104 // bit time @9600 baud diff --git a/src/version.h b/src/version.h index 0f71dbb50..4c1414bcc 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "2.0.0a10" +#define EMSESP_APP_VERSION "2.0.0a11"