This commit is contained in:
MichaelDvP
2020-06-14 10:12:36 +02:00
24 changed files with 228 additions and 205 deletions

View File

@@ -61,7 +61,7 @@ Note: Version 2.0 is not backward compatible with v1.0. The File system structur
common commands available in all contexts: common commands available in all contexts:
exit exit
help help
log [level] [pretty | raw] [trace ID] log [level]
su su
(top root level) (top root level)
@@ -86,6 +86,7 @@ ems
show devices show devices
show emsbus show emsbus
show values show values
watch <on | off | raw> [ID]
mqtt mqtt
publish publish

View File

@@ -23,7 +23,7 @@ namespace uuid {
namespace log { namespace log {
char format_level_char(Level level) { char format_level_char(Level level) {
constexpr char log_level_chars[(int)Level::ALL - (int)Level::OFF + 1] = {' ', 'P', 'A', 'C', 'E', 'W', 'N', 'I', 'T', 'D', ' '}; // changed by proddy constexpr char log_level_chars[(int)Level::ALL - (int)Level::OFF + 1] = {' ', 'P', 'A', 'C', 'E', 'W', 'N', 'I', 'D', 'T', ' '};
return log_level_chars[(int)level + 1]; return log_level_chars[(int)level + 1];
} }

View File

@@ -47,8 +47,8 @@ PROGMEM = {reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_of
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_warning), reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_warning),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_notice), reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_notice),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_info), reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_info),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_trace), // switched by proddy
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_debug), reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_debug),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_trace),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_all)}; reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_all)};
const __FlashStringHelper * format_level_lowercase(Level level) { const __FlashStringHelper * format_level_lowercase(Level level) {

View File

@@ -47,8 +47,8 @@ PROGMEM = {reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_of
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_warning), reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_warning),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_notice), reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_notice),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_info), reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_info),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_trace), // switched by proddy
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_debug), reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_debug),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_trace),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_all)}; reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_all)};
const __FlashStringHelper * format_level_uppercase(Level level) { const __FlashStringHelper * format_level_uppercase(Level level) {

View File

@@ -33,8 +33,8 @@ std::vector<Level> levels() {
Level::WARNING, Level::WARNING,
Level::NOTICE, Level::NOTICE,
Level::INFO, Level::INFO,
Level::DEBUG,
Level::TRACE, Level::TRACE,
Level::DEBUG, // switched by proddy
Level::ALL}; Level::ALL};
} }

View File

@@ -35,8 +35,8 @@ std::vector<std::string> levels_lowercase() {
uuid::read_flash_string(format_level_lowercase(Level::WARNING)), uuid::read_flash_string(format_level_lowercase(Level::WARNING)),
uuid::read_flash_string(format_level_lowercase(Level::NOTICE)), uuid::read_flash_string(format_level_lowercase(Level::NOTICE)),
uuid::read_flash_string(format_level_lowercase(Level::INFO)), uuid::read_flash_string(format_level_lowercase(Level::INFO)),
uuid::read_flash_string(format_level_lowercase(Level::TRACE)), // switched by proddy
uuid::read_flash_string(format_level_lowercase(Level::DEBUG)), uuid::read_flash_string(format_level_lowercase(Level::DEBUG)),
uuid::read_flash_string(format_level_lowercase(Level::TRACE)),
uuid::read_flash_string(format_level_lowercase(Level::ALL))}; uuid::read_flash_string(format_level_lowercase(Level::ALL))};
} }

View File

@@ -35,8 +35,8 @@ std::vector<std::string> levels_uppercase() {
uuid::read_flash_string(format_level_uppercase(Level::WARNING)), uuid::read_flash_string(format_level_uppercase(Level::WARNING)),
uuid::read_flash_string(format_level_uppercase(Level::NOTICE)), uuid::read_flash_string(format_level_uppercase(Level::NOTICE)),
uuid::read_flash_string(format_level_uppercase(Level::INFO)), uuid::read_flash_string(format_level_uppercase(Level::INFO)),
uuid::read_flash_string(format_level_uppercase(Level::TRACE)), // switched by proddy
uuid::read_flash_string(format_level_uppercase(Level::DEBUG)), uuid::read_flash_string(format_level_uppercase(Level::DEBUG)),
uuid::read_flash_string(format_level_uppercase(Level::TRACE)),
uuid::read_flash_string(format_level_uppercase(Level::ALL))}; uuid::read_flash_string(format_level_uppercase(Level::ALL))};
} }

View File

@@ -71,7 +71,7 @@ namespace uuid {
namespace log { namespace log {
/** /**
* Severity level of log messages. Proddy switches trace & debug * Severity level of log messages.
* *
* @since 1.0.0 * @since 1.0.0
*/ */
@@ -84,8 +84,8 @@ enum Level : int8_t {
WARNING, /*!< Warning conditions. @since 1.0.0 */ WARNING, /*!< Warning conditions. @since 1.0.0 */
NOTICE, /*!< Normal but significant conditions. @since 1.0.0 */ NOTICE, /*!< Normal but significant conditions. @since 1.0.0 */
INFO, /*!< Informational messages. @since 1.0.0 */ INFO, /*!< Informational messages. @since 1.0.0 */
TRACE, /*!< Trace messages. @since 1.0.0 */
DEBUG, /*!< Debug-level messages. @since 1.0.0 */ DEBUG, /*!< Debug-level messages. @since 1.0.0 */
TRACE, /*!< Trace messages. @since 1.0.0 */
ALL, /*!< Meta level representing all log messages. @since 1.0.0 */ ALL, /*!< Meta level representing all log messages. @since 1.0.0 */
}; };

View File

@@ -225,9 +225,8 @@ void Console::load_standard_commands(unsigned int context) {
context, context,
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(log)}, flash_string_vector{F_(log)},
flash_string_vector{F_(log_level_optional), F_(trace_format_optional), F_(traceid_optional)}, flash_string_vector{F_(log_level_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) { [](Shell & shell, const std::vector<std::string> & arguments) {
uint16_t watch_id;
if (!arguments.empty()) { if (!arguments.empty()) {
uuid::log::Level level; uuid::log::Level level;
@@ -237,41 +236,10 @@ void Console::load_standard_commands(unsigned int context) {
shell.printfln(F_(invalid_log_level)); shell.printfln(F_(invalid_log_level));
return; return;
} }
// trace logic
if (level == uuid::log::Level::TRACE || level == uuid::log::Level::DEBUG) {
watch_id = LOG_TRACE_WATCH_NONE; // no watch ID set
if (arguments.size() > 1) {
// next argument is raw or full
if (arguments[1] == read_flash_string(F_(raw))) {
emsesp::EMSESP::trace_raw(true);
} else if (arguments[1] == read_flash_string(F_(pretty))) {
emsesp::EMSESP::trace_raw(false);
} else {
emsesp::EMSESP::trace_watch_id(Helpers::hextoint(arguments[1].c_str()));
}
// get the watch_id if its set
if (arguments.size() == 3) {
emsesp::EMSESP::trace_watch_id(Helpers::hextoint(arguments[2].c_str()));
}
} else {
// it was "log trace" so reset the watch id and switch back to pretty
emsesp::EMSESP::trace_raw(false);
emsesp::EMSESP::trace_watch_id(LOG_TRACE_WATCH_NONE);
}
}
} }
// print out logging settings // print out logging settings
shell.printfln(F_(log_level_fmt), uuid::log::format_level_uppercase(shell.log_level())); shell.printfln(F_(log_level_fmt), uuid::log::format_level_uppercase(shell.log_level()));
watch_id = emsesp::EMSESP::trace_watch_id();
if (watch_id == LOG_TRACE_WATCH_NONE) {
shell.printfln(F("Tracing all telegrams"));
} else {
shell.printfln(F("Tracing only telegrams that match a device ID or telegram type of 0x%02X"), watch_id);
}
shell.printfln(F_(trace_raw_fmt), emsesp::EMSESP::trace_raw() ? F("as raw bytes") : F("in decoded format"));
}, },
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> std::vector<std::string> { [](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> std::vector<std::string> {
return uuid::log::levels_lowercase(); return uuid::log::levels_lowercase();

View File

@@ -42,8 +42,8 @@ using uuid::log::Level;
// clang-format off // clang-format off
#define LOG_DEBUG(...) if (logger_.enabled(Level::DEBUG)) {logger_.debug(__VA_ARGS__);} #define LOG_DEBUG(...) if (logger_.enabled(Level::DEBUG)) {logger_.debug(__VA_ARGS__);}
#define LOG_TRACE(...) if (logger_.enabled(Level::TRACE)) {logger_.trace(__VA_ARGS__);}
#define LOG_INFO(...) logger_.info(__VA_ARGS__) #define LOG_INFO(...) logger_.info(__VA_ARGS__)
#define LOG_TRACE(...) logger_.trace(__VA_ARGS__)
#define LOG_NOTICE(...) logger_.notice(__VA_ARGS__) #define LOG_NOTICE(...) logger_.notice(__VA_ARGS__)
#define LOG_WARNING(...) logger_.warning(__VA_ARGS__) #define LOG_WARNING(...) logger_.warning(__VA_ARGS__)
#define LOG_ERROR(...) logger_.err(__VA_ARGS__) #define LOG_ERROR(...) logger_.err(__VA_ARGS__)
@@ -90,7 +90,7 @@ MAKE_PSTR_WORD(restart)
MAKE_PSTR_WORD(reconnect) MAKE_PSTR_WORD(reconnect)
MAKE_PSTR_WORD(format) MAKE_PSTR_WORD(format)
MAKE_PSTR_WORD(raw) MAKE_PSTR_WORD(raw)
MAKE_PSTR_WORD(pretty) MAKE_PSTR_WORD(watch)
// context menus // context menus
MAKE_PSTR_WORD(mqtt) MAKE_PSTR_WORD(mqtt)
@@ -102,9 +102,6 @@ MAKE_PSTR(degrees_mandatory, "<degrees>")
MAKE_PSTR(asterisks, "********") MAKE_PSTR(asterisks, "********")
MAKE_PSTR(n_mandatory, "<n>") MAKE_PSTR(n_mandatory, "<n>")
MAKE_PSTR(n_optional, "[n]") MAKE_PSTR(n_optional, "[n]")
MAKE_PSTR(traceid_optional, "[trace ID]")
MAKE_PSTR(trace_raw_fmt, "Displaying telegrams %s")
MAKE_PSTR(trace_format_optional, "[pretty | raw]")
MAKE_PSTR(bool_mandatory, "<on | off>") MAKE_PSTR(bool_mandatory, "<on | off>")
MAKE_PSTR(typeid_mandatory, "<type ID>") MAKE_PSTR(typeid_mandatory, "<type ID>")
MAKE_PSTR(deviceid_mandatory, "<device ID>") MAKE_PSTR(deviceid_mandatory, "<device ID>")

View File

@@ -98,6 +98,7 @@
{207, DeviceType::CONTROLLER, F("Sense II/CS200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10 {207, DeviceType::CONTROLLER, F("Sense II/CS200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10
{209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 {209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50 {218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
{224, DeviceType::CONTROLLER, F("Bosch 9000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 {230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{241, DeviceType::CONTROLLER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09 {241, DeviceType::CONTROLLER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09

View File

@@ -51,7 +51,6 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
// the telegram handlers... // the telegram handlers...
register_telegram_type(0x10, F("UBAErrorMessage1"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1)); register_telegram_type(0x10, F("UBAErrorMessage1"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1));
register_telegram_type(0x11, F("UBAErrorMessage2"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1)); register_telegram_type(0x11, F("UBAErrorMessage2"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1));
register_telegram_type(0x12, F("UBAErrorMessage3"), false, std::bind(&Boiler::process_UBAErrorMessage, this, _1));
register_telegram_type(0x18, F("UBAMonitorFast"), false, std::bind(&Boiler::process_UBAMonitorFast, this, _1)); register_telegram_type(0x18, F("UBAMonitorFast"), false, std::bind(&Boiler::process_UBAMonitorFast, this, _1));
register_telegram_type(0x19, F("UBAMonitorSlow"), true, std::bind(&Boiler::process_UBAMonitorSlow, this, _1)); register_telegram_type(0x19, F("UBAMonitorSlow"), true, std::bind(&Boiler::process_UBAMonitorSlow, this, _1));
register_telegram_type(0x34, F("UBAMonitorWW"), false, std::bind(&Boiler::process_UBAMonitorWW, this, _1)); register_telegram_type(0x34, F("UBAMonitorWW"), false, std::bind(&Boiler::process_UBAMonitorWW, this, _1));

View File

@@ -32,6 +32,10 @@ MAKE_PSTR(tx_mode_fmt, "Tx mode = %d")
MAKE_PSTR(bus_id_fmt, "Bus ID = %02X") MAKE_PSTR(bus_id_fmt, "Bus ID = %02X")
MAKE_PSTR(read_only_fmt, "Read-only mode is %s") MAKE_PSTR(read_only_fmt, "Read-only mode is %s")
MAKE_PSTR(watchid_optional, "[ID]")
MAKE_PSTR(watch_format_mandatory, "<off | on | raw>")
MAKE_PSTR(invalid_watch, "Invalid watch type")
MAKE_PSTR(logger_name, "emsesp") MAKE_PSTR(logger_name, "emsesp")
namespace emsesp { namespace emsesp {
@@ -56,8 +60,8 @@ Shower EMSESP::shower_; // Shower logic
// static/common variables // static/common variables
uint8_t EMSESP::actual_master_thermostat_ = EMSESP_DEFAULT_MASTER_THERMOSTAT; // which thermostat leads when multiple found uint8_t EMSESP::actual_master_thermostat_ = EMSESP_DEFAULT_MASTER_THERMOSTAT; // which thermostat leads when multiple found
uint16_t EMSESP::trace_watch_id_ = LOG_TRACE_WATCH_NONE; // for when log is TRACE. 0 means no trace set uint16_t EMSESP::watch_id_ = WATCH_NONE; // for when log is TRACE. 0 means no trace set
bool EMSESP::trace_raw_ = false; // not showing raw when in trace logging uint8_t EMSESP::watch_ = 0; // trace off
bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower() bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
bool EMSESP::ems_read_only_; bool EMSESP::ems_read_only_;
uint32_t EMSESP::last_fetch_ = 0; uint32_t EMSESP::last_fetch_ = 0;
@@ -97,12 +101,12 @@ uint8_t EMSESP::actual_master_thermostat() {
} }
// to watch both type IDs and device IDs // to watch both type IDs and device IDs
void EMSESP::trace_watch_id(uint16_t trace_watch_id) { void EMSESP::watch_id(uint16_t watch_id) {
// if it's a device ID, which is a single byte, remove the MSB so to support both Buderus and HT3 protocols // if it's a device ID, which is a single byte, remove the MSB so to support both Buderus and HT3 protocols
if (trace_watch_id <= 0xFF) { if (watch_id <= 0xFF) {
trace_watch_id_ = (trace_watch_id & 0x7F); watch_id_ = (watch_id & 0x7F);
} else { } else {
trace_watch_id_ = trace_watch_id; watch_id_ = watch_id;
} }
} }
@@ -389,10 +393,9 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
// We also check for common telgram types, like the Version(0x02) // We also check for common telgram types, like the Version(0x02)
// returns false if there are none found // returns false if there are none found
bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) { bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
if ((logger_.enabled(Level::TRACE)) && !trace_raw()) { if (watch() == 1) {
if ((trace_watch_id_ == LOG_TRACE_WATCH_NONE) || (telegram->src == trace_watch_id_) || (telegram->dest == trace_watch_id_) if ((watch_id_ == WATCH_NONE) || (telegram->src == watch_id_) || (telegram->dest == watch_id_) || (telegram->type_id == watch_id_)) {
|| (telegram->type_id == trace_watch_id_)) { LOG_INFO(pretty_telegram(telegram).c_str());
LOG_TRACE(pretty_telegram(telegram).c_str());
} }
} }
@@ -568,9 +571,9 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
if (((first_value & 0x7F) == txservice_.ems_bus_id()) && (length > 1)) { if (((first_value & 0x7F) == txservice_.ems_bus_id()) && (length > 1)) {
// if we ask ourself at roomcontrol for version e.g. 0B 98 02 00 20 // if we ask ourself at roomcontrol for version e.g. 0B 98 02 00 20
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data); Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data);
//#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
LOG_DEBUG(F("Echo: %s"), Helpers::data_to_hex(data, length).c_str()); LOG_DEBUG(F("[DEBUG] Echo: %s"), Helpers::data_to_hex(data, length).c_str());
//#endif #endif
return; // it's an echo return; // it's an echo
} }
@@ -613,7 +616,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
// So re-send the last Tx and increment retry count // So re-send the last Tx and increment retry count
uint8_t retries = txservice_.retry_tx(); // returns 0 if exceeded count uint8_t retries = txservice_.retry_tx(); // returns 0 if exceeded count
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
LOG_DEBUG(F("Last Tx operation failed. Retry #%d. Sent: %s, received: %s"), LOG_DEBUG(F("[DEBUG] Last Tx operation failed. Retry #%d. Sent: %s, received: %s"),
retries, retries,
txservice_.last_tx_to_string().c_str(), txservice_.last_tx_to_string().c_str(),
Helpers::data_to_hex(data, length).c_str()); Helpers::data_to_hex(data, length).c_str());
@@ -725,7 +728,6 @@ void EMSESP::console_commands(Shell & shell, unsigned int context) {
flash_string_vector{F_(set), F_(tx_mode)}, flash_string_vector{F_(set), F_(tx_mode)},
flash_string_vector{F_(n_mandatory)}, flash_string_vector{F_(n_mandatory)},
[](Shell & shell, const std::vector<std::string> & arguments) { [](Shell & shell, const std::vector<std::string> & arguments) {
// uint8_t tx_mode = (arguments[0]).at(0) - '0';
uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10); uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10);
if ((tx_mode > 0) && (tx_mode <= 30)) { if ((tx_mode > 0) && (tx_mode <= 30)) {
Settings settings; Settings settings;
@@ -824,6 +826,50 @@ void EMSESP::console_commands(Shell & shell, unsigned int context) {
shell.printfln(F_(read_only_fmt), settings.ems_read_only() ? F_(on) : F_(off)); shell.printfln(F_(read_only_fmt), settings.ems_read_only() ? F_(on) : F_(off));
}); });
EMSESPShell::commands->add_command(ShellContext::EMS,
CommandFlags::USER,
flash_string_vector{F_(watch)},
flash_string_vector{F_(watch_format_mandatory), F_(watchid_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) {
// get raw/pretty
if (arguments[0] == read_flash_string(F_(raw))) {
emsesp::EMSESP::watch(2); // raw
} else if (arguments[0] == read_flash_string(F_(on))) {
emsesp::EMSESP::watch(1); // on
} else if (arguments[0] == read_flash_string(F_(off))) {
emsesp::EMSESP::watch(0); // off
} else {
shell.printfln(F_(invalid_watch));
return;
}
uint16_t watch_id;
if (arguments.size() == 2) {
// get the watch_id if its set
watch_id = Helpers::hextoint(arguments[1].c_str());
} else {
watch_id = WATCH_NONE;
}
emsesp::EMSESP::watch_id(watch_id);
uint8_t watch = emsesp::EMSESP::watch();
if (watch == 0) {
shell.printfln(F("Watch is off"));
} else if (watch == 1) {
shell.printfln(F("Watching incoming telegrams, displayed in decoded format"));
} else {
shell.printfln(F("Watching incoming telegrams, displayed as raw bytes"));
}
watch_id = emsesp::EMSESP::watch_id();
if (watch_id != WATCH_NONE) {
shell.printfln(F("Filtering only telegrams that match a device ID or telegram type of 0x%02X"), watch_id);
}
});
// enter the context // enter the context
Console::enter_custom_context(shell, context); Console::enter_custom_context(shell, context);
} }
@@ -839,7 +885,6 @@ void EMSESP::start() {
network_.start(); network_.start();
console_.start(); console_.start();
sensors_.start(); sensors_.start();
rxservice_.start();
txservice_.start(); txservice_.start();
shower_.start(); shower_.start();
mqtt_.start(); mqtt_.start();

View File

@@ -46,7 +46,7 @@
#include "devices/boiler.h" #include "devices/boiler.h"
#define LOG_TRACE_WATCH_NONE 0 // no watch id set #define WATCH_NONE 0 // no watch id set
namespace emsesp { namespace emsesp {
@@ -99,18 +99,18 @@ class EMSESP {
return sensors_.devices(); return sensors_.devices();
} }
static void trace_watch_id(uint16_t id); static void watch_id(uint16_t id);
static uint16_t trace_watch_id() { static uint16_t watch_id() {
return trace_watch_id_; return watch_id_;
} }
static void trace_raw(bool trace_raw) { static void watch(uint8_t watch) {
trace_raw_ = trace_raw; watch_ = watch; // 0=off, 1=on, 2=raw
} }
static bool trace_raw() { static uint8_t watch() {
return trace_raw_; return watch_; // 0=off, 1=on, 2=raw
} }
static bool tap_water_active() { static bool tap_water_active() {
@@ -166,8 +166,8 @@ class EMSESP {
static std::vector<Device_record> device_library_; static std::vector<Device_record> device_library_;
static uint8_t actual_master_thermostat_; static uint8_t actual_master_thermostat_;
static uint16_t trace_watch_id_; static uint16_t watch_id_;
static bool trace_raw_; static uint8_t watch_;
static bool tap_water_active_; static bool tap_water_active_;
static bool ems_read_only_; static bool ems_read_only_;
}; };

View File

@@ -104,7 +104,7 @@ char * Helpers::render_value(char * result, uint8_t value, uint8_t format) {
return (result); return (result);
} }
static char s2[5] = {0}; char s2[5] = {0};
switch (format) { switch (format) {
case 2: case 2:
@@ -226,7 +226,7 @@ char * Helpers::render_value(char * result, const uint32_t value, const uint8_t
return (result); return (result);
} }
static char s[20] = {0}; char s[20] = {0};
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
if (format <= 1) { if (format <= 1) {

View File

@@ -340,7 +340,7 @@ void Mqtt::on_message(char * topic, char * payload, size_t len) {
strlcpy(message, payload, len + 1); strlcpy(message, payload, len + 1);
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
LOG_DEBUG(F("Received %s => %s (length %d)"), topic, message, len); LOG_DEBUG(F("[DEBUG] Received %s => %s (length %d)"), topic, message, len);
#endif #endif
// strip out everything until the last / // strip out everything until the last /

View File

@@ -29,12 +29,7 @@
#include "telegram.h" #include "telegram.h"
#if defined(ESP8266) #if defined(ESP8266)
#include "uart/emsuart_esp8266.h"
#include <RTCVars.h> #include <RTCVars.h>
#elif defined(ESP32)
#include "uart/emsuart_esp32.h"
#elif defined(EMSESP_STANDALONE)
#include <emsuart_standalone.h>
#endif #endif
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE

View File

@@ -68,8 +68,8 @@ Telegram::Telegram(uint8_t operation, uint8_t src, uint8_t dest, uint16_t type_i
, type_id(type_id) , type_id(type_id)
, offset(offset) , offset(offset)
, message_length(message_length) { , message_length(message_length) {
// copy complete telegram data over // copy complete telegram data over, preventing buffer overflow
for (uint8_t i = 0; i < message_length; i++) { for (uint8_t i = 0; ((i < message_length) && (i != EMS_MAX_TELEGRAM_MESSAGE_LENGTH - 1)); i++) {
message_data[i] = data[i]; message_data[i] = data[i];
} }
} }
@@ -211,36 +211,26 @@ void RxService::flush_rx_queue() {
rx_telegram_id_ = 0; rx_telegram_id_ = 0;
} }
// start and initialize the Rx incoming buffer
void RxService::start() {
// LOG_DEBUG(F("RxStart"));
// function not currently used
}
// Rx loop, run as many times as you can // Rx loop, run as many times as you can
// processes all telegrams on the queue. Assumes there are valid (i.e. CRC checked) // processes all telegrams on the queue. Assumes there are valid (i.e. CRC checked)
void RxService::loop() { void RxService::loop() {
/*
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
// give rx some breathing space give rx some breathing space if ((uuid::get_uptime() - last_rx_check_) < RX_LOOP_WAIT) {
//if ((uuid::get_uptime() - last_rx_check_) < RX_LOOP_WAIT) { return;
// return; }
//} last_rx_check_ = uuid::get_uptime();
//last_rx_check_ = uuid::get_uptime();
#endif #endif
*/
while (!rx_telegrams_.empty()) { while (!rx_telegrams_.empty()) {
auto telegram = rx_telegrams_.front().telegram_; auto telegram = rx_telegrams_.front().telegram_;
// rx_telegrams_overflow_ = false;
(void)EMSESP::process_telegram(telegram); // further process the telegram (void)EMSESP::process_telegram(telegram); // further process the telegram
increment_telegram_count(); // increase count increment_telegram_count(); // increase count
// remove it from the queue rx_telegrams_.pop_front(); // remove it from the queue
// if (!rx_telegrams_overflow_) {
rx_telegrams_.pop_front();
// }
} }
} }
@@ -249,10 +239,15 @@ void RxService::loop() {
// length includes the CRC // length includes the CRC
// for EMS+ the type_id has the value + 256. We look for these type of telegrams with F7, F9 and FF in 3rd byte // for EMS+ the type_id has the value + 256. We look for these type of telegrams with F7, F9 and FF in 3rd byte
void RxService::add(uint8_t * data, uint8_t length) { void RxService::add(uint8_t * data, uint8_t length) {
if (length < 2) {
return;
}
// validate the CRC // validate the CRC
uint8_t crc = calculate_crc(data, length - 1); uint8_t crc = calculate_crc(data, length - 1);
if (data[length - 1] != crc) {
LOG_TRACE(F("Rx: %s %s(BAD, CRC %02X != %02X)%s"), Helpers::data_to_hex(data, length).c_str(), COLOR_RED, data[length - 1], crc, COLOR_RESET); if ((data[length - 1] != crc) && (EMSESP::watch() != 0)) {
LOG_ERROR(F("Rx: %s %s(BAD, CRC %02X != %02X)%s"), Helpers::data_to_hex(data, length).c_str(), COLOR_RED, data[length - 1], crc, COLOR_RESET);
increment_telegram_error_count(); increment_telegram_error_count();
return; return;
} }
@@ -270,43 +265,36 @@ void RxService::add(uint8_t * data, uint8_t length) {
uint8_t dest = data[1] & 0x7F; // strip MSB, don't care if its read or write for processing uint8_t dest = data[1] & 0x7F; // strip MSB, don't care if its read or write for processing
uint8_t offset = data[3]; // offset is always 4th byte uint8_t offset = data[3]; // offset is always 4th byte
uint16_t type_id = 0; // this could be 2 bytes for ems+ // set default values, which will be adjusted depending on the EMS1.0/2.0 logic below
uint8_t * message_data; uint16_t type_id = 0;
uint8_t message_length; uint8_t * message_data = data;
uint8_t message_length = length;
// work out depending on the type where the data message block starts // work out depending on the type where the data message block starts
if (data[2] < 0xF0 || length < 6) { if (data[2] < 0xF0 || length < 6) {
// EMS 1.0 // EMS 1.0
type_id = data[2]; type_id = data[2];
message_data = data + 4; // message block starts at 5th byte message_data += 4; // message block starts at 5th byte
message_length = length - 5; // remove 4 bytes header plus CRC message_length -= 5; // remove 4 bytes header plus CRC
} else { } else {
// EMS 2.0 / EMS+ // EMS 2.0 / EMS+
if (data[2] == 0xFF) { if (data[2] == 0xFF) {
// check for empty data // check for empty data
// special broadcast telegrams on ems+ have no data values, some even don't have a type ID, e.g. "21 0B FF 00" // special broadcast telegrams on ems+ have no data values, some even don't have a type ID, e.g. "21 0B FF 00"
if (length <= 7) { if (length > 8) {
message_data = data; // bogus pointer, will not be used message_length -= 7; // remove 6 byte header plus CRC
message_length = 0; message_data += 6; // message block starts at 7th position
if (length <= 5) {
type_id = 0; // has also an empty type_id
} else {
type_id = (data[4] << 8) + data[5] + 256;
} }
} else { if (length > 5) {
type_id = (data[4] << 8) + data[5] + 256; type_id = (data[4] << 8) + data[5] + 256; // set type_id if there is one
message_length = length - 7; // remove 6 byte header plus CRC
message_data = data + 6; // message block starts at 7th position
} }
} else { } else {
// its F9 or F7 // its F9 or F7
uint8_t shift = (data[4] != 0xFF) ? 1 : 0; // true (1) if 5th byte is not 0xFF, then telegram is 1 byte longer uint8_t shift = (data[4] != 0xFF) ? 1 : 0; // true (1) if 5th byte is not 0xFF, then telegram is 1 byte longer
type_id = (data[5 + shift] << 8) + data[6 + shift] + 256; type_id = (data[5 + shift] << 8) + data[6 + shift] + 256;
message_data = data + 6 + shift; // there is a special byte after the typeID which we ignore for now message_data += 6 + shift; // there is a special byte after the typeID which we ignore for now
if (length <= (9 + shift)) { if (length > (9 + shift)) {
message_length = 0; // special broadcast on ems+ have no data values message_length -= (9 + shift);
} else {
message_length = length - (9 + shift);
} }
} }
} }
@@ -316,26 +304,27 @@ void RxService::add(uint8_t * data, uint8_t length) {
return; return;
} }
// if we're in "trace" and "raw" print out actual telegram as bytes to the console
if (EMSESP::watch() == 2) {
uint16_t trace_watch_id = EMSESP::watch_id();
if ((trace_watch_id == WATCH_NONE) || (src == trace_watch_id) || (dest == trace_watch_id) || (type_id == trace_watch_id)) {
LOG_INFO(F("Rx: %s"), Helpers::data_to_hex(data, length).c_str());
}
}
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] New Rx [#%d] telegram, message length %d"), rx_telegram_id_, message_length);
#endif
// create the telegram // create the telegram
auto telegram = std::make_shared<Telegram>(Telegram::Operation::RX, src, dest, type_id, offset, message_data, message_length); auto telegram = std::make_shared<Telegram>(Telegram::Operation::RX, src, dest, type_id, offset, message_data, message_length);
// check if queue is full, if so remove top item to make space // check if queue is full, if so remove top item to make space
if (rx_telegrams_.size() >= MAX_RX_TELEGRAMS) { if (rx_telegrams_.size() >= MAX_RX_TELEGRAMS) {
// rx_telegrams_overflow_ = true;
rx_telegrams_.pop_front(); rx_telegrams_.pop_front();
} }
// add to queue rx_telegrams_.emplace_back(rx_telegram_id_++, std::move(telegram)); // add to queue
LOG_DEBUG(F("New Rx [#%d] telegram, length %d"), rx_telegram_id_, message_length);
rx_telegrams_.emplace_back(rx_telegram_id_++, std::move(telegram));
// if we're in "trace" and "raw" print out actual telegram as bytes to the console
if (logger_.enabled(Level::TRACE) && EMSESP::trace_raw()) {
uint16_t trace_watch_id = EMSESP::trace_watch_id();
if ((trace_watch_id == LOG_TRACE_WATCH_NONE) || (src == trace_watch_id) || (dest == trace_watch_id) || (type_id == trace_watch_id)) {
LOG_TRACE(F("Rx: %s"), Helpers::data_to_hex(data, length).c_str());
}
}
} }
@@ -380,7 +369,7 @@ void TxService::loop() {
// sends a 1 byte poll which is our own device ID // sends a 1 byte poll which is our own device ID
void TxService::send_poll() { void TxService::send_poll() {
//LOG_TRACE(F("Ack %02X"),ems_bus_id() ^ ems_mask()); //LOG_DEBUG(F("Ack %02X"),ems_bus_id() ^ ems_mask());
EMSuart::send_poll(ems_bus_id() ^ ems_mask()); EMSuart::send_poll(ems_bus_id() ^ ems_mask());
} }
@@ -477,23 +466,18 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
tx_telegram.id_, tx_telegram.id_,
telegram->to_string(telegram_raw, length).c_str()); telegram->to_string(telegram_raw, length).c_str());
// if we're watching an ID, then always show the full telegram
if ((logger_.enabled(Level::TRACE))
&& ((telegram->src == EMSESP::trace_watch_id()) || (telegram->dest == EMSESP::trace_watch_id()) || (telegram->type_id == EMSESP::trace_watch_id()))) {
logger_.trace(F("Sending %s Tx [#%d], telegram: %s"),
(telegram->operation == Telegram::Operation::TX_WRITE) ? F("write") : F("read"),
tx_telegram.id_,
telegram->to_string(telegram_raw, length).c_str());
}
// send the telegram to the UART Tx // send the telegram to the UART Tx
uint16_t status = EMSuart::transmit(telegram_raw, length); uint16_t status = EMSuart::transmit(telegram_raw, length);
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
LOG_TRACE(F("Tx: %s"), Helpers::data_to_hex(telegram_raw, length).c_str()); // if watching in 'raw' mode
if (EMSESP::watch() == 2) {
LOG_INFO(F("[DEBUG] Tx: %s"), Helpers::data_to_hex(telegram_raw, length).c_str());
}
#endif #endif
if (status != EMS_TX_STATUS_OK) { if (status == EMS_TX_STATUS_ERR) {
LOG_ERROR(F("Failed to transmit Tx via UART.")); LOG_ERROR(F("Failed to transmit Tx via UART."));
increment_telegram_fail_count(); // another Tx fail
tx_waiting(false); // nothing send, tx not in wait state tx_waiting(false); // nothing send, tx not in wait state
return; return;
} }
@@ -514,11 +498,15 @@ void TxService::send_telegram(const uint8_t * data, const uint8_t length) {
LOG_DEBUG(F("Sending Raw telegram: %s (length=%d)"), Helpers::data_to_hex(telegram_raw, length).c_str(), length); LOG_DEBUG(F("Sending Raw telegram: %s (length=%d)"), Helpers::data_to_hex(telegram_raw, length).c_str(), length);
tx_waiting(false); // no post validation
// send the telegram to the UART Tx // send the telegram to the UART Tx
uint16_t status = EMSuart::transmit(telegram_raw, length); uint16_t status = EMSuart::transmit(telegram_raw, length);
//LOG_TRACE(F("Tx: %s"), Helpers::data_to_hex(telegram_raw, length).c_str()); //LOG_DEBUG(F("Tx: %s"), Helpers::data_to_hex(telegram_raw, length).c_str());
if (status != EMS_TX_STATUS_OK) {
LOG_ERROR(F("Failed to transmit Tx via UART")); if (status == EMS_TX_STATUS_ERR) {
LOG_ERROR(F("Failed to transmit Tx via UART."));
increment_telegram_fail_count(); // another Tx fail
} }
} }
@@ -526,7 +514,9 @@ void TxService::send_telegram(const uint8_t * data, const uint8_t length) {
// given some details like the destination, type, offset and message block // given some details like the destination, type, offset and message block
void TxService::add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length) { void TxService::add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length) {
auto telegram = std::make_shared<Telegram>(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length); auto telegram = std::make_shared<Telegram>(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length);
LOG_DEBUG(F("New Tx [#%d] telegram, length %d"), tx_telegram_id_, message_length); #ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] New Tx [#%d] telegram, length %d"), tx_telegram_id_, message_length);
#endif
// if the queue is full, make room but removing the last one // if the queue is full, make room but removing the last one
if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) { if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
@@ -537,14 +527,17 @@ void TxService::add(const uint8_t operation, const uint8_t dest, const uint16_t
} }
// builds a Tx telegram and adds to queue, using only raw data // builds a Tx telegram and adds to queue, using only raw data
// format is EMS 1.0 (src, dest, type_id, offset, data)
// length is the length of the whole telegram data // length is the length of the whole telegram data
void TxService::add(uint8_t * data, const uint8_t length) { // front = true if adding to the front of queue, e.g. with an Tx retry. Default is false.
uint8_t message_length = length - 4; void TxService::add(uint8_t * data, const uint8_t length, bool front) {
if (!message_length) { if (length < 5) {
LOG_ERROR(F("Bad Tx telegram, too short (message length is %d)"), message_length); LOG_ERROR(F("Tx telegram too short (telegram length is %d)"), length);
return; return;
} }
uint8_t message_length = length - 4;
// build header // build header
uint8_t src = data[0]; uint8_t src = data[0];
uint8_t dest = data[1]; uint8_t dest = data[1];
@@ -559,9 +552,17 @@ void TxService::add(uint8_t * data, const uint8_t length) {
tx_telegrams_.pop_front(); tx_telegrams_.pop_front();
} }
LOG_DEBUG(F("New Tx [#%d] telegram, length %d"), tx_telegram_id_, message_length); #ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] New Tx [#%d] telegram, length %d"), tx_telegram_id_, message_length);
#endif
// add to either front or back of queue
if (front) {
tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram));
} else {
tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram)); tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram));
} }
}
// send a Tx telegram to request data from an EMS device // send a Tx telegram to request data from an EMS device
void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset) { void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset) {
@@ -626,25 +627,8 @@ uint8_t TxService::retry_tx() {
increment_telegram_fail_count(); // another Tx fail increment_telegram_fail_count(); // another Tx fail
return 0; return 0;
} }
// if the queue is full, throw away
if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
reset_retry_count(); // give up
increment_telegram_fail_count(); // another Tx fail
return 0;
}
uint8_t message_length = telegram_last_length_ - 4; add(telegram_last_, telegram_last_length_, true); // add the last Tx telegram to the front of the tx queue, at the top
// build header
uint8_t src = telegram_last_[0];
uint8_t dest = telegram_last_[1];
uint8_t type_id = telegram_last_[2];
uint8_t offset = telegram_last_[3];
uint8_t * message_data = telegram_last_ + 4;
auto telegram = std::make_shared<Telegram>(Telegram::Operation::TX_RAW, src, dest, type_id, offset, message_data, message_length);
tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram));
return retry_count_; return retry_count_;
} }

View File

@@ -23,7 +23,15 @@
#include <deque> #include <deque>
#include <memory> // for unique ptrs #include <memory> // for unique ptrs
#include <vector> #include <vector>
// #include <atomic> // for overflow
// UART drivers
#if defined(ESP8266)
#include "uart/emsuart_esp8266.h"
#elif defined(ESP32)
#include "uart/emsuart_esp32.h"
#elif defined(EMSESP_STANDALONE)
#include <emsuart_standalone.h>
#endif
#include <uuid/log.h> #include <uuid/log.h>
@@ -43,8 +51,8 @@ static constexpr int16_t EMS_VALUE_SHORT_INVALID = 0x8000;
static constexpr uint32_t EMS_VALUE_ULONG_NOTSET = 0xFFFFFFFF; // for 3-byte and 4-byte longs static constexpr uint32_t EMS_VALUE_ULONG_NOTSET = 0xFFFFFFFF; // for 3-byte and 4-byte longs
static constexpr uint32_t EMS_VALUE_ULONG_INVALID = 0x80000000; static constexpr uint32_t EMS_VALUE_ULONG_INVALID = 0x80000000;
static constexpr uint8_t EMS_MAX_TELEGRAM_LENGTH = 32; // max length of a telegram static constexpr uint8_t EMS_MAX_TELEGRAM_LENGTH = 32; // max length of a complete EMS telegram
static constexpr uint8_t EMS_MAX_TELEGRAM_MESSAGE_LENGTH = EMS_MAX_TELEGRAM_LENGTH - 5; // max length of message block static constexpr uint8_t EMS_MAX_TELEGRAM_MESSAGE_LENGTH = 27; // max length of message block, assuming EMS1.0
namespace emsesp { namespace emsesp {
@@ -170,7 +178,6 @@ class RxService : public EMSbus {
RxService() = default; RxService() = default;
~RxService() = default; ~RxService() = default;
void start();
void loop(); void loop();
void add(uint8_t * data, uint8_t length); void add(uint8_t * data, uint8_t length);
@@ -210,7 +217,6 @@ class RxService : public EMSbus {
static constexpr uint32_t RX_LOOP_WAIT = 800; // delay in processing Rx queue static constexpr uint32_t RX_LOOP_WAIT = 800; // delay in processing Rx queue
uint32_t last_rx_check_ = 0; uint32_t last_rx_check_ = 0;
// std::atomic<bool> rx_telegrams_overflow_{false};
uint8_t rx_telegram_id_ = 0; // queue counter uint8_t rx_telegram_id_ = 0; // queue counter
uint16_t telegram_count_ = 0; // # Rx received uint16_t telegram_count_ = 0; // # Rx received
@@ -234,7 +240,7 @@ class TxService : public EMSbus {
void send(); void send();
void add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length); void add(const uint8_t operation, const uint8_t dest, const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length);
void add(uint8_t * data, const uint8_t length); void add(uint8_t * data, const uint8_t length, bool front = false);
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0); void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0);

View File

@@ -122,7 +122,12 @@ void EMSuart::start(uint8_t tx_mode) {
.parity = UART_PARITY_DISABLE, .parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1, .stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
}; }
if (tx_mode_ == 5) {
EMS_UART.conf0.stop_bit_num = UART_STOP_BITS_1_5;
} else {
EMS_UART.conf0.stop_bit_num = UART_STOP_BITS_1;
}
ESP_ERROR_CHECK(uart_param_config(EMSUART_UART, &uart_config)); ESP_ERROR_CHECK(uart_param_config(EMSUART_UART, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(EMSUART_UART, EMSUART_TXPIN, EMSUART_RXPIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); ESP_ERROR_CHECK(uart_set_pin(EMSUART_UART, EMSUART_TXPIN, EMSUART_RXPIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
@@ -161,7 +166,12 @@ void EMSuart::restart() {
EMS_UART.int_ena.brk_det = 1; // activate only break EMS_UART.int_ena.brk_det = 1; // activate only break
emsTxBufIdx = 0; emsTxBufIdx = 0;
emsTxBufLen = 0; emsTxBufLen = 0;
}; if (tx_mode_ == 5) {
EMS_UART.conf0.stop_bit_num = UART_STOP_BITS_1_5;
} else {
EMS_UART.conf0.stop_bit_num = UART_STOP_BITS_1;
}
}
/* /*
* Sends a 1-byte poll, ending with a <BRK> * Sends a 1-byte poll, ending with a <BRK>
@@ -186,7 +196,7 @@ void EMSuart::send_poll(uint8_t data) {
*/ */
uint16_t EMSuart::transmit(uint8_t * buf, uint8_t len) { uint16_t EMSuart::transmit(uint8_t * buf, uint8_t len) {
if (len == 0 || len > 32) { if (len == 0 || len > 32) {
return EMS_TX_STATUS_ERROR; return EMS_TX_STATUS_ERR;
} }
if (tx_mode_ == EMS_TXMODE_NEW || tx_mode_ == 5) { if (tx_mode_ == EMS_TXMODE_NEW || tx_mode_ == 5) {
for (uint8_t i = 0; i < len; i++) { for (uint8_t i = 0; i < len; i++) {

View File

@@ -58,7 +58,7 @@
namespace emsesp { namespace emsesp {
#define EMS_TX_STATUS_OK 1 #define EMS_TX_STATUS_OK 1
#define EMS_TX_STATUS_ERROR 0 #define EMS_TX_STATUS_ERR 0
class EMSuart { class EMSuart {
public: public:

View File

@@ -35,6 +35,7 @@ uint8_t phantomBreak = 0;
uint8_t tx_mode_ = 0xFF; uint8_t tx_mode_ = 0xFF;
bool drop_next_rx = true; bool drop_next_rx = true;
uint32_t emsRxTime; uint32_t emsRxTime;
// uint32_t emsTxTime = 0;
uint8_t emsTxBuf[EMS_MAXBUFFERSIZE]; uint8_t emsTxBuf[EMS_MAXBUFFERSIZE];
uint8_t emsTxBufIdx; uint8_t emsTxBufIdx;
uint8_t emsTxBufLen; uint8_t emsTxBufLen;
@@ -88,7 +89,10 @@ void ICACHE_FLASH_ATTR EMSuart::emsuart_recvTask(os_event_t * events) {
phantomBreak = 0; phantomBreak = 0;
length--; // remove phantom break from Rx buffer length--; // remove phantom break from Rx buffer
} }
// if (emsTxTime > 0) {
// LOG_INFO(F("tx duration: %d ms"), uuid::get_uptime() - emsTxTime);
// emsTxTime = 0;
// }
// it's a poll or status code, single byte and ok to send on, then quit // it's a poll or status code, single byte and ok to send on, then quit
if (length == 2) { if (length == 2) {
EMSESP::incoming_telegram((uint8_t *)pCurrent->buffer, 1); EMSESP::incoming_telegram((uint8_t *)pCurrent->buffer, 1);
@@ -124,7 +128,7 @@ void ICACHE_RAM_ATTR EMSuart::emsuart_tx_timer_intr_handler() {
} else if (emsTxBufIdx == emsTxBufLen) { } else if (emsTxBufIdx == emsTxBufLen) {
USC0(EMSUART_UART) |= (1 << UCBRK); // set <BRK> USC0(EMSUART_UART) |= (1 << UCBRK); // set <BRK>
if (tx_mode_ > 5 || tx_mode_ < 11) { if (tx_mode_ > 5 || tx_mode_ < 11) {
timer1_write(5 * EMSUART_TX_BIT_TIME * 11); timer1_write(5 * EMSUART_TX_BIT_TIME * 12);
USIE(EMSUART_UART) &= ~(1 << UIBD); // disable break interrupt USIE(EMSUART_UART) &= ~(1 << UIBD); // disable break interrupt
} }
} else if (USC0(EMSUART_UART) & (1 << UCBRK)) { } else if (USC0(EMSUART_UART) & (1 << UCBRK)) {
@@ -259,9 +263,9 @@ void ICACHE_FLASH_ATTR EMSuart::tx_brk() {
USC0(EMSUART_UART) |= (1 << UCBRK); // set bit USC0(EMSUART_UART) |= (1 << UCBRK); // set bit
if (tx_mode_ == EMS_TXMODE_EMSPLUS) { // EMS+ mode if (tx_mode_ == EMS_TXMODE_EMSPLUS) { // EMS+ mode
delayMicroseconds(EMSUART_TX_BRK_WAIT); // 2070 delayMicroseconds(EMSUART_TX_WAIT_PLUS); // 2070
} else if (tx_mode_ == EMS_TXMODE_HT3) { // junkers mode } else { // junkers and EMS1.0
delayMicroseconds(EMSUART_TX_BRK_WAIT_HT3); // 1144 delayMicroseconds(EMSUART_TX_WAIT_BRK); // 1144
} }
USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear BRK bit USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear BRK bit
@@ -283,10 +287,17 @@ void EMSuart::send_poll(uint8_t data) {
} else if (tx_mode_ >= EMS_TXMODE_NEW) { // hardware controlled modes } else if (tx_mode_ >= EMS_TXMODE_NEW) { // hardware controlled modes
USF(EMSUART_UART) = data; USF(EMSUART_UART) = data;
USC0(EMSUART_UART) |= (1 << UCBRK); // send <BRK> at the end USC0(EMSUART_UART) |= (1 << UCBRK); // send <BRK> at the end
} else { // software controlled modes } else if (tx_mode_ == EMS_TXMODE_HT3) {
// EMS1.0, EMS+ and HT3
USF(EMSUART_UART) = data; USF(EMSUART_UART) = data;
delayMicroseconds(EMSUART_TX_BRK_WAIT); delayMicroseconds(EMSUART_TX_WAIT_HT3);
tx_brk(); // send <BRK>
} else if (tx_mode_ == EMS_TXMODE_EMSPLUS) {
USF(EMSUART_UART) = data;
delayMicroseconds(EMSUART_TX_WAIT_PLUS);
tx_brk(); // send <BRK>
} else { // EMS1.0
USF(EMSUART_UART) = data;
delayMicroseconds(EMSUART_TX_WAIT_BRK);
tx_brk(); // send <BRK> tx_brk(); // send <BRK>
} }
} }
@@ -303,6 +314,7 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
// LOG_INFO(F("[DEBUG] UART Response time: %d ms"), uuid::get_uptime() - emsRxTime); // LOG_INFO(F("[DEBUG] UART Response time: %d ms"), uuid::get_uptime() - emsRxTime);
#endif #endif
// emsTxTime = uuid::get_uptime();
// if ((uuid::get_uptime() - emsRxTime) > EMS_RX_TO_TX_TIMEOUT)) { // send allowed within 20 ms // if ((uuid::get_uptime() - emsRxTime) > EMS_RX_TO_TX_TIMEOUT)) { // send allowed within 20 ms
// return EMS_TX_STATUS_ERR; // return EMS_TX_STATUS_ERR;
// } // }
@@ -317,6 +329,8 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
emsTxBufIdx = 0; emsTxBufIdx = 0;
emsTxBufLen = len; emsTxBufLen = len;
USF(EMSUART_UART) = buf[0]; USF(EMSUART_UART) = buf[0];
// timer1_attachInterrupt(emsuart_tx_timer_intr_handler); // Add ISR Function
// timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE); // 5 MHz timer
timer1_write(emsTxWait); timer1_write(emsTxWait);
return EMS_TX_STATUS_OK; return EMS_TX_STATUS_OK;
} }
@@ -334,7 +348,7 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
if (tx_mode_ == EMS_TXMODE_EMSPLUS) { // With extra tx delay for EMS+ if (tx_mode_ == EMS_TXMODE_EMSPLUS) { // With extra tx delay for EMS+
for (uint8_t i = 0; i < len; i++) { for (uint8_t i = 0; i < len; i++) {
USF(EMSUART_UART) = buf[i]; USF(EMSUART_UART) = buf[i];
delayMicroseconds(EMSUART_TX_BRK_WAIT); // 2070 delayMicroseconds(EMSUART_TX_WAIT_PLUS); // 2070
} }
tx_brk(); // send <BRK> tx_brk(); // send <BRK>
return EMS_TX_STATUS_OK; return EMS_TX_STATUS_OK;
@@ -350,7 +364,7 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
; ;
// wait until bits are sent on wire // wait until bits are sent on wire
delayMicroseconds(EMSUART_TX_BRK_WAIT_HT3); delayMicroseconds(EMSUART_TX_WAIT_HT3);
} }
tx_brk(); // send <BRK> tx_brk(); // send <BRK>
return EMS_TX_STATUS_OK; return EMS_TX_STATUS_OK;

View File

@@ -42,13 +42,16 @@
// LEGACY // LEGACY
#define EMSUART_TX_BIT_TIME 104 // bit time @9600 baud #define EMSUART_TX_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_BRK (EMSUART_TX_BIT_TIME * 11) // 1144
// EMS 1.0 // EMS 1.0
#define EMSUART_TX_BUSY_WAIT (EMSUART_TX_BIT_TIME / 8) // 13 #define EMSUART_TX_BUSY_WAIT (EMSUART_TX_BIT_TIME / 8) // 13
// HT3/Junkers - Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit). The -8 is for lag compensation. // HT3/Junkers - Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit) plus 7 bit delay. The -8 is for lag compensation.
#define EMSUART_TX_BRK_WAIT_HT3 (EMSUART_TX_BIT_TIME * 11) - 8 // 1136 #define EMSUART_TX_WAIT_HT3 (EMSUART_TX_BIT_TIME * 17) - 8 // 1760
// EMS+ - Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit) and delay of another Bytetime.
#define EMSUART_TX_WAIT_PLUS 2070
namespace emsesp { namespace emsesp {

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "2.0.0a17" #define EMSESP_APP_VERSION "2.0.0a18"