Arduino:I2C 16x2 LCD 攝氏溫度、濕度與時間顯示電路
Clock with Thermo-hygrometer Using Arduino and I2C 16x2 LCD
功能
圖一 LCD 顯示攝氏溫度、相對濕度與時間
電路接線圖
圖二 Arduino:I2C 16x2 LCD 顯示攝氏溫度、濕度與時間的電路接線圖
LCD 與感測器操作所需函數庫
LiquidCrystal_I2C.h 與 DHT.h 是兩個必要引入的標頭檔。為了能成功引入這兩個標頭檔,我們可在 https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library 網址下載 Arduino-LiquidCrystal-I2C-library-master 資料夾,並在 https://github.com/adafruit/DHT-sensor-library 下載 DHT-sensor-library 資料夾,如圖三所示再將此二資料夾複製到電腦的 C: > 使用者 > user > 文件 > Arduino > libraries 資料夾下 ( 若 libraries 資料夾不存在,則自行建立) ,即可在程式中引用上述的兩個標頭檔。
圖三 libraries 資料夾
LCD 與感測器操作所需的建構式與方法說明
這是 LiquidCrystal_I2C 類別提供的建構式,用來建構基於該類別的物件,物件建立後才可使用該類別所提供的方法 (method)。
參數說明:
- lcd_addr: 若 LCD 模組採用的是 PCF8754 晶片提供的 I2C 介面,位址設置為 0x27;若採用的是 PCF8574AN,則位址設置為 0x3F。
- lcd_cols:LCD 的行數 (Columns),在此為 16。
- lcd_rows:LCD 的列數 (Rows),在此為 2。
- charsize:LCD 顯示字元大小,預設為 5x8 像素。
DHT(uint8_t pin, uint8_t type, uint8_t count = 6)
這是 DHT 類別提供的建構式。
參數說明:
- pin:指定連接到 DHT11 、DHT21 或 DHT22 的 Arduino 接腳。
- type:指定採用的 DHT 系列溫溼度感測器類型,本電路採用的是 DHT11。
- count:感測器數量,預設值為 6,是一個可以不使用的參數,當不使用在編譯時也不會引發編譯器發出警告訊息。
void DHT物件.begin(uint8_t usec = 55)
這是 DHT 類別提供的方法,用來初始化 DHT 物件。
參數說明:
- usec:視情況在讀取 DHT 前會傳入的上拉 (pull-up) 時間,單位為微秒,預設值為 55。
float DHT物件.readTemperature(bool S = false, bool force = false)
這是 DHT 類別提供的方法,用來讀取 DHT 溫濕度感測器偵測到的溫度值,回傳溫度值的資料型態為浮點數。
參數說明:
- S:設定刻度 (Scale) 用,當為 true 時,表示讀取華氏溫度值 (Fahrenheit);反之為 false 時,則表示讀取攝氏溫度值 (Celcius)。
- force:設定是否在強制模式,預設為 false。
float DHT物件.readHumidity(bool force = false)
這是 DHT 類別提供的方法,用來讀取 DHT 溫濕度感測器偵測到的濕度值,回傳濕度值的資料型態為浮點數。
參數說明:
- force:設定是否在強制模式,預設為 false。
void LiquidCrystal_I2C物件.begin()
這是 LiquidCrystal_I2C 類別提供的方法,用來初始化 LiquidCrystal_I2C 物件。
void LiquidCrystal_I2C物件.clear()
這是 LiquidCrystal_I2C 類別提供的方法,用來清除目前顯示的所有字元。下一次列印/寫入的操作將從螢幕的第一個位置開始。
void LiquidCrystal_I2C物件.createChar(uint8_t location, uint8_t charmap[])
這是 LiquidCrystal_I2C 類別提供的方法,用來將自建的字元碼儲存進字元產生器唯讀記憶體 (character generator ROM, CGROM) 中。
參數說明:
- location:自建的字元碼在 CGROM 中所要儲存的起始位址,若是要儲存 5x8 大小字元,會使用到 0~7 這 8 個位址;若是要儲存 5x10 大小字元,則會使用到 0~3 這 4 個位址。
- charmap[]:存放自建的字元碼的陣列位址。
size_t LiquidCrystal_I2C物件.write(uint8_t value)
這是 LiquidCrystal_I2C 類別提供的方法,用來將 CGROM 中起始位址為 value 的資料列印在螢幕上。執行後回傳值為一無號整數 1。
void LiquidCrystal_I2C物件.setCursor(uint8_t col, uint8_t row)
這是 LiquidCrystal_I2C 類別提供的方法,用來將螢幕上的游標定位至第 col 行第 row 列位置。
void LiquidCrystal_I2C物件.printstr(const char c[])
這是 LiquidCrystal_I2C 類別提供的方法,用來將字串列印在游標定位處。由於 printstr() 方法執行時,其實是呼叫 print() 來完成列印工作,因此也可以直接使用 print() 方法。
自建的度符號字元碼
如圖四所示:
圖四 度符號的 5x8 字元碼
程式碼
C++
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
int I2C_Address = 0x27; /* 宣告 I2C 位址 */
LiquidCrystal_I2C I2C_LCD(I2C_Address, 16, 2); /* 建構 LiquidCrystal_I2C 物件為每列
最多 16 字元,2 列 */
int dhtDataPin = A0;
int DIP_Switch = 7;
int hourSetPb = 6;
int minuteSetPb = 5;
DHT myDHT(dhtDataPin, DHT11);
byte degreeCharacter[] = {0b00110, /* 度符號的自建字元 */
0b01001,
0b01001,
0b00110,
0b00000,
0b00000,
0b00000,
0b00000};
float tempInDegreesCelsius = 0.0, humidity = 0.0;
int displayData[] = {0,0,0,0,0,0}; /* 顯示暫存區初值對應的顯示格式為 00:00:00 */
/* displayData[5]:時的十位數; displayData[4]:時的個位數 */
/* displayData[3]:分的十位數; displayData[2]:分的個位數 */
/* displayData[1]:秒的十位數; displayData[0]:秒的個位數 */
unsigned long CLK = 0;
byte seconds = 0;
byte minutes = 0;
byte hours = 0;
void minSetPB();
void hrSetPB();
bool SW;
void displayLCD(); /* 宣告 LCD 顯示函式 */
int hourLast = 0;
int minuteLast = 0;
void setup() {
myDHT.begin();
I2C_LCD.begin(); /* 啟動 LCD */
I2C_LCD.clear(); /* 清除螢幕 */
I2C_LCD.createChar(0, degreeCharacter);
I2C_LCD.setCursor(2,0);
I2C_LCD.printstr(":");
I2C_LCD.setCursor(5,0);
I2C_LCD.printstr(":");
I2C_LCD.setCursor(15,0);
I2C_LCD.printstr("C");
I2C_LCD.setCursor(14,0);
I2C_LCD.write(0);
I2C_LCD.setCursor(14,1);
I2C_LCD.printstr("%");
displayLCD();
CLK = millis(); /* 取得系統時間 */
}
void loop(){
SW = digitalRead(DIP_Switch); /* 讀取指撥開關狀態 */
while(SW) /* 指撥開關 ON 時 SW = "0",計時模式;指撥開關 OFF 時 SW = "1",調整模式 */
{
seconds = 0; /* 秒數歸零 */
displayData[1] = 0;
displayData[0] = 0;
SW = digitalRead(DIP_Switch); /* 持續讀取指撥開關狀態 */
minSetPB(); /* 調整分 */
hrSetPB(); /* 調整時 */
displayLCD(); /* LCD 顯示 */
hourLast = 0;
minuteLast = 0;
CLK = millis(); /* 取得系統時間 */
}
if (hourLast == 2 && minuteLast == 50){
seconds++; /* 修正秒數誤差 (測試發現每 2 小時 50 分約慢 1 秒) */
displayData[1] = seconds/10; // 取得秒之十位數
displayData[0] = seconds%10; // 取得秒之個位數
displayLCD(); // 顯示時間
hourLast = 0;
minuteLast = 0;
}
while(millis() - CLK < 999); /* 等待 1 秒時間到 */
CLK = millis(); // 儲存目前系統時間
if (++seconds == 60){
seconds = 0;
minuteLast++;
if (++minutes == 60){
minutes = 0;
hourLast++;
if(++hours == 24){
hours = 0;
}
}
if(minuteLast == 60)
minuteLast = 0;
}
if(seconds == 0){
displayData[3] = minutes/10; /* 取得分之十位數 */
displayData[2] = minutes%10; /* 取得分之個位數 */
}
if(minutes == 0){
displayData[5] = hours/10; /* 取得時之十位數 */
displayData[4] = hours%10; /* 取得時之個位數 */
}
displayData[1] = seconds/10; /* 取得秒之十位數 */
displayData[0] = seconds%10; /* 取得秒之個位數 */
displayLCD(); /* LCD 顯示 */
}
void minSetPB()
{
int buttonStatus = digitalRead(minuteSetPb);
if(buttonStatus == 0){
delay(200);
buttonStatus = digitalRead(minuteSetPb);
if (buttonStatus == 1){
seconds = 0;
minutes++;
if(minutes == 60)
minutes = 0;
displayData[3] = minutes/10; /* 取得分之十位數 */
displayData[2] = minutes%10; /* 取得分之個位數 */
displayLCD();
}
}
}
void hrSetPB()
{
int buttonStatus = digitalRead(hourSetPb);
if(buttonStatus == 0){
delay(200);
buttonStatus = digitalRead(hourSetPb);
if (buttonStatus == 1){
hours++;
if (hours == 24)
hours = 0;
displayData[5] = hours /10;
displayData[4] = hours%10;
displayLCD();
}
}
}
void displayLCD()
{
tempInDegreesCelsius = myDHT.readTemperature(0);
humidity = myDHT.readHumidity();
I2C_LCD.setCursor(9,0);
I2C_LCD.print(tempInDegreesCelsius);
I2C_LCD.setCursor(9,1);
I2C_LCD.print(humidity);
I2C_LCD.setCursor(0,0);
I2C_LCD.print(displayData[5]);
I2C_LCD.print(displayData[4]);
I2C_LCD.setCursor(3,0);
I2C_LCD.print(displayData[3]);
I2C_LCD.print(displayData[2]);
I2C_LCD.setCursor(6,0);
I2C_LCD.print(displayData[1]);
I2C_LCD.print(displayData[0]);
}
實際 LCD 顯示
圖五 實際 LCD 顯示結果





留言
張貼留言