This commit is contained in:
Paul
2020-05-25 23:18:30 +02:00
parent d3953d90ca
commit 69646c1a1c
21 changed files with 335 additions and 104 deletions

View File

@@ -41,7 +41,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] [trace ID] [raw] log [level] [raw] [trace ID]
su su
(top level) (top level)
@@ -124,8 +124,9 @@ thermostat
### **Known issues, bugs and improvements currently working on** ### **Known issues, bugs and improvements currently working on**
``` ```
TODO ESP32 - when saving SPIFFS the UART stop and restart() functions need to flush queue to avoid miss fires
TODO network issues with ESP8266 - can take a while to get an IP address. DNS issue?
TODO figure out why sometimes telnet on ESP32 (and sometimes ESP8266) has slow response times. After a manual reset it seems to fix itself. Perhaps the telnet service needs to start after the wifi is up & running. TODO figure out why sometimes telnet on ESP32 (and sometimes ESP8266) has slow response times. After a manual reset it seems to fix itself. Perhaps the telnet service needs to start after the wifi is up & running.
TODO Get the ESP32 UART code working.
TODO sometimes with tx_mode 0 there are a few CRC errors due to collision when waiting for a BRK signal. TODO sometimes with tx_mode 0 there are a few CRC errors due to collision when waiting for a BRK signal.
TODO console auto-complete with 'set' command in the system context is not showing all commands, only the hostname. TODO console auto-complete with 'set' command in the system context is not showing all commands, only the hostname.
``` ```

View File

@@ -26,15 +26,25 @@ uint64_t get_uptime_ms() {
static uint32_t high_millis = 0; static uint32_t high_millis = 0;
static uint32_t low_millis = 0; static uint32_t low_millis = 0;
uint32_t now_millis = ::millis(); if (get_uptime() < low_millis) {
if (now_millis < low_millis) {
high_millis++; high_millis++;
} }
low_millis = now_millis; low_millis = get_uptime();
return ((uint64_t)high_millis << 32) | low_millis; return ((uint64_t)high_millis << 32) | low_millis;
} }
// added by proddy
static uint32_t now_millis; // added by proddy
void set_uptime() {
now_millis = ::millis();
}
uint32_t get_uptime() {
return now_millis;
}
} // namespace uuid } // namespace uuid

View File

@@ -21,6 +21,7 @@
namespace uuid { namespace uuid {
void loop() { void loop() {
set_uptime(); // added by proddy
get_uptime_ms(); get_uptime_ms();
} }

View File

@@ -86,6 +86,9 @@ void loop();
*/ */
uint64_t get_uptime_ms(); uint64_t get_uptime_ms();
uint32_t get_uptime(); // added by proddy
void set_uptime();
} // namespace uuid } // namespace uuid
#endif #endif

View File

@@ -21,7 +21,7 @@ extra_configs = pio_local.ini
;debug_flags = -DDEBUG_ESP_PORT=Serial -DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM ;debug_flags = -DDEBUG_ESP_PORT=Serial -DDEBUG_ESP_CORE -DDEBUG_ESP_SSL -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM
debug_flags = debug_flags =
-D EMSESP_DEBUG ; -D EMSESP_DEBUG
; -D EMSESP_SAFE_MODE ; -D EMSESP_SAFE_MODE
; -D ENABLE_CORS -D CORS_ORIGIN=\"http://localhost:3000\" ; -D ENABLE_CORS -D CORS_ORIGIN=\"http://localhost:3000\"
@@ -69,7 +69,7 @@ upload_protocol = espota
upload_flags = upload_flags =
--port=8266 --port=8266
--auth=neo --auth=neo
upload_port = ems-esp32.local upload_port = ems-esp.local
[env:esp8266] [env:esp8266]
build_type = release build_type = release

View File

@@ -221,7 +221,7 @@ 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_(traceid_optional), F_(raw_optional)}, flash_string_vector{F_(log_level_optional), F_(trace_format_optional), F_(traceid_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) { [](Shell & shell, const std::vector<std::string> & arguments) {
uint16_t watch_id; uint16_t watch_id;
if (!arguments.empty()) { if (!arguments.empty()) {
@@ -238,16 +238,17 @@ void Console::load_standard_commands(unsigned int context) {
if (level == uuid::log::Level::TRACE) { if (level == uuid::log::Level::TRACE) {
watch_id = LOG_TRACE_WATCH_NONE; // no watch ID set watch_id = LOG_TRACE_WATCH_NONE; // no watch ID set
if (arguments.size() > 1) { if (arguments.size() > 1) {
watch_id = Helpers::hextoint(arguments[1].c_str()); // get the watch ID // 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_(full))) {
emsesp::EMSESP::trace_raw(false);
} }
emsesp::EMSESP::trace_watch_id(watch_id); // get the watch_id if its set
if (arguments.size() == 3) {
// see if we have a "raw" emsesp::EMSESP::trace_watch_id(Helpers::hextoint(arguments[2].c_str()));
if ((arguments.size() == 3) && (arguments[2] == read_flash_string(F_(raw)))) { }
emsesp::EMSESP::trace_raw(true);
} else {
emsesp::EMSESP::trace_raw(false);
} }
} }
} }

View File

@@ -89,6 +89,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(full)
// context menus // context menus
MAKE_PSTR_WORD(mqtt) MAKE_PSTR_WORD(mqtt)
@@ -102,7 +103,7 @@ MAKE_PSTR(n_mandatory, "<n>")
MAKE_PSTR(n_optional, "[n]") MAKE_PSTR(n_optional, "[n]")
MAKE_PSTR(traceid_optional, "[trace ID]") MAKE_PSTR(traceid_optional, "[trace ID]")
MAKE_PSTR(trace_raw_fmt, "Displaying raw bytes = %s") MAKE_PSTR(trace_raw_fmt, "Displaying raw bytes = %s")
MAKE_PSTR(raw_optional, "[raw]") MAKE_PSTR(trace_format_optional, "[full|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

@@ -333,7 +333,7 @@ void EMSESP::process_UBADevices(std::shared_ptr<const Telegram> telegram) {
// if we haven't already detected this device, request it's version details, unless its us (EMS-ESP) // if we haven't already detected this device, request it's version details, unless its us (EMS-ESP)
// when the version info is received, it will automagically add the device // when the version info is received, it will automagically add the device
if ((device_id != ems_bus_id) && !(EMSESP::device_exists(device_id))) { if ((device_id != ems_bus_id) && !(EMSESP::device_exists(device_id))) {
LOG_INFO(F("New EMS device detected with ID 0x%02X. Requesting version information."), device_id); LOG_DEBUG(F("New EMS device detected with ID 0x%02X. Requesting version information."), device_id);
send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id); send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
} }
} }
@@ -619,7 +619,7 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
// check for poll to us, if so send top message from Tx queue immediately and quit // check for poll to us, if so send top message from Tx queue immediately and quit
// if ht3 poll must be ems_bus_id else if Buderus poll must be (ems_bus_id | 0x80) // if ht3 poll must be ems_bus_id else if Buderus poll must be (ems_bus_id | 0x80)
if ((first_value ^ 0x80 ^ rxservice_.ems_mask()) == txservice_.ems_bus_id()) { if ((first_value ^ 0x80 ^ rxservice_.ems_mask()) == txservice_.ems_bus_id()) {
EMSbus::last_bus_activity(millis()); // set the flag indication the EMS bus is active EMSbus::last_bus_activity(uuid::get_uptime()); // set the flag indication the EMS bus is active
txservice_.send(); txservice_.send();
} }
return; return;
@@ -695,17 +695,17 @@ void EMSESP::console_commands(Shell & shell, unsigned int context) {
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 = (arguments[0]).at(0) - '0';
if ((tx_mode > 0) && (tx_mode <= 3)) { if ((tx_mode > 0) && (tx_mode <= 4)) {
Settings settings; Settings settings;
settings.ems_tx_mode(tx_mode); settings.ems_tx_mode(tx_mode);
settings.commit(); settings.commit();
shell.printfln(F_(tx_mode_fmt), settings.ems_tx_mode()); shell.printfln(F_(tx_mode_fmt), settings.ems_tx_mode());
} else { } else {
shell.println(F("Must be 1 for EMS generic, 2 for EMS+ or 3 for HT3")); shell.println(F("Must be 1 for EMS generic, 2 for EMS+, 3 for HT3, 4 for experimental"));
} }
}, },
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> { [](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments __attribute__((unused))) -> const std::vector<std::string> {
return std::vector<std::string>{read_flash_string(F("1")), read_flash_string(F("2")), read_flash_string(F("3"))}; return std::vector<std::string>{read_flash_string(F("1")), read_flash_string(F("2")), read_flash_string(F("3")), read_flash_string(F("4"))};
}); });
EMSESPShell::commands->add_command( EMSESPShell::commands->add_command(
@@ -808,19 +808,20 @@ void EMSESP::start() {
// loop de loop // loop de loop
void EMSESP::loop() { void EMSESP::loop() {
// network returns false if an OTA is being carried out. // network returns false if an OTA is being carried out
// so we disable all services when an OTA is happening
if (network_.loop()) { if (network_.loop()) {
console_.loop(); // telnet/serial console console_.loop(); // telnet/serial console
system_.loop(); // does LED and checks system health, and syslog service system_.loop(); // does LED and checks system health, and syslog service
mqtt_.loop(); // starts mqtt, and sends out anything in the queue mqtt_.loop(); // starts mqtt, and sends out anything in the queue
rxservice_.loop(); // process what ever is in the rx queue rxservice_.loop(); // process what ever is in the rx queue
txservice_.loop(); // check that the Tx is all ok
shower_.loop(); // check for shower on/off shower_.loop(); // check for shower on/off
sensors_.loop(); // this will also send out via MQTT sensors_.loop(); // this will also send out via MQTT
// force a query on the EMS devices to fetch latest data // force a query on the EMS devices to fetch latest data at a set interval (1 min)
uint32_t currentMillis = millis(); if ((uuid::get_uptime() - last_fetch_ > EMS_FETCH_FREQUENCY)) {
if ((currentMillis - last_fetch_ > EMS_FETCH_FREQUENCY)) { last_fetch_ = uuid::get_uptime();
last_fetch_ = currentMillis;
fetch_device_values(); fetch_device_values();
} }
} }

View File

@@ -142,7 +142,7 @@ void Mqtt::setup() {
#endif #endif
mqtt_connecting_ = false; mqtt_connecting_ = false;
mqtt_last_connection_ = millis(); mqtt_last_connection_ = uuid::get_uptime();
mqtt_reconnect_delay_ = Mqtt::MQTT_RECONNECT_DELAY_MIN; mqtt_reconnect_delay_ = Mqtt::MQTT_RECONNECT_DELAY_MIN;
LOG_DEBUG(F("Configuring MQTT service...")); LOG_DEBUG(F("Configuring MQTT service..."));
@@ -177,7 +177,7 @@ void Mqtt::init() {
} }
// Reset reconnection delay // Reset reconnection delay
mqtt_last_connection_ = millis(); mqtt_last_connection_ = uuid::get_uptime();
mqtt_connecting_ = false; mqtt_connecting_ = false;
mqtt_start_ = false; // will force a new start() mqtt_start_ = false; // will force a new start()
}); });
@@ -236,7 +236,7 @@ void Mqtt::loop() {
} }
// send out heartbeat // send out heartbeat
uint32_t currentMillis = millis(); uint32_t currentMillis = uuid::get_uptime();
if ((currentMillis - last_heartbeat_ > MQTT_HEARTBEAT_INTERVAL)) { if ((currentMillis - last_heartbeat_ > MQTT_HEARTBEAT_INTERVAL)) {
last_heartbeat_ = currentMillis; last_heartbeat_ = currentMillis;
send_heartbeat(); send_heartbeat();
@@ -258,7 +258,7 @@ void Mqtt::loop() {
} }
// We need to reconnect. Check when was the last time we tried this // We need to reconnect. Check when was the last time we tried this
if (mqtt_last_connection_ && (millis() - mqtt_last_connection_ < mqtt_reconnect_delay_)) { if (mqtt_last_connection_ && (uuid::get_uptime() - mqtt_last_connection_ < mqtt_reconnect_delay_)) {
return; return;
} }
@@ -394,7 +394,9 @@ void Mqtt::on_publish(uint16_t packetId) {
} }
if (mqtt_message.packet_id_ == packetId) { if (mqtt_message.packet_id_ == packetId) {
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("Acknowledged PID %d. Removing from queue"), packetId); LOG_DEBUG(F("Acknowledged PID %d. Removing from queue"), packetId);
#endif
} else { } else {
LOG_DEBUG(F("Mismatch, expecting PID %d, got %d"), mqtt_message.packet_id_, packetId); LOG_DEBUG(F("Mismatch, expecting PID %d, got %d"), mqtt_message.packet_id_, packetId);
mqtt_publish_fails_++; // increment error count mqtt_publish_fails_++; // increment error count
@@ -436,7 +438,7 @@ void Mqtt::send_start_topic() {
// MQTT onConnect - when a connect is established // MQTT onConnect - when a connect is established
void Mqtt::on_connect() { void Mqtt::on_connect() {
mqtt_reconnect_delay_ = Mqtt::MQTT_RECONNECT_DELAY_MIN; mqtt_reconnect_delay_ = Mqtt::MQTT_RECONNECT_DELAY_MIN;
mqtt_last_connection_ = millis(); mqtt_last_connection_ = uuid::get_uptime();
mqtt_connecting_ = false; mqtt_connecting_ = false;
LOG_INFO(F("MQTT connected")); LOG_INFO(F("MQTT connected"));
} }
@@ -600,7 +602,9 @@ void Mqtt::process_queue() {
// but add the packet_id so we can check it later // but add the packet_id so we can check it later
if (mqtt_qos_ != 0) { if (mqtt_qos_ != 0) {
mqtt_messages_.front().packet_id_ = packet_id; mqtt_messages_.front().packet_id_ = packet_id;
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("Setting packetID for ACK to %d"), packet_id); LOG_DEBUG(F("Setting packetID for ACK to %d"), packet_id);
#endif
return; return;
} }

View File

@@ -54,8 +54,10 @@ void Sensors::start() {
void Sensors::loop() { void Sensors::loop() {
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
uint32_t time_now = uuid::get_uptime();
if (state_ == State::IDLE) { if (state_ == State::IDLE) {
if (millis() - last_activity_ >= READ_INTERVAL_MS) { if (time_now - last_activity_ >= READ_INTERVAL_MS) {
// LOG_DEBUG(F("Read sensor temperature")); // uncomment for debug // LOG_DEBUG(F("Read sensor temperature")); // uncomment for debug
if (bus_.reset()) { if (bus_.reset()) {
bus_.skip(); bus_.skip();
@@ -67,7 +69,7 @@ void Sensors::loop() {
// LOG_ERROR(F("Bus reset failed")); // uncomment for debug // LOG_ERROR(F("Bus reset failed")); // uncomment for debug
devices_.clear(); // remove all know devices incase we have a disconnect devices_.clear(); // remove all know devices incase we have a disconnect
} }
last_activity_ = millis(); last_activity_ = time_now;
} }
} else if (state_ == State::READING) { } else if (state_ == State::READING) {
if (temperature_convert_complete()) { if (temperature_convert_complete()) {
@@ -76,18 +78,18 @@ void Sensors::loop() {
found_.clear(); found_.clear();
state_ = State::SCANNING; state_ = State::SCANNING;
last_activity_ = millis(); last_activity_ = time_now;
} else if (millis() - last_activity_ > READ_TIMEOUT_MS) { } else if (time_now - last_activity_ > READ_TIMEOUT_MS) {
LOG_ERROR(F("Sensor read timeout")); LOG_ERROR(F("Sensor read timeout"));
state_ = State::IDLE; state_ = State::IDLE;
last_activity_ = millis(); last_activity_ = time_now;
} }
} else if (state_ == State::SCANNING) { } else if (state_ == State::SCANNING) {
if (millis() - last_activity_ > SCAN_TIMEOUT_MS) { if (time_now - last_activity_ > SCAN_TIMEOUT_MS) {
LOG_ERROR(F("Sensor scan timeout")); LOG_ERROR(F("Sensor scan timeout"));
state_ = State::IDLE; state_ = State::IDLE;
last_activity_ = millis(); last_activity_ = time_now;
} else { } else {
uint8_t addr[ADDR_LEN] = {0}; uint8_t addr[ADDR_LEN] = {0};
@@ -125,7 +127,7 @@ void Sensors::loop() {
found_.clear(); found_.clear();
// LOG_DEBUG(F("Found %zu sensor(s). Adding them."), devices_.size()); // uncomment for debug // LOG_DEBUG(F("Found %zu sensor(s). Adding them."), devices_.size()); // uncomment for debug
state_ = State::IDLE; state_ = State::IDLE;
last_activity_ = millis(); last_activity_ = time_now;
} }
} }
} }

View File

@@ -100,8 +100,8 @@ class Sensors {
bool temperature_convert_complete(); bool temperature_convert_complete();
float get_temperature_c(const uint8_t addr[]); float get_temperature_c(const uint8_t addr[]);
uint32_t last_activity_ = millis(); uint32_t last_activity_ = uuid::get_uptime();
uint32_t last_publish_ = millis(); uint32_t last_publish_ = uuid::get_uptime();
State state_ = State::IDLE; State state_ = State::IDLE;
std::vector<Device> found_; std::vector<Device> found_;
std::vector<Device> devices_; std::vector<Device> devices_;

View File

@@ -36,7 +36,8 @@ void Shower::loop() {
return; return;
} }
uint32_t time_now = millis(); uint32_t time_now = uuid::get_uptime();
// if already in cold mode, ignore all this logic until we're out of the cold blast // if already in cold mode, ignore all this logic until we're out of the cold blast
if (!doing_cold_shot_) { if (!doing_cold_shot_) {
// is the hot water running? // is the hot water running?

View File

@@ -246,9 +246,8 @@ void System::set_led_speed(uint32_t speed) {
void System::system_check() { void System::system_check() {
static uint32_t last_system_check_ = 0; static uint32_t last_system_check_ = 0;
uint32_t currentMillis = millis(); if (!last_system_check_ || ((uint32_t)(uuid::get_uptime() - last_system_check_) >= SYSTEM_CHECK_FREQUENCY)) {
if (!last_system_check_ || ((uint32_t)(currentMillis - last_system_check_) >= SYSTEM_CHECK_FREQUENCY)) { last_system_check_ = uuid::get_uptime();
last_system_check_ = currentMillis;
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
if ((WiFi.status() != WL_CONNECTED) || safe_mode()) { if ((WiFi.status() != WL_CONNECTED) || safe_mode()) {
@@ -262,6 +261,7 @@ void System::system_check() {
if (!EMSbus::bus_connected()) { if (!EMSbus::bus_connected()) {
system_healthy_ = false; system_healthy_ = false;
set_led_speed(LED_WARNING_BLINK); // flash every 1/2 second from now on set_led_speed(LED_WARNING_BLINK); // flash every 1/2 second from now on
LOG_ERROR(F("No connection to the EMS bus!"));
} else { } else {
// if it was unhealthy but now we're better, make sure the LED is solid again cos we've been healed // if it was unhealthy but now we're better, make sure the LED is solid again cos we've been healed
if (!system_healthy_) { if (!system_healthy_) {
@@ -278,9 +278,8 @@ void System::system_check() {
void System::led_monitor() { void System::led_monitor() {
static uint32_t led_last_blink_ = 0; static uint32_t led_last_blink_ = 0;
uint32_t currentMillis = millis(); if (!led_last_blink_ || (uint32_t)(uuid::get_uptime() - led_last_blink_) >= led_flash_speed_) {
if (!led_last_blink_ || (uint32_t)(currentMillis - led_last_blink_) >= led_flash_speed_) { led_last_blink_ = uuid::get_uptime();
led_last_blink_ = currentMillis;
// if bus_not_connected or network not connected, start flashing // if bus_not_connected or network not connected, start flashing
if (!system_healthy_) { if (!system_healthy_) {

View File

@@ -74,8 +74,8 @@ class System {
static uuid::syslog::SyslogService syslog_; static uuid::syslog::SyslogService syslog_;
#endif #endif
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 5000; // check every 5 seconds static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 10000; // check every 10 seconds
static constexpr uint32_t LED_WARNING_BLINK = 1000; // pulse to show no connection static constexpr uint32_t LED_WARNING_BLINK = 1000; // pulse to show no connection, 1 sec
static constexpr uint32_t LED_WARNING_BLINK_FAST = 100; // flash quickly for boot up sequence or safe-mode static constexpr uint32_t LED_WARNING_BLINK_FAST = 100; // flash quickly for boot up sequence or safe-mode
// internal LED // internal LED

View File

@@ -200,9 +200,10 @@ void RxService::flush_rx_queue() {
rx_telegram_id_ = 0; rx_telegram_id_ = 0;
} }
// start and initialize the Rx incoming buffer. Not currently used. // start and initialize the Rx incoming buffer
void RxService::start() { void RxService::start() {
// LOG_DEBUG(F("RxStart")); // 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
@@ -210,11 +211,10 @@ void RxService::start() {
void RxService::loop() { void RxService::loop() {
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
// give rx some breathing space // give rx some breathing space
uint32_t time_now = millis(); if ((uuid::get_uptime() - last_rx_check_) < RX_LOOP_WAIT) {
if ((time_now - last_rx_check_) < RX_LOOP_WAIT) {
return; return;
} }
last_rx_check_ = time_now; last_rx_check_ = uuid::get_uptime();
#endif #endif
while (!rx_telegrams_.empty()) { while (!rx_telegrams_.empty()) {
@@ -235,6 +235,7 @@ void RxService::loop() {
// add a new rx telegram object // add a new rx telegram object
// data is the whole telegram, assuming last byte holds the CRC // data is the whole telegram, assuming last byte holds 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) {
// validate the CRC // validate the CRC
@@ -263,7 +264,7 @@ 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; // this could be 2 bytes for ems+ uint16_t type_id = 0; // this could be 2 bytes for ems+
uint8_t * message_data; uint8_t * message_data;
uint8_t message_length; uint8_t message_length;
@@ -274,15 +275,21 @@ void RxService::add(uint8_t * data, uint8_t length) {
message_data = data + 4; // message block starts at 5th byte message_data = data + 4; // message block starts at 5th byte
message_length = length - 5; // remove 4 bytes header plus CRC message_length = length - 5; // remove 4 bytes header plus CRC
} else { } else {
// EMS 2.0 // EMS 2.0 / EMS+
if (data[2] == 0xFF) { if (data[2] == 0xFF) {
type_id = (data[4] << 8) + data[5] + 256; // check for empty data
message_data = data + 6; // message block starts at 7th position // special broadcast telegrams on ems+ have no data values, some even don't have a type ID
if (length <= 7) { if (length <= 7) {
message_length = 0; // special broadcast on ems+ have no data values message_data = data; // bogus pointer, will not be used
message_length = 0;
if (length <= 5) {
type_id = 0; // has also an empty type_id
} else {
type_id = (data[4] << 8) + data[5] + 256;
}
} else { } else {
message_length = length - 7; // remove 6 byte header plus CRC 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
@@ -297,20 +304,28 @@ void RxService::add(uint8_t * data, uint8_t length) {
} }
} }
// if we don't have a type_id, exit
if (type_id == 0) {
return;
}
// 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 // check if queue is full
if (rx_telegrams_.size() >= maximum_rx_telegrams_) { if (rx_telegrams_.size() >= MAX_RX_TELEGRAMS) {
// rx_telegrams_overflow_ = true; // rx_telegrams_overflow_ = true;
rx_telegrams_.pop_front(); rx_telegrams_.pop_front();
} }
// add to queue, with timestamp // add to queue
LOG_DEBUG(F("New Rx [#%d] telegram, length %d"), rx_telegram_id_, message_length); LOG_DEBUG(F("New Rx [#%d] telegram, length %d"), rx_telegram_id_, message_length);
rx_telegrams_.emplace_back(rx_telegram_id_++, std::move(telegram)); rx_telegrams_.emplace_back(rx_telegram_id_++, std::move(telegram));
} }
//
// Tx CODE here
//
TxService::QueuedTxTelegram::QueuedTxTelegram(uint16_t id, std::shared_ptr<Telegram> && telegram) TxService::QueuedTxTelegram::QueuedTxTelegram(uint16_t id, std::shared_ptr<Telegram> && telegram)
: id_(id) : id_(id)
@@ -325,8 +340,6 @@ void TxService::flush_tx_queue() {
// start and initialize Tx // start and initialize Tx
void TxService::start() { void TxService::start() {
// LOG_DEBUG(F("TxStart()"));
// grab the bus ID // grab the bus ID
Settings settings; Settings settings;
ems_bus_id(settings.ems_bus_id()); ems_bus_id(settings.ems_bus_id());
@@ -336,6 +349,19 @@ void TxService::start() {
read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER); read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
} }
// Tx loop
// here we check if the Tx is not full and report an error
void TxService::loop() {
#ifndef EMSESP_STANDALONE
if ((uuid::get_uptime() - last_tx_check_) > TX_LOOP_WAIT) {
last_tx_check_ = uuid::get_uptime();
if ((tx_telegrams_.size() >= MAX_TX_TELEGRAMS - 1) && (EMSbus::bus_connected())) {
LOG_ERROR(F("Tx buffer full. Looks like Tx is not working?"));
}
}
#endif
}
// 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() {
EMSuart::send_poll(ems_bus_id() ^ ems_mask()); EMSuart::send_poll(ems_bus_id() ^ ems_mask());
@@ -352,9 +378,8 @@ void TxService::send() {
// if there's nothing in the queue to send // if there's nothing in the queue to send
// optionally, send back a poll and quit // optionally, send back a poll and quit
// for now I've disabled the poll
if (tx_telegrams_.empty()) { if (tx_telegrams_.empty()) {
// send_poll(); // send_poll(); // TODO commented out poll for now. should add back when stable.
return; return;
} }
@@ -482,7 +507,7 @@ void TxService::add(const uint8_t operation, const uint8_t dest, const uint16_t
LOG_DEBUG(F("New Tx [#%d] telegram, length %d"), tx_telegram_id_, message_length); LOG_DEBUG(F("New Tx [#%d] telegram, length %d"), tx_telegram_id_, message_length);
// 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() >= maximum_tx_telegrams_) { if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
tx_telegrams_.pop_front(); tx_telegrams_.pop_front();
} }
@@ -508,7 +533,7 @@ void TxService::add(uint8_t * data, const uint8_t length) {
auto telegram = std::make_shared<Telegram>(Telegram::Operation::TX_RAW, src, dest, type_id, offset, message_data, message_length); auto telegram = std::make_shared<Telegram>(Telegram::Operation::TX_RAW, src, dest, type_id, offset, message_data, message_length);
// 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() >= maximum_tx_telegrams_) { if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
tx_telegrams_.pop_front(); tx_telegrams_.pop_front();
} }

View File

@@ -96,7 +96,7 @@ class EMSbus {
static bool bus_connected() { static bool bus_connected() {
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
if ((millis() - last_bus_activity_) > EMS_BUS_TIMEOUT) { if ((uuid::get_uptime() - last_bus_activity_) > EMS_BUS_TIMEOUT) {
bus_connected_ = false; bus_connected_ = false;
} }
return bus_connected_; return bus_connected_;
@@ -202,8 +202,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;
size_t maximum_rx_telegrams_ = MAX_RX_TELEGRAMS;
// std::atomic<bool> rx_telegrams_overflow_{false}; // std::atomic<bool> rx_telegrams_overflow_{false};
uint8_t rx_telegram_id_ = 0; // queue counter uint8_t rx_telegram_id_ = 0; // queue counter
@@ -224,6 +222,7 @@ class TxService : public EMSbus {
~TxService() = default; ~TxService() = default;
void start(); void start();
void loop();
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);
@@ -292,9 +291,11 @@ class TxService : public EMSbus {
private: private:
static constexpr uint8_t MAXIMUM_TX_RETRIES = 3; static constexpr uint8_t MAXIMUM_TX_RETRIES = 3;
size_t maximum_tx_telegrams_ = MAX_TX_TELEGRAMS;
uint8_t tx_telegram_id_ = 0; // queue counter uint8_t tx_telegram_id_ = 0; // queue counter
static constexpr uint32_t TX_LOOP_WAIT = 10000; // when to check if Tx is up and running (10 sec)
uint32_t last_tx_check_ = 0;
std::deque<QueuedTxTelegram> tx_telegrams_; std::deque<QueuedTxTelegram> tx_telegrams_;
uint8_t telegram_last_[EMS_MAX_TELEGRAM_LENGTH]; // copy of last telegram uint8_t telegram_last_[EMS_MAX_TELEGRAM_LENGTH]; // copy of last telegram

View File

@@ -7,8 +7,8 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
emsdevices.push_back(EMSFactory::add(EMSdevice::DeviceType::BOILER, EMSdevice::EMS_DEVICE_ID_BOILER, 0, "", "My Boiler", 0, 0)); emsdevices.push_back(EMSFactory::add(EMSdevice::DeviceType::BOILER, EMSdevice::EMS_DEVICE_ID_BOILER, 0, "", "My Boiler", 0, 0));
// A fake response - UBADevices(0x07) // A fake response - UBADevices(0x07)
uint8_t t0[] = {0x08, 0x00, 0x07, 0x00, 0x0B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47}; uint8_t t[] = {0x08, 0x00, 0x07, 0x00, 0x0B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47};
rxservice_.add(t0, sizeof(t0)); rxservice_.add(t, sizeof(t));
return; return;
} }
@@ -28,8 +28,8 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
rxservice_.loop(); rxservice_.loop();
// simulate getting version information back from an unknown device // simulate getting version information back from an unknown device
uint8_t u1[] = {0x09, 0x0B, 0x02, 0x00, 0x59, 0x01, 0x02, 0x56}; uint8_t t[] = {0x09, 0x0B, 0x02, 0x00, 0x59, 0x01, 0x02, 0x56};
rxservice_.add(u1, sizeof(u1)); rxservice_.add(t, sizeof(t));
rxservice_.loop(); rxservice_.loop();
return; return;
@@ -63,9 +63,9 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
// SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200 // SM100Monitor - type 0x0362 EMS+ - for SM100 and SM200
// B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80 // B0 0B FF 00 02 62 00 44 02 7A 80 00 80 00 80 00 80 00 80 00 80 00 00 7C 80 00 80 00 80 00 80
uint8_t s1[] = {0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00, uint8_t t[] = {0xB0, 0x0B, 0xFF, 00, 0x02, 0x62, 00, 0x44, 0x02, 0x7A, 0x80, 00, 0x80, 0x00, 0x80, 00,
0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x89}; 0x80, 00, 0x80, 00, 0x80, 00, 00, 0x7C, 0x80, 00, 0x80, 00, 0x80, 00, 0x80, 0x89};
rxservice_.add(s1, sizeof(s1)); rxservice_.add(t, sizeof(t));
rxservice_.loop(); rxservice_.loop();
shell.loop_all(); shell.loop_all();
@@ -84,9 +84,9 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
// RCPLUSStatusMessage_HC1(0x01A5) // RCPLUSStatusMessage_HC1(0x01A5)
// 98 00 FF 00 01 A5 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03 // 98 00 FF 00 01 A5 00 CF 21 2E 00 00 2E 24 03 25 03 03 01 03 25 00 C8 00 00 11 01 03
uint8_t c1[] = {0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24, 0x03, uint8_t t[] = {0x98, 0x00, 0xFF, 0x00, 0x01, 0xA5, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24, 0x03,
0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03, 0x13}; 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03, 0x13};
rxservice_.add(c1, sizeof(c1)); rxservice_.add(t, sizeof(t));
rxservice_.loop(); rxservice_.loop();
shell.loop_all(); shell.loop_all();
@@ -240,19 +240,19 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
incoming_telegram(poll, 1); incoming_telegram(poll, 1);
// incoming Rx // incoming Rx
uint8_t t17[] = {0x17, 0x08, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A}; uint8_t t1[] = {0x17, 0x08, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A};
incoming_telegram(t17, sizeof(t17)); incoming_telegram(t1, sizeof(t1));
rxservice_.loop(); rxservice_.loop();
// Simulate adding a Poll - should send retry // Simulate adding a Poll - should send retry
incoming_telegram(poll, 1); incoming_telegram(poll, 1);
show_emsbus(shell); show_emsbus(shell);
uint8_t t18[] = {0x21, 0x22}; uint8_t t2[] = {0x21, 0x22};
send_write_request(0x91, 0x17, 0x00, t18, sizeof(t18), 0); send_write_request(0x91, 0x17, 0x00, t2, sizeof(t2), 0);
show_emsbus(shell); show_emsbus(shell);
incoming_telegram(t17, sizeof(t17)); incoming_telegram(t1, sizeof(t1));
txservice_.flush_tx_queue(); txservice_.flush_tx_queue();
@@ -324,9 +324,17 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
if (command == "rx2") { if (command == "rx2") {
// incoming Rx // incoming Rx
uint8_t t71[] = {0x1B, 0x5B, 0xFD, 0x2D, 0x9E, 0x3A, 0xB6, 0xE5, 0x02, 0x20, 0x33, 0x30, 0x32, 0x3A, 0x20, 0x5B, 0x73, uint8_t t[] = {0x1B, 0x5B, 0xFD, 0x2D, 0x9E, 0x3A, 0xB6, 0xE5, 0x02, 0x20, 0x33, 0x30, 0x32, 0x3A, 0x20, 0x5B, 0x73,
0xFF, 0xFF, 0xCB, 0xDF, 0xB7, 0xA7, 0xB5, 0x67, 0x77, 0x77, 0xE4, 0xFF, 0xFD, 0x77, 0xFF, 0xD1}; 0xFF, 0xFF, 0xCB, 0xDF, 0xB7, 0xA7, 0xB5, 0x67, 0x77, 0x77, 0xE4, 0xFF, 0xFD, 0x77, 0xFF, 0xD1};
incoming_telegram(t71, sizeof(t71)); incoming_telegram(t, sizeof(t));
return;
}
// https://github.com/proddy/EMS-ESP/issues/380#issuecomment-633663007
if (command == "rx3") {
// incoming Rx
uint8_t t[] = {0x21, 0x0B, 0xFF, 0x00, 0xDA};
incoming_telegram(t, sizeof(t));
return; return;
} }
@@ -341,8 +349,8 @@ void EMSESP::run_test(uuid::console::Shell & shell, const std::string & command)
// testing the UART tx command, without a queue // testing the UART tx command, without a queue
if (command == "tx2") { if (command == "tx2") {
uint8_t tx[] = {0x0B, 0x88, 0x18, 0x00, 0x20, 0xD4}; // including CRC uint8_t t[] = {0x0B, 0x88, 0x18, 0x00, 0x20, 0xD4}; // including CRC
EMSuart::transmit(tx, sizeof(tx)); EMSuart::transmit(t, sizeof(t));
return; return;
} }

View File

@@ -29,6 +29,9 @@ EMSuart::EMSRxBuf_t * pEMSRxBuf;
EMSuart::EMSRxBuf_t * paEMSRxBuf[EMS_MAXBUFFERS]; EMSuart::EMSRxBuf_t * paEMSRxBuf[EMS_MAXBUFFERS];
uint8_t emsRxBufIdx = 0; uint8_t emsRxBufIdx = 0;
uint8_t phantomBreak = 0;
uint8_t tx_mode_ = EMS_TXMODE_DEFAULT;
// Main interrupt handler // Main interrupt handler
// Important: must not use ICACHE_FLASH_ATTR // Important: must not use ICACHE_FLASH_ATTR
void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) { void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) {
@@ -64,6 +67,12 @@ void ICACHE_FLASH_ATTR EMSuart::emsuart_recvTask(os_event_t * events) {
uint8_t length = pCurrent->length; // number of bytes including the BRK at the end uint8_t length = pCurrent->length; // number of bytes including the BRK at the end
pCurrent->length = 0; pCurrent->length = 0;
// LEGACY CODE
if (phantomBreak) {
phantomBreak = 0;
length--; // remove phantom break from Rx buffer
}
// 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);
@@ -81,6 +90,8 @@ void ICACHE_FLASH_ATTR EMSuart::emsuart_recvTask(os_event_t * events) {
* init UART0 driver * init UART0 driver
*/ */
void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) { void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) {
tx_mode_ = tx_mode;
// 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++) {
EMSRxBuf_t * p = (EMSRxBuf_t *)malloc(sizeof(EMSRxBuf_t)); EMSRxBuf_t * p = (EMSRxBuf_t *)malloc(sizeof(EMSRxBuf_t));
@@ -139,16 +150,163 @@ void EMSuart::send_poll(uint8_t data) {
* buf contains the CRC and len is #bytes including the CRC * buf contains the CRC and len is #bytes including the CRC
*/ */
EMSUART_STATUS ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) { EMSUART_STATUS ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
if (len) { if (len == 0) {
return EMS_TX_STATUS_OK; // nothing to send
}
// new code from Michael. See https://github.com/proddy/EMS-ESP/issues/380
if (tx_mode_ == EMS_TXMODE_NEW) {
USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear <BRK> bit USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear <BRK> bit
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];
} }
USC0(EMSUART_UART) |= (1 << UCBRK); // send <BRK> at the end USC0(EMSUART_UART) |= (1 << UCBRK); // send <BRK> at the end
}
return EMS_TX_STATUS_OK; return EMS_TX_STATUS_OK;
} }
// EMS+ https://github.com/proddy/EMS-ESP/issues/23#
if (tx_mode_ == EMS_TXMODE_EMSPLUS) { // With extra tx delay for EMS+
for (uint8_t i = 0; i < len; i++) {
USF(EMSUART_UART) = buf[i];
delayMicroseconds(EMSUART_TX_BRK_WAIT); // 2070
}
tx_brk(); // send <BRK>
return EMS_TX_STATUS_OK;
}
// Junkers logic by @philrich
if (tx_mode_ == EMS_TXMODE_HT3) {
for (uint8_t i = 0; i < len; i++) {
USF(EMSUART_UART) = buf[i];
// just to be safe wait for tx fifo empty (still needed?)
while (((USS(EMSUART_UART) >> USTXC) & 0xff))
;
// wait until bits are sent on wire
delayMicroseconds(EMSUART_TX_WAIT_BYTE - EMSUART_TX_LAG + EMSUART_TX_WAIT_GAP); // 1760
}
tx_brk(); // send <BRK>
return EMS_TX_STATUS_OK;
}
/*
* Logic for tx_mode of 0 (EMS_TXMODE_DEFAULT)
* based on code from https://github.com/proddy/EMS-ESP/issues/103 by @susisstrolch
*
* Logic:
* 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 Status to EMS_TX_WTD_TIMEOUT and return
*
* Case 2. is handled via a BRK chk during transmission.
* We set Status to EMS_TX_BRK_DETECT and return
*
*/
EMSUART_STATUS result = EMS_TX_STATUS_OK;
// disable rx interrupt
// clear Rx status register, resetting the Rx FIFO and flush it
ETS_UART_INTR_DISABLE();
USC0(EMSUART_UART) |= (1 << UCRXRST);
emsuart_flush_fifos();
// send the bytes along the serial line
for (uint8_t i = 0; i < len; i++) {
uint16_t wdc = EMS_TX_TO_COUNT; // 1760
volatile uint8_t _usrxc = (USS(EMSUART_UART) >> USRXC) & 0xFF;
USF(EMSUART_UART) = buf[i]; // send each Tx byte
// wait for echo from the busmaster
while (((USS(EMSUART_UART) >> USRXC) & 0xFF) == _usrxc) {
delayMicroseconds(EMSUART_BUSY_WAIT); // burn CPU cycles...
if (--wdc == 0) {
ETS_UART_INTR_ENABLE();
return EMS_TX_WTD_TIMEOUT;
}
if (USIR(EMSUART_UART) & (1 << UIBD)) {
USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ
ETS_UART_INTR_ENABLE();
return 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) {
// neither bus collision nor timeout - send terminating BRK signal
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_BIT_TIME);
}
USC0(EMSUART_UART) &= ~((1 << UCBRK) | (1 << UCLBE)); // disable loopback & clear <BRK>
USIC(EMSUART_UART) = (1 << UIBD); // clear BRK detect IRQ
phantomBreak = 1;
}
}
ETS_UART_INTR_ENABLE(); // open up the FIFO again to start receiving
return result; // send the Tx status back
}
/*
* flush everything left over in buffer, this clears both rx and tx FIFOs
*/
void ICACHE_FLASH_ATTR EMSuart::emsuart_flush_fifos() {
uint32_t tmp = ((1 << UCRXRST) | (1 << UCTXRST)); // bit mask
USC0(EMSUART_UART) |= (tmp); // set bits
USC0(EMSUART_UART) &= ~(tmp); // clear bits
}
/*
* Send a BRK signal
* Which is a 11-bit set of zero's (11 cycles)
*/
void ICACHE_FLASH_ATTR EMSuart::tx_brk() {
uint32_t tmp;
// must make sure Tx FIFO is empty
while (((USS(EMSUART_UART) >> USTXC) & 0xFF))
;
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);
USC0(EMSUART_UART) |= (tmp); // set bit
if (tx_mode_ == EMS_TX_WTD_TIMEOUT) { // EMS+ mode
delayMicroseconds(EMSUART_TX_BRK_WAIT);
} else if (tx_mode_ == EMS_TXMODE_HT3) { // junkers mode
delayMicroseconds(EMSUART_TX_WAIT_BRK - EMSUART_TX_LAG); // 1144 (11 Bits)
}
USC0(EMSUART_UART) &= ~(tmp); // clear bit
}
} // namespace emsesp } // namespace emsesp

View File

@@ -31,11 +31,23 @@
#define EMS_MAXBUFFERSIZE 33 // max size of the buffer. EMS packets are max 32 bytes, plus extra 2 for BRKs #define EMS_MAXBUFFERSIZE 33 // max size of the buffer. EMS packets are max 32 bytes, plus extra 2 for BRKs
#define EMSUART_recvTaskPrio 1 // 0, 1 or 2. 0 being the lowest #define EMSUART_recvTaskPrio 1 // 0, 1 or 2. 0 being the lowest
#define EMSUART_recvTaskQueueLen 10 // number of queued'd Rx triggers #define EMSUART_recvTaskQueueLen 10 // number of queued Rx triggers
#define EMS_TXMODE_DEFAULT 1 #define EMS_TXMODE_DEFAULT 1
#define EMS_TXMODE_EMSPLUS 2 #define EMS_TXMODE_EMSPLUS 2
#define EMS_TXMODE_HT3 3 #define EMS_TXMODE_HT3 3
#define EMS_TXMODE_NEW 4 // for michael
// LEGACY
#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_BUSY_WAIT (EMSUART_BIT_TIME / 8)
#define EMS_TX_TO_CHARS (2 + 20)
#define EMS_TX_TO_COUNT ((EMS_TX_TO_CHARS)*10 * 8)
namespace emsesp { namespace emsesp {
@@ -64,6 +76,9 @@ class EMSuart {
private: private:
static void ICACHE_RAM_ATTR emsuart_rx_intr_handler(void * para); static void ICACHE_RAM_ATTR 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);
static void ICACHE_FLASH_ATTR emsuart_flush_fifos();
static void ICACHE_FLASH_ATTR tx_brk();
}; };
} // namespace emsesp } // namespace emsesp

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "2.0.0a7" #define EMSESP_APP_VERSION "2.0.0a8"