# LAB09 自動降溫控制 ## DEMO <iframe width="560" height="315" src="https://www.youtube.com/embed/Dh5zqItUowA?si=WRT53tQFTmeiFQRc" 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/H1yEFwN41g.png) ## 程式碼 - 處理按鈕的任務 ```cpp= void buttonTask(void *pvParameters) { while (true) { // 切換模式按鈕 switch (change.check()) { case Switch::RELEASED_FROM_PRESS: case Switch::PRESSING: settingMode = !settingMode; break; } // 增加按鈕 if (settingMode) { switch (inc.check()) { case Switch::RELEASED_FROM_PRESS: case Switch::PRESSING: thresholdTemp++; if (thresholdTemp > 35){ thresholdTemp = 35; } break; } } // 減少按鈕 if (settingMode) { switch (dec.check()) { case Switch::RELEASED_FROM_PRESS: case Switch::PRESSING: thresholdTemp--; if (thresholdTemp < 20){ thresholdTemp = 20; } break; } } } } ``` - 處理溫度的程式 - 熱敏電阻的處理 ```cpp= const float ntcResistance = 10000.0; // NTC的標稱電阻值 (10kΩ) const float nominalTemperature = 25.0; // NTC的標稱溫度 (25°C) const float betaValue = 3950.0; // NTC的Beta值 (通常為3950) int rawValue = analogRead(ntcPin); // 計算NTC的電阻值 float resistance = ntcResistance / (4095.0 / rawValue - 1.0); // 計算溫度(攝氏度) float steinhart = resistance / ntcResistance; // 計算Steinhart-Hart公式的中間值 steinhart = log(steinhart); // 自然對數 steinhart /= betaValue; // 除以Beta值 steinhart += 1.0 / (nominalTemperature + 273.15); // 加上(1 / T0) steinhart = 1.0 / steinhart; // 取倒數,得到溫度(Kelvin) steinhart -= 273.15; // 轉換為攝氏度 ``` - 繼電器的程式 ```cpp= void fanTask(void *pvParameters) { while (true) { if (temperature > thresholdTemp) { digitalWrite(relayPin, HIGH); // 啟動繼電器 } else { digitalWrite(relayPin, LOW); // 關閉繼電器 } } } ``` - OLED輸出 ```cpp= u8g2.firstPage(); do { String msg; msg = "溫度 : " + String((float)temperature, 1) + " °C"; u8g2.setCursor(0, 20); u8g2.print(msg.c_str()); msg = "臨界值 : " + String((int)thresholdTemp) + " °C"; u8g2.setCursor(0, 40); u8g2.print(msg.c_str()); // 顯示設定模式狀態 msg = "Mode: " + (settingMode ? String("Open") : String("Close")) ; u8g2.setCursor(0, 60); u8g2.print(msg.c_str()); } while (u8g2.nextPage()); ``` ## 程式碼 ```cpp= #include <U8g2lib.h> #include <SimpleDHT.h> #include <switch.h> #include <freertos/FreeRTOS.h> #include <freertos/task.h> // 設定OLED的數值 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 // 設定各PIN腳 #define pinNTC 4 #define incSwitchPin 27 #define decSwitchPin 26 #define modeSwitchPin 25 #define relayPin 17 // 全域變數供個任務使用 int thresholdTemp = 25; float temperature = 0; bool settingMode = false; Switch inc(incSwitchPin, LOW, true); Switch dec(decSwitchPin, LOW, true); Switch change(modeSwitchPin, LOW, true); U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); //按鈕處理任務 void buttonTask(void *pvParameters) { while (true) { // 切換模式按鈕 switch (change.check()) { case Switch::RELEASED_FROM_PRESS: case Switch::PRESSING: settingMode = !settingMode; break; } // 增加按鈕 if (settingMode) { switch (inc.check()) { case Switch::RELEASED_FROM_PRESS: case Switch::PRESSING: thresholdTemp++; if (thresholdTemp > 35){ thresholdTemp = 35; } break; } } // 減少按鈕 if (settingMode) { switch (dec.check()) { case Switch::RELEASED_FROM_PRESS: case Switch::PRESSING: thresholdTemp--; if (thresholdTemp < 20){ thresholdTemp = 20; } break; } } } } void NTCTask(void *pvParameters){ const static float ntcResistance = 10000.0; const static float nominalTemperature = 25.0; const static float betaValue = 3950.0; while(true){ int rawValue = analogRead(pinNTC); if (rawValue == 0) { continue; } float resistance = ntcResistance / (4095.0 / rawValue - 1.0); float steinhart = resistance / ntcResistance; steinhart = log(steinhart); steinhart /= betaValue; steinhart += 1.0 / (nominalTemperature + 273.15); steinhart = 1.0 / steinhart; steinhart -= 273.15; temperature = steinhart; } } // 風扇控制任務 void fanTask(void *pvParameters) { while (true) { if (temperature > thresholdTemp) { digitalWrite(relayPin, HIGH); // 啟動繼電器 } else { digitalWrite(relayPin, LOW); // 關閉繼電器 } } } // 更新 OLED 顯示 void displayInfo() { u8g2.firstPage(); do { String msg; msg = "溫度 : " + String((float)temperature, 1) + " °C"; u8g2.setCursor(0, 20); u8g2.print(msg.c_str()); msg = "臨界值 : " + String((int)thresholdTemp) + " °C"; u8g2.setCursor(0, 40); u8g2.print(msg.c_str()); // 顯示設定模式狀態 msg = "Mode: " + (settingMode ? String("Open") : String("Close")) ; u8g2.setCursor(0, 60); u8g2.print(msg.c_str()); } while (u8g2.nextPage()); } void setup() { Serial.begin(9600); u8g2.begin(); u8g2.enableUTF8Print(); u8g2.setFont(u8g2_font_unifont_t_chinese1); pinMode(relayPin, OUTPUT); digitalWrite(relayPin, LOW); xTaskCreate(buttonTask, "Button Task", 1024, NULL, 1, NULL); xTaskCreate(NTCTask, "DHT11 Task", 1024, NULL, 0, NULL); xTaskCreate(fanTask, "Fan Task", 1024, NULL, 0, NULL); } void loop() { displayInfo(); delay(100); } ```