9.4. 建立連結¶
前一頁介紹的連結層大致上是自動的,但有一個地方 Python 指令碼必須介入:告訴相機要加入哪個網路。在這個步驟成功之前,本節其餘部分所涵蓋的網路功能都無法運作。
9.4.1. network 模組¶
network 模組將相機的網路硬體開放給 Python 使用。確切的介面組合取決於板子:許多相機具有無線晶片並提供 WLAN 類別(命名取自 Wireless Local Area Network,無線區域網路);某些板子還具有內建的乙太網路埠並提供 LAN 類別(命名取自 Local Area Network,區域網路,即有線版本)。兩者的使用模式相同,但有一個重要差異:無線介面必須被告知要加入哪個網路,而乙太網路則直接接收纜線上的任何內容。
9.4.2. Wi-Fi 流程¶
加入 Wi-Fi 網路分為三個步驟:建構介面、將其啟用、要求它以密碼連線到某個指定名稱的網路。介面會在背景與存取點協商;isconnected() 呼叫會回報連結何時建立完成:
import network
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)
print("link up")
引數 IF_STA 選擇工作站(station)模式——相機加入由其他人託管的網路。相反的模式 IF_AP 則讓相機託管自己的小型網路供其他裝置加入;這對設定介面與現場安裝很有用,但並非常見情況。
一旦 isconnected() 回傳 True,相機便已連上網路。較高層需要自我設定的其他一切,都已在連結建立的過程中自動完成;接下來的頁面會逐一說明這些環節。
9.4.3. 可能出錯的地方¶
在這個步驟會出現幾種實際的失敗模式。
網路名稱或密碼錯誤。 連線嘗試會默默地重試,直到應用程式放棄為止。請用逾時來包裹這個等待,讓上述迴圈不會永遠阻塞:
start = time.ticks_ms() while not wlan.isconnected(): if time.ticks_diff(time.ticks_ms(), start) > 10000: raise OSError("Wi-Fi did not come up in 10 s") time.sleep_ms(100)
超出範圍。 相機與存取點必須夠近,使訊號強到足以維持連結。
status()會回傳一個代碼,指出連結未建立的原因;scan()會回傳無線電可見的網路清單,這是在connect無法成功時應該執行的診斷。存取點要求的不只是密碼。 開放網路(無密碼)與常見的密碼保護網路都由上面所示的
connect涵蓋。職場與學校的較大型網路有時會使用不同的機制,其中相機必須對另一個獨立的登入伺服器進行身分驗證;那些需要傳給connect額外的引數。完整的內容請參閱 class WLAN -- 控制內建 WiFi 介面。
9.4.4. 保持連線¶
建立連結只是問題的一半。保持連線是另一半——存取點會重新開機、相機會漫遊到範圍之外、DHCP 租約會到期、無線電韌體偶爾會卡住。一台要在網路上存活數個月的相機,必須察覺這些情況並自行恢復。
偵測的模式是在每次主迴圈反覆運算時呼叫一次 isconnected(),並在它回傳 False 時做出反應。當連線已中斷但無線電尚未察覺時,isconnected() 可能會短暫地說謊——在連結「應該」正常時卻失敗的 socket 傳送,是應用程式判斷連線中斷的另一項證據。當兩者不一致時,status() 是較具權威性的來源。
重新連線的模式是先 disconnect(),接著以相同的憑證 connect(),並像初次連線時一樣以逾時包裹等待。在每次嘗試之間採用退避策略——一秒、兩秒、四秒,加倍到約一分鐘為止——讓長時間的中斷不會猛烈地衝擊存取點,也不會在空轉迴圈上耗盡無線電的電力預算:
import network
import time
_BACKOFF_S = (1, 2, 4, 8, 16, 32, 60)
def reconnect(wlan, ssid, password):
for delay in _BACKOFF_S:
wlan.disconnect()
wlan.connect(ssid, password)
deadline = time.ticks_add(time.ticks_ms(), 10_000)
while not wlan.isconnected():
if time.ticks_diff(deadline, time.ticks_ms()) < 0:
break
time.sleep_ms(100)
if wlan.isconnected():
return True
time.sleep(delay)
return False
當那個輔助函式持續回傳 False 時,無線電韌體本身可能已經卡死。最後的手段是對無線電進行電源循環:active(False)、短暫暫停、active(True)、從頭重新連線。這會讓無線電韌體回到已知狀態,代價是額外幾秒的停機時間:
def radio_power_cycle(wlan, ssid, password):
wlan.active(False)
time.sleep(1)
wlan.active(True)
return reconnect(wlan, ssid, password)
一台已離線數分鐘的相機是應用程式必須察覺的真正故障。恢復程式碼應該將該狀態顯現出來——在主迴圈會檢查的旗標中將網路標記為不健康,並讓應用程式在該旗標清除前略過原本要進行的網路傳送——這樣長時間的中斷才不會讓應用程式停滯在永遠不會寫入的 socket 上。
9.4.5. 乙太網路(如有配備)¶
具有內建乙太網路的板子在不需要 connect 步驟的情況下也採用相同的模式。LAN 介面以 active() 啟用,而纜線一插上,介面便可立即使用:
import network
lan = network.LAN()
lan.active(True)
print("link up")
從這一點起,無論是哪個介面讓相機接上網路,本節其餘的頁面都以相同的方式適用。較高層不在意它們下方的連結是 Wi-Fi 還是乙太網路——「已連線」就是「已連線」。
如需完整的 WLAN 與 LAN 參考,包括這裡放不下的設定旋鈕,請參閱 network --- 網路設定。