diff --git a/src/custom.htm b/src/custom.htm index 4b45a8899..2cdefa42f 100644 --- a/src/custom.htm +++ b/src/custom.htm @@ -128,13 +128,26 @@ data-content="Select the main heating circuit to use. Default is HC1."> +
+ + + + +
+
diff --git a/src/custom.js b/src/custom.js index 2b56a9de5..f458ad465 100644 --- a/src/custom.js +++ b/src/custom.js @@ -23,6 +23,7 @@ function listcustom() { document.getElementById("dallas_gpio").value = custom_config.settings.dallas_gpio; document.getElementById("publish_time").value = custom_config.settings.publish_time; document.getElementById("heating_circuit").value = custom_config.settings.heating_circuit; + document.getElementById("tx_mode").value = custom_config.settings.tx_mode; if (custom_config.settings.led) { $("input[name=\"led\"][value=\"1\"]").prop("checked", true); @@ -72,6 +73,7 @@ function savecustom() { custom_config.settings.publish_time = parseInt(document.getElementById("publish_time").value); custom_config.settings.heating_circuit = parseInt(document.getElementById("heating_circuit").value); + custom_config.settings.tx_mode = parseInt(document.getElementById("tx_mode").value); custom_uncommited(); } diff --git a/src/ems-esp.cpp b/src/ems-esp.cpp index 424144a6b..3b4079bb8 100644 --- a/src/ems-esp.cpp +++ b/src/ems-esp.cpp @@ -8,12 +8,12 @@ */ // local libraries +#include "MyESP.h" #include "ems.h" #include "ems_devices.h" #include "emsuart.h" #include "my_config.h" #include "version.h" -#include "MyESP.h" // Dallas external temp sensors #include "ds18.h" @@ -88,6 +88,7 @@ typedef struct { uint8_t dallas_gpio; // pin for attaching external dallas temperature sensors bool dallas_parasite; // on/off is using parasite uint8_t heating_circuit; // number of heating circuit, 1 or 2 + uint8_t tx_mode; // TX mode 1,2 or 3 } _EMSESP_Status; typedef struct { @@ -109,6 +110,7 @@ static const command_t project_cmds[] PROGMEM = { {true, "shower_alert ", "stop hot water to send 3 cold burst warnings after max shower time is exceeded"}, {true, "publish_time ", "set frequency for publishing data to MQTT (0=off)"}, {true, "heating_circuit <1 | 2>", "set the main thermostat HC to work with (if using multiple heating circuits)"}, + {true, "tx_mode ", "changes Tx logic. 1=ems generic, 2=ems+, 3=Junkers HT3"}, {false, "info", "show current captured on the devices"}, {false, "log ", "set logging mode to none, basic, thermostat only, raw or verbose"}, @@ -1166,6 +1168,9 @@ bool LoadSaveCallback(MYESP_FSACTION action, JsonObject json) { EMSESP_Status.heating_circuit = settings["heating_circuit"] | DEFAULT_HEATINGCIRCUIT; ems_setThermostatHC(EMSESP_Status.heating_circuit); + EMSESP_Status.tx_mode = settings["tx_mode"] | 1; // default to 1 (generic) + ems_setTxMode(EMSESP_Status.tx_mode); + return true; } @@ -1181,6 +1186,7 @@ bool LoadSaveCallback(MYESP_FSACTION action, JsonObject json) { settings["shower_alert"] = EMSESP_Status.shower_alert; settings["publish_time"] = EMSESP_Status.publish_time; settings["heating_circuit"] = EMSESP_Status.heating_circuit; + settings["tx_mode"] = EMSESP_Status.tx_mode; return true; } @@ -1298,6 +1304,18 @@ bool SetListCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, co myDebug_P(PSTR("Error. Usage: set heating_circuit <1 | 2>")); } } + + // tx_mode + if ((strcmp(setting, "tx_mode") == 0) && (wc == 2)) { + uint8_t mode = atoi(value); + if ((mode >= 1) && (mode <= 3)) { + EMSESP_Status.tx_mode = mode; + ems_setTxMode(mode); + ok = true; + } else { + myDebug_P(PSTR("Error. Usage: set tx_mode <1 | 2 | 3>")); + } + } } if (action == MYESP_FSACTION_LIST) { @@ -1306,6 +1324,7 @@ bool SetListCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, co myDebug_P(PSTR(" dallas_gpio=%d"), EMSESP_Status.dallas_gpio); myDebug_P(PSTR(" dallas_parasite=%s"), EMSESP_Status.dallas_parasite ? "on" : "off"); myDebug_P(PSTR(" heating_circuit=%d"), EMSESP_Status.heating_circuit); + myDebug_P(PSTR(" tx_mode=%d"), EMSESP_Status.tx_mode); myDebug_P(PSTR(" listen_mode=%s"), EMSESP_Status.listen_mode ? "on" : "off"); myDebug_P(PSTR(" shower_timer=%s"), EMSESP_Status.shower_timer ? "on" : "off"); myDebug_P(PSTR(" shower_alert=%s"), EMSESP_Status.shower_alert ? "on" : "off"); diff --git a/src/ems.cpp b/src/ems.cpp index 6c8705ace..39932a117 100644 --- a/src/ems.cpp +++ b/src/ems.cpp @@ -7,10 +7,10 @@ */ #include "ems.h" +#include "MyESP.h" #include "ems_devices.h" #include "emsuart.h" #include // https://github.com/rlogiacco/CircularBuffer -#include "MyESP.h" #ifdef TESTS #include "test_data.h" @@ -508,6 +508,11 @@ int _ems_findType(uint16_t type) { return (typeFound ? i : -1); } +void ems_setTxMode(uint8_t mode) { + EMS_Sys_Status.emsTxMode = mode; +} + + /** * debug print a telegram to telnet/serial including the CRC */ @@ -1408,33 +1413,34 @@ void _process_EasyStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { * EMS+ messages may come in with different offsets so handle them here */ void _process_RCPLUSStatusMessage(_EMS_RxTelegram * EMS_RxTelegram) { - if (EMS_RxTelegram->offset == 0) { + // handle single data values + if (EMS_RxTelegram->data_length == 1) { + switch (EMS_RxTelegram->offset) { + case EMS_OFFSET_RCPLUSStatusMessage_curr: // setpoint target temp + EMS_Thermostat.curr_roomTemp = _toShort(0); // value is * 10 + break; + case EMS_OFFSET_RCPLUSStatusMessage_setpoint: // current target temp + EMS_Thermostat.setpoint_roomTemp = _toByte(0); // value is * 2 + break; + case EMS_OFFSET_RCPLUSStatusMessage_currsetpoint: // current setpoint temp, e.g. Thermostat -> all, telegram: 10 00 FF 06 01 A5 22 + EMS_Thermostat.setpoint_roomTemp = _toByte(0); // value is * 2 + break; + case EMS_OFFSET_RCPLUSStatusMessage_mode: // thermostat mode auto/manual + // manual : 10 00 FF 0A 01 A5 02 + // auto : Thermostat -> all, type 0x01A5 telegram: 10 00 FF 0A 01 A5 03 + EMS_Thermostat.mode = _bitRead(0, 0); // bit 1, mode (auto=1 or manual=0) + EMS_Thermostat.day_mode = _bitRead(0, 1); // get day mode flag + + break; + } + } else if (EMS_RxTelegram->data_length > 20) { // the whole telegram // e.g. Thermostat -> all, telegram: 10 00 FF 00 01 A5 00 D7 21 00 00 00 00 30 01 84 01 01 03 01 84 01 F1 00 00 11 01 00 08 63 00 // 10 00 FF 00 01 A5 80 00 01 30 28 00 30 28 01 54 03 03 01 01 54 02 A8 00 00 11 01 03 FF FF 00 EMS_Thermostat.curr_roomTemp = _toShort(EMS_OFFSET_RCPLUSStatusMessage_curr); // value is * 10 EMS_Thermostat.setpoint_roomTemp = _toByte(EMS_OFFSET_RCPLUSStatusMessage_setpoint); // value is * 2 - - EMS_Thermostat.day_mode = _bitRead(EMS_OFFSET_RCPLUSStatusMessage_mode, 1); // get day mode flag - - EMS_Thermostat.mode = _bitRead(EMS_OFFSET_RCPLUSStatusMessage_mode, 0); // bit 1, mode (auto=1 or manual=0) - } - - // current target temp - if (EMS_RxTelegram->offset == EMS_OFFSET_RCPLUSStatusMessage_setpoint) { - EMS_Thermostat.setpoint_roomTemp = _toByte(0); // value is * 2 - } - - // current setpoint temp, e.g. Thermostat -> all, telegram: 10 00 FF 06 01 A5 22 - if (EMS_RxTelegram->offset == 6) { - EMS_Thermostat.setpoint_roomTemp = _toByte(0); // value is * 2 - } - - // thermostat mode auto/manual, examples: - // manual : 10 00 FF 0A 01 A5 02 (CRC=16) #data=1 - // auto : Thermostat -> all, type 0x01A5 telegram: 10 00 FF 0A 01 A5 03 (CRC=17) #data=1 - if (EMS_RxTelegram->offset == EMS_OFFSET_RCPLUSStatusMessage_mode) { - EMS_Thermostat.mode = _bitRead(0, 0); // bit 0 + EMS_Thermostat.day_mode = _bitRead(EMS_OFFSET_RCPLUSStatusMessage_mode, 1); // get day mode flag + EMS_Thermostat.mode = _bitRead(EMS_OFFSET_RCPLUSStatusMessage_mode, 0); // bit 1, mode (auto=1 or manual=0) } EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT diff --git a/src/ems.h b/src/ems.h index 01f3327ce..42afcb503 100644 --- a/src/ems.h +++ b/src/ems.h @@ -170,6 +170,7 @@ typedef struct { uint8_t txRetryCount; // # times the last Tx was re-sent uint8_t emsIDMask; // Buderus: 0x00, Junkers: 0x80 uint8_t emsPollAck[1]; // acknowledge buffer for Poll + uint8_t emsTxMode; // Tx mode 1, 2 or 3 } _EMS_Sys_Status; // The Tx send package @@ -425,6 +426,7 @@ void ems_setWarmWaterModeComfort(uint8_t comfort); void ems_setModels(); void ems_setTxDisabled(bool b); bool ems_getTxDisabled(); +void ems_setTxMode(uint8_t mode); char * ems_getThermostatDescription(char * buffer, bool name_only = false); char * ems_getBoilerDescription(char * buffer, bool name_only = false); diff --git a/src/emsuart.cpp b/src/emsuart.cpp index 976c8f7d8..7ecfe6351 100644 --- a/src/emsuart.cpp +++ b/src/emsuart.cpp @@ -170,6 +170,37 @@ void ICACHE_FLASH_ATTR emsuart_start() { ETS_UART_INTR_ENABLE(); } +/* + * Send a BRK signal + * 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) != 0) + ; + + 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); + GPIO_H(TX_MARK_MASK); + USC0(EMSUART_UART) |= (tmp); // set bit + + if (EMS_Sys_Status.emsTxMode <= 1) { // classic mode and ems+ (0, 1) + delayMicroseconds(EMSUART_TX_BRK_WAIT); + } else if (EMS_Sys_Status.emsTxMode == 3) { // junkers mode + delayMicroseconds(EMSUART_TX_WAIT_BRK - EMSUART_TX_LAG); // 1144 (11 Bits) + } + + USC0(EMSUART_UART) &= ~(tmp); // clear bit + GPIO_L(TX_MARK_MASK); +} + /* * Send to Tx, ending with a */ @@ -182,8 +213,29 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) { if (len) { LA_PULSE(50); - /* - * + + if (EMS_Sys_Status.emsTxMode == 2) { // With extra tx delay for EMS+ + for (uint8_t i = 0; i < len; i++) { + TX_PULSE(EMSUART_BIT_TIME / 4); + USF(EMSUART_UART) = buf[i]; + delayMicroseconds(EMSUART_TX_BRK_WAIT); // https://github.com/proddy/EMS-ESP/issues/23# + } + emsuart_tx_brk(); // send + } else if (EMS_Sys_Status.emsTxMode == 3) { // Junkers logic by @philrich + for (uint8_t i = 0; i < len; i++) { + TX_PULSE(EMSUART_BIT_TIME / 4); + USF(EMSUART_UART) = buf[i]; + + // just to be safe wait for tx fifo empty (needed?) + while (((USS(EMSUART_UART) >> USTXC) & 0xff) != 0) + ; + + // wait until bits are sent on wire + delayMicroseconds(EMSUART_TX_WAIT_BYTE - EMSUART_TX_LAG + EMSUART_TX_WAIT_GAP); + } + emsuart_tx_brk(); // send + } else if (EMS_Sys_Status.emsTxMode == 1) { + /* * based on code from https://github.com/proddy/EMS-ESP/issues/103 by @susisstrolch * we emit the whole telegram, with Rx interrupt disabled, collecting busmaster response in FIFO. * after sending the last char we poll the Rx status until either @@ -209,61 +261,62 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) { #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) - uint16_t wdc = EMS_TX_TO_COUNT; - ETS_UART_INTR_DISABLE(); // disable rx interrupt + uint16_t wdc = EMS_TX_TO_COUNT; + ETS_UART_INTR_DISABLE(); // disable rx interrupt - // clear Rx status register - USC0(EMSUART_UART) |= (1 << UCRXRST); // reset uart rx fifo - emsuart_flush_fifos(); + // clear Rx status register + USC0(EMSUART_UART) |= (1 << UCRXRST); // reset uart rx fifo + emsuart_flush_fifos(); - // throw out the telegram... - for (uint8_t i = 0; i < len && result == EMS_TX_STATUS_OK;) { - GPIO_H(TX_MARK_MASK); + // throw out the telegram... + for (uint8_t i = 0; i < len && result == EMS_TX_STATUS_OK;) { + GPIO_H(TX_MARK_MASK); - wdc = EMS_TX_TO_COUNT; - volatile uint8_t _usrxc = (USS(EMSUART_UART) >> USRXC) & 0xFF; - USF(EMSUART_UART) = buf[i++]; // send each Tx byte - // wait for echo from busmaster - GPIO_L(TX_MARK_MASK); - while (((USS(EMSUART_UART) >> USRXC) & 0xFF) == _usrxc) { - delayMicroseconds(EMSUART_BUSY_WAIT); // burn CPU cycles... - if (--wdc == 0) { - EMS_Sys_Status.emsTxStatus = result = EMS_TX_WTD_TIMEOUT; - break; - } - if (USIR(EMSUART_UART) & (1 << UIBD)) { - USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ - EMS_Sys_Status.emsTxStatus = result = EMS_TX_BRK_DETECT; + wdc = EMS_TX_TO_COUNT; + volatile uint8_t _usrxc = (USS(EMSUART_UART) >> USRXC) & 0xFF; + USF(EMSUART_UART) = buf[i++]; // send each Tx byte + // wait for echo from busmaster + GPIO_L(TX_MARK_MASK); + while (((USS(EMSUART_UART) >> USRXC) & 0xFF) == _usrxc) { + delayMicroseconds(EMSUART_BUSY_WAIT); // burn CPU cycles... + if (--wdc == 0) { + EMS_Sys_Status.emsTxStatus = result = EMS_TX_WTD_TIMEOUT; + break; + } + if (USIR(EMSUART_UART) & (1 << UIBD)) { + USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ + EMS_Sys_Status.emsTxStatus = result = EMS_TX_BRK_DETECT; + } } } - } - // we got the whole telegram in the Rx buffer - // on Rx-BRK (bus collision), we simply enable Rx and leave it - // otherwise we send the final Tx-BRK in the loopback and re=enable Rx-INT. - // worst case, we'll see an additional Rx-BRK... - if (result != EMS_TX_STATUS_OK) { - LA_PULSE(200); // mark Tx error - } else { - // neither bus collision nor timeout - send terminating BRK signal - GPIO_H(TX_MARK_MASK); - if (!(USIS(EMSUART_UART) & (1 << UIBD))) { - // no bus collision - send terminating BRK signal - USC0(EMSUART_UART) |= (1 << UCLBE) | (1 << UCBRK); // enable loopback & set + // we got the whole telegram in the Rx buffer + // on Rx-BRK (bus collision), we simply enable Rx and leave it + // otherwise we send the final Tx-BRK in the loopback and re=enable Rx-INT. + // worst case, we'll see an additional Rx-BRK... + if (result != EMS_TX_STATUS_OK) { + LA_PULSE(200); // mark Tx error + } else { + // neither bus collision nor timeout - send terminating BRK signal + GPIO_H(TX_MARK_MASK); + if (!(USIS(EMSUART_UART) & (1 << UIBD))) { + // no bus collision - send terminating BRK signal + USC0(EMSUART_UART) |= (1 << UCLBE) | (1 << UCBRK); // enable loopback & set - // wait until BRK detected... - while (!(USIR(EMSUART_UART) & (1 << UIBD))) { - // delayMicroseconds(EMSUART_BUSY_WAIT); - delayMicroseconds(EMSUART_BIT_TIME); + // wait until BRK detected... + while (!(USIR(EMSUART_UART) & (1 << UIBD))) { + // delayMicroseconds(EMSUART_BUSY_WAIT); + delayMicroseconds(EMSUART_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) | (1 << UCLBE)); // disable loopback & clear - USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ - phantomBreak = 1; + GPIO_L(TX_MARK_MASK); } - GPIO_L(TX_MARK_MASK); + ETS_UART_INTR_ENABLE(); // receive anything from FIFO... } - ETS_UART_INTR_ENABLE(); // receive anything from FIFO... } return result; } diff --git a/tools/wsemulator/wserver.js b/tools/wsemulator/wserver.js index f30d608c6..40b860e56 100644 --- a/tools/wsemulator/wserver.js +++ b/tools/wsemulator/wserver.js @@ -105,7 +105,8 @@ var custom_configfile = { "shower_timer": false, "shower_alert": false, "publish_time": 120, - "heating_circuit": 1 + "heating_circuit": 1, + "tx_mode": 1 } };