add v2.1.1b7 changes, json sizes

This commit is contained in:
MichaelDvP
2020-12-14 09:37:05 +01:00
parent ffa313ebe4
commit a8f997670c
18 changed files with 122 additions and 44 deletions

View File

@@ -104,6 +104,18 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
onChange={handleValueChange('tx_gpio')}
margin="normal"
/>
<TextValidator
validators={['required', 'isNumber', 'minNumber:0', 'maxNumber:120']}
errorMessages={['Tx delay is required', "Must be a number", "Must be 0 or higher", "Max value is 120"]}
name="tx_delay"
label="Tx delayed start (seconds)"
fullWidth
variant="outlined"
value={data.tx_delay}
type="number"
onChange={handleValueChange('tx_delay')}
margin="normal"
/>
<br></br>
<Typography variant="h6" color="primary" >
Dallas Sensor
@@ -245,6 +257,16 @@ function EMSESPSettingsControllerForm(props: EMSESPSettingsControllerFormProps)
onChange={handleValueChange('syslog_mark_interval')}
margin="normal"
/>
<BlockFormControlLabel
control={
<Checkbox
checked={data.trace_raw}
onChange={handleValueChange('trace_raw')}
value="trace_raw"
/>
}
label="Trace ems-telegrams in raw format"
/>
<br></br>
<Typography variant="h6" color="primary" >
Analog Input

View File

@@ -1,5 +1,6 @@
export interface EMSESPSettings {
tx_mode: number;
tx_delay: number;
ems_bus_id: number;
syslog_enabled: boolean;
syslog_level: number;
@@ -17,6 +18,7 @@ export interface EMSESPSettings {
api_enabled: boolean;
bool_format: number;
analog_enabled: boolean;
trace_raw: boolean;
}
export enum busConnectionStatus {

View File

@@ -71,7 +71,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
id = "-1";
}
DynamicJsonDocument doc(EMSESP_JSON_SIZE_LARGE_DYN);
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
JsonObject json = doc.to<JsonObject>();
bool ok = false;
@@ -82,6 +82,11 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
if (api_enabled) {
// we only allow commands with parameters if the API is enabled
ok = Command::call(device_type, cmd.c_str(), data.c_str(), id.toInt(), json); // has cmd, data and id
} else {
request->send(401, "text/plain", F("Unauthorized"));
return;
}
}
if (ok && json.size()) {
// send json output back to web
std::string buffer;
@@ -89,12 +94,6 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
request->send(200, "text/plain", buffer.c_str());
return;
}
} else {
request->send(401, "text/plain", F("Unauthorized"));
return;
}
}
request->send(200, "text/plain", ok ? F("OK") : F("Invalid"));
}

View File

@@ -81,7 +81,7 @@ void WebDevicesService::all_devices(AsyncWebServerRequest * request) {
// The unique_id is the unique record ID from the Web table to identify which device to load
void WebDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant & json) {
if (json.is<JsonObject>()) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice) {
if (emsdevice->unique_id() == json["id"]) {

View File

@@ -31,9 +31,11 @@ WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, Securit
void WebSettings::read(WebSettings & settings, JsonObject & root) {
root["tx_mode"] = settings.tx_mode;
root["tx_delay"] = settings.tx_delay;
root["ems_bus_id"] = settings.ems_bus_id;
root["syslog_enabled"] = settings.syslog_enabled;
root["syslog_level"] = settings.syslog_level;
root["trace_raw"] = settings.trace_raw;
root["syslog_mark_interval"] = settings.syslog_mark_interval;
root["syslog_host"] = settings.syslog_host;
root["master_thermostat"] = settings.master_thermostat;
@@ -58,6 +60,7 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
// tx_mode, rx and tx pins
snprintf_P(&crc_before[0], crc_before.capacity() + 1, PSTR("%d%d%d"), settings.tx_mode, settings.rx_gpio, settings.tx_gpio);
settings.tx_mode = root["tx_mode"] | EMSESP_DEFAULT_TX_MODE;
settings.tx_delay = root["tx_delay"] | EMSESP_DEFAULT_TX_DELAY;
settings.rx_gpio = root["rx_gpio"] | EMSESP_DEFAULT_RX_GPIO;
settings.tx_gpio = root["tx_gpio"] | EMSESP_DEFAULT_TX_GPIO;
snprintf_P(&crc_after[0], crc_after.capacity() + 1, PSTR("%d%d%d"), settings.tx_mode, settings.rx_gpio, settings.tx_gpio);
@@ -77,6 +80,8 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
settings.syslog_level = root["syslog_level"] | EMSESP_DEFAULT_SYSLOG_LEVEL;
settings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST;
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW ;
EMSESP::trace_raw(settings.trace_raw);
snprintf_P(&crc_after[0],
crc_after.capacity() + 1,
PSTR("%d%d%d%s"),

View File

@@ -26,11 +26,13 @@
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/emsespSettings"
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
#define EMSESP_DEFAULT_TX_DELAY 0 // no delay
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
#define EMSESP_DEFAULT_SYSLOG_ENABLED false
#define EMSESP_DEFAULT_SYSLOG_LEVEL 3 // ERR
#define EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL 0
#define EMSESP_DEFAULT_SYSLOG_HOST ""
#define EMSESP_DEFAULT_TRACELOG_RAW false
#define EMSESP_DEFAULT_MASTER_THERMOSTAT 0 // not set
#define EMSESP_DEFAULT_SHOWER_TIMER false
#define EMSESP_DEFAULT_SHOWER_ALERT false
@@ -64,6 +66,7 @@ namespace emsesp {
class WebSettings {
public:
uint8_t tx_mode;
uint8_t tx_delay;
uint8_t ems_bus_id;
uint8_t master_thermostat;
bool shower_timer;
@@ -72,6 +75,7 @@ class WebSettings {
int8_t syslog_level; // uuid::log::Level
uint32_t syslog_mark_interval;
String syslog_host;
bool trace_raw;
uint8_t rx_gpio;
uint8_t tx_gpio;
uint8_t dallas_gpio;

View File

@@ -32,6 +32,7 @@ std::vector<Command::CmdFunction> Command::cmdfunctions_;
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) {
auto cf = find_command(device_type, cmd);
if ((cf == nullptr) || (cf->cmdfunction_json_)) {
LOG_WARNING(F("Command %s not found"), cmd);
return false; // command not found, or requires a json
}
@@ -55,6 +56,7 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val
bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json) {
auto cf = find_command(device_type, cmd);
if (cf == nullptr) {
LOG_WARNING(F("Command %s not found"), cmd);
return false; // command not found or not json
}

View File

@@ -78,6 +78,7 @@
{165, DeviceType::THERMOSTAT, F("RC100/Moduline 1000/1010"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
// Thermostat - Sieger - 0x10 / 0x17
{ 66, DeviceType::THERMOSTAT, F("ES72"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // first seen as remote 0x19, could also be RC20_2?
{ 76, DeviceType::THERMOSTAT, F("ES73"), DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
{113, DeviceType::THERMOSTAT, F("ES72/RC20"), DeviceFlags::EMS_DEVICE_FLAG_RC20_2}, // 0x17

View File

@@ -121,7 +121,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
register_device_value(boiler_data_ww,
&wWCircPumpMode_,
DeviceValueType::ENUM,
flash_string_vector{F("1x3min"), F("2x3min"), F("3x3min"), F("4x3min"), F("5x3min"), F("6x3min"), F("continuos")},
flash_string_vector{F("off"), F("1x3min"), F("2x3min"), F("3x3min"), F("4x3min"), F("5x3min"), F("6x3min"), F("continuos")},
F("wWCircPumpMode"),
F("Warm water circulation pump freq"),
DeviceValueUOM::NONE);
@@ -484,10 +484,11 @@ void Boiler::process_UBAMonitorWW(std::shared_ptr<const Telegram> telegram) {
/*
* UBAMonitorFastPlus - type 0xE4 - central heating monitor EMS+
* Still to figure out are: retTemp, sysPress
* temperatures at 7 and 23 always identical
* 88 00 E4 00 00 2D 2D 00 00 C9 34 02 21 64 3D 05 02 01 DE 00 00 00 00 03 62 14 00 02 21 00 00 33
* 88 00 E4 23 00 00 00 00 00 2B 2B 83
+ * Bosch Logamax Plus GB122: issue #620
+ * 88 00 E4 00 00 2D 2D 00 00 C9 34 02 21 64 3D 05 02 01 DE 00 00 00 00 03 62 14 00 02 21 00 00 00 00 00 00 00 2B 2B 83
+ * GB125/Logamatic MC110: issue #650: add retTemp & sysPress
+ * 08 00 E4 00 10 20 2D 48 00 C8 38 02 37 3C 27 03 00 00 00 00 00 01 7B 01 8F 11 00 02 37 80 00 02 1B 80 00 7F FF 80 00
*/
void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(selFlowTemp_, 6));
@@ -498,8 +499,11 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram
has_update(telegram->read_value(selBurnPow_, 9));
has_update(telegram->read_value(curFlowTemp_, 7));
has_update(telegram->read_value(flameCurr_, 19));
has_update(telegram->read_value(retTemp_, 17)); // can be 0 if no sensor, handled in export_values
has_update(telegram->read_value(sysPress_, 21));
//has_update(telegram->read_value(temperatur_, 13)); unknown temperature
//has_update(telegram->read_value(temperatur_, 13)); // unknown temperature
//has_update(telegram->read_value(temperatur_, 27)); // unknown temperature
// read 3 char service code / installation status as appears on the display
if ((telegram->message_length > 3) && (telegram->offset == 0)) {

View File

@@ -31,7 +31,8 @@ Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const
LOG_DEBUG(F("Adding new Switch with device ID 0x%02X"), device_id);
register_telegram_type(0x9C, F("WM10MonitorMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_WM10MonitorMessage(t); });
register_telegram_type(0x9B, F("WM10SetMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_WM10SetMessage(t); });
register_telegram_type(0x9D, F("WM10SetMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_WM10SetMessage(t); });
register_telegram_type(0x1E, F("WM10TempMessage"), false, [&](std::shared_ptr<const Telegram> t) { process_WM10TempMessage(t); });
std::string empty("");
register_device_value(empty, &activated_, DeviceValueType::BOOL, {}, F("activated"), F("Activated"), DeviceValueUOM::NONE);
@@ -71,15 +72,24 @@ bool Switch::publish_ha_config() {
return true;
}
// message 0x9B switch on/off
// message 0x9D switch on/off
// Thermostat(0x10) -> Switch(0x11), ?(0x9D), data: 00
void Switch::process_WM10SetMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(activated_, 0));
}
// message 0x9C holds flowtemp and unknown status value
// Switch(0x11) -> All(0x00), ?(0x9C), data: 01 BA 00 01 00
void Switch::process_WM10MonitorMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(flowTemp_, 0)); // is * 10
has_update(telegram->read_value(status_, 2));
// has_update(telegram->read_value(status2_, 3)); // unknown
}
// message 0x1E flow temperature, same as in 9C, published often, republished also by boiler UBAFast 0x18
// Switch(0x11) -> Boiler(0x08), ?(0x1E), data: 01 BA
void Switch::process_WM10TempMessage(std::shared_ptr<const Telegram> telegram) {
has_update(telegram->read_value(flowTemp_, 0)); // is * 10
}
} // namespace emsesp

View File

@@ -43,6 +43,7 @@ class Switch : public EMSdevice {
void process_WM10SetMessage(std::shared_ptr<const Telegram> telegram);
void process_WM10MonitorMessage(std::shared_ptr<const Telegram> telegram);
void process_WM10TempMessage(std::shared_ptr<const Telegram> telegram);
uint16_t flowTemp_;
uint8_t status_;

View File

@@ -1557,20 +1557,17 @@ bool Thermostat::set_program(const char * value, const int8_t id) {
return false;
}
if (set < 0 || set > 11) {
if (model() == EMS_DEVICE_FLAG_RC20_2 && set > 0 && set < 10) {
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
} else if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_1) && set < 11) {
write_command(timer_typeids[hc->hc_num() - 1], 84, set, timer_typeids[hc->hc_num() - 1]);
} else if ((model() == EMS_DEVICE_FLAG_RC300 || model() == EMS_DEVICE_FLAG_RC100) && (set == 1 || set == 2)) {
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
} else {
LOG_WARNING(F("Setting program: Invalid number"));
return false;
}
LOG_INFO(F("Setting program to %d for heating circuit %d"), set, hc->hc_num());
if (model() == EMS_DEVICE_FLAG_RC20_2 && set > 0 && set < 10) {
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
} else if ((model() == EMS_DEVICE_FLAG_RC35) || (model() == EMS_DEVICE_FLAG_RC30_1)) {
write_command(timer_typeids[hc->hc_num() - 1], 84, set, timer_typeids[hc->hc_num() - 1]);
} else if ((model() == EMS_DEVICE_FLAG_RC300 || model() == EMS_DEVICE_FLAG_RC100) && (set == 0 || set == 1)) {
write_command(set_typeids[hc->hc_num() - 1], 11, set, set_typeids[hc->hc_num() - 1]);
}
return true;
}
@@ -2217,7 +2214,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
register_device_value(hc_name, &hc->roominfluence, DeviceValueType::UINT, {}, F("roominfluence"), F("Room influence"), DeviceValueUOM::NONE);
register_device_value(hc_name, &hc->nofrosttemp, DeviceValueType::INT, {}, F("nofrosttemp"), F("Nofrost temperature"), DeviceValueUOM::DEGREES);
register_device_value(hc_name, &hc->targetflowtemp, DeviceValueType::UINT, {}, F("targetflowtemp"), F("Target flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(hc_name, &hc->heatingtype, DeviceValueType::UINT, {}, F("heatingtype"), F("Heating type"), DeviceValueUOM::NONE);
register_device_value(hc_name, &hc->heatingtype, DeviceValueType::ENUM, {F("off"), F("radiator"), F("convector"), F("floor")}, F("heatingtype"), F("Heating type"), DeviceValueUOM::NONE);
register_device_value(hc_name,
&hc->summer_setmode,
DeviceValueType::ENUM,
@@ -2265,7 +2262,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<emsesp::Thermostat::H
register_device_value(hc_name, &hc->minflowtemp, DeviceValueType::UINT, {}, F("minflowtemp"), F("Min flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(hc_name, &hc->maxflowtemp, DeviceValueType::UINT, {}, F("maxflowtemp"), F("Max flow temperature"), DeviceValueUOM::DEGREES);
register_device_value(hc_name, &hc->flowtempoffset, DeviceValueType::UINT, {}, F("flowtempoffset"), F("Flow temperature offset"), DeviceValueUOM::DEGREES);
register_device_value(hc_name, &hc->heatingtype, DeviceValueType::UINT, {}, F("heatingtype"), F("Heating type"), DeviceValueUOM::NONE);
register_device_value(hc_name, &hc->heatingtype, DeviceValueType::ENUM, {F("off"), F("radiator"), F("convector"), F("floor")}, F("heatingtype"), F("Heating type"), DeviceValueUOM::NONE);
register_device_value(hc_name,
&hc->reducemode,
DeviceValueType::ENUM,

View File

@@ -65,6 +65,8 @@ bool EMSESP::tap_water_active_ = false; // for when Boiler states we
uint32_t EMSESP::last_fetch_ = 0;
uint8_t EMSESP::publish_all_idx_ = 0;
uint8_t EMSESP::unique_id_count_ = 0;
bool EMSESP::trace_raw_ = false;
uint64_t EMSESP::tx_delay_ = 0;
// for a specific EMS device go and request data values
// or if device_id is 0 it will fetch from all our known and active devices
@@ -146,6 +148,7 @@ void EMSESP::init_tx() {
uint8_t tx_mode;
EMSESP::webSettingsService.read([&](WebSettings & settings) {
tx_mode = settings.tx_mode;
tx_delay_ = settings.tx_delay * 1000;
#ifndef EMSESP_FORCE_SERIAL
EMSuart::stop();
@@ -665,10 +668,10 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
if ((watch_id_ == WATCH_ID_NONE) || (telegram->type_id == watch_id_)
|| ((watch_id_ < 0x80) && ((telegram->src == watch_id_) || (telegram->dest == watch_id_)))) {
LOG_NOTICE(pretty_telegram(telegram).c_str());
} else {
} else if (!trace_raw_) {
LOG_TRACE(pretty_telegram(telegram).c_str());
}
} else {
} else if (!trace_raw_) {
LOG_TRACE(pretty_telegram(telegram).c_str());
}
@@ -941,11 +944,12 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
tx_successful = true;
// if telegram is longer read next part with offset + 25 for ems+
if (length == 32) {
txservice_.read_next_tx();
if (txservice_.read_next_tx() == read_id_) {
read_next_ = true;
}
}
}
}
// if Tx wasn't successful, retry or just give up
if (!tx_successful) {
@@ -956,7 +960,18 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
// check for poll
if (length == 1) {
static uint64_t delayed_tx_start_ = 0;
if (!rxservice_.bus_connected() && (tx_delay_ > 0)) {
delayed_tx_start_ = uuid::get_uptime_ms();
LOG_DEBUG(F("Tx delay started"));
}
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
}
// first send delayed after connect
if ((uuid::get_uptime_ms() - delayed_tx_start_) < tx_delay_) {
return;
}
#ifdef EMSESP_UART_DEBUG
char s[4];

View File

@@ -66,6 +66,7 @@
#else
#define EMSESP_JSON_SIZE_XLARGE_DYN 4096 // for very very large json docs, using DynamicJsonDocument
#endif
#define EMSESP_JSON_SIZE_XXLARGE_DYN 5120 // for very very large json docs, using DynamicJsonDocument
namespace emsesp {
@@ -159,6 +160,14 @@ class EMSESP {
tap_water_active_ = tap_water_active;
}
static bool trace_raw() {
return trace_raw_;
}
static void trace_raw(bool set) {
trace_raw_ = set;
}
static void fetch_device_values(const uint8_t device_id = 0);
static bool add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand);
@@ -220,6 +229,8 @@ class EMSESP {
static bool tap_water_active_;
static uint8_t publish_all_idx_;
static uint8_t unique_id_count_;
static bool trace_raw_;
static uint64_t tx_delay_;
};
} // namespace emsesp

View File

@@ -231,9 +231,7 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
char message[len + 2];
strlcpy(message, payload, len + 1);
#ifdef EMSESP_DEBUG
LOG_DEBUG(F("[DEBUG] Received %s => %s (length %d)"), topic, message, len);
#endif
LOG_DEBUG(F("Received %s => %s (length %d)"), topic, message, len);
// see if we have this topic in our subscription list, then call its callback handler
for (const auto & mf : mqtt_subfunctions_) {

View File

@@ -398,9 +398,11 @@ void System::system_check() {
// not healthy if bus not connected
if (!EMSbus::bus_connected()) {
if (system_healthy_) {
LOG_ERROR(F("Error: No connection to the EMS bus"));
}
system_healthy_ = false;
set_led_speed(LED_WARNING_BLINK); // flash every 1/2 second from now on
// LOG_ERROR(F("Error: No connection to the EMS bus"));
} else {
// if it was unhealthy but now we're better, make sure the LED is solid again cos we've been healed
if (!system_healthy_) {

View File

@@ -197,7 +197,11 @@ void RxService::add(uint8_t * data, uint8_t length) {
if ((trace_watch_id == WATCH_ID_NONE) || (type_id == trace_watch_id)
|| ((trace_watch_id < 0x80) && ((src == trace_watch_id) || (dest == trace_watch_id)))) {
LOG_NOTICE(F("Rx: %s"), Helpers::data_to_hex(data, length).c_str());
} else if (EMSESP::trace_raw()) {
LOG_TRACE(F("Rx: %s"), Helpers::data_to_hex(data, length).c_str());
}
} else if (EMSESP::trace_raw()) {
LOG_TRACE(F("Rx: %s"), Helpers::data_to_hex(data, length).c_str());
}
#ifdef EMSESP_DEBUG
@@ -577,10 +581,11 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram_last_), true);
}
void TxService::read_next_tx() {
uint16_t TxService::read_next_tx() {
// add to the top of the queue
uint8_t message_data[1] = {EMS_MAX_TELEGRAM_LENGTH}; // request all data, 32 bytes
add(Telegram::Operation::TX_READ, telegram_last_->dest, telegram_last_->type_id, telegram_last_->offset + 25, message_data, 1, true);
return telegram_last_->type_id;
}
// checks if a telegram is sent to us matches the last Tx request

View File

@@ -281,7 +281,7 @@ class TxService : public EMSbus {
void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length);
bool is_last_tx(const uint8_t src, const uint8_t dest) const;
uint16_t post_send_query();
void read_next_tx();
uint16_t read_next_tx();
uint8_t retry_count() const {
return retry_count_;