DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
iec60870.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 iec60870.h
6 * @brief IEC 60870-5-101 / -104 telecontrol (SCADA) codec (DETWS_ENABLE_IEC60870).
7 *
8 * The IEC 60870-5 utility-SCADA protocol in its two common transports:
9 * - **-104** over TCP: the APCI (`68 LEN` + 4 control octets) in its I / S / U formats,
10 * carrying an ASDU. I-format transfers numbered information (send/receive sequence
11 * numbers); S-format acknowledges; U-format does STARTDT / STOPDT / TESTFR.
12 * - **-101** over serial: the FT1.2 link frames - fixed length (`10 C A CS 16`) and variable
13 * length (`68 L L 68 C A <ASDU> CS 16`, 8-bit sum checksum), with a 1-octet link address.
14 * - The shared **ASDU** header (type id, variable structure qualifier, cause of transmission,
15 * common address) and the 3-octet Information Object Address used by both.
16 *
17 * The per-type-id information elements (single/double point, measured values, commands) are
18 * the application's; this is the framing + ASDU-header layer, with named type-id / COT
19 * constants for the common ones. Pure and host-tested. Bridge an RTU / outstation onto Wi-Fi:
20 * run -104 over the shipped TCP stack, or -101 over a UART through an RS-232/485 transceiver.
21 *
22 * @author Douglas Quigg (dstroy0)
23 * @date 2026
24 */
25
26#ifndef DETERMINISTICESPASYNCWEBSERVER_IEC60870_H
27#define DETERMINISTICESPASYNCWEBSERVER_IEC60870_H
28
29#include "ServerConfig.h"
30
31#if DETWS_ENABLE_IEC60870
32
33#include <stddef.h>
34#include <stdint.h>
35
36#define IEC_START_104 0x68u ///< -104 APCI start octet (also the -101 variable-frame start)
37#define IEC_START_FIXED 0x10u ///< -101 fixed-length frame start octet
38#define IEC_STOP 0x16u ///< -101 frame stop octet
39#define IEC104_APCI_LEN 6u ///< -104 control field: start + length + 4 control octets
40
41// -104 U-format control commands (octet 1).
42#define IEC104_STARTDT_ACT 0x07u
43#define IEC104_STARTDT_CON 0x0Bu
44#define IEC104_STOPDT_ACT 0x13u
45#define IEC104_STOPDT_CON 0x23u
46#define IEC104_TESTFR_ACT 0x43u
47#define IEC104_TESTFR_CON 0x83u
48
49// Common ASDU type identifications (IEC 60870-5-101 ch. 7.2.1).
50#define IEC_TYPE_M_SP_NA_1 1 ///< single-point information
51#define IEC_TYPE_M_DP_NA_1 3 ///< double-point information
52#define IEC_TYPE_M_ME_NA_1 9 ///< measured value, normalized
53#define IEC_TYPE_M_ME_NB_1 11 ///< measured value, scaled
54#define IEC_TYPE_M_ME_NC_1 13 ///< measured value, short float
55#define IEC_TYPE_C_SC_NA_1 45 ///< single command
56#define IEC_TYPE_C_DC_NA_1 46 ///< double command
57#define IEC_TYPE_M_EI_NA_1 70 ///< end of initialization
58#define IEC_TYPE_C_IC_NA_1 100 ///< interrogation command
59#define IEC_TYPE_C_CS_NA_1 103 ///< clock synchronization command
60
61// Common causes of transmission (COT, low 6 bits).
62#define IEC_COT_PERIODIC 1
63#define IEC_COT_SPONTANEOUS 3
64#define IEC_COT_REQUEST 5
65#define IEC_COT_ACTIVATION 6
66#define IEC_COT_ACT_CON 7
67#define IEC_COT_DEACTIVATION 8
68#define IEC_COT_ACT_TERM 10
69#define IEC_COT_INTERROGATED 20
70
71// -101 link-layer control-field function codes (low nibble; direction/FCB/FCV in the high bits).
72#define IEC_FC_RESET_LINK 0x00 ///< reset of remote link (SEND/CONFIRM)
73#define IEC_FC_TEST_LINK 0x02 ///< test function for link
74#define IEC_FC_USER_DATA_CONFIRM 0x03 ///< user data, confirmed
75#define IEC_FC_USER_DATA_NOREPLY 0x04 ///< user data, no reply
76#define IEC_FC_REQUEST_CLASS1 0x0A ///< request class 1 data
77#define IEC_FC_REQUEST_CLASS2 0x0B ///< request class 2 data
78
79/** @brief -104 APCI frame formats. */
80enum class Iec104Format : uint8_t
81{
82 IEC104_I = 0, ///< information transfer (numbered; carries an ASDU)
83 IEC104_S, ///< supervisory (acknowledge only)
84 IEC104_U, ///< unnumbered (STARTDT / STOPDT / TESTFR)
85};
86
87/** @brief A parsed -104 APCI. */
88struct Iec104Apci
89{
90 Iec104Format format;
91 uint16_t ns; ///< send sequence number (I-format)
92 uint16_t nr; ///< receive sequence number (I + S formats)
93 uint8_t u_cmd; ///< U-format command octet
94 const uint8_t *asdu; ///< ASDU slice (I-format), or nullptr
95 size_t asdu_len;
96};
97
98/** @brief A parsed ASDU header (the 6 octets before the information objects). */
99struct IecAsduHeader
100{
101 uint8_t type_id;
102 bool sq; ///< structure qualifier: elements share one base IOA
103 uint8_t count; ///< number of information objects / elements (0..127)
104 bool test; ///< COT test bit
105 bool negative; ///< COT positive/negative confirm bit
106 uint8_t cot; ///< cause of transmission (low 6 bits)
107 uint8_t orig_addr; ///< originator address
108 uint16_t common_addr; ///< common address of the ASDU
109};
110
111// --- IEC 60870-5-104 APCI (over TCP) ---
112
113/** @brief Build an I-format APDU carrying @p asdu (numbered with @p ns / @p nr). */
114size_t iec104_build_i(uint8_t *buf, size_t cap, uint16_t ns, uint16_t nr, const uint8_t *asdu, size_t asdu_len);
115
116/** @brief Build an S-format (supervisory) APDU acknowledging up to @p nr. */
117size_t iec104_build_s(uint8_t *buf, size_t cap, uint16_t nr);
118
119/** @brief Build a U-format APDU with command @p u_cmd (IEC104_STARTDT_ACT, ...). */
120size_t iec104_build_u(uint8_t *buf, size_t cap, uint8_t u_cmd);
121
122/** @brief Parse one -104 APDU. Fills @p out and @p consumed (the whole APDU length). */
123bool iec104_parse(const uint8_t *buf, size_t len, Iec104Apci *out, size_t *consumed);
124
125// --- ASDU header + Information Object Address (shared by -101 and -104) ---
126
127/** @brief Build the 6-octet ASDU header (type id, VSQ, COT[2], common address[2]). */
128size_t iec_asdu_build_header(uint8_t *buf, size_t cap, const IecAsduHeader *h);
129
130/** @brief Parse the 6-octet ASDU header; @p consumed is 6 on success. */
131bool iec_asdu_parse_header(const uint8_t *buf, size_t len, IecAsduHeader *out, size_t *consumed);
132
133/** @brief Write a 3-octet Information Object Address (little-endian). */
134size_t iec_put_ioa(uint8_t *buf, size_t cap, uint32_t ioa);
135
136/** @brief Read a 3-octet Information Object Address (little-endian). */
137uint32_t iec_get_ioa(const uint8_t *p);
138
139// --- IEC 60870-5-101 FT1.2 link frames (over serial) ---
140
141/** @brief Build a fixed-length frame: 10 C A CS 16 (1-octet link address). */
142size_t iec101_build_fixed(uint8_t *buf, size_t cap, uint8_t control, uint8_t addr);
143
144/** @brief Build a variable-length frame: 68 L L 68 C A <asdu> CS 16 (1-octet link address). */
145size_t iec101_build_variable(uint8_t *buf, size_t cap, uint8_t control, uint8_t addr, const uint8_t *asdu,
146 uint8_t asdu_len);
147
148/** @brief A parsed -101 FT1.2 frame. */
149struct Iec101Frame
150{
151 bool fixed; ///< true => fixed-length frame (no ASDU)
152 uint8_t control; ///< link control field
153 uint8_t addr; ///< link address
154 const uint8_t *asdu; ///< ASDU slice (variable frame), or nullptr
155 uint8_t asdu_len;
156};
157
158/** @brief Parse one -101 FT1.2 frame (validates start/stop, doubled length, sum checksum). */
159bool iec101_parse(const uint8_t *buf, size_t len, Iec101Frame *out, size_t *consumed);
160
161#endif // DETWS_ENABLE_IEC60870
162#endif // DETERMINISTICESPASYNCWEBSERVER_IEC60870_H
User-facing configuration for DeterministicESPAsyncWebServer.