A networked Stream Deck bridge on an ESP32-S3. Attach an Elgato Stream Deck directly to the network — no PC tether, no Elgato software anywhere. The ESP32 acts as a USB host to the deck and as a Bitfocus Companion Satellite client over WiFi (or Ethernet). Companion stays the brain (all 700+ device modules, page logic, button rendering); the ESP32 is a surface proxy.
Think of it as an open, cheaper equivalent of Elgato's Network Dock — with WiFi as an option, not just Ethernet.
The key idea: HammerDeck doesn't reimplement Companion — it emulates Companion Satellite, Companion's thin line-based TCP client. That's a few hundred lines of C, not a fork of a large TypeScript app. See DESIGN.md.
┌──────────────┐ USB HID ┌────────────────┐ TCP 16622 ┌──────────────┐
│ Stream Deck │◄────────────►│ ESP32-S3 │◄────────────►│ Companion │
│ (Mini) │ │ HammerDeck fw │ WiFi / Eth │ │
└──────────────┘ └────────────────┘ └──────────────┘
buttons ──────────────────────► KEY-PRESS ──────────────────►
◄──────────────── BMP/JPEG tiles ◄── KEY-STATE (base64 RGB) ──
Working end-to-end on real hardware. On a Stream Deck Mini over WiFi, the full path runs: the deck enumerates over USB, joins WiFi, connects to Companion as a Satellite surface, renders button images, and sends key presses back — with no PC and no Elgato software involved.
| Stream Deck Mini over WiFi | ✅ validated on hardware, end to end |
| USB HID host (enumerate, keys, tiles, brightness, hot-plug) | ✅ validated on the Mini |
| Companion Satellite client | ✅ validated against live Companion 5.0.2 (API 1.12) |
| Config portal, live dashboard, WiFi scan, static IP | ✅ validated on hardware |
| RGB status LED + on-deck boot info screen | ✅ validated on hardware |
| Ethernet (W5500) | ⚙️ implemented, not yet tested — planned for a future revision (no module on hand) |
| Other Stream Decks (MK.2 / XL / Neo / Plus) | ⚙️ model table + JPEG image path implemented, not yet tested (Mini is the only deck on hand) |
See Supporting the project if you'd like to help extend hardware coverage.
- USB host for the Stream Deck — enumerates the deck, polls keys, streams button images, sets brightness; survives unplug/replug.
- Companion Satellite client — connects over TCP, negotiates the API,
renders
KEY-STATEtiles, and reportsKEY-PRESS, with keepalive and automatic reconnect. - WiFi or Ethernet transport — WiFi is the primary path; an Ethernet (W5500) path and WiFi→Ethernet auto-failover are implemented for a future revision.
- First-boot provisioning portal — no app needed; a self-hosted SoftAP + web form to set WiFi and Companion details.
- Live web dashboard — connection state, deck model/serial, WiFi signal, uptime, and Companion status, with a WiFi network scanner, DHCP/static-IP configuration, a factory-reset button, and an opt-in live event console.
- On-deck boot info screen — shows the network details on the deck's own keys before Companion takes over (see below).
- RGB status LED — at-a-glance state on an onboard WS2812 (see below).
- mDNS discovery — Companion finds the device automatically.
- HTTP OTA — update firmware over the network after the first USB flash.
- Fast redraws — content-hashed tile cache and a pipelined USB image path.
When the device powers on, it spends about 10 seconds showing network details on the deck's own keys before connecting to Companion — so the unit is inspectable and recoverable with no PC:
- the WiFi network it joined, its IP address and gateway, and whether it's on DHCP or a static IP;
- an orange "WiFi SETUP" key — tap it to reboot into the WiFi provisioning portal (your current settings are kept and pre-filled for easy re-selection);
- a HammerDeck logo key — tap it to pause the countdown so you can read the details for as long as you like; tap again to resume.
After the hold, Companion takes over the display normally.
An onboard WS2812 RGB LED reflects the current state at a glance:
| LED | Meaning |
|---|---|
| Solid orange | First-boot setup portal (SoftAP) is up |
| Blinking red | Connecting to WiFi |
| Blinking yellow | WiFi up; connecting to / reconnecting to Companion |
| Solid green | Connected to Companion — fully operational |
| Brief blue flash | A key was pressed |
The built-in web server does double duty: a captive SoftAP setup page on first
boot, and a full dashboard on the LAN afterward (also reachable via mDNS at
<device-id>.local). From it you can watch live status, scan for and pick a WiFi
network (or type an SSID manually), switch between DHCP and static IP, stream a
live console of key events and Companion traffic, push an OTA update, or factory
-reset the device. Companion discovers the device over mDNS and can point it at
itself automatically.
Firmware layers live under components/; the USB host runs pinned to core 0,
networking and image transcode on core 1.
| Component | Role |
|---|---|
deck |
USB HID host: enumerate, poll keys, write tiles, brightness/reset. Per-model descriptor table. |
transcoder |
Companion's raw RGB → the deck's image format (BMP for gen1, JPEG for gen2). |
satellite |
Companion Satellite TCP client: handshake, KEY-STATE/KEY-PRESS, keepalive, reconnect. |
netmgr |
WiFi-primary, Ethernet-selectable transport (the Satellite client is transport-agnostic). |
webui |
Config portal, live dashboard/console, OTA, and the Companion discovery REST API. |
infoscreen |
Renders the on-deck boot info screen (text into RGB tiles). |
statusled |
Drives the WS2812 status LED. |
appcfg |
NVS-backed configuration. |
Deeper reference: DESIGN.md (architecture, BOM, protocol, rationale) and MINI.md (byte-exact Stream Deck Mini HID + Satellite facts).
Everything above marked ✅ was verified on this exact setup:
| Part | Used |
|---|---|
| MCU board | AITRIP ESP32-S3-DevKitC-1-N16R8 (16 MB flash, 8 MB octal PSRAM) |
| Deck | Stream Deck Mini (6 keys, 80×80 BMP tiles) |
| Transport | WiFi (2.4 GHz) |
| Status LED | onboard WS2812 (GPIO 48) |
Not yet exercised on hardware: the W5500 Ethernet module (the driver and auto-failover are written but untested) and any gen2 deck (MK.2 / XL / Neo / Plus — the model table and JPEG image path exist but are unverified). The Stream Deck Plus dials and the Pedal are not supported yet (non-grid input).
HammerDeck was built and tested on a single Stream Deck Mini over WiFi. Extending support means validating on hardware I don't currently own — the gen2 decks (MK.2, XL, Neo, Plus) and a W5500 Ethernet module. If you'd like to help, a contribution toward test hardware goes directly to validating and supporting more devices:
- GitHub Sponsors — see the Sponsor button at the top of the repo.
- Or get in touch: dave@hammer360.net.
You can also help without spending anything: run HammerDeck on a deck you own and report what works (or doesn't) — see CONTRIBUTING.md.
Requires ESP-IDF v6.0 (also builds on recent v5.x). From the repo root, with IDF exported:
idf.py set-target esp32s3
idf.py build
idf.py -p <PORT> flash monitorOn the DevKitC-1, flash and log over the UART (CP2102) port so the native USB
port stays free to host the deck. Managed components (espressif/usb,
espressif/w5500, …) are fetched automatically on the first build. Full details
in BUILD.md.
First boot with no stored WiFi comes up as an open SoftAP HammerDeck-setup —
join it and open http://192.168.4.1 to configure. After that, everything is
reachable on the LAN and configurable from the dashboard.
The image transcoder is pure C and can be validated without hardware:
make -C test # host build of the encoder vs golden vectors
idf.py -C test/qemu set-target esp32s3 && idf.py -C test/qemu build
idf.py -C test/qemu qemu monitor # runs the encoder on emulated esp32s3 -> SELFTEST: PASSValidate the Satellite protocol against a running Companion (no ESP32):
python test/companion_probe.py # 127.0.0.1:16622 by defaultIt performs the same handshake the firmware does and prints what Companion sends back (registering a transient surface that disconnects on exit). See test/qemu/README.md.
- Ethernet bring-up — validate the W5500 path on hardware (future revision).
- gen2 decks — test the JPEG image path on an MK.2 / XL / Neo.
- Stream Deck Plus — dial/encoder support (
LAYOUT_MANIFEST). - Portal hardening — authentication + TLS for the web UI.
- Custom PCB / power — integrated board, PoE.
- DESIGN.md — architecture, BOM, protocol reference, rationale
- MINI.md — verified Stream Deck Mini HID + Satellite protocol reference
- BUILD.md — build/flash instructions and per-component status
python-elgato-streamdeck (protocol reference), node-elgato-stream-deck, and
bitfocus/companion-satellite (the reference Satellite client). No published
ESP32 Satellite implementation was found when this project started.
HammerDeck is free software licensed under the GNU General Public License
v3.0 or later (GPL-3.0-or-later) — see LICENSE. You may use,
study, share, and modify it; if you distribute it (including inside a hardware
product), you must pass on the same freedoms and make your source available
under the GPL.
Copyright © 2026 David Hammer. GPL-compatible with the Apache-2.0 ESP-IDF and managed components it builds on.
The GPL requires derivative works to be open-sourced under the GPL. To build a closed-source or proprietary product on HammerDeck, a separate commercial license is available from the copyright holder — contact dave@hammer360.net.
Contributions are welcome under the exclusive-license CLA described in CONTRIBUTING.md and CLA.md: you keep the copyright to your work but grant an exclusive, sublicensable license to it, with a standing commitment that your contribution remains available under the GPL in published open-source releases. This keeps both the open project and the commercial option viable.