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/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.5.0] 2019-02-
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Support for RC10 thermostat
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- removed MQTT and WIFI settings from my_config.h. These have to be set either within the application (using set) or hardcoded in platformio.ini
|
||||||
|
- Improved Tx logic. Retries are more efficient and startup is faster
|
||||||
|
- # Rx telegrams and # Tx telegrams show number of successful Reads and Writes initiated by the user or automatically
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- poll and tx commands
|
||||||
|
- DEBUG_SUPPORT. Now controlled with the 'set serial' command
|
||||||
|
- TxCapable removed
|
||||||
|
|
||||||
## [1.4.1] 2019-01-29
|
## [1.4.1] 2019-01-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
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:
|
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.
|
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
|
## Building The Circuit
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ The EMS circuit will work with both 3.3V and 5V. It's easiest though to power di
|
|||||||
|
|
||||||
## Adding external temperature sensors
|
## Adding external temperature sensors
|
||||||
|
|
||||||
The code supports auto-detection of Dallas type temperature sensors. The default gpio pin (DQ) to the ESP8266 is D5 but this can be configured in `my_config.h`. The dallas chips DS1822, DS18S20, DS18B20, DS1825 are supported including their parasite varieties.
|
The code supports auto-detection of Dallas type temperature sensors. The default gpio pin used on the ESP8266 is D5 but this can be configured in the setting menu (`set dallas_gpio`). The dallas chips DS1822, DS18S20, DS18B20, DS1825 are supported including their parasite varieties.
|
||||||
|
|
||||||
## How The EMS Bus Works
|
## How The EMS Bus Works
|
||||||
|
|
||||||
@@ -211,6 +211,7 @@ In `ems.cpp` you can add scheduled calls to specific EMS types in the functions
|
|||||||
I am still working on adding more support to known thermostats. Any contributions here are welcome. The know types are listed in `ems_devices.h` and include
|
I am still working on adding more support to known thermostats. Any contributions here are welcome. The know types are listed in `ems_devices.h` and include
|
||||||
|
|
||||||
- RC20 and RC30, both are fully supported
|
- RC20 and RC30, both are fully supported
|
||||||
|
- RC10 support is being added
|
||||||
- RC35 with support for the 1st heating circuit (HC1)
|
- RC35 with support for the 1st heating circuit (HC1)
|
||||||
- TC100/TC200/Easy but only with support for reading the temperatures. There seems to be no way to set settings using EMS bus messages that I know of. One option is to send XMPP messages but a special server is needed and out of scope for this project.
|
- TC100/TC200/Easy but only with support for reading the temperatures. There seems to be no way to set settings using EMS bus messages that I know of. One option is to send XMPP messages but a special server is needed and out of scope for this project.
|
||||||
|
|
||||||
@@ -234,7 +235,7 @@ Similarly the thermostat values are also sent as a JSON package with the topic `
|
|||||||
|
|
||||||
If MQTT is not used set the MQTT_HOST to `NULL`.
|
If MQTT is not used set the MQTT_HOST to `NULL`.
|
||||||
|
|
||||||
Some home automation systems such as Domoticz and OpenHab have special formats for their MQTT messages so I would advise to use [node-red](https://nodered.org/) as a parser.
|
Some home automation systems such as Domoticz and OpenHab have special formats for their MQTT messages so I would advise to use [node-red](https://nodered.org/) as a parser like in [this example](https://www.domoticz.com/forum/download/file.php?id=18977&sid=67d048f1b4c8833822175eac6b55ecff).
|
||||||
|
|
||||||
### The Basic Shower Logic
|
### The Basic Shower Logic
|
||||||
|
|
||||||
@@ -286,10 +287,10 @@ edit `platformio.ini` to set `env_default` to your board type, then
|
|||||||
|
|
||||||
### Building Using Arduino IDE
|
### Building Using Arduino IDE
|
||||||
|
|
||||||
Porting to the Arduino IDE can be a little tricky but it is possible.
|
Porting to the Arduino IDE can be a little tricky but it did it once. Something along these lines:
|
||||||
|
|
||||||
- Add the ESP8266 boards (from Preferences add Additional Board URL `http://arduino.esp8266.com/stable/package_esp8266com_index.json`)
|
- Add the ESP8266 boards (from Preferences add Additional Board URL `http://arduino.esp8266.com/stable/package_esp8266com_index.json`)
|
||||||
- Go to Boards Manager and install ESP8266 2.4.x platform
|
- Go to Boards Manager and install ESP8266 2.4.x platform. Make sure your board supports SPIFFS.
|
||||||
- Select your ESP8266 from Tools->Boards and the correct port with Tools->Port
|
- Select your ESP8266 from Tools->Boards and the correct port with Tools->Port
|
||||||
- From the Library Manager install the needed libraries from platformio.ini. Note make sure you pick ArduinoJson v5 (5.13.4 and above) and not v6. See https://arduinojson.org/v5/doc/
|
- From the Library Manager install the needed libraries from platformio.ini. Note make sure you pick ArduinoJson v5 (5.13.4 and above) and not v6. See https://arduinojson.org/v5/doc/
|
||||||
- Put all the files in a single sketch folder
|
- Put all the files in a single sketch folder
|
||||||
@@ -304,13 +305,13 @@ pre-baked firmwares for some ESP8266 devices are available in the directory `/fi
|
|||||||
3. Connect the ESP via USB, figure out the COM port
|
3. Connect the ESP via USB, figure out the COM port
|
||||||
4. run `esptool.py -p <com> write_flash 0x00000 <firmware>` where firmware is the `.bin` file and \<com\> is the COM port, e.g. `COM3`
|
4. run `esptool.py -p <com> write_flash 0x00000 <firmware>` where firmware is the `.bin` file and \<com\> is the COM port, e.g. `COM3`
|
||||||
|
|
||||||
The ESP8266 will start in Access Point (AP) mode. Connect via WiFi to the SSID **EMS-ESP** and telnet to **192.168.4.1**. Then use the set command to configure your own network settings.
|
The ESP8266 will start in Access Point (AP) mode. Connect via WiFi to the SSID **EMS-ESP** and telnet to **192.168.4.1**. Then use the set command to configure your own network settings. Alternatively connect the ESP8266 to your PC and open a Serial monitor to configure the settings. Make sure you disable Serial support before connecting the EMS lines using `set serial off`.
|
||||||
|
|
||||||
`set erase` will clear all settings. `set` wil show all settings.
|
`set erase` will clear all settings. `set` wil show all settings.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
If the WiFi fails to connect you won't be able to debug using the Telnet so re-build the firmware using the `-DDEBUG_SUPPORT` by un-commenting `debug_mode` in `platformio.ini`. Then connect the ESP8266 to a USB in your computer and monitor the Serial output. A lot of detailed logging will be printed to help you pinpoint the cause of the error for the Wifi, MQTT, MDNS and other things.
|
When flashing for the first time the Serial port is enabled by default. You can then use a PC with USB to the ESP8266 to set the settings like wifi, mqtt etc and also monitor the boot up procedure. Remember to disable the serial (`set serial off`) when connecting to the EMS lines.
|
||||||
|
|
||||||
The onboard LED will flash if there is no connection with the EMS bus. You can disable LED support by the 'set led' command from the telnet client
|
The onboard LED will flash if there is no connection with the EMS bus. You can disable LED support by the 'set led' command from the telnet client
|
||||||
|
|
||||||
@@ -318,7 +319,7 @@ The onboard LED will flash if there is no connection with the EMS bus. You can d
|
|||||||
|
|
||||||
Some annoying issues that need fixing:
|
Some annoying issues that need fixing:
|
||||||
|
|
||||||
- Very infrequently an EMS write command is not sent, probably due to a collision somewhere in the UART between an incoming Rx and a Poll. The retries in the code fix this for now.
|
- On newer EMS+ Boilers the Tx commands for reading and writing may not always work. I believe there is some handshake that needs to happen before the UBA3/Master is able to send a poll request to our service device.
|
||||||
|
|
||||||
## Wish List
|
## Wish List
|
||||||
|
|
||||||
|
|||||||
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this still needs some work
|
||||||
|
bool TelnetSpy::isSerialAvailable(void) {
|
||||||
|
if (usedSer) {
|
||||||
|
usedSer->write("test");
|
||||||
|
//Wait for four seconds or till data is available on serial, whichever occurs first.
|
||||||
|
while (usedSer->available() == 0 && millis() < 4000)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (usedSer->available() > 0) {
|
||||||
|
(void)usedSer->read(); // We then clear the input buffer
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int TelnetSpy::available(void) {
|
int TelnetSpy::available(void) {
|
||||||
if (usedSer) {
|
if (usedSer) {
|
||||||
int avail = usedSer->available();
|
int avail = usedSer->available();
|
||||||
@@ -538,6 +554,12 @@ void TelnetSpy::setCallbackOnDisconnect(telnetSpyCallback callback) {
|
|||||||
callbackDisconnect = callback;
|
callbackDisconnect = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TelnetSpy::serialPrint(char c) {
|
||||||
|
if (usedSer) {
|
||||||
|
usedSer->print(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TelnetSpy::handle() {
|
void TelnetSpy::handle() {
|
||||||
if (firstMainLoop) {
|
if (firstMainLoop) {
|
||||||
firstMainLoop = false;
|
firstMainLoop = false;
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ class TelnetSpy : public Stream {
|
|||||||
void setPingTime(uint16_t pngTime);
|
void setPingTime(uint16_t pngTime);
|
||||||
void setSerial(HardwareSerial * usedSerial);
|
void setSerial(HardwareSerial * usedSerial);
|
||||||
bool isClientConnected();
|
bool isClientConnected();
|
||||||
|
void serialPrint(char c);
|
||||||
|
|
||||||
void disconnectClient(); // added by Proddy
|
void disconnectClient(); // added by Proddy
|
||||||
typedef std::function<void()> telnetSpyCallback; // added by Proddy
|
typedef std::function<void()> telnetSpyCallback; // added by Proddy
|
||||||
@@ -241,6 +242,8 @@ class TelnetSpy : public Stream {
|
|||||||
void setDebugOutput(bool);
|
void setDebugOutput(bool);
|
||||||
uint32_t baudRate(void);
|
uint32_t baudRate(void);
|
||||||
|
|
||||||
|
bool isSerialAvailable(void);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sendBlock(void);
|
void sendBlock(void);
|
||||||
void addTelnetBuf(char c);
|
void addTelnetBuf(char c);
|
||||||
|
|||||||
@@ -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;
|
_helpProjectCmds_count = 0;
|
||||||
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
|
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
|
||||||
|
|
||||||
|
_use_serial = false;
|
||||||
_mqtt_host = NULL;
|
_mqtt_host = NULL;
|
||||||
_mqtt_password = NULL;
|
_mqtt_password = NULL;
|
||||||
_mqtt_username = NULL;
|
_mqtt_username = NULL;
|
||||||
@@ -361,12 +362,13 @@ void MyESP::_telnet_setup() {
|
|||||||
SerialAndTelnet.setWelcomeMsg("");
|
SerialAndTelnet.setWelcomeMsg("");
|
||||||
SerialAndTelnet.setCallbackOnConnect([this]() { _telnetConnected(); });
|
SerialAndTelnet.setCallbackOnConnect([this]() { _telnetConnected(); });
|
||||||
SerialAndTelnet.setCallbackOnDisconnect([this]() { _telnetDisconnected(); });
|
SerialAndTelnet.setCallbackOnDisconnect([this]() { _telnetDisconnected(); });
|
||||||
SerialAndTelnet.begin(115200);
|
|
||||||
SerialAndTelnet.setDebugOutput(false);
|
|
||||||
|
|
||||||
#ifndef DEBUG_SUPPORT
|
if (!_use_serial) {
|
||||||
SerialAndTelnet.setSerial(NULL);
|
SerialAndTelnet.setSerial(NULL);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
|
SerialAndTelnet.begin(115200); // baud is 115200
|
||||||
|
SerialAndTelnet.setDebugOutput(false);
|
||||||
|
|
||||||
// init command buffer for console commands
|
// init command buffer for console commands
|
||||||
memset(_command, 0, TELNET_MAX_COMMAND_LENGTH);
|
memset(_command, 0, TELNET_MAX_COMMAND_LENGTH);
|
||||||
@@ -374,34 +376,41 @@ void MyESP::_telnet_setup() {
|
|||||||
|
|
||||||
// Show help of commands
|
// Show help of commands
|
||||||
void MyESP::_consoleShowHelp() {
|
void MyESP::_consoleShowHelp() {
|
||||||
SerialAndTelnet.printf("\n\r* Connected to: %s version %s\n\r", _app_name, _app_version);
|
SerialAndTelnet.println();
|
||||||
|
SerialAndTelnet.printf("* Connected to: %s version %s", _app_name, _app_version);
|
||||||
|
SerialAndTelnet.println();
|
||||||
|
|
||||||
if (WiFi.getMode() & WIFI_AP) {
|
if (WiFi.getMode() & WIFI_AP) {
|
||||||
SerialAndTelnet.printf("* ESP8266 is in AP mode with SSID %s\n\r", jw.getAPSSID().c_str());
|
SerialAndTelnet.printf("* ESP8266 is in AP mode with SSID %s", jw.getAPSSID().c_str());
|
||||||
|
SerialAndTelnet.println();
|
||||||
} else {
|
} else {
|
||||||
#if defined(ARDUINO_ARCH_ESP32)
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
String hostname = String(WiFi.getHostname());
|
String hostname = String(WiFi.getHostname());
|
||||||
#else
|
#else
|
||||||
String hostname = WiFi.hostname();
|
String hostname = WiFi.hostname();
|
||||||
#endif
|
#endif
|
||||||
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s\n\r",
|
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s",
|
||||||
hostname.c_str(),
|
hostname.c_str(),
|
||||||
WiFi.localIP().toString().c_str(),
|
WiFi.localIP().toString().c_str(),
|
||||||
WiFi.macAddress().c_str());
|
WiFi.macAddress().c_str());
|
||||||
SerialAndTelnet.printf("* Connected to WiFi SSID: %s\n\r", WiFi.SSID().c_str());
|
SerialAndTelnet.println();
|
||||||
SerialAndTelnet.printf("* Boot time: %s\n\r", _boottime);
|
SerialAndTelnet.printf("* Connected to WiFi SSID: %s", WiFi.SSID().c_str());
|
||||||
|
SerialAndTelnet.println();
|
||||||
|
SerialAndTelnet.printf("* Boot time: %s", _boottime);
|
||||||
|
SerialAndTelnet.println();
|
||||||
}
|
}
|
||||||
SerialAndTelnet.printf("* Free RAM:%d KB, Load:%d%%\n\r", (ESP.getFreeHeap() / 1024), getSystemLoadAverage());
|
SerialAndTelnet.printf("* Free RAM:%d KB, Load:%d%%", (ESP.getFreeHeap() / 1024), getSystemLoadAverage());
|
||||||
|
SerialAndTelnet.println();
|
||||||
// for battery power is ESP.getVcc()
|
// for battery power is ESP.getVcc()
|
||||||
|
|
||||||
#ifdef DEBUG_SUPPORT
|
SerialAndTelnet.println(FPSTR("*"));
|
||||||
SerialAndTelnet.println("* Warning: in DEBUG_SUPPORT mode!");
|
SerialAndTelnet.println(FPSTR("* Commands:"));
|
||||||
#endif
|
SerialAndTelnet.println(FPSTR("* ?=help, CTRL-D=quit, !=reboot"));
|
||||||
|
|
||||||
SerialAndTelnet.println("*\n\r* Commands:\n\r* ?=help, CTRL-D=quit, !=reboot");
|
|
||||||
SerialAndTelnet.println(FPSTR("* set"));
|
SerialAndTelnet.println(FPSTR("* set"));
|
||||||
SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]"));
|
SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]"));
|
||||||
SerialAndTelnet.println(FPSTR("* set erase"));
|
SerialAndTelnet.println(FPSTR("* set erase"));
|
||||||
|
SerialAndTelnet.println(FPSTR("* set serial"));
|
||||||
|
|
||||||
|
|
||||||
// print custom commands if available. Taken from progmem
|
// print custom commands if available. Taken from progmem
|
||||||
if (_telnetcommand_callback) {
|
if (_telnetcommand_callback) {
|
||||||
@@ -493,6 +502,20 @@ void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
|||||||
_mqtt_password = strdup(value);
|
_mqtt_password = strdup(value);
|
||||||
}
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
|
} else if (strcmp(setting, "serial") == 0) {
|
||||||
|
ok = true;
|
||||||
|
_use_serial = false;
|
||||||
|
if (value) {
|
||||||
|
if (strcmp(value, "on") == 0) {
|
||||||
|
_use_serial = true;
|
||||||
|
ok = true;
|
||||||
|
} else if (strcmp(value, "off") == 0) {
|
||||||
|
_use_serial = false;
|
||||||
|
ok = true;
|
||||||
|
} else {
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// finally check for any custom commands
|
// finally check for any custom commands
|
||||||
ok = (_fs_settings_callback)(MYESP_FSACTION_SET, wc, setting, value);
|
ok = (_fs_settings_callback)(MYESP_FSACTION_SET, wc, setting, value);
|
||||||
@@ -505,11 +528,12 @@ void MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
|||||||
|
|
||||||
// check for 2 params
|
// check for 2 params
|
||||||
if (value == nullptr) {
|
if (value == nullptr) {
|
||||||
SerialAndTelnet.printf("%s setting deleted\n\r", setting);
|
SerialAndTelnet.printf("%s setting reset to its default value", setting);
|
||||||
} else {
|
} else {
|
||||||
// 3 params
|
// must be 3 params
|
||||||
SerialAndTelnet.printf("%s changed to %s\n\r", setting, value);
|
SerialAndTelnet.printf("%s changed to %s", setting, value);
|
||||||
}
|
}
|
||||||
|
SerialAndTelnet.println();
|
||||||
|
|
||||||
if (fs_saveConfig()) {
|
if (fs_saveConfig()) {
|
||||||
SerialAndTelnet.println("Note, some changes will only have effect after the ESP is restarted (use ! command)");
|
SerialAndTelnet.println("Note, some changes will only have effect after the ESP is restarted (use ! command)");
|
||||||
@@ -536,8 +560,10 @@ void MyESP::_telnetCommand(char * commandLine) {
|
|||||||
char * ptrToCommandName = strtok((char *)temp, ", \n");
|
char * ptrToCommandName = strtok((char *)temp, ", \n");
|
||||||
if (strcmp(ptrToCommandName, "set") == 0) {
|
if (strcmp(ptrToCommandName, "set") == 0) {
|
||||||
if (wc == 1) {
|
if (wc == 1) {
|
||||||
SerialAndTelnet.println("\n\Stored settings:");
|
SerialAndTelnet.println();
|
||||||
SerialAndTelnet.printf(" wifi_ssid=%s\n\r", (!_wifi_ssid) ? "<not set>" : _wifi_ssid);
|
SerialAndTelnet.println("Stored settings:");
|
||||||
|
SerialAndTelnet.printf(" wifi_ssid=%s", (!_wifi_ssid) ? "<not set>" : _wifi_ssid);
|
||||||
|
SerialAndTelnet.println();
|
||||||
SerialAndTelnet.printf(" wifi_password=");
|
SerialAndTelnet.printf(" wifi_password=");
|
||||||
if (!_wifi_password) {
|
if (!_wifi_password) {
|
||||||
SerialAndTelnet.print("<not set>");
|
SerialAndTelnet.print("<not set>");
|
||||||
@@ -545,8 +571,11 @@ void MyESP::_telnetCommand(char * commandLine) {
|
|||||||
for (uint8_t i = 0; i < strlen(_wifi_password); i++)
|
for (uint8_t i = 0; i < strlen(_wifi_password); i++)
|
||||||
SerialAndTelnet.print("*");
|
SerialAndTelnet.print("*");
|
||||||
}
|
}
|
||||||
SerialAndTelnet.printf("\n\r mqtt_host=%s\n\r", (!_mqtt_host) ? "<not set>" : _mqtt_host);
|
SerialAndTelnet.println();
|
||||||
SerialAndTelnet.printf(" mqtt_username=%s\n\r", (!_mqtt_username) ? "<not set>" : _mqtt_username);
|
SerialAndTelnet.printf(" mqtt_host=%s", (!_mqtt_host) ? "<not set>" : _mqtt_host);
|
||||||
|
SerialAndTelnet.println();
|
||||||
|
SerialAndTelnet.printf(" mqtt_username=%s", (!_mqtt_username) ? "<not set>" : _mqtt_username);
|
||||||
|
SerialAndTelnet.println();
|
||||||
SerialAndTelnet.printf(" mqtt_password=");
|
SerialAndTelnet.printf(" mqtt_password=");
|
||||||
if (!_mqtt_password) {
|
if (!_mqtt_password) {
|
||||||
SerialAndTelnet.print("<not set>");
|
SerialAndTelnet.print("<not set>");
|
||||||
@@ -554,12 +583,17 @@ void MyESP::_telnetCommand(char * commandLine) {
|
|||||||
for (uint8_t i = 0; i < strlen(_mqtt_password); i++)
|
for (uint8_t i = 0; i < strlen(_mqtt_password); i++)
|
||||||
SerialAndTelnet.print("*");
|
SerialAndTelnet.print("*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SerialAndTelnet.println();
|
||||||
|
SerialAndTelnet.printf(" serial=%s", (_use_serial) ? "on" : "off");
|
||||||
|
|
||||||
SerialAndTelnet.println();
|
SerialAndTelnet.println();
|
||||||
|
|
||||||
// print custom settings
|
// print custom settings
|
||||||
(_fs_settings_callback)(MYESP_FSACTION_LIST, 0, NULL, NULL);
|
(_fs_settings_callback)(MYESP_FSACTION_LIST, 0, NULL, NULL);
|
||||||
|
|
||||||
SerialAndTelnet.println("\n\rUsage: set <setting> [value]");
|
SerialAndTelnet.println();
|
||||||
|
SerialAndTelnet.println("Usage: set <setting> [value]");
|
||||||
} else if (wc == 2) {
|
} else if (wc == 2) {
|
||||||
char * setting = _telnet_readWord();
|
char * setting = _telnet_readWord();
|
||||||
_changeSetting(1, setting, NULL);
|
_changeSetting(1, setting, NULL);
|
||||||
@@ -583,9 +617,11 @@ void MyESP::_telnetHandle() {
|
|||||||
// read asynchronously until full command input
|
// read asynchronously until full command input
|
||||||
while (SerialAndTelnet.available()) {
|
while (SerialAndTelnet.available()) {
|
||||||
char c = SerialAndTelnet.read();
|
char c = SerialAndTelnet.read();
|
||||||
#ifdef DEBUG_SUPPORT
|
|
||||||
Serial.print(c);
|
if (_use_serial) {
|
||||||
#endif
|
SerialAndTelnet.serialPrint(c); // echo to Serial if connected
|
||||||
|
}
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\r': // likely have full command in buffer now, commands are terminated by CR and/or LF
|
case '\r': // likely have full command in buffer now, commands are terminated by CR and/or LF
|
||||||
case '\n':
|
case '\n':
|
||||||
@@ -683,6 +719,7 @@ void MyESP::setWIFI(char * wifi_ssid, char * wifi_password, wifi_callback_f call
|
|||||||
_wifi_callback = callback;
|
_wifi_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init MQTT settings
|
||||||
void MyESP::setMQTT(char * mqtt_host,
|
void MyESP::setMQTT(char * mqtt_host,
|
||||||
char * mqtt_username,
|
char * mqtt_username,
|
||||||
char * mqtt_password,
|
char * mqtt_password,
|
||||||
@@ -833,6 +870,14 @@ bool MyESP::_fs_loadConfig() {
|
|||||||
// ok is false if there's a problem loading a custom setting (e.g. does not exist)
|
// ok is false if there's a problem loading a custom setting (e.g. does not exist)
|
||||||
bool ok = (_fs_callback)(MYESP_FSACTION_LOAD, json);
|
bool ok = (_fs_callback)(MYESP_FSACTION_LOAD, json);
|
||||||
|
|
||||||
|
// new configs after release 1.3.x
|
||||||
|
if (json.containsKey("use_serial")) {
|
||||||
|
_use_serial = (bool)json["use_serial"];
|
||||||
|
} else {
|
||||||
|
_use_serial = true; // if first time, set serial
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
configFile.close();
|
configFile.close();
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
@@ -848,6 +893,7 @@ bool MyESP::fs_saveConfig() {
|
|||||||
json["mqtt_host"] = _mqtt_host;
|
json["mqtt_host"] = _mqtt_host;
|
||||||
json["mqtt_username"] = _mqtt_username;
|
json["mqtt_username"] = _mqtt_username;
|
||||||
json["mqtt_password"] = _mqtt_password;
|
json["mqtt_password"] = _mqtt_password;
|
||||||
|
json["use_serial"] = _use_serial;
|
||||||
|
|
||||||
// callback for saving custom settings
|
// callback for saving custom settings
|
||||||
(void)(_fs_callback)(MYESP_FSACTION_SAVE, json);
|
(void)(_fs_callback)(MYESP_FSACTION_SAVE, json);
|
||||||
@@ -911,8 +957,8 @@ void MyESP::begin(char * app_hostname, char * app_name, char * app_version) {
|
|||||||
_app_version = strdup(app_version);
|
_app_version = strdup(app_version);
|
||||||
|
|
||||||
// call setup of the services...
|
// call setup of the services...
|
||||||
|
_fs_setup(); // SPIFFS setup, do this first to get values
|
||||||
_telnet_setup(); // Telnet setup
|
_telnet_setup(); // Telnet setup
|
||||||
_fs_setup(); // SPIFFS setup
|
|
||||||
_wifi_setup(); // WIFI setup
|
_wifi_setup(); // WIFI setup
|
||||||
_mqtt_setup(); // MQTT Setup
|
_mqtt_setup(); // MQTT Setup
|
||||||
_mdns_setup(); // MDNS setup
|
_mdns_setup(); // MDNS setup
|
||||||
@@ -923,9 +969,10 @@ void MyESP::begin(char * app_hostname, char * app_name, char * app_version) {
|
|||||||
* Loop. This is called as often as possible and it handles wifi, telnet, mqtt etc
|
* Loop. This is called as often as possible and it handles wifi, telnet, mqtt etc
|
||||||
*/
|
*/
|
||||||
void MyESP::loop() {
|
void MyESP::loop() {
|
||||||
_calculateLoad(); // calculate load
|
_calculateLoad();
|
||||||
|
|
||||||
jw.loop(); // WiFi
|
jw.loop(); // WiFi
|
||||||
|
|
||||||
_telnetHandle(); // Telnet/Debugger
|
_telnetHandle(); // Telnet/Debugger
|
||||||
|
|
||||||
// do nothing else until we've got a wifi connection
|
// do nothing else until we've got a wifi connection
|
||||||
|
|||||||
@@ -140,8 +140,6 @@ class MyESP {
|
|||||||
char * _mqtt_host;
|
char * _mqtt_host;
|
||||||
char * _mqtt_username;
|
char * _mqtt_username;
|
||||||
char * _mqtt_password;
|
char * _mqtt_password;
|
||||||
char * _boottime;
|
|
||||||
bool _suspendOutput;
|
|
||||||
char * _mqtt_base;
|
char * _mqtt_base;
|
||||||
unsigned long _mqtt_keepalive;
|
unsigned long _mqtt_keepalive;
|
||||||
unsigned char _mqtt_qos;
|
unsigned char _mqtt_qos;
|
||||||
@@ -192,6 +190,9 @@ class MyESP {
|
|||||||
char * _app_hostname;
|
char * _app_hostname;
|
||||||
char * _app_name;
|
char * _app_name;
|
||||||
char * _app_version;
|
char * _app_version;
|
||||||
|
char * _boottime;
|
||||||
|
bool _suspendOutput;
|
||||||
|
bool _use_serial;
|
||||||
|
|
||||||
// load average (0..100)
|
// load average (0..100)
|
||||||
void _calculateLoad();
|
void _calculateLoad();
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ env_default = d1_mini
|
|||||||
[common]
|
[common]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
flash_mode = dout
|
flash_mode = dout
|
||||||
;debug_mode = -DDEBUG_SUPPORT
|
|
||||||
debug_mode =
|
|
||||||
build_flags = -g -w
|
build_flags = -g -w
|
||||||
|
|
||||||
|
wifi_settings =
|
||||||
|
; hard code if you prefer. Recommendation is to set from within the app when in Serial or AP mode
|
||||||
|
;wifi_settings = -DWIFI_SSID="your_ssid" -DWIFI_PASSWORD="your_pw"
|
||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
CRC32
|
CRC32
|
||||||
CircularBuffer
|
CircularBuffer
|
||||||
@@ -22,7 +25,7 @@ board = nodemcuv2
|
|||||||
platform = ${common.platform}
|
platform = ${common.platform}
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps = ${common.lib_deps}
|
lib_deps = ${common.lib_deps}
|
||||||
build_flags = ${common.build_flags} ${common.debug_mode}
|
build_flags = ${common.build_flags} ${common.wifi_settings}
|
||||||
board_build.flash_mode = ${common.flash_mode}
|
board_build.flash_mode = ${common.flash_mode}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
@@ -34,7 +37,7 @@ board = d1_mini
|
|||||||
platform = ${common.platform}
|
platform = ${common.platform}
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps = ${common.lib_deps}
|
lib_deps = ${common.lib_deps}
|
||||||
build_flags = ${common.build_flags} ${common.debug_mode}
|
build_flags = ${common.build_flags} ${common.wifi_settings}
|
||||||
board_build.flash_mode = ${common.flash_mode}
|
board_build.flash_mode = ${common.flash_mode}
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
|
|||||||
@@ -85,11 +85,9 @@ command_t PROGMEM project_cmds[] = {
|
|||||||
|
|
||||||
{"info", "show the values"},
|
{"info", "show the values"},
|
||||||
{"log <n | b | t | r | v>", "set logging mode to none, basic, thermostat only, raw or verbose"},
|
{"log <n | b | t | r | v>", "set logging mode to none, basic, thermostat only, raw or verbose"},
|
||||||
{"poll", "toggle EMS poll request on/off"},
|
|
||||||
{"tx", "toggle EMX Tx line on/off"},
|
|
||||||
{"publish", "publish values to MQTT"},
|
{"publish", "publish values to MQTT"},
|
||||||
{"types", "list supported EMS telegram type IDs"},
|
{"types", "list supported EMS telegram type IDs"},
|
||||||
{"queue", "print the Tx queue"},
|
{"queue", "list Tx queue"},
|
||||||
{"autodetect", "discover EMS devices and set boiler and thermostat automatically"},
|
{"autodetect", "discover EMS devices and set boiler and thermostat automatically"},
|
||||||
{"shower <timer | alert>", "toggle either timer or alert on/off"},
|
{"shower <timer | alert>", "toggle either timer or alert on/off"},
|
||||||
{"send XX...", "send raw telegram data in hex to EMS bus"},
|
{"send XX...", "send raw telegram data in hex to EMS bus"},
|
||||||
@@ -267,17 +265,14 @@ void showInfo() {
|
|||||||
|
|
||||||
myDebug(" # connected Dallas temperature sensors=%d", EMSESP_Status.dallas_sensors);
|
myDebug(" # connected Dallas temperature sensors=%d", EMSESP_Status.dallas_sensors);
|
||||||
|
|
||||||
myDebug(" Thermostat is %s, Boiler is %s, Poll is %s, Tx is %s, Shower Timer is %s, Shower Alert is %s",
|
myDebug(" Thermostat is %s, Boiler is %s, Shower Timer is %s, Shower Alert is %s",
|
||||||
(ems_getThermostatEnabled() ? "enabled" : "disabled"),
|
(ems_getThermostatEnabled() ? "enabled" : "disabled"),
|
||||||
(ems_getBoilerEnabled() ? "enabled" : "disabled"),
|
(ems_getBoilerEnabled() ? "enabled" : "disabled"),
|
||||||
((EMS_Sys_Status.emsPollEnabled) ? "enabled" : "disabled"),
|
|
||||||
((EMS_Sys_Status.emsTxEnabled) ? "enabled" : "disabled"),
|
|
||||||
((EMSESP_Status.shower_timer) ? "enabled" : "disabled"),
|
((EMSESP_Status.shower_timer) ? "enabled" : "disabled"),
|
||||||
((EMSESP_Status.shower_alert) ? "enabled" : "disabled"));
|
((EMSESP_Status.shower_alert) ? "enabled" : "disabled"));
|
||||||
|
|
||||||
myDebug("\n%sEMS Bus Stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
myDebug("\n%sEMS Bus Stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||||
myDebug(" Bus Connected=%s, Tx Capable=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
|
myDebug(" Bus Connected=%s, # Rx telegrams=%d, # Tx telegrams=%d, # Crc Errors=%d",
|
||||||
(ems_getTxCapable() ? "yes" : "no"),
|
|
||||||
(ems_getBusConnected() ? "yes" : "no"),
|
(ems_getBusConnected() ? "yes" : "no"),
|
||||||
EMS_Sys_Status.emsRxPgks,
|
EMS_Sys_Status.emsRxPgks,
|
||||||
EMS_Sys_Status.emsTxPkgs,
|
EMS_Sys_Status.emsTxPkgs,
|
||||||
@@ -633,7 +628,7 @@ bool FSCallback(MYESP_FSACTION action, JsonObject & json) {
|
|||||||
|
|
||||||
// callback for custom settings when showing Stored Settings
|
// callback for custom settings when showing Stored Settings
|
||||||
// wc is number of arguments after the 'set' command
|
// wc is number of arguments after the 'set' command
|
||||||
// returns true if successful
|
// returns true if the setting was recognized and changed
|
||||||
bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) {
|
bool SettingsCallback(MYESP_FSACTION action, uint8_t wc, const char * setting, const char * value) {
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
@@ -721,18 +716,6 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
|
|||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(first_cmd, "poll") == 0) {
|
|
||||||
bool b = !ems_getPoll();
|
|
||||||
ems_setPoll(b);
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(first_cmd, "tx") == 0) {
|
|
||||||
bool b = !ems_getTxEnabled();
|
|
||||||
ems_setTxEnabled(b);
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(first_cmd, "publish") == 0) {
|
if (strcmp(first_cmd, "publish") == 0) {
|
||||||
publishValues(true);
|
publishValues(true);
|
||||||
ok = true;
|
ok = true;
|
||||||
@@ -925,17 +908,12 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
|||||||
|
|
||||||
// Init callback, which is used to set functions and call methods after a wifi connection has been established
|
// Init callback, which is used to set functions and call methods after a wifi connection has been established
|
||||||
void WIFICallback() {
|
void WIFICallback() {
|
||||||
// when finally we're all set up, we can fire up the uart (this will enable the UART interrupts)
|
// This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
|
||||||
#ifdef DEBUG_SUPPORT
|
|
||||||
myDebug("Warning, in DEBUG mode. EMS bus has been disabled. See -DDEBUG_SUPPORT build option.");
|
|
||||||
#else
|
|
||||||
// Important! This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
|
|
||||||
// This is done after we have a WiFi signal to avoid any resource conflicts
|
// This is done after we have a WiFi signal to avoid any resource conflicts
|
||||||
emsuart_init();
|
emsuart_init();
|
||||||
myDebug("[UART] Opened Rx/Tx connection");
|
myDebug("[UART] Opened Rx/Tx connection");
|
||||||
#endif
|
|
||||||
|
|
||||||
// now that we're connected, find out what boiler and thermostats we have specified
|
// go and find the boiler and thermostat types
|
||||||
ems_discoverModels();
|
ems_discoverModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1100,18 +1078,21 @@ void setup() {
|
|||||||
// call ems.cpp's init function to set all the internal params
|
// call ems.cpp's init function to set all the internal params
|
||||||
ems_init();
|
ems_init();
|
||||||
|
|
||||||
#ifndef DEBUG_SUPPORT
|
|
||||||
// Timers using Ticker library
|
// Timers using Ticker library
|
||||||
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values
|
publishValuesTimer.attach(PUBLISHVALUES_TIME, do_publishValues); // post MQTT values
|
||||||
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
|
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
|
||||||
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
|
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
|
||||||
#endif
|
|
||||||
|
|
||||||
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
||||||
myESP.setTelnet(project_cmds, ArraySize(project_cmds), TelnetCommandCallback, TelnetCallback); // set up Telnet commands
|
myESP.setTelnet(project_cmds, ArraySize(project_cmds), TelnetCommandCallback, TelnetCallback); // set up Telnet commands
|
||||||
|
#ifdef WIFI_SSID
|
||||||
myESP.setWIFI(WIFI_SSID, WIFI_PASSWORD, WIFICallback);
|
myESP.setWIFI(WIFI_SSID, WIFI_PASSWORD, WIFICallback);
|
||||||
myESP.setMQTT(
|
#else
|
||||||
MQTT_HOST, MQTT_USER, MQTT_PASS, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, MQTT_WILL_PAYLOAD, MQTTCallback);
|
myESP.setWIFI(NULL, NULL, WIFICallback); // pull the wifi settings from the SPIFFS stored settings
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MQTT host, username and password taken from the SPIFFS settings
|
||||||
|
myESP.setMQTT(NULL, NULL, NULL, MQTT_BASE, MQTT_KEEPALIVE, MQTT_QOS, MQTT_RETAIN, MQTT_WILL_TOPIC, MQTT_WILL_PAYLOAD, MQTTCallback);
|
||||||
|
|
||||||
// custom settings in SPIFFS
|
// custom settings in SPIFFS
|
||||||
myESP.setSettings(FSCallback, SettingsCallback);
|
myESP.setSettings(FSCallback, SettingsCallback);
|
||||||
|
|||||||
436
src/ems.cpp
@@ -24,35 +24,39 @@ CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO
|
|||||||
// callbacks per type
|
// callbacks per type
|
||||||
|
|
||||||
// generic
|
// generic
|
||||||
void _process_Version(uint8_t * data, uint8_t length);
|
void _process_Version(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
// Boiler and Buderus devices
|
// Boiler and Buderus devices
|
||||||
void _process_UBAMonitorFast(uint8_t * data, uint8_t length);
|
void _process_UBAMonitorFast(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_UBAMonitorSlow(uint8_t * data, uint8_t length);
|
void _process_UBAMonitorSlow(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length);
|
void _process_UBAMonitorWWMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_UBAParameterWW(uint8_t * data, uint8_t length);
|
void _process_UBAParameterWW(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length);
|
void _process_UBATotalUptimeMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_UBAParametersMessage(uint8_t * data, uint8_t length);
|
void _process_UBAParametersMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_SetPoints(uint8_t * data, uint8_t length);
|
void _process_SetPoints(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
// Common for most thermostats
|
// Common for most thermostats
|
||||||
void _process_RCTime(uint8_t * data, uint8_t length);
|
void _process_RCTime(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length);
|
void _process_RCOutdoorTempMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
|
// RC10
|
||||||
|
void _process_RC10Set(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
void _process_RC10StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
// RC20
|
// RC20
|
||||||
void _process_RC20Set(uint8_t * data, uint8_t length);
|
void _process_RC20Set(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_RC20StatusMessage(uint8_t * data, uint8_t length);
|
void _process_RC20StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
// RC30
|
// RC30
|
||||||
void _process_RC30Set(uint8_t * data, uint8_t length);
|
void _process_RC30Set(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_RC30StatusMessage(uint8_t * data, uint8_t length);
|
void _process_RC30StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
// RC35
|
// RC35
|
||||||
void _process_RC35Set(uint8_t * data, uint8_t length);
|
void _process_RC35Set(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
void _process_RC35StatusMessage(uint8_t * data, uint8_t length);
|
void _process_RC35StatusMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
// Easy
|
// Easy
|
||||||
void _process_EasyStatusMessage(uint8_t * data, uint8_t length);
|
void _process_EasyStatusMessage(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recognized EMS types and the functions they call to process the telegrams
|
* Recognized EMS types and the functions they call to process the telegrams
|
||||||
@@ -73,6 +77,11 @@ const _EMS_Type EMS_Types[] = {
|
|||||||
{EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", _process_UBAParametersMessage},
|
{EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", _process_UBAParametersMessage},
|
||||||
{EMS_MODEL_UBA, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
{EMS_MODEL_UBA, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||||
|
|
||||||
|
// RC10
|
||||||
|
{EMS_MODEL_RC10, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||||
|
{EMS_MODEL_RC10, EMS_TYPE_RC10Set, "RC10Set", _process_RC10Set},
|
||||||
|
{EMS_MODEL_RC10, EMS_TYPE_RC10StatusMessage, "RC10StatusMessage", _process_RC10StatusMessage},
|
||||||
|
|
||||||
// RC20 and RC20F
|
// RC20 and RC20F
|
||||||
{EMS_MODEL_RC20, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
{EMS_MODEL_RC20, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||||
{EMS_MODEL_RC20, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
{EMS_MODEL_RC20, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||||
@@ -133,14 +142,10 @@ const uint8_t ems_crc_table[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E,
|
|||||||
0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7, 0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5, 0xF7, 0xE9, 0xEB, 0xED, 0xEF,
|
0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7, 0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5, 0xF7, 0xE9, 0xEB, 0xED, 0xEF,
|
||||||
0xE1, 0xE3, 0xE5, 0xE7};
|
0xE1, 0xE3, 0xE5, 0xE7};
|
||||||
|
|
||||||
const uint8_t TX_WRITE_TIMEOUT_COUNT = 3; // 3 retries before timeout
|
const uint8_t TX_WRITE_TIMEOUT_COUNT = 2; // 3 retries before timeout
|
||||||
const unsigned long EMS_BUS_TIMEOUT = 15000; // timeout in ms before recognizing the ems bus is offline (15 seconds)
|
const unsigned long EMS_BUS_TIMEOUT = 15000; // timeout in ms before recognizing the ems bus is offline (15 seconds)
|
||||||
const unsigned long EMS_POLL_TIMEOUT = 5000; // timeout in ms before recognizing the ems bus is offline (5 seconds)
|
const unsigned long EMS_POLL_TIMEOUT = 5000; // timeout in ms before recognizing the ems bus is offline (5 seconds)
|
||||||
|
|
||||||
uint8_t _emsTxRetryCount; // used for retries when sending failed
|
|
||||||
uint8_t _ems_PollCount; // not used, but can be used to slow down sending on faster chips
|
|
||||||
uint8_t _last_TxTelgramCRC; // CRC of last Tx sent, for checking duplicates
|
|
||||||
|
|
||||||
// init stats and counters and buffers
|
// init stats and counters and buffers
|
||||||
// uses -255 or 255 for values that haven't been set yet (EMS_VALUE_INT_NOTSET and EMS_VALUE_FLOAT_NOTSET)
|
// uses -255 or 255 for values that haven't been set yet (EMS_VALUE_INT_NOTSET and EMS_VALUE_FLOAT_NOTSET)
|
||||||
void ems_init() {
|
void ems_init() {
|
||||||
@@ -148,15 +153,15 @@ void ems_init() {
|
|||||||
EMS_Sys_Status.emsRxPgks = 0;
|
EMS_Sys_Status.emsRxPgks = 0;
|
||||||
EMS_Sys_Status.emsTxPkgs = 0;
|
EMS_Sys_Status.emsTxPkgs = 0;
|
||||||
EMS_Sys_Status.emxCrcErr = 0;
|
EMS_Sys_Status.emxCrcErr = 0;
|
||||||
EMS_Sys_Status.emsRxStatus = EMS_RX_IDLE;
|
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE;
|
||||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE;
|
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
|
||||||
EMS_Sys_Status.emsRefreshed = false;
|
EMS_Sys_Status.emsRefreshed = false;
|
||||||
EMS_Sys_Status.emsPollEnabled = false; // start up with Poll disabled
|
EMS_Sys_Status.emsPollEnabled = false; // start up with Poll disabled
|
||||||
EMS_Sys_Status.emsTxEnabled = true; // start up with Tx enabled
|
|
||||||
EMS_Sys_Status.emsBusConnected = false;
|
EMS_Sys_Status.emsBusConnected = false;
|
||||||
EMS_Sys_Status.emsRxTimestamp = 0;
|
EMS_Sys_Status.emsRxTimestamp = 0;
|
||||||
EMS_Sys_Status.emsTxCapable = false;
|
EMS_Sys_Status.emsTxCapable = false;
|
||||||
EMS_Sys_Status.emsPollTimestamp = 0;
|
EMS_Sys_Status.emsPollTimestamp = 0;
|
||||||
|
EMS_Sys_Status.txRetryCount = 0;
|
||||||
|
|
||||||
// thermostat
|
// thermostat
|
||||||
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_FLOAT_NOTSET;
|
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_FLOAT_NOTSET;
|
||||||
@@ -233,11 +238,6 @@ void ems_init() {
|
|||||||
EMS_Thermostat.product_id = 0;
|
EMS_Thermostat.product_id = 0;
|
||||||
strlcpy(EMS_Thermostat.version, "not set", sizeof(EMS_Thermostat.version));
|
strlcpy(EMS_Thermostat.version, "not set", sizeof(EMS_Thermostat.version));
|
||||||
|
|
||||||
// counters
|
|
||||||
_ems_PollCount = 0;
|
|
||||||
_emsTxRetryCount = 0;
|
|
||||||
_last_TxTelgramCRC = 0;
|
|
||||||
|
|
||||||
// default logging is none
|
// default logging is none
|
||||||
ems_setLogging(EMS_SYS_LOGGING_DEFAULT);
|
ems_setLogging(EMS_SYS_LOGGING_DEFAULT);
|
||||||
}
|
}
|
||||||
@@ -252,15 +252,6 @@ bool ems_getPoll() {
|
|||||||
return EMS_Sys_Status.emsPollEnabled;
|
return EMS_Sys_Status.emsPollEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ems_setTxEnabled(bool b) {
|
|
||||||
EMS_Sys_Status.emsTxEnabled = b;
|
|
||||||
myDebug("EMS Bus Tx is set to %s", EMS_Sys_Status.emsTxEnabled ? "enabled" : "disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ems_getTxEnabled() {
|
|
||||||
return EMS_Sys_Status.emsTxEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ems_getEmsRefreshed() {
|
bool ems_getEmsRefreshed() {
|
||||||
return EMS_Sys_Status.emsRefreshed;
|
return EMS_Sys_Status.emsRefreshed;
|
||||||
}
|
}
|
||||||
@@ -462,25 +453,12 @@ void _ems_sendTelegram() {
|
|||||||
EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length); // add the CRC
|
EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length); // add the CRC
|
||||||
_debugPrintTelegram("Sending raw", EMS_TxTelegram.data, EMS_TxTelegram.length, COLOR_CYAN); // always show
|
_debugPrintTelegram("Sending raw", EMS_TxTelegram.data, EMS_TxTelegram.length, COLOR_CYAN); // always show
|
||||||
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); // send the telegram to the UART Tx
|
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length); // send the telegram to the UART Tx
|
||||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
|
|
||||||
EMS_TxQueue.shift(); // remove from queue
|
EMS_TxQueue.shift(); // remove from queue
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is no destination, also delete it from the queue
|
// if there is no destination, also delete it from the queue
|
||||||
if (EMS_TxTelegram.dest == EMS_ID_NONE) {
|
if (EMS_TxTelegram.dest == EMS_ID_NONE) {
|
||||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
|
|
||||||
EMS_TxQueue.shift(); // remove from queue
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if Tx is disabled, don't do anything and ignore the request
|
|
||||||
// this could be because the boiler has yet to be found and the type_id is still empty
|
|
||||||
if (!EMS_Sys_Status.emsTxEnabled) {
|
|
||||||
myDebug("Tx is disabled. Ignoring %s request to 0x%02X.",
|
|
||||||
((EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) ? "write" : "read"),
|
|
||||||
EMS_TxTelegram.dest & 0x7F);
|
|
||||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
|
|
||||||
EMS_TxQueue.shift(); // remove from queue
|
EMS_TxQueue.shift(); // remove from queue
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -508,16 +486,6 @@ void _ems_sendTelegram() {
|
|||||||
uint8_t crc = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length);
|
uint8_t crc = _crcCalculator(EMS_TxTelegram.data, EMS_TxTelegram.length);
|
||||||
EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = crc;
|
EMS_TxTelegram.data[EMS_TxTelegram.length - 1] = crc;
|
||||||
|
|
||||||
// check if we already sent the same one, otherwise assume the last Tx hasn't been successful
|
|
||||||
// and remove from queue and exit
|
|
||||||
if (crc == _last_TxTelgramCRC) {
|
|
||||||
// myDebug("Duplicate message, just sent this one, so removing from queue!");
|
|
||||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE; // finished sending
|
|
||||||
EMS_TxQueue.shift(); // remove the last Tx from the queue
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_last_TxTelgramCRC = crc;
|
|
||||||
|
|
||||||
// print debug info
|
// print debug info
|
||||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||||
char s[64] = {0};
|
char s[64] = {0};
|
||||||
@@ -533,42 +501,59 @@ void _ems_sendTelegram() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send the telegram to the UART Tx
|
// send the telegram to the UART Tx
|
||||||
EMS_Sys_Status.emsTxStatus = EMS_TX_ACTIVE;
|
|
||||||
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length);
|
emsuart_tx_buffer(EMS_TxTelegram.data, EMS_TxTelegram.length);
|
||||||
|
|
||||||
EMS_Sys_Status.emsTxPkgs++;
|
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_WAIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes the last write command and turns into a validate request
|
||||||
|
* placing it on the queue
|
||||||
|
*/
|
||||||
|
void _createValidate() {
|
||||||
|
if (EMS_TxQueue.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// release the Tx lock
|
||||||
|
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
|
||||||
|
|
||||||
|
// get the first in the queue, which is at the head
|
||||||
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first();
|
||||||
|
|
||||||
|
// safety check: only do a validate after a write and when we have a type to validate
|
||||||
|
if ((EMS_TxTelegram.action != EMS_TX_TELEGRAM_WRITE) || (EMS_TxTelegram.type_validate == EMS_ID_NONE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if it was a write command, check if we need to do a new read to validate the results
|
|
||||||
// we do this by turning the last write into a read
|
|
||||||
if ((EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) && (EMS_TxTelegram.type_validate != EMS_ID_NONE)) {
|
|
||||||
// create a new Telegram copying from the last write
|
// create a new Telegram copying from the last write
|
||||||
_EMS_TxTelegram new_EMS_TxTelegram;
|
_EMS_TxTelegram new_EMS_TxTelegram;
|
||||||
|
new_EMS_TxTelegram.action = EMS_TX_TELEGRAM_VALIDATE;
|
||||||
|
|
||||||
// copy details
|
// copy old Write record
|
||||||
new_EMS_TxTelegram.type_validate = EMS_TxTelegram.type_validate;
|
new_EMS_TxTelegram.type_validate = EMS_TxTelegram.type_validate;
|
||||||
new_EMS_TxTelegram.dest = EMS_TxTelegram.dest;
|
new_EMS_TxTelegram.dest = EMS_TxTelegram.dest;
|
||||||
new_EMS_TxTelegram.type = EMS_TxTelegram.type;
|
new_EMS_TxTelegram.type = EMS_TxTelegram.type;
|
||||||
new_EMS_TxTelegram.action = EMS_TX_TELEGRAM_VALIDATE;
|
|
||||||
new_EMS_TxTelegram.offset = EMS_TxTelegram.comparisonOffset; // location of byte to fetch
|
|
||||||
new_EMS_TxTelegram.dataValue = 1; // fetch single byte
|
|
||||||
new_EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; // is always 6 bytes long (including CRC at end)
|
|
||||||
new_EMS_TxTelegram.comparisonValue = EMS_TxTelegram.comparisonValue;
|
new_EMS_TxTelegram.comparisonValue = EMS_TxTelegram.comparisonValue;
|
||||||
new_EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.comparisonPostRead;
|
new_EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.comparisonPostRead;
|
||||||
new_EMS_TxTelegram.comparisonOffset = EMS_TxTelegram.comparisonOffset;
|
new_EMS_TxTelegram.comparisonOffset = EMS_TxTelegram.comparisonOffset;
|
||||||
|
|
||||||
|
// this is what is different
|
||||||
|
new_EMS_TxTelegram.offset = EMS_TxTelegram.comparisonOffset; // location of byte to fetch
|
||||||
|
new_EMS_TxTelegram.dataValue = 1; // fetch single byte
|
||||||
|
new_EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH; // is always 6 bytes long (including CRC at end)
|
||||||
|
|
||||||
// remove old telegram from queue and add this new read one
|
// remove old telegram from queue and add this new read one
|
||||||
EMS_TxQueue.shift(); // remove from queue
|
EMS_TxQueue.shift(); // remove from queue
|
||||||
EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it next in line
|
EMS_TxQueue.unshift(new_EMS_TxTelegram); // add back to queue making it first to be picked up next (FIFO)
|
||||||
}
|
|
||||||
|
|
||||||
EMS_Sys_Status.emsTxStatus = EMS_TX_IDLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the main logic that parses the telegram message, triggered by an interrupt in emsuart.cpp
|
* the main logic that parses the telegram message, triggered by an interrupt in emsuart.cpp
|
||||||
* length is only data bytes, excluding the BRK
|
* length is only data bytes, excluding the BRK
|
||||||
* Read commands are asynchronous as they're handled by the interrupt
|
* Read commands are asynchronous as they're handled by the interrupt
|
||||||
* When we receive a Poll Request we need to send any Tx packages quickly
|
* When we receive a Poll Request we need to send any Tx packages quickly within a 200ms window
|
||||||
*/
|
*/
|
||||||
void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
||||||
// check if we just received a single byte
|
// check if we just received a single byte
|
||||||
@@ -579,65 +564,52 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
|
|||||||
|
|
||||||
// check first for a Poll for us
|
// check first for a Poll for us
|
||||||
if (value == (EMS_ID_ME | 0x80)) {
|
if (value == (EMS_ID_ME | 0x80)) {
|
||||||
// store when we received a last poll
|
EMS_Sys_Status.emsPollTimestamp = millis(); // store when we received a last poll
|
||||||
EMS_Sys_Status.emsPollTimestamp = millis();
|
|
||||||
EMS_Sys_Status.emsTxCapable = true;
|
EMS_Sys_Status.emsTxCapable = true;
|
||||||
|
|
||||||
// do we have something to send thats waiting in the Tx queue? if so send it
|
// do we have something to send thats waiting in the Tx queue? if so send it if the Queue is not in a wait state
|
||||||
if (!EMS_TxQueue.isEmpty()) {
|
if ((!EMS_TxQueue.isEmpty()) && (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_IDLE)) {
|
||||||
_ems_sendTelegram(); // perform the read/write command immediately
|
_ems_sendTelegram(); // perform the read/write command immediately
|
||||||
/*
|
|
||||||
if ((_ems_PollCount++ % 2) == 0) {
|
|
||||||
_ems_sendTelegram(); // perform the read/write command, slowing it down a little
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} else {
|
} else {
|
||||||
// nothing to send so just send a poll acknowledgement back
|
// nothing to send so just send a poll acknowledgement back
|
||||||
if (EMS_Sys_Status.emsPollEnabled) {
|
if (EMS_Sys_Status.emsPollEnabled) {
|
||||||
emsaurt_tx_poll();
|
emsaurt_tx_poll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((value == EMS_TX_ERROR) || (value == EMS_TX_SUCCESS)) {
|
} else if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_WAIT) {
|
||||||
// if its a success (01) or failure (04), then see if its from one of our last writes
|
// this may be a single byte 01 (success) or 04 (error) from a recent write command?
|
||||||
// a response from UBA after a write should be within a specific time period < 100ms
|
if (value == EMS_TX_SUCCESS) {
|
||||||
if (!EMS_TxQueue.isEmpty()) {
|
EMS_Sys_Status.emsTxPkgs++;
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first(); // get current Tx package we last sent
|
// got a success 01. Send a validate to check the value of the last write
|
||||||
if ((EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) && (value == EMS_TX_ERROR)) {
|
emsaurt_tx_poll(); // send a poll to free the EMS bus
|
||||||
|
_createValidate(); // create a validate Tx request
|
||||||
|
} else if (value == EMS_TX_ERROR) {
|
||||||
|
// last write failed (04), delete it from queue and dont bother to retry
|
||||||
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||||
myDebug("* Error: last write failed. removing write request from queue");
|
myDebug("** Write command failed from host");
|
||||||
}
|
|
||||||
EMS_TxQueue.shift(); // write failed so remove from queue and forget it for now
|
|
||||||
}
|
}
|
||||||
emsaurt_tx_poll(); // send a poll to free the EMS bus
|
emsaurt_tx_poll(); // send a poll to free the EMS bus
|
||||||
|
_removeTxQueue(); // remove from queue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return; // all done here, quit. if we haven't processes anything its a poll but not for us
|
|
||||||
|
return; // all done here
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore anything that doesn't resemble a proper telegram package
|
// ignore anything that doesn't resemble a proper telegram package
|
||||||
// minimal is 5 bytes, excluding CRC at the end
|
// minimal is 5 bytes, excluding CRC at the end
|
||||||
if (length < EMS_MIN_TELEGRAM_LENGTH) {
|
if (length <= 4) {
|
||||||
//_debugPrintTelegram("Noisy data:", telegram, length, COLOR_RED);
|
//_debugPrintTelegram("Noisy data:", telegram, length, COLOR_RED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume at this point we have something that vaguely resembles a telegram
|
// Assume at this point we have something that vaguely resembles a telegram in the format [src] [dest] [type] [offset] [data] [crc]
|
||||||
// see if we got a telegram as [src] [dest] [type] [offset] [data] [crc]
|
// validate the CRC, if its bad ignore it
|
||||||
// so is at least 6 bytes long and the CRC checks out (which is last byte)
|
|
||||||
uint8_t crc = _crcCalculator(telegram, length);
|
uint8_t crc = _crcCalculator(telegram, length);
|
||||||
if (telegram[length - 1] != crc) {
|
if (telegram[length - 1] != crc) {
|
||||||
EMS_Sys_Status.emxCrcErr++;
|
EMS_Sys_Status.emxCrcErr++;
|
||||||
|
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||||
_debugPrintTelegram("Corrupt telegram:", telegram, length, COLOR_RED);
|
_debugPrintTelegram("Corrupt telegram:", telegram, length, COLOR_RED);
|
||||||
// at this point something arrived on the bus, which means if we were waiting for something to arrive then we
|
|
||||||
// can forget it as it should have arrived in a 100ms window. So remove it.
|
|
||||||
if (!EMS_TxQueue.isEmpty()) {
|
|
||||||
/*
|
|
||||||
// commented out because too much chatter
|
|
||||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
|
||||||
myDebug("Corrupt telegram, so removing last read from Tx queue.");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
EMS_TxQueue.shift();
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -755,86 +727,127 @@ void _ems_processTelegram(uint8_t * telegram, uint8_t length) {
|
|||||||
// call callback function to process it
|
// call callback function to process it
|
||||||
// as we only handle complete telegrams (not partial) check that the offset is 0
|
// as we only handle complete telegrams (not partial) check that the offset is 0
|
||||||
if (offset == EMS_ID_NONE) {
|
if (offset == EMS_ID_NONE) {
|
||||||
(void)EMS_Types[i].processType_cb(data, length - 5);
|
(void)EMS_Types[i].processType_cb(type, data, length - 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* deciphers the telegram packet
|
* 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, which has already been checked for valid CRC and has a complete header (min of 5 bytes)
|
||||||
* length is only data bytes, excluding the BRK
|
* length is only data bytes, excluding the BRK
|
||||||
* We only remove from the Tx queue if the read or write was successful
|
* We only remove from the Tx queue if the read or write was successful
|
||||||
*/
|
*/
|
||||||
void _processType(uint8_t * telegram, uint8_t length) {
|
void _processType(uint8_t * telegram, uint8_t length) {
|
||||||
// header
|
// header
|
||||||
uint8_t src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes here
|
uint8_t src = telegram[0] & 0x7F; // removing 8th bit as we deal with both reads and writes here
|
||||||
uint8_t dest = telegram[1] & 0x7F;
|
|
||||||
uint8_t type = telegram[2];
|
|
||||||
uint8_t * data = telegram + 4; // data block starts at position 5
|
|
||||||
|
|
||||||
// if its an echo of ourselves from the master, ignore
|
// if its an echo of ourselves from the master UBA, ignore
|
||||||
if (src == EMS_ID_ME) {
|
if (src == EMS_ID_ME) {
|
||||||
//_debugPrintTelegram("Telegram echo:", telegram, length, COLOR_BLUE);
|
//_debugPrintTelegram("Telegram echo:", telegram, length, COLOR_BLUE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// did we request this telegram? If so it would be either a read or a validate telegram and still
|
// if its a broadcast and we didn't just send anything, process it and exit
|
||||||
// on top of the Tx queue with the same type. because we don't remove a Tx from the queue after a send
|
if (EMS_Sys_Status.emsTxStatus == EMS_TX_STATUS_IDLE) {
|
||||||
// if its a validate check the value, or if its a read, update the Read counter
|
|
||||||
// then we can safely removed the read/validate from the queue
|
|
||||||
if ((dest == EMS_ID_ME) && (!EMS_TxQueue.isEmpty())) {
|
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first(); // get current Tx package we last sent
|
|
||||||
|
|
||||||
// do the types match? If so we were expecting this telegram response back to us
|
|
||||||
if (EMS_TxTelegram.type == type) {
|
|
||||||
emsaurt_tx_poll(); // send Acknowledgement back to free the EMS bus since we have the telegram
|
|
||||||
|
|
||||||
// if last action was a read, go ahead and process it
|
|
||||||
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_READ) {
|
|
||||||
EMS_Sys_Status.emsRxPgks++; // increment rx counter
|
|
||||||
_emsTxRetryCount = 0; // reset retry count
|
|
||||||
//
|
|
||||||
// and process the telegram. This calls the main function to process each type
|
|
||||||
//
|
|
||||||
_ems_processTelegram(telegram, length);
|
_ems_processTelegram(telegram, length);
|
||||||
if (EMS_TxTelegram.forceRefresh) {
|
return;
|
||||||
ems_setEmsRefreshed(true); // set the MQTT refresh flag to force sending to MQTT
|
|
||||||
}
|
}
|
||||||
EMS_TxQueue.shift(); // remove read from queue, all done now
|
|
||||||
return; // quit
|
// release the lock on the TxQueue
|
||||||
|
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
|
||||||
|
|
||||||
|
// at this point we can assume Txstatus is EMS_TX_STATUS_WAIT
|
||||||
|
// for READ, WRITE or VALIDATE the dest is always us, so check this
|
||||||
|
// if not just process and quit
|
||||||
|
if ((telegram[1] & 0x7F) != EMS_ID_ME) {
|
||||||
|
_ems_processTelegram(telegram, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first double check we actually have something in the queue
|
||||||
|
if (EMS_TxQueue.isEmpty()) {
|
||||||
|
_ems_processTelegram(telegram, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the Tx telegram we just sent
|
||||||
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TxQueue.first();
|
||||||
|
|
||||||
|
// check action
|
||||||
|
// if READ, match the current inbound telegram to what we sent
|
||||||
|
// if WRITE, should not happen
|
||||||
|
// if VALIDATE, check the contents
|
||||||
|
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_READ) {
|
||||||
|
uint8_t type = telegram[2];
|
||||||
|
if ((src == EMS_TxTelegram.dest) && (type == EMS_TxTelegram.type)) {
|
||||||
|
// all checks out, read was successful, remove tx from queue and continue to process telegram
|
||||||
|
_removeTxQueue();
|
||||||
|
EMS_Sys_Status.emsRxPgks++; // increment counter
|
||||||
|
// myDebug("** Read from 0x%02X ok", type);
|
||||||
|
ems_setEmsRefreshed(EMS_TxTelegram.forceRefresh); // does mqtt need refreshing?
|
||||||
|
} else {
|
||||||
|
// read not OK, we didn't get back a telegram we expected
|
||||||
|
// leave on queue and try again, but continue to process what we received as it may be important
|
||||||
|
EMS_Sys_Status.txRetryCount++;
|
||||||
|
// if retried too many times, give up and remove it
|
||||||
|
if (EMS_Sys_Status.txRetryCount >= TX_WRITE_TIMEOUT_COUNT) {
|
||||||
|
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||||
|
myDebug("Read failed. Giving up, removing from queue");
|
||||||
|
}
|
||||||
|
_removeTxQueue();
|
||||||
|
} else {
|
||||||
|
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||||
|
myDebug("...Retrying read. Attempt %d/%d...", EMS_Sys_Status.txRetryCount, TX_WRITE_TIMEOUT_COUNT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ems_processTelegram(telegram, length); // process it always
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) {
|
||||||
|
// should not get here, since this is handled earlier receiving a 01 or 04
|
||||||
|
myDebug("** Error ! Write - should not be here");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) {
|
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_VALIDATE) {
|
||||||
// this read was for a validate. Do a compare on the 1 byte result from the last write
|
// this is a read telegram which we use to validate the last write
|
||||||
|
uint8_t * data = telegram + 4; // data block starts at position 5
|
||||||
uint8_t dataReceived = data[0]; // only a single byte is returned after a read
|
uint8_t dataReceived = data[0]; // only a single byte is returned after a read
|
||||||
if (EMS_TxTelegram.comparisonValue == dataReceived) {
|
if (EMS_TxTelegram.comparisonValue == dataReceived) {
|
||||||
// there is a match, so write must have been successful
|
// validate was successful, the write changed the value
|
||||||
EMS_TxQueue.shift(); // remove validate from queue
|
_removeTxQueue(); // now we can remove the Tx validate command the queue
|
||||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||||
myDebug("Write to 0x%02X was successful", EMS_TxTelegram.dest);
|
myDebug("Write to 0x%02X was successful", EMS_TxTelegram.dest);
|
||||||
}
|
}
|
||||||
ems_doReadCommand(EMS_TxTelegram.comparisonPostRead,
|
// follow up with the post read command
|
||||||
EMS_TxTelegram.dest,
|
ems_doReadCommand(EMS_TxTelegram.comparisonPostRead, EMS_TxTelegram.dest, true);
|
||||||
true); // get values and force a refresh to MQTT
|
|
||||||
} else {
|
} else {
|
||||||
// write failed.
|
// write failed
|
||||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||||
myDebug("Last write failed. Compared set value 0x%02X with received value 0x%02X",
|
myDebug("Last write failed. Compared set value 0x%02X with received value 0x%02X",
|
||||||
EMS_TxTelegram.comparisonValue,
|
EMS_TxTelegram.comparisonValue,
|
||||||
dataReceived);
|
dataReceived);
|
||||||
}
|
}
|
||||||
if (_emsTxRetryCount++ >= TX_WRITE_TIMEOUT_COUNT) {
|
if (++EMS_Sys_Status.txRetryCount > TX_WRITE_TIMEOUT_COUNT) {
|
||||||
// give up
|
|
||||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||||
myDebug("...Giving up!");
|
myDebug("Write failed. Giving up, removing from queue");
|
||||||
}
|
}
|
||||||
EMS_TxQueue.shift(); // remove from queue
|
_removeTxQueue();
|
||||||
} else {
|
} else {
|
||||||
// retry, turn the validate back into a write and try again
|
// retry, turn the validate back into a write and try again
|
||||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||||
myDebug("...Retrying attempt %d...", _emsTxRetryCount);
|
myDebug("...Retrying write. Attempt %d/%d...", EMS_Sys_Status.txRetryCount, TX_WRITE_TIMEOUT_COUNT);
|
||||||
}
|
}
|
||||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||||
EMS_TxTelegram.dataValue = EMS_TxTelegram.comparisonValue; // restore old value
|
EMS_TxTelegram.dataValue = EMS_TxTelegram.comparisonValue; // restore old value
|
||||||
@@ -843,37 +856,17 @@ void _processType(uint8_t * telegram, uint8_t length) {
|
|||||||
EMS_TxQueue.unshift(EMS_TxTelegram); // add back to queue making it next in line
|
EMS_TxQueue.unshift(EMS_TxTelegram); // add back to queue making it next in line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
emsaurt_tx_poll(); // send Acknowledgement back to free the EMS bus since we have the telegram
|
||||||
}
|
|
||||||
}
|
|
||||||
// telegram was for us, but seems we didn't ask for it
|
|
||||||
// so kinda invalidates the last action on the queue remove from queue
|
|
||||||
myDebug("Telegram sent to us but we didn't ask for it! (src=0x%02X dest=0x%02X type=0x%02X)",
|
|
||||||
telegram[0] & 0x7F,
|
|
||||||
telegram[1] & 0x7F,
|
|
||||||
telegram[2]);
|
|
||||||
// ems_printTxQueue();
|
|
||||||
// EMS_TxQueue.shift(); // remove validate from queue
|
|
||||||
} else {
|
|
||||||
// we didn't request it, was for somebody else or a broadcast, process it anyway
|
|
||||||
_ems_processTelegram(telegram, length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if hot tap water or heating is active
|
* Check if hot tap water or heating is active
|
||||||
* using a quick hack:
|
* using a quick hack for checking the heating. Selected Flow Temp >= 70
|
||||||
* heating is on if Selected Flow Temp >= 70 (in my case)
|
|
||||||
* tap water is on if Selected Flow Temp = 0 and Selected Burner Power >= 115
|
|
||||||
*/
|
*/
|
||||||
bool _checkActive() {
|
bool _checkActive() {
|
||||||
/* old code
|
|
||||||
EMS_Boiler.tapwaterActive = ((EMS_Boiler.selFlowTemp == 0)
|
|
||||||
&& (EMS_Boiler.selBurnPow >= EMS_BOILER_BURNPOWER_TAPWATER) & (EMS_Boiler.burnGas == EMS_VALUE_INT_ON));
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// hot tap water, using flow to check insread of the burner power
|
// hot tap water, using flow to check insread of the burner power
|
||||||
EMS_Boiler.tapwaterActive = ((EMS_Boiler.wWCurFlow != 0) && (EMS_Boiler.burnGas == EMS_VALUE_INT_ON));
|
EMS_Boiler.tapwaterActive = ((EMS_Boiler.wWCurFlow != 0) && (EMS_Boiler.burnGas == EMS_VALUE_INT_ON));
|
||||||
|
|
||||||
@@ -885,7 +878,7 @@ bool _checkActive() {
|
|||||||
* UBAParameterWW - type 0x33 - warm water parameters
|
* UBAParameterWW - type 0x33 - warm water parameters
|
||||||
* received only after requested (not broadcasted)
|
* received only after requested (not broadcasted)
|
||||||
*/
|
*/
|
||||||
void _process_UBAParameterWW(uint8_t * data, uint8_t length) {
|
void _process_UBAParameterWW(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Boiler.wWActivated = (data[1] == 0xFF); // 0xFF means on
|
EMS_Boiler.wWActivated = (data[1] == 0xFF); // 0xFF means on
|
||||||
EMS_Boiler.wWSelTemp = data[2];
|
EMS_Boiler.wWSelTemp = data[2];
|
||||||
EMS_Boiler.wWCircPump = (data[6] == 0xFF); // 0xFF means on
|
EMS_Boiler.wWCircPump = (data[6] == 0xFF); // 0xFF means on
|
||||||
@@ -899,7 +892,7 @@ void _process_UBAParameterWW(uint8_t * data, uint8_t length) {
|
|||||||
* UBATotalUptimeMessage - type 0x14 - total uptime
|
* UBATotalUptimeMessage - type 0x14 - total uptime
|
||||||
* received only after requested (not broadcasted)
|
* received only after requested (not broadcasted)
|
||||||
*/
|
*/
|
||||||
void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length) {
|
void _process_UBATotalUptimeMessage(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Boiler.UBAuptime = _toLong(0, data);
|
EMS_Boiler.UBAuptime = _toLong(0, data);
|
||||||
EMS_Sys_Status.emsRefreshed = true; // when we receieve this, lets force an MQTT publish
|
EMS_Sys_Status.emsRefreshed = true; // when we receieve this, lets force an MQTT publish
|
||||||
}
|
}
|
||||||
@@ -907,7 +900,7 @@ void _process_UBATotalUptimeMessage(uint8_t * data, uint8_t length) {
|
|||||||
/*
|
/*
|
||||||
* UBAParametersMessage - type 0x16
|
* UBAParametersMessage - type 0x16
|
||||||
*/
|
*/
|
||||||
void _process_UBAParametersMessage(uint8_t * data, uint8_t length) {
|
void _process_UBAParametersMessage(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Boiler.heating_temp = data[1];
|
EMS_Boiler.heating_temp = data[1];
|
||||||
EMS_Boiler.pump_mod_max = data[9];
|
EMS_Boiler.pump_mod_max = data[9];
|
||||||
EMS_Boiler.pump_mod_min = data[10];
|
EMS_Boiler.pump_mod_min = data[10];
|
||||||
@@ -917,7 +910,7 @@ void _process_UBAParametersMessage(uint8_t * data, uint8_t length) {
|
|||||||
* UBAMonitorWWMessage - type 0x34 - warm water monitor. 19 bytes long
|
* UBAMonitorWWMessage - type 0x34 - warm water monitor. 19 bytes long
|
||||||
* received every 10 seconds
|
* received every 10 seconds
|
||||||
*/
|
*/
|
||||||
void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length) {
|
void _process_UBAMonitorWWMessage(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Boiler.wWCurTmp = _toFloat(1, data);
|
EMS_Boiler.wWCurTmp = _toFloat(1, data);
|
||||||
EMS_Boiler.wWStarts = _toLong(13, data);
|
EMS_Boiler.wWStarts = _toLong(13, data);
|
||||||
EMS_Boiler.wWWorkM = _toLong(10, data);
|
EMS_Boiler.wWWorkM = _toLong(10, data);
|
||||||
@@ -929,7 +922,7 @@ void _process_UBAMonitorWWMessage(uint8_t * data, uint8_t length) {
|
|||||||
* UBAMonitorFast - type 0x18 - central heating monitor part 1 (25 bytes long)
|
* UBAMonitorFast - type 0x18 - central heating monitor part 1 (25 bytes long)
|
||||||
* received every 10 seconds
|
* received every 10 seconds
|
||||||
*/
|
*/
|
||||||
void _process_UBAMonitorFast(uint8_t * data, uint8_t length) {
|
void _process_UBAMonitorFast(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Boiler.selFlowTemp = data[0];
|
EMS_Boiler.selFlowTemp = data[0];
|
||||||
EMS_Boiler.curFlowTemp = _toFloat(1, data);
|
EMS_Boiler.curFlowTemp = _toFloat(1, data);
|
||||||
EMS_Boiler.retTemp = _toFloat(13, data);
|
EMS_Boiler.retTemp = _toFloat(13, data);
|
||||||
@@ -965,7 +958,7 @@ void _process_UBAMonitorFast(uint8_t * data, uint8_t length) {
|
|||||||
* UBAMonitorSlow - type 0x19 - central heating monitor part 2 (27 bytes long)
|
* UBAMonitorSlow - type 0x19 - central heating monitor part 2 (27 bytes long)
|
||||||
* received every 60 seconds
|
* received every 60 seconds
|
||||||
*/
|
*/
|
||||||
void _process_UBAMonitorSlow(uint8_t * data, uint8_t length) {
|
void _process_UBAMonitorSlow(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Boiler.extTemp = _toFloat(0, data); // 0x8000 if not available
|
EMS_Boiler.extTemp = _toFloat(0, data); // 0x8000 if not available
|
||||||
EMS_Boiler.boilTemp = _toFloat(2, data); // 0x8000 if not available
|
EMS_Boiler.boilTemp = _toFloat(2, data); // 0x8000 if not available
|
||||||
EMS_Boiler.pumpMod = data[9];
|
EMS_Boiler.pumpMod = data[9];
|
||||||
@@ -974,12 +967,25 @@ void _process_UBAMonitorSlow(uint8_t * data, uint8_t length) {
|
|||||||
EMS_Boiler.heatWorkMin = _toLong(19, data);
|
EMS_Boiler.heatWorkMin = _toLong(19, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* type 0xB1 - data from the RC10 thermostat (0x17)
|
||||||
|
* For reading the temp values only
|
||||||
|
* received every 60 seconds
|
||||||
|
*/
|
||||||
|
void _process_RC10StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
|
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC10StatusMessage_setpoint]) / (float)2;
|
||||||
|
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC10StatusMessage_curr, data);
|
||||||
|
|
||||||
|
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* type 0x91 - data from the RC20 thermostat (0x17) - 15 bytes long
|
* type 0x91 - data from the RC20 thermostat (0x17) - 15 bytes long
|
||||||
* For reading the temp values only
|
* For reading the temp values only
|
||||||
* received every 60 seconds
|
* received every 60 seconds
|
||||||
*/
|
*/
|
||||||
void _process_RC20StatusMessage(uint8_t * data, uint8_t length) {
|
void _process_RC20StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC20StatusMessage_setpoint]) / (float)2;
|
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC20StatusMessage_setpoint]) / (float)2;
|
||||||
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC20StatusMessage_curr, data);
|
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC20StatusMessage_curr, data);
|
||||||
|
|
||||||
@@ -991,7 +997,7 @@ void _process_RC20StatusMessage(uint8_t * data, uint8_t length) {
|
|||||||
* For reading the temp values only
|
* For reading the temp values only
|
||||||
* received every 60 seconds
|
* received every 60 seconds
|
||||||
*/
|
*/
|
||||||
void _process_RC30StatusMessage(uint8_t * data, uint8_t length) {
|
void _process_RC30StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC30StatusMessage_setpoint]) / (float)2;
|
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC30StatusMessage_setpoint]) / (float)2;
|
||||||
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC30StatusMessage_curr, data);
|
EMS_Thermostat.curr_roomTemp = _toFloat(EMS_TYPE_RC30StatusMessage_curr, data);
|
||||||
|
|
||||||
@@ -1003,7 +1009,7 @@ void _process_RC30StatusMessage(uint8_t * data, uint8_t length) {
|
|||||||
* For reading the temp values only
|
* For reading the temp values only
|
||||||
* received every 60 seconds
|
* received every 60 seconds
|
||||||
*/
|
*/
|
||||||
void _process_RC35StatusMessage(uint8_t * data, uint8_t length) {
|
void _process_RC35StatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC35StatusMessage_setpoint]) / (float)2;
|
EMS_Thermostat.setpoint_roomTemp = ((float)data[EMS_TYPE_RC35StatusMessage_setpoint]) / (float)2;
|
||||||
|
|
||||||
// check if temp sensor is unavailable
|
// check if temp sensor is unavailable
|
||||||
@@ -1020,18 +1026,26 @@ void _process_RC35StatusMessage(uint8_t * data, uint8_t length) {
|
|||||||
* type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
|
* type 0x0A - data from the Nefit Easy/TC100 thermostat (0x18) - 31 bytes long
|
||||||
* The Easy has a digital precision of its floats to 2 decimal places, so values is divided by 100
|
* The Easy has a digital precision of its floats to 2 decimal places, so values is divided by 100
|
||||||
*/
|
*/
|
||||||
void _process_EasyStatusMessage(uint8_t * data, uint8_t length) {
|
void _process_EasyStatusMessage(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Thermostat.curr_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_curr] << 8) + data[9]))) / 100;
|
EMS_Thermostat.curr_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_curr] << 8) + data[9]))) / 100;
|
||||||
EMS_Thermostat.setpoint_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_setpoint] << 8) + data[11]))) / 100;
|
EMS_Thermostat.setpoint_roomTemp = ((float)(((data[EMS_TYPE_EasyStatusMessage_setpoint] << 8) + data[11]))) / 100;
|
||||||
|
|
||||||
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
|
EMS_Sys_Status.emsRefreshed = true; // triggers a send the values back via MQTT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* type 0xB0 - for reading the mode from the RC10 thermostat (0x17)
|
||||||
|
* received only after requested
|
||||||
|
*/
|
||||||
|
void _process_RC10Set(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
|
// mode not implemented yet
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* type 0xA8 - for reading the mode from the RC20 thermostat (0x17)
|
* type 0xA8 - for reading the mode from the RC20 thermostat (0x17)
|
||||||
* received only after requested
|
* received only after requested
|
||||||
*/
|
*/
|
||||||
void _process_RC20Set(uint8_t * data, uint8_t length) {
|
void _process_RC20Set(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Thermostat.mode = data[EMS_OFFSET_RC20Set_mode];
|
EMS_Thermostat.mode = data[EMS_OFFSET_RC20Set_mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1039,7 +1053,7 @@ void _process_RC20Set(uint8_t * data, uint8_t length) {
|
|||||||
* type 0xA7 - for reading the mode from the RC30 thermostat (0x10)
|
* type 0xA7 - for reading the mode from the RC30 thermostat (0x10)
|
||||||
* received only after requested
|
* received only after requested
|
||||||
*/
|
*/
|
||||||
void _process_RC30Set(uint8_t * data, uint8_t length) {
|
void _process_RC30Set(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Thermostat.mode = data[EMS_OFFSET_RC30Set_mode];
|
EMS_Thermostat.mode = data[EMS_OFFSET_RC30Set_mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1048,14 +1062,14 @@ void _process_RC30Set(uint8_t * data, uint8_t length) {
|
|||||||
* Working Mode Heating Circuit 1 (HC1)
|
* Working Mode Heating Circuit 1 (HC1)
|
||||||
* received only after requested
|
* received only after requested
|
||||||
*/
|
*/
|
||||||
void _process_RC35Set(uint8_t * data, uint8_t length) {
|
void _process_RC35Set(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
EMS_Thermostat.mode = data[EMS_OFFSET_RC35Set_mode];
|
EMS_Thermostat.mode = data[EMS_OFFSET_RC35Set_mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* type 0xA3 - for external temp settings from the the RC* thermostats
|
* type 0xA3 - for external temp settings from the the RC* thermostats
|
||||||
*/
|
*/
|
||||||
void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length) {
|
void _process_RCOutdoorTempMessage(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
// add support here if you're reading external sensors
|
// add support here if you're reading external sensors
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1063,7 +1077,7 @@ void _process_RCOutdoorTempMessage(uint8_t * data, uint8_t length) {
|
|||||||
* type 0x02 - get the firmware version and type of an EMS device
|
* type 0x02 - get the firmware version and type of an EMS device
|
||||||
* look up known devices via the product id and setup if not already set
|
* look up known devices via the product id and setup if not already set
|
||||||
*/
|
*/
|
||||||
void _process_Version(uint8_t * data, uint8_t length) {
|
void _process_Version(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
// ignore short messages that we can't interpret
|
// ignore short messages that we can't interpret
|
||||||
if (length < 3) {
|
if (length < 3) {
|
||||||
return;
|
return;
|
||||||
@@ -1155,7 +1169,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
|||||||
ems_getThermostatValues();
|
ems_getThermostatValues();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
myDebug("Unrecognized device found. Product ID %d, Version %s", product_id, version);
|
myDebug("Unrecognized device found. TypeID 0x%02X, Product ID %d, Version %s", type, product_id, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the boiler or thermostat values have changed, save them to SPIFFS
|
// if the boiler or thermostat values have changed, save them to SPIFFS
|
||||||
@@ -1170,7 +1184,6 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
|||||||
void ems_discoverModels() {
|
void ems_discoverModels() {
|
||||||
// boiler
|
// boiler
|
||||||
ems_doReadCommand(EMS_TYPE_Version, EMS_Boiler.type_id); // get version details of boiler
|
ems_doReadCommand(EMS_TYPE_Version, EMS_Boiler.type_id); // get version details of boiler
|
||||||
ems_getBoilerValues(); // fetch values from boiler, instead of waiting for broadcasts
|
|
||||||
|
|
||||||
// thermostat
|
// thermostat
|
||||||
// if it hasn't been set, auto discover it
|
// if it hasn't been set, auto discover it
|
||||||
@@ -1179,7 +1192,6 @@ void ems_discoverModels() {
|
|||||||
} else {
|
} else {
|
||||||
// set the model as hardcoded (see my_devices.h) and fetch the version and product id
|
// set the model as hardcoded (see my_devices.h) and fetch the version and product id
|
||||||
ems_doReadCommand(EMS_TYPE_Version, EMS_Thermostat.type_id);
|
ems_doReadCommand(EMS_TYPE_Version, EMS_Thermostat.type_id);
|
||||||
ems_getThermostatValues();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1223,24 +1235,26 @@ void _ems_setThermostatModel(uint8_t thermostat_modelid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UBASetPoint 0x1A, for RC20 and other thermostats. not really sure what to do with this data yet.
|
* UBASetPoint 0x1A
|
||||||
*/
|
*/
|
||||||
void _process_SetPoints(uint8_t * data, uint8_t length) {
|
void _process_SetPoints(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_THERMOSTAT) {
|
/*
|
||||||
|
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_VERBOSE) {
|
||||||
if (length != 0) {
|
if (length != 0) {
|
||||||
uint8_t setpoint = data[0];
|
uint8_t setpoint = data[0];
|
||||||
uint8_t hk_power = data[1];
|
uint8_t hk_power = data[1];
|
||||||
uint8_t ww_power = data[2];
|
uint8_t ww_power = data[2];
|
||||||
myDebug(" SetPoint=%d, hk_power=%d ww_power=%d", setpoint, hk_power, ww_power);
|
myDebug(" SetPoint=%d, hk_power=%d, ww_power=%d", setpoint, hk_power, ww_power);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long
|
* process_RCTime - type 0x06 - date and time from a thermostat - 14 bytes long
|
||||||
* common for all thermostats
|
* common for all thermostats
|
||||||
*/
|
*/
|
||||||
void _process_RCTime(uint8_t * data, uint8_t length) {
|
void _process_RCTime(uint8_t type, uint8_t * data, uint8_t length) {
|
||||||
if (EMS_Thermostat.model_id == EMS_MODEL_EASY) {
|
if (EMS_Thermostat.model_id == EMS_MODEL_EASY) {
|
||||||
return; // not supported
|
return; // not supported
|
||||||
}
|
}
|
||||||
@@ -1260,7 +1274,12 @@ void ems_printTxQueue() {
|
|||||||
_EMS_TxTelegram EMS_TxTelegram;
|
_EMS_TxTelegram EMS_TxTelegram;
|
||||||
char sType[20] = {0};
|
char sType[20] = {0};
|
||||||
|
|
||||||
myDebug("Tx queue (%d/%d), 1=first to send", EMS_TxQueue.size(), EMS_TxQueue.capacity);
|
if (EMS_TxQueue.size() == 0) {
|
||||||
|
myDebug("Tx queue is empty.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
myDebug("Tx queue (%d/%d)", EMS_TxQueue.size(), EMS_TxQueue.capacity);
|
||||||
|
|
||||||
for (byte i = 0; i < EMS_TxQueue.size(); i++) {
|
for (byte i = 0; i < EMS_TxQueue.size(); i++) {
|
||||||
EMS_TxTelegram = EMS_TxQueue[i]; // retrieves the i-th element from the buffer without removing it
|
EMS_TxTelegram = EMS_TxQueue[i]; // retrieves the i-th element from the buffer without removing it
|
||||||
@@ -1341,7 +1360,7 @@ void ems_getBoilerValues() {
|
|||||||
ems_doReadCommand(EMS_TYPE_UBAMonitorSlow, EMS_Boiler.type_id); // get more boiler stats, instead of waiting 60secs for the broadcast
|
ems_doReadCommand(EMS_TYPE_UBAMonitorSlow, EMS_Boiler.type_id); // get more boiler stats, instead of waiting 60secs for the broadcast
|
||||||
ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_Boiler.type_id); // get Warm Water values
|
ems_doReadCommand(EMS_TYPE_UBAParameterWW, EMS_Boiler.type_id); // get Warm Water values
|
||||||
ems_doReadCommand(EMS_TYPE_UBAParametersMessage, EMS_Boiler.type_id); // get MC10 boiler values
|
ems_doReadCommand(EMS_TYPE_UBAParametersMessage, EMS_Boiler.type_id); // get MC10 boiler values
|
||||||
ems_doReadCommand(EMS_TYPE_UBATotalUptimeMessage, EMS_Boiler.type_id); // get Warm Water values
|
ems_doReadCommand(EMS_TYPE_UBATotalUptimeMessage, EMS_Boiler.type_id); // get uptime from boiler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1495,6 +1514,7 @@ void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh) {
|
|||||||
|
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||||
|
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||||
|
|
||||||
// see if its a known type
|
// see if its a known type
|
||||||
int i = _ems_findType(type);
|
int i = _ems_findType(type);
|
||||||
@@ -1532,6 +1552,7 @@ void ems_sendRawTelegram(char * telegram) {
|
|||||||
|
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||||
|
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||||
|
|
||||||
// get first value, which should be the src
|
// get first value, which should be the src
|
||||||
if (p = strtok(telegram, " ,")) { // delimiter
|
if (p = strtok(telegram, " ,")) { // delimiter
|
||||||
@@ -1578,6 +1599,7 @@ void ems_setThermostatTemp(float temperature) {
|
|||||||
|
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||||
|
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||||
|
|
||||||
uint8_t model_id = EMS_Thermostat.model_id;
|
uint8_t model_id = EMS_Thermostat.model_id;
|
||||||
uint8_t type = EMS_Thermostat.type_id;
|
uint8_t type = EMS_Thermostat.type_id;
|
||||||
@@ -1593,6 +1615,10 @@ void ems_setThermostatTemp(float temperature) {
|
|||||||
EMS_TxTelegram.type = EMS_TYPE_RC20Set;
|
EMS_TxTelegram.type = EMS_TYPE_RC20Set;
|
||||||
EMS_TxTelegram.offset = EMS_OFFSET_RC20Set_temp;
|
EMS_TxTelegram.offset = EMS_OFFSET_RC20Set_temp;
|
||||||
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC20StatusMessage;
|
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC20StatusMessage;
|
||||||
|
} else if (model_id == EMS_MODEL_RC10) {
|
||||||
|
EMS_TxTelegram.type = EMS_TYPE_RC10Set;
|
||||||
|
EMS_TxTelegram.offset = EMS_OFFSET_RC10Set_temp;
|
||||||
|
EMS_TxTelegram.comparisonPostRead = EMS_TYPE_RC10StatusMessage;
|
||||||
} else if (model_id == EMS_MODEL_RC30) {
|
} else if (model_id == EMS_MODEL_RC30) {
|
||||||
EMS_TxTelegram.type = EMS_TYPE_RC30Set;
|
EMS_TxTelegram.type = EMS_TYPE_RC30Set;
|
||||||
EMS_TxTelegram.offset = EMS_OFFSET_RC30Set_temp;
|
EMS_TxTelegram.offset = EMS_OFFSET_RC30Set_temp;
|
||||||
@@ -1639,6 +1665,7 @@ void ems_setThermostatMode(uint8_t mode) {
|
|||||||
|
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||||
|
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||||
|
|
||||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||||
EMS_TxTelegram.dest = type;
|
EMS_TxTelegram.dest = type;
|
||||||
@@ -1679,6 +1706,7 @@ void ems_setWarmWaterTemp(uint8_t temperature) {
|
|||||||
|
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||||
|
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||||
|
|
||||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||||
EMS_TxTelegram.dest = EMS_Boiler.type_id;
|
EMS_TxTelegram.dest = EMS_Boiler.type_id;
|
||||||
@@ -1703,6 +1731,8 @@ void ems_setWarmWaterModeComfort(bool comfort) {
|
|||||||
myDebug("Setting boiler warm water to comfort mode %s\n", comfort ? "Comfort" : "Eco");
|
myDebug("Setting boiler warm water to comfort mode %s\n", comfort ? "Comfort" : "Eco");
|
||||||
|
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||||
|
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||||
|
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||||
|
|
||||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||||
EMS_TxTelegram.dest = EMS_Boiler.type_id;
|
EMS_TxTelegram.dest = EMS_Boiler.type_id;
|
||||||
@@ -1712,6 +1742,7 @@ void ems_setWarmWaterModeComfort(bool comfort) {
|
|||||||
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
|
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
|
||||||
EMS_TxTelegram.dataValue =
|
EMS_TxTelegram.dataValue =
|
||||||
(comfort ? EMS_VALUE_UBAParameterWW_wwComfort_Comfort : EMS_VALUE_UBAParameterWW_wwComfort_Eco); // 0x00 is on, 0xD8 is off
|
(comfort ? EMS_VALUE_UBAParameterWW_wwComfort_Comfort : EMS_VALUE_UBAParameterWW_wwComfort_Eco); // 0x00 is on, 0xD8 is off
|
||||||
|
|
||||||
EMS_TxQueue.push(EMS_TxTelegram);
|
EMS_TxQueue.push(EMS_TxTelegram);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1723,6 +1754,8 @@ void ems_setWarmWaterActivated(bool activated) {
|
|||||||
myDebug("Setting boiler warm water %s", activated ? "on" : "off");
|
myDebug("Setting boiler warm water %s", activated ? "on" : "off");
|
||||||
|
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||||
|
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||||
|
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||||
|
|
||||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||||
EMS_TxTelegram.dest = EMS_Boiler.type_id;
|
EMS_TxTelegram.dest = EMS_Boiler.type_id;
|
||||||
@@ -1731,6 +1764,7 @@ void ems_setWarmWaterActivated(bool activated) {
|
|||||||
EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
|
EMS_TxTelegram.length = EMS_MIN_TELEGRAM_LENGTH;
|
||||||
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
|
EMS_TxTelegram.type_validate = EMS_ID_NONE; // don't validate
|
||||||
EMS_TxTelegram.dataValue = (activated ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
|
EMS_TxTelegram.dataValue = (activated ? 0xFF : 0x00); // 0xFF is on, 0x00 is off
|
||||||
|
|
||||||
EMS_TxQueue.push(EMS_TxTelegram);
|
EMS_TxQueue.push(EMS_TxTelegram);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1744,6 +1778,7 @@ void ems_setWarmTapWaterActivated(bool activated) {
|
|||||||
|
|
||||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||||
|
EMS_Sys_Status.txRetryCount = 0; // reset retry counter
|
||||||
|
|
||||||
// clear Tx to make sure all data is set to 0x00
|
// clear Tx to make sure all data is set to 0x00
|
||||||
for (int i = 0; (i < EMS_MAX_TELEGRAM_LENGTH); i++) {
|
for (int i = 0; (i < EMS_MAX_TELEGRAM_LENGTH); i++) {
|
||||||
@@ -1762,7 +1797,6 @@ void ems_setWarmTapWaterActivated(bool activated) {
|
|||||||
EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.type;
|
EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.type;
|
||||||
EMS_TxTelegram.forceRefresh = true; // send new value to MQTT after successful write
|
EMS_TxTelegram.forceRefresh = true; // send new value to MQTT after successful write
|
||||||
|
|
||||||
|
|
||||||
// create header
|
// create header
|
||||||
EMS_TxTelegram.data[0] = EMS_ID_ME; // src
|
EMS_TxTelegram.data[0] = EMS_ID_ME; // src
|
||||||
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest
|
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest
|
||||||
|
|||||||
26
src/ems.h
@@ -61,17 +61,18 @@
|
|||||||
|
|
||||||
/* EMS UART transfer status */
|
/* EMS UART transfer status */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EMS_RX_IDLE,
|
EMS_RX_STATUS_IDLE,
|
||||||
EMS_RX_ACTIVE // Rx package is being sent
|
EMS_RX_STATUS_BUSY // Rx package is being received
|
||||||
} _EMS_RX_STATUS;
|
} _EMS_RX_STATUS;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EMS_TX_IDLE,
|
EMS_TX_STATUS_IDLE, // ready
|
||||||
EMS_TX_ACTIVE, // Tx package being sent, no break sent
|
EMS_TX_STATUS_WAIT // waiting for response from last Tx
|
||||||
EMS_TX_SUCCESS,
|
|
||||||
EMS_TX_ERROR
|
|
||||||
} _EMS_TX_STATUS;
|
} _EMS_TX_STATUS;
|
||||||
|
|
||||||
|
#define EMS_TX_SUCCESS 0x01 // EMS single byte after a Tx Write indicating a success
|
||||||
|
#define EMS_TX_ERROR 0x04 // EMS single byte after a Tx Write indicating an error
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EMS_TX_TELEGRAM_INIT, // just initialized
|
EMS_TX_TELEGRAM_INIT, // just initialized
|
||||||
EMS_TX_TELEGRAM_READ, // doing a read request
|
EMS_TX_TELEGRAM_READ, // doing a read request
|
||||||
@@ -97,18 +98,18 @@ typedef struct {
|
|||||||
uint16_t emsTxPkgs; // sent
|
uint16_t emsTxPkgs; // sent
|
||||||
uint16_t emxCrcErr; // CRC errors
|
uint16_t emxCrcErr; // CRC errors
|
||||||
bool emsPollEnabled; // flag enable the response to poll messages
|
bool emsPollEnabled; // flag enable the response to poll messages
|
||||||
bool emsTxEnabled; // flag if we're allowing sending of Tx packages
|
|
||||||
_EMS_SYS_LOGGING emsLogging; // logging
|
_EMS_SYS_LOGGING emsLogging; // logging
|
||||||
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
|
bool emsRefreshed; // fresh data, needs to be pushed out to MQTT
|
||||||
bool emsBusConnected; // is there an active bus
|
bool emsBusConnected; // is there an active bus
|
||||||
unsigned long emsRxTimestamp; // timestamp of last EMS message received
|
unsigned long emsRxTimestamp; // timestamp of last EMS message received
|
||||||
unsigned long emsPollTimestamp; // timestamp of last EMS poll
|
unsigned long emsPollTimestamp; // timestamp of last EMS poll sent to us
|
||||||
bool emsTxCapable; // able to send via Tx
|
bool emsTxCapable; // able to send via Tx
|
||||||
|
uint8_t txRetryCount; // # times the last Tx was re-sent
|
||||||
} _EMS_Sys_Status;
|
} _EMS_Sys_Status;
|
||||||
|
|
||||||
// The Tx send package
|
// The Tx send package
|
||||||
typedef struct {
|
typedef struct {
|
||||||
_EMS_TX_TELEGRAM_ACTION action; // read or write
|
_EMS_TX_TELEGRAM_ACTION action; // read, write, validate, init
|
||||||
uint8_t dest;
|
uint8_t dest;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t offset;
|
uint8_t offset;
|
||||||
@@ -123,6 +124,8 @@ typedef struct {
|
|||||||
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
||||||
} _EMS_TxTelegram;
|
} _EMS_TxTelegram;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// default empty Tx
|
// default empty Tx
|
||||||
const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
||||||
EMS_TX_TELEGRAM_INIT, // action
|
EMS_TX_TELEGRAM_INIT, // action
|
||||||
@@ -236,8 +239,8 @@ typedef struct {
|
|||||||
uint8_t year;
|
uint8_t year;
|
||||||
} _EMS_Thermostat;
|
} _EMS_Thermostat;
|
||||||
|
|
||||||
// call back function signature
|
// call back function signature for processing telegram types
|
||||||
typedef void (*EMS_processType_cb)(uint8_t * data, uint8_t length);
|
typedef void (*EMS_processType_cb)(uint8_t type, uint8_t * data, uint8_t length);
|
||||||
|
|
||||||
// Definition for each EMS type, including the relative callback function
|
// Definition for each EMS type, including the relative callback function
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -293,6 +296,7 @@ void _ems_clearTxData();
|
|||||||
int _ems_findBoilerModel(uint8_t model_id);
|
int _ems_findBoilerModel(uint8_t model_id);
|
||||||
bool _ems_setModel(uint8_t model_id);
|
bool _ems_setModel(uint8_t model_id);
|
||||||
void _ems_setThermostatModel(uint8_t thermostat_modelid);
|
void _ems_setThermostatModel(uint8_t thermostat_modelid);
|
||||||
|
void _removeTxQueue();
|
||||||
|
|
||||||
// global so can referenced in other classes
|
// global so can referenced in other classes
|
||||||
extern _EMS_Sys_Status EMS_Sys_Status;
|
extern _EMS_Sys_Status EMS_Sys_Status;
|
||||||
|
|||||||
@@ -46,6 +46,13 @@
|
|||||||
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
|
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
|
||||||
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
|
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
|
||||||
|
|
||||||
|
// RC10 specific
|
||||||
|
#define EMS_TYPE_RC10StatusMessage 0xB1 // is an automatic thermostat broadcast giving us temps
|
||||||
|
#define EMS_TYPE_RC10Set 0xB0 // for setting values like temp and mode
|
||||||
|
#define EMS_OFFSET_RC10Set_temp 4 // position of thermostat setpoint temperature
|
||||||
|
#define EMS_TYPE_RC10StatusMessage_setpoint 1 // setpoint temp
|
||||||
|
#define EMS_TYPE_RC10StatusMessage_curr 3 // current temp
|
||||||
|
|
||||||
// RC20 specific
|
// RC20 specific
|
||||||
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
|
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
|
||||||
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
|
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
|
||||||
@@ -87,6 +94,7 @@ typedef enum {
|
|||||||
|
|
||||||
// thermostats
|
// thermostats
|
||||||
EMS_MODEL_ES73,
|
EMS_MODEL_ES73,
|
||||||
|
EMS_MODEL_RC10,
|
||||||
EMS_MODEL_RC20,
|
EMS_MODEL_RC20,
|
||||||
EMS_MODEL_RC20F,
|
EMS_MODEL_RC20F,
|
||||||
EMS_MODEL_RC30,
|
EMS_MODEL_RC30,
|
||||||
@@ -104,8 +112,9 @@ const _Boiler_Type Boiler_Types[] = {
|
|||||||
{EMS_MODEL_UBA, 72, 0x08, "MC10"},
|
{EMS_MODEL_UBA, 72, 0x08, "MC10"},
|
||||||
{EMS_MODEL_UBA, 123, 0x08, "Buderus GB172/Nefit Trendline"},
|
{EMS_MODEL_UBA, 123, 0x08, "Buderus GB172/Nefit Trendline"},
|
||||||
{EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"},
|
{EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"},
|
||||||
{EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler"},
|
{EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler/Nefit Smartline"},
|
||||||
{EMS_MODEL_UBA, 190, 0x09, "BC10 Base Controller"},
|
{EMS_MODEL_UBA, 190, 0x09, "BC10 Base Controller"},
|
||||||
|
{EMS_MODEL_UBA, 114, 0x09, "BC10 Base Controller"},
|
||||||
{EMS_MODEL_UBA, 125, 0x09, "BC25 Base Controller"},
|
{EMS_MODEL_UBA, 125, 0x09, "BC25 Base Controller"},
|
||||||
{EMS_MODEL_UBA, 68, 0x09, "RFM20 Receiver"},
|
{EMS_MODEL_UBA, 68, 0x09, "RFM20 Receiver"},
|
||||||
{EMS_MODEL_UBA, 95, 0x08, "Bosch Condens 2500"},
|
{EMS_MODEL_UBA, 95, 0x08, "Bosch Condens 2500"},
|
||||||
@@ -120,6 +129,7 @@ const _Boiler_Type Boiler_Types[] = {
|
|||||||
const _Thermostat_Type Thermostat_Types[] = {
|
const _Thermostat_Type Thermostat_Types[] = {
|
||||||
|
|
||||||
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||||
|
{EMS_MODEL_RC10, 79, 0x17, "RC10/Nefit Moduline 100)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||||
{EMS_MODEL_RC20, 77, 0x17, "RC20/Nefit Moduline 300)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
{EMS_MODEL_RC20, 77, 0x17, "RC20/Nefit Moduline 300)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||||
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||||
{EMS_MODEL_RC30, 78, 0x10, "RC30/Nefit Moduline 400)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
{EMS_MODEL_RC30, 78, 0x10, "RC30/Nefit Moduline 400)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ static void emsuart_rx_intr_handler(void * para) {
|
|||||||
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE];
|
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE];
|
||||||
|
|
||||||
// is a new buffer? if so init the thing for a new telegram
|
// is a new buffer? if so init the thing for a new telegram
|
||||||
if (EMS_Sys_Status.emsRxStatus == EMS_RX_IDLE) {
|
if (EMS_Sys_Status.emsRxStatus == EMS_RX_STATUS_IDLE) {
|
||||||
EMS_Sys_Status.emsRxStatus = EMS_RX_ACTIVE; // status set to active
|
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_BUSY; // status set to busy
|
||||||
length = 0;
|
length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ static void emsuart_rx_intr_handler(void * para) {
|
|||||||
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, length);
|
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, length);
|
||||||
|
|
||||||
// set the status flag stating BRK has been received and we can start a new package
|
// set the status flag stating BRK has been received and we can start a new package
|
||||||
EMS_Sys_Status.emsRxStatus = EMS_RX_IDLE;
|
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE;
|
||||||
|
|
||||||
// call emsuart_recvTask() at next opportunity
|
// call emsuart_recvTask() at next opportunity
|
||||||
system_os_post(EMSUART_recvTaskPrio, 0, 0);
|
system_os_post(EMSUART_recvTaskPrio, 0, 0);
|
||||||
|
|||||||
@@ -10,8 +10,10 @@
|
|||||||
|
|
||||||
#include "ems.h"
|
#include "ems.h"
|
||||||
|
|
||||||
// All MQTT topics are prefixed with the following string
|
// MQTT base name
|
||||||
#define MQTT_BASE "home"
|
#define MQTT_BASE "home" // all MQTT topics are prefix with this string, in the format <MQTT_BASE>/<app name>/<topic>
|
||||||
|
|
||||||
|
// MQTT general settings
|
||||||
#define MQTT_TOPIC_START "start"
|
#define MQTT_TOPIC_START "start"
|
||||||
#define MQTT_TOPIC_START_PAYLOAD "start"
|
#define MQTT_TOPIC_START_PAYLOAD "start"
|
||||||
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name
|
#define MQTT_WILL_TOPIC "status" // for last will & testament topic name
|
||||||
@@ -20,7 +22,7 @@
|
|||||||
#define MQTT_KEEPALIVE 300
|
#define MQTT_KEEPALIVE 300
|
||||||
#define MQTT_QOS 1
|
#define MQTT_QOS 1
|
||||||
|
|
||||||
// thermostat
|
// MQTT for thermostat
|
||||||
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values
|
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values
|
||||||
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes
|
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes
|
||||||
#define TOPIC_THERMOSTAT_CMD_MODE "thermostat_cmd_mode" // for received thermostat mode changes
|
#define TOPIC_THERMOSTAT_CMD_MODE "thermostat_cmd_mode" // for received thermostat mode changes
|
||||||
@@ -28,7 +30,7 @@
|
|||||||
#define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature
|
#define THERMOSTAT_SELTEMP "thermostat_seltemp" // selected temperature
|
||||||
#define THERMOSTAT_MODE "thermostat_mode" // mode
|
#define THERMOSTAT_MODE "thermostat_mode" // mode
|
||||||
|
|
||||||
// boiler
|
// MQTT for boiler
|
||||||
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values
|
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values
|
||||||
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
|
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
|
||||||
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
|
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
|
||||||
@@ -44,17 +46,10 @@
|
|||||||
#define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold water when shower time limit has exceeded
|
#define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold water when shower time limit has exceeded
|
||||||
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// THESE DEFAULT VALUES CAN ALSO BE SET AND STORED WITHTIN THE APPLICATION (see 'set' command) //
|
// THESE DEFAULT VALUES CAN ALSO BE SET AND STORED WITHTIN THE APPLICATION (see 'set' command) //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
// ALTHOUGH YOU MAY ALSO HARDCODE THEM HERE BUT THEY WILL BE OVERWRITTEN WITH NEW RELEASE UPDATES //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Set your wifi and mqtt params
|
|
||||||
// If set to NULL (default) you'll need to set them in AP mode using the 'set' command
|
|
||||||
#define WIFI_SSID NULL
|
|
||||||
#define WIFI_PASSWORD NULL
|
|
||||||
#define MQTT_HOST NULL
|
|
||||||
#define MQTT_USER NULL
|
|
||||||
#define MQTT_PASS NULL
|
|
||||||
|
|
||||||
// Set LED pin used for showing ems bus connection status. Solid is connected, Flashing is error
|
// Set LED pin used for showing ems bus connection status. Solid is connected, Flashing is error
|
||||||
// can be either the onboard LED on the ESP8266 (LED_BULLETIN) or external via an external pull-up LED (e.g. D1 on bbqkees' board)
|
// can be either the onboard LED on the ESP8266 (LED_BULLETIN) or external via an external pull-up LED (e.g. D1 on bbqkees' board)
|
||||||
|
|||||||
@@ -6,5 +6,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define APP_NAME "EMS-ESP"
|
#define APP_NAME "EMS-ESP"
|
||||||
#define APP_VERSION "1.4.1"
|
#define APP_VERSION "1.5.0"
|
||||||
#define APP_HOSTNAME "ems-esp"
|
#define APP_HOSTNAME "ems-esp"
|
||||||
|
|||||||