RC Cars & BoatsEngineer

Smart RC Car: Drive from Your Phone with a Live Camera

Replace the RC receiver with a Raspberry Pi and control steering and throttle from a web page.

LevelEngineer
Time4 h
Est. cost~$237
Steps6

Hobby-grade RC cars use standard servo signals for steering and the ESC. A PCA9685 board lets the Pi generate those signals precisely, and a tiny Flask app turns your phone into the remote.

⚠ SAFETY
Test with the wheels off the ground first. Keep throttle limited until the failsafe is proven. LiPo batteries can catch fire if punctured, overcharged or shorted. Charge in a LiPo bag, never unattended, and always remove propellers while working on the bench.

Video tutorial

▶ Doctor VoltWatch on YouTube ↗

Video by Doctor Volt, embedded from YouTube. The written steps below are our own and match the parts list.

Mission steps

  1. Map the wiring

    Unplug the steering servo and ESC from the receiver. Their three-wire plugs (signal, +, −) go to PCA9685 channels 0 and 1.

  2. Power smartly

    Most ESCs supply 5–6 V on their red wire (BEC) — that can power the servo rail. Power the Pi separately from a UBEC and join all grounds.

  3. Install the libraries

    Enable I2C, then pip install adafruit-circuitpython-servokit flask in a venv.

  4. Calibrate

    Find center steering and neutral throttle values. Many ESCs need to see neutral for a few seconds before they arm.

  5. Run the web remote

    Start the Flask app and open the Pi's address on your phone. Add the camera stream from the Picamera2 examples for FPV driving.

  6. Add a failsafe

    If no command arrives for 0.5 s, set throttle to neutral. A lost Wi-Fi link must stop the car.

Code

# app.py  —  pip install flask adafruit-circuitpython-servokit
import time, threading
from flask import Flask, request
from adafruit_servokit import ServoKit

kit = ServoKit(channels=16)
STEER, ESC = kit.servo[0], kit.continuous_servo[1]
last = time.time()
app = Flask(__name__)

@app.route('/drive')
def drive():
    global last
    STEER.angle = float(request.args.get('steer', 90))      # 0-180, 90 = center
    ESC.throttle = max(-0.4, min(0.4, float(request.args.get('thr', 0))))  # limit speed
    last = time.time()
    return 'ok'

def failsafe():
    while True:
        if time.time() - last > 0.5:
            ESC.throttle = 0
        time.sleep(0.1)

threading.Thread(target=failsafe, daemon=True).start()
app.run(host='0.0.0.0', port=8000)

Tip: test every script on the bench before installing it in a moving machine.

Questions

Which RC car works best?

Any hobby-grade 1/10 or 1/16 car with a separate steering servo and ESC (three-wire plugs). Toy-grade cars with all-in-one boards are much harder to convert.