Zum Inhalt

USB4VC

📅 Erstellt: 02.09.2026 🔄 Aktualisiert: 04.09.2026

USB4VC = USB for Vintage Computers

Jo, hab mir so'n Ding mal bestellt und ist die Tage angekommen. Mich reizen vor allem die Maus-Emulation und Konnektivität mit Bluetooth und modernen Gamepads, sowie die Konfigurierbarkeit zum Beispiel die analogen Sticks als Maus zu mappen.
Und natürlich desweiteren auch Flug-/Renn-/Simulator-Spiele mit analogen Triggern und analoger Lenkung/Steuerung auszuprobieren, falls es geht.
Mal sehen, ob meine Erwartungen erfüllt werden, was ich so in Streams und auf YouTube gesehen habe stimmt mich aber zuversichtlich.
Tja, mal schauen wo mein RPi 3B+ rumfliegt, ggf. das Netzteil dazu auch noch finden und dann kann der Versuchsaufbau starten.

Anmerkung: Es wird ein USB-C Netzteil benötigt, das RPi 3B+ Netzteil ist für Micro-USB.

Was habe ich bekommen?

Das Set habe ich bestellt bei Tindie.

Lieferumfang (Preis: ≈ 130 USD ≈ 110 EUR):

  • Baseboard + Accessories
  • IBM PC Protocol Card
  • Serial Mouse Cable
  • PS/2 Cable 2x
  • Gameport Cable

Packungsinhalt (mitgelieferte Kabel sind nicht auf dem Bild):
01ca6ad05c364a97efd6700f70127c66.png

Baseboard:
8fe90707525988bf6752cfbc2d78e86e.png

IBM PC Compatible Protocol Card:
IMG_20260902_160910985.jpg

Features

Technische Informationen und Software auf Github

Die IBM PC Compatible Protocol Card Features:

  • PS/2 Keyboard, AT Keyboard, XT Keyboard
  • PS/2 Mouse, Microsoft Serial Mouse, Mouse Systems Mouse
  • 15-pin Gameport Gamepad
  • Mapping USB Gamepad to PC Mouse/Keyboard

Das ganze für moderne USB Keyboards, Mäuse, Gamepads.
Das Projekt ist vollständig Open-Source.

Aufbau

Ein "Getting Started" gibt es unter: usb4vc.com

Ich brauche jetzt:

  • Raspberry Pi 1/2/3/4/5 mit 40-pin header, Model B/B+ bevorzugt (3B+ hab ich rumliegen)
  • 4 GB+ mSD-Karte (hab ich auch irgendwo™)
  • Latest boot image (0.3.3 von 2023) - Github
  • WiFi Passwort (hab ich auch 😺)
  • Gutes Netzteil mit 5V, 2A+ (nehme ein originales RPi-Netzteil 5V, 3A)

Man braucht keinen neuen RPi, der kleinste B+ soll schnell genug sein.
Zitat: "No need to get the latest model, even the earliest RPi B+ is plenty fast enough." - Quelle

SD-Karte vorbereiten

Habe eine 8 GB MicroSD-Karte genommen.
In Windows bringt man das Image mit Rufus oder Etcher auf die mSD-Karte.
In Linux mit DD:
dd if=usb4vc_rpi_img_0.3.3.img of=/dev/sdf bs=4M status=progress conv=fsync

WiFi einstellen auf der mSD-Karte unter boot in der wpa_supplicant.conf:

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
country=DE
update_config=1

network={
    ssid="Yps-Heft"
    #psk="GarfieldIstNichtDick"
    psk=f540bba776a2e46ebecbb6640200d14968980f79486b7a0cde8d2da1186abaef
}

Bei Sonderzeichen, zum Beispiel " im WLAN-Passwort -> psk als 64-stelligen Hex-Schlüssel eintragen.
Umwandlung über:
wpa_passphrase "Yps-Heft" 'GarfieldIstNichtDick'

Ausgabe:

network={
        ssid="Yps-Heft"
        #psk="GarfieldIstNichtDick"
        psk=f540bba776a2e46ebecbb6640200d14968980f79486b7a0cde8d2da1186abaef
}

Wie immer bei externen Laufwerken: Die Laufwerke SAUBER ABMELDEN bevor sie entfernt werden!

Den Raspberry "mounten"

Die mSD-Karte wird in den RPi 3B+ gesteckt.

Dann wird der Kram zusammengebaut. Eine bebilderte Bauanleitung gibt es hier: Kit Assembly

Soll:
c615c098e95b6496b942d191007a7c10.png
Ist:
fe839b664ffc99ddaa84755c0547c544.png

Da ich die Kühler des RPi 3B+ nicht runterfriemeln wollte, hab ich eine Werbekarte provisorisch zugeschnitten, um die mitgelieferte Kunststoffabdeckung zu ersetzen. Die auf der Hauptseite des RPi montierten Kühler sind übrigens kein Problem, da passt alles.

Anschluss an den Retro-PC

Achtung

Alle Kabel müssen verbunden sein bevor irgendwas unter Strom gestellt oder eingeschaltet wird!

Die modernen Eingabegeräte werden mit dem Raspberry Pi verbunden. Auch Bluetooth soll funktionieren.
Die Schnittstellen der IBM PC Compatible Protocol Card werden mit dem Retro-PC verbunden:
960d09a201a4833869931abf3d512f78.png
Bild vom Entwickler

Das Netzteil kann an eine der drei USB-C Buchsen verbunden werden:
24ca3a378fce314a65d007587eb11e5d.png
Bild vom Entwickler

Hinweis

USB4VC immer zuerst einschalten bevor man den Retro-PC einschaltet!

Das USB4VC in Aktion

Beim ersten Start hatte ich erstmal einen automatischen Neubau des Bluetooth-Treibers:
884c3ef20967a526534e9faaecfd2353.png

Wenn dann alles richtig läuft, dann meldet sich der USB4VC mit dem home screen:
6d5c299a13edbd58f99ad04b2eaa865a.png

Die FritzBox vermeldet auch den raspberrypi als Netzwerkgerät.
Login-Daten: pi@192.168.XXX.XXX, Passwort: usb4vc

Testkonfiguration

Der erste Test wird am Retro-PC Highscreen KOMPAKT Serie III 386-SX durchgeführt.

Hardware Protokoll Retro-PC
IBM Model M2 Tastatur über PS/2-USB-Adapter AT/PS2 PS/2 -> DIN Adapter
Cherry USB-Maus Microsoft Serial COM-Port 2
Bluetooth Dongle + PS5 Controller Generic 15-Pin Sound Blaster Gameport

Bluetooth-Pairing

Ablauf:

  1. RPi: Bluetooth-Dongle stecken
  2. USB4VC: Pair Bluetooth Dialog auswählen und starten
    8d613cd162cfc82763563b56e38f2380.png
    Bild vom Entwickler
  3. BT-Gerät: In Pairing-Modus versetzen
  4. USB4VC: Gerät auswählen über +/- und mit ✓ bestätigen
    e9846bff52018eb091926f678c81c6c4.png
    Bild vom Entwickler

Joystick/Gamepad Setup

Da Maus und Tastatur funktionieren, kurz im DOS-Programm EDIT und MSD getestet, Maustreiber im übrigen CTMOUSE ohne Parameter, werde ich mit dem PS5 Controller fortfahren.

Mit dem DOS-Programm "Joystick Check" wird getestet. Download: USB4VC Github - joytest.zip

Tipp

Kalibriert wird übrigens in dem man den linken Analogstick nach links oben bewegt und die Taste 1 drückt, dann nach rechts unten bewegen und die Taste 2 drückt.
Für das DPad entsprechend Anzeige: A, B, C und D - links, oben, rechts, unten.

7729f90ccea528ea796463ec5830895a.png

- Hier ggf. Video vom Test/Kalibrierung -

Konfiguration:

  • Joystick Type "Joystick #1 + Joystick #2" (Standard)
  • PS5-Controller mit Bluetooth Dongle

Folgende Signale wurden ermittelt:

Controller Taste/Funktion Gameport
Analog-Stick X Joystick #1 X analog
Analog-Stick Y Joystick #1 Y analog
DPad X Joystick #1 X digital
DPad Y Joystick #1 Y digital
Cross Joystick #1 B1
Square Joystick #1 B2
Circle Joystick #2 B1
Triangle Joystick #2 B2
LB Joystick #2 B1
RB Joystick #2 B2

Alle anderen Tasten bleiben ohne Signal.
Die digitalen Signale sind technisch auch analoge Signale, jedoch mit 100% Ausschlag.

Nachfolgend teste ich Controller mit verschiedenen Spielen.
Commander Keen bietet sich hier an, da dieser Platformer analoge Steuerung als Spielelement nutzen kann und somit präzise Kontrolle über Keen ermöglicht.

PS5 Controller (über Bluetooth-Dongle)

  • Kalibrierung: Nach längerer Inaktivität inkonsistent (rekalibriert sich scheinbar selbst). Lässt sich durch "Rühren" des Analog-Sticks und des D-Pads wieder in den Ursprungszustand versetzen
  • CPU-Last: Hohe Auslastungsspitzen von 105 % bis 110 % (SSH -> HTOP)
  • Eingabeverhalten: Ungenaue bzw. inkonsistente Eingabeverarbeitung mit Gummibandeffekt

Razer Wolverine TE (kabelgebunden)

  • Kalibrierung: Funktionierte im DOS-Joystick-Check bereits deutlich stabiler und präziser
  • CPU-Last: Geringere Systembelastung, maximale Auslastung von 90 % bis 102 %
  • Eingabeverhalten: Direkteres Ansprechverhalten mit gefühlt geringerer Latenz; geringere Inkonsistenz und Gummibandeffekt feststellbar

Fazit

Die hohe CPU-Auslastung bei der Bluetooth-Verarbeitung bringt den Raspberry Pi 3B+ mit der Python-Software anscheinend an seine Leistungsgrenzen und das ist wahrscheinlich die Ursache für die inkonsistente Eingabeverarbeitung beim PS5-Controller.
Die kabelgebundene Verbindung stellt die wesentlich performantere und latenzärmere Lösung auf der aktuellen Hardware dar, hat allerdings auch die gleichen Probleme, wenn auch wesentlich seltener.
Ein Upgrade auf einen Raspberry Pi 4 ist erwägenswert, um die Leistungsreserven zu erhöhen und eventuell engpassbedingte Eingabefehler bei kabelloser wie auch kabelgebundener Nutzung zu vermeiden.

Die spezielle Konfiguration für das Mapping des verbundenen Gamepads auf Maus/Tastatur und vielleicht auch Gameport habe ich noch nicht versucht. Die GUI des Hilfsprogramms wirkt aber eher rudimentär und ist für technisch nicht so versierte Personen wahrscheinlich unbrauchbar. Zumal hier auch erstmal Zeit investiert werden muss wie genau die Events auf die Tasten zu mappen sind, da es auch hier von Gamepad zu Gamepad Unterschiede gibt.
Aber - wie gesagt - ich habe es selbst noch nicht getestet! Die Motivation dazu fehlt mir aber, denn die Gamepadumsetzung ist hier schlicht und einfach nicht 100% konsistent.

Konfigurationsvielfalt in allen Ehren, sofern sie sich am Ende auch als praxistauglich erweist. Bei einem Preis von rund 110 € (inkl. Versand) und dem Einsatz eines RPi 3B+ liegt die Hardware schließlich weit über der Mindestanforderung eines RPi 1. Hier erwarte ich schlichtweg eine einwandfreie Konsistenz ohne jegliche Gummibandeffekte.

Ich räume ein, dass bisher Tastatur und Maus einwandfrei funktionieren. Hier gibt es aber für eine USB (PS/2 kompatible) oder PS/2 zu Serieller Maus-Emulation eine Alternative beim: Serdashop für 50€ inkl. Versand.
PS/2 kompatible, meist Office-Tastaturen sind ebenfalls noch einigermaßen verfügbar. USB auf PS/2 und PS/2 auf DIN Adapter funktionieren passiv und sind jeweils deutlich unter 10€ erhältlich.
Recherchetipp: Die Maus oder Tastatur muss auch das PS/2 Protokoll beherrschen, dann ist es egal ob sie einen USB-Anschluss hat. Das lässt sich mit eben genanntem Adapter passiv umwandeln.
Auch möchte ich den HIDman erwähnen, der auch die Konvertierung einer reinen USB-Maus und USB-Tastatur, sowie einiger USB-Gamepads (als Maus-/Tastaturemulation) beherrscht und für etwa 95€ inkl. Versand erhältlich sein sollte. Shopseite: tindie.com

Wenn man bedenkt, dass die Mindestanforderung angeblich "nur" ein RPi 1 ist, der hier schnell genug sein soll?! Puh, da finde ich das schon eine gewagte Aussage des Entwicklers, wenn ich hier schon mit dem RPi 3B+ auf diese Probleme stoße.
Da wäre ein Ansatz der Software in C statt Python wohl sinnvoller, wesentlich timing-stabiler und deutlich performanter gewesen und damit höchstwahrscheinlich auch die RPi 1 oder 2 Anforderung zu halten.

Weiterhin kommt zu den Kosten von rund 110€ (inkl. Versand) eben noch mindestens ein RPi 3B+, eher wahrscheinlich ein RPi 4 und jeweils ein passendes Netzteil, sowie eine MicroSD-Karte plus Kartenleser dazu. Damit kommt man schnell auf eine Gesamtsumme von über 150-200€.
Mir persönlich ist damit der Preis und der Aufwand für die gebotene Leistung mit KO-Kriterium Gamepadnutzung zu hoch. Und in dem Falle muss das leider heissen: ungenügend.
Das vom Entwickler gewählte Python-Konzept lässt mich hier leider zweifeln, ob mir da nicht auch später Fehleingaben über Maus und Tastatur passieren.
Ich werde noch weitere Dinge probieren, das bedeutet aber auch, dass hier Stand jetzt, Ende der Fahnenstange für den "Plug & Play"-Benutzer ist, der schmerzfrei und bequem eine Umwandlung moderner Eingabegeräte auf Retro-Hardware nutzen möchte.

Kompatibilitätsmatrix

Eingabegerät Anschluss Hidman PS2 to Serial USB4VC
Wireless Tastatur Dongle x - x
USB-Tastatur USB x - x
PS/2-kompatible Tastatur USB x - x
PS/2-Tastatur PS/2 Adapter - Adapter
Wireless Maus Dongle x - x
USB-Maus USB x - x
PS/2-kompatible Maus USB x Adapter x
PS/2-Maus PS/2 x x Adapter
Wireless Gamepad Dongle ? - ?
USB-Gamepad USB ? - x

Angaben ohne Gewähr.

Wireless können Bluetooth oder ein 2,4 GHz proprietärer Empfänger sein. Die Unterstützung kann hier stark variieren!
Passive Adapter können sein: USB -> PS/2 oder PS/2 -> USB
Vom Betrieb über USB-Hubs oder KVMs würde ich abraten. Kann funktionieren, muss aber nicht.

Emulationsmatrix

Folgende Anschlüsse für Retro-PC Hardware werden zur Verfügung gestellt:

Umwandler DIN-Tastatur PS/2-Tastatur PS/2-Maus Serielle Maus Gameport
Hidman Adapter x - x -
PS/2 to Serial - - - x -
USB4VC Adapter x x x x

DIN-Tastatur über passiven PS/2 -> DIN Adapter.

Technische Analyse

Um mal zu analysieren wie sich die einzelnen Threads, Gesamtsystem-Auslastung, I/O untereinander verhalten habe ich ein Logger-Skript gebastelt.
Die Aufbereitung erfolgt hinterher auch wieder durch ein Skript.

Installiert werden muss auf dem Pi:

  • sysstat
  • python3-matplotlib
  • python3-pandas

benchmark_logger.sh:

#!/usr/bin/env bash

# System-Locale auf C setzen (verhindert Komma/Punkt-Probleme)
export LC_ALL=C

# Messintervall in vollen Sekunden (sysstat Standard)
INTERVAL=1

# Suchbegriff für den Prozess
PROCESS_NAME="${1:-python}"

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_DIR="/tmp/perf_log_${TIMESTAMP}"
mkdir -p "${LOG_DIR}"

PIDS=$(pgrep -d, -f "${PROCESS_NAME}")

if [ -z "${PIDS}" ]; then
    echo "Fehler: Kein laufender Prozess für '${PROCESS_NAME}' gefunden."
    exit 1
fi

echo "=== Performance Logging gestartet ==="
echo "Gefundene PIDs : ${PIDS}"
echo "Intervall      : ${INTERVAL}s"
echo "Speicherort    : ${LOG_DIR}"
echo "Abbrechen mit  : STRG + C"
echo "====================================="

cleanup() {
    echo -e "\n=== Beende Aufzeichnung..."
    kill $(jobs -p) 2>/dev/null
    wait $(jobs -p) 2>/dev/null
    echo "Fertig. Log-Dateien abgelegt in: ${LOG_DIR}"
    ls -lh "${LOG_DIR}"
    exit 0
}

trap cleanup SIGINT SIGTERM

# 1. Gesamtsystem & Core-CPU-Auslastung
mpstat -P ALL ${INTERVAL} > "${LOG_DIR}/sys_cpu.log" &

# 2. Gesamtsystem Datenträger-I/O
iostat -x ${INTERVAL} > "${LOG_DIR}/sys_disk_io.log" &

# 3. Python-Threads CPU-Auslastung
pidstat -p "${PIDS}" -t ${INTERVAL} > "${LOG_DIR}/py_threads_cpu.log" &

# 4. Python-Prozess Datenträger-I/O
pidstat -p "${PIDS}" -d ${INTERVAL} > "${LOG_DIR}/py_process_io.log" &

wait

plot_sysstat.py:

#!/usr/bin/env python3
import os
import sys
import matplotlib.pyplot as plt
import pandas as pd

if len(sys.argv) < 2:
  print('Aufruf: python3 plot_sysstat.py /tmp/perf_log_20260904_xxxxxx')
  sys.exit(1)

log_dir = sys.argv[1]

# 1. Python-Threads log parsen
thread_log = os.path.join(log_dir, 'py_threads_cpu.log')
thread_data = []

if os.path.exists(thread_log):
  with open(thread_log, 'r') as f:
    for line in f:
      parts = line.strip().split()
      if not parts or parts[0].startswith('Linux') or 'TGID' in parts:
        continue
      # pidstat -t Ausgabezeile filtern (Zeilen mit gültiger TID)
      if len(parts) >= 9 and parts[3] != '-':
        time_str, tid, cpu = parts[0], parts[3], float(parts[8])
        thread_data.append({'Time': time_str, 'TID': tid, 'CPU': cpu})

df_threads = pd.DataFrame(thread_data)

# 2. System CPU log parsen
sys_log = os.path.join(log_dir, 'sys_cpu.log')
sys_data = []

if os.path.exists(sys_log):
  with open(sys_log, 'r') as f:
    for line in f:
      parts = line.strip().split()
      if not parts or parts[0].startswith('Linux') or 'CPU' in parts:
        continue
      if len(parts) >= 4:
        time_str = parts[0]
        # Berücksichtigung von AM/PM Zeitformaten
        cpu_id = parts[1] if parts[1] not in ['AM', 'PM'] else parts[2]
        idle = float(parts[-1])
        sys_data.append(
            {'Time': time_str, 'CPU_ID': cpu_id, 'Usage': 100.0 - idle}
        )

df_sys = pd.DataFrame(sys_data)

# --- Erstellung der Diagramme ---
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=False)

# Graph 1: Gesamtsystem & CPU-Kerne
if not df_sys.empty:
  for cpu_id, group in df_sys.groupby('CPU_ID'):
    label = 'Gesamtsystem (all)' if cpu_id == 'all' else f'Core {cpu_id}'
    lw = 2.5 if cpu_id == 'all' else 1.2
    ls = '-' if cpu_id == 'all' else '--'
    ax1.plot(
        range(len(group)),
        group['Usage'],
        label=label,
        linewidth=lw,
        linestyle=ls,
    )

  ax1.set_title('System CPU-Auslastung (%)')
  ax1.set_ylabel('CPU %')
  ax1.grid(True, linestyle=':', alpha=0.6)
  ax1.legend(loc='upper right')

# Graph 2: Einzelleistungsdaten der Python Threads
if not df_threads.empty:
  for tid, group in df_threads.groupby('TID'):
    ax2.plot(range(len(group)), group['CPU'], label=f'Thread (TID {tid})')

  ax2.set_title('Python Threads CPU-Auslastung (%)')
  ax2.set_xlabel('Messpunkte (Sekunden)')
  ax2.set_ylabel('CPU %')
  ax2.grid(True, linestyle=':', alpha=0.6)
  ax2.legend(loc='upper right')

plt.tight_layout()
output_img = os.path.join(log_dir, 'performance_dashboard.png')
plt.savefig(output_img, dpi=300)
print(f'Diagramm gespeichert unter: {output_img}')

Beispiel-Graph mit Commander Keen 1:
performance_dashboard_keen1.png
Dort hatte ich 3x Ghost-Input eines Buttons 🤔 Eine Analyse zeigt bei ~ 14s, ~ 41-43s und ~ 90s Auffälligkeiten durch einen Einbruch der CPU-Last. Das deckt sich plausibel mit den Stellen des Ghost-Inputs.
Hier bricht dann wohl technisch gesehen das Timing für die Gameport-Verarbeitung zusammen und verursacht unerwünschtes Verhalten wie Buttonpresses oder plötzliche Bewegungen der Achsen (Ghost-Input).

Weiterhin steht fest: Ob ein RPi 3B+ oder RPi 4B benutzt wird, die CPU-Auslastung ist immer auf "Anschlag", ob die angesteuerte Hardware das so braucht wage ich zu bezweifeln. Aber das ist Sache des Entwicklers.
Hier muss es wohl eine Obergrenze geben, k.a. 1000 Hz Polling oder irgendsowas, damit der Python-Thread nicht "bis ins unendliche" macht was er will und somit immer an 100% Auslastung kratzt und irgendwann einen "Overflow/Underrun" produziert. Die Timings geraten dadurch ja auseinander bzw. sind/werden inkonsistent.
Ich kann mich da gerade nicht exakt ausdrücken, ich denke aber man kann verstehen was ich meine.

Momentan stehe ich damit in direktem Kontakt mit dem Entwickler. Er scheint es hilfreich zu finden. Schauen wir mal, ob es zu einer Lösung kommt. :-)

TBC