51单片机物联网开发

绪论

硬件工具:STC89C52RC芯片、51单片机开发板、ESP8266通信模块、TTL转USB、各类传感器

软件工具:Keil开发环境,STC-ISP烧录工具,IDEA专业版,Postman,WebStorm,vx小程序开发工具,sscom串口助手,Socket-Tools,Tomcat8,JDK1.8,Navicat,Android Studio

开发环境:Java8、SpringBoot+MyBatis、MySQL、C51、阿里云、JavaScript、JQuery、Ajax、Windows操作系统、Chrome浏览器,Android操作系统

开发描述:基于ESP8266通信模块,通过51单片机采集环境传感数据并实现自动控制,通过AT指令将采集数据打包json格式发送目标服务器,云端部署jar包并建立数据库,将接收的数据解析并保存到设备数据表中,基于Ajax与Restful接口实现前后端分离,实现用户登录注册、设备管理、数据查询功能,并支持在Android和vx小程序上访问设备信息。

项目整体框架

一、硬件开发

        本案例通过检测MQ-5可燃气体传感器、DHT数字温湿度传感器、火焰传感器这三个传感器感知的环境数据,进而通过逻辑判断通过I/O口输出到继电器、蜂鸣器和LED报警灯。通过ESP8266-01s模块通过WIFI接入云服务器,将采集的数据实时发送到指定端口,且51单片机根据传感器采集的各种火灾安全参数进行判断,如果出现异常情况则单片机启动接入的控制元器件进行自动控制,控制报警灯、蜂鸣器和风扇进行工作,硬件整体框架如下

ESP8266调试

         我们使用sscom调试助手进行调试。首先需要一个TTL转USB工具插上ESP8266模块连接PCcom口,打开调试助手检测到设备,然后可以向8266发送指令,指令集如下:

AT+RST :初始化
AT+CWMODE=3 :设置兼容模式,可以访问其他WiFi
AT+UART=<波特率>,8,1,0,0 :设置波特率
AT+CWJAP="<SSID>","<PWD>" :设置连接的WiFi名称和密码
AT+CIPSTART="TCP","<IP地址>",<端口号>
AT+CIPSEND=5 : 用于测试字符长度
<下面输入要发送的字符串>

C51单片机程序开发

        使用Keil编写,打开工具,引入STC系列的MCU

//引入库与宏定义
#include <reg52.h>
#include <intrins.h>
#include <assert.h>
#include <stdlib.h>

#define uchar unsigned char
#define uint unsigned int

typedef unsigned char u8;
typedef unsigned int u16;
unsigned char strlen(unsigned char *str);
unsigned char flag;

sbit Data=P0^6;  
sbit Gas_DOUT = P0^4;
sbit Fire_DOUT = P0^3;
sbit beep=P0^2;	   
sbit LED = P0^1;
sbit relay = P0^5;

bit uart_busy = 0;
uchar temp_dat[9];
uchar tempture[2];
uchar string_data1[80];
uchar string_data2[10];
char gas;
char fire;
//设置发送数据
uchar string_send[12];
uint i;
uint count;
void relay_fun();
uchar len_a;
uchar len_b;
uchar len_c;
uchar len_sum;

void LED_Alarm()
{
	u8 i;
	for(i=10;i>0;i--)
	{
		LED=0;
		beep=0;
		delay_u16(10);
		LED=1;
		beep=1;
		delay_u16(10);
	}
}
//设置频率
void delay_u16(u16 i)
{
	while(i--);	
}
//控制继电器风扇函数
void relay_fun(){
	relay = ~relay;
	LED_Alarm();
	relay = ~relay;
}

void beep_led_alarm(){
	beep = 0; 
	for(i=0;i<5000;i++){
		beep=~beep;
		delay_u16(100);
	}
}

void DHT11_delay_us(uchar n)
{
    while(--n);
}

void DHT11_delay_ms(uint z)
{
   uint i,j;
   for(i=z;i>0;i--)
      for(j=110;j>0;j--);
}

void DHT11_start()
{
   Data=1;
   DHT11_delay_us(2);
   Data=0;
   DHT11_delay_ms(20);   
   Data=1;
   DHT11_delay_us(30);
}

uchar DHT11_rec_byte()      
{
   uchar i,dat=0;
  for(i=0;i<8;i++)   
   {          
      while(!Data);   
      DHT11_delay_us(8);     
      dat<<=1;           
      if(Data==1)   
         dat+=1;
      while(Data);   
    }  
    return dat;
}
//DHT11AD转换函数
void DHT11_receive()      
{
    uchar R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise; 
    DHT11_start();
    if(Data==0)
    {
        while(Data==0);     
        DHT11_delay_us(40);  
        R_H=DHT11_rec_byte();    
        R_L=DHT11_rec_byte();    
        T_H=DHT11_rec_byte();     
        T_L=DHT11_rec_byte();    
        revise=DHT11_rec_byte(); 
				
        DHT11_delay_us(25);    

        if((R_H+R_L+T_H+T_L)==revise)      
        {
            RH=R_H;
            RL=R_L;
            TH=T_H;
            TL=T_L;
        } 
				if(TH > 24){
					LED_Alarm();
					LED = 0;
					beep=0;
				}
        temp_dat[0]='0'+(TH/10);
        temp_dat[1]='0'+(TH%10);
    }
}

void delay5ms(){
    unsigned char a,b;
    for(b=201;b>0;b--)
        for(a=247;a>0;a--);
}

void Init_uart(void) {  
	TMOD = TMOD | 0x20;    
	SCON = SCON | 0x50;    
	TH1 = 0xFd;        
	TL1 = TH1;  
	TR1 = 1;              
	EA =1;                
	ES =1;               
}


void Uart_SendByteData(unsigned char msg){
	while(uart_busy); 
	SBUF=msg;		  
	uart_busy = 1;	
}

void Uart_SendStrData(unsigned char *msg){
	while(*msg){
		Uart_SendByteData(*msg++);
	}
}

//取字符串长度
unsigned char strlen(unsigned char *str)
{
    unsigned char len=0;
    while(1)
    {                             
        if(*str=='\0')break;//拷贝完成了.
        len++;
        str++;
    }
    return len;
}

void main(){
	count = 0;
	beep=1;
	LED=1;
	Init_uart();
    len_a=0;
    len_b=0;
    len_sum=0;
	while(1){
		count++;
		DHT11_receive();
		delay5ms();delay5ms();
		for(i=0;i<2;i++){
			tempture[i]=temp_dat[i];
		}
		if(Gas_DOUT == 0)
		{
			gas='1';
			LED_Alarm();
			LED = 0;
			beep=0;
		}else{
			gas='0';
		}
		if(Fire_DOUT == 0){
			fire='1';
			LED_Alarm();
			LED = 0;
			beep=0;
		}else{
			fire='0';
		}
		
		for(i=0;i<2;i++){
			string_send[i]=tempture[i];
		}
		string_send[2]='t';
		string_send[3]=fire;
		string_send[4]='g';
		string_send[5]=gas;
		string_send[6]='f';
		string_data1[]="{\"type\":\"02\",\"deviceCode\":\"Device\",\"deviceData\":\"";
        string_data2[]="\"}";
        len_a=strlen(string_data1);
        leb_b=strlen(string_data2);
        len_c=strlen(string_send);
        len_sum=len_a+len_b+len_c;
		Uart_SendStrData("AT+CIPSTART=\"TCP\",\"<ip>\",<port>\r\n");   
		delay5ms();
		Uart_SendStrData("AT+CIPSEND=");
        Uart_SendStrData(len_sum);
        Uart_SendStrData("\r\n");
		delay5ms();		
		Uart_SendStrData(string_data1);
		Uart_SendStrData(string_send);
		Uart_SendStrData(string_data2);
		delay1_relay(2);
	}
}

void UART_Interrupt(void) interrupt 4 {
	unsigned char temp;
	if(RI){
		temp = SBUF;
		RI = 0;
		if(temp == 1){
			flag = 1;
		}
	}
	
	if(TI){
		TI = 0;
		uart_busy = 0;
	}
}

调试通信

        ESP8266上的TX和RX一定要和单片机上的RX与TX交叉连接,                                                        8266-TX连接51-RX,8266-RX连接51-TX

以下为单片机串口调试图:

使用Socket-Tools,在云服务器上创建TCPServer指定端口号,开启单片机连接WiFi

下图为端口通信调试图:

二、服务器软件开发 

 TCP接收服务开发

创建socket通信端口以多线程方式等待监听端口

package com.bl.demo;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import static com.bl.utli.SocketParam.*;

public class TCPServer extends Thread {
    ServerSocket serverSocket = null;
    public static TCPServer tcpServer;

    @PostConstruct
    public void init(){
        tcpServer = this;
        tcpServer.start();
    }

    public String socketSendData(String deviceCode,String sendData){
        //不为空时
        subSocketClient temp = DeviceCode2SocketMap.get(deviceCode);
        if(null!=temp){
            return temp.sendSocketData(sendData,deviceCode);
        }else{
            return SendError;
        }
    }

    @Override
    public void run() {
        try {
            serverSocket = new ServerSocket(PortNum);
            System.out.println("PortNum: "+PortNum);
        } catch (IOException e) {
            System.out.println("the port cannot open.");
        }
        while(true){
            try {
                System.out.println("wait. .. ...");
                //使用accept()是阻塞方法
                Socket socketTemp = serverSocket.accept();
                new subSocketClient(this.serverSocket,socketTemp).start();

                //为每个连接都创建一个线程客户端
            } catch (IOException e) {
                System.out.println("happening");
            }
        }
    }
}

使用JDBC对MySQL进行操作, 在run()方法中解析数据并存储到数据库中

package com.bl.demo;

import com.alibaba.fastjson.JSONObject;
import com.bl.Data.Data;
import com.bl.Data.DataMapper;
import com.bl.Data.DataService;
import com.bl.utli.SocketParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import static com.bl.utli.SocketParam.*;

public class subSocketClient extends Thread {
    private ServerSocket serverSocket;
    private Socket socket;

    @Autowired
    @Resource
    private DataMapper tempMapper_udp_save;
    public static subSocketClient subSocketClient;

    public subSocketClient(ServerSocket serverSocket, Socket socket) {
        this.serverSocket = serverSocket;
        this.socket = socket;
    }

    //初始化通信程序
    @PostConstruct
    public void init(){
        subSocketClient = this;
        subSocketClient.tempMapper_udp_save = this.tempMapper_udp_save;
    }

    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";

    public void addTemp(String temp, String fire, String gas, String time) {
        String url = "jdbc:mysql://localhost:3306/fire?serverTimezone=UTC&allowMultiQueries=true";
        // 数据库用户名
        String user = "root";
        // 数据库密码
        String password = "123456";
        // 建立数据库连接,获得连接对象conn
        try {
            Connection conn = DriverManager.getConnection(url, user, password);
            Class.forName("com.mysql.jdbc.Driver");
            String sql = "insert into data_info_t (temp,fire,gas,time) values(?,?,?,?)";
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, temp);
            ps.setString(2, fire);
            ps.setString(3, gas);
            ps.setString(4, time);
            // 执行sql语句
            ps.executeUpdate();
            System.out.println("SQL插入完毕!");
            // 关闭数据库连接对象
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    String temp = "";
    String fire = "";
    String gas = "";
    String time = "";
    String str1 = "";
    String str2 = "";
    String str3 = "";

    public String sendSocketData(String sendData, String deviceCode) {
        try {
            BufferedReader br = null;
            PrintWriter pw = null;
            br = new BufferedReader(new InputStreamReader(System.in));
            pw = new PrintWriter(socket.getOutputStream(), true);
            //查看socket是否存在
            if (!socket.isClosed()) {
                pw.println(sendData);
                pw.flush();
                return SendSuccess;
            } else {
                //不存在则移除该socket
                DeviceCode2SocketMap.remove(deviceCode);
                return SendError;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return SendError;

    }

    @Override
    public void run() {
        try {
            //获取输入流
            InputStream inputStream = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            byte[] buf = new byte[1024];
            //接收数据
            int line = 0;
            while ((line = inputStream.read(buf)) != -1) {
                String param = new String(buf, 0, line);
                //转成json格式
                JSONObject jsonObject = JSONObject.parseObject(param);
                //获取type类型,如果是02则自动注册设备
                String type = jsonObject.getString("type");
                if (type.equals(TypeUpdateData)) {
                    //获取设备编号
                    String deviceCode = jsonObject.getString("deviceCode");
                    System.out.println(deviceCode);
                    //获取传感数据
                    String deviceData = jsonObject.getString("deviceData");
                    System.out.println(deviceData);
                    //字符串解析
                    str1 = deviceData;
                    String[] strArr1 = StringUtils.split(str1, "t");
                    for (int i = 0; i < strArr1.length; i++) {
                        temp = strArr1[0];
                        str2 = strArr1[1];
                    }
                    String[] strArr2 = StringUtils.split(str2, "f");
                    for (int i = 0; i < strArr2.length; i++) {
                        fire = strArr2[0];
                        str3 = strArr2[1];
                    }
                    String[] strArr3 = StringUtils.split(str3, "g");
                    for (int i = 0; i < strArr3.length; i++) {
                        gas = strArr3[0];
                    }

                    Calendar calendar = Calendar.getInstance();
                    SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    time = sd.format(calendar.getTime());

                    System.out.println("温度: " + temp);
                    System.out.println("湿度: " + fire);
                    System.out.println("燃气: " + gas);
                    System.out.println("时间: " + time);
                    addTemp(temp, fire, gas, time);
                }

            }
            Thread.sleep(10);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

工具类:

package com.bl.utli;

import com.bl.demo.subSocketClient;

import java.net.Socket;
import java.util.HashMap;

public class SocketParam {
    public static HashMap<String, subSocketClient> DeviceCode2SocketMap = new HashMap<>();
    public static int PortNum = 7880;
    public static String TypeRegister = "01";
    public static String TypeUpdateData = "02";
    public static String SendSuccess = "OK";
    public static String SendError = "ERROR";
}

创建restful端口,调用获取设备数据方法将设备传感数据返回,访问方法为POST

    @RequestMapping(value = "/getData", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    @CrossOrigin
    public List<Data> getData() {
        List<Data> temps = dataService.getTemps();
        return temps;
    }

创建数据服务接口,用于获取和添加传感器数据 

package com.bl.Data;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;

@Mapper
public interface DataMapper {
    int addTemp(Map<String,Object> param);

    List<Data> getTemps();

}
package com.bl.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
@Service
public class DataService {
    @Autowired
    DataMapper dataMapper;

    public int addTemp(Map<String, Object> param){
        return dataMapper.addTemp(param);
    }

    public List<com.bl.Data.Data> getTemps()
    {
        return dataMapper.getTemps();
    }
}

 编写xml文件,设备数据插入与查询,查询排序按时间递减

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bl.Data.DataMapper">
    <insert id="addTemp" parameterType="map">
        insert into data_info_t(temp,humi,beam,time) values (#{temp},#{humi},#{beam},#{time})
    </insert>

    <select id="getTemps" resultType="com.bl.Data.Data">
        select * from data_info_t ORDER BY time DESC LIMIT 100
    </select>
</mapper>

postman通信测试图

三、前端页面数据展示

 

 

 页面实现

<!DOCTYPE html>
<html>

	<head>
		<meta charset="UTF-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1">
		<meta name="renderer" content="webkit">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>后台管理系统</title>
		
		<link rel="shortcut icon" href="images/favicon.ico"/>
		<link rel="bookmark" href="images/favicon.ico"/>
		<link rel="stylesheet" type="text/css" href="css/base.css">
		<link rel="stylesheet" type="text/css" href="fonts/iconfont.css">
		<script type="text/javascript" src="framework/jquery-1.11.3.min.js" ></script>
		<script type="text/javascript" src="js/base.js"></script>
		<link rel="stylesheet" type="text/css" href="layui/css/layui.css">
		<script type="text/javascript" src="layui/layui.js"></script>
		<script src="framework/cframe.js"></script>
		<link rel="stylesheet" type="text/css" href="css/frameStyle.css">
		<script type="text/javascript" src="framework/frame.js" ></script>
	
	</head>
	
	<body>
		<div class="frameMenu">
		    <div class="logo">
		        <img src="images/logo.png"/>
		        <div class="logoText">
		            <h1>室内火灾监控平台</h1>
		        </div>
		    </div>
		    <div class="menu">
		    	<div class="hoverBox"></div>
		        <ul>
		        	<li>
		                <a class="menuFA" href="javascript:void(0)"><i class="iconfont icon-liuliangyunpingtaitubiao03 left" onmouseenter="getLeftTips(this, '用户管理')" onmouseleave="layer.closeAll('tips')"></i><font>用户管理</font><i class="iconfont icon-dajiantouyou right"></i></a>
		                <dl>
		                	<dt><a href="javascript:void(0)" onclick="menuCAClick('tgls/user/user_add.html',this, 'page10022')">添加用户</a></dt>
		                	<dt><a href="javascript:void(0)" onclick="menuCAClick('tgls/user/user_list.html',this, 'page10023')">用户列表</a></dt>
		                	<dt><a href="javascript:void(0)" onclick="menuCAClick('tgls/user/modify.html',this, 'page10024')">修改信息</a></dt>
		                </dl>
		            </li>
		        	<li>
		                <a class="menuFA" href="javascript:void(0)"><i class="iconfont icon-yunying left" onmouseenter="getLeftTips(this, '设备信息')" onmouseleave="layer.closeAll('tips')"></i><font>设备信息</font><i class="iconfont icon-dajiantouyou right"></i></a>
		                <dl>
		                	<dt><a href="javascript:void(0)" onclick="menuCAClick('tgls/view/view_add.html',this, 'page10027')">添加设备</a></dt>
		                	<dt><a href="javascript:void(0)" onclick="menuCAClick('tgls/view/view_list.html',this, 'page10028')">设备数据</a></dt>
		                </dl>
		           	</li>
		        </ul>
		    </div>
		</div>
		
		<div class="main">
			<div class="frameTop">
				<div class="shrinkBut">
		    		<div class="hoverBox"></div>
					<ul>
						<li><a href="javascript:void(0)" onclick="menuShrink(this)" onmouseenter="getTips(this, '收缩菜单')" onmouseleave="layer.closeAll('tips')"><i class="iconfont icon-caidan-shousuo"></i></a></li>
						<li><a href="javascript:void(0)" onclick="frameRefresh()" onmouseenter="getTips(this, '刷新')" onmouseleave="layer.closeAll('tips')"><i class="iconfont icon-htmal5icon23"></i></a></li>
					</ul>
				</div>
				<div class="topMenu">
		    		<div class="hoverBox"></div>
					<ul>
						<li><a href="login1.html" onmouseenter="getTips(this, '退出系统')" onmouseleave="layer.closeAll('tips')"><i class="iconfont icon-084tuichu"></i></a></li>
					</ul>
				</div>
			</div>
			
			<div class="frameMain">
				<div class="title" id="frameMainTitle">
					<i class="iconfont icon-shuangzuojiantou- leftbut" onclick="pageShow('l')"></i>
					<div class="mainPageBox">
						<div class="mainPage">
							<span class="active" onclick="pageSwitch(this)">
			    				<div class="hoverBox"></div>
								<i class="iconfont icon-shouye"></i>
							</span>
						</div>
					</div>
					<i class="iconfont icon-shuangyoujiantou- rightbut" onclick="pageShow('r')"></i>
				</div>
				<div class="con">
					<div class="mainPageCon">
						<iframe class="mainIframe" src="tgls/user/user_list.html" scrolling="yes"></iframe>
					</div>
				</div>
			</div>
		</div>
	</body>

</html>

    $("#search").click(function () {
		var name=$("#input").val();
        $.ajax
		({
			async:true,
			type: "POST",
			url: "http://47.102.42.105:8456/Device/getData",
			dataType: "json",
			data: JSON.stringify({
				"name":name
			}),
			contentType: "application/json",
			success: function (res) {
				console.log(res);
				for(var i=0; i<res.msg.length; i++){
					console.log(res.msg[i]);
					tr = '<td id="temp">'+res.resultList[i].temp+'</td>'
						+'<td id="fire">' +res.resultList[i].fire+'</td>'
						+'<td id="gas">'+res.resultList[i].gas+'</td>'
						+'<td id="time">'+res.resultList[i].time+'</td>';
					$("#table").append('<tr id="current" style="height: 30px; align-content: center">' + tr + '</tr>');
				}
			}
		})
	})
})

猜你喜欢

转载自blog.csdn.net/qq_37504771/article/details/123753590