DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
mbplus.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 mbplus.cpp
6 * @brief Modbus Plus HDLC token-bus frame codec (see mbplus.h).
7 */
8
10
11#if DETWS_ENABLE_MBPLUS
12
13#include <string.h>
14
15uint16_t detws_mbplus_crc(const uint8_t *bytes, size_t len)
16{
17 // CRC-16/X-25: reflected poly 0x8408, init 0xFFFF, xorout 0xFFFF.
18 uint16_t crc = 0xFFFF;
19 for (size_t i = 0; i < len; i++)
20 {
21 crc ^= bytes[i];
22 for (int b = 0; b < 8; b++)
23 crc = (crc & 1) ? (uint16_t)((crc >> 1) ^ 0x8408) : (uint16_t)(crc >> 1);
24 }
25 return (uint16_t)~crc;
26}
27
28size_t detws_mbplus_build(uint8_t address, uint8_t control, const uint8_t *payload, size_t payload_len, uint8_t *out,
29 size_t cap)
30{
31 if (!out || (payload_len && !payload) || address < 1 || address > Mbplus::MBPLUS_MAX_STATION)
32 return 0;
33 size_t n = 1 + 1 + 1 + payload_len + 2 + 1; // 7E addr ctrl payload CRClo CRChi 7E
34 if (n > cap)
35 return 0;
36 size_t i = 0;
37 out[i++] = Mbplus::MBPLUS_FLAG;
38 size_t body = i;
39 out[i++] = address;
40 out[i++] = control;
41 if (payload_len)
42 {
43 memcpy(out + i, payload, payload_len);
44 i += payload_len;
45 }
46 uint16_t crc = detws_mbplus_crc(out + body, (i - body)); // over addr..last payload
47 out[i++] = (uint8_t)crc; // CRC low byte first
48 out[i++] = (uint8_t)(crc >> 8);
49 out[i++] = Mbplus::MBPLUS_FLAG;
50 return i;
51}
52
53bool detws_mbplus_parse(const uint8_t *frame, size_t len, MbPlusFrame *out)
54{
55 // Min: 7E addr ctrl CRClo CRChi 7E = 6 bytes.
56 if (!frame || !out || len < 6)
57 return false;
58 if (frame[0] != Mbplus::MBPLUS_FLAG || frame[len - 1] != Mbplus::MBPLUS_FLAG)
59 return false;
60 // Body is frame[1 .. len-2), the CRC is the last 2 body bytes.
61 size_t body_end = len - 1; // index of the trailing flag
62 size_t crc_pos = body_end - 2; // low byte of the CRC
63 size_t covered = crc_pos - 1; // number of bytes (addr..payload) the CRC covers
64 uint16_t want = detws_mbplus_crc(frame + 1, covered);
65 uint16_t got = (uint16_t)(frame[crc_pos] | (frame[crc_pos + 1] << 8));
66 if (want != got)
67 return false;
68 out->address = frame[1];
69 out->control = frame[2];
70 out->payload = (covered > 2) ? (frame + 3) : nullptr;
71 out->payload_len = covered - 2; // minus addr + ctrl
72 return true;
73}
74
75uint8_t detws_mbplus_next_token(uint8_t current, uint8_t max_station)
76{
77 if (max_station < 1)
78 return 1;
79 return (current >= max_station) ? 1 : (uint8_t)(current + 1);
80}
81
82#endif // DETWS_ENABLE_MBPLUS
Modbus Plus HDLC token-bus frame codec (DETWS_ENABLE_MBPLUS).