物联网(五)---搭建自己的云平台[ESP8266+Django]

物联网(一)---快速上手[STM32+OneNET+ESP8266]
物联网(二)---原理分析[STM32+OneNET+ESP8266]
物联网(三)---WEB下发命令控制单片机[STM32+OneNET+ESP8266]
物联网(四)---搭建自己的TCP服务器[ESP8266]
物联网(五)---搭建自己的云平台[ESP8266+Django]

下面的这些文章写的都比我好很多,但:
希望你在点击完下面的连接后,最终能回归文章,继续搭建自己的物联网平台。

本节所用到的程序源码下载地址:https://github.com/keep1234quiet/IOT

使用到的工具:
  • Redis 数据库
  • Channels-redis
  • 将ESP8266变成Arduino的官方库

这篇文章来之不易,总共花费了我整整6天的时间,其中5天摸索,1天写文章,还望好好阅读。

其中未使用STM32+ESP8266的方式是因为我不知道该如何回应服务器端Ping ESP8266Pong,这里应该是要自己手动去实现WebSocketPingPong的,然后再发送给ESP8266的串口,后期需要的话再看下能自己实现不。

socket断开的原因分析

在摸索过程中只发现了ESP8266Arduino 库实现了PingPong,故采用之。

本文总共对以下几个部分进行讲解:

  • 如何将使用Arduino 来开发 ESP8266;
  • 使用python的 WEB 框架 Django 来开发自己的云平台。

一、将使用Arduino 来开发 ESP8266

1.1 ESP8266硬件连接

这里我是用的是ESP-12S,算是ESP8266的升级版本,还有一个名字叫做NodeMCU,当然,使用普通的ESP8266也是一样的,ESP8266的版本将不会对本节实验产生影响,任选一款即可。

ESP-12引脚定义
推荐接线方式

对于上图的接线方式,左上角的复位开关建议加上,右下角的轻触开关建议换成拨动开关。

我的实物图
1.2 将使用开发Arduino的方式去开发ESP8266

这里有两篇文章进行了介绍,可以参考他的方式去给Arduino添加ESP8266的包管理器。

  • [如何安装ESP8266的Arduino开发环境]
  • [arduino-esp8266.readthedocs.io->Boards Manager]

这里本来没什么难度,但要注意的是:

  • 插入链接https://arduino-esp8266.readthedocs.io/en/latest/installing.html#boards-manager至Arduino IDE后,要重启Arduino IDE才行;
  • 下载包的时候网络问题,由于总所周知的原因这里不展开介绍;
  • 程序下载的时候需要选好参数,否则会下载不成功,这里每一款模块都是不一样的,我的是ESP-12S的,仅供参考。
ESP8266 Arduino core 程序下载参数配置

当上面的工作完成后,应该就能够用Arduino IDE给ESP8266下载程序了,这里自己下载程序验证一下,推荐使用串口程序验证,看看是否生效。

二、云平台开发

2.1 为什么需要用WEB框架来进行开发

你可能会问,为什么需要用WEB框架来进行开发,如果是小型项目,确实可以不用,但是当你要做一个大项目,比如[OneNET云平台]、[机智云]、[贝壳物联] 这样的稍大型平台,甚至要做体量更大的平台,没有一个好的WEB框架,想要完成开发是十分困难的。

2.2 继续阅读下去需要什么基础

如果看完了前面的预读文章,这里应该对Django的一些概念有一定的了解了,比如路由urls、模型models、视图views这些概念都应该要知道了。当然,对其他类型框架了解的同学也可以使用其他类型的框架来代替Django

2.3 怎么搭建云平台

本文使用的云平台是由Django-channels的聊天室例程改编而来的。原文在此:
[如何搭建一个简单的网页聊天室]

文章是原作者写的,教程也写的非常详细,每一步都有详细说明。

整个过程可能需要安装各种pythont的库,这个Terminal会给出详细的说明,可以自己根据错误提示去安装需要的库,如果要我提供的程序里已经将需要用到的库写在了requirements.txt文件中了,直接使用pip3命令安装即可;这里注意一下asgiref这个库,需要升级到最新版本才行,否则会因使用了新版本里的模块而导致无法从旧版本导入而报错。

唯独Part 2里的Enable a channel layer 这里使用Redis数据库建立了一个通信管道,只告诉了要安装channels_redis库,并没有说明如何安装Redis数据库,这里在前面的使用到的工具里提供了安装方法【Redis 数据库安装方法:[Redis安装]】,里面有详细的安装教程,也对Redis数据库进行了相应的介绍与讲解。

需要注意的是,Redis数据库安装完成后,若要服务器中要使用到管道通信,则必须保持Redis数据库在后台运行,否则将无法完成多对多的通信。

下面的教程只需要完成前三个就好了,第四个自动化测试可以不用。

实现一个聊天室的例子

如果完成了上面channels教程里如何搭建一个聊天服务器,应该能实现聊天功能了。

2.4 启动服务,开始测试

首先使用redis-server命令启动数据库服务:

redis-server

然后再使用python3 manage.py runserver 0.0.0.0:8000命令开启服务器,可使局域网内都能访问网站。

python3 manage.py runserver 0.0.0.0:8000

完成上面的Tutorial之后应该是下面这种效果的,可以完成同通信功能。
1.先启动redis数据库:redis-server
2.启动django自带的服务器:python3 manage.py runserver 0.0.0.0:8000
3.查看自己的ipipconfig
4.访问自己的聊天室:http://ip:8000/chat/ChatRoom/
5.发送消息,服务器对消息进行广播,各个客户端都会收到消息

聊天室功能开启与展示.gif
2.5 适当修改ESP8266的WebSocketClient程序和服务端的程序

当前面的工作做完之后,接下来的工作就简单了,只要给ESP8266烧入好程序即可。ESP8266自带的例程里面有一个WebSocketClient.ino的例程,只要对这个例程稍加修改即可,下面是我修改好的ESP8266的程序。

注:其中主要是要注意ESP8266发送数据的格式,由于服务端聊天室只对JSON的数据进行了解析,所以ESP8266的发送的数据必须以 JSON的格式发送,否则会造成服务端崩溃。即应以这种形式发送才能在网页上正常显示出来:
webSocket.sendTXT("{\"message\":\"heartbeat\"}");

ESP8266的程序较为简短,我就直接贴了,程序阅读没有什么难点,应该不会看不懂,如果有缺少的库就自己去装一下就好了。

/*
 * 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");//发送Conneted 导致服务器抛出 Expecting value: line 1 column 1 (char 0) 异常而断开连接。//先注释掉试试
//原因是服务端只对JSON数据进行解析,"Connected"是字符串而不是JSON数据。
            }
            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); 
  //这个时间很准,可以从wireshark里面查看,ESP8266每15秒钟Ping一次服务器,期间会收到服务器的Ping,ESP8266也会Pong回去

}

int count = 0;
String inputString = "";
boolean stringComplete = false;
char sendStr[100];

void loop() {
  webSocket.loop();
  serialEvent();  //发现ESP8266没有serialEvent()事件,故在此对该函数进行调用
  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中,从烧录模式切换为正常运行模式,复位或重新上电。若服务器没有关闭,会从服务器的Debug窗口看到有新的客户端连接上了。

当连接成功之后,就可以让ESP8266和服务器的其他客户端加入同一个聊天室进行聊天了,这里必须是同一个聊天室,不然无法收到信息,聊天室是由/chat/后的字符决定的。

这里我对Web 端的服务器的consumer.py 文件中的ChatConsumer类里的receive稍加修改,以避免服务器收到非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作为客户端,发送或接收信息

电脑开启热点,让手机和服务器处于同一局域网内,如果没问题的话应该能看到如下现象:

  1. 通过串口助手向ESP8266发送数据,以\n结尾,不要以\r\n结尾,程序里没作相应的处理;
  2. ESP8266 将从串口接收到的数据进行解析,封装成JSON格式的数据,在通过调用函数webSocket.sendTXT(your JSON message)将数据发送至服务器;
  3. 服务器收到ESP8266发来的JSON格式数据后,对其进行解析,并广播出去,其中也包括客户端ESP8266
  4. 从网页端发送数据,服务器收到后,也同样会广播出去,ESP8266作为客户端也会收到被广播出来的数据,这样就完成了一次数据收发的过程了。
ESP8266与服务端通信过程.gif

到这里就完成开发了一个自己的云平台了,虽让功能比较简陋,但是整个流程已经走通了。相比于上一节所讲的,这一节似乎并没有太多的变化,但不同的是,我们这一节引入了WEB框架 Django,有了WEB框架,我们开发大型项目就方便快速多了,也非常有利于项目管理。

2.7 整体框架梳理
整个通信过程的整体框架

总结:本文内容篇幅不算长,但是大量引用了相关链接,对别人已经做得比较好的部分没有进行重复讲解,而是给出链接,希望读者能前去学习完毕后再来继续学习节教程,其中给出了DjangochannelsWebSocketESP8266 Arduino core等均给出原作者原文链接及官网,也都是很好的教程,非常详细。

本文只是走通了【搭建自己的云平台并使用ESP8266成功连接云平台】这条道路,仍然有诸多问题尚未解决,比如:

  1. 服务端不应该给所有的客户端进行消息广播,而应该是有选择性的;
  2. 这里是用了ESP8266 Arduino core的库,利用的是其内置的WebSocket 协议实现,如果加上单片机呢,换一种连接方式又该怎么实现呢?
  3. 这里的所有消息都是明文传输,未进行加解密,是十分不安全的;
  4. 浏览器端这么丑,是不是应该美化一下呢,功能是不是应该继续丰富呢?
  5. ...

虽然已经做了蛮多的,但是接下来要做的一点也比现在已经做的事要少,后续就留给读者自己去探寻吧,我此后我会间断的更新此系列的文章,以尽量使整个过程完整而详尽。

本系列的物联网文章就暂时告一段落了吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容