mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
Merge branch 'v2' of https://github.com/proddy/EMS-ESP into v2
This commit is contained in:
@@ -56,7 +56,8 @@ void EMSESPShell::stopped() {
|
||||
|
||||
// remove all custom contexts
|
||||
commands->remove_all_commands();
|
||||
_console_commands_loaded = false; // make sure they got loaded next time a console is opened
|
||||
|
||||
_console_commands_loaded = false; // make sure they get reloaded next time a console is opened
|
||||
}
|
||||
|
||||
// show welcome banner
|
||||
|
||||
@@ -340,8 +340,12 @@ void Boiler::show_values(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
print_value(shell, 2, F("Warm Water activated"), wWActivated_, nullptr, EMS_VALUE_BOOL);
|
||||
if (Helpers::hasValue(wWCircPumpType_, true)) {
|
||||
print_value(shell, 2, F("Warm Water charging type"), wWCircPumpType_ ? F("3-way valve") : F("charge pump"));
|
||||
}
|
||||
print_value(shell, 2, F("Warm Water circulation pump available"), wWCircPump_, nullptr, EMS_VALUE_BOOL);
|
||||
|
||||
if (Helpers::hasValue(wWCircPumpMode_)) {
|
||||
if (wWCircPumpMode_ == 7) {
|
||||
print_value(shell, 2, F("Warm Water circulation pump freq"), F("continuous"));
|
||||
} else {
|
||||
@@ -353,6 +357,8 @@ void Boiler::show_values(uuid::console::Shell & shell) {
|
||||
strlcat(s, "x3min", 7);
|
||||
print_value(shell, 2, F("Warm Water circulation pump freq"), s);
|
||||
}
|
||||
}
|
||||
|
||||
print_value(shell, 2, F("Warm Water circulation active"), wWCirc_, nullptr, EMS_VALUE_BOOL);
|
||||
|
||||
if (wWComfort_ == 0x00) {
|
||||
@@ -697,6 +703,8 @@ void Boiler::set_warmwater_mode(const uint8_t comfort) {
|
||||
} else if (comfort == 3) {
|
||||
LOG_INFO(F("Setting boiler warm water to intelligent"));
|
||||
set = 2;
|
||||
} else {
|
||||
return; // do nothing
|
||||
}
|
||||
write_command(EMS_TYPE_UBAParameterWW, 9, comfort);
|
||||
// some boilers do not have this setting, than it's done by thermostat
|
||||
|
||||
@@ -27,14 +27,13 @@ REGISTER_FACTORY(Controller, EMSdevice::DeviceType::CONTROLLER);
|
||||
MAKE_PSTR(logger_name, "controller")
|
||||
uuid::log::Logger Controller::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
|
||||
|
||||
Controller::Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string &version, const std::string &name, uint8_t flags, uint8_t brand)
|
||||
Controller::Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
// telegram handlers
|
||||
// register_telegram_type(EMS_TYPE_XX, "XX", false, std::bind(&Controller::process_XX, this, _1));
|
||||
|
||||
// MQTT callbacks
|
||||
// register_mqtt_topic("cmd", std::bind(&Controller::cmd, this, _1));
|
||||
|
||||
}
|
||||
|
||||
void Controller::add_context_menu() {
|
||||
|
||||
@@ -184,7 +184,7 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
|
||||
uint8_t pumpmod = pumpModulation_;
|
||||
telegram->read_value(pumpModulation_, 9);
|
||||
if (pumpmod == 0 && pumpModulation_ == 100) { // mask out boosts
|
||||
pumpModulation_ = 15; // set to minimum,
|
||||
pumpModulation_ = 15; // set to minimum
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1042,9 +1042,12 @@ void Thermostat::process_IBASettings(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// type 0x6F - FR10/FR50/FR100 Junkers
|
||||
void Thermostat::process_JunkersMonitor(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// ignore single byte telegram messages
|
||||
if (telegram->message_length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
|
||||
|
||||
telegram->read_value(hc->curr_roomTemp, 4); // value is * 10
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace emsesp {
|
||||
|
||||
uuid::log::Logger EMSdevice::logger_{F_(logger_name), uuid::log::Facility::CONSOLE};
|
||||
|
||||
std::string EMSdevice::brand() const {
|
||||
std::string EMSdevice::brand_to_string() const {
|
||||
switch (brand_) {
|
||||
case EMSdevice::Brand::BOSCH:
|
||||
return read_flash_string(F("Bosch"));
|
||||
@@ -143,7 +143,7 @@ std::string EMSdevice::to_string() const {
|
||||
snprintf_P(&str[0],
|
||||
str.capacity() + 1,
|
||||
PSTR("%s %s (DeviceID:0x%02X ProductID:%d, Version:%s)"),
|
||||
brand().c_str(),
|
||||
brand_to_string().c_str(),
|
||||
name_.c_str(),
|
||||
device_id_,
|
||||
product_id_,
|
||||
|
||||
@@ -86,11 +86,15 @@ class EMSdevice {
|
||||
brand_ = brand;
|
||||
}
|
||||
|
||||
inline uint8_t brand() const {
|
||||
return brand_;
|
||||
}
|
||||
|
||||
inline void name(const std::string & name) {
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
std::string brand() const;
|
||||
std::string brand_to_string() const;
|
||||
static uint8_t decode_brand(uint8_t value);
|
||||
|
||||
std::string to_string() const;
|
||||
@@ -171,8 +175,8 @@ class EMSdevice {
|
||||
};
|
||||
|
||||
// device IDs
|
||||
static constexpr uint16_t EMS_DEVICE_ID_BOILER = 0x08; // fixed device_id for Master Boiler/UBA
|
||||
static constexpr uint16_t EMS_DEVICE_ID_MODEM = 0x48; // gateways like the KM200
|
||||
static constexpr uint8_t EMS_DEVICE_ID_BOILER = 0x08; // fixed device_id for Master Boiler/UBA
|
||||
static constexpr uint8_t EMS_DEVICE_ID_MODEM = 0x48; // gateways like the KM200
|
||||
|
||||
// type IDs
|
||||
static constexpr uint16_t EMS_TYPE_VERSION = 0x02; // type ID for Version information. Generic across all EMS devices.
|
||||
|
||||
@@ -497,10 +497,10 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
|
||||
}
|
||||
}
|
||||
|
||||
// add the EMS device to our list of devices
|
||||
// add a new or update existing EMS device to our list of active EMS devices
|
||||
// if its not in our database, we don't add it
|
||||
bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand) {
|
||||
// don't add ourselves
|
||||
// don't add ourselves!
|
||||
if (device_id == rxservice_.ems_bus_id()) {
|
||||
return false;
|
||||
}
|
||||
@@ -509,10 +509,13 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->is_device_id(device_id)) {
|
||||
LOG_DEBUG(F("Updating details for already existing device with ID 0x%02X"), device_id);
|
||||
LOG_DEBUG(F("Updating details to already existing device ID 0x%02X"), device_id);
|
||||
emsdevice->product_id(product_id);
|
||||
emsdevice->version(version);
|
||||
// only set brand if it doesn't already exist
|
||||
if (emsdevice->brand() == EMSdevice::Brand::NO_BRAND) {
|
||||
emsdevice->brand(brand);
|
||||
}
|
||||
// find the name and flags in our database
|
||||
for (const auto & device : device_library_) {
|
||||
if (device.product_id == product_id) {
|
||||
@@ -521,28 +524,37 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
}
|
||||
}
|
||||
|
||||
return true; // finish
|
||||
return true; // finish up
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// look up the rest of the details using the product_id and create the new device object
|
||||
// then send a request to the device to get the version and any other info we may have
|
||||
bool found = false;
|
||||
for (const auto & device : device_library_) {
|
||||
Device_record * device_p = nullptr;
|
||||
for (auto & device : device_library_) {
|
||||
if (device.product_id == product_id) {
|
||||
emsdevices.push_back(
|
||||
EMSFactory::add(device.device_type, device_id, device.product_id, version, uuid::read_flash_string(device.name), device.flags, brand));
|
||||
found = true;
|
||||
// sometimes boilers share the same product id as controllers
|
||||
// so only add boilers if the device_id is 0x08, which is fixed for EMS
|
||||
if (device.device_type == DeviceType::BOILER) {
|
||||
if (device_id == EMSdevice::EMS_DEVICE_ID_BOILER) {
|
||||
device_p = &device;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// it's not a boiler, but we have a match
|
||||
device_p = &device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we don't recognize the product ID report it, but don't add it.
|
||||
if (!found) {
|
||||
if (device_p == nullptr) {
|
||||
LOG_NOTICE(F("Unrecognized EMS device with device ID 0x%02X with product ID %d. Please report on GitHub."), device_id, product_id);
|
||||
return false; // not found
|
||||
} else {
|
||||
emsdevices.push_back(
|
||||
EMSFactory::add(device_p->device_type, device_id, device_p->product_id, version, uuid::read_flash_string(device_p->name), device_p->flags, brand));
|
||||
LOG_DEBUG(F("Adding new device with device ID 0x%02X with product ID %d and version %s"), device_id, product_id, version.c_str());
|
||||
// go and fetch its data,
|
||||
fetch_device_values(device_id);
|
||||
@@ -572,7 +584,9 @@ 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)
|
||||
// the CRC check is not done here, only when it's added to the Rx queue with add()
|
||||
void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
static uint32_t tx_time_ = 0;
|
||||
#endif
|
||||
// check first for echo
|
||||
uint8_t first_value = data[0];
|
||||
if (((first_value & 0x7F) == txservice_.ems_bus_id()) && (length > 1)) {
|
||||
@@ -632,7 +646,9 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
// 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()) {
|
||||
EMSbus::last_bus_activity(uuid::get_uptime()); // set the flag indication the EMS bus is active
|
||||
#ifdef EMSESP_DEBUG
|
||||
tx_time_ = ::millis(); // get_uptime is only updated once per loop, does not give the right time
|
||||
#endif
|
||||
txservice_.send();
|
||||
}
|
||||
// send remote room temperature if active
|
||||
|
||||
@@ -61,7 +61,13 @@ uint8_t EMSbus::calculate_crc(const uint8_t * data, const uint8_t length) {
|
||||
|
||||
// creates a telegram object
|
||||
// stores header in separate member objects and the rest in the message_data block
|
||||
Telegram::Telegram(uint8_t operation, uint8_t src, uint8_t dest, uint16_t type_id, uint8_t offset, uint8_t * data, uint8_t message_length)
|
||||
Telegram::Telegram(const uint8_t operation,
|
||||
const uint8_t src,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
const uint8_t * data,
|
||||
const uint8_t message_length)
|
||||
: operation(operation)
|
||||
, src(src)
|
||||
, dest(dest)
|
||||
@@ -113,7 +119,6 @@ void RxService::flush_rx_queue() {
|
||||
// Rx loop, run as many times as you can
|
||||
// processes all telegrams on the queue. Assumes there are valid (i.e. CRC checked)
|
||||
void RxService::loop() {
|
||||
|
||||
while (!rx_telegrams_.empty()) {
|
||||
auto telegram = rx_telegrams_.front().telegram_;
|
||||
|
||||
@@ -301,7 +306,7 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
telegram_raw[1] = dest;
|
||||
|
||||
uint8_t message_p = 0; // this is the position in the telegram where we want to put our message data
|
||||
uint8_t message_p0 = 0; // this is the position of start of message data
|
||||
bool copy_data = true; // true if we want to copy over the data message block to the end of the telegram header
|
||||
|
||||
if (telegram->type_id > 0xFF) {
|
||||
// it's EMS 2.0/+
|
||||
@@ -320,7 +325,7 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
telegram_raw[5] = (telegram->type_id >> 8) - 1; // type, 1st byte, high-byte, subtract 0x100
|
||||
telegram_raw[6] = telegram->type_id & 0xFF; // type, 2nd byte, low-byte
|
||||
message_p = 7;
|
||||
message_p0 = 1; // data[0] is in raw[4]
|
||||
copy_data = false; // there are no more data values after the type_id when reading on EMS+
|
||||
}
|
||||
} else {
|
||||
// EMS 1.0
|
||||
@@ -329,14 +334,16 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
message_p = 4;
|
||||
}
|
||||
|
||||
// add the data to send to to the end of the header
|
||||
if (copy_data) {
|
||||
if (telegram->message_length > EMS_MAX_TELEGRAM_MESSAGE_LENGTH) {
|
||||
return; // too big
|
||||
}
|
||||
|
||||
for (uint8_t i = message_p0; i < telegram->message_length; i++) {
|
||||
// add the data to send to to the end of the header
|
||||
for (uint8_t i = 0; i < telegram->message_length; i++) {
|
||||
telegram_raw[message_p++] = telegram->message_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t length = message_p;
|
||||
|
||||
@@ -412,19 +419,19 @@ void TxService::add(const uint8_t operation, const uint8_t dest, const uint16_t
|
||||
// builds a Tx telegram and adds to queue
|
||||
// this is used by the retry() function to put the last failed Tx back into the queue
|
||||
// format is EMS 1.0 (src, dest, type_id, offset, data)
|
||||
// length is the length of the whole telegram data
|
||||
void TxService::add(uint8_t operation, uint8_t * data, const uint8_t length) {
|
||||
// length is the length of the whole telegram data, excluding the CRC
|
||||
void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t length) {
|
||||
// build header. src, dest and offset have fixed positions
|
||||
uint8_t src = data[0];
|
||||
uint8_t dest = data[1];
|
||||
uint8_t offset = data[3];
|
||||
|
||||
uint16_t type_id;
|
||||
uint8_t * message_data; // where the message block starts
|
||||
const uint8_t * message_data; // where the message block starts
|
||||
uint8_t message_length; // length of the message block, excluding CRC
|
||||
|
||||
// work out depending on the type, where the data message block starts and the message length
|
||||
// same logic as in RxService::add()
|
||||
// same logic as in RxService::add(), but adjusted for no appended CRC
|
||||
if (data[2] < 0xF0) {
|
||||
// EMS 1.0
|
||||
type_id = data[2];
|
||||
@@ -520,7 +527,6 @@ void TxService::send_raw(const char * telegram_data) {
|
||||
// add last Tx to tx queue and increment count
|
||||
// returns retry count, or 0 if all done
|
||||
void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length) {
|
||||
|
||||
// have we reached the limit? if so, reset count and give up
|
||||
if (++retry_count_ > MAXIMUM_TX_RETRIES) {
|
||||
reset_retry_count(); // give up
|
||||
|
||||
@@ -57,7 +57,13 @@ namespace emsesp {
|
||||
// from Rx (receiving one) or Tx for preparing one for sending
|
||||
class Telegram {
|
||||
public:
|
||||
Telegram(uint8_t operation, uint8_t src, uint8_t dest, uint16_t type_id, uint8_t offset, uint8_t * message_data, uint8_t message_length);
|
||||
Telegram(const uint8_t operation,
|
||||
const uint8_t src,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
const uint8_t * message_data,
|
||||
const uint8_t message_length);
|
||||
~Telegram() = default;
|
||||
|
||||
const uint8_t operation; // is Operation mode
|
||||
@@ -82,7 +88,7 @@ class Telegram {
|
||||
// reads a bit value from a given telegram position
|
||||
void read_bitvalue(uint8_t & value, const uint8_t index, const uint8_t bit) const {
|
||||
uint8_t abs_index = (index - offset);
|
||||
if(abs_index >= message_length) {
|
||||
if (abs_index >= message_length) {
|
||||
return; // out of bounds
|
||||
}
|
||||
|
||||
@@ -265,7 +271,7 @@ class TxService : public EMSbus {
|
||||
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, uint8_t * data, const uint8_t length);
|
||||
void add(const uint8_t operation, const uint8_t * data, const uint8_t length);
|
||||
|
||||
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0);
|
||||
|
||||
|
||||
@@ -134,14 +134,25 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
EMSESP::add_device(0x08, 123, version, EMSdevice::Brand::BUDERUS); // Nefit Trendline
|
||||
}
|
||||
|
||||
if (command == "unknown") {
|
||||
// unknown device -
|
||||
if ((command == "unknown") || (command == "u")) {
|
||||
// question: do we need to set the mask?
|
||||
std::string version("1.2.3");
|
||||
EMSESP::add_device(0x09, 89, version, EMSdevice::Brand::BUDERUS);
|
||||
|
||||
// add boiler
|
||||
EMSESP::add_device(0x08, 84, version, EMSdevice::Brand::BUDERUS);
|
||||
EMSESP::rxservice_.loop();
|
||||
|
||||
// add Controller - BC10 GB142 - but using the same device_id to see what happens
|
||||
EMSESP::add_device(0x09, 84, version, EMSdevice::Brand::BUDERUS);
|
||||
EMSESP::rxservice_.loop();
|
||||
|
||||
// simulate getting version information back from an unknown device
|
||||
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x59, 0x01, 0x02});
|
||||
// note there is no brand (byte 9)
|
||||
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x59, 0x09, 0x0a});
|
||||
|
||||
shell.loop_all();
|
||||
EMSESP::show_values(shell);
|
||||
}
|
||||
|
||||
if (command == "unknown2") {
|
||||
@@ -282,7 +293,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
uart_telegram_withCRC("90 0B FF 00 01 A5 80 00 01 26 15 00 26 2A 05 A0 03 03 03 05 A0 05 A0 00 00 11 01 03 FF FF 00 FE");
|
||||
uart_telegram_withCRC("90 00 FF 19 01 A5 01 04 00 00 00 00 FF 64 2A 00 3C 01 FF 92");
|
||||
|
||||
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::show_values(shell);
|
||||
@@ -395,26 +405,39 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & command) {
|
||||
EMSESP::txservice_.flush_tx_queue();
|
||||
|
||||
// TX queue example - Me -> Thermostat, (0x91), telegram: 0B 17 91 05 44 45 46 47 (#data=4)
|
||||
// uint8_t t11[] = {0x44, 0x45, 0x46, 0x47};
|
||||
// EMSESP::txservice_.add(Telegram::Operation::TX_RAW, 0x17, 0x91, 0x05, t11, sizeof(t11));
|
||||
uint8_t t11[] = {0x44, 0x45, 0x46, 0x47};
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_RAW, 0x17, 0x91, 0x05, t11, sizeof(t11));
|
||||
|
||||
// TX - raw example test
|
||||
// uint8_t t12[] = {0x10, 0x08, 0x63, 0x04, 0x64};
|
||||
// EMSESP::txservice_.add(t12, sizeof(t12));
|
||||
uint8_t t12[] = {0x10, 0x08, 0x63, 0x04, 0x64};
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_RAW, t12, sizeof(t12));
|
||||
|
||||
// TX - sending raw string
|
||||
// EMSESP::txservice_.send_raw("10 08 63 03 64 65 66");
|
||||
EMSESP::txservice_.send_raw("10 08 63 03 64 65 66");
|
||||
|
||||
// TX - send a read request
|
||||
EMSESP::send_read_request(0x18, 0x08);
|
||||
|
||||
// TX - send a write request
|
||||
// uint8_t t18[] = {0x52, 0x79};
|
||||
// send_write_request(0x91, 0x17, 0x00, t18, sizeof(t18));
|
||||
uint8_t t18[] = {0x52, 0x79};
|
||||
EMSESP::send_write_request(0x91, 0x17, 0x00, t18, sizeof(t18), 0x00);
|
||||
|
||||
// TX - send EMS+
|
||||
const uint8_t t13[] = {0x90, 0x0B, 0xFF, 00, 01, 0xBA, 00, 0x2E, 0x2A, 0x26, 0x1E, 0x03,
|
||||
00, 0xFF, 0xFF, 05, 0x2A, 01, 0xE1, 0x20, 0x01, 0x0F, 05, 0x2A};
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_RAW, t13, sizeof(t13));
|
||||
|
||||
// EMS+ Junkers read request
|
||||
EMSESP::send_read_request(0x16F, 0x10);
|
||||
|
||||
EMSESP::show_emsbus(shell);
|
||||
|
||||
// process whole Tx queue
|
||||
for (uint8_t i = 0; i < 10; i++) {
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
}
|
||||
|
||||
shell.loop_all();
|
||||
EMSESP::show_emsbus(shell);
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
|
||||
EMSESP::txservice_.flush_tx_queue();
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "2.0.0a27"
|
||||
#define EMSESP_APP_VERSION "2.0.0a28"
|
||||
|
||||
Reference in New Issue
Block a user