Intervene in USB Controller I/O Streams with an MCU Interposer

Series: alt.ctrl activities; builds on Game controllers

Many modern MCUs have a USB device connector and can also support an additional USB host port, sometimes referred to as an OTG (on-the-go) port.

Materials

MCUs with USB host ports, and the cables sometimes required to plug common game controllers into them
ItemAmazonAdafruitSparkFunWaveshareMouserDigi-Key
The Adafruit Feather RP2040 with USB Type A Host board, a USB-A socket at one end.
Photo: Liz Clark, Adafruit (source), CC BY-SA 3.0
Adafruit Feather RP2040 with USB Type A Host
5723485-5723
ESP32-S3 boards with two USB-C ports (such as an N16R8: 16 MB flash, 8 MB PSRAM), in the style of Espressif's ESP32-S3-DevKitC-1: one port is the ESP32-S3's native USB OTG, the other a USB-to-UART bridgeB0GBT212KM
Waveshare RP2350A USB Mini (RP2350-USB-A): USB-C for programming, and a USB-A host port driven by the RP2350's PIOB0DW8Z2K7C30274
Teensy 4.1: a USB host port on a 5-pin headerDEV-167711568-16771-ND
USB Host Cable for Teensy 4.1: the 5-pin host header to a USB-A socketCAB-283891568-CAB-28389-ND
Teensy 4.0: USB host on two pads on the underside, data only; the board does not power the USB deviceDEV-15583474-DEV-15583
USB-A socket to micro-USB plug OTG adapters, 2 pack: for host ports with a micro-USB socket, such as the USB Host BFF'sB0BX9FSCFH2910
USB-A to Micro-B cable: from a USB-A host port to a controller with a micro-USB port, such as the DualShock 4 and the Xbox Wireless Controller before its USB-C revisions592
USB-A to USB-C cable: from a USB-A host port to a controller with a USB-C port, such as the DualSense, the Switch Pro Controller, and the Xbox Elite Series 2 and third-revision Xbox Wireless Controller4474
USB-C to Micro-B cable: from a USB-C host port, such as the SparkFun shield's, to a micro-USB controller3878
USB-C to USB-C cable: from a USB-C host port to a USB-C controller4199
USB-A socket to USB-C plug adapter: puts a USB-A socket on a USB-C host port, for the USB-A cables above5030
Affordable USB host adapters based on the Maxim MAX3421E
ItemAmazonAdafruitSparkFunM5StackMouser
The USB Host FeatherWing stacked with a Feather board and a small display reading "USB Found".
Photo: Liz Clark, Adafruit (source), CC BY-SA 3.0
Adafruit USB Host FeatherWing with MAX3421E
5858485-5858
The USB Host BFF on a small board, with an OTG cable and a display connected.
Photo: Liz Clark, Adafruit (source), a still from the original animation, CC BY-SA 3.0
Adafruit USB Host BFF for QT Py or Xiao with MAX3421E, with a micro-B OTG port
5956485-5956
M5Stack Module USB v1.2 (MAX3421E), for the Basic, Core2 and CoreS3 controllersM020-V12170-M020-V12
The red SparkFun USB-C Host Shield: an Arduino-shaped board with a USB-C socket at one corner, a square controller chip in the middle and a grid of prototyping holes.
Photo: SparkFun Electronics, CC BY-NC-SA 3.0 (SparkFun, 2011)
SparkFun USB-C Host Shield with MAX3421E, an Arduino shield: the MAX3421E on the hardware SPI pins (D10–D13), and a USB-C socket that supplies 5 V to the device
DEV-21247474-DEV-21247
Mini USB Host Shield 2.0, SPI, sized for the Arduino Pro Mini. Check that the board you receive carries the MAX3421E, runs at 3.3 V and has a USB-A socketB0DC9MFKJR

Tools

  • The Arduino IDE, with the board's core and the host library
  • A USB descriptor and report dump, from the host library's own examples: USB_desc in the USB Host Shield library, HIDDeviceInfo in USBHost_t36, or Adafruit TinyUSB's hid_device_report, which prints the vendor and product IDs and each report
  • The computer's game-controller settings, or a gamepad tester in a browser, to test the pass-through
  • A coding assistant, given the Coding Prompt Build Block below

Skills

  • Programming an MCU with a USB host library

Instructions

  1. With the controller attached, identify the closest existing device that the host library already supports.
  2. Test that, then expand it with the special features the particular device exposes.
  3. Identify a USB profile similar to the device's, and create a pass-through that provides everything the controller does.
  4. Once this is tested, you can write your own remappings.

Variations

  • Configure instead of code: flash the Feather RP2040 with USB Type A Host with HID Remapper and set its mappings in a Chrome-based browser. It can also map keyboard keys and mouse buttons to gamepad buttons; the Nintendo Switch is the only console it works with out of the box. (Adafruit's turbo-button guide in Related resources does its remapping in code instead, with Adafruit TinyUSB.) Compare Replace a Game Controller with Mouse and Keyboard.
  • Merge controllers: put a USB hub on the host port and combine several devices into one gamepad, as HID Remapper's manual describes; see also Make a Universal Platform for Frankensteining Controllers.
  • Add controls the controller lacks: wire buttons or sensors to the board's free pins and merge them into the reports it passes on, such as pressure-sensing buttons from Add Pressure Sensitivity to Switches (HID Remapper takes plain buttons on its pins).

Related resources

Coding Prompt Build Block

A prompt to give a coding assistant, to start the code for this activity. Copy the box, answer its questions about your board and pins, and test what comes back on the bench before you rely on it.

I am building "Intervene in USB Controller I/O Streams with an MCU Interposer", the activity at https://adrianfreed.com/usb-controller-mcu-interposer.html.

Write a program for a microcontroller with a USB host port that sits between a USB game controller and a computer. First, list the controller's descriptors and print its reports as they arrive, so that we can see what each control sends.

Then find the closest device that the host library already supports, and test that. Then make a pass-through: the microcontroller appears to the computer as a USB device of a similar kind, and passes on everything the controller does. Then add remappings, each in its own small function: swapping buttons, scaling an axis, adding a turbo button.

If the board cannot be a USB host and a USB device at once (on an ESP32-S3, the USB host takes the peripheral that the USB device stack would use), send the data to the computer another way, such as serial over the board's other port.

Libraries to explore:

Before writing anything, ask me what I am using: the board and its pins, or the software (such as Max, Pd or Python), and check its documentation for what this needs. Put the pin numbers, ranges and other settings in one block at the top, each with a comment. Say which of the libraries above you use, and why. Start with a test that shows the raw readings, so that I can check the wiring and the ranges before the rest.

This activity by Adrian Freed is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0): you may share and adapt it with attribution, for non-commercial purposes such as personal projects and teaching, and you must share adaptations under the same licence. Product names and links belong to their suppliers.

Design strategies: