
Giao thức 1-Wire: Nguyên lý và ứng dụng (DS18B20)
Nguyên lý giao thức 1-Wire (bus open-drain 1 dây, địa chỉ ROM 64-bit), thực hành đọc nhiệt độ DS18B20 với ESP32 DevKit và điện trở pull-up 4.7kΩ.
1-Wire là giao thức giao tiếp nối tiếp do Dallas Semiconductor (nay thuộc Maxim/Analog Devices) phát triển, đặc biệt vì chỉ dùng đúng 1 dây tín hiệu DATA cho cả truyền và nhận (cộng thêm GND, và tuỳ chọn VCC nếu không dùng chế độ "parasite power" lấy điện từ chính dây DATA).
Đây là bus open-drain nên bắt buộc phải có điện trở kéo lên (pull-up, thường 4.7kΩ) giữa DATA và nguồn. Điểm đặc biệt nhất: mỗi chip 1-Wire (như DS18B20) có sẵn một địa chỉ ROM 64-bit duy nhất được ghi tại nhà máy, cho phép nhiều cảm biến dùng chung 1 dây DATA mà vẫn phân biệt được từng con — khác với I2C nơi địa chỉ phải cấu hình bằng phần cứng/phần mềm và chỉ có 7 bit (tối đa 128 giá trị).
Bài này dùng cảm biến nhiệt độ DS18B20 với ESP32 DevKit qua chân GPIO4.
Hướng dẫn chi tiết
Nguyên lý 1-Wire (bus open-drain 1 dây, địa chỉ ROM 64-bit), thực hành đọc nhiệt độ DS18B20 với ESP32 DevKit.
1. Giới thiệu
1-Wire là giao thức nối tiếp bán song công (half-duplex) do Dallas Semiconductor phát triển, đặc trưng bởi việc chỉ dùng đúng 1 dây DATA cho cả gửi lệnh từ master lẫn nhận dữ liệu từ slave (cộng thêm GND làm mass chung). Giống I2C, đây là bus open-drain nên bắt buộc có điện trở pull-up (thường 4.7kΩ) giữa DATA và nguồn dương — nếu thiếu, bus sẽ luôn đọc mức thấp và không giao tiếp được.
Điểm khác biệt cốt lõi so với I2C: mỗi chip 1-Wire có sẵn địa chỉ ROM 64-bit duy nhất trên toàn thế giới, được ghi tại nhà máy (8 bit family code + 48 bit serial number + 8 bit CRC), không cần cấu hình như địa chỉ I2C 7-bit. Nhờ vậy có thể gắn hàng chục cảm biến DS18B20 lên chung 1 dây DATA mà vẫn đọc riêng từng con bằng địa chỉ ROM, mà không lo trùng địa chỉ như I2C. Bài này đọc nhiệt độ từ 1 cảm biến DS18B20 qua GPIO4 của ESP32 DevKit.
2. Linh kiện cần có
Linh kiện | Số lượng | Ghi chú |
|---|---|---|
ESP32 DevKit V4 | 1 | Board chính |
DS18B20 (cảm biến nhiệt độ 1-Wire) | 1 | Dải đo -55°C đến 125°C, độ chính xác ±0.5°C |
Điện trở 4.7kΩ | 1 | Pull-up bắt buộc cho dây DATA (bus open-drain) |
Dây jumper | 3 | VCC, GND, DATA |
3. Sơ đồ kết nối
DS18B20 | ESP32 |
|---|---|
VCC (đỏ) | 3V3 |
GND (đen) | GND |
DATA (vàng) | GPIO4 |
Điện trở 4.7kΩ nối giữa chân DATA (GPIO4) và 3V3 — đây là pull-up bắt buộc, không phải tuỳ chọn: thiếu điện trở này, dây DATA luôn ở mức thấp (do open-drain) và sensors.getDeviceCount() sẽ luôn trả về 0.
4. Code ví dụ #1 — Đọc nhiệt độ, in địa chỉ ROM
Sketch dùng thư viện OneWire + DallasTemperature, dò tìm số thiết bị trên bus bằng sensors.getDeviceCount(), in ra địa chỉ ROM 64-bit của cảm biến đầu tiên, đặt độ phân giải 12-bit rồi đọc nhiệt độ mỗi 2 giây.
/*
Giao thức 1-Wire: Nguyên lý và ứng dụng — Ví dụ #1: Đọc nhiệt độ DS18B20
Board: ESP32 DevKit V4 + DS18B20 (1-Wire) + điện trở kéo lên 4.7kΩ
Sơ đồ:
DS18B20 VCC -> 3V3
DS18B20 GND -> GND
DS18B20 DATA -> GPIO4 (đồng thời nối qua điện trở 4.7kΩ len 3V3 - pull-up
bắt buộc vì 1-Wire là bus open-drain)
1-Wire cho phép nhiều cảm biến DS18B20 dùng chung 1 dây DATA, phân biệt
nhau bằng địa chỉ ROM 64-bit duy nhất được ghi sẵn trong từng chip (khác
I2C dùng địa chỉ 7-bit cấu hình bằng phần cứng/phần mềm).
*/
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_PIN 4
OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature sensors(&oneWire);
DeviceAddress sensorAddress;
bool sensorFound = false;
void printAddress(DeviceAddress addr) {
for (uint8_t i = 0; i < 8; i++) {
if (addr[i] < 16) Serial.print('0');
Serial.print(addr[i], HEX);
}
}
void setup() {
Serial.begin(115200);
delay(300);
Serial.println(F("=== DS18B20 1-Wire demo boot ==="));
sensors.begin();
uint8_t count = sensors.getDeviceCount();
Serial.printf("So thiet bi 1-Wire tim thay tren bus: %u\n", count);
if (count > 0 && sensors.getAddress(sensorAddress, 0)) {
sensorFound = true;
Serial.print(F("Dia chi ROM 64-bit cua cam bien #0: "));
printAddress(sensorAddress);
Serial.println();
sensors.setResolution(sensorAddress, 12); // do phan giai 12-bit (~750ms/lan doc)
} else {
Serial.println(F("[WARN] Khong tim thay DS18B20 - kiem tra pull-up 4.7k va day DATA"));
}
}
void loop() {
static unsigned long lastRead = 0;
if (millis() - lastRead >= 2000) {
lastRead = millis();
sensors.requestTemperatures(); // gui lenh convert T tren bus 1-Wire
if (sensorFound) {
float tempC = sensors.getTempC(sensorAddress);
if (tempC == DEVICE_DISCONNECTED_C) {
Serial.println(F("[WARN] Cam bien mat ket noi giua chung"));
} else {
Serial.printf("Nhiet do: %.2f C (do phan giai 12-bit)\n", tempC);
}
} else {
Serial.println(F("Khong co cam bien de doc"));
}
}
}
5. Code ví dụ #2 — Ứng dụng thực tế: cảnh báo ngưỡng nhiệt độ
Biến thể thứ hai dùng chân LED onboard GPIO2 để bật cảnh báo khi nhiệt độ vượt ngưỡng 30°C — mô phỏng ứng dụng giám sát nhiệt độ thực tế (tủ điện, máy chủ, bể nuôi trồng).
/*
Giao thức 1-Wire — Ví dụ #2: Ứng dụng thực tế — Cảnh báo ngưỡng nhiệt độ
Board: ESP32 DevKit V4 + DS18B20 (DATA=GPIO4, pull-up 4.7kΩ len 3V3)
Mô phỏng ứng dụng: bật LED onboard (GPIO2) khi nhiệt độ vượt ngưỡng.
*/
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_PIN 4
#define ALERT_LED_PIN 2
#define TEMP_THRESHOLD_C 30.0F
OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature sensors(&oneWire);
void setup() {
Serial.begin(115200);
delay(300);
Serial.println(F("=== DS18B20 canh bao nguong nhiet do ==="));
pinMode(ALERT_LED_PIN, OUTPUT);
digitalWrite(ALERT_LED_PIN, LOW);
sensors.begin();
Serial.printf("Nguong canh bao: %.1f C\n", TEMP_THRESHOLD_C);
}
void loop() {
sensors.requestTemperatures();
float tempC = sensors.getTempCByIndex(0);
if (tempC == DEVICE_DISCONNECTED_C) {
Serial.println(F("[WARN] Khong doc duoc cam bien - kiem tra day 1-Wire"));
digitalWrite(ALERT_LED_PIN, LOW);
} else {
bool alert = tempC >= TEMP_THRESHOLD_C;
digitalWrite(ALERT_LED_PIN, alert ? HIGH : LOW);
Serial.printf("Nhiet do: %.2f C -> LED canh bao: %s\n", tempC, alert ? "BAT" : "TAT");
}
delay(2000);
}
6. Ứng dụng thực tế & mở rộng
1-Wire với địa chỉ ROM 64-bit sẵn có (không cần cấu hình) rất phù hợp cho hệ thống đo nhiệt độ nhiều điểm: dây điện dài chạy qua nhiều phòng/tủ điện, mỗi điểm gắn 1 DS18B20 — tất cả dùng chung 1 dây DATA về board trung tâm mà không lo trùng địa chỉ.
Ứng dụng phổ biến: giám sát nhiệt độ kho lạnh nhiều điểm, hệ thống sưởi sàn, giám sát nhiệt độ pin/tủ điện. DS18B20 còn hỗ trợ "parasite power" (lấy điện trực tiếp từ dây DATA, bỏ luôn dây VCC) — hữu ích khi cần kéo dây rất dài và tối giản số dây, dù cấu hình này yêu cầu MOSFET pull-up mạnh hơn trong code.
7. Lỗi thường gặp
Lỗi | Nguyên nhân | Cách sửa |
|---|---|---|
| Thiếu điện trở pull-up 4.7kΩ trên dây DATA | Thêm điện trở 4.7kΩ giữa DATA và 3V3 |
Đọc ra | -127 = mất kết nối cảm biến; 85 = giá trị reset mặc định do đọc quá sớm sau lệnh convert | Kiểm tra lại dây DATA; đợi đủ thời gian convert (~750ms ở 12-bit) trước khi đọc |
Nhiệt độ đọc được sai lệch nhiều so với thực tế | Độ phân giải thấp hơn 12-bit, hoặc cảm biến bị nhiễu nhiệt từ linh kiện gần đó | Gọi |
Nhiều cảm biến trên cùng dây báo cùng 1 giá trị | Code chỉ đọc theo index 0 thay vì lặp qua từng địa chỉ ROM | Dùng |
8. Tổng kết
1-Wire là lựa chọn tối giản số dây nhất trong 3 giao thức (chỉ 1 dây DATA), nhờ địa chỉ ROM 64-bit có sẵn từ nhà máy nên không cần cấu hình địa chỉ như I2C.
Đánh đổi là tốc độ thấp và giao thức phức tạp hơn ở tầng thấp (timing chính xác theo micro giây), nhưng các thư viện như OneWire/DallasTemperature đã che giấu hết độ phức tạp đó. Đây là lựa chọn kinh điển cho cảm biến nhiệt độ phân tán, dây dài, nhiều điểm đo.