热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

51单片机物联网开发

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

绪论

硬件工具: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&#43;UART&#61;<波特率>,8,1,0,0 &#xff1a;设置波特率
AT&#43;CWJAP&#61;"","" &#xff1a;设置连接的WiFi名称和密码
AT&#43;CIPSTART&#61;"TCP","",<端口号>
AT&#43;CIPSEND&#61;5 : 用于测试字符长度
<下面输入要发送的字符串>

C51单片机程序开发


        使用Keil编写&#xff0c;打开工具&#xff0c;引入STC系列的MCU

//引入库与宏定义
#include
#include
#include
#include #define uchar unsigned char
#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\",\"\",\r\n"); delay5ms();Uart_SendStrData("AT&#43;CIPSEND&#61;");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 &#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;


二、服务器软件开发 


 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 &#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 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;
&#64;Service
public class DataService {&#64;AutowiredDataMapper dataMapper;public int addTemp(Map param){return dataMapper.addTemp(param);}public List getTemps(){return dataMapper.getTemps();}
}

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



insert into data_info_t(temp,humi,beam,time) values (#{temp},#{humi},#{beam},#{time})

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].temp&#43;&#39;&#39;&#43;&#39;&#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;);}}})})
})


推荐阅读
  • 技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告
    技术日志:使用 Ruby 爬虫抓取拉勾网职位数据并生成词云分析报告 ... [详细]
  • 本文详细探讨了 jQuery 中 `ajaxSubmit` 方法的使用技巧及其应用场景。首先,介绍了如何正确引入必要的脚本文件,如 `jquery.form.js` 和 `jquery-1.8.0.min.js`。接着,通过具体示例展示了如何利用 `ajaxSubmit` 方法实现表单的异步提交,包括数据的发送、接收和处理。此外,还讨论了该方法在不同场景下的应用,如文件上传、表单验证和动态更新页面内容等,提供了丰富的代码示例和最佳实践建议。 ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • 技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统
    技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统 ... [详细]
  • 在处理大规模数据数组时,优化分页组件对于提高页面加载速度和用户体验至关重要。本文探讨了如何通过高效的分页策略,减少数据渲染的负担,提升应用性能。具体方法包括懒加载、虚拟滚动和数据预取等技术,这些技术能够显著降低内存占用和提升响应速度。通过实际案例分析,展示了这些优化措施的有效性和可行性。 ... [详细]
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 本文探讨了如何利用 jQuery 的 JSONP 技术实现跨域调用外部 Web 服务。通过详细解析 JSONP 的工作原理及其在 jQuery 中的应用,本文提供了实用的代码示例和最佳实践,帮助开发者解决跨域请求中的常见问题。 ... [详细]
  • 尽管我们尽最大努力,任何软件开发过程中都难免会出现缺陷。为了更有效地提升对支持部门的协助与支撑,本文探讨了多种策略和最佳实践,旨在通过改进沟通、增强培训和支持流程来减少这些缺陷的影响,并提高整体服务质量和客户满意度。 ... [详细]
  • 汽车电子架构与CAN网络基础解析——鉴源实验室专业解读 ... [详细]
  • 在VMware虚拟机中部署带有中文图形界面的CentOS 7 Linux系统
    本文详细介绍了在VMware虚拟机中部署带有中文图形界面的CentOS 7 Linux系统的步骤。首先,通过“文件”菜单选择“新建虚拟机”并进入自定义设置。接着,在硬盘兼容性选项中选择默认设置。为了更好地进行Linux操作系统的安装练习,建议选择稍后安装操作系统,并在虚拟机安装完成后,根据实际需求删除不必要的硬件组件。此外,本文还提供了详细的配置参数和注意事项,帮助用户顺利完成整个部署过程。 ... [详细]
  • 本文详细介绍了 jQuery 的入门知识与实战应用,首先讲解了如何引入 jQuery 库及入口函数的使用方法,为初学者提供了清晰的操作指南。此外,还深入探讨了 jQuery 在实际项目中的多种应用场景,包括 DOM 操作、事件处理和 AJAX 请求等,帮助读者全面掌握 jQuery 的核心功能与技巧。 ... [详细]
  • 本书详细介绍了在最新Linux 4.0内核环境下进行Java与Linux设备驱动开发的全面指南。内容涵盖设备驱动的基本概念、开发环境的搭建、操作系统对设备驱动的影响以及具体开发步骤和技巧。通过丰富的实例和深入的技术解析,帮助读者掌握设备驱动开发的核心技术和最佳实践。 ... [详细]
author-avatar
幸福的肖巍
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有