To create a distance sensor with Arduino, the most reliable and cost-effective approach is pairing an Arduino Uno R3 (ATmega328P) with an HC-SR04 ultrasonic module. The system works by firing a 40kHz acoustic pulse and measuring the time-of-flight for the echo to return. At sea level and 20°C, sound travels at roughly 343 meters per second, meaning the round-trip time is approximately 58.2 microseconds per centimeter. By calculating this pulse width, you can measure distances from 2 cm to 400 cm with a practical accuracy of ±3 mm.

Parts List & Specifications

Before wiring, ensure you have the exact variants listed below. Using a 3.3V board like the Arduino Due or an ESP32 requires a logic level shifter for the Echo pin, which we will address in the wiring section.

ComponentExact VariantEst. PriceKey Specification
MicrocontrollerArduino Uno R3 (ATmega328P)$12 (Clone) / $27 (Official)5V logic, 16MHz clock
SensorHC-SR04 (4-Pin)$2.00 - $4.005V VCC, 15° beam angle, 40kHz
Wiring22 AWG Solid Core Jumper Wires$5.00 (pack)Male-to-Male for breadboard
Prototyping830-Tie Point Solderless Breadboard$6.00Standard 0.1' pitch

Wiring the HC-SR04 to Arduino Uno

The HC-SR04 requires four connections. The Trig (Trigger) pin is an input that receives a 10µs HIGH pulse from the Arduino to initiate measurement. The Echo pin is an output that goes HIGH for the duration of the sound's flight time.

HC-SR04 PinArduino Uno R3 PinWire Color (Standard)Function
VCC5VRedPower supply (4.5V to 5.5V)
TrigDigital Pin 9YellowTrigger pulse input
EchoDigital Pin 10BlueEcho pulse output
GNDGNDBlackCommon ground
⚠️ 3.3V Logic Warning: If you adapt this build for an ESP32 or Arduino Nano 33 IoT, the HC-SR04 Echo pin outputs 5V. Feeding 5V into a 3.3V GPIO will fry the microcontroller. You must use a voltage divider (e.g., 1kΩ and 2kΩ resistors) on the Echo pin to step it down to ~3.3V.

Robust Arduino Code with Error Handling

Most basic tutorials use a simple pulseIn() call that blocks the main loop and returns erratic data when the sound wave scatters. The code below targets the Arduino Uno R3 and implements a median filter to discard acoustic outliers, alongside strict timeout handling to prevent the program from hanging if no echo returns. According to the official Arduino pulseIn() documentation, setting a timeout is critical for preventing infinite blocking.

// Target Board: Arduino Uno R3 (ATmega328P)
// Sensor: HC-SR04 Ultrasonic Module

#define TRIG_PIN 9
#define ECHO_PIN 10
#define MAX_DISTANCE_CM 400
#define TIMEOUT_US 30000 // 30ms timeout prevents infinite blocking
#define SAMPLE_SIZE 5    // Number of readings for median filter

float getMedianDistance();

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  digitalWrite(TRIG_PIN, LOW); // Ensure clean start state
}

void loop() {
  float distance = getMedianDistance();
  
  // Error Handling & State Reporting
  if (distance < 0) {
    Serial.println("Error: Timeout - No echo received (-1.00 cm)");
  } else if (distance < 2.0) {
    Serial.println("Error: Inside blind zone (< 2.00 cm)");
  } else if (distance >= MAX_DISTANCE_CM) {
    Serial.println("Warning: Out of range or soft target (400.00 cm)");
  } else {
    Serial.print("Distance: ");
    Serial.print(distance, 2);
    Serial.println(" cm");
  }
  
  delay(100); // 10Hz sampling rate (prevents 40kHz echo overlap)
}

float getMedianDistance() {
  float samples[SAMPLE_SIZE];
  
  for (int i = 0; i < SAMPLE_SIZE; i++) {
    // Send 10us trigger pulse
    digitalWrite(TRIG_PIN, HIGH);
    delayMicroseconds(10);
    digitalWrite(TRIG_PIN, LOW);
    
    // Read echo with timeout
    long duration = pulseIn(ECHO_PIN, HIGH, TIMEOUT_US);
    
    if (duration == 0) {
      samples[i] = -1.0; // Flag timeout
    } else {
      // Calculate distance: (duration / 2) * speed of sound (0.0343 cm/us)
      samples[i] = duration * 0.01715; 
    }
    delay(10); // Short delay between burst samples
  }
  
  // Simple bubble sort for median extraction
  for (int i = 0; i < SAMPLE_SIZE - 1; i++) {
    for (int j = 0; j < SAMPLE_SIZE - i - 1; j++) {
      if (samples[j] > samples[j+1]) {
        float temp = samples[j];
        samples[j] = samples[j+1];
        samples[j+1] = temp;
      }
    }
  }
  
  return samples[SAMPLE_SIZE / 2]; // Return median value
}

Debugging: Fixing 0 cm and 400 cm Readings

Ultrasonic sensors are notorious for throwing phantom readings. If your serial monitor is misbehaving, here is the exact diagnostic path.

The First Three Things to Check

  1. VCC/GND Swap: The HC-SR04 pinout is VCC-Trig-Echo-GND. Swapping VCC and GND will instantly overheat the onboard MAX232 equivalent chip and permanently kill the sensor. Touch the sensor's silver cans; if they are hot, replace the module.
  2. USB Cable Voltage Drop: Cheap, thin USB cables suffer from voltage drop. If the Arduino's 5V rail dips below 4.5V under load, the HC-SR04's internal oscillator fails to generate the 40kHz burst. Measure the 5V pin with a multimeter; it must read >4.8V.
  3. Trig/Echo Cross-Wiring: If you get a constant 0 cm or -1.00 cm, you likely have the Trig and Echo pins reversed in your physical wiring or your #define statements.

Ranked Causes for Specific Error Strings

Error String: Error: Timeout - No echo received (-1.00 cm)

  • Cause 1: Target is completely out of range (>4 meters) or angled away, causing the 15° beam to scatter.
  • Cause 2: Echo pin is disconnected or wired to a non-PWM/digital pin.
  • Cause 3: The target material is highly sound-absorbent (e.g., thick foam, heavy curtains).

Error String: Warning: Out of range or soft target (400.00 cm)

  • Cause 1: The sensor is picking up secondary reflections (multipath interference) from a nearby wall, causing the pulse to travel further than expected before returning.
  • Cause 2: You are polling the sensor too fast. If you trigger a new measurement before the previous 40kHz burst has dissipated, the sensor reads the 'ghost' of the last ping. The 100ms delay in the code above prevents this.

Extending and Simplifying the Build

The HC-SR04 is a bench-top prototyping part. It is not sealed against dust or moisture, and its exposed silver mesh degrades in high humidity. Here is how to adapt the architecture for real-world environments.

Extending: Waterproof & Industrial Applications

If you need to measure water tank levels or outdoor vehicle proximity, swap the HC-SR04 for the JSN-SR04T. It uses a sealed, waterproof transducer on a 2.5-meter cable. The electrical protocol is identical to the HC-SR04, meaning the code above works without modification. However, the JSN-SR04T has a larger blind zone (approx. 20 cm) and a narrower beam angle, requiring more precise aiming. For detailed acoustic transducer characteristics, refer to Adafruit's ultrasonic sensor overview.

Simplifying: UART and I2C Alternatives

If you want to eliminate the pulseIn() blocking issue entirely and free up GPIO pins, use the A02YYUW waterproof sensor. It outputs distance data via a 9600-baud UART serial stream, meaning you just read bytes from a HardwareSerial port. Alternatively, if you need to mount five sensors around a robot chassis without 40kHz crosstalk, use an I2C multiplexer (like the TCA9548A) paired with I2C-enabled ultrasonic modules like the Grove Ultrasonic Ranger, which handles the pulse timing on its own internal MCU.

Frequently Asked Questions

How to create a distance sensor with Arduino for water level monitoring?

Do not use the standard HC-SR04 for water tanks; the humidity will corrode the mesh and cause short circuits. Use the waterproof JSN-SR04T or A02YYUW. Mount the sensor at the top of the tank pointing straight down. To calculate the water volume, subtract the sensor's distance reading from the total height of the tank, then multiply by the tank's cross-sectional area in your Arduino code.

How to create a distance sensor with Arduino using multiple HC-SR04 modules?

Running multiple HC-SR04 modules simultaneously causes 'crosstalk'—Sensor A hears the echo from Sensor B's trigger. To fix this, you must poll them sequentially with a minimum 50ms delay between each sensor's trigger pulse. Alternatively, wire all Trig pins to separate GPIOs, but tie all Echo pins to a single GPIO through diodes, firing one Trig pin at a time.

How to create a distance sensor with Arduino and display on an LCD?

Connect a standard 16x2 I2C LCD to the Arduino's A4 (SDA) and A5 (SCL) pins. Include the LiquidCrystal_I2C library. In your loop(), after calculating the median distance, use lcd.setCursor(0, 1) and lcd.print(distance) to push the live data to the screen. Ensure you clear the previous line with spaces to prevent ghost characters when the distance drops from 100 cm to 9 cm.

How to create a distance sensor with Arduino that works in the dark?

Ultrasonic sensors rely on mechanical sound waves, not light. The HC-SR04 will work perfectly in pitch black, heavy fog, or direct sunlight. Unlike Infrared (IR) time-of-flight sensors (like the VL53L0X), ultrasonic modules are completely immune to ambient light interference, making them superior for dark environments, though they struggle with sound-absorbing materials that IR can easily detect.