A parking sensor is a system that measures the distance to an obstacle and warns of its proximity using light or acoustic signals.
If in the traffic light project we learned to control timing, here we will learn to measure the physical world. We will not simply turn on a light; our Arduino will have to send a signal, wait for it to bounce back, and based on the time it took, calculate the distance to an obstacle with precision.
This project is the perfect introduction to wave physics and the use of Time of Flight sensors, principles used from submarines (sonar) to the most advanced LiDAR sensors.
How We Measure Distance with Sound
For this project we will use the ubiquitous HC-SR04 sensor. This component is, in essence, a small electronic bat.
Its operation is based on echolocation. The sensor has two “eyes” (which are actually transducers):
- Transmitter (Trig): Emits a burst of ultrasound at 40 kHz (inaudible to humans).
- Receiver (Echo): Listens and waits to receive the bounce of that wave.
The physical principle is basic kinematics. We know that speed equals distance divided by time (v = d / t). Since we know the speed of sound in air and can measure time, we solve for distance.
Why do we divide by 2? Crucial! The sound has to go to the obstacle and return to the sensor. The time we measure is for the round trip. If we didn’t divide by 2, we would be calculating double the actual distance.
The speed of sound is approximately 343 m/s (at 20°C).
To make calculations easier for the microcontroller (which works in microseconds,
Components
We are going to build a complete system: measurement (sensor) and warning (buzzer + LEDs).
- [1x|blue] Arduino Uno | The brain of the project
- [1x|orange] Ultrasonic Sensor HC-SR04 | Measures distances with ultrasound
- [1x|purple] Active Buzzer | Acoustic warning
- [1x|green] Green LED | Safe zone
- [1x|yellow] Yellow LED | Caution
- [1x|red] Red LED | Imminent danger
- [3x|gray] 220Ω Resistor | Limits LED current
- [Several|gray] Breadboard and wires | Connections
Connection Diagram
The HC-SR04 has 4 pins: VCC, Trig, Echo, and GND.
HC-SR04 Sensor
The sensor needs 5V power and two signals: Trig to send the pulse, and Echo to measure how long it takes to return.
[ { “from”: “HC-SR04 VCC”, “to”: “Arduino 5V”, “color”: “red” }, { “from”: “HC-SR04 GND”, “to”: “Arduino GND”, “color”: “black” }, { “from”: “HC-SR04 Trig”, “to”: “Pin 9”, “color”: “orange”, “note”: “Arduino Output” }, { “from”: “HC-SR04 Echo”, “to”: “Pin 10”, “color”: “yellow”, “note”: “Arduino Input” } ]
Warning Buzzer
The buzzer activates with proximity beeps.
[ { “from”: “Buzzer (+)”, “to”: “Pin 6”, “color”: “purple” }, { “from”: “Buzzer (-)”, “to”: “Arduino GND”, “color”: “black” } ]
Indicator LEDs
The LEDs act as a visual traffic light for safe distance, caution, and danger.
[ { “from”: “Red LED + 220Ω”, “to”: “Pin 3”, “color”: “red” }, { “from”: “Yellow LED + 220Ω”, “to”: “Pin 4”, “color”: “yellow” }, { “from”: “Green LED + 220Ω”, “to”: “Pin 5”, “color”: “green” }, { “from”: “LED Cathodes”, “to”: “Arduino GND”, “color”: “black” } ]
The Code
Let’s divide the code into two logical parts: the function that measures and the logic that warns.
To measure the pulse, we will use the native pulseIn() function, which waits for a pin to change state and counts how much time has passed in microseconds.
// --- Hardware Configuration ---
const int pinTrig = 9;
const int pinEcho = 10;
const int pinBuzzer = 6;
// Pins for the LEDs (optional, but recommended)
const int pinGreenLed = 5;
const int pinYellowLed = 4;
const int pinRedLed = 3;
// Physical constants
// Speed of sound / 2 = 0.0343 / 2 = 0.01715
const float SPEED_OF_SOUND = 0.01715;
void setup() {
Serial.begin(9600); // For screen debugging
// Pin configuration
pinMode(pinTrig, OUTPUT);
pinMode(pinEcho, INPUT);
pinMode(pinBuzzer, OUTPUT);
pinMode(pinGreenLed, OUTPUT);
pinMode(pinYellowLed, OUTPUT);
pinMode(pinRedLed, OUTPUT);
// Start with Trigger off
digitalWrite(pinTrig, LOW);
}
The Main Loop and Warning Logic
In the loop, we take the measurement. Depending on the distance, we make the buzzer sound at different rates (like in a real car: closer = faster beeps).
void loop() {
// 1. GET DISTANCE
long distance = measureDistance();
// Print for debugging
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
// 2. WARNING LOGIC (The "Beep Beep")
// Define distance thresholds
if (distance < 10) {
// IMMINENT DANGER (< 10cm)
// Continuous beep and red light
digitalWrite(pinBuzzer, HIGH); // Continuous sound
turnOnTrafficLight(true, false, false); // Red only
}
else if (distance < 30) {
// CAUTION (< 30cm)
// Fast beeps and yellow light
intermittentAlert(100); // Fast beep
turnOnTrafficLight(false, true, false); // Yellow only
}
else if (distance < 60) {
// ATTENTION (< 60cm)
// Slow beeps and green light
intermittentAlert(300); // Slow beep
turnOnTrafficLight(false, false, true); // Green only
}
else {
// SAFE ZONE (> 60cm)
digitalWrite(pinBuzzer, LOW); // Silence
turnOffAll();
}
delay(50); // Small pause for stability
}
Helper Functions
Here is where we encapsulate the complexity. The measureDistance function performs the strict sequence required by the HC-SR04 sensor to work.
- Send a 10 microsecond pulse on the Trig pin.
- Listen on the Echo pin.
- Apply the formula.
long measureDistance() {
// 1. Generate trigger pulse
digitalWrite(pinTrig, LOW);
delayMicroseconds(2);
digitalWrite(pinTrig, HIGH);
delayMicroseconds(10); // The pulse must last at least 10us
digitalWrite(pinTrig, LOW);
// 2. Measure the echo time
// pulseIn waits for the pin to go HIGH and counts until it goes LOW
unsigned long duration = pulseIn(pinEcho, HIGH, 30000UL);
// 3. Convert to distance
// If duration is 0, it means no echo was received (out of range)
if (duration == 0) return 999;
return duration * SPEED_OF_SOUND;
}
// Function to make the "Beep-Beep"
void intermittentAlert(int speed) {
digitalWrite(pinBuzzer, HIGH);
delay(speed);
digitalWrite(pinBuzzer, LOW);
delay(speed);
}
// Helper to manage the LEDs cleanly
void turnOnTrafficLight(bool r, bool y, bool g) {
digitalWrite(pinRedLed, r ? HIGH : LOW);
digitalWrite(pinYellowLed, y ? HIGH : LOW);
digitalWrite(pinGreenLed, g ? HIGH : LOW);
}
void turnOffAll() {
digitalWrite(pinRedLed, LOW);
digitalWrite(pinYellowLed, LOW);
digitalWrite(pinGreenLed, LOW);
}
Analysis of Common Problems
You will likely encounter strange readings when you set this up. This is normal; we are working with sound in the real world.
- False echoes: If the sensor points at a soft surface (like cloth or a sponge), the sound doesn’t bounce back well and the sensor “sees” nothing.
- Angles: If the surface is very slanted relative to the sensor, the sound bounces elsewhere and never returns to the receiver (like a billiard ball hitting a rail).
- Code blocking: The
intermittentAlertfunction usesdelay. This means that while it is beeping, it does not measure. In a more reactive setup we would usemillis()to beep and measure without blocking the processor. - Echo timeout: The third parameter of
pulseIn()limits the wait to 30 ms. Without this limit, a measurement without an echo could block the program for a much longer time.
Next Steps
This project is the foundation of mobile robotics. A robot that doesn’t bump into things is basically this code on wheels.
Do you dare improve it?
- Replace the LEDs with an LCD Screen that shows the exact distance in centimeters.
- Use the
tone()function on the buzzer so that the sound becomes higher-pitched the closer the object is, instead of just changing the beep speed. - Try implementing the NewPing library, which greatly optimizes reading and prevents blocking if the sensor doesn’t receive a response.