OneNet使用起來要比lewei50復雜一些,它沒有前台需要自己開發。命令下發也和之前介紹的lewei50有一些區別,這里着重介紹一下使用MQTT協議來進行通訊。
一、准備
1、Esp8266開發板(+燒寫器或者直接用WeMos D1 Wifi之類的就不要燒寫器了)
2、Arduino IDE for VS
3、OneNet注冊
二、OneNet配置
OneNet整體結構上與lewei50不同,它從產品開始下面可以有若干按APIKEY組織的設備群組(實名認證與否決定了能用多少產品和設備),每個設備下面可以有若干傳感器和控制器(測試過程未發現個數限制)。而其中控制器狀態是和傳感器一起上傳的,服務器下發的命令以Qos0進行,即不接收回復。更多的內容就請參考開發文檔吧。
1、進入開發者中心創建產品
接入方式選擇公開協議,設備接入協議選擇MQTT。創建完成后,會得到一個APIKEY,這個APIKEY可以用於訪問產品下的任何一個設備。當然,也可以創建一個新的APIKEY,然后設置該APIKEY關聯的設備。
2、創建設備
其中設備名稱和鑒權信息都是自定義,鑒權信息與設備ID是通用的,如果想要用MAC進行綁定那這里鑒權信息就填寫設備的MAC地址,我就隨便填了一個。當然這是與前台相關的內容,不在此討論。
3、創建數據流模板
數據流名稱最好簡明,因為需要ESP8266處理的字符很多,而其資源也不是十分充裕。需要主意的是,每一個數據流就是一個傳感器或控制器。即控制器也在這里創建。創建完成之后就可以把數據流和產品關聯起來,為了直觀觀察,我們再在“應用管理”里面創建一個簡單的應用:
選擇這個開關控件,然后在右面除了關聯設備數據流之外,設置一下開關開值和開關關值:
其默認值開為1,關為0。這里說明一下該參數在服務器下發命令時如何被解釋(以圖中的01和00為例):
把每個數字解釋為1字節進行下發,所以上面00被接收到的是{0,0}這樣的,它與0完全不同。而右面的按鈕我設置的開關之分別為11、10,所以在后面的代碼中前一字節被作為分組標志,后一字節被作為開關值。
三、編碼
1、IDE和配置
與前一篇一樣,這里使用Arduino IDE(實際上我用的是for vs,操作差不多,有代碼提示而已)來進行編寫。開發板選擇NodeMCU1.0(ESP-12E Module),而不論使用的是WeMos D1 Wifi還是其他8針、IO全引出啥的(估計魔改串口4針的是玩不了的)。其他配置如下:
2、層次邏輯
a、Esp8266wifi.h負責連接WIFI
b、pubsubclient.h負責MQTT通訊(可變頭和載體需要自定義,該庫只負責Fixed header)
c、ArduinoJson.h負責構建載體中數據部分,具體格式請參考OneNet MQTT開發文檔
d、eeprom.h負責將配置寫到esp8266模塊中和讀取,這個的寫操作與arduino開發板是不同的
3、主要代碼示例
這里主要說一下MQTT部分,其他部分不再贅述。
a、pubsubclient總述:
WiFiClient wClient;
PubSubClient mClient(mServerAddress,mServerPort, mCallback,wClient);
其中,mCallback是一個回調函數,該函數用於處理服務器發來的消息,其簽名如下:
void mCallback(char* topic, byte* payload, unsigned int length)
其中topic是話題,包含了topicName,Payload是實際下發的內容,例如上面的水泵開關開則有2字節:0 1。length指payload字節數。要處理的服務器數據就在該函數處理就可以了,但是最好別太長,否則影響數據的響應速度。
b、連接MQTT服務器
bool ConnectServer() { int waitConst = 0; if (!mClient.connected()) { //id="設備id",user="產品id",pass="APIkey" mClient.connect(DeviceID, ProductID, APIKey); while (!mClient.connected() && waitConst<20) { delay(100); waitConst++; } Serial.println((String)"ConnectServer " + mClient.connected()); } return mClient.connected();// mClient.state() == MQTT_CONNECTED; }
在Onenet開發者頁面的說明后面有一段問答,其中明確說明了上面的參數如何傳遞,上面注釋寫的非常清楚了;當然也可以用前面提到的設備鑒權信息代替APIKEY字段作為password傳入。
c、發布話題
bool PubTopic(String TopicName,String Json) { memset(msg_buf, 0, MQTT_MAX_PACKET_SIZE); //清理MQTT消息緩存 //設置可變頭 msg_buf[0] = char(0x03); //包數據格式標志 msg_buf[1] = char(Json.length() >> 8); //高位在后 msg_buf[2] = char(Json.length() & 0xff); //低位在前 //設置數據 memcpy(msg_buf + 3, Json.c_str(), Json.length()); mClient.publish(TopicName.c_str(), (uint8_t*)msg_buf, Json.length() + 3); //設置發送長度以免中間的00使得自動判定長度出錯。 }
可變頭部分指明了使用哪種數據包格式,設置了Json數據長度。這一部分有若干種數據格式,具體參考開發文檔,上面代碼中使用的type3格式在說明文檔中的說明如下:
數據類型3(type == 3)格式說明:
Byte 1 |
數據點類型指示:type=3 // JSON格式2字符串 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
Byte 2 |
//指示后面字符串長度 固定兩字節長度高位字節,值為0x00 |
|
|
|
|
|
|
|
|
Byte 3 |
固定兩字節長度低位字節,值為0x46 |
|
|
|
|
|
|
|
|
Byte 4 |
通用格式: { “datastream_id1”:”value1”, “datastream_id2”:”value2”, … }
示例: {“temperature”:22.5,”humidity”:”95.2%”} |
|
|
|
|
|
|
|
|
… … … … |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
||
Byte n |
|
|
|
|
|
|
|
|
看的時候無視這沒頭沒尾的0x00、0x46吧。參考代碼上面的代碼就可以了。其中Json的構建在上一篇中已經說得非常清楚、十分深入了,完成這個代碼構建是基礎中的基礎了,這里不再說明。
四、ESP8266的EEPROM
關於這個問題是看樂鑫的開發文檔還是怎么的才能解決說起來沒意義,上大家最喜歡的積木:
void SaveConfig(int cfgidx, String val) { //esp8266的EEPROM與Areduino的使用不同 //1、申請操作緩存 EEPROM.begin(eBlockSize); //2、寫入數據 for (int i = 0; i < eBlockSize; i++) { EEPROM.write(cfgidx*eBlockSize + i, val[i]); } //3、保存數據更改 EEPROM.commit(); }
好了,這篇就介紹到這里,也不把OneNet上面創建的這個設備的應用嵌入到這里了,願意做的自己做一下就可以看到效果了。我這現在就幾塊開發板,USB已經插了一堆堆各種東西,不可能長期保持這東西在線的。
五、網絡調試工具
這個不像之前的一些WIFI應用調試起來那么簡單,有時候代碼看着貌似都沒問題就是不過,而服務器端沒有提供底層的數據調試功能,一般的SOCKET調試工具也不提供MQTT服務器,自己架設一個也需要大篇幅的工作。所以具體WIFI發送了什么還是要一些其他工具。首先,得讓數據從電腦上過去我們才有機會在電腦上下個鈎子把數據呈現出來,於是我安了一個隨身WIFI,設置好之后,讓ESP8266連接它,至於用什么WIFI軟件無所謂,根本別指望他們提供底層數據,也沒指望去勾它們,那太麻煩了。開發一個新的東西也得不償失,還是用專業工具吧,祭出神器winpcap就解決一切了。使用winpcap的網絡封包工具很多,其中做的好的也不少,Ethereal就是其中一個,現在可能叫Wireshark。