fixing tx_mode 2

° stabilize emsuart_rx...
  We can get more than 32 bytes because of the trailing BRK.
  So the buffersize for Rx interrupt is (for safety) increased to 36 bytes.
  If length exceeds 36 bytes we dump them to /dev/null

° reintroduced the phantomBreak flag again
  We _must_ signal to Rx that we have a double break, otherwise
  we get problems in emsuart_recvTask...

° add ems_dumpBuffer which shows TxBuffer before send and RxBuffer after
  receive and applying phantomBreak.
  The dump is activated in "log j" mode and used to debug the
  protocol problems.

° change handling of ID bit 7
  on system start we listen for telegram until we receive a valid one,
  larger than 5 byte.
  Depending on the bit7 of the source address we decide if we have a
  Buderus EMS or a Junkers EMS bus.
  This decision is used to set the variables emsIDMask (0x00 for Buderus,
  0x80 for Junkers) and the emsPollAck buffer, used to send the propper
  acknowledge, depending on EMS type.

° move poll acknowledge function (emsuart_tx_poll) from emsuart.cpp to ems.cpp
  and rename to ems_pollAck

° add EMS_TX_REV_DETECT status for detecting the SourceID.7 bit and setting
  emsIDMask and emsPollAck buffer accordingly

° set initial emsTxStatus to EMS_TX_REV_DETECT

° add 'log j' - jabber - for more extensive debug logs
This commit is contained in:
Susis Strolch
2019-07-22 10:39:47 +02:00
parent 7ff8cd834d
commit dab66ce918
5 changed files with 174 additions and 76 deletions

View File

@@ -1467,6 +1467,9 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
} else if (strcmp(second_cmd, "n") == 0) { } else if (strcmp(second_cmd, "n") == 0) {
ems_setLogging(EMS_SYS_LOGGING_NONE); ems_setLogging(EMS_SYS_LOGGING_NONE);
ok = true; ok = true;
} else if (strcmp(second_cmd, "j") == 0) {
ems_setLogging(EMS_SYS_LOGGING_NONE);
ok = true;
} }
} }

View File

@@ -226,7 +226,7 @@ void ems_init() {
EMS_Sys_Status.emsTxPkgs = 0; EMS_Sys_Status.emsTxPkgs = 0;
EMS_Sys_Status.emxCrcErr = 0; EMS_Sys_Status.emxCrcErr = 0;
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE; EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE;
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE; EMS_Sys_Status.emsTxStatus = EMS_TX_REV_DETECT;
EMS_Sys_Status.emsRefreshed = false; EMS_Sys_Status.emsRefreshed = false;
EMS_Sys_Status.emsPollEnabled = false; // start up with Poll disabled EMS_Sys_Status.emsPollEnabled = false; // start up with Poll disabled
EMS_Sys_Status.emsBusConnected = false; EMS_Sys_Status.emsBusConnected = false;
@@ -235,8 +235,9 @@ void ems_init() {
EMS_Sys_Status.emsTxDisabled = false; EMS_Sys_Status.emsTxDisabled = false;
EMS_Sys_Status.emsPollFrequency = 0; EMS_Sys_Status.emsPollFrequency = 0;
EMS_Sys_Status.txRetryCount = 0; EMS_Sys_Status.txRetryCount = 0;
EMS_Sys_Status.emsReverse = false;
EMS_Sys_Status.emsTxMode = 0; EMS_Sys_Status.emsTxMode = 0;
EMS_Sys_Status.emsIDMask = 0x00;
EMS_Sys_Status.emsPollAck[0] = EMS_ID_ME;
// thermostat // thermostat
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_SHORT_NOTSET; EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_SHORT_NOTSET;
@@ -361,11 +362,12 @@ void ems_setTxMode(uint8_t mode) {
// special case for Junkers. If tx_mode is 3 then set the reverse poll flag // special case for Junkers. If tx_mode is 3 then set the reverse poll flag
// https://github.com/proddy/EMS-ESP/issues/103#issuecomment-495945850 // https://github.com/proddy/EMS-ESP/issues/103#issuecomment-495945850
if (mode == 3) { if (mode == 3) {
EMS_Sys_Status.emsReverse = true; EMS_Sys_Status.emsIDMask = 0x80;
myDebug_P(PSTR("Forcing emsReverse for Junkers")); myDebug_P(PSTR("Forcing emsReverse for Junkers"));
} else { } else {
EMS_Sys_Status.emsReverse = false; EMS_Sys_Status.emsIDMask = 0x00;
} }
EMS_Sys_Status.emsPollAck[0] = EMS_ID_ME ^ EMS_Sys_Status.emsIDMask;
} }
uint8_t ems_getTxMode() { uint8_t ems_getTxMode() {
@@ -453,10 +455,19 @@ void ems_setLogging(_EMS_SYS_LOGGING loglevel) {
myDebug_P(PSTR("System Logging set to Solar Module only")); myDebug_P(PSTR("System Logging set to Solar Module only"));
} else if (loglevel == EMS_SYS_LOGGING_RAW) { } else if (loglevel == EMS_SYS_LOGGING_RAW) {
myDebug_P(PSTR("System Logging set to Raw mode")); myDebug_P(PSTR("System Logging set to Raw mode"));
} else if (loglevel == EMS_SYS_LOGGING_JABBER) {
myDebug_P(PSTR("System Logging set to Jabber mode"));
} }
} }
} }
/**
* send a poll acknowledge
*/
void ems_tx_pollAck() {
emsuart_tx_buffer(&EMS_Sys_Status.emsPollAck[0], 1);
}
/** /**
* Calculate CRC checksum using lookup table for speed * Calculate CRC checksum using lookup table for speed
* len is length of all the data in bytes (including the header & CRC byte at end) * len is length of all the data in bytes (including the header & CRC byte at end)
@@ -615,20 +626,26 @@ void _ems_sendTelegram() {
} }
EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length); // add the CRC EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length); // add the CRC
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); // send the telegram to the UART Tx _EMS_TX_STATUS _txStatus = emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); // send the telegram to the UART Tx
EMS_TxQueue.shift(); // and remove from queue if (EMS_TX_BRK_DETECT == _txStatus || EMS_TX_WTD_TIMEOUT == _txStatus) {
// Tx Error!
myDebug_P(PSTR("** error sending buffer: %s"),_txStatus == EMS_TX_BRK_DETECT ?
"BRK" : "WDTO");
// EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
}
EMS_TxQueue.shift(); // and remove from queue
return; return;
} }
// create the header // create the header
EMS_TxTelegram.data[0] = (EMS_Sys_Status.emsReverse) ? EMS_ID_ME | 0x80 : EMS_ID_ME; // src EMS_TxTelegram.data[0] = EMS_ID_ME ^ EMS_Sys_Status.emsIDMask; // src
// dest // dest
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) { if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) {
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; EMS_TxTelegram.data[1] = EMS_TxTelegram.dest;
} else { } else {
// for a READ or VALIDATE // for a READ or VALIDATE
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest | 0x80; // read has 8th bit set EMS_TxTelegram.data[1] = (EMS_TxTelegram.dest ^ 0x80 ^ EMS_Sys_Status.emsIDMask); // read has 8th bit set
} }
// complete the rest of the header depending on EMS or EMS+ // complete the rest of the header depending on EMS or EMS+
@@ -671,9 +688,17 @@ void _ems_sendTelegram() {
} }
// send the telegram to the UART Tx // send the telegram to the UART Tx
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); _EMS_TX_STATUS _txStatus = emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); // send the telegram to the UART Tx
if (EMS_TX_STATUS_OK == _txStatus || EMS_TX_STATUS_IDLE == _txStatus)
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_WAIT;
else {
// Tx Error!
// Tx Error!
myDebug_P(PSTR("** error sending buffer: %s"),_txStatus == EMS_TX_BRK_DETECT ?
"BRK" : "WDTO");
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
}
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_WAIT;
} }
@@ -720,6 +745,57 @@ void _createValidate() {
EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it first to be picked up next (FIFO) EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it first to be picked up next (FIFO)
} }
/**
* dump a UART Tx or Rx buffer to console...
*/
void ems_dumpBuffer(const char *prefix, uint8_t *telegram, uint8_t length) {
uint32_t timestamp = millis();
static char output_str[200] = {0};
static char buffer[16] = {0};
if (EMS_Sys_Status.emsLogging != EMS_SYS_LOGGING_JABBER)
return;
// we only care about known devices
if (length) {
uint8_t dev = telegram[0] & 0x7F;
if (!((dev == 0x04)||(dev == 0x08)||(dev == 0x09)||(dev == 0x0a)
||(dev == 0x01)||(dev == 0x0b)||(dev == 0x10)))
return;
}
strlcpy(output_str, "(", sizeof(output_str));
strlcat(output_str, COLOR_CYAN, sizeof(output_str));
strlcat(output_str, _smallitoa((uint8_t)((timestamp / 3600000) % 24), buffer), sizeof(output_str));
strlcat(output_str, ":", sizeof(output_str));
strlcat(output_str, _smallitoa((uint8_t)((timestamp / 60000) % 60), buffer), sizeof(output_str));
strlcat(output_str, ":", sizeof(output_str));
strlcat(output_str, _smallitoa((uint8_t)((timestamp / 1000) % 60), buffer), sizeof(output_str));
strlcat(output_str, ".", sizeof(output_str));
strlcat(output_str, _smallitoa3(timestamp % 1000, buffer), sizeof(output_str));
strlcat(output_str, COLOR_RESET, sizeof(output_str));
strlcat(output_str, ") ", sizeof(output_str));
strlcat(output_str, COLOR_YELLOW, sizeof(output_str));
strlcat(output_str, prefix, sizeof(output_str));
// show some EMS_Sys_Status entries
strlcat(output_str, _hextoa(EMS_Sys_Status.emsRxStatus, buffer), sizeof(output_str));
strlcat(output_str, " ", sizeof(output_str));
strlcat(output_str, _hextoa(EMS_Sys_Status.emsTxStatus, buffer), sizeof(output_str));
strlcat(output_str, ": ", sizeof(output_str));
// print whole buffer, don't interpret any data
for (int i = 0; i < (length); i++) {
strlcat(output_str, _hextoa(telegram[i], buffer), sizeof(output_str));
strlcat(output_str, " ", sizeof(output_str));
}
strlcat(output_str, COLOR_RESET, sizeof(output_str));
myDebug(output_str);
}
/** /**
* Entry point triggered by an interrupt in emsuart.cpp * Entry point triggered by an interrupt in emsuart.cpp
* length is the number of all the telegram bytes up to and including the CRC at the end * length is the number of all the telegram bytes up to and including the CRC at the end
@@ -729,16 +805,28 @@ void _createValidate() {
void ems_parseTelegram(uint8_t * telegram, uint8_t length) { void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
static uint32_t _last_emsPollFrequency = 0; static uint32_t _last_emsPollFrequency = 0;
ems_dumpBuffer("** [DEBUG MODE] ems_parseTelegram: ", telegram, length);
/* /*
* check if we just received a single byte * check if we just received a single byte
* it could well be a Poll request from the boiler for us, which will have a value of 0x8B (0x0B | 0x80) * it could well be a Poll request from the boiler for us, which will have a value of 0x8B (0x0B | 0x80)
* or either a return code like 0x01 or 0x04 from the last Write command * or either a return code like 0x01 or 0x04 from the last Write command
* Roger Wilco: we have different types here: */
* EMS_ID_ME && length == 1 && EMS_TX_STATUS_IDLE && EMS_RX_STATUS_IDLE: polling request
* EMS_ID_ME && length > 1 && EMS_TX_STATUS_IDLE && EMS_RX_STATUS_IDLE: direct telegram /*
* (EMS_TX_SUCCESS || EMS_TX_ERROR) && EMS_TX_STATUS_WAIT: response, free the EMS bus * Detect the EMS bus type - Buderus or Junkers - and set emsIDMask accordingly.
* * we wait for the first valid telegram and look at the SourceID.
* In addition, it may happen that we where interrupted (f.e. by WIFI activity) and the * If Bit 7 is set we have a Buderus, otherwise a Junkers
*/
if (EMS_Sys_Status.emsTxStatus == EMS_TX_REV_DETECT) {
if ((length >= 5) && (telegram[length - 1] == _crcCalculator(telegram, length))) {
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
EMS_Sys_Status.emsIDMask = telegram[0] & 0x80;
EMS_Sys_Status.emsPollAck[0] = EMS_ID_ME ^ EMS_Sys_Status.emsIDMask;
} else
return; // ignore the whole telegram Rx Telegram while in DETECT mode
}
/* It may happen that we where interrupted (f.e. by WIFI activity) and the
* buffer isn't valid anymore, so we must not answer at all... * buffer isn't valid anymore, so we must not answer at all...
*/ */
if (EMS_Sys_Status.emsRxStatus != EMS_RX_STATUS_IDLE) { if (EMS_Sys_Status.emsRxStatus != EMS_RX_STATUS_IDLE) {
@@ -752,8 +840,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
uint8_t value = telegram[0]; // 1st byte of data package uint8_t value = telegram[0]; // 1st byte of data package
// check first for a Poll for us // check first for a Poll for us
// the poll has the MSB set - seems to work on both EMS and Junkers if ((value ^ 0x80 ^ EMS_Sys_Status.emsIDMask) == EMS_ID_ME) {
if ((value & 0x7F) == EMS_ID_ME) {
EMS_Sys_Status.emsTxCapable = true; EMS_Sys_Status.emsTxCapable = true;
uint32_t timenow_microsecs = micros(); uint32_t timenow_microsecs = micros();
EMS_Sys_Status.emsPollFrequency = (timenow_microsecs - _last_emsPollFrequency); EMS_Sys_Status.emsPollFrequency = (timenow_microsecs - _last_emsPollFrequency);
@@ -766,7 +853,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
} else { } else {
// nothing to send so just send a poll acknowledgement back // nothing to send so just send a poll acknowledgement back
if (EMS_Sys_Status.emsPollEnabled) { if (EMS_Sys_Status.emsPollEnabled) {
emsuart_tx_poll(); ems_tx_pollAck();
} }
} }
} else if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_WAIT) { } else if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_WAIT) {
@@ -774,14 +861,14 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
if (value == EMS_TX_SUCCESS) { if (value == EMS_TX_SUCCESS) {
EMS_Sys_Status.emsTxPkgs++; EMS_Sys_Status.emsTxPkgs++;
// got a success 01. Send a validate to check the value of the last write // got a success 01. Send a validate to check the value of the last write
emsuart_tx_poll(); // send a poll to free the EMS bus ems_tx_pollAck(); // send a poll to free the EMS bus
_createValidate(); // create a validate Tx request (if needed) _createValidate(); // create a validate Tx request (if needed)
} else if (value == EMS_TX_ERROR) { } else if (value == EMS_TX_ERROR) {
// last write failed (04), delete it from queue and dont bother to retry // last write failed (04), delete it from queue and dont bother to retry
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) { if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
myDebug_P(PSTR("** Write command failed from host")); myDebug_P(PSTR("** Write command failed from host"));
} }
emsuart_tx_poll(); // send a poll to free the EMS bus ems_tx_pollAck(); // send a poll to free the EMS bus
_removeTxQueue(); // remove from queue _removeTxQueue(); // remove from queue
} }
} }
@@ -792,7 +879,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
// ignore anything that doesn't resemble a proper telegram package // ignore anything that doesn't resemble a proper telegram package
// minimal is 5 bytes, excluding CRC at the end // minimal is 5 bytes, excluding CRC at the end
if (length <= 4) { if (length <= 4) {
//_debugPrintTelegram("Noisy data:", &EMS_RxTelegram COLOR_RED); _debugPrintTelegram("Noisy data:", &EMS_RxTelegram, COLOR_RED);
return; return;
} }
@@ -1035,7 +1122,7 @@ void _processType(_EMS_RxTelegram * EMS_RxTelegram) {
// if its an echo of ourselves from the master UBA, ignore. This should never happen mind you // if its an echo of ourselves from the master UBA, ignore. This should never happen mind you
if (EMS_RxTelegram->src == EMS_ID_ME) { if (EMS_RxTelegram->src == EMS_ID_ME) {
// _debugPrintTelegram("echo:", EMS_RxTelegram, COLOR_WHITE); _debugPrintTelegram("echo:", EMS_RxTelegram, COLOR_WHITE);
return; return;
} }
@@ -1143,10 +1230,9 @@ void _processType(_EMS_RxTelegram * EMS_RxTelegram) {
} }
} }
emsuart_tx_poll(); // send Acknowledgement back to free the EMS bus since we have the telegram ems_tx_pollAck(); // send Acknowledgement back to free the EMS bus since we have the telegram
} }
/** /**
* Check if hot tap water or heating is active * Check if hot tap water or heating is active
* using a quick hack for checking the heating. Selected Flow Temp >= 70 * using a quick hack for checking the heating. Selected Flow Temp >= 70
@@ -1684,7 +1770,8 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
// check to see if its a Junkers Heatronic3, which has a different poll'ing logic // check to see if its a Junkers Heatronic3, which has a different poll'ing logic
if (EMS_Boiler.product_id == EMS_PRODUCTID_HEATRONICS) { if (EMS_Boiler.product_id == EMS_PRODUCTID_HEATRONICS) {
EMS_Sys_Status.emsReverse = true; EMS_Sys_Status.emsIDMask = 0x80;
EMS_Sys_Status.emsPollAck[0] = EMS_ID_ME ^ EMS_Sys_Status.emsIDMask;
} }
myESP.fs_saveConfig(); // save config to SPIFFS myESP.fs_saveConfig(); // save config to SPIFFS

View File

@@ -29,39 +29,39 @@
#define GPIO_H(mask) (GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, (mask))) #define GPIO_H(mask) (GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, (mask)))
#define GPIO_L(mask) (GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, (mask))) #define GPIO_L(mask) (GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, (mask)))
#define RX_PULSE(pulse) \ #define RX_PULSE(pulse) \
do { \ do { \
GPIO_H(RX_MARK_MASK); \ GPIO_H(RX_MARK_MASK); \
delayMicroseconds(pulse); \ delayMicroseconds(pulse); \
GPIO_L(RX_MARK_MASK); \ GPIO_L(RX_MARK_MASK); \
} while (0) } while (0)
#define TX_PULSE(pulse) \ #define TX_PULSE(pulse) \
do { \ do { \
GPIO_H(TX_MARK_MASK); \ GPIO_H(TX_MARK_MASK); \
delayMicroseconds(pulse); \ delayMicroseconds(pulse); \
GPIO_L(TX_MARK_MASK); \ GPIO_L(TX_MARK_MASK); \
} while (0) } while (0)
#define LA_PULSE(pulse) \ #define LA_PULSE(pulse) \
do { \ do { \
GPIO_H(MARKERS_MASK); \ GPIO_H(MARKERS_MASK); \
delayMicroseconds(pulse); \ delayMicroseconds(pulse); \
GPIO_L(MARKERS_MASK); \ GPIO_L(MARKERS_MASK); \
} while (0) } while (0)
#define INIT_MARKERS(void) \ #define INIT_MARKERS(void) \
do { \ do { \
pinMode(RX_MARK_PIN, OUTPUT); \ pinMode(RX_MARK_PIN, OUTPUT); \
pinMode(TX_MARK_PIN, OUTPUT); \ pinMode(TX_MARK_PIN, OUTPUT); \
GPIO_L(MARKERS_MASK); \ GPIO_L(MARKERS_MASK); \
} while (0) } while (0)
#else #else
#define RX_PULSE(pulse) \ #define RX_PULSE(pulse) \
{} {}
#define TX_PULSE(pulse) \ #define TX_PULSE(pulse) \
{} {}
#define LA_PULSE(pulse) \ #define LA_PULSE(pulse) \
{} {}
#define INIT_MARKERS(void) \ #define INIT_MARKERS(void) \
{} {}
#define RX_MARK_MASK #define RX_MARK_MASK
#define TX_MARK_MASK #define TX_MARK_MASK
@@ -124,7 +124,8 @@ typedef enum {
EMS_TX_STATUS_IDLE, // ready EMS_TX_STATUS_IDLE, // ready
EMS_TX_STATUS_WAIT, // waiting for response from last Tx EMS_TX_STATUS_WAIT, // waiting for response from last Tx
EMS_TX_WTD_TIMEOUT, // watchdog timeout during send EMS_TX_WTD_TIMEOUT, // watchdog timeout during send
EMS_TX_BRK_DETECT // incoming BRK during Tx EMS_TX_BRK_DETECT, // incoming BRK during Tx
EMS_TX_REV_DETECT // waiting to detect reverse bit
} _EMS_TX_STATUS; } _EMS_TX_STATUS;
#define EMS_TX_SUCCESS 0x01 // EMS single byte after a Tx Write indicating a success #define EMS_TX_SUCCESS 0x01 // EMS single byte after a Tx Write indicating a success
@@ -145,7 +146,8 @@ typedef enum {
EMS_SYS_LOGGING_BASIC, // only basic read/write messages EMS_SYS_LOGGING_BASIC, // only basic read/write messages
EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat
EMS_SYS_LOGGING_SOLARMODULE, // only telegrams sent from thermostat EMS_SYS_LOGGING_SOLARMODULE, // only telegrams sent from thermostat
EMS_SYS_LOGGING_VERBOSE // everything EMS_SYS_LOGGING_VERBOSE, // everything
EMS_SYS_LOGGING_JABBER // lots of debug output...
} _EMS_SYS_LOGGING; } _EMS_SYS_LOGGING;
// status/counters since last power on // status/counters since last power on
@@ -164,8 +166,9 @@ typedef struct {
bool emsTxCapable; // able to send via Tx bool emsTxCapable; // able to send via Tx
bool emsTxDisabled; // true to prevent all Tx bool emsTxDisabled; // true to prevent all Tx
uint8_t txRetryCount; // # times the last Tx was re-sent uint8_t txRetryCount; // # times the last Tx was re-sent
bool emsReverse; // if true, poll logic is reversed
uint8_t emsTxMode; // handles Tx logic uint8_t emsTxMode; // handles Tx logic
uint8_t emsIDMask; // Buderus: 0x00, Junkers: 0x80
uint8_t emsPollAck[1]; // acknowledge buffer
} _EMS_Sys_Status; } _EMS_Sys_Status;
// The Tx send package // The Tx send package
@@ -390,6 +393,7 @@ typedef struct {
} _EMS_Type; } _EMS_Type;
// function definitions // function definitions
extern void ems_dumpBuffer(const char *prefix, uint8_t *telegram, uint8_t length);
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len); extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
void ems_init(); void ems_init();
void ems_doReadCommand(uint16_t type, uint8_t dest, bool forceRefresh = false); void ems_doReadCommand(uint16_t type, uint8_t dest, bool forceRefresh = false);

View File

@@ -13,6 +13,7 @@
_EMSRxBuf * pEMSRxBuf; _EMSRxBuf * pEMSRxBuf;
_EMSRxBuf * paEMSRxBuf[EMS_MAXBUFFERS]; _EMSRxBuf * paEMSRxBuf[EMS_MAXBUFFERS];
uint8_t emsRxBufIdx = 0; uint8_t emsRxBufIdx = 0;
uint8_t phantomBreak= 0;
os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen]; // our Rx queue os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen]; // our Rx queue
@@ -22,7 +23,7 @@ os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen]; // our Rx queue
// //
static void emsuart_rx_intr_handler(void * para) { static void emsuart_rx_intr_handler(void * para) {
static uint8_t length; static uint8_t length;
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE]; static uint8_t uart_buffer[EMS_MAXBUFFERSIZE + 2];
// is a new buffer? if so init the thing for a new telegram // is a new buffer? if so init the thing for a new telegram
if (EMS_Sys_Status.emsRxStatus == EMS_RX_STATUS_IDLE) { if (EMS_Sys_Status.emsRxStatus == EMS_RX_STATUS_IDLE) {
@@ -33,7 +34,9 @@ static void emsuart_rx_intr_handler(void * para) {
// fill IRQ buffer, by emptying Rx FIFO // fill IRQ buffer, by emptying Rx FIFO
if (USIS(EMSUART_UART) & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) { if (USIS(EMSUART_UART) & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) {
while ((USS(EMSUART_UART) >> USRXC) & 0xFF) { while ((USS(EMSUART_UART) >> USRXC) & 0xFF) {
uart_buffer[length++] = USF(EMSUART_UART); uint8_t rx = USF(EMSUART_UART);
if (length < EMS_MAXBUFFERSIZE)
uart_buffer[length++] = rx;
} }
// clear Rx FIFO full and Rx FIFO timeout interrupts // clear Rx FIFO full and Rx FIFO timeout interrupts
@@ -46,8 +49,9 @@ static void emsuart_rx_intr_handler(void * para) {
ETS_UART_INTR_DISABLE(); // disable all interrupts and clear them ETS_UART_INTR_DISABLE(); // disable all interrupts and clear them
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt
pEMSRxBuf->length = length; pEMSRxBuf->length = (length > EMS_MAXBUFFERSIZE) ? EMS_MAXBUFFERSIZE : length;
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, length); // copy data into transfer buffer, including the BRK 0x00 at the end os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, pEMSRxBuf->length); // copy data into transfer buffer, including the BRK 0x00 at the end
length = 0;
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE; // set the status flag stating BRK has been received and we can start a new package EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE; // set the status flag stating BRK has been received and we can start a new package
ETS_UART_INTR_ENABLE(); // re-enable UART interrupts ETS_UART_INTR_ENABLE(); // re-enable UART interrupts
@@ -67,18 +71,21 @@ static void ICACHE_FLASH_ATTR emsuart_recvTask(os_event_t * events) {
uint8_t length = pCurrent->length; // number of bytes including the BRK at the end uint8_t length = pCurrent->length; // number of bytes including the BRK at the end
pCurrent->length = 0; pCurrent->length = 0;
// validate and transmit the EMS buffer, excluding the BRK if (phantomBreak) {
phantomBreak = 0;
length--; // remove phantom break from Rx buffer
}
if (length == 2) { if (length == 2) {
RX_PULSE(20); RX_PULSE(20);
// it's a poll or status code, single byte and ok to send on // it's a poll or status code, single byte and ok to send on
ems_parseTelegram((uint8_t *)pCurrent->buffer, 1); ems_parseTelegram((uint8_t *)pCurrent->buffer, 1);
} else if ((length > 4) && (length <= EMS_MAXBUFFERSIZE + 1) && (pCurrent->buffer[length - 2] != 0x00)) { } else if ((length > 4) && (length <= EMS_MAXBUFFERSIZE + 1)) {
// ignore double BRK at the end, possibly from the Tx loopback // ignore double BRK at the end, possibly from the Tx loopback
// also telegrams with no data value // also telegrams with no data value
RX_PULSE(40); RX_PULSE(40);
ems_parseTelegram((uint8_t *)pCurrent->buffer, length - 1); // transmit EMS buffer, excluding the BRK ems_parseTelegram((uint8_t *)pCurrent->buffer, length - 1); // transmit EMS buffer, excluding the BRK
} }
// memset(pCurrent->buffer, 0x00, EMS_MAXBUFFERSIZE); // wipe memory just to be safe
} }
/* /*
@@ -122,10 +129,10 @@ void ICACHE_FLASH_ATTR emsuart_init() {
// UCFFT = RX FIFO Full Threshold (7 bit) = want this to be 31 for 32 bytes of buffer (default was 127) // UCFFT = RX FIFO Full Threshold (7 bit) = want this to be 31 for 32 bytes of buffer (default was 127)
// see https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf // see https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf
// //
// change: we set UCFFT to 1 to get an immediate indicator about incoming trafffic. // change: we set UCFFT to 1 to get an immediate indicator about incoming traffic.
// Otherwise, we're only noticed by UCTOT or RxBRK! // Otherwise, we're only noticed by UCTOT or RxBRK!
USC1(EMSUART_UART) = 0; // reset config first USC1(EMSUART_UART) = 0; // reset config first
USC1(EMSUART_UART) = (0x01 << UCFFT) | (0x01 << UCTOT) | (1 << UCTOE); // enable interupts USC1(EMSUART_UART) = (0x01 << UCFFT) | (0x01 << UCTOT) | (0 << UCTOE); // enable interupts
// set interrupts for triggers // set interrupts for triggers
USIC(EMSUART_UART) = 0xFFFF; // clear all interupts USIC(EMSUART_UART) = 0xFFFF; // clear all interupts
@@ -133,7 +140,8 @@ void ICACHE_FLASH_ATTR emsuart_init() {
// enable rx break, fifo full and timeout. // 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 // but not frame error UIFR (because they are too frequent) or overflow UIOF because our buffer is only max 32 bytes
USIE(EMSUART_UART) = (1 << UIBD) | (1 << UIFF) | (1 << UITO); // change: we don't care about Rx Timeout - it may lead to wrong readouts
USIE(EMSUART_UART) = (1 << UIBD) | (1 << UIFF) | (0 << UITO);
// set up interrupt callbacks for Rx // set up interrupt callbacks for Rx
system_os_task(emsuart_recvTask, EMSUART_recvTaskPrio, recvTaskQueue, EMSUART_recvTaskQueueLen); system_os_task(emsuart_recvTask, EMSUART_recvTaskPrio, recvTaskQueue, EMSUART_recvTaskQueueLen);
@@ -199,6 +207,7 @@ void ICACHE_FLASH_ATTR emsuart_tx_brk() {
*/ */
_EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) { _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
_EMS_TX_STATUS result = EMS_TX_STATUS_OK; _EMS_TX_STATUS result = EMS_TX_STATUS_OK;
ems_dumpBuffer("emsuart_tx_buffer: ", buf, len); // validate and transmit the EMS buffer, excluding the BRK
if (len) { if (len) {
LA_PULSE(50); LA_PULSE(50);
// temp code until we get mode 2 working without resets // temp code until we get mode 2 working without resets
@@ -254,9 +263,9 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
// shorter busy poll... // shorter busy poll...
#define EMSUART_BUSY_WAIT (EMSUART_BIT_TIME / 8) #define EMSUART_BUSY_WAIT (EMSUART_BIT_TIME / 8)
#define EMS_TX_TO_COUNT ((20 + 10000 / 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; uint16_t wdc = EMS_TX_TO_COUNT;
ETS_UART_INTR_DISABLE(); // disable rx interrupt ETS_UART_INTR_DISABLE(); // disable rx interrupt
// clear Rx status register // clear Rx status register
@@ -272,7 +281,6 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
USF(EMSUART_UART) = buf[i++]; // send each Tx byte USF(EMSUART_UART) = buf[i++]; // send each Tx byte
// wait for echo from busmaster // wait for echo from busmaster
GPIO_L(TX_MARK_MASK); GPIO_L(TX_MARK_MASK);
while (((USS(EMSUART_UART) >> USRXC) & 0xFF) == _usrxc) { while (((USS(EMSUART_UART) >> USRXC) & 0xFF) == _usrxc) {
delayMicroseconds(EMSUART_BUSY_WAIT); // burn CPU cycles... delayMicroseconds(EMSUART_BUSY_WAIT); // burn CPU cycles...
if (--wdc == 0) { if (--wdc == 0) {
@@ -301,11 +309,13 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
// wait until BRK detected... // wait until BRK detected...
while (!(USIR(EMSUART_UART) & (1 << UIBD))) { while (!(USIR(EMSUART_UART) & (1 << UIBD))) {
delayMicroseconds(EMSUART_BUSY_WAIT); // delayMicroseconds(EMSUART_BUSY_WAIT);
delayMicroseconds(EMSUART_BIT_TIME);
} }
USC0(EMSUART_UART) &= ~((1 << UCBRK) | (1 << UCLBE)); // disable loopback & clear <BRK> USC0(EMSUART_UART) &= ~((1 << UCBRK) | (1 << UCLBE)); // disable loopback & clear <BRK>
USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ
phantomBreak = 1;
} }
GPIO_L(TX_MARK_MASK); GPIO_L(TX_MARK_MASK);
} }
@@ -317,13 +327,8 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
/* /*
* Send the Poll (our own ID) to Tx as a single byte and end with a <BRK> * Send the Poll (our own ID) to Tx as a single byte and end with a <BRK>
*/ * *** moved to ems.cpp, renamed to ems_tx_pollAck
void ICACHE_FLASH_ATTR emsuart_tx_poll() { void ICACHE_FLASH_ATTR emsuart_tx_poll() {
static uint8_t buf[1]; static uint8_t buf[1] = {EMS_ID_ME ^ EMS_Sys_Status.emsIDMask};
if (EMS_Sys_Status.emsReverse) {
buf[0] = {EMS_ID_ME | 0x80};
} else {
buf[0] = {EMS_ID_ME};
}
emsuart_tx_buffer(buf, 1);
} }
*/

View File

@@ -37,4 +37,3 @@ void ICACHE_FLASH_ATTR emsuart_init();
void ICACHE_FLASH_ATTR emsuart_stop(); void ICACHE_FLASH_ATTR emsuart_stop();
void ICACHE_FLASH_ATTR emsuart_start(); void ICACHE_FLASH_ATTR emsuart_start();
_EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len); _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len);
void ICACHE_FLASH_ATTR emsuart_tx_poll();