DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
sunspec.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 sunspec.cpp
6 * @brief SunSpec Modbus model-chain walker + point readers + map writer (pure, host-tested).
7 */
8
10
11#if DETWS_ENABLE_SUNSPEC
12
13#include <string.h>
14
15static uint16_t be16(const uint8_t *p)
16{
17 return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
18}
19
20static uint32_t be32(const uint8_t *p)
21{
22 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
23}
24
25bool sunspec_check_marker(const uint8_t *regs, size_t len)
26{
27 return regs && len >= 4 && be32(regs) == SUNSPEC_MARKER;
28}
29
30bool sunspec_begin(const uint8_t *regs, size_t len, size_t *offset)
31{
32 if (!offset || !sunspec_check_marker(regs, len))
33 return false;
34 *offset = 4; // past the 2-register marker
35 return true;
36}
37
38bool sunspec_next_model(const uint8_t *regs, size_t len, size_t *offset, SunSpecModel *out)
39{
40 if (!regs || !offset || !out)
41 return false;
42 size_t o = *offset;
43 if (o + 4 > len) // need the [id][length] header
44 return false;
45 uint16_t id = be16(regs + o);
46 uint16_t length = be16(regs + o + 2);
47 if (id == SUNSPEC_END_MODEL)
48 return false; // map terminator
49 size_t body_len = (size_t)length * 2;
50 if (o + 4 + body_len > len) // body must be fully buffered
51 return false;
52 out->id = id;
53 out->length = length;
54 out->body = regs + o + 4;
55 out->body_len = body_len;
56 *offset = o + 4 + body_len;
57 return true;
58}
59
60uint16_t sunspec_u16(const uint8_t *body, size_t reg)
61{
62 return be16(body + reg * 2);
63}
64
65int16_t sunspec_i16(const uint8_t *body, size_t reg)
66{
67 return (int16_t)be16(body + reg * 2);
68}
69
70uint32_t sunspec_u32(const uint8_t *body, size_t reg)
71{
72 return be32(body + reg * 2);
73}
74
75int32_t sunspec_i32(const uint8_t *body, size_t reg)
76{
77 return (int32_t)be32(body + reg * 2);
78}
79
80bool sunspec_string(const uint8_t *body, size_t reg, size_t nregs, char *out, size_t out_cap)
81{
82 if (!body || !out || out_cap == 0)
83 return false;
84 size_t avail = nregs * 2;
85 size_t i = 0;
86 const uint8_t *p = body + reg * 2;
87 for (; i < avail && i < out_cap - 1; i++)
88 {
89 if (p[i] == 0) // NUL padding ends the content
90 break;
91 out[i] = (char)p[i];
92 }
93 out[i] = '\0';
94 return true;
95}
96
97// ---- writer ----
98
99void sunspec_writer_init(SunSpecWriter *w, uint8_t *buf, size_t cap)
100{
101 w->buf = buf;
102 w->cap = cap;
103 w->pos = 0;
104 w->error = false;
105}
106
107static bool ss_put(SunSpecWriter *w, const uint8_t *p, size_t n)
108{
109 if (w->error)
110 return false;
111 if (w->pos + n > w->cap)
112 {
113 w->error = true;
114 return false;
115 }
116 memcpy(w->buf + w->pos, p, n);
117 w->pos += n;
118 return true;
119}
120
121bool sunspec_write_u16(SunSpecWriter *w, uint16_t v)
122{
123 uint8_t b[2] = {(uint8_t)(v >> 8), (uint8_t)v};
124 return ss_put(w, b, 2);
125}
126
127bool sunspec_write_i16(SunSpecWriter *w, int16_t v)
128{
129 return sunspec_write_u16(w, (uint16_t)v);
130}
131
132bool sunspec_write_u32(SunSpecWriter *w, uint32_t v)
133{
134 uint8_t b[4] = {(uint8_t)(v >> 24), (uint8_t)(v >> 16), (uint8_t)(v >> 8), (uint8_t)v};
135 return ss_put(w, b, 4);
136}
137
138bool sunspec_write_i32(SunSpecWriter *w, int32_t v)
139{
140 return sunspec_write_u32(w, (uint32_t)v);
141}
142
143bool sunspec_write_marker(SunSpecWriter *w)
144{
145 return sunspec_write_u32(w, SUNSPEC_MARKER);
146}
147
148bool sunspec_write_model_header(SunSpecWriter *w, uint16_t id, uint16_t length)
149{
150 return sunspec_write_u16(w, id) && sunspec_write_u16(w, length);
151}
152
153bool sunspec_write_string(SunSpecWriter *w, const char *s, size_t nregs)
154{
155 if (!s)
156 return false;
157 size_t field = nregs * 2;
158 if (w->error)
159 return false;
160 if (w->pos + field > w->cap)
161 {
162 w->error = true;
163 return false;
164 }
165 size_t slen = strnlen(s, field);
166 for (size_t i = 0; i < field; i++)
167 w->buf[w->pos + i] = (i < slen) ? (uint8_t)s[i] : 0; // NUL-pad the remainder
168 w->pos += field;
169 return true;
170}
171
172bool sunspec_write_end_model(SunSpecWriter *w)
173{
174 return sunspec_write_u16(w, SUNSPEC_END_MODEL) && sunspec_write_u16(w, 0);
175}
176
177size_t sunspec_writer_finish(SunSpecWriter *w)
178{
179 return w->error ? 0 : w->pos;
180}
181
182#endif // DETWS_ENABLE_SUNSPEC
uint32_t be32(const uint8_t *p)
Definition ghash.h:39
SunSpec Modbus device-information-model codec (DETWS_ENABLE_SUNSPEC) - zero-heap model-chain walker +...