ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
s7comm.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 s7comm.h
6 * @brief Siemens S7comm PDU codec (PC_ENABLE_S7COMM) - zero-heap builder + parser for the
7 * S7-300/400 communication PDUs, carried inside a COTP Data TPDU (services/fieldbus/cotp) over
8 * ISO-on-TCP (port 102).
9 *
10 * An S7comm PDU starts with a header then a parameter section then an optional data section:
11 * @code
12 * 0x32 ROSCTR redundancy(2) pdu-ref(2) param-len(2) data-len(2) [err-class err-code]
13 * <parameter ...> <data ...>
14 * @endcode
15 * The header is 10 octets, or 12 for an Ack_Data response (which adds a 2-octet error code).
16 * A Read Var job (function 0x04) carries one or more S7-ANY request items (area / DB / byte
17 * address / element count); the Ack_Data response carries, per item, a return code + a data
18 * transport size + a length + the value bytes. Per the protocol, the response length is in
19 * BITS for the bit/byte/int transport sizes (3/4/5) and in BYTES otherwise, and each item
20 * is padded to an even length except the last.
21 *
22 * Constants and the length rule are verified against the Wireshark S7comm dissector. This
23 * codec produces / consumes the S7 PDU; wrap it with `pc_cotp_build_dt` + `pc_tpkt_build`.
24 *
25 * @author Douglas Quigg (dstroy0)
26 * @date 2026
27 */
28
29#ifndef PROTOCORE_S7COMM_H
30#define PROTOCORE_S7COMM_H
31
32#include "protocore_config.h"
33
34#if PC_ENABLE_S7COMM
35
36#include <stddef.h>
37#include <stdint.h>
38
39#define S7_PROTOCOL_ID 0x32 ///< constant first octet of every S7comm PDU
40
41// ROSCTR (message type).
42#define S7_ROSCTR_JOB 0x01
43#define S7_ROSCTR_ACK 0x02
44#define S7_ROSCTR_ACK_DATA 0x03
45#define S7_ROSCTR_USERDATA 0x07
46
47// Parameter function codes.
48#define S7_FUNC_SETUP_COMM 0xF0
49#define S7_FUNC_READ_VAR 0x04
50#define S7_FUNC_WRITE_VAR 0x05
51
52// Memory area codes (in an S7-ANY item).
53#define S7_AREA_INPUTS 0x81 ///< process inputs (I/E)
54#define S7_AREA_OUTPUTS 0x82 ///< process outputs (Q/A)
55#define S7_AREA_FLAGS 0x83 ///< flags / merker (M)
56#define S7_AREA_DB 0x84 ///< data blocks (DB)
57#define S7_AREA_COUNTER 0x1C
58#define S7_AREA_TIMER 0x1D
59
60// Request-item transport sizes (the element type).
61#define S7_TS_BIT 1
62#define S7_TS_BYTE 2
63#define S7_TS_CHAR 3
64#define S7_TS_WORD 4
65#define S7_TS_INT 5
66#define S7_TS_DWORD 6
67#define S7_TS_DINT 7
68#define S7_TS_REAL 8
69
70// Response data transport sizes (length-in-bits for BIT/BYTE/INT = 3/4/5).
71#define S7_DTS_NULL 0
72#define S7_DTS_BIT 3
73#define S7_DTS_BYTE 4
74#define S7_DTS_INT 5
75#define S7_DTS_DINT 6
76#define S7_DTS_REAL 7
77#define S7_DTS_OCTET 9
78
79#define S7_SYNTAX_S7ANY 0x10 ///< S7-ANY address syntax id
80#define S7_RET_OK 0xFF ///< data item return code: success
81
82/** @brief Build a Setup Communication job. Returns the PDU length, or 0 on overflow. */
83size_t pc_s7_build_setup(uint8_t *buf, size_t cap, uint16_t pdu_ref, uint16_t max_amq_calling, uint16_t max_amq_called,
84 uint16_t pdu_size);
85
86/** @brief One Read Var item (an S7-ANY pointer). */
87struct S7ReadItem
88{
89 uint8_t area; ///< S7_AREA_*
90 uint16_t db_number; ///< DB number (0 for non-DB areas)
91 uint32_t byte_address; ///< starting byte address
92 uint8_t transport_size; ///< S7_TS_* (element type)
93 uint16_t count; ///< number of elements
94};
95
96/** @brief Build a Read Var job for @p n items. Returns the PDU length, or 0 on overflow. */
97size_t pc_s7_build_read_request(uint8_t *buf, size_t cap, uint16_t pdu_ref, const S7ReadItem *items, size_t n);
98
99/** @brief One Write Var item: an S7-ANY pointer (as for a read) plus the value bytes to write. */
100struct S7WriteItem
101{
102 uint8_t area; ///< S7_AREA_*
103 uint16_t db_number; ///< DB number (0 for non-DB areas)
104 uint32_t byte_address; ///< starting byte address
105 uint8_t transport_size; ///< S7_TS_* (parameter-spec element type)
106 uint16_t count; ///< number of elements (parameter spec)
107 uint8_t data_transport_size; ///< S7_DTS_* (data item; sets the bit/byte length rule)
108 const uint8_t *data; ///< value bytes to write
109 uint16_t data_len; ///< value length in BYTES
110};
111
112/**
113 * @brief Build a Write Var job (function 0x05) for @p n items. Mirrors the read request's parameter (the same
114 * 12-octet S7-ANY item specs) and appends a data section: per item a return code (0x00) + data
115 * transport size + a 2-octet length + the value bytes, each item padded to an even length except the
116 * last. The length field is in BITS for the bit/byte/int data transport sizes (3/4/5) and in BYTES
117 * otherwise, matching pc_s7_read_next_item. @return the PDU length, or 0 on overflow / bad input.
118 */
119size_t pc_s7_build_write_request(uint8_t *buf, size_t cap, uint16_t pdu_ref, const S7WriteItem *items, size_t n);
120
121/** @brief A parsed S7comm header. @ref param / @ref data point INTO the source buffer. */
122struct S7Header
123{
124 uint8_t rosctr;
125 uint16_t pdu_ref;
126 uint16_t param_len;
127 uint16_t data_len;
128 uint8_t error_class; ///< Ack_Data only
129 uint8_t error_code; ///< Ack_Data only
130 size_t header_len; ///< 10 or 12
131 const uint8_t *param;
132 const uint8_t *data;
133};
134
135/** @brief Parse + validate an S7comm header (protocol id, lengths). */
136bool pc_s7_parse_header(const uint8_t *buf, size_t len, S7Header *out);
137
138/** @brief One Read Var response data item. @ref data points INTO the source buffer. */
139struct S7DataItem
140{
141 uint8_t return_code; ///< S7_RET_OK on success
142 uint8_t transport_size; ///< S7_DTS_*
143 const uint8_t *data; ///< value bytes
144 size_t data_len; ///< value length in BYTES (the bit length is converted)
145};
146
147/**
148 * @brief Read the next Read Var response data item from the data section.
149 * @param data the @ref S7Header data pointer; @param data_len its data_len.
150 * @param offset in/out cursor, start at 0; advanced past the item (and its even-pad).
151 * @return true on a complete item; false at end-of-section or on truncation.
152 */
153bool pc_s7_read_next_item(const uint8_t *data, size_t data_len, size_t *offset, S7DataItem *out);
154
155#endif // PC_ENABLE_S7COMM
156
157#endif // PROTOCORE_S7COMM_H
User-facing configuration for ProtoCore.