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
}
};