國 立 交 通 大 學
網路工程研究所
碩 士 論 文
基於 ESP8266 之 IoTtalk 裝置應用:
實作與效能測試
ESP8266-based IoTtalk Device
Application :Implementation and Performance Evaluation
研 究 生:林俊翰
指導教授:林一平 教授
中 華 民 國 108 年 7 月
基於 ESP8266 之 IoTtalk 裝置應用:實作與效能測試 ESP8266-based IoTtalk Device Application: Implementation and
Performance Evaluation
研 究 生:林俊翰 Student:Jun-Han Lin
指導教授:林一平 Advisor:Yi-Bing Lin
國 立 交 通 大 學 網 路 工 程 研 究 所
碩 士 論 文
A Thesis
Submitted to Institute of Network Engineering College of Computer Science
National Chiao Tung University in partial Fulfillment of the Requirements
for the Degree of Master
In
Computer Science
July 2019
Hsinchu, Taiwan, Republic of China
中華民國 108 年 7 月
基於 ESP8266 之 IoTtalk 裝置應用:實作與效能測試
學生:林俊翰 指導教授:林一平 教授
國立交通大學網路與工程研究所碩士班
摘要
近年來越來越多物聯網技術的應用開始發展,應用的場景也越來越多,如家 庭、工廠及汽車等等。為了可以讓這些應用都可以連結,我們運用 IoTtalk 物聯網 裝置管理系統。在 IoTtalk 系統中,可以將各式各樣的物聯網應用連接起來。本碩 論中將使用 ESP8266 開發板,一個具有 Wi-Fi 功能的開發板,連接到 IoTtalk 系 統,並且可以控制其他電器或者感測器,讓原本不具上網功能的電器也可以享受 物聯網帶來的好處。
關鍵字:物聯網、裝置應用、ESP8266
ESP8266-based IoTtalk Device Application :Implementation and Performance Evaluation
Student : Jun-Han Lin Advisor : Prof. Yi-Bing Lin
Institute of Network Engineering, National Chiao Tung University
Abstract
In recent years, more and more applications of Internet of Things technology have begun to develop, and more and more application scenarios, such as homes, factories, and automobiles. In order to make these applications connectable, we use the IoTtalk IoT device management system. In the IoTtalk system, you can connect a wide range of IoT applications. This master will use the ESP8266 development board, a Wi-Fi-enabled development board, connected to the IoTtalk system, and can control other appliances or sensors, so that appliances that do not have Internet access can also enjoy the Internet of Things.
Keywords: Internet of Things, device applications, ESP8266
致謝
首先我要感謝我的指導老師林一平教授,在我做研究以及寫論文的過程中,
他從不鬆懈,不斷的修正我的方向和研究方法。我從老師身上學到做學問時的謹 慎態度和縝密的邏輯,以及最重要的:從更高的視野看整個系統架構。
感謝兩位口試委員:鄭昌杰教授與謝旻錚教授對我的研究給予的建議與指 導。也要感謝林勻蔚博士平時細心的技術提點,以及各種生活上的經驗分享。感 謝泰翔學長與俊頤學長,很高興實驗室有你們兩位程式能力強大的學長,我收穫 良多。感謝啟亦跟育旋,有你們的實驗室生活充滿樂趣。感謝實驗室的學長姐、
同學及學弟妹,你們的陪伴是我研究生活中最大的動力。最後還要感謝我的家 人,常常會打電話來關心我,是我重要的精神支柱。
目錄
摘要 ... i
Abstract ... ii
致謝 ... iii
目錄 ... iv
圖表目錄 ... v
一、Introduction ... 1
IoTtalk ... 1
系統架構 ... 1
裝置應用 (Device Application, DA) ... 3
ESP8266 ... 3
二、HTTP 架構之 DA ... 5
DA API ... 5
CSM API ... 14
三、MQTT 架構之 DA ... 19
MQTT 架構 ... 19
DA API ... 21
CSM API ... 24
四、ESP8266 的擴展 ... 27
腳位擴充 (Pin Extension)... 27
有線網路 ... 34
HTTPS ... 35
五、效能評估 ... 38
HTTP 架構 ... 38
MQTT 架構 ... 46
HTTP 架構與 MQTT 架構比較 ... 48
六、結論 ... 50
參考文獻 ... 51
圖表目錄
圖 1-1 ESP12F 與其他開發板之比較 ... 2
圖 1-2 ESP12F 模組實體照片 ... 2
圖 1-3 IoTtalk 與 ESP8266 架構圖 ... 3
圖 2-1 Arduino 程式碼實作架構圖 ... 5
圖 2-2 Register (HTTP 架構)流程圖 ... 6
圖 2-3 HTTP 架構(V1) 註冊資料(JSON 格式) ... 6
圖 2-4 Register () 函式程式碼 ... 7
圖 2-5 getprofile () 程式碼 ... 8
圖 2-6 Pull () 函式流程圖 ... 10
圖 2-7 Pull () 函式程式碼 ... 11
圖 2-8 GET 封包回傳之 Payload 範例 ... 12
圖 2-9 Push () 函式運作流程圖 ... 12
圖 2-10 push () 函式程式碼 ... 13
圖 2-11 POST() 函式程式碼... 14
圖 2-12 GET()函式程式碼 ... 16
圖 2-13 PUT()函式程式碼 ... 17
圖 2-14 Send_HTTPS 函式程式碼 ... 18
圖 3-1 MQTT 封包格式 ... 20
圖 3-2 IoTtalk V2 註冊流程 ... 21
圖 3-3 V2 Register() 函式程式碼 ... 22
圖 3-4 Callback() 函式程式碼 ... 23
圖 3-5 MQTT Publish() 函式使用範例 ... 23
圖 3-6 MQTT_Conn ()函式程式碼... 24
圖 3-7 MQTT 控制用訊息範例 ... 25
圖 3-8 CtrlHandle () 函式程式碼 ... 26
圖 4-1 ESP12F 模組的數位及類比腳位圖 ... 27
圖 4-2 SN74HC164 實體照片 ... 28
圖 4-3ESP8266 與 SN74HC164 腳位連接圖 ... 28
圖 4-4 SN74HC164 時序圖範例 ... 30
圖 4-5 SN74HC164 腳位說明 ... 31
圖 4-6 CD74HC4067 實體照 ... 31
圖 4-7 ESP8266 與 CD74HC4067 模組連接圖 ... 32
圖 4-8 乙太網路模組(ENC28J60)實體照片 ... 34
圖 4-9 ESP8266 與 ENC28J60 連接示意圖 ... 35
圖 4-10 HTTPS 範例程式 ... 37
圖 5-1 LAN 中測試 ESM 運作週期之架構圖... 38
圖 5-2 使用 Wi-Fi,IoTtalk 伺服器在本地端,ESM 拉動週期對延遲的影響 ... 39
圖 5-3 使用 Wi-Fi,IoTtalk 伺服器在遠端,ESM 拉動週期對延遲的影響 ... 39
圖 5-4 使用 Ethernet,IoTtalk 伺服器在本地端,ESM 拉動週期對延遲的影響 ... 40
圖 5-5 使用 Ethernet,IoTtalk 伺服器在遠端,ESM 拉動週期對延遲的影響 ... 40
圖 5-6 ESP8266 在不同測試環境下之 ESM 拉動週期對延遲的影響結果 ... 41
圖 5-7 ESP8266 本地與遠端伺服器延遲比較 ... 42
圖 5-8 ESP8266 Wi-Fi 與 Ethernet 平均延遲比較圖 ... 43
圖 5-9 ESP8266 使用 Wi-Fi 與 Ethernet 的延遲分布圖 ... 44
圖 5-10 ESP8266 與 Arduino Yun 比較 Wi-Fi 與 Ethernet ... 45
圖 5-11 ESP8266 與 Arduino Yun 比較本地端與遠端 ... 45
圖 5-12 ESP8266 與 Arduino Yun 比較 ESM 拉動週期 ... 46
圖 5-13 IoTtalk V2, Wi-Fi vs Ethernet ... 47
圖 5-14 IoTtalk V2, Local vs Remote ... 47
圖 5-15 IoTtalk V2, ESP8266 vs Arduino YUN ... 48
圖 5-16 ESP8266, HTTP vs MQTT ... 49
一、Introduction
IoTtalk
近年來 IoT 技術在國際間逐年被重視,市場上 IoT 的應用也蓬勃發展,但是 IoT 的應用開發往往需要具備電子電路、程式撰寫等方面的知識,造成應用開發 的門檻,如果能夠提供一套對使用者友善的圖像化 IoT 應用發展系統,讓使用者 只要透過圖形化介面(GUI),控制與設計 IoT 之間的連線就可開發出 IoT 應用的 話,如此能大大降低對開發 IoT 應用的門檻。因此我們發展了 IoTtalk 物聯網管理 系統,本系統幫助使用者快速地建立物聯網設備間的通訊連結,並且於該連結上 設計與開發連線邏輯,使用者不用為連線方式花費心力,可專心致力於開發物聯 網應用的核心邏輯。在我們的系統中,物聯網設備是根據其功能(feature)來進行分 類,諸如偵測溫度、發出震動、移動、飛行高度、方向等功能,而這些功能能夠 被網路端程式所控制而應用到不同的目的。在網路端能夠獨立個別控制這些功能 的前提下,我們就能夠發展針對不同的功能開發對應的獨立軟體模組(independent software module),而網路端程式的開發只要選擇要納入那些功能的軟體模組即可 快速發展出不同的物聯網應用。IoTtalk 系統是一個基於設備功能 (Device Feature) 特徵概念之物聯網設備平台,當物聯網設備連接至系統時,針對各種感測器,
IoTtalk 會自動產生或使用應用軟體來處理,因此每一個輸入設備可以相當方便地 連接至輸出設備。既然輸入功能與輸出功能各自相互獨立,這些軟體模組即可重 複使用來建立網路應用,也就能大幅縮短物聯網應用開發之時程。
系統架構
IoT 設備可採用不同方式聯上 IoTtalk。在 ArduTalk 系列[1], IoTtalk 可連接不 同的 IoT 設備控制板,包括 Arduino Yun,MediaTek LinkIt Smart 7688 duo, ROHM IoT kit, 以及 ESP8266 ESP-12F。以下圖 1-1 將比較以 ESP8266 為控制板的 IoTtalk 架構設計。ESP8266 控制板的實體照片如圖 1-2 所示。為什麼不使用 Arduino Yun 或者 Raspberry Pi3? 因為 ESP8266 加上 ENC28J60 模組的成本遠低於 Arduino Yun 或 Raspberry Pi3,而且 Arduino Yun 可以做到的事情 ESP8266 也都可以做到,並且 延遲結果上並沒有太大的差異,所以不需要選擇效能過剩的 Arduino Yun 或者 Raspberry Pi3,而是選擇成本更低的 ESP8266。
圖 1-1 ESP12F 與其他開發板之比較
圖 1-2 ESP12F 模組實體照片
在本架構中,IoT 設備將會透過 ESP8266 控制板連接上 IoTtalk 伺服器(server;
圖 1-3 (a))。ESP8266 控制板上面運行的 IoT Device Application 軟體(圖 1-3 (b))負 責處理將 IoT 設備接上控制板,包含接受來自 IoTtalk 的命令控制制動器(actuator;
圖 1-3 (c)),或是蒐集資料往上送的感測器(sensor; 圖 1-3 (c))。ESP8266 控制板則 會透過 device application 軟體 (DA; 圖 1-3(d))連到 IoTtalk 伺服器。該 DA 可分成 兩部分,Device application to IoT 軟體(DAI; 圖 1-3(e))負責將 IDA 傳過來的資訊往 後傳遞到 Device Application to Network 軟體(DAN; 圖 1-3(f)),或是將 DAN 的命令 傳遞到 IDA。DAN 透過 Wi-Fi 通訊界面(圖 1-3(g)),接收或傳遞資料到 IoTtalk 伺 服器上。
IoTtalk 伺服器(圖 1-3(a))運行在一台電腦上。此伺服器可以是實體的主機、
雲端上的虛擬機(virtual machine),或是 Wi-Fi AP 上。IoTtalk 伺服器提供網頁圖形 用戶介面(Web-based GUI; 圖 1-3(h)),讓開發者可以設定 IoT 設備之間的輸出端、
輸入端、與連線邏輯,其設定的結果可透過配置、連接和管理(Configuration, Connection, and Management 或簡稱 CCM; 圖 1-3(i))存到資料庫(database; 圖 1-3(j)) 中。控制次模組(Control Sub-Module 或簡寫為 CSM; 圖 1-3(k))負責接收/傳送控制
訊息(Control Message)到 DA。執行次模組(Execution Sub-module; ESM; 圖 1-3 (l)) 負責處理數據訊息(Data Message 如傳感器數據)進行邏輯運算後,再透過 DAN 傳 送到輸出端的 IDA 中,達到控制的目的。基於這樣的設計架構以及連線設定的方 式,開發者就能讓真實 IoT 設備連上 IoTtalk,並利用 GUI 介面進行連線建立與設 計,開發 IoT 應用。
圖 1-3 IoTtalk 與 ESP8266 架構圖
裝置應用 (Device Application, DA)
如圖 1-3 所示,裝置應用(Device Application, DA) 由兩個軟體部分組成。設備 應用至網路(Device Application to the Network,縮寫 DAN)經由 Wi-Fi、3G 或 LTE 與 IoTtalk 進行通訊,用來執行 IDA 註冊(registration)與資料交換。設備應用至物 聯網設備(Device Application to IoT Device,縮寫 DAI)依據 IDA 所指定之訊息格 式與物聯網設備進行通訊,通常是經由藍芽(Bluetooth)、 Wi-Fi、ZigBee 等無線網 路所傳送之字串。
其中每個 DA 又可以包含多個特徵,稱之為”Features”。如果每一個特徵都能夠 被 DA 獨立處理,那我們只需要重新組合這些特徵模組,就能夠組合成新的 DA,如此一來就可以重複使用已開發的軟體。在開發新的物聯網應用時,組合所 需的特徵,即可以產生需要的應用。
在我們的實作當中,我們將 DAI 與 DAN 合併為 DA。因為 ESP8266 與 Arduino YUN 不同的是,ESP8266 晶片可以直接使用 Wi-Fi 連到網際網路,而 Arduino YUN 需要透過 Bridge 與 Atheros AR9331 溝通才能連到網際網路。因此我們在 ESP8266 上整合 DAN 與 DAI 以提升執行效率。
ESP8266
ESP8266 是樂鑫信息(Espressif Systems)公司所開發之低成本且具有完整
TCP/IP 協議 Wi-Fi 微晶片。
ESP8266 是微控制器晶片的型號,其延伸包括有 ESP-01~ES-P12 等等模組,模組 提供相關的電路設計,將 ESP8266 晶片上的腳位拉出來,使我們可以方便的與其 他模組連接。
ESP8266 有多項優點,如(1)成本非常便宜,ESP12F 模組在台灣市售價格只要 80 元新台幣,非常有利於推廣物聯網產品。如果未來要開發成產品,大量採購的 情況下勢必可以再降低成本。(2)低功耗,在使用 Wi-Fi 傳輸資料時,平均電流僅 71 豪安培。在深度睡眠模式時,更低於 0.02 豪安培。(3)硬體功能齊全,核心為 32 位元的微控制處理器,核心頻率最高可至 160MHz,32KB 的 RAM,4MB 的 EEPROM 可以儲存資料使重新開機後還可以讀取舊有資料,有 16 個數位腳位 (GPIO),1 個類比腳位(ADC),支援多種通訊協定,如 UART/IIC/SPI/One-Wire。(4) 與 Arduino 相容,可以使用 Arduino IDE 進行開發,這意味著 Arduino 平台大量的 開發資源,都可以被 ESP8266 使用。
本論文中使用的 ESP8266 皆是使用 ESP-12F 模組來進行開發,如圖 1-2。
ESP-12 模組有 ESP8266 全部的 GPIO 腳位可以使用,但是價格一樣便宜。
二、HTTP 架構之 DA
在 Arduino 的架構中,程式碼的實作如圖 2-1 所示。我們使用的嵌入式系統 架構為單執行緒的 CPU 架構。當電源啟動後,會先執行一次 Setup () 函式,這個 函式只會在電源啟動後執行一次。接著程式碼會執行 Loop () 函式,之後系統會 反覆的執行 Loop () 函式中的程式碼,直到電源關閉。
圖 2-1 Arduino 程式碼實作架構圖
我們在“Setup ()”函式中執行初始化函式( Init () )以及以 Register ()函式註冊 到 IoTtalk 伺服器。當註冊成功後,程式開始執行 Loop () 函式,使用者可以使用 我們提供的 DA API 與 IoTtalk 伺服器互動,以及在 ida () 中撰寫所需的物聯網應 用。我們將在 2.1 節說明 DA API,以及在 2.2 節說明 CSM API。
DA API
本節,我們提供使用者三個可直接使用的基本函式,Register()、Pull()及 Push()。Register()函式將 IoTtalk DA 註冊到 IoTtalk 伺服器,使得 IoTtalk 伺服器上 的控制頁面可以選用該物聯網裝置。Pull() 函式從 IoTtalk 伺服器取得 ODF 的資 料,使用時需要帶入一個參數,即想要取得資料的 ODF 名稱。Push () 函式將 DA 的資料送到 IoTtalk 伺服器,使用時需要帶入兩個參數,包括(1)IDF 與(2)相對應 的資料,資料可以是數字或者字串。
Register ()
Register () 函式將物聯網裝置註冊到 IoTtalk 伺服器,註冊成功後,使用者可 以在 IoTtalk 伺服器的控制頁面中,選擇該物聯網裝置。Register () 函式的運作流 程如圖 2-2 所示。
圖 2-2 Register (HTTP 架構)流程圖
首先程式將執行圖 2-2 中的 getprofile( )函式,這個函式會依據我們的設定檔 以 JSON 格式回傳註冊資料,註冊資料如裝置名稱(d_name)、模組名稱
(dm_name)、DF 清單,註冊資料使用 JSON 格式表示,如圖 2-3 所示。
1 {
2 "profile": {
3 "d_name": "ESP12F.0405", 4 "dm_name": "ESP12F", 5 "is_sim": false, 6 "df_list": [ 7 "ESP12F_IDF", 8 "ESP12F_ODF"
9 ] 10 } 11 }
圖 2-3 HTTP 架構(V1) 註冊資料(JSON 格式)
接著會將得到的註冊資料以 HTTP POST method 送給 IoTtalk 伺服器,如圖 2-2 中 Send HTTP_POST 所示。在 HTTP 的規範中,狀態碼 200 表示該封包傳送正 確,伺服器確認內容無誤。如果這時候收到 IoTtalk 伺服器回傳的封包中狀態碼是 200,表示我們已經成功註冊。如果超過一段時間沒有收到 IoTtalk 伺服器回傳的 封包,或者回傳的 HTTP 封包狀態碼不是 200 的話,我們會再次傳送 HTTP POST 封包,直到收到 HTTP 狀態碼 200 的封包。此時註冊完成。
1 int Register(void){
2
3 httpresp result;
4 result.HTTPStatusCode = 0;
5 result.payload =
(char*)malloc(HTTP_RESPONSE_PAYLOAD_SIZE);
6 memset(result.payload, 0, HTTP_RESPONSE_PAYLOAD_SIZE);
7 POST(&result, getProfile().c_str());
8 while ( result.HTTPStatusCode != 200){
9 Serial.println("[Register]Fail, code"+String(result.HTTPStatusCode));
10 Serial.println(result.payload);
11 delay(1000);
12 memset(result.payload, 0, HTTP_RESPONSE_PAYLOAD_SIZE);
13 POST(&result, getProfile().c_str());
14 }
15 Serial.println("[Register] Successful");
16 return (result.HTTPStatusCode);
17 }
圖 2-4 Register () 函式程式碼
Register () 函式的程式碼如圖 2-4 所示。行 1 宣告 Register () 函式,該函式 會回傳註冊成功的 HTTP 狀態碼,讓使用使用者知道註冊成功了。行 2-5 使用我 們定義的結構 “httpresp”來存放回傳封包的訊息,該結構包含兩個變數,
(1)HTTPStatusCode 用來存放由 IoTtalk 伺服器回應的 HTTP 封包內的狀態碼,
(2)payload 用來存放回傳的 HTTP 封包內的有效附載,在 IoTtalk 伺服器的設定 中,正確的情況下並不會有 payload 這個內容,但若發生錯誤時,IoTtalk 伺服器
會將錯誤訊息放在 Payload 裡面回傳。使用者可以依此訊息進行除錯,常見的錯 誤有(1)錯誤的封包內容、(2)使用錯誤 port、(3)錯誤的標頭內容。所以如果使用者 有需要改動設定的話,建議只改動設定檔內的內容,減少改程式過程中錯誤發 生。行 6 使用 CSM API 中提供的 POST 函式送出 HTTP POST 封包,此函數需要 傳入兩個參數,第一個是行 2 建立的 “result” 結構的指標,用來存放 IoTtalk 伺服器回傳的封包訊息,第二個是註冊資料,由 “getprofile”函式取得,該函式 會回傳由設定檔中所設定的資料,回傳註冊資料,型態是 JSON 字串,該函式會 在下文詳述。行 7-13 使用一個迴圈反覆檢查回傳的 HTTP 狀態碼是否為 200,如 果狀態碼不是 200 的話,將會印出回傳封包中的錯誤訊息,並且等待一秒後再傳 送一次 HTTP POST 封包,直到收到狀態碼為 200 的回傳封包,表示註冊成功。行 14 印出註冊成功訊息到序列輸出埠。行 15 回傳註冊成功的狀態碼。
1 #include <ArduinoJson.h>
2 String getProfile(void){
3 const char* df_list[] = DF_LIST;
4 StaticJsonBuffer<512> JB_root;
5 JsonObject& JO_root = JB_root.createObject();
6 JsonObject& JO_profile =
JO_root.createNestedObject("profile");
7 JO_profile["d_name"] = String(DM_NAME) + "." + String(deviceid).substring(8);
8 JO_profile["dm_name"] = DM_NAME;
9 JO_profile["is_sim"] = false;
10 JsonArray& JO_df_list =
JO_profile.createNestedArray("df_list");
11 for(int i = 0; i < sizeof(df_list)/sizeof(char*); i++) 12 JO_df_list.add( String(df_list[i]) );
13 String result;
14 JO_root.printTo(result);
15 JB_root.clear();
16 return result;
17 }
圖 2-5 getprofile () 程式碼
getprofile () 函式的程式碼如圖 2-5 所示。行 1,匯入函示庫
“ArduinoJson.h”。該函式庫可以從 Arduino IDE 的函式庫管理工具中取得。行 2,宣告 getprofile () 函式,該函式回傳資料的型態為字串(String),使用此函式不 需要額外帶入參數。行 3 宣告一個字元指標陣列,用來存放 DF 的清單。若變數 名稱開頭為 “JO_” 表示該變數是 JsonObject 物件,若變數名稱開頭為 “JB_”
表示該變數是 JsonBuffer 物件。行 4 使用 ArduinoJson.h 函式庫中的 API 宣告一個 記憶體空間,用來存放接下來會用到的 Json 資料,該空間的大小是 512 個位元組 (bytes),我們建議使用者宣告固定大小的記憶體空間,而非動態記憶體空間,因 為 ESP8266 的記憶體空間,並非十分充裕,所以如果使用動態記憶體空間,有可 能會複寫到意料之外的記憶體空間,造成程式運作錯誤或者 ESP8266 當機自動重 開。行 5 宣告一個 ArduinoJson 物件,該物件的資料存放在行 4 所宣告的記憶體 空間。行 6 在行 5 所創造的 JsonObject 物件內在創造一層 JsonObject 物件,以符 合 IoTtalk 伺服器指定之資料格式。行 7 在 JO_profile 物件中加入 “d_name”欄 位。行 8 在 JO_profile 物件中加入 “dm_name”欄位。 “dm_name”是裝置模組 (Device Module,簡稱 DM)的名稱,可以有多個物聯網裝置都是使用 ESP8266 為 控制板。而 “d_name”是每個裝置的名稱,不可重複,命名方式為 dm_name 再 加上一段字串,該字串由 mac Address 組成,因為每一塊 ESP8266 的 mac Address 不會重複,所以 d_name 就不會重複。行 10-12 在 JO_profile 中創造一個陣列物 件,用來存放 DF。行 13-16 將整個 Json 物件轉成 String 型態後回傳。
Pull ()
Pull () 函式用來從 IoTtalk 伺服器取得 DF 的資料,使用時需要帶入一個參 數,即想要 Pull 的 ODF 名稱。要使用 Pull () 函式前,使用者需要先在 IoTtalk 伺 服器控制頁面中選取( bind ) 物連網裝置後,IoTtalk 伺服器才會回應 Pull () 函式 送出的請求。Pull () 函式的流程圖如圖 2-6 所示。首先我們會向 IoTtalk 伺服器送 出一個 HTTP GET 封包,如果收到 IoTtalk 伺服器回傳的封包的 HTTP 狀態碼是 200 的話表示成功 Pull 資料,在該回傳封包的 Payload 中,將會有該 ODF 最後兩 筆資料的時間戳記(Timestamp)及資料。接著會檢查時間戳記,如果與我們上一筆 資料的時間戳記不同,表示這筆資料是新的資料。接著會更新 ESP8266 記憶體中 的時間戳記表,每一個 ODF 會有各自對應的時間戳記。接著將最後一筆資料回傳 給使用者。
圖 2-6 Pull () 函式流程圖
1 String Pull(char *df_name){
2 httpresp result;
3 result.HTTPStatusCode = 0;
4 result.payload =
(char*)malloc(HTTP_RESPONSE_PAYLOAD_SIZE);
5 memset(result.payload, 0, HTTP_RESPONSE_PAYLOAD_SIZE);
6 GET(&result, df_name,0);
7 if (result.HTTPStatusCode != 200) {
8 Serial.println("[PULL]ERROR, " + String(df_name) +
"," + String(result.HTTPStatusCode) +"\n"+String(result.payload));
9 }
10 else {
11 StaticJsonBuffer<HTTP_RESPONSE_PAYLOAD_SIZE>
JB_resp;
12 JsonObject& JO_resp =
JB_resp.parseObject(String(result.payload));
13 int index = get_ODF_index(String(df_name));
14 if( TS[index] !=
JO_resp["samples"][0][0].as<String>() ){
15 TS[index] =
JO_resp["samples"][0][0].as<String>();
16 return JO_resp["samples"][0][1][0].as<String>();
17 } 18 }
19 return "___NULL_DATA___";
20 }
圖 2-7 Pull () 函式程式碼
Pull () 函式的程式碼如圖 2-7 所示。行 1,宣告 Pull () 函式將會回傳 String 型態的資料,並且要代入一個參數,即想要 Pull 的 ODF 名稱。行 2-5,用我們自 行定義的結構宣告一個變數,用來儲存回傳封包的 HTTP 狀態碼及 Payload。行 6,會送出 HTTP GET 封包,代入“httpresp”結構指標及 ODF 的名稱。GET () 函 式會在 2.2.2 節詳細解釋。行 7-9,如果回傳封包的 HTTP 狀態碼不是 200 的話,
就印出封包中的錯誤訊息。行 11-12,宣告一個“ArduinoJson”物件,用來解析回 傳封包中的 Payload。行 13,使用 get_ODF_index() 函式取得該 ODF 名稱在時間戳 記表中的索引。行 14,檢查時間戳記表中的時間與回傳封包中的時間戳記是否不 同,如果不同表示是新的資料。行 15,更新時間戳記表中的時間戳記。行 16,回 傳最新一筆的資料給使用者。
1 {
2 "samples": [
3 ["2019-05-17 19:21:53.615241",[570]], 4 ["2019-05-17 19:21:53.129755",[569]]
5 ] 6 }
圖 2-8 GET 封包回傳之 Payload 範例
圖 2-8 為送出 HTTP GET 封包後,IoTtalk 伺服器回應封包中的 Payload 範 例。該 Payload 使用 Json 格式編排資料,並且會回傳最新兩筆資料,每一筆資料 包含一個時間戳記及數據,數據可以是數字或字串,也可以是一維陣列或多維陣 列。
Push ()
Push () 函式用來將物聯網裝置的資料傳到 IoTtalk 伺服器,需要帶入兩個參 數,(1) IDF 名稱,(2)要傳送的資料,資料的型態需要為字串。
圖 2-9 Push () 函式運作流程
運作流程圖如圖 2-9 所示,使用者輸入的兩個參數,會使用 HTTP PUT 方式 送往 IoTtalk 伺服器,接著會收到 IoTtalk 伺服器回傳的 HTTP 封包,接著回傳 HTTP 狀態碼給使用者,使用者可以從狀態碼知道是否傳送成功,或者傳送失敗 要做什麼相對應的處理。在這裡我們不會有重傳的機制,因為時間戳記都是以 IoTtalk 伺服器的時間為主,所以如果同一筆資料因為上傳失敗而重新上傳,
IoTtalk 伺服器紀錄的是收到重傳封包的時間。所以我們認為資料上傳失敗,使用 者應自行設計重傳機制。
1 int Push(char *df_name, String value){
2 httpresp result;
3 result.HTTPStatusCode = 0;
4 result.payload =
(char*)malloc(sizeof(char*)*HTTP_RESPONSE_PAYLOAD_SIZE);
5 memset(result.payload, 0,
sizeof(char*)*HTTP_RESPONSE_PAYLOAD_SIZE);
6 PUT(&result, value.c_str(), df_name);
7 if (result.HTTPStatusCode != 200)
8 Serial.println("[PUSH] \""+String(df_name)+"\":"
+value+", error:" + String(result.HTTPStatusCode) );
9 return result.HTTPStatusCode;
10 }
圖 2-10 push () 函式程式碼
圖 2-10 為 Push () 函式的程式碼。行 1,宣告 Push ()函式,該函式將回傳一 個型態為整數的值,即 IoTtalk 伺服器回傳的 HTTP 狀態碼。並且需要帶入兩個參 數,(1)為目標 IDF 名稱,型態為字元指標,(2)要傳送的資料,型態為字串。行 2- 5 宣告一個我們自行定義的結構 “httpresp”儲存我們將要回傳的資料。行 6,將 行 1 得到的兩個參數及行 2 宣告的結構,帶入到 PUT () 函式,該函式將在 2.2.3 節介紹。行 7-8,如果回傳的封包中的 HTTP 狀態碼不是 200 的話,將會印出錯誤 訊息。行 9,回傳 HTTP 狀態碼給使用者。
CSM API
本節敘述 CSM API 當中 2.2.1 節 POST ()、2.2.2 節 GET ()及 2.2.3 節 PUT (),
三個函式專注於“傳送 HTTP 封包”這個行為,裡面包含更多實作細節,並且依 據設定檔使用 Wi-Fi 或是 Ethernet 傳送封包。如果使用 Wi-Fi,係採用 ESP8266 官 方提供的 HTTP 函式庫。如果使用 Ethernet 有線網路,因為 ESP8266 官方並沒有 提供有線網路傳輸 HTTP 封包的功能,所以這個功能是由我們自行實作
Application Layer 的 HTTP 封包的程式碼,再藉由 Ethernet 函式庫提供的 TCP/IP Layer 的 API 將封包送出。
POST ()
POST ()函式的功能是將我們傳入的 Payload 以 HTTP POST method 送出,而會 用到此函式的只有 2.1.1 節介紹的 Register ()函式,所以傳入的參數就是註冊時的 註冊資料。
1 #include <ESP8266HTTPClient.h>
2 void POST(httpresp *result, const char* payload) { 3 #if defined(USE_ETHERNET)
4 Send_HTTPS(result, "POST", "", payload, 0);
5 #else
6 HTTPClient httpclient;
7 httpclient.begin("http://" + String(ServerIP) +
":"+String(ServerPort)+"/"+String(deviceid));
8 httpclient.addHeader("Content-Type", "applica- tion/json");
9 result->HTTPStatusCode = httpclient.POST(String(pay- load));
10 String http_resp = httpclient.getString();
11 http_resp.toCharArray(result->payload, http_resp.length());
12 httpclient.end();
13 #endif
14 }
圖 2-11 POST() 函式程式碼
程式碼如圖 2-11 所示。行 1,引用 ESP8266HTTPClient 函式庫,該函式庫為 ESP8266 官方提供之 HTTP 函式庫[2],行 2,宣告 POST()函式,需要傳入兩個參 數,(1) httpresp 結構指標,用來將 IoTtalk 伺服器回傳的訊息回傳給 DA API,(2) Payload 字元指標,即註冊資料,將此註冊資料送給 IoTtalk 伺服器以註冊到 IoTtalk 伺服器。行 3、4、13,是 C 語言的語法之一,用來判斷是有定義(#define) 某個參數值,使用這個語法的好處是,在編譯的時候會依據設定檔只編譯部分的 程式碼,有助於減少記憶體使用量,也可以使用相同名稱的變數卻不會造成衝 突。行 4,使用 Ethernet 有線網路時會使用該函式來傳送 HTTP 封包,此函示是由 我們自行撰寫,細節將在 2.2.4 節說明。行 6,使用 ESP8266HTTPClient 函式庫內 的物件“HTTPClient”宣告一個變數“httpclient”。行 7,在開始使用 HTTP 時,
必須要使用此函示,並且帶入一個參數,即我們想要連到哪一個網址,型態是字 串(string),此網址的格式是 “http://<IoTtalk 伺服器 IP>:<CSM port>/<裝置編號
>" 。行 8,可以添加多個 HTTP 標頭檔,為了減少記憶體使用,以及提高運作效 率,我們只有填入必要的一個欄位“Content-Type”,說明 payload 是 Json 格式的 文本資料。行 9,使用 HTTPClient 物件內的 POST ()函式將我們的 payload 送出,
該函式完成時,會回傳 HTTP 狀態碼,我們將之存入我們預先準備好的
“httpresp”結構中。行 10-11,使用 HTTPClient 物件內的 getString()函式,取得回 傳封包的 payload,同樣是存入“httpresp”結構中。行 12,關閉 HTTP 連線。到此 該函式結束。
GET ()
ESP8266 使用 GET () 函式傳送 HTTP GET 封包到 IoTtalk 伺服器,對某一個 網址做請求資料的行為。而這一段網址中包含了 IoTtalk 伺服器的 IP 或者是網域 名稱(domain name)、CSM Port、裝置名稱(d_name)及 ODF 名稱。GET 封包並不需 要傳送 payload。GET () 函式與 POST () 函式大致相同。
1 #include <ESP8266HTTPClient.h>
2 void GET(httpresp *result, const char* odf_name){
3 #if defined(USE_ETHERNET)
4 Send_HTTPS(result, "GET", odf_name, "",0);
5 #else
6 httpclient.begin( "http://" + String(ServerIP) +
":"+String(ServerPort)+"/"+String(deviceid)+"/" + String(odf_name) );
7 httpclient.addHeader("Content-Type",
"application/json");
8 result->HTTPStatusCode = httpclient.GET();
9 String http_resp = httpclient.getString();
10 http_resp.toCharArray(result->payload, http_resp.length());
11 httpclient.end();
12 #endif 13 }
圖 2-12 GET()函式程式碼
GET () 函式程式碼如圖 2-12 所示。行 1,匯入“ESP8266HTTPClient.h”函式 庫。行 2,宣告 GET () 函式,需要傳入兩個參數,包括 (1)“httpresp”結構指 標,(2) ODF 名稱的字元指標。行 3、5、12,同樣是 C 語言的語法。行 4,與 POST () 函式不同,此處 Send_HTTPS () 函式中傳入的參數的第二個欄位為
“GET”。行 6,此處傳入的網址與 POST () 函式中不同的是在網址最後多了一 個裝置特徵名稱(odf_name),即想要獲得資料的 ODF 名稱。行 7,添加必要 HTTP 標頭,說明資料格式為 Json 格式。行 8,使用 HTTPClient 物件中的 GET () 函 式,此函式將回傳 HTTP 狀態碼,儲存到我們的“httpresp”結構的
“HTTPStatusCode”中。行 9-10,使用 HTTPClient 物件中的 getString () 函式得到 回傳封包中的 payload,並且存到“httpresp”結構中的“payload”中。行 11 關閉 HTTP 連線。到此函式結束。
PUT ()
PUT () 函式用來將資料 Push 到 IoTtalk 伺服器的 IDF。PUT () 函式傳送 HTTP PUT 封包,封包包含目標網址及 Payload。目標網址包含 IoTtalk 伺服器的 IP、
CSM 的 Port 及 IDF 名稱。若收到 IoTtalk 伺服器回傳封包的 HTTP 狀態碼為 200 的 話,表示上傳成功,並且回傳 HTTP 狀態碼給使用者。如果 HTTP 狀態碼並非 200 的話,使用者需要自行判斷是否要重傳。
1 #include <ESP8266HTTPClient.h>
2 void PUT(httpresp *result, const char* value, const char*
df_name ) {
3 String data = "{\"data\":[" + String(value) + "]}";
4 #if defined(USE_ETHERNET)
5 Send_HTTPS(result, "PUT", df_name, data.c_str(), 0);
6 #else
7 HTTPClient httpclient;
8 httpclient.begin( "http://" + String(ServerIP) +
":"+String(ServerPort)+"/"+String(deviceid)+"/" + String(df_name));
9 httpclient.addHeader("Content-Type",
"application/json");
10 result->HTTPStatusCode = httpclient.PUT(data);
11 httpclient.end();
12 #endif 13 }
圖 2-13 PUT()函式程式碼
PUT () 函式程式碼如圖 2-13 所示,行 1,匯入 ESP8266HTTPClient 函式庫。
行 2,宣告 PUT () 函式,該函式需要帶入三個參數,(1)要回傳的 httpresp 結構指 標,(2)要 Push 的值,(3)要 Push 的 IDF 名稱。行 3,因為 Payload 需要使用 JSON 格式,但是由於我們的資料很小,所以我們就不使用 ArduinoJson 函式庫,直接編 寫 JSON 格式。行 4、5,使用 Ethernet 時使用 “Send_HTTPS 函式”。行 7-11,
與 POST ()函式類似。但是 PUT ()函式回傳的封包並不會有 Payload,所以不需要 回傳資料。
Send_HTTPS ()
因為 Ethernet 的函式庫並沒有支援 HTTP 功能,所以我們需要實作 Application Layer 的 HTTP 相關功能。HTTP 協定是相當簡單的文本封包協定,所以我們只需 要將伺服器 IP、網址、HTTP 標頭、封包長度及 Payload 以規定的文字格式,使用 TCP 連線送出即可。
1 #include <UIPEthernet.h>
2 void Send_HTTPS(httpresp *result, const char* HTTP_Type, const char* feature, const char* payload, bool close_TCP) {
3 EthernetClient TCPclient;
4 TCPclient.println(pack_http(HTTP_Type, feature, pay- load));
5 TCPclient.flush();
6 if(TCPclient.available() > 0) 7 decodehttp(result);
8 }
圖 2-14 Send_HTTPS 函式程式碼
Send_HTTPS () 函式程式碼如圖 2-14 所示。行 1,匯入 UIPEthernet.h 函式 庫,使用此函式庫來控制 Ethernet 模組,及建立 TCP 連線。行 2,宣告函式及需 要帶入的參數,包括 httpresp 結構指標、HTTP method(如 PUT、GET、POST 等 等)、目標 DF 名稱(IDF 或 ODF)及 Payload。 行 3,宣告 EthernetClient 物件,使 用此物件傳送 TCP 封包。行 4-5,將 pack_http () 函式編排好的 HTTP 文字,用 EthernetClient 物件送出。行 6,EthernetClient 物件內的 available ()函式用來檢查是 否有封包回傳,如果有的話將會回傳封包長度。如果有封包回傳,我們將用 decodehttp ()函式解析回傳封包的內容,並且將 HTTP 狀態碼及 Payload 填入 httpresp 結構中回傳。
三、MQTT 架構之 DA
MQTT 架構與 HTTP 架構最大的不同是,MQTT 架構獲得 ODF 的資料的方式 是由 IoTtalk 伺服器主動送給裝置端。對於裝置端而言是事件觸發式的行為,即當 訊息送到裝置端時,裝置端會做出反應。相對於 HTTP 架構,裝置不用定時向 IoTtalk 伺服器請求資料,封包的利用上顯得更有效率,對於伺服器端傳送資料到 裝置端的延遲也可以更短。
MQTT 架構
MQTT 介紹
IoTtalk V2 伺服器使用 MQTT(Message Queuing Telemetry Transport)[3]架構取代 V1 的 HTTP 架構。MQTT 最初是在狹窄的網路頻寬和微小電力損耗的需求前提之 下設計,提供石油管線感測器和人造衛星之間一個輕量、可靠的二進制通訊協 定。。由於 MQTT 協定的訊息內容很精簡,非常適合用於處理器資源及網路頻寬 有限的物聯網裝置,再加上已經有許多 MQTT 程式庫被陸續開發出來,用於 Arduino 控制板(C/C++)、JavaScript(Node.js, Espruino 控制板), Python 等等,還有 開放原始碼的 MQTT 伺服器,使得開發 MQTT 物聯網、機器之間(Machine-to- Machine, M2M)的通訊變得非常簡單。Facebook Messenger 的即時通訊也是用 MQTT 協定。MQTT 和 HTTP 都是應用層(Application layer)的協定,傳輸層
(Transport layer)都是 TCP/IP 協定,也就是說物聯網裝置可以沿用既有的網路架構 和設備,只是在網路上流通的「訊息格式」以及應用程式的處理機制不同。
發布者、訂閱者與經紀人
根據 MQTT 3.1.1 版本規格書[3]的描述,MQTT 是一種基於「發布∕訂閱」機 制的訊息傳輸協定(MQTT is a Client Server publish/subscribe messaging transport protocol)。MQTT 協定裏面有三個角色,發布者(Publisher)、訂閱者(Subscriber)及經 紀人(Broker)。MQTT 可以想像是一個雜誌發行與訂閱的機制,發布者就像是出版 社,對某個主題發布新的消息,然後交給經紀人。經紀人就像是經銷商,將各出 版社的消息傳遞給訂閱者。訂閱者就像是消費者,我們可以訂閱多個主題,當發 布者有新消息時,經紀人就會將新消息送給訂閱者。這樣的架構與我們的 IoTtalk 相當類似,IDF 就像是發布者,可以有多個 IDF 同時連接到同一個 Join 點。Join 點就像是經紀人,可以接受多個 IDF 對於同一個 Join 點送資料,然後 Join 點會將 資料送到所有有連接的 ODF 端,而 ODF 端也可以連接多個 Join 點。MQTT 可以 接受將同一份資料送至不同的主題,也可以同時訂閱多個主題,提供相當大的彈 性。
MQTT 封包格式
圖 3-1 是 MQTT 協定的封包格式,主要分成三個部分綠色底色的固定長度標 頭(Fixed header)、黃色底色的變動長度標頭(Variable header)及有效負載(Payload)。
圖 3-1 綠色底色之固定長度標頭的內容長度固定為兩個位元組(bytes),第一個位 元組的第 7~4 個位元決定 MQTT 的類別,像是連接(CONNECT)、發布
(PUBLISH)、訂閱(SUBSCRIBE)等等功能的封包,詳情請見[3] 2.2.1 小節。第 3~0 個位元會依據第 7~4 個位元的功能做決定,最有影響的是發布(PUBLISH)封包,
這四個欄位會決定封包的服務品質(QoS),詳情請見[3] 2.2.1 小節。第二個位元組 是剩餘封包長度,表示黃色底線的可變長度標頭(Variable header)及有效負載 (Payload)還有幾個位元組。
圖 3-1 黃色底色的部分為可變長度標頭(Variable header),長度會依據固定長度標 頭裡的封包類型有所不同,像是發布封包的主題(Topic)就是接在封包序號(Packet Identifier)之後,詳情請見[3] 第三章 ”MQTT Control Packets”。
圖 3-1 藍色底色的部分為有效負載,最大可以傳送 256MB 的資料。使用 UTF-8 編 碼的文本。
圖 3-1 MQTT 封包格式
DA API
Register ()
與 HTTP 架構相比,MQTT 裝置註冊到 IoTtalk 伺服器多一個步驟。步驟如圖 3-2,首先裝置端會把裝置資料,如裝置名稱(Device name)、模組名稱(Model name)、
裝置特徵名稱(DF name)等等資料,以 HTTP PUT 封包送給 IoTtalk 伺服器。IoTtalk 伺服器接受裝置端的註冊後,將回傳該裝置專屬的控制用 MQTT 主題(Control Channel Topic),裝置端會訂閱控制用 MQTT 主題。IoTtalk 伺服器會使用此控制用 主題傳遞 DF 連結或斷開的訊息,裝置端再依據此訊息訂閱或取消訂閱 DF 主題。
圖 3-2 IoTtalk V2 註冊流程
1 int Register(void){
2 CheckNetworkStatus();
3 httpresp result;
4 result.HTTPStatusCode = 0;
5 result.payload =
(char*)malloc(HTTP_RESPONSE_PAYLOAD_SIZE);
6 memset(result.payload, 0, HTTP_RESPONSE_PAYLOAD_SIZE);
7 while(result.HTTPStatusCode != 200)
8 PUT(&result,"ServerIP","CSMPort",C_deviceid, getProfile());
9 dec_ctrl_chan(String(result.payload));
10 MQTT_Conn();
11 return (result.HTTPStatusCode);
12 }
圖 3-3 V2 Register() 函式程式碼
Register () 函式程式碼如圖 3-3 所示。行 1,宣告函式。行 2 檢查網路狀態。
行 3-6,宣告“httpresp”結構。行 7-8,重複傳送 HTTP PUT 註冊封包,直到收到 IoTtalk 伺服器回傳 HTTP 狀態碼 200 的封包,表示註冊成功。行 9,對 IoTtalk 伺 服器回傳的封包進行解碼,記錄控制用 MQTT 主題。行 10,訂閱控制用 MQTT 主題後,即可接收 IoTtalk 伺服器專案中 DF 的連接狀態。當 DF 與裝置連接或斷 開,裝置會收到控制用主題傳來的 DF 狀態訊息,Callback() 函式會負責解析,並 且訂閱連線的 DF 主題,取消訂閱斷開連線的 DF 主題。
Callback()
Callback () 函式負責處理收到的 MQTT 封包。封包有兩種,(1)DF 狀態變更 的封包,(2)連接中 ODF 新的值。DF 狀態變更是指, IoTtalk 伺服器網頁上操作 裝置與 DF 綁定(Bind)或取消綁定的時候。當有新的資料傳送到 ODF 端時,IoTtalk 伺服器會轉傳至裝置端,裝置端可以得知是哪個主題傳來的訊息,經過查表就可 以知道是從哪一個 ODF 傳來。接著使用者就可以自行設計應用。我們不建議使用 者直接在 Callback() 函式中撰寫處理 ODF 資料的程式碼,而應該要盡快處理完 Callback() 函式,然後回到 Loop() 函式中,再處理 ODF 送來的資料。
1 void Callback(char* topic, byte* payload, int length) { 2 mqtt_mes = "";
3 strcpy(mqtt_mes, payload);
4 if(mqtt_mes.indexOf("command")) 5 ctrl_message = true;
6 }
圖 3-4 Callback() 函式程式碼
Callback() 函式程式碼如圖 3-4 所示。行 1,宣告 Callback 函式,需要帶入三 個參數,這個欄位是函式庫中要求的格式,不須更動。行 2,“mqtt_mes”是一個 字串型態全域變數,用來儲存送來的資料,在離開 Callback ()函式後還可以在 Loop ()函式中使用。行 3,將 payload 複製到 mqtt_mes。行 4-5,判斷 mqtt_mes 字 串中是否有“command”子字串。以此判斷這個訊息是否為控制用訊息。如果是 控制用訊息,就將“ctrl_message”這個 bool 型態變數值設為 true,回到 Loop() 函 式後,會依據這個變數,執行 CtrlHandle() 函式,稍後的章節將會介紹。
Publish ()
Publish () 函式用來將資料傳送到 IoTtalk 伺服器。該函式是由 MQTT 函式庫
“PubSubClient.h“提供。使用範例如圖 3-5 所示。行 1,匯入“PubSubClient.h”
函式庫。行 2,“WiFiClient”物件是 ESP8266 官方提供之 Wi-Fi 物件,包含連上 Wi-Fi AP 等等功能。行 3,此函式是“MQTTclient”物件內的一個函式,所以要 使用之前需要先宣告“MQTTclient”物件。此物件需要帶入一個參數,Physical Layer 的物件,即行 2 所宣告的物件。行 4,此函式需要帶入兩個參數,(1)目標主 題的字元指標,(2)欲上傳的值的字元指標。
1 #include <PubSubClient.h>
2 WiFiClient espClient;
3 PubSubClient MQTTclient(espClient);
4 MQTTclient.publish(c_IDF_topic, c_value);
圖 3-5 MQTT Publish() 函式使用範例
CSM API
MQTT_Conn()
此函式在 Register ()函式裡被使用,由 HTTP PUT 封包取得控制用主題,控制 用主題又分為兩種,輸入與輸出。輸入為裝置端到伺服器端。輸出為伺服器端到 裝置端。裝置將用輸入控制用主題建立 MQTT 連線。MQTT 有斷線通知的機制,
在起初建立連線時,可以同時向 MQTT Broker 告知,如果連線斷了要傳什麼訊息 給訂閱者,此訊息又稱“遺言”。而 IoTtalk 伺服器也會訂閱輸入控制用主題。裝 置端會以 IoTtalk 伺服器規定的狀態訊息格式,告知 IoTtalk 伺服器裝置已經啟 用。同樣斷線時也會用同樣的狀態訊息格式,通知 IoTtalk 伺服器裝置目前離線。
1 #include <PubSubClient.h>
2 void MQTT_Conn(void){
3 while (!MQTTclient.connected())
4 if ( !MQTTclient.connect(Str_deviceid, ctrl_i, 0, true, state_mess("broken",rev)))
5 delay(5000);
6 MQTTclient.subscribe(ctrl_o);
7 MQTTclient.publish(ctrl_i, state_rev("online",rev));
8 }
圖 3-6 MQTT_Conn ()函式程式碼
MQTT_Conn 程式碼如圖 3-6 所示。行 1,引用 MQTT 函式庫
“PubSubClient.h”。行 2,宣告 MQTT_Conn ()函式,此函式不需要帶入參數。行 3,重複執行直到成功建立 MQTT 連線。行 4,使用 MQTT 用戶端的物件
“MQTTclient”內的 Connect ()函式連線到 MQTT 伺服器。Connect ()函式需要帶入 五個參數,(1)裝置編號,由 ESP8266 的 MAC 位址產生,(2)狀態訊息要傳送至輸 入控制用主題,(3)MQTT QoS,訊息品質,(4)是否傳送狀態訊息,(5) 狀態訊息,
我們使用 state_mess () 函式將我們要的狀態訊息編寫成 IoTtalk 伺服器規定的格 式。行 5,如果建立連線失敗會等五秒後,再嘗試建立連線。行 6,訂閱輸出控 制用主題。行 7,透過輸入控制用主題,傳送狀態訊息告知 IoTtalk 伺服器裝置已 經上線。
CtrlHandle ()
CtrlHandle ()函式用來處理輸出控制用主題所送來的訊息,訊息範例如圖 3-7,此訊息為 JSON 格式。行 2,訊息 ID,每一筆訊息的 ID 都不同,由 IoTtalk 伺服器產生。行 3,該次連線的 MQTT 主題,當裝置在 IoTtalk 伺服器網頁中被綁 定後,或者斷線後自動重新連線,每次新連線的主題都會不同。因此需要更新 ESP8266 記憶體中,每個 ODF 對應的主題,取消訂閱舊主題,並且訂閱新主題。
行 4,有“CONNECT”及“DISCONNECT”兩種指令,當收到“CONNECT”時 會去訂閱新主題,當收到“DISCONNECT”時會取消訂閱舊主題。行 5,JSON key 值會是“idf”或者“odf”,對應的 JSON Value 值就是 DF 名稱。
1 {
2 "msg_id": "12d19891-5237-43e8-ae0a-82722b421fce", 3 "topic": "iottalk/esm/35c55e46-6070-4203-af8a-
04d0173ed4b7",
4 "command": "CONNECT", 5 "odf": "Dummy_Control"
6 }
圖 3-7 MQTT 控制用訊息範例
CtrlHandle ()函式程式碼如圖 3-8 所示。行 1,宣告函式。行 2,重置 flag。行 3-4,宣告一個暫時的 JSON 物件,較好處理 JSON 格式的內容。行 5,檢查是否 有“Command”指令。行 6、16,檢查指令狀態是連線還是斷線。行 7-12,檢查 是 IDF 或 IDF,如果是 ODF 的話就需要訂閱 ODF 主題,並且使用 store () 函式將 DF 名稱、MQTT 主題、狀態指令儲存。行 13-14,回傳訊息給 IoTtalk 伺服器,通 至 IoTtalk 伺服器已收到訊息。行 17-21,當指令是斷線的時候,使用 DF 名稱取得 相對應的 MQTT 主題,並且取消 MQTT 訂閱,接著從我們的對照表中,將斷線的 DF 紀錄刪除。
1 void CtrlHandle(void){
2 new_message = false;
3 DynamicJsonBuffer JB_temp;
4 JsonObject& JO_temp = JB_temp.parseObject(mqtt_mes);
5 if(JO_temp.containsKey("command")){
6 if( JO_temp["command"] == "CONNECT"){
7 if(JO_temp.containsKey("odf")){
8 store(JO_temp["odf"],
JO_temp["topic"],JO_temp["command"]);
9 MQTTclient.subscribe(JO_temp["topic"]);
10 }
11 else if(JO_temp.containsKey("idf")) 12 store(JO_temp["idf"],
JO_temp["topic"],JO_temp["command"]);
13 String ok_mes = "{\"state\":\"ok\",\"msg_id\":\""
+ JO_temp["msg_id"].as<String>() + "\"}";
14 MQTTclient.publish(ctrl_i, ok_mes);
15 }
16 else if(JO_temp["command"] == "DISCONNECT"){
17 for(int i=0; i< JA_CD.size(); i++)
18 if(JA_CD[i][0] == JO_temp["idf"] || JA_CD[i][0]
== JO_temp["odf"] ){
19 MQTTclient.unsubscribe(JA_CD[i][1]);
20 break;
21 }
22 String remove_df_name = JO_temp["idf"].length() >
0 ? JO_temp["idf"] : JO_temp["odf"];
23 for(int i =0; i<JA_CD.size(); i++) 24 if( remove_df_name == JA_CD[i][0]) 25 JA_CD.remove(i);
26 } 27 } 28 }
圖 3-8 CtrlHandle () 函式程式碼
四、ESP8266 的擴展
腳位擴充 (Pin Extension)
ESP12F 模組從 ESP8266 晶片拉出最多腳位只有 11 隻數位腳位(digital pins)、1 隻類比腳位(analog pins),如圖 4-1 所示。而數位腳位有 3 隻預設使用於燒錄及開 機模式的選取,並不適合使用於連接其他模組1。所以可以自由使用的數位腳位並 不多,如果要用來控制多組開關或者接多個模組的話,數位腳位就會不敷使用,
所以我們想到使用移位暫存器及多工器,分別用來擴充 ESP8266 的數位及類比腳 位。
圖 4-1 ESP12F 模組的數位及類比腳位圖
1 如果還是有需要用到這 3 隻預設的開機腳位的話,可以使用兩顆 BS170 場效電晶體 (Metal-Oxide-Semiconductor Field-Effect Transistor, MOSFET)讓開機時電位可以開機,開機後就 可以像一般數位腳位使用。
數位腳位擴充 (Digital pin extension)
為了擴充數位腳位,我們使用移位暫存器(Shift Register),型號是
SN74HC164(如圖 4-2)[4],接上 SN74HC164 的 ESP8266 模組如圖 4-3 所示。移位 暫存器的原理是序列輸入並列輸出,如圖 4-4 所示,所以可以用比較少的腳位輸 入訊號,然後並列的同時輸出多個腳位,進而做到擴充腳位的目的。適合的使用 方式為,當需要維持數位腳位電位的行為,例如控制 LED 亮暗(如圖 4-3)、控 制繼電器的開關等等。
圖 4-2 SN74HC164 實體照片
圖 4-3ESP8266 與 SN74HC164 腳位連接圖
首先我們要說明圖 4-3 中,麵包板的用途是讓我們方便連結線路及裝置,橘 色雙箭頭表示是該方向上的點是互通的,只要有線路或者裝置接在同樣的橘色雙 箭頭上就會連接在一起。而中間的四組橘色雙箭頭左右兩直排是不相通的,而上 下半部也不相通。而圖最右邊的紅、藍線將上下兩橫列分別連接在一起。
圖 4-3 中所有的紅線表示連接到電源的正極。ESP12F 的 CH_EN 是晶片啟動腳位 (Chip Enable),需要維持在高電位晶片才會啟動。Vcc 是 ESP12F 的電源輸入腳 位,連接至供電電源的正極。74HC164 晶片左邊有一個半圓形凹槽,用以辨識方 向,74HC164 的腳位說明請見圖 4-5,後面段落將會詳細說明。74HC164 左上角 是電源輸入腳位紅線同樣是電源輸入腳位,連接至供電電源正極。
圖 4-3 中所有的藍線表示連接到電源的負極。ESP12F 右上角的接地腳位(GND)由 藍線連接至供電電源的負極。74HC164 的右下角為接地腳位(GND)連接至電源負 極。
圖 4-3 中 ESP12F 有兩條綠線分別是時脈(CLK)及訊號輸入腳位(Input)。時脈連接 至 74HC164 的 CLK 腳位。ESP12 的訊號輸入腳位同時連接至 74HC164 的序列輸 入腳位 1、2(Gated Serial Input 1, 2)。
圖 4-3 中 74HC164 有三條黃線分別是並列輸出腳位(Parallel Output)QA、QB、QC分 別連接至三顆 LED 燈的正極,而 LED 的負極連接至供電電源負極,LED 燈的兩 隻腳位通常長的腳位是正極,短的是負極。
圖 4-4 中 Serial Inputs A, B 是兩隻輸入腳位的電位狀態,兩個輸入源如何決定 要輸入什麼進入輸出端請參考表 4-1。只有當 A, B 都是高電位時,才會輸出高電 位,其他情況都是輸出低電位,所以我們將 A, B 腳位都連接到 ESP8266 的同一隻 數位腳位上。原因有兩點,(1)當 ESP8266 輸出低電位時,A, B 同時為低電位就會 輸出低電位。當 ESP8266 輸出高電位時,A, B 同時為高電位就會輸出高電位,所 以當 ESP8266 輸出高或低電位時,輸入到 SN74HC164 的電位也會是我們想要的電 位。(2)我們可以省下 1 個數位腳位,挪作其他功能使用。而 SN74HC164 這顆 IC 的時脈(Clock,簡稱 CLK)屬於上緣觸發2(positive or rising-edge, from LOW to HIGH),觸發的時間點為時脈的電位從低電位變成高電位的瞬間,會讀取 A, B 輸 入的結果當作輸出,並且將前一級的電位狀態傳給下一級。
2 另有負緣觸發(falling edge-triggered),原理與上緣觸發相反,觸發的時機點 是當電位從高電位變成低電位的時間點。
圖 4-4 SN74HC164 時序圖範例
表 4-1 SN74HC164 輸入訊號功能表
SN74HC164 IC 的腳位說明如圖 4-5。在 Name 欄位中的 A, B 是輸入訊號的來 源。QA~QH的系列為輸出訊號的腳位,最先輸入的訊號會先送到 QH,接著依序輸 入,最後才是 QA。GND 是接地。VCC是該 IC 的所需的工作電源輸入腳位,該 IC 可以接受-0.5V~7V 的電壓,為了方便與 ESP8266 共用,我們使用 5V 當做工作電 壓。CLK 是時脈(Clock),可以使用石英振盪器,或者我們使用數位腳位自己控制 電位的高低電壓與達成同樣的時脈,因為該 IC 接受訊號的方式是 CLK 的電位由 低電壓到高電壓的上緣觸發,所以並不要求時脈要非常精準,但是會要求每個時 脈的高低電位各要維持多久。
圖 4-5 SN74HC164 腳位說明
類比腳位擴充 (Analog pin extension)
ESP8266 除了數位腳位數量不足,同時也有類比腳位數量不足的問題,所以 在 4.1.2 小節要說明我們如何解決腳位不足的問題。我們選擇 16 通道類比多工/解 多工器,型號是 CD74HC4067[4],實體照片如圖 4-6。
圖 4-6 CD74HC4067 實體照
圖 4-7 中,CD74HC4067 模組左側是 C0 ~ C15 通道,用來連接感測器的類比輸出 腳位。右側的選擇腳位(S0 ~ S3)會決定要將哪一個通道(C0~C15)連接至右側的訊 號輸出腳位(SIG),而 ESP8266 再去讀取訊號輸出腳位(SIG)。黃線表示類比訊號資 料線,類比資料的數值由黃線傳遞。圖中用光敏電阻當作示範,光敏電阻會因為 周圍光線強弱而改變電阻值,進而影響到電阻兩端的電壓。
圖 4-7 中,紅線代表電源正極。ESP8266 端同樣是將 Vcc 腳位與晶片啟動腳位 (EN)連接到電源正極。CD74HC4067 的工作電壓是 5 伏特,輸入到 VCC 腳位。當 CD74HC4067 晶片的啟動腳位(EN)是高電位時,這時候的通行方向是由通道 (C0~C15)到訊號端(SIG)。此時的應用場景是可以切換讀取周邊多個類比裝置,但 同時只能讀取到其中一個類比裝置。當啟動腳位是低電位時,訊號由訊號端(SIG) 到通道(C0 ~C15)。應用場景是 ESP8266 要輸出類比訊號給多個類比裝置時,但是 同時只能有一個輸出。
圖 4-7 中,藍線代表電源負極。CD74HC4067 與 ESP8266 都需要接地。
圖 4-7 中,綠線代表通道選擇線,由 ESP8266 控制輸入到 CD74HC4067 的 S0~S3 四隻腳位。CD74HC4067 通道選擇真值表,如表 4-2 所示。若周邊的類比裝置不 多的話,可以直接將 S3 甚至 S2 直接接地,這樣就可以減少 ESP8266 控制腳位的 數量。
圖 4-7 ESP8266 與 CD74HC4067 模組連接圖
表 4-2 CD74HC4067 通道選擇真值表
總結 4.1 ,CD74HC4067 類比多工器與 SN74HC164 移位暫存器,皆用來擴展 ESP8266 腳位不足的問題。兩者各有其優點與缺點,也可以進行組合搭配,發揮 更大的功用。
CD74HC4067 類比多工器,可以依據啟動腳位(EN)進行雙向的通訊,而
SN74HC164 僅能單向的輸出訊號,如果有多個數位腳位需要讀取的話,也可以使 用 CD74HC4067 輔助。
CD74HC4067 需要四隻通道選擇腳位,而 SN74HC164 只需要一隻序列輸入腳位就 可。所以可以使用 SN74HC164 輸出訊號當作 CD74HC4067 的通道選擇訊號,這樣 就可以只使用一隻數位腳位選擇 16 個類比通道。
如果應用情境超過 16 的類比裝置的時候,可以使用階層的概念,串連
CD74HC4067,一層的 CD74HC4067 就可以控制 16 的類比裝置,兩層的話就可以 控制 162 (256)個類比裝置,以此方式可以輕易的擴充我們的應用,同時搭配 SN74HC164 控制通道的選擇就可以滿足很多 ESP8266 上腳位的需求。
有線網路
Wi-Fi 容易受到周遭的無線裝置的干擾,尤其是在多個 Wi-Fi 的環境、
Bluetooth 等等,或者無線通訊使用 HTTP 被竊聽甚至攻擊的風險,或者我們需要 遠超過 Wi-Fi 通訊距離的應用。在上述情境,我們會有有線網路的需求。
在有線網路的環境,我們使用 ESP8266 搭配乙太網路模組(ENC28J60[5]),實體照 如圖 4-8 所示,這時的 ESP8266 就只有擔當微控制器(Micro Control Unit,簡稱 MCU)的功能,透過乙太網路模組有線連接至 Internet。除了 OSI 模型(Open System Interconnection Reference Model)的物理層(physical layer)不同以外,從資料連接層 (data link layer)到應用層(application layer)都跟使用 Wi-Fi 時一樣,所以在程式碼的 部分我們只需要做少量的修改,就可以沿用原本的設計,擴展有線網路的應用。
圖 4-8 乙太網路模組(ENC28J60)實體照片
ESP8266 與 ENC28J60 模組的連接方式如圖 4-9 所示,兩者使用的通訊方式是 SPI 協定[6]。SPI 協定是嵌入式系統常見的通訊協定之一,優點是可以全雙工、傳 輸速度快(5/10/20 Mbps)、MCU 可以共用腳位控制多個使用 SPI 通訊的周邊模 組。SPI 協定需要四隻腳位控制,選擇腳位 (slave selected 或 control selected, 簡稱 SS 或 CS)、主機輸入從機輸出訊號 (Master Input Slave Output,簡稱 MISO)、主機 輸出從機輸入訊號 (Master Output Slave Input,簡稱 MOSI)及時脈(clock)。
圖 4-9 中,橘線代表電源正極,根據 ENC28J60 的規格表,我們使用 3.3 伏特 的電壓,電源需要能提供超過 180 豪安培的電流,以確保 ENC26J60 運作正常。
圖 4-9 中,黑線代表電源負極,ESP8266、ENC28J60、BS170 電晶體及電阻都需要 接地。
圖 4-9 中,紫線代表 SPI 協定中的選擇腳位,中間通過兩個 BS170 電晶體。需要 兩個電晶體的原因是因為 ESP8266 上的 GPIO15 腳位除了是 SPI 協定的預設的 CS 腳位之外,也是選擇開機模式的三個腳位之一。為了要確保在通電的瞬間,
GPIO15 腳位的電壓選擇的開機模式是正常的,我們需要使用兩個電晶體,而因為 BS170 電晶體的運作方式,造成的結果是開機的瞬間 GPIO15 輸入會是高電壓,
而開機後就不會維持高電壓,可以被 ESP8266 控制電壓高低,將控制權還給程式 碼控制。簡而言之這兩個電晶體的功能是確保 ESP8266 可以正確的開機,開機之 後 GPIO15 就可以當作一般的數位腳位使用。
圖 4-9 中,綠線代表 MISO,如字面上所寫的就是 ESP8266 傳送資料給 ENC28J60 所用的腳位。
圖 4-9 中,黃線代表 MOSI,如字面上所寫的就是 ENC28J60 傳送資料給 ESP8266 所用的腳位。使用了兩個腳位各自負責單向通訊,就可以達成全雙工通訊。
圖 4-9 中,藍線代表時脈(clock),用來同步兩裝置間的時間,時脈與 SPI 協定的 觸發方式有關,所以說兩者間必須同步,才能作為 MOSI 與 MISO 的參考基準。
而資料的傳輸速度也是由時脈決定的,時脈常用 8MHz。
圖 4-9 中,棕線代表重置(reset),用來重新啟動 ENC28J60。
圖 4-9 ESP8266 與 ENC28J60 連接示意圖
HTTPS
IoTtalk V1 支援 HTTPS 以提高物聯網裝置的安全性。在瀏覽器上傳送隱私資 料 (例如 GPS 座標)時,因為網路協定與瀏覽器安全規範的原因,我們必須使用 HTTPS 才能傳送。所以使用 Wi-Fi 時,可以選擇要使用 HTTP 或者 HTTPS。因為 ESP8266 官方有提供 HTTPS 相關的函式庫可供開發使用,所以我們不需實作 HTTPS 的加密過程與交換金鑰過程。但是官方並沒有提供相關 Ethernet 的支援,
所以使用 Ethernet 時,並沒辦法使用 HTTPS,但還是能使用 HTTP 來與 IoTtalk 伺 服器互動。
以下將用圖 4-10 來說明,如何使用 ESP8266 的 HTTPS 函式庫。行 1,引用 ESP8266 Wi-Fi 的函式庫。行 2,引用 ESP8266 HTTPS 的函式庫。行 3-4,由使用 者設定 ESP8266 要連到哪一個 Wi-Fi 與密碼。行 5-6,使用者設定目標網站的網
址與目標網站的埠(port)。行 7,使用者要先使用電腦上的瀏覽器,開啟該網站,
並且查看網頁的 SHA1 加密的指紋,該指紋將被 ESP8266 用來加密 HTTPS。行 8, 程式碼開始。行 9,初始化序列輸出,使 ESP8266 可以從序列埠(Serial Port)輸 出字串供使用者除錯。行 10,啟動 Wi-Fi,ESP8266 將使用使用者設定之 Wi-Fi SSID 與密碼連接上 Wi-Fi AP。行 11-14,等待 ESP8266 連接上 Wi-Fi AP。行 15,
宣告一個 WiFiClientSecure 物件,使用這個物件送出封包時,程式會執行相關的加 密程序。行 16-19,ESP8266 使用 Line8 所宣告之 WiFiClientSecure 物件,與目標網 站建立 TCP 連線。判斷是否有成功建立 TCP 連線,如果失敗就退出程序。行 20,此網址為目標網站內其中一個網頁。行 21,送出 HTTPS 封包,因為 HTTP 協 定是文本式的封包內容,所以我們只要用文字將相關的標頭文字及有效附載 (Payload)送至 TCP 連線即可。此處我們所送出的封包是 HTTP GET Request 封包,
因此不需填寫有效附載。行 25-31,ESP8266 等待 HTTPS Response 送回來 ESP8266 端,直到讀到回頭字元及換行字元(\r\n),表示封包標頭部分結束。行 32-33,讀取 HTTPS 封包的有效附載部分,並且輸出到序列埠給使用者看。
以上是 ESP8266 如何使用 HTTPS 的介紹,如果我們要在 IoTtalk 伺服器上使 用 HTTPS 的話,我們需要先進行圖 4-10 Line 5 的步驟,將目標網站設定為要連 接的 IoTtalk 伺服器。接著進行圖 4-10 Line7 的步驟,先使用電腦上的瀏覽器取得 與 IoTtalk 伺服器的 SHA1 指紋,接著寫入到程式碼,就能開始使用 HTTPS 加密 方式與 IoTtalk 伺服器通訊。
1 #include <ESP8266WiFi.h>
2 #include <WiFiClientSecure.h>
3 const char* ssid = "...";
4 const char* password = "...";
5 const char* host = "api.github.com";
6 const int httpsPort = 443;
7 const char* fingerprint = "35 85 74 EF 67 35 A7 CE 40 69 50 F3 C0 F6 80 CF 80 3B 2E 19";
8 void setup() {
9 Serial.begin(115200);
10 WiFi.begin(ssid, password);
11 while (WiFi.status() != WL_CONNECTED) { 12 delay(500);
13 Serial.print(".");
14 }
15 WiFiClientSecure HTTPS_Client;
16 if (!HTTPS_Client.connect(host, httpsPort)) { 17 Serial.println("connection failed");
18 return;
19 }
20 String url = "/repos/esp8266/Arduino/commits/mas- ter/status";
21 HTTPS_Client.print(String("GET ") + url + "
HTTP/1.1\r\n" +
22 "Host: " + host + "\r\n" +
23 "User-Agent: BuildFailureDetectorESP8266\r\n" + 24 "Connection: close\r\n\r\n");
25 while (HTTPS_Client.connected()) {
26 String line = HTTPS_Client.readStringUntil('\n');
27 if (line == "\r") {
28 Serial.println("headers received");
29 break;
30 } 31 }
32 String line = HTTPS_Client.readStringUntil('\n');
33 Serial.println("reply was:\n" + line ) 34 }
圖 4-10 HTTPS 範例程式
五、效能評估
HTTP 架構
5.1 小節是以 IoTtalk V1 伺服器為測試環境,而 IoTtalk V1 是 HTTP 架構,進 行不同條件下的裝置端反應延遲測試。以下將會探討 ESM 拉動週期、本地端與遠 端伺服器、使用 Wi-Fi 與 Ethernet 對於裝置端反應延遲的影響。在架構圖圖 5-1 中,IoTtalk 伺服器(圖 5-1 (1))與 ESP8266(圖 5-1 (2))在同一個 Wi-Fi 路由器 (圖 5-1 (3))的 LAN 裡。此處 IoTtalk 伺服器是 V1 版本,即 HTTP/HTTPS 架構。
ESP8266 使用 Wi-Fi 或 Ethernet(圖 5-1 (4))與路由器連接。IoTtalk 伺服器用乙太網 路線(圖 5-1 (5))連接至路由器。
測試方式:(1)測試 1000 筆資料,每一筆測試資料之間間隔 500ms。因為我們希望 能減少伺服器的封包隊列對每一筆資料的處理速度的影響,同時也減少 ESP8266 記憶體的負擔。(2)Push 一筆資料後隨即 Pull 下來。
圖 5-1 LAN 中測試 ESM 運作週期之架構圖
以下四張圖(圖 5-2 ~ 圖 5-5)是 ESP8266 延遲測試結果的分布圖,將說明 ESP8266 在不同的情境下,ESM 拉動週期長短對延遲的影響,並且於 5.1.1 ~5.1.3 小節深入討論。
圖 5-2 使用 Wi-Fi,IoTtalk 伺服器在本地端,ESM 拉動週期對延遲的影響
圖 5-3 使用 Wi-Fi,IoTtalk 伺服器在遠端,ESM 拉動週期對延遲的影響
圖 5-4 使用 Ethernet,IoTtalk 伺服器在本地端,ESM 拉動週期對延遲的影響
圖 5-5 使用 Ethernet,IoTtalk 伺服器在遠端,ESM 拉動週期對延遲的影響
ESM 拉動週期的影響
5.1.1 小節要比較的是 IoTtalk V1 伺服器(圖 5-1 (1)) 的 ESM 拉動週期對 ESP8266 反應時間的影響。
ESM 負責處理裝置端 Push 與 Pull 到伺服器的資料。在 IoTtalk V1 的架構下,ESM 週期性的檢查是否有裝置端 Push 新的資料到伺服器端,而這個週期就是 ESM 拉 動週期(Pulling Period)。
ESM 拉動週期越短,Push 到伺服器端的資料就越快地被處理。反之 ESM 拉動週 期越長,資料就會比較慢被處理,但是伺服器的負擔會比較小,沒有執行時伺服 器就是處於閒置的狀態,可以隨伺服器的硬體規格,及使用情境的需求進行調 整。
因此我們將測量,當 ESM 拉動週期設為 10、30 與 50ms 時,ESP8266 Push 一筆資 料到 IoTtalk 伺服器後再 Pull 同筆資料回來,所需多少時間,此段時間就代表著 ESP8266 對於 IDF 所 Push 到 IoTtalk 伺服器的資料作出反應所需的時間,單位為毫 秒(ms)。
在我們的測試環境中,我們先測量網路中封包的延遲及 ESP8266 執行 Push、Pull 程式碼所需的時間。(1)ESP8266 與伺服器之間網路的平均延遲為 2.5ms。
(2)ESP8266 各執行一次 Push 與 Pull 的指令平均合計需要 30ms。
圖 5-6 的結果為圖 5-2 ~ 圖 5-5 分布圖所計算出的平均延遲。圖 5-6 測試結果是 ESP8266 在不同的測試環境下,受 ESM 拉動週期對於延遲的影響的平均結果。由 圖 5-6 可以明顯的看出,ESM 拉動週期越高,ESP8266 的延遲就越高,且在四種 測試環境中,都呈現同樣的走勢。
圖 5-6 ESP8266 在不同測試環境下之 ESM 拉動週期對延遲的影響結果
更深入的探討圖 5-3,可以從圖中看出發生頻率最高的延遲時間點約是 ESM 週