RS-485 與 Modbus 實戰:MD02 溫濕度感測器程式解說 (3/3)

RS-485 與 Modbus 實戰:MD02 溫濕度感測器程式解說 (3/3)

歡迎來到本系列程式解說的最後一篇!在前面的段落中,我們已經成功將硬體連線、完成初始化,並架構了一個不會阻塞系統運作的 Modbus 狀態機。本篇我們將聚焦於最後的關鍵步驟:「如何將收到的 16 進位原始數據轉換為實際溫濕度」,以及「如何透過非阻塞計時器將數據上傳至 ThingSpeak 雲端」


一、 數據解碼:從 16 進位到人類可讀的溫濕度

當我們的開發板透過狀態機的 `master.poll()` 成功接收到感測器的回應後,資料會被存放在我們預先宣告好的 au16data 陣列中。然而,這個陣列裡裝的是「原始的 16 進位數據」,我們必須依據感測器原廠規格書的指示來進行換算。

// 假設狀態機已經順利將溫度暫存器資料讀入 au16data
// 宣告有號整數變數以正確處理可能的負溫度
int16_t temperature_raw = au16data; 

// 將原始數據除以 10.0 以獲得帶有小數點的實際溫度
float actual_temperature = (float)temperature_raw / 10.0;

Serial.print("目前溫度: ");
Serial.println(actual_temperature);
【插圖說明:數據解碼與換算邏輯】

舉例來說,當感測器回傳的溫度封包數據為 0x0131 時,換算步驟如下:

🔢 步驟 1 (提取數據):從陣列取出原始值 0x0131
🔢 步驟 2 (轉十進制):16 進位 0x0131 轉換為十進位是 305
🔢 步驟 3 (應用換算):將 305 除以 10,得到真實溫度 30.5°C

⚠️ 負溫度處理的陷阱:
溫度值是「帶有符號的 16 進制數 (signed integer)」。如果傳回的數值是 0xFF33,它代表的是十進位的 -205,除以 10 後就是 -20.5°C。如果程式宣告錯變數型態 (用了無號整數 uint16_t),就會變成六萬多度!

二、 定時雲端上傳:ThingSpeak 整合

在取得真實的 actual_temperature 後,我們準備將其上傳到 ThingSpeak。為了不干擾 Modbus 狀態機持續傾聽網路上的封包,我們絕不能使用 `delay()` 來控制上傳頻率。取而代之的是,我們使用 millis() 函數搭配我們先前設定好的 intervalSwitch (1分鐘) 來做時間差判斷。

CurrentTime = millis(); 
// 檢查是否距離上次上傳已經超過 1 分鐘 (intervalSwitch)
if(CurrentTime - preTime > intervalSwitch){ 
  preTime = CurrentTime; // 更新最後一次上傳的時間記錄

  // 確保 WiFi 處於連線狀態
  if (WiFi.status() == WL_CONNECTED) {
    // 建立與 ThingSpeak 的 HTTP 連線
    if (client.connect(server, 80)) {
      // 組合 HTTP POST 字串,夾帶 API Key 與溫度欄位 (field1)
      String postStr = api_key;
      postStr += "&field1=";
      postStr += String(actual_temperature);
      postStr += "\r\n\r\n";
      
      // 送出標準 HTTP 標頭
      client.print("POST /update HTTP/1.1\n");
      client.print("Host: api.thingspeak.com\n");
      client.print("Connection: close\n");
      client.print("X-THINGSPEAKAPIKEY: " + api_key + "\n");
      client.print("Content-Type: application/x-www-form-urlencoded\n");
      client.print("Content-Length: ");
      client.print(postStr.length());
      client.print("\n\n");
      client.print(postStr); // 送出實際數據
      
      client.stop(); // 傳送完畢後關閉連線
      Serial.println("成功上傳數據至 ThingSpeak!");
    }
  }
}

📝 重點解析: 透過這段非阻塞式的計時器,主程式的 `loop()` 可以每秒鐘跑成千上萬次,隨時處理 Modbus 封包與 WiFi 斷線偵測,而只有在「剛剛好過了一分鐘」的那個瞬間,才會進入這個 if 判斷式執行一次 HTTP 請求上傳資料。


三、 總結與進階應用探索

透過這三段的程式碼解析,我們完整走過了一次從「底層通訊架構」、「主從非同步收發」到「數據換算與雲端上傳」的實戰流程!

【插圖說明:物聯網資料流總結】
MD-02 感測器
16進位原封包
RTL8720DF 狀態機
解碼與 /10 運算
非阻塞計時器
每分鐘觸發
ThingSpeak 雲端
資料可視化

🚀 下一步的擴展:本地端儲存 (SD Card)
在許多嚴苛的工業環境中,WiFi 連線可能隨時中斷。當您的專案走出實驗室,我們會強烈建議在 Datalogger 擴展板上整合 MicroSD 卡模組 (SDC)。利用相同的非阻塞狀態機邏輯,您可以將 actual_temperature 記錄成 CSV 檔案格式保存在本地端。這樣即使網路斷線,寶貴的溫濕度數據也依然被穩穩地鎖在記憶卡中!