9.16. 時間與 NTP¶
剛通電的相機並不知道現在是什麼時間。板載時鐘會從某個任意時刻開始(在大多數開發板上是 1970-01-01),然後一直往前計數,直到有東西告訴它正確的時間為止。NTP(網路時間協定,Network Time Protocol)正是相機向網路詢問真實世界牆上時鐘時間、並據此設定自身時鐘的方式。
9.16.1. 為什麼相機需要知道時間¶
對許多指令碼而言,相機的時鐘並不重要——影格擷取迴圈並不在乎今天是星期幾。但對於少數常見的情況,它就非常重要了:
日誌或上傳資料中的時間戳記。 如果所有條目都標示為
1970-01-01,事後就很難理解這些資料的意義。排程任務。 「在 03:00 執行」需要相機知道 03:00 到底是什麼時候。
9.16.2. NTP 的作用¶
NTP 是一項小巧的公共服務:一個伺服器網路,透過單次 UDP 交換來回答「現在幾點?」。相機向已知的 NTP 伺服器送出一個簡短的請求;伺服器以精確的時間戳記回覆(對任何常見的公共伺服器而言,精確度可達數毫秒);相機便用它來設定自身的時鐘。相機預設使用的伺服器是 pool.ntp.org,這是一個全球負載平衡的伺服器池,正是為這類用戶端所設計的。
9.16.3. Python API:ntptime¶
MicroPython 將這個協定包裝成單一呼叫。常見的做法是先把連線建立起來,再向 NTP 詢問時間:
import network
import ntptime
import time
wlan = network.WLAN(network.WLAN.IF_STA)
wlan.active(True)
wlan.connect("my-network", "my-password")
while not wlan.isconnected():
time.sleep_ms(100)
ntptime.settime() # cam's clock is now UTC
print(time.localtime())
ntptime.settime() 回傳之後,板載即時時鐘與 time.localtime() 便會反映目前的 UTC 時間。有兩個旋鈕可以調整預設值:
ntptime.host是要查詢的伺服器名稱。在呼叫settime()之前覆寫它(ntptime.host = "time.google.com"),即可指向不同的伺服器。ntptime.timeout是在放棄之前等待回覆的秒數;預設值很短。
9.16.4. 何時呼叫它¶
在網路連線建立之後。 NTP 運行於 UDP 之上,而 UDP 又運行於已建立的 IP 設定之上。請先等待
isconnected()回傳True。在長時間運行的相機上定期呼叫。 板載時鐘會在數小時與數天之內逐漸漂移。每天或每週呼叫一次
settime(),可以讓它保持準確。
9.16.5. 時區¶
NTP 回傳的是 UTC。MicroPython 並未隨附時區資料庫,因此將 UTC 轉換為當地時間是指令碼的工作。針對部署所在時區使用固定的偏移量是常用的做法:
import time
offset = -5 * 3600 # hours -> seconds, US Eastern
local = time.localtime(time.time() + offset)
print(local)
這種做法並未處理日光節約時間、閏秒以及歷史上的時區變更。對大多數相機部署而言,固定的偏移量就已足夠;如果某個指令碼確實需要含日光節約時間的民用時間,請在伺服器端進行轉換。
9.16.6. 可能出錯的地方¶
尚無網路連線。 如果
ntptime.settime()無法連上伺服器,就會引發OSError。可能是連線尚未建立、名稱查詢失敗、伺服器無法連線,或是在ntptime.timeout之內沒有收到回覆。請在連線穩定後重試。強制門戶(Captive portal)。 攔截 DNS 的 Wi-Fi 網路可能會以門戶本身的 IP 來回應 NTP 伺服器名稱,而送往該 IP 的 NTP 請求則會回傳無意義的資料。相機會以為網路已連通,但時間設定卻會失敗或嚴重錯誤。請改用乾淨的網路,或硬性指定一個 IP。
猛烈轟炸公共伺服器池。 公共 NTP 伺服器池會對濫用的用戶端進行流量限制。每小時一次就綽綽有餘;每分鐘一次則會害相機被封鎖。
完整的 ntptime 參考資料,請參閱 ntptime --- 簡易 NTP 用戶端。