绪论
硬件工具: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="","" :设置连接的WiFi名称和密码
AT+CIPSTART="TCP","",
AT+CIPSEND=5 : 用于测试字符长度
C51单片机程序开发
使用Keil编写,打开工具,引入STC系列的MCU
//引入库与宏定义
#include
#include
#include
#include
#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;i0;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 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==' ')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",rn");
delay5ms();
Uart_SendStrData("AT+CIPSEND=");
Uart_SendStrData(len_sum);
Uart_SendStrData("rn");
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
工具类:
package com.bl.utli;
import com.bl.demo.subSocketClient;
import java.net.Socket;
import java.util.HashMap;
public class SocketParam {
public static HashMap 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 getData() {
List 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 param);
List 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 param){
return dataMapper.addTemp(param);
}
public List getTemps()
{
return dataMapper.getTemps();
}
}
编写xml文件,设备数据插入与查询,查询排序按时间递减
insert into data_info_t(temp,humi,beam,time) values (#{temp},#{humi},#{beam},#{time})
postman通信测试图
三、前端页面数据展示
页面实现
后台管理系统
室内火灾监控平台
$("#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.resultList[i].fire+' '
+''+res.resultList[i].gas+' '
+''+res.resultList[i].time+' ';
$("#table").append('' + tr + ' ');
}
}
})
})
})
本文章来源于互联网,如有侵权,请联系删除!原文地址:51单片机物联网开发
许多新技术新产品的发展正在塑造 2022 年的工业物联网,从软件和硬件方面的创新到增强人类感官能力的智能穿戴设备,所有这些都有可能通过改进的数据驱动智能影响生产系统和流程。工业物联网正在将传统的线性制造转变为动态的、互联的系统,帮助工厂释放潜力,更高效、更高产…