# 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); } ```