DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
directnet.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 directnet.cpp
6 * @brief AutomationDirect / Koyo DirectNET serial frame codec (see directnet.h).
7 */
8
10
11#if DETWS_ENABLE_DIRECTNET
12
13#include <string.h>
14
15namespace
16{
17char hex_digit(uint8_t nibble)
18{
19 nibble &= 0x0F;
20 return (char)(nibble < 10 ? ('0' + nibble) : ('A' + nibble - 10));
21}
22// Write @p value as @p digits ASCII-hex chars (big-endian) at p.
23void put_hex(uint8_t *p, uint32_t value, int digits)
24{
25 for (int i = 0; i < digits; i++)
26 p[i] = (uint8_t)hex_digit((uint8_t)(value >> (4 * (digits - 1 - i))));
27}
28} // namespace
29
30uint8_t detws_dnet_lrc(const uint8_t *bytes, size_t len)
31{
32 uint8_t lrc = 0;
33 for (size_t i = 0; i < len; i++)
34 lrc ^= bytes[i];
35 return lrc;
36}
37
38size_t detws_dnet_header(uint8_t slave, uint8_t type, uint16_t address, uint8_t blocks, uint8_t *out, size_t cap)
39{
40 // SOH + slave(2) + type(1) + addr(4) + blocks(2) + ETB + LRC = 11 bytes.
41 const size_t n = 1 + 2 + 1 + 4 + 2 + 1 + 1;
42 if (!out || cap < n)
43 return 0;
44 size_t i = 0;
45 out[i++] = DnetByte::DNET_SOH;
46 put_hex(out + i, slave, 2);
47 i += 2;
48 out[i++] = type;
49 put_hex(out + i, address, 4);
50 i += 4;
51 put_hex(out + i, blocks, 2);
52 i += 2;
53 out[i++] = DnetByte::DNET_ETB;
54 // LRC over the framed body (slave..ETB), i.e. everything after SOH up to and including ETB.
55 out[i] = detws_dnet_lrc(out + 1, i - 1);
56 i++;
57 return i;
58}
59
60size_t detws_dnet_data(const uint8_t *data, size_t data_len, uint8_t *out, size_t cap)
61{
62 if (!out || (data_len && !data))
63 return 0;
64 size_t n = 1 + data_len + 1 + 1; // STX + data + ETX + LRC
65 if (n > cap)
66 return 0;
67 size_t i = 0;
68 out[i++] = DnetByte::DNET_STX;
69 if (data_len)
70 {
71 memcpy(out + i, data, data_len);
72 i += data_len;
73 }
74 out[i++] = DnetByte::DNET_ETX;
75 // LRC over data..ETX (everything after STX up to and including ETX).
76 out[i] = detws_dnet_lrc(out + 1, i - 1);
77 i++;
78 return i;
79}
80
81bool detws_dnet_data_parse(const uint8_t *frame, size_t len, const uint8_t **data, size_t *data_len)
82{
83 if (!frame || len < 3) // STX + ETX + LRC minimum
84 return false;
85 if (frame[0] != DnetByte::DNET_STX)
86 return false;
87 // The byte before the LRC must be ETX.
88 size_t etx_idx = len - 2;
89 if (frame[etx_idx] != DnetByte::DNET_ETX)
90 return false;
91 if (detws_dnet_lrc(frame + 1, len - 2) != frame[len - 1]) // over data..ETX
92 return false;
93 if (data)
94 *data = (etx_idx > 1) ? (frame + 1) : nullptr;
95 if (data_len)
96 *data_len = etx_idx - 1;
97 return true;
98}
99
100#endif // DETWS_ENABLE_DIRECTNET
AutomationDirect / Koyo DirectNET serial frame codec (DETWS_ENABLE_DIRECTNET).