IoTLabs

Nghiên cứu, Sáng tạo và Thử nghiệm

Series 37 Module Cảm Biến – Nguyên Lý Hoạt Động HC-SR04: Sóng Siêu Âm 40kHz, Thời Gian Bay & Đo Khoảng Cách Chính Xác

HC-SR04 là cảm biến khoảng cách siêu âm phổ biến nhất trong hobbyist và giáo dục. Nguyên lý đơn giản: phát sóng âm, đo thời gian sóng đi-về. Nhưng có nhiều chi tiết quan trọng về vùng mù, góc quét, nhiễu đa phản xạ, và hạn chế khi dùng với ESP32 cần nắm rõ trước khi dùng thực tế.

Nguyên Lý Hoạt Động

1. Bộ Phát Siêu Âm Piezoelectric

HC-SR04 gồm 2 “mắt” tròn:

  • T (Transmitter): Phát sóng siêu âm 40kHz
  • R (Receiver): Nhận sóng phản xạ

Cả hai đều là piezoelectric transducer — vật liệu áp điện (thường là PZT — lead zirconate titanate) biến đổi qua lại giữa điện áp và dao động cơ học.

Piezo Transmitter:
Điện áp xung 40kHz → Màng piezo dao động 40,000 lần/giây
                    → Phát sóng âm 40kHz vào không khí

Piezo Receiver:
Sóng âm 40kHz chạm vào màng → Màng dao động
                              → Tạo ra điện áp nhỏ
                              → Mạch khuếch đại → Báo hiệu ECHO

Tại sao 40kHz? Tần số này nằm trên ngưỡng nghe của con người (>20kHz = siêu âm), giảm nhiễu từ môi trường, và có bước sóng ~8.5mm (phù hợp phát hiện vật thể thông dụng).

2. Quy Trình Đo — Trigger và Echo

Chuỗi sự kiện chính xác:

Bước 1: MCU gửi Trigger
   Trig pin ────┐ 10µs HIGH ┐──── LOW
                └───────────┘

Bước 2: HC-SR04 tự động phát 8 xung siêu âm 40kHz (~200µs)
   Transmitter: ┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐ (8 burst)

Bước 3: Sóng truyền qua không khí → chạm vật thể → phản xạ

Bước 4: Receiver nhận sóng phản xạ → kéo Echo lên HIGH
   Echo pin ─────────────┌─────────────┐──── LOW
                          ↑             ↑
                    Gửi xung      Nhận phản xạ
                    (8 burst)
                          └─── t_flight ─────┘

Bước 5: MCU đo thời gian Echo HIGH = t_flight (µs)

Công thức khoảng cách:

Khoảng cách (cm) = t_flight (µs) × tốc độ âm / 2

Tốc độ âm ở 20°C = 343 m/s = 34300 cm/s = 0.0343 cm/µs

→ Khoảng cách (cm) = t_flight × 0.0343 / 2
                    = t_flight / 58.3
                    ≈ t_flight / 58  (thường dùng gần đúng này)

Hoặc: Khoảng cách (mm) = t_flight / 5.82 ≈ t_flight / 6

Ví dụ:

  • Vật cách 50cm → sóng đi 50cm + về 50cm = 100cm
  • t_flight = 100cm / 0.0343 cm/µs = 2915µs
  • Tính ngược: 2915 / 58 = 50.26 cm ✓

3. Ảnh Hưởng Của Nhiệt Độ Đến Kết Quả

Tốc độ âm thay đổi theo nhiệt độ:

v = 331.3 × √(1 + T/273.15) m/s

T = 0°C  → v = 331.3 m/s
T = 20°C → v = 343.2 m/s
T = 35°C → v = 351.8 m/s (sai ~2.5% so với 20°C)

Nếu dùng hằng số 343 m/s ở nhiệt độ thực 35°C → khoảng cách sai ~2.5%. Với ứng dụng thông thường (<1cm sai số chấp nhận được), bỏ qua bù nhiệt. Với ứng dụng đo chính xác → kết hợp DHT22 để lấy nhiệt độ thực tế → bù vào công thức.

4. Vùng Mù và Giới Hạn

Vùng mù (Blind Zone): HC-SR04 không đo được vật < 2cm. Ngay sau khi phát trigger, Transmitter cần vài µs để dừng rung, Receiver cần thêm vài µs để ổn định. Trong thời gian này, Echo luôn LOW bất kể vật ở đâu.

Góc beam ~15°: HC-SR04 phát sóng thành hình nón với góc ±15° (tổng 30°). Vật thể nhỏ ở góc cạnh của beam có thể bị phát hiện dù không nằm thẳng trước sensor.

         15°
          ╱ ╲
         ╱   ╲
       HC-SR04      ← Nếu có tường ở góc → phản xạ giả
         ╲   ╱
          ╲ ╱
         15°

Nhiễu đa phản xạ: Trong phòng nhỏ hoặc góc hẹp, sóng có thể phản xạ nhiều lần → Echo nhận tín hiệu giả → khoảng cách đọc sai (thường ngắn hơn thực tế).

Bề mặt hấp thu âm: Vải, xốp, vật liệu mềm hấp thu sóng siêu âm → Echo yếu → có thể không phát hiện được.

Thông Số Kỹ Thuật

Thông sốGiá trị
Điện áp hoạt động5V DC (HC-SR04) / 3.3V – 5V (HC-SR04P)
Dòng tiêu thụ~15mA
Tần số siêu âm40kHz
Góc quét hiệu quả≤15°
Khoảng cách đo2cm – 400cm
Độ chính xác (lý thuyết)±3mm
Trigger pulse10µs HIGH minimum
Khoảng cách tối thiểu giữa 2 lần đo≥60ms
Kích thước45mm × 20mm × 15mm

Sơ Đồ Chân (Pinout)

        [T]    [R]
     ┌──────────────────┐
     │  HC-SR04         │
     │  Transmitter     │
     │  Receiver        │
     └──────────────────┘
     VCC  Trig  Echo  GND
ChânChức năng
VCC5V
TrigTrigger Input — MCU gửi 10µs pulse để kích hoạt
EchoEcho Output — HIGH trong thời gian t_flight
GNDĐất

Phân Biệt HC-SR04 Và Các Biến Thể

ModelĐiện ápĐặc điểm
HC-SR045V onlyPhổ biến, rẻ nhất
HC-SR04P3.3V – 5VTương thích ESP32 trực tiếp
JSN-SR04T5VChống nước (waterproof), đầu đo rời
US-1005VCó UART mode + bù nhiệt độ
HY-SRF055V5 chân, mode thêm
VL53L0X3.3VLaser ToF, 2cm–200cm, chính xác hơn

HC-SR04 vs HC-SR04P: Chỉ khác mức điện áp. Nếu dùng ESP32, nên chọn HC-SR04P để tránh cần level shifter trên Echo pin.

HC-SR04 với ESP32 3.3V:

  • VCC cấp 5V (OK)
  • Trig: ESP32 GPIO HIGH = 3.3V → HC-SR04 nhận Trig tốt (đủ ngưỡng)
  • Echo: HC-SR04 output 5V HIGH → nguy hiểm cho GPIO ESP32 (max 3.6V)
  • Cần voltage divider hoặc logic level shifter trên Echo pin khi dùng HC-SR04 với ESP32

HC-SR04P với ESP32: Echo output = nguồn cấp → nếu cấp 3.3V thì Echo 3.3V → an toàn.

Kết Nối Phần Cứng

Kết Nối HC-SR04P với ESP32 (3.3V — An Toàn Nhất)

ESP32 DevKit V1 (3.3V)     HC-SR04P
────────────────────────   ─────────────
3V3  ──────────────────→   VCC  (3.3V — dùng HC-SR04P, không phải HC-SR04)
GND  ──────────────────→   GND
GPIO5  (Trig) ──────────→  Trig
GPIO18 (Echo) ←──────────  Echo  (output 3.3V — an toàn với ESP32)

Kết Nối HC-SR04 với ESP32 (5V — Cần Voltage Divider)

Nếu chỉ có HC-SR04 thông thường (5V):

ESP32 DevKit V1            HC-SR04 (5V)
────────────────────────   ─────────────
5V   ──────────────────→   VCC  (nguồn 5V — từ VIN hoặc USB)
GND  ──────────────────→   GND
GPIO5  (Trig) ──────────→  Trig  (3.3V out đủ kích hoạt HC-SR04)
                           Echo  → Voltage divider → GPIO18
                                   [1kΩ]
                           Echo ───────┬─── GPIO18
                                       │
                                     [2kΩ]
                                       │
                                      GND
              Echo lên 5V → qua divider (1k+2k) → GPIO nhận 3.3V

Kết Nối HC-SR04 với Arduino Uno (5V — Đơn Giản Nhất)

Arduino Uno (5V)           HC-SR04
────────────────────────   ─────────────
5V   ──────────────────→   VCC
GND  ──────────────────→   GND
Pin 9  (Trig) ──────────→  Trig
Pin 10 (Echo) ←──────────  Echo  (5V logic — Arduino Uno chịu được)

Code Arduino IDE

Code Đơn Giản Không Dùng Library — Arduino Uno

/*
 * HC-SR04 Ultrasonic Distance Sensor — Đo khoảng cách
 * Board: Arduino Uno
 * Kết nối: VCC→5V, GND→GND, Trig→Pin9, Echo→Pin10
 * Không cần library thêm
 */

const int TRIG_PIN = 9;
const int ECHO_PIN = 10;

void setup() {
  Serial.begin(9600);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  Serial.println("=== HC-SR04 - Arduino Uno ===");
}

// Đọc khoảng cách, trả về cm. Trả về -1 nếu ngoài tầm đo.
float readDistance() {
  // Đảm bảo Trig ban đầu là LOW
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);

  // Gửi pulse 10µs lên Trig
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Đo thời gian Echo HIGH (µs)
  // Timeout 23200µs = tương đương 400cm (23200/58 = 400cm)
  long duration = pulseIn(ECHO_PIN, HIGH, 23200);

  if (duration == 0) {
    return -1; // Timeout — vật quá xa hoặc không phát hiện
  }

  // Tính khoảng cách: duration(µs) / 58 ≈ cm
  float distance = duration / 58.0;
  return distance;
}

void loop() {
  float cm = readDistance();

  if (cm < 0) {
    Serial.println("Khoảng cách: Ngoài tầm (>400cm hoặc không phát hiện)");
  } else if (cm < 2) {
    Serial.println("Khoảng cách: Vùng mù (<2cm) — không tin cậy");
  } else {
    Serial.print("Khoảng cách: ");
    Serial.print(cm, 1);
    Serial.println(" cm");
  }

  // Tối thiểu 60ms giữa 2 lần đo để tránh echo cũ nhiễu
  delay(100);
}

Code Với NewPing Library — Arduino Uno (Nhiều Tính Năng Hơn)

Cài library: Tìm NewPing trong Library Manager → tác giả Tim Eckel.

/*
 * HC-SR04 Ultrasonic — Dùng NewPing library
 * Board: Arduino Uno
 * Library: NewPing (Tim Eckel)
 * Kết nối: Trig→Pin9, Echo→Pin10
 *
 * NewPing xử lý: timeout tự động, lọc kết quả không hợp lệ, đơn vị đa dạng
 */

#include <NewPing.h>

const int TRIG_PIN    = 9;
const int ECHO_PIN    = 10;
const int MAX_DIST_CM = 400; // Giới hạn tầm đo tối đa

// Khởi tạo NewPing: (trig, echo, max_distance)
NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DIST_CM);

void setup() {
  Serial.begin(9600);
  Serial.println("=== HC-SR04 + NewPing - Arduino Uno ===");
}

void loop() {
  // Đo 1 lần — trả về khoảng cách cm, 0 nếu ngoài tầm
  unsigned int cm = sonar.ping_cm();

  // Đo median của 5 lần — lọc nhiễu tốt hơn
  unsigned int medianCm = sonar.convert_cm(sonar.ping_median(5));

  if (cm == 0) {
    Serial.println("Ngoài tầm đo");
  } else {
    Serial.print("Đo 1 lần: "); Serial.print(cm); Serial.println(" cm");
    Serial.print("Median 5 lần: "); Serial.print(medianCm); Serial.println(" cm");
  }

  delay(200); // NewPing tự handle 60ms cooldown giữa các ping
}

Code ESP32 — Interrupt-Based (Chính Xác Hơn pulseIn)

/*
 * HC-SR04P Ultrasonic — ESP32 dùng interrupt để đo Echo
 * Board: ESP32 DevKit V1
 * Dùng HC-SR04P (3.3V compatible) hoặc HC-SR04 với voltage divider trên Echo
 * Kết nối: Trig→GPIO5, Echo→GPIO18
 *
 * Interrupt approach: chính xác hơn pulseIn() vì không bị interrupt lag
 */

const int TRIG_PIN = 5;
const int ECHO_PIN = 18;

volatile unsigned long echoStartTime = 0;
volatile unsigned long echoDuration  = 0;
volatile bool echoComplete = false;

// Interrupt handler — chạy khi Echo pin thay đổi trạng thái
void IRAM_ATTR echoISR() {
  if (digitalRead(ECHO_PIN) == HIGH) {
    // Echo lên HIGH: ghi timestamp bắt đầu
    echoStartTime = micros();
  } else {
    // Echo xuống LOW: tính duration
    echoDuration = micros() - echoStartTime;
    echoComplete = true;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);

  // Gắn interrupt CHANGE: kích hoạt khi pin thay đổi HIGH↔LOW
  attachInterrupt(digitalPinToInterrupt(ECHO_PIN), echoISR, CHANGE);

  Serial.println("=== HC-SR04P - ESP32 Interrupt Mode ===");
}

// Gửi trigger, đợi echo, trả về cm. Timeout 500ms.
float readDistance() {
  echoComplete = false;
  echoDuration = 0;

  // Gửi 10µs trigger
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Đợi interrupt hoàn thành (timeout 50ms)
  unsigned long timeout = millis() + 50;
  while (!echoComplete && millis() < timeout) {
    // Chờ — không delay, chỉ yield
    yield(); // Quan trọng với ESP32 — cho phép background tasks chạy
  }

  if (!echoComplete) return -1; // Timeout

  float cm = echoDuration / 58.0;
  if (cm > 400) return -1; // Vượt tầm

  return cm;
}

void loop() {
  float cm = readDistance();

  if (cm < 0) {
    Serial.println("Ngoài tầm");
  } else {
    Serial.printf("Khoảng cách: %.1f cm\n", cm);
  }

  delay(100); // 100ms giữa các lần đo
}

Code ESP32 — pulseIn Đơn Giản (Dễ Đọc Hơn)

/*
 * HC-SR04P Ultrasonic — ESP32 dùng pulseIn (đơn giản)
 * Board: ESP32 DevKit V1
 * Kết nối: VCC→3V3, Trig→GPIO5, Echo→GPIO18, GND→GND
 * (Dùng HC-SR04P — không cần level shifter)
 */

const int TRIG_PIN = 5;
const int ECHO_PIN = 18;

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  Serial.println("=== HC-SR04P - ESP32 ===");
}

float readDistance() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // pulseIn timeout: 23200µs = tương đương 400cm
  long duration = pulseIn(ECHO_PIN, HIGH, 23200);
  if (duration == 0) return -1;

  return duration / 58.0;
}

void loop() {
  float cm = readDistance();

  if (cm < 0) {
    Serial.println("Ngoài tầm");
  } else if (cm < 2) {
    Serial.println("Vùng mù (<2cm)");
  } else {
    Serial.printf("%.1f cm\n", cm);
  }

  delay(100);
}

Kết Quả Mong Đợi

=== HC-SR04P - ESP32 ===
25.3 cm
25.4 cm
25.2 cm
Ngoài tầm
12.7 cm
Vùng mù (<2cm)

Ứng Dụng Thực Tế

Ứng dụngChi tiết
Robot tránh vật cảnĐo khoảng cách trước → rẽ phải nếu <30cm
Đo mức nước trong bồnSensor nhìn xuống từ trên → khoảng cách = mức còn trống
Cửa tự độngPhát hiện người → mở cửa
Hệ thống đỗ xeCảnh báo khoảng cách còn lại khi lùi
Chống trộmCảnh báo khi vật thể đến gần hơn 1m
Đo chiều cao ngườiĐặt trên cao nhìn xuống → chiều cao = khoảng cách sàn – khoảng cách đến đỉnh đầu

Lưu Ý Khi Sử Dụng

1. HC-SR04 thường (5V) nguy hiểm cho GPIO ESP32

Echo pin của HC-SR04 xuất 5V HIGH → vượt ngưỡng an toàn GPIO ESP32 (3.3V max 3.6V). Nếu bắt buộc dùng HC-SR04 với ESP32: thêm voltage divider (1kΩ + 2kΩ) trên Echo. Tốt hơn: dùng HC-SR04P (3.3V tương thích).

2. Tối thiểu 60ms giữa hai lần đo

Sóng âm 40kHz di chuyển 343m/s. Đo lần 1 xong, sóng có thể vẫn đang dội trong phòng. Nếu trigger lần 2 quá sớm → Echo nhận sóng cũ → đọc khoảng cách sai (ngắn hơn thực). Luôn dùng delay ≥ 60ms hoặc ≥100ms.

3. Vùng mù dưới 2cm — kết quả không tin cậy

Không đặt vật thể trong vòng 2cm của sensor. HC-SR04 cần thời gian để Transmitter dừng dao động và Receiver bắt đầu lắng nghe. Khoảng 0-2cm là vùng mù hoàn toàn.

4. Bề mặt hấp thu và góc nghiêng

  • Vật mềm (vải, xốp): hấp thu sóng → có thể không phát hiện
  • Vật nghiêng góc: sóng phản xạ lệch hướng → Receiver không nhận được → timeout
  • Góc tốt nhất: bề mặt vuông góc với beam HC-SR04

5. pulseIn() trên ESP32 có thể bị interrupt lag

Nếu cần độ chính xác cao với ESP32, dùng interrupt-based approach (như code interrupt ở trên). pulseIn() trên ESP32 hoạt động nhưng có thể bị độ trễ 10–50µs khi FreeRTOS interrupt chen vào, tương đương sai số 0.5–2cm.

6. Không dùng trong nước hoặc ngoài trời ẩm

HC-SR04 không chống nước. Nếu cần đo trong điều kiện ẩm hoặc tiếp xúc nước → dùng JSN-SR04T (waterproof, đầu đo bọc nhựa, dây dài).

Bài tiếp theo: