# LAB6 使用 OLED 顯示溫溼度與溫度過高警報器設計
## DEMO
<iframe width="560" height="315" src="https://www.youtube.com/embed/TE4zFPAgy0g?si=dw03MAMgLYe8EH4y" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
## 電路圖

## 程式說明
- 本程式碼使用多緒撰寫,有三個任務
- buttonTask:處理按鈕資料的任務
- DHT11Task:處理溫濕度讀取的任務
- buzzerTask:處理蜂鳴器叫的任務
- 而OLED的刷新寫在main loop當中
- buttonTask
```cpp=
void buttonTask(void *pvParameters) {
while (true) {
switch (inc.check()) {
case Switch::RELEASED_FROM_PRESS:
case Switch::PRESSING:
maximum++;
if (maximum >= 35) maximum = 35;
break;
}
switch (dec.check()) {
case Switch::RELEASED_FROM_PRESS:
case Switch::PRESSING:
maximum--;
if (maximum <= 20) maximum = 20;
break;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
```
- DHT11Task
```cpp=
void DHT11Task(void *pvParameters){
while(true){
int err = SimpleDHTErrSuccess;
if ((err = dht11.read(pinDHT11, &temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
Serial.println("DHT11 read error");
Serial.println(err);
}
vTaskDelay(3000/ portTICK_PERIOD_MS);
}
}
```
- buzzerTask
- ~~chatGPT寫的~~
```cpp=
void buzzerTask(void *pvParameters){
int melody[] = {
NOTE_E4, NOTE_E4, NOTE_F4, NOTE_G4,
NOTE_G4, NOTE_F4, NOTE_E4, NOTE_D4,
NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4,
NOTE_E4, NOTE_D4, NOTE_D4
};
int noteDurations[] = {
4, 4, 4, 4,
4, 4, 4, 4,
4, 4, 4, 4,
4, 4, 4
};
while(true){
printf("%d\n",temperature >= maximum);
if (temperature >= maximum){
for (int thisNote = 0; thisNote < 14; thisNote++) {
if (temperature < maximum) {
ledcWriteTone(0, 0);
break;
}
int duration = 1000 / noteDurations[thisNote];
ledcWriteTone(0, melody[thisNote]);
vTaskDelay(duration / portTICK_PERIOD_MS);
ledcWriteTone(0, 0);
vTaskDelay(50/ portTICK_PERIOD_MS);
}
ledcWriteTone(0, 0);
}
}
}
```
- OLED刷新
```cpp=
u8g2.firstPage();
do {
msg = "溫度 : " + String((int)temperature) + " °C";
u8g2.setCursor(0, 20);
u8g2.print(msg.c_str());
msg = "濕度 : " + String((int)humidity) + " %";
u8g2.setCursor(0, 40);
u8g2.print(msg.c_str());
msg = "臨界值 : " + String((int)maximum) + " °C";
u8g2.setCursor(0, 60);
u8g2.print(msg.c_str());
} while (u8g2.nextPage());
Serial.printf("Temperature: %d, Humidity: %d\n", temperature, humidity);
delay(1000);
```
- `u8g2.nextPage()`其實就是送出到OLED上,程式碼當中使用loop是如果寫入失敗在重新寫
- 實驗上發現`delay(1000)`較為穩定
## 程式碼
```cpp=
#include <U8g2lib.h>
#include <SimpleDHT.h>
#include <switch.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "melody.h"
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define pinDHT11 4
#define incSwitchPin 27
#define decSwitchPin 26
#define buzzerPin 33
String msg;
int maximum = 25;
byte temperature = 0;
byte humidity = 0;
SimpleDHT11 dht11(pinDHT11);
Switch inc(incSwitchPin, LOW, true);
Switch dec(decSwitchPin, LOW, true);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
// 按鈕處理的 Task
void buttonTask(void *pvParameters) {
while (true) {
// 檢查增加按鈕狀態
switch (inc.check()) {
case Switch::RELEASED_FROM_PRESS:
case Switch::PRESSING:
maximum++;
if (maximum >= 35) maximum = 35;
break;
}
// 檢查減少按鈕狀態
switch (dec.check()) {
case Switch::RELEASED_FROM_PRESS:
case Switch::PRESSING:
maximum--;
if (maximum <= 20) maximum = 20;
break;
}
// 小延遲,避免過度頻繁檢查
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
void DHT11Task(void *pvParameters){
while(true){
int err = SimpleDHTErrSuccess;
if ((err = dht11.read(pinDHT11, &temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
Serial.println("DHT11 read error");
Serial.println(err);
}
vTaskDelay(3000/ portTICK_PERIOD_MS);
}
}
void buzzerTask(void *pvParameters){
int melody[] = {
NOTE_E4, NOTE_E4, NOTE_F4, NOTE_G4,
NOTE_G4, NOTE_F4, NOTE_E4, NOTE_D4,
NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4,
NOTE_E4, NOTE_D4, NOTE_D4
};
int noteDurations[] = {
4, 4, 4, 4,
4, 4, 4, 4,
4, 4, 4, 4,
4, 4, 4
};
while(true){
printf("%d\n",temperature >= maximum);
if (temperature >= maximum){
for (int thisNote = 0; thisNote < 14; thisNote++) {
if (temperature < maximum) {
ledcWriteTone(0, 0);
break;
}
int duration = 1000 / noteDurations[thisNote];
ledcWriteTone(0, melody[thisNote]);
vTaskDelay(duration / portTICK_PERIOD_MS);
ledcWriteTone(0, 0);
vTaskDelay(50/ portTICK_PERIOD_MS);
}
ledcWriteTone(0, 0);
}
}
}
void setup() {
Serial.begin(9600);
u8g2.begin();
u8g2.enableUTF8Print();
u8g2.setFont(u8g2_font_unifont_t_chinese1);
pinMode(buzzerPin, OUTPUT);
ledcSetup(0, 3000, 10);
ledcAttachPin(buzzerPin, 0);
// 創建按鈕檢查任務
xTaskCreate(buttonTask, "Button Task", 2048, NULL, 1, NULL);
xTaskCreate(DHT11Task, "DHT11 Task", 2048, NULL, 1, NULL);
xTaskCreate(buzzerTask, "buzzer Task", 2048, NULL, 1, NULL);
}
void loop() {
// 顯示更新
u8g2.firstPage();
do {
msg = "溫度 : " + String((int)temperature) + " °C";
u8g2.setCursor(0, 20);
u8g2.print(msg.c_str());
msg = "濕度 : " + String((int)humidity) + " %";
u8g2.setCursor(0, 40);
u8g2.print(msg.c_str());
msg = "臨界值 : " + String((int)maximum) + " °C";
u8g2.setCursor(0, 60);
u8g2.print(msg.c_str());
} while (u8g2.nextPage());
Serial.printf("Temperature: %d, Humidity: %d\n", temperature, humidity);
delay(1000);
}
```