PolterHID

A deliberately limited MicroPython awareness-training firmware for an ESP32 USB stick. On boot, it presents as a USB keyboard/mouse and, at independently random intervals, sends only:

  • one Return key press and release; and
  • a one-pixel relative mouse movement.

The authenticated web page also provides manual Volume up and Volume down controls for a clearly observable, non-destructive HID demonstration.

A password-protected local web page changes the enabled event types and their timing. The device connects to one preconfigured Wi-Fi network; it does not create an access point.

Hardware and firmware requirement

This needs an ESP32-S2 or ESP32-S3 board/stick whose USB connector is wired to the chip's native USB peripheral. A classic ESP32 with a CP210x/CH340 USB-to-serial adapter cannot act as a USB HID device.

Flash a current MicroPython build for that exact board which provides machine.USBDevice. Check at the REPL:

from machine import USBDevice

If that import fails, use an up-to-date native-USB MicroPython build for the board. This project uses the custom USB-device API and does not work with CircuitPython's usb_hid API.

Install

  1. Flash a current ESP32-S3 MicroPython build, then verify that from machine import USBDevice succeeds at the REPL. A separate mdns module is not required on the standard ESP32 port.
  2. Copy settings.example.json to the device filesystem as settings.json and replace all three credentials. Do this before first boot: the firmware cannot join Wi-Fi with the placeholder defaults.
  3. Copy boot.py, main.py, hid.py, settings.py, web.py, and display.py to the device root filesystem.
  4. Reset the board and plug its native USB connector into a dedicated test host. It should enumerate as a keyboard, mouse, and media-control HID device.
  5. Once it joins Wi-Fi, visit http://polterhid.local/. If the client/network does not support mDNS, find the device IP address in the training Wi-Fi DHCP leases and visit http://DEVICE_IP/ instead.
  6. Sign in with username admin and the web_password from settings.json.

boot.py configures the native USB interface before USB initialisation, while main.py runs the application. The device is intentionally HID-only: its built-in USB serial/CDC REPL is replaced by the HID device. Use the BOOT button to enter download mode for recovery, and verify the network configuration before deployment. On a LILYGO T-Dongle-S3, its built-in 160×80 ST7735 screen shows the configured Wi-Fi SSID and either connecting... or the DHCP-assigned IPv4 address. The firmware sets the ESP32 network hostname to polterhid before joining Wi-Fi; standard ESP32 MicroPython builds use their built-in mDNS responder to announce polterhid.local. Configuration saved in the web page is retained in settings.json and takes effect immediately.

Operational safeguards

  • Change the example credentials before deployment. HTTP Basic authentication and all form data, including the password, are unencrypted because this intentionally has no TLS. Use an isolated/trusted training Wi-Fi network.
  • The page provides an immediate Enable all injection switch. Turn it off before handing the device to anyone or ending a session.
  • This firmware intentionally has no shell commands, keystroke payloads, text injection, storage emulation, Wi-Fi scanning, AP mode, or remote firmware-update endpoint.
  • Test first on a dedicated demonstration machine. The configured defaults are intentionally infrequent but still cause a Return and minimal mouse movement.

USB implementation note

hid.py exposes one composite HID interface with keyboard report ID 1, mouse report ID 2, and Consumer Control/media report ID 3 (Volume up/down). boot.py selects USBDevice.BUILTIN_NONE, supplies explicit USB device/configuration descriptors, and activates the interface. It queues reports while the endpoint is busy and discards a transfer if the host disconnects, avoiding stale events being replayed after a reconnect.

S
Description
PolterHID is a harmless ESP32-S3 HID awareness trainer that demonstrates the risks of untrusted USB devices using randomized Return/mouse events, Wi‑Fi controls, mDNS, display animations, and RGB feedback.
Readme
66 KiB
Languages
Python 100%