Cảm biến siêu âm HC-SR04: nguyên lý, sai số theo nhiệt độ, nối ESP32 3,3 V và code đo khoảng cách

Cảm biến siêu âm HC-SR04 đo khoảng cách thế nào, nối với ESP32 3,3 V qua cầu chia ở chân Echo, sai số theo nhiệt độ và cách bù, chương trình đo có lọc trung vị, khi nào cảm biến đo sai.

Cảm biến siêu âm HC-SR04 là module đo khoảng cách rẻ và phổ biến nhất cho Arduino, ESP32: đo được từ khoảng 2 cm đến 4 m, dùng cho xe tránh vật cản, đo mực nước, phát hiện người đến gần. Nhưng HC-SR04 chạy 5 V còn ESP32 là 3,3 V, và số đo lệch vài phần trăm nếu bỏ qua nhiệt độ không khí. Bài này giải thích HC-SR04 đo bằng cách nào, cách nối an toàn với ESP32, vì sao phải bù nhiệt độ, chương trình đo có lọc trung vị, và những trường hợp cảm biến đo sai.

1. Thông số HC-SR04

Thông sốGiá trị
Nguồn5 V, dòng khoảng 15 mA
Tần số siêu âm40 kHz
Khoảng đoKhoảng 2 cm đến 400 cm (vật phẳng, đặt thẳng góc)
Góc đoKhoảng 15° (hình nón hẹp)
Độ phân giảiKhoảng 0,3 cm
ChânVCC, Trig (vào), Echo (ra, mức 5 V), GND

Trên module có hai đầu dò hình trụ: một đầu phát (thường ghi T) và một đầu thu (R). Có các bản tương tự chạy được ở 3,3 V như HC-SR04P, RCWL-1601: với các bản này, chân Echo ra 3,3 V và nối thẳng ESP32 được. Đọc chữ in trên mạch để biết đang có bản nào.

2. HC-SR04 đo khoảng cách thế nào

Giản đồ thời gian HC-SR04: xung Trig từ ESP32 dài ít nhất 10 micro giây, module phát 8 chu kỳ sóng 40 kHz, chân Echo lên mức cao trong khoảng thời gian bằng thời gian âm đi và về
Xung Trig, chùm sóng 40 kHz và xung Echo (hình minh họa, không theo tỉ lệ thời gian).
  1. Vi điều khiển đưa chân Trig lên mức cao ít nhất 10 µs.
  2. Module phát một chùm 8 chu kỳ sóng siêu âm 40 kHz rồi đưa chân Echo lên mức cao.
  3. Khi đầu thu nghe tiếng vọng dội về từ vật, chân Echo xuống mức thấp. Không có tiếng vọng thì Echo tự xuống sau khoảng 38 ms.

Độ rộng xung Echo là thời gian âm đi tới vật và quay về, nên:

khoảng cách = thời gian Echo × tốc độ âm / 2

Ví dụ Echo dài 5800 µs ở 30 °C (tốc độ âm khoảng 349,5 m/s): 5800 × 10⁻⁶ × 349,5 / 2 ≈ 1,01 m. Các ví dụ hay viết cm = us / 58: đó là công thức gần đúng với tốc độ âm khoảng 343 m/s (20 °C).

3. Nối HC-SR04 với ESP32 3,3 V

Sơ đồ nối HC-SR04 với ESP32 DevKit V1: VCC vào VIN, Trig vào D5, Echo vào D18 qua cầu chia, GND vào GND
Nối HC-SR04 với ESP32; chân Echo đi qua cầu chia ở hình 3 (hình minh họa).
  • VCC vào chân VIN của ESP32 DevKit (có 5 V khi cắm USB) hoặc nguồn 5 V riêng nối chung GND.
  • Trig vào D5. Mức 3,3 V từ ESP32 đủ để module 5 V nhận là mức cao.
  • Echo vào D18 qua cầu chia áp. Chân Echo ra 5 V, vượt mức 3,3 V của ESP32; nối thẳng có thể chạy được một thời gian nhưng làm chân ESP32 làm việc quá mức cho phép.
Cầu chia áp cho chân Echo: Echo 5 V qua điện trở R1 1 kilo ohm tới điểm nối D18 của ESP32, từ điểm này điện trở R2 2 kilo ohm xuống GND, điện áp ở D18 bằng 3,33 V
Cầu chia 1 kΩ / 2 kΩ hạ 5 V xuống khoảng 3,33 V (hình minh họa).

Chuẩn bị: ESP32 DevKit V1, module HC-SR04 (shop chưa bán), điện trở 1 kΩ và 2 kΩ (hoặc hai điện trở 1 kΩ nối tiếp thay cho 2 kΩ), breadboard và dây cắm. Cách chọn cầu chia và mạch chuyển mức khác có ở bài về mức logic 3,3 V và 5 V (link cuối bài).

4. Sai số theo nhiệt độ: vì sao phải bù

Bảng sai số với vật ở 100 cm: ở 0 độ C tốc độ âm 331,3 m/s đo thành 102,6 cm; 20 độ C 343,4 m/s đo 99,0 cm; 30 độ C 349,5 m/s đo 97,3 cm; 40 độ C 355,5 m/s đo 95,6 cm nếu luôn dùng 340 m/s
Sai số khi chương trình luôn dùng 340 m/s, vật thật ở 100 cm.

Tốc độ âm trong không khí tăng theo nhiệt độ, gần đúng c = 331,3 + 0,606 × T (m/s, T tính bằng °C). Mùa hè ở Việt Nam, không khí 30–35 °C, âm đi nhanh hơn khoảng 3 % so với 340 m/s. Chương trình dùng số cố định sẽ đo vật ở 2 m thiếu khoảng 5 cm. Với xe tránh vật cản, sai số này không đáng kể; với đo mực nước bồn để tính thể tích, cần bù nhiệt độ.

Cách bù: đo nhiệt độ không khí bằng một cảm biến (DHT22, BME280, DS18B20 đặt cạnh HC-SR04) rồi tính lại tốc độ âm mỗi lần đo. Chương trình dưới đây cho gõ nhiệt độ qua Serial Monitor để thấy rõ ảnh hưởng; trong thiết bị thật, thay bằng giá trị đọc từ cảm biến nhiệt độ. Độ ẩm cũng làm âm đi nhanh hơn một chút, nhưng ảnh hưởng nhỏ hơn nhiệt độ nhiều.

5. Chương trình đo khoảng cách có bù nhiệt độ

// PYWORLD.VN - Cảm biến siêu âm HC-SR04 với ESP32: đo khoảng cách có bù nhiệt độ không khí
// PYWORLD.VN - Trig nối D5, Echo nối D18 qua cầu chia 1 kΩ / 2 kΩ (Echo của HC-SR04 ra 5 V); nguồn module lấy từ VIN 5 V
// PYWORLD.VN - Mỗi lần đo lấy trung vị của 5 lần phát để bỏ số đo lỗi; gõ nhiệt độ (ví dụ 32.5) vào Serial Monitor để đổi

const int CHAN_TRIG = 5;
const int CHAN_ECHO = 18;
const unsigned long TIMEOUT_US = 30000;    // khoảng 5 m đi và về; quá thời gian này coi như không có vật
const int SO_LAN_DO = 5;
const unsigned long NGHI_GIUA_HAI_LAN_MS = 60;   // chờ tiếng vọng cũ tắt hẳn

float nhietDoC = 30.0;                     // nhiệt độ không khí, sửa qua Serial

float tocDoAm(float t) {                   // m/s, gần đúng trong khoảng −20…50 °C
  return 331.3f + 0.606f * t;
}

unsigned long doMotLan() {
  digitalWrite(CHAN_TRIG, LOW);
  delayMicroseconds(4);
  digitalWrite(CHAN_TRIG, HIGH);           // xung kích tối thiểu 10 µs
  delayMicroseconds(10);
  digitalWrite(CHAN_TRIG, LOW);
  return pulseIn(CHAN_ECHO, HIGH, TIMEOUT_US);   // độ rộng xung Echo, 0 nếu quá thời gian
}

unsigned long doTrungVi() {
  unsigned long ds[SO_LAN_DO];
  for (int i = 0; i < SO_LAN_DO; i++) {
    ds[i] = doMotLan();
    delay(NGHI_GIUA_HAI_LAN_MS);
  }
  for (int i = 1; i < SO_LAN_DO; i++) {    // sắp xếp chèn 5 phần tử
    unsigned long x = ds[i];
    int j = i - 1;
    while (j >= 0 && ds[j] > x) {
      ds[j + 1] = ds[j];
      j--;
    }
    ds[j + 1] = x;
  }
  return ds[SO_LAN_DO / 2];
}

void docNhietDoTuSerial() {
  if (Serial.available()) {
    float t = Serial.parseFloat();
    if (t > -30 && t < 60) {
      nhietDoC = t;
      Serial.printf("Da dat nhiet do khong khi = %.1f C\n", nhietDoC);
    }
    while (Serial.available()) {
      Serial.read();
    }
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(CHAN_TRIG, OUTPUT);
  pinMode(CHAN_ECHO, INPUT);
  Serial.println("HC-SR04 + ESP32: do khoang cach, bu nhiet do");
}

void loop() {
  docNhietDoTuSerial();
  unsigned long us = doTrungVi();
  if (us == 0) {
    Serial.println("Khong co tieng vong (qua xa, vat hut am, hoac sai day)");
  } else {
    float c = tocDoAm(nhietDoC);
    float cm = us * 1e-6f * c / 2.0f * 100.0f;          // chia 2: sóng đi và về
    float cm340 = us * 1e-6f * 340.0f / 2.0f * 100.0f;  // nếu coi tốc độ âm luôn 340 m/s
    Serial.printf("Echo %lu us | T = %.1f C, c = %.1f m/s | %.1f cm (neu dung 340 m/s: %.1f cm)\n",
                  us, nhietDoC, c, cm, cm340);
  }
  delay(500);
}

Giải thích:

  • pulseIn(CHAN_ECHO, HIGH, TIMEOUT_US) đo độ rộng xung Echo bằng micro giây. Thời gian chờ 30 000 µs tương ứng khoảng 5 m; quá thời gian, hàm trả 0 và chương trình báo không có tiếng vọng.
  • Mỗi lần đo phát 5 lần, sắp xếp rồi lấy giá trị giữa (trung vị). Cách này bỏ được số đo lỗi do tiếng vọng lạc, tốt hơn lấy trung bình.
  • Giữa hai lần phát nghỉ 60 ms để tiếng vọng cũ tắt hẳn, tránh đo nhầm tiếng vọng của lần trước. Dùng nhiều HC-SR04 cạnh nhau thì cho chúng phát lần lượt, không phát cùng lúc.
  • Dòng in có cả kết quả bù nhiệt độ và kết quả nếu dùng 340 m/s để so sánh.
Serial Monitor in thời gian Echo 583, 2914, 5800 micro giây với khoảng cách 10,2 cm, 50,9 cm, 101,3 cm ở 30 độ C, so với 9,9 cm, 49,5 cm, 98,6 cm nếu dùng 340 m/s; sau khi đặt 22 độ C thì 5800 micro giây là 99,9 cm; dòng cuối báo không có tiếng vọng
Kết quả mong đợi trên Serial Monitor (hình minh họa).

6. Khi nào HC-SR04 đo sai

  • Vật nghiêng: sóng dội đi hướng khác, không về đầu thu. Mặt phẳng nghiêng quá khoảng 15–20° so với hướng phát thường cho kết quả 0 hoặc số rất lớn.
  • Vật mềm, xốp (vải, mút, lông thú): hút âm, tiếng vọng yếu.
  • Vật nhỏ, mảnh (dây điện, chân bàn mảnh): mặt phản xạ quá nhỏ.
  • Gần hơn 2 cm: tiếng vọng về trong lúc đầu dò còn đang rung sau khi phát.
  • Đo trong ống hẹp, bồn nhỏ: sóng dội từ thành ống tới trước mặt nước. Dùng ống dẫn sóng đủ rộng hoặc loại đầu dò góc hẹp.
  • Hơi nước, ngoài trời mưa: đầu dò HC-SR04 không chống nước. Đo mực nước bồn, hố ga thì dùng loại đầu dò kín như JSN-SR04T.

7. Lỗi hay gặp

Hiện tượngNguyên nhânCách xử lý
Luôn báo không có tiếng vọngSai dây Trig/Echo; VCC chỉ có 3,3 V với bản 5 VKiểm tra dây, cấp 5 V vào VCC
Số đo nhảy lung tungNguồn 5 V nhiễu; tiếng vọng lạc; phát quá dàyThêm tụ 100 µF cạnh module, lọc trung vị, nghỉ 60 ms giữa hai lần phát
Luôn đo khoảng 2–3 cmCó vật cản ngay trước đầu dò, hoặc đầu dò áp sát vỏ hộpĐể đầu dò lộ ra ngoài, không bị che
Đo đúng ở gần, sai ở xaVật nghiêng, vật nhỏ, ngoài góc đoDùng vật phẳng, thẳng góc khi thử
Kết quả lệch đều vài phần trămKhông bù nhiệt độTính tốc độ âm theo nhiệt độ không khí

8. Xem thêm


Tin tức liên quan

Xe tránh vật cản với ESP32: cảm biến siêu âm HC-SR04 trên servo quét, tự chọn đường
Xe tránh vật cản với ESP32: cảm biến siêu âm HC-SR04 trên servo quét, tự chọn đường

Làm xe robot tự tránh vật cản với ESP32: cảm biến siêu âm HC-SR04 gắn trên servo quét trái phải, cầu phân áp cho chân ECHO, thuật toán chọn đường, lưu đồ và thí nghiệm STEM.

Điều khiển relay bằng điện thoại với Blynk IoT và ESP32: template, datastream, đồng bộ trạng thái
Điều khiển relay bằng điện thoại với Blynk IoT và ESP32: template, datastream, đồng bộ trạng thái

Hướng dẫn Blynk IoT với ESP32 từ đầu: tạo template, datastream V0–V5, thiết bị và token, nối relay 4 kênh, chương trình bật tắt bằng điện thoại kèm nút nhấn tại chỗ.

Micro I2S INMP441 với ESP32: đo mức âm thanh dBFS, dB SPL và vỗ tay hai lần bật tắt đèn
Micro I2S INMP441 với ESP32: đo mức âm thanh dBFS, dB SPL và vỗ tay hai lần bật tắt đèn

Khung I2S của micro INMP441, nối với ESP32, đổi số đo 24 bit ra dBFS và ước tính dB SPL, nhận tiếng vỗ tay bằng mức nền và độ vọt, bật tắt đèn khi vỗ tay hai lần.

Hướng dẫn sử dụng màn hình LCD 1602 I2C với Arduino Uno
Hướng dẫn sử dụng màn hình LCD 1602 I2C với Arduino Uno

Hướng dẫn nối màn hình LCD 1602 I2C với Arduino Uno, cài thư viện LiquidCrystal_I2C, chạy code hiển thị và quét địa chỉ I2C 0x27/0x3F.

Hướng dẫn dùng PWM trên ESP32: điều chỉnh độ sáng LED bằng analogWrite và LEDC
Hướng dẫn dùng PWM trên ESP32: điều chỉnh độ sáng LED bằng analogWrite và LEDC

Hướng dẫn tạo PWM trên ESP32 bằng Arduino IDE: analogWrite, ledcAttach và ledcWrite của gói esp32 3.x, chỉnh độ sáng LED bằng biến trở, chọn tần số và độ phân giải.

Hướng dẫn dùng màn hình OLED 0.96 inch SSD1306 với Arduino Nano
Hướng dẫn dùng màn hình OLED 0.96 inch SSD1306 với Arduino Nano

Hướng dẫn nối màn hình OLED 0.96 inch SSD1306 (I2C) với Arduino Nano, cài thư viện Adafruit, hiện chữ nhiều cỡ, vẽ thanh mức theo điện áp và lưu ý về bộ nhớ RAM.

Hướng dẫn ESP-NOW trên ESP32: truyền dữ liệu giữa hai mạch không cần router Wi-Fi
Hướng dẫn ESP-NOW trên ESP32: truyền dữ liệu giữa hai mạch không cần router Wi-Fi

Hướng dẫn dùng ESP-NOW để hai mạch ESP32 truyền dữ liệu trực tiếp không cần router: xem địa chỉ MAC, chương trình gửi, chương trình nhận, đọc RSSI và xử lý lỗi thường gặp.

AccelStepper: tăng tốc, giảm tốc mượt cho động cơ bước NEMA 17, A4988, ESP32, về gốc, hàm hay dùng
AccelStepper: tăng tốc, giảm tốc mượt cho động cơ bước NEMA 17, A4988, ESP32, về gốc, hàm hay dùng

Dùng thư viện AccelStepper cho động cơ bước NEMA 17 và A4988 trên ESP32: vì sao cần tăng tốc, cách thư viện tính khoảng cách bước, nối dây, về gốc bằng công tắc, hàm hay dùng, lỗi thường gặp.


Bình luận
Đã thêm vào giỏ hàng