release 1.5.0
18
CHANGELOG.md
@@ -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/),
|
||||
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
|
||||
|
||||
### Added
|
||||
|
||||
19
README.md
@@ -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:
|
||||
|
||||

|
||||

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

|
||||
|
||||
**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
|
||||
|
||||
@@ -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
|
||||
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
- RC20 and RC30, both are fully supported
|
||||
- RC10 support is being added
|
||||
- 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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
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
|
||||
|
||||
@@ -286,10 +287,10 @@ edit `platformio.ini` to set `env_default` to your board type, then
|
||||
|
||||
### 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`)
|
||||
- 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
|
||||
- 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
|
||||
@@ -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
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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:
|
||||
|
||||
- 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
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 84 KiB |
BIN
doc/telnet/telnet_menu.jpg
Normal file
|
After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 130 KiB |
@@ -273,6 +273,22 @@ size_t TelnetSpy::write(uint8_t data) {
|
||||
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) {
|
||||
if (usedSer) {
|
||||
int avail = usedSer->available();
|
||||
@@ -538,6 +554,12 @@ void TelnetSpy::setCallbackOnDisconnect(telnetSpyCallback callback) {
|
||||
callbackDisconnect = callback;
|
||||
}
|
||||
|
||||
void TelnetSpy::serialPrint(char c) {
|
||||
if (usedSer) {
|
||||
usedSer->print(c);
|
||||
}
|
||||
}
|
||||
|
||||
void TelnetSpy::handle() {
|
||||
if (firstMainLoop) {
|
||||
firstMainLoop = false;
|
||||
|
||||
@@ -186,6 +186,7 @@ class TelnetSpy : public Stream {
|
||||
void setPingTime(uint16_t pngTime);
|
||||
void setSerial(HardwareSerial * usedSerial);
|
||||
bool isClientConnected();
|
||||
void serialPrint(char c);
|
||||
|
||||
void disconnectClient(); // added by Proddy
|
||||
typedef std::function<void()> telnetSpyCallback; // added by Proddy
|
||||
@@ -241,6 +242,8 @@ class TelnetSpy : public Stream {
|
||||
void setDebugOutput(bool);
|
||||
uint32_t baudRate(void);
|
||||
|
||||
bool isSerialAvailable(void);
|
||||
|
||||
protected:
|
||||
void sendBlock(void);
|
||||
void addTelnetBuf(char c);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ MyESP::MyESP() {
|
||||
_helpProjectCmds_count = 0;
|
||||
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
|
||||
|
||||
_use_serial = false;
|
||||
_mqtt_host = NULL;
|
||||
_mqtt_password = NULL;
|
||||
_mqtt_username = NULL;
|
||||
@@ -361,12 +362,13 @@ void MyESP::_telnet_setup() {
|
||||
SerialAndTelnet.setWelcomeMsg("");
|
||||
SerialAndTelnet.setCallbackOnConnect([this]() { _telnetConnected(); });
|
||||
SerialAndTelnet.setCallbackOnDisconnect([this]() { _telnetDisconnected(); });
|
||||
SerialAndTelnet.begin(115200);
|
||||
SerialAndTelnet.setDebugOutput(false);
|
||||
|
||||
#ifndef DEBUG_SUPPORT
|
||||
if (!_use_serial) {
|
||||
SerialAndTelnet.setSerial(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
SerialAndTelnet.begin(115200); // baud is 115200
|
||||
SerialAndTelnet.setDebugOutput(false);
|
||||
|
||||
// init command buffer for console commands
|
||||
memset(_command, 0, TELNET_MAX_COMMAND_LENGTH);
|
||||
@@ -374,34 +376,41 @@ void MyESP::_telnet_setup() {
|
||||
|
||||
// Show help of commands
|
||||
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) {
|
||||
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 {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
String hostname = String(WiFi.getHostname());
|
||||
#else
|
||||
String hostname = WiFi.hostname();
|
||||
#endif
|
||||
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s\n\r",
|
||||
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s",
|
||||
hostname.c_str(),
|
||||
WiFi.localIP().toString().c_str(),
|
||||
WiFi.macAddress().c_str());
|
||||
SerialAndTelnet.printf("* Connected to WiFi SSID: %s\n\r", WiFi.SSID().c_str());
|
||||
SerialAndTelnet.printf("* Boot time: %s\n\r", _boottime);
|
||||
SerialAndTelnet.println();
|
||||
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()
|
||||
|
||||
#ifdef DEBUG_SUPPORT
|
||||
SerialAndTelnet.println("* Warning: in DEBUG_SUPPORT mode!");
|
||||
#endif
|
||||
|
||||
SerialAndTelnet.println("*\n\r* Commands:\n\r* ?=help, CTRL-D=quit, !=reboot");
|
||||
SerialAndTelnet.println(FPSTR("*"));
|
||||
SerialAndTelnet.println(FPSTR("* Commands:"));
|
||||
SerialAndTelnet.println(FPSTR("* ?=help, CTRL-D=quit, !=reboot"));
|
||||
SerialAndTelnet.println(FPSTR("* set"));
|
||||
SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]"));
|
||||
SerialAndTelnet.println(FPSTR("* set erase"));
|
||||
SerialAndTelnet.println(FPSTR("* set serial"));
|
||||
|
||||
|
||||
// print custom commands if available. Taken from progmem
|
||||
if (_telnetcommand_callback) {
|
||||
@@ -493,6 +502,20 @@ void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
||||
_mqtt_password = strdup(value);
|
||||
}
|
||||
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 {
|
||||
// finally check for any custom commands
|
||||
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
|
||||
if (value == nullptr) {
|
||||
SerialAndTelnet.printf("%s setting deleted\n\r", setting);
|
||||
SerialAndTelnet.printf("%s setting reset to its default value", setting);
|
||||
} else {
|
||||
// 3 params
|
||||
SerialAndTelnet.printf("%s changed to %s\n\r", setting, value);
|
||||
// must be 3 params
|
||||
SerialAndTelnet.printf("%s changed to %s", setting, value);
|
||||
}
|
||||
SerialAndTelnet.println();
|
||||
|
||||
if (fs_saveConfig()) {
|
||||
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");
|
||||
if (strcmp(ptrToCommandName, "set") == 0) {
|
||||
if (wc == 1) {
|
||||
SerialAndTelnet.println("\n\Stored settings:");
|
||||
SerialAndTelnet.printf(" wifi_ssid=%s\n\r", (!_wifi_ssid) ? "<not set>" : _wifi_ssid);
|
||||
SerialAndTelnet.println();
|
||||
SerialAndTelnet.println("Stored settings:");
|
||||
SerialAndTelnet.printf(" wifi_ssid=%s", (!_wifi_ssid) ? "<not set>" : _wifi_ssid);
|
||||
SerialAndTelnet.println();
|
||||
SerialAndTelnet.printf(" wifi_password=");
|
||||
if (!_wifi_password) {
|
||||
SerialAndTelnet.print("<not set>");
|
||||
@@ -545,8 +571,11 @@ void MyESP::_telnetCommand(char * commandLine) {
|
||||
for (uint8_t i = 0; i < strlen(_wifi_password); i++)
|
||||
SerialAndTelnet.print("*");
|
||||
}
|
||||
SerialAndTelnet.printf("\n\r mqtt_host=%s\n\r", (!_mqtt_host) ? "<not set>" : _mqtt_host);
|
||||
SerialAndTelnet.printf(" mqtt_username=%s\n\r", (!_mqtt_username) ? "<not set>" : _mqtt_username);
|
||||
SerialAndTelnet.println();
|
||||
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=");
|
||||
if (!_mqtt_password) {
|
||||
SerialAndTelnet.print("<not set>");
|
||||
@@ -554,12 +583,17 @@ void MyESP::_telnetCommand(char * commandLine) {
|
||||
for (uint8_t i = 0; i < strlen(_mqtt_password); i++)
|
||||
SerialAndTelnet.print("*");
|
||||
}
|
||||
|
||||
SerialAndTelnet.println();
|
||||
SerialAndTelnet.printf(" serial=%s", (_use_serial) ? "on" : "off");
|
||||
|
||||
SerialAndTelnet.println();
|
||||
|
||||
// print custom settings
|
||||
(_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) {
|
||||
char * setting = _telnet_readWord();
|
||||
_changeSetting(1, setting, NULL);
|
||||
@@ -583,9 +617,11 @@ void MyESP::_telnetHandle() {
|
||||
// read asynchronously until full command input
|
||||
while (SerialAndTelnet.available()) {
|
||||
char c = SerialAndTelnet.read();
|
||||
#ifdef DEBUG_SUPPORT
|
||||
Serial.print(c);
|
||||
#endif
|
||||
|
||||
if (_use_serial) {
|
||||
SerialAndTelnet.serialPrint(c); // echo to Serial if connected
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '\r': // likely have full command in buffer now, commands are terminated by CR and/or LF
|
||||
case '\n':
|
||||
@@ -683,6 +719,7 @@ void MyESP::setWIFI(char * wifi_ssid, char * wifi_password, wifi_callback_f call
|
||||
_wifi_callback = callback;
|
||||
}
|
||||
|
||||
// init MQTT settings
|
||||
void MyESP::setMQTT(char * mqtt_host,
|
||||
char * mqtt_username,
|
||||
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)
|
||||
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();
|
||||
|
||||
return ok;
|
||||
@@ -848,6 +893,7 @@ bool MyESP::fs_saveConfig() {
|
||||
json["mqtt_host"] = _mqtt_host;
|
||||
json["mqtt_username"] = _mqtt_username;
|
||||
json["mqtt_password"] = _mqtt_password;
|
||||
json["use_serial"] = _use_serial;
|
||||
|
||||
// callback for saving custom settings
|
||||
(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);
|
||||
|
||||
// call setup of the services...
|
||||
_fs_setup(); // SPIFFS setup, do this first to get values
|
||||
_telnet_setup(); // Telnet setup
|
||||
_fs_setup(); // SPIFFS setup
|
||||
_wifi_setup(); // WIFI setup
|
||||
_mqtt_setup(); // MQTT 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
|
||||
*/
|
||||
void MyESP::loop() {
|
||||
_calculateLoad(); // calculate load
|
||||
_calculateLoad();
|
||||
|
||||
jw.loop(); // WiFi
|
||||
|
||||
_telnetHandle(); // Telnet/Debugger
|
||||
|
||||
// do nothing else until we've got a wifi connection
|
||||
|
||||
@@ -140,8 +140,6 @@ class MyESP {
|
||||
char * _mqtt_host;
|
||||
char * _mqtt_username;
|
||||
char * _mqtt_password;
|
||||
char * _boottime;
|
||||
bool _suspendOutput;
|
||||
char * _mqtt_base;
|
||||
unsigned long _mqtt_keepalive;
|
||||
unsigned char _mqtt_qos;
|
||||
@@ -192,6 +190,9 @@ class MyESP {
|
||||
char * _app_hostname;
|
||||
char * _app_name;
|
||||
char * _app_version;
|
||||
char * _boottime;
|
||||
bool _suspendOutput;
|
||||
bool _use_serial;
|
||||
|
||||
// load average (0..100)
|
||||
void _calculateLoad();
|
||||
|
||||
@@ -6,9 +6,12 @@ env_default = d1_mini
|
||||
[common]
|
||||
platform = espressif8266
|
||||
flash_mode = dout
|
||||
;debug_mode = -DDEBUG_SUPPORT
|
||||
debug_mode =
|
||||
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 =
|
||||
CRC32
|
||||
CircularBuffer
|
||||
@@ -22,7 +25,7 @@ board = nodemcuv2
|
||||
platform = ${common.platform}
|
||||
framework = arduino
|
||||
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}
|
||||
upload_speed = 921600
|
||||
monitor_speed = 115200
|
||||
@@ -34,7 +37,7 @@ board = d1_mini
|
||||
platform = ${common.platform}
|
||||
framework = arduino
|
||||
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}
|
||||
upload_speed = 921600
|
||||
monitor_speed = 115200
|
||||
|
||||
@@ -85,11 +85,9 @@ command_t PROGMEM project_cmds[] = {
|
||||
|
||||
{"info", "show the values"},
|
||||
{"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"},
|
||||
{"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"},
|
||||
{"shower <timer | alert>", "toggle either timer or alert on/off"},
|
||||
{"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(" # 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_getBoilerEnabled() ? "enabled" : "disabled"),
|
||||
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
||||
((EMS_Sys_Status.emsTxEnabled) ? "enabled" : "disabled"),
|
||||
((EMSESP_Status.shower_timer) ? "enabled" : "disabled"),
|
||||
((EMSESP_Status.shower_alert) ? "enabled" : "disabled"));
|
||||
|
||||
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",
|
||||
(ems_getTxCapable() ? "yes" : "no"),
|
||||
myDebug(" Bus Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
|
||||
(ems_getBusConnected() ? "yes" : "no"),
|
||||
EMS_Sys_Status.emsRxPgks,
|
||||
EMS_Sys_Status.emsTxPkgs,
|
||||
@@ -633,7 +628,7 @@ bool FSCallback(MYESP_FSACTION action, JsonObject & json) {
|
||||
|
||||
// callback for custom settings when showing Stored Settings
|
||||
// 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 ok = false;
|
||||
|
||||
@@ -721,18 +716,6 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
|
||||
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) {
|
||||
publishValues(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
|
||||
void WIFICallback() {
|
||||
// when finally we're all set up, we can fire up the uart (this will enable the UART interrupts)
|
||||
#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 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
|
||||
emsuart_init();
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -1100,18 +1078,21 @@ void setup() {
|
||||
// call ems.cpp's init function to set all the internal params
|
||||
ems_init();
|
||||
|
||||
#ifndef DEBUG_SUPPORT
|
||||
// Timers using Ticker library
|
||||
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values
|
||||
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
|
||||
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
|
||||
#endif
|
||||
|
||||
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
||||
myESP.setTelnet(project_cmds, ArraySize(project_cmds), TelnetCommandCallback, TelnetCallback); // set up Telnet commands
|
||||
#ifdef WIFI_SSID
|
||||
myESP.setWIFI(WIFI_SSID, WIFI_PASSWORD, WIFICallback);
|
||||
myESP.setMQTT(
|
||||
MQTT_HOST, MQTT_USER, MQTT_PASS, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, MQTT_WILL_PAYLOAD, MQTTCallback);
|
||||
#else
|
||||
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
|
||||
myESP.setSettings(FSCallback, SettingsCallback);
|
||||
|
||||
438
src/ems.cpp
@@ -24,35 +24,39 @@ CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO
|
||||
// callbacks per type
|
||||
|
||||
// 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
|
||||
void _process_UBAMonitorFast(uint8_t * data, uint8_t length);
|
||||
void _process_UBAMonitorSlow(uint8_t * data, uint8_t length);
|
||||
void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length);
|
||||
void _process_UBAParameterWW(uint8_t * data, uint8_t length);
|
||||
void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length);
|
||||
void _process_UBAParametersMessage(uint8_t * data, uint8_t length);
|
||||
void _process_SetPoints(uint8_t * data, uint8_t length);
|
||||
void _process_UBAMonitorFast(uint8_t type, uint8_t * data, uint8_t length);
|
||||
void _process_UBAMonitorSlow(uint8_t type, uint8_t * data, uint8_t length);
|
||||
void _process_UBAMonitorWWMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||
void _process_UBAParameterWW(uint8_t type, uint8_t * data, uint8_t length);
|
||||
void _process_UBATotalUptimeMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||
void _process_UBAParametersMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||
void _process_SetPoints(uint8_t type, uint8_t * data, uint8_t length);
|
||||
|
||||
// Common for most thermostats
|
||||
void _process_RCTime(uint8_t * data, uint8_t length);
|
||||
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length);
|
||||
void _process_RCTime(uint8_t type, 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
|
||||
void _process_RC20Set(uint8_t * data, uint8_t length);
|
||||
void _process_RC20StatusMessage(uint8_t * data, uint8_t length);
|
||||
void _process_RC20Set(uint8_t type, uint8_t * data, uint8_t length);
|
||||
void _process_RC20StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||
|
||||
// RC30
|
||||
void _process_RC30Set(uint8_t * data, uint8_t length);
|
||||
void _process_RC30StatusMessage(uint8_t * data, uint8_t length);
|
||||
void _process_RC30Set(uint8_t type, uint8_t * data, uint8_t length);
|
||||
void _process_RC30StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||
|
||||
// RC35
|
||||
void _process_RC35Set(uint8_t * data, uint8_t length);
|
||||
void _process_RC35StatusMessage(uint8_t * data, uint8_t length);
|
||||
void _process_RC35Set(uint8_t type, uint8_t * data, uint8_t length);
|
||||
void _process_RC35StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||
|
||||
// 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
|
||||
@@ -73,6 +77,11 @@ const _EMS_Type EMS_Types[] = {
|
||||
{EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", _process_UBAParametersMessage},
|
||||
{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
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{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,
|
||||
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_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
|
||||
// uses -255 or 255 for values that haven't been set yet (EMS_VALUE_INT_NOTSET and EMS_VALUE_FLOAT_NOTSET)
|
||||
void ems_init() {
|
||||
@@ -148,15 +153,15 @@ void ems_init() {
|
||||
EMS_Sys_Status.emsRxPgks = 0;
|
||||
EMS_Sys_Status.emsTxPkgs = 0;
|
||||
EMS_Sys_Status.emxCrcErr = 0;
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_IDLE;
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE;
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE;
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
|
||||
EMS_Sys_Status.emsRefreshed = false;
|
||||
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.emsRxTimestamp = 0;
|
||||
EMS_Sys_Status.emsTxCapable = false;
|
||||
EMS_Sys_Status.emsPollTimestamp = 0;
|
||||
EMS_Sys_Status.txRetryCount = 0;
|
||||
|
||||
// thermostat
|
||||
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_FLOAT_NOTSET;
|
||||
@@ -233,11 +238,6 @@ void ems_init() {
|
||||
EMS_Thermostat.product_id = 0;
|
||||
strlcpy(EMS_Thermostat.version, "not set", sizeof(EMS_Thermostat.version));
|
||||
|
||||
// counters
|
||||
_ems_PollCount = 0;
|
||||
_emsTxRetryCount = 0;
|
||||
_last_TxTelgramCRC = 0;
|
||||
|
||||
// default logging is none
|
||||
ems_setLogging(EMS_SYS_LOGGING_DEFAULT);
|
||||
}
|
||||
@@ -252,15 +252,6 @@ bool ems_getPoll() {
|
||||
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() {
|
||||
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
|
||||
_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
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
|
||||
EMS_TxQueue.shift(); // remove from queue
|
||||
return;
|
||||
}
|
||||
|
||||
// if there is no destination, also delete it from the queue
|
||||
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
|
||||
return;
|
||||
}
|
||||
@@ -508,16 +486,6 @@ void _ems_sendTelegram() {
|
||||
uint8_t crc = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length);
|
||||
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
|
||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||
char s[64] = {0};
|
||||
@@ -533,42 +501,59 @@ void _ems_sendTelegram() {
|
||||
}
|
||||
|
||||
// send the telegram to the UART Tx
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_ACTIVE;
|
||||
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
|
||||
_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.dest = EMS_TxTelegram.dest;
|
||||
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.comparisonPostRead = EMS_TxTelegram.comparisonPostRead;
|
||||
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
|
||||
EMS_TxQueue.shift(); // remove from queue
|
||||
EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it next in line
|
||||
}
|
||||
|
||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE;
|
||||
EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it first to be picked up next (FIFO)
|
||||
}
|
||||
|
||||
/**
|
||||
* the main logic that parses the telegram message, triggered by an interrupt in emsuart.cpp
|
||||
* length is only data bytes, excluding the BRK
|
||||
* 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) {
|
||||
// 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
|
||||
if (value == (EMS_ID_ME | 0x80)) {
|
||||
// store when we received a last poll
|
||||
EMS_Sys_Status.emsPollTimestamp = millis();
|
||||
EMS_Sys_Status.emsPollTimestamp = millis(); // store when we received a last poll
|
||||
EMS_Sys_Status.emsTxCapable = true;
|
||||
|
||||
// do we have something to send thats waiting in the Tx queue? if so send it
|
||||
if (!EMS_TxQueue.isEmpty()) {
|
||||
// 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()) && (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_IDLE)) {
|
||||
_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 {
|
||||
// nothing to send so just send a poll acknowledgement back
|
||||
if (EMS_Sys_Status.emsPollEnabled) {
|
||||
emsaurt_tx_poll();
|
||||
}
|
||||
}
|
||||
} else if ((value == EMS_TX_ERROR) || (value == EMS_TX_SUCCESS)) {
|
||||
// if its a success (01) or failure (04), then see if its from one of our last writes
|
||||
// a response from UBA after a write should be within a specific time period < 100ms
|
||||
if (!EMS_TxQueue.isEmpty()) {
|
||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first(); // get current Tx package we last sent
|
||||
if ((EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) && (value == EMS_TX_ERROR)) {
|
||||
} else if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_WAIT) {
|
||||
// this may be a single byte 01 (success) or 04 (error) from a recent write command?
|
||||
if (value == EMS_TX_SUCCESS) {
|
||||
EMS_Sys_Status.emsTxPkgs++;
|
||||
// got a success 01. Send a validate to check the value of the last write
|
||||
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) {
|
||||
myDebug("* Error: last write failed. removing write request from queue");
|
||||
}
|
||||
EMS_TxQueue.shift(); // write failed so remove from queue and forget it for now
|
||||
myDebug("** Write command failed from host");
|
||||
}
|
||||
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
|
||||
// minimal is 5 bytes, excluding CRC at the end
|
||||
if (length < EMS_MIN_TELEGRAM_LENGTH) {
|
||||
// _debugPrintTelegram("Noisy data:", telegram, length, COLOR_RED);
|
||||
if (length <= 4) {
|
||||
//_debugPrintTelegram("Noisy data:", telegram, length, COLOR_RED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Assume at this point we have something that vaguely resembles a telegram
|
||||
// see if we got a telegram as [src] [dest] [type] [offset] [data] [crc]
|
||||
// so is at least 6 bytes long and the CRC checks out (which is last byte)
|
||||
// Assume at this point we have something that vaguely resembles a telegram in the format [src] [dest] [type] [offset] [data] [crc]
|
||||
// validate the CRC, if its bad ignore it
|
||||
uint8_t crc = _crcCalculator(telegram, length);
|
||||
if (telegram[length - 1] != crc) {
|
||||
EMS_Sys_Status.emxCrcErr++;
|
||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||
_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;
|
||||
}
|
||||
@@ -755,86 +727,127 @@ void _ems_processTelegram(uint8_t * telegram, uint8_t length) {
|
||||
// call callback function to process it
|
||||
// as we only handle complete telegrams (not partial) check that the offset is 0
|
||||
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
|
||||
* We only remove from the Tx queue if the read or write was successful
|
||||
*/
|
||||
void _processType(uint8_t * telegram, uint8_t length) {
|
||||
// header
|
||||
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) {
|
||||
// _debugPrintTelegram("Telegram echo:", telegram, length, COLOR_BLUE);
|
||||
//_debugPrintTelegram("Telegram echo:", telegram, length, COLOR_BLUE);
|
||||
return;
|
||||
}
|
||||
|
||||
// did we request this telegram? If so it would be either a read or a validate telegram and still
|
||||
// on top of the Tx queue with the same type. because we don't remove a Tx from the queue after a send
|
||||
// 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
|
||||
//
|
||||
// if its a broadcast and we didn't just send anything, process it and exit
|
||||
if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_IDLE) {
|
||||
_ems_processTelegram(telegram, length);
|
||||
if (EMS_TxTelegram.forceRefresh) {
|
||||
ems_setEmsRefreshed(true); // set the MQTT refresh flag to force sending to MQTT
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
// 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
|
||||
if (EMS_TxTelegram.comparisonValue == dataReceived) {
|
||||
// there is a match, so write must have been successful
|
||||
EMS_TxQueue.shift(); // remove validate from queue
|
||||
// validate was successful, the write changed the value
|
||||
_removeTxQueue(); // now we can remove the Tx validate command the queue
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Write to 0x%02X was successful", EMS_TxTelegram.dest);
|
||||
}
|
||||
ems_doReadCommand(EMS_TxTelegram.comparisonPostRead,
|
||||
EMS_TxTelegram.dest,
|
||||
true); // get values and force a refresh to MQTT
|
||||
// follow up with the post read command
|
||||
ems_doReadCommand(EMS_TxTelegram.comparisonPostRead, EMS_TxTelegram.dest, true);
|
||||
} else {
|
||||
// write failed.
|
||||
// write failed
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Last write failed. Compared set value 0x%02X with received value 0x%02X",
|
||||
EMS_TxTelegram.comparisonValue,
|
||||
dataReceived);
|
||||
}
|
||||
if (_emsTxRetryCount++ >= TX_WRITE_TIMEOUT_COUNT) {
|
||||
// give up
|
||||
if (++EMS_Sys_Status.txRetryCount > TX_WRITE_TIMEOUT_COUNT) {
|
||||
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 {
|
||||
// retry, turn the validate back into a write and try again
|
||||
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.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
emsaurt_tx_poll(); // send Acknowledgement back to free the EMS bus since we have the telegram
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if hot tap water or heating is active
|
||||
* using a quick hack:
|
||||
* 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
|
||||
* using a quick hack for checking the heating. Selected Flow Temp >= 70
|
||||
*/
|
||||
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
|
||||
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
|
||||
* 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.wWSelTemp = data[2];
|
||||
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
|
||||
* 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_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
|
||||
*/
|
||||
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.pump_mod_max = data[9];
|
||||
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
|
||||
* 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.wWStarts = _toLong(13, 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)
|
||||
* 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.curFlowTemp = _toFloat(1, 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)
|
||||
* 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.boilTemp = _toFloat(2, data); // 0x8000 if not available
|
||||
EMS_Boiler.pumpMod = data[9];
|
||||
@@ -974,12 +967,25 @@ void _process_UBAMonitorSlow(uint8_t * data, uint8_t length) {
|
||||
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
|
||||
* For reading the temp values only
|
||||
* 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.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
|
||||
* 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.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
|
||||
* 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;
|
||||
|
||||
// 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
|
||||
* 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.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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* 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];
|
||||
}
|
||||
|
||||
@@ -1039,7 +1053,7 @@ void _process_RC20Set(uint8_t * data, uint8_t length) {
|
||||
* type 0xA7 - for reading the mode from the RC30 thermostat (0x10)
|
||||
* 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];
|
||||
}
|
||||
|
||||
@@ -1048,14 +1062,14 @@ void _process_RC30Set(uint8_t * data, uint8_t length) {
|
||||
* Working Mode Heating Circuit 1 (HC1)
|
||||
* 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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* 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
|
||||
if (length < 3) {
|
||||
return;
|
||||
@@ -1155,7 +1169,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
ems_getThermostatValues();
|
||||
}
|
||||
} 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
|
||||
@@ -1170,7 +1184,6 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
void ems_discoverModels() {
|
||||
// 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
|
||||
// if it hasn't been set, auto discover it
|
||||
@@ -1179,7 +1192,6 @@ void ems_discoverModels() {
|
||||
} else {
|
||||
// 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_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) {
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_THERMOSTAT) {
|
||||
void _process_SetPoints(uint8_t type, uint8_t * data, uint8_t length) {
|
||||
/*
|
||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||
if (length != 0) {
|
||||
uint8_t setpoint = data[0];
|
||||
uint8_t hk_power = data[1];
|
||||
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
|
||||
* 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) {
|
||||
return; // not supported
|
||||
}
|
||||
@@ -1260,7 +1274,12 @@ void ems_printTxQueue() {
|
||||
_EMS_TxTelegram EMS_TxTelegram;
|
||||
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++) {
|
||||
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_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_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.timestamp = millis(); // set timestamp
|
||||
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||
|
||||
// see if its a known 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.timestamp = millis(); // set timestamp
|
||||
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||
|
||||
// get first value, which should be the src
|
||||
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.timestamp = millis(); // set timestamp
|
||||
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||
|
||||
uint8_t model_id = EMS_Thermostat.model_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.offset = EMS_OFFSET_RC20Set_temp;
|
||||
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) {
|
||||
EMS_TxTelegram.type = EMS_TYPE_RC30Set;
|
||||
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.timestamp = millis(); // set timestamp
|
||||
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||
|
||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||
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.timestamp = millis(); // set timestamp
|
||||
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||
|
||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||
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");
|
||||
|
||||
_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.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.dataValue =
|
||||
(comfort ? EMS_VALUE_UBAParameterWW_wwComfort_Comfort : EMS_VALUE_UBAParameterWW_wwComfort_Eco); // 0x00 is on, 0xD8 is off
|
||||
|
||||
EMS_TxQueue.push(EMS_TxTelegram);
|
||||
}
|
||||
|
||||
@@ -1723,6 +1754,8 @@ void ems_setWarmWaterActivated(bool activated) {
|
||||
myDebug("Setting boiler warm water %s", activated ? "on" : "off");
|
||||
|
||||
_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.dest = EMS_Boiler.type_id;
|
||||
@@ -1731,6 +1764,7 @@ void ems_setWarmWaterActivated(bool activated) {
|
||||
EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
|
||||
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
|
||||
EMS_TxTelegram.dataValue = (activated ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
|
||||
|
||||
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.timestamp = millis(); // set timestamp
|
||||
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||
|
||||
// clear Tx to make sure all data is set to 0x00
|
||||
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.forceRefresh = true; // send new value to MQTT after successful write
|
||||
|
||||
|
||||
// create header
|
||||
EMS_TxTelegram.data[0] = EMS_ID_ME; // src
|
||||
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest
|
||||
|
||||
26
src/ems.h
@@ -61,17 +61,18 @@
|
||||
|
||||
/* EMS UART transfer status */
|
||||
typedef enum {
|
||||
EMS_RX_IDLE,
|
||||
EMS_RX_ACTIVE // Rx package is being sent
|
||||
EMS_RX_STATUS_IDLE,
|
||||
EMS_RX_STATUS_BUSY // Rx package is being received
|
||||
} _EMS_RX_STATUS;
|
||||
|
||||
typedef enum {
|
||||
EMS_TX_IDLE,
|
||||
EMS_TX_ACTIVE, // Tx package being sent, no break sent
|
||||
EMS_TX_SUCCESS,
|
||||
EMS_TX_ERROR
|
||||
EMS_TX_STATUS_IDLE, // ready
|
||||
EMS_TX_STATUS_WAIT // waiting for response from last Tx
|
||||
} _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 {
|
||||
EMS_TX_TELEGRAM_INIT, // just initialized
|
||||
EMS_TX_TELEGRAM_READ, // doing a read request
|
||||
@@ -97,18 +98,18 @@ typedef struct {
|
||||
uint16_t emsTxPkgs; // sent
|
||||
uint16_t emxCrcErr; // CRC errors
|
||||
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
|
||||
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
|
||||
bool emsBusConnected; // is there an active bus
|
||||
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
|
||||
uint8_t txRetryCount; // # times the last Tx was re-sent
|
||||
} _EMS_Sys_Status;
|
||||
|
||||
// The Tx send package
|
||||
typedef struct {
|
||||
_EMS_TX_TELEGRAM_ACTION action; // read or write
|
||||
_EMS_TX_TELEGRAM_ACTION action; // read, write, validate, init
|
||||
uint8_t dest;
|
||||
uint8_t type;
|
||||
uint8_t offset;
|
||||
@@ -123,6 +124,8 @@ typedef struct {
|
||||
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
||||
} _EMS_TxTelegram;
|
||||
|
||||
|
||||
|
||||
// default empty Tx
|
||||
const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
||||
EMS_TX_TELEGRAM_INIT, // action
|
||||
@@ -236,8 +239,8 @@ typedef struct {
|
||||
uint8_t year;
|
||||
} _EMS_Thermostat;
|
||||
|
||||
// call back function signature
|
||||
typedef void (*EMS_processType_cb)(uint8_t * data, uint8_t length);
|
||||
// call back function signature for processing telegram types
|
||||
typedef void (*EMS_processType_cb)(uint8_t type, uint8_t * data, uint8_t length);
|
||||
|
||||
// Definition for each EMS type, including the relative callback function
|
||||
typedef struct {
|
||||
@@ -293,6 +296,7 @@ void _ems_clearTxData();
|
||||
int _ems_findBoilerModel(uint8_t model_id);
|
||||
bool _ems_setModel(uint8_t model_id);
|
||||
void _ems_setThermostatModel(uint8_t thermostat_modelid);
|
||||
void _removeTxQueue();
|
||||
|
||||
// global so can referenced in other classes
|
||||
extern _EMS_Sys_Status EMS_Sys_Status;
|
||||
|
||||
@@ -46,6 +46,13 @@
|
||||
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
|
||||
#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
|
||||
#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
|
||||
@@ -87,6 +94,7 @@ typedef enum {
|
||||
|
||||
// thermostats
|
||||
EMS_MODEL_ES73,
|
||||
EMS_MODEL_RC10,
|
||||
EMS_MODEL_RC20,
|
||||
EMS_MODEL_RC20F,
|
||||
EMS_MODEL_RC30,
|
||||
@@ -104,8 +112,9 @@ const _Boiler_Type Boiler_Types[] = {
|
||||
{EMS_MODEL_UBA, 72, 0x08, "MC10"},
|
||||
{EMS_MODEL_UBA, 123, 0x08, "Buderus GB172/Nefit Trendline"},
|
||||
{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, 114, 0x09, "BC10 Base Controller"},
|
||||
{EMS_MODEL_UBA, 125, 0x09, "BC25 Base Controller"},
|
||||
{EMS_MODEL_UBA, 68, 0x09, "RFM20 Receiver"},
|
||||
{EMS_MODEL_UBA, 95, 0x08, "Bosch Condens 2500"},
|
||||
@@ -120,6 +129,7 @@ const _Boiler_Type Boiler_Types[] = {
|
||||
const _Thermostat_Type Thermostat_Types[] = {
|
||||
|
||||
{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_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},
|
||||
|
||||
@@ -28,8 +28,8 @@ static void emsuart_rx_intr_handler(void * para) {
|
||||
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE];
|
||||
|
||||
// is a new buffer? if so init the thing for a new telegram
|
||||
if (EMS_Sys_Status.emsRxStatus == EMS_RX_IDLE) {
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_ACTIVE; // status set to active
|
||||
if (EMS_Sys_Status.emsRxStatus == EMS_RX_STATUS_IDLE) {
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_BUSY; // status set to busy
|
||||
length = 0;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ static void emsuart_rx_intr_handler(void * para) {
|
||||
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
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_IDLE;
|
||||
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE;
|
||||
|
||||
// call emsuart_recvTask() at next opportunity
|
||||
system_os_post(EMSUART_recvTaskPrio, 0, 0);
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
|
||||
#include "ems.h"
|
||||
|
||||
// All MQTT topics are prefixed with the following string
|
||||
#define MQTT_BASE "home"
|
||||
// MQTT base name
|
||||
#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_PAYLOAD "start"
|
||||
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name
|
||||
@@ -20,7 +22,7 @@
|
||||
#define MQTT_KEEPALIVE 300
|
||||
#define MQTT_QOS 1
|
||||
|
||||
// thermostat
|
||||
// MQTT for thermostat
|
||||
#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_MODE "thermostat_cmd_mode" // for received thermostat mode changes
|
||||
@@ -28,7 +30,7 @@
|
||||
#define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature
|
||||
#define THERMOSTAT_MODE "thermostat_mode" // mode
|
||||
|
||||
// boiler
|
||||
// MQTT for boiler
|
||||
#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_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 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) //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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
|
||||
// ALTHOUGH YOU MAY ALSO HARDCODE THEM HERE BUT THEY WILL BE OVERWRITTEN WITH NEW RELEASE UPDATES //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
#pragma once
|
||||
|
||||
#define APP_NAME "EMS-ESP"
|
||||
#define APP_VERSION "1.4.1"
|
||||
#define APP_VERSION "1.5.0"
|
||||
#define APP_HOSTNAME "ems-esp"
|
||||
|
||||