物聯(lián)網(wǎng)(一)---快速上手[STM32+OneNET+ESP8266]
物聯(lián)網(wǎng)(二)---原理分析[STM32+OneNET+ESP8266]
物聯(lián)網(wǎng)(三)---WEB下發(fā)命令控制單片機(jī)[STM32+OneNET+ESP8266]
物聯(lián)網(wǎng)(四)---搭建自己的TCP服務(wù)器[ESP8266]
物聯(lián)網(wǎng)(五)---搭建自己的云平臺[ESP8266+Django]
下面的這些文章寫的都比我好很多,但:
希望你在點(diǎn)擊完下面的連接后,最終能回歸文章,繼續(xù)搭建自己的物聯(lián)網(wǎng)平臺。
本節(jié)所用到的程序源碼下載地址:https://github.com/keep1234quiet/IOT
使用到的工具:
- Redis 數(shù)據(jù)庫
- Channels-redis
- 將ESP8266變成Arduino的官方庫
這篇文章來之不易,總共花費(fèi)了我整整6天的時間,其中5天摸索,1天寫文章,還望好好閱讀。
其中未使用STM32+ESP8266的方式是因?yàn)槲也恢涝撊绾位貞?yīng)服務(wù)器端Ping ESP8266的Pong,這里應(yīng)該是要自己手動去實(shí)現(xiàn)WebSocket的Ping、Pong的,然后再發(fā)送給ESP8266的串口,后期需要的話再看下能自己實(shí)現(xiàn)不。

在摸索過程中只發(fā)現(xiàn)了ESP8266 的Arduino 庫實(shí)現(xiàn)了Ping 、Pong,故采用之。
本文總共對以下幾個部分進(jìn)行講解:
- 如何將使用Arduino 來開發(fā) ESP8266;
- 使用python的 WEB 框架 Django 來開發(fā)自己的云平臺。
一、將使用Arduino 來開發(fā) ESP8266
1.1 ESP8266硬件連接
這里我是用的是ESP-12S,算是ESP8266的升級版本,還有一個名字叫做NodeMCU,當(dāng)然,使用普通的ESP8266也是一樣的,ESP8266的版本將不會對本節(jié)實(shí)驗(yàn)產(chǎn)生影響,任選一款即可。


對于上圖的接線方式,左上角的復(fù)位開關(guān)建議加上,右下角的輕觸開關(guān)建議換成撥動開關(guān)。

1.2 將使用開發(fā)Arduino的方式去開發(fā)ESP8266
這里有兩篇文章進(jìn)行了介紹,可以參考他的方式去給Arduino添加ESP8266的包管理器。
- [如何安裝ESP8266的Arduino開發(fā)環(huán)境]
- [arduino-esp8266.readthedocs.io->Boards Manager]
這里本來沒什么難度,但要注意的是:
- 插入鏈接
https://arduino-esp8266.readthedocs.io/en/latest/installing.html#boards-manager至Arduino IDE后,要重啟Arduino IDE才行; - 下載包的時候網(wǎng)絡(luò)問題,由于總所周知的原因這里不展開介紹;
- 程序下載的時候需要選好參數(shù),否則會下載不成功,這里每一款模塊都是不一樣的,我的是ESP-12S的,僅供參考。

當(dāng)上面的工作完成后,應(yīng)該就能夠用Arduino IDE給ESP8266下載程序了,這里自己下載程序驗(yàn)證一下,推薦使用串口程序驗(yàn)證,看看是否生效。
二、云平臺開發(fā)
2.1 為什么需要用WEB框架來進(jìn)行開發(fā)
你可能會問,為什么需要用WEB框架來進(jìn)行開發(fā),如果是小型項(xiàng)目,確實(shí)可以不用,但是當(dāng)你要做一個大項(xiàng)目,比如[OneNET云平臺]、[機(jī)智云]、[貝殼物聯(lián)] 這樣的稍大型平臺,甚至要做體量更大的平臺,沒有一個好的WEB框架,想要完成開發(fā)是十分困難的。
2.2 繼續(xù)閱讀下去需要什么基礎(chǔ)
如果看完了前面的預(yù)讀文章,這里應(yīng)該對Django的一些概念有一定的了解了,比如路由urls、模型models、視圖views這些概念都應(yīng)該要知道了。當(dāng)然,對其他類型框架了解的同學(xué)也可以使用其他類型的框架來代替Django。
2.3 怎么搭建云平臺
本文使用的云平臺是由Django-channels的聊天室例程改編而來的。原文在此:
[如何搭建一個簡單的網(wǎng)頁聊天室]
文章是原作者寫的,教程也寫的非常詳細(xì),每一步都有詳細(xì)說明。
整個過程可能需要安裝各種
pythont的庫,這個Terminal會給出詳細(xì)的說明,可以自己根據(jù)錯誤提示去安裝需要的庫,如果要我提供的程序里已經(jīng)將需要用到的庫寫在了requirements.txt文件中了,直接使用pip3命令安裝即可;這里注意一下asgiref這個庫,需要升級到最新版本才行,否則會因使用了新版本里的模塊而導(dǎo)致無法從舊版本導(dǎo)入而報(bào)錯。
唯獨(dú)Part 2里的Enable a channel layer 這里使用Redis數(shù)據(jù)庫建立了一個通信管道,只告訴了要安裝channels_redis庫,并沒有說明如何安裝Redis數(shù)據(jù)庫,這里在前面的使用到的工具里提供了安裝方法【Redis 數(shù)據(jù)庫安裝方法:[Redis安裝]】,里面有詳細(xì)的安裝教程,也對Redis數(shù)據(jù)庫進(jìn)行了相應(yīng)的介紹與講解。
需要注意的是,
Redis數(shù)據(jù)庫安裝完成后,若要服務(wù)器中要使用到管道通信,則必須保持Redis數(shù)據(jù)庫在后臺運(yùn)行,否則將無法完成多對多的通信。
下面的教程只需要完成前三個就好了,第四個自動化測試可以不用。

如果完成了上面channels教程里如何搭建一個聊天服務(wù)器,應(yīng)該能實(shí)現(xiàn)聊天功能了。
2.4 啟動服務(wù),開始測試
首先使用redis-server命令啟動數(shù)據(jù)庫服務(wù):

然后再使用python3 manage.py runserver 0.0.0.0:8000命令開啟服務(wù)器,可使局域網(wǎng)內(nèi)都能訪問網(wǎng)站。

完成上面的Tutorial之后應(yīng)該是下面這種效果的,可以完成同通信功能。
1.先啟動redis數(shù)據(jù)庫:redis-server
2.啟動django自帶的服務(wù)器:python3 manage.py runserver 0.0.0.0:8000
3.查看自己的ip:ipconfig
4.訪問自己的聊天室:http://ip:8000/chat/ChatRoom/
5.發(fā)送消息,服務(wù)器對消息進(jìn)行廣播,各個客戶端都會收到消息

2.5 適當(dāng)修改ESP8266的WebSocketClient程序和服務(wù)端的程序
當(dāng)前面的工作做完之后,接下來的工作就簡單了,只要給ESP8266燒入好程序即可。ESP8266自帶的例程里面有一個WebSocketClient.ino的例程,只要對這個例程稍加修改即可,下面是我修改好的ESP8266的程序。
注:其中主要是要注意
ESP8266發(fā)送數(shù)據(jù)的格式,由于服務(wù)端聊天室只對JSON的數(shù)據(jù)進(jìn)行了解析,所以ESP8266的發(fā)送的數(shù)據(jù)必須以JSON的格式發(fā)送,否則會造成服務(wù)端崩潰。即應(yīng)以這種形式發(fā)送才能在網(wǎng)頁上正常顯示出來:
webSocket.sendTXT("{\"message\":\"heartbeat\"}");
ESP8266的程序較為簡短,我就直接貼了,程序閱讀沒有什么難點(diǎn),應(yīng)該不會看不懂,如果有缺少的庫就自己去裝一下就好了。
/*
* WebSocketClient.ino
*
* Created on: 24.05.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial
bool isConnected = false;
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[WSc] Disconnected!\n");
isConnected = false;
break;
case WStype_CONNECTED: {
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
isConnected = true;
// send message to server when Connected
// webSocket.sendTXT("Connected");//發(fā)送Conneted 導(dǎo)致服務(wù)器拋出 Expecting value: line 1 column 1 (char 0) 異常而斷開連接。//先注釋掉試試
//原因是服務(wù)端只對JSON數(shù)據(jù)進(jìn)行解析,"Connected"是字符串而不是JSON數(shù)據(jù)。
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
//webSocket.sendTXT(rcvMsg);
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup() {
// USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//Serial.setDebugOutput(true);
USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
WiFiMulti.addAP("YOUR-WIFIF-NAME", "YOUR-PASSWORD");
//WiFi.disconnect();
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
// server address, port and URL
webSocket.begin("192.168.43.102", 8000, "/ws/chat/M/");
// event handler
webSocket.onEvent(webSocketEvent);
// use HTTP Basic Authorization this is optional remove if not needed
////webSocket.setAuthorization("user", "Password");
// try ever 5000 again if connection has failed
webSocket.setReconnectInterval(5000);
// start heartbeat (optional)
// ping server every 15000 ms
// expect pong from server within 3000 ms
// consider connection disconnected if pong is not received 2 times
webSocket.enableHeartbeat(15000, 3000, 2);
//這個時間很準(zhǔn),可以從wireshark里面查看,ESP8266每15秒鐘Ping一次服務(wù)器,期間會收到服務(wù)器的Ping,ESP8266也會Pong回去
}
int count = 0;
String inputString = "";
boolean stringComplete = false;
char sendStr[100];
void loop() {
webSocket.loop();
serialEvent(); //發(fā)現(xiàn)ESP8266沒有serialEvent()事件,故在此對該函數(shù)進(jìn)行調(diào)用
count++;
if(count == 60*100){
count = 0;
if(isConnected){ //這里并不是心跳,心跳需要抓包才能看到
webSocket.sendTXT("{\"message\":\"heartbeat\"}");
}
}
if (stringComplete) {
String s1 = "{\"message\":\"";
String s2 = "\"}";
webSocket.sendTXT(s1+inputString+s2);
// clear the string:
inputString = "";
stringComplete = false;
memset(sendStr,0,100);
}
delay(10);
}
void serialEvent() {
while (Serial.available() && !stringComplete) {
// get the new byte:
char inChar = (char)Serial.read();
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n') {
stringComplete = true;
return;
}
// add it to the inputString:
inputString += inChar;
}
}
將上面的程序下載至ESP8266中,從燒錄模式切換為正常運(yùn)行模式,復(fù)位或重新上電。若服務(wù)器沒有關(guān)閉,會從服務(wù)器的Debug窗口看到有新的客戶端連接上了。
當(dāng)連接成功之后,就可以讓ESP8266和服務(wù)器的其他客戶端加入同一個聊天室進(jìn)行聊天了,這里必須是同一個聊天室,不然無法收到信息,聊天室是由/chat/后的字符決定的。
這里我對Web 端的服務(wù)器的consumer.py 文件中的ChatConsumer類里的receive稍加修改,以避免服務(wù)器收到非JSON消息而崩潰,修改如下:
# Receive message from WebSocket
def receive(self, text_data):
print('====' * 10)
print('rawMsg:'+text_data)
print('====' * 10)
try:
text_data_json = json.loads(text_data)
message = text_data_json['message']
except:
message = text_data
pass
# Send message to room group
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
2.6 讓ESP8266作為客戶端,發(fā)送或接收信息
電腦開啟熱點(diǎn),讓手機(jī)和服務(wù)器處于同一局域網(wǎng)內(nèi),如果沒問題的話應(yīng)該能看到如下現(xiàn)象:
- 通過串口助手向
ESP8266發(fā)送數(shù)據(jù),以\n結(jié)尾,不要以\r\n結(jié)尾,程序里沒作相應(yīng)的處理;ESP8266將從串口接收到的數(shù)據(jù)進(jìn)行解析,封裝成JSON格式的數(shù)據(jù),在通過調(diào)用函數(shù)webSocket.sendTXT(your JSON message)將數(shù)據(jù)發(fā)送至服務(wù)器;- 服務(wù)器收到
ESP8266發(fā)來的JSON格式數(shù)據(jù)后,對其進(jìn)行解析,并廣播出去,其中也包括客戶端ESP8266。- 從網(wǎng)頁端發(fā)送數(shù)據(jù),服務(wù)器收到后,也同樣會廣播出去,
ESP8266作為客戶端也會收到被廣播出來的數(shù)據(jù),這樣就完成了一次數(shù)據(jù)收發(fā)的過程了。

到這里就完成開發(fā)了一個自己的云平臺了,雖讓功能比較簡陋,但是整個流程已經(jīng)走通了。相比于上一節(jié)所講的,這一節(jié)似乎并沒有太多的變化,但不同的是,我們這一節(jié)引入了
WEB框架Django,有了WEB框架,我們開發(fā)大型項(xiàng)目就方便快速多了,也非常有利于項(xiàng)目管理。
2.7 整體框架梳理

總結(jié):本文內(nèi)容篇幅不算長,但是大量引用了相關(guān)鏈接,對別人已經(jīng)做得比較好的部分沒有進(jìn)行重復(fù)講解,而是給出鏈接,希望讀者能前去學(xué)習(xí)完畢后再來繼續(xù)學(xué)習(xí)節(jié)教程,其中給出了
Django、channels、WebSocket、ESP8266 Arduino core等均給出原作者原文鏈接及官網(wǎng),也都是很好的教程,非常詳細(xì)。
本文只是走通了【搭建自己的云平臺并使用
ESP8266成功連接云平臺】這條道路,仍然有諸多問題尚未解決,比如:
- 服務(wù)端不應(yīng)該給所有的客戶端進(jìn)行消息廣播,而應(yīng)該是有選擇性的;
- 這里是用了
ESP8266 Arduino core的庫,利用的是其內(nèi)置的WebSocket協(xié)議實(shí)現(xiàn),如果加上單片機(jī)呢,換一種連接方式又該怎么實(shí)現(xiàn)呢?- 這里的所有消息都是明文傳輸,未進(jìn)行加解密,是十分不安全的;
- 瀏覽器端這么丑,是不是應(yīng)該美化一下呢,功能是不是應(yīng)該繼續(xù)豐富呢?
- ...
雖然已經(jīng)做了蠻多的,但是接下來要做的一點(diǎn)也比現(xiàn)在已經(jīng)做的事要少,后續(xù)就留給讀者自己去探尋吧,我此后我會間斷的更新此系列的文章,以盡量使整個過程完整而詳盡。
本系列的物聯(lián)網(wǎng)文章就暫時告一段落了吧。