2017年9月12日 星期二

MQTT bridged LoRa networks with MicroPython on ESP32

Wei Lin
2017/09/12

緣由


  前一陣子做了 MQTT 的實驗,最近又開始接觸 LoRa,有天想到可以把這兩項技術結合在一起,採用 蜂巢網路的模式,透過 MQTT 串聯與整合 分散在各地區的 LoRa networks。

   為了以後可能的需要,就花了一個週末的時間先把這個模型做出來,如果以後我需要佈署自己的 LoRa 網路時就可以派上用場。

  目前只有做出這個模型,沒有經過壓力測試。我手上只有四個 LoRa transceivers,所以只能用這樣兩組 gateway + node 的組成來測試。

  不 follow LoRaWAN 的規範,純粹個人實驗。

GitHub repo.



LoRa 的優點

    LoRa 最近很熱門,因為它具有 範圍大,低耗能,成本低的特點:

  • 長距: 涵蓋面積大,只需要少數 gateways 即可涵蓋頗大的地理範圍。
  • 低耗能: 靈敏度可以到 -148dbm ,可以用較低的能量傳遞較遠的距離。
  • 末端節點成本低: 單一 channel 的 transceiver 並不算貴。
  • 平行作業: 使用無線電廣播,訊號可同時傳達 多個點。



出發點與目的

    其實我比較注重 如何建構 M2M (machine to machine) 的溝通機制,主要是想利用 LoRa 低成本、低耗能的優勢,用大範圍無線通訊 作為 M2M event system 中,事件推播的機制。



LoRa 的缺點

    但是如果要在城市中佈署 LoRa 網路,也是會遇到很多缺點與困難,例如:

  • 制高點得之不易: 制高點相對稀少 且可能有額外成本。
  • gateway 設備並不是很便宜:
    • 市面上的 LoRa gateway 通常具有同時多 channels 收發訊號的功能,價格由數千元至上萬元不等,實在不算便宜。
  • 整體資料throughput 不大,因為:
    • 障礙
      • 在都市叢林中會受到障礙與干擾的影響,傳輸速率受限。
    • 環境吵雜 訊號碰撞
      • 涵蓋範圍越大,表示其中的 nodes 越多,訊號碰撞的機率越高,整體有效的資料傳輸量較容易受到限制。
      • 訊號涵蓋範圍大,如果主要是為了 downlink 的用途的話 那就太棒了,因為發送的訊息可以同時送達範圍內所有的 nodes。但如果主要的用途是要從廣大地區收集資料回來,也就是以 uplink 為主的用途,那麼 涵蓋範圍越大,nodes 數量越多 越容易發生訊號碰撞的情況。
    • 傳輸速度不高
      • 為了要傳比較遠端距離,傳輸速度不能太快,這算是 傳輸距離遠 的代價。



想法

    主要的想法是:

  • 採用 GSM 蜂巢網路的模式

    • 城市中 建物障礙多,line of sight 難得,LoRa 訊號的傳輸功率開大的話,影響範圍更大,涵蓋的 node 數量會較多,容易會有更多的訊號碰撞。
    • 不如採用 GSM 蜂巢網路的模式,功率開小一點,gateway 多一點,每個 gateway 因為功率較小,涵蓋範圍也較小,範圍內的 node 數量少,訊號碰撞的機率就會跟著比較低,傳輸速率可以提高;而且 每個 gateway 的涵蓋範圍雖然不大,但因為數量多, 所以整體涵蓋範圍也不小,也比較容易消除訊號的死角,反而比較適合在城市之中佈署。
  • 以 MQTT 作為傳遞資料的 backbone
    • MQTT 因為採用 publish / subscribe 的模式,可以做到廣播的效果,也可以針對性地傳遞資料給指定的對象,相當彈性且高效能,可以做為 在各 gateways 之間傳遞資料的backbone。
    • 一個 gateway 其實就是一個 MQTT client,可以收發 MQTT 訊息。
  • gateway as woker
    • 以 Celery 的觀點來看,一個 gateway 也是一個 worker,我們可以透過 MQTT 傳達 message 給各個 gateways,指揮它們做特定的工作。各個 gateway 之間也可以藉此機制互相溝通與合作。




作法與特色

    主要的作法與特色是:

  • 定址
    • 每個 LoRa node 或者 gateway 都有一個 EUI-64 的地址
  • 無主機
    • gateways 們會互相分享與交換資料,並無中央主機來做管理。
  • Routing table 自動產生與更新
    • LoRa node 每次發射訊號的時候,會被依據訊號強度 自動 clustering 並歸屬到最接近的 gateway,由該 gateway 負責回應 ACK 或收發其他 LoRa packet。而且 gateways 之間會隨時互相交換 routing table 資料,LoRa node 可以無隙 roaming。
    • 可以用 RSSI 來做 地理位置定位 (Geolocation)。
  • 兩種傳遞資料的模式
    • 如果 payload 中有註明 目的地 的 EUI,則會藉由 MQTT 將之 route 到離目的地最近的 gateway 才轉而以 LoRa 的訊號發送出去。
      • 一個 LoRa node 可以 "撥號" 並傳遞資料給某一個特定的 LoRa node ,只要指定對方的 EUI 地址即可,payload 會由 gateways 自動做 routing,降低對其他地區不相關的 LoRa nodes 的干擾.
    • 如果 payload 中沒有註明 目的地 的 EUI,則會藉由 MQTT 將 payload 傳送給每一個 gateway,再轉為 LoRa 訊號發射出去給各 network 中的 LoRa nodes,效果等同是做全域的廣播。因此,台中,台北,高雄若各有一個 小型的 LoRa network,可以藉由此 MQTT 平台 串聯並視為單一個 LoRa network。
  • 降低佈署 gateway 的門檻
    • 成本低,每個 gateway 本身的硬體成本 相對低很多, 志願者負擔得起。
所使用的 Gateway (ESP32 + RFM96W)


市面上也有賣這種 整合 ESP32 + SX1278 的模組,
成本可以壓低到三百元以下


    • 不需要設定,只需要連上WiFi 網路,即可成為一個gateway,服務周圍幾百公尺內的 LoRa nodes。
  • 提升資料 throughput
    • 降低碰撞的機率
      • 因為可以依據 目的地的 EUI 位址決定 MQTT route 資料的路線,所以可以避免做全域性的廣播,可以傳遞資料並且避免干擾不相關的其他地區網路。
  • MQTT 與 LoRa 雙向的資料交換
    • 可以由 外部送一個 MQTT message 給 gateway,命令其發送 LoRa 訊號並傳遞資料給特定的 node 。
    • 可以藉由 MQTT 的機制收集 末端節點透過 LoRa 訊號 所傳送出來的資料。



實驗模型說明

  • 由左到右分別是 node2, gateway2, gateway1, node1
  • 使用 SF(Spreading Factor) 隔離:  node2, gateway2 的 SF = 9,node1, gateway1 的 SF = 8。這樣可以模擬兩個距離相當遠,原本無法相通訊息的 LoRa networks。
  • node1, node2 隨機 每隔 2~4 秒傳送一個 packet 給各自的 gateway。
  • node 或 gateway,收到 packet 之後 LED燈會閃一下。
  • gateway 收到 node 所傳來的訊號之後
    • 會透過 MQTT 互相交換網路拓樸資料。
    • 由最接近的 gateway 回應 ACK。
    • 會依據所收到的 payload 中所標註的 目的地 EUI,將 payload forward 給 最接近目的地 的 gateway 做 transmit。
  • 可以由 PC端發送 MQTT message 命令某一個 gateway 傳送 message中夾帶的 payload 給任一指定 EUI位址的 LoRa node。
  • node1 是用 ESP8266 做的,LED燈在上方,有點被擋住,透過電腦螢幕的鏡面反射反而看得比較清楚。


後記
  • 2017/09/15: 
    • 打破界線,將 SF 都設為 8,所以現在四個 device 彼此都會聽到對方的 LoRa 訊號,而兩個 gateways 就必須要能判斷一個訊號是由別的 gateway 還是  node 傳過來的,並且藉由 RSSI 來隨時判斷每個 node 要由哪一個 gateway 負責對應來收發 LoRa 訊號。
  • 最近入手這種 ESP32 + SX1278 LoRa + SSD1306 OLED 整合模組,整體設計相當簡潔,目前的價格大約台幣 300元,以後可能更便宜。

      為了兼容這個模組,特別加了一個 controller_esp_ttgo_lora_oled 的 subclass,並且將有使用到的 SSD1306 driver 含括進來。 雖然腳位定義不同,但已經都封裝在 controller 的子類別 (controller_esp_ttgo_lora_oled ) 中,所以應用的時候使用者可以如常使用 controller 所定義的 interface 即可,不需要去操煩底層實作的細節。





有個 display 還是比較方便一點,也許以後可以做個 LoRa 推文顯示器




參考資料




    2017年8月11日 星期五

    SX127x (LoRa transceiver) driver for (Micro)Python on ESP8266/ESP32/Raspberry_Pi

    Wei Lin
    2017/08/11



    1. 緣由

        最近幾年 LoRa 很紅,使用這種技術的無線通訊模組 通常比較 便宜(使用免費頻段)、小、省電、通訊距離長 (~15Km),主要用來讓末端遠距的 IoT 設備能互相溝通 或 與主機交換資料。市面上的 LoRa 模組很多,最近入手的是 HopeRF 出的 RFM96W其上搭載 SX1278 (相容)晶片體積很小 很迷人。

    RFM96W 模組的外觀如下圖,大約只有 16mm x 16mm 見方,中間的正方形 IC 就是 SX1278 (相容)晶片 

    使用 Arduino 搭配 RFM96W 很快就可以做出一個可以收發資料的裝置,網路上有 SX127x library for Arduino 可以用,很方便。

    如果是使用 Raspberry Pi 來主控,可以用 Python 搭配 pySX127x 來驅動 RFM96W。

    但是我最近比較常用 ESP8266 + MicroPython pySX127x 太大了 無法在 ESP8266 上面使用,所以我只好用 Python 把 arduino-LoRa 改寫成 for  MicroPython ESP8266 的版本。

    GitHub repo. 在這



    2. 效果

    可以半雙工 互傳資料,很有趣,改天再來試試看能打多遠的距離。

    LoRa 半雙工 互傳資料
    使用 SX1278 + MicroPython + ESP8266 


    reboot 的時候 LED 會閃兩次
    後續每收到一個 packet 的時候 LED 會閃一下



    3. Config

    RFM96W vs. NodeMCU 接腳對應

    RFM96W pinout



    NodeMCU (ESP8266) pinout


    接腳對應


    接好大概就像這樣




    4. 使用方式

    簡單的收發測試方式:

    1. 按照上述的接腳對應方式準備兩組 NodeMCU+SX1278模組 (RFM96W)
    2. NodeMCU (ESP8266) 燒錄 MicroPython 韌體
    3. 由 GitHub 下載 檔案
    4. 將下圖 DuplexCallback 資料夾中的檔案分別 全部上傳到兩組 NodeMCU上 (直接放在 根目錄之下即可)。
    5. 上電之後兩台 NodeMCU 應該就會開始互傳資料。






    5. 使用 ESP32

    ESP8266 內建 WiFi 連網的能力,這樣已經足以作為一個 Gateway 了,但是 ESP8266 的記憶體比較小 有時候不夠用,所以今天把其中一台改為使用 ESP32,程式也調整了一下 可以兼容。

    for ESP32 的 MicroPython 韌體 在這

    NodeMCU (ESP32) pinout

    接腳對應

    接好線 大概像這樣

    哥倆好



    6. 使用 Raspberry Pi

    既然已經做到這邊,索性再多花一點時間兼容 Raspberry Pi,我是使用 Raspberry Pi Zero W
    以前一直沒有用 Raspberry Pi 實作硬體專案,這次剛好藉這個機會熟悉一下 RPi 的硬體介面操作。



    Raspberry Pi 的 pinout


    接腳對應
    (這是對應 2017/09/02 之前的程式碼)

     (這是對應 2017/09/02 之後的程式碼)


    接好線 大概像這樣
    (這是對應 2017/09/02 之前的程式碼,
    之後已經調整 NSS和DIO0 的對應腳位)


    LoRa 半雙工 互傳資料
    ESP8266+ESP32+Raspberry Pi Zero W



    6.1 使用 Raspberry Pi 測試
    1. 要先 enable Raspberry 上的 SPI 介面
    2. RPi 上需安裝:
      1. Python3
      2. sudo pip3 install RPi.GPIO
      3. sudo pip3 install spidev
    3. 按照上述的接腳對應方式接線。
    4. 由 GitHub 下載 檔案
    5. 將下圖 DuplexCallback 資料夾中的檔案 全部上傳到 RPi 上的某一個資料夾中。
    6. cd 到該資料夾的位置。
    7. 在該資料夾中 執行如下的 Python 程式碼,RPi 就會開始半雙工收發資料 : 
      • sudo python3 main.py





    7. 支援雙通道 (雙 transceivers)

    看到網路上有人用 ESP8266 搭配兩個 LoRa transceivers 做成雙通道的模組,這倒是一個好主意,我也來改一下。


    接腳對應 for ESP8266


    接好線 大概像這樣

    LoRa 雙通道 on ESP8266




    接腳對應 for ESP32

    接好線 大概像這樣

    LoRa 雙通道 on ESP32




    接腳對應 for Raspberry Pi

    接好線 大概像這樣



    7.1 使用 Raspberry Pi 測試
    1. 要先 enable Raspberry 上的 SPI 介面
    2. RPi 上需安裝:
      1. Python3
      2. sudo pip3 install RPi.GPIO
      3. sudo pip3 install spidev
    3. 按照上述的接腳對應方式接線。
    4. 由 GitHub 下載 檔案
    5. 將所有的 *.py 檔案都複製到 RPi 上的某一個資料夾中。
    6. cd 到該資料夾中。
    7. 將 main.py 中 第 8 行的 "import test" 改為 "import test_dual_channels as test"
    8. 進入 Python3 環境:
      • sudo python3
    9. 執行如下的 Python 指令,RPi 就會開始使用兩個 RFM96W 模組接收資料 : 
      • import main

    8. 使用 TTGO 的 ESP32+LoRa+OLED 整合模組

      網路上有賣這種 ESP32 + SX1278 LoRa + SSD1306 OLED 的整合模組,整體設計相當簡潔,目前的價格大約台幣 300元,以後可能更便宜。

      為了兼容這個模組,特別加了一個 controller_esp_ttgo_lora_oled 的 subclass,並且將有使用到的 SSD1306 driver 含括進來。 雖然腳位定義不同,但已經都封裝在 controller 的子類別 (controller_esp_ttgo_lora_oled ) 中,所以應用的時候使用者可以如常使用 controller 所定義的 interface 即可,不需要去操煩底層實作的細節。







    有個 display 還是比較方便一點,也許以後可以做個 LoRa 推文顯示器





    A. MQTT bridged LoRa networks with MicroPython on ESP32

      有天想到可以用 蜂巢網路的模式,透過 MQTT 串聯分散在各地區的 LoRa networks,於是就做了 這個實驗



    2017年2月7日 星期二

    Elastic Network of Things with MQTT and MicroPython

    Wei Lin
    20170207
    圖片來源


    摘要

     IoT 系統的末端節點通常是由小型的嵌入式設備來擔任,當節點數量較多時,之間資訊交換的複雜性也就跟著提高,而系統在佈署或改版調整時,更加需要一個可以由遠端主機動態規劃、佈署、管理與控制的機制,才能 具有省時省力、動態與彈性的效果。
     
     現今 IoT 的主流 frameworks,有許多都是基於 MQTT 的協定,屬於一種在 broker-client 架構上的 publisher / subscriber 機制,雖然推拉訊息的方式相當具有彈性,然而對訊息的操作僅有 publish 和 subscribe 兩種方法,普遍缺少 RPC 的功能,在撰寫程式與建構系統的時候會受到某種程度的限制。

     然而,有很多優秀的 message-queue frameworks,例如 CeleryIPython Parallel,有著類似的broker-client 架構與 producer / consumer 機制,並且具有 RPC 的功能,造就了強大的 平行/分散式運算的環境。

     本實驗中基於類似 IPython Parallel 的架構,運用幾顆運行 MicroPythonESP8266 模組,將之視為 Worker(Engine)來建構一個小型系統,並且受惠於 Python 的特性,可以動態地傳送 任意的程式碼 要求遠端端點執行,因此端點上的運行邏輯隨時可變,並不受限於預設的程式碼。
     
     本系統採用 Mosquitto 作為 broker,遵循  MQTT 協定,各節點透過 publisher / subscriber 的方式來傳遞訊息時具有相當的彈性與效率。除此之外,本系統還提供了 RPC 的機制,在建構系統的程式方法上有更大的自由度。

     基於上述的機制,我們可以經由中央主機讀寫各遠端節點上的GPIO來進行動態控制,也可以透過網路佈署程式碼交由 各節點獨立運行,各節點  (worker) 雖然很小,但其之間也可以互相溝通,共同建構一個自主、動態的 IoT 系統。  

     


     

    說明

     Arduino 的出現掀起了一股 Maker 的風潮,它的成功歸功於很多因素,很重要的一點是因為 Arduino 具有相當的方便性,開發模式與環境都相當容易上手,個人覺得具有下列的特色:


    Arduino 運作模式的優缺點:

     使用 Arduino 的時候,都會將寫好的程式燒錄到 Arduino 開發板子上,然後 Arudino 即可獨立運作,必要的時候透過 Serial、Bluebooth、WiFi 與外界溝通傳輸資料。 這種模式的好處是:
    • 獨立性高而且相當的穩定,
     但相對的 缺點是:
    • 程式改版很麻煩,需要將機器下線,燒錄新版的程式,再重新上線。
    • 無法容納複雜的邏輯,因為 Arudino 上的記憶體空間有限。
     一方面因為需要容納更複雜的邏輯,另一方面如果機器的數量很多,地理位置分散,更新程式將是費時費力的工作,因而 有了 Firmata 這種混合式的作法。


    Firmata 的優缺點:

     Firmata 基本上就是把 Arduino 當作一個 interpreter,接收主機透過 Serial 介面傳來的指令,解譯之後處理之。而主機上有一個對應的代表Arduino的軟體物件,主機與代表Arduino的軟體物件的互動,都將會透過 Serial介面傳遞並指揮遠端的 Arduino實體硬體做對應的動作 (範 例)。
    這種模式之下的優點是:
    • 遠端主機可以藉由連線 讀寫硬體機器上的 GPIOs,並做對應的處理與控制,邏 輯上可以更複雜與彈性,遇到邏輯需要變更的時候 能保有相當程度的彈性與方便性。
    • Arduino除了接收並執行遠端主機傳過來的指令之外,也可以以 loop迴圈同時執行固定的工作,具 有某種程度的獨立性
    而相對地 缺點是:
    • 如果需要主機讀寫GPIOs並加以控制,則會增加主機的負擔
    • 如果 Arudino 中的 loop 迴圈程式的邏輯需要調整,還是必須要經過 下線、燒錄新版程式、重新上線的過程,仍然逃不過改版費時費力的 宿命。



      在 IoT 很熱門的今天,一個系統已經不是一兩台設備的規模而已,設備的數量會比較多,除了上述的 系統佈署、程式改版的彈性與方便性之外,更需要有一個高效的資訊溝通機制,也因此有了適合 IoT 設備的 MQTT 通訊協定。




    MQTT 的角色與特性:

     目前有許多 IoT 的 frameworks,都是基於 MQTT 的協定,基本上是一種在 broker-client 架構上的 publisher / subscriber 機制,其中設立了很多 "Topic",各節點透過定義好的 topics 來溝通。可惜大部分的 MQTT implementations 都沒有提供 RPC 的機制。如果可以具有 RPC 的功能,我覺得會大大地提升 IoT 系統的威力。

    Swarm 圖片來源


     物聯網裡面的物件當然要有聯網的能力,近兩年 ESP8266 因為具有 WiFi 的聯網能力,價格也相當便宜,因此在 Maker 界相當的受到歡迎與愛用,而且它可以獨立運作,不一定要搭配 Arduino 。



    ESP8266 可以獨立運作:

      ESP8266 是一款相當受到歡迎的WiFi晶片,衍生的模組與可以找到的資 源相當多。
    其實 ESP8266 並不是必須搭配 Arduino 才能工作,它裡面其實也有一個 MCU,而且具有自己的 GPIOs、ADC、Serial... 腳位。 有的 ESP8266 模組具有 512KB ~ 4MB 或以上的 Flash,因此,我們可以將程式碼直接燒錄在 ESP8266 模組上面,把它當成一個獨立的個體,用在各種適合的用途上,也就是說,可以把它當成一個具有 WiFi能力的 Arduino 來用

     既然 ESP8266模組可以獨立運作,又可以連上網路,我們可以把它視為可獨立使用的 最小的 IoT 單元。然而,是否還是只能燒錄一些 MQTT 或者其他協定的資料收發程式來使用呢? 有沒有更有彈性與威力的作法?

     

    IPython Parallel:

     在上述關於 MQTT 的介紹中我們可以看到,系統是以一個 MQTT broker 為中心,各節點透過 broker 來交換訊息。這種架構因為很優秀,所以也常常可見。MQTT 和 之前淺略接觸過的 IPython Parallel 在架構上都有相似之處。

    圖 片來源
     
       
     IPython Parallel 最讓我印象深刻的,也是和 Celery 很大的一個差異點,就是透過 IPython Parallel 可以動態地派送任意的程式碼給遠端的 engine (worker) 去執行,程式碼不用先存放一份在遠端節點上,這讓 IPython Parallel 顯得比較彈性多了。另外不管是 IPython Parallel 或者 Celery,都提供 RPC 的機制,讓建構於其上的系統更有效能。

     能與遠端節點做 RPC 的互動,而且能動態的佈署程式與邏輯到遠端的節點上,我覺得可以是 IoT 系統的兩隻翅膀,有之 應該會具有相當的優勢。我們可以把Arduino 或者 ESP8266 這類的節點視為 Engine(Worker),可以由中央主機統一調度,發派任意的程式碼要求遠端執行。

     但是,要在 Arduino 上面用 C 語言建構這類的平台,至少對我來說是有點不敢想像的。




    然而,現在有了 MicroPython



     之前參加 Taichung.py 的一場 Meetup, 由 Max Lai 解說 MicorPython 的技術應用,內容生動有趣,看到小小的 ESP8266 上居然可以跑 Python 的程式,覺得很神奇,但是一直到最近才有時間動手試試看。




      

      
    NodeMCU簡介:

     Max Lai 使用的硬體是 NodeMCU ,基本上是一塊 ESP8266-12E 的模組加上 4MB 的 flash 和一些電源與USB-UART的線路所組成,可以用一般的 MicroUSB 線連接到電腦,就可以很方便地與電腦溝通,可以使用 C、Lua、Python (MicroPython 版本) 程式語言開發程式並燒錄到 ESP8266上,網路上可參考的範例很 多。

     
    另外也有 D1 mini ,也是基於 ESP8266-12E,接腳較少但是體積更小巧一點。
    HTML5 Icon

     

    Python 與新的運作模式:

     既然現在有了 MicorPython, 在 ESP8266 上面可以實行 Python 的程式,我們是否可以利用 Python 的優勢,在使用 ESP8266 作為 IoT端點 的時候,具有以下的優點:
    • 可如同一般的 Arduino 一樣獨立運作, 不需要外力介入。
    • 也可以採取 Firmata 的模式,由遠端的主機透過過網路讀寫 ESP8266 上的 GPIOs,作相對的處理與控制,邏 輯上可以更複雜與彈性
    • 程式改版方便,可以透過網路即時佈署
    • 可以動態地傳送 任意的程式碼 要求遠端端點執行。
    • 提供 RPC 的機制。
    • 各節點可以互相彼此自主溝通。
     ESP8266 是用來作為 IoT端點的絕佳選擇,借助 Python 的優點,希望在建置私有 IoT系統的時候,可以比較方便快速。




    實驗性小型系統

     於是,我花了幾天的時間依據 IPython Parallel 的精神, 寫了一個 Hub-Workers 架構的小型系統,把 NodeMCU 和 D1 mini 視為 Engine(Worker),並且提供了 RPC 的機制,node 之間可以互相溝通,管理者也可以派送任意的程式碼要求遠端節點執行,感覺上這樣方便多了,因為每個節點的行為可以透過網路動態的被調整。

     原本第一版的系統是以自建的一個 socket server 作為 broker 來收發訊息,但是因為只有提供 QoS level 0 的服務,後來還是改用 Mosquitto 作為 broker,遵循 MQTT 協定,惟 MicroPython 官方所提供的 MQTT client 端 library 的限制,只能支援到 QoS level 1。以 Mosquitto 來做 broker 也更能融入既有的系統環境,相容性好資源豐富,好處多多。



    相關準備與設定

    config.py

     基本上只需要修改 BROKER_HOST 的位址即可,ip 或 domain name 皆可。
     各節點連上網路之後,會嘗試跟 broker 連線,因此如果 broker 具有 public ip,應該就可以不受防火牆的限制。

    # filename: codes/shared/config.py
    ...
    # Client ************************
    # Must config ******************
    BROKER_HOST = '192.168.0.114'
    # Must config ******************

    燒錄 MicroPython 到 NodeMCU 上

    設定 NodeMCU 連上 WiFI 網路

     將 MicroPython 燒錄進去之後,Serial 連線進去就會看到 Python 的 prompt >>>
     執行這一行指令就可以了 (SSID 和 password 要填):

     import network; nic=network.WLAN(network.STA_IF); nic.active(True); nic.connect('SSID','password');nic.ifconfig()
    
    

    上傳基本程式到 NodeMCU

     實驗過程中需要常常上傳 Python 程式檔案到 NodeMCU 上面,所以我寫了一個 IPython Notebook 來自動化:

     notebooks/上傳檔案到 NodeMCU (Upload files to NodeMCU).ipynb
    
    

    啟動 Broker

     都準備好了之後,就把 Broker 叫起來,我是在 Raspberry Pi 上面 run 一個 Mosquitto 的 Docker container,可以參考 這裡 所述的方法。

    
    

    測試:

    對遠端節點讀寫 GPIO

     Client 可以透過網路對遠端的節點做以下的事情:
    • 讀寫 GPIO
    • 傳給遠端節點 一段任意的程式碼並要求節點執行
    • 上傳檔案給遠端節點,檔案若是 Python 程式碼,並可以要求遠端節點 import 並執行
      基於上述的機制,等於是可以做任何 節點的硬體能力範圍內的事情。
     我把一些範例都放在這個範例程式碼 ( 2_start_client.py ) 內,直接執行就可以了。
     這個範例城市開始執行時會先透過一個 MQTT 的 topic 廣播 點名要求,要求上線的節點都發訊息過來報到,因此可以很方便地收集上線節點的名單。



    範例程式碼中提供一些用法案例,例如:

    這個訊息,要求遠端節點閃燈 3次。
    messages['blink_led'] = {'type': 'command',
     'command': 'blink led',
     'kwargs': {'times': 3, 'forever': False, 'on_seconds': 0.05, 'off_seconds': 0.05}}
    
    
    
    
    由 PC 對遠端 6 個ESP8266模組做讀寫,並遙控遠端節點閃動 LED。



    這個訊息,要求遠端節點回傳 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)

    但是就如之前所說的,還是需要在硬體的能力範圍之內,ESP8266上面只有 160KB的 RAM,硬塞大檔案當然就是會當機。




    Summary

     這次實驗藉由 Broker-Workers 的架構,以 Mosquitto 作為 MQTT 的 broker,並參考 IPython Parallel 的功能,並提供 RPC 的機制,讓在主機上的 client 端可以透過網路讀寫遠端 ESP8266 模組上的 GPIO 並加以控制,並且,可以動態傳遞一段程式碼要求節點執行,節點也可以藉由執行主機交付的迴圈程式而成為一個獨立運作的單元。


     跟 IPython Parallel 不同的是,本系統中的 Client 端並不直接跟 Hub 溝通,Client 端其實是透過一個 local node(worker)來跟其他節點溝通,node 和 node 之間可以溝通,並不需要 client 端的介入。

     MicroPython 的出現或許會帶來另外一股風潮,除了程式撰寫之便以外,受惠於 Python 語言的特性,末端裝置或許將會更加彈性可塑,這也令人期待更加蓬勃發展的物聯網。

    參考資料


    ESP8266 WiKi
    NodeMCU WiKi
    A python proxy in less than 100 lines of code
    Boards Running MicroPython Asynchronous Socket Programming
    ESP8266 first project: home automation with relays, switches, PWM, and an ADC

    2017年1月21日 星期六

    Elastic Network of Things with MicroPython

    Elastic Network of Things with MicroPython

    Wei Lin
    20170120

    Abstract


     An IoT system has many nodes, each node with very limited resources and usually needs to be uninstalled to have software modified. Considering an IoT system of 100 nodes, it will be a tedious task to revising them. 

      IPython Parallel is a famous distributed-task-queue framework, I built a small system to demonstrate that we can use ESP8266 modules running MicroPython as Workers(Engines) in a IPython Parallel liked Hub-Workers architecture. In this system we can read/write remote nodes' GPIO pins and control them accordingly in a central-control fashion. We can also send any arbitrary script for remote nodes to execute. Also, script files can be uploaded to nodes and have them start operating autonomously.

     The mechanism described above can be used to deploy and manage an IoT system in a whole new way, in which the functionalities and behavior of nodes can be modified remotely and dynamically,  although they had the identical initial program on installation.
    Github Repository


    IPython Parallel:
    In  IPython Parallel's architecture, a hub is responsible for bridging the communication between worker nodes.  Moreover, with IPython Parallel we can send any arbitrary code for workers to execute. That's impressing. Since we have MicroPython now, why don't we follow this paradigm in building IoT system? It will be far more elastic and powerful !
    Just image that we don't need to flash each nodes differently, they all have the same initial code. A system plan will be deployed over the network to each node and modify them accordingly, all at once. Each node and the whole is highly elastic !
    photo source


    The Devices:
    I use  NodeMCU and D1 min
    NodeMCU
    HTML5 Icon




    Experiment Preparation and Setup:

    config.py

    In this configuration file you only need to set the BROKER_HOST address,ip or domain name will do.
    Every node will try to connect the broker after boot, so if the broker has a public ip, then every node will be able to connect it.
    Modify the HUB_PORT to 80 if necessary, however not recommended (there are scanners out there).
    # filename: codes/shared/config.py
    
    ## 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 ****************



    Start the Broker

    Run the script below to start Broker. If nodes have already connected the Broker via WiFi network, then you will see a list of nodes' on broker's console. You will need the name list afterward.
     1_start_broker.py 
    

    Tests:

    Read / Write remote GPIO pins

    For demonstration, please run the script ` 2_start_client.py `, there are some example messages in the file.
    Before running the script, your have to write the names of nodes into the script. There is a list variable for that.


    This message will ask remote nodes to blink LED 
    messages['blink_led'] = {'type': 'command',
        'command': 'blink led',
        'kwargs': {'times': 10, 'forever': False, 'on_seconds': 0.05, 'off_seconds': 0.05}}
    This message will ask remote nodes to return the status of GPIO pins. 
    messages['read_GPIOs'] = {'type': 'command',
         'command': 'read GPIOs',
           'need_result': True}
    This message will write values to remote nodes' GPIO pins. 
    messages['write_GPIOs'] = {'type': 'command',
          'command': 'write GPIOs',
          'kwargs': {'pins_and_values': [(2, 0), (2, 1), (2, 0),]}}
    This message will ask remote nodes to evaluate  '2+3' and return result.
    messages['test eval'] = {'type': 'eval',
        'to_evaluate': '2+3',
        'need_result': True}
    This message will ask remote nodes to execute a statement.
    messages['test exec'] = {'type': 'exec',
        'to_exec': 'print("Testing exec !")'}
    This message will sent to remote nodes with this function.
    client.request(remote_node, message)

    Deploy Script file to remote nodes and have them operating autonomously

    If needed, a script file can be uploaded to remote nodes and an algorithm  is put inside a  while True loop, so the node will function autonomously and only report to central periodically.
    For example, the on board LED will flash continuously  after the following script file was uploaded onto remote nodes.
    # 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 logics here.  
    
    # main() will be invoked after this script is uploaded.
    main()
    We upload the file with just few lines of code below.
    with open('script_to_deploy.py') as f:
     script = f.read() 
    
    messages['test upload script'] = {'type': 'script',
          'script': script}
    
    client.request(remote_node, message)

    Summary� 

    In this mini system, the network of things has highly elasticity. Functionalities and behavior of nodes can be continuously adjusted if necessary.
    Also, unlike some cluster system, in this system a node can communicate with other nodes, ask them to execute a function and return value, without client's intervention, thus M2M communication is feasible.