有了前面兩個實驗的經驗,廢話不多說,直接使用OneNET。
使用OneNET
首先注冊與登陸OneNET,然後進入開發者中心:https://open.iot.10086.cn/develop/global/product/#/public,選擇公有協定産品,點選“添加産品”,填寫産品相關的資訊,聯網方式選擇wifi(其實我們是以太網,但是沒有這個選項,那就選擇wifi,沒啥影響的),接入協定必須選擇MQTT,作業系統選擇無,營運商這個随意選擇,具體見圖。

圖 添加産品
在添加産品完畢,繼續添加裝置,填寫裝置的相關資訊,鑒權資訊就是登陸密碼,不過需要注意的是,這個鑒權資訊在産品内是唯一的,一般推薦使用産品序列号,可作為裝置登入參數之一,不同協定裝置可能鑒權資訊的參數不一緻,不過現在是做實驗,選擇一個好記的即可,資料保密性要選擇私有,除此之外還剩下一些裝置相關的資訊,就不過多贅述,具體見圖,為了測試,我們在這裡就建立2 個裝置,大家可以自行建立更多的裝置用于測試也是可以的。
圖 建立裝置資訊
測試連接配接
OneNET 的資料互動做的很好,它支援動态建立主題(除系統主題外),即不用我們在平台上建立任何的主題,隻需要随意訂閱某個主題即可,同一個産品下,即使是不同裝置之間的主題之間的消息是共享的,簡單來說,我們在開發闆上可以随意向某個主題發起訂閱請求,也可以向這個主題釋出消息,而同一産品的其他裝置如果訂閱了這個主題,那麼将收到開發闆釋出的消息資料,這樣子更加友善嵌入式的開發者,隻不過這樣子的資訊安全就沒有阿裡雲物聯那麼好。
現在我們打開MQTT 軟體,進行連接配接測試,與前面的實驗操作是一樣的,配置好相關資訊,即可,這些資訊都可以在平台上找到,需要注意的是伺服器位址是183.230.40.39;端口号是6002,這與我們常見的1883 是不一樣的;Client ID 是裝置ID,在裝置清單中是可以找到的;使用者名是産品ID,在産品清單中可以找到;密碼就是建立裝置時候的鑒權資訊,具體見圖。
圖 MQTT 軟體配置
配置好就可以連接配接,然後随便訂閱一個主題,再用用戶端進行釋出消息,如果收到消息,表明通信成功,具體見圖。
圖 測試連接配接結果
開發闆連接配接OneNET
在開發闆中,我們将連接配接阿裡雲物聯的代碼拷貝一份,然後修改一下mqttclient.h 頭檔案的宏定義即可,具體見代碼清單。
代碼清單 mqttclient.h 頭檔案的宏定義
#if LWIP_DNS
#define HOST_NAME "mqtt.heclouds.com" //伺服器域名
#else
#define HOST_NAME "183.230.40.39" //伺服器IP 位址
#endif
#define HOST_PORT 6002
#define CLIENT_ID "518725049" //
#define USER_NAME "217537" //使用者名
#define PASSWORD "12345" //秘鑰
#define TOPIC "temp_hum" //訂閱的主題
#define TEST_MESSAGE "test_message" //發送測試消息
下載下傳到開發闆上,就能連接配接到OneNET 了,因為都是使用MQTT 協定,是以連接配接的過程都是一樣的,而且資料的訂閱與釋出也是一樣的,編譯好程式,然後下載下傳到開發闆,就能看到釋出資料與接收資料了,具體見圖。
圖 實驗現象
此外,我們再使用MQTT 軟體來訂閱一下開發闆釋出的主題“temp_hum”,很顯然,軟體收到了開發闆釋出的消息,說明正如我們所了解一樣,通信正常,具體見圖。
圖 MQTT 軟體訂閱temp_hum 主題
添加資料流
OneNET 平台通過資料流與資料點來組織裝置上行資料,裝置上傳并存儲資料時,必須以keyvalue的格式上傳資料,其中key 即為資料流(stream)名稱,value 為實際存儲的資料點(point),value 格式可以為int、float、string、json 等多種自定義格式,我們本書就教大家向OneNET 平台上報資料點,而平台會将所有的資料點按時序進行存儲,這就形成了資料流,資料流中的資料在存儲的同時可以“流向”後續服務,資料流是平台後續資料服務(規則、觸發器、消息隊列等)的服務對象,使用者可以通過選擇資料流的方式選擇服務的資料來源,這與我們之前的規則引擎差不多,隻不過換了個說法而已。
首先在OneNET 控制台中添加兩個資料流,分别為temp 與hum,表示溫度與濕度的資料流,打開:https://open.iot.10086.cn/develop/global/product/#/datasm?pid=217537,點選“添加資料流模闆”,填寫相關資訊,資料流名稱填寫你釋出資料的JSON 格式的字元串,類似于規則引擎的篩選,具體見圖,添加temp 完成後再添加一個hum 資料流模闆。
圖 添加資料流模闆
裝置是使用publish 封包來上傳資料點的,而且上報資料點的格式也是有要求的,對于普通的主題,釋出消息資料根本沒有任何格式要求,随意釋出資料都可以,但是對于釋出資料點等與平台資料相關的操作,則必須通過系統主題進行釋出,釋出資料點的主題是“$ dp”,“$ ”符号表示系統的主題,所有的資料都要往這個主題釋出,否則系統是不會存儲資料的,大家在建立資料流之後,打開開發闆,向OneNET 平台上報資料(非“$dp”主題),然後在背景可以看到并無資料記錄,具體見圖。
如果想要發送資料并讓平台存儲,形成資料流,将資料應用到可視化上或者其他服務,那麼就必須向“ $dp”主題發送資料,而向系統主題發送資料就會有格式要求,目前OneNET 支援7 種格式的釋出,我在這裡隻講解第三種格式的資料上報,因為這與我們的前面實驗的資料最為相似,而其他的資料格式大家可以參考一下官方的接入資料,可以到論壇裡面擷取:http://www.firebbs.cn/forum.php?mod=viewthread&tid=26274&fromuid=37393。
圖 無資料
系統主題的釋出格式
從MQTT 釋出封包(PUBLISH)的格式我們可以知道,主題名必須是PUBLISH 封包可變報頭的第一個字段,而系統主題名就是“$dp”,隻有當QoS 等級是1 或2 時,封包辨別符(Packet Identifier)字段才能出現在PUBLISH 封包中,那麼可變報頭的結構具體見圖。
圖 PUBLISH 封包可變報頭
有效載荷(Payload)則包含真正的資料點内容,本書隻講解第三種類型的資料格式,具體見圖,第1 個位元組指明了釋出封包的資料類型,OneNET 平台支援7 種資料類型,第三種是JSON格式,是以資料字段就需要是JSON 格式的資料,否則就會發生錯誤;第2 第3 位元組指明了後面攜帶的資料長度,第二位元組是資料長度的高位元組位,第3 位元組是資料長度的低位元組位;而第4 位元組之後就是真正的資料區域,使用JSON 資料即可。
圖 Payload 内容
使用開發闆釋出資料點
因為是有格式的要求,我們就稍微改一下代碼的接口,在資料之前添加類型、長度等字段,因為我們的代碼也是自己封裝好的,是以想修改也是比較簡單,而MQTT 層的代碼也無需改動,因為是釋出資料點,是以我們實作一個MQTTMsgPublish2dp() 函數用于釋出到“$dp”主題中,具體見代碼清單。
代碼清單 MQTTMsgPublish2dp() 源碼
/************************************************************************
** 函數名稱: MQTTMsgPublish2dp
** 函數功能: 使用者推送消息到'$dp'系統主題
** 入口參數: MQTT_USER_MSG *msg:消息結構體指針
** 出口參數: >=0: 發送成功<0: 發送失敗
** 備注:
************************************************************************/
int32_t MQTTMsgPublish2dp(int32_t sock, int8_t qos, int8_t type,uint8_t* msg)
{
int32_t ret;
uint16_t msg_len = 0;
msg_len = strlen((char *)msg);
uint8_t* q = pvPortMalloc(msg_len+3); //目前隻支援1、3、4 類型的json 資料
switch (type)
{
case TopicType1:
*(uint8_t*)&q[0] = 0x01;
break;
case TopicType3:
*(uint8_t*)&q[0] = 0x03;
break;
case TopicType5:
*(uint8_t*)&q[0] = 0x05;
break;
default:
goto publish2dpfail;
}
*(uint8_t*)&q[0] = 0x03;
*(uint8_t*)&q[1] = ((msg_len)&0xff00)>>8;
*(uint8_t*)&q[2] = (msg_len)&0xff;
memcpy((uint8_t*)(&q[3]),(uint8_t*)msg,msg_len);
//釋出消息
ret = MQTTMsgPublish(MQTT_Socket,(char*)"$dp",qos,(uint8_t*)q,msg_len+3);
publish2dpfail:
vPortFree(q);
q = NULL;
return ret;
}
而為了配合MQTTMsgPublish2dp() 函數,我們還需要把MQTTMsgPublish() 函數也稍作修改,讓其傳入指定的資料長度,因為strlen() 函數會在遇到ASCII 碼‘00’的時候認為資料已經沒有了,而有效載荷的資料長度高位元組位很有可能是0,是以不使用自動統計資料,隻傳入指定資料長度,具體見代碼清單。
代碼清單 MQTTMsgPublish() 源碼
/********************************************************
** 函數名稱: mqtt_msg_publish
** 函數功能: 使用者推送消息
** 入口參數: MQTT_USER_MSG *msg:消息結構體指針
** 出口參數: >=0: 發送成功<0: 發送失敗
** 備注:
*******************************************************/
int32_t MQTTMsgPublish(int32_t sock,
char *topic,
int8_t qos,
uint8_t* msg,
uint16_t msg_len)
{
int8_t retained = 0; //保留标志位
// uint32_t msg_len; //資料長度
uint8_t buf[MSG_MAX_LEN];
int32_t buflen = sizeof(buf),len;
MQTTString topicString = MQTTString_initializer;
uint16_t packid = 0,packetidbk;
//填充主題
topicString.cstring = (char *)topic;
//填充資料包ID
if ((qos == QOS1)||(qos == QOS2))
{
packid = GetNextPackID();
}
else
{
qos = QOS0;
retained = 0;
packid = 0;
}
// msg_len = strlen((char *)msg);
//推送消息
len = MQTTSerialize_publish(buf, buflen, 0, qos, retained,
packid, topicString,
(unsigned char*)msg, msg_len);
if (len <= 0)
return -1;
if (transport_sendPacketBuffer(buf, len) < 0)
return -2;
//品質等級0,不需要傳回
if (qos == QOS0)
{
return 0;
}
//等級1
if (qos == QOS1)
{
//等待PUBACK
if (WaitForPacket(sock,PUBACK,5) < 0)
return -3;
return 1;
}
//等級2
if (qos == QOS2)
{
//等待PUBREC
if (WaitForPacket(sock,PUBREC,5) < 0)
return -3;
//發送PUBREL
len = MQTTSerialize_pubrel(buf, buflen,0, packetidbk);
if (len == 0)
return -4;
if (transport_sendPacketBuffer(buf, len) < 0)
return -6;
//等待PUBCOMP
if (WaitForPacket(sock,PUBREC,5) < 0)
return -7;
return 2;
}
//等級錯誤
return -8;
}
然後在mqttclient.h 頭檔案中修改相關的宏定義即可,在這裡多添加了一個枚舉類型的TopicType,表示選擇釋出到“$dp”主題的類型,具體見代碼清單。
代碼清單mqttclient.h 頭檔案宏定義
#if LWIP_DNS
#define HOST_NAME "mqtt.heclouds.com" //伺服器域名
#else
#define HOST_NAME "183.230.40.39" //伺服器IP 位址
#endif
#define HOST_PORT 6002
#define CLIENT_ID "518725049" //
#define USER_NAME "217537" //使用者名
#define PASSWORD "12345" //秘鑰
#define TOPIC "temp_hum" //訂閱的主題
#define TEST_MESSAGE "test_message" //發送測試消息
enum TopicType
{
TopicType1 = 1,
TopicType3 = 3,
TopicType5 = 5
};
編譯好程式後,就下載下傳到開發闆中,然後在OneNET 的控制台中可以看到資料點已經被存儲了,而且能看到資料流資訊,具體見圖,然後我們就能用這些資料流作為可視化的資料源。
圖 資料流
資料可視化
與百度雲一樣,資料可視化需要依賴資料的來源,而上一章我們得到資料的來源,那麼就利用平台自身的可視化技術來讓資料實時顯示,首先進入“應用管理”頁面:https://open.iot.10086.cn/develop/global/product/#/app/independent?pid=217537,點選添加應用,然後填寫應用的相關資訊,因為是測試應用,是以選擇“私有”選項即可,具體見圖。
圖 添加應用
在應用編輯頁面,選中兩個儀表盤,放到畫布中,然後點選儀表盤,選擇“屬性”,選擇裝置為開發闆裝置“fire_stm32f4”,資料流選擇我們在一開始添加的資料流“temp”,選擇自動重新整理的頻率為3 秒,同樣的另一個濕度的儀表盤也是這樣子操作,此外還可以添加折線圖到畫布中,也是一樣的選擇裝置與資料流,至此可視化應用的設計部署就已經完成,具體見圖。然後我們就能看到資料會随着開發闆釋出的資料更新,示範連結:https://open.iot.10086.cn/iotbox/appsquare/appview?openid=c673cc3ee3e436d298494aba2e5c08b8。
圖 應用編輯
參考資料:LwIP 應用開發實戰指南—基于野火STM32