DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
dnp3.h
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 dnp3.h
6 * @brief DNP3 (IEEE 1815) data-link frame codec (DETWS_ENABLE_DNP3) - zero-heap builder +
7 * CRC-validating parser for the SCADA / utility outstation link layer.
8 *
9 * A DNP3 data-link frame:
10 * @code
11 * 0x05 0x64 LEN CTRL DEST(2,LE) SRC(2,LE) CRC(2) // 10-octet header block
12 * [<=16 user-data octets][CRC(2)] ... // data blocks, each CRC'd
13 * @endcode
14 * - LEN counts CTRL + DEST + SRC + user data (the start word, LEN itself, and the CRCs are
15 * excluded), so LEN = 5 + user_data_len; min 5, max 255 (<= 250 user-data octets).
16 * - Addresses are little-endian (LSB first). User data is carried in blocks of up to 16
17 * octets, each followed by its own CRC; the header is its own CRC'd block.
18 * - CRC is CRC-16/DNP (poly 0x3D65, init 0x0000, reflected in/out, final XOR 0xFFFF),
19 * transmitted low octet first.
20 *
21 * This is the data-link layer (framing + CRC). The transport-function reassembly and the
22 * application layer (objects / function codes) are layered on the de-blocked user data.
23 *
24 * @author Douglas Quigg (dstroy0)
25 * @date 2026
26 */
27
28#ifndef DETERMINISTICESPASYNCWEBSERVER_DNP3_H
29#define DETERMINISTICESPASYNCWEBSERVER_DNP3_H
30
31#include "ServerConfig.h"
32
33#if DETWS_ENABLE_DNP3
34
35#include <stddef.h>
36#include <stdint.h>
37
38#define DNP3_START0 0x05
39#define DNP3_START1 0x64
40#define DNP3_MAX_USER_DATA 250 ///< LEN max 255 minus the 5 header octets it counts
41
42// Frame geometry (octets). The header is one CRC'd block; user data is carried in
43// fixed-size CRC'd blocks. These are wire constants from IEEE 1815, not tunables.
44#define DNP3_BLOCK_LEN 16 ///< user-data octets per data block (the last may be shorter)
45#define DNP3_CRC_LEN 2 ///< CRC-16/DNP appended after each block, low octet first
46#define DNP3_HEADER_LEN 8 ///< header octets the header CRC covers: 0x0564 LEN CTRL DEST SRC
47#define DNP3_HEADER_BLOCK_LEN 10 ///< whole header block = DNP3_HEADER_LEN + DNP3_CRC_LEN
48#define DNP3_LEN_OVERHEAD 5 ///< octets LEN counts beyond user data: CTRL + DEST + SRC
49
50/** @brief CRC-16/DNP (poly 0x3D65, init 0, reflected, final XOR 0xFFFF). */
51uint16_t dnp3_crc(const uint8_t *data, size_t len);
52
53/**
54 * @brief Build a complete data-link frame (header block + CRC'd data blocks).
55 * @param control link-layer control octet (DIR/PRM/FCB/FCV + function code).
56 * @param dest 16-bit destination address (written little-endian).
57 * @param src 16-bit source address (written little-endian).
58 * @return total octets written, or 0 on overflow / user_data_len > DNP3_MAX_USER_DATA.
59 */
60size_t dnp3_build_frame(uint8_t *buf, size_t cap, uint8_t control, uint16_t dest, uint16_t src,
61 const uint8_t *user_data, size_t user_data_len);
62
63/** @brief A parsed data-link frame header (the user data is de-blocked separately). */
64struct Dnp3Frame
65{
66 uint8_t length; ///< the LEN field value
67 uint8_t control; ///< link-layer control octet
68 uint16_t dest;
69 uint16_t src;
70};
71
72/**
73 * @brief Parse + CRC-validate a frame, de-blocking the user data (per-block CRCs stripped).
74 * @param out_user receives the reassembled user data.
75 * @param out_cap capacity of @p out_user.
76 * @param out_user_len receives the user-data length.
77 * @return true on a complete, all-CRC-valid frame; false on a bad start word, an invalid
78 * LEN, truncation, a header or block CRC mismatch, or an out_user overflow.
79 */
80bool dnp3_parse_frame(const uint8_t *buf, size_t len, Dnp3Frame *out, uint8_t *out_user, size_t out_cap,
81 size_t *out_user_len);
82
83#endif // DETWS_ENABLE_DNP3
84
85#endif // DETERMINISTICESPASYNCWEBSERVER_DNP3_H
User-facing configuration for DeterministicESPAsyncWebServer.