From cfeb1ce2916af2605b4c54602936904cc123235e Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 30 May 2020 14:13:24 +0200 Subject: [PATCH] michael's updates --- src/emsdevice.cpp | 2 +- src/emsesp.cpp | 7 +- src/emsesp.h | 3 +- src/settings.h | 2 +- src/thermostat.cpp | 181 +++++++++++++++++++++++++++++------ src/thermostat.h | 15 ++- src/uart/emsuart_esp32.cpp | 84 +++++++++------- src/uart/emsuart_esp32.h | 16 +++- src/uart/emsuart_esp8266.cpp | 74 +++++++++----- src/uart/emsuart_esp8266.h | 4 +- src/version.h | 2 +- 11 files changed, 280 insertions(+), 110 deletions(-) diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 1a5a27942..567b12c4f 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -228,7 +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) != 0xF0))) { return uuid::read_flash_string(tf.telegram_type_name_); } } diff --git a/src/emsesp.cpp b/src/emsesp.cpp index f7d9cb2b2..fe6fdb95e 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) { @@ -422,7 +417,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/settings.h b/src/settings.h index a8ccd07cb..795c41319 100644 --- a/src/settings.h +++ b/src/settings.h @@ -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 }; + 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/thermostat.cpp b/src/thermostat.cpp index defd87f7a..3314741c0 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 @@ -222,6 +232,48 @@ void Thermostat::thermostat_cmd(const char * message) { return; } + for (const auto & hc : heating_circuits_) { + char hc_name[6], s[3]; // hc{1-4} + 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); + } + if (float f = doc[hc_name]["temp"]) { + set_temperature(f, HeatingCircuit::Mode::AUTO, hc_num); + } + if (float f = doc[hc_name]["nighttemp"]) { + set_temperature(f, HeatingCircuit::Mode::NIGHT, hc_num); + } + if (float f = doc[hc_name]["daytemp"]) { + set_temperature(f, HeatingCircuit::Mode::DAY, hc_num); + } + if (float f = doc[hc_name]["nofrosttemp"]) { + set_temperature(f, HeatingCircuit::Mode::NOFROST, hc_num); + } + if (float f = doc[hc_name]["summertemp"]) { + set_temperature(f, HeatingCircuit::Mode::SUMMER, hc_num); + } + if (float f = doc[hc_name]["designtemp"]) { + set_temperature(f, HeatingCircuit::Mode::DESIGN, hc_num); + } + if (float f = doc[hc_name]["offsettemp"]) { + set_temperature(f, HeatingCircuit::Mode::OFFSET, hc_num); + } + if (float f = doc[hc_name]["holidaytemp"]) { // + set_temperature(f, HeatingCircuit::Mode::HOLIDAY, hc_num); + } + if (float f = doc[hc_name]["remotetemp"]) { + if (f > 100 || f < 0) { + hc->remotetemp = EMS_VALUE_SHORT_NOTSET; + } else { + hc->remotetemp = (uint16_t)(f * 10); + } + } + } + const char * command = doc["cmd"]; if (command == nullptr) { return; @@ -301,6 +353,27 @@ void Thermostat::thermostat_cmd(const char * message) { } return; } + if (strcmp(command, "summertemp") == 0) { + float f = doc["data"]; + if (f) { + set_temperature(f, HeatingCircuit::Mode::SUMMER, hc_num); + } + return; + } + if (strcmp(command, "designtemp") == 0) { + float f = doc["data"]; + if (f) { + set_temperature(f, HeatingCircuit::Mode::DESIGN, hc_num); + } + return; + } + if (strcmp(command, "offettemp") == 0) { + float f = doc["data"]; + if (f) { + set_temperature(f, HeatingCircuit::Mode::OFFSET, hc_num); + } + return; + } } void Thermostat::thermostat_cmd_temp(const char * message) { @@ -314,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 @@ -357,16 +430,43 @@ void Thermostat::publish_values() { JsonObject rootThermostat = doc.to(); JsonObject dataThermostat; - // optional, add external temp. I don't think anyone actually is interested in this - if ((flags == EMS_DEVICE_FLAG_RC35) && (mqtt_format_ == Settings::MQTT_format::SINGLE)) { + // 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(); + } if (dampedoutdoortemp != EMS_VALUE_INT_NOTSET) { - doc["dampedtemp"] = dampedoutdoortemp; + rootThermostat["dampedtemp"] = dampedoutdoortemp; } if (tempsensor1 != EMS_VALUE_USHORT_NOTSET) { - doc["tempsensor1"] = (float)tempsensor1 / 10; + rootThermostat["inttemp1"] = (float)tempsensor1 / 10; } if (tempsensor2 != EMS_VALUE_USHORT_NOTSET) { - doc["tempsensor1"] = (float)tempsensor2 / 10; + 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"; + } else if (ibaBuildingType == 1) { + rootThermostat["building"] = "medium"; + } else if (ibaBuildingType == 2) { + rootThermostat["building"] = "heavy"; + } + } + + if (mqtt_format_ == Settings::MQTT_format::SINGLE) { + Mqtt::publish("thermostat_data", doc); + rootThermostat = doc.to(); // clear object } } @@ -436,7 +536,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) { @@ -465,14 +566,14 @@ void Thermostat::publish_values() { dataThermostat["modetype"] = mode_tostring(hc->get_mode_type(flags)); } - // if format is single, send immediately and quit + // if format is single, send immediately and clear object for next hc if (mqtt_format_ == Settings::MQTT_format::SINGLE) { char topic[30]; char s[3]; // for formatting strings strlcpy(topic, "thermostat_data", 30); strlcat(topic, Helpers::itoa(s, hc->hc_num()), 30); // append hc to topic Mqtt::publish(topic, doc); - return; + rootThermostat = doc.to(); // clear object } } @@ -654,6 +755,15 @@ std::string Thermostat::mode_tostring(uint8_t mode) const { case HeatingCircuit::Mode::AUTO: return read_flash_string(F("auto")); break; + case HeatingCircuit::Mode::SUMMER: + return read_flash_string(F("summer")); + break; + case HeatingCircuit::Mode::OFFSET: + return read_flash_string(F("offset")); + break; + case HeatingCircuit::Mode::DESIGN: + return read_flash_string(F("design")); + break; default: case HeatingCircuit::Mode::UNKNOWN: return read_flash_string(F("unknown")); @@ -860,19 +970,14 @@ void Thermostat::process_EasyMonitor(std::shared_ptr telegram) { // Settings Parameters - 0xA5 - RC30_1 void Thermostat::process_IBASettings(std::shared_ptr telegram) { - uint8_t extTemp = 100; // Min. ext temperature is coded as int8, 0xF6=-10, 0x0 = 0, 0xFF=-1. 100 is out of permissible range - + // 22 - display line on RC35 telegram->read_value(ibaMainDisplay, 0); // display on Thermostat: 0 int. temp, 1 int. setpoint, 2 ext. temp., 3 burner temp., 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp - telegram->read_value(ibaLanguage, 6); // language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian - telegram->read_value(ibaBuildingType, 2); // building type: 0 = light, 1 = medium, 2 = heavy + telegram->read_value(ibaLanguage, 1); // language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian telegram->read_value(ibaCalIntTemperature, 2); // offset int. temperature sensor, by * 0.1 Kelvin - telegram->read_value(extTemp, 5); // min ext temp for heating curve, in deg., 0xF6=-10, 0x0 = 0, 0xFF=-1 - if (extTemp != 100) { - // code as signed short, to benefit from negative value rendering - ibaMinExtTemperature = (int16_t)(extTemp > 127) ? (extTemp - 256) : extTemp; - } - telegram->read_value(ibaClockOffset, 12); // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s + telegram->read_value(ibaBuildingType, 6); // building type: 0 = light, 1 = medium, 2 = heavy + telegram->read_value(ibaMinExtTemperature, 5); // min ext temp for heating curve, in deg., 0xF6=-10, 0x0 = 0, 0xFF=-1 + telegram->read_value(ibaClockOffset, 12); // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s } // type 0x6F - FR10/FR50/FR100 Junkers @@ -936,7 +1041,7 @@ void Thermostat::process_RC30Set(std::shared_ptr telegram) { // type 0x3E (HC1), 0x48 (HC2), 0x52 (HC3), 0x5C (HC4) - data from the RC35 thermostat (0x10) - 16 bytes void Thermostat::process_RC35Monitor(std::shared_ptr telegram) { // exit if the 15th byte (second from last) is 0x00, which I think is calculated flow setpoint temperature - // with weather controlled RC35s this value can be zero and our setpoint temps will be incorrect + // with weather controlled RC35s this value is >=5, otherwise can be zero and our setpoint temps will be incorrect // see https://github.com/proddy/EMS-ESP/issues/373#issuecomment-627907301 if (telegram->message_data[14] == 0x00) { return; @@ -944,11 +1049,7 @@ void Thermostat::process_RC35Monitor(std::shared_ptr telegram) { std::shared_ptr hc = heating_circuit(telegram); - // ignore if the value is 0 (see https://github.com/proddy/EMS-ESP/commit/ccc30738c00f12ae6c89177113bd15af9826b836) - if (telegram->message_data[2] != 0x00) { - telegram->read_value8(hc->setpoint_roomTemp, 2); // is * 2, force to single byte - } - + telegram->read_value8(hc->setpoint_roomTemp, 2); // is * 2, force to single byte, is 0 in summermode telegram->read_value(hc->curr_roomTemp, 3); // is * 10 - or 0x7D00 if thermostat is mounted on boiler telegram->read_value(hc->mode_type, 1, 1); telegram->read_value(hc->summer_mode, 1, 0); @@ -970,10 +1071,12 @@ void Thermostat::process_RC35Set(std::shared_ptr telegram) { telegram->read_value(hc->daytemp, 2); // is * 2 telegram->read_value(hc->nighttemp, 1); // is * 2 telegram->read_value(hc->holidaytemp, 3); // is * 2 - telegram->read_value(hc->heatingtype, 0); // byte 0 bit floor heating = 3 + telegram->read_value(hc->heatingtype, 0); // 0- off, 1-radiator, 2-convector, 3-floor - telegram->read_value(hc->designtemp, 17); - telegram->read_value(hc->offsettemp, 6); + telegram->read_value(hc->summertemp, 22); // is * 1 + telegram->read_value(hc->nofrosttemp, 23); // is * 1 + telegram->read_value(hc->designtemp, 17); // is * 1 + telegram->read_value(hc->offsettemp, 6); // is * 2 } // process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long @@ -1144,7 +1247,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()) { @@ -1201,10 +1303,29 @@ void Thermostat::set_temperature(const float temperature, const uint8_t mode, co case HeatingCircuit::Mode::HOLIDAY: // change the holiday temp offset = EMS_OFFSET_RC35Set_temp_holiday; break; + case HeatingCircuit::Mode::OFFSET: // change the offset temp + offset = EMS_OFFSET_RC35Set_temp_offset; + break; + case HeatingCircuit::Mode::DESIGN: + offset = EMS_OFFSET_RC35Set_temp_design; + break; + case HeatingCircuit::Mode::SUMMER: + offset = EMS_OFFSET_RC35Set_temp_summer; + break; + case HeatingCircuit::Mode::NOFROST: + offset = EMS_OFFSET_RC35Set_temp_nofrost; + break; default: case HeatingCircuit::Mode::AUTO: // automatic selection, if no type is defined, we use the standard code if (model == EMS_DEVICE_FLAG_RC35) { - offset = EMS_OFFSET_RC35Set_seltemp; // https://github.com/proddy/EMS-ESP/issues/310 + uint8_t mode_ = hc->get_mode(flags()); + if (mode_ == HeatingCircuit::Mode::NIGHT) { + offset = EMS_OFFSET_RC35Set_temp_night; + } else if (mode_ == HeatingCircuit::Mode::DAY) { + offset = EMS_OFFSET_RC35Set_temp_day; + } else { + offset = EMS_OFFSET_RC35Set_seltemp; // https://github.com/proddy/EMS-ESP/issues/310 + } } else { uint8_t mode_type = hc->get_mode_type(flags()); offset = (mode_type == HeatingCircuit::Mode::NIGHT) ? EMS_OFFSET_RC35Set_temp_night : EMS_OFFSET_RC35Set_temp_day; diff --git a/src/thermostat.h b/src/thermostat.h index 9ad8c17ab..e38c2a835 100644 --- a/src/thermostat.h +++ b/src/thermostat.h @@ -57,9 +57,12 @@ class Thermostat : public EMSdevice { uint8_t holidaytemp = EMS_VALUE_UINT_NOTSET; uint8_t heatingtype = EMS_VALUE_UINT_NOTSET; // type of heating: 1 radiator, 2 convectors, 3 floors, 4 room supply uint8_t circuitcalctemp = EMS_VALUE_UINT_NOTSET; + uint8_t summertemp = EMS_VALUE_UINT_NOTSET; + uint8_t nofrosttemp = EMS_VALUE_UINT_NOTSET; + uint8_t designtemp = EMS_VALUE_UINT_NOTSET; // heatingcurve design temp at MinExtTemp + uint8_t offsettemp = 100; // heatingcurve offest temp at roomtemp signed! + uint16_t remotetemp = EMS_VALUE_SHORT_NOTSET; // for simulating a RC20 remote - uint8_t designtemp = EMS_VALUE_UINT_NOTSET; // heatingcurve design temp at MinExtTemp - uint8_t offsettemp = 100; // heatingcurve offest temp at roomtemp signed! uint8_t hc_num() const { return hc_num_; @@ -76,7 +79,7 @@ class Thermostat : public EMSdevice { return set_typeid_; } - enum Mode : uint8_t { UNKNOWN, OFF, MANUAL, AUTO, DAY, NIGHT, HEAT, NOFROST, ECO, HOLIDAY, COMFORT, OFFSET, DESIGN }; + enum Mode : uint8_t { UNKNOWN, OFF, MANUAL, AUTO, DAY, NIGHT, HEAT, NOFROST, ECO, HOLIDAY, COMFORT, OFFSET, DESIGN, SUMMER }; private: uint8_t hc_num_; @@ -159,6 +162,10 @@ class Thermostat : public EMSdevice { static constexpr uint8_t EMS_OFFSET_RC35Set_heatingtype = 0; // e.g. floor heating = 3 static constexpr uint8_t EMS_OFFSET_RC35Set_circuitcalctemp = 14; // calculated circuit temperature static constexpr uint8_t EMS_OFFSET_RC35Set_seltemp = 37; // selected temp + static constexpr uint8_t EMS_OFFSET_RC35Set_temp_offset = 6; + static constexpr uint8_t EMS_OFFSET_RC35Set_temp_design = 17; + static constexpr uint8_t EMS_OFFSET_RC35Set_temp_summer = 22; + static constexpr uint8_t EMS_OFFSET_RC35Set_temp_nofrost = 23; static constexpr uint8_t EMS_OFFSET_EasyStatusMessage_setpoint = 10; // setpoint temp static constexpr uint8_t EMS_OFFSET_EasyStatusMessage_curr = 8; // current temp @@ -239,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 45d5b66b2..17685f969 100644 --- a/src/uart/emsuart_esp32.cpp +++ b/src/uart/emsuart_esp32.cpp @@ -29,14 +29,14 @@ namespace emsesp { static intr_handle_t uart_handle; -static RingbufHandle_t buf_handle = NULL; -static uint8_t rxbuf[UART_FIFO_LEN]; -static uint8_t rxlen; +static RingbufHandle_t buf_handle = NULL; +static bool drop_first_rx = true; +static uint8_t tx_mode_ = 0xFF; /* * Task to handle the incoming data */ -void EMSuart::emsuart_recvTask(void * param) { +void EMSuart::emsuart_recvTask(void * para) { while (1) { size_t item_size; uint8_t * telegram = (uint8_t *)xRingbufferReceive(buf_handle, &item_size, portMAX_DELAY); @@ -48,29 +48,41 @@ void EMSuart::emsuart_recvTask(void * param) { } } } - /* * UART interrupt, on break read the fifo and put the whole telegram to ringbuffer */ -static void IRAM_ATTR uart_intr_handle(void * arg) { +void IRAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) { + static uint8_t rxbuf[EMS_MAXBUFFERSIZE]; + static uint8_t length; + if (EMS_UART.int_st.brk_det) { - uint8_t rx_fifo_len = EMS_UART.status.rxfifo_cnt; - for (rxlen = 0; rxlen < rx_fifo_len; rxlen++) { - rxbuf[rxlen] = EMS_UART.fifo.rw_byte; // read all bytes into buffer - } - if ((rxlen == 2) || ((rxlen > 4) && (rxlen <= EMS_MAXBUFFERSIZE))) { - int baseType = 0; - xRingbufferSendFromISR(buf_handle, rxbuf, rxlen - 1, &baseType); - } EMS_UART.int_clr.brk_det = 1; // clear flag EMS_UART.conf0.txd_brk = 0; // if it was break from sending, clear bit + length = 0; + while (EMS_UART.status.rxfifo_cnt) { + uint8_t rx = EMS_UART.fifo.rw_byte; // read all bytes into buffer + if (length < EMS_MAXBUFFERSIZE) { + rxbuf[length++] = rx; + } + } + if ((!drop_first_rx) && ((length == 2) || ((length > 4)))) { + int baseType = 0; + xRingbufferSendFromISR(buf_handle, rxbuf, length - 1, &baseType); + } + drop_first_rx = false; } } - /* * init UART driver */ void EMSuart::start(uint8_t tx_mode) { + if (tx_mode_ != 0xFF) { // uart already initialized + restart(); + return; + } + + tx_mode_ = tx_mode; + uart_config_t uart_config = { .baud_rate = EMSUART_BAUD, .data_bits = UART_DATA_8_BITS, @@ -78,17 +90,21 @@ 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.conf1.rxfifo_full_thrhd = 127; // enough to hold the incoming telegram, should never reached - //EMS_UART.idle_conf.tx_brk_num = 12; // breaklength 12 bit - EMS_UART.int_ena.val = 0; // disable all intr. - EMS_UART.int_clr.val = 0xFFFFFFFF; // clear all intr. flags - EMS_UART.int_ena.brk_det = 1; // activate only break - buf_handle = xRingbufferCreate(128, RINGBUF_TYPE_NOSPLIT); - ESP_ERROR_CHECK(uart_isr_register(EMSUART_UART, uart_intr_handle, NULL, ESP_INTR_FLAG_IRAM, &uart_handle)); + 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 = 12; + + drop_first_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 } /* @@ -102,18 +118,21 @@ void EMSuart::stop() { * Restart Interrupt */ void EMSuart::restart() { - EMS_UART.int_clr.val = 0xFFFFFFFF; // clear all intr. flags - EMS_UART.int_ena.brk_det = 1; // activate only break + if (EMS_UART.int_raw.brk_det) { + EMS_UART.int_clr.brk_det = 1; // clear flag + drop_first_rx = true; // and drop first frame + } + EMS_UART.int_ena.brk_det = 1; // activate only break }; /* * Sends a 1-byte poll, ending with a */ void EMSuart::send_poll(uint8_t data) { - EMS_UART.conf0.txd_brk = 0; // just to make sure the bit is cleared - EMS_UART.fifo.rw_byte = data; - EMS_UART.idle_conf.tx_brk_num = 12; // breaklength 12 bit - EMS_UART.conf0.txd_brk = 1; // sending ends in a break + EMS_UART.conf0.txd_brk = 0; // just to make sure the bit is cleared + EMS_UART.fifo.rw_byte = data; + //EMS_UART.idle_conf.tx_brk_num = 11; // breaklength 11 bit + EMS_UART.conf0.txd_brk = 1; // sending ends in a break } /* @@ -123,16 +142,13 @@ void EMSuart::send_poll(uint8_t data) { */ EMSUART_STATUS EMSuart::transmit(uint8_t * buf, uint8_t len) { if (len > 0) { - if (EMS_UART.status.txfifo_cnt > 0) { // fifo not empty - return EMS_TX_WTD_TIMEOUT; - } EMS_UART.conf0.txd_brk = 0; // just to make sure the bit is cleared for (uint8_t i = 0; i < len; i++) { EMS_UART.fifo.rw_byte = buf[i]; } //uart_tx_chars(EMSUART_UART, (const char *)buf, len); - EMS_UART.idle_conf.tx_brk_num = 12; // breaklength 12 bit - EMS_UART.conf0.txd_brk = 1; // sending ends in a break + //EMS_UART.idle_conf.tx_brk_num = 11; // breaklength 11 bit + EMS_UART.conf0.txd_brk = 1; // sending ends in a break } return EMS_TX_STATUS_OK; } diff --git a/src/uart/emsuart_esp32.h b/src/uart/emsuart_esp32.h index d021f3e66..dc7c78480 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 @@ -27,15 +32,15 @@ #include "freertos/queue.h" #include -#define EMS_MAXBUFFERSIZE 33 // max size of the buffer. EMS packets are max 32 bytes, plus extra 2 for BRKs +#define EMS_MAXBUFFERSIZE 33 // max size of the buffer. EMS packets are max 32 bytes, plus extra for BRK #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 17 // 17 is UART2 RX. Use 23 for D7 on a Wemos D1-32 mini for backwards compatabilty -#define EMSUART_TXPIN 16 // 16 is UART2 TX. Use 5 for D8 on a Wemos D1-32 mini for backwards compatabilty +#define EMSUART_RXPIN 23 // 17 is UART2 RX. Use 23 for D7 on a Wemos D1-32 mini for backwards compatabilty +#define EMSUART_TXPIN 5 // 16 is UART2 TX. Use 5 for D8 on a Wemos D1-32 mini for backwards compatabilty namespace emsesp { @@ -57,7 +62,8 @@ class EMSuart { static EMSUART_STATUS transmit(uint8_t * buf, uint8_t len); private: - static void emsuart_recvTask(void * param); + static void emsuart_recvTask(void * para); + static void IRAM_ATTR emsuart_rx_intr_handler(void * para); }; } // namespace emsesp diff --git a/src/uart/emsuart_esp8266.cpp b/src/uart/emsuart_esp8266.cpp index 72fa9930d..1b249f8ff 100644 --- a/src/uart/emsuart_esp8266.cpp +++ b/src/uart/emsuart_esp8266.cpp @@ -28,25 +28,26 @@ os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen]; // our Rx queue EMSuart::EMSRxBuf_t * pEMSRxBuf; EMSuart::EMSRxBuf_t * paEMSRxBuf[EMS_MAXBUFFERS]; -uint8_t emsRxBufIdx = 0; -uint8_t phantomBreak = 0; -uint8_t tx_mode_ = EMS_TXMODE_DEFAULT; +uint8_t emsRxBufIdx = 0; +uint8_t phantomBreak = 0; +uint8_t tx_mode_ = 0xFF; +bool drop_first_rx = true; // // Main interrupt handler // 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 bool rx_idle_ = true; static uint8_t uart_buffer[EMS_MAXBUFFERSIZE + 2]; + /* // is a new buffer? if so init the thing for a new telegram if (rx_idle_) { rx_idle_ = false; // status set to busy length = 0; } - // fill IRQ buffer, by emptying Rx FIFO if (USIS(EMSUART_UART) & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) { while ((USS(EMSUART_UART) >> USRXC) & 0xFF) { @@ -58,15 +59,25 @@ void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) { // clear Rx FIFO full and Rx FIFO timeout interrupts USIC(EMSUART_UART) = (1 << UIFF) | (1 << UITO); } - +*/ // BREAK detection = End of EMS data block if (USIS(EMSUART_UART) & ((1 << UIBD))) { - ETS_UART_INTR_DISABLE(); // disable all interrupts and clear them - USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt - - pEMSRxBuf->length = (length > EMS_MAXBUFFERSIZE) ? EMS_MAXBUFFERSIZE : length; - os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, pEMSRxBuf->length); // copy data into transfer buffer, including the BRK 0x00 at the end - rx_idle_ = true; // check set the status flag stating BRK has been received and we can start a new package + length = 0; + while ((USS(EMSUART_UART) >> USRXC) & 0xFF) { + uint8_t rx = USF(EMSUART_UART); + if (length < EMS_MAXBUFFERSIZE) { + uart_buffer[length++] = rx; + } + } + USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear bit + ETS_UART_INTR_DISABLE(); // disable all interrupts and clear them + USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt + if (!drop_first_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 + } + drop_first_rx = false; ETS_UART_INTR_ENABLE(); // re-enable UART interrupts system_os_post(EMSUART_recvTaskPrio, 0, 0); // call emsuart_recvTask() at next opportunity @@ -116,8 +127,12 @@ void ICACHE_FLASH_ATTR EMSuart::emsuart_flush_fifos() { * 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 + tx_mode_ = tx_mode; + restart(); + return; + } tx_mode_ = 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)); @@ -148,8 +163,9 @@ 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! - USC1(EMSUART_UART) = 0; // reset config first - USC1(EMSUART_UART) = (0x01 << UCFFT) | (0x01 << UCTOT) | (0 << UCTOE); // enable interupts + // change: don't care, we do not use these interrupts + //USC1(EMSUART_UART) = 0; // reset config first + //USC1(EMSUART_UART) = (0x01 << UCFFT) | (0x01 << UCTOT) | (0 << UCTOE); // enable interupts // set interrupts for triggers USIC(EMSUART_UART) = 0xFFFF; // clear all interupts @@ -158,7 +174,8 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) { // enable rx break, fifo full and timeout. // but not frame error UIFR (because they are too frequent) or overflow UIOF because our buffer is only max 32 bytes // change: we don't care about Rx Timeout - it may lead to wrong readouts - USIE(EMSUART_UART) = (1 << UIBD) | (1 << UIFF) | (0 << UITO); + // change:we don't care about Fifo full and read only on break-detect + USIE(EMSUART_UART) = (1 << UIBD) | (0 << UIFF) | (0 << UITO); // set up interrupt callbacks for Rx system_os_task(emsuart_recvTask, EMSUART_recvTaskPrio, recvTaskQueue, EMSUART_recvTaskQueueLen); @@ -171,7 +188,7 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) { ETS_UART_INTR_ATTACH(emsuart_rx_intr_handler, nullptr); ETS_UART_INTR_ENABLE(); - + drop_first_rx = true; // LOG_INFO(F("UART service for Rx/Tx started")); } @@ -187,6 +204,10 @@ void ICACHE_FLASH_ATTR EMSuart::stop() { * re-start UART0 driver */ void ICACHE_FLASH_ATTR EMSuart::restart() { + if (USIS(EMSUART_UART) & ((1 << UIBD))) { + USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt + drop_first_rx = true; + } ETS_UART_INTR_ENABLE(); // emsuart_flush_fifos(); } @@ -211,7 +232,7 @@ void ICACHE_FLASH_ATTR EMSuart::tx_brk() { tmp = (1 << UCBRK); USC0(EMSUART_UART) |= (tmp); // set bit - if (tx_mode_ == EMS_TX_WTD_TIMEOUT) { // EMS+ mode + if (tx_mode_ == EMS_TXMODE_EMSPLUS) { // EMS+ mode delayMicroseconds(EMSUART_TX_BRK_WAIT); } else if (tx_mode_ == EMS_TXMODE_HT3) { // junkers mode delayMicroseconds(EMSUART_TX_WAIT_BRK - EMSUART_TX_LAG); // 1144 (11 Bits) @@ -225,9 +246,15 @@ 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) { - USF(EMSUART_UART) = data; - delayMicroseconds(EMSUART_TX_BRK_WAIT); - tx_brk(); // send + if (tx_mode_ == EMS_TXMODE_NEW) { + USC0(EMSUART_UART) &= ~(1 << UCBRK); // make sure bit is cleared + USF(EMSUART_UART) = data; + USC0(EMSUART_UART) |= (1 << UCBRK); // send at the end + } else { + USF(EMSUART_UART) = data; + delayMicroseconds(EMSUART_TX_BRK_WAIT); + tx_brk(); // send + } } /* @@ -242,12 +269,11 @@ EMSUART_STATUS ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) { // new code from Michael. See https://github.com/proddy/EMS-ESP/issues/380 if (tx_mode_ == EMS_TXMODE_NEW) { - USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear bit + USC0(EMSUART_UART) &= ~(1 << UCBRK); // make sure bit is cleared for (uint8_t i = 0; i < len; i++) { USF(EMSUART_UART) = buf[i]; } USC0(EMSUART_UART) |= (1 << UCBRK); // send at the end - return EMS_TX_STATUS_OK; } diff --git a/src/uart/emsuart_esp8266.h b/src/uart/emsuart_esp8266.h index 72aee85e3..c92ea6fd5 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 @@ -47,7 +47,7 @@ #define EMSUART_TX_LAG 8 #define EMSUART_BUSY_WAIT (EMSUART_BIT_TIME / 8) #define EMS_TX_TO_CHARS (2 + 20) -#define EMS_TX_TO_COUNT ((EMS_TX_TO_CHARS)*10 * 8) +#define EMS_TX_TO_COUNT ((EMS_TX_TO_CHARS)*8) namespace emsesp { 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"