A headless machine in a closet - a home server, a hypervisor, a NAS - is wonderful right up until it doesn't boot. No ping, no SSH, dead web UI. Is it stuck in the BIOS? Did the kernel panic? Is it politely waiting for someone to press F1 after a RAM change three weeks ago? You can't know without dragging a monitor and keyboard to it.
The grown-up answer is an IP-KVM: a box that captures the machine's HDMI output, pretends to be its USB keyboard and mouse, and puts both in a browser. You see exactly what a monitor would - all the way down to the BIOS and the boot menu - and you type and click as if you were there. The target needs no agent, no OS, not even a working boot. It has no idea its "monitor" and "keyboard" are a little board on the network.
Commercial IP-KVMs are expensive. PiKVM is great but rides the Raspberry Pi supply. Then Espressif shipped the ESP32-P4: a RISC-V chip with a MIPI-CSI camera interface, 32 MB of PSRAM, and USB 2.0 High-Speed. A camera input means you can hang an HDMI-to-CSI bridge off it and capture video - so a full IP-KVM becomes two dev boards and a ribbon cable, with no soldering. That project is ESP-KVM, and it's fully open source (Apache-2.0).
Credit where it's due
This builds directly on Jonathan Rowny's work - jrowny/p4kvm and his Hackaday project ESP32-P4 IP KVM. An IP-KVM on the ESP32-P4 is an obvious thing to want, but he's the one who cracked the genuinely hard part - bringing up the Toshiba TC358743 HDMI-to-CSI bridge and getting frames off the P4's CSI receiver - and put it out in the open. He also named the exact wall this project later climbed: he wanted H.264 but was stuck on MJPEG, because rev-1.3 silicon forces a colour-space detour that the newer revision removes. ESP-KVM is everything built on top of his driver (web console, USB HID, security, virtual media, networking, OTA) - and on rev-3.2 silicon that H.264 wall is gone. Same licence as his.
What you get (all working today)
- Video that follows the target's resolution - a 640x480 BIOS, the boot menu, a 1080p desktop, handled live. Sleep it or swap the HDMI and the stream recovers by itself.
- Two codecs - hardware MJPEG and hardware H.264 (a fraction of the bandwidth).
- Absolute mouse (clicks land where you aim) plus a full keyboard, media keys, one-button macros (Ctrl-Alt-Del, REISUB), and paste-a-string in the target's keyboard layout.
- Touch mode for phones/tablets - a real virtual trackpad and on-screen keyboard - as an installable PWA.
- Multiple viewers, one in control at a time, with takeover.
- Virtual media - boot the target from an ISO/IMG on a microSD card, or from a small rescue image in the device's own flash (no card needed).
- ATX power control (power/reset via optocouplers, power-LED sense) and Wake-on-LAN.
- Networking - Ethernet, or WiFi on boards with an onboard ESP32-C6: station or the device's own access point, with a rescue hotspot and a captive portal so a device whose network is down is still set up from a phone.
- Remote access - native Tailscale, or point it at your own self-hosted Headscale, to join a tailnet directly (no gateway, VPS or port-forward, cert valid over the tailnet); a built-in WireGuard client too.
- Home Assistant over MQTT (auto-discovered sensors plus power/reset/Wake-on-LAN buttons).
- An automation surface - plain REST to grab a JPEG frame and move/click/type, so you can script it or point an AI agent at a real machine it can see and drive.
- Security/ops - HTTPS with a certificate the device issues itself (plus a downloadable CA to trust it, or bring your own), a login, a physical password reset, OTA update with rollback, thermal protection, diagnostics.
The interesting hardware bits
- The MIPI link sets a pixel-clock ceiling. Capture is 2 CSI lanes at 972 Mbit/s = 1.944 Gbit/s, RGB888, so ~81 Mpixel/s - exactly 1080p30. Anything above (1080p60, 1280x1024) is refused in the EDID, because an over-advertised mode doesn't degrade, it just goes black. The device hands the source a hand-built EDID that advertises only modes under the ceiling.
- Silicon revision decides your H.264 frame rate. Below rev 3.0 the CSI receiver won't hand over YUV and the encoder won't take RGB, so every frame detours through the pixel accelerator to convert colour space - about 104 ms of a ~150 ms frame, which is why H.264 was ~7 fps at 1080p. On rev 3.x that detour is gone: capture feeds YUV422 straight into the encoder, and 1080p H.264 jumps to 22-24 fps (28 at 720p), as fast as MJPEG but a fraction of the bandwidth - and the chip runs cooler doing it (34 °C vs 46 °C).
- The P4 has no radio, so WiFi rides an onboard ESP32-C6 over SDIO (esp-hosted + esp_wifi_remote). Gotcha: the single SD host controller is shared between the C6's SDIO and the microSD slot, so they're mutually exclusive - Ethernet mode mounts the card, WiFi mode gives the radio the bus.
- The "monitor" identifies itself. The EDID's manufacturer and product name are overridden, so the target sees an "ESP-KVM" display instead of the capture chip's Toshiba identity.
Boards it runs on
It's built per board (one-file overlays), and a browser flasher installs the right image over USB:
- Waveshare ESP32-P4-ETH (the reference build) plus a Geekworm C790 TC358743 capture board.
- Espressif ESP32-P4 Function EV (rev 3.2 - the fast-H.264 board, with onboard C6 for WiFi).
- Waveshare ESP32-P4-NANO and Guition ESP32-P4-M3-Dev - configured from datasheets, community-testing.
Full build story, hardware gotchas and measured numbers are in the logs.
Dexif