ZSZaid Saifan

Projects / Wireless RC Car

Western Engineering AutoPilot

Wireless RC Car with Electronic Differential

A custom RC car driven from a PS5 controller. Each rear wheel has its own motor, and the firmware sets their speeds from the steering angle so the car corners without the inside wheel slipping.

My roleSteering, chassis, electronic differential
  • Arduino C++
  • Python
  • SolidWorks
  • L298N
  • PWM
Fig. 0The finished car: printed upper deck and rear wing over the drivetrain, with the steering linkage visible at the front.

ProblemWhy an electronic differential

A fixed rear axle forces both wheels to turn at the same speed. In a corner the inside wheel travels a shorter arc, so one wheel has to slip, which shows up as understeer and lost traction.

Giving each rear wheel its own motor lets the control code set a speed offset from the steering input, so each wheel turns at the speed the corner asks for.

FirmwareControl path

  • A PS5 controller pairs to a Python script that mixes the triggers and stick into separate left and right rear-motor speeds.
  • Those commands go wirelessly to C++ firmware on the Arduino, which turns them into PWM signals for the motor driver.
  • The loop is short, so the car responds without noticeable lag.
PS5controllerPythonL/R motor mixingwirelessArduinoC++ firmwarePWM outputL298NH-bridgeM · LM · RSteering servoown supply rail11.1 V 3S LiPo → dual buck converters
DiagramControl path from thumbstick to PWM. The Python script mixes throttle and steering into left/right speeds; the Arduino turns them into PWM.

PythonThe controller script

This is the laptop side of the control path above. It reads the PS5 controller with pygame and sends commands to the car's Bluetooth module with pyserial at 9600 baud.

  • R2 is gas, L2 is brake and reverse, and the left stick steers, with a 10% deadzone.
  • Throttle and steering become separate left and right rear-motor commands from −255 to 255, sent as a left,right line. The car turns by driving its two rear wheels at different speeds.
  • The controller is polled every 20 ms, and a command is only sent when it changes.
  • ✕ toggles an autopilot mode (AUTO / MAN), and Ctrl+C sends 0,0 so the car stops before the script exits.
ps5_bluetooth_controller.pyDownload .py
import pygame
import serial
import time

# ── Configuration ──────────────────────────────────────────────
BLUETOOTH_PORT = 'COM5'  # <--- CHANGE TO YOUR PORT
BAUD_RATE = 9600
UPDATE_RATE = 0.02
DEADZONE = 0.1

try:
    bt = serial.Serial(BLUETOOTH_PORT, BAUD_RATE, timeout=1)
    print("Bluetooth connected successfully!")
except Exception as e:
    print(f"Error connecting to Bluetooth: {e}")
    exit()

pygame.init()
pygame.joystick.init()

if pygame.joystick.get_count() == 0:
    print("No controller detected.")
    exit()

controller = pygame.joystick.Joystick(0)
controller.init()
print(f"Controller Connected: {controller.get_name()}")

# PS5 Controller Mappings
AXIS_STEER = 0
AXIS_L2 = 4
AXIS_R2 = 5
BTN_CROSS = 0  # The 'X' Button on PS5

last_command = ""
auto_mode = False
last_btn_state = False

print("\n--- PS5 Controls Active ---")
print("R2: Gas | L2: Brake/Reverse | L3: Steer")
print("Press 'X' to toggle Autopilot!")

try:
    while True:
        pygame.event.pump()

        # --- AUTOPILOT TOGGLE LOGIC ---
        btn_state = controller.get_button(BTN_CROSS)
        if btn_state and not last_btn_state:  # Button was just pushed down
            auto_mode = not auto_mode  # Toggle the state

            if auto_mode:
                bt.write(b"AUTO\n")
                print("\n[!] AUTOPILOT ENGAGED [!]\n")
            else:
                bt.write(b"MAN\n")
                print("\n[-] MANUAL CONTROL RESTORED [-]\n")

        last_btn_state = btn_state

        # --- MANUAL DRIVING LOGIC ---
        # We only calculate and send joystick data if Autopilot is OFF
        if not auto_mode:
            steer_axis = controller.get_axis(AXIS_STEER)
            l2_axis = controller.get_axis(AXIS_L2)
            r2_axis = controller.get_axis(AXIS_R2)

            if abs(steer_axis) < DEADZONE:
                steer_axis = 0.0

            brake = max(0.0, (l2_axis + 1.0) / 2.0)
            gas = max(0.0, (r2_axis + 1.0) / 2.0)

            throttle = gas - brake

            left_motor = throttle + steer_axis
            right_motor = throttle - steer_axis

            left_motor = max(-1.0, min(1.0, left_motor))
            right_motor = max(-1.0, min(1.0, right_motor))

            left_pwm = int(left_motor * 255)
            right_pwm = int(right_motor * 255)

            command = f"{left_pwm},{right_pwm}\n"

            if command != last_command:
                bt.write(command.encode('utf-8'))
                last_command = command
                # Only print the dashboard occasionally so it doesn't flood the terminal
                print(f"\rMANUAL >> L2: {brake:.2f} | R2: {gas:.2f} | Motor L: {left_pwm:4d} | Motor R: {right_pwm:4d}",
                      end="")

        time.sleep(UPDATE_RATE)

except KeyboardInterrupt:
    print("\nExiting. Stopping car...")
    bt.write(b"0,0\n")
    bt.close()
    pygame.quit()
CodeLaptop-side controller script: PS5 triggers and stick → left/right rear-motor commands over Bluetooth serial.

ElectricalPower and motor hardware

  • An 11.1 V 3S LiPo powers the whole car, split by dual buck converters so logic and actuators never share a rail.
  • An L298N dual H-bridge drives both rear motors.
  • The steering servo has its own supply, so a stall cannot brown out the microcontroller mid-corner.
  • I designed the circuit and wiring layout and selected every part against a BOM.

MechanicalChassis

I modelled the chassis in SolidWorks to package the steering servo, motors and electronics, then completed the mechanical assembly. The car was demonstrated live to club leadership, which tested control response, chassis integrity and firmware stability under real driving.

DemoSee it drive

VideoDriving test on carpet, controlled wirelessly from the PS5 controller.
VideoBench test of the servo steering sweeping lock to lock.

FiguresBuild photos and CAD

Select any figure to open it full size.

05Contact

Let's build something.

I'm looking for 12–16 month co-op and internship terms starting May 2027 in power and electrical design, controls, robotics, embedded systems or hardware test.