This project pairs an ultrasonic distance sensor with a buzzer to build a simple proximity alarm -- point it at something and get an audible warning that gets more urgent as the object gets closer. It's a good first sensor project because both the sensor and the output are digital and easy to reason about.
What the Controller Does
The controller repeatedly triggers a distance measurement, reads the result, compares it against a threshold, and decides whether to sound the buzzer -- the same read-decide-act loop that underlies most sensor-driven projects, just with a single sensor and a single output.
What the Ultrasonic Sensor Does
The HC-SR04 measures distance by sending a short ultrasonic pulse on its trigger pin and timing how long its echo pin stays high while the pulse travels out and reflects back. Combined with the known speed of sound, that timing converts directly into a distance -- the sensor itself doesn't report a distance value, the controller calculates it from the timing.
What the Buzzer Does
The active buzzer is the simplest possible output here: it makes noise for as long as the controller holds its control pin high, and stays silent otherwise. All the actual warning logic (should it be sounding right now?) lives entirely in the controller's code.
Sensor Voltage Compatibility
Before wiring anything, confirm the sensor's logic voltage against what your controller provides. This is a real, specific gotcha with HC-SR04 boards: some are designed for 5V logic, and using one directly with a 3.3V controller pin without a level shifter or voltage divider can produce unreliable readings or, in some cases, risk the controller's input pin. Check your specific board's documentation rather than assuming.
Software and Library Concepts
Most HC-SR04 libraries handle the trigger/echo timing for you and return a distance value directly, so you don't need to write the pulse-timing math yourself. Confirm you're getting sane, repeatable readings on their own -- before adding the buzzer threshold logic on top.
Testing and Troubleshooting
No distance readings at all -- check the trigger and echo wiring first, and confirm the sensor's logic voltage actually matches what your controller provides on that pin.
Distance always reads near-zero -- the sensor is likely facing something too close, or there's an obstruction right at the sensor face blocking the pulse.
Buzzer never triggers despite valid readings -- check the threshold value and comparison logic in your code before assuming it's a wiring problem; a threshold set below any distance you're actually testing will simply never fire.
Readings jump around unpredictably -- soft or angled surfaces can scatter the ultrasonic pulse and produce inconsistent echoes; test against a flat, hard surface first to confirm the sensor itself is working.
Once a single sensor and buzzer are working reliably, a natural extension is adding a second HC-SR04 pointed in a different direction and giving each zone its own alert tone or pattern, turning a single-direction alarm into basic spatial awareness.
Wiring
HC-SR04 VCC → ESP32-C3 SuperMini 5V — sensor needs 5V, not 3.3V, to drive its ultrasonic transducer
HC-SR04 GND → ESP32-C3 SuperMini GND
HC-SR04 TRIG → GPIO4 (`TRIG_PIN`) — digital output, direct connection is fine (3.3V trigger is well above the sensor's logic threshold)
HC-SR04 ECHO → 1kΩ resistor → GPIO5 (`ECHO_PIN`), with a 2kΩ resistor from that same junction to GND — ECHO is a 5V signal and ESP32 pins are only 3.3V-tolerant, so this divider is required to avoid damaging the pin
Active Buzzer signal/+ → GPIO6 (`BUZZER_PIN`)
Active Buzzer GND → ESP32-C3 SuperMini GND
Build the voltage divider on the breadboard before connecting ECHO to the ESP32 — never wire ECHO straight to a GPIO. Power the HC-SR04 from 5V (USB VBUS) while everything else can share the ESP32-C3's 3.3V rail and common GND. Test with `Serial.println` first to confirm sane distance readings before trusting the buzzer timing.
Wiring Diagram
Starter Code
#include <Arduino.h>
#define TRIG_PIN 4 // HC-SR04 TRIG - any digital output GPIO, adjust to match your wiring
#define ECHO_PIN 5 // HC-SR04 ECHO (via voltage divider) - any digital input GPIO
#define BUZZER_PIN 6 // Active buzzer signal - any digital output GPIO
const int MIN_DISTANCE_CM = 3; // closer than this = fastest beep rate
const int MAX_DISTANCE_CM = 100; // farther than this = no beeping
const int MIN_BEEP_INTERVAL_MS = 60; // fastest beep rate (object very close)
const int MAX_BEEP_INTERVAL_MS = 800; // slowest beep rate (object far away)
unsigned long lastToggle = 0;
bool buzzerOn = false;
int currentInterval = -1; // -1 = silent (nothing in range)
long readDistanceCm() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000); // 30ms timeout, ~5m max range
if (duration == 0) return -1; // no echo received (out of range / no object)
return duration / 58; // speed-of-sound conversion: microseconds -> cm
}
void setup() {
Serial.begin(115200);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
}
void loop() {
long distance = readDistanceCm();
if (distance > 0 && distance <= MAX_DISTANCE_CM) {
long clamped = constrain(distance, MIN_DISTANCE_CM, MAX_DISTANCE_CM);
currentInterval = map(clamped, MIN_DISTANCE_CM, MAX_DISTANCE_CM, MIN_BEEP_INTERVAL_MS, MAX_BEEP_INTERVAL_MS);
Serial.printf("Distance: %ld cm | Beep interval: %d ms\n", distance, currentInterval);
} else {
currentInterval = -1; // no object in range, stay silent
}
unsigned long now = millis();
if (currentInterval < 0) {
digitalWrite(BUZZER_PIN, LOW);
buzzerOn = false;
} else if (now - lastToggle >= (unsigned long)currentInterval) {
buzzerOn = !buzzerOn;
digitalWrite(BUZZER_PIN, buzzerOn ? HIGH : LOW);
lastToggle = now;
}
delay(20); // small pacing delay between HC-SR04 pings
}