Giao thức 1-Wire: Nguyên lý và ứng dụng (DS18B20)
Cơ bản1/8/2026- Tác giả: IoTlabs Maker

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-Wiregiao-thucESP32DS18B20kien-thuc-nen-tang

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

getDeviceCount() luôn trả về 0

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.00 hoặc 85.00

-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 setResolution(addr, 12); đặt cảm biến xa nguồn nhiệt (ESP32, nguồn điện)

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 sensors.getAddress() lấy đúng địa chỉ ROM cho từng cảm biến khi có nhiều thiết bị

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.