多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# PM空气质量检测仪 * * * 在日常工作中,我们经常会对接一些奇奇怪怪的小设备,他们既没采用大型设备厂家的通讯协议,也没有通用的统计数值的方法。这时候就得需要用到我们的自定义逻辑代码去实现这些设备数据的采集以及数值转换,今天我们将通过济南顾问通讯技术有限公司生产的pm2.5/pm10激光传感器(USB版本)讲解自定义逻辑代码实现数据采集 ## 一、产品 今天需要用到的产品有: 1. 海创Box智能采集网关 2. pm2.5/pm10激光传感器(USB版本)[使用说明书](https://pan.baidu.com/s/13l0Bz0D9XoKiLiBaWzF5QA) ![](https://box.kancloud.cn/f59a000d9e4ea255b3b4468a20cee396_474x635.png) 如上图所示将pm2.5/pm10激光传感器(USB版本)的USB插口连接到海创Box智能采集网关的USB口上 ## 二、通讯协议 通过厂家给的说明书里的通讯协议我们可得知设备默认参数 * 串口通讯协议:9600 8N1(速率 9600, 数据位 8,校验位无,停止位 1) * 串口自动上报通讯周期:1+0.5 秒 * 数据帧(10 字节):报文头+指令号+数据(6 字节)+校验和+报文尾 示例报文为 ~~~ AA C0 71 01 CA 01 B9 93 89 AB ~~~ 具体含义: * AA----报文头 * C0----指令号,客户开发产品时,看到接收到有 CO,即表示是由 PM2.5 传感器输出的信号 * 71----PM2.5 低字节 * 01----PM2.5 高字节 * CA----PM10 低字 * 01----PM10 高字节 * B9----传感器的 ID * 93----传感器的 ID * 89----校验和,即 71+01+CA+01+B9+93=289 即 0x0289 ,这里我们舍弃了高字节 02,只保留了低字节89 * AB----报文尾 因为输出的是,16 进制数据,请转换成 10 进制数进行计算。 PM2.5 值的计算:`71 01` * 低字节 71: 7\*16+1=113 * 高字节 01: 0\*16+1=1 ((PM2.5 高字节\*256) + PM2.5 低字节)/10 (1\*256+113)/10=36.9ug/m3 PM10 值的计算:CA 01 * 低字节 CA: C\*16+A=202 * 高字节 01: 0\*16+1=1 ((PM10 高字节\*256) + PM10 低字节)/10 (1\*256+202)/10=45.8ug/m3 校验和: * 89 71+01+CA+01+B9+93=289,舍弃高字节 02,留低字节 89。 ## 三、节点流 在桌面打开**海创Box智能采集网关**可视化开发平台进入可视化编辑器。本次教程需要用到如下节点,在左侧**节点栏**中拖拽出使用 1. `serial`在**输入**栏目,用于读取串口二进制流 ![](http://am-img.gkiiot.com/uploads/serial.png) 2. `延迟`在**功能**栏目,用于延迟消息速率 ![](http://am-img.gkiiot.com/uploads/%E5%BB%B6%E8%BF%9F.png) 3. `function`在**功能**栏目,用于配置逻辑代码 ![](http://am-img.gkiiot.com/uploads/function.png) 4. `调试`在**输出**栏目,用于调试输出 ![](http://am-img.gkiiot.com/uploads/%E8%B0%83%E8%AF%95.png) 配置界面详情(快速复用请导航到文章末端) ![](https://box.kancloud.cn/3c11d35e6c46c40d36834e18aba9612a_1890x897.png) 接下来我们来配置如上图的节点流,首先将左侧节点栏的`serial`、`延迟`、`function`、`调试`节点分别拖拽到工作区,再点击相应的流节点的端口依次按配置界面所示连接起来,再双击相应流节点进入配置界面配置相应属性 * `serial`是用于读取串口输出的节点工具。该工具需要配置串口名称(`Serial Port`)、波特率(`Baud Rate`)`9600`、数据位(`Data Bits`)`8`、校验位(`Parity`)`Node`、停止位(`Stop Bits`)`1`。该USB转串口程序在奥迈智能网关中使用不需要下载任何驱动程序,即插即用。在插入智能网关的USB口后,会在系统的驱动目录下`/dev`生成一个串口文件`ttyUSB0`(没有其他USB转串口的工具插入下),我们需要在配置串口名称(`Serial Port`)配置`/dev/ttyUSB0`。Windows下需要安装[官方驱动](http://www.gu-wen.com/down/GW-usb_to_uart_drive.rar)。由于`serial in`读取串口数据是连续的,所以我们需要将Split input(拆分输入)配置成after a silence of(在没有新的二进制流输出后),默认时间填写50ms。详情配置如下图 ![](http://am-img.gkiiot.com/img/1544839945401image.png)![](http://am-img.gkiiot.com/img/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20190122100828.png) * `延迟`用于延迟serial节点输出的信息速率。由于该设备自定义速率为0.5秒/信息,我们实际采集的时候用不到这么频繁的数据,所以我们选择限制消息速率,如下配置限制消息1分钟/条 ![](http://am-img.gkiiot.com/img/1547603774441image.png) * `function`是用于编写自定义代码的节点工具,该控件支持nodejs语法,可以实现您所有的业务逻辑。根据该设备的通讯协议,我们将分为两个逻辑块,分别存放在两个**function**里面,用于数据校验以及取得我们需要的数据。需要注意的是在我们的框架内定义一般流的数据向下流动时都将数据存入msg.payload这个对象中。关于更多Buffer类的更多使用请参考[Buffer](http://www.runoob.com/nodejs/nodejs-buffer.html)。详情配置如下图 ![](http://am-img.gkiiot.com/img/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20190122093040.png)![](http://am-img.gkiiot.com/img/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20190122093107.png) CRC校验代码如下: ~~~ var count=0; let buf=0; //以0开始取第2位字节点到第7字节的数据 for(var i=2;i<msg.payload.length-2;i++) { buf+=msg.payload.readUInt8(i); } //创建一个2字节的buff将buf转成16进制并取低字节 var bufTemp=Buffer.alloc(2); bufTemp.writeUInt16BE(buf); //删除高字节 bufTemp=bufTemp.slice(bufTemp.length-1); if(bufTemp.compare(msg.payload.slice(8,9))==0) { msg._crc=true; return msg } else { msg._crc=false; node.error('数据位校验失败',msg); } ~~~ pm2.5/pm10取值代码如下: ~~~ let PM25Value= msg.payload.readUInt16LE(2)/10; let PM10= msg.payload.readUInt16LE(4)/10; msg.payload={PM25Value:PM25Value,PM10:PM10} return msg; ~~~ * `调试`用于界面调试输出结果。我们需要将上面的程序输出结果打印在界面右侧的调试窗口,按配置界面图链接即可 ## 四、部署调试 经过上面所有步骤后,即可部署程序,部署后可以在右侧的调试窗口看到输出,如下图 ![](https://box.kancloud.cn/dbd08f1a4bb44a6878705596378aab4d_1894x924.png) ## 五、示例 以上教程可以通过拷贝下面代码实现快速复用,在新建的流程中点击界面右侧**菜单栏**\-**导入**\-**剪贴板**,在文本框中粘贴下面代码后点击确定,即可快速复用 ~~~ [ { "id": "e2618b1e.c557f8", "type": "delay", "z": "7fce5a14.7da9c4", "name": "", "pauseType": "rate", "timeout": "5", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "minute", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": true, "x": 335, "y": 260, "wires": [ [ "be9d7a30.0c43c8" ] ] }, { "id": "47ea0be9.2ffb14", "type": "serial in", "z": "7fce5a14.7da9c4", "name": "", "serial": "f8003df1.9aa5c", "x": 215, "y": 260, "wires": [ [ "e2618b1e.c557f8" ] ] }, { "id": "be9d7a30.0c43c8", "type": "function", "z": "7fce5a14.7da9c4", "name": "CRC检验", "func": "var count=0;\nlet buf=0;\n//以0开始取第2位字节点到第7字节的数据\nfor(var i=2;i<msg.payload.length-2;i++)\n{\n\tbuf+=msg.payload.readUInt8(i);\n}\n//创建一个2字节的buff将buf转成16进制并取低字节\nvar bufTemp=Buffer.alloc(2);\nbufTemp.writeUInt16BE(buf);\n//删除高字节\nbufTemp=bufTemp.slice(bufTemp.length-1);\nif(bufTemp.compare(msg.payload.slice(8,9))==0)\n{\n msg._crc=true;\n return msg\n}\nelse\n{\n msg._crc=false;\n node.error('数据位校验失败',msg);\n}\n", "outputs": 1, "noerr": 0, "x": 475, "y": 260, "wires": [ [ "5625f516.97a3dc" ] ] }, { "id": "5625f516.97a3dc", "type": "function", "z": "7fce5a14.7da9c4", "name": "pm2.5/pm10取值", "func": "let PM25Value= msg.payload.readUInt16LE(2)/10;\nlet PM10= msg.payload.readUInt16LE(4)/10;\nmsg.payload={PM25Value:PM25Value,PM10:PM10}\nreturn msg;", "outputs": 1, "noerr": 0, "x": 615, "y": 260, "wires": [ [ "e65320dc.562b5" ] ] }, { "id": "e65320dc.562b5", "type": "debug", "z": "7fce5a14.7da9c4", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "x": 735, "y": 260, "wires": [] }, { "id": "f8003df1.9aa5c", "type": "serial-port", "z": "", "serialport": "/dev/ttyUSB0", "serialbaud": "9600", "databits": "8", "parity": "none", "stopbits": "1", "newline": "50", "bin": "bin", "out": "interbyte", "addchar": false, "responsetimeout": "10000" } ] ~~~