Layer: L6 Presentation ยท Build flags: none (core; WebSocket is on by default)
What this example teaches
This registers a WebSocket endpoint (RFC 6455) at /ws with three callbacks - connect, message, close - and echoes each text frame back with an echo: prefix. A tiny test page at / connects and lets you type.
One call, three callbacks. on_ws(path, connect, message, close) wires the whole endpoint:
server.on_ws(
"/ws", ws_connect, ws_message,
ws_close);
void ws_close(WsConn *ws, WsCloseCode code)
Send a Close frame and mark the slot WsParseState::WS_CLOSED.
Reading the incoming frame. When a message arrives, the payload is in ws_pool[ws_id].buf (null-terminated, up to WS_FRAME_SIZE). You reply with ws_send_text() (or ws_send_binary()):
void ws_message(uint8_t ws_id) {
snprintf(out,
sizeof(out),
"echo: %s", (
const char *)
ws_pool[ws_id].buf);
server.ws_send_text(ws_id, out);
}
#define WS_FRAME_SIZE
Maximum WebSocket frame payload in bytes.
WsConn ws_pool[MAX_WS_CONNS]
Pool of WebSocket connection state, one per MAX_WS_CONNS.
The connect callback greets the new client; the close callback is where you would release any per-connection state (none here). Each WebSocket occupies one connection slot (capped at MAX_WS_CONNS). For wss:// over TLS see SecureWebSocket; for transparent compression see WebSocketCompression.
Build and run
pio ci --board=esp32dev --project-option="framework=arduino" \
--lib="." examples/L6-Presentation/WebSocket/WebSocket.ino
Flash, browse to http://<ip>/, and type - each line comes back echoed.
Annotated source
The complete sketch (WebSocket.ino), reproduced verbatim with added explanatory comments:
static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";
static const char PAGE[] = "<!doctype html><meta charset=utf-8><title>WS echo</title>"
"<input id=i autofocus><pre id=o></pre><script>"
"var w=new WebSocket('ws://'+location.host+'/ws');"
"w.onmessage=function(e){o.textContent+=e.data+'\\n'};"
"i.onkeydown=function(e){if(e.key=='Enter'){w.send(i.value);i.value=''}}</script>";
void ws_connect(uint8_t ws_id)
{
server.ws_send_text(ws_id, "connected to /ws - type something");
}
void ws_message(uint8_t ws_id)
{
snprintf(out,
sizeof(out),
"echo: %s", (
const char *)
ws_pool[ws_id].buf);
server.ws_send_text(ws_id, out);
}
{
(void)ws_id;
}
void setup()
{
Serial.print("Connecting to WiFi");
{
delay(250);
Serial.print('.');
}
Serial.printf("IP: %u.%u.%u.%u\n", (unsigned)(ip & 0xFF), (unsigned)((ip >> 8) & 0xFF),
(unsigned)((ip >> 16) & 0xFF), (unsigned)((ip >> 24) & 0xFF));
server.on_ws(
"/ws", ws_connect, ws_message,
ws_close);
}
void loop()
{
}
Single-port HTTP server with deterministic, zero-allocation execution.
void send(uint8_t slot_id, int code, const char *content_type, const char *payload)
Send an HTTP response with a body and close the connection.
int32_t begin(const WebServerConfig *cfg=nullptr)
Initialize all connection slots and open all registered listeners.
void on(const char *path, HttpMethod method, Handler callback)
Register a route handler.
void handle()
Drive the server - call every Arduino loop() iteration.
bool init_wifi_physical(const char *, const char *)
Connect to a WiFi access point.
uint32_t pc_net_egress_ip(void)
IPv4 (network byte order) of the current egress interface, or 0 if none.
bool wifi_ready()
True if the WiFi station link is up (associated + an IP is assigned).
Layer 1 (Physical) - link bring-up and live egress-interface reporting.
Layer 7 (Application) - public HTTP routing API.
@ HTTP_GET
Safe, idempotent read.
Fully-parsed HTTP/1.1 request.