19static constexpr uint16_t MODBUS_TX_BUFFER_SIZE = 15;
20static constexpr uint16_t MODBUS_TX_MAX_DELAY_MS = 5;
24static constexpr uint16_t MODBUS_FRAME_INLINE_SIZE = 8;
34 uint8_t *buf = this->data.
init(pdu_len + 3);
36 memcpy(buf + 1, pdu, pdu_len);
37 auto crc =
crc16(buf, pdu_len + 1);
38 buf[pdu_len + 1] = crc >> 0;
39 buf[pdu_len + 2] = crc >> 8;
42 uint16_t
size()
const {
return static_cast<uint16_t
>(this->data.
size()); }
49 void setup()
override;
65 void clear_rx_buffer_(
const LogString *reason,
bool warn =
false,
size_t bytes_to_clear = 0);
104 ESPDEPRECATED(
"Use send_pdu() with create_client_pdu() instead. Removed in 2026.10.0",
"2026.4.0")
155 uint16_t number_of_registers);
158 void send_response_(uint8_t
address, uint8_t function_code,
const uint8_t *payload, uint16_t payload_len);
185 virtual void on_response(std::span<const uint8_t> request_pdu, std::span<const uint8_t> response_pdu) {}
192#pragma GCC diagnostic push
193#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
194 this->on_modbus_not_sent();
195#pragma GCC diagnostic pop
201#pragma GCC diagnostic push
202#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
203 return this->on_modbus_no_response();
204#pragma GCC diagnostic pop
207 ESPDEPRECATED(
"Override on_not_sent() instead. Removed in 2027.2.0",
"2026.8.0")
208 virtual
void on_modbus_not_sent() {}
210 ESPDEPRECATED(
"Override on_no_response() instead. Removed in 2027.2.0",
"2026.8.0")
211 virtual
bool on_modbus_no_response() {
return false; }
212 void send(uint8_t function, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len = 0,
213 const uint8_t *payload =
nullptr) {
216 payload, payload_len),
227 ESPDEPRECATED(
"Use ready_for_immediate_send() instead. Removed in 2026.9.0",
"2026.3.0")
238using ModbusDevice
ESPDEPRECATED(
"Use ModbusClientDevice instead. Removed in 2026.12.0",
239 "2026.6.0") = ModbusClientDevice;
Small buffer optimization - stores data inline when small, heap-allocates for large data This avoids ...
uint8_t * init(size_t size)
Resize to size bytes of (uninitialized) storage and return a writable pointer to fill.
Minimal static vector - saves memory by avoiding std::vector overhead.
virtual void on_response(std::span< const uint8_t > request_pdu, std::span< const uint8_t > response_pdu)
Called with the request PDU this device sent and the response PDU received (both: function code + dat...
void send(uint8_t function, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len=0, const uint8_t *payload=nullptr)
ModbusClientDevice()=default
ModbusClientDevice & operator=(ModbusClientDevice &&)=delete
void send_pdu(std::span< const uint8_t > pdu)
virtual void on_not_sent()
ModbusClientDevice(const ModbusClientDevice &)=delete
bool ready_for_immediate_send()
ModbusClientDevice & operator=(const ModbusClientDevice &)=delete
void set_parent(ModbusClientHub *parent)
ModbusClientDevice(ModbusClientDevice &&)=delete
ESPDEPRECATED("Override on_no_response() instead. Removed in 2027.2.0", "2026.8.0") virtual bool on_modbus_no_response()
ModbusClientDevice(ModbusClientHub *parent, uint8_t address)
virtual ~ModbusClientDevice()
void set_address(uint8_t address)
virtual void on_error(std::span< const uint8_t > request_pdu, ModbusExceptionCode exception_code)
Called with the request PDU and the modbus exception code decoded from the error response.
ModbusClientHub * parent_
ESPDEPRECATED("Use ready_for_immediate_send() instead. Removed in 2026.9.0", "2026.3.0") bool waiting_for_response()
void send_raw(const std::vector< uint8_t > &payload)
void clear_tx_queue_for_address(bool clear_sent=true)
virtual bool on_no_response()
Called when no (valid) response arrived; return true to have the hub re-queue the frame for a retry.
void clear_tx_queue_for_device()
ESPDEPRECATED("Override on_not_sent() instead. Removed in 2027.2.0", "2026.8.0") virtual void on_modbus_not_sent()
std::optional< ModbusDeviceCommand > waiting_for_response_
uint8_t uint16_t start_address
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
ModbusClientHub()=default
void clear_tx_queue_for_address(uint8_t address, bool clear_sent=true)
uint8_t uint16_t uint16_t number_of_entities
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)
ESPDEPRECATED("Use send_pdu() with create_client_pdu() instead. Removed in 2026.10.0", "2026.4.0") void send(uint8_t address
std::deque< ModbusDeviceCommand > tx_buffer_
bool tx_blocked() override
uint8_t uint16_t uint16_t uint8_t payload_len
void send_pdu(uint8_t address, std::span< const uint8_t > pdu, ModbusClientDevice *device=nullptr)
void process_modbus_server_frame(uint8_t address, std::span< const uint8_t > pdu) override
void set_send_wait_time(uint16_t time_in_ms)
void set_turnaround_time(uint16_t time_in_ms)
void set_flow_control_pin(GPIOPin *flow_control_pin)
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
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_
ModbusServerDevice & operator=(ModbusServerDevice &&)=delete
virtual ~ModbusServerDevice()=default
ModbusServerDevice(const ModbusServerDevice &)=delete
ModbusServerDevice & operator=(const ModbusServerDevice &)=delete
ModbusServerDevice()=default
void set_address(uint8_t address)
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_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)
uint8_t get_address() const
ModbusServerDevice(ModbusServerDevice &&)=delete
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
void process_modbus_server_frame(uint8_t address, std::span< const uint8_t > pdu) override
bool check_register_range_(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_registers)
ModbusServerHub()=default
uint16_t deferred_payload_len_
void register_device(ModbusServerDevice *device)
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)
std::array< uint8_t, MAX_RAW_SIZE > deferred_payload_
uint8_t expecting_peer_response_
StaticVector< uint8_t, MAX_PDU_SIZE > create_client_pdu(ModbusFunctionCode function_code, uint16_t start_address, uint16_t number_of_entities, const uint8_t *values, size_t values_len)
Create a modbus clinet pdu for reading/writing single/multiple coils/register/inputs.
std::optional< ModbusExceptionCode > ResponseStatus
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.
ModbusDeviceCommand(ModbusClientDevice *device, uint8_t address, const uint8_t *src, uint16_t len)
ModbusClientDevice * device
ModbusFrame(uint8_t address, const uint8_t *pdu, uint16_t pdu_len)
SmallInlineBuffer< MODBUS_FRAME_INLINE_SIZE > data