DIY F1 Racing Steering Wheel Controller Using a Raspberry Pi
by danieman in Design > Game Design
1739 Views, 17 Favorites, 0 Comments
DIY F1 Racing Steering Wheel Controller Using a Raspberry Pi
I’m a big F1 fan, and because the Saudi Arabia and Bahrain races were not on the calendar at this point in the season, the wait for the next F1 weekend felt too long. So instead of waiting in anticipation, I decided to build my own racing wheel controller to bring the F1 experience closer to home.
The main component of the controller is a Raspberry Pi 5, which reads inputs from different switches and sensors and displays information on a 5-inch LCD screen. The controller works well with racing games because the left and right steering is controlled using an MPU-6050 accelerometer and gyroscope sensor. It also includes vibration feedback to make the experience feel more interactive.
The controller includes six tactile switches, two toggle switches, an accelerometer/gyroscope sensor, a vibration motor, and three potentiometer selector knobs. Different games can be played with this controller.
The next sections explain how to build the controller step by step.
Downloads
Supplies
The tools and items used for this project are listed here:
- Raspberry Pi 5 (16GB RAM)
- 5 ich HDMI LCD (XPT2046 touch controller - Waveshare)
- Arduino Pro Micro
- Keystudio vibration motor
- Laptop for programming
- Soldering Iron
- Solder
- Heat shrink
- 3d printer (Creality K2 Pro)
- 3d filament
- Double sided tape
- Jumper Wires
- 0.25mm wrapping wire
- Metal inserts
- Nuts and washers
- Side cutter
- Screwdriver
Design the Controller Body and 3d Print
The controller body design was based on the Mercedes F1 steering wheel. I used a photo of the steering wheel, imported it into Autodesk Inventor, and traced the profile of the body. I then scaled the design so that the LCD screen would fit neatly into the housing.
The body was designed so that all components fit precisely into their respective positions, eliminating the need for additional supports. Grooves were also added for each electronic component to neatly route the wiring. After completing the main body, the top plate was designed, and the text was added using the Creality 3D print slicer.
All the electronics and wiring are housed in the body, and the top plate securely covers everything. The Raspberry Pi is connected directly to the LCD screen via the header pins, and the screen is mounted using stand-off pillars onto a back plate. This back plate is attached to the rear of the steering wheel, which is why the center hole remains open on both the front and back sides.
The next component designed in Inventor was the potentiometer mounting bracket. The three potentiometers are positioned at the bottom of the steering wheel, and the bracket was designed to hold them firmly in place.
After that, custom potentiometer knobs were designed. These knobs slide onto the shafts of the potentiometers.
The final 3D model is the screen frame, which fits around the LCD screen to give a clean finish.
Once all the designs were completed, the parts were 3D printed using a Creality K2 Pro printer. All components were printed in PLA, with the main body printed in carbon fibre PLA and the back plate in wood PLA.
Adding Electronics: Tactile and Toggle Switches
The electronic components include six tactile switches, two toggle switches, an accelerometer/gyro sensor, a vibration motor, and three potentiometers.
This section focuses on the tactile and toggle switches that were added. All eight switches are connected directly to the Raspberry Pi GPIO pins.
Tactile switches are momentary, meaning they make contact when pressed and disconnect when released. Toggle switches are latching, so they remain in their position until manually switched back.
The following GPIO pins were used:
Left blue: 20, Left white: 13, Yellow: 12, Left toggle: 19, Right blue: 5, Right white: 6, Red: 26, Right toggle: 16
Each switch was wired as follows: one side of the switch is connected to the GPIO pin, and the other side is connected to ground. The Raspberry Pi’s internal pull-up resistors were enabled, so no external resistors were required.
I initially used standard jumper wires, but their thickness made it difficult to route them neatly inside the enclosure. I then switched to 0.25 mm wrapping wire, which made the wiring much more compact and manageable. The wires were soldered to the switches and insulated using heat shrink tubing.
Adding Electronics: Potentiometers
The potentiometers were added to replicate the rotary switches used on F1 steering wheels. These are typically used to select items from a large list of options. For example, the strategy rotary on the Mercedes F1 steering wheel has options from 1 to 16, which are used to select preset engine settings for different race scenarios.
Three 10 kΩ potentiometers were used in this project. Reading the analog signals was not that simple, as the Raspberry Pi does not have built-in analog input capability. To solve this, I used an Arduino Pro Micro to read the three analog signals and then transmitted the data to the Raspberry Pi using the UART protocol.
An analog-to-digital converter (ADC) chip could have been used as a simpler solution, but using the Arduino provides more flexibility for processing and scaling the analog signals if needed.
The potentiometers were powered using 5 V, and the wiper outputs were connected to the Arduino analog inputs A0, A1, and A2.
One important consideration was converting the Arduino TX output from 5 V logic to the 3.3 V logic level required by the Raspberry Pi. This was done using a simple voltage divider consisting of a 2 kΩ and 3.3 kΩ resistor.
Adding Electronics: MPU-6050 Motion Sensor
These sensors works great to detect the orientation of the steering wheel and it makes a racing game experience so much more real to physically turn the steering wheel instead of pressing buttons to steer left or right. This device uses an accelerometer and gyroscope to detect tild and rotation. The accelerometer measures tilt that I used for left and right turning. The device sends these measurements out via I2C communication which plug straight into the Pi SDA and SCL pins. I used GPIO 2 (SDA) and GPIO 3 (SCL).
Adding Electronics: Vibration Motor
The vibration motor adds haptic feedback to the steering wheel and makes the experience feel more like a real controller. This module can be used directly from a Pi GPIO pin. The board terminals are G, V and S, where G is ground, V is supply voltage (I used 5VDC from the Pi) and S is signal, which I connected to a GPIO pin and will turn the motor on when the GPIO goes to 3.3VDC.
Adding Electronics: Raspberry Pi and LCD Display
The display makes the controller completely portable so you can use it anywhere you are. It's also great that it is touch screen so you dont have to remote connect to the Pi or add a mouse to open applications. Here is a link to the LCD display that I used.
https://www.waveshare.com/wiki/5inch_HDMI_LCD?srsltid=AfmBOoqBIIzniFmpg74kpkeexUbfWhjt1pBwi6YrM2BHJxfojPh8PP2g
It connects straight to the Pi's terminal strip. The LCD screen plugs into the Pi pins 1 to 26 which is a bit annoying if you want to use those pins. The LCD actually just use 5 Pi pins, but use the others as extra support. I had to use some of the pins, but the LCD have a terminal strip that is connected to all the pins connected to the Pi. I soldered the wires straight to the terminal strips, but I do not recommend that. The best would be to solder a header strip to those pins in order to plug jumper wires in. Those strips are very small and the wires break of very easy when you move the LCD screen.
The LCD comes with a HDMI plug that connects to the display and Pi HDMI ports. The display immediately worked with my Pi 5, but the touch screen did not work immediately. I had to set some settings in the Pi before that worked. The link above helps with that.
To communicate and program the Pi I used remote connections via VNC. Just enter the Pi IP address and you can remotely connect to the Pi display.
Circuit Diagrams
This section show the circuits that was used to connect all the components to the Pi. All the G's indicate ground and the Arduino and Raspberry Pi grounds should be linked. The connections are already mentioned and specified in the previous sections, but the attached images just show a better image of all the connections.
Software
The Raspberry Pi was programmed using Python and the Arduino C++. This section explain what was done and also all the code used.
Many games can be played with this controller, but the one I found to be fun and a cool racing game is SuperTuxKart. I downloaded and installed the game on the Raspberry Pi and programmed the Pi so that the game recognise the controller as a joystick. When I open the Python program it opens SuperTuxKart immediately and configure all my components to be used in the game. The important part is that in the game at the control setup it must recognise the controller as a joystick.
I used Geany to run the Python code.
The full working .py program is attached and main section explained next:
1. At the top of the code, several Python libraries are imported:
gpiozero is used to read the physical buttons connected to the Raspberry Pi GPIO pins.
evdev is used to create virtual input devices, such as a joystick and keyboard.
mpu6050 is used to read tilt data from the MPU-6050 sensor.
threading allows the motion sensor to be read continuously while the buttons still work.
subprocess is used to launch SuperTuxKart automatically from the script.
2. Setting up the MPU-6050 motion sensor
sensor = mpu6050(0x68)
The MPU-6050 is connected to the Pi over I2C. Its default address is 0x68.
These settings are then used to tune the steering:
MAX_AY defines how much sensor movement represents full steering.
DEADBAND creates a small neutral zone around the centre, so tiny vibrations do not steer the car.
SMOOTHING reduces jitter and makes the steering feel smoother.
STEERING_DIRECTION is used to reverse the steering direction if left and right are swapped.
3. Creating a virtual joystick
joystick_capabilities creates a virtual joystick device
This is what allows SuperTuxKart to see the homemade steering wheel as a real game controller.
4. Creating a virtual keyboard for menus
The joystick is used for driving, but menus are easier to control with keyboard inputs. For this reason, the program also creates a small virtual keyboard.
keyboard_capabilities does that
Left white → left menu movement
Right white → right menu movement
Right blue → enter/select
Left toggle → escape/back
5. Mapping the physical buttons
Each physical button is connected to a Raspberry Pi GPIO pin:
btn_red = Button(26, pull_up=True)
btn_yellow = Button(12, pull_up=True)
btn_left_white = Button(13, pull_up=True)
btn_right_white = Button(6, pull_up=True)
btn_left_blue = Button(20, pull_up=True)
btn_right_blue = Button(5, pull_up=True)
btn_left_toggle = Button(19, pull_up=True)
btn_right_toggle = Button(16, pull_up=True)
The button assignments are:
Red button = GPIO 26 = Accelerate; Yellow button = GPIO 12 = Brake / reverse; Left white = GPIO 13 = Menu left; Right white = GPIO 6 = Menu right; Left blue = GPIO 20 = nitro; Right blue = GPIO 5 = Enter / select; Left toggle = GPIO 19 = Escape / back; Right toggle = GPIO 16 = Fire boost.
6. Mapping the MPU-6050 to analog steering
def steering_loop(): Here the program reads the Y acceleration value from the MPU-6050.
def map_ay(ay) : The sensor value is smoothed, then converted into a joystick X-axis value
This converts the sensor reading into the joystick range:
-32768 → full left
0 → centre
32767 → full right
7. Running the steering loop in the background
threading.Thread(target=steering_loop, daemon=True).start()
This allows the MPU-6050 to be read continuously while the rest of the program still responds to button presses. Without threading, the program would either read the sensor or read buttons, but not both smoothly at the same time.
8. Launching the game automatically
game = subprocess.Popen(["supertuxkart"])
This starts SuperTuxKart automatically when the controller script runs. That means I only need to run one Python file, and the game opens with the controller already active.
The complete program is attached below.
The vibration motor and the potentiometers is'nt used in this game, but can be configured for another F1 simulator. The Arduino is used to send the analog potentiometer values continiously and the C++ program is also attached. The program is simple, it just read the analog voltages from the three potentiometers and continiously sends the data via UART from the Arduino TX pin to the Pi RX pin.
Controller Sensor Test Program
I made a test program that display all the controller components and also activate the vibration motor when the red button is pushed. The video show that and I used it as a fall-back test code to see if everything is still working.
Next Phase: Testing Energy Deployment Strategies
The next phase is to build a real RC car that will simulate the 2026 F1 hybrid cars. This project originally started with that in mind and therefore the header on the controller that say F1 Hybrid 2026. I want to build the RC car so that it will have two motors. The one motor will simulate the internal combustion engine of the F1 cars and the seconds motor will simulate the electric motor. The idea is then to test different strategies for energy consumption. The current F1 season (2026) mostly rely on how well the teams use their energy from fuel and battery storage and when to deploy what part of the car and at what stage of the track. I think it would be cool to play at home with these settings and see what will result in better lap times on a test RC circuit.
I started by dismatling a RC McClaren I had at home and installed an extra motor on it and used a belt to drive the shaft.