Merge pull request #392 from MichaelDvP/v2

V2
This commit is contained in:
Proddy
2020-06-03 12:18:16 +02:00
committed by GitHub
23 changed files with 319 additions and 244 deletions

1
.gitignore vendored
View File

@@ -19,3 +19,4 @@ emsesp
doc/github.txt
doc/test_data.txt
/src/uart/uart_proddy.txt

View File

@@ -4,6 +4,7 @@
[platformio]
default_envs = esp8266
; default_envs = esp32
; default_envs = esp32_d1
# override any settings with your own local ones in pio_local.ini
extra_configs = pio_local.ini
@@ -21,7 +22,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 =
-D EMSESP_DEBUG
; -D EMSESP_DEBUG
; -D EMSESP_SAFE_MODE
; -D ENABLE_CORS -D CORS_ORIGIN=\"http://localhost:3000\"
@@ -62,7 +63,7 @@ check_flags =
clangtidy: --checks=-*,clang-analyzer-*,performance-*
; USB upload
; upload_protocol = esptool
;upload_protocol = esptool
; example ports for OSX
;upload_port = /dev/cu.wchusbserial14403
;upload_port = /dev/cu.usbserial-1440
@@ -73,7 +74,7 @@ upload_protocol = espota
upload_flags =
--port=8266
--auth=neo
upload_port = ems-esp.local
;upload_port = 192.168.0.20
[env:esp8266]
build_type = release
@@ -86,6 +87,7 @@ board_build.f_cpu = 160000000L ; 160MHz
board_build.ldscript = eagle.flash.4m1m.ld ; 1019 KB sketch, 1000 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 2052 KB OTA & buffer
; board_build.ldscript = eagle.flash.4m2m.ld ; 1019 KB sketch, 2024 KB SPIFFS. 4KB EEPROM, 4KB RFCAL, 12KB WIFI stack, 1028 KB OTA & buffer
build_flags = ${common.build_flags} ${common.debug_flags} -D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
upload_port = 192.168.0.20
[env:esp32]
build_type = release
@@ -94,3 +96,10 @@ board = esp32dev
lib_deps = ${common.libs_core} ${common.libs_esp32}
build_flags = ${common.build_flags} ${common.debug_flags} -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
[env:esp32_d1]
build_type = release
platform = espressif32
board = wemos_d1_mini32
lib_deps = ${common.libs_core} ${common.libs_esp32}
build_flags = ${common.build_flags} ${common.debug_flags} -D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -D WEMOS_D1_32
upload_port = 192.168.0.23

View File

@@ -49,7 +49,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
LOG_DEBUG(F("Registering new Boiler with device ID 0x%02X"), device_id);
// the telegram handlers...
register_telegram_type(0x18, F("UBAMonitorFast"), true, 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(0x34, F("UBAMonitorWW"), false, std::bind(&Boiler::process_UBAMonitorWW, this, _1));
register_telegram_type(0x1C, F("UBAMaintenanceStatus"), false, std::bind(&Boiler::process_UBAMaintenanceStatus, this, _1));
@@ -61,12 +61,12 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
register_telegram_type(0x16, F("UBAParameters"), true, std::bind(&Boiler::process_UBAParameters, this, _1));
register_telegram_type(0x1A, F("UBASetPoints"), false, std::bind(&Boiler::process_UBASetPoints, this, _1));
register_telegram_type(0xD1, F("UBAOutdoorTemp"), false, std::bind(&Boiler::process_UBAOutdoorTemp, this, _1));
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), true, std::bind(&Boiler::process_UBAMonitorFastPlus, this, _1));
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), true, std::bind(&Boiler::process_UBAMonitorSlowPlus, this, _1));
register_telegram_type(0xE4, F("UBAMonitorFastPlus"), false, std::bind(&Boiler::process_UBAMonitorFastPlus, this, _1));
register_telegram_type(0xE5, F("UBAMonitorSlowPlus"), false, std::bind(&Boiler::process_UBAMonitorSlowPlus, this, _1));
register_telegram_type(0xE9, F("UBADHWStatus"), false, std::bind(&Boiler::process_UBADHWStatus, this, _1));
register_telegram_type(0xE3, F("HeatPumpMonitor1"), true, std::bind(&Boiler::process_HPMonitor1, this, _1));
register_telegram_type(0xE5, F("HeatPumpMonitor2"), true, std::bind(&Boiler::process_HPMonitor2, this, _1));
register_telegram_type(0xE3, F("HeatPumpMonitor1"), false, std::bind(&Boiler::process_HPMonitor1, this, _1));
register_telegram_type(0xE5, F("HeatPumpMonitor2"), false, std::bind(&Boiler::process_HPMonitor2, this, _1));
// MQTT callbacks
register_mqtt_topic("boiler_cmd", std::bind(&Boiler::boiler_cmd, this, _1));
@@ -190,14 +190,17 @@ void Boiler::publish_values() {
doc["pumpMod"] = pumpMod_;
}
if (wWCircPump_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWCircPump"] = wWCircPump_;
doc["wWCircPump"] = Helpers::render_value(s, wWCircPump_, EMS_VALUE_BOOL);
}
if (wWCircPumpType_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWCiPuType"] = wWCircPumpType_;
doc["wWCiPuType"] = wWCircPumpType_ ? "valve" : "pump";
}
if (wWCircPumpMode_ != EMS_VALUE_UINT_NOTSET) {
doc["wWCiPuMode"] = wWCircPumpMode_;
}
if (wWCirc_ != EMS_VALUE_BOOL_NOTSET) {
doc["wWCirc"] = Helpers::render_value(s, wWCirc_, EMS_VALUE_BOOL);
}
if (extTemp_ != EMS_VALUE_SHORT_NOTSET) {
doc["outdoorTemp"] = (float)extTemp_ / 10;
}

View File

@@ -235,7 +235,7 @@ void Console::load_standard_commands(unsigned int context) {
}
// trace logic
if (level == uuid::log::Level::TRACE) {
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
@@ -243,6 +243,8 @@ void Console::load_standard_commands(unsigned int context) {
emsesp::EMSESP::trace_raw(true);
} else if (arguments[1] == read_flash_string(F_(full))) {
emsesp::EMSESP::trace_raw(false);
} else {
emsesp::EMSESP::trace_watch_id(Helpers::hextoint(arguments[1].c_str()));
}
// get the watch_id if its set

View File

@@ -112,6 +112,7 @@ MAKE_PSTR(deviceid_optional, "[device ID]")
MAKE_PSTR(invalid_log_level, "Invalid log level")
MAKE_PSTR(ip_address_optional, "[IP address]")
MAKE_PSTR(ip_address_mandatory, "<IP address>")
MAKE_PSTR(port_mandatory, "<port>")
MAKE_PSTR(log_level_fmt, "Log level = %s")
MAKE_PSTR(log_level_optional, "[level]")
MAKE_PSTR(name_mandatory, "<name>")

View File

@@ -24,36 +24,38 @@
*/
// Boilers - 0x08
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{133, DeviceType::BOILER, F("GB125/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{208, DeviceType::BOILER, F("Logamax plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 64, DeviceType::BOILER, F("BK13,BK15/Smartline/GB1x2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{234, DeviceType::BOILER, F("Logamax Plus GB122"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 72, DeviceType::BOILER, F("GB125/MC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 84, DeviceType::BOILER, F("Logamax Plus GB022"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 95, DeviceType::BOILER, F("Condens 2500/Logamax/Logomatic/Cerapur Top/Greenstar/Generic HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{133, DeviceType::BOILER, F("GB125/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{172, DeviceType::BOILER, F("Enviline/Compress 6000AW"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{ 72, DeviceType::BOILER, F("MC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{195, DeviceType::BOILER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{203, DeviceType::BOILER, F("Logamax U122/Cerapur"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{208, DeviceType::BOILER, F("Logamax plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{234, DeviceType::BOILER, F("Logamax Plus GB122"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Solar Modules - 0x30
{ 73, DeviceType::SOLAR, F("SM10"), DeviceFlags::EMS_DEVICE_FLAG_SM10},
{163, DeviceType::SOLAR, F("SM100"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
{164, DeviceType::SOLAR, F("SM200"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
{101, DeviceType::SOLAR, F("ISM1"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
{162, DeviceType::SOLAR, F("SM50"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
{163, DeviceType::SOLAR, F("SM100"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
{164, DeviceType::SOLAR, F("SM200"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
// Mixing Modules - 0x20-0x27 for HC, 0x28-0x29 for WWC
{160, DeviceType::MIXING, F("MM100"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{161, DeviceType::MIXING, F("MM200"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{ 69, DeviceType::MIXING, F("MM10"), DeviceFlags::EMS_DEVICE_FLAG_MM10},
{159, DeviceType::MIXING, F("MM50"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{160, DeviceType::MIXING, F("MM100"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
{161, DeviceType::MIXING, F("MM200"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
// Heat Pumps - 0x38
{252, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{200, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
{252, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Switches - 0x11
{ 71, DeviceType::SWITCH, F("WM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x11
@@ -61,36 +63,38 @@
// Controllers - 0x09 / 0x10 / 0x50
{ 68, DeviceType::CONTROLLER, F("BC10/RFM20"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{ 89, DeviceType::CONTROLLER, F("BC10 GB142"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
{190, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{ 95, DeviceType::CONTROLLER, F("HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{114, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{125, DeviceType::CONTROLLER, F("BC25"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{169, DeviceType::CONTROLLER, F("BC40"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{152, DeviceType::CONTROLLER, F("Controller"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{ 95, DeviceType::CONTROLLER, F("HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{169, DeviceType::CONTROLLER, F("BC40"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{190, DeviceType::CONTROLLER, F("BC10"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{207, DeviceType::CONTROLLER, F("Sense II/CS200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10
{209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
{241, DeviceType::CONTROLLER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
// Connect devices - 0x02
{171, DeviceType::CONNECT, F("OpenTherm Converter"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
{205, DeviceType::CONNECT, F("Moduline Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
{206, DeviceType::CONNECT, F("Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
{171, DeviceType::CONNECT, F("OpenTherm Converter"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x02
// Gateways - 0x48 / 0x18
{189, DeviceType::GATEWAY, F("KM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x48
{ 94, DeviceType::GATEWAY, F("RFM20 Remote Base for RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
{189, DeviceType::GATEWAY, F("KM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x48
// Thermostat - not currently supporting write operations, like the Easy/100 types - 0x18
{202, DeviceType::THERMOSTAT, F("Logamatic TC100/Moduline Easy"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
{203, DeviceType::THERMOSTAT, F("EasyControl CT200"), DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
// Thermostat - Common for Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18
{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_1},// 0x10 - based on RC35
{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
{ 79, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC10},// 0x17
{ 80, DeviceType::THERMOSTAT, F("Moduline 200"), DeviceFlags::EMS_DEVICE_FLAG_RC10}, // 0x17
{ 77, DeviceType::THERMOSTAT, F("RC20/Moduline 300"), DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
{ 67, DeviceType::THERMOSTAT, F("RC30"), DeviceFlags::EMS_DEVICE_FLAG_RC30_1},// 0x10 - based on RC35
{ 78, DeviceType::THERMOSTAT, F("Moduline 400"), DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
{ 86, DeviceType::THERMOSTAT, F("RC35"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
{ 93, DeviceType::THERMOSTAT, F("RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
@@ -99,7 +103,7 @@
// Thermostat - Sieger - 0x10 / 0x17
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
{113, DeviceType::THERMOSTAT, F("ES72"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17
{113, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17
// Thermostat - Junkers - 0x10
{105, DeviceType::THERMOSTAT, F("FW100"), DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},

View File

@@ -228,7 +228,7 @@ std::string EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegr
}
for (const auto & tf : telegram_functions_) {
if (tf.telegram_type_id_ == (telegram->type_id && ((telegram->type_id & 0xF0) != 0xF0))) {
if ((tf.telegram_type_id_ == telegram->type_id) && ((telegram->type_id & 0x0F0) != 0xF0)) {
return uuid::read_flash_string(tf.telegram_type_name_);
}
}

View File

@@ -189,7 +189,7 @@ void EMSESP::show_values(uuid::console::Shell & shell) {
if (!sensor_devices().empty()) {
shell.printfln(F("External temperature sensors:"));
for (const auto & device : sensor_devices()) {
shell.printfln(F(" Sensor ID %s: %s°C"), device.to_string().c_str(), Helpers::render_value(valuestr, device.temperature_c_, 2));
shell.printfln(F(" Sensor ID %s: %s°C"), device.to_string().c_str(), Helpers::render_value(valuestr, device.temperature_c_, 2));
}
shell.println();
}
@@ -247,6 +247,7 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
// get the type name, any match will do
if (type_name.empty()) {
type_name = emsdevice->telegram_type_name(telegram);
}
}
}
@@ -362,13 +363,12 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
uint8_t product_id = telegram->message_data[offset]; // product ID
// get version as XX.XX
char buf[6] = {0};
std::string version(5, '\0');
snprintf_P(&version[0],
version.capacity() + 1,
PSTR("%s.%s"),
Helpers::smallitoa(buf, telegram->message_data[offset + 1]),
Helpers::smallitoa(buf, telegram->message_data[offset + 2]));
PSTR("%02d.%02d"),
telegram->message_data[offset + 1],
telegram->message_data[offset + 2]);
// some devices store the protocol type (HT3, Buderus) in the last byte
uint8_t brand;
@@ -565,8 +565,10 @@ void EMSESP::send_write_request(const uint16_t type_id,
// we check if its a complete telegram or just a single byte (which could be a poll or a return status)
void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
// check first for echo
//LOG_TRACE(F("Rx: %s"), Helpers::data_to_hex(data, length).c_str());
uint8_t first_value = data[0];
if (((first_value & 0x7F) == txservice_.ems_bus_id()) && (length > 1)) {
rxservice_.add(data, length); // just for logging
return; // it's an echo
}
@@ -750,7 +752,7 @@ void EMSESP::console_commands(Shell & shell, unsigned int context) {
shell.printfln(F("Performing a deep scan by pinging our device library..."));
std::vector<uint8_t> Device_Ids;
Device_Ids.push_back(0x09); // Controllers - 0x09
Device_Ids.push_back(0x08); // Boilers - 0x08
Device_Ids.push_back(0x38); // HeatPump - 0x38
Device_Ids.push_back(0x30); // Solar Module - 0x30
Device_Ids.push_back(0x09); // Controllers - 0x09
@@ -758,10 +760,16 @@ void EMSESP::console_commands(Shell & shell, unsigned int context) {
Device_Ids.push_back(0x48); // Gateway - 0x48
Device_Ids.push_back(0x20); // Mixing Devices - 0x20
Device_Ids.push_back(0x21); // Mixing Devices - 0x21
Device_Ids.push_back(0x22); // Mixing Devices - 0x22
Device_Ids.push_back(0x23); // Mixing Devices - 0x23
Device_Ids.push_back(0x28); // Mixing Devices WW- 0x28
Device_Ids.push_back(0x29); // Mixing Devices WW- 0x29
Device_Ids.push_back(0x10); // Thermostats - 0x10
Device_Ids.push_back(0x17); // Thermostats - 0x17
Device_Ids.push_back(0x18); // Thermostats - 0x18
Device_Ids.push_back(0x19); // Thermostats - 0x19
Device_Ids.push_back(0x18); // Thermostat remote - 0x18
Device_Ids.push_back(0x19); // Thermostat remote - 0x19
Device_Ids.push_back(0x1A); // Thermostat remote - 0x1A
Device_Ids.push_back(0x1B); // Thermostat remote - 0x1B
Device_Ids.push_back(0x11); // Switches - 0x11
// send the read command with Version command

View File

@@ -152,7 +152,7 @@ char * Helpers::render_value(char * result, const int16_t value, const uint8_t f
result[0] = '\0';
// remove errors or invalid values, 0x7D00 and higher
if ((value == EMS_VALUE_SHORT_NOTSET) || (value == EMS_VALUE_SHORT_INVALID)) {
if ((value == EMS_VALUE_SHORT_NOTSET) || (value == EMS_VALUE_SHORT_INVALID) || (value == EMS_VALUE_USHORT_NOTSET)) {
strlcpy(result, "?", 10);
return result;
}
@@ -279,6 +279,8 @@ uint32_t Helpers::hextoint(const char * hex) {
byte = byte - 'a' + 10;
else if (byte >= 'A' && byte <= 'F')
byte = byte - 'A' + 10;
else
return 0; // error
// shift 4 to make space for new digit, and add the 4 bits of the new digit
val = (val << 4) | (byte & 0xF);
}

View File

@@ -37,7 +37,6 @@ Mixing::Mixing(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
register_telegram_type(device_id - 0x28 + 0x0331, F("MMPLUSStatusMessage_WWC"), true, std::bind(&Mixing::process_MMPLUSStatusMessage_WWC, this, _1));
}
}
// EMS 1.0
if (flags == EMSdevice::EMS_DEVICE_FLAG_MM10) {
register_telegram_type(0x00AA, F("MMConfigMessage"), false, nullptr);
@@ -77,11 +76,10 @@ void Mixing::show_values(uuid::console::Shell & shell) {
} else {
shell.printfln(F(" Heating Circuit #: %d"), hc_);
}
print_value(shell, 2, F("Current flow temperature"), F_(degrees), Helpers::render_value(buffer, flowTemp_, 10));
print_value(shell, 2, F("Setpoint flow temperature"), F_(degrees), Helpers::render_value(buffer, flowSetTemp_, 1));
print_value(shell, 2, F("Current pump modulation"), Helpers::render_value(buffer, pumpMod_, 1));
print_value(shell, 2, F("Current valve status"), Helpers::render_value(buffer, status_, 1));
print_value(shell, 4, F("Current flow temperature"), F_(degrees), Helpers::render_value(buffer, flowTemp_, 10));
print_value(shell, 4, F("Setpoint flow temperature"), F_(degrees), Helpers::render_value(buffer, flowSetTemp_, 1));
print_value(shell, 4, F("Current pump modulation"), Helpers::render_value(buffer, pumpMod_, 1));
print_value(shell, 4, F("Current valve status"), Helpers::render_value(buffer, status_, 1));
}
// publish values via MQTT
@@ -120,11 +118,10 @@ void Mixing::publish_values() {
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] Performing a mixing module publish"));
#endif
char topic[30];
char s[3]; // for formatting strings
strlcpy(topic, "mixing_data", 30);
strlcat(topic, Helpers::itoa(s, hc_), 30); // append hc to topic
strlcat(topic, Helpers::itoa(s, device_id() - 0x20 + 1), 30); // append hc to topic
Mqtt::publish(topic, doc);
}

View File

@@ -63,6 +63,7 @@ class Mixing : public EMSdevice {
uint8_t status_ = EMS_VALUE_UINT_NOTSET;
uint8_t flowSetTemp_ = EMS_VALUE_UINT_NOTSET;
Type type_ = Type::NONE;
};
} // namespace emsesp

View File

@@ -24,9 +24,11 @@ MAKE_PSTR_WORD(qos)
MAKE_PSTR_WORD(base)
MAKE_PSTR_WORD(heartbeat)
MAKE_PSTR_WORD(ip)
MAKE_PSTR_WORD(port)
MAKE_PSTR_WORD(nested)
MAKE_PSTR_WORD(single)
MAKE_PSTR_WORD(ha)
MAKE_PSTR_WORD(custom)
MAKE_PSTR_WORD(publish_time)
MAKE_PSTR_WORD(publish)
MAKE_PSTR_WORD(connected)
@@ -646,6 +648,8 @@ void Mqtt::console_commands(Shell & shell, unsigned int context) {
value = Settings::MQTT_format::NESTED;
} else if (arguments[0] == read_flash_string(F_(ha))) {
value = Settings::MQTT_format::HA;
} else if (arguments[0] == read_flash_string(F_(custom))) {
value = Settings::MQTT_format::CUSTOM;
} else {
shell.println(F("Must be single, nested or ha"));
return;
@@ -655,7 +659,7 @@ void Mqtt::console_commands(Shell & shell, unsigned int context) {
shell.println(F("Please restart EMS-ESP"));
},
[](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_(single)), read_flash_string(F_(nested)), read_flash_string(F_(ha))};
return std::vector<std::string>{read_flash_string(F_(single)), read_flash_string(F_(nested)), read_flash_string(F_(ha)), read_flash_string(F_(custom))};
});
EMSESPShell::commands->add_command(ShellContext::MQTT,
@@ -723,6 +727,19 @@ void Mqtt::console_commands(Shell & shell, unsigned int context) {
reconnect();
});
EMSESPShell::commands->add_command(ShellContext::MQTT,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(port)},
flash_string_vector{F_(port_mandatory)},
[](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
Settings settings;
if (!arguments.empty()) {
settings.mqtt_port(atoi(arguments.front().c_str()));
settings.commit();
}
reconnect();
});
EMSESPShell::commands->add_command(ShellContext::MQTT,
CommandFlags::ADMIN,
flash_string_vector{F_(set), F_(qos)},
@@ -820,6 +837,8 @@ void Mqtt::console_commands(Shell & shell, unsigned int context) {
shell.printfln(F_(mqtt_format_fmt), F_(nested));
} else if (settings.mqtt_format() == Settings::MQTT_format::HA) {
shell.printfln(F_(mqtt_format_fmt), F_(ha));
} else if (settings.mqtt_format() == Settings::MQTT_format::CUSTOM) {
shell.printfln(F_(mqtt_format_fmt), F_(custom));
}
shell.printfln(F_(mqtt_heartbeat_fmt), settings.mqtt_heartbeat() ? F_(enabled) : F_(disabled));
shell.printfln(F_(mqtt_publish_time_fmt), settings.mqtt_publish_time());

View File

@@ -222,7 +222,6 @@ uint64_t Sensors::Device::id() const {
std::string Sensors::Device::to_string() const {
std::string str(20, '\0');
snprintf_P(&str[0],
str.capacity() + 1,
PSTR("%02X-%04X-%04X-%04X-%02X"),
@@ -234,6 +233,7 @@ std::string Sensors::Device::to_string() const {
return str;
}
// send all dallas sensor values as a JSON package to MQTT
// assumes there are devices
void Sensors::publish_values() {
@@ -274,13 +274,18 @@ void Sensors::publish_values() {
uint8_t i = 1;
for (const auto & device : devices_) {
char sensorID[10]; // sensor{1-n}
strlcpy(sensorID, "sensor", 10);
char s[5];
strlcat(sensorID, Helpers::itoa(s, i++), 10);
JsonObject dataSensor = doc.createNestedObject(sensorID);
dataSensor["id"] = device.to_string();
dataSensor["temp"] = Helpers::render_value(s, device.temperature_c_, 2);
if (mqtt_format_ == Settings::MQTT_format::CUSTOM) {
char s[5];
doc[device.to_string()] = Helpers::render_value(s, device.temperature_c_, 2);
} else {
char sensorID[10]; // sensor{1-n}
strlcpy(sensorID, "sensor", 10);
char s[5];
strlcat(sensorID, Helpers::itoa(s, i++), 10);
JsonObject dataSensor = doc.createNestedObject(sensorID);
dataSensor["id"] = device.to_string();
dataSensor["temp"] = Helpers::render_value(s, device.temperature_c_, 2);
}
}
if (mqtt_format_ == Settings::MQTT_format::HA) {

View File

@@ -66,7 +66,11 @@ class Sensors {
#if defined(ESP8266)
static constexpr uint8_t SENSOR_GPIO = 14; // D5
#elif defined(ESP32)
#ifdef WEMOS_D1_32
static constexpr uint8_t SENSOR_GPIO = 18; // Wemos D1-32 for compatibility D5
#else
static constexpr uint8_t SENSOR_GPIO = 14;
#endif
#endif
enum class State { IDLE, READING, SCANNING };

View File

@@ -172,9 +172,9 @@ void Solar::process_SM100Status2(std::shared_ptr<const Telegram> telegram) {
* e.g. 30 00 FF 00 02 8E 00 00 00 00 00 00 06 C5 00 00 76 35
*/
void Solar::process_SM100Energy(std::shared_ptr<const Telegram> telegram) {
telegram->read_value(energyLastHour_, 0); // last hour / 10 in Wh
telegram->read_value(energyToday_, 4); // todays in Wh
telegram->read_value(energyTotal_, 8); // total / 10 in kWh
telegram->read_value32(energyLastHour_, 0); // last hour / 10 in Wh
telegram->read_value32(energyToday_, 4); // todays in Wh
telegram->read_value32(energyTotal_, 8); // total / 10 in kWh
}
/*

View File

@@ -83,8 +83,13 @@ class System {
static constexpr uint8_t LED_GPIO = 2;
static constexpr uint8_t LED_ON = LOW;
#elif defined(ESP32)
static constexpr uint8_t LED_GPIO = 5; // 5 on Lolin D32, 2 on Wemos D1-32 mini. Use 0 for off.
static constexpr uint8_t LED_ON = LOW; // LOW on Lolin D32, HIGH on Wemos D1-32 mini
#ifdef WEMOS_D1_32
static constexpr uint8_t LED_GPIO = 2; // on Wemos D1-32
static constexpr uint8_t LED_ON = HIGH;
#else
static constexpr uint8_t LED_GPIO = 5;
static constexpr uint8_t LED_ON = LOW;
#endif
#else
static constexpr uint8_t LED_GPIO = 0;
static constexpr uint8_t LED_ON = 0;

View File

@@ -104,13 +104,13 @@ std::string Telegram::to_string(const uint8_t * telegram, uint8_t length) const
// if offset is 0, it takes the whole telegram. if it's for example 1 it'll show the 2nd data item and
// everything after it
// returns -1 if out of bounds
int8_t Telegram::_getDataPosition(const uint8_t index) const {
return ((index - offset) >= message_length) ? -1 : (index - offset);
int8_t Telegram::_getDataPosition(const uint8_t index, const uint8_t size) const {
return ((index - offset + size - 1) >= message_length) ? -1 : (index - offset);
}
// unsigned byte
void Telegram::read_value(uint8_t & param, const uint8_t index) const {
int8_t pos = _getDataPosition(index);
int8_t pos = _getDataPosition(index, sizeof(param));
if (pos < 0) {
return;
}
@@ -119,7 +119,7 @@ void Telegram::read_value(uint8_t & param, const uint8_t index) const {
// signed byte
void Telegram::read_value(int8_t & param, const uint8_t index) const {
int8_t pos = _getDataPosition(index);
int8_t pos = _getDataPosition(index, sizeof(param));
if (pos < 0) {
return;
}
@@ -128,7 +128,7 @@ void Telegram::read_value(int8_t & param, const uint8_t index) const {
// unsigned short
void Telegram::read_value(uint16_t & param, const uint8_t index) const {
int8_t pos = _getDataPosition(index);
int8_t pos = _getDataPosition(index, sizeof(param));
if (pos < 0) {
return;
}
@@ -145,7 +145,7 @@ void Telegram::read_value(uint16_t & param, const uint8_t index) const {
// signed short
void Telegram::read_value(int16_t & param, const uint8_t index) const {
int8_t pos = _getDataPosition(index);
int8_t pos = _getDataPosition(index, sizeof(param));
if (pos < 0) {
return;
}
@@ -160,9 +160,9 @@ void Telegram::read_value(int16_t & param, const uint8_t index) const {
param = value;
}
// Long
// Long 24 bit
void Telegram::read_value(uint32_t & param, const uint8_t index) const {
int8_t pos = _getDataPosition(index);
int8_t pos = _getDataPosition(index, 3);
if (pos < 0) {
return;
}
@@ -170,9 +170,19 @@ void Telegram::read_value(uint32_t & param, const uint8_t index) const {
param = (uint32_t)((message_data[pos] << 16) + (message_data[pos + 1] << 8) + (message_data[pos + 2]));
}
// Long 32 bit
void Telegram::read_value32(uint32_t & param, const uint8_t index) const {
int8_t pos = _getDataPosition(index, sizeof(param));
if (pos < 0) {
return;
}
param = (uint32_t)((message_data[pos] << 24) + (message_data[pos] << 16) + (message_data[pos + 1] << 8) + (message_data[pos + 2]));
}
// bit from an unsigned byte
void Telegram::read_value(uint8_t & param, const uint8_t index, const uint8_t bit) const {
int8_t pos = _getDataPosition(index);
int8_t pos = _getDataPosition(index, sizeof(param));
if (pos < 0) {
return;
}
@@ -182,7 +192,7 @@ void Telegram::read_value(uint8_t & param, const uint8_t index, const uint8_t bi
// convert signed short to single 8 byte, for setpoint thermostat temperatures that don't store their temps in 2 bytes
void Telegram::read_value8(int16_t & param, const uint8_t index) const {
int8_t pos = _getDataPosition(index);
int8_t pos = _getDataPosition(index, 1);
if (pos < 0) {
return;
}
@@ -212,10 +222,10 @@ void RxService::start() {
void RxService::loop() {
#ifndef EMSESP_STANDALONE
// give rx some breathing space
if ((uuid::get_uptime() - last_rx_check_) < RX_LOOP_WAIT) {
return;
}
last_rx_check_ = uuid::get_uptime();
//if ((uuid::get_uptime() - last_rx_check_) < RX_LOOP_WAIT) {
// return;
//}
//last_rx_check_ = uuid::get_uptime();
#endif
while (!rx_telegrams_.empty()) {
@@ -270,7 +280,7 @@ void RxService::add(uint8_t * data, uint8_t length) {
uint8_t message_length;
// work out depending on the type where the data message block starts
if (data[2] < 0xF0) {
if (data[2] < 0xF0 || length < 6) {
// EMS 1.0
type_id = data[2];
message_data = data + 4; // message block starts at 5th byte
@@ -365,6 +375,7 @@ void TxService::loop() {
// sends a 1 byte poll which is our own device ID
void TxService::send_poll() {
//LOG_TRACE(F("Ack %02X"),ems_bus_id() ^ ems_mask());
EMSuart::send_poll(ems_bus_id() ^ ems_mask());
}
@@ -473,7 +484,7 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
// send the telegram to the UART Tx
EMSUART_STATUS status = EMSuart::transmit(telegram_raw, length);
//LOG_TRACE(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. Error: %s"), status == EMS_TX_WTD_TIMEOUT ? F("Timeout") : F("BRK"));
}
@@ -496,6 +507,7 @@ void TxService::send_telegram(const uint8_t * data, const uint8_t length) {
// send the telegram to the UART Tx
EMSUART_STATUS status = EMSuart::transmit(telegram_raw, length);
//LOG_TRACE(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. Error: %s"), status == EMS_TX_WTD_TIMEOUT ? F("Timeout") : F("BRK"));
}

View File

@@ -76,6 +76,7 @@ class Telegram {
void read_value(uint16_t & param, const uint8_t index) const;
void read_value(uint32_t & param, const uint8_t index) const;
void read_value32(uint32_t & param, const uint8_t index) const;
void read_value(uint8_t & param, const uint8_t index, const uint8_t bit) const;
void read_value(uint8_t & param, const uint8_t index) const;
void read_value(int16_t & param, const uint8_t index) const;
@@ -83,7 +84,7 @@ class Telegram {
void read_value(int8_t & param, const uint8_t index) const;
private:
int8_t _getDataPosition(const uint8_t index) const;
int8_t _getDataPosition(const uint8_t index, const uint8_t size) const;
};
class EMSbus {

View File

@@ -400,7 +400,8 @@ bool Thermostat::updated_values() {
static uint16_t current_value_ = 0;
for (const auto & hc : heating_circuits_) {
// don't publish if we haven't yet received some data
if ((hc->setpoint_roomTemp == EMS_VALUE_SHORT_NOTSET) || (hc->curr_roomTemp == EMS_VALUE_SHORT_NOTSET)) {
// if ((hc->setpoint_roomTemp == EMS_VALUE_SHORT_NOTSET) || (hc->curr_roomTemp == EMS_VALUE_SHORT_NOTSET)) {
if (hc->setpoint_roomTemp == EMS_VALUE_SHORT_NOTSET) {
return false;
}
new_value += hc->setpoint_roomTemp + hc->curr_roomTemp + hc->mode;
@@ -445,15 +446,12 @@ void Thermostat::publish_values() {
if (tempsensor2 != EMS_VALUE_USHORT_NOTSET) {
rootThermostat["inttemp2"] = (float)tempsensor2 / 10;
}
if (ibaCalIntTemperature != EMS_VALUE_INT_NOTSET) {
rootThermostat["intoffset"] = (float)ibaCalIntTemperature / 2;
}
if (ibaMinExtTemperature != EMS_VALUE_INT_NOTSET) {
rootThermostat["minexttemp"] = (float)ibaMinExtTemperature; // min ext temp for heating curve, in deg.
}
if (ibaBuildingType != EMS_VALUE_UINT_NOTSET) {
if (ibaBuildingType == 0) {
rootThermostat["building"] = "light";
@@ -463,7 +461,6 @@ void Thermostat::publish_values() {
rootThermostat["building"] = "heavy";
}
}
if (mqtt_format_ == Settings::MQTT_format::SINGLE) {
Mqtt::publish("thermostat_data", doc);
rootThermostat = doc.to<JsonObject>(); // clear object
@@ -472,8 +469,9 @@ void Thermostat::publish_values() {
// go through all the heating circuits
for (const auto & hc : heating_circuits_) {
if ((hc->setpoint_roomTemp == EMS_VALUE_SHORT_NOTSET) || (hc->curr_roomTemp == EMS_VALUE_SHORT_NOTSET)) {
break; // skip this HC as we don't have the temperature values yet
// if ((hc->setpoint_roomTemp == EMS_VALUE_SHORT_NOTSET) || (hc->curr_roomTemp == EMS_VALUE_SHORT_NOTSET)) {
if (hc->setpoint_roomTemp == EMS_VALUE_SHORT_NOTSET) {
break; // skip this HC
}
has_data = true;
@@ -506,7 +504,8 @@ void Thermostat::publish_values() {
if (hc->setpoint_roomTemp != EMS_VALUE_SHORT_NOTSET) {
dataThermostat["seltemp"] = Helpers::round2((float)hc->setpoint_roomTemp / setpoint_temp_divider);
}
if (hc->curr_roomTemp != EMS_VALUE_SHORT_NOTSET) {
if (hc->curr_roomTemp != EMS_VALUE_SHORT_NOTSET && hc->curr_roomTemp != EMS_VALUE_USHORT_NOTSET) {
dataThermostat["currtemp"] = Helpers::round2((float)hc->curr_roomTemp / curr_temp_divider);
}
@@ -555,10 +554,10 @@ void Thermostat::publish_values() {
// special handling of mode type, for the RC35 replace with summer/holiday
// https://github.com/proddy/EMS-ESP/issues/373#issuecomment-619810209
if ((flags & 0x0F) == EMS_DEVICE_FLAG_RC35) {
if (hc->holiday_mode != EMS_VALUE_UINT_NOTSET) {
dataThermostat["modetype"] = F("holiday");
} else if (hc->summer_mode != EMS_VALUE_UINT_NOTSET) {
if (hc->summer_mode) {
dataThermostat["modetype"] = F("summer");
} else if (hc->holiday_mode) {
dataThermostat["modetype"] = F("holiday");
} else if (hc->mode_type != EMS_VALUE_UINT_NOTSET) {
dataThermostat["modetype"] = mode_tostring(hc->get_mode_type(flags));
}
@@ -586,6 +585,8 @@ void Thermostat::publish_values() {
Mqtt::publish("thermostat_data", doc);
} else if (mqtt_format_ == Settings::MQTT_format::HA) {
Mqtt::publish("homeassistant/climate/ems-esp/state", doc);
} else if (mqtt_format_ == Settings::MQTT_format::CUSTOM) {
Mqtt::publish("thermostat_data", doc);
}
}
@@ -775,25 +776,80 @@ std::string Thermostat::mode_tostring(uint8_t mode) const {
void Thermostat::show_values(uuid::console::Shell & shell) {
EMSdevice::show_values(shell); // always call this to show header
char buffer[10]; // for formatting only
char buffer[10]; // for formatting only
uint8_t flags = (this->flags() & 0x0F); // specific thermostat characteristics, strip the option bits
if (datetime_.size()) {
shell.printfln(F(" Clock: %s"), datetime_.c_str());
if (ibaClockOffset != EMS_VALUE_UINT_NOTSET) {
print_value(shell, 1, F("Offset clock"), Helpers::render_value(buffer, ibaClockOffset, 1)); // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s
shell.printfln(F(" Clock: %s"), datetime_.c_str());
if (ibaClockOffset != EMS_VALUE_UINT_NOTSET && flags == EMS_DEVICE_FLAG_RC30_1) {
print_value(shell, 2, F("Offset clock"), Helpers::render_value(buffer, ibaClockOffset, 1)); // offset (in sec) to clock, 0xff = -1 s, 0x02 = 2 s
}
}
uint8_t flags = (this->flags() & 0x0F); // specific thermostat characteristics, strip the option bits
if (flags == EMS_DEVICE_FLAG_RC35) {
print_value(shell, 1, F("Damped Outdoor temperature"), F_(degrees), Helpers::render_value(buffer, dampedoutdoortemp, 1));
print_value(shell, 1, F("Tempsensor 1"), F_(degrees), Helpers::render_value(buffer, tempsensor1, 10));
print_value(shell, 1, F("Tempsensor 2"), F_(degrees), Helpers::render_value(buffer, tempsensor2, 10));
print_value(shell, 2, F("Damped Outdoor temperature"), F_(degrees), Helpers::render_value(buffer, dampedoutdoortemp, 1));
print_value(shell, 2, F("Tempsensor 1"), F_(degrees), Helpers::render_value(buffer, tempsensor1, 10));
print_value(shell, 2, F("Tempsensor 2"), F_(degrees), Helpers::render_value(buffer, tempsensor2, 10));
}
if (flags == EMS_DEVICE_FLAG_RC30_1) {
// settings parameters
if (ibaMainDisplay != EMS_VALUE_UINT_NOTSET) {
if (ibaMainDisplay == 0) {
shell.printfln(F(" Display: internal temperature"));
} else if (ibaMainDisplay == 1) {
shell.printfln(F(" Display: internal setpoint"));
} else if (ibaMainDisplay == 2) {
shell.printfln(F(" Display: external temperature"));
} else if (ibaMainDisplay == 3) {
shell.printfln(F(" Display: burner temperature"));
} else if (ibaMainDisplay == 4) {
shell.printfln(F(" Display: WW temperature"));
} else if (ibaMainDisplay == 5) {
shell.printfln(F(" Display: functioning mode"));
} else if (ibaMainDisplay == 6) {
shell.printfln(F(" Display: time"));
} else if (ibaMainDisplay == 7) {
shell.printfln(F(" Display: date"));
} else if (ibaMainDisplay == 9) {
shell.printfln(F(" Display: smoke temperature"));
}
}
if (ibaLanguage != EMS_VALUE_UINT_NOTSET) {
if (ibaLanguage == 0) {
shell.printfln(F(" Language: German"));
} else if (ibaLanguage == 1) {
shell.printfln(F(" Language: Dutch"));
} else if (ibaLanguage == 2) {
shell.printfln(F(" Language: French"));
} else if (ibaLanguage == 3) {
shell.printfln(F(" Language: Italian"));
}
}
}
if (flags == EMS_DEVICE_FLAG_RC35 || flags == EMS_DEVICE_FLAG_RC30_1) {
if (ibaCalIntTemperature != EMS_VALUE_INT_NOTSET) {
print_value(shell, 2, F("Offset int. temperature"), F_(degrees), Helpers::render_value(buffer, ibaCalIntTemperature, 2));
}
if (ibaMinExtTemperature != EMS_VALUE_INT_NOTSET) {
print_value(shell, 2, F("Min ext. temperature"), F_(degrees), Helpers::render_value(buffer, ibaMinExtTemperature, 0)); // min ext temp for heating curve, in deg.
}
if (ibaBuildingType != EMS_VALUE_UINT_NOTSET) {
if (ibaBuildingType == 0) {
shell.printfln(F(" Building: light"));
} else if (ibaBuildingType == 1) {
shell.printfln(F(" Building: medium"));
} else if (ibaBuildingType == 2) {
shell.printfln(F(" Building: heavy"));
}
}
}
for (const auto & hc : heating_circuits_) {
shell.printfln(F(" Heating Circuit %d:"), hc->hc_num());
shell.printfln(F(" Heating Circuit %d:"), hc->hc_num());
// different thermostat types store their temperature values differently
uint8_t format_setpoint, format_curr;
@@ -812,13 +868,13 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
break;
}
print_value(shell, 2, F("Current room temperature"), F_(degrees), Helpers::render_value(buffer, hc->curr_roomTemp, format_curr));
print_value(shell, 2, F("Setpoint room temperature"), F_(degrees), Helpers::render_value(buffer, hc->setpoint_roomTemp, format_setpoint));
print_value(shell, 4, F("Current room temperature"), F_(degrees), Helpers::render_value(buffer, hc->curr_roomTemp, format_curr));
print_value(shell, 4, F("Setpoint room temperature"), F_(degrees), Helpers::render_value(buffer, hc->setpoint_roomTemp, format_setpoint));
if (hc->mode != EMS_VALUE_UINT_NOTSET) {
print_value(shell, 2, F("Mode"), mode_tostring(hc->get_mode(flags)).c_str());
print_value(shell, 4, F("Mode"), mode_tostring(hc->get_mode(flags)).c_str());
}
if (hc->mode_type != EMS_VALUE_UINT_NOTSET) {
print_value(shell, 2, F("Mode Type"), mode_tostring(hc->get_mode_type(flags)).c_str());
print_value(shell, 4, F("Mode Type"), mode_tostring(hc->get_mode_type(flags)).c_str());
}
if ((flags == EMS_DEVICE_FLAG_RC35) || (flags == EMS_DEVICE_FLAG_RC30_1)) {
@@ -828,72 +884,19 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
shell.printfln(F(" Program is set to Holiday mode"));
}
print_value(shell, 2, F("Day temperature"), F_(degrees), Helpers::render_value(buffer, hc->daytemp, 2));
print_value(shell, 2, F("Night temperature"), F_(degrees), Helpers::render_value(buffer, hc->nighttemp, 2));
print_value(shell, 2, F("Vacation temperature"), F_(degrees), Helpers::render_value(buffer, hc->holidaytemp, 2));
print_value(shell, 4, F("Day temperature"), F_(degrees), Helpers::render_value(buffer, hc->daytemp, 2));
print_value(shell, 4, F("Night temperature"), F_(degrees), Helpers::render_value(buffer, hc->nighttemp, 2));
print_value(shell, 4, F("Holiday temperature"), F_(degrees), Helpers::render_value(buffer, hc->holidaytemp, 2));
if (hc->offsettemp < 100) {
print_value(shell, 2, F("Offset temperature"), F_(degrees), Helpers::render_value(buffer, hc->offsettemp, 2));
print_value(shell, 4, F("Offset temperature"), F_(degrees), Helpers::render_value(buffer, hc->offsettemp, 2));
}
print_value(shell, 2, F("Design temperature"), F_(degrees), Helpers::render_value(buffer, hc->designtemp, 2));
print_value(shell, 4, F("Design temperature"), F_(degrees), Helpers::render_value(buffer, hc->designtemp, 0));
}
// show flow temp if we have it
if (hc->circuitcalctemp != EMS_VALUE_UINT_NOTSET) {
print_value(shell, 2, F("Calculated flow temperature"), F_(degrees), Helpers::render_value(buffer, hc->circuitcalctemp, 1));
}
// settings parameters
if (ibaMainDisplay != EMS_VALUE_UINT_NOTSET) {
if (ibaMainDisplay == 0) {
shell.printfln(F(" Display: internal temperature"));
} else if (ibaMainDisplay == 1) {
shell.printfln(F(" Display: internal setpoint"));
} else if (ibaMainDisplay == 2) {
shell.printfln(F(" Display: external temperature"));
} else if (ibaMainDisplay == 3) {
shell.printfln(F(" Display: burner temperature"));
} else if (ibaMainDisplay == 4) {
shell.printfln(F(" Display: WW temperature"));
} else if (ibaMainDisplay == 5) {
shell.printfln(F(" Display: functioning mode"));
} else if (ibaMainDisplay == 6) {
shell.printfln(F(" Display: time"));
} else if (ibaMainDisplay == 7) {
shell.printfln(F(" Display: date"));
} else if (ibaMainDisplay == 9) {
shell.printfln(F(" Display: smoke temperature"));
}
}
if (ibaLanguage != EMS_VALUE_UINT_NOTSET) {
if (ibaLanguage == 0) {
shell.printfln(F(" Language: German"));
} else if (ibaLanguage == 1) {
shell.printfln(F(" Language: Dutch"));
} else if (ibaLanguage == 2) {
shell.printfln(F(" Language: French"));
} else if (ibaLanguage == 3) {
shell.printfln(F(" Language: Italian"));
}
}
if (ibaCalIntTemperature != EMS_VALUE_INT_NOTSET) {
print_value(shell, 2, F("Offset int. temperature"), F_(degrees), Helpers::render_value(buffer, ibaCalIntTemperature, 2));
}
if (ibaMinExtTemperature != EMS_VALUE_INT_NOTSET) {
print_value(shell, 2, F("Min ext. temperature"), F_(degrees), Helpers::render_value(buffer, ibaMinExtTemperature, 10)); // min ext temp for heating curve, in deg.
}
if (ibaBuildingType != EMS_VALUE_UINT_NOTSET) {
if (ibaBuildingType == 0) {
shell.printfln(F(" Building: light"));
} else if (ibaBuildingType == 1) {
shell.printfln(F(" Building: medium"));
} else if (ibaBuildingType == 2) {
shell.printfln(F(" Building: heavy"));
}
print_value(shell, 4, F("Calculated flow temperature"), F_(degrees), Helpers::render_value(buffer, hc->circuitcalctemp, 1));
}
}
}

View File

@@ -29,9 +29,12 @@
namespace emsesp {
static intr_handle_t uart_handle;
static RingbufHandle_t buf_handle = NULL;
static bool drop_first_rx = true;
static uint8_t tx_mode_ = 0xFF;
static RingbufHandle_t buf_handle = NULL;
static bool drop_next_rx = true;
static uint8_t tx_mode_ = 0xFF;
static uint32_t emsRxTime;
#define EMS_RX_TO_TX_TIMEOUT 20
/*
* Task to handle the incoming data
@@ -46,7 +49,7 @@ void EMSuart::emsuart_recvTask(void * para) {
EMSESP::incoming_telegram(telegram, telegramSize);
vRingbufferReturnItem(buf_handle, (void *)telegram);
}
}
}
}
/*
* UART interrupt, on break read the fifo and put the whole telegram to ringbuffer
@@ -57,19 +60,21 @@ void IRAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) {
if (EMS_UART.int_st.brk_det) {
EMS_UART.int_clr.brk_det = 1; // clear flag
EMS_UART.conf0.txd_brk = 0; // if it was break from sending, clear bit
length = 0;
length = 0;
while (EMS_UART.status.rxfifo_cnt) {
uint8_t rx = EMS_UART.fifo.rw_byte; // read all bytes into buffer
uint8_t rx = EMS_UART.fifo.rw_byte; // read all bytes from fifo
if (length < EMS_MAXBUFFERSIZE) {
rxbuf[length++] = rx;
} else {
drop_next_rx = true; // we have a overflow
}
}
if ((!drop_first_rx) && ((length == 2) || ((length > 4)))) {
if ((!drop_next_rx) && ((length == 2) || (length > 4))) {
int baseType = 0;
xRingbufferSendFromISR(buf_handle, rxbuf, length - 1, &baseType);
emsRxTime = millis();
}
drop_first_rx = false;
drop_next_rx = false;
}
}
/*
@@ -80,9 +85,7 @@ void EMSuart::start(uint8_t tx_mode) {
restart();
return;
}
tx_mode_ = tx_mode;
uart_config_t uart_config = {
.baud_rate = EMSUART_BAUD,
.data_bits = UART_DATA_8_BITS,
@@ -93,17 +96,14 @@ void EMSuart::start(uint8_t tx_mode) {
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));
EMS_UART.int_ena.val = 0; // disable all intr.
EMS_UART.int_ena.val = 0; // disable all intr.
EMS_UART.int_clr.val = 0xFFFFFFFF; // clear all intr. flags
EMS_UART.idle_conf.tx_brk_num = 11; // breaklength 11 bit
EMS_UART.idle_conf.rx_idle_thrhd = 12;
drop_first_rx = true;
buf_handle = xRingbufferCreate(128, RINGBUF_TYPE_NOSPLIT);
EMS_UART.idle_conf.tx_brk_num = 11; // breaklength 11 bit
EMS_UART.idle_conf.rx_idle_thrhd = 256;
drop_next_rx = true;
buf_handle = xRingbufferCreate(128, RINGBUF_TYPE_NOSPLIT);
ESP_ERROR_CHECK(uart_isr_register(EMSUART_UART, emsuart_rx_intr_handler, NULL, ESP_INTR_FLAG_IRAM, &uart_handle));
xTaskCreate(emsuart_recvTask, "emsuart_recvTask", 2048, NULL, 12, NULL);
EMS_UART.int_ena.brk_det = 1; // activate only break
}
@@ -119,8 +119,8 @@ void EMSuart::stop() {
*/
void EMSuart::restart() {
if (EMS_UART.int_raw.brk_det) {
EMS_UART.int_clr.brk_det = 1; // clear flag
drop_first_rx = true; // and drop first frame
EMS_UART.int_clr.brk_det = 1; // clear flag
drop_next_rx = true; // and drop first frame
}
EMS_UART.int_ena.brk_det = 1; // activate only break
};
@@ -129,10 +129,8 @@ void EMSuart::restart() {
* Sends a 1-byte poll, ending with a <BRK>
*/
void EMSuart::send_poll(uint8_t data) {
EMS_UART.conf0.txd_brk = 0; // just to make sure the bit is cleared
EMS_UART.fifo.rw_byte = data;
//EMS_UART.idle_conf.tx_brk_num = 11; // breaklength 11 bit
EMS_UART.conf0.txd_brk = 1; // sending ends in a break
EMS_UART.conf0.txd_brk = 1; // <brk> after send
}
/*
@@ -141,14 +139,14 @@ void EMSuart::send_poll(uint8_t data) {
* returns code, 1=success
*/
EMSUART_STATUS EMSuart::transmit(uint8_t * buf, uint8_t len) {
if (millis() -emsRxTime > EMS_RX_TO_TX_TIMEOUT) {
return EMS_TX_WTD_TIMEOUT;
}
if (len > 0) {
EMS_UART.conf0.txd_brk = 0; // just to make sure the bit is cleared
for (uint8_t i = 0; i < len; i++) {
EMS_UART.fifo.rw_byte = buf[i];
}
//uart_tx_chars(EMSUART_UART, (const char *)buf, len);
//EMS_UART.idle_conf.tx_brk_num = 11; // breaklength 11 bit
EMS_UART.conf0.txd_brk = 1; // sending ends in a break
EMS_UART.conf0.txd_brk = 1; // <brk> after send
}
return EMS_TX_STATUS_OK;
}

View File

@@ -39,8 +39,13 @@
#define EMSUART_BAUD 9600 // uart baud rate for the EMS circuit
// customize the GPIO pins for RX and TX here
#ifdef WEMOS_D1_32
#define EMSUART_RXPIN 23 // 17 is UART2 RX. Use 23 for D7 on a Wemos D1-32 mini for backwards compatabilty
#define EMSUART_TXPIN 5 // 16 is UART2 TX. Use 5 for D8 on a Wemos D1-32 mini for backwards compatabilty
#define EMSUART_TXPIN 5 // 16 is UART2 TX. Use 5 for D8 on a Wemos D1-32 mini for backwards compatabilty
#else
#define EMSUART_RXPIN 17 // 17 is UART2 RX. Use 23 for D7 on a Wemos D1-32 mini for backwards compatabilty
#define EMSUART_TXPIN 16 // 16 is UART2 TX. Use 5 for D8 on a Wemos D1-32 mini for backwards compatabilty
#endif
namespace emsesp {

View File

@@ -28,10 +28,13 @@ os_event_t recvTaskQueue[EMSUART_recvTaskQueueLen]; // our Rx queue
EMSuart::EMSRxBuf_t * pEMSRxBuf;
EMSuart::EMSRxBuf_t * paEMSRxBuf[EMS_MAXBUFFERS];
uint8_t emsRxBufIdx = 0;
uint8_t phantomBreak = 0;
uint8_t tx_mode_ = 0xFF;
bool drop_first_rx = true;
uint8_t emsRxBufIdx = 0;
uint8_t phantomBreak = 0;
uint8_t tx_mode_ = 0xFF;
bool drop_next_rx = true;
uint32_t emsRxTime;
#define EMS_RX_TO_TX_TIMEOUT 20
//
// Main interrupt handler
@@ -39,47 +42,26 @@ bool drop_first_rx = true;
//
void ICACHE_RAM_ATTR EMSuart::emsuart_rx_intr_handler(void * para) {
static uint8_t length = 0;
// static bool rx_idle_ = true;
static uint8_t uart_buffer[EMS_MAXBUFFERSIZE + 2];
/*
// is a new buffer? if so init the thing for a new telegram
if (rx_idle_) {
rx_idle_ = false; // status set to busy
length = 0;
}
// fill IRQ buffer, by emptying Rx FIFO
if (USIS(EMSUART_UART) & ((1 << UIFF) | (1 << UITO) | (1 << UIBD))) {
while ((USS(EMSUART_UART) >> USRXC) & 0xFF) {
uint8_t rx = USF(EMSUART_UART);
if (length < EMS_MAXBUFFERSIZE)
uart_buffer[length++] = rx;
}
// clear Rx FIFO full and Rx FIFO timeout interrupts
USIC(EMSUART_UART) = (1 << UIFF) | (1 << UITO);
}
*/
// BREAK detection = End of EMS data block
if (USIS(EMSUART_UART) & ((1 << UIBD))) {
if (USIS(EMSUART_UART) & ((1 << UIBD))) { // BREAK detection = End of EMS data block
length = 0;
while ((USS(EMSUART_UART) >> USRXC) & 0xFF) {
while ((USS(EMSUART_UART) >> USRXC) & 0xFF) { // read fifo into buffer
uint8_t rx = USF(EMSUART_UART);
if (length < EMS_MAXBUFFERSIZE) {
uart_buffer[length++] = rx;
} else {
drop_next_rx = true;
}
}
USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear <BRK> bit
ETS_UART_INTR_DISABLE(); // disable all interrupts and clear them
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt
if (!drop_first_rx) {
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt
USC0(EMSUART_UART) &= ~(1 << UCBRK); // reset tx-brk
if (!drop_next_rx) {
pEMSRxBuf->length = length;
os_memcpy((void *)pEMSRxBuf->buffer, (void *)&uart_buffer, pEMSRxBuf->length); // copy data into transfer buffer, including the BRK 0x00 at the end
// rx_idle_ = true; // check set the status flag stating BRK has been received and we can start a new package
emsRxTime = millis();
}
drop_first_rx = false;
ETS_UART_INTR_ENABLE(); // re-enable UART interrupts
drop_next_rx = false;
system_os_post(EMSUART_recvTaskPrio, 0, 0); // call emsuart_recvTask() at next opportunity
}
}
@@ -109,7 +91,7 @@ void ICACHE_FLASH_ATTR EMSuart::emsuart_recvTask(os_event_t * events) {
// ignore double BRK at the end, possibly from the Tx loopback
// also telegrams with no data value
// then transmit EMS buffer, excluding the BRK
if ((length > 4) && (length <= EMS_MAXBUFFERSIZE + 1)) {
if (length > 4) {
EMSESP::incoming_telegram((uint8_t *)pCurrent->buffer, length - 1);
}
}
@@ -188,7 +170,7 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) {
ETS_UART_INTR_ATTACH(emsuart_rx_intr_handler, nullptr);
ETS_UART_INTR_ENABLE();
drop_first_rx = true;
drop_next_rx = true;
// LOG_INFO(F("UART service for Rx/Tx started"));
}
@@ -204,12 +186,11 @@ void ICACHE_FLASH_ATTR EMSuart::stop() {
* re-start UART0 driver
*/
void ICACHE_FLASH_ATTR EMSuart::restart() {
if (USIS(EMSUART_UART) & ((1 << UIBD))) {
if (USIR(EMSUART_UART) & ((1 << UIBD))) {
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt
drop_first_rx = true;
drop_next_rx = true;
}
ETS_UART_INTR_ENABLE();
// emsuart_flush_fifos();
}
/*
@@ -246,8 +227,9 @@ void ICACHE_FLASH_ATTR EMSuart::tx_brk() {
* It's a bit dirty. there is no special wait logic per tx_mode type, fifo flushes or error checking
*/
void EMSuart::send_poll(uint8_t data) {
noInterrupts();
if (tx_mode_ == EMS_TXMODE_NEW) {
USC0(EMSUART_UART) &= ~(1 << UCBRK); // make sure <BRK> bit is cleared
USC0(EMSUART_UART) &= ~(1 << UCBRK); // reset tx-brk
USF(EMSUART_UART) = data;
USC0(EMSUART_UART) |= (1 << UCBRK); // send <BRK> at the end
} else {
@@ -255,6 +237,7 @@ void EMSuart::send_poll(uint8_t data) {
delayMicroseconds(EMSUART_TX_BRK_WAIT);
tx_brk(); // send <BRK>
}
interrupts();
}
/*
@@ -266,29 +249,37 @@ EMSUART_STATUS ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
if (len == 0) {
return EMS_TX_STATUS_OK; // nothing to send
}
if(millis() > (emsRxTime + EMS_RX_TO_TX_TIMEOUT)) { // send allowed within 20 ms
return EMS_TX_WTD_TIMEOUT;
}
// new code from Michael. See https://github.com/proddy/EMS-ESP/issues/380
if (tx_mode_ == EMS_TXMODE_NEW) {
USC0(EMSUART_UART) &= ~(1 << UCBRK); // make sure <BRK> bit is cleared
USC0(EMSUART_UART) &= ~(1 << UCBRK); // reset tx-brk
noInterrupts();
for (uint8_t i = 0; i < len; i++) {
USF(EMSUART_UART) = buf[i];
}
USC0(EMSUART_UART) |= (1 << UCBRK); // send <BRK> at the end
interrupts();
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+
noInterrupts();
for (uint8_t i = 0; i < len; i++) {
USF(EMSUART_UART) = buf[i];
delayMicroseconds(EMSUART_TX_BRK_WAIT); // 2070
}
tx_brk(); // send <BRK>
interrupts();
return EMS_TX_STATUS_OK;
}
// Junkers logic by @philrich
if (tx_mode_ == EMS_TXMODE_HT3) {
noInterrupts();
for (uint8_t i = 0; i < len; i++) {
USF(EMSUART_UART) = buf[i];
@@ -300,6 +291,7 @@ EMSUART_STATUS ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
delayMicroseconds(EMSUART_TX_WAIT_BYTE - EMSUART_TX_LAG + EMSUART_TX_WAIT_GAP); // 1760
}
tx_brk(); // send <BRK>
interrupts();
return EMS_TX_STATUS_OK;
}
@@ -332,8 +324,8 @@ EMSUART_STATUS ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
// disable rx interrupt
// clear Rx status register, resetting the Rx FIFO and flush it
ETS_UART_INTR_DISABLE();
USC0(EMSUART_UART) |= (1 << UCRXRST);
noInterrupts();
//ETS_UART_INTR_DISABLE();
emsuart_flush_fifos();
// send the bytes along the serial line
@@ -345,12 +337,14 @@ EMSUART_STATUS ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
while (((USS(EMSUART_UART) >> USRXC) & 0xFF) == _usrxc) {
delayMicroseconds(EMSUART_BUSY_WAIT); // burn CPU cycles...
if (--wdc == 0) {
ETS_UART_INTR_ENABLE();
interrupts();
//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();
interrupts();
//ETS_UART_INTR_ENABLE();
return EMS_TX_BRK_DETECT;
}
}
@@ -377,7 +371,8 @@ EMSUART_STATUS ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
}
}
ETS_UART_INTR_ENABLE(); // open up the FIFO again to start receiving
interrupts();
//ETS_UART_INTR_ENABLE(); // open up the FIFO again to start receiving
return result; // send the Tx status back
}

View File

@@ -47,7 +47,7 @@
#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)*8)
#define EMS_TX_TO_COUNT ((EMS_TX_TO_CHARS) * 8)
namespace emsesp {