Realize the host computer function of wireless sensor network based on the serialport module in node.js

Half a month ago, I encountered many difficulties in the realization of the upper computer in the wireless sensor network class. I wrote this article to give some help to friends in need. Welcome to private message to discuss


foreword

本文所要实现的功能以及使用到的技术栈

功能:根据课设要求,当协调器收到信息时,我要解析收到的数据,动态显示出拓扑图,当点击拓扑图节点时,显示该节点的地址以及采集的湿度和温度
技术栈node.js解决串口通信问题,串口收到的数据通过express框架上传到服务器,前端通过axios请求服务器拿取数据,前端拿到数据后通过echarts渲染拓扑图,前后端通信存在跨域问题,利用代理转发解决
所遇困难
1. 接收串口数据断断续续,零零散散(后端获取串口数据)
2.访问数据存在跨域问题(前端获取后端数据)
3.echarts拓扑图配置项陌生(数据可视化)
4.下行数据时,需要从前端向后端传输用户指令,不方便利用Post,这里使用Get查询字符串传参
5.下行数据,无法像串口助手一样按十六进制给协调器发指令,这里要对数据进行一个特殊处理


以下是本篇文章正文内容,若要源码参考,私信作者

1. The serialport module in node.js

serialport is used to solve the serial communication, it can receive the data from the serial port, and it can also realize the downlink. First, let’s talk about how to install it.

npm install serialport@10.4.0

Note here that there are many versions of the serialport package, and the usage of each version is different. Here is the version number
"serialport" used in this article: "^10.4.0"
After installation, it can be used now, and the code used is given

// 引入串口通信的模块
const {
    
     SerialPort } = require('serialport');

//打开COM5串口 串口号 波特率 这些都是设置好的 可以查看
const serialport = new SerialPort({
    
     path: 'COM5', baudRate: 115200,
    dataBits: 8, }, (err) => {
    
    
    if (err) {
    
    
        console.log('端口打开失败!');
        return;
    }
    console.log('端口打开成功!',serialport.isOpen);
});

// 监听串口 只要串口有数据发送过来 都会执行回调函数
serialport.on('data',(data) => {
    
    
     //接收串口传递来的数据
    console.log(data.toString());
})


//错误监听
serialport.on('error',function (error) {
    
    
    console.log('error: '+error)
})

//写入  实现下行数据
 serialport.write('')

写到这就已经可以监听串口了,这里讲一个本人遇到的一个棘手的问题,相信大家都会碰到
就是串口传递过来的数据,会被一段一段的切分开,这样就会很难处理数据,例如: 原数据为01 3D 47 00 00 12 A4 32 33
可能我第一次接收到的就为 01 3 剩下的D 47 00 00 12 A4 32 33 会被遗留到第二次发送,这样数据就会很乱,很难处理
这里给出解决思路:可以定义一个全局数组,把每次来的数据全部压入这个数组中,然后把数组拼接,按照原始数据长度进行切割,切割出来的数据赋值给一个变量,剩下的数据接着放置在数组中,每次来的数据都压入数组内,继续切割,继续压入…循环操作
拿到数据以后进行切割,比如01 3D 47 00 00 12 A4 32 33,01代表类型,47 3D 代表路由器地址,00 00代表协调器地址, A4 12代表终端地址,32、33分别代表终端采集到的温度和湿度,所以01这条数据类型就代表,温湿度数据通过终端发送给了路由器,路由器转发给协调器
我要对数据进行切割,然后给不同的变量赋值,将这些变量通过express框架发送给前端

Two, express framework

//导入express
const express = require('express');

//创建express实例对象
const app = express()

//编写接口
app.get('/zigbee/upo', (req, res) => {
    
    
     res.send({
    
    
        'nodes' :  [router,terminal1] ,
        'lines' : lines1
    })}
    )
    
//启动服务
app.listen('8888', () => {
    
    
    console.log('服务器启动成功!');
})

The things written on the back end are basically done, and the rest is the front end to render the data. The difficulty is the topology map . The topology map is based on a big guy on csdn

3. echarts realizes the topology map

All the codes in app.main are posted here, including axios requesting data, rendering the topology map, and registering click events for the topology map
Version number: "echarts": "^5.4.2",

<template>
  <div align="center" class="echart-block">
    <el-row style="padding: 0 1000px 0 1000px">
      <el-button @click="update1" size="medium" type="primary" round
        >终端1-路由-协调器</el-button
      >
      <el-button @click="update2" type="success" round>终端2-协调器</el-button>
    </el-row>

    <div style="height: 100%" ref="graphchart"></div>
    <el-input
      type="textarea"
      class="talk-textarea"
      v-model="message"
      @keyup.enter.native="enterFun"
    >
    </el-input>
  </div>
</template>
<script>
import * as echarts from 'echarts'
import axios from 'axios'
export default {
    
    
  data () {
    
    
    return {
    
    
      message: '',
      res: '',
      option: {
    
    },
      echart: null,
      nodes: [
        // symbol 默认为圆形 diamond 菱形 triangle 三角形
        {
    
    
          name: '协调器',
          value: [0, 0],
          symbol: 'circle'
        }
      ],
      lines: []
    }
  },
  components: {
    
    },
  mounted () {
    
    
    this.drawChart()
  },
  methods: {
    
    
    async enterFun () {
    
    
      await axios.get(`http://localhost:8081/zigbee/message?message=${
      
      this.message}`)
    },
    async update1 () {
    
    
      const res = await axios.get('http://localhost:8081/zigbee/upo')
      console.log(res)
      if (res.data.lines.length === 0) {
    
    
        return
      }
      for (let i = 0; i < 2; i++) {
    
    
        this.nodes.push(res.data.nodes[i])
      }
      for (let i = 0; i < 4; i++) {
    
    
        this.lines.push(res.data.lines[i])
      }
      this.echart.clear()
      this.echart.setOption(this.option)
      console.log(res.data)
    },
    async update2 () {
    
    
      const res2 = await axios.get('http://localhost:8081/zigbee/upt')
      if (res2.data.lines.length === 0) {
    
    
        return
      }

      this.nodes.push(res2.data.nodes[0])
      for (let i = 0; i < 2; i++) {
    
    
        this.lines.push(res2.data.lines[i])
      }
      this.echart.clear()
      this.echart.setOption(this.option)
      console.log(res2.data)
    },
    drawChart () {
    
    
      this.echart = echarts.init(this.$refs.graphchart)
      this.option = {
    
    
        tooltip: {
    
     trigger: '1' },
        legend: {
    
    
          textStyle: {
    
     fontSize: 20 },
          top: '5%',
          bottom: '30%',
          left: 'center',
          itemWidth: 20,
          itemHeight: 20,
          data: [
            {
    
     icon: 'circle', name: '协调器' },
            {
    
     icon: 'diamond', name: '终端' },
            {
    
     icon: 'triangle', name: '路由器' }
          ]
        },
        title: {
    
    
          text: '无线传感网拓扑图',
          textStyle: {
    
    
            fontSize: 70
          }
        },
        polar: {
    
    },
        // 极坐标系的径向轴
        radiusAxis: {
    
    
          show: false
        },
        // 极坐标系的角度轴
        angleAxis: {
    
    
          type: 'value',
          min: 0,
          max: 360,
          show: false
        },
        series: [
          {
    
    
            name: '终端',
            type: 'graph',
            coordinateSystem: 'polar',
            label: {
    
    
              show: true,
              position: 'inside',
              fontSize: 14
            },

            // layout:'circular',
            symbol: 'circle',
            symbolSize: 50,
            symbolPosition: 'start',
            nodes: this.nodes
            // links: this.links
          },
          {
    
    
            name: '路由器',
            type: 'lines',
            coordinateSystem: 'polar',
            zlevel: 1,
            symbol: ['none', 'arrow'],
            symbolSize: 10,
            polyline: true,
            effect: {
    
    
              show: true,
              period: 4,
              smooth: true,
              trailLength: 0.2,
              symbol: 'arrow',
              // symbol: 'circle',
              color: 'rgba(55,155,255,0.5)',
              symbolSize: 20,
              loop: true
            },
            lineStyle: {
    
    
              normal: {
    
    
                color: '#1DE9B6',
                width: 3, // 线条宽度
                opacity: 0.6, // 尾迹线条透明度
                curveness: 0.3 // 尾迹线条曲直度
              }
            },
            data: this.lines
          },
          {
    
    
            name: '协调器',
            type: 'graph',
            coordinateSystem: 'polar',
            label: {
    
    
              show: true,
              position: 'inside',
              fontSize: 14
            }, // layout:'circular', symbol: 'circle',
            symbolSize: 50,
            symbolPosition: 'start'
          }
        ]
      }
      this.echart.setOption(this.option)
      this.echart.on('click', async function (params) {
    
    
        console.log('myChart----click---:', params.name)
        const res = await axios.get('http://localhost:8081/zigbee/data')
        console.log(res.data)
        switch (params.name) {
    
    
          case '路由器':
            alert('路由器地址为' + res.data.address3)
            break

          case '终端1':
            alert(
              '终端2地址为' +
                res.data.address1 +
                '\n' +
                '温度为' +
                res.data.tem1 +
                '\n' +
                '湿度为' +
                res.data.humidity1 +
                '\n' +
                '历史温度为' +
                res.data.tem1 +
                '\n' +
                '历史湿度为' +
                res.data.humidity1
            )

            break
          case '协调器':
            alert('协调器地址为' + '00 00')
            break
          case '终端2':
            alert(
              '终端2地址为' +
                res.data.address2 +
                '\n' +
                '温度为' +
                res.data.tem2 +
                '\n' +
                '湿度为' +
                res.data.humidity2 +
                '\n' +
                '历史温度为' +
                res.data.historyT2 +
                '\n' +
                '历史湿度为' +
                res.data.historyH2
            )
            break
        }
      })
    }
  }
}
</script>

<style scoped>
.echart-block {
    
    
  height: 150vh;
}
</style>

4. Realize downlink data

Implementation ideas: When the user presses Enter in the input box, the data entered by the user is passed to the backend, and the backend sends the data to the coordinator. It is easy to say, but the difficulty lies in the format of the data. How to send it to the coordinator in hexadecimal like a serial port debugging assistant ?

//前端发送数据给后端  由于express框架 不方便利用post方式传值,所以这里利用查询字符串的方式传递参数
async enterFun () {
    
    
      await axios.get(`http://localhost:8081/zigbee/message?message=${
      
      this.message}`)
    }

// 后端接收数据 并且处理数据 数据处理好后 通过write方法 发送给协调器
app.get('/zigbee/message', (req, res) => {
    
    
	//  拿get方式传递过来的参数
    let str = req.query.message
    strs = str.split(" ");//将一个十六进制报文转为字符数组
	for(let i = 0;i<strs.length;i++){
    
    
	
	  strs[i] = "0x"+strs[i];
	
	}//每个字符加上0x
	
	let buffer = Buffer.from(strs);//将数组放到buffer
		// 发送数据给协调器
	    serialport.write(buffer)
})

5. Results display

When a device joins the wireless sensor network, the topology map is updated.
insert image description here
When the user clicks on the node of the topology map, the relevant data is displayed.
insert image description here

insert image description here

Summarize

The part I am responsible for in this lesson is to use the front-end and back-end to realize the upper computer , and the implementation ideas and logic have been fully explained.
Because of the design of various knowledge points in this article, the author has written all the difficulties I encountered here. If you have any questions, please feel free to communicate with me!

Guess you like

Origin blog.csdn.net/m0_57524265/article/details/131478575