ESPHome 2026.3.3
Loading...
Searching...
No Matches
modbus.h
Go to the documentation of this file.
1#pragma once
2
5
7
8#include <cstring>
9#include <memory>
10#include <vector>
11#include <queue>
12
13namespace esphome {
14namespace modbus {
15
16static constexpr uint16_t MODBUS_TX_BUFFER_SIZE = 15;
17
22
23class ModbusDevice;
24
26 // Frame with exact-size allocation to avoid std::vector overhead
27 std::unique_ptr<uint8_t[]> data;
28 uint16_t size; // Modbus RTU max is 256 bytes
29
30 ModbusDeviceCommand(const uint8_t *src, uint16_t len) : data(std::make_unique<uint8_t[]>(len + 2)), size(len + 2) {
31 std::memcpy(this->data.get(), src, len);
32 auto crc = crc16(data.get(), len);
33 data[len + 0] = crc >> 0;
34 data[len + 1] = crc >> 8;
35 }
36};
37
38class Modbus : public uart::UARTDevice, public Component {
39 public:
40 Modbus() = default;
41
42 void setup() override;
43
44 void loop() override;
45
46 void dump_config() override;
47
48 void register_device(ModbusDevice *device) { this->devices_.push_back(device); }
49
50 float get_setup_priority() const override;
51 bool tx_buffer_empty();
52 bool tx_blocked();
53
54 void send(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_entities,
55 uint8_t payload_len = 0, const uint8_t *payload = nullptr);
56 void send_raw(const std::vector<uint8_t> &payload);
57 void set_role(ModbusRole role) { this->role = role; }
58 void set_flow_control_pin(GPIOPin *flow_control_pin) { this->flow_control_pin_ = flow_control_pin; }
59 void set_send_wait_time(uint16_t time_in_ms) { this->send_wait_time_ = time_in_ms; }
60 void set_turnaround_time(uint16_t time_in_ms) { this->turnaround_delay_ms_ = time_in_ms; }
61 void set_disable_crc(bool disable_crc) { this->disable_crc_ = disable_crc; }
62
64
65 protected:
66 bool parse_modbus_byte_(uint8_t byte);
68 void clear_rx_buffer_(const LogString *reason, bool warn = false);
69 void send_next_frame_();
70 void queue_raw_(const uint8_t *data, uint16_t len);
71
75 uint16_t frame_delay_ms_{5};
77 uint16_t send_wait_time_{250};
78 uint16_t turnaround_delay_ms_{100};
80 bool disable_crc_{false};
81
83
84 std::vector<uint8_t> rx_buffer_;
85 std::vector<ModbusDevice *> devices_;
86 // std::deque is appropriate here since we need a FIFO buffer, and we can't know ahead of time how many
87 // requests will be queued. Each modbus component may queue multiple requests, and the sequence of scheduling
88 // may change at run time.
89 std::deque<ModbusDeviceCommand> tx_buffer_;
90};
91
93 public:
94 void set_parent(Modbus *parent) { parent_ = parent; }
95 void set_address(uint8_t address) { address_ = address; }
96 virtual void on_modbus_data(const std::vector<uint8_t> &data) = 0;
97 virtual void on_modbus_error(uint8_t function_code, uint8_t exception_code) {}
98 virtual void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers){};
99 virtual void on_modbus_write_registers(uint8_t function_code, const std::vector<uint8_t> &data){};
100 void send(uint8_t function, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len = 0,
101 const uint8_t *payload = nullptr) {
102 this->parent_->send(this->address_, function, start_address, number_of_entities, payload_len, payload);
103 }
104 void send_raw(const std::vector<uint8_t> &payload) { this->parent_->send_raw(payload); }
105 void send_error(uint8_t function_code, ModbusExceptionCode exception_code) {
106 std::vector<uint8_t> error_response;
107 error_response.reserve(3);
108 error_response.push_back(this->address_);
109 error_response.push_back(function_code | FUNCTION_CODE_EXCEPTION_MASK);
110 error_response.push_back(static_cast<uint8_t>(exception_code));
111 this->send_raw(error_response);
112 }
113 // If more than one device is connected block sending a new command before a response is received
114 ESPDEPRECATED("Use ready_for_immediate_send() instead. Removed in 2026.9.0", "2026.3.0")
115 bool waiting_for_response() { return !ready_for_immediate_send(); }
117
118 protected:
119 friend Modbus;
120
122 uint8_t address_;
123};
124
125} // namespace modbus
126} // namespace esphome
uint8_t address
Definition bl0906.h:4
void send_raw(const std::vector< uint8_t > &payload)
Definition modbus.h:104
void set_parent(Modbus *parent)
Definition modbus.h:94
virtual void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers)
Definition modbus.h:98
void send(uint8_t function, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len=0, const uint8_t *payload=nullptr)
Definition modbus.h:100
virtual void on_modbus_data(const std::vector< uint8_t > &data)=0
ESPDEPRECATED("Use ready_for_immediate_send() instead. Removed in 2026.9.0", "2026.3.0") bool waiting_for_response()
Definition modbus.h:114
void send_error(uint8_t function_code, ModbusExceptionCode exception_code)
Definition modbus.h:105
virtual void on_modbus_error(uint8_t function_code, uint8_t exception_code)
Definition modbus.h:97
void set_address(uint8_t address)
Definition modbus.h:95
virtual void on_modbus_write_registers(uint8_t function_code, const std::vector< uint8_t > &data)
Definition modbus.h:99
void set_flow_control_pin(GPIOPin *flow_control_pin)
Definition modbus.h:58
std::deque< ModbusDeviceCommand > tx_buffer_
Definition modbus.h:89
bool parse_modbus_byte_(uint8_t byte)
Definition modbus.cpp:120
void send_raw(const std::vector< uint8_t > &payload)
Definition modbus.cpp:384
void setup() override
Definition modbus.cpp:19
uint16_t frame_delay_ms_
Definition modbus.h:75
uint16_t send_wait_time_
Definition modbus.h:77
void set_turnaround_time(uint16_t time_in_ms)
Definition modbus.h:60
void set_role(ModbusRole role)
Definition modbus.h:57
uint8_t waiting_for_response_
Definition modbus.h:79
uint32_t last_modbus_byte_
Definition modbus.h:72
GPIOPin * flow_control_pin_
Definition modbus.h:82
void set_send_wait_time(uint16_t time_in_ms)
Definition modbus.h:59
std::vector< ModbusDevice * > devices_
Definition modbus.h:85
uint32_t last_send_tx_offset_
Definition modbus.h:74
void loop() override
Definition modbus.cpp:40
void queue_raw_(const uint8_t *data, uint16_t len)
Definition modbus.cpp:403
void set_disable_crc(bool disable_crc)
Definition modbus.h:61
float get_setup_priority() const override
Definition modbus.cpp:332
uint16_t long_rx_buffer_delay_ms_
Definition modbus.h:76
void dump_config() override
Definition modbus.cpp:320
void receive_and_parse_modbus_bytes_()
Definition modbus.cpp:92
void register_device(ModbusDevice *device)
Definition modbus.h:48
std::vector< uint8_t > rx_buffer_
Definition modbus.h:84
void clear_rx_buffer_(const LogString *reason, bool warn=false)
Definition modbus.cpp:414
void send(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len=0, const uint8_t *payload=nullptr)
Definition modbus.cpp:337
uint16_t turnaround_delay_ms_
Definition modbus.h:78
const uint8_t FUNCTION_CODE_EXCEPTION_MASK
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
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.
Definition helpers.cpp:73
std::string size_t len
Definition helpers.h:892
static void uint32_t
std::unique_ptr< uint8_t[]> data
Definition modbus.h:27
ModbusDeviceCommand(const uint8_t *src, uint16_t len)
Definition modbus.h:30