National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 80 24°C PM2.5 21
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 18

專題:教室環境監測站

整合感測、顯示、記錄與網頁的完整作品
Interaction導覽 30 minute(s)

Learning Objectives

  • 把一個專題需求寫成規格與接線表,說明多個 I2C 裝置怎麼共用匯流排
  • 寫出「讀取、判斷、顯示、記錄」的主迴圈,加上門檻警示與人體感測控制背光
  • 把系統拆成記錄服務與網頁服務,讓網頁 API 讀取記錄檔,並用 MQTT 發布資料
  • 用 systemd 部署兩個服務,依測試清單檢查系統在異常情況下的行為
  • 說明環境監測資料涉及的隱私考量

專題目標:一台會自己工作的監測站

這一課把整個樹莓派軌學過的東西組成一個完整的系統:放在教室角落的環境監測站。

需求(先用一般的話寫下來):

  1. 測量溫度與光線,判斷教室裡有沒有人。
  2. 在現場的 LCD 顯示目前的數值;沒有人的時候關掉背光。
  3. 溫度超過 28°C 時亮紅色警示燈。
  4. 每一筆資料記錄下來,之後可以分析。
  5. 同一個網路上的電腦可以用網址查最新數值與統計;管理室可以透過 MQTT 收到資料。
  6. 停電復電後自動恢復運作,不需要有人去按。

規格(把需求變成可以檢查的數字與介面):

項目 規格
取樣間隔 每 60 秒一次(下面的模擬為了好看,縮短成 1 秒)
警示門檻 溫度 > 28.0°C 亮燈,回到門檻以下熄滅
記錄格式 CSV:UTC 秒數, 溫度, 光線(0~100), 有沒有人(0/1)
網頁 API GET /api/latest、GET /api/summary,回傳 JSON
MQTT 主題 classroom/101/env,內容是 JSON
自動執行 兩個 systemd 服務,Restart=on-failure

把需求寫成規格,做完之後才有辦法逐項檢查「到底完成了沒有」。

零件與接線表

零件 介面 接到樹莓派 學過的課
TMP102 溫度感測器 I2C,位址 0x48 SDA=GPIO2、SCL=GPIO3 第 09 課
LCD1602(I2C 轉接板) I2C,位址 0x27 和 TMP102 接在同一組 SDA、SCL 第 10 課
光敏電阻+MCP3008 SPI,第 0 通道 SPI0(CE0) 第 08 課
PIR 人體感測器 數位輸入 GPIO4 第 12 課
紅色 LED+220Ω 電阻 數位輸出 GPIO17 第 05 課

I2C 是匯流排:好幾個裝置可以接在同樣兩條線上,靠位址區分。只要位址不重複,TMP102(0x48)和 LCD(0x27)就能共用。 接好後用 i2cdetect -y 1 檢查,應該看到 48 與 27 兩個位址(第 09 課)。

接線前先關機;LCD 模組的供電電壓與訊號電位照第 10 課的說明處理,不要讓 5V 的訊號直接進到樹莓派 3.3V 的腳位。

系統架構:拆成兩個服務

如果把「每 60 秒讀感測器」和「回應網頁請求」寫在同一支程式,兩件事會互相卡住: app.run() 一直在等請求,迴圈就跑不到;迴圈在 sleep,網頁就沒有回應。要同時做就得用執行緒,程式變得複雜。

比較簡單、也比較穩定的做法是拆成兩支程式,各自是一個 systemd 服務,透過檔案交換資料:

┌──────── logger.py(記錄服務)────────┐        ┌──── web.py(網頁服務)────┐
│ 讀感測器 → 判斷 → LCD、警示燈         │        │ /api/latest               │
│        → 附加一列到 env.csv ──────────┼──────▶ │ /api/summary  讀 env.csv  │
│        → MQTT 發布 classroom/101/env │        └───────────────────────────┘
└──────────────────────────────────────┘

好處:網頁服務當掉不影響記錄;改網頁時重新啟動 web 服務就好,記錄不會中斷。 資料量變大之後,把 env.csv 換成第 14 課的 SQLite,兩邊的介面不變。

逐步看:記錄服務的主迴圈

電路上有五個零件。溫度在 4 秒內從 27°C 升到 29°C,人體感測在 2.5 秒後就沒有人了。 注意兩個變化:溫度超過 28 時第 22 行讓警示燈亮起;沒有人時第 23 行關掉 LCD 背光。

逐步看:網頁服務讀記錄檔

這支程式不讀感測器,只讀記錄服務寫好的 env.csv(已經有四筆資料)。請求時間表:0.7 秒 /api/latest、1.7 秒 /api/summary、2.7 秒 /alert/on、3.5 秒 /alert/off。 看每一個請求執行了哪幾行,以及電路畫面下方的 JSON 回應。

互動練習:完成監測站

起始程式只會讀溫度與光線、顯示在 LCD。模擬 6 秒:溫度從 26°C 升到 30°C,前 3 秒有人。依序完成任務:

  1. 直接執行,看 LCD 顯示的內容。
  2. 溫度超過 LIMIT 時亮警示燈,沒超過時熄滅。
  3. 有人時開 LCD 背光,沒人時關掉(lcd.backlight_enabled)。
  4. 每一輪把溫度與光線用 MQTT 發布到 classroom/101/env(JSON;連線方式見第 16 課,broker 是 broker.example.com)。
  5. 回答一個問題。

可以用「輸入設定」改溫度與有人的時間,看你的程式反應對不對。

部署:兩個服務、開機自動執行

在樹莓派上把專題放進 /home/student/station,照第 17 課的方法部署。輸出經過節錄。

  1. 1

    專題資料夾的內容

    程式、虛擬環境、記錄檔放在同一個資料夾;unit 檔的 WorkingDirectory 指到這裡,程式裡的 env.csv 就會存在這裡。

  2. 2

    確認 I2C 裝置都在

    應該看到 27(LCD)與 48(溫度感測器)。少了哪一個,先檢查那個零件的接線。

  3. 3

    寫兩個 unit 檔

    記錄服務的內容如下;網頁服務的 station-web.service 一樣寫法,只是 ExecStart 改成執行 web.py,Description 改成網頁服務。

  4. 4

    啟用並啟動

    一個指令可以同時啟用好幾個服務。

  5. 5

    從另一台電腦測試網頁 API

    在同一個網路的電腦上用 curl(或瀏覽器)打開網址。raspberrypi.local 是樹莓派的主機名稱,也可以直接用 IP。

這段導覽完成了

測試清單:故意讓它出錯

系統在一切正常時能動,只是起點。放到教室之前,照下面的清單故意製造問題,確認每一項的反應都在預期內:

測試 怎麼做 預期
斷電復電 拔掉電源再插上 兩個服務自動啟動,記錄接續寫在 env.csv 後面
程式當掉 sudo kill -9 記錄服務的PID 約 5 秒後自動重新啟動
感測器鬆脫 關機拔掉 TMP102 的線再開機 程式記下錯誤、繼續執行(第 17 課的 try/except),不是一直重啟
溫度超標 用手握住感測器 超過 28°C 時警示燈亮,放開後熄滅
沒有人 離開 PIR 的範圍 一段時間後 LCD 背光關閉
網路斷線 拔掉網路 記錄照常寫檔;MQTT 重新連線後恢復發布

每一項都寫下「做了什麼、看到什麼、是否符合預期」。不符合的項目就是下一輪要改的地方。

隱私

PIR 記錄的是「這個時間教室裡有沒有人」,看起來無害,累積下來卻能看出某間教室、某個人的作息。

  • 在教室貼出說明:測量什麼、存多久、誰看得到。
  • 只收需要的資料:只需要溫度與光線時,就不要記錄有沒有人。
  • 對外分享時用彙總後的數字(例如每小時平均),而不是逐筆的原始紀錄。
  • 網頁與 MQTT 只在校內網路開放,不要直接放上網際網路(第 15、16 課)。

小測驗:設計與接線

1.

TMP102(位址 0x48)與 LCD1602 轉接板(位址 0x27)接在同一組 SDA、SCL 上,會怎樣?

2.

下面的程式模擬記錄服務的判斷,會印出什麼?

LIMIT = 28.0
for temp, people in [(27.5, True), (28.0, True), (28.6, False)]:
    alert = temp > LIMIT
    backlight = people
    print(int(alert), int(backlight), end=" ")
3.

網頁服務的 /api/summary 讀到 env.csv 的溫度是 27.0、27.6、28.3、28.9,回傳的平均(四捨五入到小數 2 位)是多少?

第 1 次作答

小測驗:部署、測試與隱私

1.

測試時拔掉 TMP102 再開機,發現記錄服務每 5 秒重新啟動一次、什麼都沒記到。最好的改法是?

2.

監測站的資料要分享給全校看,下面哪一種做法最符合隱私原則?

第 1 次作答

重點整理與延伸

  • 先把需求寫成可以檢查的規格(間隔、門檻、格式、介面),再畫接線表;I2C 裝置靠位址共用匯流排。
  • 記錄服務的主迴圈:讀取 → 判斷(警示、背光)→ 顯示 → 記錄 →(發布)→ 等待。
  • 一直等請求的網頁與一直迴圈的記錄拆成兩個服務,透過檔案或資料庫交換資料,各自用 systemd 管理。
  • 放到現場之前,照測試清單故意製造斷電、當機、感測器鬆脫、網路中斷,確認每一項的反應。
  • 記錄與人有關的資料前先想隱私:告知、只收需要的、分享彙總結果、不直接對外開放。

延伸題目:加上溫溼度感測器或二氧化碳感測器;把 CSV 換成 SQLite 並在網頁畫出一天的溫度曲線(Chart.js); 用 MQTT 把好幾間教室的監測站集中到一台樹莓派,比較各教室的差異;溫度持續超標時用 MQTT 通知管理室。

這一課完成了!
看我的學習進度
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。