物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议

目录

一、想法及需求

1.1最初设想

1.2需求分析

1.3方案设计

二、硬件

2.1器材选型

2.2原理图解释

2.3PCB绘制

2.4焊接及成品

三、软件

3.1NETTY自定义协议的TCP服务器

3.1.1使用原因

3.1.2制定协议

3.1.3编写解码器

3.1.4编写服务器

3.1.5部署服务器(将netty服务器部署到阿里云服务器)

3.1.6 netty连接数据库

3.2嵌入式代码

3.3基于Thymeleaf的前端展示


一、想法及需求

1.1最初设想

题源于物联网综合设计课设,要求做一套简易的物联网综合系统。由于宿舍楼下就是操场,很经常举办一些类似运动会、草地弹唱会的活动,影响到同学们的休息,所以想做一套校园噪声监测系统,这篇文章记录了制作过程中的心路历程。

首先来看看最后的效果,大屏展示出各个节点的实时状态以及位置,当出现噪声时进行报警并发送短信到工作人员处,由于短信需要费用,这里使用邮件代替:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第1张图片

 物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第2张图片

1.2需求分析

校园噪声检测系统的用户是保卫科管理人员,对象的需求大致为以下三点:

及时提示有噪声出现并且显示噪声出现的地点

②及时通知工作人员,保证消息传送的及时性

③记录噪声出现的地点、时间以及大小,归纳噪声规律,从而更好地解决噪声问题

1.3方案设计

在校园的各个容易产生噪声的地方安装传感器节点,但节点之间不进行互相通信(不构成WSN无线传感网)。每个节点由四个单元组成,分别是感知单元、处理单元、通信单元和能量供给单元,最后所有节点的数据将在大屏上进行展示(如下图所示)。

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第3张图片

对于数据的传输,分为以下几个部分:传感器节点发送TCP数据包到使用NETTY搭建的数据服务器,数据服务器解析数据包为一个个数据对象,使用JDBC存储到数据库中,而数据服务器一样可以通过JDBC来获取数据库中的数据,以进行数据的处理及展示。

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第4张图片

 本篇文章将分为以下几个部分进行讲解:硬件、软件以及成功展示。

二、硬件

2.1器材选型

首先是关于器材的选型,在第一部分已经说过,每个传感器节点由四个单元组成,分别是感知单元、处理单元、通信单元和能量供给单元。感知单元最后选型为LM386声音传感器,它实际上是一个AD转化器,将噪声模拟量转化为电压的大小;处理单元以及能量供给单元最后选用STM32F103C8T6的最小系统板,不论从IO输出,传输效率,处理效率上,STM32相对于C51而言都占了较大的优势;通信模块选用了ESP8266进行wifi传输,由于节点之间距离太远,并不进行相互通信,而是各自通过wifi连上互联网,发送TCP数据包到数据服务器。各个模块的图片如下:

STM32F103C8T6最小系统板:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第5张图片

 ESP8266:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第6张图片

USB转串口模块CP2102: 

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第7张图片

LM386声音传感器模块:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第8张图片

OLED显示屏模块:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第9张图片

2.2原理图解释

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第10张图片

按从左到右,从上至下的顺序进行说明:

首先是wifi模块,这里有一个需要注意的地方,wifi模块必须要与串口相连,通过串口来进行收发数据。对于SMT32F103C8T6而言,它的串口1为PA9和PA10,而串口2为PA2和PA3,这里注意ESP8266同串口1连接好像容易出问题,所以选择串口2,接着就是wifi模块的RXD要连在STM32的TXD上,TXD连在RXD上,这是由于它的工作机制:STM32发送AT指令到wifi模块处,wifi模块识别指令并进行数据的转发,通过互联网传到数据服务器处。

接着是声音传感器模块,本质上是一个AD转化起,将模拟量噪声转化为电压的大小进行读取,这里注意,这个传感器并没有给出分贝与电压之间的转化公式,需要自己用声级计进行测试,这里不推荐使用这个传感器,最好去找一些精确度较高的传感器模块。

接着是LED模块,当测量噪声达到阈值时,LED发光进行报警。

USB转串口模块方便于同电脑间进行信息的传输以获取芯片实时的状态,方便调试。

OLED模块使用IIC接口,将7针的SPI接口缩小到了4针,合理利用了IO口的资源。

BUTTON模块并没有用到,可以进行复位或者调解参数使用,但是注意:这里的BUTTON接法有误,由于没有用到就没有进行修改,正确的接法如下:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第11张图片

STM32最小系统板:注意绘制封装时将每一个引脚对应上,以及每一个模块的尺寸都要画正确。

2.3PCB绘制

根据上图画好电路图后,转为PCB并进行布局、布线以及铺铜,布局及布线要进行合理规划,如果STM32的引脚的接入端被大致均匀的分到了左右两边,一般建议将STM32放在中间,就可以将连线错开,减少过孔的数量。在板的四角还可以进行开孔,使用M3六角铜柱进行支撑。

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第12张图片

2.4焊接及成品

PCB的绘制只需最简单的对排母的绘制,这里可以进行一些改进:比如OLED屏是很小的排母支撑起较大的屏幕,重心不稳且容易断,可以在它的附近再焊上一个排母,起支撑作用。CP2102由于没有用到就没有进行焊接。

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第13张图片

三、软件

3.1NETTY自定义协议的TCP服务器

3.1.1使用原因

为什么要使用自定义的协议呢,原因有三:

①常规的物联网系统是连接到大公司搭好的平台上,如使用mqtt连接到中国移动的onenet平台,但缺点就是公司可以掌握你的所有数据,以及有一种受制于人的感觉。

②自定义协议方便扩展其余节点,只需要所有节点统一协议即可。

③想学一些新东西,比如这里用到的netty。

3.1.2制定协议

由于本项目只需要传输声音值的大小,传输的内容简单,也不需要定义非常复杂的协议,协议结构如下:

魔数 传感器节点序号 数据部分的长度 数据

其中魔数用来第一时间判断出是否为无效包,是收发双方提前约定好的数据,这里我定义为‘0717’四个字节;由于传感器得到的数据时确定三位的,所以可以确定数据部分的长度,节点每次发送时只需修改对应的数据部分以及节点序号即可。以下是iot_protocol 协议类:

package com.zhiqi.IOT.protocol;

import java.util.Arrays;

/***
 * 自定义的协议如下
 * | ---- | -------------- | -------------- | ---- |
 * | 魔数 | 传感器节点序号 | 数据部分的长度 | 数据 |
 * | ---- | -------------- | -------------- | ---- |
 */

public class iot_protocol {
    /**
     * 魔数 4个字节
     */
    private int head_data ;
    /**
     * 传感器节点序号 1个字节
     */
    private int id ;
    /**
     * 数据的长度 3个字节
     */
    private int contentLength;
    /**
     * 数据的内容
     */
    private String content;

    @Override
    public String toString() {
        return "iot_protocol{" +
                "head_data=" + head_data +
                ", id=" + id +
                ", contentLength=" + contentLength +
                ", content='" + content + '\'' +
                '}';
    }

    public int getHead_data() {
        return head_data;
    }

    public void setHead_data(int head_data) {
        this.head_data = head_data;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getContentLength() {
        return contentLength;
    }

    public void setContentLength(int contentLength) {
        this.contentLength = contentLength;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public iot_protocol() {
    }

    public iot_protocol(int head_data, int id, int contentLength, String content) {
        this.head_data = head_data;
        this.id = id;
        this.contentLength = contentLength;
        this.content = content;
    }
}

3.1.3编写解码器

对于解码器而言,判断正误,依次读出每一个数据即可,但是有几个值得关注的点:

①对于魔数0717,解码器使用buffer.readInt()方法是一次读取四个字节的(一个int占四个字节),所以这里的0717会被转化成16进制的ascii码:30 37 31 37,合起来看做一个数,使用计算机计算结果如下,为808923447:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第14张图片

②对于节点编号只有一个字节,发送端发送后转化为ascii码,在接收端解码时使用字节读出,读出的是十进制ascii码,将这个数字减去48后就是正确的编号。

③接下来是3位的数据长度,这里我选择使用三次readByte()读出三位,读出的三位分别减去48后,换算成整体的十进制。

④得到数据长度后,可以就可以创建一个数据长度大小的byte数组,使用buffer.getbytes方法直接获取所有数据,再使用String的构造函数转化为String。

解码器代码如下:

package com.zhiqi.IOT.protocol;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

//自定义协议的解码器 根据协议的格式进行对发来的数据包的解码
public class iot_protocol_decoder extends ByteToMessageDecoder {
    /***
     * 魔数 4个字节
     * 传感器节点序号 1个字节
     * 数据长度 3个字节
     * 最基本的长度为10个字节 如果数据包小于10则发生异常
     */
    public final int BASE_LENGTH = 4 + 1 + 3;

    //重写解码方法
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf buffer, List list) throws Exception {
        if (buffer.readableBytes() >= BASE_LENGTH) {
            /***
             * 防止socket字节流攻击 数据量太大不合理
             */
            if (buffer.readableBytes() > 2048) {
                buffer.skipBytes(buffer.readableBytes());
            }

            // 记录包头开始在缓冲区中的index
            int beginReader;

            while (true) {
                // 获取及标记包头开始的index
                beginReader = buffer.readerIndex();
                buffer.markReaderIndex();
                // 读到了协议的开始标志,结束while循环
                //int一次读入四个字节 将四个字节转化为一个数 计算出0717转化为的结果是
                if (buffer.readInt() == 808923447) {
                    break;
                }


                buffer.resetReaderIndex();
                buffer.readByte();

                // 当略过,一个字节之后,
                // 数据包的长度,又变得不满足
                // 此时,应该结束。等待后面的数据到达
                if (buffer.readableBytes() < BASE_LENGTH) {
                    return;
                }
            }

            iot_protocol data=new iot_protocol();
            data.setHead_data(808923447);
            // 节点的序号
            data.setId(buffer.readByte()-48);
            //数据的长度为3位 计算出长度
            int length=0;
            for(int i=0;i<3;i++){
                length=10*length+(buffer.readByte())-48;
            }
            //得到长度后获取整个数据部分 作为字符串
            byte[] temp=new byte[length];
            data.setContentLength(length);
            buffer.getBytes(buffer.readerIndex(),temp);
            data.setContent(new String(temp));


            list.add(data);
        }
    }
}

 
  

3.1.4编写服务器

遇到的几个注意点:

①在Netty开发当中,需要自定义ChannelInboundHandlerAdapter,在重写channelRead时会传入msg对象,此对象在使用完毕后必须释放,否则会导致对象池泄露内存溢出(报错Discarded inbound message {} that reached at the tail of the pipeline.Please check your pipeline configuration.)。可以直接使用SimpleChannelInboundHandler,会自动释放对象。

②可以使用sockettool来测试服务器,这里来演示一下:比如说之前说的魔数0717会被识别成一个新的int,我们连接上localhost:8266,进行测试:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第15张图片

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第16张图片

对整体进行测试,观察解码器是否正确。

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第17张图片

3.1.5部署服务器(将netty服务器部署到阿里云服务器)

首先对maven项目进行打包:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第18张图片

使用winscp将jar包上传至云端:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第19张图片

打开putty运行此jar包(记得将目录切换到jar包的位置):

运行后打开日志发现报错,是因为没有指定主类,回到idea中来指定(修改pom文件):

 
        
            
                
                    org.apache.maven.plugins
                    maven-jar-plugin

                    
                        
                            
                                com.zhiqi.IOT.Server.server
                            
                        
                    
                
            
        
    

继续报错:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第20张图片

没想到大半夜的在这个地方卡了好久,netty导出jar包不知道为什么不能用maven的package,而要用idea自带的build,先在project structure中设置:物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第21张图片

接着继续设置:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第22张图片

再次运行,服务器启动:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第23张图片

接着进行测试:

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第24张图片

这里又踩坑了,可以和这个端口建立连接但是一直接收不到数据,经过了好长时间的调试,修改了阿里云安全组、给netty绑定上公网ip等等,最后发现代码根本没错,是由于解码器中设置了发过来的数据的格式,而我一开始没有按这个格式发送而导致接收不到,被当做无用包舍弃了,查阅资料后有如下解释:

首先,对于一个程序来讲,它所绑定的IP只能是其所在机器(无论物理机还是虚拟机)上的某个网卡的IP地址,这个你可以到机器上运行ifconfig查看。

其次,所谓绑定的含义是规定程序能够监听到哪个目的地IP的IP包,比如机器有两个网卡A和B,IP地址分别是AIP和BIP,你的程序绑定AIP,那么操作系统只会将目的地是AIP的IP包转发给你的程序。0.0.0.0是特殊的,它代表着能够转发目的地IP是机器上任意IP的IP包到你的程序。

最后,为何可以通过公网IP访问到你的机器?这是因为云服务商给你做了NAT,而这个地址你的机器是不知道的,也不属于你的机器上的任意一张网卡,所以你无法绑定。

按规定格式发送数据后可以通信:

3.1.6 netty连接数据库

数据库表格如下:

①position表:

id是每个节点的编号,lat纬度,lng经度,address是文字版的详细地址,ismail记录发生噪声干扰时是否已经发送邮件给管理员。

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第25张图片

②now_data表

 记录每个节点的当前数据,即网页地图实时展示的数据,当每次接收到传感器节点传来的数据时,就会进行now_data数据的更新,并将之前的数据记录到history_data表中。

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第26张图片

③history_data表

 此表结构与now_data相同。

注意的几个点:

①super传播

super.channelRead(ctx, msg);

在每一个Handler中都应该让数据传输到下一个ChannelInboundHandler中。如果没有其他处理程序在意msg,则无需调用它。

②业务逻辑

在netty的业务逻辑中,将IO操作和占用时间的数据库操作分开,以免造成IO进程的堵塞。IO操作是放在NioEventLoopGroup中的,而数据库等业务是放在DefaultEventLoopGroup中的。代码如下:

首先在server类中放上DefaultEventLoopGroup,要在一个私有类中进行使用。

 //配置业务线程组
    final EventLoopGroup businessGroup=new DefaultEventLoopGroup(16);

接着在pipeline中添加businessGroup:

    /**
     * 网络事件处理器
     */
    private class ChildChannelHandler extends ChannelInitializer {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            // 添加自定义协议的解码工具 处理完会将一个iot_protocol类型的数据传给ServerHandler
            ch.pipeline().addLast(new iot_protocol_decoder());
            // 处理网络IO
            ch.pipeline().addLast(new ServerHandler());
            //处理数据库业务逻辑
            ch.pipeline().addLast(bussinessGroup,new bussinessHandler());
        }
    }

然后定义绑定上的businessHandler类,重写方法进行对数据库的操作,记得要对错误进行捕获处理,避免服务器宕机:


    private class bussinessHandler extends  ChannelInboundHandlerAdapter{
        /**
         * 在这里写入对数据库的操作
         * @param ctx
         * @param msg
         * @throws Exception
         */
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            iot_protocol sound =(iot_protocol)msg;
            Data data=new Data();
            statement =connection.createStatement();
            //把原先的数据取出来
            ResultSet resultSet =statement.executeQuery("select * from now_data where id=" + sound.getId());

            if(resultSet.next()){
                data.setId((int)resultSet.getObject(1));
                data.setSound((int)resultSet.getObject(2));
                data.setTime(((Timestamp) resultSet.getObject(3)).toString());
            }
            if(data.getTime()!=null) {
                //有数据才进行这些步骤  没有数据就直接插入

                //插入到历史记录中
                String sql = "insert into history_data values(" + data.getId() + "," + data.getSound() + ",'" + data.getTime() + "')";
                System.out.println(sql);
                statement.execute(sql);


                //删除原有数据
                statement.execute("delete from now_data where id=" + data.getId());
            }


            //更新新数据
            statement.execute("insert into now_data(id,sound) values("+sound.getId()+","+Integer.parseInt(sound.getContent().substring(sound.getContent().length()-3))+")");

            super.channelRead(ctx, msg);
        }
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
            Channel channel = ctx.channel();
            if (channel.isActive()) ctx.close();
        }
    }

③timestamp的插入

注意timestamp插入数据库中的格式,需要引号。

④部署服务器报错

        ClassLoader classLoader = JdbcUtils.class.getClassLoader();
        URL resource = classLoader.getResource("db.properties");
        System.out.println(resource);
        String path = resource.getPath();
        //2.加载文件
        try {
            pro.load(new FileReader(path));
        } catch (IOException e) {
            e.printStackTrace();
        }

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第27张图片

原因是读取配置文件出错,在windows下可以,但是在linux下,url.getPath()得到的是properties文件的绝对路径,所以出错,改用以下方法:

    ResourceBundle rb = ResourceBundle.getBundle("db");


    url = rb.getString("url");
    username = rb.getString("username");
    password = rb.getString("password");
    driver = rb.getString("driver");

到此netty服务器已搭建完成。

3.2嵌入式代码

写累了,就简单写写吧...

嵌入式的代码时一步步慢慢完善的,从一开始点一个灯来确定PCB是否可用,到oled的显示,到传感器数据的读取及显示,再到使用esp8266对其联网上传。

点灯的程序过于简单就不说了,从oled开始说,导入中景园提供的oled代码后,首先修改oled.h头文件中对引脚的宏定义:先查看绘制的电路图

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第28张图片

 可以看到SCL接的是PB6,SDA接的是PB7,修改宏定义:

//-----------------OLED端口定义---------------- 

#define OLED_SCL_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_6)//SCL
#define OLED_SCL_Set() GPIO_SetBits(GPIOB,GPIO_Pin_6)

#define OLED_SDA_Clr() GPIO_ResetBits(GPIOB,GPIO_Pin_7)//DIN
#define OLED_SDA_Set() GPIO_SetBits(GPIOB,GPIO_Pin_7)

然后注意还有oled.c中的init函数有对GPIO的初始化,也需要进行修改:

void OLED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能B端口时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
 	GPIO_Init(GPIOB, &GPIO_InitStructure);	  //初始化PB6,7
 	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);

}

接着是传感器数据的读取,使用了原子哥的adc代码,同样要修改GPIO引脚,这次在h头文件中没有宏定义,直接修改c文件的init函数:

void  Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1	, ENABLE );	  //使能ADC1通道时钟
 

	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

	//PA4 作为模拟通道输入引脚                           
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值

	...

}		

同时,由于使用的是PA4引脚,查询数据手册可知是通道4,所以在调用读取函数时需要选择为channel4。

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第29张图片

	adcx=Get_Adc_Average1(ADC_Channel_4,30);

最后是esp8266的代码

一开始使用onenet官方给的代码,遇到了wifi接不上的问题,查询后发现是命令错误,官方给的是AT+CWJAP=,而实际上要是AT+CWJAP_DEF=。

更改后就可以连接上wifi,传输数据到netty成功:

输出重定向:不是printf而是sprintf,需要引入头文件#include "stdio.h".

注:这里%3d也有错误,前面不会补0,使用%03d才可以

在使用wifi模块遇到了很严重的问题,在定时器中断内无法发送数据,原先设想在定时器中断时读取并显示数据,再上传至数据服务器,但是在中断内不知道为什么发送到服务器端的却是AT指令,尝试了好久(设置中断优先级、查阅AT指令原理)也没有找到解决方法,最后只能在main函数中使用while和delay对数据进行发送,这也就意味着每次测量的显示值和发送到云端的值不同。

物联网嵌入式 校园噪声监测系统 ESP8266 STM32 LM386声音传感器 NETTY自定义协议_第30张图片

3.3基于Thymeleaf的前端展示

原本想用VUE来进行前端的搭建,框架选用ElementUI,一个界面已经搭建好了,但是发现如果想要部署到服务器上,需要使用Nginx。但是!!现在是凌晨1点,距离答辩还有七小时,只能找到以前做的一个项目进行修改,调用了腾讯地图api,在前端设置了定时器进行Ajax轮询来定时获取数据库。

你可能感兴趣的:(嵌入式,java,ajax,netty,stm32)