2026年10月6日 星期二

JSON 與 IoT 資料格式|Pico 2 W

Pico 2 W × Thonny|第 5 次課程

JSON 與 IoT 資料格式

感測器產生資料,Pico 負責整理與傳送;要讓不同設備、程式和伺服器都能理解同一筆資訊,就需要清楚一致的資料格式。本篇介紹 JSON 的基本結構、Python 字典與 JSON 的轉換,以及 IoT 遙測欄位的設計方法。

作者:施朝斌

一、為什麼 IoT 需要 JSON?

如果只傳送 28.3,71.2,350,接收端雖然看到三個數字,卻不知道它們各自代表溫度、濕度還是光照。JSON 在資料旁附上欄位名稱,讓資料較容易閱讀,也讓程式能依欄位取值。

{
  "temperature": 28.3,
  "humidity": 71.2,
  "light": 350
}

以上數字沿用課程投影片作為格式示例,不代表實際場域量測結果。正式資料應由感測器讀取,並清楚標明單位與資料來源。

二、JSON 的基本結構

JSON 物件使用大括號 {},以「欄位名稱:值」的方式組織資料;陣列使用中括號 [],可以放置多筆項目。常見值的型態包括字串、數字、布林值、物件、陣列,以及 null。

{
  "device": "PICO01",
  "temperature": 28.3,
  "humidity": 71.2,
  "relay": true,
  "location": {
    "room": "A101"
  },
  "alerts": []
}
  • 字串:文字需使用雙引號,例如 "PICO01"。
  • 數字:例如 28.3;不要把數字不必要地存成文字。
  • 布林值:使用小寫的 true 或 false。
  • 物件:可再包含一組欄位,例如 location。
  • 陣列:使用中括號,可表示多筆資料或多個項目。
  • null:表示目前沒有可用值;它不同於字串 "null"或數字 0。

三、Python 字典與 JSON 互相轉換

在 Python 程式中,資料常先以字典整理。json.dumps()把 Python 資料轉成 JSON 文字;json.loads()則把 JSON 文字解析回 Python 資料。

import json

data = {
    "temperature": 28.3,
    "humidity": 71.2,
    "relay": True
}

json_text = json.dumps(data)
print(json_text)

decoded_data = json.loads(json_text)
print(decoded_data["temperature"])

Python 的 True轉成 JSON 後會是小寫的 true;JSON 解析回 Python 後則會成為 True。同樣地,Python 的 None會轉成 JSON 的 null。

兩個常見動詞:dumps把資料「序列化」成文字;loads把文字「解析」成資料。HTTP 傳輸時,送出或收到的通常是文字內容。

四、設計 IoT 遙測資料欄位

一筆可交換的遙測資料,除了感測值,也應讓接收端知道資料來自哪個設備、何時產生,以及每個數字代表什麼。欄位名稱與格式最好固定,後續接入 HTTP API、MQTT 或 Dashboard 時才能共用。

{
  "deviceId": "PICO01",
  "timestamp": null,
  "temperature": 28.3,
  "humidity": 71.2,
  "light": 350,
  "battery": 4.05
}

這些感測值是投影片中的示範數字,不是實際量測結果。timestamp目前設為 null,表示範例沒有提供時間;只有在 Pico 已取得可靠時鐘並採用一致的時間格式後,才應填入時間戳記。每個欄位也應訂定單位,例如溫度使用 °C、電壓使用 V,避免不同設備各自解讀。

設計欄位時,還要決定設備識別方式、欄位拼法、數值型態、單位、取樣時間與缺值表示法。這些規則就是資料格式的一部分。

五、用 Python 建立自己的 JSON 遙測資料

以下以示範值展示資料結構。實作時請把數值換成程式實際讀到的感測器資料;若某欄位目前沒有讀值,使用 None轉成 JSON null,不要假裝已取得測量結果。

import json

telemetry = {
    "deviceId": "PICO01",
    "timestamp": None,
    "temperature": 28.3,
    "humidity": 71.2,
    "relay": False
}

payload = json.dumps(telemetry)
print(payload)

接著可以把 payload放進 HTTP POST 的 Body,或在下一堂課將它作為 MQTT 訊息內容。只要欄位規則維持一致,接收端就能用相同方式解析。

六、錯誤資料也要處理

網路上的資料不一定完整、型態正確或仍然新鮮。接收端應先檢查必需欄位,再確認型態與時間資訊,並適當處理伺服器錯誤。

import json

response_text = '{"temperature": 28.3}'

try:
    data = json.loads(response_text)

    temperature = data.get("temperature")
    humidity = data.get("humidity")

    if temperature is None:
        print("Temperature is missing")
    elif not isinstance(temperature, (int, float)):
        print("Temperature has the wrong type")
    else:
        print("Temperature:", temperature)

    if humidity is None:
        print("Humidity is missing")
    else:
        print("Humidity:", humidity)

except ValueError as error:
    print("Invalid JSON:", error)
  • 欄位不存在:使用 get()或明確檢查,避免直接索引而產生 KeyError。
  • 型態錯誤:數字 28.3與文字 "28.3"不是同一種資料。
  • 資料過期:若有可靠時間戳記,可比較資料時間與目前時間;沒有時鐘時不要假裝能判定新舊。
  • 伺服器錯誤:檢查 HTTP 狀態碼,必要時有限次重試並記錄結果。

七、REST API 路徑與資料責任

REST API 常用不同路徑區分資源或功能。例如 /api/telemetry可代表感測資料、/api/status可代表設備狀態、/api/control可代表控制要求、/api/config可代表設定資訊。路徑名稱只是設計示例,實際 API 應依服務規格使用。

把感測資料、設備狀態、控制命令與設定分開,有助於開發者理解每個端點的用途。欄位名稱與格式也要在設備端和伺服器端保持一致。

八、實作任務:定義自己的 Pico 資料格式

  1. 建立一個穩定的 deviceId。
  2. 選擇 2 至 3 個實際存在或課堂指定的感測欄位。
  3. 加入一個設備狀態欄位,例如繼電器開關狀態。
  4. 為每個欄位記錄型態、單位與缺值表示方式。
  5. 使用 json.dumps()轉成 JSON,並在 Shell 檢查輸出。
  6. 依課程提供的測試服務 POST,檢查狀態碼與回應內容。
不要把示範數據當成實測結果:完成實驗紀錄時,註明數值來自範例、模擬器或實際感測器。若來源不同,也要避免混在一起比較或宣稱為場域成效。

本篇重點回顧

  • JSON 以欄位名稱標示資料意義,方便不同設備和程式交換資訊。
  • Python 字典可用 json.dumps()轉為 JSON 文字,用 json.loads()解析回資料。
  • IoT 遙測格式應固定欄位名稱、型態、單位、設備識別與時間規則。
  • 要檢查欄位缺漏、型態不符、資料是否過期及 HTTP 錯誤。
  • 範例數值只示範格式;實際結果應來自感測器或明確標示的測試資料。

下一篇將把一致的 JSON 遙測資料帶入 MQTT,讓 Pico 發布感測訊息,也能訂閱控制命令。

沒有留言:

張貼留言