IoT 系統的末端節點通常是由小型的嵌入式設備來擔任,因為節點數量很多,在佈署或改版調整時,需要一個可以由遠端主機動態規劃、佈署、管理與控制的機制,才能具有省時省力、動態與彈性的優點。
現今有許多 IoT 的 frameworks,都是基於 MQTT 的協定。MQTT 與 AMQP 都是一種 Message Queue 的機制,相關 implementations 都有類似的架構,由一個 Broker(Hub) 對應很多 Workers (Engines) 來組成,Celery 和 IPython Parallel 都是很有名的應用 Message Queue 的 framework。
本實驗中基於類似 IPython Parallel 的架構,運用幾顆運行 MicroPython 的 ESP8266 模組,將之視為 Worker(Engine)來建構一個 swarm 型態的小型系統,並且受惠於 Python 的特性,可以動態地傳送 任意的程式碼 要求遠端端點執行,因此端點上的運行邏輯隨時可變,並不受限於端點上的預設程式碼。
基於上述的機制,我們可以經由中央主機讀寫各遠端節點上的GPIO來進行動態控制,也可以透過網路佈署程式碼交由各節點獨立運行,與一般 cluster 架構不同的是,各節點雖然很小,但其之間也可以互相直接溝通,共同建構一個自主、動態的 IoT 系統。
自造者與物聯網的趨勢相當程度地促使 Arudino 成為熱門的工具。網路上也有很多教學,使用 Arduino 透過 WiFi 模組連上網路並傳輸資料,這種作法應該已經是Maker界最基本的必備技能了。
Arduino 具有相當的方便性,開發模式與環境都相當容易上手,個人覺得具有下列的特色:
**Arduino 運作模式的優缺點**:
使用 Arduino 的時候,都會將寫好的程式燒錄到 Arduino 開發板子上,然後 Arudino 即可獨立運作,必要的時候透過 Serial、Bluebooth、WiFi 與外界溝通傳輸資料。
這種模式的好處是:
但相對的 缺點是:
一方面因為需要容納更複雜的邏輯,另一方面如果機器的數量很多,地理位置分散,更新程式將是費時費力的工作,因而有了 Firmata 這種混合式的作法。
**Firmata 的優缺點**:
Firmata 基本上就是把 Arduino 當作一個 interpreter,接收主機透過 Serial 介面傳來的指令,解譯之後處理之。而主機上有一個對應的代表Arduino的軟體物件,主機與代表Arduino的軟體物件的互動,都將會透過Serial介面傳遞並指揮遠端的 Arduino實體硬體做對應的動作 (範例)。
這種模式之下的優點是:
而相對地 缺點是:
然而,在 IoT 很熱門的今天,已經不是一兩台設備的規模而已,設備的數量會比較多,之間要怎麼傳遞訊息與資料呢?
**IPython Parallel**:
在上述關於 MQTT 的介紹中我們可以看到,系統是以一個 MQTT broker 為中心,各節點透過 broker 來交換訊息。
有很多 Desigh Pattern 因為很優秀,所以常常可見。MQTT 和 之前淺略接觸過的 IPython Parallel 在架構上都有相似之處。
IPython Parallel 基本上也是以一個 hub 為中心,而它所稱的 engine 其實就是 worker。
IPython Parallel 最讓我印象深刻的,也是和 Celery 很大的一個差異點,就是透過 IPython Parallel 可以動態地派送任意的程式碼給遠端的 engine (worker) 去執行,程式碼不用先存放一份在遠端節點上,這讓 IPython Parallel 顯得比較彈性多了。
能與遠端節點做 RPC 的互動,而且能動態的佈署程式與邏輯到遠端的節點上,我覺得可以是 IoT 系統的兩隻翅膀,有之 應該會具有相當的優勢。我們可以把Arduino 或者 ESP8266 這類的節點視為 Engine(Worker),可以由中央主機統一調度,發派任意的程式碼要求遠端執行。
但是,要在 Arduino 上面用 C 語言建構這類的平台,至少對我來說是有點不敢想像的。
之前參加 Taichung.py 的一場 Meetup,由 Max Lai 解說 MicorPython 的技術應用,內容生動有趣,看到小小的 ESP8266 上居然可以跑 Python 的程式,覺得很神奇,但是一直到最近才有時間動手試試看。
另外也有 D1 mini ,也是基於 ESP8266-12E,接腳較少但是體積更小巧一點。
**Python 與新的運作模式**:
既然現在有了 MicorPython,在 ESP8266 上面可以實行 Python 的程式,我們是否可以利用 Python 的優勢,在使用 ESP8266 作為 IoT端點 的時候,具有以下的優點:
ESP8266 是用來作為 IoT端點的絕佳選擇,借助 Python 的優點,希望在建置私有 IoT系統的時候,可以比較方便快速。
# filename: codes/shared/config.py
import sys
## Must config ******************
BROKER_HOST = '192.168.0.105'
BIND_IP = '0.0.0.0' # the ip which broker listens to.
HUB_PORT = 9662
## Must config ******************
這個訊息,要求遠端節點閃燈 10次。
messages['blink_led'] = {'type': 'command',
'command': 'blink led',
'kwargs': {'times': 10, 'forever': False, 'on_seconds': 0.05, 'off_seconds': 0.05}}
這個訊息,要求遠端節點回傳 GPIO pins 的狀態。
messages['read_GPIOs'] = {'type': 'command',
'command': 'read GPIOs',
'need_result': True}
這個訊息,要求遠端節點依序設定指定 pin 的 value (pin 2 代表 on board LED)。
messages['write_GPIOs'] = {'type': 'command',
'command': 'write GPIOs',
'kwargs': {'pins_and_values': [(2, 0), (2, 1), (2, 0),]}}
這個訊息,要求遠端節點 evaluate '2+3',並傳回結果。
messages['test eval'] = {'type': 'eval',
'to_evaluate': '2+3',
'need_result': True}
這個訊息,要求遠端節點執行一個 statement。
messages['test exec'] = {'type': 'exec',
'to_exec': 'print("Testing exec !")'}
訊息寫好之後,就要求 client 將之傳送給遠端的節點。
client.request(remote_node, message)
如果主機很忙,希望節點可以獨立自主運作,那也可把控制邏輯放在一個 while True 的迴圈中,並將程式碼遠端佈署給節點執行,節點就會進入自主運作的狀態 (不過 watch dog 或其他中斷的機制需先設定好)。
例如,我們可以將以下程式碼,遠端佈署給節點執行,這段程式碼象徵性的會讓 LED閃個不停。
# filename: codes/_demo/script_to_deploy.py
print('_______ testing remote deploy ______')
print('_______ deployed from remote _______')
import machine
import time
def blink(pin, on_seconds = 0.5, off_seconds = 0.5, on = 0, off = 1):
pin.value(on);time.sleep(on_seconds);pin.value(off);time.sleep(off_seconds)
def main():
on_board_led = machine.Pin(2, machine.Pin.OUT)
while True:
blink(on_board_led) # 主要邏輯放這邊
# main() will be invoked after this script is uploaded.
main()
Script 檔案準備好之後,就在 client 端用以下的程式把檔案上傳到遠端節點並令其執行,LED燈就會閃個不停了。
with open('script_to_deploy.py') as f:
script = f.read()
messages['test upload script'] = {'type': 'script',
'script': script}
client.request(remote_node, message)
這次實驗藉由 Broker-Workers 的架構,讓在主機上的 client 端可以透過網路讀寫遠端 ESP8266 模組上的 GPIO 並加以控制,並且,可以動態傳遞一段程式碼要求節點執行,節點也可以藉由執行主機交付的迴圈程式而成為一個獨立運作的單元。
跟 IPython Parallel 不同的是,本系統中的 Client 端並不直接跟 Hub 溝通,Client 端其實是透過一個 local node(worker)來跟其他節點溝通,node 和 node 之間可以溝通,並不需要 client 端的介入。
目前 Client 端雖然可以讀寫遠端的節點,並且可以要求遠端節點執行指定動作並 return 答案,但須再加強介面與功能,後續若有時間再做。
P.S.: