/*
功能:
1. 將環境溫溼度資料推播至MQTT伺服器,讓所有訂閱者都能收到溫溼度資料訊息(每10分鐘1次)
2. 使用者可以發訊息要求即時讀取溫溼度資料(Topic:YourTopic/class000/controlDHT)
連線資訊:
伺服器名稱:MQTTGO
伺服器網址:mqttgo.io
帳號密碼:無
MQTT Port:1883
Topic:
推播
yourTopic/class000/temp(記得改名)
yourTopic/class000/humi(記得改名)
訂閱
YourTopic/class000/temp
YourTopic/class000/controlDHT
*/
#include <WiFi.h>
#include <DHTesp.h> // 使用 DHT sensor library for ESPx 函式庫
#include <PubSubClient.h> // 使用 PubSubClient 函式庫
// --------------------請修改以下參數--------------------
// WiFi 帳號密碼
char SSID[] = "YourSSID"; // WiFi 帳號
char PASSWORD[] = "YourPassword"; // WiFi 密碼
// LED 接腳
int ledPin = 2;
// DHT22 接腳
int DHT_PIN = 32; // 32、33、34、35為佳
// MQTT 設定
char* MQTTServer = "mqttgo.io"; // 免註冊MQTT伺服器
char* MQTTUser = ""; // MQTT伺服器帳號
char* MQTTPassword = ""; // MQTT伺服器密碼
int MQTTPort = 1883; // MQTT Port
// 推播主題
char* MQTTPubTopic1 = "YourTopic/class000/temp"; // 推播溫度
char* MQTTPubTopic2 = "YourTopic/class000/humi"; // 推播濕度
// 訂閱主題
char* MQTTSubTopic1 = "YourTopic/class000/controlDHT"; // 訂閱LED,控制LED用
//char* MQTTSubTopic2 = "YourTopic/class000/temp"; // 訂閱自己,測試用
// 推播時間設定
long MQTTLastPublishTime = 0; // 紀錄上次推播時間
long MQTTPublishInterval = 60000; // 每1分鐘推播1次
// -----------------------------------------------------
DHTesp dhtSensor; // DHT22物件
WiFiClient WifiClient; // WiFiClient物件
PubSubClient MQTTClient(WifiClient); // PubSubCliednt物件
// 讀取溫濕度自訂函式
void readDHT(double *temperature, double *humidity){
// 讀取溫度和濕度資料
TempAndHumidity data = dhtSensor.getTempAndHumidity();
// 判斷是否有正確讀取
if(dhtSensor.getStatus() != 0){
Serial.println("DHT sensor error status: " + String(dhtSensor.getStatusString())); // 回傳是 timeout 或是 checksum 錯誤
}else if (isnan(data.humidity) || isnan(data.temperature)){
Serial.println("Data is NaN!");
}else{
//Serial.println("------------------------------------------------------");
//Serial.println("Temperature : " + String(data.temperature, 2) + " °C"); // 參考語法 String(val, decimalPlaces)
//Serial.println("Humidity : " + String(data.humidity, 2) + " %");
*temperature = data.temperature;
*humidity = data.humidity;
}
}
// WiFi連線自訂函式
void WiFiConnect(){
// 連線到指定的WiFi SSID
Serial.print("Connecting Wifi: ");
Serial.println(SSID);
//WiFi.begin(SSID, PASSWORD); // 以STA(網路終端)模式連接到WiFi基地台
WiFi.begin("Wokwi-GUEST", "", 6); // wokwi提供的虛擬 WiFi 接入點
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
//連線成功,顯示取得的IP
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
IPAddress ip = WiFi.localIP();
Serial.println(ip);
}
// MQTT連線自訂函式
void MQTTConnect(){
// 設定連線的MQTT伺服器及連接埠
MQTTClient.setServer(MQTTServer, MQTTPort);
// 當訂閱主題更新時會觸發 MQTTCallback 副程式
MQTTClient.setCallback(MQTTCallback);
while(!MQTTClient.connected()){
// 以亂數為 ClientID
String MQTTClientID = "esp32-" + String(random(1000000, 9999999));
if(MQTTClient.connect(MQTTClientID.c_str(), MQTTUser, MQTTPassword)){
// 若連線成功,顯示「已連線」
Serial.println("MQTT已連線");
// 訂閱主題
MQTTClient.subscribe(MQTTSubTopic1);
//MQTTClient.subscribe(MQTTSubTopic2); // 訂閱自己,測試用
}else{
// 若連線不成功,顯示「錯誤訊息」,並重新連線
Serial.print("MQTT連線失敗,狀態碼=");
Serial.println(MQTTClient.state());
Serial.println("5秒後重新連線");
delay(5000);
}
}
}
// MQTTCallback 副程式
void MQTTCallback(char* topic, byte* payload, unsigned int length){
Serial.print(topic);Serial.print("訂閱通知:");
// 顯示訂閱內容
// 將接收的payload轉成字串
String payloadString;
for(int i=0; i < length; i++){
payloadString += (char)payload[i];
}
Serial.println(payloadString);
// 比對主題是否為訂閱主題2-控制發送溫濕度資料
if(strcmp(topic, MQTTSubTopic1)==0){
Serial.println("控制讀取溫濕度資料:" + payloadString);
// 讀取溫濕度
double temperature = 0.0;
double humidity = 0.0;
readDHT(&temperature, &humidity);
if(payloadString == "temp"){
MQTTClient.publish(MQTTPubTopic1, String(temperature).c_str());
Serial.println(" 溫度 資料已推播到MQTT Broker");
}else if(payloadString == "humi"){
MQTTClient.publish(MQTTPubTopic2, String(humidity).c_str());
Serial.println(" 濕度 資料已推播到MQTT Broker");
}
}
}
void setup() {
Serial.begin(115200);
// LED控制腳位
pinMode(ledPin, OUTPUT);
// DHT22 初始化
dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
// 開始WiFi連線
WiFiConnect();
// 開始MQTT連線
MQTTConnect();
}
void loop() {
// 如果WiFi連線中斷,則重啟WiFi連線
if(WiFi.status() != WL_CONNECTED){WiFiConnect();}
// 如果MQTT連線中斷,則重啟MQTT連線
if(!MQTTClient.connected()){MQTTConnect();}
// 如果距離上次傳輸已超過 MQTTPublishInterval 設定時間,則 Publish 溫濕度
if(millis() - MQTTLastPublishTime >= MQTTPublishInterval){
// 讀取溫濕度
double temperature = 0.0;
double humidity = 0.0;
readDHT(&temperature, &humidity);
/*-------------------- 將溫濕度資料送到 MQTT 主題 --------------------*/
MQTTClient.publish(MQTTPubTopic1, String(temperature).c_str());
MQTTClient.publish(MQTTPubTopic2, String(humidity).c_str());
Serial.println("溫濕度資料已推播到MQTT Broker");
MQTTLastPublishTime = millis(); // 更新最後傳輸時間
}
// 更新訂閱狀態
MQTTClient.loop();
delay(50);
}