Skip to content

Latest commit

 

History

48 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

USB HID to PS/2 adapter

This is an adapter to use USB Mouse and Keyboard on old computers with PS/2 connectors, based on an ESP32-S3.

This is currently very much WIP and a lot of things don't work as expected.

Libraries

This project makes ue of the following libs:

Development

I was using PlatformIO initially but since i need more recent USB features of esp-idf, i switched to pioarduino: https://github.com/pioarduino

Hardware

I am using a ESP32-S3-WROOM-1.

PS/2 cables

The PS/2 cables are wired like this:

PS/2 ESP32-S3 PIN
Mouse CLK 17
Mouse DATA 16
Mouse GND GND
Mouse +5v 5v
Keyboard CLK 5
Keyboard DATA 4
Keyboard GND GND
Keyboard +5v VBUS of USB socket

USB Socket

There is a USB Type-A socket connected like this:

USB ESP32-S3 PIN
D+ 21
D- 20
GND GND
VBus PS/2 Keyboard 5v

I have routed the +5v lines of the PS/2 cables through a dip switch block so i can decide if the ESP or the USB device will be powered by the PS/2 cables or not.

Configuration

I've also attached 4 Pins of the ESP to a DIP Switch block in order to have config options:

ESP32 PIN Option
38 Puts the device in OTA only mode
39 Swaps the PS/2 cables pins
40 Deactivates wifi, webserver, ota, webserial
41 currently unused

The Pins are set to HIGH with internal pullups and the dip switch connects them to GND when set to ON

Mouse detection

When a USB mouse connects, the adapter reads its HID report descriptor — the machine-readable table the device provides that describes the exact binary layout of its input reports. The descriptor is parsed once at connect time to extract:

  • Buttons — bit offset and count of button fields (usage page 0x09)
  • X / Y axes — bit offset, bit width, and logical min/max (Generic Desktop usages 0x30 / 0x31)
  • Scroll wheel — bit offset and bit width (usage 0x38), if present

This means the adapter works correctly with any mouse regardless of whether it uses 8-bit, 12-bit, 16-bit, or other coordinate widths, and regardless of how many padding or extra fields the report contains. The parsed layout is logged at connect time, for example:

[USB] Mouse layout: reportId=2 buttons=8@bit0 X=12bit@16 Y=12bit@28 Wheel=8bit@52

Each incoming report is then decoded by extracting signed integers at the exact bit positions the descriptor specified, without any assumptions about byte boundaries or field order.

If the descriptor cannot be parsed (e.g. the device does not expose one, or it contains no recognisable X/Y axes), the adapter falls back to the values the underlying EspUsbHost library provides, which handles standard boot-protocol mice correctly.

Coordinates are clamped to ±127 before being forwarded over PS/2. The PS/2 protocol carries overflow flags for larger movements, but many retro host drivers react to those flags with wild cursor jumps or a mouse reset; clamping avoids that while staying well below the 255 overflow threshold even when the host applies its optional 2:1 scale mode.

Problems

USB enumeration diagnostic

Two standalone environments isolate the ESP-IDF USB host driver from PS/2, WiFi and EspUsbHost. Upload through the board's UART/programming connection; these test firmwares do not provide OTA or the adapter functions.

pio run -e usb-probe -t upload
pio device monitor -b 115200

Reset the board after opening the monitor. Connect one mouse or keyboard directly, without a hub, and preserve the device's normal 5 V supply. [PROBE] ENUM shows that the device descriptor reached the enumeration callback; [PROBE] NEW_DEV shows that enumeration completed. The periodic alive line reports the number of enumerated devices, not electrical attachment detection.

For comparison, upload usb-probe-no-filter using the same procedure. It omits the enumeration callback to test the reported Arduino-ESP32 3.3.11 regression: espressif/arduino-esp32#12778 If only usb-probe detects the device, that reproduces the callback-dependent failure. It does not by itself explain an adapter failure, since the currently installed EspUsbHost 2.8.0 already registers an accepting callback.

Restore the adapter afterwards with pio run -e esp32s3 -t upload via UART.

If the callback-enabled test also finds no device, compare with pio run -e usb-probe-3310 -t upload. This uses the same diagnostic source, Arduino 3.3.10 and its matching ESP-IDF 5.5.4 libraries. Check the startup version line and look for NEW_DEV while keeping the device, wiring and power unchanged. enumeration callback OFF is expected with the 3.3.10 prebuilt configuration; ENUM is therefore absent even when detection works. Only the comparison environment overrides the framework packages; the adapter configuration remains on its existing version selection.

To compare the full adapter against a working usb-probe-3310, upload esp32s3-3310. This uses the same Arduino/IDF packages and pins the two direct Git dependencies to the revisions installed when the comparison was prepared. Keep wiring and USB power unchanged, and record the USB startup messages.

Currently my main problem is the USB Host implementation on the ESP32:

  • It seems to have problems with USB hubs

Mouse response curve

The Mouse Settings web panel includes a five-point graph. Drag points vertically or edit the numeric gains (0.25–3.0). The speed knots are 0, 100, 300, 1000 and 3000 USB counts/second, shown with compressed spacing. Gain is linearly interpolated between knots and multiplied by the existing Speed % setting. The last gain applies above 3000 counts/second.

The curve is disabled by default, including when loading older settings files. Linear sets all gains to 1. Fine control boosts slow movement and reduces fast movement. Test applies the editor in RAM without flash writes. Save activates and persists it with the mouse settings. Reset editor restores the page-load values; press Test to apply those values again. Saving the general mouse calibration also persists the currently active curve.

Velocity uses the combined X/Y magnitude and elapsed time between movement reports, smoothed with a 20 ms time constant. After 100 ms without movement the estimator restarts; its first report uses a 10 ms interval. Both axes receive the same gain, fractional counts are retained, and large vectors are limited proportionally. Wheel movement does not enter the curve. Counts/second depend on the physical mouse DPI, so tune for the connected mouse. This cannot remove acceleration in the downstream RISC OS driver or transport stalls.

Compile-time curve interpolation checks: xtensa-esp32s3-elf-g++ -std=c++17 -fsyntax-only test/mouse_curve.cpp

About

A USB HID to PS/2 computer adapter

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages