mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 08:19:52 +03:00
merged with dev 1.8.1
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,7 +3,9 @@
|
|||||||
.gcc-flags.json
|
.gcc-flags.json
|
||||||
.vscode
|
.vscode
|
||||||
.env
|
.env
|
||||||
|
.DS_Store
|
||||||
platformio.ini
|
platformio.ini
|
||||||
lib/readme.txt
|
lib/readme.txt
|
||||||
.travis.yml
|
.travis.yml
|
||||||
scripts/stackdmp.txt
|
scripts/stackdmp.txt
|
||||||
|
*.bin
|
||||||
|
|||||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.8.1dev] 2019-07-27
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added back -DCRASH in Debug build target for capturing any ESP8266 stack dumps during crashes
|
||||||
|
- Web Interface, for checking stats and setting wifi credentials. See wiki for more details.
|
||||||
|
- reset firmware option. If the reset button on the ESP is pressed during boot up sequence (the LED is flashing very fast) all settings are erased and goes into AP mode.
|
||||||
|
- Added tx_mode back with options 0,1 and 2 until we've fixed option 2 that works for everyone and doesn't reset ESP
|
||||||
|
- More solar module data captured, thanks to @Vuego123
|
||||||
|
- Detect thermostat mode for EMS+ RC300/Moduline 3000
|
||||||
|
- MQTT message to set boiler flowtemp (`boiler_cmd_flowtemp`). See [wiki](https://github.com/proddy/EMS-ESP/wiki/MQTT).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Detecting unset values in the SPIFFS and setting default values
|
||||||
|
- Bosch Easy Connect wrongly classified as a thermostat
|
||||||
|
- Correctly handle telegrams who's size are exactly 32 bytes (e.g. 0x19 MonitorSlow)
|
||||||
|
- Telnet also available when in AP mode
|
||||||
|
- Handling of thermostat temperatures that were single bytes and couldn't exceed 25.5 (0xFF) degrees!
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved handling of Solar Modules (thanks @Vuego123)
|
||||||
|
- `publish_wait` renamed to `publish_time`, a value of 0 means disabling all MQTT sending
|
||||||
|
- How signed shorts are handled such as the current and setpoint temps on RC300s
|
||||||
|
- Stopped automatic refresh of web page, which causes crashes/memory loss after a short time
|
||||||
|
- Support HA 0.96 climate component changes
|
||||||
|
- -DDEFAULT_NO_SERIAL changed to -DFORCE_SERIAL
|
||||||
|
- some code cleanups, removing NULLS and moving some things fron heap to stack to prevent memory fragmentation
|
||||||
|
|
||||||
## [1.8.0] 2019-06-15
|
## [1.8.0] 2019-06-15
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
- platform: mqtt
|
- platform: mqtt
|
||||||
name: boiler
|
name: boiler
|
||||||
modes:
|
modes:
|
||||||
- "on"
|
- "auto"
|
||||||
- "off"
|
- "off"
|
||||||
min_temp: 40
|
min_temp: 40
|
||||||
max_temp: 60
|
max_temp: 60
|
||||||
temp_step: 1
|
temp_step: 1
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
temperature_command_topic: "home/ems-esp/boiler_cmd_wwtemp"
|
temperature_command_topic: "home/ems-esp/boiler_cmd_wwtemp"
|
||||||
current_temperature_template: "{{ value_json.wWCurTmp }}"
|
current_temperature_template: "{{ value_json.wWCurTmp }}"
|
||||||
temperature_state_template: "{{ value_json.wWSelTemp }}"
|
temperature_state_template: "{{ value_json.wWSelTemp }}"
|
||||||
mode_state_template: "{{ value_json.wWActivated }}"
|
mode_state_template: "{% if value_json.wWActivated == 'off' %} off {% else %} auto {% endif %}"
|
||||||
mode_state_topic: "home/ems-esp/boiler_data"
|
mode_state_topic: "home/ems-esp/boiler_data"
|
||||||
mode_command_topic: "home/ems-esp/wwactivated"
|
mode_command_topic: "home/ems-esp/wwactivated"
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -6,23 +6,26 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef MyEMS_h
|
#ifndef MyESP_h
|
||||||
#define MyEMS_h
|
#define MyESP_h
|
||||||
|
|
||||||
#define MYESP_VERSION "1.1.16"
|
#define MYESP_VERSION "1.1.24"
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
#include <AsyncMqttClient.h> // https://github.com/marvinroger/async-mqtt-client and for ESP32 see https://github.com/marvinroger/async-mqtt-client/issues/127
|
#include <AsyncMqttClient.h> // https://github.com/marvinroger/async-mqtt-client and for ESP32 see https://github.com/marvinroger/async-mqtt-client/issues/127
|
||||||
#include <DNSServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
#include <FS.h>
|
#include <FS.h>
|
||||||
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
||||||
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
||||||
|
|
||||||
|
#ifdef CRASH
|
||||||
#include <EEPROM_Rotate.h>
|
#include <EEPROM_Rotate.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
void custom_crash_callback(struct rst_info *, uint32_t, uint32_t);
|
||||||
#include "user_interface.h"
|
#include "user_interface.h"
|
||||||
void custom_crash_callback(struct rst_info *, uint32_t, uint32_t);
|
|
||||||
extern struct rst_info resetInfo;
|
extern struct rst_info resetInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,10 +41,10 @@ extern struct rst_info resetInfo;
|
|||||||
|
|
||||||
#define MYEMS_CONFIG_FILE "/config.json"
|
#define MYEMS_CONFIG_FILE "/config.json"
|
||||||
|
|
||||||
#define LOADAVG_INTERVAL 30000 // Interval between calculating load average (in ms)
|
#define LOADAVG_INTERVAL 30000 // Interval between calculating load average (in ms) = 30 seconds
|
||||||
|
|
||||||
// WIFI
|
// WIFI
|
||||||
#define WIFI_CONNECT_TIMEOUT 10000 // Connecting timeout for WIFI in ms
|
#define WIFI_CONNECT_TIMEOUT 10000 // Connecting timeout for WIFI in ms (10 seconds)
|
||||||
#define WIFI_RECONNECT_INTERVAL 600000 // If could not connect to WIFI, retry after this time in ms. 10 minutes
|
#define WIFI_RECONNECT_INTERVAL 600000 // If could not connect to WIFI, retry after this time in ms. 10 minutes
|
||||||
|
|
||||||
// MQTT
|
// MQTT
|
||||||
@@ -65,6 +68,8 @@ extern struct rst_info resetInfo;
|
|||||||
#define TELNET_MAX_COMMAND_LENGTH 80 // length of a command
|
#define TELNET_MAX_COMMAND_LENGTH 80 // length of a command
|
||||||
#define TELNET_EVENT_CONNECT 1
|
#define TELNET_EVENT_CONNECT 1
|
||||||
#define TELNET_EVENT_DISCONNECT 0
|
#define TELNET_EVENT_DISCONNECT 0
|
||||||
|
#define TELNET_EVENT_SHOWCMD 10
|
||||||
|
#define TELNET_EVENT_SHOWSET 20
|
||||||
|
|
||||||
// ANSI Colors
|
// ANSI Colors
|
||||||
#define COLOR_RESET "\x1B[0m"
|
#define COLOR_RESET "\x1B[0m"
|
||||||
@@ -143,12 +148,11 @@ PROGMEM const char * const custom_reset_string[] = {custom_reset_hardware, cus
|
|||||||
#define RTCMEM_OFFSET 32u
|
#define RTCMEM_OFFSET 32u
|
||||||
#define RTCMEM_ADDR (RTCMEM_ADDR_BASE + (RTCMEM_OFFSET * 4u))
|
#define RTCMEM_ADDR (RTCMEM_ADDR_BASE + (RTCMEM_OFFSET * 4u))
|
||||||
#define RTCMEM_BLOCKS 96u
|
#define RTCMEM_BLOCKS 96u
|
||||||
#define RTCMEM_MAGIC 0x45535075
|
#define RTCMEM_MAGIC 0x45535076
|
||||||
|
|
||||||
struct RtcmemData {
|
struct RtcmemData {
|
||||||
uint32_t magic; // RTCMEM_MAGIC
|
uint32_t magic; // RTCMEM_MAGIC
|
||||||
uint32_t sys; // system reset reason (1-4)
|
uint32_t sys; // system details
|
||||||
uint32_t energy; // store energy count
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
|
static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
|
||||||
@@ -165,6 +169,13 @@ typedef struct {
|
|||||||
|
|
||||||
typedef enum { MYESP_FSACTION_SET, MYESP_FSACTION_LIST, MYESP_FSACTION_SAVE, MYESP_FSACTION_LOAD } MYESP_FSACTION;
|
typedef enum { MYESP_FSACTION_SET, MYESP_FSACTION_LIST, MYESP_FSACTION_SAVE, MYESP_FSACTION_LOAD } MYESP_FSACTION;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MYESP_BOOTSTATUS_POWERON = 0,
|
||||||
|
MYESP_BOOTSTATUS_BOOTED = 1,
|
||||||
|
MYESP_BOOTSTATUS_BOOTING = 2,
|
||||||
|
MYESP_BOOTSTATUS_RESETNEEDED = 3
|
||||||
|
} MYESP_BOOTSTATUS; // boot messages
|
||||||
|
|
||||||
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callback_f;
|
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callback_f;
|
||||||
typedef std::function<void()> wifi_callback_f;
|
typedef std::function<void()> wifi_callback_f;
|
||||||
typedef std::function<void()> ota_callback_f;
|
typedef std::function<void()> ota_callback_f;
|
||||||
@@ -172,6 +183,7 @@ typedef std::function<void(uint8_t, const char *)>
|
|||||||
typedef std::function<void(uint8_t)> telnet_callback_f;
|
typedef std::function<void(uint8_t)> telnet_callback_f;
|
||||||
typedef std::function<bool(MYESP_FSACTION, const JsonObject json)> fs_callback_f;
|
typedef std::function<bool(MYESP_FSACTION, const JsonObject json)> fs_callback_f;
|
||||||
typedef std::function<bool(MYESP_FSACTION, uint8_t, const char *, const char *)> fs_settings_callback_f;
|
typedef std::function<bool(MYESP_FSACTION, uint8_t, const char *, const char *)> fs_settings_callback_f;
|
||||||
|
typedef std::function<void(char *)> web_callback_f;
|
||||||
|
|
||||||
// calculates size of an 2d array at compile time
|
// calculates size of an 2d array at compile time
|
||||||
template <typename T, size_t N>
|
template <typename T, size_t N>
|
||||||
@@ -179,14 +191,55 @@ constexpr size_t ArraySize(T (&)[N]) {
|
|||||||
return N;
|
return N;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void PROGMEM_readAnything(const T * sce, T & dest) {
|
||||||
|
memcpy_P(&dest, sce, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
#define UPTIME_OVERFLOW 4294967295 // Uptime overflow value
|
#define UPTIME_OVERFLOW 4294967295 // Uptime overflow value
|
||||||
|
|
||||||
|
// web min and max length of wifi ssid and password
|
||||||
|
#define MAX_SSID_LEN 32
|
||||||
|
#define MAX_PWD_LEN 64
|
||||||
|
|
||||||
|
#define MYESP_BOOTUP_FLASHDELAY 50 // flash duration for LED at bootup sequence
|
||||||
|
#define MYESP_BOOTUP_DELAY 2000 // time before we open the window to reset. This is to stop resetting values when uploading firmware via USB
|
||||||
|
|
||||||
|
// max size of char buffer for storing web page
|
||||||
|
#define MYESP_MAXCHARBUFFER 800
|
||||||
|
|
||||||
|
// Holds the admin webpage in the program memory
|
||||||
|
const char webCommonPage_start[] = "<html>"
|
||||||
|
"<head>"
|
||||||
|
"<style>input {font-size: 1.2em; width: 100%; max-width: 350px; display: block; margin: 5px auto; }"
|
||||||
|
"body {background-color: #FFA500;font: normal 18px Verdana, Arial, sans-serif;} </style>";
|
||||||
|
|
||||||
|
const char webCommonPage_start_body[] = "</head><body>";
|
||||||
|
|
||||||
|
const char webCommonPage_end[] = "</body></html>";
|
||||||
|
|
||||||
|
const char webResetPage_form[] = "<form id='form' action='/reset' method='post'>"
|
||||||
|
"<input name='newssid' type='text' maxlength='32' placeholder='SSID'>"
|
||||||
|
"<input name='newpassword' id='password1' type='password' maxlength='64' placeholder='Password'>"
|
||||||
|
"<input type='submit' value='Save and reboot'>"
|
||||||
|
"</form>";
|
||||||
|
|
||||||
|
const char webResetPage_post[] =
|
||||||
|
"<p>New wifi credentials set. System is now rebooting. Please wait a few seconds and then reconnect via telnet or browser to its new IP given address.</p>";
|
||||||
|
|
||||||
|
const char webResetAllPage_form[] = "<form id='resetform' action='/resetall' method='post'>"
|
||||||
|
"<input name='confirm' type='text' minlength='3' maxlength='16' placeholder='yes'>"
|
||||||
|
"<input type='submit' value='Reset All'>"
|
||||||
|
"</form>";
|
||||||
|
|
||||||
// class definition
|
// class definition
|
||||||
class MyESP {
|
class MyESP {
|
||||||
public:
|
public:
|
||||||
MyESP();
|
MyESP();
|
||||||
~MyESP();
|
~MyESP();
|
||||||
|
|
||||||
|
ESP8266WebServer webServer; // Web server on port 80
|
||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
void setWIFICallback(void (*callback)());
|
void setWIFICallback(void (*callback)());
|
||||||
void setWIFI(const char * wifi_ssid, const char * wifi_password, wifi_callback_f callback);
|
void setWIFI(const char * wifi_ssid, const char * wifi_password, wifi_callback_f callback);
|
||||||
@@ -216,7 +269,7 @@ class MyESP {
|
|||||||
// debug & telnet
|
// debug & telnet
|
||||||
void myDebug(const char * format, ...);
|
void myDebug(const char * format, ...);
|
||||||
void myDebug_P(PGM_P format_P, ...);
|
void myDebug_P(PGM_P format_P, ...);
|
||||||
void setTelnet(command_t * cmds, uint8_t count, telnetcommand_callback_f callback_cmd, telnet_callback_f callback);
|
void setTelnet(telnetcommand_callback_f callback_cmd, telnet_callback_f callback);
|
||||||
bool getUseSerial();
|
bool getUseSerial();
|
||||||
void setUseSerial(bool toggle);
|
void setUseSerial(bool toggle);
|
||||||
|
|
||||||
@@ -224,6 +277,9 @@ class MyESP {
|
|||||||
void setSettings(fs_callback_f callback, fs_settings_callback_f fs_settings_callback);
|
void setSettings(fs_callback_f callback, fs_settings_callback_f fs_settings_callback);
|
||||||
bool fs_saveConfig();
|
bool fs_saveConfig();
|
||||||
|
|
||||||
|
// Web
|
||||||
|
void setWeb(web_callback_f callback_web);
|
||||||
|
|
||||||
// Crash
|
// Crash
|
||||||
void crashClear();
|
void crashClear();
|
||||||
void crashDump();
|
void crashDump();
|
||||||
@@ -231,18 +287,17 @@ class MyESP {
|
|||||||
void crashInfo();
|
void crashInfo();
|
||||||
|
|
||||||
// general
|
// general
|
||||||
void end();
|
void end();
|
||||||
void loop();
|
void loop();
|
||||||
void begin(const char * app_hostname, const char * app_name, const char * app_version);
|
void begin(const char * app_hostname, const char * app_name, const char * app_version);
|
||||||
void setBoottime(const char * boottime);
|
void setBoottime(const char * boottime);
|
||||||
void resetESP();
|
void resetESP();
|
||||||
int getWifiQuality();
|
int getWifiQuality();
|
||||||
void showSystemStats();
|
void showSystemStats();
|
||||||
bool getHeartbeat();
|
bool getHeartbeat();
|
||||||
|
uint32_t getSystemLoadAverage();
|
||||||
// rtcmem and reset reason
|
|
||||||
bool rtcmemStatus();
|
|
||||||
uint32_t getSystemResetReason();
|
uint32_t getSystemResetReason();
|
||||||
|
uint8_t getSystemBootStatus();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// mqtt
|
// mqtt
|
||||||
@@ -271,7 +326,6 @@ class MyESP {
|
|||||||
bool _rtcmem_status;
|
bool _rtcmem_status;
|
||||||
|
|
||||||
// wifi
|
// wifi
|
||||||
DNSServer dnsServer; // For Access Point (AP) support
|
|
||||||
void _wifiCallback(justwifi_messages_t code, char * parameter);
|
void _wifiCallback(justwifi_messages_t code, char * parameter);
|
||||||
void _wifi_setup();
|
void _wifi_setup();
|
||||||
wifi_callback_f _wifi_callback;
|
wifi_callback_f _wifi_callback;
|
||||||
@@ -285,6 +339,7 @@ class MyESP {
|
|||||||
ota_callback_f _ota_post_callback;
|
ota_callback_f _ota_post_callback;
|
||||||
void _ota_setup();
|
void _ota_setup();
|
||||||
void _OTACallback();
|
void _OTACallback();
|
||||||
|
bool _ota_doing_update;
|
||||||
|
|
||||||
// crash
|
// crash
|
||||||
void _eeprom_setup();
|
void _eeprom_setup();
|
||||||
@@ -298,8 +353,6 @@ class MyESP {
|
|||||||
char * _telnet_readWord(bool allow_all_chars);
|
char * _telnet_readWord(bool allow_all_chars);
|
||||||
void _telnet_setup();
|
void _telnet_setup();
|
||||||
char _command[TELNET_MAX_COMMAND_LENGTH]; // the input command from either Serial or Telnet
|
char _command[TELNET_MAX_COMMAND_LENGTH]; // 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 _consoleShowHelp();
|
||||||
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
|
telnetcommand_callback_f _telnetcommand_callback; // Callable for projects commands
|
||||||
telnet_callback_f _telnet_callback; // callback for connect/disconnect
|
telnet_callback_f _telnet_callback; // callback for connect/disconnect
|
||||||
@@ -316,19 +369,25 @@ class MyESP {
|
|||||||
fs_settings_callback_f _fs_settings_callback;
|
fs_settings_callback_f _fs_settings_callback;
|
||||||
void _printSetCommands();
|
void _printSetCommands();
|
||||||
|
|
||||||
|
// web
|
||||||
|
web_callback_f _web_callback;
|
||||||
|
|
||||||
// general
|
// general
|
||||||
char * _app_hostname;
|
char * _app_hostname;
|
||||||
char * _app_name;
|
char * _app_name;
|
||||||
char * _app_version;
|
char * _app_version;
|
||||||
char * _boottime;
|
char * _boottime;
|
||||||
bool _suspendOutput;
|
bool _suspendOutput;
|
||||||
bool _use_serial;
|
bool _serial;
|
||||||
bool _heartbeat;
|
bool _heartbeat;
|
||||||
unsigned long _getUptime();
|
unsigned long _getUptime();
|
||||||
String _buildTime();
|
String _buildTime();
|
||||||
|
bool _firstInstall;
|
||||||
|
|
||||||
// reset reason and rtcmem
|
// reset reason and rtcmem
|
||||||
bool _rtcmemStatus();
|
bool _rtcmemStatus();
|
||||||
|
bool _getRtcmemStatus();
|
||||||
|
|
||||||
void _rtcmemInit();
|
void _rtcmemInit();
|
||||||
void _rtcmemSetup();
|
void _rtcmemSetup();
|
||||||
|
|
||||||
@@ -337,27 +396,33 @@ class MyESP {
|
|||||||
uint8_t _getSystemStabilityCounter();
|
uint8_t _getSystemStabilityCounter();
|
||||||
void _setSystemStabilityCounter(uint8_t counter);
|
void _setSystemStabilityCounter(uint8_t counter);
|
||||||
|
|
||||||
uint8_t _getSystemResetReason();
|
|
||||||
void _setSystemResetReason(uint8_t reason);
|
void _setSystemResetReason(uint8_t reason);
|
||||||
|
uint8_t _getCustomResetReason();
|
||||||
|
void _setCustomResetReason(uint8_t reason);
|
||||||
|
uint8_t _getSystemResetReason();
|
||||||
|
|
||||||
unsigned char _getCustomResetReason();
|
void _setSystemBootStatus(uint8_t status);
|
||||||
void _setCustomResetReason(unsigned char reason);
|
|
||||||
|
|
||||||
bool _systemStable;
|
bool _systemStable;
|
||||||
|
void _bootupSequence();
|
||||||
bool getSystemCheck();
|
bool _getSystemCheck();
|
||||||
void _systemCheckLoop();
|
void _systemCheckLoop();
|
||||||
void _setSystemCheck(bool stable);
|
void _setSystemCheck(bool stable);
|
||||||
|
|
||||||
// load average (0..100) and heap ram
|
// load average (0..100) and heap ram
|
||||||
uint32_t getSystemLoadAverage();
|
|
||||||
void _calculateLoad();
|
void _calculateLoad();
|
||||||
uint32_t _load_average;
|
uint32_t _load_average;
|
||||||
uint32_t getInitialFreeHeap();
|
uint32_t _getInitialFreeHeap();
|
||||||
uint32_t getUsedHeap();
|
uint32_t _getUsedHeap();
|
||||||
|
|
||||||
// heartbeat
|
// heartbeat
|
||||||
void _heartbeatCheck(bool force);
|
void _heartbeatCheck(bool force);
|
||||||
|
|
||||||
|
// webserver
|
||||||
|
void _webserver_setup();
|
||||||
|
void _webRootPage();
|
||||||
|
void _webResetPage();
|
||||||
|
void _webResetAllPage();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MyESP myESP;
|
extern MyESP myESP;
|
||||||
|
|||||||
@@ -573,6 +573,7 @@ void TelnetSpy::handle() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!listening) {
|
if (!listening) {
|
||||||
|
|
||||||
if ((WiFi.status() == WL_DISCONNECTED) && (WiFi.getMode() & WIFI_AP)) {
|
if ((WiFi.status() == WL_DISCONNECTED) && (WiFi.getMode() & WIFI_AP)) {
|
||||||
if (usedSer) {
|
if (usedSer) {
|
||||||
usedSer->println("[TELNET] in AP mode"); // added by Proddy
|
usedSer->println("[TELNET] in AP mode"); // added by Proddy
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
;
|
;
|
||||||
; PlatformIO Project Configuration File for EMS-ESP
|
; PlatformIO Project Configuration File for EMS-ESP
|
||||||
; Uses PlatformIO 4.0
|
|
||||||
;
|
;
|
||||||
|
|
||||||
[platformio]
|
[platformio]
|
||||||
@@ -8,24 +7,15 @@ default_envs = release
|
|||||||
;default_envs = debug
|
;default_envs = debug
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
debug_flags = -Wall -Wextra -Werror -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-variable -DTESTS
|
; -DMYESP_TIMESTAMP -DTESTS -DCRASH -DFORCE_SERIAL -DNO_GLOBAL_EEPROM -DLOGICANALYZER
|
||||||
general_flags = -g -w -DNO_GLOBAL_EEPROM
|
extra_flags = -DNO_GLOBAL_EEPROM
|
||||||
|
|
||||||
arduino_core_2_3_0 = espressif8266@1.5.0
|
|
||||||
arduino_core_2_4_0 = espressif8266@1.6.0
|
|
||||||
arduino_core_2_4_1 = espressif8266@1.7.3
|
|
||||||
arduino_core_2_4_2 = espressif8266@1.8.0
|
|
||||||
arduino_core_2_5_0 = espressif8266@2.0.4
|
|
||||||
arduino_core_2_5_1 = espressif8266@2.1.1
|
|
||||||
arduino_core_2_5_2 = espressif8266@2.2.1
|
|
||||||
arduino_core_latest = espressif8266
|
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
board = d1_mini
|
board = d1_mini
|
||||||
; board = nodemcuv2
|
; board = nodemcuv2
|
||||||
; board = d1_mini_pro
|
; board = d1_mini_pro
|
||||||
framework = arduino
|
framework = arduino
|
||||||
platform = ${common.arduino_core_latest}
|
platform = espressif8266
|
||||||
lib_deps =
|
lib_deps =
|
||||||
CRC32
|
CRC32
|
||||||
CircularBuffer
|
CircularBuffer
|
||||||
@@ -41,16 +31,17 @@ monitor_speed = 115200
|
|||||||
;upload_port = ems-esp.local
|
;upload_port = ems-esp.local
|
||||||
|
|
||||||
[env:debug]
|
[env:debug]
|
||||||
build_flags = ${common.general_flags} ${common.debug_flags}
|
build_type = debug
|
||||||
|
build_flags = ${common.extra_flags} -DCRASH
|
||||||
extra_scripts = pre:scripts/rename_fw.py
|
extra_scripts = pre:scripts/rename_fw.py
|
||||||
|
|
||||||
[env:clean]
|
[env:clean]
|
||||||
extra_scripts = pre:scripts/clean_fw.py
|
extra_scripts = pre:scripts/clean_fw.py
|
||||||
|
|
||||||
[env:release]
|
[env:release]
|
||||||
build_flags = ${common.general_flags}
|
build_flags = ${common.extra_flags}
|
||||||
extra_scripts = pre:scripts/rename_fw.py
|
extra_scripts = pre:scripts/rename_fw.py
|
||||||
|
|
||||||
[env:checkcode]
|
[env:checkcode]
|
||||||
build_flags = ${common.general_flags}
|
build_flags = ${common.extra_flags}
|
||||||
extra_scripts = scripts/checkcode.py
|
extra_scripts = scripts/checkcode.py
|
||||||
|
|||||||
@@ -17,4 +17,8 @@ import os
|
|||||||
# 3fffffb0: feefeffe feefeffe 3ffe8558 40100b01
|
# 3fffffb0: feefeffe feefeffe 3ffe8558 40100b01
|
||||||
# <<<stack<<<
|
# <<<stack<<<
|
||||||
|
|
||||||
call(['python', 'scripts/decoder.py', '-s', '-e', os.getcwd()+"/.pio/build/debug/firmware_d1_mini.elf", 'scripts/stackdmp.txt'])
|
call(['python', 'scripts/decoder.py ', '-s', '-e', os.getcwd()+"/.pio/build/debug/firmware_d1_mini_debug.elf", 'scripts/stackdmp.txt'])
|
||||||
|
|
||||||
|
# example for linux:
|
||||||
|
# % cd EMS-ESP
|
||||||
|
# % python scripts/decoder_linux.py -s -e .pio/build/debug/firmware_d1_mini.elf scripts/stackdmp.txt
|
||||||
|
|||||||
307
scripts/decoder_linux.py
Normal file
307
scripts/decoder_linux.py
Normal 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")
|
||||||
|
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)
|
||||||
@@ -1,8 +1,26 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from subprocess import call
|
import re
|
||||||
import os
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
# see http://docs.platformio.org/en/latest/projectconf/advanced_scripting.html#before-pre-and-after-post-actions
|
bag = {}
|
||||||
# env.Replace(PROGNAME="firmware_%s" % defines.get("VERSION"))
|
exprs = [
|
||||||
env.Replace(PROGNAME="firmware_%s" % env['BOARD'])
|
(re.compile(r'^#define APP_VERSION\s+"(\S+)"'), 'app_version'),
|
||||||
|
(re.compile(r'^#define APP_NAME\s+"(\S+)"'), 'app_name'),
|
||||||
|
(re.compile(r'^#define APP_HOSTNAME\s+"(\S+)"'), 'app_hostname')
|
||||||
|
]
|
||||||
|
with open('./src/version.h', 'r') as f:
|
||||||
|
for l in f.readlines():
|
||||||
|
for expr, var in exprs:
|
||||||
|
m = expr.match(l)
|
||||||
|
if m and len(m.groups()) > 0:
|
||||||
|
bag[var] = m.group(1)
|
||||||
|
|
||||||
|
app_version = bag.get('app_version')
|
||||||
|
app_name = bag.get('app_name')
|
||||||
|
app_hostname = bag.get('app_hostname')
|
||||||
|
|
||||||
|
board = env['BOARD']
|
||||||
|
branch = env['PIOENV']
|
||||||
|
|
||||||
|
# build filename, replacing . with _ for the version
|
||||||
|
env.Replace(PROGNAME="firmware_%s" % branch + "_" + app_version.replace(".", "_"))
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
std::vector<ds_device_t> _devices;
|
std::vector<ds_device_t> _devices;
|
||||||
|
|
||||||
DS18::DS18() {
|
DS18::DS18() {
|
||||||
_wire = NULL;
|
_wire = nullptr;
|
||||||
_count = 0;
|
_count = 0;
|
||||||
_gpio = GPIO_NONE;
|
_gpio = GPIO_NONE;
|
||||||
_parasite = 0;
|
_parasite = 0;
|
||||||
|
|||||||
609
src/ems-esp.cpp
609
src/ems-esp.cpp
File diff suppressed because it is too large
Load Diff
642
src/ems.cpp
642
src/ems.cpp
File diff suppressed because it is too large
Load Diff
200
src/ems.h
200
src/ems.h
@@ -12,27 +12,90 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
/* debug helper for logic analyzer
|
||||||
|
* create marker puls on GPIOx
|
||||||
|
* ° for Rx, we use GPIO14
|
||||||
|
* ° for Tx, we use GPIO12
|
||||||
|
*/
|
||||||
|
// clang-format off
|
||||||
|
#ifdef LOGICANALYZER
|
||||||
|
#define RX_MARK_PIN 14
|
||||||
|
#define TX_MARK_PIN 12
|
||||||
|
|
||||||
|
#define RX_MARK_MASK (1 << RX_MARK_PIN)
|
||||||
|
#define TX_MARK_MASK (1 << TX_MARK_PIN)
|
||||||
|
#define MARKERS_MASK (RX_MARK_PIN | TX_MARK_PIN)
|
||||||
|
|
||||||
|
#define GPIO_H(mask) (GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, (mask)))
|
||||||
|
#define GPIO_L(mask) (GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, (mask)))
|
||||||
|
|
||||||
|
#define RX_PULSE(pulse) \
|
||||||
|
do { \
|
||||||
|
GPIO_H(RX_MARK_MASK); \
|
||||||
|
delayMicroseconds(pulse); \
|
||||||
|
GPIO_L(RX_MARK_MASK); \
|
||||||
|
} while (0)
|
||||||
|
#define TX_PULSE(pulse) \
|
||||||
|
do { \
|
||||||
|
GPIO_H(TX_MARK_MASK); \
|
||||||
|
delayMicroseconds(pulse); \
|
||||||
|
GPIO_L(TX_MARK_MASK); \
|
||||||
|
} while (0)
|
||||||
|
#define LA_PULSE(pulse) \
|
||||||
|
do { \
|
||||||
|
GPIO_H(MARKERS_MASK); \
|
||||||
|
delayMicroseconds(pulse); \
|
||||||
|
GPIO_L(MARKERS_MASK); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define INIT_MARKERS(void) \
|
||||||
|
do { \
|
||||||
|
pinMode(RX_MARK_PIN, OUTPUT); \
|
||||||
|
pinMode(TX_MARK_PIN, OUTPUT); \
|
||||||
|
GPIO_L(MARKERS_MASK); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define RX_PULSE(pulse) \
|
||||||
|
{}
|
||||||
|
#define TX_PULSE(pulse) \
|
||||||
|
{}
|
||||||
|
#define LA_PULSE(pulse) \
|
||||||
|
{}
|
||||||
|
#define INIT_MARKERS(void) \
|
||||||
|
{}
|
||||||
|
#define RX_MARK_MASK
|
||||||
|
#define TX_MARK_MASK
|
||||||
|
#define GPIO_H(mask)
|
||||||
|
#define GPIO_L(mask)
|
||||||
|
#endif
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
#define EMS_ID_NONE 0x00 // used as a dest in broadcast messages and empty device IDs
|
#define EMS_ID_NONE 0x00 // used as a dest in broadcast messages and empty device IDs
|
||||||
|
|
||||||
// Fixed EMS IDs
|
// Fixed EMS IDs
|
||||||
#define EMS_ID_ME 0x0B // our device, hardcoded as the "Service Key"
|
#define EMS_ID_ME 0x0B // our device, hardcoded as the "Service Key"
|
||||||
#define EMS_ID_BOILER 0x08 // all UBA Boilers have 0x08
|
#define EMS_ID_BOILER 0x08 // all UBA Boilers have 0x08
|
||||||
#define EMS_ID_SM 0x30 // Solar Module SM10 and SM100
|
#define EMS_ID_SM 0x30 // Solar Module SM10, SM100 and ISM1
|
||||||
#define EMS_ID_HP 0x38 // HeatPump
|
#define EMS_ID_HP 0x38 // HeatPump
|
||||||
#define EMS_ID_GATEWAY 0x48 // KM200 Web Gateway
|
#define EMS_ID_GATEWAY 0x48 // KM200 Web Gateway
|
||||||
|
|
||||||
#define EMS_PRODUCTID_HEATRONICS 95 // ProductID for a Junkers Heatronic3 device
|
#define EMS_PRODUCTID_HEATRONICS 95 // ProductID for a Junkers Heatronic3 device
|
||||||
|
|
||||||
|
#define EMS_PRODUCTID_SM10 73 // ProductID for SM10 solar module
|
||||||
|
#define EMS_PRODUCTID_SM100 163 // ProductID for SM10 solar module
|
||||||
|
#define EMS_PRODUCTID_ISM1 101 // ProductID for SM10 solar module
|
||||||
|
|
||||||
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
|
#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.
|
// max length of a telegram, including CRC, for Rx and Tx.
|
||||||
#define EMS_MAX_TELEGRAM_LENGTH 32
|
#define EMS_MAX_TELEGRAM_LENGTH 32
|
||||||
|
|
||||||
// default values
|
// default values for null values
|
||||||
#define EMS_VALUE_INT_ON 1 // boolean true
|
#define EMS_VALUE_INT_ON 1 // boolean true
|
||||||
#define EMS_VALUE_INT_OFF 0 // boolean false
|
#define EMS_VALUE_INT_OFF 0 // boolean false
|
||||||
#define EMS_VALUE_INT_NOTSET 0xFF // for 8-bit ints
|
#define EMS_VALUE_INT_NOTSET 0xFF // for 8-bit unsigned ints/bytes
|
||||||
#define EMS_VALUE_SHORT_NOTSET 0x8000 // for 2-byte signed shorts
|
#define EMS_VALUE_SHORT_NOTSET -32768 // for 2-byte signed shorts
|
||||||
|
#define EMS_VALUE_USHORT_NOTSET 0x8000 // for 2-byte unsigned shorts
|
||||||
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs
|
#define EMS_VALUE_LONG_NOTSET 0xFFFFFF // for 3-byte longs
|
||||||
|
|
||||||
#define EMS_THERMOSTAT_WRITE_YES true
|
#define EMS_THERMOSTAT_WRITE_YES true
|
||||||
@@ -57,8 +120,11 @@ typedef enum {
|
|||||||
} _EMS_RX_STATUS;
|
} _EMS_RX_STATUS;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
EMS_TX_STATUS_OK,
|
||||||
EMS_TX_STATUS_IDLE, // ready
|
EMS_TX_STATUS_IDLE, // ready
|
||||||
EMS_TX_STATUS_WAIT // waiting for response from last Tx
|
EMS_TX_STATUS_WAIT, // waiting for response from last Tx
|
||||||
|
EMS_TX_WTD_TIMEOUT, // watchdog timeout during send
|
||||||
|
EMS_TX_BRK_DETECT // incoming BRK during Tx
|
||||||
} _EMS_TX_STATUS;
|
} _EMS_TX_STATUS;
|
||||||
|
|
||||||
#define EMS_TX_SUCCESS 0x01 // EMS single byte after a Tx Write indicating a success
|
#define EMS_TX_SUCCESS 0x01 // EMS single byte after a Tx Write indicating a success
|
||||||
@@ -74,11 +140,12 @@ typedef enum {
|
|||||||
|
|
||||||
/* EMS logging */
|
/* EMS logging */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EMS_SYS_LOGGING_NONE, // no messages
|
EMS_SYS_LOGGING_NONE, // no messages
|
||||||
EMS_SYS_LOGGING_RAW, // raw data mode
|
EMS_SYS_LOGGING_RAW, // raw data mode
|
||||||
EMS_SYS_LOGGING_BASIC, // only basic read/write messages
|
EMS_SYS_LOGGING_BASIC, // only basic read/write messages
|
||||||
EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat
|
EMS_SYS_LOGGING_THERMOSTAT, // only telegrams sent from thermostat
|
||||||
EMS_SYS_LOGGING_VERBOSE // everything
|
EMS_SYS_LOGGING_SOLARMODULE, // only telegrams sent from thermostat
|
||||||
|
EMS_SYS_LOGGING_VERBOSE // everything
|
||||||
} _EMS_SYS_LOGGING;
|
} _EMS_SYS_LOGGING;
|
||||||
|
|
||||||
// status/counters since last power on
|
// status/counters since last power on
|
||||||
@@ -98,6 +165,7 @@ typedef struct {
|
|||||||
bool emsTxDisabled; // true to prevent all Tx
|
bool emsTxDisabled; // true to prevent all Tx
|
||||||
uint8_t txRetryCount; // # times the last Tx was re-sent
|
uint8_t txRetryCount; // # times the last Tx was re-sent
|
||||||
bool emsReverse; // if true, poll logic is reversed
|
bool emsReverse; // if true, poll logic is reversed
|
||||||
|
uint8_t emsTxMode; // handles Tx logic
|
||||||
} _EMS_Sys_Status;
|
} _EMS_Sys_Status;
|
||||||
|
|
||||||
// The Tx send package
|
// The Tx send package
|
||||||
@@ -148,20 +216,30 @@ const _EMS_TxTelegram EMS_TX_TELEGRAM_NEW = {
|
|||||||
{0x00} // data
|
{0x00} // data
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// where defintions are stored
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t model_id;
|
|
||||||
uint8_t product_id;
|
uint8_t product_id;
|
||||||
char model_string[50];
|
char model_string[50];
|
||||||
} _Boiler_Type;
|
} _Boiler_Type;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t model_id;
|
uint8_t product_id;
|
||||||
|
uint8_t device_id;
|
||||||
|
char model_string[50];
|
||||||
|
} _SolarModule_Type;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
uint8_t product_id;
|
uint8_t product_id;
|
||||||
uint8_t device_id;
|
uint8_t device_id;
|
||||||
char model_string[50];
|
char model_string[50];
|
||||||
} _Other_Type;
|
} _Other_Type;
|
||||||
|
|
||||||
// Definition for thermostat devices
|
typedef struct {
|
||||||
|
uint8_t product_id;
|
||||||
|
uint8_t device_id;
|
||||||
|
char model_string[50];
|
||||||
|
} _HeatPump_Type;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t model_id;
|
uint8_t model_id;
|
||||||
uint8_t product_id;
|
uint8_t product_id;
|
||||||
@@ -170,6 +248,7 @@ typedef struct {
|
|||||||
bool write_supported;
|
bool write_supported;
|
||||||
} _Thermostat_Type;
|
} _Thermostat_Type;
|
||||||
|
|
||||||
|
// for consolidating all types
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t product_id;
|
uint8_t product_id;
|
||||||
uint8_t device_id;
|
uint8_t device_id;
|
||||||
@@ -189,8 +268,8 @@ typedef struct { // UBAParameterWW
|
|||||||
|
|
||||||
// UBAMonitorFast
|
// UBAMonitorFast
|
||||||
uint8_t selFlowTemp; // Selected flow temperature
|
uint8_t selFlowTemp; // Selected flow temperature
|
||||||
int16_t curFlowTemp; // Current flow temperature
|
uint16_t curFlowTemp; // Current flow temperature
|
||||||
int16_t retTemp; // Return temperature
|
uint16_t retTemp; // Return temperature
|
||||||
uint8_t burnGas; // Gas on/off
|
uint8_t burnGas; // Gas on/off
|
||||||
uint8_t fanWork; // Fan on/off
|
uint8_t fanWork; // Fan on/off
|
||||||
uint8_t ignWork; // Ignition on/off
|
uint8_t ignWork; // Ignition on/off
|
||||||
@@ -206,14 +285,14 @@ typedef struct { // UBAParameterWW
|
|||||||
|
|
||||||
// UBAMonitorSlow
|
// UBAMonitorSlow
|
||||||
int16_t extTemp; // Outside temperature
|
int16_t extTemp; // Outside temperature
|
||||||
int16_t boilTemp; // Boiler temperature
|
uint16_t boilTemp; // Boiler temperature
|
||||||
uint8_t pumpMod; // Pump modulation
|
uint8_t pumpMod; // Pump modulation
|
||||||
uint32_t burnStarts; // # burner starts
|
uint32_t burnStarts; // # burner starts
|
||||||
uint32_t burnWorkMin; // Total burner operating time
|
uint32_t burnWorkMin; // Total burner operating time
|
||||||
uint32_t heatWorkMin; // Total heat operating time
|
uint32_t heatWorkMin; // Total heat operating time
|
||||||
|
|
||||||
// UBAMonitorWWMessage
|
// UBAMonitorWWMessage
|
||||||
int16_t wWCurTmp; // Warm Water current temperature:
|
uint16_t wWCurTmp; // Warm Water current temperature
|
||||||
uint32_t wWStarts; // Warm Water # starts
|
uint32_t wWStarts; // Warm Water # starts
|
||||||
uint32_t wWWorkM; // Warm Water # minutes
|
uint32_t wWWorkM; // Warm Water # minutes
|
||||||
uint8_t wWOneTime; // Warm Water one time function on/off
|
uint8_t wWOneTime; // Warm Water one time function on/off
|
||||||
@@ -241,21 +320,39 @@ typedef struct { // UBAParameterWW
|
|||||||
* Telegram package defintions for Other EMS devices
|
* Telegram package defintions for Other EMS devices
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// SM Solar Module - SM10Monitor/SM100Monitor
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool SM; // set true if there is a Solar Module available
|
|
||||||
bool HP; // set true if there is a Heat Pump available
|
|
||||||
int16_t SMcollectorTemp; // collector temp
|
|
||||||
int16_t SMbottomTemp; // bottom temp
|
|
||||||
uint8_t SMpumpModulation; // modulation solar pump
|
|
||||||
uint8_t SMpump; // pump active
|
|
||||||
int16_t SMEnergyLastHour;
|
|
||||||
int16_t SMEnergyToday;
|
|
||||||
int16_t SMEnergyTotal;
|
|
||||||
uint8_t HPModulation; // heatpump modulation in %
|
uint8_t HPModulation; // heatpump modulation in %
|
||||||
uint8_t HPSpeed; // speed 0-100 %
|
uint8_t HPSpeed; // speed 0-100 %
|
||||||
|
uint8_t device_id; // the device ID of the Heat Pump (e.g. 0x30)
|
||||||
|
uint8_t model_id; // Solar Module / Heat Pump model (e.g. 3 > EMS_MODEL_OTHER )
|
||||||
|
uint8_t product_id;
|
||||||
|
char version[10];
|
||||||
|
} _EMS_HeatPump;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t device_id;
|
||||||
|
uint8_t model_id;
|
||||||
|
uint8_t product_id;
|
||||||
|
char version[10];
|
||||||
} _EMS_Other;
|
} _EMS_Other;
|
||||||
|
|
||||||
|
// SM Solar Module - SM10/SM100/ISM1
|
||||||
|
typedef struct {
|
||||||
|
int16_t collectorTemp; // collector temp
|
||||||
|
int16_t bottomTemp; // bottom temp
|
||||||
|
uint8_t pumpModulation; // modulation solar pump
|
||||||
|
uint8_t pump; // pump active
|
||||||
|
int16_t setpoint_maxBottomTemp; // setpoint for maximum collector temp
|
||||||
|
uint16_t EnergyLastHour;
|
||||||
|
uint16_t EnergyToday;
|
||||||
|
uint16_t EnergyTotal;
|
||||||
|
uint32_t pumpWorkMin; // Total solar pump operating time
|
||||||
|
uint8_t device_id; // the device ID of the Solar Module
|
||||||
|
uint8_t model_id; // Solar Module
|
||||||
|
uint8_t product_id;
|
||||||
|
char version[10];
|
||||||
|
} _EMS_SolarModule;
|
||||||
|
|
||||||
// Thermostat data
|
// Thermostat data
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t device_id; // the device ID of the thermostat
|
uint8_t device_id; // the device ID of the thermostat
|
||||||
@@ -300,39 +397,48 @@ void ems_sendRawTelegram(char * telegram);
|
|||||||
void ems_scanDevices();
|
void ems_scanDevices();
|
||||||
void ems_printAllDevices();
|
void ems_printAllDevices();
|
||||||
void ems_printDevices();
|
void ems_printDevices();
|
||||||
|
uint8_t ems_printDevices_s(char * buffer, uint16_t len);
|
||||||
void ems_printTxQueue();
|
void ems_printTxQueue();
|
||||||
void ems_testTelegram(uint8_t test_num);
|
void ems_testTelegram(uint8_t test_num);
|
||||||
void ems_startupTelegrams();
|
void ems_startupTelegrams();
|
||||||
bool ems_checkEMSBUSAlive();
|
bool ems_checkEMSBUSAlive();
|
||||||
void ems_clearDeviceList();
|
void ems_clearDeviceList();
|
||||||
|
void ems_setTxMode(uint8_t mode);
|
||||||
|
|
||||||
void ems_setThermostatTemp(float temperature, uint8_t temptype = 0);
|
void ems_setThermostatTemp(float temperature, uint8_t temptype = 0);
|
||||||
void ems_setThermostatMode(uint8_t mode);
|
void ems_setThermostatMode(uint8_t mode);
|
||||||
void ems_setThermostatHC(uint8_t hc);
|
void ems_setThermostatHC(uint8_t hc);
|
||||||
void ems_setWarmWaterTemp(uint8_t temperature);
|
void ems_setWarmWaterTemp(uint8_t temperature);
|
||||||
void ems_setFlowTemp(uint8_t temperature);
|
void ems_setFlowTemp(uint8_t temperature);
|
||||||
void ems_setWarmWaterActivated(bool activated);
|
void ems_setWarmWaterActivated(bool activated);
|
||||||
void ems_setWarmTapWaterActivated(bool activated);
|
void ems_setWarmTapWaterActivated(bool activated);
|
||||||
void ems_setPoll(bool b);
|
void ems_setPoll(bool b);
|
||||||
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
|
void ems_setLogging(_EMS_SYS_LOGGING loglevel);
|
||||||
void ems_setEmsRefreshed(bool b);
|
void ems_setEmsRefreshed(bool b);
|
||||||
void ems_setWarmWaterModeComfort(uint8_t comfort);
|
void ems_setWarmWaterModeComfort(uint8_t comfort);
|
||||||
void ems_setModels();
|
void ems_setModels();
|
||||||
void ems_setTxDisabled(bool b);
|
void ems_setTxDisabled(bool b);
|
||||||
|
bool ems_getTxDisabled();
|
||||||
|
uint8_t ems_getTxMode();
|
||||||
|
|
||||||
char * ems_getThermostatDescription(char * buffer);
|
char * ems_getThermostatDescription(char * buffer);
|
||||||
char * ems_getBoilerDescription(char * buffer);
|
char * ems_getBoilerDescription(char * buffer);
|
||||||
|
char * ems_getSolarModuleDescription(char * buffer);
|
||||||
|
char * ems_getHeatPumpDescription(char * buffer);
|
||||||
void ems_getThermostatValues();
|
void ems_getThermostatValues();
|
||||||
void ems_getBoilerValues();
|
void ems_getBoilerValues();
|
||||||
void ems_getOtherValues();
|
void ems_getSolarModuleValues();
|
||||||
bool ems_getPoll();
|
bool ems_getPoll();
|
||||||
bool ems_getTxEnabled();
|
bool ems_getTxEnabled();
|
||||||
bool ems_getThermostatEnabled();
|
bool ems_getThermostatEnabled();
|
||||||
bool ems_getBoilerEnabled();
|
bool ems_getBoilerEnabled();
|
||||||
|
bool ems_getSolarModuleEnabled();
|
||||||
|
bool ems_getHeatPumpEnabled();
|
||||||
bool ems_getBusConnected();
|
bool ems_getBusConnected();
|
||||||
_EMS_SYS_LOGGING ems_getLogging();
|
_EMS_SYS_LOGGING ems_getLogging();
|
||||||
bool ems_getEmsRefreshed();
|
bool ems_getEmsRefreshed();
|
||||||
uint8_t ems_getThermostatModel();
|
uint8_t ems_getThermostatModel();
|
||||||
|
uint8_t ems_getSolarModuleModel();
|
||||||
void ems_discoverModels();
|
void ems_discoverModels();
|
||||||
bool ems_getTxCapable();
|
bool ems_getTxCapable();
|
||||||
uint32_t ems_getPollFrequency();
|
uint32_t ems_getPollFrequency();
|
||||||
@@ -342,12 +448,12 @@ uint8_t _crcCalculator(uint8_t * data, uint8_t len);
|
|||||||
void _processType(_EMS_RxTelegram * EMS_RxTelegram);
|
void _processType(_EMS_RxTelegram * EMS_RxTelegram);
|
||||||
void _debugPrintPackage(const char * prefix, _EMS_RxTelegram * EMS_RxTelegram, const char * color);
|
void _debugPrintPackage(const char * prefix, _EMS_RxTelegram * EMS_RxTelegram, const char * color);
|
||||||
void _ems_clearTxData();
|
void _ems_clearTxData();
|
||||||
int _ems_findBoilerModel(uint8_t model_id);
|
|
||||||
bool _ems_setModel(uint8_t model_id);
|
|
||||||
void _removeTxQueue();
|
void _removeTxQueue();
|
||||||
|
|
||||||
// global so can referenced in other classes
|
// global so can referenced in other classes
|
||||||
extern _EMS_Sys_Status EMS_Sys_Status;
|
extern _EMS_Sys_Status EMS_Sys_Status;
|
||||||
extern _EMS_Boiler EMS_Boiler;
|
extern _EMS_Boiler EMS_Boiler;
|
||||||
extern _EMS_Thermostat EMS_Thermostat;
|
extern _EMS_Thermostat EMS_Thermostat;
|
||||||
extern _EMS_Other EMS_Other;
|
extern _EMS_SolarModule EMS_SolarModule;
|
||||||
|
extern _EMS_HeatPump EMS_HeatPump;
|
||||||
|
extern _EMS_Other EMS_Other;
|
||||||
|
|||||||
@@ -41,14 +41,17 @@
|
|||||||
#define EMS_OFFSET_UBASetPoints_flowtemp 0 // flow temp
|
#define EMS_OFFSET_UBASetPoints_flowtemp 0 // flow temp
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
#define EMS_TYPE_SM10Monitor 0x97 // SM10Monitor
|
#define EMS_TYPE_SM10Monitor 0x97 // SM10Monitor
|
||||||
#define EMS_TYPE_SM100Monitor 0x0262 // SM100Monitor
|
#define EMS_TYPE_SM100Monitor 0x0262 // SM100Monitor
|
||||||
#define EMS_TYPE_SM100Status 0x0264 // SM100Status
|
#define EMS_TYPE_SM100Status 0x0264 // SM100Status
|
||||||
#define EMS_TYPE_SM100Status2 0x026A // SM100Status2
|
#define EMS_TYPE_SM100Status2 0x026A // SM100Status2
|
||||||
#define EMS_TYPE_SM100Energy 0x028E // SM100Energy
|
#define EMS_TYPE_SM100Energy 0x028E // SM100Energy
|
||||||
#define EMS_TYPE_HPMonitor1 0xE3 // HeatPump Monitor 1
|
#define EMS_TYPE_HPMonitor1 0xE3 // HeatPump Monitor 1
|
||||||
#define EMS_TYPE_HPMonitor2 0xE5 // HeatPump Monitor 2
|
#define EMS_TYPE_HPMonitor2 0xE5 // HeatPump Monitor 2
|
||||||
#define EMS_TYPE_ISM1StatusMessage 0x0003 // Solar Module Junkers ISM1 Status
|
|
||||||
|
#define EMS_TYPE_ISM1StatusMessage 0x0003 // Solar Module Junkers ISM1 Status
|
||||||
|
#define EMS_TYPE_ISM1Set 0x0001 // for setting values of the solar module like max boiler temp
|
||||||
|
#define EMS_OFFSET_ISM1Set_MaxBoilerTemp 6 // position of max boiler temp e.g. 50 in the following example: 90 30 FF 06 00 01 50 (CRC=2C)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Thermostats...
|
* Thermostats...
|
||||||
@@ -63,7 +66,7 @@
|
|||||||
#define EMS_TYPE_RC10Set 0xB0 // for setting values like temp and mode
|
#define EMS_TYPE_RC10Set 0xB0 // for setting values like temp and mode
|
||||||
#define EMS_OFFSET_RC10Set_temp 4 // position of thermostat setpoint temperature
|
#define EMS_OFFSET_RC10Set_temp 4 // position of thermostat setpoint temperature
|
||||||
#define EMS_OFFSET_RC10StatusMessage_setpoint 1 // setpoint temp
|
#define EMS_OFFSET_RC10StatusMessage_setpoint 1 // setpoint temp
|
||||||
#define EMS_OFFSET_RC10StatusMessage_curr 3 // current temp
|
#define EMS_OFFSET_RC10StatusMessage_curr 2 // current temp
|
||||||
|
|
||||||
// RC20 specific
|
// RC20 specific
|
||||||
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
|
#define EMS_TYPE_RC20StatusMessage 0x91 // is an automatic thermostat broadcast giving us temps
|
||||||
@@ -109,6 +112,7 @@
|
|||||||
#define EMS_OFFSET_RCPLUSStatusMessage_setpoint 3 // setpoint temp
|
#define EMS_OFFSET_RCPLUSStatusMessage_setpoint 3 // setpoint temp
|
||||||
#define EMS_OFFSET_RCPLUSStatusMessage_curr 0 // current temp
|
#define EMS_OFFSET_RCPLUSStatusMessage_curr 0 // current temp
|
||||||
#define EMS_OFFSET_RCPLUSGet_mode_day 8 // day/night mode
|
#define EMS_OFFSET_RCPLUSGet_mode_day 8 // day/night mode
|
||||||
|
#define EMS_OFFSET_RCPLUSStatusMessage_mode 0x0A // thermostat mode (auto, manual)
|
||||||
|
|
||||||
// Junkers FR10, FW100 (EMS Plus)
|
// Junkers FR10, FW100 (EMS Plus)
|
||||||
#define EMS_TYPE_JunkersStatusMessage 0x6F // is an automatic thermostat broadcast giving us temps
|
#define EMS_TYPE_JunkersStatusMessage 0x6F // is an automatic thermostat broadcast giving us temps
|
||||||
@@ -118,16 +122,19 @@
|
|||||||
|
|
||||||
// Known EMS types
|
// Known EMS types
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EMS_MODEL_NONE,
|
EMS_MODEL_NONE, // unset
|
||||||
EMS_MODEL_ALL, // common for all devices
|
EMS_MODEL_ALL, // common for all devices
|
||||||
|
|
||||||
// generic ID for the boiler
|
// heatpump
|
||||||
|
EMS_MODEL_HP,
|
||||||
|
|
||||||
|
// solar module
|
||||||
|
EMS_MODEL_SM,
|
||||||
|
|
||||||
|
// boiler
|
||||||
EMS_MODEL_UBA,
|
EMS_MODEL_UBA,
|
||||||
|
|
||||||
// generic ID for all the other weird devices
|
// and the thermostats
|
||||||
EMS_MODEL_OTHER,
|
|
||||||
|
|
||||||
// and finally the thermostats
|
|
||||||
EMS_MODEL_ES73,
|
EMS_MODEL_ES73,
|
||||||
EMS_MODEL_RC10,
|
EMS_MODEL_RC10,
|
||||||
EMS_MODEL_RC20,
|
EMS_MODEL_RC20,
|
||||||
@@ -135,7 +142,7 @@ typedef enum {
|
|||||||
EMS_MODEL_RC30,
|
EMS_MODEL_RC30,
|
||||||
EMS_MODEL_RC35,
|
EMS_MODEL_RC35,
|
||||||
EMS_MODEL_EASY,
|
EMS_MODEL_EASY,
|
||||||
EMS_MODEL_RC310,
|
EMS_MODEL_RC300,
|
||||||
EMS_MODEL_CW100,
|
EMS_MODEL_CW100,
|
||||||
EMS_MODEL_1010,
|
EMS_MODEL_1010,
|
||||||
EMS_MODEL_OT,
|
EMS_MODEL_OT,
|
||||||
@@ -147,56 +154,71 @@ typedef enum {
|
|||||||
|
|
||||||
} _EMS_MODEL_ID;
|
} _EMS_MODEL_ID;
|
||||||
|
|
||||||
// EMS types for known devices. This list will be extended when new devices are recognized.
|
// EMS types for known boilers. This list will be extended when new devices are recognized.
|
||||||
// The device_id is always 0x08
|
// The device_id is always 0x08
|
||||||
// format is MODEL_ID, PRODUCT ID, DESCRIPTION
|
// format is PRODUCT ID, DESCRIPTION
|
||||||
const _Boiler_Type Boiler_Types[] = {
|
const _Boiler_Type Boiler_Types[] = {
|
||||||
|
|
||||||
{EMS_MODEL_UBA, 72, "MC10 Module"},
|
{72, "MC10 Module"},
|
||||||
{EMS_MODEL_UBA, 123, "Buderus GB172/Nefit Trendline/Junkers Cerapur"},
|
{123, "Buderus GB172/Nefit Trendline/Junkers Cerapur"},
|
||||||
{EMS_MODEL_UBA, 115, "Nefit Topline Compact/Buderus GB162"},
|
{115, "Nefit Topline Compact/Buderus GB162"},
|
||||||
{EMS_MODEL_UBA, 203, "Buderus Logamax U122/Junkers Cerapur"},
|
{203, "Buderus Logamax U122/Junkers Cerapur"},
|
||||||
{EMS_MODEL_UBA, 208, "Buderus Logamax plus/GB192"},
|
{208, "Buderus Logamax plus/GB192"},
|
||||||
{EMS_MODEL_UBA, 64, "Sieger BK15/Nefit Smartline/Buderus GB152"},
|
{64, "Sieger BK15/Nefit Smartline/Buderus GB152"},
|
||||||
{EMS_MODEL_UBA, EMS_PRODUCTID_HEATRONICS, "Bosch Condens 2500/Junkers Heatronics3"}, // Junkers
|
{EMS_PRODUCTID_HEATRONICS, "Bosch Condens 2500/Junkers Heatronics3"},
|
||||||
{EMS_MODEL_UBA, 122, "Nefit Proline"},
|
{122, "Nefit Proline"},
|
||||||
{EMS_MODEL_UBA, 172, "Nefit Enviline"}
|
{172, "Nefit Enviline"}
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Other EMS devices which are not considered boilers or thermostats
|
|
||||||
const _Other_Type Other_Types[] = {
|
|
||||||
|
|
||||||
{EMS_MODEL_OTHER, 69, 0x21, "MM10 Mixer Module"},
|
|
||||||
{EMS_MODEL_OTHER, 71, 0x11, "WM10 Switch Module"},
|
|
||||||
{EMS_MODEL_OTHER, 160, 0x20, "MM100 Mixing Module"},
|
|
||||||
{EMS_MODEL_OTHER, 160, 0x21, "MM100 Mixing Module"},
|
|
||||||
{EMS_MODEL_OTHER, 159, 0x21, "MM50 Mixing Module"},
|
|
||||||
{EMS_MODEL_OTHER, 68, 0x09, "BC10/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, 152, 0x09, "Junkers Controller"},
|
|
||||||
{EMS_MODEL_OTHER, 205, 0x02, "Nefit Moduline Easy Connect"},
|
|
||||||
{EMS_MODEL_OTHER, 73, EMS_ID_SM, "SM10 Solar Module"},
|
|
||||||
{EMS_MODEL_OTHER, 163, EMS_ID_SM, "SM100 Solar Module"},
|
|
||||||
{EMS_MODEL_OTHER, 171, 0x02, "EMS-OT OpenTherm converter"},
|
|
||||||
{EMS_MODEL_OTHER, 252, EMS_ID_HP, "HeatPump Module"}, // warning, fake product id!
|
|
||||||
{EMS_MODEL_OTHER, 101, 0x30, "Junkers ISM1 Solar Controller"},
|
|
||||||
{EMS_MODEL_OTHER, 189, EMS_ID_GATEWAY, "Web Gateway KM200"}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Known Solar Module types
|
||||||
|
* format is PRODUCT ID, DEVICE ID, DESCRIPTION
|
||||||
|
*/
|
||||||
|
const _SolarModule_Type SolarModule_Types[] = {
|
||||||
|
|
||||||
|
{EMS_PRODUCTID_SM10, EMS_ID_SM, "SM10 Solar Module"},
|
||||||
|
{EMS_PRODUCTID_SM100, EMS_ID_SM, "SM100 Solar Module"},
|
||||||
|
{EMS_PRODUCTID_ISM1, EMS_ID_SM, "Junkers ISM1 Solar Module"}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Other EMS devices which are not considered boilers, thermostats or solar modules
|
||||||
|
// format is PRODUCT ID, DEVICE ID, DESCRIPTION
|
||||||
|
const _Other_Type Other_Types[] = {
|
||||||
|
|
||||||
|
{69, 0x21, "MM10 Mixer Module"},
|
||||||
|
{71, 0x11, "WM10 Switch Module"},
|
||||||
|
{160, 0x20, "MM100 Mixing Module"},
|
||||||
|
{160, 0x21, "MM100 Mixing Module"},
|
||||||
|
{159, 0x21, "MM50 Mixing Module"},
|
||||||
|
{68, 0x09, "BC10/RFM20 Receiver"},
|
||||||
|
{190, 0x09, "BC10 Base Controller"},
|
||||||
|
{114, 0x09, "BC10 Base Controller"},
|
||||||
|
{125, 0x09, "BC25 Base Controller"},
|
||||||
|
{152, 0x09, "Junkers Controller"},
|
||||||
|
{205, 0x02, "Nefit Moduline Easy Connect"},
|
||||||
|
{206, 0x02, "Bosch Easy Connect"},
|
||||||
|
{171, 0x02, "EMS-OT OpenTherm converter"},
|
||||||
|
{252, EMS_ID_HP, "HeatPump Module"},
|
||||||
|
{189, EMS_ID_GATEWAY, "Web Gateway KM200"}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// heatpump
|
||||||
|
// format is PRODUCT ID, DEVICE ID, DESCRIPTION
|
||||||
|
const _HeatPump_Type HeatPump_Types[] = {{252, EMS_ID_HP, "HeatPump Module"}};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Known thermostat types and their capabilities
|
* Known thermostat types and their capabilities
|
||||||
|
* format is MODEL_ID, PRODUCT ID, DEVICE ID, DESCRIPTION
|
||||||
*/
|
*/
|
||||||
const _Thermostat_Type Thermostat_Types[] = {
|
const _Thermostat_Type Thermostat_Types[] = {
|
||||||
|
|
||||||
// Easy devices - not currently supporting write operations
|
// Easy devices - not currently supporting write operations
|
||||||
{EMS_MODEL_EASY, 202, 0x18, "TC100/Nefit Easy", EMS_THERMOSTAT_WRITE_NO},
|
{EMS_MODEL_EASY, 202, 0x18, "Logamatic TC100/Nefit Moduline Easy", EMS_THERMOSTAT_WRITE_NO},
|
||||||
{EMS_MODEL_EASY, 203, 0x18, "Bosch EasyControl CT200", EMS_THERMOSTAT_WRITE_NO},
|
{EMS_MODEL_EASY, 203, 0x18, "Bosch EasyControl CT200", EMS_THERMOSTAT_WRITE_NO},
|
||||||
{EMS_MODEL_EASY, 206, 0x02, "Bosch Easy", EMS_THERMOSTAT_WRITE_NO},
|
{EMS_MODEL_CW100, 157, 0x18, "Bosch CW100", EMS_THERMOSTAT_WRITE_NO},
|
||||||
{EMS_MODEL_CW100, 157, 0x18, "CW100", EMS_THERMOSTAT_WRITE_NO},
|
|
||||||
|
|
||||||
// Buderus/Nefit
|
// Buderus/Nefit
|
||||||
{EMS_MODEL_RC10, 79, 0x17, "RC10/Nefit Moduline 100", EMS_THERMOSTAT_WRITE_YES},
|
{EMS_MODEL_RC10, 79, 0x17, "RC10/Nefit Moduline 100", EMS_THERMOSTAT_WRITE_YES},
|
||||||
@@ -204,7 +226,7 @@ const _Thermostat_Type Thermostat_Types[] = {
|
|||||||
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_WRITE_YES},
|
{EMS_MODEL_RC20F, 93, 0x18, "RC20F", EMS_THERMOSTAT_WRITE_YES},
|
||||||
{EMS_MODEL_RC30, 78, 0x10, "RC30/Nefit Moduline 400", EMS_THERMOSTAT_WRITE_YES},
|
{EMS_MODEL_RC30, 78, 0x10, "RC30/Nefit Moduline 400", EMS_THERMOSTAT_WRITE_YES},
|
||||||
{EMS_MODEL_RC35, 86, 0x10, "RC35", EMS_THERMOSTAT_WRITE_YES},
|
{EMS_MODEL_RC35, 86, 0x10, "RC35", EMS_THERMOSTAT_WRITE_YES},
|
||||||
{EMS_MODEL_RC310, 158, 0x10, "RC3x0/Nefit Moduline 1010H", EMS_THERMOSTAT_WRITE_NO},
|
{EMS_MODEL_RC300, 158, 0x10, "RC300/RC310/Nefit Moduline 3000", EMS_THERMOSTAT_WRITE_NO},
|
||||||
{EMS_MODEL_1010, 165, 0x18, "Nefit Moduline 1010", EMS_THERMOSTAT_WRITE_NO},
|
{EMS_MODEL_1010, 165, 0x18, "Nefit Moduline 1010", EMS_THERMOSTAT_WRITE_NO},
|
||||||
|
|
||||||
// Sieger
|
// Sieger
|
||||||
@@ -217,5 +239,4 @@ const _Thermostat_Type Thermostat_Types[] = {
|
|||||||
{EMS_MODEL_FR110, 108, 0x18, "Junkers FR110", EMS_THERMOSTAT_WRITE_NO},
|
{EMS_MODEL_FR110, 108, 0x18, "Junkers FR110", EMS_THERMOSTAT_WRITE_NO},
|
||||||
{EMS_MODEL_FW120, 192, 0x10, "Junkers FW120", EMS_THERMOSTAT_WRITE_NO}
|
{EMS_MODEL_FW120, 192, 0x10, "Junkers FW120", EMS_THERMOSTAT_WRITE_NO}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
227
src/emsuart.cpp
227
src/emsuart.cpp
@@ -29,7 +29,7 @@ static void emsuart_rx_intr_handler(void * para) {
|
|||||||
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_BUSY; // status set to busy
|
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_BUSY; // status set to busy
|
||||||
length = 0;
|
length = 0;
|
||||||
}
|
}
|
||||||
|
GPIO_H(RX_MARK_MASK);
|
||||||
// fill IRQ buffer, by emptying Rx FIFO
|
// fill IRQ buffer, by emptying Rx FIFO
|
||||||
if (USIS(EMSUART_UART) & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) {
|
if (USIS(EMSUART_UART) & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) {
|
||||||
while ((USS(EMSUART_UART) >> USRXC) & 0xFF) {
|
while ((USS(EMSUART_UART) >> USRXC) & 0xFF) {
|
||||||
@@ -39,20 +39,20 @@ static void emsuart_rx_intr_handler(void * para) {
|
|||||||
// clear Rx FIFO full and Rx FIFO timeout interrupts
|
// clear Rx FIFO full and Rx FIFO timeout interrupts
|
||||||
USIC(EMSUART_UART) = (1 << UIFF) | (1 << UITO);
|
USIC(EMSUART_UART) = (1 << UIFF) | (1 << UITO);
|
||||||
}
|
}
|
||||||
|
GPIO_L(RX_MARK_MASK);
|
||||||
|
|
||||||
// BREAK detection = End of EMS data block
|
// BREAK detection = End of EMS data block
|
||||||
if (USIS(EMSUART_UART) & ((1 << UIBD))) {
|
if (USIS(EMSUART_UART) & ((1 << UIBD))) {
|
||||||
ETS_UART_INTR_DISABLE(); // disable all interrupts and clear them
|
ETS_UART_INTR_DISABLE(); // disable all interrupts and clear them
|
||||||
|
|
||||||
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt
|
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt
|
||||||
|
|
||||||
pEMSRxBuf->length = length;
|
pEMSRxBuf->length = length;
|
||||||
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, length); // copy data into transfer buffer, including the BRK 0x00 at the end
|
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, length); // copy data into transfer buffer, including the BRK 0x00 at the end
|
||||||
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE; // set the status flag stating BRK has been received and we can start a new package
|
EMS_Sys_Status.emsRxStatus = EMS_RX_STATUS_IDLE; // set the status flag stating BRK has been received and we can start a new package
|
||||||
|
ETS_UART_INTR_ENABLE(); // re-enable UART interrupts
|
||||||
|
|
||||||
system_os_post(EMSUART_recvTaskPrio, 0, 0); // call emsuart_recvTask() at next opportunity
|
system_os_post(EMSUART_recvTaskPrio, 0, 0); // call emsuart_recvTask() at next opportunity
|
||||||
|
RX_PULSE(EMSUART_BIT_TIME / 2);
|
||||||
ETS_UART_INTR_ENABLE(); // re-enable UART interrupts
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,21 +63,22 @@ static void emsuart_rx_intr_handler(void * para) {
|
|||||||
*/
|
*/
|
||||||
static void ICACHE_FLASH_ATTR emsuart_recvTask(os_event_t * events) {
|
static void ICACHE_FLASH_ATTR emsuart_recvTask(os_event_t * events) {
|
||||||
_EMSRxBuf * pCurrent = pEMSRxBuf;
|
_EMSRxBuf * pCurrent = pEMSRxBuf;
|
||||||
uint8_t length = pCurrent->length; // number of bytes including the BRK at the end
|
pEMSRxBuf = paEMSRxBuf[++emsRxBufIdx % EMS_MAXBUFFERS]; // next free EMS Receive buffer
|
||||||
|
uint8_t length = pCurrent->length; // number of bytes including the BRK at the end
|
||||||
|
pCurrent->length = 0;
|
||||||
|
|
||||||
// validate and transmit the EMS buffer, excluding the BRK
|
// validate and transmit the EMS buffer, excluding the BRK
|
||||||
if (length == 2) {
|
if (length == 2) {
|
||||||
// it's a poll or status code, single byte
|
RX_PULSE(20);
|
||||||
|
// it's a poll or status code, single byte and ok to send on
|
||||||
ems_parseTelegram((uint8_t *)pCurrent->buffer, 1);
|
ems_parseTelegram((uint8_t *)pCurrent->buffer, 1);
|
||||||
} else if ((length > 4) && (pCurrent->buffer[length - 2] != 0x00)) {
|
} else if ((length > 4) && (length <= EMS_MAXBUFFERSIZE + 1) && (pCurrent->buffer[length - 2] != 0x00)) {
|
||||||
// ignore double BRK at the end, possibly from the Tx loopback
|
// ignore double BRK at the end, possibly from the Tx loopback
|
||||||
// also telegrams with no data value
|
// also telegrams with no data value
|
||||||
|
RX_PULSE(40);
|
||||||
ems_parseTelegram((uint8_t *)pCurrent->buffer, length - 1); // transmit EMS buffer, excluding the BRK
|
ems_parseTelegram((uint8_t *)pCurrent->buffer, length - 1); // transmit EMS buffer, excluding the BRK
|
||||||
}
|
}
|
||||||
|
// memset(pCurrent->buffer, 0x00, EMS_MAXBUFFERSIZE); // wipe memory just to be safe
|
||||||
memset(pCurrent->buffer, 0x00, EMS_MAXBUFFERSIZE); // wipe memory just to be safe
|
|
||||||
|
|
||||||
pEMSRxBuf = paEMSRxBuf[++emsRxBufIdx % EMS_MAXBUFFERS]; // next free EMS Receive buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -94,7 +95,7 @@ static inline void ICACHE_FLASH_ATTR emsuart_flush_fifos() {
|
|||||||
*/
|
*/
|
||||||
void ICACHE_FLASH_ATTR emsuart_init() {
|
void ICACHE_FLASH_ATTR emsuart_init() {
|
||||||
ETS_UART_INTR_DISABLE();
|
ETS_UART_INTR_DISABLE();
|
||||||
ETS_UART_INTR_ATTACH(NULL, NULL);
|
ETS_UART_INTR_ATTACH(nullptr, nullptr);
|
||||||
|
|
||||||
// allocate and preset EMS Receive buffers
|
// allocate and preset EMS Receive buffers
|
||||||
for (int i = 0; i < EMS_MAXBUFFERS; i++) {
|
for (int i = 0; i < EMS_MAXBUFFERS; i++) {
|
||||||
@@ -117,11 +118,14 @@ void ICACHE_FLASH_ATTR emsuart_init() {
|
|||||||
|
|
||||||
// conf1 params
|
// conf1 params
|
||||||
// UCTOE = RX TimeOut enable (default is 1)
|
// UCTOE = RX TimeOut enable (default is 1)
|
||||||
// UCTOT = RX TimeOut Threshold (7 bit) = want this when no more data after 2 characters (default is 2)
|
// UCTOT = RX TimeOut Threshold (7 bit) = want this when no more data after 1 characters (default is 2)
|
||||||
// UCFFT = RX FIFO Full Threshold (7 bit) = want this to be 31 for 32 bytes of buffer (default was 127)
|
// UCFFT = RX FIFO Full Threshold (7 bit) = want this to be 31 for 32 bytes of buffer (default was 127)
|
||||||
// see https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf
|
// see https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf
|
||||||
USC1(EMSUART_UART) = 0; // reset config first
|
//
|
||||||
USC1(EMSUART_UART) = (EMS_MAX_TELEGRAM_LENGTH << UCFFT) | (0x02 << UCTOT) | (1 << UCTOE); // enable interupts
|
// change: we set UCFFT to 1 to get an immediate indicator about incoming trafffic.
|
||||||
|
// Otherwise, we're only noticed by UCTOT or RxBRK!
|
||||||
|
USC1(EMSUART_UART) = 0; // reset config first
|
||||||
|
USC1(EMSUART_UART) = (0x01 << UCFFT) | (0x01 << UCTOT) | (1 << UCTOE); // enable interupts
|
||||||
|
|
||||||
// set interrupts for triggers
|
// set interrupts for triggers
|
||||||
USIC(EMSUART_UART) = 0xFFFF; // clear all interupts
|
USIC(EMSUART_UART) = 0xFFFF; // clear all interupts
|
||||||
@@ -138,9 +142,9 @@ void ICACHE_FLASH_ATTR emsuart_init() {
|
|||||||
system_set_os_print(0);
|
system_set_os_print(0);
|
||||||
|
|
||||||
// swap Rx and Tx pins to use GPIO13 (D7) and GPIO15 (D8) respectively
|
// swap Rx and Tx pins to use GPIO13 (D7) and GPIO15 (D8) respectively
|
||||||
system_uart_swap();
|
//system_uart_swap();
|
||||||
|
|
||||||
ETS_UART_INTR_ATTACH(emsuart_rx_intr_handler, NULL);
|
ETS_UART_INTR_ATTACH(emsuart_rx_intr_handler, nullptr);
|
||||||
ETS_UART_INTR_ENABLE();
|
ETS_UART_INTR_ENABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,66 +164,155 @@ void ICACHE_FLASH_ATTR emsuart_start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set loopback mode and clear Tx/Rx FIFO
|
* Send a BRK signal
|
||||||
|
* Which is a 11-bit set of zero's (11 cycles)
|
||||||
*/
|
*/
|
||||||
static inline void ICACHE_FLASH_ATTR emsuart_loopback(bool enable) {
|
void ICACHE_FLASH_ATTR emsuart_tx_brk() {
|
||||||
if (enable)
|
uint32_t tmp;
|
||||||
USC0(EMSUART_UART) |= (1 << UCLBE); // enable loopback
|
|
||||||
else
|
// must make sure Tx FIFO is empty
|
||||||
USC0(EMSUART_UART) &= ~(1 << UCLBE); // disable loopback
|
while (((USS(EMSUART_UART) >> USTXC) & 0xFF) != 0)
|
||||||
|
;
|
||||||
|
|
||||||
|
tmp = ((1 << UCRXRST) | (1 << UCTXRST)); // bit mask
|
||||||
|
USC0(EMSUART_UART) |= (tmp); // set bits
|
||||||
|
USC0(EMSUART_UART) &= ~(tmp); // clear bits
|
||||||
|
|
||||||
|
// To create a 11-bit <BRK> we set TXD_BRK bit so the break signal will
|
||||||
|
// automatically be sent when the tx fifo is empty
|
||||||
|
tmp = (1 << UCBRK);
|
||||||
|
GPIO_H(TX_MARK_MASK);
|
||||||
|
USC0(EMSUART_UART) |= (tmp); // set bit
|
||||||
|
|
||||||
|
if (EMS_Sys_Status.emsTxMode <= 1) { // classic mode and ems+ (0, 1)
|
||||||
|
delayMicroseconds(EMSUART_TX_BRK_WAIT);
|
||||||
|
} else if (EMS_Sys_Status.emsTxMode == 3) { // junkers mode
|
||||||
|
delayMicroseconds(EMSUART_TX_WAIT_BRK - EMSUART_TX_LAG); // 1144 (11 Bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
USC0(EMSUART_UART) &= ~(tmp); // clear bit
|
||||||
|
GPIO_L(TX_MARK_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send to Tx, ending with a <BRK>
|
* Send to Tx, ending with a <BRK>
|
||||||
*/
|
*/
|
||||||
void ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
_EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) {
|
||||||
if (len == 0)
|
_EMS_TX_STATUS result = EMS_TX_STATUS_OK;
|
||||||
return;
|
if (len) {
|
||||||
|
LA_PULSE(50);
|
||||||
|
// temp code until we get mode 2 working without resets
|
||||||
|
if (EMS_Sys_Status.emsTxMode == 0) { // classic mode logic
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
TX_PULSE(EMSUART_BIT_TIME / 4);
|
||||||
|
USF(EMSUART_UART) = buf[i];
|
||||||
|
}
|
||||||
|
emsuart_tx_brk(); // send <BRK>
|
||||||
|
} else if (EMS_Sys_Status.emsTxMode == 1) { // With extra tx delay for EMS+
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
TX_PULSE(EMSUART_BIT_TIME / 4);
|
||||||
|
USF(EMSUART_UART) = buf[i];
|
||||||
|
delayMicroseconds(EMSUART_TX_BRK_WAIT); // https://github.com/proddy/EMS-ESP/issues/23#
|
||||||
|
}
|
||||||
|
emsuart_tx_brk(); // send <BRK>
|
||||||
|
} else if (EMS_Sys_Status.emsTxMode == 3) { // Junkers logic by @philrich
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
TX_PULSE(EMSUART_BIT_TIME / 4);
|
||||||
|
USF(EMSUART_UART) = buf[i];
|
||||||
|
|
||||||
/*
|
// just to be safe wait for tx fifo empty (needed?)
|
||||||
* based on code from https://github.com/proddy/EMS-ESP/issues/103 by @susisstrolch
|
while (((USS(EMSUART_UART) >> USTXC) & 0xff) != 0)
|
||||||
* we emit the whole telegram, with Rx interrupt disabled, collecting busmaster response in FIFO.
|
;
|
||||||
* after sending the last char we poll the Rx status until either
|
|
||||||
* - size(Rx FIFO) == size(Tx-Telegram)
|
|
||||||
* - <BRK> is detected
|
|
||||||
* At end of receive we re-enable Rx-INT and send a Tx-BRK in loopback mode.
|
|
||||||
*/
|
|
||||||
ETS_UART_INTR_DISABLE(); // disable rx interrupt
|
|
||||||
|
|
||||||
// clear Rx status register
|
// wait until bits are sent on wire
|
||||||
USC0(EMSUART_UART) |= (1 << UCRXRST); // reset uart rx fifo
|
delayMicroseconds(EMSUART_TX_WAIT_BYTE - EMSUART_TX_LAG + EMSUART_TX_WAIT_GAP);
|
||||||
emsuart_flush_fifos();
|
}
|
||||||
|
emsuart_tx_brk(); // send <BRK>
|
||||||
|
} else if (EMS_Sys_Status.emsTxMode == 2) {
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* based on code from https://github.com/proddy/EMS-ESP/issues/103 by @susisstrolch
|
||||||
|
* we emit the whole telegram, with Rx interrupt disabled, collecting busmaster response in FIFO.
|
||||||
|
* after sending the last char we poll the Rx status until either
|
||||||
|
* - size(Rx FIFO) == size(Tx-Telegram)
|
||||||
|
* - <BRK> is detected
|
||||||
|
* At end of receive we re-enable Rx-INT and send a Tx-BRK in loopback mode.
|
||||||
|
*
|
||||||
|
* EMS-Bus error handling
|
||||||
|
* 1. Busmaster stops echoing on Tx w/o permission
|
||||||
|
* 2. Busmaster cancel telegram by sending a BRK
|
||||||
|
*
|
||||||
|
* Case 1. is handled by a watchdog counter which is reset on each
|
||||||
|
* Tx attempt. The timeout should be 20x EMSUART_BIT_TIME plus
|
||||||
|
* some smart guess for processing time on targeted EMS device.
|
||||||
|
* We set EMS_Sys_Status.emsTxStatus to EMS_TX_WTD_TIMEOUT and return
|
||||||
|
*
|
||||||
|
* Case 2. is handled via a BRK chk during transmission.
|
||||||
|
* We set EMS_Sys_Status.emsTxStatus to EMS_TX_BRK_DETECT and return
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
// throw out the telegram...
|
// shorter busy poll...
|
||||||
for (uint8_t i = 0; i < len;) {
|
#define EMSUART_BUSY_WAIT (EMSUART_BIT_TIME / 8)
|
||||||
USF(EMSUART_UART) = buf[i++]; // send each Tx byte
|
#define EMS_TX_TO_COUNT ((20 + 10000 / EMSUART_BIT_TIME) * 8)
|
||||||
// wait for echo from busmaster
|
uint16_t wdc = EMS_TX_TO_COUNT;
|
||||||
while ((((USS(EMSUART_UART) >> USRXC) & 0xFF) < i || (USIS(EMSUART_UART) & (1 << UIBD)))) {
|
|
||||||
delayMicroseconds(EMSUART_BIT_TIME); // burn CPU cycles...
|
ETS_UART_INTR_DISABLE(); // disable rx interrupt
|
||||||
|
|
||||||
|
// clear Rx status register
|
||||||
|
USC0(EMSUART_UART) |= (1 << UCRXRST); // reset uart rx fifo
|
||||||
|
emsuart_flush_fifos();
|
||||||
|
|
||||||
|
// throw out the telegram...
|
||||||
|
for (uint8_t i = 0; i < len && result == EMS_TX_STATUS_OK;) {
|
||||||
|
GPIO_H(TX_MARK_MASK);
|
||||||
|
|
||||||
|
wdc = EMS_TX_TO_COUNT;
|
||||||
|
volatile uint8_t _usrxc = (USS(EMSUART_UART) >> USRXC) & 0xFF;
|
||||||
|
USF(EMSUART_UART) = buf[i++]; // send each Tx byte
|
||||||
|
// wait for echo from busmaster
|
||||||
|
GPIO_L(TX_MARK_MASK);
|
||||||
|
|
||||||
|
while (((USS(EMSUART_UART) >> USRXC) & 0xFF) == _usrxc) {
|
||||||
|
delayMicroseconds(EMSUART_BUSY_WAIT); // burn CPU cycles...
|
||||||
|
if (--wdc == 0) {
|
||||||
|
EMS_Sys_Status.emsTxStatus = result = EMS_TX_WTD_TIMEOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (USIR(EMSUART_UART) & (1 << UIBD)) {
|
||||||
|
USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ
|
||||||
|
EMS_Sys_Status.emsTxStatus = result = EMS_TX_BRK_DETECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we got the whole telegram in the Rx buffer
|
||||||
|
// on Rx-BRK (bus collision), we simply enable Rx and leave it
|
||||||
|
// otherwise we send the final Tx-BRK in the loopback and re=enable Rx-INT.
|
||||||
|
// worst case, we'll see an additional Rx-BRK...
|
||||||
|
if (result != EMS_TX_STATUS_OK) {
|
||||||
|
LA_PULSE(200); // mark Tx error
|
||||||
|
} else {
|
||||||
|
// neither bus collision nor timeout - send terminating BRK signal
|
||||||
|
GPIO_H(TX_MARK_MASK);
|
||||||
|
if (!(USIS(EMSUART_UART) & (1 << UIBD))) {
|
||||||
|
// no bus collision - send terminating BRK signal
|
||||||
|
USC0(EMSUART_UART) |= (1 << UCLBE) | (1 << UCBRK); // enable loopback & set <BRK>
|
||||||
|
|
||||||
|
// wait until BRK detected...
|
||||||
|
while (!(USIR(EMSUART_UART) & (1 << UIBD))) {
|
||||||
|
delayMicroseconds(EMSUART_BUSY_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
USC0(EMSUART_UART) &= ~((1 << UCBRK) | (1 << UCLBE)); // disable loopback & clear <BRK>
|
||||||
|
USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ
|
||||||
|
}
|
||||||
|
GPIO_L(TX_MARK_MASK);
|
||||||
|
}
|
||||||
|
ETS_UART_INTR_ENABLE(); // receive anything from FIFO...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
// we got the whole telegram in the Rx buffer
|
|
||||||
// on Rx-BRK (bus collision), we simply enable Rx and leave it
|
|
||||||
// otherwise we send the final Tx-BRK in the loopback and re=enable Rx-INT.
|
|
||||||
// worst case, we'll see an additional Rx-BRK...
|
|
||||||
if (!(USIS(EMSUART_UART) & (1 << UIBD))) {
|
|
||||||
// no bus collision - send terminating BRK signal
|
|
||||||
emsuart_loopback(true);
|
|
||||||
USC0(EMSUART_UART) |= (1 << UCBRK); // set <BRK>
|
|
||||||
|
|
||||||
// wait until BRK detected...
|
|
||||||
while (!(USIS(EMSUART_UART) & (1 << UIBD))) {
|
|
||||||
delayMicroseconds(EMSUART_BIT_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear <BRK>
|
|
||||||
|
|
||||||
USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ
|
|
||||||
emsuart_loopback(false); // disable loopback mode
|
|
||||||
}
|
|
||||||
|
|
||||||
ETS_UART_INTR_ENABLE(); // receive anything from FIFO...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -8,16 +8,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <ems.h>
|
||||||
|
|
||||||
#define EMSUART_UART 0 // UART 0
|
#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 EMSUART_BAUD 9600 // uart baud rate for the EMS circuit
|
||||||
|
|
||||||
#define EMS_MAXBUFFERS 10 // buffers for circular filling to avoid collisions
|
#define EMS_MAXBUFFERS 5 // buffers for circular filling to avoid collisions
|
||||||
#define EMS_MAXBUFFERSIZE 32 // max size of the buffer. packets are max 32 bytes to support EMS 1.0
|
#define EMS_MAXBUFFERSIZE (EMS_MAX_TELEGRAM_LENGTH + 2) // max size of the buffer. EMS packets are max 32 bytes, plus extra 2 for BRKs
|
||||||
|
|
||||||
#define EMSUART_BIT_TIME 104 // bit time @9600 baud
|
#define EMSUART_BIT_TIME 104 // bit time @9600 baud
|
||||||
|
|
||||||
|
#define EMSUART_TX_BRK_WAIT 2070 // the BRK from Boiler master is roughly 1.039ms, so accounting for hardware lag using around 2078 (for half-duplex) - 8 (lag)
|
||||||
|
#define EMSUART_TX_WAIT_BYTE EMSUART_BIT_TIME * 10 // Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit)
|
||||||
|
#define EMSUART_TX_WAIT_BRK EMSUART_BIT_TIME * 11 // Time to send a BRK Signal (11 Bit)
|
||||||
|
#define EMSUART_TX_WAIT_GAP EMSUART_BIT_TIME * 7 // Gap between to Bytes
|
||||||
|
#define EMSUART_TX_LAG 8
|
||||||
|
|
||||||
#define EMSUART_recvTaskPrio 1
|
#define EMSUART_recvTaskPrio 1
|
||||||
#define EMSUART_recvTaskQueueLen 64
|
#define EMSUART_recvTaskQueueLen 64
|
||||||
|
|
||||||
@@ -29,5 +36,5 @@ typedef struct {
|
|||||||
void ICACHE_FLASH_ATTR emsuart_init();
|
void ICACHE_FLASH_ATTR emsuart_init();
|
||||||
void ICACHE_FLASH_ATTR emsuart_stop();
|
void ICACHE_FLASH_ATTR emsuart_stop();
|
||||||
void ICACHE_FLASH_ATTR emsuart_start();
|
void ICACHE_FLASH_ATTR emsuart_start();
|
||||||
void ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len);
|
_EMS_TX_STATUS ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len);
|
||||||
void ICACHE_FLASH_ATTR emsuart_tx_poll();
|
void ICACHE_FLASH_ATTR emsuart_tx_poll();
|
||||||
|
|||||||
@@ -41,12 +41,13 @@
|
|||||||
#define THERMOSTAT_CIRCUITCALCTEMP "thermostat_circuitcalctemp" // RC35 specific
|
#define THERMOSTAT_CIRCUITCALCTEMP "thermostat_circuitcalctemp" // RC35 specific
|
||||||
|
|
||||||
// MQTT for boiler
|
// MQTT for boiler
|
||||||
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values to MQTT
|
#define TOPIC_BOILER_DATA "boiler_data" // for sending boiler values to MQTT
|
||||||
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
|
#define TOPIC_BOILER_TAPWATER_ACTIVE "tapwater_active" // if hot tap water is running
|
||||||
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
|
#define TOPIC_BOILER_HEATING_ACTIVE "heating_active" // if heating is on
|
||||||
#define TOPIC_BOILER_WWACTIVATED "wwactivated" // for receiving MQTT message to change water on/off
|
#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_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
|
#define TOPIC_BOILER_CMD_COMFORT "boiler_cmd_comfort" // for received boiler ww comfort setting via MQTT
|
||||||
|
#define TOPIC_BOILER_CMD_FLOWTEMP "boiler_cmd_flowtemp" // for received boiler flowtemp value via MQTT
|
||||||
|
|
||||||
// MQTT for SM10/SM100 Solar Module
|
// MQTT for SM10/SM100 Solar Module
|
||||||
#define TOPIC_SM_DATA "sm_data" // topic name
|
#define TOPIC_SM_DATA "sm_data" // topic name
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
#define SM_ENERGYLASTHOUR "energylasthour" // energy last hour
|
#define SM_ENERGYLASTHOUR "energylasthour" // energy last hour
|
||||||
#define SM_ENERGYTODAY "energytoday" // energy today
|
#define SM_ENERGYTODAY "energytoday" // energy today
|
||||||
#define SM_ENERGYTOTAL "energytotal" // energy total
|
#define SM_ENERGYTOTAL "energytotal" // energy total
|
||||||
|
#define SM_PUMPWORKMIN "pumpWorkMin" // Total minutes
|
||||||
|
|
||||||
// MQTT for HP (HeatPump)
|
// MQTT for HP (HeatPump)
|
||||||
#define TOPIC_HP_DATA "hp_data" // topic name
|
#define TOPIC_HP_DATA "hp_data" // topic name
|
||||||
@@ -83,10 +85,15 @@
|
|||||||
// can be enabled and disabled via the 'set led' command and pin set by 'set led_gpio'
|
// can be enabled and disabled via the 'set led' command and pin set by 'set led_gpio'
|
||||||
#define EMSESP_LED_GPIO LED_BUILTIN
|
#define EMSESP_LED_GPIO LED_BUILTIN
|
||||||
|
|
||||||
|
#ifdef LOGICANALYZER
|
||||||
|
#define EMSESP_DALLAS_GPIO D1
|
||||||
|
#define EMSESP_DALLAS_PARASITE false
|
||||||
|
#else
|
||||||
// set this if using an external temperature sensor like a DS18B20
|
// set this if using an external temperature sensor like a DS18B20
|
||||||
// D5 is the default on a bbqkees board
|
// D5 is the default on a bbqkees board
|
||||||
#define EMSESP_DALLAS_GPIO D5
|
#define EMSESP_DALLAS_GPIO D5
|
||||||
#define EMSESP_DALLAS_PARASITE false
|
#define EMSESP_DALLAS_PARASITE false
|
||||||
|
#endif
|
||||||
|
|
||||||
// By default the EMS bus will be scanned for known devices based on the 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
|
// You can override the Thermostat and Boiler types here
|
||||||
|
|||||||
@@ -49,7 +49,10 @@ static const char * TEST_DATA[] = {
|
|||||||
"90 00 FF 00 00 6F 03 01 00 BE 00 BF", // test 44 - FR10
|
"90 00 FF 00 00 6F 03 01 00 BE 00 BF", // test 44 - FR10
|
||||||
"08 00 E3 00 01 00 01 00 00 00 00 00 00 00 00 00 DF 00 64 55", // test 45 - heatpump Enviline
|
"08 00 E3 00 01 00 01 00 00 00 00 00 00 00 00 00 DF 00 64 55", // test 45 - heatpump Enviline
|
||||||
"08 00 E5 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A", // test 46 - heatpump Enviline
|
"08 00 E5 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A", // test 46 - heatpump Enviline
|
||||||
"38 10 FF 00 03 2B 00 C7 07 C3 01" // test 47 - heatpump Enviline
|
"38 10 FF 00 03 2B 00 C7 07 C3 01", // test 47 - heatpump Enviline
|
||||||
|
"08 0B 19 00 00 F7 80 00 80 00 00 00 00 00 03 58 97 0C 7B 1F 00 00 00 06 C4 DF 02 64 48 80 00", // test 48 - outdoor temp check
|
||||||
|
"88 00 19 00 00 DC 80 00 80 00 FF FF 00 00 00 21 9A 06 E1 7C 00 00 00 06 C2 13 00 1E 90 80 00", // test 49 - check max length
|
||||||
|
"30 00 FF 00 02 8E 00 00 41 82 00 00 28 36 00 00 82 21" // test 50 - SM100
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,5 +6,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define APP_NAME "EMS-ESP"
|
#define APP_NAME "EMS-ESP"
|
||||||
#define APP_VERSION "1.8.0"
|
#define APP_VERSION "1.8.1"
|
||||||
#define APP_HOSTNAME "ems-esp"
|
#define APP_HOSTNAME "ems-esp"
|
||||||
|
|||||||
Reference in New Issue
Block a user