2021年5月25日 星期二

什麼是 MQTT?

MQTT是由 IBM 的 Andy Stanford-Clark 博士和 Arcom(已更名為 Eurotech)的 Arlen Nipper 博士於 1999 年發明的通訊協定。他們當時是為了在狹窄的網路頻寬和微小電力損耗的需求前提之下,提供石油管線感測器和人造衛星之間一個輕量、可靠的二進制通訊協定。2011 年 11 月,IBM 和 Eurotech 將 MQTT 協定捐贈給負責管理開放原始碼專案的 Eclipse 基金會,並且加入 Eclipse M2M Industry 工作組織。2014 年十月,MQTT 正式變成一個開放的 OASIS 國際標準(Organization Advancement Structured Information Standards,資訊標準架構促進會,一個制定電子商務、網路服務和電子出版的非營利機構)。

MQTT 最初代表的意思是 Message Queueing Telemetry Transport(訊息佇列遙測傳輸),現在已經不用這種說法,MQTT 就是 MQTT,不是其他單字的縮寫。由於 MQTT 協定的訊息內容很精簡,非常適合用於處理器資源及網路頻寬有限的物聯網裝置,再加上已經有許多 MQTT 程式庫被陸續開發出來,用於 Arduino 控制板(C/C++)、JavaScript(Node.js, Espruino 控制板),Python …等等,還有開放原始碼的 MQTT 伺服器,使得開發 MQTT 物聯網、機器之間(Machine-to-Machine, M2M)的通訊變得非常簡單。Facebook Messenger 的即時通訊也是用 MQTT 協定。

採用 Arduino 和 ESP8266 實作 MQTT 之前,我們先探討有關 MQTT 的背景知識和一些術語說明。

比較 HTTP 和 MQTT 通訊協定

MQTT 和 HTTP 的底層都是 TCP/IP,也就是物聯網裝置可以沿用既有的網路架構和設備,只是在網路上流通的「訊息格式」以及應用程式的處理機制不同。

某個裝置透過 Web 瀏覽器,以 HTTP 協定傳送溫度值給網站伺服器,此 HTTP POST 訊息內容大概像這樣:

除了 HTTP 請求指令以及代表 21 度的訊息本體,這段訊息中間夾帶了一堆描述用戶端的的標頭(header)資訊,相當於向伺服器介紹:我來自 Chrome 瀏覽器、作業系統是 Android 7、我讀懂中文和英文…等等。這些額外的標頭訊息在許多物聯網通訊應用不僅僅是多餘的,還會佔用網路頻寬、記憶體並且浪費處理時間。

MQTT 訊息格式

採用 MQTT 發布溫度的訊息格式類似這樣:

不同於 HTTP 的標頭採用文字描述,MQTT 的標頭採用數字編碼,整個長度只佔 2 位元組,等同兩個字元,後面跟著訊息的主題(topic)和內容(payload),實際格式如下:

MQTT 標頭裡的訊息類型、品質…等內容,留待下文說明。除了精簡的標頭,讀者可以發現,MQTT 的標頭區並沒有標示傳送目標的 IP 位址。

MQTT 的 Publisher、Broker 和 Subscriber

根據 MQTT 3.1.1 版本規格書的描述,MQTT 是一種基於「發布∕訂閱」機制的訊息傳輸協定(MQTT is a Client Server publish/subscribe messaging transport protocol),我們可以把它想成雜誌發行和訂閱的機制。MQTT 訊息發送端,相當於雜誌出版社,雜誌出版之後並不直接寄給消費者,而是交給經銷商或者書店一般的代理人(broker),來統籌管理發行和訂閱事宜。每一個訊息來源(刊物)都有個唯一的主題名稱(刊物名稱)。

代理人是個伺服器軟體,向伺服器發送主題的一方是發布者(publisher),從伺服器獲取主題的一方則是訂閱者(subscriber)。以下圖為例,傳送感測器資料的一邊是發布者,接收感測器資料的一邊則是訂閱者。每個感測器∕微控器的訊息都需要有個主題名稱以利識別,像下圖的主題 A、B 和 C。

代理人(broker)可儲存發布者的訊息,在發布者中斷連線的情況下,提供訂閱者最近更新的訊息。「訂閱者」需要告知代理人想要訂閱的主題,每當「發布者」傳入新訊息時,代理人就會依照主題,傳送給所有訂閱者。「發布者」和「訂閱者」都是用戶端,代理人是伺服器。由於兩個用戶端之間有伺服器當作中繼站,所以兩邊並不需要知道彼此的IP位址。

MQTT 的主題(Topic)名稱

MQTT 主題名稱是 UTF-8(萬國碼)編碼的字串,我們可以自行決定主題名稱,例如,傳送溫度的訊息主題可命名成「溫度」、傳送亮度的訊息主題叫做「照度」…等等。主題名稱也支援類似檔案路徑的階層式命名方式,假設住家裡面有許多感測器,我們可依照測器所在位置,規劃如下的命名階層結構:

每個階層之間用斜線分隔,例如,位於庭院的人體感測器 #1,其主題名稱可命名為:

    命名主題的注意事項:
  • 由於某些微控器或程式語言不支援 UTF-8 編碼或中文,主題名稱請使用英文,並且取個有意義的名字。
  • 名稱長度不可超過 216 位元組(65536個字元)。
  • 自訂的主題名稱請勿用 $ 開頭(“$SYS”是 MQTT 伺服器的控制介面主題的保留字),也不可包含 # 和 + 字元;減號和乘號(*)在程式語言中有特殊意義,為了避免誤會,也不建議使用。
  • 名稱的英文大小寫有區別,home 和 Home 是兩個不同的名稱。
  • 雖然名稱可以包含空格,但是英文的「半形」空格和中文的「全形」空格的內碼不一樣,若輸入名稱時沒有統一,會導致程式讀取不到,因此名稱最好不要加入空格。
  • 階層名稱可以空白,像這樣的命名(連續的斜線)是合法的:“home//yard”,代表有三個階層,中間階層沒有名字,在語意上怪怪的。
  • 有些程式設計師習慣在主題名稱最前面加上一個斜線(在 Linux 系統中,檔案路徑開頭的斜線代表根目錄),但這是不必要的。請注意,“/home” 和 “home” 是兩個不同的名稱,前者代表「空白名稱的根階層」底下的 “home”,單一個 “/” 也是合法的名稱。
  • 除了依據裝置安裝地點來命名主題,當同一個地點包含許多感測器的時候,用編號或者唯一識別碼來命名主題是比較合理的選擇。例如,假設某個位於廚房的裝置的 MAC 位址是 DEADBEEFFEED,它可以被命名成:

    Home/kitchen/DEADBEEFFEED