第09篇 ESP32 溫濕度顯示器(DHT11+1602 LCD)

溫濕度感測是物聯網實驗重要的一環,收集大量的資料可以了解溫濕度數值對環境的影響,例如偵測倉庫的溫濕度

我的ESP32實做書籍:我出書了 ESP32 物聯網專題
博客來網址:https://www.books.com.tw/products/0010901195


前言

溫濕度感測是物聯網實驗重要的一環,收集大量的資料可以了解溫濕度數值對環境的影響,例如偵測倉庫的溫濕度,可以避免商品受潮而故障或腐敗,監控教室的溫濕度也可以避免太熱影響到學生的學習效率,而本章將會以DHT11感測器來感測環境溫濕度,並利用1602LCD顯示數值。

溫濕度感測(DHT11)

1.感測器種類介紹

在Arduino中有許多感測溫度的感測器,例如DHT系列(11或22)、DS18B20、LM35等,其中LM35及DS18B20為三極體外型,DHT系列則分成藍色DHT11及白色DHT22,其規格比較表如下,LM35與DS18B20在感測上效果差不多,可以精確到小數點以下兩位,市售DS18B20常包覆不鏽鋼,可以用於偵測一些極端環境,例如測量水族箱溫度,或者化學溶液中。本書則是以DHT11為範例,可同時測量溫度及濕度變化。

各式溫度感測器
感測器型號感測內容外觀感測範圍誤差通訊方式
LM35溫度三極體-55 ~ 150℃±0.25℃類比
DS18B02溫度三極體-55 ~125℃±0.5℃數位
DHT11溫度、濕度藍色網狀溫度: 0 ~ 50℃
濕度: 20 ~ 95%
±2℃
±5%
數位
DHT12溫度、濕度白色網狀溫度:-40~ -80℃
濕度: 0 ~ 100%
±0.5℃
±2%
數位

DHT11的外觀如下圖,由左到右依序為訊號(s)、VCC(+)及GND(-)及三個腳位,其中VCC因為位置不夠因此沒標明,特別要說明的是DHT11與其他感測器比較起來較為脆弱,若VCC、GND接反了很快就會燒毀,因此在接線上必須特別注意,以免發生危險。

DHT11感測器外觀

DHT系列規格上雖然標明為數位傳輸,但是非digitalRead這麼簡單,而是一連串的訊號交換及解譯才能獲得數值,為了減少這個複雜的過程,我們可以安裝simpleDHT.h程式庫,只要呼叫一行指令就可以直接取得感測器數值。

DHT 資料時序圖
安裝simpleDHT程式庫

2.安裝simpleDHT程式庫

安裝simpleDHT程式庫後
接下來我們將DHT11與ESP32連接起來,其腳位圖如下,除了GND及VCC之外,將DHT11的訊號(S)連接到ESP32的GPIO4,不過若要將DHT11接在GPIO12或GPIO2時,則須等程式上傳完畢後,再將DHT11接上,否則可能會遇到Time out的錯誤訊息。

DHT11 ESP32接線圖

DHT11請勿插在GPIO2或12腳位,將會導致Time out 錯誤

Time out 錯誤畫面

完成接線後,我們開啟上方功能表/檔案/範例/simpleDHT,找到程式庫提供的範例程式DHT11Default,開啟後,僅須修改第7行的腳位,從預設的2改為4即可執行。

#include <SimpleDHT.h>
int pinDHT11 = 4;
SimpleDHT11 dht11(pinDHT11);

void setup() {
  Serial.begin(115200);
}

void loop() {
  // start working...  Serial.println("=================================");
  Serial.println("Sample DHT11...");
  
  // read without samples.
  byte temperature = 0;
  byte humidity = 0;
  int err = SimpleDHTErrSuccess;
  if ((err = dht11.read(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
    Serial.print("Read DHT11 failed, err="); Serial.println(err);delay(1000);
    return;
  }
  Serial.print("Sample OK: ");
  Serial.print((int)temperature); Serial.print(" *C, "); 
  Serial.print((int)humidity); Serial.println(" H");  
  // DHT11 sampling rate is 1HZ.
  delay(1500);
}

上述的範例中,關鍵語法是

dht11.read(&temperature, &humidity, NULL))

這句的意思是開始讀取dht11的溫濕度數值,並將結果放置在temperature及humidity變數的位址,也就是說,當執行完dht11.read指令後,就可以得到環境的溫濕度數值了,但因為接線錯誤或者感測器故障而讀取失敗的話,則會顯示錯誤並且返回(return)。

而範例程式在顯示溫濕度時,前方加上(int)則是為了型別轉換,例如(int)temperature,因為dht11.read的資料格式是byte,讀取出來後則用(int)轉成整數。

程式在顯示溫濕度時,後續我們在使用上也建議都加上(int)以免出錯

DHT11讀取正常與錯誤的訊息

1602LCD顯示器

1.LCD顯示器種類介紹

上一節當我們想知道溫濕度資料時,可以透過序列監控視窗查看,可是如果手邊沒有電腦可以開啟序列監控,那要如何知道DHT11所感測的資料呢?

其實Arduino系列有相當多種的顯示裝置可以利用,例如TFT、OLED,甚至是電子紙(ePaper)都相當流行,各有不同的適用環境,不過對初學者來說最簡單的應該是點陣式LCD(Liquid Crystal Display)了,現在大部份都製作為I2C界面的1602 LCD模組(LCM:Liquid Crystal Display Module),也就是2行每行16個字的LCD顯示模組,為了介紹方便,以下我們通稱為1602LCD,除了1602之外,2004規格也很常見(4行x 20字),讀者可以依需求自行購買。

目前常見顯示器(多色電子紙仍不普遍)

讀者在購買1602LCD時,務必要選擇具有I2C界面的LCD,所謂的I2C是Inter-Integrated Circuit的簡稱,也可寫作IIC(讀法為I-squared-C)是一種相當普遍的低速傳輸模式,由於可以利用定址方式連接最多112個節點,大量用於單晶片、手機、主機板之內,I2C界面僅須SDA及SCL兩個腳位,Arduino系列都有支援,若購入沒有I2C界面LCD,則需要用到6~8個腳位,安裝控制都會相當複雜,相形之下I2C安裝較為簡單,且直接呼叫函式庫即可顯示內容相當方便。

I2C界面與1602LCD

2.ESP32連接LCD與設定

接下來我們將ESP32與1602LCD連線,除了VCC及GND之外,ESP32預設的I2C界面為SDA是GPIO21、SCL為GPIO22,因此將LCD的SDA->GPIO21,SCL->GPIO22即可。不過使用1602LCD仍有許多常見問題,這裡大致列出如下:

  1. 未使用5V:LCD須使用5V電源驅動,否則背光可能無法點亮。
  2. 背光接口沒接:當接上5V點源,LCD仍無法點亮,這可能是背光接口沒接,通常背光接口會有一個Jump將腳位短路,若JUMP遺失可使用母母杜邦線對接即可。
  3. 位址設定錯誤:i2C裝置都會有一個位址,宣告錯誤的位址是不會有任何反應的,所以必須先以做一次i2c scan掃描,取得正確位址。
  4. 對比沒調整:若上述都完成,依然無法顯示文字,那應該是對比沒調整,請利用小螺絲起子旋轉對比旋鈕,調整一個讀者覺得清楚的對比。
    我們先來說3的I2C位址掃描是什麼?上一段提及I2C可以透過定址連接最多112個裝置,那麼不同裝置必然會有不同的位址,當我們要執行程式時就要先指定這個命令是要給哪一個裝置執行,不然命令會被錯誤的裝置接收,就導致運作不正常,因此我們要在使用前先利用以下的程式掃描裝置的位址是多少。
i2C裝置接線圖
#include <Wire.h>

void setup()
{
  Serial.begin (115200);  
  Wire.begin (21, 22);   // sda= GPIO_21 /scl= GPIO_22
}

void Scanner ()
{
  Serial.println ();
  Serial.println ("I2C scanner. Scanning ...");
  byte count = 0;

  Wire.begin();
  for (byte i = 8; i < 120; i++)
  {
    Wire.beginTransmission (i); // Begin I2C transmission Address (i)
    if (Wire.endTransmission () == 0) //0=success(ACK response) 
    {
      Serial.print ("Found address: ");
      Serial.print (i, DEC);
      Serial.print (" (0x");
      Serial.print (i, HEX);     // PCF8574 7 bit address
      Serial.println (")");
      count++;
    }
  }
  Serial.print ("Found ");      
  Serial.print (count, DEC);        // numbers of devices
  Serial.println (" device(s).");
}

void loop()
{
  Scanner ();
  delay (5000);
}

程式上傳後,若接線正確沒問題的話,就會出現下方的內容,其中
I2C device found at address 0x27

代表在ESP32上有找到一組I2C裝置位址在0x27,除了0x27之外,1602LCD也會有0x30或其他數值,這個數值很重要,請先把數值寫下來,等等就會用到。

I2C 掃描結果

3.安裝LCD程式庫

接下來我們需要安裝LCD的程式庫才能在上面顯示文字,利用上方功能表/草稿碼/匯入程式庫/管理程式庫

打開程式庫管理員之後,在上方空白處輸入關鍵字:「LiquidCrystal I2C」,並找到作者為Frank de Brabander的程式庫後(大約在中間的位置),點選右側的安裝即可。

安裝液晶螢幕程式庫

完成安裝後,我們可以開啟下面的HelloWorld程式,裡面的關鍵語法包括:

1.指定LCD位址與規格:

LiquidCrystal_I2C lcd(0x27,16,2);

其中0x27代表我們LCD的I2C位址,此處讀者應填入上方i2c scan時所獲得的位址,而16代表我們的LCD是一行16個字,2代表是2排字。

2.LCD初始化:

lcd.init();

代表lcd初始化,LCD使用前必須要執行一次初始化。

3.開啟背光:

lcd.backlight();

代表開啟LCD背光,若缺少這句LCD無背光顯示。

4.移動游標

lcd.setCursor(3,0);

代表將游標移至第0行,第3個字(下圖○A的位置),顯示文字前要先利用這個指令指定從何處開始顯示文字。

5.印出文字

lcd.print("Hello, world!");

則是在步驟4指定的游標處印出文字,這裡要注意若印出的文字超過螢幕寬度,並不會自動換行,會直接被隱藏,因此顯示時讀者須自行計算游標與文字數量。

6.清除內容

lcd.clear();//清除所有內容

清除LCD內的內容,等候顯示更新後的資料。

LCD游標位置

4.LCD完整程式碼

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);//設定LCD位址與大小

void setup()
{
  lcd.init();   //初始化LCD
  lcd.backlight(); //開啟LCD背光
}

void loop(){
  lcd.setCursor(3,0);//設定游標
  lcd.print("Hello, world!");//印出文字
  lcd.setCursor(2,1);//設定游標
  lcd.print("ESP32 LCD Test!"); //印出文字
  delay(1000);
  lcd.clear();//清除所有內容
  delay(1000);

}

若您的LCD並沒有出現Hello World字樣,那請您依序本節一開始時所說明的LCD使用注意4個事項進行檢查,排除問題。
當顯示沒問題的話,就可以將兩隻範例程式合在一起,也就是把DHT11溫濕度的DHT11default程式碼及LCD的HelloWorld結合在一起,以本例而言,我們將較為簡單的LCD HelloWorld程式的setup()、loop()、以及最上方宣告區都搬到DHT11default中,整合後的程式如下。

#include <SimpleDHT.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
int pinDHT11 = 14;
SimpleDHT11 dht11(pinDHT11);
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
  Serial.begin(115200);
  lcd.init();
  lcd.backlight();
}

void loop() {
 Serial.println("=================================");
  Serial.println("Sample DHT11...");
  byte temperature = 0;
  byte humidity = 0;
  int err = SimpleDHTErrSuccess;
  if ((err = dht11.read(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
    Serial.print("Read DHT11 failed, err="); Serial.println(err); delay(1000);
    return;
  }

  Serial.print("Sample OK: ");
  Serial.print((int)temperature); Serial.print(" *C, ");
  Serial.print((int)humidity); Serial.println(" H");
  lcd.clear();
  lcd.setCursor(0, 0); //先設定游標
  lcd.print("Temperature: ");//顯示溫度
  lcd.print((int)temperature);
  lcd.print("c");
  lcd.setCursor(0, 1); //顯示溼度
  lcd.print("Humidity:    ");
  lcd.print((int)humidity);
  lcd.print("%");
  delay(1500);
}
DHT11結合LCD螢幕成果

延伸閱讀

???? 第十篇 ESP32 WiFi網路連線即時PM2.5顯示器

Leave a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *