Your 10-Step RC Car Journey
1
๐ฆ
โพ
Before we write any robot code, we need to make sure Python has the right tools installed.
We'll use RPi.GPIO to talk to the Raspberry Pi's pins and keyboard
to read arrow-key input later on.
- Open a terminal on your Raspberry Pi
- Run the install commands shown in the code
- Then run the Python script to confirm everything works
These libraries come pre-installed on Raspberry Pi OS โ but the pip commands make sure you have the latest versions!
๐ Python Concept โ
import loads a library โ a package of pre-written code that someone else built so you don't have to!
Think of it like downloading an app: it's already finished and you just use it.
Writing import statementsimport RPi.GPIO as GPIO loads the library and gives it a short nickname
(GPIO) so we can type GPIO.setmode() instead of RPi.GPIO.setmode() every time.
import time gives us tools for pausing the program and measuring elapsed time.
# โโ Step 1: Set Up Your Environment โโโโโโโโโโโโโโโโโโ
# First, open a terminal and run these install commands:
# pip install RPi.GPIO
# pip install keyboard
# Then run this script to confirm everything is working!
import RPi.GPIO as GPIO
import time
import sys
# Check Python version
print(f"๐ Python version: {sys.version}")
# Quick GPIO test โ just sets up and immediately cleans up
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.cleanup()
print("โ
RPi.GPIO is working!")
print("๐ Environment ready โ let's build that RC car!")
2
๐
โพ
The L298N motor driver has input pins (IN1โIN4) that tell the motors which direction to spin,
and enable pins (ENA, ENB) that control speed. We connect these to specific
GPIO pins on the Raspberry Pi.
- IN1/IN2 โ direction for the left motor
- IN3/IN4 โ direction for the right motor
- ENA/ENB โ speed control (we'll use PWM in Step 4)
You can change the pin numbers if your wiring is different โ just update the values at the top of your file!
๐ Python Concept โ Variables & Naming Conventions
In Python you create a variable just by writing NAME = value โ no need to declare a type like in Java or C!
We write pin names in ALL_CAPS because that's the Python convention for constants
(values you don't plan to change). Python won't enforce this, but the name acts as a friendly warning:
"hey, don't change me!". Lines starting with # are comments โ Python ignores them completely.
They exist purely as notes for humans reading the code. Good comments are a sign of a thoughtful programmer!
# โโ Step 2: Define Motor Pins โโโโโโโโโโโโโโโโโโโโโโโโ
# These numbers match the GPIO pins on your Raspberry Pi
# (using BCM numbering โ the numbers printed on the board)
import RPi.GPIO as GPIO
# โโ Left Motor (Motor A) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
MOTOR_A_IN1 = 17 # GPIO 17 โ Forward signal
MOTOR_A_IN2 = 18 # GPIO 18 โ Backward signal
MOTOR_A_EN = 12 # GPIO 12 โ Speed (PWM)
# โโ Right Motor (Motor B) โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
MOTOR_B_IN3 = 22 # GPIO 22 โ Forward signal
MOTOR_B_IN4 = 23 # GPIO 23 โ Backward signal
MOTOR_B_EN = 13 # GPIO 13 โ Speed (PWM)
print("๐ Motor pins defined:")
print(f" Left Motor: IN1={MOTOR_A_IN1}, IN2={MOTOR_A_IN2}, EN={MOTOR_A_EN}")
print(f" Right Motor: IN3={MOTOR_B_IN3}, IN4={MOTOR_B_IN4}, EN={MOTOR_B_EN}")
3
โก
โพ
Every GPIO pin can be either an input (reading a sensor) or an
output (sending a signal). We set all motor pins as outputs and
wrap everything in a
setup() function so we can call it easily.
GPIO.BCMโ use the BCM pin numbering printed on the boardGPIO.OUTโ we're sending signals out to the motors
Always call GPIO.cleanup() when your program ends โ it resets all pins so nothing stays on accidentally!
๐ Python Concept โ Functions (
A function (def) & for loopsdef setup_gpio():) is a reusable, named block of code.
Instead of copying 6 GPIO.setup() lines into every file, you write it once and
call it anywhere with just setup_gpio(). This follows the famous
DRY rule: Don't Repeat Yourself โ one of the most important ideas in programming.
The for loop (for pin in ALL_PINS:) automatically repeats code for
every item in a list, so you never have to copy-paste the same line over and over!
# โโ Step 3: Initialize GPIO Pins โโโโโโโโโโโโโโโโโโโโโ
import RPi.GPIO as GPIO
# Pin definitions (from Step 2)
MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_A_EN = 17, 18, 12
MOTOR_B_IN3, MOTOR_B_IN4, MOTOR_B_EN = 22, 23, 13
ALL_PINS = [MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_A_EN,
MOTOR_B_IN3, MOTOR_B_IN4, MOTOR_B_EN]
def setup_gpio():
GPIO.setmode(GPIO.BCM) # Use BCM pin numbering
GPIO.setwarnings(False) # Silence "pin already in use" warnings
for pin in ALL_PINS:
GPIO.setup(pin, GPIO.OUT) # Set every pin as an OUTPUT
print("โก GPIO pins initialized as outputs!")
setup_gpio()
GPIO.cleanup() # Always clean up when done
print("๐งน Pins cleaned up safely.")
4
๐๏ธ
โพ
PWM (Pulse Width Modulation) turns a pin on and off really fast โ so fast
the motor "feels" an average voltage between 0V and 3.3V. A duty cycle of
75% means the pin is HIGH 75% of the time โ ~75% speed!
- Frequency: 100 Hz (100 on/off cycles per second)
- Duty cycle: 0โ100 (0 = stop, 100 = full speed)
Start with a duty cycle around 50โ75%. Too high and the car zooms away; too low and it might stall!
๐ Python Concept โ Return Values &
range()return pwm_a, pwm_b hands back two values at once โ Python lets you return multiple values
separated by commas! The caller catches both in one line: pwm_a, pwm_b = setup_pwm().
This is called tuple unpacking and it's very handy.
Also notice range(0, 101, 10) โ this generates numbers from 0 to 100 stepping by 10
(so: 0, 10, 20 โฆ 100). The stop value 101 is not included, which is why
you write 101 to actually reach 100. A classic beginner gotcha in Python!
# โโ Step 4: PWM Speed Control โโโโโโโโโโโโโโโโโโโโโโโโโ
import RPi.GPIO as GPIO
import time
MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_A_EN = 17, 18, 12
MOTOR_B_IN3, MOTOR_B_IN4, MOTOR_B_EN = 22, 23, 13
def setup_gpio():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
for pin in [MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_A_EN,
MOTOR_B_IN3, MOTOR_B_IN4, MOTOR_B_EN]:
GPIO.setup(pin, GPIO.OUT)
def setup_pwm():
# Create PWM objects at 100 Hz frequency
pwm_a = GPIO.PWM(MOTOR_A_EN, 100)
pwm_b = GPIO.PWM(MOTOR_B_EN, 100)
pwm_a.start(0) # Start with 0% duty cycle (motors off)
pwm_b.start(0)
print("๐๏ธ PWM ready on both motors!")
return pwm_a, pwm_b
setup_gpio()
pwm_a, pwm_b = setup_pwm()
# Demo: ramp speed from 0% โ 100% โ 0%
print("๐ Ramping speed up...")
for speed in range(0, 101, 10):
pwm_a.ChangeDutyCycle(speed)
pwm_b.ChangeDutyCycle(speed)
print(f" Speed: {speed}%")
time.sleep(0.3)
GPIO.cleanup()
5
โฌ๏ธ
โพ
To spin a motor forward, we set IN1=HIGH and IN2=LOW.
To spin it backward, we flip them: IN1=LOW and IN2=HIGH.
We do this for both motors at the same time!
GPIO.HIGH= 3.3V (signal ON)GPIO.LOW= 0V (signal OFF)- The
speedparameter (0โ100) lets you control how fast
Try calling move_forward() and move_backward() with different speed values โ what's the minimum speed before the motors stall?
๐ Python Concept โ Default Parameters & f-strings
Notice speed=75 inside the function definition? That's a default parameter.
If you call move_forward(pwm_a, pwm_b) without a speed, Python automatically uses 75.
You can still override it: move_forward(pwm_a, pwm_b, speed=50). Default parameters
make your functions flexible without requiring every caller to specify everything.
Also see f"Forward at {speed}% speed" โ the f-string embeds the
value of speed directly inside the text using curly braces. Much cleaner than
old-style "Forward at " + str(speed) + "% speed"!
# โโ Step 5: Forward & Backward Movement โโโโโโโโโโโโโโ
import RPi.GPIO as GPIO
import time
MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_A_EN = 17, 18, 12
MOTOR_B_IN3, MOTOR_B_IN4, MOTOR_B_EN = 22, 23, 13
def setup():
GPIO.setmode(GPIO.BCM); GPIO.setwarnings(False)
for p in [MOTOR_A_IN1,MOTOR_A_IN2,MOTOR_A_EN,MOTOR_B_IN3,MOTOR_B_IN4,MOTOR_B_EN]:
GPIO.setup(p, GPIO.OUT)
pwm_a = GPIO.PWM(MOTOR_A_EN, 100); pwm_a.start(0)
pwm_b = GPIO.PWM(MOTOR_B_EN, 100); pwm_b.start(0)
return pwm_a, pwm_b
def move_forward(pwm_a, pwm_b, speed=75):
GPIO.output(MOTOR_A_IN1, GPIO.HIGH) # Left motor โ forward
GPIO.output(MOTOR_A_IN2, GPIO.LOW)
GPIO.output(MOTOR_B_IN3, GPIO.HIGH) # Right motor โ forward
GPIO.output(MOTOR_B_IN4, GPIO.LOW)
pwm_a.ChangeDutyCycle(speed)
pwm_b.ChangeDutyCycle(speed)
print(f"โฌ๏ธ Forward at {speed}% speed")
def move_backward(pwm_a, pwm_b, speed=75):
GPIO.output(MOTOR_A_IN1, GPIO.LOW) # Left motor โ backward
GPIO.output(MOTOR_A_IN2, GPIO.HIGH)
GPIO.output(MOTOR_B_IN3, GPIO.LOW) # Right motor โ backward
GPIO.output(MOTOR_B_IN4, GPIO.HIGH)
pwm_a.ChangeDutyCycle(speed)
pwm_b.ChangeDutyCycle(speed)
print(f"โฌ๏ธ Backward at {speed}% speed")
# โโ Test it! โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
pwm_a, pwm_b = setup()
move_forward(pwm_a, pwm_b, speed=70)
time.sleep(2)
move_backward(pwm_a, pwm_b, speed=70)
time.sleep(2)
GPIO.cleanup()
6
โฉ๏ธ
โพ
Turning works by spinning the two motors in opposite directions.
To turn left, the right motor goes forward while the left motor goes backward (or stops).
The car pivots in place โ like a tank!
- Turn left โ left motor backward, right motor forward
- Turn right โ left motor forward, right motor backward
- Stop โ set all direction pins LOW and duty cycle to 0
Turning speed should usually be lower than driving speed โ try 50โ60% to keep control!
๐ Python Concept โ Code Reuse & the DRY Principle
Look at the stop() function โ instead of writing 4 separate
GPIO.output(pin, GPIO.LOW) lines, we loop over a list.
This is the DRY principle (Don't Repeat Yourself) in action!
If you ever need to change how stopping works, you edit one place instead of four.
Also notice turn_left and turn_right have near-identical structure.
That's a hint to your future self: one day you could refactor these into a single
turn(direction) function โ a great next challenge as your Python skills grow!
# โโ Step 6: Turn Left, Right & Stop โโโโโโโโโโโโโโโโโโ
import RPi.GPIO as GPIO
import time
MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_A_EN = 17, 18, 12
MOTOR_B_IN3, MOTOR_B_IN4, MOTOR_B_EN = 22, 23, 13
# ... setup() and forward/backward functions from Step 5 ...
def turn_left(pwm_a, pwm_b, speed=55):
# Left motor goes backward, right motor goes forward
GPIO.output(MOTOR_A_IN1, GPIO.LOW)
GPIO.output(MOTOR_A_IN2, GPIO.HIGH)
GPIO.output(MOTOR_B_IN3, GPIO.HIGH)
GPIO.output(MOTOR_B_IN4, GPIO.LOW)
pwm_a.ChangeDutyCycle(speed)
pwm_b.ChangeDutyCycle(speed)
print("โฉ๏ธ Turning left!")
def turn_right(pwm_a, pwm_b, speed=55):
# Left motor goes forward, right motor goes backward
GPIO.output(MOTOR_A_IN1, GPIO.HIGH)
GPIO.output(MOTOR_A_IN2, GPIO.LOW)
GPIO.output(MOTOR_B_IN3, GPIO.LOW)
GPIO.output(MOTOR_B_IN4, GPIO.HIGH)
pwm_a.ChangeDutyCycle(speed)
pwm_b.ChangeDutyCycle(speed)
print("โช๏ธ Turning right!")
def stop(pwm_a, pwm_b):
# Turn off all direction signals and set speed to 0
for pin in [MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_B_IN3, MOTOR_B_IN4]:
GPIO.output(pin, GPIO.LOW)
pwm_a.ChangeDutyCycle(0)
pwm_b.ChangeDutyCycle(0)
print("๐ Stopped!")
7
๐ฎ
โพ
Now we add a control loop that reads arrow key presses in real-time using the
keyboard library. The loop runs 10 times per second and calls
the correct movement function based on which key is held down.
- Press โ to go forward
- Press โ to go backward
- Press โ / โ to turn
- Press Q to quit safely
Run this script as root (sudo python3 step7_keyboard.py) โ the keyboard library requires elevated permissions on Linux!
๐ Python Concept โ
while True, if/elif/else & breakwhile True: creates an infinite loop that runs forever โ until break exits it.
That sounds scary, but it's perfectly normal in hardware code! You want the car to keep
listening until you decide to quit.
The if / elif / else chain checks conditions in order โ Python tries each
one from top to bottom and only runs the first that's True. The final else
is the "none of the above" case โ if no key is pressed, stop the motors.
time.sleep(0.1) pauses the loop for 0.1 seconds, giving you ~10 checks per second
without overloading the CPU.
# โโ Step 7: Keyboard Control โโโโโโโโโโโโโโโโโโโโโโโโโโ
import RPi.GPIO as GPIO
import keyboard
import time
MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_A_EN = 17, 18, 12
MOTOR_B_IN3, MOTOR_B_IN4, MOTOR_B_EN = 22, 23, 13
# ... paste setup(), move_forward(), move_backward(),
# turn_left(), turn_right(), stop() from previous steps ...
def keyboard_control(pwm_a, pwm_b):
print("๐ฎ RC Car ready! Use arrow keys to drive.")
print(" Press Q to quit safely.\n")
while True:
if keyboard.is_pressed('up'):
move_forward(pwm_a, pwm_b)
elif keyboard.is_pressed('down'):
move_backward(pwm_a, pwm_b)
elif keyboard.is_pressed('left'):
turn_left(pwm_a, pwm_b)
elif keyboard.is_pressed('right'):
turn_right(pwm_a, pwm_b)
elif keyboard.is_pressed('q'):
stop(pwm_a, pwm_b)
print("๐ Goodbye! Cleaning up...")
break
else:
stop(pwm_a, pwm_b)
time.sleep(0.1) # ~10 updates per second
pwm_a, pwm_b = setup()
try:
keyboard_control(pwm_a, pwm_b)
finally:
GPIO.cleanup() # Always runs, even if an error occurs
8
๐ก
โพ
The HC-SR04 ultrasonic sensor fires a sound pulse and measures how long
it takes to bounce back. From the time, we calculate the distance in centimetres.
- TRIG pin โ triggers the pulse (output)
- ECHO pin โ goes HIGH while waiting for the echo (input)
- Distance formula:
distance = (time ร speed_of_sound) / 2
Mount the sensor on the front of your car pointing forward. It works best for objects 2 cm โ 400 cm away!
๐ Python Concept โ
time.time() & Built-in Functionstime.time() returns the current moment as a float (decimal number)
representing seconds since January 1, 1970 โ this is called a Unix timestamp.
By recording the time before and after the echo pulse, we calculate its
duration with simple subtraction: pulse_end - pulse_start.
round(distance, 1) is a Python built-in function โ no import needed!
It rounds to 1 decimal place for clean output. Knowing which functions are built-in vs.
need an import is a key Python skill to develop over time.
# โโ Step 8: Ultrasonic Distance Sensor โโโโโโโโโโโโโโโ
import RPi.GPIO as GPIO
import time
# HC-SR04 sensor pins
TRIG = 24 # GPIO 24 โ Trigger (output)
ECHO = 25 # GPIO 25 โ Echo (input)
def setup_sensor():
GPIO.setmode(GPIO.BCM)
GPIO.setup(TRIG, GPIO.OUT)
GPIO.setup(ECHO, GPIO.IN)
GPIO.output(TRIG, GPIO.LOW)
time.sleep(0.5) # Let sensor settle
print("๐ก Sensor ready!")
def get_distance():
# Fire a 10-microsecond trigger pulse
GPIO.output(TRIG, GPIO.HIGH)
time.sleep(0.00001)
GPIO.output(TRIG, GPIO.LOW)
# Wait for ECHO to go HIGH (pulse starts)
pulse_start = time.time()
while GPIO.input(ECHO) == 0:
pulse_start = time.time()
# Wait for ECHO to go LOW (pulse ends)
pulse_end = time.time()
while GPIO.input(ECHO) == 1:
pulse_end = time.time()
# Distance = (time ร speed of sound) / 2
duration = pulse_end - pulse_start
distance = (duration * 34300) / 2 # cm
return round(distance, 1)
setup_sensor()
for _ in range(10):
dist = get_distance()
print(f"๐ Distance: {dist} cm")
time.sleep(0.5)
GPIO.cleanup()
9
๐ง
โพ
We combine the sensor with our motor functions to create basic
autonomous obstacle avoidance. If the car detects something closer than
20 cm, it stops, backs up, turns, and then continues forward.
- Threshold: 20 cm โ closer than this = obstacle!
- When blocked: stop โ back up 0.5 s โ turn right 0.6 s โ go again
- This loop runs forever until you press Ctrl+C
Experiment with the 20 cm threshold and the backup/turn timing to make your car smarter. Every robot needs tuning!
๐ Python Concept โ
This is Python's error handling system โ and it's essential for hardware!
Code inside try / except / finallytry: runs normally. When you press Ctrl+C, Python raises
a KeyboardInterrupt โ the except KeyboardInterrupt: block catches it
gracefully, showing a friendly message instead of a scary red error.
The finally: block always runs โ even if an unexpected crash occurs.
That's exactly why GPIO.cleanup() lives there: your motors will never
get stuck running even if your code crashes halfway through!
# โโ Step 9: Obstacle Avoidance โโโโโโโโโโโโโโโโโโโโโโโโ
import RPi.GPIO as GPIO
import time
# Motor pins
MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_A_EN = 17, 18, 12
MOTOR_B_IN3, MOTOR_B_IN4, MOTOR_B_EN = 22, 23, 13
# Sensor pins
TRIG, ECHO = 24, 25
SAFE_DISTANCE = 20 # cm โ stop if closer than this
# ... paste setup(), setup_sensor(), get_distance(),
# move_forward(), move_backward(), turn_right(), stop() ...
def avoid_obstacles(pwm_a, pwm_b):
print("๐ค Autonomous mode ON! Press Ctrl+C to stop.\n")
while True:
dist = get_distance()
print(f"๐ {dist} cm", end="\r")
if dist > SAFE_DISTANCE:
move_forward(pwm_a, pwm_b, speed=65) # All clear!
else:
print(f"\n๐ง Obstacle at {dist} cm! Avoiding...")
stop(pwm_a, pwm_b); time.sleep(0.2)
move_backward(pwm_a, pwm_b); time.sleep(0.5)
turn_right(pwm_a, pwm_b); time.sleep(0.6)
stop(pwm_a, pwm_b); time.sleep(0.1)
time.sleep(0.05)
pwm_a, pwm_b = setup()
setup_sensor()
try:
avoid_obstacles(pwm_a, pwm_b)
except KeyboardInterrupt:
print("\n๐ Stopped by user.")
finally:
GPIO.cleanup()
10
๐
โพ
This is the complete, production-ready RC car script. It combines all
previous steps into one clean file with:
- Full motor setup with PWM speed control
- All four movement directions + stop
- Ultrasonic obstacle detection
- Interactive menu: Manual (keyboard) or Auto (avoid obstacles)
- Safe GPIO cleanup on exit โ no matter what
This is your starting point โ now customize it! Add a buzzer, LED headlights, a camera, or even Wi-Fi control. The sky's the limit! ๐
๐ Python Concept โ
This is one of Python's most important and most-Googled patterns! When Python runs a file
directly, it sets a special variable if __name__ == "__main__"__name__ to "__main__".
If another file imports your code instead, __name__ becomes the filename.
The code inside this if block only runs when you run this file
directly โ not when someone imports your functions into a bigger project.
It's what separates a reusable module from a runnable script, and all professional
Python projects use this pattern. You've just graduated to writing professional-style code! ๐
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# ๐ค COMPLETE RC CAR โ rc_car_complete.py
# Built with Python + Raspberry Pi + L298N + HC-SR04
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
import RPi.GPIO as GPIO
import keyboard
import time
# โโ Pin Configuration โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
MOTOR_A_IN1, MOTOR_A_IN2, MOTOR_A_EN = 17, 18, 12
MOTOR_B_IN3, MOTOR_B_IN4, MOTOR_B_EN = 22, 23, 13
TRIG, ECHO = 24, 25
SAFE_DIST = 20 # cm
# โโ Setup โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def setup():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
for p in [MOTOR_A_IN1,MOTOR_A_IN2,MOTOR_A_EN,
MOTOR_B_IN3,MOTOR_B_IN4,MOTOR_B_EN]:
GPIO.setup(p, GPIO.OUT)
GPIO.setup(TRIG, GPIO.OUT)
GPIO.setup(ECHO, GPIO.IN)
GPIO.output(TRIG, GPIO.LOW)
time.sleep(0.5)
pwm_a = GPIO.PWM(MOTOR_A_EN, 100); pwm_a.start(0)
pwm_b = GPIO.PWM(MOTOR_B_EN, 100); pwm_b.start(0)
return pwm_a, pwm_b
# โโ Motor Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def _drive(a1, a2, b1, b2, pwm_a, pwm_b, spd):
GPIO.output(MOTOR_A_IN1, a1); GPIO.output(MOTOR_A_IN2, a2)
GPIO.output(MOTOR_B_IN3, b1); GPIO.output(MOTOR_B_IN4, b2)
pwm_a.ChangeDutyCycle(spd); pwm_b.ChangeDutyCycle(spd)
def forward(p, q, s=70): _drive(1,0,1,0,p,q,s)
def backward(p, q, s=70): _drive(0,1,0,1,p,q,s)
def left(p, q, s=55): _drive(0,1,1,0,p,q,s)
def right(p, q, s=55): _drive(1,0,0,1,p,q,s)
def stop(p, q): _drive(0,0,0,0,p,q,0)
# โโ Sensor โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def get_distance():
GPIO.output(TRIG, GPIO.HIGH); time.sleep(0.00001)
GPIO.output(TRIG, GPIO.LOW)
t0 = t1 = time.time()
while GPIO.input(ECHO) == 0: t0 = time.time()
while GPIO.input(ECHO) == 1: t1 = time.time()
return round((t1 - t0) * 17150, 1)
# โโ Manual Mode โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def manual_mode(p, q):
print("๐ฎ MANUAL MODE โ Arrow keys to drive, Q to quit")
while True:
if keyboard.is_pressed('up'): forward(p, q)
elif keyboard.is_pressed('down'): backward(p, q)
elif keyboard.is_pressed('left'): left(p, q)
elif keyboard.is_pressed('right'): right(p, q)
elif keyboard.is_pressed('q'): stop(p, q); break
else: stop(p, q)
time.sleep(0.1)
# โโ Auto Mode โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def auto_mode(p, q):
print("๐ค AUTO MODE โ Ctrl+C to quit")
while True:
d = get_distance()
if d > SAFE_DIST:
forward(p, q, 65)
else:
print(f"๐ง Obstacle at {d}cm!")
stop(p,q); time.sleep(0.2)
backward(p,q); time.sleep(0.5)
right(p,q); time.sleep(0.6)
time.sleep(0.05)
# โโ Main โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
if __name__ == "__main__":
pwm_a, pwm_b = setup()
print("\n๐ค RC CAR READY!")
print(" [1] Manual (keyboard control)")
print(" [2] Auto (obstacle avoidance)")
choice = input("Choose mode: ").strip()
try:
if choice == "1": manual_mode(pwm_a, pwm_b)
elif choice == "2": auto_mode(pwm_a, pwm_b)
else: print("Invalid choice.")
except KeyboardInterrupt:
print("\n๐ Stopped!")
finally:
GPIO.cleanup()
print("โ
GPIO cleaned up. See you next time!")