Giao tiếp Module cảm biến nhiệt độ và độ ẩm AM2301/DHT21 với Arduino

Giới thiệu

AM2301 (hay DHT21) là cảm biến đo nhiệt độ và độ ẩm tương đối với độ chính xác cao và khả năng truyền dữ liệu dễ dàng qua giao thức 1-Wire. Nó được sử dụng phổ biến trong các ứng dụng IoT, tự động hóa và kiểm soát môi trường.

Thông số kỹ thuật

  • Điện áp hoạt động: 3.3 - 5V
  • Phạm vi đo nhiệt độ: -40°C đến +80°C (Sai số ±0.5°C)
  • Phạm vi đo độ ẩm: 0 - 100% RH (Sai số ±3%)
  • Tần suất lấy mẫu: Tối đa 0.5 Hz (1 lần/2 giây)
  • Giao tiếp: 1-Wire
  • Kích thước: 59mm x 27mm x 13.5mm

Sơ đồ chân AM2301/DHT21

Mô tả các chân

  1. Dây màu đỏ: Chân cấp nguồn, có thể kết nối với 3.3V hoặc 5V từ Arduino.
  2. Dây đen: nối đất (GND)
  3. Dây màu vàng: Data (phải được kết nối với một trong các chân kỹ thuật số của vi điều khiển)

Thành phần cần thiết


STT Thành phần SL
1 Arduino Uno R3 ATmega328 or Arduino Uno R3 ATmega328 SMD 1
2 USB Cable Type A to B 1
3 AM2301 DHT21 Cảm Biến Nhiệt Độ Độ Ẩm 1
4 Arduino LCD 1602 Keypad Shield 1
5 Jumper Wires / Dây cắm test board
6 UNO Screw Shield V2 1
7 Module 1 Relay 5V Kích Mức Cao/Thấp 1

Cài đặt thư viện và phần mềm

  1. Mở Arduino IDE.
  2. Vào Tools > Manage Libraries.
  3. Tìm và cài đặt thư viện DHT sensor library của Adafruit.
  4. Đảm bảo bạn đã cài thư viện Adafruit Unified Sensor (được yêu cầu kèm theo).

VD1: Hiển thị nhiệt độ độ ẩm đo được lên serial monitor

Sơ đồ kết nối

  • VCC: Kết nối 3.3V hoặc 5V từ Arduino
  • GND: Kết nối chân GND của Arduino
  • DATA: Kết nối với chân kỹ thuật số của Arduino (VD: D2) ở bài viết này mình nối với chân A1

Code

#include <LiquidCrystal.h>
#include <DHT.h>

#define DHTPIN A1          // Chân DATA nối với chân A1
#define DHTTYPE DHT21     // Cảm biến AM2301/DHT21

DHT dht(DHTPIN, DHTTYPE);

float h = dht.readHumidity();   // Đọc độ ẩm
float t = dht.readTemperature(); // Đọc nhiệt độ

void setup() {
  Serial.begin(9600);
  dht.begin();
  Serial.println("truongcongly.com");
  Serial.println("Đang đọc dữ liệu từ cảm biến...");
}

void loop() {
  delay(1000);
  //Đọc dữ liệu và lưu trữ vào các biến hum và temp
  hum = dht.readHumidity();
  temp = dht.readTemperature();

  // Kiểm tra lỗi đọc dữ liệu
  if (isnan(hum) || isnan(temp)) {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  } else {
    // In giá trị nhiệt độ và độ ẩm ra Serial Monitor
    Serial.print("Temperature: ");
    Serial.print(temp, 1);
    Serial.print(" Hummidity: ");
    Serial.print(hum, 1);
    Serial.println();
  }
}

Hướng dẫn kiểm tra

  • Nạp code vào Arduino qua cổng USB.
  • Mở Serial Monitor (Ctrl + Shift + M) trong Arduino IDE để xem dữ liệu cảm biến được in ra mỗi 1 giây.
Nếu mọi thứ được thực hiện chính xác, dữ liệu nhiệt độ và độ ẩm trong đầu ra nối tiếp của màn hình sẽ trông giống như hình ảnh sau.

VD2: Làm mạch khống chế nhiệt độ độ ẩm hiển thị lên lcd và điều khiển ngõ ra relay

Sơ đồ kết nối

Arduino  HC-SR04 Buzzer 
GND GND GND
5V Vcc  
D6 Echo  
D7 Trig  
D13   Positive (+)

Code

#include <LiquidCrystal.h>
#include <DHT.h>
#include <EEPROM.h>

//Constants
#define DHTPIN A1          //what pin we're connected to
#define DHTTYPE DHT21      //DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE);  //Initialize DHT sensor for normal 16mhz Arduino



//Variables
float hum;   //Stores humidity value
float temp;  //Stores temperature value

int setT = 0, setH = 0;
enum Mode { HOT,
            COOL };
Mode mode = HOT;

unsigned long previousMillis = 0;          // Lưu thời gian trước đó
unsigned long lastPressTime = 0;           // Thời gian của lần nhấn trước
const unsigned long delayTime = 100;       // Thời gian nhấn chậm (ms)
const unsigned long rapidDelayTime = 200;  // Thời gian nhấn giữ (ms)
int lastKeyState = 0;                      // Trạng thái của nút trước đó

int homeIndex = 0, settingIndex = 0;


//LCD pin to Arduino
const int pin_RS = 8;
const int pin_EN = 9;
const int pin_d4 = 4;
const int pin_d5 = 5;
const int pin_d6 = 6;
const int pin_d7 = 7;
const int pin_BL = 10;

LiquidCrystal lcd(pin_RS, pin_EN, pin_d4, pin_d5, pin_d6, pin_d7);

byte degree[8] = {
  0B01110,
  0B01010,
  0B01110,
  0B00000,
  0B00000,
  0B00000,
  0B00000,
  0B00000
};

const int Q1 = 2;  //Ngõ ra 1
const int Q2 = 3;  //Ngõ ra 2

char keyPad() {
  int x = analogRead(0);
  if (x < 60) {
    return 'R';
  } else if (x < 200) {
    return 'U';
  } else if (x < 400) {
    return 'D';
  } else if (x < 600) {
    return 'L';
  } else if (x < 800) {
    return 'S';
  }
}
bool clearCursor = false;  // Biến để theo dõi trạng thái xóa cursor

void setTemp() {
  unsigned long currentMillis = millis();  // Lấy thời gian hiện tại
  char buffer[3];                          // Buffer để lưu chuỗi định dạng
  // Cập nhật LCD
  lcd.setCursor(4, 0);
  lcd.print("Settings");
  lcd.setCursor(0, 1);
  lcd.print("T=");
  sprintf(buffer, "%02d", setT);  // Định dạng setT thành 2 chữ số
  lcd.print(buffer);              // In giá trị đã định dạng
  lcd.setCursor(5, 1);
  lcd.print("H=");
  sprintf(buffer, "%02d", setH);  // Định dạng setH thành 2 chữ số
  lcd.print(buffer);              // In giá trị đã định dạng
  lcd.setCursor(10, 1);
  lcd.print("M=");
  if (mode == HOT) {
    sprintf(buffer, "HOT");
  } else {
    sprintf(buffer, "COOL");
  }
  lcd.print(buffer);  // In giá trị đã định dạng

  // Kiểm tra xem có đến thời gian để cập nhật hay không
  if (currentMillis - previousMillis >= 200) {
    previousMillis = currentMillis;  // Cập nhật thời gian cuối cùng

    // Thay đổi trạng thái xóa cursor
    clearCursor = !clearCursor;
    if (clearCursor) {
      lcd.setCursor(4, 1);
      lcd.print("  ");  // Xóa cursor
    } else {
      lcd.setCursor(4, 1);
      lcd.print(".");  // Hiển thị lại chữ H
    }
  }

  // Gọi các hàm khác hoặc xử lý công việc khác ở đây nếu cần
}

void setHum() {
  unsigned long currentMillis = millis();  // Lấy thời gian hiện tại
  char buffer[3];                          // Buffer để lưu chuỗi định dạng
  // Cập nhật LCD
  lcd.setCursor(4, 0);
  lcd.print("Settings");
  lcd.setCursor(0, 1);
  lcd.print("T=");
  sprintf(buffer, "%02d", setT);  // Định dạng setT thành 2 chữ số
  lcd.print(buffer);              // In giá trị đã định dạng
  lcd.setCursor(5, 1);
  lcd.print("H=");
  sprintf(buffer, "%02d", setH);  // Định dạng setH thành 2 chữ số
  lcd.print(buffer);              // In giá trị đã định dạng
  lcd.setCursor(10, 1);
  lcd.print("M=");
  if (mode == HOT) {
    sprintf(buffer, "HOT");
  } else {
    sprintf(buffer, "COOL");
  }
  lcd.print(buffer);  // In giá trị đã định dạng

  // Kiểm tra xem có đến thời gian để cập nhật hay không
  if (currentMillis - previousMillis >= 200) {
    previousMillis = currentMillis;  // Cập nhật thời gian cuối cùng

    // Thay đổi trạng thái xóa cursor
    clearCursor = !clearCursor;
    if (clearCursor) {
      lcd.setCursor(9, 1);
      lcd.print("  ");  // Xóa cursor
    } else {
      lcd.setCursor(9, 1);
      lcd.print(".");  // Hiển thị lại chữ H
    }
  }

  // Gọi các hàm khác hoặc xử lý công việc khác ở đây nếu cần
}

void setMode() {
  unsigned long currentMillis = millis();  // Lấy thời gian hiện tại
  char buffer[3];                          // Buffer để lưu chuỗi định dạng
  // Cập nhật LCD
  lcd.setCursor(4, 0);
  lcd.print("Settings");
  lcd.setCursor(0, 1);
  lcd.print("T=");
  sprintf(buffer, "%02d", setT);  // Định dạng setT thành 2 chữ số
  lcd.print(buffer);              // In giá trị đã định dạng
  lcd.setCursor(5, 1);
  lcd.print("H=");
  sprintf(buffer, "%02d", setH);  // Định dạng setH thành 2 chữ số
  lcd.print(buffer);              // In giá trị đã định dạng
  lcd.setCursor(10, 1);
  lcd.print("M=");
  if (mode == HOT) {
    sprintf(buffer, "HOT");
  } else {
    sprintf(buffer, "COOL");
  }
  lcd.print(buffer);  // In giá trị đã định dạng

  // Kiểm tra xem có đến thời gian để cập nhật hay không
  if (currentMillis - previousMillis >= 200) {
    previousMillis = currentMillis;  // Cập nhật thời gian cuối cùng

    // Thay đổi trạng thái xóa cursor
    clearCursor = !clearCursor;
    if (clearCursor) {
      lcd.setCursor(15, 1);
      lcd.print("  ");  // Xóa cursor
    } else {
      lcd.setCursor(15, 1);
      lcd.print(".");  // Hiển thị lại chữ H
    }
  }

  // Gọi các hàm khác hoặc xử lý công việc khác ở đây nếu cần
}

void switchScreen(int &index) {
  int key = keyPad();  // Lấy trạng thái nút

  // Kiểm tra nút phải
  if (key == 'R') {
    if (lastKeyState != 'R') {   // Nếu nút vừa được nhấn
      lastPressTime = millis();  // Ghi lại thời gian nhấn
    }

    // Tăng nhanh nếu giữ nút
    if (millis() - lastPressTime < rapidDelayTime) {
      // Không làm gì, chỉ tăng sau khi nhấn giữ
    } else if (millis() - lastPressTime >= delayTime) {  // Nếu đã qua thời gian nhấn chậm
      index++;
      if (index > 1) index = 0;
      lcd.clear();
      lastPressTime = millis();  // Cập nhật thời gian
    }
  }

  // Kiểm tra nút trái
  else if (key == 'L') {
    if (lastKeyState != 'L') {   // Nếu nút vừa được nhấn
      lastPressTime = millis();  // Ghi lại thời gian nhấn
    }

    // Tăng nhanh nếu giữ nút
    if (millis() - lastPressTime < rapidDelayTime) {
      // Không làm gì, chỉ tăng sau khi nhấn giữ
    } else if (millis() - lastPressTime >= delayTime) {  // Nếu đã qua thời gian nhấn chậm
      index--;
      if (index < 0) index = 1;
      lcd.clear();
      lastPressTime = millis();  // Cập nhật thời gian
    }
  }

  lastKeyState = key;  // Cập nhật trạng thái nút trước đó
}
void setup() {
  lcd.createChar(1, degree);
  pinMode(Q1, OUTPUT);
  pinMode(Q2, OUTPUT);
  digitalWrite(Q1, LOW);
  digitalWrite(Q2, LOW);
  Serial.begin(9600);
  lcd.begin(16, 2);
  dht.begin();
  lcd.setCursor(2, 0);
  lcd.print("AM2301 DHT21");
  lcd.setCursor(6, 1);
  lcd.print("test");
  delay(500);
  lcd.clear();
}

void loop() {
  //Đọc dữ liệu và lưu trữ vào các biến hum và temp
  hum = dht.readHumidity();
  temp = dht.readTemperature();
  setT = EEPROM.read(5);
  setH = EEPROM.read(6);

  if (isnan(hum) || isnan(temp)) {
    Serial.println(F("Failed to read from DHT sensor!"));
    lcd.setCursor(0, 0);
    lcd.print("DHT sensor error!");
    lcd.setCursor(0, 1);
    lcd.print("                ");
    return;
  } else {
    switchScreen(homeIndex);
    switch (homeIndex) {
      case 0:
        // Kiểm tra xem đã đủ 2000ms kể từ lần cập nhật trước chưa
        if (millis() - previousMillis >= 2000) {
          previousMillis = millis();  // Cập nhật thời gian hiện tại

          lcd.setCursor(0, 0);
          lcd.print("T:");
          lcd.print(temp, 1);
          lcd.write(1);
          lcd.print("C");
          lcd.setCursor(0, 1);
          lcd.print("H:");
          lcd.print(hum, 1);
          lcd.print(" %");

          // In giá trị nhiệt độ và độ ẩm ra Serial Monitor
          Serial.print("Temp: ");
          Serial.print(temp, 1);
          Serial.print(" Hum: ");
          Serial.print(hum, 1);
          Serial.println();

          switch (mode) {
            case HOT:
              if (temp > setT) {
                lcd.setCursor(10, 0);
                lcd.print("Q1: CL");
                digitalWrite(Q1, LOW);
              } else {
                lcd.setCursor(10, 0);
                lcd.print("Q1: OP");
                digitalWrite(Q1, HIGH);
              }
              if (hum > setH) {
                lcd.setCursor(10, 1);
                lcd.print("Q2: CL");
                digitalWrite(Q2, LOW);
              } else {
                lcd.setCursor(10, 1);
                lcd.print("Q2: OP");
                digitalWrite(Q2, HIGH);
              }
              break;
            case COOL:
              if (temp < setT) {
                lcd.setCursor(10, 0);
                lcd.print("Q1: CL");
                digitalWrite(Q1, LOW);
              } else {
                lcd.setCursor(10, 0);
                lcd.print("Q1: OP");
                digitalWrite(Q1, HIGH);
              }
              if (hum < setH) {
                lcd.setCursor(10, 1);
                lcd.print("Q2: CL");
                digitalWrite(Q2, LOW);
              } else {
                lcd.setCursor(10, 1);
                lcd.print("Q2: OP");
                digitalWrite(Q2, HIGH);
              }
          }
        }
        break;
      case 1:
        if (keyPad() == 'S') {
          settingIndex++;
          if (settingIndex > 3) settingIndex = 0;
          lcd.setCursor(0, 1);
          lcd.print("                ");
          while (keyPad() == 'S')
            ;
        }
        switch (settingIndex) {
          case 0:
            {
              char buffer[3];
              lcd.setCursor(4, 0);
              lcd.print("Settings");
              lcd.setCursor(0, 1);
              lcd.print("T=");
              sprintf(buffer, "%02d", setT);  // Định dạng setT thành 2 chữ số
              lcd.print(buffer);              // In giá trị đã định dạng
              lcd.setCursor(5, 1);
              lcd.print("H=");
              sprintf(buffer, "%02d", setH);  // Định dạng setH thành 2 chữ số
              lcd.print(buffer);              // In giá trị đã định dạng
              lcd.setCursor(10, 1);
              lcd.print("M=");
              if (mode == HOT) {
                sprintf(buffer, "HOT");
              } else {
                sprintf(buffer, "COOL");
              }
              lcd.print(buffer);  // In giá trị đã định dạng
              break;
            }
          case 1:
            {
              setTemp();
              int key = keyPad();
              // Kiểm tra nút lên
              if (key == 'U') {
                if (lastKeyState != 'U') {   // Nếu nút vừa được nhấn
                  lastPressTime = millis();  // Ghi lại thời gian nhấn
                }

                // Tăng nhanh nếu giữ nút
                if (millis() - lastPressTime < rapidDelayTime) {
                  // Không làm gì, chỉ tăng sau khi nhấn giữ
                } else if (millis() - lastPressTime >= delayTime) {  // Nếu đã qua thời gian nhấn chậm
                  setT++;
                  if (setT > 80) setT = 0;
                  lastPressTime = millis();  // Cập nhật thời gian
                }
              }
              // Kiểm tra nút xuông
              if (key == 'D') {
                if (lastKeyState != 'D') {   // Nếu nút vừa được nhấn
                  lastPressTime = millis();  // Ghi lại thời gian nhấn
                }

                // Tăng nhanh nếu giữ nút
                if (millis() - lastPressTime < rapidDelayTime) {
                  // Không làm gì, chỉ tăng sau khi nhấn giữ
                } else if (millis() - lastPressTime >= delayTime) {  // Nếu đã qua thời gian nhấn chậm
                  setT--;
                  if (setT < 0) setT = 80;

                  lastPressTime = millis();  // Cập nhật thời gian
                }
              }
              EEPROM.write(5, setT);
              break;
            }
          case 2:
            {
              int key = keyPad();
              // Kiểm tra nút lên
              if (key == 'U') {
                if (lastKeyState != 'U') {   // Nếu nút vừa được nhấn
                  lastPressTime = millis();  // Ghi lại thời gian nhấn
                }

                // Tăng nhanh nếu giữ nút
                if (millis() - lastPressTime < rapidDelayTime) {
                  // Không làm gì, chỉ tăng sau khi nhấn giữ
                } else if (millis() - lastPressTime >= delayTime) {  // Nếu đã qua thời gian nhấn chậm
                  setH++;
                  if (setH > 80) setH = 0;
                  lastPressTime = millis();  // Cập nhật thời gian
                }
              }
              // Kiểm tra nút xuông
              if (key == 'D') {
                if (lastKeyState != 'D') {   // Nếu nút vừa được nhấn
                  lastPressTime = millis();  // Ghi lại thời gian nhấn
                }

                // Tăng nhanh nếu giữ nút
                if (millis() - lastPressTime < rapidDelayTime) {
                  // Không làm gì, chỉ tăng sau khi nhấn giữ
                } else if (millis() - lastPressTime >= delayTime) {  // Nếu đã qua thời gian nhấn chậm
                  setH--;
                  if (setH < 0) setH = 80;

                  lastPressTime = millis();  // Cập nhật thời gian
                }
              }
              setHum();
              EEPROM.write(6, setH);
              break;
            }
          case 3:
            {
              int key = keyPad();
              // Kiểm tra nút lên
              if (key == 'U') {
                if (lastKeyState != 'U') {   // Nếu nút vừa được nhấn
                  lastPressTime = millis();  // Ghi lại thời gian nhấn
                }

                // Tăng nhanh nếu giữ nút
                if (millis() - lastPressTime < rapidDelayTime) {
                  // Không làm gì, chỉ tăng sau khi nhấn giữ
                } else if (millis() - lastPressTime >= delayTime) {  // Nếu đã qua thời gian nhấn chậm
                                                                     // Thay đổi giữa HOT và COOL
                  if (mode == HOT) {
                    mode = COOL;
                  } else {
                    mode = HOT;
                  }
                  lastPressTime = millis();  // Cập nhật thời gian
                }
              }
              // Kiểm tra nút xuông
              if (key == 'D') {
                if (lastKeyState != 'D') {   // Nếu nút vừa được nhấn
                  lastPressTime = millis();  // Ghi lại thời gian nhấn
                }

                // Tăng nhanh nếu giữ nút
                if (millis() - lastPressTime < rapidDelayTime) {
                  // Không làm gì, chỉ tăng sau khi nhấn giữ
                } else if (millis() - lastPressTime >= delayTime) {  // Nếu đã qua thời gian nhấn chậm
                  if (mode == HOT) {
                    mode = COOL;
                  } else {
                    mode = HOT;
                  }
                  lastPressTime = millis();  // Cập nhật thời gian
                }
              }
              setMode();
              EEPROM.write(7, mode);
              break;
            }
        }
        break;
    }
  }
}

Video

Sự khác nhau giữa DHT11, DHT22 và DHT21 (AM2301)

Phạm vi và độ chính xác của phép đo nhiệt độ

  • DHT11: -20 đến 60°C với độ chính xác ±2°C
  • DHT21: -40 đến 80°C với độ chính xác ±0,3°C
  • DHT22: -40 đến 80°C với độ chính xác ±0,5°C

Phạm vi và độ chính xác của phép đo độ ẩm

  • DHT11: 5 đến 95% với độ chính xác ±5%
  • DHT21: 0 đến 99% với độ chính xác ±2%
  • DHT22: 0 đến 99% với độ chính xác ±2%

Nhận xét

Mới hơn Cũ hơn