ESPHome 2026.8.0b4
Loading...
Searching...
No Matches
modbus_server.h
Go to the documentation of this file.
1#pragma once
2
4
8
9#include <utility>
10#include <vector>
11
12namespace esphome::modbus_server {
13
15
17 bool enabled{false};
18 uint16_t register_last_address{0xFFFF};
19 uint16_t register_value{0};
20};
21
23 using ReadLambda = std::function<optional<int64_t>()>;
24 using WriteLambda = std::function<bool(int64_t value)>;
25
26 public:
28 this->address = address;
29 this->value_type = value_type;
30 this->register_count = register_count;
31 }
32
35 template<typename T> void set_read_lambda(const std::function<optional<T>(uint16_t address)> &&user_read_lambda) {
36 this->read_lambda = [this, user_read_lambda]() -> optional<int64_t> {
37 const optional<T> user_value = user_read_lambda(this->address);
38 if (!user_value.has_value()) {
39 return {};
40 }
41 if constexpr (std::is_same_v<T, float>) {
42 return bit_cast<uint32_t>(*user_value);
43 } else {
44 return static_cast<int64_t>(*user_value);
45 }
46 };
47 }
48
49 template<typename T>
50 void set_write_lambda(const std::function<bool(uint16_t address, const T v)> &&user_write_lambda) {
51 this->write_lambda = [this, user_write_lambda](int64_t number) {
52 if constexpr (std::is_same_v<T, float>) {
53 float float_value = bit_cast<float>(static_cast<uint32_t>(number));
54 return user_write_lambda(this->address, float_value);
55 }
56 return user_write_lambda(this->address, static_cast<T>(number));
57 };
58 }
59
60 // max 44: float with %.1f can be up to 42 chars (3.4e38 → 39 integer digits + sign + decimal + 1 digit)
61 // plus null terminator = 43, rounded to 44 for 4-byte alignment
62 static constexpr size_t FORMAT_VALUE_BUF_SIZE = 44;
63
64 // Formats a raw value into a caller-provided buffer based on the value type for debugging.
65 // Returns buf for convenience.
66 const char *format_value(int64_t value, char *buf, size_t buf_size) const {
67 switch (this->value_type) {
74 buf_append_printf(buf, buf_size, 0, "%" PRIu64, static_cast<uint64_t>(value));
75 return buf;
82 buf_append_printf(buf, buf_size, 0, "%" PRId64, value);
83 return buf;
86 buf_append_printf(buf, buf_size, 0, "%.1f", bit_cast<float>(static_cast<uint32_t>(value)));
87 return buf;
88 default:
89 buf_append_printf(buf, buf_size, 0, "%" PRId64, value);
90 return buf;
91 }
92 }
93
94 void set_allow_partial_read(bool allow_partial_read) { this->allow_partial_read = allow_partial_read; }
95
96 uint16_t address{0};
98 uint8_t register_count{0};
99 // When true, a read may cover only part of this multi-register value; otherwise it must read the whole value.
101 ReadLambda read_lambda;
102 WriteLambda write_lambda;
103};
104
111 using ReadLambda = std::function<optional<bool>(uint16_t address)>;
112 using WriteLambda = std::function<bool(uint16_t address, bool value)>;
113
114 public:
115 explicit ServerBit(uint16_t address) : address(address) {}
116 void set_read_lambda(ReadLambda &&read_lambda) { this->read_lambda = std::move(read_lambda); }
117 void set_write_lambda(WriteLambda &&write_lambda) { this->write_lambda = std::move(write_lambda); }
118
119 uint16_t address{0};
120 ReadLambda read_lambda;
121 WriteLambda write_lambda;
122};
123
125 public:
126 void dump_config() override;
127
129 void add_server_register(ServerRegister *server_register) { server_registers_.push_back(server_register); }
131 void add_server_bit(ServerBit *server_bit) { server_bits_.push_back(server_bit); }
133 modbus::ResponseStatus on_read_registers(uint16_t start_address, uint16_t number_of_registers,
134 modbus::RegisterValues &registers) final;
136 modbus::ResponseStatus on_write_registers(uint16_t start_address, const modbus::RegisterValues &registers) final;
139 modbus::ResponseStatus on_read_bits(uint16_t start_address, modbus::MutablePackedBits bits) final;
141 modbus::ResponseStatus on_write_coils(uint16_t start_address, modbus::PackedBits bits) final;
143 void set_server_courtesy_response(const ServerCourtesyResponse &server_courtesy_response) {
144 this->server_courtesy_response_ = server_courtesy_response;
145 }
148
149 protected:
153 ServerBit *find_bit_(uint16_t address) const;
155 std::vector<ServerRegister *> server_registers_{};
157 std::vector<ServerBit *> server_bits_{};
160 .enabled = false, .register_last_address = 0xFFFF, .register_value = 0};
161};
162
163} // namespace esphome::modbus_server
uint8_t address
Definition bl0906.h:4
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:227
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),...
modbus::ResponseStatus on_write_registers(uint16_t start_address, const modbus::RegisterValues &registers) final
called when a modbus request (function code 0x06 or 0x10) was parsed without errors
modbus::ResponseStatus on_read_bits(uint16_t start_address, modbus::MutablePackedBits bits) final
called when a modbus request (function code 0x01 or 0x02) was parsed without errors; both are served ...
void set_server_courtesy_response(const ServerCourtesyResponse &server_courtesy_response)
Called by esphome generated code to set the server courtesy response object.
ServerCourtesyResponse get_server_courtesy_response() const
Get the server courtesy response object.
void add_server_bit(ServerBit *server_bit)
Registers a server bit with the controller. Called by esphomes code generator.
ServerRegister * find_containing_register_(uint32_t address) const
Find the registered value whose register span contains address, or nullptr if none does.
std::vector< ServerBit * > server_bits_
Collection of all server bits (coils/discrete inputs) for this component.
std::vector< ServerRegister * > server_registers_
Collection of all server registers for this component.
modbus::ResponseStatus on_read_registers(uint16_t start_address, uint16_t number_of_registers, modbus::RegisterValues &registers) final
called when a modbus request (function code 0x03 or 0x04) was parsed without errors
ServerBit * find_bit_(uint16_t address) const
Find the registered bit at address, or nullptr if none is.
modbus::ResponseStatus on_write_coils(uint16_t start_address, modbus::PackedBits bits) final
called when a modbus request (function code 0x05 or 0x0F) was parsed without errors
void add_server_register(ServerRegister *server_register)
Registers a server register with the controller. Called by esphomes code generator.
ServerCourtesyResponse server_courtesy_response_
Server courtesy response.
A single bit in the server's coil/discrete-input table.
void set_write_lambda(WriteLambda &&write_lambda)
void set_read_lambda(ReadLambda &&read_lambda)
void set_allow_partial_read(bool allow_partial_read)
void set_write_lambda(const std::function< bool(uint16_t address, const T v)> &&user_write_lambda)
const char * format_value(int64_t value, char *buf, size_t buf_size) const
ServerRegister(uint16_t address, SensorValueType value_type, uint8_t register_count)
void set_read_lambda(const std::function< optional< T >(uint16_t address)> &&user_read_lambda)
The user lambda returns optional<T>: an empty optional declines the read, answering the whole request...
static constexpr size_t FORMAT_VALUE_BUF_SIZE
std::optional< ExceptionCode > ResponseStatus
Definition modbus.h:336
To bit_cast(const From &src)
Convert data between types, without aliasing issues or undefined behaviour.
Definition helpers.h:84
static void uint32_t