# 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>
## 電路圖

## 程式碼
- 處理按鈕的任務
```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);
}
```