mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
version 1.3.0
This commit is contained in:
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -9,8 +9,8 @@ assignees: ''
|
||||
|
||||
*Before creating a new issue please check that you have:*
|
||||
|
||||
* *searched the existing [issues](https://github.com/proddy/EMS-ESP-Boiler/issues) (both open and closed)*
|
||||
* *searched the [doc](https://github.com/proddy/EMS-ESP-Boiler/blob/master/README.md)*
|
||||
* *searched the existing [issues](https://github.com/proddy/EMS-ESP/issues) (both open and closed)*
|
||||
* *searched the [doc](https://github.com/proddy/EMS-ESP/blob/master/README.md)*
|
||||
|
||||
*Fulfilling this template will help developers and contributors to address the issue. Try to be as specific and extensive as possible. If the information provided is not enough the issue will likely be closed.*
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,7 +7,7 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
*Before creating a new feature request please check that you have searched the existing [issues](https://github.com/proddy/EMS-ESP-Boiler/issues) (both open and closed)*
|
||||
*Before creating a new feature request please check that you have searched the existing [issues](https://github.com/proddy/EMS-ESP/issues) (both open and closed)*
|
||||
|
||||
*Fulfilling this template will help developers and contributors evaluating the feature. If the information provided is not enough the issue will likely be closed.*
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ assignees: ''
|
||||
|
||||
*Before creating a new issue please check that you have:*
|
||||
|
||||
* *searched the existing [issues](https://github.com/proddy/EMS-ESP-Boiler/issues) (both open and closed)*
|
||||
* *searched the [doc](https://github.com/proddy/EMS-ESP-Boiler/blob/master/README.md)*
|
||||
* *searched the existing [issues](https://github.com/proddy/EMS-ESP/issues) (both open and closed)*
|
||||
* *searched the [doc](https://github.com/proddy/EMS-ESP/blob/master/README.md)*
|
||||
|
||||
|
||||
*Fulfilling this template will help developers and contributors help you. Try to be as specific and extensive as possible. If the information provided is not enough the issue will likely be closed.*
|
||||
|
||||
49
CHANGELOG.md
49
CHANGELOG.md
@@ -1,16 +1,37 @@
|
||||
# EMS-ESP-Boiler Changelog
|
||||
# EMS-ESP Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.3.0] 2019-01-09
|
||||
|
||||
### Changed
|
||||
|
||||
- Renamed project from EMS-ESP-Boiler to EMS-ESP since it's kinda EMS generic now
|
||||
- Support for RC20F and RFM20 (https://github.com/proddy/EMS-ESP/issues/18)
|
||||
- Moved all EMS device information into a separate file `ems_devices.h` so no longer need to touch `ems.h`
|
||||
- Telnet commands can be strings now and output is suspended when typing
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed SHOWER_TEST
|
||||
- Removed WIFI and MQTT credentials from the platformio.ini file
|
||||
|
||||
### Added
|
||||
|
||||
- Settings are saved and loaded from the ESP8266's file system (SPIFFS). Can be set using the 'set' command
|
||||
- Improved support when in Access Point mode (192.168.4.1)
|
||||
- pre-built firmwares are back
|
||||
|
||||
## [1.2.4] 2019-01-04
|
||||
|
||||
### Changed
|
||||
|
||||
- Scanning known EMS Devices now ignores duplicates (https://github.com/proddy/EMS-ESP-Boiler/pull/30)
|
||||
- Scanning known EMS Devices now ignores duplicates (https://github.com/proddy/EMS-ESP/pull/30)
|
||||
- ServiceCode stored as a two byte char
|
||||
- Support for RC20F and RFM20 (https://github.com/proddy/EMS-ESP/issues/18)
|
||||
|
||||
## [1.2.3] 2019-01-03
|
||||
|
||||
@@ -18,23 +39,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- Can now hardcode Boiler and Thermostat types in my_config.h to bypass auto-detection
|
||||
- Fixed MQTT subscribing to Heating and Hot Water active topics
|
||||
- Fixed for listening to incoming MQTT topics (https://github.com/proddy/EMS-ESP-Boiler/issues/27)
|
||||
- Fixed handling of current temperature on an RC35-type thermostat that doesn't have a sensor (https://github.com/proddy/EMS-ESP-Boiler/issues/18)
|
||||
- Fixed for listening to incoming MQTT topics (https://github.com/proddy/EMS-ESP/issues/27)
|
||||
- Fixed handling of current temperature on an RC35-type thermostat that doesn't have a sensor (https://github.com/proddy/EMS-ESP/issues/18)
|
||||
|
||||
## [1.2.2] 2019-01-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Issues in 1.2.1 (see https://github.com/proddy/EMS-ESP-Boiler/issues/25)
|
||||
- Issues in 1.2.1 (see https://github.com/proddy/EMS-ESP/issues/25)
|
||||
- Logic for determining if there is activity on the EMS bus and using the onboard LEDs properly
|
||||
|
||||
## [1.2.1] 2019-01-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Only process broadcast messages if the offset (byte 4) is 0. (https://github.com/proddy/EMS-ESP-Boiler/issues/23)
|
||||
- Only process broadcast messages if the offset (byte 4) is 0. (https://github.com/proddy/EMS-ESP/issues/23)
|
||||
- Improved checking for duplicate sent Tx telegrams by comparing CRCs
|
||||
- Removed distiquishing between noise on the line and corrupt telegrams (https://github.com/proddy/EMS-ESP-Boiler/issues/24)
|
||||
- Removed distiquishing between noise on the line and corrupt telegrams (https://github.com/proddy/EMS-ESP/issues/24)
|
||||
|
||||
## [1.2.0] 2019-01-01
|
||||
|
||||
@@ -54,7 +75,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Fetch UBATotalUptimeMessage from Boiler to get total working minutes
|
||||
- Added check to see if bus is connected. Shown in stats page
|
||||
- If no Wifi connection can be made, start up as a WiFi Access Point (AP)
|
||||
- Report out service codes and water-flow [pull-request](https://github.com/proddy/EMS-ESP-Boiler/pull/20/files). Thanks @Bonusbartus
|
||||
- Report out service codes and water-flow [pull-request](https://github.com/proddy/EMS-ESP/pull/20/files). Thanks @Bonusbartus
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -83,16 +104,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- Fixed handling of negative floating point values (like outdoor temp)
|
||||
- Fixed handling of auto & manual mode on an RC30
|
||||
- [Fixed condition where all telegram types were processed, instead of only broadcasts or our own reads](https://github.com/proddy/EMS-ESP-Boiler/issues/15)
|
||||
- [Fixed condition where all telegram types were processed, instead of only broadcasts or our own reads](https://github.com/proddy/EMS-ESP/issues/15)
|
||||
|
||||
### Added
|
||||
|
||||
- Created this CHANGELOG.md file!
|
||||
- [Added support for the Nefit Easy thermostat, reading of temperature values only](https://github.com/proddy/EMS-ESP-Boiler/issues/9) - note *read only* (big thanks @**kroon040** for lending me an Easy device)
|
||||
- [Added support for RC35/Moduline 400](https://github.com/proddy/EMS-ESP-Boiler/issues/14) - *read only*
|
||||
- [New raw logging mode for logging](https://github.com/proddy/EMS-ESP-Boiler/issues/11)
|
||||
- [New 'r'command to send raw data to EMS](https://github.com/proddy/EMS-ESP-Boiler/issues/11)
|
||||
- [Added MQTT messages for hot water on and heating on](https://github.com/proddy/EMS-ESP-Boiler/issues/10)
|
||||
- [Added support for the Nefit Easy thermostat, reading of temperature values only](https://github.com/proddy/EMS-ESP/issues/9) - note *read only* (big thanks @**kroon040** for lending me an Easy device)
|
||||
- [Added support for RC35/Moduline 400](https://github.com/proddy/EMS-ESP/issues/14) - *read only*
|
||||
- [New raw logging mode for logging](https://github.com/proddy/EMS-ESP/issues/11)
|
||||
- [New 'r'command to send raw data to EMS](https://github.com/proddy/EMS-ESP/issues/11)
|
||||
- [Added MQTT messages for hot water on and heating on](https://github.com/proddy/EMS-ESP/issues/10)
|
||||
- Implemented FIFO circular buffer queue for up to 20 Tx messages (Q command to show queue)
|
||||
- Toggle Tx transmission via telnet (use 'X' command)
|
||||
- Show thermostat type in help stats (use 's' command)
|
||||
|
||||
108
README.md
108
README.md
@@ -1,13 +1,13 @@
|
||||
# EMS-ESP-Boiler
|
||||
# EMS-ESP
|
||||
|
||||
EMS-ESP-Boiler is a project to build a controller circuit running with an ESP8266 to communicate with EMS (Energy Management System) based Boilers and Thermostats from the Bosch range and compatibles such as Buderus, Nefit, Junkers etc.
|
||||
EMS-ESP is a project to build a controller circuit running with an ESP8266 to communicate with EMS (Energy Management System) based Boilers and Thermostats from the Bosch range and compatibles such as Buderus, Nefit, Junkers etc.
|
||||
|
||||
There are 3 parts to this project, first the design of the circuit, second the code for the ESP8266 microcontroller firmware and lastly an example configuration for Home Assistant to monitor the data and issue direct commands via MQTT.
|
||||
|
||||
[](https://app.codacy.com/app/proddy/EMS-ESP-Boiler?utm_source=github.com&utm_medium=referral&utm_content=proddy/EMS-ESP-Boiler&utm_campaign=Badge_Grade_Settings)
|
||||
[](https://app.codacy.com/app/proddy/EMS-ESP?utm_source=github.com&utm_medium=referral&utm_content=proddy/EMS-ESP&utm_campaign=Badge_Grade_Settings)
|
||||
[](CHANGELOG.md)
|
||||
|
||||
- [EMS-ESP-Boiler](#ems-esp-boiler)
|
||||
- [EMS-ESP](#ems-esp)
|
||||
- [Introduction](#introduction)
|
||||
- [Supported Boilers Types](#supported-boilers-types)
|
||||
- [Supported ESP8266 devices](#supported-esp8266-devices)
|
||||
@@ -22,11 +22,7 @@ There are 3 parts to this project, first the design of the circuit, second the c
|
||||
- [EMS Reading and Writing](#ems-reading-and-writing)
|
||||
- [The ESP8266 Source Code](#the-esp8266-source-code)
|
||||
- [Supported EMS Types](#supported-ems-types)
|
||||
- [Supported Thermostats](#supported-thermostats)
|
||||
- [RC20 (Moduline 300)](#rc20-moduline-300)
|
||||
- [RC30 (Moduline 400)](#rc30-moduline-400)
|
||||
- [RC35](#rc35)
|
||||
- [TC100/TC200 (Nefit Easy)](#tc100tc200-nefit-easy)
|
||||
- [Which thermostats are supported?](#which-thermostats-are-supported)
|
||||
- [Customizing The Code](#customizing-the-code)
|
||||
- [Using MQTT](#using-mqtt)
|
||||
- [The Basic Shower Logic](#the-basic-shower-logic)
|
||||
@@ -34,6 +30,7 @@ There are 3 parts to this project, first the design of the circuit, second the c
|
||||
- [Building The Firmware](#building-the-firmware)
|
||||
- [Using PlatformIO Standalone](#using-platformio-standalone)
|
||||
- [Building Using Arduino IDE](#building-using-arduino-ide)
|
||||
- [Using the Pre-built Firmware](#using-the-pre-built-firmware)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Known Issues](#known-issues)
|
||||
- [Wish List](#wish-list)
|
||||
@@ -48,7 +45,7 @@ Firstly, some acknowledgments and kudos to the following people who have open-so
|
||||
|
||||
**susisstrolch** - Probably the first working version of the EMS bridge circuit I found designed for the ESP8266. I borrowed Juergen's [schematic](https://github.com/susisstrolch/EMS-ESP12) and parts of his code logic for reading telegrams.
|
||||
|
||||
**bbqkees** - Kees built a working [circuit](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz) and some sample Arduino code to read from the EMS and push messages to Domoticz. His SMD board is also now available for purchase.
|
||||
**bbqkees** - Kees built a working [circuit](https://shop.hotgoodies.nl/ems/) and some sample Arduino code to read from the EMS and push messages to Domoticz. His SMD board is also available for purchase.
|
||||
|
||||
**EMS Wiki** - A comprehensive [reference](https://emswiki.thefischer.net/doku.php?id=wiki:ems:telegramme) (in German) for the EMS bus which is a little outdated, not 100% accurate and unfortunately no longer maintained.
|
||||
|
||||
@@ -62,7 +59,7 @@ I've tested the code and circuit with a few ESP8266 development boards such as t
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Either build the circuit below or purchase a ready built board from bbqkees via his [GitHub](https://github.com/bbqkees/Nefit-Buderus-EMS-bus-Arduino-Domoticz) page or the [Domoticz forum](http://www.domoticz.com/forum/viewtopic.php?f=22&t=22079&start=20).
|
||||
1. Either build the circuit described below or purchase a ready built board from bbqkees via [website](https://shop.hotgoodies.nl/ems/).
|
||||
2. Get an ESP8266 dev board and connect the 2 EMS output lines from the boiler to the circuit and the Rx and Tx out to ESP pins D7 and D8 respectively. The EMS connection can either be the 12-15V AC direct from the thermostat bus line or from the 3.5" Service Jack at the front.
|
||||
3. Optionally connect an external LED or decide to use the onboard ESP8266 LED. This will flash when there is an error on the EMS bus line or stay solid when it's connected.
|
||||
4. Modify `my_custom.h`
|
||||
@@ -133,7 +130,7 @@ The EMS circuit will work with both 3.3V and 5V. It's easiest though to power di
|
||||
|
||||
| With Power Circuit |
|
||||
| ------------------------------------------ |
|
||||
|  |
|
||||
|  |
|
||||
|
||||
## How The EMS Bus Works
|
||||
|
||||
@@ -211,10 +208,12 @@ The code is built on the Arduino framework and is dependent on these external li
|
||||
|
||||
`ems.cpp` is the logic to read the EMS packets (telegrams), validates them and process them based on the type.
|
||||
|
||||
`boiler.ino` is the Arduino code for the ESP8266 that kicks it all off. This is where we have specific logic such as the code to monitor and alert on the Shower timer and light up the LEDs. LED support is enabled by default and can be switched off at compile time using the -DNO_LED build flag.
|
||||
`ems-esp.ino` is the Arduino code for the ESP8266 that kicks it all off. This is where we have specific logic such as the code to monitor and alert on the Shower timer and light up the LEDs. LED support is enabled by default and can be switched off at compile time using the -DNO_LED build flag.
|
||||
|
||||
`my_config.h` all the custom settings
|
||||
|
||||
`ems_devices.h` has all the configuration for the known EMS devices this supports
|
||||
|
||||
`MyESP.cpp` is my custom library to handle WiFi, MQTT, MDNS and Telnet. Uses a modified version of TelnetSpy (https://github.com/yasheena/telnetspy)
|
||||
|
||||
### Supported EMS Types
|
||||
@@ -232,44 +231,17 @@ The code is built on the Arduino framework and is dependent on these external li
|
||||
| Thermostat | 0x02 | Version | reads Version major/minor |
|
||||
| Thermostat | 0x91, 0x41, 0x0A | Status Message | read monitor values |
|
||||
|
||||
In `boiler.ino` you can make calls to automatically request these types in the function *regularUpdates()*.
|
||||
In `ems-esp.ino` you can make calls to automatically request these types in the function *regularUpdates()*.
|
||||
|
||||
### Supported Thermostats
|
||||
### Which thermostats are supported?
|
||||
|
||||
I am still working on adding more support to known thermostats.
|
||||
I am still working on adding more support to known thermostats. Any contributions here are welcome. Please use to
|
||||
|
||||
Currently known types and collected versions:
|
||||
The know types are listed in `ems_devices.h`. Some special notes
|
||||
|
||||
Moduline 300 = Type 77 Version 03.03
|
||||
Moduline 400 = Type 78 Version 03.03
|
||||
Buderus RC35 = Type 86 Version 01.15
|
||||
Nefit Easy = Type 202 Version 02.19
|
||||
Nefit Trendline HRC30 = Type 123 Version 06.01
|
||||
BC10 = Type 123 Version 04.05
|
||||
|
||||
#### RC20 (Moduline 300)
|
||||
|
||||
Read and write of setpoint temperature and mode are supported.
|
||||
|
||||
#### RC30 (Moduline 400)
|
||||
|
||||
Read and write of setpoint temperature and mode are supported.
|
||||
|
||||
Note I found type's 3F, 49, 53, 5D are identical. So are 4B, 55, 5F and mostly zero's. Types 40, 4A, 54 and 5E are also the same.
|
||||
|
||||
#### RC35
|
||||
|
||||
An RC35 thermostat can support up to 4 heating circuits each controlled with their own Monitor and Working Mode IDs.
|
||||
|
||||
Fetching the thermostats setpoint temperature us by requesting 0x3E and looking at the 3rd byte in the data telegram (data[2]) and dividing by 2.
|
||||
|
||||
The mode is on type 0x47 (or 0x3D) and the 8th byte (data[7]). 0=off, 1=on, 2=auto
|
||||
|
||||
This is roughly supported but not fully tested.
|
||||
|
||||
#### TC100/TC200 (Nefit Easy)
|
||||
|
||||
There is limited support for an Nefit Easy TC100/TC200 type thermostat. The current room temperature and setpoint temperature can be read. What I'm still figuring out is how to read the mode and set the temperature values without sending http post commands to their web server.
|
||||
- RC20 and RC30 are fully supported
|
||||
- RC35 only supports the 1st heating circuit (HC1)
|
||||
- TC100/TC200/Easy has only support for reading the temperatures. There seems to be no way to set settngs using EMS bus messages. The device only listens to XMPP requests.
|
||||
|
||||
### Customizing The Code
|
||||
|
||||
@@ -277,20 +249,17 @@ There is limited support for an Nefit Easy TC100/TC200 type thermostat. The curr
|
||||
- set flags for enabled/disabling functionality such as `BOILER_SHOWER_ENABLED` and `BOILER_SHOWER_TIMER`.
|
||||
- Set WIFI and MQTT settings, instead of doing this in `platformio.ini`
|
||||
- To add new handlers for EMS data types, first create a callback function and add to the `EMS_Types` array at the top of the file `ems.cpp` and modify `ems.h`
|
||||
- To add new devices modify `ems_devices.h`
|
||||
|
||||
### Using MQTT
|
||||
|
||||
When the ESP8266 boots it will send a start signal via MQTT. This is picked up by Home Assistant and sends a notification informing me that the device has booted. Useful for knowing when the ESP gets reset.
|
||||
|
||||
I run Mosquitto on my Raspberry PI 3 as the MQTT broker.
|
||||
|
||||
The boiler data is collected and sent as a single JSON object to MQTT TOPIC `home/boiler/boiler_data`. A hash is generated (CRC32 based) to determine if the payload has changed, otherwise don't send it. An example payload looks roughly like:
|
||||
The boiler data is collected and sent as a single JSON object to MQTT TOPIC `home/ems-esp/boiler_data`. A hash is generated (CRC32 based) to determine if the payload has changed, otherwise don't send it. An example payload looks roughly like:
|
||||
|
||||
`{"wWCurTmp":"43.0","wWHeat":"on","curFlowTemp":"51.7","retTemp":"48.0","burnGas":"off","heatPmp":"off","fanWork":"off","ignWork":"off","wWCirc":"off","selBurnPow":"0","curBurnPow":"0","sysPress":"1.6","boilTemp":"54.7","pumpMod":"4"}`
|
||||
|
||||
Similarly the thermostat values are sent as a json package under a topic named `home/boiler/thermostat_data` with the current mode, room temperature and set temperature.
|
||||
Similarly the thermostat values are sent as a json package under a topic named `home/ems-esp/thermostat_data` with the current mode, room temperature and set temperature.
|
||||
|
||||
These topics can be configured in the `TOPIC_*` defines in `boiler.ino`. Make sure you change the HA configuration too to match.
|
||||
the `home` is the MQTT topic prefix and can be customized in my_config.h
|
||||
|
||||
### The Basic Shower Logic
|
||||
|
||||
@@ -333,11 +302,11 @@ You can find the .yaml configuration files under `doc/ha`. See also https://comm
|
||||
% sudo platformio upgrade
|
||||
% platformio platform update
|
||||
|
||||
% git clone https://github.com/proddy/EMS-ESP-Boiler.git
|
||||
% cd EMS-ESP-Boiler
|
||||
% git clone https://github.com/proddy/EMS-ESP.git
|
||||
% cd EMS-ESP
|
||||
% cp platformio.ini-example platformio.ini
|
||||
```
|
||||
- edit `platformio.ini` to set `env_default` and the flags like `WIFI_SSID WIFI_PASSWORD, MQTT_IP, MQTT_USER, MQTT_PASS`. If you're not using MQTT leave MQTT_IP empty (`MQTT_IP=""`)
|
||||
- edit `platformio.ini` to set `env_default` to your board type
|
||||
```c
|
||||
% platformio run -t upload
|
||||
```
|
||||
@@ -350,19 +319,20 @@ Porting to the Arduino IDE can be a little tricky but it is possible.
|
||||
- Go to Boards Manager and install ESP8266 2.4.x platform
|
||||
- Select your ESP8266 from Tools->Boards and the correct port with Tools->Port
|
||||
- From the Library Manager install the needed libraries from platformio.ini
|
||||
- The Arduino IDE doesn't have a common way to set build flags (ugh!) so you'll need to un-comment these lines in `boiler.ino`:
|
||||
|
||||
```c
|
||||
#define WIFI_SSID "<my_ssid>"
|
||||
#define WIFI_PASSWORD "<my_password>"
|
||||
#define MQTT_IP "<broker_ip>"
|
||||
#define MQTT_USER "<broker_username>"
|
||||
#define MQTT_PASS "<broker_password>"
|
||||
```
|
||||
|
||||
- Put all the files in a single sketch folder (`ESPHelper.*, boiler.ino, ems.*, emsuart.*`)
|
||||
- Put all the files in a single sketch folder
|
||||
- cross your fingers and hit CTRL-R to compile...
|
||||
|
||||
## Using the Pre-built Firmware
|
||||
|
||||
pre-baked firmwares for some ESP8266 devices are available in the directory `/firmware` which you can upload yourself using [esptool](https://github.com/espressif/esptool) bootloader. On Windows, follow these instructions:
|
||||
|
||||
1. Check if you have **python 2.7** installed. If not [download it](https://www.python.org/downloads/) and make sure you select the option to add Python to the windows PATH
|
||||
2. Install the ESPTool by running `pip install esptool` from a command prompt
|
||||
3. Connect the ESP via USB, figure out the COM port
|
||||
4. run `esptool.py -p <com> write_flash 0x00000 <firmware>` where firmware is the `.bin` file and \<com\> is the COM port, e.g. `COM3`
|
||||
|
||||
The ESP8266 will start in Access Point (AP) mode, so 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.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the WiFi, MQTT, MDNS or something else fails to connect, re-build the firmware using the `-DDEBUG_SUPPORT` option, 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.
|
||||
@@ -384,6 +354,8 @@ Some annoying issues that need fixing:
|
||||
- https://github.com/robertklep/nefit-easy-core
|
||||
- Store custom params like wifi credentials, mqtt, thermostat type on ESP8266 using SPIFFS
|
||||
- Add support for a temperature sensor on the circuit (DS18B20)
|
||||
- Improve detection of Heating Off without checking for selFlowTemp (selected flow temperature)
|
||||
- Split MQTT into smaller chunks. Now the messages can be up to 600 bytes which may cause issues.
|
||||
|
||||
## Your Comments and Feedback
|
||||
|
||||
|
||||
@@ -25,4 +25,4 @@ env.AddPreAction("buildprog", code_check)
|
||||
|
||||
# see http://docs.platformio.org/en/latest/projectconf/advanced_scripting.html#before-pre-and-after-post-actions
|
||||
# env.Replace(PROGNAME="firmware_%s" % defines.get("VERSION"))
|
||||
# env.Replace(PROGNAME="firmware_%s" % env['BOARD'])
|
||||
env.Replace(PROGNAME="firmware_%s" % env['BOARD'])
|
||||
@@ -2,7 +2,7 @@
|
||||
alias: Alert shower time
|
||||
trigger:
|
||||
platform: mqtt
|
||||
topic: home/boiler/showertime
|
||||
topic: home/ems-esp/showertime
|
||||
action:
|
||||
- service: notify.general_notify
|
||||
data_template:
|
||||
@@ -13,7 +13,7 @@
|
||||
alias: Alert shower too long
|
||||
trigger:
|
||||
platform: mqtt
|
||||
topic: home/boiler/command
|
||||
topic: home/ems-esp/command
|
||||
payload: 'shower_alarm'
|
||||
action:
|
||||
- service: notify.admin_notify
|
||||
@@ -21,21 +21,21 @@
|
||||
title: "Shower Alert!"
|
||||
message: "Shower time exceeded limit"
|
||||
|
||||
# when boiler starts send boottime
|
||||
# when ems-esp starts send boottime
|
||||
- id: boiler_restart
|
||||
alias: See if boiler restarts
|
||||
alias: See if ems-esp restarts
|
||||
trigger:
|
||||
platform: mqtt
|
||||
topic: home/boiler/start
|
||||
topic: home/ems-esp/start
|
||||
payload: 'start'
|
||||
action:
|
||||
- service: notify.admin_notify
|
||||
data_template:
|
||||
title: "boiler has booted"
|
||||
message: "Boiler"
|
||||
title: "ems-esp has booted"
|
||||
message: "EMS-ESP"
|
||||
- service: mqtt.publish
|
||||
data_template:
|
||||
topic: 'home/boiler/start'
|
||||
topic: 'home/ems-esp/start'
|
||||
payload: >
|
||||
{{ now().strftime("%H:%M:%S %-d/%b/%Y") }}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
- platform: mqtt
|
||||
name: 'Tap Water'
|
||||
state_topic: 'home/boiler/tapwater_active'
|
||||
state_topic: 'home/ems-esp/tapwater_active'
|
||||
payload_on: "1"
|
||||
payload_off: "0"
|
||||
|
||||
- platform: mqtt
|
||||
name: 'Heating'
|
||||
state_topic: 'home/boiler/heating_active'
|
||||
state_topic: 'home/ems-esp/heating_active'
|
||||
payload_on: "1"
|
||||
payload_off: "0"
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
- manual
|
||||
- auto
|
||||
|
||||
mode_state_topic: "home/boiler/thermostat_data"
|
||||
current_temperature_topic: "home/boiler/thermostat_data"
|
||||
temperature_state_topic: "home/boiler/thermostat_data"
|
||||
mode_state_topic: "home/ems-esp/thermostat_data"
|
||||
current_temperature_topic: "home/ems-esp/thermostat_data"
|
||||
temperature_state_topic: "home/ems-esp/thermostat_data"
|
||||
|
||||
temperature_command_topic: "home/boiler/thermostat_cmd_temp"
|
||||
mode_command_topic: "home/boiler/thermostat_cmd_mode"
|
||||
temperature_command_topic: "home/ems-esp/thermostat_cmd_temp"
|
||||
mode_command_topic: "home/ems-esp/thermostat_cmd_mode"
|
||||
|
||||
mode_state_template: "{{ value_json.thermostat_mode }}"
|
||||
current_temperature_template: "{{ value_json.thermostat_currtemp }}"
|
||||
|
||||
@@ -2,6 +2,6 @@ shower_coldshot:
|
||||
sequence:
|
||||
- service: mqtt.publish
|
||||
data_template:
|
||||
topic: 'home/boiler/shower_coldshot'
|
||||
topic: 'home/ems-esp/shower_coldshot'
|
||||
payload: '1'
|
||||
|
||||
|
||||
@@ -1,138 +1,138 @@
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/thermostat_data'
|
||||
state_topic: 'home/esp-esp/thermostat_data'
|
||||
name: 'Current Room Temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: "{{ value_json.thermostat_currtemp }}"
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/thermostat_data'
|
||||
state_topic: 'home/esp-esp/thermostat_data'
|
||||
name: 'Current Set Temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: "{{ value_json.thermostat_seltemp }}"
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/thermostat_data'
|
||||
state_topic: 'home/esp-esp/thermostat_data'
|
||||
name: 'Current Mode'
|
||||
value_template: "{{ value_json.thermostat_mode }}"
|
||||
|
||||
# last time boiler was started
|
||||
# last time esp-esp was started
|
||||
- platform: template
|
||||
sensors:
|
||||
boiler_boottime:
|
||||
value_template: '{{ as_timestamp(states.automation.see_if_boiler_restarts.attributes.last_triggered) | timestamp_custom("%H:%M:%S %d/%m/%y") }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/showertime'
|
||||
state_topic: 'home/esp-esp/showertime'
|
||||
name: 'Last shower duration'
|
||||
force_update: true
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Tap Water'
|
||||
value_template: '{{ value_json.tapwaterActive }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Heating'
|
||||
value_template: '{{ value_json.heatingActive }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Warm Water selected temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: '{{ value_json.wWSelTemp }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Warm Water tapwater flow rate'
|
||||
unit_of_measurement: 'l/min'
|
||||
value_template: '{{ value_json.wWCurFlow }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Warm Water current temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: '{{ value_json.wWCurTmp }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Warm Water activated'
|
||||
value_template: '{{ value_json.wWActivated }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Warm Water 3-way valve'
|
||||
value_template: '{{ value_json.wWHeat }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Current flow temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: '{{ value_json.curFlowTemp }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Return temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: '{{ value_json.retTemp }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Gas'
|
||||
value_template: '{{ value_json.burnGas }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Boiler pump'
|
||||
value_template: '{{ value_json.heatPmp }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Fan'
|
||||
value_template: '{{ value_json.fanWork }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Ignition'
|
||||
value_template: '{{ value_json.ignWork }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Circulation pump'
|
||||
value_template: '{{ value_json.wWCirc }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Burner max power'
|
||||
unit_of_measurement: '%'
|
||||
value_template: '{{ value_json.selBurnPow }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Burner max power'
|
||||
unit_of_measurement: '%'
|
||||
value_template: '{{ value_json.selBurnPow }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Burner current power'
|
||||
unit_of_measurement: '%'
|
||||
value_template: '{{ value_json.curBurnPow }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'System Pressure'
|
||||
unit_of_measurement: 'bar'
|
||||
value_template: '{{ value_json.sysPress }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Boiler temperature'
|
||||
unit_of_measurement: '°C'
|
||||
value_template: '{{ value_json.boilTemp }}'
|
||||
|
||||
- platform: mqtt
|
||||
state_topic: 'home/boiler/boiler_data'
|
||||
state_topic: 'home/esp-esp/boiler_data'
|
||||
name: 'Pump modulation'
|
||||
unit_of_measurement: '%'
|
||||
value_template: '{{ value_json.pumpMod }}'
|
||||
@@ -143,7 +143,6 @@
|
||||
value_template: '{{ as_timestamp(states.sensor.last_shower_duration.last_updated) | int | timestamp_custom("%-I:%M %P on %a %-d %b") }}'
|
||||
|
||||
boiler_updated:
|
||||
# value_template: '{{ (as_timestamp(now()) - as_timestamp(states.sensor.boiler_temperature.last_updated)) | int | timestamp_custom("%-M min %-S seconds ago") }}'
|
||||
value_template: '{{ as_timestamp(states.sensor.boiler_temperature.last_updated) | timestamp_custom("%H:%M on %d/%b") }}'
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
- platform: mqtt
|
||||
name: "Shower Timer"
|
||||
state_topic: "home/boiler/shower_timer"
|
||||
command_topic: "home/boiler/shower_timer"
|
||||
state_topic: "home/esp-esp/shower_timer"
|
||||
command_topic: "home/esp-esp/shower_timer"
|
||||
payload_on: "1"
|
||||
payload_off: "0"
|
||||
optimistic: false
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
- platform: mqtt
|
||||
name: "Long Shower Alert"
|
||||
state_topic: "home/boiler/shower_alert"
|
||||
command_topic: "home/boiler/shower_alert"
|
||||
state_topic: "home/esp-esp/shower_alert"
|
||||
command_topic: "home/esp-esp/shower_alert"
|
||||
payload_on: "1"
|
||||
payload_off: "0"
|
||||
optimistic: false
|
||||
|
||||
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
@@ -550,9 +550,14 @@ void TelnetSpy::handle() {
|
||||
return;
|
||||
}
|
||||
if (!listening) {
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
if ((WiFi.status() == WL_DISCONNECTED) && (WiFi.getMode() & WIFI_AP)) {
|
||||
if (usedSer) {
|
||||
usedSer->println("[TELNET] in AP mode"); // added by Proddy
|
||||
}
|
||||
} else if (WiFi.status() != WL_CONNECTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
telnetServer = new WiFiServer(port);
|
||||
telnetServer->begin();
|
||||
telnetServer->setNoDelay(bufLen > 0);
|
||||
|
||||
@@ -158,8 +158,8 @@
|
||||
#define TELNETSPY_PING_TIME 1500
|
||||
#define TELNETSPY_PORT 23
|
||||
#define TELNETSPY_CAPTURE_OS_PRINT true
|
||||
#define TELNETSPY_WELCOME_MSG "Connection established via TelnetSpy2.\n"
|
||||
#define TELNETSPY_REJECT_MSG "TelnetSpy: Only one connection possible.\n"
|
||||
#define TELNETSPY_WELCOME_MSG "Connection established via Telnet.\n"
|
||||
#define TELNETSPY_REJECT_MSG "Telnet: Only one connection possible.\n"
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
632
lib/TelnetSpy/TelnetSpy.xxx
Normal file
632
lib/TelnetSpy/TelnetSpy.xxx
Normal file
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,31 +3,32 @@
|
||||
*
|
||||
* Paul Derbyshire - December 2018
|
||||
*
|
||||
* Some ideas from https://github.com/JoaoLopesF/ESP8266-RemoteDebug-Telnet
|
||||
* Ideas from Espurna https://github.com/xoseperez/espurna
|
||||
* Ideas borrowed from Espurna https://github.com/xoseperez/espurna
|
||||
*/
|
||||
|
||||
#include "MyESP.h"
|
||||
|
||||
// constructor
|
||||
MyESP::MyESP() {
|
||||
_app_hostname = strdup("MyESP");
|
||||
_app_name = strdup("MyESP");
|
||||
_app_version = strdup("1.0.0");
|
||||
_boottime = strdup("unknown");
|
||||
_extern_WIFICallback = NULL;
|
||||
_extern_WIFICallbackSet = false;
|
||||
_consoleCallbackProjectCmds = NULL;
|
||||
_helpProjectCmds = NULL;
|
||||
_helpProjectCmds_count = 0;
|
||||
_mqtt_host = NULL;
|
||||
_mqtt_password = NULL;
|
||||
_mqtt_username = NULL;
|
||||
_wifi_password = NULL;
|
||||
_wifi_ssid = NULL;
|
||||
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
|
||||
_suspendMessages = true;
|
||||
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
|
||||
_app_hostname = strdup("MyESP");
|
||||
_app_name = strdup("MyESP");
|
||||
_app_version = strdup("1.0.0");
|
||||
_boottime = strdup("unknown");
|
||||
_extern_WIFICallback = NULL;
|
||||
_extern_WIFICallbackSet = false;
|
||||
_telnetcommand_callback = NULL;
|
||||
_telnet_callback = NULL;
|
||||
_helpProjectCmds = NULL;
|
||||
_helpProjectCmds_count = 0;
|
||||
_mqtt_host = NULL;
|
||||
_mqtt_password = NULL;
|
||||
_mqtt_username = NULL;
|
||||
_wifi_password = NULL;
|
||||
_wifi_ssid = NULL;
|
||||
_mqttbase = NULL;
|
||||
_suspendOutput = false;
|
||||
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
|
||||
_command = (char *)malloc(TELNET_MAX_COMMAND_LENGTH); // reserve buffer for Serial/Telnet commands
|
||||
}
|
||||
|
||||
MyESP::~MyESP() {
|
||||
@@ -41,10 +42,9 @@ void MyESP::end() {
|
||||
jw.disconnect();
|
||||
}
|
||||
|
||||
|
||||
// general debug to the telnet or serial channels
|
||||
void MyESP::myDebug(const char * format, ...) {
|
||||
if (!_suspendMessages)
|
||||
if (_suspendOutput)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
@@ -62,6 +62,9 @@ void MyESP::myDebug(const char * format, ...) {
|
||||
|
||||
// for flashmemory. Must use PSTR()
|
||||
void MyESP::myDebug_P(PGM_P format_P, ...) {
|
||||
if (_suspendOutput)
|
||||
return;
|
||||
|
||||
char format[strlen_P(format_P) + 1];
|
||||
memcpy_P(format, format_P, sizeof(format));
|
||||
|
||||
@@ -85,7 +88,7 @@ void MyESP::myDebug_P(PGM_P format_P, ...) {
|
||||
|
||||
// called when WiFi is connected, and used to start MDNS
|
||||
void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
if ((code == MESSAGE_CONNECTED) || (code == MESSAGE_ACCESSPOINT_CREATED)) {
|
||||
if ((code == MESSAGE_CONNECTED)) {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
String hostname = String(WiFi.getHostname());
|
||||
#else
|
||||
@@ -102,13 +105,6 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
myDebug_P(PSTR("[WIFI] DNS %s"), WiFi.dnsIP().toString().c_str());
|
||||
myDebug_P(PSTR("[WIFI] HOST %s"), hostname.c_str());
|
||||
|
||||
if (WiFi.getMode() & WIFI_AP) {
|
||||
myDebug_P(PSTR("[WIFI] MODE AP --------------------------------------"));
|
||||
myDebug_P(PSTR("[WIFI] SSID %s"), jw.getAPSSID().c_str());
|
||||
myDebug_P(PSTR("[WIFI] IP %s"), WiFi.softAPIP().toString().c_str());
|
||||
myDebug_P(PSTR("[WIFI] MAC %s"), WiFi.softAPmacAddress().c_str());
|
||||
}
|
||||
|
||||
// start MDNS
|
||||
if (MDNS.begin((char *)hostname.c_str())) {
|
||||
myDebug_P(PSTR("[MDNS] OK"));
|
||||
@@ -118,7 +114,18 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
|
||||
// call any final custom settings
|
||||
if (_extern_WIFICallbackSet) {
|
||||
// myDebug_P(PSTR("[WIFI] calling custom wifi settings function"));
|
||||
_extern_WIFICallback(); // call callback to set any custom things
|
||||
}
|
||||
}
|
||||
|
||||
if (code == MESSAGE_ACCESSPOINT_CREATED) {
|
||||
myDebug_P(PSTR("[WIFI] MODE AP --------------------------------------"));
|
||||
myDebug_P(PSTR("[WIFI] SSID %s"), jw.getAPSSID().c_str());
|
||||
myDebug_P(PSTR("[WIFI] IP %s"), WiFi.softAPIP().toString().c_str());
|
||||
myDebug_P(PSTR("[WIFI] MAC %s"), WiFi.softAPmacAddress().c_str());
|
||||
|
||||
// call any final custom settings
|
||||
if (_extern_WIFICallbackSet) {
|
||||
_extern_WIFICallback(); // call callback to set any custom things
|
||||
}
|
||||
}
|
||||
@@ -160,9 +167,9 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
||||
topic = topic_magnitude + 1;
|
||||
}
|
||||
|
||||
// check for bootime, something specific I fetch as an acknolwegdemtn from Home Assistant
|
||||
// check for bootime, something specific I fetch as an acknowledgement from Home Assistant
|
||||
if (strcmp(topic, MQTT_TOPIC_START) == 0) {
|
||||
myDebug_P(PSTR("[MQTT] boottime: %s"), message);
|
||||
myDebug_P(PSTR("[MQTT] received boottime: %s"), message);
|
||||
setBoottime(message);
|
||||
return;
|
||||
}
|
||||
@@ -172,20 +179,22 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
||||
}
|
||||
|
||||
// MQTT subscribe
|
||||
// to MQTT_BASE/app_hostname/topic
|
||||
void MyESP::mqttSubscribe(const char * topic) {
|
||||
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
||||
char s[100];
|
||||
snprintf(s, sizeof(s), "%s%s/%s", MQTT_BASE, _app_hostname, topic);
|
||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, topic);
|
||||
unsigned int packetId = mqttClient.subscribe(s, MQTT_QOS);
|
||||
myDebug_P(PSTR("[MQTT] Subscribing to %s (PID %d)"), s, packetId);
|
||||
}
|
||||
}
|
||||
|
||||
// MQTT unsubscribe
|
||||
// to MQTT_BASE/app_hostname/topic
|
||||
void MyESP::mqttUnsubscribe(const char * topic) {
|
||||
if (mqttClient.connected() && (strlen(topic) > 0)) {
|
||||
char s[100];
|
||||
snprintf(s, sizeof(s), "%s%s/%s", MQTT_BASE, _app_hostname, topic);
|
||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, topic);
|
||||
unsigned int packetId = mqttClient.unsubscribe(s);
|
||||
myDebug_P(PSTR("[MQTT] Unsubscribing to %s (PID %d)"), s, packetId);
|
||||
}
|
||||
@@ -194,7 +203,7 @@ void MyESP::mqttUnsubscribe(const char * topic) {
|
||||
// MQTT Publish
|
||||
void MyESP::mqttPublish(const char * topic, const char * payload) {
|
||||
char s[MQTT_MAX_SIZE];
|
||||
snprintf(s, sizeof(s), "%s%s/%s", MQTT_BASE, _app_hostname, topic);
|
||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, topic);
|
||||
// myDebug_P(PSTR("[MQTT] Sending pubish to %s with payload %s"), s, payload);
|
||||
mqttClient.publish(s, MQTT_QOS, false, payload);
|
||||
}
|
||||
@@ -211,11 +220,11 @@ void MyESP::_mqttOnConnect() {
|
||||
|
||||
// send specific start command to HA via MQTT, which returns the boottime
|
||||
char s[48];
|
||||
snprintf(s, sizeof(s), "%s%s/%s", MQTT_BASE, _app_hostname, MQTT_TOPIC_START);
|
||||
snprintf(s, sizeof(s), "%s/%s/%s", _mqttbase, _app_hostname, MQTT_TOPIC_START);
|
||||
mqttClient.publish(s, MQTT_QOS, false, MQTT_TOPIC_START_PAYLOAD);
|
||||
#endif
|
||||
|
||||
// call custom
|
||||
// call custom function to handle mqtt receives
|
||||
(_mqtt_callback)(MQTT_CONNECT_EVENT, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -265,7 +274,7 @@ void MyESP::_wifi_setup() {
|
||||
jw.enableAP(false);
|
||||
jw.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
|
||||
jw.setReconnectTimeout(WIFI_RECONNECT_INTERVAL);
|
||||
jw.enableAPFallback(true); // AP mode only as fallback, but disabled
|
||||
jw.enableAPFallback(true); // AP mode only as fallback
|
||||
jw.enableSTA(true); // Enable STA mode (connecting to a router)
|
||||
jw.enableScan(false); // Configure it to scan available networks and connect in order of dBm
|
||||
jw.cleanNetworks(); // Clean existing network configuration
|
||||
@@ -299,6 +308,10 @@ void MyESP::_mdns_setup() {
|
||||
|
||||
// OTA Setup
|
||||
void MyESP::_ota_setup() {
|
||||
if (!_wifi_ssid) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArduinoOTA.setPort(OTA_PORT);
|
||||
ArduinoOTA.setHostname(_app_hostname);
|
||||
ArduinoOTA.onStart([this]() { myDebug_P(PSTR("[OTA] Start")); });
|
||||
@@ -336,25 +349,38 @@ void MyESP::setBoottime(char * boottime) {
|
||||
_boottime = strdup(boottime);
|
||||
}
|
||||
|
||||
// returns boottime
|
||||
char * MyESP::getBoottime() {
|
||||
return _boottime;
|
||||
// sets boottime
|
||||
void MyESP::setMQTTbase(char * mqttbase) {
|
||||
if (_mqttbase) {
|
||||
free(_mqttbase);
|
||||
}
|
||||
_mqttbase = strdup(mqttbase);
|
||||
}
|
||||
|
||||
// Set callback of sketch function to process project messages
|
||||
void MyESP::consoleSetCallBackProjectCmds(command_t * cmds, uint8_t count, void (*callback)()) {
|
||||
_helpProjectCmds = cmds; // command list
|
||||
_helpProjectCmds_count = count; // number of commands
|
||||
_consoleCallbackProjectCmds = callback; // external function to handle commands
|
||||
void MyESP::setTelnetCommands(command_t * cmds, uint8_t count, telnetcommand_callback_f callback) {
|
||||
_helpProjectCmds = cmds; // command list
|
||||
_helpProjectCmds_count = count; // number of commands
|
||||
_telnetcommand_callback = callback; // external function to handle commands
|
||||
}
|
||||
|
||||
void MyESP::setTelnetCallback(telnet_callback_f callback) {
|
||||
_telnet_callback = callback;
|
||||
}
|
||||
|
||||
void MyESP::_telnetConnected() {
|
||||
myDebug_P(PSTR("[TELNET] Telnet connection established"));
|
||||
_consoleShowHelp(); // Show the initial message
|
||||
if (_telnet_callback) {
|
||||
(_telnet_callback)(TELNET_EVENT_CONNECT); // call callback
|
||||
}
|
||||
}
|
||||
|
||||
void MyESP::_telnetDisconnected() {
|
||||
myDebug_P(PSTR("[TELNET] Telnet connection closed"));
|
||||
if (_telnet_callback) {
|
||||
(_telnet_callback)(TELNET_EVENT_DISCONNECT); // call callback
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the telnet server
|
||||
@@ -375,37 +401,48 @@ void MyESP::_telnet_setup() {
|
||||
|
||||
// Show help of commands
|
||||
void MyESP::_consoleShowHelp() {
|
||||
SerialAndTelnet.printf("\n\r* Connected to: %s version %s\n\r", _app_name, _app_version);
|
||||
|
||||
if (WiFi.getMode() & WIFI_AP) {
|
||||
SerialAndTelnet.printf("* ESP8266 is in AP mode with SSID %s\n\r", jw.getAPSSID().c_str());
|
||||
} else {
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
String hostname = String(WiFi.getHostname());
|
||||
String hostname = String(WiFi.getHostname());
|
||||
#else
|
||||
String hostname = WiFi.hostname();
|
||||
String hostname = WiFi.hostname();
|
||||
#endif
|
||||
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s\n\r",
|
||||
hostname.c_str(),
|
||||
WiFi.localIP().toString().c_str(),
|
||||
WiFi.macAddress().c_str());
|
||||
SerialAndTelnet.printf("* Connected to WiFi SSID: %s\n\r", WiFi.SSID().c_str());
|
||||
SerialAndTelnet.printf("* Boot time: %s\n\r", _boottime);
|
||||
}
|
||||
|
||||
|
||||
SerialAndTelnet.println("*********************************");
|
||||
SerialAndTelnet.println("* Console and Log Monitoring *");
|
||||
SerialAndTelnet.println("*********************************");
|
||||
SerialAndTelnet.printf("* %s version %s\n\r", _app_name, _app_version);
|
||||
SerialAndTelnet.printf("* Hostname: %s IP: %s MAC: %s\n\r",
|
||||
hostname.c_str(),
|
||||
WiFi.localIP().toString().c_str(),
|
||||
WiFi.macAddress().c_str());
|
||||
SerialAndTelnet.printf("* Connected to WiFi AP: %s\n\r", WiFi.SSID().c_str());
|
||||
SerialAndTelnet.printf("* Boot time: %s\n\r", _boottime);
|
||||
SerialAndTelnet.printf("* Free RAM: %d bytes\n\r", ESP.getFreeHeap());
|
||||
#ifdef DEBUG_SUPPORT
|
||||
SerialAndTelnet.println("* !! in DEBUG_SUPPORT mode !!\n\r");
|
||||
SerialAndTelnet.println("* Warning: in DEBUG_SUPPORT mode!");
|
||||
#endif
|
||||
|
||||
SerialAndTelnet.println("*\n\r* Commands:\n\r* ?=this help, CTRL-D=quit, $=show free memory, !=reboot ESP, &=suspend all messages");
|
||||
SerialAndTelnet.println("*\n\r* Commands:\n\r* ?=help, CTRL-D=quit, !=reboot");
|
||||
SerialAndTelnet.println(FPSTR("* set <wifi_ssid | wifi_password | mqtt_host | mqtt_username | mqtt_password> [value]"));
|
||||
SerialAndTelnet.println(FPSTR("* set erase"));
|
||||
SerialAndTelnet.println(FPSTR("*"));
|
||||
|
||||
// print custom commands if available. Take from progmem
|
||||
if (_consoleCallbackProjectCmds) {
|
||||
if (_telnetcommand_callback) {
|
||||
// find the longest key length so we can right align it
|
||||
uint8_t max_len = 0;
|
||||
for (uint8_t i = 0; i < _helpProjectCmds_count; i++) {
|
||||
if (strlen(_helpProjectCmds[i].key) > max_len)
|
||||
max_len = strlen(_helpProjectCmds[i].key);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < _helpProjectCmds_count; i++) {
|
||||
SerialAndTelnet.print(FPSTR("* "));
|
||||
SerialAndTelnet.print(FPSTR(_helpProjectCmds[i].key));
|
||||
for (uint8_t j = 0; j < (8 - strlen(_helpProjectCmds[i].key)); j++) {
|
||||
SerialAndTelnet.print(FPSTR(" ")); // padding
|
||||
for (uint8_t j = 0; j < ((max_len + 5) - strlen(_helpProjectCmds[i].key)); j++) { // account for longest string length
|
||||
SerialAndTelnet.print(FPSTR(" ")); // padding
|
||||
}
|
||||
SerialAndTelnet.println(FPSTR(_helpProjectCmds[i].description));
|
||||
}
|
||||
@@ -424,90 +461,207 @@ void MyESP::resetESP() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get last command received
|
||||
char * MyESP::consoleGetLastCommand() {
|
||||
return _command;
|
||||
}
|
||||
|
||||
// Process user command over telnet
|
||||
void MyESP::consoleProcessCommand() {
|
||||
uint8_t cmd = _command[0];
|
||||
|
||||
// Process the command
|
||||
if (cmd == '?') {
|
||||
_consoleShowHelp(); // Show help
|
||||
} else if (cmd == '$') {
|
||||
myDebug("* Free RAM (bytes): %d", ESP.getFreeHeap());
|
||||
} else if (cmd == '!') {
|
||||
resetESP();
|
||||
} else if (cmd == '&') {
|
||||
myDebug("Suspend all messages is %s", !_suspendMessages ? "disabled" : "enabled");
|
||||
_suspendMessages = !_suspendMessages; // toggle
|
||||
} else {
|
||||
// custom Project commands
|
||||
if (_consoleCallbackProjectCmds) {
|
||||
_consoleCallbackProjectCmds();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_suspendMessages) {
|
||||
myDebug("Warning, all log messages have been supsended. Use & to re-enable.");
|
||||
}
|
||||
}
|
||||
|
||||
// sends a MQTT notification message to Home Assistant
|
||||
// sends a MQTT notification message to Home Assistant (HA)
|
||||
void MyESP::sendHANotification(const char * message) {
|
||||
char payload[48];
|
||||
snprintf(payload, sizeof(payload), "%s : %s", _app_hostname, message);
|
||||
myDebug_P(PSTR("[MQTT] Sending HA notification %s"), payload);
|
||||
mqttClient.publish(MQTT_NOTIFICATION, MQTT_QOS, false, payload);
|
||||
mqttClient.publish(MQTT_HA_NOTIFICATION, MQTT_QOS, false, payload);
|
||||
}
|
||||
|
||||
// send specific command to HA via MQTT
|
||||
// send specific command to Home Assistant (HA) via MQTT
|
||||
// format is: home/<hostname>/command with payload <cmd>
|
||||
void MyESP::sendHACommand(const char * cmd) {
|
||||
myDebug_P(PSTR("[MQTT] Sending HA command %s"), cmd);
|
||||
char topic[48];
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", MQTT_BASE, _app_hostname, MQTT_TOPIC_COMMAND);
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", _mqttbase, _app_hostname, MQTT_TOPIC_COMMAND);
|
||||
mqttClient.publish(topic, MQTT_QOS, false, cmd);
|
||||
}
|
||||
|
||||
char * MyESP::_telnet_readWord() {
|
||||
char * word = strtok(NULL, ", \n");
|
||||
return word;
|
||||
}
|
||||
|
||||
// change settings - always as strings
|
||||
// messy code but effective since we don't have too many settings
|
||||
void MyESP::_changeSetting(const char * setting, const char * value) {
|
||||
bool ok = false;
|
||||
|
||||
// validate 2nd argument
|
||||
if (strcmp(setting, "erase") == 0) {
|
||||
_fs_eraseConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "wifi_ssid") == 0) {
|
||||
if (_wifi_ssid)
|
||||
free(_wifi_ssid);
|
||||
_wifi_ssid = NULL; // just to be sure
|
||||
if (value) {
|
||||
_wifi_ssid = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "wifi_password") == 0) {
|
||||
if (_wifi_password)
|
||||
free(_wifi_password);
|
||||
_wifi_password = NULL; // just to be sure
|
||||
if (value) {
|
||||
_wifi_password = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "mqtt_host") == 0) {
|
||||
if (_mqtt_host)
|
||||
free(_mqtt_host);
|
||||
_mqtt_host = NULL; // just to be sure
|
||||
if (value) {
|
||||
_mqtt_host = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "mqtt_username") == 0) {
|
||||
if (_mqtt_username)
|
||||
free(_mqtt_username);
|
||||
_mqtt_username = NULL; // just to be sure
|
||||
if (value) {
|
||||
_mqtt_username = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(setting, "mqtt_password") == 0) {
|
||||
if (_mqtt_password)
|
||||
free(_mqtt_password);
|
||||
_mqtt_password = NULL; // just to be sure
|
||||
if (value) {
|
||||
_mqtt_password = strdup(value);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
SerialAndTelnet.println("\nInvalid parameter for set command.");
|
||||
return;
|
||||
}
|
||||
|
||||
// check for 2 params
|
||||
if (value == nullptr) {
|
||||
SerialAndTelnet.printf("%s setting deleted\n\r", setting);
|
||||
} else {
|
||||
// 3 params
|
||||
SerialAndTelnet.printf("%s changed to %s\n\r", setting, value);
|
||||
}
|
||||
|
||||
if (_fs_saveConfig()) {
|
||||
SerialAndTelnet.println("Changes will have effect after the next restart. Please reboot using ! command");
|
||||
}
|
||||
}
|
||||
|
||||
void MyESP::_telnetCommand(char * commandLine) {
|
||||
// count the number of arguments
|
||||
char * str = commandLine;
|
||||
bool state = false;
|
||||
unsigned wc = 0;
|
||||
while (*str) {
|
||||
if (*str == ' ' || *str == '\n' || *str == '\t') {
|
||||
state = false;
|
||||
} else if (state == false) {
|
||||
state = true;
|
||||
++wc;
|
||||
}
|
||||
++str;
|
||||
}
|
||||
|
||||
// check first for reserved commands
|
||||
char * temp = strdup(commandLine); // because strotok kills original string buffer
|
||||
char * ptrToCommandName = strtok((char *)temp, ", \n");
|
||||
if (strcmp(ptrToCommandName, "set") == 0) {
|
||||
if (wc == 1) {
|
||||
SerialAndTelnet.println("\n\Stored settings:");
|
||||
SerialAndTelnet.printf(" wifi_ssid=%s\n\r", (!_wifi_ssid) ? "<not set>" : _wifi_ssid);
|
||||
SerialAndTelnet.printf(" wifi_password=");
|
||||
if (!_wifi_password) {
|
||||
SerialAndTelnet.print("<not set>");
|
||||
} else {
|
||||
for (uint8_t i = 0; i < strlen(_wifi_password); i++)
|
||||
SerialAndTelnet.print("*");
|
||||
}
|
||||
SerialAndTelnet.printf("\n\r mqtt_host=%s\n\r", (!_mqtt_host) ? "<not set>" : _mqtt_host);
|
||||
SerialAndTelnet.printf(" mqtt_username=%s\n\r", (!_mqtt_username) ? "<not set>" : _mqtt_username);
|
||||
SerialAndTelnet.printf(" mqtt_password=");
|
||||
if (!_mqtt_password) {
|
||||
SerialAndTelnet.print("<not set>");
|
||||
} else {
|
||||
for (uint8_t i = 0; i < strlen(_mqtt_password); i++)
|
||||
SerialAndTelnet.print("*");
|
||||
}
|
||||
SerialAndTelnet.println("\n\r\n\rUsage: set <setting> <value>");
|
||||
} else if (wc == 2) {
|
||||
char * setting = _telnet_readWord();
|
||||
_changeSetting(setting, NULL);
|
||||
} else if (wc == 3) {
|
||||
char * setting = _telnet_readWord();
|
||||
char * value = _telnet_readWord();
|
||||
_changeSetting(setting, value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// call callback function
|
||||
(_telnetcommand_callback)(wc, commandLine);
|
||||
}
|
||||
|
||||
// handler for Telnet
|
||||
void MyESP::_telnetHandle() {
|
||||
SerialAndTelnet.handle();
|
||||
|
||||
char last = ' '; // To avoid processing double "\r\n"
|
||||
|
||||
static uint8_t charsRead = 0;
|
||||
// read asynchronously until full command input
|
||||
while (SerialAndTelnet.available()) {
|
||||
char character = SerialAndTelnet.read(); // Get character
|
||||
|
||||
// check for ctrl-D (EOF) or EOT
|
||||
if ((character == 0xEC) || (character == 0x04)) {
|
||||
SerialAndTelnet.disconnectClient();
|
||||
}
|
||||
|
||||
// if we reached our buffer limit, send what we have
|
||||
if (strlen(_command) >= TELNET_MAX_COMMAND_LENGTH) {
|
||||
consoleProcessCommand(); // Process the command
|
||||
memset(_command, 0, TELNET_MAX_COMMAND_LENGTH); // reset for next command
|
||||
}
|
||||
|
||||
// Check for newline (CR or LF)
|
||||
if (_isCRLF(character) == true) {
|
||||
if (_isCRLF(last) == false) {
|
||||
if (strlen(_command) > 0) {
|
||||
consoleProcessCommand(); // Process the command
|
||||
}
|
||||
char c = SerialAndTelnet.read();
|
||||
#ifdef DEBUG_SUPPORT
|
||||
Serial.print(c);
|
||||
#endif
|
||||
switch (c) {
|
||||
case '\r': // likely have full command in buffer now, commands are terminated by CR and/or LF
|
||||
case '\n':
|
||||
_command[charsRead] = '\0'; // null terminate our command char array
|
||||
if (charsRead > 0) {
|
||||
charsRead = 0; // is static, so have to reset
|
||||
_suspendOutput = false;
|
||||
_telnetCommand(_command);
|
||||
}
|
||||
memset(_command, 0, TELNET_MAX_COMMAND_LENGTH); // reset for next command
|
||||
|
||||
} else if (isPrintable(character)) {
|
||||
// Concat char to end of buffer
|
||||
uint16_t len = strlen(_command);
|
||||
_command[len] = character;
|
||||
_command[len + 1] = '\0';
|
||||
break;
|
||||
case '\b': // handle backspace in input: put a space in last char
|
||||
if (charsRead > 0) { // and adjust commandLine and charsRead
|
||||
_command[--charsRead] = '\0';
|
||||
SerialAndTelnet << byte('\b') << byte(' ') << byte('\b'); //no idea how this works, found it on the Internet
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
_consoleShowHelp();
|
||||
break;
|
||||
case '!':
|
||||
resetESP();
|
||||
break;
|
||||
case 0x04: // EOT
|
||||
myDebug_P(PSTR("* exiting telnet session"));
|
||||
SerialAndTelnet.disconnectClient();
|
||||
break;
|
||||
default:
|
||||
_suspendOutput = true;
|
||||
c = tolower(c);
|
||||
if (charsRead < TELNET_MAX_COMMAND_LENGTH) {
|
||||
_command[charsRead++] = c;
|
||||
}
|
||||
_command[charsRead] = '\0'; // just in case
|
||||
break;
|
||||
}
|
||||
last = character; // remember last char
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,11 +676,6 @@ void MyESP::setMQTTCallback(mqtt_callback_f callback) {
|
||||
_mqtt_callback = callback;
|
||||
}
|
||||
|
||||
// Is CR or LF ?
|
||||
bool MyESP::_isCRLF(char character) {
|
||||
return (character == '\r' || character == '\n');
|
||||
}
|
||||
|
||||
// ensure we have a connection to MQTT broker
|
||||
void MyESP::_mqttConnect() {
|
||||
if (!_mqtt_host || mqttClient.connected() || (WiFi.status() != WL_CONNECTED)) {
|
||||
@@ -560,19 +709,7 @@ void MyESP::_mqttConnect() {
|
||||
}
|
||||
|
||||
// Setup everything we need
|
||||
void MyESP::setup(char * app_hostname,
|
||||
char * app_name,
|
||||
char * app_version,
|
||||
char * wifi_ssid,
|
||||
char * wifi_password,
|
||||
char * mqtt_host,
|
||||
char * mqtt_username,
|
||||
char * mqtt_password) {
|
||||
// get general params first
|
||||
_app_hostname = strdup(app_hostname);
|
||||
_app_name = strdup(app_name);
|
||||
_app_version = strdup(app_version);
|
||||
|
||||
void MyESP::setConnection(char * wifi_ssid, char * wifi_password, char * mqtt_host, char * mqtt_username, char * mqtt_password) {
|
||||
// Check SSID too long or missing
|
||||
if (!wifi_ssid || *wifi_ssid == 0x00 || strlen(wifi_ssid) > 31) {
|
||||
_wifi_ssid = NULL;
|
||||
@@ -581,7 +718,7 @@ void MyESP::setup(char * app_hostname,
|
||||
}
|
||||
|
||||
// Check PASS too long
|
||||
if (wifi_password && strlen(wifi_password) > 63) {
|
||||
if (!wifi_password || *wifi_ssid == 0x00 || strlen(wifi_password) > 31) {
|
||||
_wifi_password = NULL;
|
||||
} else {
|
||||
_wifi_password = strdup(wifi_password);
|
||||
@@ -607,9 +744,120 @@ void MyESP::setup(char * app_hostname,
|
||||
} else {
|
||||
_mqtt_password = strdup(mqtt_password);
|
||||
}
|
||||
}
|
||||
|
||||
// print contents of file
|
||||
void MyESP::_fs_printConfig() {
|
||||
File configFile = SPIFFS.open("/config.json", "r");
|
||||
|
||||
myDebug_P(PSTR("[FS] Contents...."));
|
||||
|
||||
while (configFile.available()) {
|
||||
SerialAndTelnet.print((char)configFile.read());
|
||||
}
|
||||
SerialAndTelnet.println();
|
||||
configFile.close();
|
||||
}
|
||||
|
||||
// format File System
|
||||
void MyESP::_fs_eraseConfig() {
|
||||
myDebug_P(PSTR("[FS] Erasing settings. Please wait. ESP will automatically restart when finished."));
|
||||
|
||||
if (SPIFFS.format()) {
|
||||
resetESP();
|
||||
}
|
||||
}
|
||||
|
||||
// load from spiffs
|
||||
bool MyESP::_fs_loadConfig() {
|
||||
File configFile = SPIFFS.open("/config.json", "r");
|
||||
if (!configFile) {
|
||||
myDebug_P(PSTR("[FS] Failed to open config file"));
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size = configFile.size();
|
||||
if (size > 1024) {
|
||||
myDebug_P(PSTR("[FS] Config file size is too large"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// assign buffer
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
|
||||
// use configFile.readString
|
||||
configFile.readBytes(buf.get(), size);
|
||||
|
||||
StaticJsonBuffer<300> jsonBuffer; // https://arduinojson.org/v5/assistant/
|
||||
JsonObject & json = jsonBuffer.parseObject(buf.get());
|
||||
|
||||
const char * value;
|
||||
|
||||
value = json["wifi_ssid"];
|
||||
_wifi_ssid = (value) ? strdup(value) : NULL;
|
||||
|
||||
value = json["wifi_password"];
|
||||
_wifi_password = (value) ? strdup(value) : NULL;
|
||||
|
||||
value = json["mqtt_host"];
|
||||
_mqtt_host = (value) ? strdup(value) : NULL;
|
||||
|
||||
value = json["mqtt_username"];
|
||||
_mqtt_username = (value) ? strdup(value) : NULL;
|
||||
|
||||
value = json["mqtt_password"];
|
||||
_mqtt_password = (value) ? strdup(value) : NULL;
|
||||
|
||||
configFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// save settings to spiffs
|
||||
bool MyESP::_fs_saveConfig() {
|
||||
StaticJsonBuffer<200> jsonBuffer;
|
||||
JsonObject & json = jsonBuffer.createObject();
|
||||
|
||||
json["wifi_ssid"] = _wifi_ssid;
|
||||
json["wifi_password"] = _wifi_password;
|
||||
json["mqtt_host"] = _mqtt_host;
|
||||
json["mqtt_username"] = _mqtt_username;
|
||||
json["mqtt_password"] = _mqtt_password;
|
||||
|
||||
File configFile = SPIFFS.open("/config.json", "w");
|
||||
if (!configFile) {
|
||||
myDebug_P(PSTR("[FS] Failed to open config file for writing"));
|
||||
return false;
|
||||
}
|
||||
|
||||
json.printTo(configFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// init the SPIFF file system and load the config
|
||||
// if it doesn't exist try and create it
|
||||
void MyESP::_fs_setup() {
|
||||
if (!SPIFFS.begin()) {
|
||||
myDebug_P(PSTR("[FS] Failed to mount the file system"));
|
||||
return;
|
||||
}
|
||||
|
||||
// load the config file. if it doesn't exist create it with anything that was specified
|
||||
if (!_fs_loadConfig()) {
|
||||
_fs_saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// register new instance
|
||||
void MyESP::begin(char * app_hostname, char * app_name, char * app_version) {
|
||||
_app_hostname = strdup(app_hostname);
|
||||
_app_name = strdup(app_name);
|
||||
_app_version = strdup(app_version);
|
||||
|
||||
// call setup of the services...
|
||||
_telnet_setup(); // Telnet setup
|
||||
_fs_setup(); // SPIFFS setup
|
||||
_wifi_setup(); // WIFI setup
|
||||
_mqtt_setup(); // MQTT Setup
|
||||
_mdns_setup(); // MDNS setup
|
||||
@@ -620,8 +868,13 @@ void MyESP::setup(char * app_hostname,
|
||||
* Loop. This is called as often as possible and it handles wifi, telnet, mqtt etc
|
||||
*/
|
||||
void MyESP::loop() {
|
||||
jw.loop(); // WiFi
|
||||
_telnetHandle(); // Telnet/Debugger
|
||||
jw.loop(); // WiFi
|
||||
_telnetHandle(); // Telnet/Debugger
|
||||
|
||||
if (WiFi.getMode() & WIFI_AP) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArduinoOTA.handle(); // OTA
|
||||
_mqttConnect(); // MQTT
|
||||
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
#include <AsyncMqttClient.h> // https://github.com/marvinroger/async-mqtt-client
|
||||
#include <DNSServer.h>
|
||||
#include <ESPAsyncTCP.h> // https://github.com/me-no-dev/ESPAsyncTCP
|
||||
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
||||
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
||||
#include <FS.h>
|
||||
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
||||
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
||||
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
#include <ESPmDNS.h>
|
||||
@@ -31,12 +33,12 @@
|
||||
#define OTA_PORT 8266 // OTA port
|
||||
|
||||
// MQTT
|
||||
#define MQTT_BASE "home/"
|
||||
#define MQTT_NOTIFICATION MQTT_BASE "notification"
|
||||
#define MQTT_TOPIC_COMMAND "command"
|
||||
#define MQTT_TOPIC_START "start"
|
||||
#define MQTT_TOPIC_START_PAYLOAD "start"
|
||||
#define MQTT_HA MQTT_BASE "ha"
|
||||
#define MQTT_HA "/home/ha" // HA specific
|
||||
#define MQTT_HA_NOTIFICATION "home/notification" // HA specific
|
||||
#define MQTT_TOPIC_COMMAND "command" // HA specific
|
||||
#define MQTT_TOPIC_START "start" // HA specific
|
||||
#define MQTT_TOPIC_START_PAYLOAD "start" // HA specific
|
||||
|
||||
#define MQTT_PORT 1883 // MQTT port
|
||||
#define MQTT_QOS 1
|
||||
#define MQTT_RECONNECT_DELAY_MIN 5000 // Try to reconnect in 5 seconds upon disconnection
|
||||
@@ -50,6 +52,8 @@
|
||||
|
||||
// Telnet
|
||||
#define TELNET_MAX_COMMAND_LENGTH 80 // length of a command
|
||||
#define TELNET_EVENT_CONNECT 1
|
||||
#define TELNET_EVENT_DISCONNECT 0
|
||||
#define COLOR_RESET "\x1B[0m"
|
||||
#define COLOR_BLACK "\x1B[0;30m"
|
||||
#define COLOR_RED "\x1B[0;31m"
|
||||
@@ -61,12 +65,16 @@
|
||||
#define COLOR_WHITE "\x1B[0;37m"
|
||||
|
||||
typedef struct {
|
||||
char key[10];
|
||||
char description[400];
|
||||
char key[30];
|
||||
char description[100];
|
||||
} command_t;
|
||||
|
||||
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callback_f;
|
||||
|
||||
typedef std::function<void(uint8_t, const char *)> telnetcommand_callback_f;
|
||||
|
||||
typedef std::function<void(uint8_t)> telnet_callback_f;
|
||||
|
||||
// calculates size of an 2d array at compile time
|
||||
template <typename T, size_t N>
|
||||
constexpr size_t ArraySize(T (&)[N]) {
|
||||
@@ -81,7 +89,6 @@ class MyESP {
|
||||
|
||||
// wifi
|
||||
void setWIFICallback(void (*callback)());
|
||||
void setMQTTCallback(mqtt_callback_f callback);
|
||||
|
||||
// ha
|
||||
void sendHACommand(const char * cmd);
|
||||
@@ -91,28 +98,22 @@ class MyESP {
|
||||
void mqttSubscribe(const char * topic);
|
||||
void mqttUnsubscribe(const char * topic);
|
||||
void mqttPublish(const char * topic, const char * payload);
|
||||
void setMQTTbase(char * mqttbase);
|
||||
void setMQTTCallback(mqtt_callback_f callback);
|
||||
|
||||
// debug & telnet
|
||||
void myDebug(const char * format, ...);
|
||||
void myDebug_P(PGM_P format_P, ...);
|
||||
void consoleSetCallBackProjectCmds(command_t * cmds, uint8_t count, void (*callback)());
|
||||
char * consoleGetLastCommand();
|
||||
void consoleProcessCommand();
|
||||
void myDebug(const char * format, ...);
|
||||
void myDebug_P(PGM_P format_P, ...);
|
||||
void setTelnetCommands(command_t * cmds, uint8_t count, telnetcommand_callback_f callback);
|
||||
void setTelnetCallback(telnet_callback_f callback);
|
||||
|
||||
// general
|
||||
void end();
|
||||
void loop();
|
||||
void setup(char * app_hostname,
|
||||
char * app_name,
|
||||
char * app_version,
|
||||
char * wifi_ssid,
|
||||
char * wifi_password,
|
||||
char * mqtt_host,
|
||||
char * mqtt_username,
|
||||
char * mqtt_password);
|
||||
|
||||
char * getBoottime();
|
||||
void setBoottime(char * boottime);
|
||||
void resetESP();
|
||||
void begin(char * app_hostname, char * app_name, char * app_version);
|
||||
void setConnection(char * wifi_ssid, char * wifi_password, char * mqtt_host, char * mqtt_username, char * mqtt_password);
|
||||
void setBoottime(char * boottime);
|
||||
void resetESP();
|
||||
|
||||
private:
|
||||
// mqtt
|
||||
@@ -128,6 +129,8 @@ class MyESP {
|
||||
char * _mqtt_username;
|
||||
char * _mqtt_password;
|
||||
char * _boottime;
|
||||
bool _suspendOutput;
|
||||
char * _mqttbase;
|
||||
|
||||
// wifi
|
||||
DNSServer dnsServer; // For Access Point (AP) support
|
||||
@@ -145,19 +148,27 @@ class MyESP {
|
||||
void _ota_setup();
|
||||
|
||||
// telnet & debug
|
||||
TelnetSpy SerialAndTelnet;
|
||||
void _telnetConnected();
|
||||
void _telnetDisconnected();
|
||||
void _telnetHandle();
|
||||
void _telnet_setup();
|
||||
char * _command; // the input command from either Serial or Telnet
|
||||
command_t * _helpProjectCmds; // Help of commands setted by project
|
||||
uint8_t _helpProjectCmds_count; // # available commands
|
||||
void _consoleShowHelp();
|
||||
void (*_consoleCallbackProjectCmds)(); // Callable for projects commands
|
||||
void _consoleProcessCommand();
|
||||
bool _isCRLF(char character);
|
||||
bool _suspendMessages;
|
||||
TelnetSpy SerialAndTelnet;
|
||||
void _telnetConnected();
|
||||
void _telnetDisconnected();
|
||||
void _telnetHandle();
|
||||
void _telnetCommand(char * commandLine);
|
||||
char * _telnet_readWord();
|
||||
void _telnet_setup();
|
||||
char * _command; // the input command from either Serial or Telnet
|
||||
command_t * _helpProjectCmds; // Help of commands setted by project
|
||||
uint8_t _helpProjectCmds_count; // # available commands
|
||||
void _consoleShowHelp();
|
||||
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
|
||||
telnet_callback_f _telnet_callback; // callback for connect/disconnect
|
||||
void _changeSetting(const char * setting, const char * value);
|
||||
|
||||
// fs
|
||||
void _fs_setup();
|
||||
bool _fs_saveConfig();
|
||||
bool _fs_loadConfig();
|
||||
void _fs_printConfig();
|
||||
void _fs_eraseConfig();
|
||||
|
||||
// general
|
||||
char * _app_hostname;
|
||||
|
||||
@@ -8,7 +8,6 @@ platform = espressif8266
|
||||
flash_mode = dout
|
||||
; optional flags are -DNO_LED -DDEBUG_SUPPORT
|
||||
build_flags = -g -w
|
||||
build_flags_custom = '-DWIFI_SSID="my_ssid"' '-DWIFI_PASSWORD="my_password"' '-DMQTT_IP="my_broker_ip"' '-DMQTT_USER="my_broker_username"' '-DMQTT_PASS="my_broker_password"'
|
||||
lib_deps =
|
||||
CRC32
|
||||
CircularBuffer
|
||||
@@ -21,15 +20,11 @@ board = nodemcuv2
|
||||
platform = ${common.platform}
|
||||
framework = arduino
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags} ${common.build_flags_custom}
|
||||
build_flags = ${common.build_flags}
|
||||
board_build.flash_mode = ${common.flash_mode}
|
||||
upload_speed = 921600
|
||||
monitor_speed = 115200
|
||||
; comment out next line if using USB and not OTA
|
||||
upload_port = "boiler"
|
||||
; examples....
|
||||
;upload_port = "boiler."
|
||||
;upload_port = "boiler.local"
|
||||
upload_port = ems-esp.local
|
||||
;upload_port = 10.10.10.6
|
||||
|
||||
[env:d1_mini]
|
||||
@@ -37,14 +32,10 @@ board = d1_mini
|
||||
platform = ${common.platform}
|
||||
framework = arduino
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_flags = ${common.build_flags} ${common.build_flags_custom}
|
||||
build_flags = ${common.build_flags}
|
||||
board_build.flash_mode = ${common.flash_mode}
|
||||
upload_speed = 921600
|
||||
monitor_speed = 115200
|
||||
; comment out next line if using USB and not OTA
|
||||
upload_port = "boiler"
|
||||
; examples....
|
||||
;upload_port = "boiler."
|
||||
;upload_port = "boiler.local"
|
||||
upload_port = ems-esp.local
|
||||
;upload_port = 10.10.10.6
|
||||
|
||||
|
||||
13
rename_fw.py
Normal file
13
rename_fw.py
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python
|
||||
from subprocess import call
|
||||
import os
|
||||
Import("env")
|
||||
|
||||
#my_flags = env.ParseFlags(env['BUILD_FLAGS'])
|
||||
#defines = {k: v for (k, v) in my_flags.get("CPPDEFINES")}
|
||||
# print defines
|
||||
# print env.Dump()
|
||||
|
||||
# see http://docs.platformio.org/en/latest/projectconf/advanced_scripting.html#before-pre-and-after-post-actions
|
||||
# env.Replace(PROGNAME="firmware_%s" % defines.get("VERSION"))
|
||||
env.Replace(PROGNAME="firmware_%s" % env['BOARD'])
|
||||
@@ -1,13 +1,15 @@
|
||||
/*
|
||||
* EMS-ESP-Boiler
|
||||
* Paul Derbyshire - May 2018 - https://github.com/proddy/EMS-ESP-Boiler
|
||||
* https://community.home-assistant.io/t/thermostat-and-boiler-controller-for-ems-based-boilers-nefit-buderus-bosch-using-esp/53382
|
||||
* EMS-ESP
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*
|
||||
* See README for Acknowledgments
|
||||
* See ChangeLog.md for history
|
||||
* See README.md for Acknowledgments
|
||||
*/
|
||||
|
||||
// local libraries
|
||||
#include "ems.h"
|
||||
#include "ems_devices.h"
|
||||
#include "emsuart.h"
|
||||
#include "my_config.h"
|
||||
#include "version.h"
|
||||
@@ -45,6 +47,8 @@ uint8_t scanThermostat_count = 0;
|
||||
|
||||
Ticker showerColdShotStopTimer;
|
||||
|
||||
static unsigned long timestamp; // for internal timings, via millis()
|
||||
|
||||
// thermostat
|
||||
#define TOPIC_THERMOSTAT_DATA "thermostat_data" // for sending thermostat values
|
||||
#define TOPIC_THERMOSTAT_CMD_TEMP "thermostat_cmd_temp" // for received thermostat temp changes
|
||||
@@ -65,19 +69,11 @@ Ticker showerColdShotStopTimer;
|
||||
#define TOPIC_SHOWER_COLDSHOT "shower_coldshot" // used to trigger a coldshot from HA publish
|
||||
#define SHOWER_ALARM "shower_alarm" // for notifying HA that shower time has reached its limit
|
||||
|
||||
// shower settings for DEBUGGING only
|
||||
#ifdef SHOWER_TEST
|
||||
#undef SHOWER_PAUSE_TIME
|
||||
#undef SHOWER_MIN_DURATION
|
||||
#undef SHOWER_MAX_DURATION
|
||||
#undef SHOWER_COLDSHOT_DURATION
|
||||
#undef SHOWER_OFFSET_TIME
|
||||
const unsigned long SHOWER_PAUSE_TIME = 15000; // 15 seconds, max time if water is switched off & on during a shower
|
||||
const unsigned long SHOWER_MIN_DURATION = 20000; // 20 secs, before recognizing its a shower
|
||||
const unsigned long SHOWER_MAX_DURATION = 25000; // 25 secs, before trigger a shot of cold water
|
||||
const unsigned long SHOWER_COLDSHOT_DURATION = 5; // in seconds! how long for cold water shot
|
||||
const unsigned long SHOWER_OFFSET_TIME = 0; // 0 seconds grace time, to calibrate actual time under the shower
|
||||
#endif
|
||||
// if using the shower timer, change these settings
|
||||
#define SHOWER_PAUSE_TIME 15000 // in ms. 15 seconds, max time if water is switched off & on during a shower
|
||||
#define SHOWER_MIN_DURATION 120000 // in ms. 2 minutes, before recognizing its a shower
|
||||
#define SHOWER_OFFSET_TIME 5000 // in ms. 5 seconds grace time, to calibrate actual time under the shower
|
||||
#define SHOWER_COLDSHOT_DURATION 10 // in seconds. 10 seconds for cold water before turning back hot water
|
||||
|
||||
typedef struct {
|
||||
bool shower_timer; // true if we want to report back on shower times
|
||||
@@ -94,24 +90,23 @@ typedef struct {
|
||||
|
||||
command_t PROGMEM project_cmds[] = {
|
||||
|
||||
{"l [n]", "set logging (0=none, 1=raw, 2=basic, 3=thermostat only, 4=verbose)"},
|
||||
{"s", "show statistics"},
|
||||
{"D", "scan EMS connected Devices"},
|
||||
{"h", "list supported EMS telegram type IDs"},
|
||||
{"M", "publish to MQTT"},
|
||||
{"Q", "print Tx Queue"},
|
||||
{"U [n]", "do a deep scan of all thermostat message types, starting at n"},
|
||||
{"P", "toggle EMS Poll response on/off"},
|
||||
{"X", "toggle EMS Tx transmission on/off"},
|
||||
{"S", "toggle Shower timer on/off"},
|
||||
{"A", "toggle shower Alert on/off"},
|
||||
{"r [s]", "send raw telegram in hex to EMS (s=XX XX XX...)"},
|
||||
{"b [xx]", "send boiler read request (xx=telegram type ID in hex)"},
|
||||
{"t [xx]", "send thermostat read request (xx=telegram type ID in hex)"},
|
||||
{"w [n]", "set boiler warm water temperature (min 30)"},
|
||||
{"a [n]", "set boiler warm tap water (0=off, 1=on)"},
|
||||
{"T [n]", "set thermostat temperature"},
|
||||
{"m [n]", "set thermostat mode (0=low/night, 1=manual/day, 2=auto)"}
|
||||
{"info", "show the values"},
|
||||
{"log <n | b | t | r | v>", "set logging mode to none, basic, thermostat only, raw or verbose"},
|
||||
{"poll", "toggle EMS poll request on/off"},
|
||||
{"tx", "toggle EMX Tx on/off"},
|
||||
{"publish", "publish values to MQTT"},
|
||||
{"types", "list supported EMS telegram type IDs"},
|
||||
{"queue", "print the Tx queue"},
|
||||
{"autodetect", "discover EMS devices and set boiler and thermostat automatically"},
|
||||
{"shower <timer | alert>", "toggle either timer or alert on/off"},
|
||||
{"send XX...", "send raw telegram data in hex to EMS bus"},
|
||||
{"thermostat read <hex type ID>", "send read request to thermostat"},
|
||||
{"thermostat temp <degrees>", "set current thermostat temperature"},
|
||||
{"thermostat mode <mode>", "set mode (0=low/night, 1=manual/day, 2=auto)"},
|
||||
{"thermostat scan <hex type ID>", "do a deep scan of all thermostat message types, starting at n"},
|
||||
{"boiler read <hex type ID>", "send read request to boiler"},
|
||||
{"boiler wwtemp <degrees>", "set warm water temperature"},
|
||||
{"boiler tapwater <on | off>", "set warm tap water on or off"}
|
||||
|
||||
};
|
||||
|
||||
@@ -127,8 +122,6 @@ uint32_t previousThermostatPublishCRC = 0;
|
||||
const unsigned long POLL_TIMEOUT_ERR = 10000; // if no signal from boiler for last 10 seconds, assume its offline
|
||||
const unsigned long TX_HOLD_LED_TIME = 2000; // how long to hold the Tx LED because its so quick
|
||||
|
||||
unsigned long timestamp; // for internal timings, via millis()
|
||||
|
||||
uint8_t last_boilerActive = 0xFF; // for remembering last setting of the tap water or heating on/off
|
||||
|
||||
// logging messages with fixed strings
|
||||
@@ -269,7 +262,7 @@ void _renderBoolValue(const char * prefix, uint8_t value) {
|
||||
void showInfo() {
|
||||
// General stats from EMS bus
|
||||
|
||||
myDebug("%sEMS-ESP-Boiler system stats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
myDebug("%sEMS-ESP System setstats:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
_EMS_SYS_LOGGING sysLog = ems_getLogging();
|
||||
if (sysLog == EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug(" System logging set to Basic");
|
||||
@@ -519,116 +512,203 @@ void publishValues(bool force) {
|
||||
// sets the shower timer on/off
|
||||
void set_showerTimer() {
|
||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||
myDebug("Shower timer is %s", Boiler_Status.shower_timer ? "enabled" : "disabled");
|
||||
myDebug("Shower timer has been set to %s", Boiler_Status.shower_timer ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
// sets the shower alert on/off
|
||||
void set_showerAlert() {
|
||||
if (ems_getLogging() != EMS_SYS_LOGGING_NONE) {
|
||||
myDebug("Shower alert is %s", Boiler_Status.shower_alert ? "enabled" : "disabled");
|
||||
myDebug("Shower alert has been set to %s", Boiler_Status.shower_alert ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer and convert to an 8 bit int
|
||||
uint8_t _readIntNumber() {
|
||||
char * numTextPtr = strtok(NULL, ", \n");
|
||||
if (numTextPtr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return atoi(numTextPtr);
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer as a hex value and convert to an 8 bit int
|
||||
uint8_t _readHexNumber() {
|
||||
char * numTextPtr = strtok(NULL, ", \n");
|
||||
if (numTextPtr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return (uint8_t)strtol(numTextPtr, 0, 16);
|
||||
}
|
||||
|
||||
// used to read the next string from an input buffer
|
||||
char * _readWord() {
|
||||
char * word = strtok(NULL, ", \n");
|
||||
return word;
|
||||
}
|
||||
|
||||
// initiate a force scan by sending type read requests from 0 to FF to the thermostat
|
||||
// used to analyze repsonses for debugging
|
||||
void startThermostatScan(uint8_t start) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_THERMOSTAT);
|
||||
publishValuesTimer.detach();
|
||||
systemCheckTimer.detach();
|
||||
regularUpdatesTimer.detach();
|
||||
scanThermostat_count = start;
|
||||
myDebug("Starting a deep message scan on thermostat");
|
||||
scanThermostat.attach(SCANTHERMOSTAT_TIME, do_scanThermostat);
|
||||
}
|
||||
|
||||
// call back when a telnet client connects or disconnects
|
||||
// we set the logging here
|
||||
void TelnetCallback(uint8_t event) {
|
||||
if (event == TELNET_EVENT_CONNECT) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_BASIC);
|
||||
} else if (event == TELNET_EVENT_DISCONNECT) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
// extra commands options for telnet debug window
|
||||
void myDebugCallback() {
|
||||
char * cmd = myESP.consoleGetLastCommand();
|
||||
uint8_t len = strlen(cmd);
|
||||
bool b;
|
||||
// wc is the word count, i.e. number of arguments. Everything is in lower case.
|
||||
void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
|
||||
bool ok = false;
|
||||
// get first command argument
|
||||
char * first_cmd = strtok((char *)commandLine, ", \n");
|
||||
|
||||
// look for single letter commands
|
||||
if (len == 1) {
|
||||
switch (cmd[0]) {
|
||||
case 's':
|
||||
showInfo();
|
||||
break;
|
||||
case 'P': // toggle Poll
|
||||
b = !ems_getPoll();
|
||||
ems_setPoll(b);
|
||||
break;
|
||||
case 'X': // toggle Tx
|
||||
b = !ems_getTxEnabled();
|
||||
ems_setTxEnabled(b);
|
||||
break;
|
||||
case 'M':
|
||||
publishValues(true);
|
||||
break;
|
||||
case 'h': // show type handlers
|
||||
ems_printAllTypes();
|
||||
break;
|
||||
case 'S': // toggle Shower timer support
|
||||
Boiler_Status.shower_timer = !Boiler_Status.shower_timer;
|
||||
myESP.mqttPublish(TOPIC_SHOWER_TIMER, Boiler_Status.shower_timer ? "1" : "0");
|
||||
break;
|
||||
case 'A': // toggle Shower alert
|
||||
Boiler_Status.shower_alert = !Boiler_Status.shower_alert;
|
||||
myESP.mqttPublish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
||||
break;
|
||||
case 'Q': // print Tx Queue
|
||||
ems_printTxQueue();
|
||||
break;
|
||||
case 'D': // Auto detect EMS devices
|
||||
ems_scanDevices();
|
||||
break;
|
||||
default:
|
||||
myDebug("Unknown command. Use ? for help.");
|
||||
break;
|
||||
if (strcmp(first_cmd, "info") == 0) {
|
||||
showInfo();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "poll") == 0) {
|
||||
bool b = !ems_getPoll();
|
||||
ems_setPoll(b);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "tx") == 0) {
|
||||
bool b = !ems_getTxEnabled();
|
||||
ems_setTxEnabled(b);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "publish") == 0) {
|
||||
publishValues(true);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "types") == 0) {
|
||||
ems_printAllTypes();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "queue") == 0) {
|
||||
ems_printTxQueue();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (strcmp(first_cmd, "autodetect") == 0) {
|
||||
ems_scanDevices();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// shower settings
|
||||
if (strcmp(first_cmd, "shower") == 0) {
|
||||
if (wc == 2) {
|
||||
char * second_cmd = _readWord();
|
||||
if (strcmp(second_cmd, "timer") == 0) {
|
||||
Boiler_Status.shower_timer = !Boiler_Status.shower_timer;
|
||||
myESP.mqttPublish(TOPIC_SHOWER_TIMER, Boiler_Status.shower_timer ? "1" : "0");
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "alert") == 0) {
|
||||
Boiler_Status.shower_alert = !Boiler_Status.shower_alert;
|
||||
myESP.mqttPublish(TOPIC_SHOWER_ALERT, Boiler_Status.shower_alert ? "1" : "0");
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// for commands with parameters, assume command is just a single letter followed by a space
|
||||
switch (cmd[0]) {
|
||||
case 'T': // set thermostat temp
|
||||
ems_setThermostatTemp(strtof(&cmd[2], 0));
|
||||
break;
|
||||
case 'm': // set thermostat mode
|
||||
ems_setThermostatMode(cmd[2] - '0');
|
||||
break;
|
||||
case 'w': // set warm water temp
|
||||
ems_setWarmWaterTemp((uint8_t)strtol(&cmd[2], 0, 10));
|
||||
break;
|
||||
case 'l': // logging
|
||||
ems_setLogging((_EMS_SYS_LOGGING)(cmd[2] - '0'));
|
||||
break;
|
||||
case 'a': // set ww activate on or off
|
||||
if ((cmd[2] - '0') == 1)
|
||||
ems_setWarmTapWaterActivated(true);
|
||||
else if ((cmd[2] - '0') == 0)
|
||||
ems_setWarmTapWaterActivated(false);
|
||||
break;
|
||||
case 'b': // boiler read command
|
||||
ems_doReadCommand((uint8_t)strtol(&cmd[2], 0, 16), EMS_Boiler.type_id);
|
||||
break;
|
||||
case 't': // thermostat command
|
||||
ems_doReadCommand((uint8_t)strtol(&cmd[2], 0, 16), EMS_Thermostat.type_id);
|
||||
break;
|
||||
case 'r': // send raw data
|
||||
ems_sendRawTelegram(&cmd[2]);
|
||||
break;
|
||||
case 'x': // experimental, not displayed!
|
||||
myDebug("Calling experimental...");
|
||||
ems_setLogging(EMS_SYS_LOGGING_VERBOSE);
|
||||
ems_setExperimental((uint8_t)strtol(&cmd[2], 0, 16)); // takes HEX param
|
||||
break;
|
||||
case 'U': // thermostat scan
|
||||
ems_setLogging(EMS_SYS_LOGGING_THERMOSTAT);
|
||||
publishValuesTimer.detach();
|
||||
systemCheckTimer.detach();
|
||||
regularUpdatesTimer.detach();
|
||||
scanThermostat_count = (uint8_t)strtol(&cmd[2], 0, 16);
|
||||
myDebug("Starting a deep message scan on thermometer");
|
||||
scanThermostat.attach(SCANTHERMOSTAT_TIME, do_scanThermostat);
|
||||
break;
|
||||
default:
|
||||
myDebug("Unknown command. Use ? for help.");
|
||||
break;
|
||||
// logging
|
||||
if (strcmp(first_cmd, "log") == 0) {
|
||||
if (wc == 2) {
|
||||
char * second_cmd = _readWord();
|
||||
if (strcmp(second_cmd, "v") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_VERBOSE);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "b") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_BASIC);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "t") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_THERMOSTAT);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "r") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_RAW);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "n") == 0) {
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// thermostat commands
|
||||
if (strcmp(first_cmd, "thermostat") == 0) {
|
||||
if (wc == 3) {
|
||||
char * second_cmd = _readWord();
|
||||
if (strcmp(second_cmd, "temp") == 0) {
|
||||
ems_setThermostatTemp(_readIntNumber());
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "mode") == 0) {
|
||||
ems_setThermostatMode(_readIntNumber());
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "read") == 0) {
|
||||
ems_doReadCommand(_readHexNumber(), EMS_Thermostat.type_id);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "scan") == 0) {
|
||||
startThermostatScan(_readIntNumber());
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// boiler commands
|
||||
if (strcmp(first_cmd, "boiler") == 0) {
|
||||
if (wc == 3) {
|
||||
char * second_cmd = _readWord();
|
||||
if (strcmp(second_cmd, "wwtemp") == 0) {
|
||||
ems_setWarmWaterTemp(_readIntNumber());
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "read") == 0) {
|
||||
ems_doReadCommand(_readHexNumber(), EMS_Boiler.type_id);
|
||||
ok = true;
|
||||
} else if (strcmp(second_cmd, "tapwater") == 0) {
|
||||
char * third_cmd = _readWord();
|
||||
if (strcmp(third_cmd, "on") == 0) {
|
||||
ems_setWarmTapWaterActivated(true);
|
||||
ok = true;
|
||||
} else if (strcmp(third_cmd, "off") == 0) {
|
||||
ems_setWarmTapWaterActivated(false);
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send raw
|
||||
if (strcmp(first_cmd, "send") == 0) {
|
||||
ems_sendRawTelegram((char *)&commandLine[5]);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// check for invalid command
|
||||
if (!ok) {
|
||||
myDebug("Unknown command. Use ? for help.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// MQTT Callback to handle incoming/outgoing changes
|
||||
void MQTTcallback(unsigned int type, const char * topic, const char * message) {
|
||||
void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
||||
// we're connected. lets subscribe to some topics
|
||||
if (type == MQTT_CONNECT_EVENT) {
|
||||
myESP.mqttSubscribe(TOPIC_THERMOSTAT_CMD_TEMP);
|
||||
@@ -708,7 +788,7 @@ void WIFICallback() {
|
||||
|
||||
// Initialize the boiler settings
|
||||
void initShower() {
|
||||
// default showr settings
|
||||
// default shower settings
|
||||
Boiler_Status.shower_timer = BOILER_SHOWER_TIMER;
|
||||
Boiler_Status.shower_alert = BOILER_SHOWER_ALERT;
|
||||
Boiler_Shower.timerStart = 0;
|
||||
@@ -719,7 +799,10 @@ void initShower() {
|
||||
|
||||
// call PublishValues without forcing, so using CRC to see if we really need to publish
|
||||
void do_publishValues() {
|
||||
publishValues(false);
|
||||
// don't publish if we're not connected to the EMS bus
|
||||
if (ems_getBusConnected()) {
|
||||
publishValues(false);
|
||||
}
|
||||
}
|
||||
|
||||
// callback to light up the LED, called via Ticker every second
|
||||
@@ -745,16 +828,20 @@ void do_scanThermostat() {
|
||||
// do a system health check every now and then to see if we all connections
|
||||
void do_systemCheck() {
|
||||
if (!ems_getBusConnected()) {
|
||||
myDebug("Error! Unable to connect to EMS bus. Please make sure you're not in DEBUG_SUPPORT mode. Retrying in %d seconds...",
|
||||
myDebug("Error! Unable to connect to EMS bus. Check connection and make sure you're not in DEBUG_SUPPORT mode. Retrying in %d "
|
||||
"seconds...",
|
||||
SYSTEMCHECK_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
// force calls to get data from EMS for the types that aren't sent as broadcasts
|
||||
// only if we have a EMS connection
|
||||
void do_regularUpdates() {
|
||||
myDebugLog("Calling scheduled data refresh from EMS devices..");
|
||||
ems_getThermostatValues(); // get Thermostat temps (if supported)
|
||||
ems_getBoilerValues();
|
||||
if (ems_getBusConnected()) {
|
||||
myDebugLog("Calling scheduled data refresh from EMS devices..");
|
||||
ems_getThermostatValues(); // get Thermostat temps (if supported)
|
||||
ems_getBoilerValues();
|
||||
}
|
||||
}
|
||||
|
||||
// turn off hot water to send a shot of cold
|
||||
@@ -860,16 +947,20 @@ void setup() {
|
||||
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS
|
||||
|
||||
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
||||
// with callbacks
|
||||
myESP.setup(APP_HOSTNAME, APP_NAME, APP_VERSION, WIFI_SSID, WIFI_PASSWORD, MQTT_IP, MQTT_USER, MQTT_PASS);
|
||||
myESP.consoleSetCallBackProjectCmds(project_cmds, ArraySize(project_cmds), myDebugCallback); // set up Telnet commands
|
||||
myESP.setTelnetCommands(project_cmds, ArraySize(project_cmds), TelnetCommandCallback); // set up Telnet commands
|
||||
myESP.setConnection(WIFI_SSID, WIFI_PASSWORD, MQTT_IP, MQTT_USER, MQTT_PASS); // optional
|
||||
myESP.begin(APP_HOSTNAME, APP_NAME, APP_VERSION);
|
||||
myESP.setMQTTbase(MQTT_BASE);
|
||||
|
||||
// callbacks
|
||||
myESP.setWIFICallback(WIFICallback);
|
||||
myESP.setMQTTCallback(MQTTcallback);
|
||||
myESP.setMQTTCallback(MQTTCallback);
|
||||
myESP.setTelnetCallback(TelnetCallback);
|
||||
|
||||
// init Shower specific parameters
|
||||
initShower();
|
||||
|
||||
ems_setLogging(BOILER_DEFAULT_LOGGING); // set default logging. see my_config.h
|
||||
ems_setLogging(EMS_SYS_LOGGING_NONE); // set default logging to none
|
||||
|
||||
// init the EMS bus
|
||||
// call ems.cpp's init function to set all the internal params
|
||||
@@ -892,7 +983,7 @@ void loop() {
|
||||
ems_setEmsRefreshed(false);
|
||||
}
|
||||
|
||||
// do shower logic if it is enabled
|
||||
// do shower logic, if enabled
|
||||
if (Boiler_Status.shower_timer) {
|
||||
showerCheck();
|
||||
}
|
||||
123
src/ems.cpp
123
src/ems.cpp
@@ -1,10 +1,18 @@
|
||||
/**
|
||||
* ems.cpp
|
||||
*
|
||||
* handles all the processing of the EMS messages
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP-Boiler
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
|
||||
#include "ems.h"
|
||||
#include "ems_devices.h"
|
||||
#include "emsuart.h"
|
||||
#include <Arduino.h>
|
||||
#include <CircularBuffer.h> // https://github.com/rlogiacco/CircularBuffer
|
||||
#include <MyESP.h>
|
||||
#include <list> // std::list
|
||||
|
||||
// myESP
|
||||
#define myDebug(...) myESP.myDebug(__VA_ARGS__)
|
||||
@@ -42,42 +50,9 @@ void _process_RC35StatusMessage(uint8_t * data, uint8_t length);
|
||||
// Easy
|
||||
void _process_EasyStatusMessage(uint8_t * data, uint8_t length);
|
||||
|
||||
// EMS types for known Buderus devices
|
||||
// Note: This is still incomplete
|
||||
const _Model_Type Model_Types[] = {
|
||||
|
||||
// me
|
||||
{EMS_MODEL_SERVICEKEY, 999, 0x0B, "Service Key"},
|
||||
|
||||
// various boilers and buderus type devices
|
||||
{EMS_MODEL_UBA, 123, 0x08, "MC10/UBA3 Boiler"}, // verified
|
||||
{EMS_MODEL_BK15, 64, 0x08, "Sieger BK15 Boiler"}, // verified
|
||||
{EMS_MODEL_BC10, 190, 0x09, "BC10 Base Controller"}, // verified
|
||||
{EMS_MODEL_MM10, 125, 0x21, "MM10 Mixer Module"}, // warning, fake product id!
|
||||
{EMS_MODEL_WM10, 126, 0x11, "WM10 Switch Module"}, // warning, fake product id!
|
||||
|
||||
// controllers and thermostats
|
||||
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73"},
|
||||
{EMS_MODEL_RC20, 77, 0x17, "RC20 (Nefit Moduline 300)"},
|
||||
{EMS_MODEL_RC30, 78, 0x10, "RC30 (Nefit Moduline 400)"},
|
||||
{EMS_MODEL_RC35, 86, 0x10, "RC35 (or compatible"},
|
||||
{EMS_MODEL_EASY, 202, 0x18, "TC100 (Nefit Easy/CT100)"}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Known thermostat types and their abilities
|
||||
* Recognized EMS types and the functions they call to process the telegrams
|
||||
*/
|
||||
const _Thermostat_Type Thermostat_Types[] = {
|
||||
|
||||
{EMS_MODEL_RC20, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC30, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC35, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_EASY, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO},
|
||||
{EMS_MODEL_ES73, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}
|
||||
|
||||
};
|
||||
|
||||
const _EMS_Type EMS_Types[] = {
|
||||
|
||||
// common
|
||||
@@ -93,13 +68,19 @@ const _EMS_Type EMS_Types[] = {
|
||||
{EMS_MODEL_UBA, EMS_TYPE_UBAParametersMessage, "UBAParametersMessage", NULL},
|
||||
{EMS_MODEL_UBA, EMS_TYPE_UBAMaintenanceStatusMessage, "UBAMaintenanceStatusMessage", NULL},
|
||||
|
||||
// RC20
|
||||
// RC20 and RC20F
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RC20Set, "RC20Set", _process_RC20Set},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_RC20StatusMessage, "RC20StatusMessage", _process_RC20StatusMessage},
|
||||
{EMS_MODEL_RC20, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RC20Set, "RC20Set", _process_RC20Set},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_RC20StatusMessage, "RC20StatusMessage", _process_RC20StatusMessage},
|
||||
{EMS_MODEL_RC20F, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints},
|
||||
|
||||
// RC30
|
||||
{EMS_MODEL_RC30, EMS_TYPE_RCOutdoorTempMessage, "RCOutdoorTempMessage", _process_RCOutdoorTempMessage},
|
||||
{EMS_MODEL_RC30, EMS_TYPE_RCTime, "RCTime", _process_RCTime},
|
||||
@@ -125,7 +106,6 @@ const _EMS_Type EMS_Types[] = {
|
||||
{EMS_MODEL_EASY, EMS_TYPE_EasyStatusMessage, "EasyStatusMessage", _process_EasyStatusMessage},
|
||||
{EMS_MODEL_EASY, EMS_TYPE_UBASetPoints, "UBASetPoints", _process_SetPoints}
|
||||
|
||||
|
||||
};
|
||||
|
||||
// calculate sizes of arrays
|
||||
@@ -163,7 +143,7 @@ uint8_t _last_TxTelgramCRC; // CRC of last Tx sent, for checking duplicates
|
||||
|
||||
// init stats and counters and buffers
|
||||
// uses -255 or 255 for values that haven't been set yet (EMS_VALUE_INT_NOTSET and EMS_VALUE_FLOAT_NOTSET)
|
||||
void ems_init(_EMS_MODEL_ID boiler_modelid, _EMS_MODEL_ID thermostat_modelid) {
|
||||
void ems_init(uint8_t boiler_modelid, uint8_t thermostat_modelid) {
|
||||
// overall status
|
||||
EMS_Sys_Status.emsRxPgks = 0;
|
||||
EMS_Sys_Status.emsTxPkgs = 0;
|
||||
@@ -816,7 +796,7 @@ void _processType(uint8_t * telegram, uint8_t length) {
|
||||
// there is a match, so write must have been successful
|
||||
EMS_TxQueue.shift(); // remove validate from queue
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Write to 0x%02X successful.", EMS_TxTelegram.dest);
|
||||
myDebug("Write to 0x%02X was successful", EMS_TxTelegram.dest);
|
||||
}
|
||||
ems_doReadCommand(EMS_TxTelegram.comparisonPostRead,
|
||||
EMS_TxTelegram.dest,
|
||||
@@ -1102,7 +1082,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
ems_getThermostatValues(); // get Thermostat values (if supported)
|
||||
}
|
||||
} else {
|
||||
// otherwise assume its a boiler
|
||||
// otherwise assume its a boiler or some other EMS device
|
||||
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
|
||||
myDebug("Boiler recognized. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
|
||||
Model_Types[i].model_string,
|
||||
@@ -1125,7 +1105,7 @@ void _process_Version(uint8_t * data, uint8_t length) {
|
||||
* Given a MODEL_ID, look up its data and set either a Thermostat or Boiler
|
||||
* return false if not found or no need to set
|
||||
*/
|
||||
bool _ems_setModel(_EMS_MODEL_ID model_id) {
|
||||
bool _ems_setModel(uint8_t model_id) {
|
||||
if (model_id == EMS_MODEL_NONE) {
|
||||
return false; // invalid model_id
|
||||
}
|
||||
@@ -1301,8 +1281,8 @@ void ems_getThermostatValues() {
|
||||
return;
|
||||
}
|
||||
|
||||
_EMS_MODEL_ID model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
uint8_t model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
|
||||
if (model_id == EMS_MODEL_RC20) {
|
||||
ems_doReadCommand(EMS_TYPE_RC20StatusMessage, type); // to get the setpoint temp
|
||||
@@ -1332,7 +1312,7 @@ void ems_getBoilerValues() {
|
||||
|
||||
|
||||
// return pointer to Model details
|
||||
int _ems_findModel(_EMS_MODEL_ID model_id) {
|
||||
int _ems_findModel(uint8_t model_id) {
|
||||
uint8_t i = 0;
|
||||
bool found = false;
|
||||
|
||||
@@ -1351,7 +1331,7 @@ int _ems_findModel(_EMS_MODEL_ID model_id) {
|
||||
return i;
|
||||
}
|
||||
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, _EMS_MODEL_ID model_id) {
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, uint8_t model_id) {
|
||||
int i = _ems_findModel(model_id);
|
||||
if (i != -1) {
|
||||
char tmp[6] = {0};
|
||||
@@ -1394,12 +1374,12 @@ char * ems_getBoilerType(char * buffer) {
|
||||
}
|
||||
|
||||
// returns the model type for a thermostat
|
||||
_EMS_MODEL_ID ems_getThermostatModel() {
|
||||
uint8_t ems_getThermostatModel() {
|
||||
return (EMS_Thermostat.model_id);
|
||||
}
|
||||
|
||||
// returns the model type for a boiler
|
||||
_EMS_MODEL_ID ems_getBoilerModel() {
|
||||
uint8_t ems_getBoilerModel() {
|
||||
return (EMS_Boiler.model_id);
|
||||
}
|
||||
|
||||
@@ -1407,7 +1387,11 @@ _EMS_MODEL_ID ems_getBoilerModel() {
|
||||
* Find the versions of our connected devices
|
||||
*/
|
||||
void ems_scanDevices() {
|
||||
myDebug("Scanning EMS bus for devices. This may take a few seconds.");
|
||||
if (!ems_getBusConnected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
myDebug("Scanning EMS bus for devices. This may take a few seconds...");
|
||||
|
||||
// copy over the IDs from Model-type to a list
|
||||
std::list<uint8_t> Device_Ids;
|
||||
@@ -1430,7 +1414,7 @@ void ems_scanDevices() {
|
||||
* Print out all handled types
|
||||
*/
|
||||
void ems_printAllTypes() {
|
||||
myDebug("These %d telegram TypeIDs are recognized currently:", _EMS_Types_max);
|
||||
myDebug("These %d telegram TypeIDs are recognized:", _EMS_Types_max);
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < _EMS_Types_max; i++) {
|
||||
@@ -1444,7 +1428,7 @@ void ems_printAllTypes() {
|
||||
}
|
||||
}
|
||||
|
||||
myDebug("\nThese %d thermostats are natively supported:", _Thermostat_Types_max);
|
||||
myDebug("\nThese %d thermostats models are supported:", _Thermostat_Types_max);
|
||||
for (i = 0; i < _Thermostat_Types_max; i++) {
|
||||
// find the model's details
|
||||
for (int j = 0; j < _Model_Types_max; j++) {
|
||||
@@ -1503,6 +1487,7 @@ void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh) {
|
||||
|
||||
/**
|
||||
* Send a raw telegram to the bus
|
||||
* telegram is a string of hex values
|
||||
*/
|
||||
void ems_sendRawTelegram(char * telegram) {
|
||||
uint8_t count = 0;
|
||||
@@ -1558,8 +1543,8 @@ void ems_setThermostatTemp(float temperature) {
|
||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||
|
||||
_EMS_MODEL_ID model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
uint8_t model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
|
||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_WRITE;
|
||||
EMS_TxTelegram.dest = type;
|
||||
@@ -1606,8 +1591,8 @@ void ems_setThermostatMode(uint8_t mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
_EMS_MODEL_ID model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
uint8_t model_id = EMS_Thermostat.model_id;
|
||||
uint8_t type = EMS_Thermostat.type_id;
|
||||
|
||||
myDebug("Setting thermostat mode to %d", mode);
|
||||
|
||||
@@ -1756,33 +1741,3 @@ void ems_setWarmTapWaterActivated(bool activated) {
|
||||
|
||||
EMS_TxQueue.push(EMS_TxTelegram); // add to queue
|
||||
}
|
||||
|
||||
/**
|
||||
* experimental code for debugging - not in production
|
||||
*/
|
||||
void ems_setExperimental(uint8_t value) {
|
||||
/*
|
||||
_EMS_TxTelegram EMS_TxTelegram = EMS_TX_TELEGRAM_NEW; // create new Tx
|
||||
EMS_TxTelegram.timestamp = millis(); // set timestamp
|
||||
|
||||
EMS_TxTelegram.action = EMS_TX_TELEGRAM_READ; // read command
|
||||
EMS_TxTelegram.dest = EMS_Thermostat.type; // set 8th bit to indicate a read
|
||||
EMS_TxTelegram.offset = 0; // 0 for all data
|
||||
EMS_TxTelegram.length = 8;
|
||||
EMS_TxTelegram.type = 0xF0;
|
||||
EMS_TxTelegram.type_validate = EMS_ID_NONE;
|
||||
|
||||
// EMS Plus test
|
||||
// Sending read to 0x18: telegram: 0B 98 F0 00 01 B9 63 DB (len 8)
|
||||
EMS_TxTelegram.data[0] = EMS_ID_ME; // src
|
||||
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest
|
||||
EMS_TxTelegram.data[2] = 0xF0; // marker
|
||||
EMS_TxTelegram.data[3] = EMS_TxTelegram.offset; // offset
|
||||
EMS_TxTelegram.data[4] = 0x01; // hi byte
|
||||
EMS_TxTelegram.data[5] = 0xB9; // low byte
|
||||
|
||||
EMS_TxTelegram.data[6] = 99; // max length
|
||||
|
||||
EMS_TxQueue.push(EMS_TxTelegram);
|
||||
*/
|
||||
}
|
||||
|
||||
211
src/ems.h
211
src/ems.h
@@ -1,25 +1,15 @@
|
||||
/*
|
||||
* Header file for EMS.cpp
|
||||
* Header file for ems.cpp
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "emsuart.h"
|
||||
#include "my_config.h" // include custom configuration settings
|
||||
#include <Arduino.h>
|
||||
#include <CircularBuffer.h> // https://github.com/rlogiacco/CircularBuffer
|
||||
#include <MyESP.h>
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
// EMS IDs
|
||||
#define EMS_ID_NONE 0x00 // Fixed - used as a dest in broadcast messages and empty type IDs
|
||||
#define EMS_ID_ME 0x0B // Fixed - our device, hardcoded as "Service Key"
|
||||
|
||||
//#define EMS_ID_THERMOSTAT 0xFF // Fixed - to recognize a Thermostat
|
||||
//#define EMS_ID_BOILER 0x08 // Fixed - also known as MC10.
|
||||
#define EMS_ID_ME 0x0B // Fixed - our device, hardcoded as the "Service Key"
|
||||
|
||||
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
|
||||
|
||||
@@ -27,70 +17,9 @@
|
||||
// This can differs per firmware version and typically 32 is the max
|
||||
#define EMS_MAX_TELEGRAM_LENGTH 99
|
||||
|
||||
// define here the EMS telegram types you need
|
||||
|
||||
// Common for all EMS devices
|
||||
#define EMS_TYPE_Version 0x02 // version of the UBA controller (boiler)
|
||||
|
||||
/*
|
||||
* Boiler...
|
||||
*/
|
||||
#define EMS_TYPE_UBAMonitorFast 0x18 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMonitorSlow 0x19 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMonitorWWMessage 0x34 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMaintenanceStatusMessage 0x1C // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAParameterWW 0x33
|
||||
#define EMS_TYPE_UBATotalUptimeMessage 0x14
|
||||
#define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15
|
||||
#define EMS_TYPE_UBAParametersMessage 0x16
|
||||
#define EMS_TYPE_UBASetPoints 0x1A
|
||||
#define EMS_TYPE_UBAFunctionTest 0x1D
|
||||
|
||||
#define EMS_OFFSET_UBAParameterWW_wwtemp 2 // WW Temperature
|
||||
#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated
|
||||
#define EMS_OFFSET_UBAParameterWW_wwComfort 9 // WW is in comfort or eco mode
|
||||
#define EMS_VALUE_UBAParameterWW_wwComfort_Comfort 0x00 // the value for comfort
|
||||
#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco
|
||||
|
||||
/*
|
||||
* Thermostat...
|
||||
*/
|
||||
|
||||
// Common for all thermostats
|
||||
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
|
||||
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
|
||||
|
||||
// RC20 specific
|
||||
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
|
||||
#define EMS_OFFSET_RC20Set_mode 23 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC20Set_temp 28 // position of thermostat setpoint temperature
|
||||
#define EMS_TYPE_RC20StatusMessage_setpoint 1 // setpoint temp
|
||||
#define EMS_TYPE_RC20StatusMessage_curr 2 // current temp
|
||||
|
||||
// RC30 specific
|
||||
#define EMS_TYPE_RC30StatusMessage 0x41 // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC30Set 0xA7 // for setting values like temp and mode
|
||||
#define EMS_OFFSET_RC30Set_mode 23 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature
|
||||
#define EMS_TYPE_RC30StatusMessage_setpoint 1 // setpoint temp
|
||||
#define EMS_TYPE_RC30StatusMessage_curr 2 // current temp
|
||||
|
||||
// RC35 specific
|
||||
#define EMS_TYPE_RC35StatusMessage 0x3E // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC35StatusMessage_setpoint 2 // desired temp
|
||||
#define EMS_TYPE_RC35StatusMessage_curr 3 // current temp
|
||||
#define EMS_TYPE_RC35Set 0x3D // for setting values like temp and mode (Working mode HC1)
|
||||
#define EMS_OFFSET_RC35Set_mode 7 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC35Set_temp_day 2 // position of thermostat setpoint temperature for day time
|
||||
#define EMS_OFFSET_RC35Set_temp_night 1 // position of thermostat setpoint temperature for night time
|
||||
|
||||
// Easy specific
|
||||
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
|
||||
#define EMS_TYPE_EasyStatusMessage_setpoint 10 // setpoint temp
|
||||
#define EMS_TYPE_EasyStatusMessage_curr 8 // current temp
|
||||
|
||||
|
||||
// default values
|
||||
#define EMS_VALUE_INT_ON 1 // boolean true
|
||||
#define EMS_VALUE_INT_OFF 0 // boolean false
|
||||
@@ -98,6 +27,34 @@
|
||||
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs
|
||||
#define EMS_VALUE_FLOAT_NOTSET -255 // float unset
|
||||
|
||||
#define EMS_THERMOSTAT_READ_YES true
|
||||
#define EMS_THERMOSTAT_READ_NO false
|
||||
#define EMS_THERMOSTAT_WRITE_YES true
|
||||
#define EMS_THERMOSTAT_WRITE_NO false
|
||||
|
||||
// ANSI Colors
|
||||
#define COLOR_RESET "\x1B[0m"
|
||||
#define COLOR_BLACK "\x1B[0;30m"
|
||||
#define COLOR_RED "\x1B[0;31m"
|
||||
#define COLOR_GREEN "\x1B[0;32m"
|
||||
#define COLOR_YELLOW "\x1B[0;33m"
|
||||
#define COLOR_BLUE "\x1B[0;34m"
|
||||
#define COLOR_MAGENTA "\x1B[0;35m"
|
||||
#define COLOR_CYAN "\x1B[0;36m"
|
||||
#define COLOR_WHITE "\x1B[0;37m"
|
||||
#define COLOR_BOLD_ON "\x1B[1m"
|
||||
#define COLOR_BOLD_OFF "\x1B[21m"
|
||||
|
||||
// trigger settings to determine if hot tap water or the heating is active
|
||||
#define EMS_BOILER_BURNPOWER_TAPWATER 100
|
||||
#define EMS_BOILER_SELFLOWTEMP_HEATING 70
|
||||
|
||||
//define maximum settable tapwater temperature, not every installation supports 90 degrees
|
||||
#define EMS_BOILER_TAPWATER_TEMPERATURE_MAX 60
|
||||
|
||||
#define EMS_TX_TELEGRAM_QUEUE_MAX 50 // max size of Tx FIFO queue
|
||||
|
||||
|
||||
/* EMS UART transfer status */
|
||||
typedef enum {
|
||||
EMS_RX_IDLE,
|
||||
@@ -160,8 +117,6 @@ typedef struct {
|
||||
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
||||
} _EMS_TxTelegram;
|
||||
|
||||
#define EMS_TX_TELEGRAM_QUEUE_MAX 20 // max size of Tx FIFO queue
|
||||
|
||||
// default empty Tx
|
||||
const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
||||
EMS_TX_TELEGRAM_INIT, // action
|
||||
@@ -179,35 +134,11 @@ const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
||||
{0x00} // data
|
||||
};
|
||||
|
||||
// Known Buderus non-Thermostat types
|
||||
typedef enum {
|
||||
EMS_MODEL_NONE,
|
||||
EMS_MODEL_ALL, // common for all devices
|
||||
|
||||
// service key
|
||||
EMS_MODEL_SERVICEKEY, // this is us
|
||||
|
||||
// main buderus boiler type devices
|
||||
EMS_MODEL_BK15,
|
||||
EMS_MODEL_UBA,
|
||||
EMS_MODEL_BC10,
|
||||
EMS_MODEL_MM10,
|
||||
EMS_MODEL_WM10,
|
||||
|
||||
// thermostats
|
||||
EMS_MODEL_ES73,
|
||||
EMS_MODEL_RC20,
|
||||
EMS_MODEL_RC30,
|
||||
EMS_MODEL_RC35,
|
||||
EMS_MODEL_EASY
|
||||
|
||||
} _EMS_MODEL_ID;
|
||||
|
||||
typedef struct {
|
||||
_EMS_MODEL_ID model_id;
|
||||
uint8_t product_id;
|
||||
uint8_t type_id;
|
||||
char model_string[50];
|
||||
uint8_t model_id;
|
||||
uint8_t product_id;
|
||||
uint8_t type_id;
|
||||
char model_string[50];
|
||||
} _Model_Type;
|
||||
|
||||
/*
|
||||
@@ -259,39 +190,34 @@ typedef struct { // UBAParameterWW
|
||||
uint8_t heatingActive; // Central heating is on/off
|
||||
|
||||
// settings
|
||||
char version[10];
|
||||
uint8_t type_id;
|
||||
_EMS_MODEL_ID model_id;
|
||||
char version[10];
|
||||
uint8_t type_id;
|
||||
uint8_t model_id;
|
||||
} _EMS_Boiler;
|
||||
|
||||
// Definition for thermostat type
|
||||
typedef struct {
|
||||
_EMS_MODEL_ID model_id;
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
uint8_t model_id;
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
} _Thermostat_Type;
|
||||
|
||||
#define EMS_THERMOSTAT_READ_YES true
|
||||
#define EMS_THERMOSTAT_READ_NO false
|
||||
#define EMS_THERMOSTAT_WRITE_YES true
|
||||
#define EMS_THERMOSTAT_WRITE_NO false
|
||||
|
||||
// Thermostat data
|
||||
typedef struct {
|
||||
uint8_t type_id; // the type ID of the thermostat
|
||||
_EMS_MODEL_ID model_id; // which Thermostat type
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
char version[10];
|
||||
float setpoint_roomTemp; // current set temp
|
||||
float curr_roomTemp; // current room temp
|
||||
uint8_t mode; // 0=low, 1=manual, 2=auto
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint8_t year;
|
||||
uint8_t type_id; // the type ID of the thermostat
|
||||
uint8_t model_id; // which Thermostat type
|
||||
bool read_supported;
|
||||
bool write_supported;
|
||||
char version[10];
|
||||
float setpoint_roomTemp; // current set temp
|
||||
float curr_roomTemp; // current room temp
|
||||
uint8_t mode; // 0=low, 1=manual, 2=auto
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint8_t year;
|
||||
} _EMS_Thermostat;
|
||||
|
||||
// call back function signature
|
||||
@@ -299,28 +225,15 @@ typedef void (*EMS_processType_cb)(uint8_t * data, uint8_t length);
|
||||
|
||||
// Definition for each EMS type, including the relative callback function
|
||||
typedef struct {
|
||||
_EMS_MODEL_ID model_id;
|
||||
uint8_t model_id;
|
||||
uint8_t type;
|
||||
const char typeString[50];
|
||||
EMS_processType_cb processType_cb;
|
||||
} _EMS_Type;
|
||||
|
||||
// ANSI Colors
|
||||
#define COLOR_RESET "\x1B[0m"
|
||||
#define COLOR_BLACK "\x1B[0;30m"
|
||||
#define COLOR_RED "\x1B[0;31m"
|
||||
#define COLOR_GREEN "\x1B[0;32m"
|
||||
#define COLOR_YELLOW "\x1B[0;33m"
|
||||
#define COLOR_BLUE "\x1B[0;34m"
|
||||
#define COLOR_MAGENTA "\x1B[0;35m"
|
||||
#define COLOR_CYAN "\x1B[0;36m"
|
||||
#define COLOR_WHITE "\x1B[0;37m"
|
||||
#define COLOR_BOLD_ON "\x1B[1m"
|
||||
#define COLOR_BOLD_OFF "\x1B[21m"
|
||||
|
||||
// function definitions
|
||||
extern void ems_parseTelegram(uint8_t * telegram, uint8_t len);
|
||||
void ems_init(_EMS_MODEL_ID boiler_modelid, _EMS_MODEL_ID thermostat_modelid);
|
||||
void ems_init(uint8_t boiler_modelid, uint8_t thermostat_modelid);
|
||||
void ems_doReadCommand(uint8_t type, uint8_t dest, bool forceRefresh = false);
|
||||
void ems_sendRawTelegram(char * telegram);
|
||||
|
||||
@@ -329,7 +242,6 @@ void ems_setThermostatMode(uint8_t mode);
|
||||
void ems_setWarmWaterTemp(uint8_t temperature);
|
||||
void ems_setWarmWaterActivated(bool activated);
|
||||
void ems_setWarmTapWaterActivated(bool activated);
|
||||
void ems_setExperimental(uint8_t value);
|
||||
void ems_setPoll(bool b);
|
||||
void ems_setTxEnabled(bool b);
|
||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
|
||||
@@ -348,8 +260,8 @@ bool ems_getBusConnected();
|
||||
_EMS_SYS_LOGGING ems_getLogging();
|
||||
uint8_t ems_getEmsTypesCount();
|
||||
bool ems_getEmsRefreshed();
|
||||
_EMS_MODEL_ID ems_getThermostatModel();
|
||||
_EMS_MODEL_ID ems_getBoilerModel();
|
||||
uint8_t ems_getThermostatModel();
|
||||
uint8_t ems_getBoilerModel();
|
||||
|
||||
void ems_scanDevices();
|
||||
void ems_printAllTypes();
|
||||
@@ -362,10 +274,9 @@ uint8_t _crcCalculator(uint8_t * data, uint8_t len);
|
||||
void _processType(uint8_t * telegram, uint8_t length);
|
||||
void _debugPrintPackage(const char * prefix, uint8_t * data, uint8_t len, const char * color);
|
||||
void _ems_clearTxData();
|
||||
int _ems_findModel(_EMS_MODEL_ID model_id);
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, _EMS_MODEL_ID model_id);
|
||||
bool _ems_setModel(_EMS_MODEL_ID model_id);
|
||||
|
||||
int _ems_findModel(uint8_t model_id);
|
||||
char * _ems_buildModelString(char * buffer, uint8_t size, uint8_t model_id);
|
||||
bool _ems_setModel(uint8_t model_id);
|
||||
|
||||
// global so can referenced in other classes
|
||||
extern _EMS_Sys_Status EMS_Sys_Status;
|
||||
|
||||
132
src/ems_devices.h
Normal file
132
src/ems_devices.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* General information about known EMS devices
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ems.h"
|
||||
|
||||
/*
|
||||
* Boiler...
|
||||
*/
|
||||
#define EMS_TYPE_UBAMonitorFast 0x18 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMonitorSlow 0x19 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMonitorWWMessage 0x34 // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAMaintenanceStatusMessage 0x1C // is an automatic monitor broadcast
|
||||
#define EMS_TYPE_UBAParameterWW 0x33
|
||||
#define EMS_TYPE_UBATotalUptimeMessage 0x14
|
||||
#define EMS_TYPE_UBAMaintenanceSettingsMessage 0x15
|
||||
#define EMS_TYPE_UBAParametersMessage 0x16
|
||||
#define EMS_TYPE_UBASetPoints 0x1A
|
||||
#define EMS_TYPE_UBAFunctionTest 0x1D
|
||||
|
||||
#define EMS_OFFSET_UBAParameterWW_wwtemp 2 // WW Temperature
|
||||
#define EMS_OFFSET_UBAParameterWW_wwactivated 1 // WW Activated
|
||||
#define EMS_OFFSET_UBAParameterWW_wwComfort 9 // WW is in comfort or eco mode
|
||||
#define EMS_VALUE_UBAParameterWW_wwComfort_Comfort 0x00 // the value for comfort
|
||||
#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco
|
||||
|
||||
/*
|
||||
* Thermostat...
|
||||
*/
|
||||
|
||||
// Common for all thermostats
|
||||
#define EMS_TYPE_RCTime 0x06 // is an automatic thermostat broadcast
|
||||
#define EMS_TYPE_RCOutdoorTempMessage 0xA3 // is an automatic thermostat broadcast, outdoor external temp
|
||||
|
||||
// RC20 specific
|
||||
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC20Set 0xA8 // for setting values like temp and mode
|
||||
#define EMS_OFFSET_RC20Set_mode 23 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC20Set_temp 28 // position of thermostat setpoint temperature
|
||||
#define EMS_TYPE_RC20StatusMessage_setpoint 1 // setpoint temp
|
||||
#define EMS_TYPE_RC20StatusMessage_curr 2 // current temp
|
||||
|
||||
// RC30 specific
|
||||
#define EMS_TYPE_RC30StatusMessage 0x41 // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC30Set 0xA7 // for setting values like temp and mode
|
||||
#define EMS_OFFSET_RC30Set_mode 23 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC30Set_temp 28 // position of thermostat setpoint temperature
|
||||
#define EMS_TYPE_RC30StatusMessage_setpoint 1 // setpoint temp
|
||||
#define EMS_TYPE_RC30StatusMessage_curr 2 // current temp
|
||||
|
||||
// RC35 specific
|
||||
#define EMS_TYPE_RC35StatusMessage 0x3E // is an automatic thermostat broadcast giving us temps
|
||||
#define EMS_TYPE_RC35StatusMessage_setpoint 2 // desired temp
|
||||
#define EMS_TYPE_RC35StatusMessage_curr 3 // current temp
|
||||
#define EMS_TYPE_RC35Set 0x3D // for setting values like temp and mode (Working mode HC1)
|
||||
#define EMS_OFFSET_RC35Set_mode 7 // position of thermostat mode
|
||||
#define EMS_OFFSET_RC35Set_temp_day 2 // position of thermostat setpoint temperature for day time
|
||||
#define EMS_OFFSET_RC35Set_temp_night 1 // position of thermostat setpoint temperature for night time
|
||||
|
||||
// Easy specific
|
||||
#define EMS_TYPE_EasyStatusMessage 0x0A // reading values on an Easy Thermostat
|
||||
#define EMS_TYPE_EasyStatusMessage_setpoint 10 // setpoint temp
|
||||
#define EMS_TYPE_EasyStatusMessage_curr 8 // current temp
|
||||
|
||||
// Known EMS types
|
||||
typedef enum {
|
||||
EMS_MODEL_NONE,
|
||||
EMS_MODEL_ALL, // common for all devices
|
||||
|
||||
// service key
|
||||
EMS_MODEL_SERVICEKEY, // this is us
|
||||
|
||||
// main buderus boiler type devices
|
||||
EMS_MODEL_BK15,
|
||||
EMS_MODEL_UBA,
|
||||
EMS_MODEL_BC10,
|
||||
EMS_MODEL_BC25,
|
||||
EMS_MODEL_MM10,
|
||||
EMS_MODEL_WM10,
|
||||
EMS_MODEL_RFM20,
|
||||
|
||||
// thermostats
|
||||
EMS_MODEL_ES73,
|
||||
EMS_MODEL_RC20,
|
||||
EMS_MODEL_RC20F,
|
||||
EMS_MODEL_RC30,
|
||||
EMS_MODEL_RC35,
|
||||
EMS_MODEL_EASY
|
||||
|
||||
} _EMS_MODEL_ID;
|
||||
|
||||
// EMS types for known Buderus devices. This list will be extended when new devices are recognized.
|
||||
// format is MODEL_ID, PRODUCT ID, TYPE_ID, DESCRIPTION
|
||||
const _Model_Type Model_Types[] = {
|
||||
|
||||
// me
|
||||
{EMS_MODEL_SERVICEKEY, 999, 0x0B, "Service Key"},
|
||||
|
||||
// various boilers and buderus type devices
|
||||
{EMS_MODEL_UBA, 123, 0x08, "MC10/UBA3 Boiler"},
|
||||
{EMS_MODEL_BK15, 64, 0x08, "Sieger BK15 Boiler"},
|
||||
{EMS_MODEL_BC10, 190, 0x09, "BC10 Base Controller"},
|
||||
{EMS_MODEL_BC25, 125, 0x09, "BC25 Base Controller"},
|
||||
{EMS_MODEL_RFM20, 68, 0x09, "RFM20 RC20F Receiver"},
|
||||
{EMS_MODEL_MM10, 251, 0x21, "MM10 Mixer Module"}, // warning, fake product id!
|
||||
{EMS_MODEL_WM10, 250, 0x11, "WM10 Switch Module"}, // warning, fake product id!
|
||||
|
||||
// controllers and thermostats
|
||||
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73"},
|
||||
{EMS_MODEL_RC20, 77, 0x17, "RC20 (e.g. Nefit Moduline 300)"},
|
||||
{EMS_MODEL_RC20F, 93, 0x18, "RC20F"},
|
||||
{EMS_MODEL_RC30, 78, 0x10, "RC30 (e.g. Nefit Moduline 400)"},
|
||||
{EMS_MODEL_RC35, 86, 0x10, "RC35 (or compatible"},
|
||||
{EMS_MODEL_EASY, 202, 0x18, "TC100 (e.g. Nefit Easy or CT100)"}
|
||||
|
||||
};
|
||||
/*
|
||||
* Known thermostat types and their abilities
|
||||
*/
|
||||
const _Thermostat_Type Thermostat_Types[] = {
|
||||
|
||||
{EMS_MODEL_RC20, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC20F, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC30, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_RC35, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
|
||||
{EMS_MODEL_EASY, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO},
|
||||
{EMS_MODEL_ES73, EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}
|
||||
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
* emsuart.cpp
|
||||
*
|
||||
* The low level UART code for ESP8266 to read and write to the EMS bus via uart
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP-Boiler
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
|
||||
#include "emsuart.h"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* emsuart.h
|
||||
* Header file for emsuart.cpp
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP-Boiler
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,57 +1,43 @@
|
||||
/*
|
||||
* my_config.h
|
||||
*
|
||||
* All configurations and customization's go here
|
||||
*
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP-Boiler
|
||||
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// these are set as -D build flags during compilation
|
||||
// they can be set in platformio.ini or alternatively hard coded here
|
||||
/*
|
||||
#define WIFI_SSID "<my_ssid>"
|
||||
#define WIFI_PASSWORD "<my_password>"
|
||||
#define MQTT_IP "<broker_ip>"
|
||||
#define MQTT_USER "<broker_username>"
|
||||
#define MQTT_PASS "<broker_password>"
|
||||
*/
|
||||
#include "ems.h"
|
||||
|
||||
// Set your wifi and mqtt params
|
||||
// If set to NULL (default) you'll need to set them in AP mode using the 'set' command
|
||||
// as these values are stored in SPIFFs for persisted
|
||||
#define WIFI_SSID NULL
|
||||
#define WIFI_PASSWORD NULL
|
||||
#define MQTT_IP NULL
|
||||
#define MQTT_USER NULL
|
||||
#define MQTT_PASS NULL
|
||||
|
||||
// All MQTT topics are prefixed with the following string
|
||||
#define MQTT_BASE "home"
|
||||
|
||||
// default values for shower logic on/off
|
||||
#define BOILER_SHOWER_TIMER 1 // monitors how long a shower has taken
|
||||
#define BOILER_SHOWER_ALERT 0 // send alert if showetime exceeded
|
||||
#define BOILER_SHOWER_TIMER 1 // enable (1) to monitor shower time
|
||||
#define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold watewr when shower time limit has exceeded
|
||||
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
||||
|
||||
// trigger settings to determine if hot tap water or the heating is active
|
||||
#define EMS_BOILER_BURNPOWER_TAPWATER 100
|
||||
#define EMS_BOILER_SELFLOWTEMP_HEATING 70
|
||||
|
||||
//define maximum settable tapwater temperature, not every installation supports 90 degrees
|
||||
#define EMS_BOILER_TAPWATER_TEMPERATURE_MAX 60
|
||||
|
||||
// if using the shower timer, change these settings
|
||||
#define SHOWER_PAUSE_TIME 15000 // in ms. 15 seconds, max time if water is switched off & on during a shower
|
||||
#define SHOWER_MIN_DURATION 120000 // in ms. 2 minutes, before recognizing its a shower
|
||||
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
|
||||
#define SHOWER_OFFSET_TIME 5000 // in ms. 5 seconds grace time, to calibrate actual time under the shower
|
||||
#define SHOWER_COLDSHOT_DURATION 10 // in seconds. 10 seconds for cold water before turning back hot water
|
||||
|
||||
// The LED for showing connection errors, either onboard or via an external pull-up LED
|
||||
// can be disabled using -DNO_LED build flag in platformio.ini
|
||||
// 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 or external via an external pull-up LED (e.g. D1)
|
||||
// note: can be disabled completely using -DNO_LED build flag in platformio.ini
|
||||
#define BOILER_LED LED_BUILTIN // LED_BULLETIN for onboard LED
|
||||
//#define BOILER_LED D1 // for external LED like on the latest bbqkees boards
|
||||
|
||||
// set this if using an external temperature sensor like a DS18B20
|
||||
#define TEMPERATURE_SENSOR_PIN D7
|
||||
|
||||
// logging - EMS_SYS_LOGGING_VERBOSE, EMS_SYS_LOGGING_NONE, EMS_SYS_LOGGING_BASIC (see ems.h)
|
||||
// this can be changed via the Telnet console using the 'l' command
|
||||
#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_NONE
|
||||
//#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_VERBOSE
|
||||
//#define BOILER_DEFAULT_LOGGING EMS_SYS_LOGGING_BASIC
|
||||
|
||||
// By default the EMS bus will be scanned for known devices. You can override this here
|
||||
// by fixing the Boiler and Thermostat types
|
||||
// Options are in ems.h and include..
|
||||
// By default the EMS bus will be scanned for known devices (EMS_MODEL_NONE).
|
||||
// You can override this here by fixing the Boiler and Thermostat types.
|
||||
// See ems.h for the list of recognized types. For example:
|
||||
// boilers: EMS_MODEL_BK15, EMS_MODEL_UBA, EMS_MODEL_BC10, EMS_MODEL_MM10, EMS_MODEL_WM10
|
||||
// thermostats: EMS_MODEL_ES73, EMS_MODEL_RC20, EMS_MODEL_RC30, EMS_MODEL_RC35, EMS_MODEL_EASY
|
||||
#define MY_BOILER_MODELID EMS_MODEL_NONE
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define APP_NAME "EMS-ESP-Boiler"
|
||||
#define APP_VERSION "1.2.4"
|
||||
#define APP_HOSTNAME "boiler"
|
||||
#define APP_NAME "EMS-ESP Interface"
|
||||
#define APP_VERSION "1.3.0"
|
||||
#define APP_HOSTNAME "ems-esp"
|
||||
|
||||
Reference in New Issue
Block a user