RASPBERRY PI | RC RACE CAR (PART 3)
In this stage of the project, you’ll set up your Raspberry Pi so it can act as the brain of your RC car.
The Raspberry Pi will manage network communication, process video from the camera, and send driving commands to the Arduino. A clean, stable setup is crucial for smooth performance and minimal lag later in the project.
During this stage, you will:
Raspberry Pi OS Setup
The Raspberry Pi will manage network communication, process video from the camera, and send driving commands to the Arduino. A clean, stable setup is crucial for smooth performance and minimal lag later in the project.
During this stage, you will:
Raspberry Pi OS Setup
- Recommended OS (Raspberry Pi OS Lite or Desktop)
- Enabling SSH, camera, and serial interface
- Installing pigpio, webrtc-streamer, Flask, and pyserial
- Connecting Pi → Arduino via USB
- Sending test commands (M1500S1500) from Python
- Arduino parsing and outputting PWM to ESC and servo
- Format and meaning of each control message
- Ensuring fast and reliable serial communication
- Drive commands from terminal (python3 drive_test.py)
- Adjusting for correct direction and calibration
- Common serial errors
- How to reset ESC calibration
SECTION 1 | RASPBERRY PI OS SETUP
Download the latest version of Raspberry Pi OS (Bookworm or later) using the Raspberry Pi Imager tool. Choose Raspberry Pi OS (Lite) for the best performance (no desktop interface is needed).
Under “Settings” in the Imager:
1: You can connect a monitor and keyboard for the first boot or log in via SSH from your PC using:
ssh pi@<your_pi_ip_address>
2: After logging in, update your system:
sudo apt update && sudo apt upgrade -y
3: Enable the interfaces required for this project:
sudo raspi-config
In the configuration menu:
4: Reboot your Pi when done:
sudo reboot
5: Installing Required Packages | Install the following Python libraries and tools:
sudo apt install python3-pip python3-serial python3-flask python3-pigpio -y
sudo apt install git libcamera-tools -y
These tools will allow the Pi to:
6: Connecting to the Local Network
For smooth communication and low latency. Connect the Raspberry Pi to your dedicated 5 GHz router or local access point. Check the IP address with: (Note uppercase i)
hostname -I
7: Make sure your Windows PC and Pi are on the same subnet (e.g., both in 192.168.4.x). You can test connectivity with:
ping <pi_ip_address>
If successful, your PC and Pi are ready to exchange data over the network.
8: Setting Up Serial Communication with Arduino Connect your Arduino to the Pi using a USB cable.
ls /dev/tty*
You should see something like /dev/ttyUSB0 or /dev/ttyACM0. The to test communication - on the Pi create a new script:
If you are using Pi OS Lite
nano serial_test.py
If you are using desktop mode, then create the script in Thonny
import serial
ser = serial.Serial('/dev/ttyUSB0', 115200)
ser.write(b'Hello Arduino!\n')
Save and run the script:
python3 serial_test.py
On the Arduino side, open the Arduino IDE’s Serial Monitor (on your PC) or include a line in the Arduino code to print received messages:
if (Serial.available()) {
String msg = Serial.readStringUntil('\n');
Serial.println("Received: " + msg);
}
If you see "Received: Hello Arduino!" printed from the Arduino, the serial communication between the Pi and Arduino is working perfectly
9: Installing Video Streaming Tools (Preview for Later). Although you’ll configure video in Week 3, it’s worth installing the tools now:
sudo apt install webrtc-streamer -y
or alternatively
sudo apt install gstreamer1.0-tools -y
This prepares your Pi for low-latency video streaming from the onboard camera.
Under “Settings” in the Imager:
- Set a hostname (e.g., pi-rc-car)
- Enable SSH for remote access
- Set your Wi-Fi SSID and password
- Create a username and password
- Flash the SD card, insert it into your Raspberry Pi, and power on the device.
1: You can connect a monitor and keyboard for the first boot or log in via SSH from your PC using:
ssh pi@<your_pi_ip_address>
2: After logging in, update your system:
sudo apt update && sudo apt upgrade -y
3: Enable the interfaces required for this project:
sudo raspi-config
In the configuration menu:
- Enable Camera
- Enable SSH
- Enable Serial Port (for Arduino communication)
- Disable Serial Console (so it doesn’t interfere with USB serial)
4: Reboot your Pi when done:
sudo reboot
5: Installing Required Packages | Install the following Python libraries and tools:
sudo apt install python3-pip python3-serial python3-flask python3-pigpio -y
sudo apt install git libcamera-tools -y
These tools will allow the Pi to:
- Communicate with the Arduino (pyserial)
- Control GPIO and PWM if needed (pigpio)
- Stream camera video (libcamera or webrtc-streamer)
- Host a small web server or socket interface (flask)
6: Connecting to the Local Network
For smooth communication and low latency. Connect the Raspberry Pi to your dedicated 5 GHz router or local access point. Check the IP address with: (Note uppercase i)
hostname -I
7: Make sure your Windows PC and Pi are on the same subnet (e.g., both in 192.168.4.x). You can test connectivity with:
ping <pi_ip_address>
If successful, your PC and Pi are ready to exchange data over the network.
8: Setting Up Serial Communication with Arduino Connect your Arduino to the Pi using a USB cable.
- The Arduino runs its own sketch, listening for data through its USB serial interface.
- The Raspberry Pi acts as the “sender,” using Python to transmit messages over that serial link
ls /dev/tty*
You should see something like /dev/ttyUSB0 or /dev/ttyACM0. The to test communication - on the Pi create a new script:
If you are using Pi OS Lite
nano serial_test.py
If you are using desktop mode, then create the script in Thonny
import serial
ser = serial.Serial('/dev/ttyUSB0', 115200)
ser.write(b'Hello Arduino!\n')
Save and run the script:
python3 serial_test.py
On the Arduino side, open the Arduino IDE’s Serial Monitor (on your PC) or include a line in the Arduino code to print received messages:
if (Serial.available()) {
String msg = Serial.readStringUntil('\n');
Serial.println("Received: " + msg);
}
If you see "Received: Hello Arduino!" printed from the Arduino, the serial communication between the Pi and Arduino is working perfectly
9: Installing Video Streaming Tools (Preview for Later). Although you’ll configure video in Week 3, it’s worth installing the tools now:
sudo apt install webrtc-streamer -y
or alternatively
sudo apt install gstreamer1.0-tools -y
This prepares your Pi for low-latency video streaming from the onboard camera.
Reflection and Logbook Tasks
After completing this setup, record the following in your engineering logbook:
After completing this setup, record the following in your engineering logbook:
- What interfaces did you enable on the Raspberry Pi and why?
- What is the purpose of SSH and how does it improve your workflow?
- Why is it better to use a local Wi-Fi network instead of the school’s shared network?
- What Python libraries were installed, and what will each be used for later?
SECTION 2 | SERIAL COMMUNICATION WITH ARDUINO
Now that your Raspberry Pi is configured and can recognise the Arduino, it’s time to create a reliable serial communication link between them. This connection allows the Raspberry Pi to send driving commands such as speed and steering to the Arduino, which will then output precise PWM signals to control the car’s motor and steering servo.
The serial connection forms the main bridge between software and hardware.
This separation keeps the system stable: the Pi focuses on logic and networking, while the Arduino ensures smooth, accurate signal timing.
To keep the communication simple and consistent, we’ll send messages as short strings that include both motor and steering values. Example message: M1700S1500
Where:
Each message ends with a newline \n, so the Arduino knows when a full command has been received.
1. Arduino Code – Serial Listener
Upload the following sketch to your Arduino using the Arduino IDE. It will read messages from the Raspberry Pi and control the ESC and servo accordingly.
The serial connection forms the main bridge between software and hardware.
- The Raspberry Pi runs Python code that interprets your control inputs (from the PC or network).
- The Arduino runs a program that listens for incoming serial data, extracts PWM values, and updates the ESC and servo in real time.
This separation keeps the system stable: the Pi focuses on logic and networking, while the Arduino ensures smooth, accurate signal timing.
To keep the communication simple and consistent, we’ll send messages as short strings that include both motor and steering values. Example message: M1700S1500
Where:
- M1700 → Motor PWM = 1700 µs (forward)
- S1500 → Steering PWM = 1500 µs (centered)
Each message ends with a newline \n, so the Arduino knows when a full command has been received.
1. Arduino Code – Serial Listener
Upload the following sketch to your Arduino using the Arduino IDE. It will read messages from the Raspberry Pi and control the ESC and servo accordingly.
C++ CODE TO CONTROL THE ESC(ELECTRONIC SPEED CONTROL) AND SERVO
This program continuously listens for new data from the Pi and updates the motor and steering in real time. The Arduino also prints back what it receives — useful for debugging and confirming communication.
2: Raspberry Pi Code – Sending Commands
On your Raspberry Pi, create a Python script (e.g., send_command.py) to send commands to the Arduino. In the terminal:
nano send_command.py
Or use Thonny
2: Raspberry Pi Code – Sending Commands
On your Raspberry Pi, create a Python script (e.g., send_command.py) to send commands to the Arduino. In the terminal:
nano send_command.py
Or use Thonny
PYTHON CODE TO SEND COMMANDS TO ARDUINO
In the terminal run it with:
python3 send_command.py
You should see the Arduino printing the received values to its serial monitor or the Pi terminal if you connect both serials.
3. Testing and common issues
send_command(1600, 1500)
send_command(1800, 1500)
If you see smooth responses with minimal delay, your serial communication is functioning correctly.
Common Issues and Fixes
Key Concepts Explained
Reflection and Logbook Tasks
After completing this stage, answer these questions in your engineering logbook:
python3 send_command.py
You should see the Arduino printing the received values to its serial monitor or the Pi terminal if you connect both serials.
3. Testing and common issues
- Watch the Arduino Serial Monitor — it should display the incoming values.
- The motor and steering servo should respond to the PWM changes.
- Adjust the numbers gradually (e.g., 1600, 1700, 1800) to test different speeds and angles.
send_command(1600, 1500)
send_command(1800, 1500)
If you see smooth responses with minimal delay, your serial communication is functioning correctly.
Common Issues and Fixes
- Arduino not responding | Wrong serial port - Use ls /dev/tty* to find the correct one
- Strange characters appearing | Baud rate mismatch - Ensure both are set to 115200
- Motor not moving | ESC not calibrated or armed - Recalibrate ESC as before
- Servo jittering | Missing common ground - Connect all GND pins together
Key Concepts Explained
- Baud Rate (115200) | The speed of communication between Pi and Arduino (bits per second). Both must match.
- Message Format | M and S are labels so the Arduino knows which number is for which function.
- Pulse Width Range | 1000–2000 µs typically represents full reverse → neutral → full forward for both motor and steering.
- Serial Commands | The Pi sends one line at a time, and the Arduino waits for the newline \n before acting.
Reflection and Logbook Tasks
After completing this stage, answer these questions in your engineering logbook:
- Why is serial communication used instead of GPIO pins for sending control data?
- What are the advantages of using short text-based messages (e.g., “M1700S1500”)?
- How can you verify that messages are being received correctly?
- How would you modify the system to also include a camera servo or sensor data from the Arduino?
SECTION 3 | BUILDING THE COMMAND PROTOCOL
Now that your Raspberry Pi and Arduino can communicate, it’s time to define a clear command protocol — a set of simple rules for how data is formatted, sent, and understood between devices. Just like human language, both sides (the Pi and Arduino) must follow the same “grammar” to ensure every command is correctly interpreted.
A command protocol is a structured message format that allows two systems to exchange information reliably. For this project, our protocol will carry:
It must be:
1: Designing the Message Format
We’ll continue using short text-based messages, but now include an optional third control and command identifiers. Example message:
M1700S1500C1600
Where:
Each message ends with a newline (\n) so the Arduino knows when it’s complete. You could also extend this format to include other parameters later, like: M1500S1500C1600L1 where L1 might turn on an LED or trigger another function.
2: Updating the Arduino Code
Modify your previous Arduino sketch to handle the optional camera servo. This version still controls the ESC and steering, but now also accepts and processes camera movement commands.
A command protocol is a structured message format that allows two systems to exchange information reliably. For this project, our protocol will carry:
- Motor speed (for the ESC)
- Steering angle (for the servo)
- Camera pan position (for the camera servo)
- Special commands (e.g. emergency stop)
It must be:
- Simple – easy to read and debug
- Consistent – same structure every time
- Expandable – support future features (like lights or sensors)
1: Designing the Message Format
We’ll continue using short text-based messages, but now include an optional third control and command identifiers. Example message:
M1700S1500C1600
Where:
- M → Motor PWM value
- S → Steering PWM value
- C → Camera servo PWM value
Each message ends with a newline (\n) so the Arduino knows when it’s complete. You could also extend this format to include other parameters later, like: M1500S1500C1600L1 where L1 might turn on an LED or trigger another function.
2: Updating the Arduino Code
Modify your previous Arduino sketch to handle the optional camera servo. This version still controls the ESC and steering, but now also accepts and processes camera movement commands.
ARDUINO CODE TO PROCESS MOTORS AND CAMERA MOVEMENT COMMANDS
4: Sending Commands from the Raspberry Pi
Now update the Raspberry Pi Python script to include the optional camera control:
Now update the Raspberry Pi Python script to include the optional camera control:
PYTHON CODE WITH ADDED CAMERA CONTROL
5: Adding Safety and Special Command
You can extend your protocol to include non-PWM control signals — for example, an emergency stop command. Example special message: VSTOP
Add a simple check in your Arduino code:
if (command.startsWith("STOP")) {
esc.writeMicroseconds(1500); // Stop motor
steering.writeMicroseconds(1500);
Serial.println("Emergency Stop Activated");
}
6: Reflection and logbook
A well-designed protocol makes your system:
Add to your logbook
A: Why is it important that both the Raspberry Pi and Arduino agree on the same message format?
B: How could you extend this protocol to control more features in the future?
C: What is the advantage of using letters (M, S, C) instead of relying on position or spacing?
D: How could you add a safety or reset command to the protocol?
You can extend your protocol to include non-PWM control signals — for example, an emergency stop command. Example special message: VSTOP
Add a simple check in your Arduino code:
if (command.startsWith("STOP")) {
esc.writeMicroseconds(1500); // Stop motor
steering.writeMicroseconds(1500);
Serial.println("Emergency Stop Activated");
}
6: Reflection and logbook
A well-designed protocol makes your system:
- Readable – you can see exactly what each command means
- Reliable – each message follows the same predictable structure
- Expandable – easy to add new features later (camera tilt, headlights, sensors)
- Debuggable – serial monitor output clearly shows what’s being received
Add to your logbook
A: Why is it important that both the Raspberry Pi and Arduino agree on the same message format?
B: How could you extend this protocol to control more features in the future?
C: What is the advantage of using letters (M, S, C) instead of relying on position or spacing?
D: How could you add a safety or reset command to the protocol?
SECTION 4 | TESTING MOTOT CONTROL
Now that your Raspberry Pi and Arduino can exchange commands using the new protocol, it’s time to bring your RC car to life. In this section, you’ll test the full control chain:
Raspberry Pi → Arduino → ESC & Servo → RC Car.
This test will confirm that your car’s drivetrain and steering respond smoothly to commands sent from the Pi, preparing the system for wireless control and video streaming.
Powering Up the System:
This indicates that it’s powered and ready to receive PWM signals.
1: Running a Simple Python Test Script
Create and run this Python script on your Raspberry Pi to verify the control link. Save it as motor_test.py.
Raspberry Pi → Arduino → ESC & Servo → RC Car.
This test will confirm that your car’s drivetrain and steering respond smoothly to commands sent from the Pi, preparing the system for wireless control and video streaming.
- Send motor and steering commands from the Raspberry Pi to the Arduino.
- Observe how the Electronic Speed Controller (ESC) and steering servo respond.
- Verify that the car moves correctly and safely under software control.
- By the end of this step, you’ll have complete basic control of your RC car via code.
Powering Up the System:
- Make sure the wheels are off the ground
- Connect the LiPo battery to the ESC.
- Power the Raspberry Pi (from a UBEC or power bank).
- Connect the Arduino to the Pi via USB.
- Wait a few seconds — you should hear the ESC’s startup tones.
This indicates that it’s powered and ready to receive PWM signals.
1: Running a Simple Python Test Script
Create and run this Python script on your Raspberry Pi to verify the control link. Save it as motor_test.py.
PYTHON SCRIPT TO TEST THE MOTORS
2: What Should Happen
When you run the script:
If all of this works, you’ve achieved end-to-end communication and control — your Raspberry Pi is now successfully driving the RC car’s systems through the Arduino.
Troubleshooting
3: Smooth Acceleration (Optional)
This version builds on your previous motor control script but focuses specifically on gradual acceleration and deceleration — preventing jerky movement and teaching students how to use loops, parameters, and timing to create smoother control. Save this on the Raspberry Pi as smooth_acceleration.py.
When you run the script:
- The ESC should beep once and the motor may twitch slightly when it first arms.
- During the acceleration loop, the motor should spin smoothly faster, then slow back down.
- The steering servo should turn left, right, and then return to centre.
- You should see each command printed in the terminal as it’s sent.
If all of this works, you’ve achieved end-to-end communication and control — your Raspberry Pi is now successfully driving the RC car’s systems through the Arduino.
Troubleshooting
- Motor doesn’t spin | ESC not armed or needs calibration - Re-run ESC calibration process
- Servo doesn’t move | Wrong pin or missing ground - Check wiring and common ground
- Arduino not responding | Incorrect serial port or baud rate - Run ls /dev/tty* and confirm correct port
- Motor suddenly reverses | PWM values out of range - Keep values between 1000–2000 µs
- Laggy or inconsistent response | Excessive delays or power issue - Ensure Pi and Arduino have stable 5 V supply
3: Smooth Acceleration (Optional)
This version builds on your previous motor control script but focuses specifically on gradual acceleration and deceleration — preventing jerky movement and teaching students how to use loops, parameters, and timing to create smoother control. Save this on the Raspberry Pi as smooth_acceleration.py.
PYTHON CODE WITH SMOOTH ACCELERATION
What This Code Does
In the main sequence, it:
4: Experiment!
try:
- Connects to the Arduino and waits for it to initialise.
- Defines a send_command() function to transmit the same "M####S####" message format you built earlier.
- Defines a ramp_to_speed() function that loops between start and end PWM values in gradual steps.
In the main sequence, it:
- Starts neutral
- Accelerates slowly
- Holds speed briefly
- Decelerates and reverses slightly
- Returns to neutral
4: Experiment!
try:
- Changing step to 5 for smoother transitions.
- Increasing delay to 0.1 s for slower ramps.
- Adjusting the end value (e.g. 1750 or 1800) to test faster speeds.
- Adding steering values inside the ramp to simulate gentle turning