王 臻 成 瀚 桂 林
(公牛集團股份有限公司 慈溪 315314)
隨著電子技術的發展,通信技術的進步,產品的定義發生了較大的變化,特別是與生活息息相關的生活電器。人們開始不滿足于簡單的手動操作,厭倦繁瑣的控制,定制簡便、界面友好的手機應用逐漸成為主流。與它的通信協議也由開始的私有協議逐步被通用性更好、安全性更高的物聯網通信所替代。因此,在智能家居產品的開發中,物聯網的接入成為我們避不開的思考。
例如電飯煲,隨著需求的提升功能越來越多,最終布滿產品甚至導致選擇困擾,通過接入物聯網平臺將設置簡潔化,并實現了一鍵定制;又例如空調,一般采用距離較近的紅外控制,開啟后需要較長時間等待,通過接入物聯網平臺實現了遠程控制,并可根據習慣設置任務,省去了開啟后等待的問題;再例如家里的燈具和窗簾,睡前需要一個個去關閉,通過接入物聯網平臺可以設置睡眠模式實現整體調整燈光亮度、關閉窗簾的一系列操作。可見接入物聯網不僅能方便控制,還能提升生活的舒適感受,滿足我們對電器使用的更高要求。
物聯網通信是一種通用的開發方式,它不同于私有協議,具有協議規范、內容可讀性高的特點,這也是為什么接入物聯網能實現互聯互通的原因,而實現它得益于MQTT和JSON的應用。
MQTT是一個輕巧、開放、簡單、規范、易于實現的通用協議,是一種標準消息隊列。有別于動輒幾十上百條的私有協議,MQTT定義規范、分類明確,有效內容僅十四條,不僅使用方便,解讀也很容易。而且基礎通信僅需完成連接、訂閱和發布消息三條即可,入手開發比較方便。
它使用發布、訂閱的消息模式,特別適用于對代碼封裝要求嚴格、網絡帶寬非常昂貴的環境,正如我們需要接入的物聯網環境以及機器之間的通信環境。消息具有一對多的分發特點,通信可以根據應用場景選擇三種不同的通信質量,協議數據交互及傳輸消耗均較少確保了較少的流量消耗。
交互時我們比較關系的是通信質量,有最多一次、至少一次、僅一次三種方式。它們在應用中有著各自的特點和適用的場景:
最多一次,應用與對數據要求不高的情況,類似于UDP通信,是允許數據丟失的,例如非故障傳感器返回的信息;
至少一次,應用于對數據要求較高的情況,類似于TCP通信,數據通信一定要達成,例如需要立即執行的故障傳感器;
僅一次,應用于需要較準確的場景,避免因多次通信請求導致錯誤的發生,例如收費和校時功能。
1.1.1 連接CONNECT
通信的前置操作,就像是鑰匙,開啟后才能搭建設備與服務器的通信,由固定包頭、可變包頭、消息體三部分組成。
1.1.1.1 固定包頭
固定包頭含首字節和可變長度兩部分。
首字節的高4位用于表示報文類型,bit4置1表示CONNECT,即CONNECT的首字節為0×10;可變長度表示后面所有數據的長度,可通過1~4字節表示,其中每字節的bit7有特殊意義,置1下個字節會繼續表示數據長度,并將內容銜接到上一字節的bit7處,例如長度為128字節表示為0×80,但是因為bit7置1了,因此需要表示為0×80 0×01。
1.1.1.2 可變包頭
可變包頭含固定長度標識、MQTT標識、協議級別、鏈接標志、保活時間五部分。
固定長度:
固定的值,表示后面MQTT標識的長度,為0×00 0×04。
MQTT標識:
MQTT大寫的ASCII碼對應值,為0×4D 0×51 0×54 0×54。
協議級別:
協議的等級,此處遵循的是MQTT3.1.1版本,對應為等級4。
鏈接標志:
連接標記,由具有特定意義的位組合而成,組成方式如表1所示。Clean Session置1表示需清除Session;Will Flag置1表示需要在設備掉線后提醒訂閱方掉線;QoS為0表示進行最多一次的服務質量通信,為1表示進行最少一次的服務質量通信,為2表示進行僅一次的服務質量通信;Will Retain置1表示新訂閱者也能收到離線消息;Password置1表示消息體包含Password,UserName置1表示消息體包含User Name。此處鏈接標志我們假設消息體帶有User Name和Password,且需要清除Session,那么鏈接標志即為0×C2。

表1 鏈接標志字節內容組成
保活時間:
最大不發送數據的時間間隔,單位為秒,用以服務器確認設備是否在線的時間當量,一般在1.5倍的該時間內收到客戶端的報文即認為設備離線。此處我們假定需要的保活時間為100 s,則此位為0×00 0×64。
1.1.1.3 消息體
消息體的基礎內容有clientId、安全模式、算法類型,還有做為補充的timestamp,然后是由鏈接標志確定的User Name和 Password。
消息體的每一項都分為長度和內容兩部分,前兩個字節表示當前消息體的字節長度,后面的字節即為消息體的實際內容。
clientId是用戶設置的長度最大64字節的設備唯一碼,假設此處為ALSJ-00000001;安全模式有TLS直連模式的2和TCP直連模式的3,此處我們選擇TCP直連模式;算法類型可以選擇hmacmd5和hmacsha1,這里我們選擇hmacsha1;補充部分此處暫不做討論。
根據上述設定及連接規則“clientId|securemode,sig nmethod|”,可以得到消息體的基礎內容一為“ALSJ-00000001|securemode=3,signmethod=hmacsha1|”;
在鏈接標志置位User Name的情況下,假設此時clientId對 應 的Device Name為ALSJ-00000001,Product Key為a1SwMLEkaCz,根據上述設定及連接規則“DeviceName&ProductKey”,可以得到消息體的基礎內容二為“ALSJ-00000001&a1SwMLEkaCz”;
在鏈接標志置位Password的情況下,又假設此 時Device Secret為fzcxQhVRSwCDk6Z0XH9AeCy qZ0sjGxtA,根據上述設定及連接內容“clientId$de viceName$productKey$”,可以得到消息體的源碼內 容 為“clientIdALSJ-00000001deviceNameALSJ-00000001productKeya1SwMLEkaCz”, 再 使 用 Device Secret對此進行hmacsha1加密后即可得到消息體的基礎內容三為“8a4b0b81a63016946c7f0ce19473f6de7546fe6d”;
通過組合基礎內容一、二、三后進行消息推送,即可與云端Device Name為ALSJ-00000001、ClientID為ALSJ-00000001、Product Key為 a1SwMLEkaCz、Device Secret為fzcxQhVRSwCDk6Z0XH9AeCyqZ0sjGxtA的數字設備構成連接。
1.1.2 訂閱SUBSCRIBE
訂閱的數據結構與連接CONNECT相同,也由固定包頭、可變包頭、消息體三部分組成,為想要接收到的內容。
固定包頭:
與連接有區別的是此處的首字節為0×82。
可變包頭:
此處與連接有較大區別,僅有報文標識符。報文標識符為自己定義,服務器應答時會回復該值,用于確認回復與發送的關系,假定此處為0×00 0×0A。
消息體:
與訂閱內容相關,保持長度加內容的方式。需要訂閱的內容有屬性、事件、服務等,幾種內容的格式是完全相同的,僅Topic有差異。這里假定需要訂閱的屬性為/sys/a1SwMLEkaCz/ALSJ-00000001/thing/service/property/set,報文等級為0x00的等級1。
針對例子對內容進行組合即可完成屬性的訂閱操作,訂閱后我們就可以收到屬性的內容了,也可以進行發布消息來改變屬性。
1.1.3 發布消息PUBLISH
發布消息的數據結構與連接CONNECT相同,不過數據內容與訂閱更相近些。
固定包頭:首字節為0×30。
可變包頭:與訂閱相同采用報文標識符的形式,值得注意的是具有可變包頭的情況是QoS為1、2時才具備的,QoS為0時是沒有可變包頭字節的。假定此處QoS為0×
消息體:
為Topic與物模型內容組合的方式,這里涉及物模型的JSON格式推送詳見下節。首先需要關聯推送的Topic,它可以是屬性、事件、服務中的一種,但格式完全相同。假定此時需要推送的方式為屬性,屬性Topic為“/sys/a1SwMLEkaCz/ALSJ-00000001/thing/event/property/post”,屬性物模型PowerSwitch為關閉狀態0,則推送的 JSON 內容為 {“method”:”thing.service.property.set”,”id”:”1061986371”,”params”:{“PowerSwitch”:0},”version”:”1.0.1_0001”},將Topic與JSON內容組合進行消息的推送即可實現關燈的操作。需要注意的是通信一問一答的方式需要通過id進行對應,params中的內容可直接讀取,閱讀非常方便。
JSON則是一種格式化的文本數據交換格式,有很好的可讀性,同時其結構化的報文也非常方便設備解析。JSON數據對比私有協議的數據就像C語言相對于匯編,可更直觀地解讀,獲取內容、排查問題。
JSON的語法結構是以JavaScript對象表示語法的子集。數據在名稱/值對中成對出現,數據由逗號分隔,使用斜桿來轉義字符,大括號保存對象,中括號保存數組,且數組可以包含多個對象。表示的內容有兩種,分別是對象和數組,并根據使用的靈活性value是可以嵌套的。
JSON對象:
大括號內指定的內容稱為對象,是由名稱/值對組成的集合,每個名稱/值對之間使用逗號分隔。一個對象以左括號開始右括號結束,每個名稱后跟一個冒號并接上具體的值,值的類型有字符串、數值等。具體形式如圖1所示。

圖1 JSON對象的內容格式
例如 {“LedNumber”:1,“PowerSwitch”:0,“Description”:”LeftLight”},這個對象就表示了三個名稱/值對,其名稱分別為“LedNumber”、“PowerSwitch”、“Description”,定義值分別為數量1、狀態0、名稱LeftLight。
JSON數組:
中括號內指定的內容為數組,一般中間為多個對象,且多個對象格式相近,每個對象以逗號隔開。具體形式如圖2所示。

圖2 JSON數組的內容格式
例如

表征的是一個數組,里面有4個對象,分別對應LedNumber 號碼為1、2、3、4的對象內容,并在每個對象內定義了PowerSwitch的狀態及Description的字符描述。
JSON 值的嵌套:
是指名稱/值對中的值進行了重定義,嵌套入了新的對象或是數組,用于定義更多的信息。具體形式如圖3所示。

圖3 名稱/值對中值的類型展示
這里我們例舉個嵌套例子便于理解。


例子是個對象,里面包含了兩個名稱/值對,其名稱分別為“params”和“temp”,其中params也是一個對象,內部包含一個名稱/值對,其名稱為“state”,值由一個嵌套的數組替代,而數組又包含了4個對象,分別對應了號碼為LedNumber1、2、3、4的PowerSwitch狀態和Description字符描述。
可以看出,狀態的給出是非常工整的,也易于查看,不需像私有協議一樣頻繁查看協議進行解讀。使用中,我們只需要將名稱/值對通過固定的方式進行組合、嵌套就可以很容易的與云端進行數據的交互了,其中交互信息里的名稱/值對在平臺上一般稱為物模型,對其進行定義和替換即可完成內容的傳遞。
在產品的設計中,我們比較關心的是數據的交互,而數據的交互最關鍵的是屬性、事件與服務,分別對應三種不同的數據交互方式。
屬性的交互可通過對物模型的讀寫實現狀態的獲取或控制,但時效性一般,例如對燈具開關屬性的獲取或控制;事件的交互為產品發起,具有較高的時效性,一般為緊急狀況的特別推送,例如燈具發生的過溫故障;屬性交互為服務端發起,具有較高的時效性,需要產品立即執行,例如燈具定時任務的下發。
根據產品的功能特性對屬性、事件、服務進行合理定義,應用中設定好它們的實現邏輯,再結合產品本身的成熟方案,那么一個好的物聯網產品也就呼之欲出了。
從標準的MQTT協議、格式化的JSON通信,不難看出物聯網極力想做的是統一,根本目的是互聯互通,從而實現智能家居產品的聯動,并最終構建智慧社區。
我們身邊也不乏這種例子,例如小米的小愛同學,通過與它的交互,可以實現對接入的電視進行開啟、關閉等操作,對接入的窗簾進行打開和關上的控制,對接入的空調進行溫度的調控,以及對接入的燈具進行統一的管理等等。
因此掌握好這項技術,并將它應用到智能家居的產品中,給用戶帶來更便捷、更具新意、更有科技感的產品,勢必具有深遠的意義。