---
# System prepended metadata

title: LAB6 使用 OLED 顯示溫溼度與溫度過高警報器設計
tags: [嵌入式系統]

---

# 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>

## 電路圖
![image](https://hackmd.io/_uploads/ByADSPwZJe.png)

## 程式說明
- 本程式碼使用多緒撰寫，有三個任務
    - 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);
}
```