14static constexpr uint32_t DEFERRED_SEND_TIMEOUT_ID = 0;
16static constexpr size_t MODBUS_MAX_LOG_BYTES = 64;
18static constexpr uint32_t US_PER_SEC = 1000000;
19static constexpr uint32_t US_PER_MS = 1000;
22static constexpr uint32_t MODBUS_MIN_FRAME_DELAY_US = 1750;
27 const uint32_t elapsed = last_modbus_byte - last_send;
28 return (int32_t) elapsed < 0 ? 0 : elapsed;
51 static constexpr uint32_t DEFAULT_LONG_RX_BUFFER_DELAY_US = 50 * US_PER_MS;
55 : DEFAULT_LONG_RX_BUFFER_DELAY_US;
98 ESP_LOGW(TAG,
"Stop waiting for response from %" PRIu8
" %" PRIu32
"us after last send", cmd->
frame.
address(),
99 this->last_receive_check_ - this->last_send_);
131 return elapsed >= required ? 0 : required - elapsed;
142 return (int32_t) std::max(
184 if (this->
rx_buffer_.size() >= this->parent_->get_rx_full_threshold())
186 if (buffer_size == 0) {
187 ESP_LOGV(TAG,
"Received first byte %" PRIu8
" (0X%x) of %zu bytes %" PRIu32
"us after last send",
200 }
while (!this->
rx_buffer_.empty() && size > this->rx_buffer_.size());
211 ESP_LOGD(TAG,
"Dropped deferred reply to %" PRIu8
": a new frame arrived first", this->
deferred_payload_[0]);
215 ESP_LOGVV(TAG,
"Parsing frames buffer size = %zu",
size);
216 bool retry_as_client =
false;
218 const bool is_broadcast = this->
rx_buffer_[0] == BROADCAST_ADDRESS;
223 ESP_LOGV(TAG,
"Stop expecting peer response from %" PRIu8
" due to parse failure, and retry parse",
226 retry_as_client =
true;
230 "Stop expecting peer response from %" PRIu8
" due to timeout after partial response, and retry parse",
233 retry_as_client =
true;
252 const auto max_len =
static_cast<uint16_t
>(std::min(
size,
size_t(MAX_FRAME_SIZE)));
253 if (min_length > max_len)
258 uint16_t crc =
crc16(
raw, min_length);
261 for (uint16_t
len = min_length;
len < max_len;
len++) {
273 if (
size < frame_length)
281 if (frame_length == 0)
282 return size < MAX_FRAME_SIZE;
283 ESP_LOGD(TAG,
"Unknown-length function %02X found", function_code);
292 std::span<const uint8_t> pdu(this->
rx_buffer_.data() + 1, frame_length - 3);
304 if (
size < frame_length)
312 if (frame_length == 0)
313 return size < MAX_FRAME_SIZE;
314 ESP_LOGD(TAG,
"Unknown-length function %02X found", function_code);
324 uint16_t data_len = frame_length - 2 - data_offset;
325 uint8_t data_buffer[MAX_FRAME_SIZE] = {};
326 std::memcpy(data_buffer, this->
rx_buffer_.data() + data_offset, data_len);
327 std::span<const uint8_t> data(data_buffer, data_len);
330 if (
address == BROADCAST_ADDRESS) {
343 const uint8_t function_code =
pdu[0];
345 if (cmd ==
nullptr) {
347 "Received unexpected frame from address %" PRIu8
", function code 0x%X, %" PRIu32
"us after last send",
354 const uint8_t expected_function_code = cmd->
frame.
pdu()[0];
357 "Received incorrect frame address %" PRIu8
" <> %" PRIu8
" or function code 0x%X <> 0x%X, %" PRIu32
358 "us after last send",
372 "Ignoring response from %" PRIu8
" - transmission interrupted by previous unexpected response, %" PRIu32
373 "us after last send",
384 uint8_t exception =
pdu[1];
385 ESP_LOGW(TAG,
"Error function code: 0x%X exception: %" PRIu8
", address: %" PRIu8
", %" PRIu32
"us after last send",
389 ESP_LOGV(TAG,
"Ignoring response from %" PRIu8
" - no callback device set, %" PRIu32
"us after last send",
address,
396 ESP_LOGE(TAG,
"Unexpected response from address %" PRIu8
", which is mapped to this device.",
address);
400 ESP_LOGV(TAG,
"Expected response from peer %" PRIu8
" received",
address);
402 ESP_LOGV(TAG,
"Unexpected response from peer %" PRIu8
" received",
address);
411 for (
auto *device : this->
devices_) {
412 if (device->get_address() ==
address) {
421 ESP_LOGW(TAG,
"Address out of range - start: %" PRIu16
" num: %" PRIu16, start_address,
count);
430static constexpr size_t WRITE_SINGLE_VALUES_OFFSET = 2;
431static constexpr size_t WRITE_MULTIPLE_VALUES_OFFSET = 5;
433static constexpr size_t READ_WRITE_VALUES_OFFSET = 9;
437static_assert(1 + WRITE_MULTIPLE_VALUES_OFFSET +
packed_bit_bytes(MAX_NUM_OF_COILS_TO_WRITE) <= MAX_PDU_SIZE,
438 "the largest FC 0x0F coil write must fit within MAX_PDU_SIZE");
444 this->
assemble_registers_(data.subspan(WRITE_SINGLE_VALUES_OFFSET,
sizeof(uint16_t)), registers);
453 if (number_of_registers == 0 || number_of_registers > MAX_NUM_OF_REGISTERS_TO_WRITE ||
454 number_of_registers * 2 != number_of_bytes) {
455 ESP_LOGW(TAG,
"Invalid number of registers %" PRIu16
" or bytes %" PRIu8, number_of_registers, number_of_bytes);
461 this->
assemble_registers_(data.subspan(WRITE_MULTIPLE_VALUES_OFFSET, number_of_bytes), registers);
466 const LogString *entity_name, uint16_t &start_address,
473 ESP_LOGW(TAG,
"Invalid number of %s %" PRIu16, LOG_STR_ARG(entity_name),
count);
483 if (raw_value != 0xFF00 && raw_value != 0x0000) {
484 ESP_LOGW(TAG,
"Invalid coil value 0x%04X", raw_value);
488 value = raw_value == 0xFF00;
493 uint16_t &
count, std::span<const uint8_t> &packed_bytes) {
497 if (number_of_bits == 0 || number_of_bits > MAX_NUM_OF_COILS_TO_WRITE ||
499 ESP_LOGW(TAG,
"Invalid number of coils %" PRIu16
" or bytes %" PRIu8, number_of_bits, number_of_bytes);
505 count = number_of_bits;
507 packed_bytes = data.subspan(WRITE_MULTIPLE_VALUES_OFFSET, number_of_bytes);
512 for (
size_t offset = 0; offset + 1 < values.size(); offset += 2) {
522 uint16_t start_address;
524 uint16_t coil_count = 0;
525 std::span<const uint8_t> packed_bytes;
526 uint8_t single_bit = 0;
540 single_bit = value ? 0x01 : 0x00;
542 packed_bytes = std::span<const uint8_t>(&single_bit, 1);
551 ESP_LOGV(TAG,
"Ignoring broadcast with unsupported function code %" PRIu8, function_code);
559 for (
auto *device : this->
devices_) {
563 coils ? device->on_write_coils(start_address,
PackedBits(packed_bytes, coil_count))
564 : device->on_write_registers(start_address, registers);
565 if (device_status.has_value()) {
566 ESP_LOGV(TAG,
"Device %" PRIu8
" rejected broadcast write with exception %" PRIu8, device->get_address(),
567 static_cast<uint8_t
>(device_status.value()));
574 std::span<uint8_t> response_buffer, uint16_t &response_len) {
581 if (registers.
size() != number_of_registers) {
582 ESP_LOGE(TAG,
"Incorrect response %" PRIu16
" requested, %zu returned", number_of_registers, registers.
size());
589 if (number_of_registers > MAX_NUM_OF_REGISTERS_TO_READ) {
590 ESP_LOGE(TAG,
"Read response of %" PRIu16
" registers exceeds the limit of %" PRIu16, number_of_registers,
591 MAX_NUM_OF_REGISTERS_TO_READ);
599 const size_t required =
static_cast<size_t>(response_len) + 1 +
static_cast<size_t>(number_of_registers) * 2;
600 if (required > response_buffer.size()) {
601 ESP_LOGE(TAG,
"Read response needs %zu bytes but only %zu are available", required, response_buffer.size());
606 response_buffer[response_len++] =
static_cast<uint8_t
>(number_of_registers * 2);
607 for (
auto r : registers) {
609 response_buffer[response_len++] = register_bytes[0];
610 response_buffer[response_len++] = register_bytes[1];
616 std::span<const uint8_t> data) {
618 if (device ==
nullptr) {
620 ESP_LOGV(TAG,
"Request to peer %" PRIu8
" received",
address);
625 uint8_t response_buffer[modbus::MAX_RAW_SIZE];
626 const uint8_t *response_data = response_buffer;
627 uint16_t response_len = 0;
632 uint16_t start_address;
633 uint16_t number_of_registers;
635 number_of_registers);
647 response_buffer, response_len)) {
655 uint16_t start_address;
666 response_data = data.data();
672 uint16_t start_address;
673 uint16_t number_of_bits;
675 this->
parse_read_request_(data, MAX_NUM_OF_COILS_TO_READ, LOG_STR(
"bits"), start_address, number_of_bits);
681 const uint8_t byte_count =
static_cast<uint8_t
>(
packed_bit_bytes(number_of_bits));
682 response_buffer[response_len++] = byte_count;
685 std::span<uint8_t> packed_out = std::span<uint8_t>(response_buffer).subspan(response_len, byte_count);
686 std::fill(packed_out.begin(), packed_out.end(), 0);
696 response_len += byte_count;
702 uint16_t start_address;
708 const uint8_t single_bit = value ? 0x01 : 0x00;
710 response_data = data.data();
716 uint16_t start_address;
718 std::span<const uint8_t> packed_bytes;
724 response_data = data.data();
736 if (number_of_registers == 0 || number_of_registers > MAX_NUM_OF_REGISTERS_TO_READ ||
737 number_of_write_registers == 0 || number_of_write_registers > MAX_NUM_OF_REGISTERS_TO_WRITE_RW ||
738 number_of_write_registers * 2 != number_of_bytes) {
739 ESP_LOGW(TAG,
"Invalid number of registers (read %" PRIu16
", write %" PRIu16
") or bytes %" PRIu8,
740 number_of_registers, number_of_write_registers, number_of_bytes);
745 if (!
status.has_value()) {
755 this->
assemble_registers_(data.subspan(READ_WRITE_VALUES_OFFSET, number_of_bytes), write_registers);
768 response_buffer, response_len)) {
774 ESP_LOGW(TAG,
"Unsupported function code %" PRIu8, function_code);
779 this->
send_response_(address, function_code, response_data, response_len);
787 if (tx_delay_remaining > 0) {
794 if (tx_delay_remaining > 0)
815#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
818 ESP_LOGV(TAG,
"Write: %s %" PRIu32
"us after last send, %" PRIu32
"us after last receive",
820 now - this->last_modbus_byte_);
837 ESP_LOGV(TAG,
"Send deferred for %" PRIu8
": a frame arrived during the send delay, will retry",
848 ESP_LOGV(TAG,
"Broadcast to address 0 sent; no reply expected");
859 " Send Wait Time: %" PRIu32
" ms\n"
860 " Turnaround Time: %" PRIu32
" ms\n"
861 " Frame Delay: %" PRIu32
" us\n"
862 " Long Rx Buffer Delay: %" PRIu32
" us\n"
863 " Bits Per Character: %" PRIu8
"\n"
864 " Rx Detect Latency: %" PRIu32
" us",
872 " Frame Delay: %" PRIu32
" us\n"
873 " Long Rx Buffer Delay: %" PRIu32
" us\n"
874 " Bits Per Character: %" PRIu8
"\n"
875 " Rx Detect Latency: %" PRIu32
" us",
887 uint16_t payload_len) {
890 if (payload_len + 2 > MAX_RAW_SIZE) {
891 ESP_LOGE(TAG,
"Server response too large (%" PRIu16
" bytes)",
static_cast<uint16_t
>(payload_len + 2));
894 uint8_t raw_frame[MAX_RAW_SIZE];
896 raw_frame[1] = function_code;
897 std::memcpy(raw_frame + 2, payload, payload_len);
898 this->
send_raw_(raw_frame, payload_len + 2);
908 ESP_LOGD(TAG,
"Exception %" PRIu8
" replied to function 0x%02X for address %" PRIu8,
909 static_cast<uint8_t
>(
status.value()), function_code,
address);
915 uint8_t raw_frame[3];
918 raw_frame[2] =
static_cast<uint8_t
>(exception_code);
924 if (cmd.waiting_state())
934 const auto age = [now](
const ModbusDeviceCommand &cmd) -> uint16_t {
return now - cmd.seq; };
941 (cmd.priority() == best->
priority() && older(cmd, *best))) {
951 if (this->
device ==
nullptr)
960 if (this->
device !=
nullptr)
970 if (this->
device ==
nullptr)
980 if (this->
device ==
nullptr)
1003 if (this->
device ==
nullptr)
1019 const size_t work_set = this->
tx_buffer_.size();
1021 bool callback_ran =
true;
1022 while (callback_ran) {
1023 callback_ran =
false;
1024 for (
size_t i = 0; i != work_set && !callback_ran; i++) {
1026 switch (cmd.
state) {
1054 for (
size_t i = this->
tx_buffer_.size(); i-- > 0;) {
1070 ESP_LOGW(TAG,
"Empty PDU refused for address %" PRIu8,
address);
1074 if (
pdu.size() > MAX_PDU_SIZE) {
1075 ESP_LOGE(TAG,
"Frame too large, refused: %" PRIu8
":%zu bytes",
address,
pdu.size());
1080 ESP_LOGW(TAG,
"Exception PDU refused for address %" PRIu8
": function code 0x%X has the exception bit set",
address,
1089 ESP_LOGW(TAG,
"continuous is ignored for a mutating function (0x%X, address %" PRIu8
")",
pdu[0],
address);
1092 if (
address != BROADCAST_ADDRESS) {
1093 options.allow_broadcast_read =
false;
1094 options.expect_broadcast_write_response =
false;
1097 if (
options.allow_broadcast_read && broadcastable) {
1098 ESP_LOGV(TAG,
"allow_broadcast_read is ignored for function 0x%X: it is broadcastable",
pdu[0]);
1099 options.allow_broadcast_read =
false;
1101 if (
options.expect_broadcast_write_response && !broadcastable) {
1102 ESP_LOGV(TAG,
"expect_broadcast_write_response is ignored for function 0x%X: it is not broadcastable",
pdu[0]);
1103 options.expect_broadcast_write_response =
false;
1105 if (!broadcastable && !
options.allow_broadcast_read) {
1106 ESP_LOGW(TAG,
"Broadcast refused for function 0x%X: a broadcast (address 0) is never answered",
pdu[0]);
1123 ESP_LOGD(TAG,
"Anonymous duplicate of active frame for %" PRIu8
" (function 0x%X), dropped",
address,
pdu[0]);
1126 "Anonymous duplicate of active frame for %" PRIu8
" (function 0x%X), dropped - register a "
1127 "device for delivery accounting",
1133 item.make_continuous(
true);
1134 ESP_LOGV(TAG,
"Frame already active for %" PRIu8
", now polled continuously",
address);
1135 }
else if (item.options.continuous) {
1138 item.make_continuous(
false);
1139 ESP_LOGV(TAG,
"Frame already active for %" PRIu8
", downgraded from continuous to one-shot",
address);
1140 }
else if (!item.increment_pending()) {
1143 ESP_LOGD(TAG,
"Frame already active for %" PRIu8
" with %" PRIu8
" requests pending, refused",
address,
1147 ESP_LOGV(TAG,
"Frame already active for %" PRIu8
", request absorbed (pending %" PRIu8
")",
address,
1150 item.options.expect_broadcast_write_response |=
options.expect_broadcast_write_response;
1156 if (this->tx_buffer_.size() >= MODBUS_TX_BUFFER_SIZE) {
1157#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
1160 ESP_LOGE(TAG,
"Write buffer full, refused: %" PRIu8
":%s",
address,
1164 ESP_LOGV_HEX(TAG,
pdu.data(),
pdu.size(), MODBUS_MAX_LOG_BYTES,
"Adding frame to tx queue: %" PRIu8
":%s",
address);
1172 if (cmd.frame.address() !=
address)
1183 if (cmd.device !=
device)
1185 cmd.silent_retire();
1190void ModbusClientHub::send_raw(
const std::vector<uint8_t> &payload,
ModbusClientDevice *device) {
1191 if (payload.size() < 2) {
1192 ESP_LOGW(TAG,
"send_raw() payload too short to contain a PDU, refused");
1195 this->
queue_pdu(payload[0], std::span<const uint8_t>(payload).subspan(1),
device);
1203 if (
len > MAX_RAW_SIZE) {
1204 ESP_LOGE(TAG,
"Server send frame too large (%" PRIu16
" bytes)",
len);
1219 this->deferred_payload_len_ - 1);
1222 ESP_LOGE(TAG,
"Deferred server reply dropped: transmission still blocked");
1230 ESP_LOGE(TAG,
"Server reply dropped: a frame arrived during the send delay");
1236 if (bytes_to_clear > 0 && bytes >= bytes_to_clear)
1237 bytes = bytes_to_clear;
1240 ESP_LOGW(TAG,
"Clearing buffer of %zu bytes - %s %" PRIu32
"us after last send", bytes, LOG_STR_ARG(reason),
1242#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
1245 bytes > MODBUS_MAX_LOG_BYTES ? LOG_STR_LITERAL(
" ...") : LOG_STR_LITERAL(
""));
1248 ESP_LOGV(TAG,
"Clearing buffer of %zu bytes - %s %" PRIu32
"us after last send", bytes, LOG_STR_ARG(reason),
1261void ModbusClientDevice::dispatch_response_(std::span<const uint8_t> request_pdu, std::span<const uint8_t> response_pdu,
1263 if (request_pdu.empty())
1265 auto function_code =
static_cast<FunctionCode>(request_pdu[0]);
1268 if (request_pdu.size() < READ_PDU_SIZE) {
1293 const size_t expected_data_size =
1294 bits ?
packed_bit_bytes(count_or_value) : static_cast<size_t>(count_or_value) * 2;
1295 if (response_pdu.size() != expected_data_size + 2) {
1296 ESP_LOGD(TAG,
"Response length %zu does not match request (expected %zu) for function code 0x%X",
1297 response_pdu.size(), expected_data_size + 2, static_cast<uint8_t>(function_code));
1307 switch (function_code) {
1323 for (
size_t i = 0; i != count_or_value; i++) {
1327 std::span<const uint16_t> register_span(registers.data(), registers.size());
1345 std::span<const uint8_t> packed_bytes;
1348 packed_bytes = response_pdu.subspan(2);
1349 count = count_or_value;
1351 PackedBits bits(packed_bytes,
count);
1364 const uint16_t
value = (
succeeded(status) && response_pdu.size() >= WRITE_SINGLE_PDU_SIZE)
1380 for (
size_t i = 0; i != count_or_value; i++) {
1383 std::span<const uint16_t> register_span(registers.data(), registers.size());
1392 std::span<const uint8_t> packed_bytes = request_pdu.subspan(6);
1393 PackedBits bits(packed_bytes, count_or_value);
1406 const uint8_t function_code = request_pdu.empty() ? 0 : request_pdu[0];
1411 ESP_LOGW(TAG,
"Non-standard request or response for function code 0x%X. No on_custom_response handler declared",
1414 ESP_LOGV(TAG,
"Non-standard request or response for function code 0x%X (unhandled)", function_code);
bool cancel_timeout(const char *name)
Cancel a timeout function.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
virtual void digital_write(bool value)=0
Minimal static vector - saves memory by avoiding std::vector overhead.
void push_back(const T &value)
virtual void on_response(std::span< const uint8_t > request_pdu, std::span< const uint8_t > response_pdu)
Low-level response hook: called with the request PDU this device sent and the response PDU received T...
virtual void on_write_multiple_coils(uint16_t start_address, PackedBits bits, ResponseStatus status)
virtual void on_read_holding_registers(uint16_t start_address, std::span< const uint16_t > registers, ResponseStatus status)
virtual void on_write_multiple_registers(uint16_t start_address, std::span< const uint16_t > registers, ResponseStatus status)
virtual void on_sent(std::span< const uint8_t > request_pdu)
Called when this device's frame is actually written to the wire.
virtual void on_write_single_register(uint16_t address, uint16_t value, ResponseStatus status)
Write acknowledgements.
virtual bool on_no_response(std::span< const uint8_t > request_pdu)
Called when no matching, uninterrupted response arrived; return true to have the hub re-queue the fra...
virtual void on_custom_response(std::span< const uint8_t > request_pdu, std::span< const uint8_t > response_pdu, ResponseStatus status)
Catch-all for custom function codes and anything that is not a standard-conformant transaction (see d...
virtual void on_read_discrete_inputs(uint16_t start_address, PackedBits bits, ResponseStatus status)
virtual void on_error(std::span< const uint8_t > request_pdu, ExceptionCode exception_code)
Low-level error hook: called with the request PDU and the modbus exception code from the error respon...
virtual void on_not_sent(std::span< const uint8_t > request_pdu)
Called when an accepted request was dropped before transmission by clear_tx_queue_for_address().
virtual void on_read_coils(uint16_t start_address, PackedBits bits, ResponseStatus status)
virtual void on_write_single_coil(uint16_t address, bool value, ResponseStatus status)
bool custom_response_warned_
virtual void on_read_input_registers(uint16_t start_address, std::span< const uint16_t > registers, ResponseStatus status)
void clear_tx_queue_for_device(ModbusClientDevice *device)
void parse_modbus_frames() override
ModbusDeviceCommand * find_waiting_()
std::span< const uint8_t > pdu
uint32_t turnaround_delay_us_
void dump_config() override
bool waiting_for_response_
ModbusDeviceCommand * select_next_ready_()
uint32_t send_wait_time_us_
int32_t tx_delay_remaining() override
std::deque< ModbusDeviceCommand > tx_buffer_
bool tx_blocked() override
std::span< const uint8_t > ModbusClientDevice * device
bool queue_pdu(uint8_t address, std::span< const uint8_t > pdu, ModbusClientDevice *device=nullptr, CommandOptions options={})
Queue a request.
void clear_tx_queue_for_address(uint8_t address)
void process_modbus_server_frame(uint8_t address, std::span< const uint8_t > pdu) override
uint32_t long_rx_buffer_delay_us_
virtual void process_modbus_server_frame(uint8_t address, std::span< const uint8_t > pdu)=0
bool parse_modbus_server_frame_()
virtual void parse_modbus_frames()=0
bool send_frame_(const ModbusFrame &frame)
uint32_t last_modbus_byte_
GPIOPin * flow_control_pin_
uint32_t last_send_tx_offset_
virtual bool tx_blocked()
void clear_rx_buffer_(const LogString *reason, bool warn=false, size_t bytes_to_clear=0)
float get_setup_priority() const override
bool exceeded_rx_full_threshold_
virtual int32_t tx_delay_remaining()
uint16_t find_frame_end_by_crc_(uint16_t min_length) const
std::vector< uint8_t > rx_buffer_
uint32_t rx_detect_latency_us_
uint32_t last_receive_check_
virtual ResponseStatus on_read_coils(uint16_t start_address, MutablePackedBits bits)
virtual ResponseStatus on_write_registers(uint16_t start_address, const RegisterValues ®isters)
virtual ResponseStatus on_read_holding_registers(uint16_t start_address, uint16_t number_of_registers, RegisterValues ®isters)
virtual ResponseStatus on_read_discrete_inputs(uint16_t start_address, MutablePackedBits bits)
virtual ResponseStatus on_write_coils(uint16_t start_address, PackedBits bits)
Coil writes deliver the values as a PackedBits view over the hub's receive buffer (only valid during ...
virtual ResponseStatus on_read_input_registers(uint16_t start_address, uint16_t number_of_registers, RegisterValues ®isters)
std::vector< ModbusServerDevice * > devices_
ResponseStatus check_address_range_(uint16_t start_address, uint16_t count)
ResponseStatus parse_read_request_(std::span< const uint8_t > data, uint16_t max_entities, const LogString *entity_name, uint16_t &start_address, uint16_t &count)
void process_modbus_client_frame_(uint8_t address, uint8_t function_code, std::span< const uint8_t > data)
void parse_modbus_frames() override
void process_modbus_server_frame(uint8_t address, std::span< const uint8_t > pdu) override
ResponseStatus parse_write_multiple_coils_(std::span< const uint8_t > data, uint16_t &start_address, uint16_t &count, std::span< const uint8_t > &packed_bytes)
void process_broadcast_frame_(uint8_t function_code, std::span< const uint8_t > data)
uint16_t deferred_payload_len_
ModbusServerDevice * find_device_(uint8_t address)
ResponseStatus parse_write_single_coil_(std::span< const uint8_t > data, uint16_t &start_address, bool &value)
bool build_or_reject_read_response_(uint8_t address, uint8_t function_code, ResponseStatus status, uint16_t number_of_registers, const RegisterValues ®isters, std::span< uint8_t > response_buffer, uint16_t &response_len)
void send_exception_(uint8_t address, uint8_t function_code, ExceptionCode exception_code)
void assemble_registers_(std::span< const uint8_t > values, RegisterValues ®isters)
void send_raw_(const uint8_t *payload, uint16_t len)
bool parse_modbus_client_frame_()
void dump_config() override
void send_response_(uint8_t address, uint8_t function_code, const uint8_t *payload, uint16_t payload_len)
ResponseStatus parse_write_multiple_(std::span< const uint8_t > data, uint16_t &start_address, RegisterValues ®isters)
bool rejected_(uint8_t address, uint8_t function_code, ResponseStatus status)
ResponseStatus parse_write_single_(std::span< const uint8_t > data, uint16_t &start_address, RegisterValues ®isters)
std::array< uint8_t, MAX_RAW_SIZE > deferred_payload_
uint8_t expecting_peer_response_
Mutable counterpart of PackedBits: set() writes bits in place (deliberately no proxy operator[]=).
Read-only view of Modbus-packed bits: bit 0 of byte 0 is the first bit (LSB first),...
UARTParityOptions get_parity() const
uint8_t get_data_bits() const
uint8_t get_stop_bits() const
uint32_t get_baud_rate() const
static constexpr size_t RX_FULL_THRESHOLD_UNSET
size_t get_rx_full_threshold()
optional< std::array< uint8_t, N > > read_array()
void write_array(const uint8_t *data, size_t len)
uint8_t UARTParityOptions uint8_t data_bits
bool address_range_fits(uint16_t start_address, size_t count)
bool is_function_code_read_only(uint8_t function_code)
uint8_t client_frame_data_offset(const uint8_t *, size_t)
T get_data(const uint8_t *data, size_t buffer_offset)
Extract data from modbus response buffer.
bool is_function_code_read(uint8_t function_code)
bool is_function_code_write(uint8_t function_code)
bool is_function_code_broadcastable(uint8_t function_code)
True when the underlying function code (exception bit masked off) may be broadcast (address 0).
uint16_t client_frame_length(const uint8_t *frame, size_t size)
bool is_server_pdu_standard(const uint8_t *pdu, size_t size)
uint16_t server_frame_length(const uint8_t *frame, size_t size)
bool is_function_code_unknown_length(uint8_t function_code)
True for any function code whose frame length the parsers cannot predict - everything the server_pdu_...
bool is_client_pdu_standard(const uint8_t *pdu, size_t size)
bool is_function_code_exception(uint8_t function_code)
const uint8_t FUNCTION_CODE_MASK
StaticVector< uint16_t, MAX_NUM_OF_REGISTERS_TO_READ > RegisterValues
const uint8_t FUNCTION_CODE_EXCEPTION_MASK
std::optional< ExceptionCode > ResponseStatus
bool succeeded(ResponseStatus status)
True when a transaction carried no exception.
@ WRITE_MULTIPLE_REGISTERS
@ READ_WRITE_MULTIPLE_REGISTERS
constexpr size_t packed_bit_bytes(size_t bits)
Bits pack 8 per data byte, rounded up to whole bytes.
ESPHOME_LOG_TAG(TAG, "modbus")
constexpr float BUS
For communication buses like i2c/spi.
value[LAYOUT.day_index()]
@ UART_CONFIG_PARITY_NONE
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse_poly, bool refin, bool refout)
Calculate a CRC-16 checksum of data with size len.
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
uint32_t IRAM_ATTR HOT micros()
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
void HOT delay(uint32_t ms)
constexpr std::array< uint8_t, sizeof(T)> decode_value(T val)
Decode a value into its constituent bytes (from most to least significant).
bool response(std::span< const uint8_t > response_pdu)
void complete_broadcast()
CommandPriority priority() const
bool error(ExceptionCode exception_code)
bool waiting_state() const
void requeue(uint16_t seq)
bool fire_and_forget() const
ModbusClientDevice * device
SmallInlineBuffer< MODBUS_FRAME_INLINE_SIZE > data
std::span< const uint8_t > pdu() const
A PDU is [function code][data...] without address or CRC.