18static constexpr uint16_t MODBUS_TX_BUFFER_SIZE = 15;
19static constexpr uint16_t MODBUS_TX_MAX_DELAY_MS = 5;
23static constexpr uint16_t MODBUS_FRAME_INLINE_SIZE = 8;
33 uint8_t *buf = this->data.
init(pdu_len + 3);
35 memcpy(buf + 1, pdu, pdu_len);
36 auto crc =
crc16(buf, pdu_len + 1);
37 buf[pdu_len + 1] = crc >> 0;
38 buf[pdu_len + 2] = crc >> 8;
41 uint16_t
size()
const {
return static_cast<uint16_t
>(this->data.
size()); }
48 void setup()
override;
64 void clear_rx_buffer_(
const LogString *reason,
bool warn =
false,
size_t bytes_to_clear = 0);
103 ESPDEPRECATED(
"Use send_pdu() with create_client_pdu() instead. Removed in 2026.10.0",
"2026.4.0")
156 uint16_t number_of_registers);
159 void send_response_(uint8_t
address, uint8_t function_code,
const uint8_t *payload, uint16_t payload_len);
190 void send(uint8_t function, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len = 0,
191 const uint8_t *payload =
nullptr) {
194 payload, payload_len),
205 ESPDEPRECATED(
"Use ready_for_immediate_send() instead. Removed in 2026.9.0",
"2026.3.0")
216using ModbusDevice
ESPDEPRECATED(
"Use ModbusClientDevice instead. Removed in 2026.12.0",
217 "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 bool on_modbus_no_response()
Called when no (valid) response arrived; return true to have the hub re-queue the frame for a retry.
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)
ModbusClientDevice(const ModbusClientDevice &)=delete
virtual void on_modbus_data(const std::vector< uint8_t > &data)
bool ready_for_immediate_send()
ModbusClientDevice & operator=(const ModbusClientDevice &)=delete
void set_parent(ModbusClientHub *parent)
virtual void on_modbus_not_sent()
ModbusClientDevice(ModbusClientDevice &&)=delete
ModbusClientDevice(ModbusClientHub *parent, uint8_t address)
virtual ~ModbusClientDevice()
void set_address(uint8_t address)
ModbusClientHub * parent_
ESPDEPRECATED("Use ready_for_immediate_send() instead. Removed in 2026.9.0", "2026.3.0") bool waiting_for_response()
virtual void on_modbus_error(uint8_t function_code, uint8_t exception_code)
void send_raw(const std::vector< uint8_t > &payload)
void clear_tx_queue_for_address(bool clear_sent=true)
void clear_tx_queue_for_device()
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
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)
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 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)
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_
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
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)
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_
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