查看: 1237|回复: 2

[经验] ZS原创-使用ESP8266和Arduino采集DHT11传感数据

[复制链接]

该用户从未签到

发表于 2018-6-20 11:43 | 显示全部楼层 |阅读模式
本帖最后由 zshawk1982 于 2018-6-20 12:12 编辑

首先我说一下我做的功能是:利用DHT11采集数据,然后经由Arduino传递给ESP8266向服务器端发送WebSocket请求
同时将采集的数据在服务器端以C3.js的折线图的形式显示。

这里提一点,在论坛上看了大量高手的esp8266的各种操作,绝大多数都是使用AT命令来操纵ESP8266进行wifi传输控制,个人觉得这种方式确实比较直接,但是
麻烦之处在于必须要去了解AT命令,这里ESP8266的操纵不在使用AT命令,而是将代码通过ArduinoIDE烧录进ESP8266中来控制ESP8266,ESP8266只负责做到将传递来的数据
通过webSocket,注意是WebSocket,而非传统的TCP协议

用到的硬件设备:
1.Arduino UNO一块
2.ESP8266一块
3.USB转TTL的CP2102一块
4.DHT11温湿度传感器一块

用到的软件:
arduino端的代码
dht11.h的库
ESP8266WiFi.h的库
WebSocketClient.h的库
服务端的代码:
基于nodejs的Koa2代码
C3.js代码
mongodb数据库


效果:
psb.jpg psb (1).jpg

下面开始贴代码:
1.DHT11的代码:(DHT11的代码会烧入Arduino,注意这里定义了软串口(10,11),用于连接ESP8266的TX和RX)
#include <SoftwareSerial.h>
dht11 DHT11;
#define DHT11PIN 3
SoftwareSerial mySerial(10, 11); // 定义软串口RX, TX
void setup()
{
  Serial.begin(115200);//注意我这里Arduino串口使用的是115200
  Serial.println("DHT11 TEST PROGRAM ");
  Serial.print("LIBRARY VERSION: ");
  Serial.println(DHT11LIB_VERSION);
  Serial.println();
  mySerial.begin(115200);//注意我这里软串口使用的也是115200
  mySerial.println("softwareSerial begin");
}

void loop()
{
  Serial.println("\n");
//下面这一段校验代码个人认为已无用处,貌似新的DHT11模块一直会报Checksum error错误
/**********校验代码***************/
  int chk = DHT11.read(DHT11PIN);
  Serial.print("Read sensor: ");
  switch (chk)
  {
    case DHTLIB_OK:
                Serial.println("OK");
                break;
    case DHTLIB_ERROR_CHECKSUM:
                Serial.println("Checksum error");
                break;
    case DHTLIB_ERROR_TIMEOUT:
                Serial.println("Time out error");
                break;
    default:
                Serial.println("Unknown error");
                break;
  }
/**********校验代码***************/
   float tempH = (float)DHT11.humidity;//转换成小数
   float tempT = (float)DHT11.temperature;
   char buffer[10];
    String temph = dtostrf(tempH, 4, 1, buffer); //转换成字符串
    String tempt = dtostrf(tempT, 4, 1, buffer);   
    updateTemp(temph,tempt);
/****输出在COM串口显示(调试用)********/
  Serial.print("Humidity (%): ");
  Serial.println(DHT11.humidity);

  Serial.print("Temperature (oC): ");
  Serial.println(DHT11.temperature);/****输出在COM串口显示(调试用)********/
    delay(6000);
}

void updateTemp(String temph,String tempt)         
{
//将湿度和温度通过软串口输入出到ESP8266,温湿度之间用逗号隔开
  mySerial.println(temph+","+tempt);
}

2.烧入ESP8266的websocket客户端代码:
贴代码前我先说一下烧录,个人觉得烧录应该算是比较容易的,只要线接对,本人烧录使用的开发板是Generic ESP8266 Module
这里安装额外的开发板比较麻烦,论坛上都是采用附加开发板网址的方式,也就是从github上下载ESP8266的开发板,本人第一次搞这个也折腾了半天,因为github经常连不上,所以一度很郁闷,最后参考了一个帖子,发现官网也给出了其他的方式下载开发板模块,最好用的其实是采用了直接下载的方式,这里贴一个网上的解决方案:https://blog.csdn.net/fengyu09/article/details/51820624,这哥们人很好的给出了下载链接

下面入正题,贴上代码:
#include <ESP8266WiFi.h>
#include <WebSocketClient.h>
const char* ssid     = "xxxx_zs";//wifi的名称
const char* password = "xxxx";//wifi密码
char path[] = "/temp";//我网站上的路径
char host[] = "192.168.0.100";//我服务器的IP地址

WebSocketClient webSocketClient;
WiFiClient client;

void setup() {
/****USB转TTL测试用****/
  Serial.begin(115200);
  delay(10);

  // We start by connecting to a WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  /****USB转TTL测试用****/

  WiFi.begin(ssid, password);//链接wifi

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  /****USB转TTL测试用****/
  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  /****USB转TTL测试用****/
  delay(5000);


  // 链接到websocket服务器,因为我nodejs的koa2使用的是3000端口,这里可以根据你实际的服务器端口来,比如tomcat的就是8080
  if (client.connect(host, 3000)) {
    Serial.println("Connected");
  } else {
    Serial.println("Connection failed.");
    while(1) {
      // Hang on failure
    }
  }

  // 与服务器握手,因为websocket的规则
  webSocketClient.path = path;
  webSocketClient.host = host;
  if (webSocketClient.handshake(client)) {
    Serial.println("Handshake successful");
  } else {
    Serial.println("Handshake failed.");
    while(1) {
      // Hang on failure
    }  
  }
}

void loop() {
  String data;
  if (client.connected()) {//客户端链接上服务器
    if (Serial.available())
    {
      String s = Serial.readString();//从arduino软串口读取数据
      webSocketClient.sendData(s);//将读取的数据发送到服务器
    }
    webSocketClient.getData(data);//接收来自服务器的数据,websocket是全双工通信,可以很好的来回交流
    if (data.length() > 0) {
      Serial.print("Received data: ");
      Serial.println(data);
    }
  } else {
    Serial.println("Client disconnected.");
  }
  // wait to fully let the client disconnect
  delay(6000);
}

这里使用的WebSocketClient.h库是github上提供的一个websocket库,很好使
附上地址:
https://github.com/larkin/ESP8266-Websocket

好了,上面都是客户端的代码,下面是基于nodejs的服务端代码
首先是服务器端的代码:该代码基于Koa2,而非传统的Express
const Koa = require('koa');
var koa = new Koa();
const router = require('koa-router')();//使用了koa2的路由组件
const route = require('koa-route');
const websockify = require('koa-websocket');//使用了koa-websocket库
const app = websockify(koa);
const DHT11Model = require('./db');//mogodb的数据库模型
const static = require('koa-static');//koa-static解决静态文件问题
const render = require('koa-art-template');//使用了国人的art-template模板引擎
const path=require('path');
const moment = require('moment');//moment库用于转换时间

render(koa, {
    root: path.join(__dirname, 'views'),
    extname: '.art',
    debug: process.env.NODE_ENV !== 'production'
});

app.use(static(__dirname+'/public'));
//esp8266作为客户端向服务器的/temp发送websocket信息
app.ws.use(route.all('/temp', function (ctx) {
    ctx.websocket.on('message', function (message) {
        let array = message.split(",");//将接受到的逗号隔开温湿度信息提取出来
        let temph = array[0];
        let tempt = array[1];
        ctx.websocket.send('server receive temp success');//向服务器发送接受成功的消息
       //将温湿度信息存入mongodb数据库,这里之所以选用mongodb,是因为传统的sql数据库无法做到高效的实时存储
        var data = new DHT11Model({'humidity':temph,'temperature':tempt,'time':moment(new Date()).format('YYYY-MM-DD h:mm:ss')});
        data.save(function (err) {
            if(err){
                console.log("error happen");
            }else{
                console.log("save success");
            }
        })
    })
}));
//浏览器的图表作为客户端向服务器的/temp2发送websocket信息
app.ws.use(route.all('/temp2', async function (ctx) {
    ctx.websocket.on('message', async function (message) {
        console.log("向网页客户端发送");
        //查询数据库中最新的一条数据发送到网页上的C3图表组件
        var result = await DHT11Model.find().select('-_id -__v').sort({'_id':-1})
            .limit(1).exec();
        console.log(result);
        ctx.websocket.send(JSON.stringify(result));
    })
}));
//显示展现图表的网页
router.get('/chart2',async (ctx)=>{
    await ctx.render('chart2');
});
app.use(router.routes());
app.listen(3000);//服务器监听在3000端口


然后是网页上的图表组件的代码,该网页是一个客户端websocket:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>websocket</title>
<!--引入c3.js和c3.css以及c3所依赖的D3.js-->   
    <link href="css/c3.min.css" rel="stylesheet" type="text/css">
    <script src="js/d3.v3.min.js"></script>
    <script src="js/c3.min.js"></script>
</head>
<body>
<div id="chart"></div>
</body>
<script type="text/javascript">
    //调用C3的相应api创建C3图表
    var chart = c3.generate({
        bindto: '#chart',
        data: {
            x:"time",
            xFormat:"%Y-%m-%d %H:%M:%S",
            json:[],
            keys: {
                x: "time",
                value: ["humidity", "temperature"]
            }
        },
    axis: {
        x: {
            type: "timeseries",
                tick: {format: "%H时%M分%S秒"}
        },
        y: {
            label: {
                text: "温湿度",
                    position: "outer-middle"
            }
        }
    }
    });
    //判断浏览器是否支持websocket
    var CreateWebSocket = (function () {
        return function (urlValue) {
            if (window.WebSocket) return new WebSocket(urlValue);
            if (window.MozWebSocket) return new MozWebSocket(urlValue);
            return false;
        }
    })();
    // 实例化websoscket,向服务器发送请求,用于获取存储在数据库的温湿度信息
    var webSocket = CreateWebSocket("ws://127.0.0.1:3000/temp2");
    webSocket.onopen = function (evt) {
        // 一旦连接成功,就发送成功信息
        setInterval(function () {
            webSocket.send("第一条数据");
        },5000)
    };
    webSocket.onmessage = function (evt) {
        //console.log("server said" + JSON.parse(evt.data));
        //将最新的温湿度添加到图表中
        chart.flow({
            json:JSON.parse(evt.data),
            keys: {
                x: "time",
                value: ["humidity", "temperature"]
            },
            length:0,
            duration:3000
        });

    };
    // 关闭连接
    webSocket.onclose = function (evt) {
        console.log("Connection closed.")
    };
</script>
</html>





该用户从未签到

发表于 2018-6-21 13:19 | 显示全部楼层
666,抢个沙发

该用户从未签到

发表于 2018-6-26 13:40 | 显示全部楼层
学习了 谢谢分享
您需要登录后才可以回帖 登录 | 立即注册  

本版积分规则

热门推荐

Uno A4988驱动42步进电机若干问题?求高手进
Uno A4988驱动42步进电机
上代码: void setup() { pinMode(8, OUTPUT); //8引脚为使能引脚
arduino可以控制cw250驱动器吗
arduino可以控制cw250驱动
我想用arduino来控制cw250步进电机驱动器,实现步进电机的运转,求大神给解释一下如何
Arduino的一个小问题。
Arduino的一个小问题。
像这种,第一个划线处定义了变量tepTimer ,,后面根本没有赋初值就直接用了,,为什么
原创 drawbot平面关节机械臂 教程直播贴
原创 drawbot平面关节机械
这个项目上个月就在做了,结构和代码反反复复改了多次,加上自己又太忙,一直没来得及
DS18B20代码编译出错
DS18B20代码编译出错
这个是DallsTemperature的示例,但是报了一个错,不知是为何
Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   ( 蜀ICP备14017632号-3 )
快速回复 返回顶部 返回列表