8static const char *
const TAG =
"modbus";
11static constexpr size_t MODBUS_MAX_LOG_BYTES = 64;
14static constexpr uint32_t MODBUS_BITS_PER_CHAR = 11;
16static constexpr uint32_t MS_PER_SEC = 1000;
31 static constexpr uint16_t DEFAULT_LONG_RX_BUFFER_DELAY_MS = 50;
36 : DEFAULT_LONG_RX_BUFFER_DELAY_MS;
56 (this->
rx_buffer_.empty() || this->rx_buffer_[0] != expected_address)) {
57 ESP_LOGW(TAG,
"Stop waiting for response from %" PRIu8
" %" PRIu32
"ms after last send", expected_address,
72 const uint16_t timeout = std::max(
74 (uint16_t) (this->
rx_buffer_.size() >= this->parent_->get_rx_full_threshold() ? this->long_rx_buffer_delay_ms_
87 return std::max({(int32_t) 0,
94 return std::max({(int32_t) 0,
132 if (buffer_size == 0) {
133 ESP_LOGV(TAG,
"Received first byte %" PRIu8
" (0X%x) of %zu bytes %" PRIu32
"ms after last send",
146 }
while (!this->
rx_buffer_.empty() && size > this->rx_buffer_.size());
155 ESP_LOGVV(TAG,
"Parsing frames buffer size = %" PRIu32,
size);
156 bool retry_as_client =
false;
159 ESP_LOGV(TAG,
"Stop expecting peer response from %" PRIu8
" due to parse failure, and retry parse",
162 retry_as_client =
true;
166 "Stop expecting peer response from %" PRIu8
" due to timeout after partial response, and retry parse",
169 retry_as_client =
true;
188 for (uint16_t
len = min_length;
len <= std::min(
size,
size_t(MAX_FRAME_SIZE));
len++) {
199 if (
size < frame_length)
207 if (frame_length == 0)
208 return size < MAX_FRAME_SIZE;
209 ESP_LOGD(TAG,
"User-defined function %02X found", function_code);
218 const uint8_t *data = this->
rx_buffer_.data() + data_offset;
219 uint16_t data_len = frame_length - 2 - data_offset;
231 if (
size < frame_length)
239 if (frame_length == 0)
240 return size < MAX_FRAME_SIZE;
241 ESP_LOGD(TAG,
"User-defined function %02X found", function_code);
251 uint16_t data_len = frame_length - 2 - data_offset;
252 uint8_t data[MAX_FRAME_SIZE] = {};
253 std::memcpy(data, this->
rx_buffer_.data() + data_offset, data_len);
265 "Received unexpected frame from address %" PRIu8
", function code 0x%X, %" PRIu32
"ms after last send",
276 "Received incorrect frame address %" PRIu8
" <> %" PRIu8
" or function code 0x%X <> 0x%X, %" PRIu32
277 "ms after last send",
289 "Ignoring response from %" PRIu8
" - transmission interrupted by previous unexpected response, %" PRIu32
290 "ms after last send",
299 uint8_t exception =
len > 0 ? data[0] : 0;
301 "Error function code: 0x%X exception: %" PRIu8
", address: %" PRIu8
", %" PRIu32
"ms after last send",
310 ESP_LOGV(TAG,
"Ignoring response from %" PRIu8
" - no callback device set, %" PRIu32
"ms after last send",
319 ESP_LOGE(TAG,
"Unexpected response from address %" PRIu8
", which is mapped to this device.",
address);
323 ESP_LOGV(TAG,
"Expected response from peer %" PRIu8
" received",
address);
325 ESP_LOGV(TAG,
"Unexpected response from peer %" PRIu8
" received",
address);
334 for (
auto *device : this->
devices_) {
335 if (device->get_address() ==
address) {
343 uint16_t number_of_registers) {
344 if ((
uint32_t) start_address + number_of_registers > 0x10000u) {
345 ESP_LOGW(TAG,
"Register address out of range - start: %" PRIu16
" num: %" PRIu16, start_address,
346 number_of_registers);
355 if (device ==
nullptr) {
357 ESP_LOGV(TAG,
"Request to peer %" PRIu8
" received",
address);
362 uint8_t response_buffer[modbus::MAX_RAW_SIZE];
363 const uint8_t *response_data = response_buffer;
364 uint16_t response_len = 0;
372 if (number_of_registers == 0 || number_of_registers > MAX_NUM_OF_REGISTERS_TO_READ) {
373 ESP_LOGW(TAG,
"Invalid number of registers %" PRIu16, number_of_registers);
394 if (registers.
size() != number_of_registers) {
395 ESP_LOGE(TAG,
"Incorrect response %" PRIu16
" requested, %zu returned", number_of_registers, registers.
size());
400 response_buffer[response_len++] =
static_cast<uint8_t
>(number_of_registers * 2);
401 for (
auto r : registers) {
403 response_buffer[response_len++] = register_bytes[0];
404 response_buffer[response_len++] = register_bytes[1];
415 uint16_t number_of_registers = 1;
416 uint16_t values_offset = 2;
421 if (number_of_registers == 0 || number_of_registers > MAX_NUM_OF_REGISTERS_TO_WRITE ||
422 number_of_registers * 2 != number_of_bytes) {
423 ESP_LOGW(TAG,
"Invalid number of registers %" PRIu16
" or bytes %" PRIu8, number_of_registers,
434 for (uint16_t i = 0; i < number_of_registers; i++) {
438 response_data = data;
443 ESP_LOGW(TAG,
"Unsupported function code %" PRIu8, function_code);
450 this->
send_response_(address, function_code, response_data, response_len);
456 ESP_LOGE(TAG,
"Attempted to send while transmission blocked");
459 if (frame.
size() > MAX_FRAME_SIZE) {
460 ESP_LOGE(TAG,
"Attempted to send frame larger than max frame size of %" PRIu16
" bytes", MAX_FRAME_SIZE);
465 if (tx_delay_remaining > 0) {
481#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
484 ESP_LOGV(TAG,
"Write: %s %" PRIu32
"ms after last send, %" PRIu32
"ms after last receive",
486 now - this->last_modbus_byte_);
512 ESP_LOGV(TAG,
"Write queue contains %zu items.", this->
tx_buffer_.size());
519 " Send Wait Time: %" PRIu16
" ms\n"
520 " Turnaround Time: %" PRIu16
" ms\n"
521 " Frame Delay: %" PRIu16
" ms\n"
522 " Long Rx Buffer Delay: %" PRIu16
" ms",
530 " Frame Delay: %" PRIu16
" ms\n"
531 " Long Rx Buffer Delay: %" PRIu16
" ms",
542 uint16_t payload_len) {
545 if (payload_len + 2 > MAX_RAW_SIZE) {
546 ESP_LOGE(TAG,
"Server response too large (%" PRIu16
" bytes)",
static_cast<uint16_t
>(payload_len + 2));
549 uint8_t raw_frame[MAX_RAW_SIZE];
551 raw_frame[1] = function_code;
552 std::memcpy(raw_frame + 2, payload, payload_len);
553 this->
send_raw_(raw_frame, payload_len + 2);
557 uint8_t raw_frame[3];
560 raw_frame[2] =
static_cast<uint8_t
>(exception_code);
566 if (wfr.
device ==
nullptr)
571 if (retry && wfr.
device !=
nullptr)
579 if (this->
tx_buffer_.size() >= MODBUS_TX_BUFFER_SIZE) {
580 ESP_LOGE(TAG,
"Write buffer full, dropped retry for address %" PRIu8, frame.
data.
data()[0]);
581 if (wfr.
device !=
nullptr)
596 if (this->
tx_buffer_.size() < MODBUS_TX_BUFFER_SIZE) {
597#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
603#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
616 std::remove_if(tx_buffer.begin(), tx_buffer.end(),
620 if (clear_sent && this->
waiting_for_response_.has_value() && this->waiting_for_response_.value().device) {
622 ESP_LOGV(TAG,
"Clearing waiting for response for address %" PRIu8,
address);
631 tx_buffer.erase(std::remove_if(tx_buffer.begin(), tx_buffer.end(),
637 ESP_LOGV(TAG,
"Clearing waiting for response");
658 if (
len > MAX_RAW_SIZE) {
659 ESP_LOGE(TAG,
"Server send frame too large (%" PRIu16
" bytes)",
len);
673 this->deferred_payload_len_ - 1);
684 if (bytes_to_clear > 0 && bytes >= bytes_to_clear)
685 bytes = bytes_to_clear;
688 ESP_LOGW(TAG,
"Clearing buffer of %zu bytes - %s %" PRIu32
"ms after last send", bytes, LOG_STR_ARG(reason),
691 ESP_LOGV(TAG,
"Clearing buffer of %zu bytes - %s %" PRIu32
"ms after last send", bytes, LOG_STR_ARG(reason),
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 bool on_modbus_no_response()
Called when no (valid) response arrived; return true to have the hub re-queue the frame for a retry.
virtual void on_modbus_data(const std::vector< uint8_t > &data)
virtual void on_modbus_not_sent()
virtual void on_modbus_error(uint8_t function_code, uint8_t exception_code)
std::optional< ModbusDeviceCommand > waiting_for_response_
void clear_tx_queue_for_device(ModbusClientDevice *device)
void parse_modbus_frames() override
void requeue_waiting_frame_(ModbusDeviceCommand &wfr)
void dump_config() override
void notify_no_response_(ModbusDeviceCommand &wfr)
uint16_t turnaround_delay_ms_
uint8_t uint16_t uint16_t uint8_t const uint8_t ModbusClientDevice * device
void clear_tx_queue_for_address(uint8_t address, bool clear_sent=true)
void process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *data, uint16_t len) override
uint8_t uint16_t uint16_t uint8_t const uint8_t * payload
void queue_raw_(uint8_t address, const uint8_t *pdu, uint16_t pdu_len, ModbusClientDevice *device=nullptr)
int32_t tx_delay_remaining() override
void send_raw(const std::vector< uint8_t > &payload, ModbusClientDevice *device=nullptr)
std::deque< ModbusDeviceCommand > tx_buffer_
bool tx_blocked() override
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)
virtual void process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *data, uint16_t len)=0
float get_setup_priority() const override
uint16_t long_rx_buffer_delay_ms_
virtual int32_t tx_delay_remaining()
std::vector< uint8_t > rx_buffer_
uint16_t find_custom_frame_end_(uint16_t min_length) const
uint32_t last_receive_check_
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_input_registers(uint16_t start_address, uint16_t number_of_registers, RegisterValues ®isters)
std::vector< ModbusServerDevice * > devices_
void process_modbus_client_frame_(uint8_t address, uint8_t function_code, const uint8_t *data)
void parse_modbus_frames() override
bool check_register_range_(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_registers)
uint16_t deferred_payload_len_
ModbusServerDevice * find_device_(uint8_t address)
void send_exception_(uint8_t address, uint8_t function_code, ModbusExceptionCode exception_code)
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)
void process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *data, uint16_t len) override
std::array< uint8_t, MAX_RAW_SIZE > deferred_payload_
uint8_t expecting_peer_response_
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 server_frame_data_offset(const uint8_t *frame, size_t size)
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.
uint16_t client_frame_length(const uint8_t *frame, size_t size)
uint16_t server_frame_length(const uint8_t *frame, size_t size)
bool is_function_code_custom(uint8_t function_code)
bool is_function_code_exception(uint8_t function_code)
const uint8_t FUNCTION_CODE_MASK
const uint8_t FUNCTION_CODE_EXCEPTION_MASK
@ WRITE_MULTIPLE_REGISTERS
std::optional< ModbusExceptionCode > ResponseStatus
constexpr float BUS
For communication buses like i2c/spi.
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.
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).
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).
uint32_t IRAM_ATTR HOT millis()
ModbusClientDevice * device
SmallInlineBuffer< MODBUS_FRAME_INLINE_SIZE > data