An IP-KVM built from an ESP32-P4 and a TC358743 HDMI bridge. The target machine’s screen, and a USB keyboard and mouse it cannot tell from real ones, over HTTPS — even when it has no operating system at all.
The point is to reach a machine that has no working operating system — a BIOS screen, a boot menu, a kernel that will not come up — from a device that costs a fraction of a commercial KVM-over-IP. It captures HDMI, presents itself over USB as a keyboard and mouse, hands the target a disk image to boot from, and serves the whole thing over HTTPS from a megabyte of firmware — the console inside it is 44 KB.
What the firmware ships with. Anything the hardware cannot support, the device shows disabled with the reason — it does not pretend.
Laptop, phone or tablet. Nothing to install on either end, and touch gets a trackpad and a keyboard of its own.
800×600 firmware screen to 1080p desktop, followed without being told.
An absolute pointer: the target’s mouse acceleration cannot drift the cursor off the button you meant.
A BIOS, a boot loader, a console: select it with the mouse like a page, or copy the whole screen. Not OCR — the characters are read from the grid the firmware drew them on, so a serial number comes back exactly or not at all.
Both encoded by hardware. H.264 costs a fraction of the bandwidth on a screen that barely changes.
A rescue or live image, handed to the target as a USB drive it can boot from — from a microSD card, or a small one kept in the device’s own flash with no card at all.
Two app slots and automatic rollback: an image that fails to start returns the device to the one that worked. The console shows the version on each slot, and can boot the other one on demand.
A certificate the device issues itself on first boot, and a password you can clear with the board button if you forget it.
Turn on MQTT and it arrives as one auto-discovered device, sensors and buttons included.
Reach it across the internet without exposing it. WireGuard for a classic hub, or Tailscale to join a tailnet with no gateway, VPS or port-forward. Off by default.
Join a network or be the access point, on boards with an ESP32-C6. Out of range, a rescue hotspot and a captive portal put the console on your phone.
A cheap OLED or a round colour LCD shows the IP, link, capture and health with no browser open. Pins are picked in the console.
The round one also fills with a code your phone scans to join the rescue hotspot, and draws the recovery button press as a ring.
A key press wakes it, the way a real keyboard would. Failing that, Wake-on-LAN or the power button.
Two optocouplers on the front-panel header put the machine’s own buttons in the browser. It reads the power LED back, so the state shown is the machine’s.
Give it phrases — no boot device, kernel
panic — and it alerts with a still attached. A screen
gone one flat colour counts too.
One device, discovered automatically over MQTT. Off by default, TLS optional.
Virtual media, honestly. The microSD is served read-only — this board cannot write it reliably — so images go on the card in a reader, formatted FAT32, up to 4 GB each. Reads run around 1.5 MB/s: quick for a rescue or minimal image, several minutes for a heavy graphical one. It is a known ESP32-P4 SD limitation, and the console tells you so. For the rescue case there is another option that sidesteps the card entirely: a small image (iPXE, memtest, a DOS floppy) kept in a 4 MB flash partition, served fast from memory and — because flash writes are reliable here — writable from the browser.
IPMI, the BMC chip built into server motherboards, manages a machine from the inside: real power control, internal sensors, POST codes, over a standard protocol. But it lives only on server-class hardware, and the full remote console is often behind a license.
ESP-KVM is external. It clips onto any machine with an HDMI output and a USB port, captures the physical video, and acts as its keyboard and mouse — bringing the remote console, virtual media and wake-on-LAN part of what a BMC does to hardware that has no BMC and never will: a desktop, a laptop, a mini-PC, an SBC, for about $40. It can press the machine’s power and reset buttons and read its power LED through optocouplers (ATX power control), though it does not read the host’s internal sensors the way a BMC does. Think of it as closer to PiKVM than to IPMI: not a replacement for a BMC on a server, but the useful slice of one for everything that never had a BMC.
ESP-KVM presents the keyboard and mouse to the target over the ESP32-P4’s high-speed USB OTG (OTG_HS). It has to be a proper USB data cable, not a charge-only one — the data lines are what carry the keyboard and mouse.
On the Waveshare ESP32-P4-ETH specifically, OTG_HS is not on a USB port at all — it is brought out on a small MX1.25 header (the board’s single USB-C is the CH343 flashing/console port). So you need an MX1.25-to-USB cable ending in a male Type-A plug — ESP-KVM is the USB device, so it plugs into the target’s USB port. A link to one that fits is in this discussion.
On other ESP32-P4 boards the OTG_HS may be routed to a USB-C port instead — check your board’s pinout.
Almost always because the target has more than one display. ESP-KVM presents itself as an absolute pointing device by default, the way a graphics tablet does: it sends “the pointer is at this position” rather than “move this far”. That is what lets the cursor land exactly where you aim it, with nothing to capture and nothing to run away.
The catch is that an absolute device addresses the target’s whole desktop, not the one output you are watching. With a second monitor attached, that desktop is wider than the screen ESP-KVM captures, so your side of the picture is stretched across both. With two equally wide screens the pointer travels twice as far as the mouse, and half of the picture aims at the display you cannot see — move into it and the cursor simply disappears onto the other monitor.
Two ways out. Unplug or disable the target’s second display while you are working through ESP-KVM — then the desktop and the captured screen are the same thing again. Or switch Settings → Input → mouse mode to relative, which sends movement instead of position and so does not depend on the desktop’s size or layout; click the picture to take control, and use Relative sensitivity if the speed does not suit you.
A pointer that drifts only near the edges while it is accurate in the middle is a different thing entirely, and one that affected macOS targets — that one is fixed.
No — this design needs two things that, across Espressif’s line-up, only the ESP32-P4 has together: a MIPI-CSI-2 receiver and a hardware H.264 encoder.
The capture board (a Toshiba TC358743) outputs MIPI-CSI-2, so the host chip needs a CSI-2 receiver to read it. The other ESP32 parts — including the newer ESP32-S31 — have a DVP (parallel) camera interface, not CSI-2, so they can’t take the bridge at all. And moving 1080p leans on the P4’s hardware H.264 encoder; the S-series has a JPEG codec but no H.264, so software encoding on the cores wouldn’t keep up.
The S31 does have things the P4 lacks (WiFi 6 on-die, no co-processor), so if Espressif ever ships a P-series successor with the radio built in, that would be the dream chip for this — but for now the P4 is the one with the CSI-2 + H.264 combination it needs.
Every number here came off the hardware, at 1080p. The ones that contradicted the documentation are written down in the hardware notes.
| MJPEG | H.264 | |
|---|---|---|
| Frame rate — Waveshare ESP32-P4-ETH (rev v1.3) | 20 fps | ~7 fps |
| Frame rate — Function EV (rev v3.2) | 23 fps | 22–24 fps |
| Idle screen | 0 kbit/s | 170 kbit/s |
| Screen in motion | 8.5 Mbit/s | ~500 kbit/s |
| Chip temperature, full load — rev v1.3 | 46 °C in open air | |
| Chip temperature, full load — rev v3.2 | 34 °C in open air | |
H.264 costs a fraction of MJPEG’s bandwidth either way — an idle screen is 170 kbit/s against MJPEG’s 8.5 Mbit/s in motion — and browsers decode it through WebCodecs, which they only offer on secure pages (one reason the device serves HTTPS). What differs between the two boards is the frame rate.
The Waveshare figures come from an ESP32-P4 revision v1.3, and the biggest cost in its H.264 path is something that revision forces on us. Below silicon revision 3.0 the CSI receiver cannot hand over YUV420 and the encoder will not accept RGB, so every frame takes a detour through the pixel accelerator to change colour space — measured at ~104 ms of the ~150 ms a 1080p frame costs.
On the Espressif Function EV board (revision v3.2) that detour is gone: the capture feeds YUV422 straight into the encoder, no conversion pass. H.264 goes from ~7 fps to 22–24 fps at 1080p (and 28 fps at 720p) — as fast as MJPEG (23 fps) but at a fraction of the bandwidth — and the chip runs cooler doing it, 34 °C against 46 °C. The revision test was in Espressif’s own driver, quoted in the hardware notes; the prediction held.
An honest status board, the same one the device reports about itself.
| Video capture, resolution changes, MJPEG, H.264 | works |
| Keyboard, absolute and relative pointer, media keys, paste | works |
| Touch mode for phones and tablets (trackpad, on-screen keyboard) | works |
| Multiple viewers, one in control at a time with takeover | works |
| HTTPS, login, physical password reset | works |
| Bring your own TLS certificate (or the self-signed default) | works |
| Firmware updates over the network, with rollback | works |
| Thermal protection | works |
| Virtual media — boot the target from a disk image | works (card or on-flash) |
| Guessing the target’s OS from how it enumerates USB | works |
| User macros — replayable key sequences | works |
| Wake-on-LAN (no ATX wiring) | works |
| Static IP addressing, or DHCP | works |
| Status screen on the device (OLED or round LCD), with a scannable hotspot code | works |
| IPv6 alongside IPv4, autoconfigured | works |
| WiFi — station or access point, with a rescue hotspot and captive portal (boards with an ESP32-C6) | works |
| ATX power control (power/reset buttons, power-LED sensing) | works |
| Home Assistant over MQTT (auto-discovered sensors and buttons) | works |
| VPN — WireGuard or native Tailscale (pick one; Tailscale needs no gateway or port-forward) | works |
| HDMI audio | not yet |
Not for the public internet. There is a login and there is TLS, but nothing here has been through a security review, and a device holding a keyboard on someone else’s machine is worth more to an attacker than most things on a network. Keep it on a network you trust, or reach it over the built-in WireGuard client or native Tailscale.
Two boards and three cables, plus optional modules — power control and a little status display. Everything is off-the-shelf; any equivalent works, these are the ones this project is built and tested on.
ESP32-P4 with 100M Ethernet, a Raspberry-Pi-compatible CSI connector, USB-C OTG and a microSD slot. Another ESP32-P4 board with Ethernet and the same CSI connector can run it too — the pins are set in menuconfig, not the code.
Waveshare product page →
Espressif’s own ESP32-P4 board, with a rev 3.2 chip. It has its own build target and the browser flasher offers it directly. The newer silicon captures native YUV422 straight into the video encoders, which frees the colour-convert engine and lifts 1080p to a little over 20 fps.
Espressif user guide →
Same IP101 Ethernet and onboard ESP32-C6 as the boards above, with 32 MB PSRAM and 16 MB flash. A contributor confirmed capture, USB and Ethernet; the units tested were pre-3.0 silicon, so it runs the slower H.264 path (a rev-3.x unit can be built for the fast one).
Waveshare product page →
A display board (4.3″ MIPI-DSI touch, unused by the KVM) that also carries Ethernet and an ESP32-C6; 32 MB PSRAM, 16 MB flash. Two USB-C ports — the target goes on the OTG-HS one. A contributor confirmed capture, USB and Ethernet (on pre-3.0 silicon).
The first supported board that takes PoE: one cable to a KVM in a rack instead of two. Same Ethernet, microSD and ESP32-C6 wiring as the boards above, on the same pins; 32 MB PSRAM, 32 MB flash, and a full-size USB-A port for the target. Built and published from its schematic — nobody has run it on one yet, so tell us how it goes.
Ethernet and WiFi 6 on one board: a 100M magjack that also takes PoE, plus the ESP32-C6. Same pins as the boards above, so the build is almost the stock one; 32 MB PSRAM, 32 MB flash. Its USB OTG port is switched between host and device by a jumper — the KVM needs device. Built from the schematic, not yet run on one.
Waveshare product page →
The PoE board without the wired port, contributed by someone who has one: capture and the USB keyboard and mouse are confirmed on hardware. 32 MB PSRAM, 32 MB flash. Its WiFi link used to stall; since 0.41.1 the chip's own pull-ups are switched on for this board and it holds. That is one board and one tester, and WiFi is the only way in here, so tell us if yours behaves differently.
Waveshare product page →
A TC358743 HDMI → MIPI CSI-2 bridge that turns the target’s HDMI into a camera stream the ESP32-P4 can read. Any other TC358743 capture board should do just as well — the firmware talks to that chip, not to the board around it. The C792 is the same bridge with a splitter in front and should work too, though nobody here has run one; use its 15-pin connector, and see the README on what its EDID may do to a 1080p60 source.
Geekworm wiki page →
A pair of these cheap two-channel boards lets ESP-KVM press the target’s power and reset buttons and read its power LED, electrically isolated from the machine. A relay board does the two buttons just as well, but cannot sense the LED. Skip it all and everything else still works. Wiring is in docs/wiring.md.
A mono OLED on four wires, in any of its usual sizes from 0.96″ 128×64 down to 0.49″ 64×32. It shares the capture chip’s I2C bus, so it needs no pins of its own — the device draws its IP, link, capture status and health across a few pages, fewer of them on shorter glass.
A 1.28″ 240×240 round colour SPI LCD, e.g. the Waveshare module. Wire its SPI pins to any free GPIOs and choose them in the console; the same status pages, in colour — plus the hotspot join code and the recovery-button ring, which need the room.
A printed box for the ESP32-P4-ETH and the C790 together, published by Colin Hickey. It keeps the two boards and the OTG cable to the target tidy; HDMI, Ethernet and that cable all come out of one side. The P4 board is held tightly, so take care when you take it out again. Photo by the author, CC BY-NC.
Model on Printables →
A second box for the same pair of boards, adapted for this project by Fabrion365 after a user asked. Vented along one side, with the ports coming out of the ends. Photo by the author.
Model on MakerWorld →Cables: a CSI ribbon between the two boards, HDMI from the target, and USB-C from the board to the target. A microSD card if you want boot-from-image.
The easiest way is flashing straight from the browser — Chrome and Edge can talk to the board over USB, and nothing needs installing. If you would rather use the command line, download the merged firmware image for your board from the releases page and write it at offset 0 with esptool — one file, no unpacking:
esptool --chip esp32p4 -b 921600 write-flash 0x0 espkvm-<version>-<board>-merged.bin
Installing esptool, finding the serial port and the driver notes for Linux, macOS and Windows are all in the flashing guide.
Then plug in Ethernet, HDMI from the target and USB to the target,
and open https://espkvm.local/. The browser will warn
about the certificate once — the device signed it itself, and
nothing else vouches for it. Sign in as admin / admin;
the console will not go any further until that password is changed.
Accepting the warning is enough to watch the screen. For H.264 and for the keyboard and mouse the browser needs a trusted connection, so download the device’s CA (Settings → Security) and add it to your trusted authorities once — the README has the steps.
This project exists because of Jonathan Rowny and
his p4kvm proof of
concept. He was the one who got an ESP32-P4 to pull frames off a
TC358743 at all, and put it in the open with a working
demonstration for anyone
to build on — this is that thing built on. Two pieces of his
work are reverse engineering no datasheet would have handed us: the
TC358743 bring-up sequence, and the direct programming of the
ESP32-P4’s MIPI_CSI_BRIDGE registers, which the
camera API does not expose. Both still carry this firmware; the
layers above them were rewritten. Thank you, Jonathan, for
publishing it.
Independent coverage of ESP-KVM:
Also picked up and translated internationally — French, Greek, Spanish, Russian, Chinese, Japanese, Thai and German.
It has started turning up as a reference point when other devices in the category are written about, rather than only as a story of its own — PC de Mano names it alongside the Sipeed NanoKVM in a review of the USBridge-KVM 2.0 (in Spanish).
Waveshare, the maker of the capture adapter, links ESP-KVM from its HDMI to CSI adapter wiki.
ESP-KVM is free and open source. If it saved you a trip to a dead machine you can buy me a coffee — entirely optional. And if you would rather help build it, issues, ideas and pull requests are just as welcome.
Release notes, work in progress and the occasional picture of a board go out on Telegram and X:
Ask a question Report a bug Contribute on GitHub
Built with the help of these contributors: