Nhiệt kế – ẩm kế phòng hiển thị LCD, có đèn báo mức dễ chịu

Đo nhiệt độ và độ ẩm trong phòng bằng DHT22, hiện lên màn LCD 1602 cùng mức cao nhất – thấp nhất trong ngày. Đèn xanh báo phòng đang dễ chịu, đèn đỏ báo quá nóng hoặc quá ẩm. Một nút nhấn để đổi màn hình và xoá mốc đo.

⏱ 70 phút🔧 Mới học💰 Linh kiện ~167.500đ🧠 Arduino Nano⚡ Nguồn USB 5V

✅ Đã kiểm thử

Luật điệnKhông lỗiSai chân, chập nguồn, quá áp 5V vào chân 3,3V, thiếu đi-ốt, thiếu trở kéo…
Mô phỏng SPICE3/3 kịch bản · 21 phép đo đạtDòng/áp từng linh kiện ở trường hợp xấu nhất (ngspice)
Biên dịch codeĐạt · dùng 36% bộ nhớarduino:avr:nano
Xem chi tiết 21 phép đo

Thử đèn lúc khởi động: cả hai đèn cùng sáng, đang bấm nút

  • D1: dòng 8.2mA (cho phép 20.0mA).
  • D2: dòng 9.0mA (cho phép 20.0mA).
  • Chân D8 cấp 8.2mA (an toàn ≤ 20.0mA).
  • Chân D9 cấp 9.0mA (an toàn ≤ 20.0mA).
  • Tổng dòng đỉnh từ USB 79.6mA (đủ với cục sạc 5V 1A).
  • Dòng LED xanh: 8.2mA ✓
  • Dòng LED đỏ: 9.0mA ✓
  • Dòng chân D8 (giới hạn an toàn 20mA): 8.2mA ✓
  • Áp chân nút khi nhấn (phải về 0V): 0.00V ✓
  • Tổng dòng từ cổng USB: 79.6mA ✓

Phòng dễ chịu: chỉ đèn xanh sáng

  • D1: dòng 8.2mA (cho phép 20.0mA).
  • Chân D8 cấp 8.2mA (an toàn ≤ 20.0mA).
  • Tổng dòng đỉnh từ USB 70.7mA (đủ với cục sạc 5V 1A).
  • Dòng LED xanh: 8.2mA ✓
  • LED đỏ phải tắt: 0.0mA ✓
  • Áp chân nút khi thả (trở kéo lên trong chip): 4.98V ✓

Phòng quá nóng: chỉ đèn đỏ sáng

  • D2: dòng 9.0mA (cho phép 20.0mA).
  • Chân D9 cấp 9.0mA (an toàn ≤ 20.0mA).
  • Tổng dòng đỉnh từ USB 71.5mA (đủ với cục sạc 5V 1A).
  • Dòng LED đỏ: 9.0mA ✓
  • LED xanh phải tắt: 0.0mA ✓
Kiểm lần cuối: 08:50:49 13/11/2026

Nguyên lý hoạt động

DHT22 đo nhiệt độ (−40…80°C, sai số khoảng 0,5°C) và độ ẩm (0–100%, sai số khoảng 2%). Nó nói chuyện với Nano bằng một dây dữ liệu duy nhất ở D2 theo kiểu đếm độ dài xung; module 3 chân đã có sẵn trở kéo lên nên không cần gắn thêm. Màn LCD 1602 dùng mạch chuyển I2C nên chỉ tốn 2 dây A4 (SDA) và A5 (SCL), địa chỉ thường là 0x27 hoặc 0x3F — code tự dò. Nano so số đo với vùng dễ chịu (26–30°C và 45–70% độ ẩm, sửa được trong code): nằm trong vùng thì bật LED xanh ở D8, vượt ra thì bật LED đỏ ở D9, mỗi đèn qua một điện trở 330Ω nên chỉ ăn khoảng 9mA. Nút nhấn ở D3 dùng trở kéo lên bên trong chip: nhấn là chân về 0V, bấm nhanh để đổi màn hình, giữ 2 giây để xoá mốc cao nhất – thấp nhất.

Sơ đồ đấu dây

Mỗi màu là một dây. Chấm tròn là điểm hàn. Chạm vào tên dây trong bảng để làm nổi bật.

⚡ Mô phỏng tương tácBấm thử — mạch được giải ngay trên máy bạn, điện áp và dòng hiện lên sơ đồ bên dưới.
Tình huống mẫu
Chân D8điều khiển R1
Chân D9điều khiển R2
Nút SW1

Đây là mô phỏng một chiều (DC) ở trạng thái tĩnh: dùng để kiểm tra mức áp, dòng và công suất. Hiệu ứng nhấp nháy, PWM hay giao tiếp số do code quyết định nên không chạy ở đây.

5V5VGNDGNDDHT_DATADHT_DATASDASDASCLSCLLED_OK_DRVLED_OK_DRVLED_OK_ALED_OK_ALED_HOT_DRVLED_HOT_DRVLED_HOT_ALED_HOT_ABTNBTNU1 · Arduino Nano5VA4A5D2D3D8D9GNDU3 · Màn hình LCD 1602 kèm mạch I2CVCCSCLSDAGNDU2 · Cảm biến nhiệt độ – độ ẩm DHT22VCCDATAGNDSW1 · Nút nhấn12R1 · Điện trở 330Ω12D1 · Đèn LED 5mmA · chân dài (+)K · chân ngắn (−)R2 · Điện trở 330Ω12D2 · Đèn LED 5mmA · chân dài (+)K · chân ngắn (−)
#DâyNối các điểmÁp mô phỏng
1đỏ
5V
U1 5V ⟷ U2 VCC ⟷ U3 VCC—
2đen
GND
U1 GND ⟷ U2 GND ⟷ U3 GND ⟷ D1 K · chân ngắn (−) ⟷ D2 K · chân ngắn (−) ⟷ SW1 2—
3vàng
DHT_DATA
U2 DATA ⟷ U1 D2—
4xanh lá
SDA
U3 SDA ⟷ U1 A4—
5xanh dương
SCL
U3 SCL ⟷ U1 A5—
6tím
LED_OK_DRV
U1 D8 ⟷ R1 1—
7xanh ngọc
LED_OK_A
R1 2 ⟷ D1 A · chân dài (+)—
8nâu
LED_HOT_DRV
U1 D9 ⟷ R2 1—
9hồng
LED_HOT_A
R2 2 ⟷ D2 A · chân dài (+)—
10xám
BTN
SW1 1 ⟷ U1 D3—

Danh sách linh kiện

Ký hiệuLinh kiệnGiá tham khảo
U1Arduino Nano (chip nạp CH340)75.000đTìm mua
U2Cảm biến nhiệt độ – độ ẩm DHT22 module 3 chân (AM2302)48.000đTìm mua
U3Màn hình LCD 1602 kèm mạch chuyển I2C (PCF8574)42.000đTìm mua
D1LED xanh lá 5mm (green)300đTìm mua
R1Điện trở 330Ω 1/4W (cam-cam-nâu)200đTìm mua
D2LED đỏ 5mm (red)300đTìm mua
R2Điện trở 330Ω 1/4W (cam-cam-nâu)200đTìm mua
SW1Nút nhấn 4 chân 6×6mm1.500đTìm mua
Tổng (chưa gồm board hàn, dây, thiếc)167.500đ

Các bước làm

Code nạp cho mạch

Cài thư viện: DHT sensor library, Adafruit Unified Sensor, LiquidCrystal I2C (Arduino IDE → Library Manager). Chọn board Arduino Nano.

/*
  Mạch Gọn — Nhiệt kế – ẩm kế phòng (Arduino Nano + DHT22 + LCD1602 I2C)
  Thư viện: DHT sensor library, Adafruit Unified Sensor, LiquidCrystal I2C
  Chân : D2 <- DHT22 ; A4/A5 -> LCD I2C ; D8 -> 330Ω -> LED xanh
         D9 -> 330Ω -> LED đỏ ; D3 nút nhấn về GND (INPUT_PULLUP)
*/
#include <Arduino.h>
#include <Wire.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>

// ---------- Cấu hình: sửa cho hợp khí hậu nơi bạn ở ----------
const float NHIET_MIN = 20.0, NHIET_MAX = 30.0;   // vùng nhiệt độ dễ chịu (°C)
const float AM_MIN    = 45.0, AM_MAX    = 70.0;   // vùng độ ẩm dễ chịu (%)
const uint32_t CHU_KY_DOC_MS = 2500;              // DHT22 cần ≥2s giữa 2 lần đọc
const uint32_t GIU_XOA_MS    = 2000;              // giữ nút bao lâu thì xoá mốc

const uint8_t PIN_DHT = 2, PIN_NUT = 3, PIN_LED_OK = 8, PIN_LED_NONG = 9;

DHT dht(PIN_DHT, DHT22);
LiquidCrystal_I2C* lcd = nullptr;

float nhiet = NAN, am = NAN;
float nhietMax = -99, nhietMin = 99, amMax = -99, amMin = 999;
bool  camBienOk = false;
uint8_t manHinh = 0;                 // 0: hiện tại, 1: mốc nhiệt độ, 2: mốc độ ẩm
uint8_t soLanLoi = 0;
uint32_t mocDoc = 0, mocVe = 0, mocNhanNut = 0, giuThongBao = 0;
bool nutDangNhan = false, daXoa = false;

uint8_t timDiaChiLcd() {
  for (uint8_t a = 0x20; a < 0x28; a++) { Wire.beginTransmission(a); if (Wire.endTransmission() == 0) return a; }
  for (uint8_t a = 0x38; a < 0x40; a++) { Wire.beginTransmission(a); if (Wire.endTransmission() == 0) return a; }
  return 0x27;
}

void inDong(uint8_t hang, const char* s) {
  if (!lcd) return;
  lcd->setCursor(0, hang);
  lcd->print(s);
  for (uint8_t i = strlen(s); i < 16; i++) lcd->print(' ');
}

void xoaMoc() {
  nhietMax = nhietMin = nhiet;
  amMax = amMin = am;
  Serial.println(F("Da xoa moc cao nhat - thap nhat"));
}

void veManHinh() {
  char d1[17], d2[17];
  if (!camBienOk) {
    inDong(0, "Khong doc duoc");
    inDong(1, "DHT22 - xem day");
    return;
  }
  if (manHinh == 0) {
    snprintf(d1, sizeof(d1), "Nhiet do: %d.%01dC", (int)nhiet, abs((int)(nhiet * 10) % 10));
    snprintf(d2, sizeof(d2), "Do am  : %d.%01d%%", (int)am, abs((int)(am * 10) % 10));
  } else if (manHinh == 1) {
    snprintf(d1, sizeof(d1), "Nhiet cao: %d.%01dC", (int)nhietMax, abs((int)(nhietMax * 10) % 10));
    snprintf(d2, sizeof(d2), "Nhiet thap:%d.%01dC", (int)nhietMin, abs((int)(nhietMin * 10) % 10));
  } else {
    snprintf(d1, sizeof(d1), "Am cao : %d.%01d%%", (int)amMax, abs((int)(amMax * 10) % 10));
    snprintf(d2, sizeof(d2), "Am thap: %d.%01d%%", (int)amMin, abs((int)(amMin * 10) % 10));
  }
  inDong(0, d1);
  inDong(1, d2);
}

void setup() {
  pinMode(PIN_LED_OK, OUTPUT);
  pinMode(PIN_LED_NONG, OUTPUT);
  pinMode(PIN_NUT, INPUT_PULLUP);
  Serial.begin(115200);
  Serial.println(F("Mach Gon - nhiet ke am ke phong"));

  // thử đèn 1 giây
  digitalWrite(PIN_LED_OK, HIGH);
  digitalWrite(PIN_LED_NONG, HIGH);

  Wire.begin();
  uint8_t addr = timDiaChiLcd();
  Serial.print(F("Dia chi LCD I2C: 0x")); Serial.println(addr, HEX);
  static LiquidCrystal_I2C l(addr, 16, 2);
  lcd = &l;
  lcd->init();
  lcd->backlight();
  inDong(0, "Mach Gon");
  inDong(1, "Nhiet ke phong");
  dht.begin();
  delay(1000);
  digitalWrite(PIN_LED_OK, LOW);
  digitalWrite(PIN_LED_NONG, LOW);
}

void loop() {
  uint32_t bayGio = millis();

  // 1. Đọc cảm biến
  if (bayGio - mocDoc >= CHU_KY_DOC_MS) {
    mocDoc = bayGio;
    float t = dht.readTemperature();
    float h = dht.readHumidity();
    if (isnan(t) || isnan(h) || h > 100 || t < -40 || t > 80) {
      if (++soLanLoi >= 3) camBienOk = false;
    } else {
      soLanLoi = 0;
      bool lanDau = !camBienOk;
      camBienOk = true;
      nhiet = t; am = h;
      if (lanDau) xoaMoc();
      if (t > nhietMax) nhietMax = t;
      if (t < nhietMin) nhietMin = t;
      if (h > amMax) amMax = h;
      if (h < amMin) amMin = h;
    }
    Serial.print(F("T=")); Serial.print(nhiet, 1);
    Serial.print(F("C RH=")); Serial.print(am, 1);
    Serial.print(F("% ok=")); Serial.println(camBienOk ? 1 : 0);
  }

  // 2. Đèn báo mức dễ chịu
  bool deChiu = camBienOk && nhiet >= NHIET_MIN && nhiet <= NHIET_MAX && am >= AM_MIN && am <= AM_MAX;
  bool vuotMuc = camBienOk && (nhiet > NHIET_MAX || am > AM_MAX);
  digitalWrite(PIN_LED_OK, deChiu ? HIGH : LOW);
  digitalWrite(PIN_LED_NONG, vuotMuc ? HIGH : LOW);

  // 3. Nút: bấm nhanh đổi màn hình, giữ 2 giây xoá mốc
  bool dangNhan = (digitalRead(PIN_NUT) == LOW);
  if (dangNhan && !nutDangNhan) { nutDangNhan = true; daXoa = false; mocNhanNut = bayGio; }
  if (dangNhan && nutDangNhan && !daXoa && bayGio - mocNhanNut >= GIU_XOA_MS) {
    daXoa = true;
    if (camBienOk) xoaMoc();
    inDong(0, "Da xoa moc do"); inDong(1, "");
    giuThongBao = bayGio + 1000;   // giữ thông báo trên màn 1 giây
  }
  if (!dangNhan && nutDangNhan) {
    nutDangNhan = false;
    if (!daXoa && bayGio - mocNhanNut > 30) manHinh = (manHinh + 1) % 3;
  }

  // 4. Vẽ màn hình 500ms một lần
  if (bayGio - mocVe >= 500 && (int32_t)(bayGio - giuThongBao) >= 0) { mocVe = bayGio; veManHinh(); }
}

🔌 Cắm thử trên breadboard

Cắm nháp trên board cắm trước cho chắc, chạy đúng rồi hãy làm mạch in. Sơ đồ này do máy tự xếp từ chính netlist ở trên và đã đối chiếu lại: hai chân cùng một cột (cùng nửa board) là thông nhau, không có net nào bị chập.

+−+−5510101515202025253030JIHGFEDCBAU1 · Arduino NanoGND5VD2D3A5A4D8D9U2U3D1 greenR1 330D2 redR2 330SW1
  • Mỗi cột 5 lỗ trong cùng một nửa board thông nhau; hai nửa cách nhau bởi rãnh giữa.
  • Hai đường kẻ dọc mép board là đường nguồn: hàng + (vạch đỏ) và hàng − (vạch xanh). Nhớ bắc dây nối đường nguồn trên với đường nguồn dưới nếu dùng cả hai nửa.
  • Màu dây trên hình trùng với màu trong bảng nối dây ở mục Sơ đồ, cắm theo màu cho dễ dò.

🧲 Mạch in để ủi (PCB 1 mặt)

Bản mạch in một mặt đồng, tự xếp linh kiện và tự đi dây, đã kiểm quy tắc khoảng cách và đối chiếu đúng 100% với sơ đồ nguyên lý ở trên. In tỉ lệ 1:1 rồi ủi lên tấm đồng.

Kích thước board53,3×48,3 mm49 lỗ khoan · mũi 0,8 / 0,9 / 1mm
Đi dây 1 mặt18 đoạn · 340,9 mm · 1 cầu nhảyCầu nhảy = đoạn dây hàn ở mặt linh kiện
Kiểm tra bản mạchĐạt DRC · khớp 100% netlistĐường ≥0,8mm, khe ≥0,5mm; mọi net đối chiếu lại từ hình học thật
Mặt đồng — đã lật gương sẵn để in rồi ủi úp xuống tấm đồng
Mặt linh kiện — vị trí, chiều cắm và tên từng chân
U1U2U3D1R1D2R2SW1D1D0RSTGNDD2D3D4D5D6D7D8D9D10D11D12VINGND2RST25VA7A6A5A4A3A2A1A0AREF3V3D13VCCDATAGNDGNDVCCSDASCL
Cầu nhảy cần hàn (dây đơn ở mặt linh kiện):
  • net BTN: U1.D3 → SW1.1
Hướng dẫn ủi – ăn mòn – khoan – hàn (đầy đủ)

Hướng dẫn làm mạch in bằng phương pháp ủi

Mạch: Nhiệt kế – ẩm kế phòng hiển thị LCD, có đèn báo mức dễ chịu

Kích thước board: 53,3×48,3 mm · 49 lỗ khoan (mũi 0,8, 0,9 và 1 mm) · 18 đoạn mạch, tổng 340,9 mm · 1 cầu nhảy.

Quy tắc đã kiểm: đường mạch rộng 0,8mm (nguồn và GND 1mm), khe cách tối thiểu 0,5mm. Mạch in đã được máy đối chiếu đúng 100% với sơ đồ nguyên lý.

1. In đúng tỉ lệ 1:1

  • In trang "MẶT ĐỒNG" bằng máy in LASER, đặt tỉ lệ 100%, KHÔNG chọn "Fit to page" hay "Scale to fit".
  • Dùng thước trên bản in đo lại đoạn 100mm: phải đúng 100mm mới dùng được.
  • In trên giấy in ảnh bóng, giấy decal bóc, hoặc giấy tạp chí láng. Mực càng dày càng tốt.
  • Trang "MẶT ĐỒNG" đã được LẬT GƯƠNG sẵn: úp mặt có mực vào tấm đồng là đúng chiều.

2. Chuẩn bị tấm đồng

  • Cắt phíp đồng (board đồng một mặt) lớn hơn kích thước mạch mỗi bề khoảng 5mm.
  • Chà mặt đồng bằng giấy nhám mịn (P800–P1200) rồi rửa sạch, lau khô. Mặt đồng phải sáng, không vết dầu mỡ, không chạm tay vào.

3. Ủi mực sang đồng

  • Úp mặt có mực vào tấm đồng, dùng băng keo giấy dán cố định một cạnh.
  • Bàn ủi để mức nóng nhất (cotton/linen), KHÔNG dùng chế độ phun hơi nước.
  • Ủi đè mạnh và đều 3–5 phút, tập trung từng vùng nhỏ, chú ý các góc và mép.
  • Để nguội hẳn rồi ngâm cả tấm vào nước ấm 5–10 phút, bóc giấy thật nhẹ. Giấy decal thì bóc khi còn ấm.
  • Soi kỹ: chỗ nào mực bị thiếu hoặc đứt đường thì tô lại bằng bút lông dầu (bút CD) rồi để khô.

4. Ăn mòn (etching)

  • Pha dung dịch sắt(III) clorua FeCl3 theo hướng dẫn trên bao bì, hoặc dung dịch oxy già + muối ăn + a-xít; dùng hộp nhựa, KHÔNG dùng đồ kim loại.
  • Nhiệt độ dung dịch khoảng 40–50°C thì ăn nhanh hơn. Lắc nhẹ hộp cho đều, thường 10–25 phút là xong.
  • An toàn: đeo găng tay cao su và kính bảo hộ, làm ở nơi thoáng. Dung dịch ăn mòn làm hỏng quần áo, bồn rửa và gây bỏng rát da.
  • Xong thì rửa board bằng nước sạch, lau khô, dùng xăng thơm hoặc axeton lau sạch lớp mực.
  • Dung dịch đã dùng KHÔNG đổ xuống bồn rửa hay cống; đựng vào can kín và bỏ đúng nơi thu gom chất thải.

5. Khoan lỗ

  • Đối chiếu trang "BẢN VẼ LỖ KHOAN" để biết lỗ nào dùng mũi nào.
  • Dùng mũi khoan 0,8mm, 0,9mm và 1mm, khoan từ MẶT ĐỒNG vào để mũi tự định tâm giữa pad.
  • Khoan tốc độ cao, đẩy nhẹ; mũi nhỏ rất dễ gãy. Khoan xong chà nhẹ bavia hai mặt.

6. Hàn linh kiện

  • Linh kiện cắm ở MẶT KHÔNG CÓ ĐỒNG, hàn ở mặt đồng. Theo trang "MẶT LINH KIỆN" để đặt đúng vị trí và đúng chiều.
  • Hàn linh kiện thấp trước (điện trở, đi-ốt), cao sau (tụ, hàng chân, board vi điều khiển).
  • Chú ý chiều: LED chân dài là (+); đi-ốt có vạch trắng là K; tụ hoá có vạch là (−); transistor theo mặt chữ như nét vẽ đỏ.
  • Mạch này còn 1 CẦU NHẢY: hàn đoạn dây đơn ở mặt linh kiện theo nét đỏ gạch trên bản vẽ:
  • cầu nhảy net BTN: từ U1.D3 sang SW1.1
  • Trước khi cấp điện: đo thông mạch giữa chân nguồn và GND — KHÔNG được kêu (không được chập).

Lưu ý với board vi điều khiển

  • Board nhái có thể khác thứ tự chân. Đối chiếu tên chân in cạnh từng lỗ trên trang "MẶT LINH KIỆN" với chữ in trên board thật TRƯỚC KHI ăn mòn. Nếu khác, đừng hàn mà báo lại để sửa bản vẽ.
Bản vẽ lỗ khoan
BẢN VẼ LỖ KHOAN (nhìn từ mặt linh kiện)mũi khoan 0,8mm — 4 lỗmũi khoan 0,9mm — 8 lỗmũi khoan 1mm — 37 lỗ

Đo kiểm bằng đồng hồ vạn năng

Giữa chân 5V và GND của Nano4,7 – 5,1V
Giữa VCC và GND của DHT224,7 – 5,1V
Giữa D3 và GND khi thả nútkhoảng 5V
Giữa D3 và GND khi nhấn nútdưới 0,2V
Serial Monitor 115200Mỗi 2 giây một dòng T=…C RH=…%

Sửa lỗi thường gặp

LCD sáng nền nhưng không có chữ

Vặn biến trở xanh sau lưng mạch I2C để chỉnh tương phản. Nếu vẫn trắng, xem Serial Monitor: code in địa chỉ I2C dò được; không thấy địa chỉ nào là sai dây SDA/SCL.

Số đo hiện "--"

DHT22 chưa trả lời: kiểm tra đúng chân DATA vào D2, cấp đủ 5V, dây dài dưới 1m. DHT22 cần 2 giây giữa hai lần đọc nên lúc mới cắm điện hiện "--" vài giây là bình thường.

Độ ẩm luôn 99%

Cảm biến bị hơi nước đọng (vừa mang từ phòng lạnh ra, hoặc để gần bếp). Để nơi khô thoáng 30 phút sẽ về bình thường.

Nhiệt độ cao hơn thực tế 2–3°C

Cảm biến đang hứng hơi nóng của Nano hoặc LCD. Kéo DHT22 ra xa board ít nhất 5cm bằng dây nối, hướng ra ngoài hộp.

Đèn đỏ sáng suốt ngày

Vùng dễ chịu trong code đang hẹp so với khí hậu nơi bạn ở. Sửa hai hằng số NHIET_MAX và AM_MAX ở đầu file rồi nạp lại.

⚠️ An toàn

  • Mạch dùng 5V từ cổng USB, an toàn khi chạm.
  • Không đặt DHT22 ngoài trời mưa — loại module này không chống nước.
  • Không dùng số đo của mạch này cho mục đích y tế hay bảo quản thuốc, vì cảm biến phổ thông không được hiệu chuẩn.