ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
profibus.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 profibus.cpp
6 * @brief PROFIBUS-DP FDL telegram codec (see profibus.h).
7 */
8
10
11#if PC_ENABLE_PROFIBUS
12
13#include <string.h>
14
15uint8_t pc_pb_fcs(const uint8_t *bytes, size_t len)
16{
17 uint8_t sum = 0;
18 for (size_t i = 0; i < len; i++)
19 {
20 sum = (uint8_t)(sum + bytes[i]);
21 }
22 return sum;
23}
24
25size_t pc_pb_build_sd1(uint8_t da, uint8_t sa, uint8_t fc, uint8_t *out, size_t cap)
26{
27 if (!out || cap < 6)
28 {
29 return 0;
30 }
31 out[0] = Profibus::PB_SD1;
32 out[1] = da;
33 out[2] = sa;
34 out[3] = fc;
35 uint8_t body[3] = {da, sa, fc};
36 out[4] = pc_pb_fcs(body, 3);
37 out[5] = Profibus::PB_ED;
38 return 6;
39}
40
41size_t pc_pb_build_sd2(uint8_t da, uint8_t sa, uint8_t fc, const uint8_t *data, size_t data_len, uint8_t *out,
42 size_t cap)
43{
44 if (!out || (data_len && !data) || data_len > 246)
45 {
46 return 0;
47 }
48 // SD2 LE LEr SD2 DA SA FC [data] FCS ED
49 size_t n = 4 + 3 + data_len + 2; // (SD2,LE,LEr,SD2) + (DA,SA,FC) + data + (FCS,ED)
50 if (n > cap)
51 {
52 return 0;
53 }
54 uint8_t le = (uint8_t)(3 + data_len); // length of DA+SA+FC+data
55 size_t i = 0;
56 out[i++] = Profibus::PB_SD2;
57 out[i++] = le;
58 out[i++] = le; // LEr (redundant length)
59 out[i++] = Profibus::PB_SD2;
60 out[i++] = da;
61 out[i++] = sa;
62 out[i++] = fc;
63 if (data_len)
64 {
65 memcpy(out + i, data, data_len);
66 i += data_len;
67 }
68 // FCS over DA+SA+FC+data (out[4 .. 4+le-1]).
69 out[i++] = pc_pb_fcs(out + 4, le);
70 out[i++] = Profibus::PB_ED;
71 return i;
72}
73
74size_t pc_pb_build_sd3(uint8_t da, uint8_t sa, uint8_t fc, const uint8_t *data, uint8_t *out, size_t cap)
75{
76 if (!out || !data || cap < 14) // SD3 DA SA FC data[8] FCS ED
77 {
78 return 0;
79 }
80 out[0] = Profibus::PB_SD3;
81 out[1] = da;
82 out[2] = sa;
83 out[3] = fc;
84 memcpy(out + 4, data, 8);
85 out[12] = pc_pb_fcs(out + 1, 11); // FCS over DA+SA+FC+data(8)
86 out[13] = Profibus::PB_ED;
87 return 14;
88}
89
90// SD3 fixed-length telegram: SD3 DA SA FC data[8] FCS ED (14 octets).
91static bool pb_parse_sd3(const uint8_t *frame, size_t len, PbTelegram *out)
92{
93 if (len < 14)
94 {
95 return false;
96 }
97 if (pc_pb_fcs(frame + 1, 11) != frame[12] || frame[13] != Profibus::PB_ED)
98 {
99 return false;
100 }
101 out->sd = Profibus::PB_SD3;
102 out->da = frame[1];
103 out->sa = frame[2];
104 out->fc = frame[3];
105 out->data = frame + 4;
106 out->data_len = 8;
107 return true;
108}
109
110// SD1 no-data telegram: SD1 DA SA FC FCS ED (6 octets).
111static bool pb_parse_sd1(const uint8_t *frame, size_t len, PbTelegram *out)
112{
113 (void)len; // len >= 6 already guaranteed by pc_pb_parse
114 uint8_t body[3] = {frame[1], frame[2], frame[3]};
115 if (pc_pb_fcs(body, 3) != frame[4] || frame[5] != Profibus::PB_ED)
116 {
117 return false;
118 }
119 out->sd = Profibus::PB_SD1;
120 out->da = frame[1];
121 out->sa = frame[2];
122 out->fc = frame[3];
123 out->data = nullptr;
124 out->data_len = 0;
125 return true;
126}
127
128// SD2 variable-length telegram: SD2 LE LEr SD2 DA SA FC [data] FCS ED.
129static bool pb_parse_sd2(const uint8_t *frame, size_t len, PbTelegram *out)
130{
131 if (len < 9)
132 {
133 return false;
134 }
135 uint8_t le = frame[1];
136 if (frame[2] != le || frame[3] != Profibus::PB_SD2)
137 {
138 return false;
139 }
140 if (le < 3)
141 {
142 return false;
143 }
144 size_t total = 4 + le + 2; // header(4) + le body + FCS + ED
145 if (len < total)
146 {
147 return false;
148 }
149 if (pc_pb_fcs(frame + 4, le) != frame[4 + le] || frame[4 + le + 1] != Profibus::PB_ED)
150 {
151 return false;
152 }
153 out->sd = Profibus::PB_SD2;
154 out->da = frame[4];
155 out->sa = frame[5];
156 out->fc = frame[6];
157 size_t dl = le - 3;
158 out->data = dl ? (frame + 7) : nullptr;
159 out->data_len = dl;
160 return true;
161}
162
163bool pc_pb_parse(const uint8_t *frame, size_t len, PbTelegram *out)
164{
165 if (!frame || !out || len < 6)
166 {
167 return false;
168 }
169 if (frame[0] == Profibus::PB_SD3)
170 {
171 return pb_parse_sd3(frame, len, out);
172 }
173 if (frame[0] == Profibus::PB_SD1)
174 {
175 return pb_parse_sd1(frame, len, out);
176 }
177 if (frame[0] == Profibus::PB_SD2)
178 {
179 return pb_parse_sd2(frame, len, out);
180 }
181 return false;
182}
183
184#endif // PC_ENABLE_PROFIBUS
PROFIBUS-DP FDL telegram codec (PC_ENABLE_PROFIBUS).