mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
merge with txmode2 branch
This commit is contained in:
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.9.0b2_web] 2019-08-11
|
||||
|
||||
### Changed
|
||||
|
||||
- Merged with @susisstrolch's TxMode2 branch for improved support for sending EMS packages
|
||||
|
||||
## [1.9.0b1_web] 2019-08-02
|
||||
|
||||
### Breaking changes for first time use
|
||||
|
||||
7862
doc/HT3-Bus_Telegramme.html
Normal file
7862
doc/HT3-Bus_Telegramme.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -210,6 +210,8 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
// start OTA
|
||||
ArduinoOTA.begin(); // moved to support esp32
|
||||
myDebug_P(PSTR("[OTA] listening to %s.local:%u"), ArduinoOTA.getHostname().c_str(), OTA_PORT);
|
||||
// unconditionaly show the last reset reason
|
||||
myDebug_P(PSTR("[SYSTEM] Last reset info: %s"), (char *)ESP.getResetInfo().c_str());
|
||||
|
||||
// MQTT Setup
|
||||
_mqtt_setup();
|
||||
@@ -1114,6 +1116,7 @@ bool MyESP::_rtcmemStatus() {
|
||||
//case REASON_EXT_SYS_RST: // external system reset
|
||||
case REASON_WDT_RST: // hardware watch dog reset
|
||||
case REASON_DEFAULT_RST: // normal startup by power on
|
||||
case REASON_SOFT_WDT_RST: // Software watchdog
|
||||
readable = false;
|
||||
break;
|
||||
default:
|
||||
@@ -2565,7 +2568,7 @@ void MyESP::_bootupSequence() {
|
||||
|
||||
if (isWifiConnected()) {
|
||||
_setSystemBootStatus(MYESP_BOOTSTATUS_BOOTED); // completed, reset flag
|
||||
digitalWrite(LED_BUILTIN, HIGH); // turn off LED
|
||||
digitalWrite(LED_BUILTIN, HIGH); // turn off LED, 1=OFF with LED_BULLETIN
|
||||
|
||||
// write a log message if we're not using NTP, otherwise wait for the internet time to arrive
|
||||
if (!_ntp_enabled) {
|
||||
@@ -2617,12 +2620,14 @@ void MyESP::loop() {
|
||||
_systemCheckLoop();
|
||||
_heartbeatCheck();
|
||||
_bootupSequence(); // see if a reset was pressed during bootup
|
||||
_telnetHandle(); // telnet
|
||||
|
||||
ESP.wdtFeed(); // TODO see if this helps with WDT resets
|
||||
|
||||
jw.loop(); // WiFi
|
||||
ArduinoOTA.handle(); // OTA
|
||||
|
||||
ESP.wdtFeed(); // feed the watchdog...
|
||||
_telnetHandle(); // telnet
|
||||
ESP.wdtFeed(); // feed the watchdog...
|
||||
|
||||
_mqttConnect(); // MQTT
|
||||
|
||||
if (_timerequest) {
|
||||
|
||||
@@ -135,20 +135,6 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="row form-group">
|
||||
<label class="col-xs-3">TX mode<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
|
||||
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
|
||||
data-content="TX mode settings for various EMS brands"></i></label>
|
||||
<span class="col-xs-9 col-md-5">
|
||||
<select class="form-control input-sm" id="tx_mode">
|
||||
<option selected="selected" value="0">0=EMS 1.0 (default)</option>
|
||||
<option value="1">1=EMS+</option>
|
||||
<option value="2">2=Generic (experimental!)</option>
|
||||
<option value="3">3=Junkers</option>
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<div class="row form-group">
|
||||
<div class="col-xs-9 col-md-8">
|
||||
|
||||
@@ -9,8 +9,7 @@ var custom_config = {
|
||||
"shower_timer": false,
|
||||
"shower_alert": false,
|
||||
"publish_time": 120,
|
||||
"heating_circuit": 1,
|
||||
"tx_mode": 0
|
||||
"heating_circuit": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +23,6 @@ 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);
|
||||
@@ -74,7 +72,6 @@ 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();
|
||||
}
|
||||
|
||||
@@ -104,7 +104,6 @@ static const command_t project_cmds[] PROGMEM = {
|
||||
{true, "shower_alert <on | off>", "stop hot water to send 3 cold burst warnings after max shower time is exceeded"},
|
||||
{true, "publish_time <seconds>", "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 <n>", "changes Tx logic. 0=ems 1.0, 1=ems+, 2=generic (experimental!), 3=HT3"},
|
||||
|
||||
{false, "info", "show current captured on the devices"},
|
||||
{false, "log <n | b | t | r | v>", "set logging mode to none, basic, thermostat only, raw or verbose"},
|
||||
@@ -417,6 +416,8 @@ void showInfo() {
|
||||
myDebug_P(PSTR(" System logging set to Thermostat only"));
|
||||
} else if (sysLog == EMS_SYS_LOGGING_SOLARMODULE) {
|
||||
myDebug_P(PSTR(" System logging set to Solar Module only"));
|
||||
} else if (sysLog == EMS_SYS_LOGGING_JABBER) {
|
||||
myDebug_P(PSTR(" System logging set to Jabber"));
|
||||
} else {
|
||||
myDebug_P(PSTR(" System logging set to None"));
|
||||
}
|
||||
@@ -1141,8 +1142,6 @@ bool LoadSaveCallback(MYESP_FSACTION action, JsonObject json) {
|
||||
EMSESP_Status.shower_alert = settings["shower_alert"];
|
||||
EMSESP_Status.publish_time = settings["publish_time"] | DEFAULT_PUBLISHTIME;
|
||||
|
||||
ems_setTxMode(settings["tx_mode"]);
|
||||
|
||||
EMSESP_Status.listen_mode = settings["listen_mode"];
|
||||
ems_setTxDisabled(EMSESP_Status.listen_mode);
|
||||
|
||||
@@ -1164,7 +1163,6 @@ 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"] = ems_getTxMode();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1282,12 +1280,6 @@ bool SetListCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, co
|
||||
myDebug_P(PSTR("Error. Usage: set heating_circuit <1 | 2>"));
|
||||
}
|
||||
}
|
||||
|
||||
// tx delay/ tx mode
|
||||
if (((strcmp(setting, "tx_mode") == 0) || (strcmp(setting, "tx_delay") == 0)) && (wc == 2)) {
|
||||
ems_setTxMode(atoi(value));
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == MYESP_FSACTION_LIST) {
|
||||
@@ -1300,7 +1292,6 @@ bool SetListCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, co
|
||||
myDebug_P(PSTR(" shower_timer=%s"), EMSESP_Status.shower_timer ? "on" : "off");
|
||||
myDebug_P(PSTR(" shower_alert=%s"), EMSESP_Status.shower_alert ? "on" : "off");
|
||||
myDebug_P(PSTR(" publish_time=%d"), EMSESP_Status.publish_time);
|
||||
myDebug_P(PSTR(" tx_mode=%d"), ems_getTxMode());
|
||||
}
|
||||
|
||||
return ok;
|
||||
@@ -1437,6 +1428,9 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
|
||||
} else if (strcmp(second_cmd, "n") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "j") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_JABBER);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
159
src/ems.cpp
159
src/ems.cpp
@@ -221,7 +221,7 @@ void ems_init() {
|
||||
EMS_Sys_Status.emsTxPkgs = 0;
|
||||
EMS_Sys_Status.emxCrcErr = 0;
|
||||
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.emsPollEnabled = false; // start up with Poll disabled
|
||||
EMS_Sys_Status.emsBusConnected = false;
|
||||
@@ -230,8 +230,8 @@ void ems_init() {
|
||||
EMS_Sys_Status.emsTxDisabled = false;
|
||||
EMS_Sys_Status.emsPollFrequency = 0;
|
||||
EMS_Sys_Status.txRetryCount = 0;
|
||||
EMS_Sys_Status.emsReverse = false;
|
||||
EMS_Sys_Status.emsTxMode = 0;
|
||||
EMS_Sys_Status.emsIDMask = 0x00;
|
||||
EMS_Sys_Status.emsPollAck[0] = EMS_ID_ME;
|
||||
|
||||
// thermostat
|
||||
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_SHORT_NOTSET;
|
||||
@@ -350,23 +350,6 @@ bool ems_getPoll() {
|
||||
return EMS_Sys_Status.emsPollEnabled;
|
||||
}
|
||||
|
||||
void ems_setTxMode(uint8_t mode) {
|
||||
EMS_Sys_Status.emsTxMode = mode;
|
||||
|
||||
// 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
|
||||
if (mode == 3) {
|
||||
EMS_Sys_Status.emsReverse = true;
|
||||
myDebug_P(PSTR("Forcing emsReverse for Junkers"));
|
||||
} else {
|
||||
EMS_Sys_Status.emsReverse = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ems_getTxMode() {
|
||||
return EMS_Sys_Status.emsTxMode;
|
||||
}
|
||||
|
||||
bool ems_getEmsRefreshed() {
|
||||
return EMS_Sys_Status.emsRefreshed;
|
||||
}
|
||||
@@ -434,7 +417,7 @@ _EMS_SYS_LOGGING ems_getLogging() {
|
||||
}
|
||||
|
||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel) {
|
||||
if (loglevel <= EMS_SYS_LOGGING_VERBOSE) {
|
||||
if (loglevel <= EMS_SYS_LOGGING_JABBER) {
|
||||
EMS_Sys_Status.emsLogging = loglevel;
|
||||
if (loglevel == EMS_SYS_LOGGING_NONE) {
|
||||
myDebug_P(PSTR("System Logging set to None"));
|
||||
@@ -448,10 +431,19 @@ void ems_setLogging(_EMS_SYS_LOGGING loglevel) {
|
||||
myDebug_P(PSTR("System Logging set to Solar Module only"));
|
||||
} else if (loglevel == EMS_SYS_LOGGING_RAW) {
|
||||
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
|
||||
* len is length of all the data in bytes (including the header & CRC byte at end)
|
||||
@@ -610,20 +602,25 @@ void _ems_sendTelegram() {
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) {
|
||||
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest;
|
||||
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest ^ EMS_Sys_Status.emsIDMask;
|
||||
} else {
|
||||
// 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+
|
||||
@@ -666,9 +663,15 @@ void _ems_sendTelegram() {
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -715,6 +718,60 @@ void _createValidate() {
|
||||
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
|
||||
* length is the number of all the telegram bytes up to and including the CRC at the end
|
||||
@@ -724,16 +781,28 @@ void _createValidate() {
|
||||
void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||
static uint32_t _last_emsPollFrequency = 0;
|
||||
|
||||
ems_dumpBuffer("ems_parseTelegram: ", telegram, length);
|
||||
/*
|
||||
* 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)
|
||||
* 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
|
||||
*
|
||||
* In addition, it may happen that we where interrupted (f.e. by WIFI activity) and the
|
||||
*/
|
||||
|
||||
/*
|
||||
* Detect the EMS bus type - Buderus or Junkers - and set emsIDMask accordingly.
|
||||
* we wait for the first valid telegram and look at the SourceID.
|
||||
* 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...
|
||||
*/
|
||||
if (EMS_Sys_Status.emsRxStatus != EMS_RX_STATUS_IDLE) {
|
||||
@@ -747,8 +816,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||
uint8_t value = telegram[0]; // 1st byte of data package
|
||||
|
||||
// check first for a Poll for us
|
||||
// the poll has the MSB set - seems to work on both EMS and Junkers
|
||||
if ((value & 0x7F) == EMS_ID_ME) {
|
||||
if ((value ^ 0x80 ^ EMS_Sys_Status.emsIDMask) == EMS_ID_ME) {
|
||||
EMS_Sys_Status.emsTxCapable = true;
|
||||
uint32_t timenow_microsecs = micros();
|
||||
EMS_Sys_Status.emsPollFrequency = (timenow_microsecs - _last_emsPollFrequency);
|
||||
@@ -761,7 +829,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||
} else {
|
||||
// nothing to send so just send a poll acknowledgement back
|
||||
if (EMS_Sys_Status.emsPollEnabled) {
|
||||
emsuart_tx_poll();
|
||||
ems_tx_pollAck();
|
||||
}
|
||||
}
|
||||
} else if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_WAIT) {
|
||||
@@ -769,14 +837,14 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||
if (value == EMS_TX_SUCCESS) {
|
||||
EMS_Sys_Status.emsTxPkgs++;
|
||||
// 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)
|
||||
} else if (value == EMS_TX_ERROR) {
|
||||
// last write failed (04), delete it from queue and dont bother to retry
|
||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -787,7 +855,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||
// ignore anything that doesn't resemble a proper telegram package
|
||||
// minimal is 5 bytes, excluding CRC at the end
|
||||
if (length <= 4) {
|
||||
//_debugPrintTelegram("Noisy data:", &EMS_RxTelegram COLOR_RED);
|
||||
// _debugPrintTelegram("Noisy data:", &EMS_RxTelegram, COLOR_RED);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -835,7 +903,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||
|
||||
// if we are in raw logging mode then just print out the telegram as it is
|
||||
// but still continue to process it
|
||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_RAW) {
|
||||
if ((EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_RAW)) {
|
||||
_debugPrintTelegram("", &EMS_RxTelegram, COLOR_WHITE, true);
|
||||
}
|
||||
|
||||
@@ -1030,7 +1098,8 @@ void _processType(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
|
||||
// if its an echo of ourselves from the master UBA, ignore. This should never happen mind you
|
||||
if (EMS_RxTelegram->src == EMS_ID_ME) {
|
||||
// _debugPrintTelegram("echo:", EMS_RxTelegram, COLOR_WHITE);
|
||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_JABBER)
|
||||
_debugPrintTelegram("echo:", EMS_RxTelegram, COLOR_WHITE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1138,10 +1207,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
|
||||
* using a quick hack for checking the heating. Selected Flow Temp >= 70
|
||||
@@ -1677,7 +1745,8 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
|
||||
// check to see if its a Junkers Heatronic3, which has a different poll'ing logic
|
||||
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;
|
||||
}
|
||||
|
||||
ems_getBoilerValues(); // get Boiler values that we would usually have to wait for
|
||||
@@ -1816,9 +1885,11 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
|
||||
* Do a read command for the version with the src having the MSB set
|
||||
*/
|
||||
void _ems_detectJunkers() {
|
||||
#ifdef JUNKERS_DETECT
|
||||
char s[30] = {0};
|
||||
snprintf(s, sizeof(s), "%02X %02X %02X 00 %02X", (EMS_ID_ME | 0x80), (EMS_ID_BOILER | 0x080), EMS_TYPE_Version, EMS_MAX_TELEGRAM_LENGTH);
|
||||
ems_sendRawTelegram(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
10
src/ems.h
10
src/ems.h
@@ -125,7 +125,8 @@ typedef enum {
|
||||
EMS_TX_STATUS_IDLE, // ready
|
||||
EMS_TX_STATUS_WAIT, // waiting for response from last Tx
|
||||
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;
|
||||
|
||||
#define EMS_TX_SUCCESS 0x01 // EMS single byte after a Tx Write indicating a success
|
||||
@@ -146,7 +147,8 @@ typedef enum {
|
||||
EMS_SYS_LOGGING_BASIC, // only basic read/write messages
|
||||
EMS_SYS_LOGGING_THERMOSTAT, // 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;
|
||||
|
||||
// status/counters since last power on
|
||||
@@ -165,8 +167,9 @@ typedef struct {
|
||||
bool emsTxCapable; // able to send via Tx
|
||||
bool emsTxDisabled; // true to prevent all Tx
|
||||
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 emsIDMask; // Buderus: 0x00, Junkers: 0x80
|
||||
uint8_t emsPollAck[1]; // acknowledge buffer
|
||||
} _EMS_Sys_Status;
|
||||
|
||||
// The Tx send package
|
||||
@@ -392,6 +395,7 @@ typedef struct {
|
||||
} _EMS_Type;
|
||||
|
||||
// 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);
|
||||
void ems_init();
|
||||
void ems_doReadCommand(uint16_t type, uint8_t dest, bool forceRefresh = false);
|
||||
|
||||
109
src/emsuart.cpp
109
src/emsuart.cpp
@@ -12,6 +12,7 @@
|
||||
_EMSRxBuf * pEMSRxBuf;
|
||||
_EMSRxBuf * paEMSRxBuf[EMS_MAXBUFFERS];
|
||||
uint8_t emsRxBufIdx = 0;
|
||||
uint8_t phantomBreak = 0;
|
||||
|
||||
os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen]; // our Rx queue
|
||||
|
||||
@@ -21,7 +22,7 @@ os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen]; // our Rx queue
|
||||
//
|
||||
static void emsuart_rx_intr_handler(void * para) {
|
||||
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
|
||||
if (EMS_Sys_Status.emsRxStatus == EMS_RX_STATUS_IDLE) {
|
||||
@@ -32,7 +33,9 @@ static void emsuart_rx_intr_handler(void * para) {
|
||||
// fill IRQ buffer, by emptying Rx FIFO
|
||||
if (USIS(EMSUART_UART) & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) {
|
||||
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
|
||||
@@ -45,8 +48,9 @@ static void emsuart_rx_intr_handler(void * para) {
|
||||
ETS_UART_INTR_DISABLE(); // disable all interrupts and clear them
|
||||
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt
|
||||
|
||||
pEMSRxBuf->length = length;
|
||||
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, length); // copy data into transfer buffer, including the BRK 0x00 at the end
|
||||
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
|
||||
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
|
||||
ETS_UART_INTR_ENABLE(); // re-enable UART interrupts
|
||||
|
||||
@@ -66,18 +70,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
|
||||
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) {
|
||||
RX_PULSE(20);
|
||||
// it's a poll or status code, single byte and ok to send on
|
||||
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
|
||||
// also telegrams with no data value
|
||||
RX_PULSE(40);
|
||||
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
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -124,7 +131,7 @@ void ICACHE_FLASH_ATTR emsuart_init() {
|
||||
// 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) | (1 << UCTOE); // enable interupts
|
||||
USC1(EMSUART_UART) = (0x01 << UCFFT) | (0x01 << UCTOT) | (0 << UCTOE); // enable interupts
|
||||
|
||||
// set interrupts for triggers
|
||||
USIC(EMSUART_UART) = 0xFFFF; // clear all interupts
|
||||
@@ -132,7 +139,8 @@ void ICACHE_FLASH_ATTR emsuart_init() {
|
||||
|
||||
// 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
|
||||
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
|
||||
system_os_task(emsuart_recvTask, EMSUART_recvTaskPrio, recvTaskQueue, EMSUART_recvTaskQueueLen);
|
||||
@@ -162,72 +170,14 @@ 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 <BRK> 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 <BRK>
|
||||
*/
|
||||
_EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
||||
_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) {
|
||||
LA_PULSE(50);
|
||||
// temp code until we get mode 2 working without resets
|
||||
if (EMS_Sys_Status.emsTxMode == 0) { // classic mode logic
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
TX_PULSE(EMSUART_BIT_TIME / 4);
|
||||
USF(EMSUART_UART) = buf[i];
|
||||
}
|
||||
emsuart_tx_brk(); // send <BRK>
|
||||
} else if (EMS_Sys_Status.emsTxMode == 1) { // 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 <BRK>
|
||||
} 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 <BRK>
|
||||
} else if (EMS_Sys_Status.emsTxMode == 2) {
|
||||
/*
|
||||
*
|
||||
* based on code from https://github.com/proddy/EMS-ESP/issues/103 by @susisstrolch
|
||||
@@ -253,9 +203,9 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
||||
|
||||
// shorter busy poll...
|
||||
#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;
|
||||
|
||||
ETS_UART_INTR_DISABLE(); // disable rx interrupt
|
||||
|
||||
// clear Rx status register
|
||||
@@ -271,7 +221,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
|
||||
// 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) {
|
||||
@@ -300,29 +249,17 @@ _EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
||||
|
||||
// wait until BRK detected...
|
||||
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>
|
||||
USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ
|
||||
phantomBreak = 1;
|
||||
}
|
||||
GPIO_L(TX_MARK_MASK);
|
||||
}
|
||||
ETS_UART_INTR_ENABLE(); // receive anything from FIFO...
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send the Poll (our own ID) to Tx as a single byte and end with a <BRK>
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR emsuart_tx_poll() {
|
||||
static uint8_t buf[1];
|
||||
if (EMS_Sys_Status.emsReverse) {
|
||||
buf[0] = {EMS_ID_ME | 0x80};
|
||||
} else {
|
||||
buf[0] = {EMS_ID_ME};
|
||||
}
|
||||
emsuart_tx_buffer(buf, 1);
|
||||
}
|
||||
|
||||
@@ -36,4 +36,3 @@ void ICACHE_FLASH_ATTR emsuart_init();
|
||||
void ICACHE_FLASH_ATTR emsuart_stop();
|
||||
void ICACHE_FLASH_ATTR emsuart_start();
|
||||
_EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len);
|
||||
void ICACHE_FLASH_ATTR emsuart_tx_poll();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
#define APP_NAME "EMS-ESP"
|
||||
#define APP_VERSION "1.9.0b1_web"
|
||||
#define APP_VERSION "1.9.0b2_web"
|
||||
#define APP_HOSTNAME "ems-esp"
|
||||
#define APP_HELPURL "https://github.com/proddy/EMS-ESP/wiki"
|
||||
#define APP_UPDATEURL "https://api.github.com/repos/proddy/EMS-ESP/releases/latest"
|
||||
|
||||
@@ -106,8 +106,7 @@ var custom_configfile = {
|
||||
"shower_timer": false,
|
||||
"shower_alert": false,
|
||||
"publish_time": 120,
|
||||
"heating_circuit": 1,
|
||||
"tx_mode": 2
|
||||
"heating_circuit": 1
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user