43 return uint32_t(start_address) + count <= 0x10000u;
99 return MIN_FRAME_SIZE;
108 return MIN_FRAME_SIZE;
125ESPDEPRECATED(
"Use server_pdu_payload() on the response PDU instead. Removed in 2027.2.0",
"2026.8.0")
126inline uint8_t server_frame_data_offset(const uint8_t *frame,
size_t size) {
151 return pdu.size() > offset ? pdu.subspan(offset) : std::span<const uint8_t>();
213inline uint8_t
c_to_hex(
char c) {
return (c >=
'A') ? (c >=
'a') ? (c -
'a' + 10) : (c -
'A' + 10) : (c -
'0'); }
224 if (value.length() <
pos * 2 + 2)
266template<
typename T> T
get_data(
const uint8_t *
data,
size_t buffer_offset) {
267 if (
sizeof(T) ==
sizeof(uint8_t)) {
268 return T(
data[buffer_offset]);
270 if (
sizeof(T) ==
sizeof(uint16_t)) {
271 return T((uint16_t(
data[buffer_offset + 0]) << 8) | (uint16_t(
data[buffer_offset + 1]) << 0));
273 if (
sizeof(T) ==
sizeof(
uint32_t)) {
277 if (
sizeof(T) ==
sizeof(uint64_t)) {
281 static_assert(
sizeof(T) ==
sizeof(uint8_t) ||
sizeof(T) ==
sizeof(uint16_t) ||
sizeof(T) ==
sizeof(
uint32_t) ||
282 sizeof(T) ==
sizeof(uint64_t),
283 "Unsupported type size in get_data; only 1, 2, 4, or 8-byte integer types are supported.");
287template<
typename T> T
get_data(
const std::vector<uint8_t> &
data,
size_t buffer_offset) {
300 auto data_byte = bit / 8;
301 return (
data[data_byte] & (1 << (bit % 8))) > 0;
305ESPDEPRECATED(
"Use bit_from_packed() instead. Removed in 2027.2.0",
"2026.8.0")
315template<
typename Out,
typename Bits>
void pack_bits(Out &out,
const Bits &bits) {
318 for (
bool b : bits) {
342 auto result = (mask &
data);
343 if (result == 0 || mask == 0xFFFFFFFF) {
346 for (
size_t pos = 0;
pos <
sizeof(N) << 3;
pos++) {
347 if (
pos < 32 && (mask & (1UL <<
pos)) != 0)
348 return result >>
pos;
360 switch (value_type) {
363 data.push_back(value & 0xFFFF);
367 data.push_back(
byteswap(
static_cast<uint16_t
>(value & 0xFFFF)));
372 data.push_back((value & 0xFFFF0000) >> 16);
373 data.push_back(value & 0xFFFF);
378 data.push_back(value & 0xFFFF);
379 data.push_back((value & 0xFFFF0000) >> 16);
383 data.push_back((value & 0xFFFF000000000000) >> 48);
384 data.push_back((value & 0xFFFF00000000) >> 32);
385 data.push_back((value & 0xFFFF0000) >> 16);
386 data.push_back(value & 0xFFFF);
390 data.push_back(value & 0xFFFF);
391 data.push_back((value & 0xFFFF0000) >> 16);
392 data.push_back((value & 0xFFFF00000000) >> 32);
393 data.push_back((value & 0xFFFF000000000000) >> 48);
419ESPDEPRECATED(
"Use the std::span overload returning std::optional<int64_t> instead. Removed in 2027.2.0",
"2026.8.0")
423 return payload_to_number(std::span<const uint8_t>(
data), sensor_value_type, offset, bitmask).value_or(0);
471 const uint8_t *values =
nullptr,
size_t values_len = 0);
495 uint16_t write_start_address,
496 std::span<const uint16_t> write_values);
553 val = llroundf(value);
560ESPDEPRECATED(
"Use the container overload of float_to_payload() instead. Removed in 2027.2.0",
"2026.8.0")
562 std::vector<uint16_t>
data;
Minimal static vector - saves memory by avoiding std::vector overhead.
Read-only view of Modbus-packed bits: bit 0 of byte 0 is the first bit (LSB first),...
bool value_type_is_float(SensorValueType v)
bool address_range_fits(uint16_t start_address, size_t count)
PduBuffer create_write_coils_pdu(uint16_t start_address, PackedBits bits)
Create modbus write multiple coils command (function 0x0F) from bits packed as on the wire.
bool is_function_code_read_only(uint8_t function_code)
uint8_t client_frame_data_offset(const uint8_t *, size_t)
void number_to_payload(Container &data, int64_t value, SensorValueType value_type)
Append the Modbus register words for value to data.
void float_to_payload(Container &data, float value, SensorValueType value_type)
Append a float converted to register words to any push_back container (heap-free with StaticVector).
WriteSinglePdu create_write_single_coil_pdu(uint16_t address, bool value)
Create modbus write single coil command Function 0x05 Write Single Coil.
std::span< const uint8_t > data
void pack_bits(Out &out, const Bits &bits)
Append packed bytes (LSB first) for the given bits onto a growable byte container.
T get_data(const uint8_t *data, size_t buffer_offset)
Extract data from modbus response buffer.
ReadPdu create_read_pdu(FunctionCode function_code, uint16_t start_address, uint16_t number_of_entities)
Create a modbus read request PDU.
bool is_function_code_read(uint8_t function_code)
bool is_function_code_write(uint8_t function_code)
WriteSinglePdu create_write_single_register_pdu(uint16_t start_address, uint16_t value)
Create modbus write single register command Function 0x06 Write Single Register.
PduBuffer create_client_pdu(FunctionCode function_code, uint16_t start_address, uint16_t number_of_entities, const uint8_t *values, size_t values_len)
Create a modbus client pdu for reading/writing single/multiple coils/register/inputs.
uint16_t server_pdu_length(const uint8_t *frame, size_t size)
FunctionCode modbus_register_write_function(EntityType reg_type, bool multiple=false)
std::span< const uint8_t > server_pdu_payload(std::span< const uint8_t > pdu)
Returns the payload portion of a server response PDU: the bytes after the function code,...
uint64_t qword_from_hex_str(const std::string &value, uint8_t pos)
Get a qword from a hex string.
uint16_t client_frame_length(const uint8_t *frame, size_t size)
N mask_and_shift_by_rightbit(N data, uint32_t mask)
Extract bits from value and shift right according to the bitmask if the bitmask is 0x00F0 we want the...
bool is_server_pdu_standard(const uint8_t *pdu, size_t size)
uint32_t dword_from_hex_str(const std::string &value, uint8_t pos)
Get a dword from a hex string.
FunctionCode modbus_register_read_function(EntityType reg_type)
uint8_t byte_from_hex_str(const std::string &value, uint8_t pos)
Get a byte from a hex string byte_from_hex_str("1122", 1) returns uint_8 value 0x22 == 34 byte_from_h...
uint16_t server_frame_length(const uint8_t *frame, size_t size)
bool is_function_code_unknown_length(uint8_t function_code)
True for any function code whose frame length the parsers cannot predict - everything the server_pdu_...
void log_unsupported_value_type(SensorValueType value_type)
bool is_function_code_custom(uint8_t function_code)
bool is_client_pdu_standard(const uint8_t *pdu, size_t size)
uint16_t word_from_hex_str(const std::string &value, uint8_t pos)
Get a word from a hex string.
PduBuffer create_write_registers_pdu(uint16_t start_address, std::span< const uint16_t > values)
Create modbus write multiple registers command Function 0x10 Write Multiple Registers.
bool bit_from_packed(int bit, std::span< const uint8_t > data)
Extract coil data from modbus response buffer Responses for coil are packed into bytes .
PduBuffer create_read_write_multiple_registers_pdu(uint16_t read_start_address, uint16_t read_count, uint16_t write_start_address, std::span< const uint16_t > write_values)
Create modbus read/write multiple registers command Function 0x17 Read/Write Multiple Registers Write...
bool is_entity_type_binary(EntityType type)
Coils and discrete inputs are the bit-addressed entity tables; the other types are 16-bit registers.
std::optional< int64_t > registers_to_number(const uint16_t *registers, size_t count, SensorValueType sensor_value_type)
Reconstruct a number from register words (host byte order).
uint16_t client_pdu_length(const uint8_t *frame, size_t size)
bool is_function_code_exception(uint8_t function_code)
std::optional< int64_t > payload_to_number(const uint8_t *data, size_t size, SensorValueType sensor_value_type, uint8_t offset, uint32_t bitmask)
Convert a raw response payload to a number.
const uint8_t FUNCTION_CODE_MASK
const uint8_t FUNCTION_CODE_EXCEPTION_MASK
const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_2_INIT
const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_1_INIT
Modbus definitions from specs: https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3....
const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_2_END
@ WRITE_MULTIPLE_REGISTERS
@ READ_WRITE_MULTIPLE_REGISTERS
const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_1_END
constexpr size_t packed_bit_bytes(size_t bits)
Bits pack 8 per data byte, rounded up to whole bytes.
To bit_cast(const From &src)
Convert data between types, without aliasing issues or undefined behaviour.