DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
hart.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 hart.h
6 * @brief HART / HART-IP process-instrument protocol codec (DETWS_ENABLE_HART).
7 *
8 * HART (Highway Addressable Remote Transducer, FieldComm) is the field-instrument protocol that rides
9 * the 4-20 mA current loop as an FSK signal, and - as **HART-IP** - travels over UDP/TCP 5094 as the
10 * gateway-friendly, front-end-free path. This is the wire codec for both:
11 *
12 * - The **HART command frame**: `[delimiter][address...][command][byte-count][data...][checksum]`, where
13 * the checksum is the longitudinal XOR parity of every byte from the delimiter through the last data
14 * byte (the preamble of 0xFF sync bytes is transport, not checksummed). Short (1-byte polling) and
15 * long (5-byte unique-ID) addressing are both handled by passing the address bytes.
16 * - The **HART-IP message header** (8 octets): version, message type, message id, status, a 2-byte
17 * sequence number, and the 2-byte total message length - wraps a HART PDU for UDP/TCP transport.
18 *
19 * Pure, zero heap, no stdlib, host-testable. The FSK physical layer (a HART modem IC over UART) is the
20 * hardware-gated path; HART-IP needs no front end.
21 */
22
23#ifndef DETERMINISTICESPASYNCWEBSERVER_HART_H
24#define DETERMINISTICESPASYNCWEBSERVER_HART_H
25
26#include "ServerConfig.h"
27#include <stddef.h>
28#include <stdint.h>
29
30#if DETWS_ENABLE_HART
31
32/** @brief HART frame delimiter frame-type bits (low 3 bits) + long-address bit (bit 7). Wire values,
33 * the LONG_ADDR bit is OR'd in, so integer constants in a namespacing struct (cast-free). */
34struct HartDelim
35{
36 static constexpr uint8_t HART_DELIM_BACK = 0x01; ///< burst (field device, unsolicited).
37 static constexpr uint8_t HART_DELIM_STX = 0x02; ///< master -> field device (request).
38 static constexpr uint8_t HART_DELIM_ACK = 0x06; ///< field device -> master (response).
39 static constexpr uint8_t HART_DELIM_LONG_ADDR = 0x80; ///< OR into the delimiter for 5-byte unique-ID addressing.
40};
41
42/** @brief HART-IP message types + common message ids (wire constants). */
43struct HartIp
44{
45 static constexpr uint8_t HARTIP_MSG_REQUEST = 0;
46 static constexpr uint8_t HARTIP_MSG_RESPONSE = 1;
47 static constexpr uint8_t HARTIP_MSG_PUBLISH = 2;
48 static constexpr uint8_t HARTIP_ID_SESSION_INIT = 0;
49 static constexpr uint8_t HARTIP_ID_SESSION_CLOSE = 1;
50 static constexpr uint8_t HARTIP_ID_KEEPALIVE = 2;
51 static constexpr uint8_t HARTIP_ID_TOKEN_PDU = 3; ///< a HART token-passing PDU (a HART frame) is the payload.
52 static constexpr uint8_t HARTIP_HEADER_LEN = 8;
53};
54
55/** @brief Longitudinal XOR checksum of @p len bytes (the HART frame check byte). */
56uint8_t detws_hart_checksum(const uint8_t *bytes, size_t len);
57
58/**
59 * @brief Build a HART command frame (no preamble - the transport prepends the 0xFF sync bytes).
60 * @param delimiter frame delimiter (e.g. HART_DELIM_STX, OR HART_DELIM_LONG_ADDR for long addressing).
61 * @param addr address bytes (1 for short, 5 for long).
62 * @param addr_len 1 or 5.
63 * @param command HART command number.
64 * @param data data bytes (may be null when data_len == 0).
65 * @param data_len number of data bytes (also the frame's byte-count field).
66 * @param out output buffer.
67 * @param cap capacity of @p out.
68 * @return the frame length written, or 0 if it would not fit or addr_len is invalid.
69 */
70size_t detws_hart_build(uint8_t delimiter, const uint8_t *addr, size_t addr_len, uint8_t command, const uint8_t *data,
71 size_t data_len, uint8_t *out, size_t cap);
72
73/** @brief A parsed HART frame (pointers into the input buffer). */
74struct HartFrame
75{
76 uint8_t delimiter;
77 const uint8_t *addr;
78 size_t addr_len; ///< 1 (short) or 5 (long), derived from the delimiter's long-address bit.
79 uint8_t command;
80 uint8_t byte_count;
81 const uint8_t *data;
82 size_t data_len;
83};
84
85/**
86 * @brief Validate + parse a HART frame (checksum checked).
87 * @return true if the frame is well-formed and the checksum matches; fills @p out.
88 */
89bool detws_hart_parse(const uint8_t *frame, size_t len, HartFrame *out);
90
91/**
92 * @brief Build the 8-octet HART-IP message header into @p out (>= 8 bytes).
93 * @param msg_type HARTIP_MSG_*.
94 * @param msg_id HARTIP_ID_*.
95 * @param status status byte (0 in a request).
96 * @param seq sequence number.
97 * @param total_len total message length including this header (header + payload).
98 * @return 8, or 0 if @p cap < 8.
99 */
100size_t detws_hartip_build_header(uint8_t msg_type, uint8_t msg_id, uint8_t status, uint16_t seq, uint16_t total_len,
101 uint8_t *out, size_t cap);
102
103#endif // DETWS_ENABLE_HART
104#endif // DETERMINISTICESPASYNCWEBSERVER_HART_H
User-facing configuration for DeterministicESPAsyncWebServer.