diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index ac183af9c..031b74333 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -49,8 +49,9 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const LOG_DEBUG(F("Registering new Boiler with device ID 0x%02X"), device_id); // the telegram handlers... - register_telegram_type(0x10, F("UBAErrorMessage1"), false, nullptr); - register_telegram_type(0x11, F("UBAErrorMessage2"), false, nullptr); + 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(0x12, F("UBAErrorMessage3"), 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)); @@ -679,6 +680,12 @@ void Boiler::process_UBAMaintenanceStatus(std::shared_ptr telegr void Boiler::process_UBAMaintenanceSettings(std::shared_ptr telegram) { } +// 0x10, 0x11, 0x12 +// not yet implemented +void Boiler::process_UBAErrorMessage(std::shared_ptr telegram) { + // data: displaycode(2), errornumner(2), year, month, hour, day, minute, duration(2), src-addr +} + #pragma GCC diagnostic pop // Set the warm water temperature 0x33 diff --git a/src/devices/boiler.h b/src/devices/boiler.h index 3c15da307..9dc36c8c3 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -141,6 +141,7 @@ class Boiler : public EMSdevice { void process_MC10Status(std::shared_ptr telegram); void process_UBAMaintenanceStatus(std::shared_ptr telegram); void process_UBAMaintenanceSettings(std::shared_ptr telegram); + void process_UBAErrorMessage(std::shared_ptr telegram); void process_UBADHWStatus(std::shared_ptr telegram); void process_HPMonitor1(std::shared_ptr telegram); diff --git a/src/devices/mixing.cpp b/src/devices/mixing.cpp index c6a97080a..1ef7dd385 100644 --- a/src/devices/mixing.cpp +++ b/src/devices/mixing.cpp @@ -40,9 +40,9 @@ Mixing::Mixing(uint8_t device_type, uint8_t device_id, uint8_t product_id, const } // EMS 1.0 if (flags == EMSdevice::EMS_DEVICE_FLAG_MM10) { - register_telegram_type(0x00AA, F("MMConfigMessage"), false, nullptr); + 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, nullptr); + register_telegram_type(0x00AC, F("MMSetMessage"), false, std::bind(&Mixing::process_MMSetMessage, this, _1)); } // MQTT callbacks @@ -131,8 +131,9 @@ void Mixing::process_MMPLUSStatusMessage_HC(std::shared_ptr tele type_ = Type::HC; hc_ = telegram->type_id - 0x02D7 + 1; // determine which circuit this is telegram->read_value(flowTemp_, 3); // is * 10 - telegram->read_value(pumpMod_, 5); - telegram->read_value(status_, 2); // valve status + telegram->read_value(flowSetTemp_, 5); + telegram->read_value(pumpMod_, 2); + telegram->read_value(status_, 1); // valve status } // Mixing module warm water loading/DHW - 0x0331, 0x0332 @@ -161,4 +162,19 @@ void Mixing::process_MMStatusMessage(std::shared_ptr telegram) { telegram->read_value(flowSetTemp_, 0); } +// 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 telegram) { + hc_ = device_id() - 0x20 + 1; + // pos 0: active FF = on + // pos 1: valve runtime 0C = 120 sec in units of 10 sec +} +// 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 telegram) { + hc_ = device_id() - 0x20 + 1; + // pos 0: flowtemp setpoint 1E = 30°C + // pos 1: position in % +} + } // namespace emsesp \ No newline at end of file diff --git a/src/devices/mixing.h b/src/devices/mixing.h index d81443b7f..ca9e4308c 100644 --- a/src/devices/mixing.h +++ b/src/devices/mixing.h @@ -49,6 +49,8 @@ class Mixing : public EMSdevice { void process_MMPLUSStatusMessage_HC(std::shared_ptr telegram); void process_MMPLUSStatusMessage_WWC(std::shared_ptr telegram); void process_MMStatusMessage(std::shared_ptr telegram); + void process_MMConfigMessage(std::shared_ptr telegram); + void process_MMSetMessage(std::shared_ptr telegram); enum class Type { NONE, diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index 65a3b8af9..b0c349073 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -37,9 +37,9 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s 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)); + register_telegram_type(0x038E, F("SM100Energy"), true, std::bind(&Solar::process_SM100Energy, this, _1)); + 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)); // MQTT callbacks // register_mqtt_topic("cmd", std::bind(&Solar::cmd, this, _1)); @@ -130,10 +130,11 @@ void Solar::process_SM10Monitor(std::shared_ptr telegram) { 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); + telegram->read_value(pumpWorkMin_, 8); } /* - * SM100Monitor - type 0x0162 EMS+ - for SM100 and SM200 + * SM100Monitor - type 0x0262 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 diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index b5152bd50..f50e29927 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -31,7 +31,6 @@ MAKE_PSTR(master_thermostat_fmt, "Master Thermostat device ID = %s") namespace emsesp { REGISTER_FACTORY(Thermostat, EMSdevice::DeviceType::THERMOSTAT); - MAKE_PSTR(logger_name, "thermostat") uuid::log::Logger Thermostat::logger_{F_(logger_name), uuid::log::Facility::CONSOLE}; @@ -239,7 +238,6 @@ void Thermostat::thermostat_cmd(const char * message) { strlcpy(hc_name, "hc", 6); uint8_t hc_num = hc->hc_num(); strlcat(hc_name, Helpers::itoa(s, hc_num), 6); - if (nullptr != doc[hc_name]["mode"]) { std::string mode = doc[hc_name]["mode"]; // first check mode set_mode(mode, hc_num); @@ -279,29 +277,29 @@ void Thermostat::thermostat_cmd(const char * message) { uint8_t ctrl = doc[hc_name]["control"]; set_control(ctrl, hc_num); } - if (float ct = doc["calinttemp"]) { - set_settings_calinttemp((int8_t)(ct * 10)); - } - if (nullptr != doc["minexttemp"]) { - int8_t mt = doc["minexttemp"]; - set_settings_minexttemp(mt); - } - if (nullptr != doc["building"]) { - uint8_t bd = doc["building"]; - set_settings_building(bd); - } - if (nullptr != doc["language"]) { - uint8_t lg = doc["language"]; - set_settings_language(lg); - } - if (nullptr != doc["display"]) { - uint8_t dp = doc["display"]; - set_settings_display(dp); - } - if (nullptr != doc["clockoffset"]) { - int8_t co = doc["clockoffset"]; - set_settings_clockoffset(co); - } + } + if (float ct = doc["calinttemp"]) { + set_settings_calinttemp((int8_t)(ct * 10)); + } + if (nullptr != doc["minexttemp"]) { + int8_t mt = doc["minexttemp"]; + set_settings_minexttemp(mt); + } + if (nullptr != doc["building"]) { + uint8_t bd = doc["building"]; + set_settings_building(bd); + } + if (nullptr != doc["language"]) { + uint8_t lg = doc["language"]; + set_settings_language(lg); + } + if (nullptr != doc["display"]) { + uint8_t dp = doc["display"]; + set_settings_display(dp); + } + if (nullptr != doc["clockoffset"]) { + int8_t co = doc["clockoffset"]; + set_settings_clockoffset(co); } const char * command = doc["cmd"]; @@ -1023,17 +1021,17 @@ void Thermostat::process_JunkersMonitor(std::shared_ptr telegram // type 0x02A5 - data from the Nefit RC1010/3000 thermostat (0x18) and RC300/310s on 0x10 void Thermostat::process_RC300Monitor(std::shared_ptr telegram) { + if (telegram->message_data[2] == 0x00) { + return; + } std::shared_ptr hc = heating_circuit(telegram); - telegram->read_value(hc->curr_roomTemp, 0); // is * 10 telegram->read_value(hc->mode_type, 10, 1); telegram->read_value(hc->mode, 10, 0); // bit 1, mode (auto=1 or manual=0) // setpoint is in offset 3 and also 7. We're sticking to 3 for now. // also ignore if its 0 - see https://github.com/proddy/EMS-ESP/issues/256#issuecomment-585171426 - if (telegram->message_data[3] != 0) { - telegram->read_value8(hc->setpoint_roomTemp, 3); // is * 2, force as single byte - } + telegram->read_value8(hc->setpoint_roomTemp, 3); // is * 2, force as single byte } // type 0x02B9 EMS+ for reading from RC300/RC310 thermostat @@ -1044,11 +1042,12 @@ void Thermostat::process_RC300Set(std::shared_ptr telegram) { // manual is position 10 // comfort is position 2 // I think auto is position 8? - telegram->read_value8(hc->setpoint_roomTemp, 8); // single byte conversion, value is * 2 - auto? - telegram->read_value8(hc->setpoint_roomTemp, 10); // single byte conversion, value is * 2 - manual + // actual setpoint taken from RC300Monitor (Michael 12.06.2020) + // telegram->read_value8(hc->setpoint_roomTemp, 8); // single byte conversion, value is * 2 - auto? + // telegram->read_value8(hc->setpoint_roomTemp, 10); // single byte conversion, value is * 2 - manual // check why mode is both in the Monitor and Set for the RC300. It'll be read twice! - telegram->read_value(hc->mode, 0); // Auto = xFF, Manual = x00 eg. 10 00 FF 08 01 B9 FF + // telegram->read_value(hc->mode, 0); // Auto = xFF, Manual = x00 eg. 10 00 FF 08 01 B9 FF telegram->read_value(hc->daytemp, 2); // is * 2 telegram->read_value(hc->nighttemp, 4); // is * 2 @@ -1182,7 +1181,7 @@ void Thermostat::set_settings_building(const uint8_t bg) { // 0xA5 Set the language settings void Thermostat::set_settings_language(const uint8_t lg) { if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) { - LOG_INFO(F("Setting building to %d"), lg); + LOG_INFO(F("Setting language to %d"), lg); write_command(EMS_TYPE_IBASettings, 1, lg); } } @@ -1199,8 +1198,10 @@ void Thermostat::set_control(const uint8_t ctrl, const uint8_t hc_num) { return; } if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35 || (flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) { - LOG_INFO(F("Setting Circuit-control for hc%d to %d"), hc_num, ctrl); + LOG_INFO(F("Setting circuit-control for hc%d to %d"), hc_num, ctrl); write_command(set_typeids[hc->hc_num() - 1], 26, ctrl); + } else { + LOG_INFO(F("Setting circuit-control not possible")); } } diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index 65852f345..455418339 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -29,7 +29,6 @@ #include "emsesp.h" #include "helpers.h" #include "mqtt.h" -#include "roomcontrol.h" #include diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 422f92c21..4152eaf71 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -728,8 +728,8 @@ void EMSESP::console_commands(Shell & shell, unsigned int context) { flash_string_vector{F_(set), F_(tx_mode)}, flash_string_vector{F_(n_mandatory)}, [](Shell & shell, const std::vector & arguments) { - uint8_t tx_mode = (arguments[0]).at(0) - '0'; - if ((tx_mode > 0) && (tx_mode <= 4)) { + uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10); + if ((tx_mode > 0) && (tx_mode <= 30)) { Settings settings; settings.ems_tx_mode(tx_mode); settings.commit(); diff --git a/src/devices/helpers.cpp b/src/helpers.cpp similarity index 99% rename from src/devices/helpers.cpp rename to src/helpers.cpp index c0c176559..ae83cd0b6 100644 --- a/src/devices/helpers.cpp +++ b/src/helpers.cpp @@ -233,7 +233,7 @@ char * Helpers::render_value(char * result, const uint32_t value, const uint8_t strlcat(result, ltoa(value, s, 10), 20); } else { strlcat(result, ltoa(value / format, s, 10), 20); - strlcat(result, ".", 2); + strlcat(result, ".", 20); strlcat(result, ltoa(value % format, s, 10), 20); } diff --git a/src/roomcontrol.cpp b/src/roomcontrol.cpp index 12f08be15..e5af386dc 100644 --- a/src/roomcontrol.cpp +++ b/src/roomcontrol.cpp @@ -20,21 +20,23 @@ namespace emsesp { -uint32_t rc_time_ = 0; -uint16_t hc_ = EMS_VALUE_USHORT_NOTSET; -int16_t remotetemp[4] = {EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET}; +static uint32_t rc_time_ = 0; +static int16_t remotetemp[4] = {EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET, EMS_VALUE_SHORT_NOTSET}; /** * set the temperature, */ -void Roomctrl::set_remotetemp(uint8_t hc, int16_t temp) { +void Roomctrl::set_remotetemp(const uint8_t hc, const int16_t temp) { + if (hc > 3) { + return; + } remotetemp[hc] = temp; } /** * if remote control is active send the temperature every minute */ -void Roomctrl::send(uint8_t addr) { +void Roomctrl::send(const uint8_t addr) { uint8_t hc_ = addr - ADDR; // check address, reply only on addresses 0x18..0x1B if (hc_ > 3) { @@ -48,7 +50,7 @@ void Roomctrl::send(uint8_t addr) { rc_time_ = uuid::get_uptime(); // use EMS-ESP's millis() to prevent overhead temperature(addr, 0x00); // send to all } else { - // acknowledge every poll, otherwise the master shows error A11-822 + // acknowledge every poll, otherwise the master shows error A22-816 EMSuart::send_poll(addr); } } @@ -56,19 +58,22 @@ void Roomctrl::send(uint8_t addr) { /** * check if there is a message for the remote room controller */ -void Roomctrl::check(uint8_t addr, uint8_t * data) { - uint8_t hc_num = addr - ADDR; +void Roomctrl::check(const uint8_t addr, const uint8_t * data) { + uint8_t hc_ = (addr & 0x7F) - ADDR; // check address, reply only on addresses 0x18..0x1B - if (hc_num > 3) { + if (hc_ > 3) { return; } - // no reply if the temperature is not set - if (remotetemp[hc_num] == EMS_VALUE_SHORT_NOTSET) { + if (remotetemp[hc_] == EMS_VALUE_SHORT_NOTSET) { + return; + } + // reply to writes with write nack byte + if(addr & 0x80) { // it's a write to us + nack_write(); // we don't accept writes. return; } - // for now we only reply to version and remote temperature if (data[2] == 0x02) { version(addr, data[0]); @@ -124,5 +129,13 @@ void Roomctrl::temperature(uint8_t addr, uint8_t dst) { data[7] = EMSbus::calculate_crc(data, 7); // apppend CRC EMSuart::transmit(data, 8); } +/** + * send a nack if someone want to write to us. + */ +void Roomctrl::nack_write() { + uint8_t data[1]; + data[0] = TxService::TX_WRITE_FAIL; + EMSuart::transmit(data, 1); +} } // namespace emsesp diff --git a/src/roomcontrol.h b/src/roomcontrol.h index b4917173a..47f66654c 100644 --- a/src/roomcontrol.h +++ b/src/roomcontrol.h @@ -20,17 +20,14 @@ #define EMSESP_ROOMCONTROL_H #include "emsesp.h" -#include "telegram.h" -#include "uart/emsuart_esp8266.h" -#include "uart/emsuart_esp32.h" namespace emsesp { class Roomctrl { public: - static void send(uint8_t addr); - static void check(uint8_t addr, uint8_t * data); - static void set_remotetemp(uint8_t hc, int16_t temp); + static void send(const uint8_t addr); + static void check(const uint8_t addr, const uint8_t * data); + static void set_remotetemp(const uint8_t hc, const int16_t temp); private: static constexpr uint8_t ADDR = 0x18; @@ -39,6 +36,8 @@ class Roomctrl { static void version(uint8_t addr, uint8_t dst); static void unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset); static void temperature(uint8_t addr, uint8_t dst); + static void nack_write(); + }; } // namespace emsesp diff --git a/src/system.cpp b/src/system.cpp index 4b4c07aec..f21d7a668 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -62,23 +62,72 @@ void System::mqtt_commands(const char * message) { LOG_DEBUG(F("MQTT error: payload %s, error %s"), message, error.c_str()); return; } + + // restart EMS-ESP + if (strcmp(message, "restart") == 0) { + LOG_INFO(F("Restart command received")); + restart(); + } + + if (doc["send"] != nullptr) { + const char * data = doc["send"]; + EMSESP::send_raw_telegram(data); + LOG_INFO(F("Sending raw: %s"),data); + + } + +#if defined(ESP8266) + const uint8_t d0_ = 16; + const uint8_t d1_ = 5; + const uint8_t d2_ = 4; + const uint8_t d3_ = 0; +#elif defined(ESP32) + const uint8_t d0_ = 26; + const uint8_t d1_ = 22; + const uint8_t d2_ = 21; + const uint8_t d3_ = 17; +#endif + if(doc["D0"] != nullptr) { + const int8_t set = doc["D0"]; + pinMode(d0_, OUTPUT); + if (set == 1) digitalWrite(d0_, HIGH); + else if (set == 0) digitalWrite(d0_, LOW); + LOG_INFO(F("Port D0 set to %d"),set); + } + if(doc["D1"] != nullptr) { + const int8_t set = doc["D1"]; + pinMode(d1_, OUTPUT); + if (set == 1) digitalWrite(d1_, HIGH); + else if (set == 0) digitalWrite(d1_, LOW); + LOG_INFO(F("Port D1 set to %d"),set); + } + if(doc["D2"] != nullptr) { + const int8_t set = doc["D2"]; + pinMode(d2_, OUTPUT); + if (set == 1) digitalWrite(d2_, HIGH); + else if (set == 0) digitalWrite(d2_, LOW); + LOG_INFO(F("Port D2 set to %d"),set); + } + if(doc["D3"] != nullptr) { + const int8_t set = doc["D3"]; + pinMode(d3_, OUTPUT); + if (set == 1) digitalWrite(d3_, HIGH); + else if (set == 0) digitalWrite(d3_, LOW); + LOG_INFO(F("Port D3 set to %d"),set); + } + const char * command = doc["cmd"]; if (command == nullptr) { return; } - - // restart EMS-ESP - if (strcmp(message, "restart") == 0) { - restart(); - } - - // boiler ww comfort setting + // send raw command if (strcmp(command, "send") == 0) { const char * data = doc["data"]; if (data == nullptr) { return; } EMSESP::send_raw_telegram(data); + LOG_INFO(F("Sending raw: %s"),data); return; } } diff --git a/src/telegram.cpp b/src/telegram.cpp index 154b9e892..a393093bc 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -177,7 +177,7 @@ void Telegram::read_value32(uint32_t & param, const uint8_t index) const { return; } - param = (uint32_t)((message_data[pos] << 24) + (message_data[pos] << 16) + (message_data[pos + 1] << 8) + (message_data[pos + 2])); + param = (uint32_t)((message_data[pos] << 24) + (message_data[pos + 1] << 16) + (message_data[pos + 2] << 8) + (message_data[pos + 3])); } // bit from an unsigned byte @@ -290,7 +290,7 @@ void RxService::add(uint8_t * data, uint8_t length) { } } else { // its F9 or F7 - uint8_t shift = (data[4] != 0xFF); // true (1) if 5th byte is not 0xFF, then telegram is 1 byte longer + uint8_t shift = (data[4] != 0xFF) ? 1 : 0; // true (1) if 5th byte is not 0xFF, then telegram is 1 byte longer type_id = (data[5 + shift] << 8) + data[6 + shift] + 256; message_data += 6 + shift; // there is a special byte after the typeID which we ignore for now if (length > (9 + shift)) { @@ -468,7 +468,6 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) { // send the telegram to the UART Tx uint16_t status = EMSuart::transmit(telegram_raw, length); - #ifdef EMSESP_DEBUG // if watching in 'raw' mode if (EMSESP::watch() == 2) { @@ -614,6 +613,10 @@ void TxService::remember_tx(const uint8_t * data, const uint8_t length) { telegram_last_[i] = data[i]; } telegram_last_length_ = length; + if (ems_mask() != EMS_MASK_UNSET) { + telegram_last_[0] ^= ems_mask(); + } + } // add last Tx to tx queue and increment count diff --git a/src/uart/emsuart_esp32.cpp b/src/uart/emsuart_esp32.cpp index c2369217a..091363cec 100644 --- a/src/uart/emsuart_esp32.cpp +++ b/src/uart/emsuart_esp32.cpp @@ -30,8 +30,14 @@ namespace emsesp { static intr_handle_t uart_handle; static RingbufHandle_t buf_handle = NULL; -static bool drop_next_rx = true; -static uint8_t tx_mode_ = 0xFF; +static hw_timer_t * timer = NULL; +bool drop_next_rx = true; +uint8_t tx_mode_ = 0xFF; +//portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; +uint8_t emsTxBuf[EMS_MAXBUFFERSIZE]; +uint8_t emsTxBufIdx; +uint8_t emsTxBufLen; +uint32_t emsTxWait; /* * Task to handle the incoming data @@ -73,11 +79,39 @@ void IRAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) { drop_next_rx = false; } } + + +void IRAM_ATTR EMSuart::emsuart_tx_timer_intr_handler() { + if (emsTxBufIdx > 32) { + return; + } + emsTxBufIdx++; + if (emsTxBufIdx < emsTxBufLen) { + EMS_UART.fifo.rw_byte = emsTxBuf[emsTxBufIdx]; + timerAlarmWrite(timer, emsTxWait, false); + timerAlarmEnable(timer); + } else if (emsTxBufIdx == emsTxBufLen) { + EMS_UART.conf0.txd_brk = 1; // after send + } +} + /* * init UART driver */ void EMSuart::start(uint8_t tx_mode) { + if (tx_mode == EMS_TXMODE_DEFAULT) { + emsTxWait = EMSUART_BIT_TIME * 11; + } else if (tx_mode == EMS_TXMODE_EMSPLUS) { + emsTxWait = EMSUART_BIT_TIME * 20; + } else if (tx_mode == EMS_TXMODE_HT3) { + emsTxWait = EMSUART_BIT_TIME * 17; + } else if(tx_mode > 10 ) { + emsTxWait = EMSUART_BIT_TIME * tx_mode; + } else if(tx_mode > 5 ) { + emsTxWait = EMSUART_BIT_TIME * tx_mode * 2; + } if (tx_mode_ != 0xFF) { // uart already initialized + tx_mode_ = tx_mode; restart(); return; } @@ -101,6 +135,11 @@ void EMSuart::start(uint8_t tx_mode) { 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, configMAX_PRIORITIES - 1, NULL); EMS_UART.int_ena.brk_det = 1; // activate only break + + emsTxBufIdx = 0; + emsTxBufLen = 0; + timer = timerBegin(1, 80, true); // timer prescale to 1 µs, countup + timerAttachInterrupt(timer, &emsuart_tx_timer_intr_handler, true); // Timer with edge interrupt } /* @@ -108,6 +147,7 @@ void EMSuart::start(uint8_t tx_mode) { */ void EMSuart::stop() { EMS_UART.int_ena.val = 0; // disable all intr. + // timerAlarmDisable(timer); }; /* @@ -119,14 +159,24 @@ void EMSuart::restart() { drop_next_rx = true; // and drop first frame } EMS_UART.int_ena.brk_det = 1; // activate only break + emsTxBufIdx = 0; + emsTxBufLen = 0; }; /* * Sends a 1-byte poll, ending with a */ void EMSuart::send_poll(uint8_t data) { - EMS_UART.fifo.rw_byte = data; - EMS_UART.conf0.txd_brk = 1; // after send + if (tx_mode_ == EMS_TXMODE_NEW || tx_mode_ == 5) { + EMS_UART.fifo.rw_byte = data; + EMS_UART.conf0.txd_brk = 1; // after send + } else { + EMS_UART.fifo.rw_byte = data; + emsTxBufIdx = 0; + emsTxBufLen = 1; + timerAlarmWrite(timer, emsTxWait, false); + timerAlarmEnable(timer); + } } /* @@ -134,12 +184,24 @@ void EMSuart::send_poll(uint8_t data) { * buf contains the CRC and len is #bytes including the CRC * returns code, 1=success */ -EMSUART_STATUS EMSuart::transmit(uint8_t * buf, uint8_t len) { - if (len > 0) { +uint16_t EMSuart::transmit(uint8_t * buf, uint8_t len) { + if (len == 0 || len > 32) { + return EMS_TX_STATUS_ERROR; + } + if (tx_mode_ == EMS_TXMODE_NEW || tx_mode_ == 5) { for (uint8_t i = 0; i < len; i++) { EMS_UART.fifo.rw_byte = buf[i]; } EMS_UART.conf0.txd_brk = 1; // after send + } else { + for (uint8_t i = 0; i < len; i++) { + emsTxBuf[i] = buf[i]; + } + EMS_UART.fifo.rw_byte = buf[0]; + emsTxBufIdx = 0; + emsTxBufLen = len; + timerAlarmWrite(timer, emsTxWait, false); + timerAlarmEnable(timer); } return EMS_TX_STATUS_OK; } diff --git a/src/uart/emsuart_esp32.h b/src/uart/emsuart_esp32.h index 4a2a9e289..4e958469e 100644 --- a/src/uart/emsuart_esp32.h +++ b/src/uart/emsuart_esp32.h @@ -20,6 +20,7 @@ /* * ESP32 UART port by @ArwedL and improved by @MichaelDvP. See https://github.com/proddy/EMS-ESP/issues/380 */ +#if defined(ESP32) #ifndef EMSESP_EMSUART_H #define EMSESP_EMSUART_H @@ -37,6 +38,13 @@ #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_BIT_TIME 104 // bit time @9600 baud + +#define EMS_TXMODE_DEFAULT 1 +#define EMS_TXMODE_EMSPLUS 2 +#define EMS_TXMODE_HT3 3 +#define EMS_TXMODE_NEW 4 // for michael's testing + // customize the GPIO pins for RX and TX here #ifdef WEMOS_D1_32 @@ -49,11 +57,8 @@ namespace emsesp { -typedef enum { - EMS_TX_STATUS_OK = 1, - EMS_TX_WTD_TIMEOUT, // watchdog timeout during send - EMS_TX_BRK_DETECT, // incoming BRK during Tx -} EMSUART_STATUS; +#define EMS_TX_STATUS_OK 1 +#define EMS_TX_STATUS_ERROR 0 class EMSuart { public: @@ -64,13 +69,16 @@ class EMSuart { static void send_poll(uint8_t data); static void stop(); static void restart(); - static EMSUART_STATUS transmit(uint8_t * buf, uint8_t len); + static uint16_t transmit(uint8_t * buf, uint8_t len); private: static void emsuart_recvTask(void * para); static void IRAM_ATTR emsuart_rx_intr_handler(void * para); + static void IRAM_ATTR emsuart_tx_timer_intr_handler(); + }; } // namespace emsesp #endif +#endif diff --git a/src/uart/emsuart_esp8266.cpp b/src/uart/emsuart_esp8266.cpp index c3b81a297..d77a77de3 100644 --- a/src/uart/emsuart_esp8266.cpp +++ b/src/uart/emsuart_esp8266.cpp @@ -51,10 +51,8 @@ void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) { if (USIS(EMSUART_UART) & ((1 << UIBD))) { // BREAK detection = End of EMS data block USC0(EMSUART_UART) &= ~(1 << UCBRK); // reset tx-brk + // just for testing if break isn't finished yet // while((USS(EMSUART_UART) >> USRXD) == 0); // wait for idle state of pin - // if((USS(EMSUART_UART) >> USRXD) == 0) { // if rx is not idle wait one bittime - // delayMicroseconds(EMSUART_TX_BIT_TIME); - // } USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt length = 0; while ((USS(EMSUART_UART) >> USRXC) & 0x0FF) { // read fifo into buffer @@ -116,23 +114,40 @@ void ICACHE_FLASH_ATTR EMSuart::emsuart_flush_fifos() { // ISR to Fire when Timer is triggered void ICACHE_RAM_ATTR EMSuart::emsuart_tx_timer_intr_handler() { + if (emsTxBufIdx > 32) { + return; + } emsTxBufIdx++; if (emsTxBufIdx < emsTxBufLen) { USF(EMSUART_UART) = emsTxBuf[emsTxBufIdx]; timer1_write(emsTxWait); } else if (emsTxBufIdx == emsTxBufLen) { USC0(EMSUART_UART) |= (1 << UCBRK); // set - // timer1_write(emsTxWait); - timer1_write(5 * EMSUART_TX_BRK_WAIT); - } else { + if (tx_mode_ > 5 || tx_mode_ < 11) { + timer1_write(5 * EMSUART_TX_BIT_TIME * 11); + USIE(EMSUART_UART) &= ~(1 << UIBD); // disable break interrupt + } + } else if (USC0(EMSUART_UART) & (1 << UCBRK)) { USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear + USIE(EMSUART_UART) |= (1 << UIBD); // enable break interrupt } } /* * init UART0 driver */ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) { - if (tx_mode_ != 0xFF) { // it's a restart no need to configure rx + if (tx_mode > 10) { + emsTxWait = 5 * EMSUART_TX_BIT_TIME * tx_mode; // bittimes for tx_mode + } else if (tx_mode > 5) { + emsTxWait = 10 * EMSUART_TX_BIT_TIME * tx_mode; // bittimes for tx_mode + } + if (tx_mode == 5) { + USC0(EMSUART_UART) = 0x2C; // 8N1,5 + } else { + USC0(EMSUART_UART) = EMSUART_CONFIG; // 8N1 + } + + if (tx_mode_ != 0xFF) { // it's a restart no need to configure uart tx_mode_ = tx_mode; restart(); return; @@ -141,6 +156,7 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) { // allocate and preset EMS Receive buffers for (int i = 0; i < EMS_MAXBUFFERS; i++) { EMSRxBuf_t * p = (EMSRxBuf_t *)malloc(sizeof(EMSRxBuf_t)); + p->length = 0; paEMSRxBuf[i] = p; } pEMSRxBuf = paEMSRxBuf[0]; // reset EMS Rx Buffer @@ -156,7 +172,6 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) { // set 9600, 8 bits, no parity check, 1 stop bit USD(EMSUART_UART) = (UART_CLK_FREQ / EMSUART_BAUD); - USC0(EMSUART_UART) = EMSUART_CONFIG; // 8N1 emsuart_flush_fifos(); @@ -169,7 +184,6 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) { // change: we set UCFFT to 1 to get an immediate indicator about incoming traffic. // Otherwise, we're only noticed by UCTOT or RxBRK! // change: don't care, we do not use these interrupts - // change proddy 13-june-2020: add back USC1(EMSUART_UART) = 0; USC1(EMSUART_UART) = 0; // reset config first // USC1(EMSUART_UART) = (0x7F << UCFFT) | (0x04 << UCTOT) | (1 << UCTOE); // enable interupts @@ -200,9 +214,6 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) { // for sending with large delay in EMS+ mode we use a timer interrupt timer1_attachInterrupt(emsuart_tx_timer_intr_handler); // Add ISR Function timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE); // 5 MHz timer - - // emsTxWait = 5 * EMSUART_TX_BIT_TIME * 20; // 20 bittimes for tx_mode 2 - emsTxWait = 5 * EMSUART_TX_BIT_TIME * 11; // 20 bittimes for tx_mode 2 } /* @@ -233,20 +244,19 @@ void ICACHE_FLASH_ATTR EMSuart::restart() { * Which is a 11-bit set of zero's (11 cycles) */ void ICACHE_FLASH_ATTR EMSuart::tx_brk() { - uint32_t tmp; // must make sure Tx FIFO is empty while (((USS(EMSUART_UART) >> USTXC) & 0xFF)) ; - - tmp = ((1 << UCRXRST) | (1 << UCTXRST)); // bit mask - USC0(EMSUART_UART) |= (tmp); // set bits - USC0(EMSUART_UART) &= ~(tmp); // clear bits + // do not clear buffers to get a echo back + // tmp = ((1 << UCRXRST) | (1 << UCTXRST)); // bit mask + // USC0(EMSUART_UART) |= (tmp); // set bits + // USC0(EMSUART_UART) &= ~(tmp); // clear bits // To create a 11-bit we set TXD_BRK bit so the break signal will // automatically be sent when the tx fifo is empty - tmp = (1 << UCBRK); - USC0(EMSUART_UART) |= (tmp); // set bit + ETS_UART_INTR_DISABLE(); + USC0(EMSUART_UART) |= (1 << UCBRK); // set bit if (tx_mode_ == EMS_TXMODE_EMSPLUS) { // EMS+ mode delayMicroseconds(EMSUART_TX_BRK_WAIT); // 2070 @@ -254,7 +264,8 @@ void ICACHE_FLASH_ATTR EMSuart::tx_brk() { delayMicroseconds(EMSUART_TX_BRK_WAIT_HT3); // 1144 } - USC0(EMSUART_UART) &= ~(tmp); // clear BRK bit + USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear BRK bit + ETS_UART_INTR_ENABLE(); } /* @@ -262,17 +273,18 @@ void ICACHE_FLASH_ATTR EMSuart::tx_brk() { * It's a bit dirty. there is no special wait logic per tx_mode type, fifo flushes or error checking */ void EMSuart::send_poll(uint8_t data) { - if (tx_mode_ == EMS_TXMODE_NEW) { - USC0(EMSUART_UART) &= ~(1 << UCBRK); // reset tx-brk + // reset tx-brk, just in case it is accidently set + USC0(EMSUART_UART) &= ~(1 << UCBRK); + if (tx_mode_ > 5) { // timer controlled modes + USF(EMSUART_UART) = data; + emsTxBufIdx = 0; + emsTxBufLen = 1; + timer1_write(emsTxWait); + } else if (tx_mode_ >= EMS_TXMODE_NEW) { // hardware controlled modes USF(EMSUART_UART) = data; USC0(EMSUART_UART) |= (1 << UCBRK); // send at the end - } else if (tx_mode_ == EMS_TXMODE_EMSPLUS) { - USF(EMSUART_UART) = data; - emsTxBufIdx = 0; - emsTxBufLen = 1; - timer1_write(emsTxWait); - } else { - // EMS1.0 and HT3 + } else { // software controlled modes + // EMS1.0, EMS+ and HT3 USF(EMSUART_UART) = data; delayMicroseconds(EMSUART_TX_BRK_WAIT); tx_brk(); // send @@ -285,8 +297,8 @@ void EMSuart::send_poll(uint8_t data) { * returns code, 0=success, 1=brk error, 2=watchdog timeout */ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) { - if (len == 0) { - return EMS_TX_STATUS_ERR; // nothing to send + if (len == 0 || len > 32) { + return EMS_TX_STATUS_ERR; // nothing or to much to send } #ifdef EMSESP_DEBUG // LOG_INFO(F("[DEBUG] UART Response time: %d ms"), uuid::get_uptime() - emsRxTime); @@ -294,10 +306,23 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) { // if ((uuid::get_uptime() - emsRxTime) > EMS_RX_TO_TX_TIMEOUT)) { // send allowed within 20 ms // return EMS_TX_STATUS_ERR; // } + // reset tx-brk, just in case it is accidently set + USC0(EMSUART_UART) &= ~(1 << UCBRK); + + // timer controlled modes with extra delay + if (tx_mode_ > 5) { + for (uint8_t i = 0; i < len; i++) { + emsTxBuf[i] = buf[i]; + } + emsTxBufIdx = 0; + emsTxBufLen = len; + USF(EMSUART_UART) = buf[0]; + timer1_write(emsTxWait); + return EMS_TX_STATUS_OK; + } // new code from Michael. See https://github.com/proddy/EMS-ESP/issues/380 - if (tx_mode_ == EMS_TXMODE_NEW) { - USC0(EMSUART_UART) &= ~(1 << UCBRK); // reset tx-brk + if (tx_mode_ >= EMS_TXMODE_NEW) { for (uint8_t i = 0; i < len; i++) { USF(EMSUART_UART) = buf[i]; } @@ -308,15 +333,10 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) { // EMS+ https://github.com/proddy/EMS-ESP/issues/23# if (tx_mode_ == EMS_TXMODE_EMSPLUS) { // With extra tx delay for EMS+ for (uint8_t i = 0; i < len; i++) { - emsTxBuf[i] = buf[i]; - // USF(EMSUART_UART) = buf[i]; - // delayMicroseconds(EMSUART_TX_WAIT_EMSPLUS); // 2070 + USF(EMSUART_UART) = buf[i]; + delayMicroseconds(EMSUART_TX_BRK_WAIT); // 2070 } - emsTxBufIdx = 0; - emsTxBufLen = len; - USF(EMSUART_UART) = buf[0]; - timer1_write(emsTxWait); - // tx_brk(); // send + tx_brk(); // send return EMS_TX_STATUS_OK; } @@ -352,7 +372,7 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) { * 2. Busmaster cancel telegram by sending a BRK * * Case 1. is handled by a watchdog counter which is reset on each - * Tx attempt. The timeout should be 20x EMSUART_TX_BIT_TIME plus + * Tx attempt. The timeout should be 20x EMSUART_TX_BIT_TIME plus * some smart guess for processing time on targeted EMS device. * We set Status to EMS_TX_WTD_TIMEOUT and return * @@ -385,16 +405,18 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) { // neither bus collision nor timeout - send terminating BRK signal if (!(USIS(EMSUART_UART) & (1 << UIBD))) { // no bus collision - send terminating BRK signal - USC0(EMSUART_UART) |= (1 << UCLBE) | (1 << UCBRK); // enable loopback & set + // USC0(EMSUART_UART) |= (1 << UCLBE) | (1 << UCBRK); // enable loopback & set + USC0(EMSUART_UART) |= (1 << UCBRK); // set // wait until BRK detected... while (!(USIR(EMSUART_UART) & (1 << UIBD))) { delayMicroseconds(EMSUART_TX_BIT_TIME); } - USC0(EMSUART_UART) &= ~((1 << UCBRK) | (1 << UCLBE)); // disable loopback & clear - USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ - phantomBreak = 1; + USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear + // USC0(EMSUART_UART) &= ~((1 << UCBRK) | (1 << UCLBE)); // disable loopback & clear + // USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ + // phantomBreak = 1; } // interrupts(); @@ -405,4 +427,4 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) { } // namespace emsesp -#endif \ No newline at end of file +#endif diff --git a/src/uart/emsuart_esp8266.h b/src/uart/emsuart_esp8266.h index e66912b13..26ca2478c 100644 --- a/src/uart/emsuart_esp8266.h +++ b/src/uart/emsuart_esp8266.h @@ -26,7 +26,7 @@ #include #define EMSUART_UART 0 // UART 0 -#define EMSUART_CONFIG 0x1C // 8N1 (8 bits, no stop bits, 1 parity) +#define EMSUART_CONFIG 0x1C // 8N1 (8 bits, no parity, 1 stop bit) #define EMSUART_BAUD 9600 // uart baud rate for the EMS circuit #define EMS_MAXBUFFERS 3 // buffers for circular filling to avoid collisions @@ -84,4 +84,4 @@ class EMSuart { } // namespace emsesp #endif -#endif \ No newline at end of file +#endif