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ồn | 5 V, dòng khoảng 15 mA |
| Tần số siêu âm | 40 kHz |
| Khoảng đo | Khoảng 2 cm đến 400 cm (vật phẳng, đặt thẳng góc) |
| Góc đo | Khoảng 15° (hình nón hẹp) |
| Độ phân giải | Khoảng 0,3 cm |
| Chân | VCC, 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

- Vi điều khiển đưa chân Trig lên mức cao ít nhất 10 µs.
- 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.
- 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

- 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.

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ù

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.

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ượng | Nguyên nhân | Cách xử lý |
|---|---|---|
| Luôn báo không có tiếng vọng | Sai dây Trig/Echo; VCC chỉ có 3,3 V với bản 5 V | Kiểm tra dây, cấp 5 V vào VCC |
| Số đo nhảy lung tung | Nguồn 5 V nhiễu; tiếng vọng lạc; phát quá dày | Thê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 cm | Có 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 ở xa | Vật nghiêng, vật nhỏ, ngoài góc đo | Dùng vật phẳng, thẳng góc khi thử |
| Kết quả lệch đều vài phần trăm | Không bù nhiệt độ | Tính tốc độ âm theo nhiệt độ không khí |
Xem thêm