This commit is contained in:
proddy
2019-03-24 21:13:08 +01:00
parent 9d3ce7bcf0
commit d671e64c5f
23 changed files with 2286 additions and 1003 deletions

3
.gitignore vendored
View File

@@ -6,4 +6,5 @@
platformio.ini
lib/readme.txt
.travis.yml
*.py
stackdmp.txt
*.jar

View File

@@ -5,6 +5,35 @@ 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.6.0] 2019-03-24
### Added
- `system` command to show ESP8266 stats
- `crash` command to see stack of last system crash, with .py files to track stack dump (compile with `-DCRASH`)
- publish dallas external temp sensors to MQTT (thanks @JewelZB)
- shower timer and shower alert options available via set commands
- added support for warm water modes Hot, Comfort and Intelligent [(issue 67)](https://github.com/proddy/EMS-ESP/issues/67)
- added `set publish_time` to set how often to publish MQTT
- support for SM10 Solar Module including MQTT [(issue 77)](https://github.com/proddy/EMS-ESP/issues/77)
- `refresh` command to force a fetch of all known data from the connected EMS devices
### Fixed
- incorrect rendering of null temperature values (the -3200 degrees issue)
- OTA is more stable
- Added a hack to overcome WiFi power issues in arduino core 2.5.0 libraries causing constant wifi re-connects
- Performance issues with telnet output
### Changed
- included various fixes and suggestions from @nomis
- upgraded MyESP library with many optimizations
- `test_mode` renamed to `silent_mode`
- `set wifi` replaced with `set wifi_ssid` and `set wifi_password` to allow values with spaces
- EMS values are stored in the raw format and only converted to strings when displayed or published, removing the need for parsing floats
- All floating point temperatures are to one decimal place [(issue 79)](https://github.com/proddy/EMS-ESP/issues/79)
## [1.5.6] 2019-03-09
### Added
@@ -16,7 +45,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- upgraded MyESP library
- minor changes
## [1.5.5] 2019-03-07
### Fixed

View File

@@ -5,7 +5,7 @@ EMS-ESP is a project to build an electronic controller circuit using an Espressi
There are 3 parts to this project, first the design of the circuit, secondly the code for the ESP8266 microcontroller firmware with telnet and MQTT support, and lastly an example configuration for Home Assistant to monitor the data and issue direct commands via a MQTT broker.
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/b8880625bdf841d4adb2829732030887)](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)
[![version](https://img.shields.io/badge/version-1.5.5-brightgreen.svg)](CHANGELOG.md)
[![version](https://img.shields.io/badge/version-1.6.0-brightgreen.svg)](CHANGELOG.md)
- [EMS-ESP](#ems-esp)
- [Introduction](#introduction)
@@ -30,7 +30,6 @@ There are 3 parts to this project, first the design of the circuit, secondly the
- [Home Assistant Configuration](#home-assistant-configuration)
- [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)
@@ -64,14 +63,14 @@ The code and circuit has been tested with a few ESP8266 development boards such
1. Either build the circuit described below or purchase a ready built board from bbqkees.
2. Grab any ESP8266 dev board. The latest bbqkees boards have a Wemos D1 pre-mounted with a copy of this firmware.
3. Optionally add external Dallas temperature sensors and an external LED. The default pins for these are D1 and D5 respectively.
4. Decide whether to compile and upload the code yourself using PlatformIO or just upload the pre-baked firmware using the esptool (read these [instructions](#using-the-pre-built-firmware)). If you want to build yourself now is the time to customize your settings in `my_custom.h`. Upload the firmware.
5. Connect a USB 5v power supply to the ESP8266 board, either via laptop/PC or external power supply.
7. When the ESP8266 starts up for the first time the onboard LED will be flashing. This is because the EMS bus is not yet connected.
3. Optionally add external Dallas temperature sensors (to D1) and an external LED (to D5).
4. Decide whether to compile and upload the code yourself using PlatformIO or just upload the pre-baked firmware using the esptool (read these [instructions](#using-the-pre-built-firmware)). If you want to build yourself now is the time to customize your settings in `my_custom.h`. Upload the firmware via USB.
5. Connect an external USB 5v power adapter to the ESP8266 board.
7. When the ESP8266 starts up for the first time the onboard LED will be flashing. This is because the EMS bus is not yet connected and receiving data.
8. If you haven't hardcoded the WiFi credentials in step 4, the ESP8266 will boot up in a WiFi Access Point (AP) mode with the ssid name `ems-esp`. Now you can either use a laptop and connect to this AP using Telnet to `192.168.1.4` or if its powered from a computers USB use a Serial monitor tool to the ESP's COM port. Tip: to enable Telnet on Windows 10 run `dism /online /Enable-Feature /FeatureName:TelnetClient` or install something like [putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html).
9. Next is to change some of the settings. Type `set` to list the current stored settings. Use `set wifi` to add your wifi credentials and if you're using MQTT set the host, username and password. There is no need to reboot the device.
9. Next is to customize some of the onboard settings. Type `set` to list the current stored settings and `?` to see the syntax. Use `set wifi_ssid` and `set wifi_password` to add your WiFi credentials and if you're using MQTT set the host, username and password. There is no need to reboot the ESP.
10. The `led_gpio` will default to the onboard LED (which is probably blinking now). Ignore `thermostat_type` and `boiler_type` as these will be auto-detected hopefully later on.
11. **Important**: If `serial` is set to `on` set it to `off` using `set serial off`. The EMS bus is disabled when the serial is on. This mode is only used for setting up a new board or debugging startup issues.
11. **Important**: By default the serial port is enabled and the EMS bus disabled. This is to allow users to configure their ESP via the serial monitor when pluged into a PC/laptop. You must disable serial with `set serial off` to get the EMS transmission working.
12. Hook up the ESP to the EMS board as follows:
| EMS board | ESP8266 dev board |
@@ -79,11 +78,11 @@ The code and circuit has been tested with a few ESP8266 development boards such
| Ground/G/J2| GND/G |
| Rx/J2 | D7 |
| Tx/J2 | D8 |
| VC/J2 | 3v3 or 5v |
13. Connect the EMS lines to the ESP. This can be done via the two EMS wires or via the 3.5" service jack if you have an bbqkees board.
| VC/J2 | 3v3 |
13. Connect the EMS lines to the ESP. This can be done via the two EMS wires or via the 3.5mm service jack if you have an bbqkees board.
14. Reboot the ESP, either by the reset switch or pulling the power.
15. The ESP will first perform an autodetect to try and discover the EMS devices attached. If your boiler and thermostat are recognized it will set these types and store them for ever and ever. You can trace the output by telnet'ing to the board `telnet ems-esp.local`. Also type `info` to check what happened.
16. If your boiler/thermostat is not discovered create a GitHub issue stating the type and product ID. These will be added to the file `ems_devices.h` in a future release.
15. The ESP will first perform an autodetect to try and discover the EMS devices attached. If your boiler and thermostat are recognized it will set these types and store them for ever and ever. You can trace the output by telnet'ing to the board `telnet ems-esp.local`. Also use `info` to check the status.
16. If your boiler/thermostat is not discovered create a GitHub issue stating the type and Product ID. These will be added to the file `ems_devices.h` in a future release.
17. If all is well and there is traffic on the EMS bus the onboard LED will stop blinking and be permanently on. If this is annoying you can disable with `set led off`. To see the EMS messages type `set log v` for verbose logging.
18. And all is not well, check the wiring, make sure serial is off and look at the telnet session for errors. If in doubt, wipe the ESP with `pio run -t erase` and start again with step #3
@@ -127,8 +126,8 @@ The EMS circuit will work with both 3.3V and 5V. It's easiest though to power di
- via the USB if your dev board has one
- using an external 5V power supply into the 5V vin on the board
- powering from the 3.5" service jack on the boiler. This will give you 8V so you need a buck converter (like a [Pololu D24C22F5](https://www.pololu.com/product/2858)) to step this down to 5V to provide enough power to the ESP8266 (250mA at least)
- powering from the EMS line, which is 15V A/C and using a buck converter as described above. Note the current design has stability issues when sending packages in this configuration so this is not recommended yet if you plan to many send commands to the thermostat or boiler.
- powering from the 3.5mm service jack (stereo jack) on the boiler. This will give you 8V so you need a buck converter (like a [Pololu D24C22F5](https://www.pololu.com/product/2858)) to step this down to 5V to provide enough power to the ESP8266 (250mA at least)
- powering direct from the EMS line, which is 15V DC and using a buck converter as described above.
| With Power Circuit |
| ------------------------------------------ |
@@ -203,13 +202,15 @@ Every telegram sent is echo'd back to Rx, along the same Bus used for all Rx/Tx
`ems.cpp` is the logic to read the EMS data packets (telegrams), validates them and process them based on the type.
`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.
`ems-esp.cpp` 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.
`my_config.h` has all the custom settings tailored to your environment. Specific values here are also stored in the ESP's SPIFFs (File system).
`ems_devices.h` has all the configuration for the known EMS devices currently supported.
`MyESP.cpp` is my custom library to handle WiFi, MQTT and Telnet. Uses a modified version of [TelnetSpy](https://github.com/yasheena/telnetspy)
`MyESP.cpp` is my custom library to handle WiFi, MQTT and Telnet. Uses a modified version of [TelnetSpy](https://github.com/yasheena/telnetspy).
`ds18.*` are the Dallas libraries for any external temperature sensors.
### Special EMS Types
@@ -228,18 +229,15 @@ In `ems.cpp` you can add scheduled calls to specific EMS types in the functions
I am still working on adding more support to known thermostats. Any contributions here are welcome. The know types are listed in `ems_devices.h` and include
- RC20 and RC30, both are fully supported
- RC10 support is being added
- RC10, RC20 and RC30 are fully supported
- RC35 with support for the 1st heating circuit (HC1)
- TC100/TC200/Easy but only with support for reading the temperatures. There seems to be no way to set settings using EMS bus messages that I know of. One option is to send XMPP messages but a special server is needed and out of scope for this project.
- TC100/TC200/Easy but only with support for *reading* the temperature values. There seems to be no way to set settings using EMS bus messages that I know of. One option is to send XMPP messages but a special server is needed and out of scope for this project.
### Customizing The Code
- To configure for your thermostat and specific boiler settings, modify `my_config.h`. Here you can
- set flags for enabled/disabling functionality such as `BOILER_SHOWER_ENABLED` and `BOILER_SHOWER_TIMER`.
- Set WIFI and MQTT settings. The values can also be set from the telnet command menu using the **set** command.
- 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`
- To configure for your thermostat and specific boiler settings, modify `my_config.h`.
- Most values can also be set from the telnet command menu using the **set** command.
- 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`. Also add to `ems_devices.h`.
### Using MQTT
@@ -302,6 +300,7 @@ Make sure Python 2.7 is installed, then...
% pip install -U platformio
% sudo platformio upgrade
% platformio platform update
% platformio lib upgrade
% git clone https://github.com/proddy/EMS-ESP.git
% cd EMS-ESP
@@ -312,17 +311,6 @@ edit `platformio.ini` to set `env_default` to your board type, then
% platformio run -t upload
```
### Building Using Arduino IDE
Porting to the Arduino IDE can be a little tricky but it did it once. Something along these lines:
- Add the ESP8266 boards (from Preferences add Additional Board URL `http://arduino.esp8266.com/stable/package_esp8266com_index.json`)
- Go to Boards Manager and install ESP8266 2.4.x platform. Make sure your board supports SPIFFS.
- Select your ESP8266 from Tools->Boards and the correct port with Tools->Port
- From the Library Manager install the needed libraries from platformio.ini. Note make sure you pick ArduinoJson v5 (5.13.4 and above) and not v6. See https://arduinojson.org/v5/doc/
- Put all the files in a single sketch folder
- cross your fingers and hit CTRL-R to compile
## Using the Pre-built Firmware
pre-baked firmware for the Wemos D1 mini is available in the GitHub [releases](https://github.com/proddy/EMS-ESP/releases) which you can upload yourself using the [esptool](https://github.com/espressif/esptool) bootloader like `esptool.py -p <com port> write_flash 0x00000 <firmware.bin file>`. Here's how to set it up on Windows:
@@ -330,7 +318,7 @@ pre-baked firmware for the Wemos D1 mini is available in the GitHub [releases](h
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. Then install the ESPTool by running `pip install esptool` from a command prompt
The ESP8266 will start in Access Point (AP) mode. Connect via WiFi to the SSID **EMS-ESP** and telnet to **192.168.4.1**. Then use the `set wifi` command to configure your own network settings like `set wifi your_ssid your_password`. Alternatively connect the ESP8266 to your PC and open a Serial monitor (with baud 115200) to configure the settings. Make sure you disable Serial support before connecting the EMS lines using `set serial off`.
The ESP8266 will start in Access Point (AP) mode. Connect via WiFi to the SSID **EMS-ESP** and telnet to **192.168.4.1**. Then use the `set wifi_ssid/set wifi_password` command to configure your own network settings. Alternatively connect the ESP8266 to your PC and open a Serial monitor (with baud 115200) to configure the settings. Make sure you disable Serial support before connecting the EMS lines using `set serial off`.
`set` wil list all currently stored settings.

26
checkcode.py Normal file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env python
from subprocess import call
import os
Import("env")
def code_check(source, target, env):
print("\n** Starting cppcheck...")
call(["cppcheck", os.getcwd()+"/.", "--force", "--enable=all"])
print("\n** Finished cppcheck...\n")
print("\n** Starting cpplint...")
call(["cpplint", "--extensions=ino,cpp,h", "--filter=-legal/copyright,-build/include,-whitespace",
"--linelength=120", "--recursive", "src", "lib/myESP"])
print("\n** Finished cpplint...")
#my_flags = env.ParseFlags(env['BUILD_FLAGS'])
#defines = {k: v for (k, v) in my_flags.get("CPPDEFINES")}
# print defines
# print env.Dump()
# built in targets: (buildprog, size, upload, program, buildfs, uploadfs, uploadfsota)
env.AddPreAction("buildprog", code_check)
# env.AddPostAction(.....)
# 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'])

14
clean_fw.py Normal file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env python
from subprocess import call
import os
Import("env")
def clean(source, target, env):
print("\n** Starting clean...")
call(["pio", "run", "-t", "erase"])
call(["esptool.py", "-p COM6", "write_flash 0x00000", os.getcwd()+"../firmware/*.bin"])
print("\n** Finished clean.")
# built in targets: (buildprog, size, upload, program, buildfs, uploadfs, uploadfsota)
env.AddPreAction("buildprog", clean)

22
debug.py Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
from subprocess import call
import os
# example stackdmp.txt would contain text like below copied & pasted from a 'crash dump' command
# >>>stack>>>
# 3fffff20: 3fff32f0 00000003 3fff3028 402101b2
# 3fffff30: 3fffdad0 3fff3280 0000000d 402148aa
# 3fffff40: 3fffdad0 3fff3280 3fff326c 3fff32f0
# 3fffff50: 0000000d 3fff326c 3fff3028 402103bd
# 3fffff60: 0000000d 3fff34cc 40211de4 3fff34cc
# 3fffff70: 3fff3028 3fff14c4 3fff301c 3fff34cc
# 3fffff80: 3fffdad0 3fff14c4 3fff3028 40210493
# 3fffff90: 3fffdad0 00000000 3fff14c4 4020a738
# 3fffffa0: 3fffdad0 00000000 3fff349c 40211e90
# 3fffffb0: feefeffe feefeffe 3ffe8558 40100b01
# <<<stack<<<
# java -jar .\EspStackTraceDecoder.jar C:\Users\Paul\.platformio\packages\toolchain-xtensa\bin\xtensa-lx106-elf-addr2line.exe .pioenvs/d1_mini/firmware_d1_mini.elf stackdmp.txt
# python decoder.py -p ESP8266 -t C:\Users\Paul\.platformio\packages\toolchain-xtensa -e .pioenvs/nodemcuv2/firmware.elf stackdmp.txt
call(['python', 'decoder.py ', '-s', '-e', os.getcwd()+"/.pioenvs/d1_mini/firmware_d1_mini.elf", 'stackdmp.txt'])

307
decoder.py Normal file
View File

@@ -0,0 +1,307 @@
#!/usr/bin/env python3
"""ESP Exception Decoder
github: https://github.com/janLo/EspArduinoExceptionDecoder
license: GPL v3
author: Jan Losinski
"""
import argparse
import re
import subprocess
from collections import namedtuple
import sys
import os
EXCEPTIONS = [
"Illegal instruction",
"SYSCALL instruction",
"InstructionFetchError: Processor internal physical address or data error during instruction fetch",
"LoadStoreError: Processor internal physical address or data error during load or store",
"Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register",
"Alloca: MOVSP instruction, if caller's registers are not in the register file",
"IntegerDivideByZero: QUOS, QUOU, REMS, or REMU divisor operand is zero",
"reserved",
"Privileged: Attempt to execute a privileged operation when CRING ? 0",
"LoadStoreAlignmentCause: Load or store to an unaligned address",
"reserved",
"reserved",
"InstrPIFDataError: PIF data error during instruction fetch",
"LoadStorePIFDataError: Synchronous PIF data error during LoadStore access",
"InstrPIFAddrError: PIF address error during instruction fetch",
"LoadStorePIFAddrError: Synchronous PIF address error during LoadStore access",
"InstTLBMiss: Error during Instruction TLB refill",
"InstTLBMultiHit: Multiple instruction TLB entries matched",
"InstFetchPrivilege: An instruction fetch referenced a virtual address at a ring level less than CRING",
"reserved",
"InstFetchProhibited: An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch",
"reserved",
"reserved",
"reserved",
"LoadStoreTLBMiss: Error during TLB refill for a load or store",
"LoadStoreTLBMultiHit: Multiple TLB entries matched for a load or store",
"LoadStorePrivilege: A load or store referenced a virtual address at a ring level less than CRING",
"reserved",
"LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads",
"StoreProhibited: A store referenced a page mapped with an attribute that does not permit stores"
]
PLATFORMS = {
"ESP8266": "lx106",
"ESP32": "esp32"
}
EXCEPTION_REGEX = re.compile("^Exception \\((?P<exc>[0-9]*)\\):$")
COUNTER_REGEX = re.compile('^epc1=(?P<epc1>0x[0-9a-f]+) epc2=(?P<epc2>0x[0-9a-f]+) epc3=(?P<epc3>0x[0-9a-f]+) '
'excvaddr=(?P<excvaddr>0x[0-9a-f]+) depc=(?P<depc>0x[0-9a-f]+)$')
CTX_REGEX = re.compile("^ctx: (?P<ctx>.+)$")
POINTER_REGEX = re.compile('^sp: (?P<sp>[0-9a-f]+) end: (?P<end>[0-9a-f]+) offset: (?P<offset>[0-9a-f]+)$')
STACK_BEGIN = '>>>stack>>>'
STACK_END = '<<<stack<<<'
STACK_REGEX = re.compile(
'^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$')
StackLine = namedtuple("StackLine", ["offset", "content"])
class ExceptionDataParser(object):
def __init__(self):
self.exception = None
self.epc1 = None
self.epc2 = None
self.epc3 = None
self.excvaddr = None
self.depc = None
self.ctx = None
self.sp = None
self.end = None
self.offset = None
self.stack = []
def _parse_exception(self, line):
match = EXCEPTION_REGEX.match(line)
if match is not None:
self.exception = int(match.group('exc'))
return self._parse_counters
return self._parse_exception
def _parse_counters(self, line):
match = COUNTER_REGEX.match(line)
if match is not None:
self.epc1 = match.group("epc1")
self.epc2 = match.group("epc2")
self.epc3 = match.group("epc3")
self.excvaddr = match.group("excvaddr")
self.depc = match.group("depc")
return self._parse_ctx
return self._parse_counters
def _parse_ctx(self, line):
match = CTX_REGEX.match(line)
if match is not None:
self.ctx = match.group("ctx")
return self._parse_pointers
return self._parse_ctx
def _parse_pointers(self, line):
match = POINTER_REGEX.match(line)
if match is not None:
self.sp = match.group("sp")
self.end = match.group("end")
self.offset = match.group("offset")
return self._parse_stack_begin
return self._parse_pointers
def _parse_stack_begin(self, line):
if line == STACK_BEGIN:
return self._parse_stack_line
return self._parse_stack_begin
def _parse_stack_line(self, line):
if line != STACK_END:
match = STACK_REGEX.match(line)
if match is not None:
self.stack.append(StackLine(offset=match.group("off"),
content=(match.group("c1"), match.group("c2"), match.group("c3"),
match.group("c4"))))
return self._parse_stack_line
return None
def parse_file(self, file, stack_only=False):
func = self._parse_exception
if stack_only:
func = self._parse_stack_begin
for line in file:
func = func(line.strip())
if func is None:
break
if func is not None:
print("ERROR: Parser not complete!")
sys.exit(1)
class AddressResolver(object):
def __init__(self, tool_path, elf_path):
self._tool = tool_path
self._elf = elf_path
self._address_map = {}
def _lookup(self, addresses):
cmd = [self._tool, "-aipfC", "-e", self._elf] + [addr for addr in addresses if addr is not None]
if sys.version_info[0] < 3:
output = subprocess.check_output(cmd)
else:
output = subprocess.check_output(cmd, encoding="utf-8")
line_regex = re.compile("^(?P<addr>[0-9a-fx]+): (?P<result>.+)$")
last = None
for line in output.splitlines():
line = line.strip()
match = line_regex.match(line)
if match is None:
if last is not None and line.startswith('(inlined by)'):
line = line [12:].strip()
self._address_map[last] += ("\n \-> inlined by: " + line)
continue
if match.group("result") == '?? ??:0':
continue
self._address_map[match.group("addr")] = match.group("result")
last = match.group("addr")
def fill(self, parser):
addresses = [parser.epc1, parser.epc2, parser.epc3, parser.excvaddr, parser.sp, parser.end, parser.offset]
for line in parser.stack:
addresses.extend(line.content)
self._lookup(addresses)
def _sanitize_addr(self, addr):
if addr.startswith("0x"):
addr = addr[2:]
fill = "0" * (8 - len(addr))
return "0x" + fill + addr
def resolve_addr(self, addr):
out = self._sanitize_addr(addr)
if out in self._address_map:
out += ": " + self._address_map[out]
return out
def resolve_stack_addr(self, addr, full=True):
addr = self._sanitize_addr(addr)
if addr in self._address_map:
return addr + ": " + self._address_map[addr]
if full:
return "[DATA (0x" + addr + ")]"
return None
def print_addr(name, value, resolver):
print("{}:{} {}".format(name, " " * (8 - len(name)), resolver.resolve_addr(value)))
def print_stack_full(lines, resolver):
print("stack:")
for line in lines:
print(line.offset + ":")
for content in line.content:
print(" " + resolver.resolve_stack_addr(content))
def print_stack(lines, resolver):
print("stack:")
for line in lines:
for content in line.content:
out = resolver.resolve_stack_addr(content, full=False)
if out is None:
continue
print(out)
def print_result(parser, resolver, full=True, stack_only=False):
if not stack_only:
print('Exception: {} ({})'.format(parser.exception, EXCEPTIONS[parser.exception]))
print("")
print_addr("epc1", parser.epc1, resolver)
print_addr("epc2", parser.epc2, resolver)
print_addr("epc3", parser.epc3, resolver)
print_addr("excvaddr", parser.excvaddr, resolver)
print_addr("depc", parser.depc, resolver)
print("")
print("ctx: " + parser.ctx)
print("")
print_addr("sp", parser.sp, resolver)
print_addr("end", parser.end, resolver)
print_addr("offset", parser.offset, resolver)
print("")
if full:
print_stack_full(parser.stack, resolver)
else:
print_stack(parser.stack, resolver)
def parse_args():
parser = argparse.ArgumentParser(description="decode ESP Stacktraces.")
parser.add_argument("-p", "--platform", help="The platform to decode from", choices=PLATFORMS.keys(),
default="ESP8266")
parser.add_argument("-t", "--tool", help="Path to the xtensa toolchain",
default="~/.platformio/packages/toolchain-xtensa/")
parser.add_argument("-e", "--elf", help="path to elf file", required=True)
parser.add_argument("-f", "--full", help="Print full stack dump", action="store_true")
parser.add_argument("-s", "--stack_only", help="Decode only a stractrace", action="store_true")
parser.add_argument("file", help="The file to read the exception data from ('-' for STDIN)", default="-")
return parser.parse_args()
if __name__ == "__main__":
args = parse_args()
if args.file == "-":
file = sys.stdin
else:
if not os.path.exists(args.file):
print("ERROR: file " + args.file + " not found")
sys.exit(1)
file = open(args.file, "r")
addr2line = os.path.join(os.path.abspath(os.path.expanduser(args.tool)),
"bin/xtensa-" + PLATFORMS[args.platform] + "-elf-addr2line.exe")
if not os.path.exists(addr2line):
print("ERROR: addr2line not found (" + addr2line + ")")
elf_file = os.path.abspath(os.path.expanduser(args.elf))
if not os.path.exists(elf_file):
print("ERROR: elf file not found (" + elf_file + ")")
parser = ExceptionDataParser()
resolver = AddressResolver(addr2line, elf_file)
parser.parse_file(file, args.stack_only)
resolver.fill(parser)
print_result(parser, resolver, args.full, args.stack_only)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
#ifndef MyEMS_h
#define MyEMS_h
#define MYESP_VERSION "1.1.5"
#define MYESP_VERSION "1.1.6"
#include <ArduinoJson.h>
#include <ArduinoOTA.h>
@@ -19,6 +19,13 @@
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
#ifdef CRASH
#include <EEPROM_Rotate.h>
extern "C" {
void custom_crash_callback(struct rst_info *, uint32_t, uint32_t);
}
#endif
#if defined(ARDUINO_ARCH_ESP32)
//#include <ESPmDNS.h>
#include <SPIFFS.h> // added for ESP32
@@ -68,13 +75,44 @@
#define COLOR_CYAN "\x1B[0;36m"
#define COLOR_WHITE "\x1B[0;37m"
#define COLOR_BOLD_ON "\x1B[1m"
#define COLOR_BOLD_OFF "\x1B[22m" // fixed by Scott Arlott
#define COLOR_BOLD_OFF "\x1B[22m" // fix by Scott Arlott to support Linux
// SPIFFS
#define SPIFFS_MAXSIZE 500 // https://arduinojson.org/v5/assistant/
#define SPIFFS_MAXSIZE 600 // https://arduinojson.org/v6/assistant/
// CRASH
/**
* Structure of the single crash data set
*
* 1. Crash time
* 2. Restart reason
* 3. Exception cause
* 4. epc1
* 5. epc2
* 6. epc3
* 7. excvaddr
* 8. depc
* 9. address of stack start
* 10. address of stack end
* 11. stack trace bytes
* ...
*/
#define SAVE_CRASH_EEPROM_OFFSET 0x0100 // initial address for crash data
#define SAVE_CRASH_CRASH_TIME 0x00 // 4 bytes
#define SAVE_CRASH_RESTART_REASON 0x04 // 1 byte
#define SAVE_CRASH_EXCEPTION_CAUSE 0x05 // 1 byte
#define SAVE_CRASH_EPC1 0x06 // 4 bytes
#define SAVE_CRASH_EPC2 0x0A // 4 bytes
#define SAVE_CRASH_EPC3 0x0E // 4 bytes
#define SAVE_CRASH_EXCVADDR 0x12 // 4 bytes
#define SAVE_CRASH_DEPC 0x16 // 4 bytes
#define SAVE_CRASH_STACK_START 0x1A // 4 bytes
#define SAVE_CRASH_STACK_END 0x1E // 4 bytes
#define SAVE_CRASH_STACK_TRACE 0x22 // variable
typedef struct {
char key[40];
bool set; // is it a set command
char key[50];
char description[100];
} command_t;
@@ -94,6 +132,8 @@ constexpr size_t ArraySize(T (&)[N]) {
return N;
}
#define UPTIME_OVERFLOW 4294967295 // Uptime overflow value
// class definition
class MyESP {
public:
@@ -104,8 +144,10 @@ class MyESP {
void setWIFICallback(void (*callback)());
void setWIFI(const char * wifi_ssid, const char * wifi_password, wifi_callback_f callback);
bool isWifiConnected();
bool isAPmode();
// mqtt
bool isMQTTConnected();
void mqttSubscribe(const char * topic);
void mqttUnsubscribe(const char * topic);
void mqttPublish(const char * topic, const char * payload);
@@ -122,7 +164,7 @@ class MyESP {
mqtt_callback_f callback);
// OTA
void setOTA(ota_callback_f OTACallback);
void setOTA(ota_callback_f OTACallback_pre, ota_callback_f OTACallback_post);
// debug & telnet
void myDebug(const char * format, ...);
@@ -134,6 +176,12 @@ class MyESP {
void setSettings(fs_callback_f callback, fs_settings_callback_f fs_settings_callback);
bool fs_saveConfig();
// Crash
void crashClear();
void crashDump();
void crashTest(uint8_t t);
void crashInfo();
// general
void end();
void loop();
@@ -142,7 +190,7 @@ class MyESP {
void resetESP();
uint16_t getSystemLoadAverage();
int getWifiQuality();
void showSystemStats();
private:
// mqtt
@@ -177,19 +225,24 @@ class MyESP {
char * _wifi_ssid;
char * _wifi_password;
bool _wifi_connected;
String _getESPhostname();
// ota
ota_callback_f _ota_callback;
ota_callback_f _ota_pre_callback;
ota_callback_f _ota_post_callback;
void _ota_setup();
void _OTACallback();
// crash
void _eeprom_setup();
// telnet & debug
TelnetSpy SerialAndTelnet;
void _telnetConnected();
void _telnetDisconnected();
void _telnetHandle();
void _telnetCommand(char * commandLine);
char * _telnet_readWord();
char * _telnet_readWord(bool allow_all_chars);
void _telnet_setup();
char _command[TELNET_MAX_COMMAND_LENGTH]; // the input command from either Serial or Telnet
command_t * _helpProjectCmds; // Help of commands setted by project
@@ -197,8 +250,7 @@ class MyESP {
void _consoleShowHelp();
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
telnet_callback_f _telnet_callback; // callback for connect/disconnect
void _changeSetting(uint8_t wc, const char * setting, const char * value);
void _changeSetting2(const char * setting, const char * value1, const char * value2);
bool _changeSetting(uint8_t wc, const char * setting, const char * value);
// fs
void _fs_setup();
@@ -206,17 +258,20 @@ class MyESP {
void _fs_printConfig();
void _fs_eraseConfig();
// settings
fs_callback_f _fs_callback;
fs_settings_callback_f _fs_settings_callback;
void _printSetCommands();
// general
char * _app_hostname;
char * _app_name;
char * _app_version;
char * _boottime;
bool _suspendOutput;
bool _use_serial;
void _printBuildTime(unsigned long rawTime);
char * _app_hostname;
char * _app_name;
char * _app_version;
char * _boottime;
bool _suspendOutput;
bool _use_serial;
unsigned long _getUptime();
String _buildTime();
// load average (0..100)
void _calculateLoad();

View File

@@ -1,11 +1,14 @@
[platformio]
; add here your board, e.g. nodemcuv2, d1_mini, d1_mini_pro
env_default = d1_mini
[common]
platform = espressif8266
flash_mode = dout
build_flags = -g -w
;build_flags = -g -w -DBUILD_TIME=$UNIX_TIME
; for debug use these...
; build_flags = -g -Wall -Wextra -Werror -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-variable -DCRASH
wifi_settings =
; hard code if you prefer. Recommendation is to set from within the app when in Serial or AP mode
@@ -17,21 +20,21 @@ lib_deps =
JustWifi
AsyncMqttClient
ArduinoJson
; https://github.com/bblanchon/ArduinoJson#v5.13.5
OneWire
EEPROM_rotate
[env:d1_mini]
board = d1_mini
platform = ${common.platform}
platform = espressif8266
framework = arduino
lib_deps = ${common.lib_deps}
build_flags = ${common.build_flags} ${common.wifi_settings}
board_build.flash_mode = ${common.flash_mode}
upload_speed = 921600
monitor_speed = 115200
; for OTA comment out these sections
;upload_protocol = espota
;upload_port = ems-esp.local
;upload_port = <add here your ip of the device>

8
rename_fw.py Normal file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env python
from subprocess import call
import os
Import("env")
# 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'])

View File

@@ -4,9 +4,6 @@
*
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
*
* See ChangeLog.md for history
* See README.md for Acknowledgments
*
*/
#include "ds18.h"
@@ -14,9 +11,10 @@
std::vector<ds_device_t> _devices;
DS18::DS18() {
_wire = NULL;
_count = 0;
_gpio = GPIO_NONE;
_wire = NULL;
_count = 0;
_gpio = GPIO_NONE;
_parasite = 0;
}
DS18::~DS18() {
@@ -25,10 +23,11 @@ DS18::~DS18() {
}
// init
uint8_t DS18::setup(uint8_t gpio) {
uint8_t DS18::setup(uint8_t gpio, bool parasite) {
uint8_t count;
_gpio = gpio;
_gpio = gpio;
_parasite = (parasite ? 1 : 0);
// OneWire
if (_wire)
@@ -62,8 +61,7 @@ void DS18::loop() {
// Start conversion
_wire->reset();
_wire->skip();
_wire->write(DS18_CMD_START_CONVERSION, DS18_PARASITE);
_wire->write(DS18_CMD_START_CONVERSION, _parasite);
} else {
// Read scratchpads
for (unsigned char index = 0; index < _devices.size(); index++) {
@@ -117,7 +115,7 @@ char * DS18::getDeviceString(char * buffer, unsigned char index) {
char a[30] = {0};
snprintf(a,
sizeof(a),
"(%02X%02X%02X%02X%02X%02X%02X%02X) @ GPIO%d",
" (%02X%02X%02X%02X%02X%02X%02X%02X) @ GPIO%d",
address[0],
address[1],
address[2],
@@ -136,7 +134,6 @@ char * DS18::getDeviceString(char * buffer, unsigned char index) {
return buffer;
}
/*
* Read sensor values
*
@@ -154,14 +151,14 @@ char * DS18::getDeviceString(char * buffer, unsigned char index) {
DS18B20 & DS1822: store for crc
byte 8: SCRATCHPAD_CRC
*/
double DS18::getValue(unsigned char index) {
int16_t DS18::getRawValue(unsigned char index) {
if (index >= _count)
return 0;
uint8_t * data = _devices[index].data;
if (OneWire::crc8(data, DS18_DATA_SIZE - 1) != data[DS18_DATA_SIZE - 1]) {
return 0;
return DS18_CRC_ERROR;
}
int16_t raw = (data[1] << 8) | data[0];
@@ -181,11 +178,13 @@ double DS18::getValue(unsigned char index) {
// 12 bit res, 750 ms
}
double value = (float)raw / 16.0;
if (value == DS18_DISCONNECTED) {
return 0;
}
return raw;
}
// return real value as a double
// The raw temperature data is in units of sixteenths of a degree, so the value must be divided by 16 in order to convert it to degrees.
double DS18::getValue(unsigned char index) {
double value = (float)getRawValue(index) / 16.0;
return value;
}

View File

@@ -4,9 +4,6 @@
*
* Paul Derbyshire - https://github.com/proddy/EMS-ESP
*
* See ChangeLog.md for history
* See README.md for Acknowledgments
*
*/
#pragma once
@@ -20,8 +17,8 @@
#define DS18_CHIP_DS1825 0x3B
#define DS18_DATA_SIZE 9
#define DS18_PARASITE 1
#define DS18_DISCONNECTED -127
#define DS18_CRC_ERROR -126
#define GPIO_NONE 0x99
#define DS18_READ_INTERVAL 2000 // Force sensor read & cache every 2 seconds
@@ -39,10 +36,11 @@ class DS18 {
DS18();
~DS18();
uint8_t setup(uint8_t gpio);
uint8_t setup(uint8_t gpio, bool parasite);
void loop();
char * getDeviceString(char * s, unsigned char index);
double getValue(unsigned char index);
int16_t getRawValue(unsigned char index); // raw values, needs / 16
protected:
bool validateID(unsigned char id);
@@ -50,6 +48,7 @@ class DS18 {
uint8_t loadDevices();
OneWire * _wire;
uint8_t _count; // # devices
uint8_t _gpio; // the sensor pin
uint8_t _count; // # devices
uint8_t _gpio; // the sensor pin
uint8_t _parasite; // parasite mode
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -16,18 +16,19 @@
#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 the "Service Key"
#define EMS_ID_DEFAULT_BOILER 0x08
#define EMS_ID_SM10 0x30 // Solar Module SM10
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
// max length of a telegram, including CRC, for Rx and Tx.
#define EMS_MAX_TELEGRAM_LENGTH 99
#define EMS_MAX_TELEGRAM_LENGTH 32
// default values
#define EMS_VALUE_INT_ON 1 // boolean true
#define EMS_VALUE_INT_OFF 0 // boolean false
#define EMS_VALUE_INT_NOTSET 0xFF // for 8-bit ints
#define EMS_VALUE_SHORT_NOTSET 0x8000 // for 2-byte shorts
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs
#define EMS_VALUE_FLOAT_NOTSET -255 // float
#define EMS_THERMOSTAT_READ_YES true
#define EMS_THERMOSTAT_READ_NO false
@@ -91,6 +92,7 @@ typedef struct {
unsigned long emsRxTimestamp; // timestamp of last EMS message received
unsigned long emsPollTimestamp; // timestamp of last EMS poll sent to us
bool emsTxCapable; // able to send via Tx
bool emsTxDisabled; // true to prevent all Tx
uint8_t txRetryCount; // # times the last Tx was re-sent
} _EMS_Sys_Status;
@@ -111,6 +113,12 @@ typedef struct {
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
} _EMS_TxTelegram;
// The Rx receive package
typedef struct {
uint32_t timestamp; // timestamp from millis()
uint8_t * telegram; // the full data package
uint8_t length; // length in bytes
} _EMS_RxTelegram;
// default empty Tx
@@ -137,6 +145,13 @@ typedef struct {
char model_string[50];
} _Boiler_Type;
typedef struct {
uint8_t model_id;
uint8_t product_id;
uint8_t type_id;
char model_string[50];
} _Other_Type;
// Definition for thermostat type
typedef struct {
uint8_t model_id;
@@ -158,31 +173,32 @@ typedef struct { // UBAParameterWW
uint8_t wWComfort; // Warm water comfort or ECO mode
// UBAMonitorFast
uint8_t selFlowTemp; // Selected flow temperature
float curFlowTemp; // Current flow temperature
float retTemp; // Return temperature
uint8_t burnGas; // Gas on/off
uint8_t fanWork; // Fan on/off
uint8_t ignWork; // Ignition on/off
uint8_t heatPmp; // Circulating pump on/off
uint8_t wWHeat; // 3-way valve on WW
uint8_t wWCirc; // Circulation on/off
uint8_t selBurnPow; // Burner max power
uint8_t curBurnPow; // Burner current power
float flameCurr; // Flame current in micro amps
float sysPress; // System pressure
char serviceCodeChar[2]; // 2 character status/service code
uint8_t selFlowTemp; // Selected flow temperature
int16_t curFlowTemp; // Current flow temperature
int16_t retTemp; // Return temperature
uint8_t burnGas; // Gas on/off
uint8_t fanWork; // Fan on/off
uint8_t ignWork; // Ignition on/off
uint8_t heatPmp; // Circulating pump on/off
uint8_t wWHeat; // 3-way valve on WW
uint8_t wWCirc; // Circulation on/off
uint8_t selBurnPow; // Burner max power
uint8_t curBurnPow; // Burner current power
uint16_t flameCurr; // Flame current in micro amps
uint8_t sysPress; // System pressure
char serviceCodeChar[3]; // 2 character status/service code
uint16_t serviceCode; // error/service code
// UBAMonitorSlow
float extTemp; // Outside temperature
float boilTemp; // Boiler temperature
int16_t extTemp; // Outside temperature
int16_t boilTemp; // Boiler temperature
uint8_t pumpMod; // Pump modulation
uint32_t burnStarts; // # burner restarts
uint32_t burnStarts; // # burner starts
uint32_t burnWorkMin; // Total burner operating time
uint32_t heatWorkMin; // Total heat operating time
// UBAMonitorWWMessage
float wWCurTmp; // Warm Water current temperature:
int16_t wWCurTmp; // Warm Water current temperature:
uint32_t wWStarts; // Warm Water # starts
uint32_t wWWorkM; // Warm Water # minutes
uint8_t wWOneTime; // Warm Water one time function on/off
@@ -206,6 +222,18 @@ typedef struct { // UBAParameterWW
uint8_t product_id;
} _EMS_Boiler;
/*
* Telegram package defintions for Other EMS devices
*/
typedef struct {
// SM10 Solar Module - SM10Monitor
bool SM10; // set true if there is a SM10 available
int16_t SM10collectorTemp; // collector temp from SM10
int16_t SM10bottomTemp; // bottom temp from SM10
uint8_t SM10pumpModulation; // modulation solar pump
uint8_t SM10pump; // pump active
} _EMS_Other;
// Thermostat data
typedef struct {
uint8_t type_id; // the type ID of the thermostat
@@ -214,8 +242,8 @@ typedef struct {
bool read_supported;
bool write_supported;
char version[10];
float setpoint_roomTemp; // current set temp
float curr_roomTemp; // current room temp
int16_t setpoint_roomTemp; // current set temp
int16_t curr_roomTemp; // current room temp
uint8_t mode; // 0=low, 1=manual, 2=auto
bool day_mode; // 0=night, 1=day
uint8_t hour;
@@ -227,7 +255,7 @@ typedef struct {
} _EMS_Thermostat;
// call back function signature for processing telegram types
typedef void (*EMS_processType_cb)(uint8_t type, uint8_t * data, uint8_t length);
typedef void (*EMS_processType_cb)(uint8_t src, uint8_t * data, uint8_t length);
// Definition for each EMS type, including the relative callback function
typedef struct {
@@ -249,15 +277,16 @@ void ems_setWarmWaterTemp(uint8_t temperature);
void ems_setWarmWaterActivated(bool activated);
void ems_setWarmTapWaterActivated(bool activated);
void ems_setPoll(bool b);
void ems_setTxEnabled(bool b);
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
void ems_setEmsRefreshed(bool b);
void ems_setWarmWaterModeComfort(bool comfort);
void ems_setWarmWaterModeComfort(uint8_t comfort);
bool ems_checkEMSBUSAlive();
void ems_setModels();
void ems_setTxDisabled(bool b);
void ems_getThermostatValues();
void ems_getBoilerValues();
void ems_getOtherValues();
bool ems_getPoll();
bool ems_getTxEnabled();
bool ems_getThermostatEnabled();
@@ -275,17 +304,21 @@ char * ems_getThermostatDescription(char * buffer);
void ems_printTxQueue();
char * ems_getBoilerDescription(char * buffer);
void ems_startupTelegrams();
// private functions
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 _processType(_EMS_RxTelegram * EMS_RxTelegram);
void _debugPrintPackage(const char * prefix, _EMS_RxTelegram * EMS_RxTelegram, const char * color);
void _ems_clearTxData();
int _ems_findBoilerModel(uint8_t model_id);
bool _ems_setModel(uint8_t model_id);
void _ems_setThermostatModel(uint8_t thermostat_modelid);
void _removeTxQueue();
void _ems_readTelegram(uint8_t * telegram, uint8_t length);
// global so can referenced in other classes
extern _EMS_Sys_Status EMS_Sys_Status;
extern _EMS_Boiler EMS_Boiler;
extern _EMS_Thermostat EMS_Thermostat;
extern _EMS_Other EMS_Other;

View File

@@ -32,11 +32,15 @@
#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
#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_Hot 0x00 // the value for hot
#define EMS_VALUE_UBAParameterWW_wwComfort_Eco 0xD8 // the value for eco
#define EMS_VALUE_UBAParameterWW_wwComfort_Intelligent 0xEC // the value for intelligent
// Other
#define EMS_TYPE_SM10Monitor 0x97 // SM10Monitor
/*
* Thermostats...
@@ -92,7 +96,10 @@ typedef enum {
// generic ID for the boiler
EMS_MODEL_UBA,
// thermostats
// generic ID for all the other weird devices
EMS_MODEL_OTHER,
// and finaly the thermostats
EMS_MODEL_ES73,
EMS_MODEL_RC10,
EMS_MODEL_RC20,
@@ -111,18 +118,26 @@ typedef enum {
// format is MODEL_ID, PRODUCT ID, TYPE_ID, DESCRIPTION
const _Boiler_Type Boiler_Types[] = {
{EMS_MODEL_UBA, 72, 0x08, "MC10"},
{EMS_MODEL_UBA, 72, 0x08, "MC10 Module"},
{EMS_MODEL_UBA, 123, 0x08, "Buderus GB172/Nefit Trendline"},
{EMS_MODEL_UBA, 115, 0x08, "Nefit Topline Compact"},
{EMS_MODEL_UBA, 203, 0x08, "Buderus Logamax U122"},
{EMS_MODEL_UBA, 64, 0x08, "Sieger BK15 Boiler/Nefit Smartline"},
{EMS_MODEL_UBA, 190, 0x09, "BC10 Base Controller"},
{EMS_MODEL_UBA, 114, 0x09, "BC10 Base Controller"},
{EMS_MODEL_UBA, 125, 0x09, "BC25 Base Controller"},
{EMS_MODEL_UBA, 68, 0x09, "RFM20 Receiver"},
{EMS_MODEL_UBA, 95, 0x08, "Bosch Condens 2500"},
{EMS_MODEL_UBA, 251, 0x21, "MM10 Mixer Module"}, // warning, fake product id!
{EMS_MODEL_UBA, 250, 0x11, "WM10 Switch Module"}, // warning, fake product id!
{EMS_MODEL_UBA, 95, 0x08, "Bosch Condens 2500"}
};
// Other EMS devices which are not considered boilers or thermostats
const _Other_Type Other_Types[] = {
{EMS_MODEL_OTHER, 251, 0x21, "MM10 Mixer Module"}, // warning, fake product id!
{EMS_MODEL_OTHER, 250, 0x11, "WM10 Switch Module"}, // warning, fake product id!
{EMS_MODEL_OTHER, 68, 0x09, "RFM20 Receiver"},
{EMS_MODEL_OTHER, 190, 0x09, "BC10 Base Controller"},
{EMS_MODEL_OTHER, 114, 0x09, "BC10 Base Controller"},
{EMS_MODEL_OTHER, 125, 0x09, "BC25 Base Controller"},
{EMS_MODEL_OTHER, 205, 0x02, "Nefit Moduline Easy Connect"},
{EMS_MODEL_OTHER, 73, EMS_ID_SM10, "SM10 Solar Module"}
};
@@ -132,15 +147,16 @@ const _Boiler_Type Boiler_Types[] = {
const _Thermostat_Type Thermostat_Types[] = {
{EMS_MODEL_ES73, 76, 0x10, "Sieger ES73", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC10, 79, 0x17, "RC10/Nefit Moduline 100)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20, 77, 0x17, "RC20/Nefit Moduline 300)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC10, 79, 0x17, "RC10/Nefit Moduline 100", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20, 77, 0x17, "RC20/Nefit Moduline 300", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC30, 78, 0x10, "RC30/Nefit Moduline 400)", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC30, 78, 0x10, "RC30/Nefit Moduline 400", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC35, 86, 0x10, "RC35", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_EASY, 202, 0x18, "TC100/Nefit Easy", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_BOSCHEASY, 206, 0x02, "Bosch Easy", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_RC310, 158, 0x10, "RC310", EMS_THERMOSTAT_READ_NO, EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_CW100, 255, 0x18, "Bosch CW100", EMS_THERMOSTAT_READ_NO, EMS_THERMOSTAT_WRITE_NO},
{EMS_MODEL_OT, 171, 0x02, "EMS-OT OpenTherm converter", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}
{EMS_MODEL_OT, 171, 0x02, "EMS-OT OpenTherm converter", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES},
{EMS_MODEL_RC10, 165, 0x02, "RC10/Nefit Moduline 1010", EMS_THERMOSTAT_READ_YES, EMS_THERMOSTAT_WRITE_YES}
};

View File

@@ -24,8 +24,8 @@ os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen]; // our Rx queue
// Important: do not use ICACHE_FLASH_ATTR !
//
static void emsuart_rx_intr_handler(void * para) {
static uint16_t length;
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE];
static uint8_t length;
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE];
// is a new buffer? if so init the thing for a new telegram
if (EMS_Sys_Status.emsRxStatus == EMS_RX_STATUS_IDLE) {
@@ -67,18 +67,13 @@ static void emsuart_rx_intr_handler(void * para) {
/*
* system task triggered on BRK interrupt
* Read commands are all asynchronous
* When a buffer is full it is sent to the ems_parseTelegram() function in ems.cpp. This is the hook
* incoming received messages are always asynchronous
* The full buffer is sent to the ems_parseTelegram() function in ems.cpp.
*/
static void ICACHE_FLASH_ATTR emsuart_recvTask(os_event_t * events) {
// get next free EMS Receive buffer
_EMSRxBuf * pCurrent = pEMSRxBuf;
pEMSRxBuf = paEMSRxBuf[++emsRxBufIdx % EMS_MAXBUFFERS];
// transmit EMS buffer, excluding the BRK
if (pCurrent->writePtr > 1) {
ems_parseTelegram((uint8_t *)pCurrent->buffer, (pCurrent->writePtr) - 1);
}
ems_parseTelegram((uint8_t *)pCurrent->buffer, (pCurrent->writePtr) - 1); // transmit EMS buffer, excluding the BRK
pEMSRxBuf = paEMSRxBuf[++emsRxBufIdx % EMS_MAXBUFFERS]; // next free EMS Receive buffer
}
/*
@@ -97,12 +92,12 @@ void ICACHE_FLASH_ATTR emsuart_init() {
// pin settings
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0RXD);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD);
PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0RXD_U);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD);
// set 9600, 8 bits, no parity check, 1 stop bit
USD(EMSUART_UART) = (ESP8266_CLOCK / EMSUART_BAUD);
USD(EMSUART_UART) = (UART_CLK_FREQ / EMSUART_BAUD);
USC0(EMSUART_UART) = EMSUART_CONFIG; // 8N1
// flush everything left over in buffer, this clears both rx and tx FIFOs
@@ -129,13 +124,13 @@ void ICACHE_FLASH_ATTR emsuart_init() {
system_os_task(emsuart_recvTask, EMSUART_recvTaskPrio, recvTaskQueue, EMSUART_recvTaskQueueLen);
// disable esp debug which will go to Tx and mess up the line
// system_set_os_print(0); // https://github.com/espruino/Espruino/issues/655
ETS_UART_INTR_ATTACH(emsuart_rx_intr_handler, NULL);
ETS_UART_INTR_ENABLE();
system_set_os_print(0); // https://github.com/espruino/Espruino/issues/655
// swap Rx and Tx pins to use GPIO13 (D7) and GPIO15 (D8) respectively
system_uart_swap();
ETS_UART_INTR_ATTACH(emsuart_rx_intr_handler, NULL);
ETS_UART_INTR_ENABLE();
}
/*
@@ -143,7 +138,17 @@ void ICACHE_FLASH_ATTR emsuart_init() {
*/
void ICACHE_FLASH_ATTR emsuart_stop() {
ETS_UART_INTR_DISABLE();
ETS_UART_INTR_ATTACH(NULL, NULL);
//ETS_UART_INTR_ATTACH(NULL, NULL);
//system_uart_swap(); // to be sure, swap Tx/Rx back.
//detachInterrupt(digitalPinToInterrupt(D7));
//noInterrupts();
}
/*
* re-start UART0 driver
*/
void ICACHE_FLASH_ATTR emsuart_start() {
ETS_UART_INTR_ENABLE();
}
/*

View File

@@ -10,15 +10,15 @@
#include <Arduino.h>
#define EMSUART_UART 0 // UART 0
#define EMSUART_CONFIG 0x1c // 8N1 (8 bits, no stop bits, 1 parity)
#define EMSUART_CONFIG 0x1C // 8N1 (8 bits, no stop bits, 1 parity)
#define EMSUART_BAUD 9600 // uart baud rate for the EMS circuit
#define EMS_MAXBUFFERS 4 // 4 buffers for circular filling to avoid collisions
#define EMS_MAXBUFFERSIZE 128 // max size of the buffer. packets are max 32 bytes
#define EMS_MAXBUFFERS 10 // 4 buffers for circular filling to avoid collisions
#define EMS_MAXBUFFERSIZE 32 // max size of the buffer. packets are max 32 bytes
// this is how long we drop the Tx signal to create a 11-bit Break of zeros
// this is how long we drop the Tx signal to create a 11-bit Break of zeros (BRK)
// At 9600 baud, 11 bits will be 1144 microseconds
// the BRK from Boiler is roughly 1.039ms, so accounting for hardware lag using around 2078 (for half-duplex) - 8 (lag)
// the BRK from Boiler master is roughly 1.039ms, so accounting for hardware lag using around 2078 (for half-duplex) - 8 (lag)
#define EMS_TX_BRK_WAIT 2070
#define EMSUART_recvTaskPrio 1
@@ -31,6 +31,7 @@ typedef struct {
void ICACHE_FLASH_ATTR emsuart_init();
void ICACHE_FLASH_ATTR emsuart_stop();
void ICACHE_FLASH_ATTR emsuart_start();
void ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len);
void ICACHE_FLASH_ATTR emsaurt_tx_poll();
void ICACHE_FLASH_ATTR emsuart_tx_brk();

View File

@@ -37,6 +37,14 @@
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
#define TOPIC_BOILER_WWACTIVATED "wwactivated" // for receiving MQTT message to change water on/off
#define TOPIC_BOILER_CMD_WWTEMP "boiler_cmd_wwtemp" // for received boiler wwtemp changes via MQTT
#define TOPIC_BOILER_CMD_COMFORT "boiler_cmd_comfort" // for received boiler ww comfort setting via MQTT
// MQTT for SM10 Solar Module
#define TOPIC_SM10_DATA "sm10_data" // topic name
#define SM10_COLLECTORTEMP "temp" // collector temp
#define SM10_BOTTOMTEMP "bottomtemp" // bottom temp
#define SM10_PUMPMODULATION "pumpmodulation" // pump modulation
#define SM10_PUMP "pump" // pump active
// shower time
#define TOPIC_SHOWERTIME "showertime" // for sending shower time results
@@ -44,28 +52,26 @@
#define TOPIC_SHOWER_ALERT "shower_alert" // toggle switch for enabling the shower alarm logic
#define TOPIC_SHOWER_COLDSHOT "shower_coldshot" // used to trigger a coldshot from an MQTT command
// default values for shower logic on/off
#define BOILER_SHOWER_TIMER 1 // enable (1) to monitor shower time
#define BOILER_SHOWER_ALERT 0 // enable (1) to send alert of cold water when shower time limit has exceeded
#define SHOWER_MAX_DURATION 420000 // in ms. 7 minutes, before trigger a shot of cold water
// MQTT for EXTERNAL SENSORS
#define TOPIC_EXTERNAL_SENSORS "sensors" // for sending sensor values to MQTT
#define PAYLOAD_EXTERNAL_SENSORS "temp_%d" // for formatting the payload for each external dallas sensor
////////////////////////////////////////////////////////////////////////////////////////////////////
// THESE DEFAULT VALUES CAN ALSO BE SET AND STORED WITHTIN THE APPLICATION (see 'set' command) //
// ALTHOUGH YOU MAY ALSO HARDCODE THEM HERE BUT THEY WILL BE OVERWRITTEN WITH NEW RELEASE UPDATES //
////////////////////////////////////////////////////////////////////////////////////////////////////
// Set LED pin used for showing ems bus connection status. Solid is connected, Flashing is error
// can be either the onboard LED on the ESP8266 (LED_BULLETIN) or external via an external pull-up LED
// (e.g. D1 on a bbqkees' board
// can be enabled and disabled via the 'set led'
// pin can be set by 'set led_gpio'
// Set LED pin used for showing the EMS bus connection status. Solid means EMS bus working, flashing is an error
// can be either the onboard LED on the ESP8266 (LED_BULLETIN) or external via an external pull-up LED (e.g. D1 on a bbqkees' board)
// can be enabled and disabled via the 'set led' command and pin set by 'set led_gpio'
#define EMSESP_LED_GPIO LED_BUILTIN
// set this if using an external temperature sensor like a DS18B20
// D5 is the default on bbqkees' board
// D5 is the default on a bbqkees board
#define EMSESP_DALLAS_GPIO D5
#define EMSESP_DALLAS_PARASITE false
// By default the EMS bus will be scanned for known devices based on product ids in ems_devices.h
// By default the EMS bus will be scanned for known devices based on the product ids in ems_devices.h
// You can override the Thermostat and Boiler types here
#define EMSESP_BOILER_TYPE EMS_ID_NONE
#define EMSESP_THERMOSTAT_TYPE EMS_ID_NONE

View File

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