Embedded devices and remote control
Last updated Sep 22, 2026 Use Realtime SFU to connect embedded hardware to browser listeners. Pocket Radio shows how firmware connects over WebRTC, publishes audio and telemetry, and receives authorized control commands from a browser.
Pocket Radio runs on an ESP32-S3. It reads a playlist from flash, publishes Opus audio, and sends spectrum data and device state to browsers. One listener can control playback and the board's LED.
The example is experimental and targets the documented board and toolchain. It does not implement microphone capture, device speaker output, a camera, or a complete robotics stack.
Open the example Follow the firmware walkthrough
How it works
The firmware and each browser maintain a separate WebRTC peer. A Worker holds the SFU credentials and handles signaling. A Durable Object tracks the device, listeners, controller lease, and cleanup.
Embedded WebRTC with Realtime SFU
Device firmwareESP32-S3 + Rust / str0mOne peer and UDP socketOpus source + application channels
Opus audio
robot + spectrum
Commands
CloudflareRealtime SFUOne session per endpointAudio and DataChannels
Audio + data
robot replies
controller only
Browser listenersOne peer per tabPlay audio, show dataOne authorized controller
HTTPS signaling
offer, answer, IDs, liveness
SFU HTTPS API
sessions, tracks, channels
HTTPS signaling
join, SDP, control lease
Trusted application backend (this example)
WorkerDevice / viewer authenticationSFU credentials
RPC
RobotRoom Durable ObjectSession state + discoveryController lease + cleanup
Device firmwareOpus audio, robot + spectrum →Realtime SFU
Realtime SFUCommands →Device firmware
Realtime SFUAudio + data →Browser listeners
Browser listenersrobot replies, controller only →Realtime SFU
Device firmwareHTTPS signaling ↔Worker
Browser listenersHTTPS signaling ↔Worker
WorkerRPC →RobotRoom Durable Object
RobotRoom Durable ObjectSFU HTTPS API →Realtime SFU
The example uses these paths:
| Path | Purpose |
|---|---|
| Device audio track to browser listeners | Publish pre-encoded Opus audio for playback |
Reliable, ordered robot DataChannel |
Send telemetry, metadata, commands, and command acknowledgments |
Unordered spectrum DataChannel with zero retransmissions |
Send replaceable spectrum samples |
Controller replies on robot |
Return authorized browser commands to the device using canReply |
| Device and browser signaling to the backend | Authenticate, allocate SFU resources, exchange Session Description Protocol (SDP) offers and answers, and manage application state |
SFU App Secrets stay on the Worker. The firmware receives its device credential, Wi-Fi configuration, and signaling origin. This example uses a Worker as its backend. Other application servers can use the same SFU interfaces.
Follow the firmware integration
The firmware/SFU walkthrough ↗︎ explains the protocol sequence and identifies the code responsible for each step:
- Create and configure the firmware's WebRTC peer.
- Exchange a session description through the application backend.
- Publish the audio track and establish negotiated DataChannels.
- Connect a browser listener and acknowledge DataChannel readiness.
- Grant the controller reply access and handle device commands.
- Retain resource identifiers for teardown and retries.
Keep one owner for the WebRTC peer and its SDP state. Adapt hardware I/O and media timing for your device. You can replace HTTPS device-to-backend signaling with another application protocol, such as Constrained Application Protocol (CoAP). The backend must still call the SFU over HTTPS.
Run Pocket Radio
The example targets the ESP32-S3-DevKitC-1 N32R16V, with 32 MiB flash, 16 MiB PSRAM, and the documented LED pin. To build it, you need a Linux x86_64 toolchain, Node.js 24, Python, Rust, and ESP-IDF. The board requires outbound IPv4 UDP and HTTPS. Refer to the complete prerequisites ↗︎ for exact tool versions and hardware requirements.
- Prepare the checkout. Run:
sh
git clone https://github.com/cloudflare/realtime-examples.git
cd realtime-examples/esp32-radio
make setup
cp .credential.env.example .credential.env
- Configure and deploy the backend. Create an SFU app. Follow the example's setup procedure ↗︎ to provision credentials, configure your hostname and account, and deploy the Worker. Keep
.credential.envprivate. - Prepare the device. Follow Back up and flash ↗︎. Use audio you have permission to distribute. Back up the board before replacing its software. Flashing resets the device.
- Listen and control. Open the Worker URL, sign in with the configured viewer password, and select Start listening. Open another listener tab. Both tabs should receive audio, spectrum, and telemetry.
In one tab, select Take control, then change the LED or pause playback. The other tab keeps listening. Select Release control before transferring control to another listener.
Reconnect and clean up
Browsers rejoin manually after a board restart or terminal connection failure. When the board is online, select Start listening again. The example uses a shared viewer password for application authentication. For a deployment that serves distinct users, per-user identity and abuse controls remain part of the integration.
The example allows eight listeners per board and one controller. The application sets the eight-listener cap. It does not define the SFU subscriber limit.
To stop, release control and disconnect listeners, then power off the board. Keep the backend available for as long as required cleanup remains pending. The example's operations guide ↗︎ explains publisher replacement, state expiry, and resource cleanup before removing the Worker or SFU app.
Adapt to other devices
For a shared camera or microscope monitor, keep the observer and controller model, then add camera capture, supported video encoding, and browser video playback. Use hardware or a gateway capable of supplying that video.
For a sensor dashboard, replace spectrum samples with your measurements. Add sensor capture, units, timestamps, and behavior for stale readings.
Adapt the pattern to robotics
Apply the observer/operator model to a robot gateway using these substitutions:
| Radio component | Robotics adaptation |
|---|---|
| Playlist audio source | Camera, microphone, or other supported media source |
| Device telemetry and spectrum samples | Sensor state, diagnostics, and replaceable measurements |
| Playback and LED commands | Validated commands to a robot's local control interface |
| Listener membership and controller lease | Observer and operator roles for the selected robot |
| Firmware platform layer | The robot's hardware or Robot Operating System (ROS) integration |
Implement camera capture, ROS integration, and actuator behavior separately. Include command expiry, stale-command rejection, and local watchdog or stopping behavior in that integration. Emergency stopping and actuator limits belong on the device or its local controller.
For a related implementation with a native video publisher, the cloud-gaming example uses separate channels for reliable and transient input.
Inspect the implementation
Inspect the architecture guide ↗︎ for authentication, controller ownership, and reconnect behavior.
Try another example
For a smaller browser-only exercise, run the DataChannel example.
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/realtime/sfu/examples/embedded-devices/#page","headline":"Embedded devices and remote control","description":"Connect embedded firmware to Realtime SFU for audio, telemetry, and browser control using the ESP32 Pocket Radio example.","url":"https://developers.cloudflare.com/realtime/sfu/examples/embedded-devices/","inLanguage":"en","image":"https://developers.cloudflare.com/realtime/sfu/examples/embedded-devices/og.png?v=39e1a846a14cc8cf","dateModified":"2026-09-22","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: Build browser video rooms with Realtime SFU publication, track discovery, backend authorization, and explicit cleanup. title: Custom video room image: https://developers.cloudflare.com/realtime/sfu/examples/video-room/og.png?v=2db1e1fb23741ebc
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt
Use this file to discover all available pages before exploring further.
