DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
iface_bridge.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 iface_bridge.h
6 * @brief User-defined address:port -> hardware-bus translation (DETWS_ENABLE_IFACE_BRIDGE).
7 *
8 * A configurable "device server": the application registers rules mapping a listen address:port (plus
9 * TCP/UDP) to a hardware endpoint - a UART, an SPI chip-select, or an I2C address - so a network client
10 * talking to `x.x.x.x:nnnn` is transparently bridged to that bus. Two payload models:
11 *
12 * - STREAM (UART): raw bidirectional passthrough. Socket bytes are written to the UART and UART bytes
13 * flow back to the socket, with no framing (a classic serial-device server / ser2net).
14 * - TRANSACTION (SPI / I2C, also usable for UART): the socket carries framed write-then-read
15 * transactions, which is what master-initiated buses need. Each request frame is
16 * uint16 write_len (big-endian) || uint16 read_len (big-endian) || write_bytes[write_len]
17 * and the reply is the read_len bytes clocked/read back. The bus address (I2C 7-bit addr) or
18 * chip-select (SPI CS gpio) + clock/mode come from the rule's target, so the frame stays generic.
19 *
20 * This header is the pure, host-tested core: the fixed-capacity rule table (zero heap) and the
21 * transaction frame codec. The actual bus I/O (Serial / SPI / Wire) and the PROTO_BRIDGE listener are
22 * the ESP32 step (iface_bridge_hw.*), kept separate exactly like the peripheral services.
23 *
24 * @author Douglas Quigg (dstroy0)
25 * @date 2026
26 */
27
28#ifndef DETERMINISTICESPASYNCWEBSERVER_IFACE_BRIDGE_H
29#define DETERMINISTICESPASYNCWEBSERVER_IFACE_BRIDGE_H
30
31#include "ServerConfig.h"
32
33#if DETWS_ENABLE_IFACE_BRIDGE
34
35#include "network_drivers/network/ip.h" // DetIp (carry the full bind address, never a flattened one)
36#include <stddef.h>
37#include <stdint.h>
38
39// DETWS_BRIDGE_MAX_RULES is defined in ServerConfig.h (the config owner).
40
41#define DETWS_BRIDGE_TXN_HDR 4 ///< transaction frame header: write_len(2) + read_len(2), big-endian
42
43/// Which hardware bus a rule targets.
44enum class BridgeBus : uint8_t
45{
46 uart = 0,
47 spi = 1,
48 i2c = 2
49};
50
51/// How socket bytes map to bus activity.
52enum class BridgeMode : uint8_t
53{
54 stream = 0, ///< raw bidirectional passthrough (UART)
55 transaction = 1 ///< framed write-then-read (SPI / I2C; also usable for UART)
56};
57
58/// The transport a rule listens on (matches the value stored on the wire; kept generic to avoid a hard
59/// dependency on ConnProto here in the pure core).
60enum class BridgeProto : uint8_t
61{
62 tcp = 0,
63 udp = 1
64};
65
66/// One hardware endpoint a network port is bridged to.
67struct BridgeTarget
68{
69 BridgeBus bus;
70 BridgeMode mode;
71 uint8_t unit; ///< UART port # / SPI host # / I2C bus #
72 uint16_t addr_cs; ///< I2C 7-bit address, or SPI chip-select GPIO
73 uint32_t rate; ///< UART baud, or SPI/I2C clock (Hz)
74 uint8_t spi_mode; ///< SPI mode 0..3 (SPI only)
75 uint8_t bit_order; ///< 0 = MSB-first, 1 = LSB-first (SPI only)
76};
77
78/// A single address:port -> bus mapping.
79struct BridgeRule
80{
81 DetIp listen_ip; ///< bind address (x.x.x.x / [v6]); family DET_IP_NONE = any interface
82 uint16_t listen_port; ///< nnnn
83 BridgeProto proto; ///< TCP or UDP
84 BridgeTarget target;
85 bool used;
86};
87
88// ---------------------------------------------------------------------------------------------
89// Rule table (zero heap; register before begin()). Pure - host-testable.
90// ---------------------------------------------------------------------------------------------
91
92/// Remove all rules.
93void bridge_clear();
94
95/// Register a rule. Returns false if the table is full or a rule already binds the same port+proto.
96bool bridge_add(const BridgeRule *rule);
97
98/// Convenience: build + add a rule in one call. @p ip may be NULL for "any interface". Returns false on
99/// a bad address, a full table, or a duplicate port+proto.
100bool bridge_map(const char *ip, uint16_t port, BridgeProto proto, const BridgeTarget *target);
101
102/// Find the rule bound to @p port + @p proto, or NULL. This is the listener-dispatch lookup.
103const BridgeRule *bridge_find(uint16_t port, BridgeProto proto);
104
105/// Number of registered rules.
106uint8_t bridge_count();
107
108// ---------------------------------------------------------------------------------------------
109// Transaction frame codec (pure). write_len / read_len are big-endian.
110// ---------------------------------------------------------------------------------------------
111
112/**
113 * @brief Parse a transaction request from a socket buffer.
114 *
115 * On a complete frame, sets @p write_len / @p read_len, points @p write_data at the write payload inside
116 * @p buf, and returns the total bytes the frame occupies (header + write payload). Returns 0 when @p buf
117 * does not yet hold the whole frame (the caller should read more), so a partial header or a partial write
118 * payload both yield 0.
119 */
120size_t bridge_txn_parse(const uint8_t *buf, size_t len, uint16_t *write_len, uint16_t *read_len,
121 const uint8_t **write_data);
122
123/**
124 * @brief Build a transaction request frame (header + write payload) into @p out.
125 * @return bytes written, or 0 if @p out is too small.
126 */
127size_t bridge_txn_build(uint8_t *out, size_t cap, const uint8_t *write_data, uint16_t write_len, uint16_t read_len);
128
129#endif // DETWS_ENABLE_IFACE_BRIDGE
130
131#endif // DETERMINISTICESPASYNCWEBSERVER_IFACE_BRIDGE_H
User-facing configuration for DeterministicESPAsyncWebServer.
Layer 3 (Network) - a family-tagged IP address (IPv4 or IPv6) with RFC-faithful text parsing,...
A v4 or v6 address in network (big-endian) byte order.
Definition ip.h:52