release 1.5.0

This commit is contained in:
proddy
2019-02-03 20:44:15 +01:00
parent 43ed0a30db
commit 83bad9dbaa
21 changed files with 466 additions and 979 deletions

View File

@@ -5,6 +5,24 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.5.0] 2019-02-
### Added
- Support for RC10 thermostat
### Changed
- removed MQTT and WIFI settings from my_config.h. These have to be set either within the application (using set) or hardcoded in platformio.ini
- Improved Tx logic. Retries are more efficient and startup is faster
- # Rx telegrams and # Tx telegrams show number of successful Reads and Writes initiated by the user or automatically
### Removed
- poll and tx commands
- DEBUG_SUPPORT. Now controlled with the 'set serial' command
- TxCapable removed
## [1.4.1] 2019-01-29 ## [1.4.1] 2019-01-29
### Added ### Added

View File

@@ -73,7 +73,7 @@ The code and circuit has been tested with a few ESP8266 development boards such
Use the telnet client to inform you of all activity and errors real-time. This is an example of the telnet output: Use the telnet client to inform you of all activity and errors real-time. This is an example of the telnet output:
![Telnet](doc/telnet/telnet_example.jpg) ![Telnet](doc/telnet/telnet_menu.jpg)
Type 'log v' and Enter and you'll be seeing verbose logging messages. ANSI colors with white text for info messages, green are for broadcast telegrams, yellow are the ones sent to us and red are for unknown data or telegrans which have failed the CRC check. Type 'log v' and Enter and you'll be seeing verbose logging messages. ANSI colors with white text for info messages, green are for broadcast telegrams, yellow are the ones sent to us and red are for unknown data or telegrans which have failed the CRC check.
@@ -83,7 +83,7 @@ To see the current stats and collected values type 'info'. Watch out for unsucce
![Telnet](doc/telnet/telnet_stats.PNG) ![Telnet](doc/telnet/telnet_stats.PNG)
**Disclaimer: be careful when sending values to the boiler. If in doubt you can always reset the boiler to its original factory settings by following the instructions in the user guide. On my **Nefit Trendline** that is done by holding down the Home and Menu buttons simultaneously for a few seconds, selecting factory settings from the scroll menu followed by pressing the Reset button.** **Disclaimer: be careful when sending values to the boiler. If in doubt you can always reset the boiler to its original factory settings by following the instructions in the user guide. For example on my Nefit Trendline that is done by holding down the Home and Menu buttons simultaneously for a few seconds, selecting factory settings from the scroll menu followed by pressing the Reset button.**
## Building The Circuit ## Building The Circuit
@@ -118,7 +118,7 @@ The EMS circuit will work with both 3.3V and 5V. It's easiest though to power di
## Adding external temperature sensors ## Adding external temperature sensors
The code supports auto-detection of Dallas type temperature sensors. The default gpio pin (DQ) to the ESP8266 is D5 but this can be configured in `my_config.h`. The dallas chips DS1822, DS18S20, DS18B20, DS1825 are supported including their parasite varieties. The code supports auto-detection of Dallas type temperature sensors. The default gpio pin used on the ESP8266 is D5 but this can be configured in the setting menu (`set dallas_gpio`). The dallas chips DS1822, DS18S20, DS18B20, DS1825 are supported including their parasite varieties.
## How The EMS Bus Works ## How The EMS Bus Works
@@ -211,6 +211,7 @@ In `ems.cpp` you can add scheduled calls to specific EMS types in the functions
I am still working on adding more support to known thermostats. Any contributions here are welcome. The know types are listed in `ems_devices.h` and include I am still working on adding more support to known thermostats. Any contributions here are welcome. The know types are listed in `ems_devices.h` and include
- RC20 and RC30, both are fully supported - RC20 and RC30, both are fully supported
- RC10 support is being added
- RC35 with support for the 1st heating circuit (HC1) - RC35 with support for the 1st heating circuit (HC1)
- TC100/TC200/Easy but only with support for reading the temperatures. There seems to be no way to set settings using EMS bus messages that I know of. One option is to send XMPP messages but a special server is needed and out of scope for this project. - TC100/TC200/Easy but only with support for reading the temperatures. There seems to be no way to set settings using EMS bus messages that I know of. One option is to send XMPP messages but a special server is needed and out of scope for this project.
@@ -234,7 +235,7 @@ Similarly the thermostat values are also sent as a JSON package with the topic `
If MQTT is not used set the MQTT_HOST to `NULL`. If MQTT is not used set the MQTT_HOST to `NULL`.
Some home automation systems such as Domoticz and OpenHab have special formats for their MQTT messages so I would advise to use [node-red](https://nodered.org/) as a parser. Some home automation systems such as Domoticz and OpenHab have special formats for their MQTT messages so I would advise to use [node-red](https://nodered.org/) as a parser like in [this example](https://www.domoticz.com/forum/download/file.php?id=18977&sid=67d048f1b4c8833822175eac6b55ecff).
### The Basic Shower Logic ### The Basic Shower Logic
@@ -286,10 +287,10 @@ edit `platformio.ini` to set `env_default` to your board type, then
### Building Using Arduino IDE ### Building Using Arduino IDE
Porting to the Arduino IDE can be a little tricky but it is possible. Porting to the Arduino IDE can be a little tricky but it did it once. Something along these lines:
- Add the ESP8266 boards (from Preferences add Additional Board URL `http://arduino.esp8266.com/stable/package_esp8266com_index.json`) - Add the ESP8266 boards (from Preferences add Additional Board URL `http://arduino.esp8266.com/stable/package_esp8266com_index.json`)
- Go to Boards Manager and install ESP8266 2.4.x platform - Go to Boards Manager and install ESP8266 2.4.x platform. Make sure your board supports SPIFFS.
- Select your ESP8266 from Tools->Boards and the correct port with Tools->Port - Select your ESP8266 from Tools->Boards and the correct port with Tools->Port
- From the Library Manager install the needed libraries from platformio.ini. Note make sure you pick ArduinoJson v5 (5.13.4 and above) and not v6. See https://arduinojson.org/v5/doc/ - From the Library Manager install the needed libraries from platformio.ini. Note make sure you pick ArduinoJson v5 (5.13.4 and above) and not v6. See https://arduinojson.org/v5/doc/
- Put all the files in a single sketch folder - Put all the files in a single sketch folder
@@ -304,13 +305,13 @@ pre-baked firmwares for some ESP8266 devices are available in the directory `/fi
3. Connect the ESP via USB, figure out the COM port 3. Connect the ESP via USB, figure out the COM port
4. run `esptool.py -p <com> write_flash 0x00000 <firmware>` where firmware is the `.bin` file and \<com\> is the COM port, e.g. `COM3` 4. run `esptool.py -p <com> write_flash 0x00000 <firmware>` where firmware is the `.bin` file and \<com\> is the COM port, e.g. `COM3`
The ESP8266 will start in Access Point (AP) mode. Connect via WiFi to the SSID **EMS-ESP** and telnet to **192.168.4.1**. Then use the set command to configure your own network settings. The ESP8266 will start in Access Point (AP) mode. Connect via WiFi to the SSID **EMS-ESP** and telnet to **192.168.4.1**. Then use the set command to configure your own network settings. Alternatively connect the ESP8266 to your PC and open a Serial monitor to configure the settings. Make sure you disable Serial support before connecting the EMS lines using `set serial off`.
`set erase` will clear all settings. `set` wil show all settings. `set erase` will clear all settings. `set` wil show all settings.
## Troubleshooting ## Troubleshooting
If the WiFi fails to connect you won't be able to debug using the Telnet so re-build the firmware using the `-DDEBUG_SUPPORT` by un-commenting `debug_mode` in `platformio.ini`. Then connect the ESP8266 to a USB in your computer and monitor the Serial output. A lot of detailed logging will be printed to help you pinpoint the cause of the error for the Wifi, MQTT, MDNS and other things. When flashing for the first time the Serial port is enabled by default. You can then use a PC with USB to the ESP8266 to set the settings like wifi, mqtt etc and also monitor the boot up procedure. Remember to disable the serial (`set serial off`) when connecting to the EMS lines.
The onboard LED will flash if there is no connection with the EMS bus. You can disable LED support by the 'set led' command from the telnet client The onboard LED will flash if there is no connection with the EMS bus. You can disable LED support by the 'set led' command from the telnet client
@@ -318,7 +319,7 @@ The onboard LED will flash if there is no connection with the EMS bus. You can d
Some annoying issues that need fixing: Some annoying issues that need fixing:
- Very infrequently an EMS write command is not sent, probably due to a collision somewhere in the UART between an incoming Rx and a Poll. The retries in the code fix this for now. - On newer EMS+ Boilers the Tx commands for reading and writing may not always work. I believe there is some handshake that needs to happen before the UBA3/Master is able to send a poll request to our service device.
## Wish List ## Wish List

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

BIN
doc/telnet/telnet_menu.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

View File

@@ -273,6 +273,22 @@ size_t TelnetSpy::write(uint8_t data) {
return 1; return 1;
} }
// this still needs some work
bool TelnetSpy::isSerialAvailable(void) {
if (usedSer) {
usedSer->write("test");
//Wait for four seconds or till data is available on serial, whichever occurs first.
while (usedSer->available() == 0 && millis() < 4000)
;
if (usedSer->available() > 0) {
(void)usedSer->read(); // We then clear the input buffer
return true;
}
}
return false;
}
int TelnetSpy::available(void) { int TelnetSpy::available(void) {
if (usedSer) { if (usedSer) {
int avail = usedSer->available(); int avail = usedSer->available();
@@ -538,6 +554,12 @@ void TelnetSpy::setCallbackOnDisconnect(telnetSpyCallback callback) {
callbackDisconnect = callback; callbackDisconnect = callback;
} }
void TelnetSpy::serialPrint(char c) {
if (usedSer) {
usedSer->print(c);
}
}
void TelnetSpy::handle() { void TelnetSpy::handle() {
if (firstMainLoop) { if (firstMainLoop) {
firstMainLoop = false; firstMainLoop = false;

View File

@@ -186,6 +186,7 @@ class TelnetSpy : public Stream {
void setPingTime(uint16_t pngTime); void setPingTime(uint16_t pngTime);
void setSerial(HardwareSerial * usedSerial); void setSerial(HardwareSerial * usedSerial);
bool isClientConnected(); bool isClientConnected();
void serialPrint(char c);
void disconnectClient(); // added by Proddy void disconnectClient(); // added by Proddy
typedef std::function<void()> telnetSpyCallback; // added by Proddy typedef std::function<void()> telnetSpyCallback; // added by Proddy
@@ -241,6 +242,8 @@ class TelnetSpy : public Stream {
void setDebugOutput(bool); void setDebugOutput(bool);
uint32_t baudRate(void); uint32_t baudRate(void);
bool isSerialAvailable(void);
protected: protected:
void sendBlock(void); void sendBlock(void);
void addTelnetBuf(char c); void addTelnetBuf(char c);

View File

@@ -1,632 +0,0 @@
/*
* TELNET SERVER FOR ESP8266 / ESP32
* Cloning the serial port via Telnet.
*
* Written by Wolfgang Mattis (arduino@yasheena.de).
* Version 1.1 / September 7, 2018.
* MIT license, all text above must be included in any redistribution.
*/
#ifdef ESP8266
extern "C" {
#include "user_interface.h"
}
#endif
#include "TelnetSpy.h"
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
static TelnetSpy * actualObject = NULL;
static void TelnetSpy_putc(char c) {
if (actualObject) {
actualObject->write(c);
}
}
static void TelnetSpy_ignore_putc(char c) {
;
}
TelnetSpy::TelnetSpy() {
port = TELNETSPY_PORT;
telnetServer = NULL;
started = false;
listening = false;
firstMainLoop = true;
usedSer = &Serial;
storeOffline = true;
connected = false;
callbackConnect = NULL;
callbackDisconnect = NULL;
welcomeMsg = strdup(TELNETSPY_WELCOME_MSG);
rejectMsg = strdup(TELNETSPY_REJECT_MSG);
minBlockSize = TELNETSPY_MIN_BLOCK_SIZE;
collectingTime = TELNETSPY_COLLECTING_TIME;
maxBlockSize = TELNETSPY_MAX_BLOCK_SIZE;
pingTime = TELNETSPY_PING_TIME;
pingRef = 0xFFFFFFFF;
waitRef = 0xFFFFFFFF;
telnetBuf = NULL;
bufLen = 0;
uint16_t size = TELNETSPY_BUFFER_LEN;
while (!setBufferSize(size)) {
size = size >> 1;
if (size < minBlockSize) {
setBufferSize(minBlockSize);
break;
}
}
debugOutput = TELNETSPY_CAPTURE_OS_PRINT;
if (debugOutput) {
setDebugOutput(true);
}
}
TelnetSpy::~TelnetSpy() {
end();
}
// added by proddy
void TelnetSpy::disconnectClient() {
if (client.connected()) {
client.flush();
client.stop();
}
if (connected && (callbackDisconnect != NULL)) {
callbackDisconnect();
}
connected = false;
}
void TelnetSpy::setPort(uint16_t portToUse) {
port = portToUse;
if (listening) {
if (client.connected()) {
client.flush();
client.stop();
}
if (connected && (callbackDisconnect != NULL)) {
callbackDisconnect();
}
connected = false;
telnetServer->close();
delete telnetServer;
telnetServer = new WiFiServer(port);
if (started) {
telnetServer->begin();
telnetServer->setNoDelay(bufLen > 0);
}
}
}
void TelnetSpy::setWelcomeMsg(char * msg) {
if (welcomeMsg) {
free(welcomeMsg);
}
welcomeMsg = strdup(msg);
}
void TelnetSpy::setRejectMsg(char * msg) {
if (rejectMsg) {
free(rejectMsg);
}
rejectMsg = strdup(msg);
}
void TelnetSpy::setMinBlockSize(uint16_t minSize) {
minBlockSize = min(max((uint16_t)1, minSize), maxBlockSize);
}
void TelnetSpy::setCollectingTime(uint16_t colTime) {
collectingTime = colTime;
}
void TelnetSpy::setMaxBlockSize(uint16_t maxSize) {
maxBlockSize = max(maxSize, minBlockSize);
}
bool TelnetSpy::setBufferSize(uint16_t newSize) {
if (telnetBuf && (bufLen == newSize)) {
return true;
}
if (newSize == 0) {
bufLen = 0;
if (telnetBuf) {
free(telnetBuf);
telnetBuf = NULL;
}
if (telnetServer) {
telnetServer->setNoDelay(false);
}
return true;
}
newSize = max(newSize, minBlockSize);
uint16_t oldBufLen = bufLen;
bufLen = newSize;
uint16_t tmp;
if (!telnetBuf || (bufUsed == 0)) {
bufRdIdx = 0;
bufWrIdx = 0;
bufUsed = 0;
} else {
if (bufLen < oldBufLen) {
if (bufRdIdx < bufWrIdx) {
if (bufWrIdx > bufLen) {
tmp = min(bufLen, (uint16_t)(bufWrIdx - max(bufLen, bufRdIdx)));
memcpy(telnetBuf, &telnetBuf[bufWrIdx - tmp], tmp);
bufWrIdx = tmp;
if (bufWrIdx > bufRdIdx) {
bufRdIdx = bufWrIdx;
} else {
if (bufRdIdx > bufLen) {
bufRdIdx = 0;
}
}
if (bufRdIdx == bufWrIdx) {
bufUsed = bufLen;
} else {
bufUsed = bufWrIdx - bufRdIdx;
}
}
} else {
if (bufWrIdx > bufLen) {
memcpy(telnetBuf, &telnetBuf[bufWrIdx - bufLen], bufLen);
bufRdIdx = 0;
bufWrIdx = 0;
bufUsed = bufLen;
} else {
tmp = min(bufLen - bufWrIdx, oldBufLen - bufRdIdx);
memcpy(&telnetBuf[bufLen - tmp], &telnetBuf[oldBufLen - tmp], tmp);
bufRdIdx = bufLen - tmp;
bufUsed = bufWrIdx + tmp;
}
}
}
}
char * temp = (char *)realloc(telnetBuf, bufLen);
if (!temp) {
return false;
}
telnetBuf = temp;
if (telnetBuf && (bufLen > oldBufLen) && (bufRdIdx > bufWrIdx)) {
tmp = bufLen - (oldBufLen - bufRdIdx);
memcpy(&telnetBuf[tmp], &telnetBuf[bufRdIdx], oldBufLen - bufRdIdx);
bufRdIdx = tmp;
}
if (telnetServer) {
telnetServer->setNoDelay(true);
}
return true;
}
uint16_t TelnetSpy::getBufferSize() {
if (!telnetBuf) {
return 0;
}
return bufLen;
}
void TelnetSpy::setStoreOffline(bool store) {
storeOffline = store;
}
bool TelnetSpy::getStoreOffline() {
return storeOffline;
}
void TelnetSpy::setPingTime(uint16_t pngTime) {
pingTime = pngTime;
if (pingTime == 0) {
pingRef = 0xFFFFFFFF;
} else {
pingRef = (millis() & 0x7FFFFFF) + pingTime;
}
}
void TelnetSpy::setSerial(HardwareSerial * usedSerial) {
usedSer = usedSerial;
}
size_t TelnetSpy::write(uint8_t data) {
if (telnetBuf) {
if (storeOffline || client.connected()) {
if (bufUsed == bufLen) {
if (client.connected()) {
sendBlock();
}
if (bufUsed == bufLen) {
char c;
while (bufUsed > 0) {
c = pullTelnetBuf();
if (c == '\n') {
addTelnetBuf('\r');
break;
}
}
if (peekTelnetBuf() == '\r') {
pullTelnetBuf();
}
}
}
addTelnetBuf(data);
/*
if (data == '\n') {
addTelnetBuf('\r'); // added by proddy, fix for Windows
}
*/
}
} else {
if (client.connected()) {
client.write(data);
}
}
if (usedSer) {
return usedSer->write(data);
}
return 1;
}
int TelnetSpy::available(void) {
if (usedSer) {
int avail = usedSer->available();
if (avail > 0) {
return avail;
}
}
if (client.connected()) {
return telnetAvailable();
}
return 0;
}
int TelnetSpy::read(void) {
int val;
if (usedSer) {
val = usedSer->read();
if (val != -1) {
return val;
}
}
if (client.connected()) {
if (telnetAvailable()) {
val = client.read();
}
}
return val;
}
int TelnetSpy::peek(void) {
int val;
if (usedSer) {
val = usedSer->peek();
if (val != -1) {
return val;
}
}
if (client.connected()) {
if (telnetAvailable()) {
val = client.peek();
}
}
return val;
}
void TelnetSpy::flush(void) {
if (usedSer) {
usedSer->flush();
}
}
#ifdef ESP8266
void TelnetSpy::begin(unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin) {
if (usedSer) {
usedSer->begin(baud, config, mode, tx_pin);
}
started = true;
}
#else // ESP32
void TelnetSpy::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert) {
if (usedSer) {
usedSer->begin(baud, config, rxPin, txPin, invert);
}
started = true;
}
#endif
void TelnetSpy::end() {
if (debugOutput) {
setDebugOutput(false);
}
if (usedSer) {
usedSer->end();
}
if (client.connected()) {
client.flush();
client.stop();
}
if (connected && (callbackDisconnect != NULL)) {
callbackDisconnect();
}
connected = false;
telnetServer->close();
delete telnetServer;
telnetServer = NULL;
listening = false;
started = false;
}
#ifdef ESP8266
void TelnetSpy::swap(uint8_t tx_pin) {
if (usedSer) {
usedSer->swap(tx_pin);
}
}
void TelnetSpy::set_tx(uint8_t tx_pin) {
if (usedSer) {
usedSer->set_tx(tx_pin);
}
}
void TelnetSpy::pins(uint8_t tx, uint8_t rx) {
if (usedSer) {
usedSer->pins(tx, rx);
}
}
bool TelnetSpy::isTxEnabled(void) {
if (usedSer) {
return usedSer->isTxEnabled();
}
return true;
}
bool TelnetSpy::isRxEnabled(void) {
if (usedSer) {
return usedSer->isRxEnabled();
}
return true;
}
#endif
int TelnetSpy::availableForWrite(void) {
if (usedSer) {
return min(usedSer->availableForWrite(), bufLen - bufUsed);
}
return bufLen - bufUsed;
}
TelnetSpy::operator bool() const {
if (usedSer) {
return (bool)*usedSer;
}
return true;
}
void TelnetSpy::setDebugOutput(bool en) {
debugOutput = en;
if (debugOutput) {
actualObject = this;
#ifdef ESP8266
os_install_putc1((void *)TelnetSpy_putc); // Set system printing (os_printf) to TelnetSpy
system_set_os_print(true);
#else // ESP32 \
// ToDo: How can be done this for ESP32 ?
#endif
} else {
if (actualObject == this) {
#ifdef ESP8266
system_set_os_print(false);
os_install_putc1((void *)TelnetSpy_ignore_putc); // Ignore system printing
#else // ESP32 \
// ToDo: How can be done this for ESP32 ?
#endif
actualObject = NULL;
}
}
}
uint32_t TelnetSpy::baudRate(void) {
if (usedSer) {
return usedSer->baudRate();
}
return 115200;
}
void TelnetSpy::sendBlock() {
uint16_t len = bufUsed;
if (len > maxBlockSize) {
len = maxBlockSize;
}
len = min(len, (uint16_t)(bufLen - bufRdIdx));
client.write(&telnetBuf[bufRdIdx], len);
bufRdIdx += len;
if (bufRdIdx >= bufLen) {
bufRdIdx = 0;
}
bufUsed -= len;
if (bufUsed == 0) {
bufRdIdx = 0;
bufWrIdx = 0;
}
waitRef = 0xFFFFFFFF;
if (pingRef != 0xFFFFFFFF) {
pingRef = (millis() & 0x7FFFFFF) + pingTime;
if (pingRef > 0x7FFFFFFF) {
pingRef -= 0x80000000;
}
}
}
void TelnetSpy::addTelnetBuf(char c) {
telnetBuf[bufWrIdx] = c;
if (bufUsed == bufLen) {
bufRdIdx++;
if (bufRdIdx >= bufLen) {
bufRdIdx = 0;
}
} else {
bufUsed++;
}
bufWrIdx++;
if (bufWrIdx >= bufLen) {
bufWrIdx = 0;
}
}
char TelnetSpy::pullTelnetBuf() {
if (bufUsed == 0) {
return 0;
}
char c = telnetBuf[bufRdIdx++];
if (bufRdIdx >= bufLen) {
bufRdIdx = 0;
}
bufUsed--;
return c;
}
char TelnetSpy::peekTelnetBuf() {
if (bufUsed == 0) {
return 0;
}
return telnetBuf[bufRdIdx];
}
int TelnetSpy::telnetAvailable() {
int n = client.available();
while (n > 0) {
if (0xff == client.peek()) { // If esc char for telnet NVT protocol data remove that telegram:
client.read(); // Remove esc char
n--;
if (0xff == client.peek()) { // If esc sequence for 0xFF data byte...
return n; // ...return info about available data (just this 0xFF data byte)
}
client.read(); // Skip the rest of the telegram of the telnet NVT protocol data
client.read();
n--;
n--;
} else { // If next char is a normal data byte...
return n; // ...return info about available data
}
}
return 0;
}
bool TelnetSpy::isClientConnected() {
return connected;
}
void TelnetSpy::setCallbackOnConnect(telnetSpyCallback callback) {
callbackConnect = callback;
}
void TelnetSpy::setCallbackOnDisconnect(telnetSpyCallback callback) {
callbackDisconnect = callback;
}
void TelnetSpy::handle() {
if (firstMainLoop) {
firstMainLoop = false;
// Between setup() and loop() the configuration for os_print may be changed so it must be renewed
if (debugOutput && (actualObject == this)) {
setDebugOutput(true);
}
}
if (!started) {
return;
}
if (!listening) {
if (WiFi.status() != WL_CONNECTED) {
// unless AP
//if (!(WiFi.getMode() & WIFI_AP)) { // proddy
// return;
// }
telnetServer = new WiFiServer(port);
telnetServer->begin();
telnetServer->setNoDelay(bufLen > 0);
listening = true;
if (usedSer) {
usedSer->println("[TELNET] Telnet server started"); // added by Proddy
}
}
if (telnetServer->hasClient()) {
if (client.connected()) {
WiFiClient rejectClient = telnetServer->available();
if (strlen(rejectMsg) > 0) {
rejectClient.write((const uint8_t *)rejectMsg, strlen(rejectMsg));
}
rejectClient.flush();
rejectClient.stop();
} else {
client = telnetServer->available();
if (strlen(welcomeMsg) > 0) {
client.write((const uint8_t *)welcomeMsg, strlen(welcomeMsg));
}
}
}
if (client.connected()) {
if (!connected) {
connected = true;
if (pingTime != 0) {
pingRef = (millis() & 0x7FFFFFF) + pingTime;
}
if (callbackConnect != NULL) {
callbackConnect();
}
}
} else {
if (connected) {
connected = false;
client.flush();
client.stop();
pingRef = 0xFFFFFFFF;
waitRef = 0xFFFFFFFF;
if (callbackDisconnect != NULL) {
callbackDisconnect();
}
}
}
if (client.connected() && (bufUsed > 0)) {
if (bufUsed >= minBlockSize) {
sendBlock();
} else {
unsigned long m = millis() & 0x7FFFFFF;
if (waitRef == 0xFFFFFFFF) {
waitRef = m + collectingTime;
if (waitRef > 0x7FFFFFFF) {
waitRef -= 0x80000000;
}
} else {
if (!((waitRef < 0x20000000) && (m > 0x60000000)) && (m >= waitRef)) {
sendBlock();
}
}
}
}
if (client.connected() && (pingRef != 0xFFFFFFFF)) {
unsigned long m = millis() & 0x7FFFFFF;
if (!((pingRef < 0x20000000) && (m > 0x60000000)) && (m >= pingRef)) {
addTelnetBuf(0);
sendBlock();
}
}
}
}

View File

@@ -27,6 +27,7 @@ MyESP::MyESP() {
_helpProjectCmds_count = 0; _helpProjectCmds_count = 0;
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands _command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
_use_serial = false;
_mqtt_host = NULL; _mqtt_host = NULL;
_mqtt_password = NULL; _mqtt_password = NULL;
_mqtt_username = NULL; _mqtt_username = NULL;
@@ -361,12 +362,13 @@ void MyESP::_telnet_setup() {
SerialAndTelnet.setWelcomeMsg(""); SerialAndTelnet.setWelcomeMsg("");
SerialAndTelnet.setCallbackOnConnect([this]() { _telnetConnected(); }); SerialAndTelnet.setCallbackOnConnect([this]() { _telnetConnected(); });
SerialAndTelnet.setCallbackOnDisconnect([this]() { _telnetDisconnected(); }); SerialAndTelnet.setCallbackOnDisconnect([this]() { _telnetDisconnected(); });
SerialAndTelnet.begin(115200);
SerialAndTelnet.setDebugOutput(false);
#ifndef DEBUG_SUPPORT if (!_use_serial) {
SerialAndTelnet.setSerial(NULL); SerialAndTelnet.setSerial(NULL);
#endif }
SerialAndTelnet.begin(115200); // baud is 115200
SerialAndTelnet.setDebugOutput(false);
// init command buffer for console commands // init command buffer for console commands
memset(_command, 0, TELNET_MAX_COMMAND_LENGTH); memset(_command, 0, TELNET_MAX_COMMAND_LENGTH);
@@ -374,34 +376,41 @@ void MyESP::_telnet_setup() {
// Show help of commands // Show help of commands
void MyESP::_consoleShowHelp() { void MyESP::_consoleShowHelp() {
SerialAndTelnet.printf("\n\r* Connected to: %s version %s\n\r", _app_name, _app_version); SerialAndTelnet.println();
SerialAndTelnet.printf("* Connected to: %s version %s", _app_name, _app_version);
SerialAndTelnet.println();
if (WiFi.getMode() & WIFI_AP) { if (WiFi.getMode() & WIFI_AP) {
SerialAndTelnet.printf("* ESP8266 is in AP mode with SSID %s\n\r", jw.getAPSSID().c_str()); SerialAndTelnet.printf("* ESP8266 is in AP mode with SSID %s", jw.getAPSSID().c_str());
SerialAndTelnet.println();
} else { } else {
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
String hostname = String(WiFi.getHostname()); String hostname = String(WiFi.getHostname());
#else #else
String hostname = WiFi.hostname(); String hostname = WiFi.hostname();
#endif #endif
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s\n\r", SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s",
hostname.c_str(), hostname.c_str(),
WiFi.localIP().toString().c_str(), WiFi.localIP().toString().c_str(),
WiFi.macAddress().c_str()); WiFi.macAddress().c_str());
SerialAndTelnet.printf("* Connected to WiFi SSID: %s\n\r", WiFi.SSID().c_str()); SerialAndTelnet.println();
SerialAndTelnet.printf("* Boot time: %s\n\r", _boottime); SerialAndTelnet.printf("* Connected to WiFi SSID: %s", WiFi.SSID().c_str());
SerialAndTelnet.println();
SerialAndTelnet.printf("* Boot time: %s", _boottime);
SerialAndTelnet.println();
} }
SerialAndTelnet.printf("* Free RAM:%d KB, Load:%d%%\n\r", (ESP.getFreeHeap() / 1024), getSystemLoadAverage()); SerialAndTelnet.printf("* Free RAM:%d KB, Load:%d%%", (ESP.getFreeHeap() / 1024), getSystemLoadAverage());
SerialAndTelnet.println();
// for battery power is ESP.getVcc() // for battery power is ESP.getVcc()
#ifdef DEBUG_SUPPORT SerialAndTelnet.println(FPSTR("*"));
SerialAndTelnet.println("* Warning: in DEBUG_SUPPORT mode!"); SerialAndTelnet.println(FPSTR("* Commands:"));
#endif SerialAndTelnet.println(FPSTR("* ?=help, CTRL-D=quit, !=reboot"));
SerialAndTelnet.println("*\n\r* Commands:\n\r* ?=help, CTRL-D=quit, !=reboot");
SerialAndTelnet.println(FPSTR("* set")); SerialAndTelnet.println(FPSTR("* set"));
SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]")); SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]"));
SerialAndTelnet.println(FPSTR("* set erase")); SerialAndTelnet.println(FPSTR("* set erase"));
SerialAndTelnet.println(FPSTR("* set serial"));
// print custom commands if available. Taken from progmem // print custom commands if available. Taken from progmem
if (_telnetcommand_callback) { if (_telnetcommand_callback) {
@@ -493,6 +502,20 @@ void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
_mqtt_password = strdup(value); _mqtt_password = strdup(value);
} }
ok = true; ok = true;
} else if (strcmp(setting, "serial") == 0) {
ok = true;
_use_serial = false;
if (value) {
if (strcmp(value, "on") == 0) {
_use_serial = true;
ok = true;
} else if (strcmp(value, "off") == 0) {
_use_serial = false;
ok = true;
} else {
ok = false;
}
}
} else { } else {
// finally check for any custom commands // finally check for any custom commands
ok = (_fs_settings_callback)(MYESP_FSACTION_SET, wc, setting, value); ok = (_fs_settings_callback)(MYESP_FSACTION_SET, wc, setting, value);
@@ -505,11 +528,12 @@ void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
// check for 2 params // check for 2 params
if (value == nullptr) { if (value == nullptr) {
SerialAndTelnet.printf("%s setting deleted\n\r", setting); SerialAndTelnet.printf("%s setting reset to its default value", setting);
} else { } else {
// 3 params // must be 3 params
SerialAndTelnet.printf("%s changed to %s\n\r", setting, value); SerialAndTelnet.printf("%s changed to %s", setting, value);
} }
SerialAndTelnet.println();
if (fs_saveConfig()) { if (fs_saveConfig()) {
SerialAndTelnet.println("Note, some changes will only have effect after the ESP is restarted (use ! command)"); SerialAndTelnet.println("Note, some changes will only have effect after the ESP is restarted (use ! command)");
@@ -536,8 +560,10 @@ void MyESP::_telnetCommand(char * commandLine) {
char * ptrToCommandName = strtok((char *)temp, ", \n"); char * ptrToCommandName = strtok((char *)temp, ", \n");
if (strcmp(ptrToCommandName, "set") == 0) { if (strcmp(ptrToCommandName, "set") == 0) {
if (wc == 1) { if (wc == 1) {
SerialAndTelnet.println("\n\Stored settings:"); SerialAndTelnet.println();
SerialAndTelnet.printf(" wifi_ssid=%s\n\r", (!_wifi_ssid) ? "<not set>" : _wifi_ssid); SerialAndTelnet.println("Stored settings:");
SerialAndTelnet.printf(" wifi_ssid=%s", (!_wifi_ssid) ? "<not set>" : _wifi_ssid);
SerialAndTelnet.println();
SerialAndTelnet.printf(" wifi_password="); SerialAndTelnet.printf(" wifi_password=");
if (!_wifi_password) { if (!_wifi_password) {
SerialAndTelnet.print("<not set>"); SerialAndTelnet.print("<not set>");
@@ -545,8 +571,11 @@ void MyESP::_telnetCommand(char * commandLine) {
for (uint8_t i = 0; i < strlen(_wifi_password); i++) for (uint8_t i = 0; i < strlen(_wifi_password); i++)
SerialAndTelnet.print("*"); SerialAndTelnet.print("*");
} }
SerialAndTelnet.printf("\n\r mqtt_host=%s\n\r", (!_mqtt_host) ? "<not set>" : _mqtt_host); SerialAndTelnet.println();
SerialAndTelnet.printf(" mqtt_username=%s\n\r", (!_mqtt_username) ? "<not set>" : _mqtt_username); SerialAndTelnet.printf(" mqtt_host=%s", (!_mqtt_host) ? "<not set>" : _mqtt_host);
SerialAndTelnet.println();
SerialAndTelnet.printf(" mqtt_username=%s", (!_mqtt_username) ? "<not set>" : _mqtt_username);
SerialAndTelnet.println();
SerialAndTelnet.printf(" mqtt_password="); SerialAndTelnet.printf(" mqtt_password=");
if (!_mqtt_password) { if (!_mqtt_password) {
SerialAndTelnet.print("<not set>"); SerialAndTelnet.print("<not set>");
@@ -554,12 +583,17 @@ void MyESP::_telnetCommand(char * commandLine) {
for (uint8_t i = 0; i < strlen(_mqtt_password); i++) for (uint8_t i = 0; i < strlen(_mqtt_password); i++)
SerialAndTelnet.print("*"); SerialAndTelnet.print("*");
} }
SerialAndTelnet.println();
SerialAndTelnet.printf(" serial=%s", (_use_serial) ? "on" : "off");
SerialAndTelnet.println(); SerialAndTelnet.println();
// print custom settings // print custom settings
(_fs_settings_callback)(MYESP_FSACTION_LIST, 0, NULL, NULL); (_fs_settings_callback)(MYESP_FSACTION_LIST, 0, NULL, NULL);
SerialAndTelnet.println("\n\rUsage: set <setting> [value]"); SerialAndTelnet.println();
SerialAndTelnet.println("Usage: set <setting> [value]");
} else if (wc == 2) { } else if (wc == 2) {
char * setting = _telnet_readWord(); char * setting = _telnet_readWord();
_changeSetting(1, setting, NULL); _changeSetting(1, setting, NULL);
@@ -583,9 +617,11 @@ void MyESP::_telnetHandle() {
// read asynchronously until full command input // read asynchronously until full command input
while (SerialAndTelnet.available()) { while (SerialAndTelnet.available()) {
char c = SerialAndTelnet.read(); char c = SerialAndTelnet.read();
#ifdef DEBUG_SUPPORT
Serial.print(c); if (_use_serial) {
#endif SerialAndTelnet.serialPrint(c); // echo to Serial if connected
}
switch (c) { switch (c) {
case '\r': // likely have full command in buffer now, commands are terminated by CR and/or LF case '\r': // likely have full command in buffer now, commands are terminated by CR and/or LF
case '\n': case '\n':
@@ -683,6 +719,7 @@ void MyESP::setWIFI(char * wifi_ssid, char * wifi_password, wifi_callback_f call
_wifi_callback = callback; _wifi_callback = callback;
} }
// init MQTT settings
void MyESP::setMQTT(char * mqtt_host, void MyESP::setMQTT(char * mqtt_host,
char * mqtt_username, char * mqtt_username,
char * mqtt_password, char * mqtt_password,
@@ -833,6 +870,14 @@ bool MyESP::_fs_loadConfig() {
// ok is false if there's a problem loading a custom setting (e.g. does not exist) // ok is false if there's a problem loading a custom setting (e.g. does not exist)
bool ok = (_fs_callback)(MYESP_FSACTION_LOAD, json); bool ok = (_fs_callback)(MYESP_FSACTION_LOAD, json);
// new configs after release 1.3.x
if (json.containsKey("use_serial")) {
_use_serial = (bool)json["use_serial"];
} else {
_use_serial = true; // if first time, set serial
ok = false;
}
configFile.close(); configFile.close();
return ok; return ok;
@@ -848,6 +893,7 @@ bool MyESP::fs_saveConfig() {
json["mqtt_host"] = _mqtt_host; json["mqtt_host"] = _mqtt_host;
json["mqtt_username"] = _mqtt_username; json["mqtt_username"] = _mqtt_username;
json["mqtt_password"] = _mqtt_password; json["mqtt_password"] = _mqtt_password;
json["use_serial"] = _use_serial;
// callback for saving custom settings // callback for saving custom settings
(void)(_fs_callback)(MYESP_FSACTION_SAVE, json); (void)(_fs_callback)(MYESP_FSACTION_SAVE, json);
@@ -911,8 +957,8 @@ void MyESP::begin(char * app_hostname, char * app_name, char * app_version) {
_app_version = strdup(app_version); _app_version = strdup(app_version);
// call setup of the services... // call setup of the services...
_fs_setup(); // SPIFFS setup, do this first to get values
_telnet_setup(); // Telnet setup _telnet_setup(); // Telnet setup
_fs_setup(); // SPIFFS setup
_wifi_setup(); // WIFI setup _wifi_setup(); // WIFI setup
_mqtt_setup(); // MQTT Setup _mqtt_setup(); // MQTT Setup
_mdns_setup(); // MDNS setup _mdns_setup(); // MDNS setup
@@ -923,9 +969,10 @@ void MyESP::begin(char * app_hostname, char * app_name, char * app_version) {
* Loop. This is called as often as possible and it handles wifi, telnet, mqtt etc * Loop. This is called as often as possible and it handles wifi, telnet, mqtt etc
*/ */
void MyESP::loop() { void MyESP::loop() {
_calculateLoad(); // calculate load _calculateLoad();
jw.loop(); // WiFi jw.loop(); // WiFi
_telnetHandle(); // Telnet/Debugger _telnetHandle(); // Telnet/Debugger
// do nothing else until we've got a wifi connection // do nothing else until we've got a wifi connection

View File

@@ -140,8 +140,6 @@ class MyESP {
char * _mqtt_host; char * _mqtt_host;
char * _mqtt_username; char * _mqtt_username;
char * _mqtt_password; char * _mqtt_password;
char * _boottime;
bool _suspendOutput;
char * _mqtt_base; char * _mqtt_base;
unsigned long _mqtt_keepalive; unsigned long _mqtt_keepalive;
unsigned char _mqtt_qos; unsigned char _mqtt_qos;
@@ -192,6 +190,9 @@ class MyESP {
char * _app_hostname; char * _app_hostname;
char * _app_name; char * _app_name;
char * _app_version; char * _app_version;
char * _boottime;
bool _suspendOutput;
bool _use_serial;
// load average (0..100) // load average (0..100)
void _calculateLoad(); void _calculateLoad();

View File

@@ -6,9 +6,12 @@ env_default = d1_mini
[common] [common]
platform = espressif8266 platform = espressif8266
flash_mode = dout flash_mode = dout
;debug_mode = -DDEBUG_SUPPORT
debug_mode =
build_flags = -g -w build_flags = -g -w
wifi_settings =
; hard code if you prefer. Recommendation is to set from within the app when in Serial or AP mode
;wifi_settings = -DWIFI_SSID="your_ssid" -DWIFI_PASSWORD="your_pw"
lib_deps = lib_deps =
CRC32 CRC32
CircularBuffer CircularBuffer
@@ -22,7 +25,7 @@ board = nodemcuv2
platform = ${common.platform} platform = ${common.platform}
framework = arduino framework = arduino
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
build_flags = ${common.build_flags} ${common.debug_mode} build_flags = ${common.build_flags} ${common.wifi_settings}
board_build.flash_mode = ${common.flash_mode} board_build.flash_mode = ${common.flash_mode}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200 monitor_speed = 115200
@@ -34,7 +37,7 @@ board = d1_mini
platform = ${common.platform} platform = ${common.platform}
framework = arduino framework = arduino
lib_deps = ${common.lib_deps} lib_deps = ${common.lib_deps}
build_flags = ${common.build_flags} ${common.debug_mode} build_flags = ${common.build_flags} ${common.wifi_settings}
board_build.flash_mode = ${common.flash_mode} board_build.flash_mode = ${common.flash_mode}
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200 monitor_speed = 115200

View File

@@ -85,11 +85,9 @@ command_t PROGMEM project_cmds[] = {
{"info", "show the values"}, {"info", "show the values"},
{"log <n | b | t | r | v>", "set logging mode to none, basic, thermostat only, raw or verbose"}, {"log <n | b | t | r | v>", "set logging mode to none, basic, thermostat only, raw or verbose"},
{"poll", "toggle EMS poll request on/off"},
{"tx", "toggle EMX Tx line on/off"},
{"publish", "publish values to MQTT"}, {"publish", "publish values to MQTT"},
{"types", "list supported EMS telegram type IDs"}, {"types", "list supported EMS telegram type IDs"},
{"queue", "print the Tx queue"}, {"queue", "list Tx queue"},
{"autodetect", "discover EMS devices and set boiler and thermostat automatically"}, {"autodetect", "discover EMS devices and set boiler and thermostat automatically"},
{"shower <timer | alert>", "toggle either timer or alert on/off"}, {"shower <timer | alert>", "toggle either timer or alert on/off"},
{"send XX...", "send raw telegram data in hex to EMS bus"}, {"send XX...", "send raw telegram data in hex to EMS bus"},
@@ -265,19 +263,16 @@ void showInfo() {
myDebug(" LED is %s", EMSESP_Status.led_enabled ? "on" : "off"); myDebug(" LED is %s", EMSESP_Status.led_enabled ? "on" : "off");
myDebug(" # connected Dallas temperature sensors = %d", EMSESP_Status.dallas_sensors); myDebug(" # connected Dallas temperature sensors=%d", EMSESP_Status.dallas_sensors);
myDebug(" Thermostat is %s, Boiler is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s", myDebug(" Thermostat is %s, Boiler is %s, Shower Timer is %s, Shower Alert is %s",
(ems_getThermostatEnabled() ? "enabled" : "disabled"), (ems_getThermostatEnabled() ? "enabled" : "disabled"),
(ems_getBoilerEnabled() ? "enabled" : "disabled"), (ems_getBoilerEnabled() ? "enabled" : "disabled"),
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
((EMS_Sys_Status.emsTxEnabled) ? "enabled" : "disabled"),
((EMSESP_Status.shower_timer) ? "enabled" : "disabled"), ((EMSESP_Status.shower_timer) ? "enabled" : "disabled"),
((EMSESP_Status.shower_alert) ? "enabled" : "disabled")); ((EMSESP_Status.shower_alert) ? "enabled" : "disabled"));
myDebug("\n%sEMS Bus Stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF); myDebug("\n%sEMS Bus Stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
myDebug(" Bus Connected=%s, Tx Capable=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d", myDebug(" Bus Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
(ems_getTxCapable() ? "yes" : "no"),
(ems_getBusConnected() ? "yes" : "no"), (ems_getBusConnected() ? "yes" : "no"),
EMS_Sys_Status.emsRxPgks, EMS_Sys_Status.emsRxPgks,
EMS_Sys_Status.emsTxPkgs, EMS_Sys_Status.emsTxPkgs,
@@ -633,7 +628,7 @@ bool FSCallback(MYESP_FSACTION action, JsonObject & json) {
// callback for custom settings when showing Stored Settings // callback for custom settings when showing Stored Settings
// wc is number of arguments after the 'set' command // wc is number of arguments after the 'set' command
// returns true if successful // returns true if the setting was recognized and changed
bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) { bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) {
bool ok = false; bool ok = false;
@@ -721,18 +716,6 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
ok = true; ok = true;
} }
if (strcmp(first_cmd, "poll") == 0) {
bool b = !ems_getPoll();
ems_setPoll(b);
ok = true;
}
if (strcmp(first_cmd, "tx") == 0) {
bool b = !ems_getTxEnabled();
ems_setTxEnabled(b);
ok = true;
}
if (strcmp(first_cmd, "publish") == 0) { if (strcmp(first_cmd, "publish") == 0) {
publishValues(true); publishValues(true);
ok = true; ok = true;
@@ -925,17 +908,12 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
// Init callback, which is used to set functions and call methods after a wifi connection has been established // Init callback, which is used to set functions and call methods after a wifi connection has been established
void WIFICallback() { void WIFICallback() {
// when finally we're all set up, we can fire up the uart (this will enable the UART interrupts) // This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
#ifdef DEBUG_SUPPORT
myDebug("Warning, in DEBUG mode. EMS bus has been disabled. See -DDEBUG_SUPPORT build option.");
#else
// Important! This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
// This is done after we have a WiFi signal to avoid any resource conflicts // This is done after we have a WiFi signal to avoid any resource conflicts
emsuart_init(); emsuart_init();
myDebug("[UART] Opened Rx/Tx connection"); myDebug("[UART] Opened Rx/Tx connection");
#endif
// now that we're connected, find out what boiler and thermostats we have specified // go and find the boiler and thermostat types
ems_discoverModels(); ems_discoverModels();
} }
@@ -1100,18 +1078,21 @@ void setup() {
// call ems.cpp's init function to set all the internal params // call ems.cpp's init function to set all the internal params
ems_init(); ems_init();
#ifndef DEBUG_SUPPORT
// Timers using Ticker library // Timers using Ticker library
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
#endif
// set up myESP for Wifi, MQTT, MDNS and Telnet // set up myESP for Wifi, MQTT, MDNS and Telnet
myESP.setTelnet(project_cmds, ArraySize(project_cmds), TelnetCommandCallback, TelnetCallback); // set up Telnet commands myESP.setTelnet(project_cmds, ArraySize(project_cmds), TelnetCommandCallback, TelnetCallback); // set up Telnet commands
#ifdef WIFI_SSID
myESP.setWIFI(WIFI_SSID, WIFI_PASSWORD, WIFICallback); myESP.setWIFI(WIFI_SSID, WIFI_PASSWORD, WIFICallback);
myESP.setMQTT( #else
MQTT_HOST, MQTT_USER, MQTT_PASS, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, MQTT_WILL_PAYLOAD, MQTTCallback); myESP.setWIFI(NULL, NULL, WIFICallback); // pull the wifi settings from the SPIFFS stored settings
#endif
// MQTT host, username and password taken from the SPIFFS settings
myESP.setMQTT(NULL, NULL, NULL, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, MQTT_WILL_PAYLOAD, MQTTCallback);
// custom settings in SPIFFS // custom settings in SPIFFS
myESP.setSettings(FSCallback, SettingsCallback); myESP.setSettings(FSCallback, SettingsCallback);

View File

@@ -24,35 +24,39 @@ CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO
// callbacks per type // callbacks per type
// generic // generic
void _process_Version(uint8_t * data, uint8_t length); void _process_Version(uint8_t type, uint8_t * data, uint8_t length);
// Boiler and Buderus devices // Boiler and Buderus devices
void _process_UBAMonitorFast(uint8_t * data, uint8_t length); void _process_UBAMonitorFast(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBAMonitorSlow(uint8_t * data, uint8_t length); void _process_UBAMonitorSlow(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length); void _process_UBAMonitorWWMessage(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBAParameterWW(uint8_t * data, uint8_t length); void _process_UBAParameterWW(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length); void _process_UBATotalUptimeMessage(uint8_t type, uint8_t * data, uint8_t length);
void _process_UBAParametersMessage(uint8_t * data, uint8_t length); void _process_UBAParametersMessage(uint8_t type, uint8_t * data, uint8_t length);
void _process_SetPoints(uint8_t * data, uint8_t length); void _process_SetPoints(uint8_t type, uint8_t * data, uint8_t length);
// Common for most thermostats // Common for most thermostats
void _process_RCTime(uint8_t * data, uint8_t length); void _process_RCTime(uint8_t type, uint8_t * data, uint8_t length);
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length); void _process_RCOutdoorTempMessage(uint8_t type, uint8_t * data, uint8_t length);
// RC10
void _process_RC10Set(uint8_t type, uint8_t * data, uint8_t length);
void _process_RC10StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
// RC20 // RC20
void _process_RC20Set(uint8_t * data, uint8_t length); void _process_RC20Set(uint8_t type, uint8_t * data, uint8_t length);
void _process_RC20StatusMessage(uint8_t * data, uint8_t length); void _process_RC20StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
// RC30 // RC30
void _process_RC30Set(uint8_t * data, uint8_t length); void _process_RC30Set(uint8_t type, uint8_t * data, uint8_t length);
void _process_RC30StatusMessage(uint8_t * data, uint8_t length); void _process_RC30StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
// RC35 // RC35
void _process_RC35Set(uint8_t * data, uint8_t length); void _process_RC35Set(uint8_t type, uint8_t * data, uint8_t length);
void _process_RC35StatusMessage(uint8_t * data, uint8_t length); void _process_RC35StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
// Easy // Easy
void _process_EasyStatusMessage(uint8_t * data, uint8_t length); void _process_EasyStatusMessage(uint8_t type, uint8_t * data, uint8_t length);
/* /*
* Recognized EMS types and the functions they call to process the telegrams * Recognized EMS types and the functions they call to process the telegrams
@@ -73,6 +77,11 @@ const _EMS_Type EMS_Types[] = {
{EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", _process_UBAParametersMessage}, {EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", _process_UBAParametersMessage},
{EMS_MODEL_UBA, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints}, {EMS_MODEL_UBA, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
// RC10
{EMS_MODEL_RC10, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
{EMS_MODEL_RC10, EMS_TYPE_RC10Set, "RC10Set", _process_RC10Set},
{EMS_MODEL_RC10, EMS_TYPE_RC10StatusMessage, "RC10StatusMessage", _process_RC10StatusMessage},
// RC20 and RC20F // RC20 and RC20F
{EMS_MODEL_RC20, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage}, {EMS_MODEL_RC20, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
{EMS_MODEL_RC20, EMS_TYPE_RCTime, "RCTime", _process_RCTime}, {EMS_MODEL_RC20, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
@@ -133,14 +142,10 @@ const uint8_t ems_crc_table[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E,
0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7, 0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5, 0xF7, 0xE9, 0xEB, 0xED, 0xEF, 0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7, 0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5, 0xF7, 0xE9, 0xEB, 0xED, 0xEF,
0xE1, 0xE3, 0xE5, 0xE7}; 0xE1, 0xE3, 0xE5, 0xE7};
const uint8_t TX_WRITE_TIMEOUT_COUNT = 3; // 3 retries before timeout const uint8_t TX_WRITE_TIMEOUT_COUNT = 2; // 3 retries before timeout
const unsigned long EMS_BUS_TIMEOUT = 15000; // timeout in ms before recognizing the ems bus is offline (15 seconds) const unsigned long EMS_BUS_TIMEOUT = 15000; // timeout in ms before recognizing the ems bus is offline (15 seconds)
const unsigned long EMS_POLL_TIMEOUT = 5000; // timeout in ms before recognizing the ems bus is offline (5 seconds) const unsigned long EMS_POLL_TIMEOUT = 5000; // timeout in ms before recognizing the ems bus is offline (5 seconds)
uint8_t _emsTxRetryCount; // used for retries when sending failed
uint8_t _ems_PollCount; // not used, but can be used to slow down sending on faster chips
uint8_t _last_TxTelgramCRC; // CRC of last Tx sent, for checking duplicates
// init stats and counters and buffers // init stats and counters and buffers
// uses -255 or 255 for values that haven't been set yet (EMS_VALUE_INT_NOTSET and EMS_VALUE_FLOAT_NOTSET) // uses -255 or 255 for values that haven't been set yet (EMS_VALUE_INT_NOTSET and EMS_VALUE_FLOAT_NOTSET)
void ems_init() { void ems_init() {
@@ -148,15 +153,15 @@ void ems_init() {
EMS_Sys_Status.emsRxPgks = 0; EMS_Sys_Status.emsRxPgks = 0;
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_IDLE; EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE;
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
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.emsTxEnabled = true; // start up with Tx enabled
EMS_Sys_Status.emsBusConnected = false; EMS_Sys_Status.emsBusConnected = false;
EMS_Sys_Status.emsRxTimestamp = 0; EMS_Sys_Status.emsRxTimestamp = 0;
EMS_Sys_Status.emsTxCapable = false; EMS_Sys_Status.emsTxCapable = false;
EMS_Sys_Status.emsPollTimestamp = 0; EMS_Sys_Status.emsPollTimestamp = 0;
EMS_Sys_Status.txRetryCount = 0;
// thermostat // thermostat
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_FLOAT_NOTSET; EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_FLOAT_NOTSET;
@@ -233,11 +238,6 @@ void ems_init() {
EMS_Thermostat.product_id = 0; EMS_Thermostat.product_id = 0;
strlcpy(EMS_Thermostat.version, "not set", sizeof(EMS_Thermostat.version)); strlcpy(EMS_Thermostat.version, "not set", sizeof(EMS_Thermostat.version));
// counters
_ems_PollCount = 0;
_emsTxRetryCount = 0;
_last_TxTelgramCRC = 0;
// default logging is none // default logging is none
ems_setLogging(EMS_SYS_LOGGING_DEFAULT); ems_setLogging(EMS_SYS_LOGGING_DEFAULT);
} }
@@ -252,15 +252,6 @@ bool ems_getPoll() {
return EMS_Sys_Status.emsPollEnabled; return EMS_Sys_Status.emsPollEnabled;
} }
void ems_setTxEnabled(bool b) {
EMS_Sys_Status.emsTxEnabled = b;
myDebug("EMS Bus Tx is set to %s", EMS_Sys_Status.emsTxEnabled ? "enabled" : "disabled");
}
bool ems_getTxEnabled() {
return EMS_Sys_Status.emsTxEnabled;
}
bool ems_getEmsRefreshed() { bool ems_getEmsRefreshed() {
return EMS_Sys_Status.emsRefreshed; return EMS_Sys_Status.emsRefreshed;
} }
@@ -462,25 +453,12 @@ 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
_debugPrintTelegram("Sending raw", EMS_TxTelegram.data, EMS_TxTelegram.length, COLOR_CYAN); // always show _debugPrintTelegram("Sending raw", EMS_TxTelegram.data, EMS_TxTelegram.length, COLOR_CYAN); // always show
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); // send the telegram to the UART Tx emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); // send the telegram to the UART Tx
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
EMS_TxQueue.shift(); // remove from queue EMS_TxQueue.shift(); // remove from queue
return; return;
} }
// if there is no destination, also delete it from the queue // if there is no destination, also delete it from the queue
if (EMS_TxTelegram.dest == EMS_ID_NONE) { if (EMS_TxTelegram.dest == EMS_ID_NONE) {
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
EMS_TxQueue.shift(); // remove from queue
return;
}
// if Tx is disabled, don't do anything and ignore the request
// this could be because the boiler has yet to be found and the type_id is still empty
if (!EMS_Sys_Status.emsTxEnabled) {
myDebug("Tx is disabled. Ignoring %s request to 0x%02X.",
((EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) ? "write" : "read"),
EMS_TxTelegram.dest & 0x7F);
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
EMS_TxQueue.shift(); // remove from queue EMS_TxQueue.shift(); // remove from queue
return; return;
} }
@@ -508,16 +486,6 @@ void _ems_sendTelegram() {
uint8_t crc = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length); uint8_t crc = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length);
EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = crc; EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = crc;
// check if we already sent the same one, otherwise assume the last Tx hasn't been successful
// and remove from queue and exit
if (crc == _last_TxTelgramCRC) {
// myDebug("Duplicate message, just sent this one, so removing from queue!");
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
EMS_TxQueue.shift(); // remove the last Tx from the queue
return;
}
_last_TxTelgramCRC = crc;
// print debug info // print debug info
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) { if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
char s[64] = {0}; char s[64] = {0};
@@ -533,42 +501,59 @@ void _ems_sendTelegram() {
} }
// send the telegram to the UART Tx // send the telegram to the UART Tx
EMS_Sys_Status.emsTxStatus = EMS_TX_ACTIVE;
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length);
EMS_Sys_Status.emsTxPkgs++; EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_WAIT;
}
/**
* Takes the last write command and turns into a validate request
* placing it on the queue
*/
void _createValidate() {
if (EMS_TxQueue.isEmpty()) {
return;
}
// release the Tx lock
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
// get the first in the queue, which is at the head
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first();
// safety check: only do a validate after a write and when we have a type to validate
if ((EMS_TxTelegram.action != EMS_TX_TELEGRAM_WRITE) || (EMS_TxTelegram.type_validate == EMS_ID_NONE)) {
return;
}
// if it was a write command, check if we need to do a new read to validate the results
// we do this by turning the last write into a read
if ((EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) && (EMS_TxTelegram.type_validate != EMS_ID_NONE)) {
// create a new Telegram copying from the last write // create a new Telegram copying from the last write
_EMS_TxTelegram new_EMS_TxTelegram; _EMS_TxTelegram new_EMS_TxTelegram;
new_EMS_TxTelegram.action = EMS_TX_TELEGRAM_VALIDATE;
// copy details // copy old Write record
new_EMS_TxTelegram.type_validate = EMS_TxTelegram.type_validate; new_EMS_TxTelegram.type_validate = EMS_TxTelegram.type_validate;
new_EMS_TxTelegram.dest = EMS_TxTelegram.dest; new_EMS_TxTelegram.dest = EMS_TxTelegram.dest;
new_EMS_TxTelegram.type = EMS_TxTelegram.type; new_EMS_TxTelegram.type = EMS_TxTelegram.type;
new_EMS_TxTelegram.action = EMS_TX_TELEGRAM_VALIDATE;
new_EMS_TxTelegram.offset = EMS_TxTelegram.comparisonOffset; // location of byte to fetch
new_EMS_TxTelegram.dataValue = 1; // fetch single byte
new_EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; // is always 6 bytes long (including CRC at end)
new_EMS_TxTelegram.comparisonValue = EMS_TxTelegram.comparisonValue; new_EMS_TxTelegram.comparisonValue = EMS_TxTelegram.comparisonValue;
new_EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.comparisonPostRead; new_EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.comparisonPostRead;
new_EMS_TxTelegram.comparisonOffset = EMS_TxTelegram.comparisonOffset; new_EMS_TxTelegram.comparisonOffset = EMS_TxTelegram.comparisonOffset;
// this is what is different
new_EMS_TxTelegram.offset = EMS_TxTelegram.comparisonOffset; // location of byte to fetch
new_EMS_TxTelegram.dataValue = 1; // fetch single byte
new_EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; // is always 6 bytes long (including CRC at end)
// remove old telegram from queue and add this new read one // remove old telegram from queue and add this new read one
EMS_TxQueue.shift(); // remove from queue EMS_TxQueue.shift(); // remove from queue
EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it next in line EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it first to be picked up next (FIFO)
}
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE;
} }
/** /**
* the main logic that parses the telegram message, triggered by an interrupt in emsuart.cpp * the main logic that parses the telegram message, triggered by an interrupt in emsuart.cpp
* length is only data bytes, excluding the BRK * length is only data bytes, excluding the BRK
* Read commands are asynchronous as they're handled by the interrupt * Read commands are asynchronous as they're handled by the interrupt
* When we receive a Poll Request we need to send any Tx packages quickly * When we receive a Poll Request we need to send any Tx packages quickly within a 200ms window
*/ */
void ems_parseTelegram(uint8_t * telegram, uint8_t length) { void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
// check if we just received a single byte // check if we just received a single byte
@@ -579,65 +564,52 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
// check first for a Poll for us // check first for a Poll for us
if (value == (EMS_ID_ME | 0x80)) { if (value == (EMS_ID_ME | 0x80)) {
// store when we received a last poll EMS_Sys_Status.emsPollTimestamp = millis(); // store when we received a last poll
EMS_Sys_Status.emsPollTimestamp = millis();
EMS_Sys_Status.emsTxCapable = true; EMS_Sys_Status.emsTxCapable = true;
// do we have something to send thats waiting in the Tx queue? if so send it // do we have something to send thats waiting in the Tx queue? if so send it if the Queue is not in a wait state
if (!EMS_TxQueue.isEmpty()) { if ((!EMS_TxQueue.isEmpty()) && (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_IDLE)) {
_ems_sendTelegram(); // perform the read/write command immediately _ems_sendTelegram(); // perform the read/write command immediately
/*
if ((_ems_PollCount++ % 2) == 0) {
_ems_sendTelegram(); // perform the read/write command, slowing it down a little
}
*/
} 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) {
emsaurt_tx_poll(); emsaurt_tx_poll();
} }
} }
} else if ((value == EMS_TX_ERROR) || (value == EMS_TX_SUCCESS)) { } else if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_WAIT) {
// if its a success (01) or failure (04), then see if its from one of our last writes // this may be a single byte 01 (success) or 04 (error) from a recent write command?
// a response from UBA after a write should be within a specific time period < 100ms if (value == EMS_TX_SUCCESS) {
if (!EMS_TxQueue.isEmpty()) { EMS_Sys_Status.emsTxPkgs++;
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first(); // get current Tx package we last sent // got a success 01. Send a validate to check the value of the last write
if ((EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) && (value == EMS_TX_ERROR)) { emsaurt_tx_poll(); // send a poll to free the EMS bus
_createValidate(); // create a validate Tx request
} 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) { if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
myDebug("* Error: last write failed. removing write request from queue"); myDebug("** Write command failed from host");
}
EMS_TxQueue.shift(); // write failed so remove from queue and forget it for now
} }
emsaurt_tx_poll(); // send a poll to free the EMS bus emsaurt_tx_poll(); // send a poll to free the EMS bus
_removeTxQueue(); // remove from queue
} }
} }
return; // all done here, quit. if we haven't processes anything its a poll but not for us
return; // all done here
} }
// 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 < EMS_MIN_TELEGRAM_LENGTH) { if (length <= 4) {
// _debugPrintTelegram("Noisy data:", telegram, length, COLOR_RED); //_debugPrintTelegram("Noisy data:", telegram, length, COLOR_RED);
return; return;
} }
// Assume at this point we have something that vaguely resembles a telegram // Assume at this point we have something that vaguely resembles a telegram in the format [src] [dest] [type] [offset] [data] [crc]
// see if we got a telegram as [src] [dest] [type] [offset] [data] [crc] // validate the CRC, if its bad ignore it
// so is at least 6 bytes long and the CRC checks out (which is last byte)
uint8_t crc = _crcCalculator(telegram, length); uint8_t crc = _crcCalculator(telegram, length);
if (telegram[length - 1] != crc) { if (telegram[length - 1] != crc) {
EMS_Sys_Status.emxCrcErr++; EMS_Sys_Status.emxCrcErr++;
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
_debugPrintTelegram("Corrupt telegram:", telegram, length, COLOR_RED); _debugPrintTelegram("Corrupt telegram:", telegram, length, COLOR_RED);
// at this point something arrived on the bus, which means if we were waiting for something to arrive then we
// can forget it as it should have arrived in a 100ms window. So remove it.
if (!EMS_TxQueue.isEmpty()) {
/*
// commented out because too much chatter
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Corrupt telegram, so removing last read from Tx queue.");
}
*/
EMS_TxQueue.shift();
} }
return; return;
} }
@@ -755,86 +727,127 @@ void _ems_processTelegram(uint8_t * telegram, uint8_t length) {
// call callback function to process it // call callback function to process it
// as we only handle complete telegrams (not partial) check that the offset is 0 // as we only handle complete telegrams (not partial) check that the offset is 0
if (offset == EMS_ID_NONE) { if (offset == EMS_ID_NONE) {
(void)EMS_Types[i].processType_cb(data, length - 5); (void)EMS_Types[i].processType_cb(type, data, length - 5);
} }
} }
} }
} }
/**
* Remove current Tx telegram from queue and release lock on Tx
*/
void _removeTxQueue() {
if (!EMS_TxQueue.isEmpty()) {
EMS_TxQueue.shift(); // remove item from top of the queue
}
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
}
/** /**
* deciphers the telegram packet * deciphers the telegram packet, which has already been checked for valid CRC and has a complete header (min of 5 bytes)
* length is only data bytes, excluding the BRK * length is only data bytes, excluding the BRK
* We only remove from the Tx queue if the read or write was successful * We only remove from the Tx queue if the read or write was successful
*/ */
void _processType(uint8_t * telegram, uint8_t length) { void _processType(uint8_t * telegram, uint8_t length) {
// header // header
uint8_t src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes here uint8_t src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes here
uint8_t dest = telegram[1] & 0x7F;
uint8_t type = telegram[2];
uint8_t * data = telegram + 4; // data block starts at position 5
// if its an echo of ourselves from the master, ignore // if its an echo of ourselves from the master UBA, ignore
if (src == EMS_ID_ME) { if (src == EMS_ID_ME) {
// _debugPrintTelegram("Telegram echo:", telegram, length, COLOR_BLUE); //_debugPrintTelegram("Telegram echo:", telegram, length, COLOR_BLUE);
return; return;
} }
// did we request this telegram? If so it would be either a read or a validate telegram and still // if its a broadcast and we didn't just send anything, process it and exit
// on top of the Tx queue with the same type. because we don't remove a Tx from the queue after a send if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_IDLE) {
// if its a validate check the value, or if its a read, update the Read counter
// then we can safely removed the read/validate from the queue
if ((dest == EMS_ID_ME) && (!EMS_TxQueue.isEmpty())) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first(); // get current Tx package we last sent
// do the types match? If so we were expecting this telegram response back to us
if (EMS_TxTelegram.type == type) {
emsaurt_tx_poll(); // send Acknowledgement back to free the EMS bus since we have the telegram
// if last action was a read, go ahead and process it
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_READ) {
EMS_Sys_Status.emsRxPgks++; // increment rx counter
_emsTxRetryCount = 0; // reset retry count
//
// and process the telegram. This calls the main function to process each type
//
_ems_processTelegram(telegram, length); _ems_processTelegram(telegram, length);
if (EMS_TxTelegram.forceRefresh) { return;
ems_setEmsRefreshed(true); // set the MQTT refresh flag to force sending to MQTT
} }
EMS_TxQueue.shift(); // remove read from queue, all done now
return; // quit // release the lock on the TxQueue
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
// at this point we can assume Txstatus is EMS_TX_STATUS_WAIT
// for READ, WRITE or VALIDATE the dest is always us, so check this
// if not just process and quit
if ((telegram[1] & 0x7F) != EMS_ID_ME) {
_ems_processTelegram(telegram, length);
return;
}
// first double check we actually have something in the queue
if (EMS_TxQueue.isEmpty()) {
_ems_processTelegram(telegram, length);
return;
}
// get the Tx telegram we just sent
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first();
// check action
// if READ, match the current inbound telegram to what we sent
// if WRITE, should not happen
// if VALIDATE, check the contents
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_READ) {
uint8_t type = telegram[2];
if ((src == EMS_TxTelegram.dest) && (type == EMS_TxTelegram.type)) {
// all checks out, read was successful, remove tx from queue and continue to process telegram
_removeTxQueue();
EMS_Sys_Status.emsRxPgks++; // increment counter
// myDebug("** Read from 0x%02X ok", type);
ems_setEmsRefreshed(EMS_TxTelegram.forceRefresh); // does mqtt need refreshing?
} else {
// read not OK, we didn't get back a telegram we expected
// leave on queue and try again, but continue to process what we received as it may be important
EMS_Sys_Status.txRetryCount++;
// if retried too many times, give up and remove it
if (EMS_Sys_Status.txRetryCount >= TX_WRITE_TIMEOUT_COUNT) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Read failed. Giving up, removing from queue");
}
_removeTxQueue();
} else {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("...Retrying read. Attempt %d/%d...", EMS_Sys_Status.txRetryCount, TX_WRITE_TIMEOUT_COUNT);
}
}
}
_ems_processTelegram(telegram, length); // process it always
}
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) {
// should not get here, since this is handled earlier receiving a 01 or 04
myDebug("** Error ! Write - should not be here");
} }
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) { if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) {
// this read was for a validate. Do a compare on the 1 byte result from the last write // this is a read telegram which we use to validate the last write
uint8_t * data = telegram + 4; // data block starts at position 5
uint8_t dataReceived = data[0]; // only a single byte is returned after a read uint8_t dataReceived = data[0]; // only a single byte is returned after a read
if (EMS_TxTelegram.comparisonValue == dataReceived) { if (EMS_TxTelegram.comparisonValue == dataReceived) {
// there is a match, so write must have been successful // validate was successful, the write changed the value
EMS_TxQueue.shift(); // remove validate from queue _removeTxQueue(); // now we can remove the Tx validate command the queue
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) { if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Write to 0x%02X was successful", EMS_TxTelegram.dest); myDebug("Write to 0x%02X was successful", EMS_TxTelegram.dest);
} }
ems_doReadCommand(EMS_TxTelegram.comparisonPostRead, // follow up with the post read command
EMS_TxTelegram.dest, ems_doReadCommand(EMS_TxTelegram.comparisonPostRead, EMS_TxTelegram.dest, true);
true); // get values and force a refresh to MQTT
} else { } else {
// write failed. // write failed
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) { if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Last write failed. Compared set value 0x%02X with received value 0x%02X", myDebug("Last write failed. Compared set value 0x%02X with received value 0x%02X",
EMS_TxTelegram.comparisonValue, EMS_TxTelegram.comparisonValue,
dataReceived); dataReceived);
} }
if (_emsTxRetryCount++ >= TX_WRITE_TIMEOUT_COUNT) { if (++EMS_Sys_Status.txRetryCount > TX_WRITE_TIMEOUT_COUNT) {
// give up
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) { if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("...Giving up!"); myDebug("Write failed. Giving up, removing from queue");
} }
EMS_TxQueue.shift(); // remove from queue _removeTxQueue();
} else { } else {
// retry, turn the validate back into a write and try again // retry, turn the validate back into a write and try again
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) { if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("...Retrying attempt %d...", _emsTxRetryCount); myDebug("...Retrying write. Attempt %d/%d...", EMS_Sys_Status.txRetryCount, TX_WRITE_TIMEOUT_COUNT);
} }
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE; EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dataValue = EMS_TxTelegram.comparisonValue; // restore old value EMS_TxTelegram.dataValue = EMS_TxTelegram.comparisonValue; // restore old value
@@ -843,37 +856,17 @@ void _processType(uint8_t * telegram, uint8_t length) {
EMS_TxQueue.unshift(EMS_TxTelegram); // add back to queue making it next in line EMS_TxQueue.unshift(EMS_TxTelegram); // add back to queue making it next in line
} }
} }
}
return; emsaurt_tx_poll(); // send Acknowledgement back to free the EMS bus since we have the telegram
}
}
// telegram was for us, but seems we didn't ask for it
// so kinda invalidates the last action on the queue remove from queue
myDebug("Telegram sent to us but we didn't ask for it! (src=0x%02X dest=0x%02X type=0x%02X)",
telegram[0] & 0x7F,
telegram[1] & 0x7F,
telegram[2]);
// ems_printTxQueue();
// EMS_TxQueue.shift(); // remove validate from queue
} else {
// we didn't request it, was for somebody else or a broadcast, process it anyway
_ems_processTelegram(telegram, length);
}
} }
/** /**
* Check if hot tap water or heating is active * Check if hot tap water or heating is active
* using a quick hack: * using a quick hack for checking the heating. Selected Flow Temp >= 70
* heating is on if Selected Flow Temp >= 70 (in my case)
* tap water is on if Selected Flow Temp = 0 and Selected Burner Power >= 115
*/ */
bool _checkActive() { bool _checkActive() {
/* old code
EMS_Boiler.tapwaterActive = ((EMS_Boiler.selFlowTemp == 0)
&& (EMS_Boiler.selBurnPow >= EMS_BOILER_BURNPOWER_TAPWATER) & (EMS_Boiler.burnGas == EMS_VALUE_INT_ON));
*/
// hot tap water, using flow to check insread of the burner power // hot tap water, using flow to check insread of the burner power
EMS_Boiler.tapwaterActive = ((EMS_Boiler.wWCurFlow != 0) && (EMS_Boiler.burnGas == EMS_VALUE_INT_ON)); EMS_Boiler.tapwaterActive = ((EMS_Boiler.wWCurFlow != 0) && (EMS_Boiler.burnGas == EMS_VALUE_INT_ON));
@@ -885,7 +878,7 @@ bool _checkActive() {
* UBAParameterWW - type 0x33 - warm water parameters * UBAParameterWW - type 0x33 - warm water parameters
* received only after requested (not broadcasted) * received only after requested (not broadcasted)
*/ */
void _process_UBAParameterWW(uint8_t * data, uint8_t length) { void _process_UBAParameterWW(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.wWActivated = (data[1] == 0xFF); // 0xFF means on EMS_Boiler.wWActivated = (data[1] == 0xFF); // 0xFF means on
EMS_Boiler.wWSelTemp = data[2]; EMS_Boiler.wWSelTemp = data[2];
EMS_Boiler.wWCircPump = (data[6] == 0xFF); // 0xFF means on EMS_Boiler.wWCircPump = (data[6] == 0xFF); // 0xFF means on
@@ -899,7 +892,7 @@ void _process_UBAParameterWW(uint8_t * data, uint8_t length) {
* UBATotalUptimeMessage - type 0x14 - total uptime * UBATotalUptimeMessage - type 0x14 - total uptime
* received only after requested (not broadcasted) * received only after requested (not broadcasted)
*/ */
void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length) { void _process_UBATotalUptimeMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.UBAuptime = _toLong(0, data); EMS_Boiler.UBAuptime = _toLong(0, data);
EMS_Sys_Status.emsRefreshed = true; // when we receieve this, lets force an MQTT publish EMS_Sys_Status.emsRefreshed = true; // when we receieve this, lets force an MQTT publish
} }
@@ -907,7 +900,7 @@ void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length) {
/* /*
* UBAParametersMessage - type 0x16 * UBAParametersMessage - type 0x16
*/ */
void _process_UBAParametersMessage(uint8_t * data, uint8_t length) { void _process_UBAParametersMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.heating_temp = data[1]; EMS_Boiler.heating_temp = data[1];
EMS_Boiler.pump_mod_max = data[9]; EMS_Boiler.pump_mod_max = data[9];
EMS_Boiler.pump_mod_min = data[10]; EMS_Boiler.pump_mod_min = data[10];
@@ -917,7 +910,7 @@ void _process_UBAParametersMessage(uint8_t * data, uint8_t length) {
* UBAMonitorWWMessage - type 0x34 - warm water monitor. 19 bytes long * UBAMonitorWWMessage - type 0x34 - warm water monitor. 19 bytes long
* received every 10 seconds * received every 10 seconds
*/ */
void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length) { void _process_UBAMonitorWWMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.wWCurTmp = _toFloat(1, data); EMS_Boiler.wWCurTmp = _toFloat(1, data);
EMS_Boiler.wWStarts = _toLong(13, data); EMS_Boiler.wWStarts = _toLong(13, data);
EMS_Boiler.wWWorkM = _toLong(10, data); EMS_Boiler.wWWorkM = _toLong(10, data);
@@ -929,7 +922,7 @@ void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length) {
* UBAMonitorFast - type 0x18 - central heating monitor part 1 (25 bytes long) * UBAMonitorFast - type 0x18 - central heating monitor part 1 (25 bytes long)
* received every 10 seconds * received every 10 seconds
*/ */
void _process_UBAMonitorFast(uint8_t * data, uint8_t length) { void _process_UBAMonitorFast(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.selFlowTemp = data[0]; EMS_Boiler.selFlowTemp = data[0];
EMS_Boiler.curFlowTemp = _toFloat(1, data); EMS_Boiler.curFlowTemp = _toFloat(1, data);
EMS_Boiler.retTemp = _toFloat(13, data); EMS_Boiler.retTemp = _toFloat(13, data);
@@ -965,7 +958,7 @@ void _process_UBAMonitorFast(uint8_t * data, uint8_t length) {
* UBAMonitorSlow - type 0x19 - central heating monitor part 2 (27 bytes long) * UBAMonitorSlow - type 0x19 - central heating monitor part 2 (27 bytes long)
* received every 60 seconds * received every 60 seconds
*/ */
void _process_UBAMonitorSlow(uint8_t * data, uint8_t length) { void _process_UBAMonitorSlow(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Boiler.extTemp = _toFloat(0, data); // 0x8000 if not available EMS_Boiler.extTemp = _toFloat(0, data); // 0x8000 if not available
EMS_Boiler.boilTemp = _toFloat(2, data); // 0x8000 if not available EMS_Boiler.boilTemp = _toFloat(2, data); // 0x8000 if not available
EMS_Boiler.pumpMod = data[9]; EMS_Boiler.pumpMod = data[9];
@@ -974,12 +967,25 @@ void _process_UBAMonitorSlow(uint8_t * data, uint8_t length) {
EMS_Boiler.heatWorkMin = _toLong(19, data); EMS_Boiler.heatWorkMin = _toLong(19, data);
} }
/**
* type 0xB1 - data from the RC10 thermostat (0x17)
* For reading the temp values only
* received every 60 seconds
*/
void _process_RC10StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC10StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC10StatusMessage_curr, data);
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
}
/** /**
* type 0x91 - data from the RC20 thermostat (0x17) - 15 bytes long * type 0x91 - data from the RC20 thermostat (0x17) - 15 bytes long
* For reading the temp values only * For reading the temp values only
* received every 60 seconds * received every 60 seconds
*/ */
void _process_RC20StatusMessage(uint8_t * data, uint8_t length) { void _process_RC20StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC20StatusMessage_setpoint]) / (float)2; EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC20StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC20StatusMessage_curr, data); EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC20StatusMessage_curr, data);
@@ -991,7 +997,7 @@ void _process_RC20StatusMessage(uint8_t * data, uint8_t length) {
* For reading the temp values only * For reading the temp values only
* received every 60 seconds * received every 60 seconds
*/ */
void _process_RC30StatusMessage(uint8_t * data, uint8_t length) { void _process_RC30StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC30StatusMessage_setpoint]) / (float)2; EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC30StatusMessage_setpoint]) / (float)2;
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC30StatusMessage_curr, data); EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC30StatusMessage_curr, data);
@@ -1003,7 +1009,7 @@ void _process_RC30StatusMessage(uint8_t * data, uint8_t length) {
* For reading the temp values only * For reading the temp values only
* received every 60 seconds * received every 60 seconds
*/ */
void _process_RC35StatusMessage(uint8_t * data, uint8_t length) { void _process_RC35StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC35StatusMessage_setpoint]) / (float)2; EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC35StatusMessage_setpoint]) / (float)2;
// check if temp sensor is unavailable // check if temp sensor is unavailable
@@ -1020,18 +1026,26 @@ void _process_RC35StatusMessage(uint8_t * data, uint8_t length) {
* type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long * type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
* The Easy has a digital precision of its floats to 2 decimal places, so values is divided by 100 * The Easy has a digital precision of its floats to 2 decimal places, so values is divided by 100
*/ */
void _process_EasyStatusMessage(uint8_t * data, uint8_t length) { void _process_EasyStatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.curr_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_curr] << 8) + data[9]))) / 100; EMS_Thermostat.curr_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_curr] << 8) + data[9]))) / 100;
EMS_Thermostat.setpoint_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_setpoint] << 8) + data[11]))) / 100; EMS_Thermostat.setpoint_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_setpoint] << 8) + data[11]))) / 100;
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
} }
/**
* type 0xB0 - for reading the mode from the RC10 thermostat (0x17)
* received only after requested
*/
void _process_RC10Set(uint8_t type, uint8_t * data, uint8_t length) {
// mode not implemented yet
}
/** /**
* type 0xA8 - for reading the mode from the RC20 thermostat (0x17) * type 0xA8 - for reading the mode from the RC20 thermostat (0x17)
* received only after requested * received only after requested
*/ */
void _process_RC20Set(uint8_t * data, uint8_t length) { void _process_RC20Set(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.mode = data[EMS_OFFSET_RC20Set_mode]; EMS_Thermostat.mode = data[EMS_OFFSET_RC20Set_mode];
} }
@@ -1039,7 +1053,7 @@ void _process_RC20Set(uint8_t * data, uint8_t length) {
* type 0xA7 - for reading the mode from the RC30 thermostat (0x10) * type 0xA7 - for reading the mode from the RC30 thermostat (0x10)
* received only after requested * received only after requested
*/ */
void _process_RC30Set(uint8_t * data, uint8_t length) { void _process_RC30Set(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.mode = data[EMS_OFFSET_RC30Set_mode]; EMS_Thermostat.mode = data[EMS_OFFSET_RC30Set_mode];
} }
@@ -1048,14 +1062,14 @@ void _process_RC30Set(uint8_t * data, uint8_t length) {
* Working Mode Heating Circuit 1 (HC1) * Working Mode Heating Circuit 1 (HC1)
* received only after requested * received only after requested
*/ */
void _process_RC35Set(uint8_t * data, uint8_t length) { void _process_RC35Set(uint8_t type, uint8_t * data, uint8_t length) {
EMS_Thermostat.mode = data[EMS_OFFSET_RC35Set_mode]; EMS_Thermostat.mode = data[EMS_OFFSET_RC35Set_mode];
} }
/** /**
* type 0xA3 - for external temp settings from the the RC* thermostats * type 0xA3 - for external temp settings from the the RC* thermostats
*/ */
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length) { void _process_RCOutdoorTempMessage(uint8_t type, uint8_t * data, uint8_t length) {
// add support here if you're reading external sensors // add support here if you're reading external sensors
} }
@@ -1063,7 +1077,7 @@ void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length) {
* type 0x02 - get the firmware version and type of an EMS device * type 0x02 - get the firmware version and type of an EMS device
* look up known devices via the product id and setup if not already set * look up known devices via the product id and setup if not already set
*/ */
void _process_Version(uint8_t * data, uint8_t length) { void _process_Version(uint8_t type, uint8_t * data, uint8_t length) {
// ignore short messages that we can't interpret // ignore short messages that we can't interpret
if (length < 3) { if (length < 3) {
return; return;
@@ -1155,7 +1169,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
ems_getThermostatValues(); ems_getThermostatValues();
} }
} else { } else {
myDebug("Unrecognized device found. Product ID %d, Version %s", product_id, version); myDebug("Unrecognized device found. TypeID 0x%02X, Product ID %d, Version %s", type, product_id, version);
} }
// if the boiler or thermostat values have changed, save them to SPIFFS // if the boiler or thermostat values have changed, save them to SPIFFS
@@ -1170,7 +1184,6 @@ void _process_Version(uint8_t * data, uint8_t length) {
void ems_discoverModels() { void ems_discoverModels() {
// boiler // boiler
ems_doReadCommand(EMS_TYPE_Version, EMS_Boiler.type_id); // get version details of boiler ems_doReadCommand(EMS_TYPE_Version, EMS_Boiler.type_id); // get version details of boiler
ems_getBoilerValues(); // fetch values from boiler, instead of waiting for broadcasts
// thermostat // thermostat
// if it hasn't been set, auto discover it // if it hasn't been set, auto discover it
@@ -1179,7 +1192,6 @@ void ems_discoverModels() {
} else { } else {
// set the model as hardcoded (see my_devices.h) and fetch the version and product id // set the model as hardcoded (see my_devices.h) and fetch the version and product id
ems_doReadCommand(EMS_TYPE_Version, EMS_Thermostat.type_id); ems_doReadCommand(EMS_TYPE_Version, EMS_Thermostat.type_id);
ems_getThermostatValues();
} }
} }
@@ -1223,24 +1235,26 @@ void _ems_setThermostatModel(uint8_t thermostat_modelid) {
} }
/** /**
* UBASetPoint 0x1A, for RC20 and other thermostats. not really sure what to do with this data yet. * UBASetPoint 0x1A
*/ */
void _process_SetPoints(uint8_t * data, uint8_t length) { void _process_SetPoints(uint8_t type, uint8_t * data, uint8_t length) {
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_THERMOSTAT) { /*
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
if (length != 0) { if (length != 0) {
uint8_t setpoint = data[0]; uint8_t setpoint = data[0];
uint8_t hk_power = data[1]; uint8_t hk_power = data[1];
uint8_t ww_power = data[2]; uint8_t ww_power = data[2];
myDebug(" SetPoint=%d, hk_power=%d ww_power=%d", setpoint, hk_power, ww_power); myDebug(" SetPoint=%d, hk_power=%d, ww_power=%d", setpoint, hk_power, ww_power);
} }
} }
*/
} }
/** /**
* process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long * process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long
* common for all thermostats * common for all thermostats
*/ */
void _process_RCTime(uint8_t * data, uint8_t length) { void _process_RCTime(uint8_t type, uint8_t * data, uint8_t length) {
if (EMS_Thermostat.model_id == EMS_MODEL_EASY) { if (EMS_Thermostat.model_id == EMS_MODEL_EASY) {
return; // not supported return; // not supported
} }
@@ -1260,7 +1274,12 @@ void ems_printTxQueue() {
_EMS_TxTelegram EMS_TxTelegram; _EMS_TxTelegram EMS_TxTelegram;
char sType[20] = {0}; char sType[20] = {0};
myDebug("Tx queue (%d/%d), 1=first to send", EMS_TxQueue.size(), EMS_TxQueue.capacity); if (EMS_TxQueue.size() == 0) {
myDebug("Tx queue is empty.");
return;
}
myDebug("Tx queue (%d/%d)", EMS_TxQueue.size(), EMS_TxQueue.capacity);
for (byte i = 0; i < EMS_TxQueue.size(); i++) { for (byte i = 0; i < EMS_TxQueue.size(); i++) {
EMS_TxTelegram = EMS_TxQueue[i]; // retrieves the i-th element from the buffer without removing it EMS_TxTelegram = EMS_TxQueue[i]; // retrieves the i-th element from the buffer without removing it
@@ -1341,7 +1360,7 @@ void ems_getBoilerValues() {
ems_doReadCommand(EMS_TYPE_UBAMonitorSlow, EMS_Boiler.type_id); // get more boiler stats, instead of waiting 60secs for the broadcast ems_doReadCommand(EMS_TYPE_UBAMonitorSlow, EMS_Boiler.type_id); // get more boiler stats, instead of waiting 60secs for the broadcast
ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_Boiler.type_id); // get Warm Water values ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_Boiler.type_id); // get Warm Water values
ems_doReadCommand(EMS_TYPE_UBAParametersMessage, EMS_Boiler.type_id); // get MC10 boiler values ems_doReadCommand(EMS_TYPE_UBAParametersMessage, EMS_Boiler.type_id); // get MC10 boiler values
ems_doReadCommand(EMS_TYPE_UBATotalUptimeMessage, EMS_Boiler.type_id); // get Warm Water values ems_doReadCommand(EMS_TYPE_UBATotalUptimeMessage, EMS_Boiler.type_id); // get uptime from boiler
} }
/** /**
@@ -1495,6 +1514,7 @@ void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
// see if its a known type // see if its a known type
int i = _ems_findType(type); int i = _ems_findType(type);
@@ -1532,6 +1552,7 @@ void ems_sendRawTelegram(char * telegram) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
// get first value, which should be the src // get first value, which should be the src
if (p = strtok(telegram, " ,")) { // delimiter if (p = strtok(telegram, " ,")) { // delimiter
@@ -1578,6 +1599,7 @@ void ems_setThermostatTemp(float temperature) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
uint8_t model_id = EMS_Thermostat.model_id; uint8_t model_id = EMS_Thermostat.model_id;
uint8_t type = EMS_Thermostat.type_id; uint8_t type = EMS_Thermostat.type_id;
@@ -1593,6 +1615,10 @@ void ems_setThermostatTemp(float temperature) {
EMS_TxTelegram.type = EMS_TYPE_RC20Set; EMS_TxTelegram.type = EMS_TYPE_RC20Set;
EMS_TxTelegram.offset = EMS_OFFSET_RC20Set_temp; EMS_TxTelegram.offset = EMS_OFFSET_RC20Set_temp;
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC20StatusMessage; EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC20StatusMessage;
} else if (model_id == EMS_MODEL_RC10) {
EMS_TxTelegram.type = EMS_TYPE_RC10Set;
EMS_TxTelegram.offset = EMS_OFFSET_RC10Set_temp;
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC10StatusMessage;
} else if (model_id == EMS_MODEL_RC30) { } else if (model_id == EMS_MODEL_RC30) {
EMS_TxTelegram.type = EMS_TYPE_RC30Set; EMS_TxTelegram.type = EMS_TYPE_RC30Set;
EMS_TxTelegram.offset = EMS_OFFSET_RC30Set_temp; EMS_TxTelegram.offset = EMS_OFFSET_RC30Set_temp;
@@ -1639,6 +1665,7 @@ void ems_setThermostatMode(uint8_t mode) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE; EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dest = type; EMS_TxTelegram.dest = type;
@@ -1679,6 +1706,7 @@ void ems_setWarmWaterTemp(uint8_t temperature) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE; EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dest = EMS_Boiler.type_id; EMS_TxTelegram.dest = EMS_Boiler.type_id;
@@ -1703,6 +1731,8 @@ void ems_setWarmWaterModeComfort(bool comfort) {
myDebug("Setting boiler warm water to comfort mode %s\n", comfort ? "Comfort" : "Eco"); myDebug("Setting boiler warm water to comfort mode %s\n", comfort ? "Comfort" : "Eco");
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE; EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dest = EMS_Boiler.type_id; EMS_TxTelegram.dest = EMS_Boiler.type_id;
@@ -1712,6 +1742,7 @@ void ems_setWarmWaterModeComfort(bool comfort) {
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
EMS_TxTelegram.dataValue = EMS_TxTelegram.dataValue =
(comfort ? EMS_VALUE_UBAParameterWW_wwComfort_Comfort : EMS_VALUE_UBAParameterWW_wwComfort_Eco); // 0x00 is on, 0xD8 is off (comfort ? EMS_VALUE_UBAParameterWW_wwComfort_Comfort : EMS_VALUE_UBAParameterWW_wwComfort_Eco); // 0x00 is on, 0xD8 is off
EMS_TxQueue.push(EMS_TxTelegram); EMS_TxQueue.push(EMS_TxTelegram);
} }
@@ -1723,6 +1754,8 @@ void ems_setWarmWaterActivated(bool activated) {
myDebug("Setting boiler warm water %s", activated ? "on" : "off"); myDebug("Setting boiler warm water %s", activated ? "on" : "off");
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE; EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
EMS_TxTelegram.dest = EMS_Boiler.type_id; EMS_TxTelegram.dest = EMS_Boiler.type_id;
@@ -1731,6 +1764,7 @@ void ems_setWarmWaterActivated(bool activated) {
EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
EMS_TxTelegram.dataValue = (activated ? 0xFF : 0x00); // 0xFF is on, 0x00 is off EMS_TxTelegram.dataValue = (activated ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
EMS_TxQueue.push(EMS_TxTelegram); EMS_TxQueue.push(EMS_TxTelegram);
} }
@@ -1744,6 +1778,7 @@ void ems_setWarmTapWaterActivated(bool activated) {
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx _EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
EMS_TxTelegram.timestamp = millis(); // set timestamp EMS_TxTelegram.timestamp = millis(); // set timestamp
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
// clear Tx to make sure all data is set to 0x00 // clear Tx to make sure all data is set to 0x00
for (int i = 0; (i < EMS_MAX_TELEGRAM_LENGTH); i++) { for (int i = 0; (i < EMS_MAX_TELEGRAM_LENGTH); i++) {
@@ -1762,7 +1797,6 @@ void ems_setWarmTapWaterActivated(bool activated) {
EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.type; EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.type;
EMS_TxTelegram.forceRefresh = true; // send new value to MQTT after successful write EMS_TxTelegram.forceRefresh = true; // send new value to MQTT after successful write
// create header // create header
EMS_TxTelegram.data[0] = EMS_ID_ME; // src EMS_TxTelegram.data[0] = EMS_ID_ME; // src
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest

View File

@@ -61,17 +61,18 @@
/* EMS UART transfer status */ /* EMS UART transfer status */
typedef enum { typedef enum {
EMS_RX_IDLE, EMS_RX_STATUS_IDLE,
EMS_RX_ACTIVE // Rx package is being sent EMS_RX_STATUS_BUSY // Rx package is being received
} _EMS_RX_STATUS; } _EMS_RX_STATUS;
typedef enum { typedef enum {
EMS_TX_IDLE, EMS_TX_STATUS_IDLE, // ready
EMS_TX_ACTIVE, // Tx package being sent, no break sent EMS_TX_STATUS_WAIT // waiting for response from last Tx
EMS_TX_SUCCESS,
EMS_TX_ERROR
} _EMS_TX_STATUS; } _EMS_TX_STATUS;
#define EMS_TX_SUCCESS 0x01 // EMS single byte after a Tx Write indicating a success
#define EMS_TX_ERROR 0x04 // EMS single byte after a Tx Write indicating an error
typedef enum { typedef enum {
EMS_TX_TELEGRAM_INIT, // just initialized EMS_TX_TELEGRAM_INIT, // just initialized
EMS_TX_TELEGRAM_READ, // doing a read request EMS_TX_TELEGRAM_READ, // doing a read request
@@ -97,18 +98,18 @@ typedef struct {
uint16_t emsTxPkgs; // sent uint16_t emsTxPkgs; // sent
uint16_t emxCrcErr; // CRC errors uint16_t emxCrcErr; // CRC errors
bool emsPollEnabled; // flag enable the response to poll messages bool emsPollEnabled; // flag enable the response to poll messages
bool emsTxEnabled; // flag if we're allowing sending of Tx packages
_EMS_SYS_LOGGING emsLogging; // logging _EMS_SYS_LOGGING emsLogging; // logging
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
bool emsBusConnected; // is there an active bus bool emsBusConnected; // is there an active bus
unsigned long emsRxTimestamp; // timestamp of last EMS message received unsigned long emsRxTimestamp; // timestamp of last EMS message received
unsigned long emsPollTimestamp; // timestamp of last EMS poll unsigned long emsPollTimestamp; // timestamp of last EMS poll sent to us
bool emsTxCapable; // able to send via Tx bool emsTxCapable; // able to send via Tx
uint8_t txRetryCount; // # times the last Tx was re-sent
} _EMS_Sys_Status; } _EMS_Sys_Status;
// The Tx send package // The Tx send package
typedef struct { typedef struct {
_EMS_TX_TELEGRAM_ACTION action; // read or write _EMS_TX_TELEGRAM_ACTION action; // read, write, validate, init
uint8_t dest; uint8_t dest;
uint8_t type; uint8_t type;
uint8_t offset; uint8_t offset;
@@ -123,6 +124,8 @@ typedef struct {
uint8_t data[EMS_MAX_TELEGRAM_LENGTH]; uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
} _EMS_TxTelegram; } _EMS_TxTelegram;
// default empty Tx // default empty Tx
const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = { const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
EMS_TX_TELEGRAM_INIT, // action EMS_TX_TELEGRAM_INIT, // action
@@ -236,8 +239,8 @@ typedef struct {
uint8_t year; uint8_t year;
} _EMS_Thermostat; } _EMS_Thermostat;
// call back function signature // call back function signature for processing telegram types
typedef void (*EMS_processType_cb)(uint8_t * data, uint8_t length); typedef void (*EMS_processType_cb)(uint8_t type, uint8_t * data, uint8_t length);
// Definition for each EMS type, including the relative callback function // Definition for each EMS type, including the relative callback function
typedef struct { typedef struct {
@@ -293,6 +296,7 @@ void _ems_clearTxData();
int _ems_findBoilerModel(uint8_t model_id); int _ems_findBoilerModel(uint8_t model_id);
bool _ems_setModel(uint8_t model_id); bool _ems_setModel(uint8_t model_id);
void _ems_setThermostatModel(uint8_t thermostat_modelid); void _ems_setThermostatModel(uint8_t thermostat_modelid);
void _removeTxQueue();
// global so can referenced in other classes // global so can referenced in other classes
extern _EMS_Sys_Status EMS_Sys_Status; extern _EMS_Sys_Status EMS_Sys_Status;

View File

@@ -46,6 +46,13 @@
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast #define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp #define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
// RC10 specific
#define EMS_TYPE_RC10StatusMessage 0xB1 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC10Set 0xB0 // for setting values like temp and mode
#define EMS_OFFSET_RC10Set_temp 4 // position of thermostat setpoint temperature
#define EMS_TYPE_RC10StatusMessage_setpoint 1 // setpoint temp
#define EMS_TYPE_RC10StatusMessage_curr 3 // current temp
// RC20 specific // RC20 specific
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps #define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode #define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
@@ -87,6 +94,7 @@ typedef enum {
// thermostats // thermostats
EMS_MODEL_ES73, EMS_MODEL_ES73,
EMS_MODEL_RC10,
EMS_MODEL_RC20, EMS_MODEL_RC20,
EMS_MODEL_RC20F, EMS_MODEL_RC20F,
EMS_MODEL_RC30, EMS_MODEL_RC30,
@@ -104,8 +112,9 @@ const _Boiler_Type Boiler_Types[] = {
{EMS_MODEL_UBA, 72, 0x08, "MC10"}, {EMS_MODEL_UBA, 72, 0x08, "MC10"},
{EMS_MODEL_UBA, 123, 0x08, "Buderus GB172/Nefit Trendline"}, {EMS_MODEL_UBA, 123, 0x08, "Buderus GB172/Nefit Trendline"},
{EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"}, {EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"},
{EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler"}, {EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler/Nefit Smartline"},
{EMS_MODEL_UBA, 190, 0x09, "BC10 Base Controller"}, {EMS_MODEL_UBA, 190, 0x09, "BC10 Base Controller"},
{EMS_MODEL_UBA, 114, 0x09, "BC10 Base Controller"},
{EMS_MODEL_UBA, 125, 0x09, "BC25 Base Controller"}, {EMS_MODEL_UBA, 125, 0x09, "BC25 Base Controller"},
{EMS_MODEL_UBA, 68, 0x09, "RFM20 Receiver"}, {EMS_MODEL_UBA, 68, 0x09, "RFM20 Receiver"},
{EMS_MODEL_UBA, 95, 0x08, "Bosch Condens 2500"}, {EMS_MODEL_UBA, 95, 0x08, "Bosch Condens 2500"},
@@ -120,6 +129,7 @@ const _Boiler_Type Boiler_Types[] = {
const _Thermostat_Type Thermostat_Types[] = { const _Thermostat_Type Thermostat_Types[] = {
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}, {EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC10, 79, 0x17, "RC10/Nefit Moduline 100)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20, 77, 0x17, "RC20/Nefit Moduline 300)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}, {EMS_MODEL_RC20, 77, 0x17, "RC20/Nefit Moduline 300)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}, {EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC30, 78, 0x10, "RC30/Nefit Moduline 400)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}, {EMS_MODEL_RC30, 78, 0x10, "RC30/Nefit Moduline 400)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},

View File

@@ -28,8 +28,8 @@ static void emsuart_rx_intr_handler(void * para) {
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE]; static uint8_t uart_buffer[EMS_MAXBUFFERSIZE];
// 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_IDLE) { if (EMS_Sys_Status.emsRxStatus == EMS_RX_STATUS_IDLE) {
EMS_Sys_Status.emsRxStatus = EMS_RX_ACTIVE; // status set to active EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_BUSY; // status set to busy
length = 0; length = 0;
} }
@@ -55,7 +55,7 @@ static void emsuart_rx_intr_handler(void * para) {
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, length); os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, length);
// set the status flag stating BRK has been received and we can start a new package // set the status flag stating BRK has been received and we can start a new package
EMS_Sys_Status.emsRxStatus = EMS_RX_IDLE; EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE;
// call emsuart_recvTask() at next opportunity // call emsuart_recvTask() at next opportunity
system_os_post(EMSUART_recvTaskPrio, 0, 0); system_os_post(EMSUART_recvTaskPrio, 0, 0);

View File

@@ -10,8 +10,10 @@
#include "ems.h" #include "ems.h"
// All MQTT topics are prefixed with the following string // MQTT base name
#define MQTT_BASE "home" #define MQTT_BASE "home" // all MQTT topics are prefix with this string, in the format <MQTT_BASE>/<app name>/<topic>
// MQTT general settings
#define MQTT_TOPIC_START "start" #define MQTT_TOPIC_START "start"
#define MQTT_TOPIC_START_PAYLOAD "start" #define MQTT_TOPIC_START_PAYLOAD "start"
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name #define MQTT_WILL_TOPIC "status" // for last will & testament topic name
@@ -20,7 +22,7 @@
#define MQTT_KEEPALIVE 300 #define MQTT_KEEPALIVE 300
#define MQTT_QOS 1 #define MQTT_QOS 1
// thermostat // MQTT for thermostat
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values #define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes #define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes
#define TOPIC_THERMOSTAT_CMD_MODE "thermostat_cmd_mode" // for received thermostat mode changes #define TOPIC_THERMOSTAT_CMD_MODE "thermostat_cmd_mode" // for received thermostat mode changes
@@ -28,7 +30,7 @@
#define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature #define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature
#define THERMOSTAT_MODE "thermostat_mode" // mode #define THERMOSTAT_MODE "thermostat_mode" // mode
// boiler // MQTT for boiler
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values #define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running #define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on #define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
@@ -44,17 +46,10 @@
#define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold water when shower time limit has exceeded #define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold water when shower time limit has exceeded
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water #define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// THESE DEFAULT VALUES CAN ALSO BE SET AND STORED WITHTIN THE APPLICATION (see 'set' command) // // THESE DEFAULT VALUES CAN ALSO BE SET AND STORED WITHTIN THE APPLICATION (see 'set' command) //
////////////////////////////////////////////////////////////////////////////////////////////////// // ALTHOUGH YOU MAY ALSO HARDCODE THEM HERE BUT THEY WILL BE OVERWRITTEN WITH NEW RELEASE UPDATES //
////////////////////////////////////////////////////////////////////////////////////////////////////
// Set your wifi and mqtt params
// If set to NULL (default) you'll need to set them in AP mode using the 'set' command
#define WIFI_SSID NULL
#define WIFI_PASSWORD NULL
#define MQTT_HOST NULL
#define MQTT_USER NULL
#define MQTT_PASS NULL
// Set LED pin used for showing ems bus connection status. Solid is connected, Flashing is error // Set LED pin used for showing ems bus connection status. Solid is connected, Flashing is error
// can be either the onboard LED on the ESP8266 (LED_BULLETIN) or external via an external pull-up LED (e.g. D1 on bbqkees' board) // can be either the onboard LED on the ESP8266 (LED_BULLETIN) or external via an external pull-up LED (e.g. D1 on bbqkees' board)

View File

@@ -6,5 +6,5 @@
#pragma once #pragma once
#define APP_NAME "EMS-ESP" #define APP_NAME "EMS-ESP"
#define APP_VERSION "1.4.1" #define APP_VERSION "1.5.0"
#define APP_HOSTNAME "ems-esp" #define APP_HOSTNAME "ems-esp"