Socket-based transport implementations for the Flowduino ESPressio Development Platform.
ESPressio Sockets provides IP/socket-oriented communication adapters separately from hardware-radio-specific ESPressio libraries. Its initial release focuses on concrete IEventTransport implementations for ESPressio Event.
The current repository version is 0.2.2.
ESPressio Sockets 0.2.2 targets the ESP32 family under Arduino-ESP32 and uses C++17.
The library uses Arduino-ESP32 native networking classes for UDP, TCP and TLS. WebSocket support is provided through the mature Links2004 arduinoWebSockets library.
The intended responsibility boundary is:
ESPressio Event
|
| IEventTransport
v
ESPressio Sockets
|
+-- UDP
+-- TCP client
+-- TCP server
+-- TLS client
+-- WebSocket client
+-- WebSocket server
+-- MQTT / MQTT over TLS
|
v
IP networking
Hardware-radio-specific protocols remain outside this library:
ESP-NOW
-> ESPressio ESP-Now
LoRa / packet radio / other hardware radios
-> future ESPressio Radio
This keeps socket/network concerns separate from radio-hardware concerns.
The core ESPressio_Sockets.hpp header contains only the common socket types and stream-framing utilities.
Event Transport adapters require:
ESPressio Event >= 5.6.2 < 6.0.0
and therefore the Serializable support used by ESPressio Event Transport.
WebSocket adapters additionally use:
Links2004 WebSockets >= 2.3.6
MQTT uses:
PubSubClient >= 2.8
Both are declared as supported repository dependencies. arduinoWebSockets provides RFC6455 client/server functionality, while PubSubClient provides the MQTT publish/subscribe client used by the MQTT adapter.
The public API is contained within:
ESPressio::SocketsCore umbrella:
#include <ESPressio_Sockets.hpp>All Event transports:
#include <ESPressio_SocketEventTransports.hpp>Or individual adapters:
#include <ESPressio_UDPEventTransport.hpp>
#include <ESPressio_TCPClientEventTransport.hpp>
#include <ESPressio_TCPServerEventTransport.hpp>
#include <ESPressio_TLSEventTransport.hpp>
#include <ESPressio_WebSocketClientEventTransport.hpp>
#include <ESPressio_WebSocketServerEventTransport.hpp>The normal ESPressio_Sockets.hpp umbrella intentionally does not pull Event Transport adapters into projects that only need the common socket layer.
UDPEventTransport sends each Event Transport packet as one UDP datagram.
Supported addressing includes:
unicast
IPv4 broadcast
IPv4 multicast destination
multicast receive binding
multiple outbound destinations
Example:
Sockets::UDPEventTransport udp;
Sockets::UDPEventTransportConfig config;
config.LocalPort = 42000;
udp.Initialize(config);
udp.AddDestination(
{
IPAddress(192, 168, 1, 50),
42000
}
);
udp.AddBroadcastDestination(
42000
);For multicast reception:
udp.InitializeMulticast(
IPAddress(239, 42, 0, 1),
42000
);
udp.AddMulticastDestination(
IPAddress(239, 42, 0, 1),
42000
);UDP preserves packet boundaries naturally, so no additional ESPressio Sockets framing is added around the Event Transport packet.
The maximum accepted packet size defaults to:
65536 bytes
through:
ESPRESSIO_SOCKETS_MAX_EVENT_PACKET_SIZEActual usable UDP payload limits remain constrained by the IP stack and network path; applications should prefer smaller Event payloads when using UDP.
TCPClientEventTransport maintains a client connection to one TCP Event endpoint.
Configuration:
Sockets::TCPClientEventTransportConfig config;
config.Host =
"192.168.1.100";
config.Port =
43000;
config.ReconnectIntervalMilliseconds =
2000;The adapter automatically retries connections and receives Event packets from the connected server.
TCP is a byte stream rather than a message protocol, so ESPressio Sockets adds a small versioned stream frame around each Event Transport packet.
TCPServerEventTransport listens for multiple clients and broadcasts each outbound Event Transport packet to every connected TCP client.
Configuration:
Sockets::TCPServerEventTransportConfig config;
config.Port = 43000;
config.MaximumClients = 8;Incoming framed Event packets from any connected client are passed into the local Event Transport Manager.
This makes a useful hub topology:
TCP Server
/ | \
/ | \
v v v
Client A Client B Client C
TCP/TLS Event transports use:
SocketEventFrameHeadercontaining:
magic
version
payload length
The frame exists only to recover Event Transport packet boundaries from a byte stream.
It does not replace the versioned EventTransportEnvelope owned by ESPressio Event.
The layering is:
SocketEventFrameHeader
|
+-- EventTransportEnvelope
+-- Serializable Event payload
TLSEventTransport provides the same Event semantics as the TCP client transport using Arduino-ESP32 WiFiClientSecure.
It supports:
server CA verification
optional client certificate/private key
explicit insecure mode for development
automatic reconnection
Example:
Sockets::TLSEventTransportConfig config;
config.Host =
"event-server.example.com";
config.Port = 4433;
config.CACertificate =
ROOT_CA;
tls.Initialize(config);For development only:
config.Insecure = true;should be used only when certificate verification is intentionally disabled.
WebSocketClientEventTransport uses binary RFC6455 WebSocket messages for Event Transport packets.
Configuration:
Sockets::WebSocketClientEventTransportConfig config;
config.Host =
"192.168.1.100";
config.Port = 44000;
config.Path = "/";
config.Protocol = "espressio";For secure WebSocket client operation:
config.Secure = true;
config.Port = 443;
config.CACertificate = ROOT_CA;The adapter supports:
ws
wss
automatic reconnect
heartbeat/ping-pong
binary Event packets
WebSocketServerEventTransport exposes an ESP32 WebSocket server and broadcasts each outbound Event Transport packet as a binary WebSocket frame to every connected WebSocket client.
Configuration:
Sockets::WebSocketServerEventTransportConfig config;
config.Port = 44000;
config.Protocol = "espressio";
webSocketServer.Initialize(
config
);Incoming binary WebSocket messages from any connected client become inbound Event Transport packets.
The initial server adapter provides plain ws operation. Secure wss client operation is supported, while TLS termination for a WebSocket server can be placed in front of the ESP32 or added in a later transport implementation.
MQTTEventTransport maps Event Transport packets onto MQTT binary payloads.
It supports:
MQTT over TCP
MQTT over TLS
username/password authentication
separate inbound/outbound topics
automatic reconnect
configurable MQTT buffer size
keep-alive/socket timeout configuration
A typical two-device topology uses complementary topics:
Device A:
publish espressio/a-to-b
subscribe espressio/b-to-a
Device B:
publish espressio/b-to-a
subscribe espressio/a-to-b
Configuration:
Sockets::MQTTEventTransportConfig config;
config.Host = "192.168.1.10";
config.Port = 1883;
config.ClientID = "espressio-device-a";
config.OutboundTopic =
"espressio/a-to-b";
config.InboundTopic =
"espressio/b-to-a";
config.BufferSize = 4096;For MQTT over TLS:
config.Secure = true;
config.Port = 8883;
config.CACertificate = ROOT_CA;MQTT packet buffer size must be large enough for the complete ESPressio Event Transport packet plus MQTT protocol overhead.
Every socket adapter implements:
ESPressio::Event::IEventTransportand therefore registers with Event 5.4 in the same way as ESP-NOW or any future transport:
auto& manager =
Event::EventTransportManager::
GetInstance();
manager.RegisterTransport(
&udpTransport
);A Serializable Event can then be routed specifically over that socket:
manager.RegisterBidirectionalEvent<
MySerializableEvent
>(
&udpTransport
);Or multiple types:
manager.RegisterOutboundEvents<
TelemetryEvent,
DiagnosticsEvent,
StatusEvent
>(
&udpTransport
);Different transport policy can be applied simultaneously:
TelemetryEvent:
UDP OUT
TCP OUT
WebSocket NONE
CommandEvent:
UDP NONE
TCP IN
WebSocket IN/OUT
That policy remains entirely inside ESPressio Event.
Each adapter that requires continuous inbound processing owns a small FreeRTOS worker task.
Common task parameters are represented by:
SocketWorkerConfigwith:
StackSize
Priority
Core
IdleDelayMilliseconds
Worker shutdown waits for the task to exit before destroying its underlying socket resources.
IEventTransport::Send() reports that the concrete socket implementation accepted/wrote the Event Transport packet.
The initial release does not add a separate application-level delivery acknowledgement.
Transport characteristics therefore remain protocol-specific:
UDP:
datagram submission only; no delivery guarantee
TCP:
ordered reliable byte-stream delivery while connection survives
TLS:
TCP reliability plus encrypted/authenticated transport
WebSocket:
binary message transport over TCP/TLS
ESPressio Event itself remains unaware of those protocol details.
ESPressio Sockets does not automatically configure Wi-Fi credentials.
The consuming application owns network establishment:
WiFi.begin(
ssid,
password
);before initializing transports that require an active interface.
This also leaves room for future Ethernet-backed socket use without coupling socket adapters directly to Wi-Fi setup.
The initial release contains:
examples/
├── UDPEventTransport/
│ └── UDPEventTransport.ino
│
├── TCPClientEventTransport/
│ └── TCPClientEventTransport.ino
│
├── TCPServerEventTransport/
│ └── TCPServerEventTransport.ino
│
├── TLSEventTransport/
│ └── TLSEventTransport.ino
│
├── WebSocketClientEventTransport/
│ └── WebSocketClientEventTransport.ino
│
├── WebSocketServerEventTransport/
│ └── WebSocketServerEventTransport.ino
│
└── MQTTEventTransport/
└── MQTTEventTransport.ino
The TCP client/server and WebSocket client/server examples are complementary starting points for two-device testing.
The UDP example demonstrates broadcast-capable Event Transport.
Typical configuration:
[env:esp32]
platform = espressif32
framework = arduino
board = esp32dev
build_flags =
-std=gnu++17
lib_deps =
flowduino/ESPressio-Sockets@^0.2.2
flowduino/ESPressio-Event@^5.6.2
links2004/WebSockets@^2.3.6
knolleary/PubSubClient@^2.8ESPressio Sockets is intentionally concerned with transports that conceptually belong to an IP/socket/network stack.
Good candidates for future expansion include:
additional UDP multicast/group helpers
IPv6 socket transports
Unix/host socket adapters where applicable
HTTP streaming transports
SSE where bidirectional semantics can be appropriately paired
MQTT Event gateways
QUIC when supported appropriately on ESP32
TLS server support
WebSocket Secure server support
Hardware radio protocols should not be added here.
The planned separation is:
ESPressio Sockets
-> IP/socket/network protocols
ESPressio ESP-Now
-> ESP-NOW
ESPressio Radio
-> LoRa and other hardware-radio transports
This makes the transport layer composable without turning one library into a collection of unrelated communication hardware and protocols.
ESPressio Sockets 0.2.0 provides the socket/network transport layer of the ESPressio ecosystem.
The initial release provides:
- UDP Event Transport;
- broadcast and multicast UDP support;
- TCP client Event Transport;
- multi-client TCP server Event Transport;
- TLS client Event Transport;
- WebSocket client Event Transport;
- Secure WebSocket (
wss) client support; - multi-client WebSocket server Event Transport;
- MQTT and MQTT-over-TLS Event Transport;
- C++17 implementation;
- common stream framing;
- configurable worker tasks;
- complete example projects;
- compatibility with ESPressio Event 5.4 per-transport routing.
The architectural boundary is deliberate:
ESPressio Event decides which Events travel. ESPressio Sockets moves them using socket/network protocols. Hardware-radio transports remain in their own ESPressio libraries.
ESPressio Sockets 0.2.0 adds opt-in network implementations for the transport-independent System Clock synchronization API in ESPressio Timing 2.2.0.
The synchronization layer is deliberately separate from Event Transport and is not included by the normal:
#include <ESPressio_Sockets.hpp>Applications that need socket-based System Clock synchronization include:
#include <ESPressio_SocketClockSynchronization.hpp>and provide:
ESPressio Timing >= 2.2.1 < 3.0.0
The ownership boundary remains:
ESPressio Timing
|
+-- SystemClock
+-- ClockSynchronizationSample
+-- offset/delay calculation
+-- clock discipline
+-- step/slew policy
+-- synchronization state
+-- Observer notifications
|
| IClockSynchronizationTarget
v
ESPressio Sockets
|
+-- UDP exchange
+-- TCP exchange
+-- WebSocket exchange
+-- SNTP external reference
Socket transports do not implement a second clock discipline. They only acquire synchronization observations and submit them into ESPressio Timing.
UDP, TCP and WebSocket request/response synchronization use the same four timestamps:
Client Reference
T1 request transmit ------------------>
T2 request receive
T3 response transmit
T4 response receive <------------------
The completed exchange is submitted as:
Timing::ClockSynchronizationSample<
Timing::ClockTick
>so Timing owns the normal calculations:
round-trip delay = (T4 - T1) - (T3 - T2)
offset = ((T2 - T1) + (T3 - T4)) / 2
This also means all existing Timing Observer callbacks and the optional SystemClockEventBridge continue to work unchanged.
UDPClockSynchronizer is the preferred socket transport for precision synchronization because it avoids TCP retransmission and stream-buffering behavior.
A reference device can use:
Sockets::UDPClockSynchronizationConfig config;
config.Mode =
Sockets::SocketClockSynchronizationMode::Reference;
config.LocalPort = 45100;
synchronizer.Initialize(config);A client uses:
Sockets::UDPClockSynchronizationConfig config;
config.Mode =
Sockets::SocketClockSynchronizationMode::Client;
config.LocalPort = 45101;
config.ReferenceAddress =
IPAddress(192, 168, 1, 50);
config.ReferencePort = 45100;
config.SynchronizationIntervalMilliseconds = 5000;The client periodically performs the full request/response exchange and submits the resulting four timestamps into ESPressio Timing.
A reference can also periodically broadcast its current System Clock:
config.Mode =
Sockets::SocketClockSynchronizationMode::Reference;
config.EnableAuthoritativeBroadcast = true;
config.BroadcastIntervalMilliseconds = 5000;Clients listening on the same UDP port can consume these one-way synchronization observations.
One-way broadcast deliberately cannot compensate for network latency, so it is intended for lower-overhead group synchronization where the stronger request/response measurement is unnecessary.
The same authoritative mode can use an IPv4 multicast group:
config.EnableAuthoritativeMulticast = true;
config.MulticastGroup =
IPAddress(239, 45, 10, 1);
config.MulticastPort = 45100;This is useful for synchronizing a defined group of devices without local-network-wide broadcast traffic.
Version 0.2.0 adds:
TCPClockSynchronizationClient
TCPClockSynchronizationServerThe server can service multiple clients using the same versioned ESPressio socket frame already used by TCP Event Transport.
Client configuration:
Sockets::TCPClockSynchronizationClientConfig config;
config.Host = "192.168.1.50";
config.Port = 45110;
config.SynchronizationIntervalMilliseconds = 5000;TCP remains a valid convenience transport where a connection is already useful, although UDP is generally preferable when minimizing network/scheduler jitter is the priority.
Version 0.2.0 also adds:
WebSocketClockSynchronizationClient
WebSocketClockSynchronizationServerThe synchronization messages are transferred as binary WebSocket messages.
The client supports both:
ws://
wss://
using the same Links2004 WebSockets dependency already used by ESPressio Sockets Event Transport.
This is particularly useful when the System Clock authority is exposed through a WebSocket-capable network endpoint rather than raw UDP/TCP.
SNTPClockSyncProvider uses the ESP-IDF/lwIP SNTP implementation as an external absolute-time source.
Example:
Sockets::SNTPClockSyncProvider provider;
Sockets::SNTPClockSyncProviderConfig config;
config.Server = "pool.ntp.org";
config.UpdateIntervalMilliseconds = 3600000;
provider.Initialize(config);When ESP-IDF reports a successful SNTP synchronization, the received Unix reference time is submitted into ESPressio Timing rather than replacing the ESPressio clock-discipline architecture.
The provider therefore establishes the ESPressio System Clock in the Unix epoch domain while preserving:
Timing step/slew policy
Timing synchronization state
Timing accepted/rejected sample accounting
Timing Observer callbacks
SystemClockEventBridge integration
The SNTP callback does not expose the underlying NTP four packet timestamps, so this provider represents the externally synchronized SNTP time as a zero-duration reference observation. Use UDP request/response between ESPressio devices when the ESPressio four-timestamp round-trip measurement itself is required.
Only one active SNTPClockSyncProvider is supported because the underlying ESP-IDF SNTP synchronization callback is process-global.
The Clock Synchronization headers are not included by ESPressio_Sockets.hpp.
Therefore a project using only socket primitives or Event Transport does not need ESPressio Timing solely because the synchronization implementations exist in the repository.
Conversely, a project using:
#include <ESPressio_SocketClockSynchronization.hpp>must supply ESPressio Timing >=2.2.1 <3.0.0.
Version 0.2.0 adds:
examples/
├── UDPClockSynchronization/
│ └── UDPClockSynchronization.ino
├── UDPClockBroadcast/
│ └── UDPClockBroadcast.ino
├── TCPClockSynchronizationClient/
│ └── TCPClockSynchronizationClient.ino
├── TCPClockSynchronizationServer/
│ └── TCPClockSynchronizationServer.ino
├── WebSocketClockSynchronizationClient/
│ └── WebSocketClockSynchronizationClient.ino
├── WebSocketClockSynchronizationServer/
│ └── WebSocketClockSynchronizationServer.ino
└── SNTPClockSynchronization/
└── SNTPClockSynchronization.ino
These are intentionally separate from the existing Event Transport examples.