Notes on heap fragmentation, UDP saturation, throttling/coalescing, buffer reuse, yielding, and payload sizing for reliable streaming/broadcast.
3.1 KiB
3.1 KiB
Streaming API (WebSocket)
Overview
The streaming API exposes an event-driven WebSocket at /ws. It bridges between external clients and the internal event bus:
- Incoming WebSocket JSON
{ event, payload }→ctx.fire(event, payload) - Local events → broadcasted to all connected WebSocket clients as
{ event, payload }
This allows real-time control and observation of the system without polling.
URL
ws://<device-ip>/ws
Message Format
- Client → Device
{
"event": "<event-name>",
"payload": "<json-string>" | { /* inline JSON */ }
}
- Device → Client
{
"event": "<event-name>",
"payload": "<json-string>"
}
Notes:
- The device accepts
payloadas a string or a JSON object/array. Objects are serialized into a string before dispatching to local subscribers to keep a consistent downstream contract. - A minimal ack
{ "ok": true }is sent after a valid inbound message.
Echo suppression (origin tagging)
- To prevent the sender from receiving an immediate echo of its own message, the server injects a private field into JSON payloads:
_origin: "ws:<clientId>"
- When re-broadcasting local events to WebSocket clients, the server:
- Strips the
_originfield from the outgoing payload - Skips the originating
clientIdso only other clients receive the message - If a payload is not valid JSON (plain string), no origin tag is injected and the message may be echoed
- Strips the
Event Bus Integration
- The WebSocket registers an
onAnysubscriber toNodeContextso that all local events are mirrored to clients. - Services should subscribe to specific events via
ctx.on("<name>", ...).
Examples
- Set a solid color on NeoPattern:
{
"event": "api/neopattern/color",
"payload": { "color": "#FF0000", "brightness": 128 }
}
- Broadcast a cluster event (delegated to core):
{
"event": "cluster/broadcast",
"payload": {
"event": "api/neopattern/color",
"data": { "color": "#00FF00", "brightness": 128 }
}
}
Reference Implementation
- WebSocket setup and bridging are implemented in
ApiServer. - Global event subscription uses
NodeContext::onAny.
Related docs:
ClusterBroadcast.md— centralized UDP broadcasting and CLUSTER_EVENT format
Things to consider
- High-frequency updates can overwhelm ESP8266:
- Frequent JSON parse/serialize and
Stringallocations fragment heap and may cause resets (e.g., Exception(3)). - UDP broadcast on every message amplifies load; WiFi/UDP buffers can back up.
- Prefer ≥50–100 ms intervals; microbursts at 10 ms are risky.
- Frequent JSON parse/serialize and
- Throttle and coalesce:
- Add a minimum interval in the core
cluster/broadcasthandler. - Optionally drop redundant updates (e.g., same color as previous).
- Add a minimum interval in the core
- Reduce allocations:
- Reuse
StaticJsonDocument/preallocated buffers in hot paths. - Avoid re-serializing when possible; pass-through payload strings.
- Reserve
Stringcapacity when reuse is needed.
- Reuse
- Yielding:
- Call
yield()in long-running or bursty paths to avoid WDT.
- Call
- Packet size:
- Keep payloads small to fit
ClusterProtocol::UDP_BUF_SIZEand reduce airtime.
- Keep payloads small to fit