A comprehensive cross-platform solution that converts your smartphone into a fully functional virtual steering wheel for PC racing games using accelerometer sensors and WebSocket communication.
- Overview
- System Architecture
- Features
- Prerequisites
- Installation Guide
- How It Works
- Usage Instructions
- Technical Documentation
- Project Structure
- Troubleshooting
- Development & Building
- License
This project consists of two main components working together to create a wireless steering wheel controller:
- Mobile Application (React Native) - Captures real-time accelerometer data from your smartphone and transmits it wirelessly
- PC Server Application (Python) - Receives sensor data and translates it into virtual joystick inputs for racing games
The system provides low-latency (<50ms) control with smooth steering response, making it suitable for casual racing game play over a local Wi-Fi network.
- Steering Control: Tilt your phone left/right to steer the vehicle
- Throttle/Gas: Press the right button on screen to accelerate
- Brake: Press the left button on screen to brake
- Real-time Feedback: Visual indicators show current steering position and button states
- Auto-orientation: Screen automatically locks to landscape mode when connected
┌─────────────────────────┐
│ Android/iOS Phone │
│ │
│ ┌──────────────────┐ │
│ │ React Native App │ │
│ │ (Expo) │ │
│ ├──────────────────┤ │
│ │ Accelerometer │ │
│ │ Sensor (50Hz) │ │
│ └──────────────────┘ │
│ │ │
│ WebSocket (JSON) │
│ ▼ │
└──────────│──────────────┘
│
Wi-Fi Network
(Local LAN)
│
▼
┌──────────────────────────┐
│ Windows PC │
│ │
│ ┌───────────────────┐ │
│ │ Python Server │ │
│ │ (WebSocket) │ │
│ ├───────────────────┤ │
│ │ pyvjoy Library │ │
│ └───────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ vJoy Driver │ │
│ │ (Virtual Joystick)│ │
│ └───────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Racing Game │ │
│ │ (DirectInput) │ │
│ └───────────────────┘ │
└──────────────────────────┘
- Sensor Reading: Accelerometer captures phone orientation at 20Hz
- Data Packaging: X, Y, Z axes + button states packed into JSON format
- Transmission: WebSocket sends data every 50ms to PC server
- Processing: Python server maps accelerometer values to joystick axes
- Virtual Input: vJoy driver creates virtual gamepad recognized by Windows
- Game Control: Racing game reads DirectInput from virtual controller
- 📱 Cross-platform: Works on Android (via Expo/React Native)
- 🎯 Real-time Sensor Data: 20 updates per second for responsive control
- 🔄 Auto-Reconnect: Attempts to reconnect if connection drops
- 🌐 Visual Feedback: Live display of connection status and sensor values
- 📐 Auto-Orientation: Locks to landscape when connected for comfortable steering
- 🎨 Intuitive UI: Large, easy-to-press gas and brake buttons
- 🔒 Stable Connection: WebSocket protocol ensures reliable data transmission
- 🖥️ Windows Compatible: Runs on Windows 7/8/10/11
- ⚡ Low Latency: <50ms response time on local network
- 🎮 vJoy Integration: Creates virtual DirectInput-compatible joystick
- 🔧 Auto-Configuration: Detects and configures vJoy device automatically
- 📊 Detailed Logging: Real-time console output for debugging
- 🛡️ Error Handling: Comprehensive error checking and user guidance
- 🔒 Admin Privilege Check: Ensures proper permissions for driver access
- 🧪 Built-in Testing: Independent vJoy test mode for verification
- X-Axis (vJoy): Steering (left/right tilt) - Full 16-bit range (32,767 steps)
- Y-Axis (vJoy): Brake pedal (on-screen button) - Binary on/off
- Z-Axis (vJoy): Gas/Throttle (on-screen button) - Binary on/off
-
Operating System
- Windows 7 or later (64-bit recommended)
- Administrator privileges required
-
vJoy Virtual Joystick Driver
- Version 2.1.9 or later
- Download: https://sourceforge.net/projects/vjoystick/
-
Python 3.7+ (if running from source)
- Not required if using pre-built
.exefile
- Not required if using pre-built
-
Network
- Wi-Fi adapter or Ethernet connection
- Firewall configured to allow port 5000
-
Android Device
- Android 6.0 or later
- Accelerometer sensor (standard on all modern phones)
- Wi-Fi capability
-
Development Mode (Optional)
- Expo Go app from Google Play Store
- For testing without building APK
-
Network
- Must be on the same Wi-Fi network as PC
- Download vJoy installer from GitHub Releases
- Run
vJoySetup.exeas Administrator - Follow installation wizard
- Restart your computer after installation
- Open Start Menu → Search "Configure vJoy"
- Select Device 1
- Enable the following axes:
- ✅ X-Axis (Steering)
- ✅ Y-Axis (Brake)
- ✅ Z-Axis (Gas)
- Click "Apply" then "OK"
- Press
Win + R - Type
joy.cpland press Enter - You should see "vJoy Device" in the list
- Select it and click "Properties" to test
Option A: Pre-built Executable (Recommended)
cd PythonDesktopApp\dist
SteeringWheelServer.exeOption B: From Source
cd PythonDesktopApp
pip install -r requirements.txt
python main.pyThe server will start and display:
============================================================
🏎️ VIRTUAL STEERING WHEEL SERVER (vJoy)
============================================================
🌐 Server: 0.0.0.0:5000
🎮 Device: vJoy Device #1
⏳ Waiting for connection...
-
Install Expo CLI globally
npm install -g eas-cli
-
Navigate to mobile app folder
cd ReactNativeMobileApp -
Install dependencies
npm install
-
Login to Expo (create free account at expo.dev)
eas login
-
Build APK for Android
eas build -p android --profile preview
- Build takes 10-15 minutes
- Download link will be provided in terminal
- Or visit expo.dev → Your account → Builds
-
Install APK on Phone
- Transfer APK file to your phone
- Enable "Install from unknown sources" in Android Settings
- Open APK and install
-
Install Expo Go from Google Play Store on your phone
-
Install dependencies
cd ReactNativeMobileApp npm install -
Start development server
npx expo start
-
Connect your phone
- Scan QR code with Expo Go app
- App loads instantly (no build needed)
Sensor Management
// Accelerometer updates at 50ms intervals (20Hz)
Accelerometer.setUpdateInterval(50);
// Listener captures X, Y, Z acceleration
Accelerometer.addListener((data) => {
const sensorData = {
x: data.x, // Up/Down tilt
y: data.y, // Left/Right tilt (STEERING)
z: data.z, // Forward/Backward tilt
};
});WebSocket Communication
// JSON payload sent every 50ms
{
"x": -0.15, // X-axis acceleration
"y": 0.42, // Y-axis acceleration (steering)
"z": 0.87, // Z-axis acceleration
"gas": true, // Gas button state
"brake": false // Brake button state
}Data Processing Pipeline
- Receive JSON data via WebSocket
- Parse accelerometer values (x, y, z)
- Map Y-axis to steering range
- Input: -1.0 (left) to +1.0 (right)
- Output: 0x1 to 0x7FFF (vJoy 16-bit range)
- Apply button states
- Gas button → Z-Axis = 0x7FFF (max) or 0x1 (min)
- Brake button → Y-Axis = 0x7FFF (max) or 0x1 (min)
- Send to vJoy driver via pyvjoy library
Axis Mapping Math
def map_to_axis(value, min_val=-1.0, max_val=1.0):
# Clamp input
value = max(min_val, min(max_val, value))
# Normalize to 0.0-1.0
normalized = (value - min_val) / (max_val - min_val)
# Map to vJoy 16-bit range (1 to 32767)
axis_value = int(0x1 + (normalized * (0x7FFF - 0x1)))
return axis_valuevJoy Axis Assignment
- X-Axis: Steering wheel (left/right)
- Y-Axis: Brake pedal (binary on/off)
- Z-Axis: Gas/Throttle pedal (binary on/off)
- Ensure vJoy is installed and configured
- Run
SteeringWheelServer.exe(orpython main.py) - Note the IP address displayed or find it manually:
- Open PowerShell:
ipconfig - Look for "IPv4 Address" under your active network
- Example:
192.168.1.251
- Open PowerShell:
- Open the Steering Wheel app on your phone
- Enter server URL:
ws://YOUR_PC_IP:5000- Example:
ws://192.168.1.251:5000
- Example:
- Tap "Connect"
- Wait for "Connected" status (green indicator)
- Hold phone horizontally (landscape orientation)
- Phone should auto-rotate to landscape mode
- Test steering:
- Tilt left → Steering goes left
- Tilt right → Steering goes right
- Center position → Wheels straight
- Test buttons:
- Press RIGHT (green) → Gas pedal
- Press LEFT (red) → Brake pedal
- Open your racing game's controller settings
- Look for "vJoy Device" in controller list
- Assign controls:
- Steering → X-Axis
- Throttle/Gas → Z-Axis (or Z-Rotation)
- Brake → Y-Axis (or Y-Rotation)
- Test in-game to verify functionality
- Hold phone like a steering wheel (landscape orientation)
- Tilt left/right to steer
- Use on-screen buttons for gas and brake
- To disconnect: Tap "✕ Disconnect" button at top
WebSocket Connection
- Protocol:
ws://(WebSocket) - Port:
5000(default, configurable) - Format: JSON
- Update Rate: 20 messages/second (50ms interval)
Message Structure
{
"x": <float>, // X-axis acceleration (-1.0 to 1.0)
"y": <float>, // Y-axis acceleration (-1.0 to 1.0) - STEERING
"z": <float>, // Z-axis acceleration (-1.0 to 1.0)
"gas": <boolean>, // Gas button pressed (true/false)
"brake": <boolean> // Brake button pressed (true/false)
}| Metric | Value | Notes |
|---|---|---|
| Update Rate | 20 Hz | Configurable in app (50ms interval) |
| Network Latency | 5-20ms | On local Wi-Fi |
| Processing Delay | <5ms | Server-side processing |
| Total Latency | <50ms | End-to-end (sensor to game) |
| Steering Resolution | 16-bit | 32,767 discrete positions |
| Axis Range | 0x1 to 0x7FFF | vJoy standard range |
Mobile Application
- Framework: React Native 0.74.0
- Runtime: Expo SDK 51.0.0
- Sensors: expo-sensors 13.0.0
- Orientation: expo-screen-orientation
- Language: JavaScript (ES6+)
PC Server
- Language: Python 3.7+
- Async I/O: asyncio, websockets
- Virtual Driver: pyvjoy (vJoy wrapper)
- Platform: Windows (vJoy dependency)
Dependencies
# Python (requirements.txt)
websockets>=10.0
pyvjoy>=1.0.3// React Native (package.json)
{
"expo": "~51.0.0",
"expo-sensors": "~13.0.0",
"expo-screen-orientation": "~7.0.0",
"react": "18.2.0",
"react-native": "0.74.0"
}SteeringWheel/
│
├── README.md # This file
│
├── PythonDesktopApp/ # PC Server Application
│ ├── main.py # Main server script
│ ├── requirements.txt # Python dependencies
│ ├── SteeringWheelServer.spec # PyInstaller spec file
│ ├── build_exe.bat # Build script for executable
│ ├── check_firewall.bat # Firewall configuration helper
│ ├── test_server.py # Server testing utilities
│ │
│ ├── build/ # Build artifacts (intermediate)
│ │ └── SteeringWheelServer/
│ │ ├── Analysis-00.toc
│ │ ├── base_library.zip
│ │ ├── EXE-00.toc
│ │ └── ...
│ │
│ └── dist/ # Compiled executable
│ └── SteeringWheelServer.exe # ⭐ Run this on PC!
│
└── ReactNativeMobileApp/ # Mobile Application
├── App.js # Main React Native component
├── package.json # npm dependencies
├── app.json # Expo configuration
├── babel.config.js # Babel transpiler config
├── eas.json # EAS Build configuration
├── build-apk.bat # Quick build script
│
└── server/ # Test server examples
├── server.py # Basic WebSocket test server
├── server_advanced.py # Advanced testing server
└── requirements.txt # Python dependencies for test server
PythonDesktopApp/main.py
- Main server application (850+ lines)
- WebSocket server implementation
- vJoy device management
- Accelerometer data processing
- Error handling and logging
- Admin privilege checking
- Built-in diagnostics
ReactNativeMobileApp/App.js
- Complete mobile app (700+ lines)
- WebSocket client
- Accelerometer sensor management
- UI components (connection, steering)
- State management
- Auto-orientation control
PythonDesktopApp/dist/SteeringWheelServer.exe
- Standalone executable (no Python installation needed)
- Built with PyInstaller
- Includes all dependencies
- Ready to run on any Windows PC
Symptoms: App shows "Connection Failed" or "Disconnected"
Solutions:
- ✅ Verify PC server is running (green text in console)
- ✅ Check both devices on same Wi-Fi network
- PC: Settings → Network → Wi-Fi
- Phone: Settings → Wi-Fi
- ✅ Verify correct IP address
- PC: Run
ipconfigin PowerShell - Use format:
ws://192.168.x.x:5000(not https://)
- PC: Run
- ✅ Check Windows Firewall
# Run as Administrator cd PythonDesktopApp check_firewall.bat
- ✅ Try disabling firewall temporarily to test
- ✅ Ensure port 5000 is not used by another application
Error: "vJoy driver is not enabled!"
Solutions:
- Install vJoy from GitHub
- Restart computer after installation
- Open "Configure vJoy" → Enable Device 1
- Run server as Administrator
Error: "Port 5000 already in use"
Solutions:
- Close other applications using port 5000
- Check for other running instances:
netstat -ano | findstr :5000
- Kill process if found:
taskkill /PID <process_id> /F
Solutions:
- Close "Game Controllers" (joy.cpl) if open
- Close other applications using vJoy
- Restart the server application
- Worst case: Restart Windows
Solutions:
- Open
joy.cpl(Win+R → joy.cpl) - Select "vJoy Device" → Properties
- Verify axes move when tilting phone
- In game settings:
- Select "vJoy Device" as controller
- Map steering to X-Axis
- Map throttle to Z-Axis
- Map brake to Y-Axis
- Some games require:
- Controller calibration in-game
- Deadzone adjustment
- Sensitivity tweaking
Error: "X-Axis not available"
Solutions:
- Open "Configure vJoy"
- Select Device 1
- Check these axes:
- ✅ X-Axis
- ✅ Y-Axis
- ✅ Z-Axis
- Click "Apply" → "OK"
- Restart server
Solutions:
- Ensure phone has accelerometer (all modern phones do)
- Grant sensor permissions if prompted
- Restart the app
- Test with phone's built-in level/compass app
Solutions:
- Enable auto-rotate in phone settings
- Unlock screen orientation
- Reconnect to server (triggers orientation lock)
Solutions:
- Make sure you're pressing within button area
- Check server console for "GAS" or "BRAKE" messages
- Verify in joy.cpl that buttons/axes respond
Windows (PowerShell):
ipconfigLook for "IPv4 Address" under your active adapter (Wi-Fi or Ethernet)
Android:
- Settings → Wi-Fi
- Tap on connected network
- Look for "IP address"
# On PC, ping phone (if you know phone's IP)
ping <phone_ip>
# Check if server is listening
netstat -an | findstr :5000Solutions:
- Ensure strong Wi-Fi signal (both devices near router)
- Close background apps on phone
- Close unnecessary programs on PC
- Check server console for error messages
- Reduce update rate in App.js if needed:
// Change from 50ms to 100ms Accelerometer.setUpdateInterval(100);
Requirements:
- Python 3.7+
- PyInstaller
Steps:
cd PythonDesktopApp
pip install -r requirements.txt
pip install pyinstaller
# Build with batch file (Windows)
build_exe.bat
# Or manually with PyInstaller
pyinstaller --onefile --console --name "SteeringWheelServer" main.pyOutput: dist/SteeringWheelServer.exe
Method 1: EAS Build (Cloud)
cd ReactNativeMobileApp
npm install
eas login
eas build -p android --profile previewMethod 2: Local Build (Advanced)
cd ReactNativeMobileApp
npm install
npx expo prebuild
cd android
./gradlew assembleReleaseOutput: APK file in android/app/build/outputs/apk/
PC Server:
cd PythonDesktopApp
python main.pyMobile App:
cd ReactNativeMobileApp
npm install
npx expo startThen scan QR code with Expo Go app
Test vJoy Independently:
cd PythonDesktopApp
python main.py
# Select option 2 when promptedTest WebSocket Server:
cd ReactNativeMobileApp/server
pip install websockets
python server.pyChange Server Port:
# In main.py
bridge = SteeringWheelBridge(
host='0.0.0.0',
port=8080, # Change this
vjoy_device_id=1
)Adjust Steering Sensitivity:
# In main.py, modify map_to_axis function
# Or apply multiplier to Y-axis value
y_adjusted = y * 1.5 # Increase sensitivityChange Update Rate:
// In App.js
Accelerometer.setUpdateInterval(100); // Slower (10 Hz)
// Or
Accelerometer.setUpdateInterval(20); // Faster (50 Hz)MIT License
Copyright (c) 2025 Steering Wheel Controller Project
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Contributions are welcome! Feel free to:
- Report bugs
- Suggest features
- Submit pull requests
- Improve documentation
Q: Does this work with all racing games? A: It works with games that support DirectInput controllers (most PC racing games). Games using XInput may require additional mapping software like x360ce.
Q: Can I use this wirelessly? A: Yes! It uses Wi-Fi for wireless communication. Both devices must be on the same network.
Q: What's the input lag? A: Typically <50ms on a good local Wi-Fi connection, which is acceptable for casual racing.
Q: Can I use multiple phones? A: Current version supports one phone at a time. Multiple connections would require server modifications.
Q: Does it work on iOS? A: The React Native app can be built for iOS with minor modifications, but it's primarily tested on Android.
Q: Do I need to keep the server window open? A: Yes, the server console must remain running while playing.
Q: Can I customize button mappings?
A: Yes, modify the axis assignments in main.py (vJoy axes) and configure game controller settings accordingly.
- GitHub Repository: SteeringWheel
- vJoy Driver: https://sourceforge.net/projects/vjoystick/
- Expo Documentation: https://docs.expo.dev
- React Native: https://reactnative.dev
Made with ❤️ for racing game enthusiasts! 🏎️💨
Transform your smartphone into a racing wheel and enjoy the thrill of mobile-controlled PC gaming!