硬件工具: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单片机根据传感器采集的各种火灾安全参数进行判断,如果出现异常情况则单片机启动接入的控制元器件进行自动控制,控制报警灯、蜂鸣器和风扇进行工作,硬件整体框架如下
我们使用sscom调试助手进行调试。首先需要一个TTL转USB工具插上ESP8266模块连接PCcom口,打开调试助手检测到设备,然后可以向8266发送指令,指令集如下:
AT+RST :初始化
AT+CWMODE=3 :设置兼容模式,可以访问其他WiFi
AT&#43;UART&#61;<波特率>,8,1,0,0 &#xff1a;设置波特率
AT&#43;CWJAP&#61;"
AT&#43;CIPSTART&#61;"TCP","
AT&#43;CIPSEND&#61;5 : 用于测试字符长度
<下面输入要发送的字符串>
//引入库与宏定义
#include
#include
#include
#include
#define uint unsigned inttypedef unsigned char u8;
typedef unsigned int u16;
unsigned char strlen(unsigned char *str);
unsigned char flag;sbit Data&#61;P0^6;
sbit Gas_DOUT &#61; P0^4;
sbit Fire_DOUT &#61; P0^3;
sbit beep&#61;P0^2;
sbit LED &#61; P0^1;
sbit relay &#61; P0^5;bit uart_busy &#61; 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&#61;10;i>0;i--){LED&#61;0;beep&#61;0;delay_u16(10);LED&#61;1;beep&#61;1;delay_u16(10);}
}
//设置频率
void delay_u16(u16 i)
{while(i--);
}
//控制继电器风扇函数
void relay_fun(){relay &#61; ~relay;LED_Alarm();relay &#61; ~relay;
}void beep_led_alarm(){beep &#61; 0; for(i&#61;0;i<5000;i&#43;&#43;){beep&#61;~beep;delay_u16(100);}
}void DHT11_delay_us(uchar n)
{while(--n);
}void DHT11_delay_ms(uint z)
{uint i,j;for(i&#61;z;i>0;i--)for(j&#61;110;j>0;j--);
}void DHT11_start()
{Data&#61;1;DHT11_delay_us(2);Data&#61;0;DHT11_delay_ms(20); Data&#61;1;DHT11_delay_us(30);
}uchar DHT11_rec_byte()
{uchar i,dat&#61;0;for(i&#61;0;i<8;i&#43;&#43;) { while(!Data); DHT11_delay_us(8); dat<<&#61;1; if(Data&#61;&#61;1) dat&#43;&#61;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&#61;&#61;0){while(Data&#61;&#61;0); DHT11_delay_us(40); R_H&#61;DHT11_rec_byte(); R_L&#61;DHT11_rec_byte(); T_H&#61;DHT11_rec_byte(); T_L&#61;DHT11_rec_byte(); revise&#61;DHT11_rec_byte(); DHT11_delay_us(25); if((R_H&#43;R_L&#43;T_H&#43;T_L)&#61;&#61;revise) {RH&#61;R_H;RL&#61;R_L;TH&#61;T_H;TL&#61;T_L;} if(TH > 24){LED_Alarm();LED &#61; 0;beep&#61;0;}temp_dat[0]&#61;&#39;0&#39;&#43;(TH/10);temp_dat[1]&#61;&#39;0&#39;&#43;(TH%10);}
}void delay5ms(){unsigned char a,b;for(b&#61;201;b>0;b--)for(a&#61;247;a>0;a--);
}void Init_uart(void) { TMOD &#61; TMOD | 0x20; SCON &#61; SCON | 0x50; TH1 &#61; 0xFd; TL1 &#61; TH1; TR1 &#61; 1; EA &#61;1; ES &#61;1;
}void Uart_SendByteData(unsigned char msg){while(uart_busy); SBUF&#61;msg; uart_busy &#61; 1;
}void Uart_SendStrData(unsigned char *msg){while(*msg){Uart_SendByteData(*msg&#43;&#43;);}
}//取字符串长度
unsigned char strlen(unsigned char *str)
{unsigned char len&#61;0;while(1){ if(*str&#61;&#61;&#39;\0&#39;)break;//拷贝完成了.len&#43;&#43;;str&#43;&#43;;}return len;
}void main(){count &#61; 0;beep&#61;1;LED&#61;1;Init_uart();len_a&#61;0;len_b&#61;0;len_sum&#61;0;while(1){count&#43;&#43;;DHT11_receive();delay5ms();delay5ms();for(i&#61;0;i<2;i&#43;&#43;){tempture[i]&#61;temp_dat[i];}if(Gas_DOUT &#61;&#61; 0){gas&#61;&#39;1&#39;;LED_Alarm();LED &#61; 0;beep&#61;0;}else{gas&#61;&#39;0&#39;;}if(Fire_DOUT &#61;&#61; 0){fire&#61;&#39;1&#39;;LED_Alarm();LED &#61; 0;beep&#61;0;}else{fire&#61;&#39;0&#39;;}for(i&#61;0;i<2;i&#43;&#43;){string_send[i]&#61;tempture[i];}string_send[2]&#61;&#39;t&#39;;string_send[3]&#61;fire;string_send[4]&#61;&#39;g&#39;;string_send[5]&#61;gas;string_send[6]&#61;&#39;f&#39;;string_data1[]&#61;"{\"type\":\"02\",\"deviceCode\":\"Device\",\"deviceData\":\"";string_data2[]&#61;"\"}";len_a&#61;strlen(string_data1);leb_b&#61;strlen(string_data2);len_c&#61;strlen(string_send);len_sum&#61;len_a&#43;len_b&#43;len_c;Uart_SendStrData("AT&#43;CIPSTART&#61;\"TCP\",\"
}void UART_Interrupt(void) interrupt 4 {unsigned char temp;if(RI){temp &#61; SBUF;RI &#61; 0;if(temp &#61;&#61; 1){flag &#61; 1;}}if(TI){TI &#61; 0;uart_busy &#61; 0;}
}
ESP8266上的TX和RX一定要和单片机上的RX与TX交叉连接&#xff0c; 8266-TX连接51-RX&#xff0c;8266-RX连接51-TX
以下为单片机串口调试图&#xff1a;
使用Socket-Tools&#xff0c;在云服务器上创建TCPServer指定端口号&#xff0c;开启单片机连接WiFi
下图为端口通信调试图&#xff1a;
创建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 &#61; null;public static TCPServer tcpServer;&#64;PostConstructpublic void init(){tcpServer &#61; this;tcpServer.start();}public String socketSendData(String deviceCode,String sendData){//不为空时subSocketClient temp &#61; DeviceCode2SocketMap.get(deviceCode);if(null!&#61;temp){return temp.sendSocketData(sendData,deviceCode);}else{return SendError;}}&#64;Overridepublic void run() {try {serverSocket &#61; new ServerSocket(PortNum);System.out.println("PortNum: "&#43;PortNum);} catch (IOException e) {System.out.println("the port cannot open.");}while(true){try {System.out.println("wait. .. ...");//使用accept()是阻塞方法Socket socketTemp &#61; serverSocket.accept();new subSocketClient(this.serverSocket,socketTemp).start();//为每个连接都创建一个线程客户端} catch (IOException e) {System.out.println("happening");}}}
}
使用JDBC对MySQL进行操作&#xff0c; 在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 java.util.HashMap;
import java.util.Map;
import java.sql.*;import static com.bl.utli.SocketParam.*;//&#64;Component
public class subSocketClient extends Thread {private ServerSocket serverSocket;private Socket socket;&#64;Autowired&#64;Resourceprivate DataMapper tempMapper_udp_save;public static subSocketClient subSocketClient;public subSocketClient(ServerSocket serverSocket, Socket socket) {this.serverSocket &#61; serverSocket;this.socket &#61; socket;}//初始化通信程序&#64;PostConstructpublic void init(){subSocketClient &#61; this;subSocketClient.tempMapper_udp_save &#61; this.tempMapper_udp_save;}private static final String DRIVER &#61; "com.mysql.jdbc.Driver";public void addTemp(String temp, String humi, String beam, String time) {String url &#61; "jdbc:mysql://localhost:3306/iot?serverTimezone&#61;UTC&allowMultiQueries&#61;true";// 数据库用户名String user &#61; "root";// 数据库密码String password &#61; "123456";// 建立数据库连接&#xff0c;获得连接对象conntry {Connection conn &#61; DriverManager.getConnection(url, user, password);Class.forName("com.mysql.jdbc.Driver");String sql &#61; "insert into data_info_t (temp,humi,beam,time) values(?,?,?,?)";PreparedStatement ps &#61; conn.prepareStatement(sql);ps.setString(1, temp);ps.setString(2, humi);ps.setString(3, beam);ps.setString(4, time);// 执行sql语句ps.executeUpdate();System.out.println("SQL插入完毕&#xff01;");// 关闭数据库连接对象conn.close();} catch (Exception e) {e.printStackTrace();}}String temp &#61; "";String humi &#61; "";String beam &#61; "";String time &#61; "";String str1 &#61; "";String str2 &#61; "";String str3 &#61; "";public String sendSocketData(String sendData, String deviceCode) {try {BufferedReader br &#61; null;PrintWriter pw &#61; null;br &#61; new BufferedReader(new InputStreamReader(System.in));pw &#61; new PrintWriter(socket.getOutputStream(), true);//查看socket是否存在if (!socket.isClosed()) {pw.println(sendData);pw.flush();return SendSuccess;} else {//不存在则移除该socketDeviceCode2SocketMap.remove(deviceCode);return SendError;}} catch (IOException e) {e.printStackTrace();}return SendError;}&#64;Overridepublic void run() {try {//获取输入流InputStream inputStream &#61; socket.getInputStream();BufferedReader br &#61; new BufferedReader(new InputStreamReader(inputStream));byte[] buf &#61; new byte[1024];//接收数据int line &#61; 0;while ((line &#61; inputStream.read(buf)) !&#61; -1) {String param &#61; new String(buf, 0, line);//转成json格式JSONObject jsonObject &#61; JSONObject.parseObject(param);//获取type类型&#xff0c;如果是02则自动注册设备String type &#61; jsonObject.getString("type");if (type.equals(TypeUpdateData)) {//获取设备编号String deviceCode &#61; jsonObject.getString("deviceCode");System.out.println(deviceCode);//获取传感数据String deviceData &#61; jsonObject.getString("deviceData");System.out.println(deviceData);//字符串解析str1 &#61; deviceData;String[] strArr1 &#61; StringUtils.split(str1, "t");for (int i &#61; 0; i
创建restful端口&#xff0c;调用获取设备数据方法将设备传感数据返回&#xff0c;访问方法为POST
&#64;RequestMapping(value &#61; "/getData", method &#61; RequestMethod.POST, produces &#61; MediaType.APPLICATION_JSON_VALUE)&#64;CrossOriginpublic List getData() {List temps &#61; dataService.getTemps();return temps;}
创建数据服务接口&#xff0c;用于获取和添加传感器数据
package com.bl.Data;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;&#64;Mapper
public interface DataMapper {int addTemp(Map
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;
&#64;Service
public class DataService {&#64;AutowiredDataMapper dataMapper;public int addTemp(Map
}
编写xml文件&#xff0c;设备数据插入与查询&#xff0c;查询排序按时间递减
postman通信测试图
页面实现
$("#search").click(function () {var name&#61;$("#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&#61;0; i
&#39; &#43;res.resultList[i].fire&#43;&#39; &#39;&#43;&#39;&#39;&#43;res.resultList[i].gas&#43;&#39; &#39;&#43;&#39;&#39;&#43;res.resultList[i].time&#43;&#39; &#39;;$("#table").append(&#39;&#39; &#43; tr &#43; &#39; &#39;);}}})})
})