ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
hart.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file hart.cpp
6 * @brief HART / HART-IP protocol codec (see hart.h).
7 */
8
10
11#if PC_ENABLE_HART
12
13#include <string.h>
14
15uint8_t pc_hart_checksum(const uint8_t *bytes, size_t len)
16{
17 uint8_t x = 0;
18 for (size_t i = 0; i < len; i++)
19 {
20 x ^= bytes[i];
21 }
22 return x;
23}
24
25size_t pc_hart_build(uint8_t delimiter, const uint8_t *addr, size_t addr_len, uint8_t command, const uint8_t *data,
26 size_t data_len, uint8_t *out, size_t cap)
27{
28 if (addr_len != 1 && addr_len != 5)
29 {
30 return 0;
31 }
32 if (!addr || (data_len && !data))
33 {
34 return 0;
35 }
36 // delimiter + addr + command + byte-count + data + checksum
37 size_t n = 1 + addr_len + 1 + 1 + data_len + 1;
38 if (n > cap || data_len > 0xFF)
39 {
40 return 0;
41 }
42
43 size_t i = 0;
44 out[i++] = delimiter;
45 memcpy(out + i, addr, addr_len);
46 i += addr_len;
47 out[i++] = command;
48 out[i++] = (uint8_t)data_len; // byte count
49 if (data_len)
50 {
51 memcpy(out + i, data, data_len);
52 i += data_len;
53 }
54 out[i] = pc_hart_checksum(out, i); // XOR over delimiter..last data byte
55 i++;
56 return i;
57}
58
59bool pc_hart_parse(const uint8_t *frame, size_t len, HartFrame *out)
60{
61 if (!frame || !out)
62 {
63 return false;
64 }
65 size_t addr_len = (frame[0] & HartDelim::HART_DELIM_LONG_ADDR) ? 5 : 1;
66 // delimiter + addr + command + byte-count + checksum = minimum with no data
67 size_t min = 1 + addr_len + 1 + 1 + 1;
68 if (len < min)
69 {
70 return false;
71 }
72
73 size_t bc_idx = 1 + addr_len + 1; // index of the byte-count field
74 uint8_t byte_count = frame[bc_idx];
75 size_t expect = 1 + addr_len + 1 + 1 + byte_count + 1; // full frame incl checksum
76 if (len < expect)
77 {
78 return false;
79 }
80
81 uint8_t want = pc_hart_checksum(frame, expect - 1);
82 if (want != frame[expect - 1])
83 {
84 return false;
85 }
86
87 out->delimiter = frame[0];
88 out->addr = frame + 1;
89 out->addr_len = addr_len;
90 out->command = frame[1 + addr_len];
91 out->byte_count = byte_count;
92 out->data = byte_count ? (frame + bc_idx + 1) : nullptr;
93 out->data_len = byte_count;
94 return true;
95}
96
97size_t pc_hartip_build_header(uint8_t msg_type, uint8_t msg_id, uint8_t status, uint16_t seq, uint16_t total_len,
98 uint8_t *out, size_t cap)
99{
100 if (cap < HartIp::HARTIP_HEADER_LEN || !out)
101 {
102 return 0;
103 }
104 out[0] = 1; // HART-IP protocol version
105 out[1] = msg_type;
106 out[2] = msg_id;
107 out[3] = status;
108 out[4] = (uint8_t)(seq >> 8);
109 out[5] = (uint8_t)seq;
110 out[6] = (uint8_t)(total_len >> 8);
111 out[7] = (uint8_t)total_len;
112 return HartIp::HARTIP_HEADER_LEN;
113}
114
115bool pc_hartip_parse_header(const uint8_t *buf, size_t len, HartIpHeader *out)
116{
117 if (!buf || !out || len < HartIp::HARTIP_HEADER_LEN)
118 {
119 return false;
120 }
121 uint16_t total = (uint16_t)((buf[6] << 8) | buf[7]);
122 if (total < HartIp::HARTIP_HEADER_LEN || total > len) // the byte count must include the header and be present
123 {
124 return false;
125 }
126 out->version = buf[0];
127 out->msg_type = buf[1];
128 out->msg_id = buf[2];
129 out->status = buf[3];
130 out->seq = (uint16_t)((buf[4] << 8) | buf[5]);
131 out->total_len = total;
132 out->payload_len = (size_t)(total - HartIp::HARTIP_HEADER_LEN);
133 out->payload = out->payload_len ? buf + HartIp::HARTIP_HEADER_LEN : nullptr;
134 return true;
135}
136
137#endif // PC_ENABLE_HART
HART / HART-IP process-instrument protocol codec (PC_ENABLE_HART).