18constexpr int HEX_ALPHA_OFFSET = 10;
23 if (ch >=
'0' && ch <=
'9')
25 ch =
static_cast<char>(std::toupper(
static_cast<unsigned char>(ch)));
26 if (ch >=
'A' && ch <=
'F')
27 return HEX_ALPHA_OFFSET + (ch -
'A');
31bool hex_to_bytes(
const std::string &hex, uint8_t *out, uint8_t len) {
36 if (hex.length() !=
static_cast<size_t>(len) * 2)
39 for (uint8_t i = 0; i < len; i++) {
42 if (high < 0 || low < 0)
44 out[i] =
static_cast<uint8_t
>((high << 4) | low);
51 char buf[NODE_ID_STRING_SIZE];
52 snprintf(buf,
sizeof(buf),
"%02X%02X%02X",
id[0],
id[1],
id[2]);
53 return std::string(buf);
60uint16_t
crc_ccitt(
const uint8_t *data, uint8_t len) {
61 uint16_t crc = 0x0000;
62 for (uint8_t i = 0; i < len; i++) {
64 for (uint8_t j = 0; j < BITS_PER_BYTE; j++)
75 f.
ctrl0 |= CTRL0_START;
77 f.
ctrl0 |= CTRL0_PROTOCOL_1W;
79 f.
ctrl1 |= CTRL1_LOW_POWER;
82void set_dst(
IoFrame &f,
const uint8_t
id[NODE_ID_SIZE]) { memcpy(f.
dst,
id, NODE_ID_SIZE); }
83void set_src(
IoFrame &f,
const uint8_t
id[NODE_ID_SIZE]) { memcpy(f.
src,
id, NODE_ID_SIZE); }
85bool set_cmd(
IoFrame &f, uint8_t cmd,
const uint8_t *params, uint8_t params_len) {
86 if (params_len > FRAME_MAX_DATA_SIZE)
90 if (params !=
nullptr && params_len > 0)
91 memcpy(f.
data, params, params_len);
92 uint8_t
const total = FRAME_MIN_SIZE + f.
data_len;
97 if (total > FRAME_MAX_DECLARED_SIZE)
99 f.
ctrl0 = (f.
ctrl0 & ~CTRL0_LENGTH_MASK) | ((total - 1) & CTRL0_LENGTH_MASK);
111 return cmd == CMD_ONEWAY_ADD_CONTROLLER;
118 if (len < FRAME_MIN_SIZE || len > FRAME_MAX_DECLARED_SIZE)
120 if (f.
data_len > FRAME_MAX_DATA_SIZE)
124 if ((uint8_t) (FRAME_MIN_SIZE + f.
data_len) != len)
134 uint8_t
const total_len = f.
has_mac ? (uint8_t) (len + HMAC_SIZE) : len;
135 if (buf_size < total_len)
138 buf[offset++] = f.
ctrl0;
139 buf[offset++] = f.
ctrl1;
140 memcpy(&buf[offset], f.
dst, NODE_ID_SIZE);
141 offset += NODE_ID_SIZE;
142 memcpy(&buf[offset], f.
src, NODE_ID_SIZE);
143 offset += NODE_ID_SIZE;
144 buf[offset++] = f.
cmd;
148 memcpy(&buf[offset], f.
mac, HMAC_SIZE);
157 if (buf_len < FRAME_MIN_SIZE)
161 f.
ctrl0 = buf[offset++];
162 f.
ctrl1 = buf[offset++];
164 if (len < FRAME_MIN_SIZE || len > FRAME_MAX_DECLARED_SIZE)
174 if (buf_len == len) {
181 if (offset + NODE_ID_SIZE > buf_len)
183 memcpy(f.
dst, &buf[offset], NODE_ID_SIZE);
184 offset += NODE_ID_SIZE;
185 if (offset + NODE_ID_SIZE > buf_len)
187 memcpy(f.
src, &buf[offset], NODE_ID_SIZE);
188 offset += NODE_ID_SIZE;
189 if (offset >= buf_len)
191 f.
cmd = buf[offset++];
195 if (f.
data_len > FRAME_MAX_DATA_SIZE)
202 memcpy(f.
mac, &buf[offset], HMAC_SIZE);
bool set_cmd(IoFrame &f, uint8_t cmd, const uint8_t *params, uint8_t params_len)
Set command and payload.
bool is_start(const IoFrame &f)
Check START flag.
uint16_t crc_ccitt(const uint8_t *data, uint8_t len)
CRC-CCITT used by the IO-Homecontrol protocol for frame validation.
void init_frame(IoFrame &f, bool is_2w, bool start, bool end, bool low_power)
Initialize an IoFrame header (ctrl0/ctrl1) with flags.
uint8_t frame_length(const IoFrame &f)
Get total frame length from ctrl0.
bool frame_carries_mac_trailer(uint8_t cmd)
Whether wire frames for a command carry the out-of-length MAC trailer described on IoFrame::has_mac/I...
void set_dst(IoFrame &f, const uint8_t id[NODE_ID_SIZE])
Set destination node ID.
bool is_end(const IoFrame &f)
Check END flag.
static constexpr uint16_t CRC_LSB_MASK
Least-significant-bit mask for reflected CRC update.
bool parse(const uint8_t *buf, uint8_t buf_len, IoFrame &f)
Parse a wire buffer into a parsed IoFrame (validates length and CTRL0).
std::string node_id_to_string(const uint8_t id[NODE_ID_SIZE])
Format a 3‑byte node ID as a 6‑character uppercase hex string.
static int hex_nibble(char ch)
static constexpr uint16_t CRC_POLYNOMIAL_REVERSED
Reversed CRC-CCITT polynomial used by IO-homecontrol.
bool hex_to_bytes(const std::string &hex, uint8_t *out, uint8_t len)
Convert a hex string (e.g., "123ABC") to a byte array.
uint8_t serialize(const IoFrame &f, uint8_t *buf, uint8_t buf_size)
Serialize a parsed frame into a wire buffer (without CRC).
void set_src(IoFrame &f, const uint8_t id[NODE_ID_SIZE])
Set source node ID.
IO-Homecontrol command IDs, result codes and protocol enumerations.
IO-Homecontrol 2W frame container: control bytes, IoFrame and (de)serialization.
Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data).
uint8_t data[FRAME_MAX_DATA_SIZE]
Command parameters (0–23 bytes). Never includes mac.
uint8_t mac[HMAC_SIZE]
Out-of-length authenticator trailer; meaningful only when has_mac.
bool has_mac
True if this frame carries the mac trailer (see struct doc).
uint8_t ctrl0
Control byte 0: flags + length.
uint8_t src[NODE_ID_SIZE]
Source node ID (3 bytes).
uint8_t dst[NODE_ID_SIZE]
Destination node ID (3 bytes).
uint8_t data_len
Actual length of data.
uint8_t ctrl1
Control byte 1: low power, beacon, etc.