43 return uint32_t(start_address) + count <= 0x10000u;
107 return MIN_FRAME_SIZE;
116 return MIN_FRAME_SIZE;
133ESPDEPRECATED(
"Use server_pdu_payload() on the response PDU instead. Removed in 2027.2.0",
"2026.8.0")
134inline uint8_t server_frame_data_offset(const uint8_t *frame,
size_t size) {
159 return pdu.size() > offset ? pdu.subspan(offset) : std::span<const uint8_t>();
170template<
typename T> T
get_data(
const uint8_t *
data,
size_t buffer_offset) {
171 if (
sizeof(T) ==
sizeof(uint8_t)) {
172 return T(
data[buffer_offset]);
174 if (
sizeof(T) ==
sizeof(uint16_t)) {
175 return T((uint16_t(
data[buffer_offset + 0]) << 8) | (uint16_t(
data[buffer_offset + 1]) << 0));
177 if (
sizeof(T) ==
sizeof(
uint32_t)) {
181 if (
sizeof(T) ==
sizeof(uint64_t)) {
185 static_assert(
sizeof(T) ==
sizeof(uint8_t) ||
sizeof(T) ==
sizeof(uint16_t) ||
sizeof(T) ==
sizeof(
uint32_t) ||
186 sizeof(T) ==
sizeof(uint64_t),
187 "Unsupported type size in get_data; only 1, 2, 4, or 8-byte integer types are supported.");
285inline uint8_t
c_to_hex(
char c) {
return (c >=
'A') ? (c >=
'a') ? (c -
'a' + 10) : (c -
'A' + 10) : (c -
'0'); }
296 if (value.length() <
pos * 2 + 2)
328template<
typename T> T
get_data(
const std::vector<uint8_t> &
data,
size_t buffer_offset) {
341 auto data_byte = bit / 8;
342 return (
data[data_byte] & (1 << (bit % 8))) > 0;
346ESPDEPRECATED(
"Use bit_from_packed() instead. Removed in 2027.2.0",
"2026.8.0")
356template<
typename Out,
typename Bits>
void pack_bits(Out &out,
const Bits &bits) {
359 for (
bool b : bits) {
383 auto result = (mask &
data);
384 if (result == 0 || mask == 0xFFFFFFFF) {
387 for (
size_t pos = 0;
pos <
sizeof(N) << 3;
pos++) {
388 if (
pos < 32 && (mask & (1UL <<
pos)) != 0)
389 return result >>
pos;
401 switch (value_type) {
404 data.push_back(value & 0xFFFF);
408 data.push_back(
byteswap(
static_cast<uint16_t
>(value & 0xFFFF)));
413 data.push_back((value & 0xFFFF0000) >> 16);
414 data.push_back(value & 0xFFFF);
419 data.push_back(value & 0xFFFF);
420 data.push_back((value & 0xFFFF0000) >> 16);
424 data.push_back((value & 0xFFFF000000000000) >> 48);
425 data.push_back((value & 0xFFFF00000000) >> 32);
426 data.push_back((value & 0xFFFF0000) >> 16);
427 data.push_back(value & 0xFFFF);
431 data.push_back(value & 0xFFFF);
432 data.push_back((value & 0xFFFF0000) >> 16);
433 data.push_back((value & 0xFFFF00000000) >> 32);
434 data.push_back((value & 0xFFFF000000000000) >> 48);
460ESPDEPRECATED(
"Use the std::span overload returning std::optional<int64_t> instead. Removed in 2027.2.0",
"2026.8.0")
464 return payload_to_number(std::span<const uint8_t>(
data), sensor_value_type, offset, bitmask).value_or(0);
478 return (
static_cast<uint32_t>(high_word) << 16) | low_word;
504 return static_cast<int16_t
>(registers[0]);
508 return static_cast<int16_t
>(
byteswap(registers[0]));
526 return static_cast<int64_t
>(
registers_to_uint64(registers[0], registers[1], registers[2], registers[3]));
528 return static_cast<int64_t
>(
registers_to_uint64(registers[3], registers[2], registers[1], registers[0]));
536template<SensorValueType VALUE_TYPE>
546template<SensorValueType VALUE_TYPE>
547constexpr std::optional<RegisterValueType<VALUE_TYPE>>
value_at(std::span<const uint16_t> registers,
548 uint16_t start_address, uint16_t
address) {
551 const size_t offset =
static_cast<size_t>(
address) - start_address;
558static constexpr uint16_t MAX_FEW_REGISTERS = 4;
597 const uint8_t *values =
nullptr,
size_t values_len = 0);
630 uint16_t write_start_address,
631 std::span<const uint16_t> write_values);
688 val = llroundf(value);
695ESPDEPRECATED(
"Use the container overload of float_to_payload() instead. Removed in 2027.2.0",
"2026.8.0")
697 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.
constexpr bool VALUE_TYPE_SUPPORTED
WriteFewRegistersPdu create_write_few_registers_pdu(uint16_t start_address, std::span< const uint16_t > values)
Create modbus write multiple registers command (function 0x10) on a right-sized stack buffer.
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.
constexpr uint32_t registers_to_uint32(uint16_t high_word, uint16_t low_word)
Combine two register words into a 32-bit value.
decltype(registers_to_value< VALUE_TYPE >(static_cast< const uint16_t * >(nullptr))) RegisterValueType
The type registers_to_value() yields for a given value type.
T get_data(const uint8_t *data, size_t buffer_offset)
Extract data from modbus response buffer.
constexpr std::optional< RegisterValueType< VALUE_TYPE > > value_at(std::span< const uint16_t > registers, uint16_t start_address, uint16_t address)
The value stored at an absolute register address, or nullopt when it is not wholly inside this respon...
constexpr auto registers_to_value(const uint16_t *registers)
Decode one value whose type is known at compile time, from registers in host byte order.
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)
constexpr uint16_t register_width_for(SensorValueType v)
Number of 16-bit registers a value of this type occupies (RAW counts as one register).
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,...
bool is_function_code_broadcastable(uint8_t function_code)
True when the underlying function code (exception bit masked off) may be broadcast (address 0).
uint8_t pdu_function_code(std::span< const uint8_t > pdu)
Function code of a PDU, exception flag masked; 0 for an empty PDU.
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)
constexpr uint64_t registers_to_uint64(uint16_t word0, uint16_t word1, uint16_t word2, uint16_t word3)
Combine four register words into a 64-bit value, most significant word first.
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...
std::optional< uint16_t > client_pdu_start_address(std::span< const uint8_t > pdu)
Start address of a standard client request PDU ([fc, addr_hi, addr_lo, ...]).
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.