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 14

記錄感測資料:CSV 與 SQLite

定時讀取感測器並存成檔案與資料庫
Interaction 25 minute(s)

Learning Objectives

  • 寫出定時讀取感測器、加上時間戳記的記錄程式,並說明取樣間隔怎麼選
  • 用 csv 模組把資料寫成 CSV 檔,知道附加模式與 newline="" 的用途
  • 用 sqlite3 建立資料表、以參數化查詢寫入,再用 SQL 算出平均與最大值
  • 比較 CSV 與 SQLite 的適用情境

只看現在的數字還不夠

前面幾課的程式讀到溫度就印出來,程式一停,數字就不見了。可是很多問題要看一段時間的變化才回答得了:

  • 教室下午三點是不是特別熱?冷氣開了多久才降下來?
  • 光線在一天裡怎麼變?什麼時候該開燈?

這一課把感測器的讀值定時記錄下來:每一筆都有時間、有數值,存成檔案或資料庫,之後可以畫圖、統計、和別人分享。

記錄程式的三個決定

決定 要想的事
取樣間隔 溫度變化慢,每分鐘一次通常就夠;按鈕、震動變化快,要密很多。太密會產生大量資料,太疏會漏掉變化
時間戳記 每一筆都要記下時間。建議記 UTC(世界協調時間),顯示時再換成當地時間,跨時區、日光節約時間都不會錯亂
存在哪裡 簡單的表格用 CSV 檔;資料多、要查詢與彙總用 SQLite 資料庫

本課的感測器沿用前幾課:I2C 溫度感測器 TMP102(位址 0x48,第 09 課)與接在 MCP3008 上的光敏電阻(第 08 課)。

CSV:最通用的表格檔

CSV(Comma-Separated Values)是純文字檔,一列一筆資料,欄位用逗號分開;第一列通常放欄位名稱。 試算表、資料分析工具、幾乎所有程式語言都讀得懂。

time_utc,temp_c,light
2026-01-01T00:00:00Z,26.5,0.62
2026-01-01T00:00:01Z,26.81,0.55

Python 內建的 csv 模組幫你處理逗號與引號(例如欄位內容本身有逗號時要加引號),不用自己組字串。兩個細節:

  • 開檔時加 newline="":讓 csv 模組自己決定換行,在 Windows 上才不會多出空白列(Python 官方文件的建議寫法)。
  • 時間寫成 2026-01-01T00:00:00Z 這種 ISO 8601 格式,最後的 Z 表示 UTC;照字母排序就是照時間排序。

time.gmtime(time.time()) 把「從 1970 年起算的秒數」換成 UTC 的年月日時分秒,time.strftime 再排成字串。 模擬器的時鐘從 2026-01-01 00:00:00 UTC 開始,所以範例裡的時間都是這一天。

逐步看:每秒取樣一次,寫成 CSV

上方的電路畫面有溫度感測器與光敏電阻,數值會隨時間慢慢變。注意第 19 行讓虛擬時間前進 1 秒;程式最後把寫好的檔案讀出來印。

長時間記錄:每一筆都寫進檔案

上一個範例寫完 3 筆才關檔。真正的記錄程式會一直跑好幾天,這時候要考慮突然斷電: 寫入的資料可能還在記憶體的緩衝區,沒有真的寫到 SD 卡上,斷電就沒了。

比較穩健的寫法是每一筆都用附加模式 "a" 打開、寫一列、關閉:

while True:
    row = [時間, 溫度]
    with open("log.csv", "a", newline="", encoding="utf-8") as f:
        csv.writer(f).writerow(row)
    time.sleep(60)
模式 檔案已經存在時
"w" 清空,從頭寫(重新開機後跑一次,舊資料就全部不見)
"a" 接在最後面繼續寫(舊資料保留)

每一筆開關一次檔案,對每分鐘幾筆的記錄來說負擔很小,換來的是斷電時最多只少最後一筆。 下面的模擬器就用這種寫法。

互動練習:改你的記錄程式

起始程式每秒記一筆溫度,同時印在下方。模擬 6 秒後停止(像拔掉電源)。依序完成任務:

  1. 直接執行,看每一列的格式。
  2. 把取樣間隔改成 0.5 秒,6 秒內記到至少 11 筆。
  3. 在每一列最後加上光線(light.value,四捨五入到小數 2 位)。
  4. 溫度超過 30°C 時,多印一行「過熱」。程式寫好後,用「輸入設定」把溫度的結束值拉到 32°C 以上再執行。
  5. 回答一個問題。

小測驗:CSV 與取樣

1.

下面的程式執行後會印出什麼?

import csv, io
buf = io.StringIO()
w = csv.writer(buf)
w.writerow(["t", "temp"])
w.writerow(["08:00", 26.5])
w.writerow(["08:01", 27])
print(len(buf.getvalue().splitlines()))
2.

每 30 秒記錄一次溫度,連續記錄一整天,大約會有幾筆?

3.

為什麼建議時間戳記存成 UTC,而不是直接存當地時間?

第 1 次作答

SQLite:一個檔案就是一個資料庫

資料一多,CSV 就不方便了:想找「溫度超過 27 度的時間」或「每小時的平均」,要把整個檔案讀進來自己算。 SQLite 是一個不需要伺服器的資料庫:整個資料庫就是一個檔案(例如 env.db),Python 內建 sqlite3 模組可以直接用。

步驟 程式
打開(不存在就建立) db = sqlite3.connect("env.db")
建立資料表 db.execute("CREATE TABLE IF NOT EXISTS readings (ts INTEGER, temp_c REAL)")
寫入一筆 db.execute("INSERT INTO readings VALUES (?, ?)", (時間, 溫度))
確定寫入 db.commit()
查詢 db.execute("SELECT AVG(temp_c) FROM readings").fetchone()

參數化查詢:值一律用 ? 佔位、另外傳一個元組,不要把值用字串拼進 SQL。 如果值來自網頁表單或其他裝置(下一課的網頁就會),拼字串會讓別人有機會改寫你的 SQL(SQL 注入)。

下面的範例為了在瀏覽器裡執行,把資料庫開在記憶體(":memory:"),程式結束就消失;在樹莓派上改成檔名就會存成檔案。 時間存成整數秒(int(time.time()),從 1970 年起算的 UTC 秒數),比較大小與排序都很方便。

逐步看:寫進 SQLite,再用 SQL 彙總

第 14~15 行是一個敘述(括號沒關之前可以換行)。看第 19 行一個 SQL 就同時算出筆數、平均與最大值;第 21 行用 ? 帶入查詢條件。

CSV 還是 SQLite?

CSV SQLite
檔案內容 純文字,用記事本就看得懂 二進位檔,要用程式或工具開
給別人用 直接用試算表打開 要會 SQL,或匯出成 CSV
查詢與彙總 自己讀檔、自己算 一個 SQL 敘述就算好
資料量大時 越來越慢 有索引,查詢仍然快
多個程式同時讀寫 容易互相干擾 資料庫處理鎖定,比較安全

常見的做法是兩者並用:樹莓派上用 SQLite 記錄,需要分享時再匯出成 CSV。 第 18 課的教室環境監測站會用到這兩種格式。

小測驗:SQLite

1.

下面的程式會印出什麼?

import sqlite3
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE r (temp REAL)")
for t in [26.0, 28.0, 30.0]:
    db.execute("INSERT INTO r VALUES (?)", (t,))
print(db.execute("SELECT COUNT(*), AVG(temp) FROM r WHERE temp > ?", (27,)).fetchone())
2.

下面哪一種寫法是安全的 SQLite 寫入方式?

3.

寫入幾筆資料後忘了呼叫 db.commit() 就直接關閉連線,會怎樣?

第 1 次作答

重點整理

  • 記錄程式要決定三件事:取樣間隔、時間戳記(建議 UTC)、存在哪裡。
  • CSV 用 csv.writer 寫,開檔加 newline="";長時間記錄用附加模式 "a",每一筆開關一次檔案,斷電時損失最少。
  • SQLite 一個檔案就是一個資料庫:connect、CREATE TABLE IF NOT EXISTS、INSERT 加 ? 參數、commit、SELECT 彙總。
  • 值一律用參數傳入,不要拼進 SQL 字串。
  • CSV 方便分享與檢視,SQLite 方便查詢與彙總,常常兩者並用。

下一課用 Flask 做一個網頁,讓同一個網路上的手機或電腦可以控制 LED、看感測器的數值。

這一課完成了!
Next lesson:用 Flask 做網頁控制面板
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。