在前面我们已经完成了课设的大部分操作,接下来是关于代码的解析与子芥在完成过程中遇到的一些调试错误,有几点还是需要将一下到:
1)子芥在百度网盘中给出的文件都是在所有操作完成之后能够正常运行的文件以及环境,所以对于后面出现的一些调试错误,在文件当中的代码都是已经改正过的。
2)在运行系统程序时需要注意的是由于UDP使用端口通信的原因,在运行时是不能够通过网络调试器来查看端口通信情况的,那样会造成端口占用问题。
3)这因为这套教程,姑且称作教程是子芥对于整个课设完成过程中的文档进行整理,所以可能并不会成完整的体系,有一些细节上的操作并没有书写详细,希望原谅。对于一些操作并不清楚,可以询问子芥,我会尽力回答。
代码解析与调试
1.代码模块化分析
1.1.Arduino代码
1.1.1.初始化模块
//定义软串口rx、tx,将DHT11数据口链接到arduino Pin A0口
#define _rxpin 9
#define _txpin 8
SoftwareSerial debug( _rxpin, _txpin ); // 定义软串口RX为数字口9,TX为数字口8
#define dht_dpin A0 // 将DHT11 Data引脚连接至Arduino A0口
//向Esp8266-01s发送“AT”测试指令,回复OK后调用WiFi连接Connect函数
void setup() {
pinMode(dht_dpin,OUTPUT); // 设置dht_dpin引脚为输出模式
digitalWrite(dht_dpin,HIGH); // 向dht_dpin引脚写一个高电平
Serial.begin( _baudrate );
debug.begin( _baudrate );
delay(300);
sendDebug("AT"); //向Esp8266发送“AT测试指令”
delay(500);
if(Serial.find("OK"))
{
debug.println("RECEIVED: OK\nData ready to sent!");
connectWiFi();
} //若收到回复OK,则输出RECEIVED:OK ready to sent!,并调用connectWIFI函数
delay(700);
}
//connectWIFI函数,初始化收到OK回复后调用
boolean connectWiFi()
{
Serial.println("AT+CWMODE=3");
delay(2000);
// 设置Esp8266为Statino+AP模式
String cmd="AT+CWJAP=\"";
cmd+=SSID;
cmd+="\",\"";
cmd+=PASS;
cmd+="\"";
sendDebug(cmd);
//借助cmd向Esp8266发送AT指令接入WiFi,指令格式为AT+CWJAP="zty","zql262144"
delay(2000);
if(Serial.find("OK"))
{
debug.println("RECEIVED: OK");
return true;
}
else
{
debug.println("RECEIVED: Error");
return false;
} //若连接成功收到回复OK,则返回打印RECEIVED:OK,若连接失败则返回打印RCEEIVED:Error
cmd = "AT+CIPMUX=0";
//借助cmd向Esp8266发送AT指令设置多路连接模式,指令格式为AT+CIPMUX=0
sendDebug( cmd );
if( Serial.find( "Error") )
{
debug.print( "RECEIVED: Error" );
return false;
}
//若设置失败收到回复Error,则返回打印RECEIVED:Error
}
1.1.2数据获取模块
//读取DHT11温湿度传感器数据,在串口打印温湿度数据,并将读取到的数据转化为字符串型方便后面UDP通信数据包的使用
void loop()
{
int chk = DHT11.read(A0); //将A0口读取到到数值赋给chk
float tempH = DHT11.humidity; //定义储存湿度的变量tenmpH,类型为浮点型
float tempT = DHT11.temperature; //定义存储温度的变量tempT,类型为浮点型
//Serial.println((float)DHT11.temperature); //串口打印湿度
//Serial.print(",\t");
//Serial.println((float)DHT11.humidity); //串口打印温度
char buffer[10];
//dtostrf函数用于将字符转化为字符串显示,这里将浮点型转化为字符串型
String temph = dtostrf(tempH, 4, 1, buffer); // 将浮点型tempH转化为字符串型temph,用于后面UDP通信
String tempt = dtostrf(tempT, 4, 1, buffer); // 将浮点型tempT转化为字符串型tempt,用于后面UDP通信
updateTemp(temph,tempt);
delay(5000);
}
1.1.3UDP通信模块
//向Esp8266发送AT指令与目标上位PC机建立UDP连接,发送温湿度数据
void updateTemp(String temph,String tempt)
{
String cmd = "AT+CIPSTART=\"UDP\",\"";
cmd += IP;
cmd += "\",8080,9090,0"; // 借助cmd组成AT指令连接上位PC机,指令格式为AT+CIPSTART="UDP","192.168.43.31",8080,9090,0。其中“192.168.43.31”,8080为上位PC机的IP地址和UDP通信端口,9090为Esp8266发送UDP数据的端口,0代表UDP使用单链接。
sendDebug(cmd); // 发送指令,连接上位PC机,建立UDP通信
delay(1000);
if( Serial.find( "Error" ) )
{
debug.print( "RECEIVED: Error\nExit1" );
return;
} // 若连接失败,收到回复Error,则返回打印RECEIED:Error
cmd = tempt+temph+GET+"\r\n"; // 将tempt、temph、GET方法组成数据包赋给cmd,记录温湿度的值
Serial.print( "AT+CIPSEND=" ); // 发送AT指令进行UDP通信发送温湿度数据,指令格式为AT+CIPSEND:length,length为所要发送的数据包的长度
Serial.println( cmd.length() ); // 统计数据包长度
debug.print(">");
debug.print(cmd);
Serial.print(cmd); // 收到>回复后向上位PC机发送数据包
if( Serial.find("OK") )
{
debug.println( "RECEIVED: OK" );
Serial.println("AT+CIPCLOSE");
}// 若收到回复OK,则发送成功返回打印RECEIVED:OK,发送AT指令断开UDP连接,指令格式为AT+CIPCLOSE
else
{
debug.println( "RECEIVED: Error\nExit2" );
} // 若发送失败返回打印RECEIVED:Error
}
1.2Eclipse代码
1.2.1udpsend.java主要代码分析
//建立UDP连接,从指定的IP地址与端口获取数据包,并将数据包中的温湿度内容赋给相应变量
package src.com.udp; // 包名
import java.io.IOException; // java的输入输出异常包,方便对程序的输入输入进行很好的检测,如果程序涉及到的输入输出没有问题,程序正常运行;否则,在控制台抛出输入输出错误。
import java.net.DatagramPacket; // DatagramPacket是信息的载体,DatagramPacket携带数据
import java.net.DatagramSocket; // DatagramSocket是收发的实体,DatagramSocket收发数据
import java.net.InetAddress; // 获取主机对应的InetAddress对象
public class udpsend {
public String udptest( )
throws IOException {
// 1、建立UDP socket端点
DatagramSocket s = new DatagramSocket(); // 创建数据报套接字并将其绑定到本地主机
// 2、提供数据,封装打包 ---DatagramPacket(byte[] buf, int length, InetAddress address, int port)
byte[] bs = "OK".getBytes(); // 封装向目标主机发送的内容
DatagramPacket dp = new DatagramPacket(bs, bs.length, InetAddress.getByName("192.168.43.253"),9090);
// 向目标对象发送DatagramPacket,目标对象IP地址为Esp8266IP地址,目标对象端口为Esp8266建立UDP连接的端口
/* 3、使用send发送 */
try {
s.send(dp); // 使用send向目标发送数据
System.out.println("发送成功 ");
System.out.println(dp); // 发送成功在控制台打印发送成功
}
catch (IOException e) {
System.out.println("发送失败: ");
e.printStackTrace(); // 发送失败则在控制台打印发送失败,并调用IOException展示错误信息
}
// 4.创建数据报,用于接收客户端发送的数据报
byte[] data = new byte[1024]; //创建字节数组,指定接收的数据包的大小
DatagramPacket packet = new DatagramPacket(data, data.length);
//5.接收客户端发送的数据
System.out.println("****服务器已经启动,等待客户端发送数据");
s.receive(packet); // 此方法在接收到数据报之前会一直阻塞
//6.读取数据
String info = new String(data, 0, packet.getLength());
System.out.println("我是服务器,客户端:" + info);
/* 7、关闭资源 */
s.close();
return info;
}
}
1.2.2index.jsp主要代码分析
// 用于查询数据库中的数据并将数据排列后在web页面中展示
<%/** 获取当前时间 **/
response.setIntHeader("Refresh", 7); // 设置页面数据自动更新,每5秒更新一次
Date d = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String now = df.format(d);%>
<div style="padding-top:10px;">
当前时间:<%=now %>
</div>
<% /** 创建按钮,调用出insert.jsp(将数据写入数据库的脚本) **/ %>
<form action="insert.jsp" method="post" style="padding-top:10px;">
<input type="submit" name="" value="更新数据" >
</form>
<%/** 查询数据库内容 **/ %>
<%
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); // 连接数据的驱动程序名,使用的驱动程序与数据库相对应
String url = "jdbc:sqlserver://localhost:1433;DatabaseName=IOT"; // 连接的数据库地址与数据库名
String user="zty"; // 登入数据库使用的数据库用户名
String password = "zql262144"; // 数据库用户密码
Connection conn = DriverManager.getConnection(url, username, password); // 连接状态
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql = "select * from data order BY date desc"; // sql查询语句,查询数据库中的表格内容
ResultSet rs=stmt.executeQuery(sql);
rs.last();
int row= rs.getRow(); // 获取数据库中保存的数据条数
int sum=row/10;
if ((row%10)!=0){
sum=sum+1;%>
<%}
%>
1.2.3insert.jsp主要代码分析
// 用与调用udpsend获取数据包中的数据,并将数据存入指定的数据库中
<%/** 获取当前时间 **/
Date d = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String now = df.format(d);%>
<%/** 获取温湿度 **/
String str; // 定义字符串型str用于接受UDP通信获得的数据包
Int temperature=0; //定义int型变量temperature用于接受数据包中的温度数据
Int humidity=0; //定义int型变量humidity用于接受数据包中的湿度数据
udpsend t=new udpsend(); // 更新UDP通信收到的数据包
str=t.udptest() ; // 将数据包中的数据赋给str
temperature=Integer.parseInt(str.substring(0, 2)); // 读取数据包中的前2位数据并转换为int型数据赋值给temperature
humidity=Integer.parseInt(str.substring(2, 4)); // 读取数据包中的后2位数据并转换为int型数据赋值给humidity
%>
<%/** 将当前时间及温湿度写入数据库 **/
try{
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); //连接数据的驱动程序名,使用的驱动程序与数据库相对应
String url = "jdbc:sqlserver://localhost:1433;DatabaseName=IOT”; // 连接的数据库地址与数据库名
String user="zty"; // 登入数据库使用的数据库用户名
String password="zql262144"; // 数据库用户密码
Connection conn = DriverManager.getConnection(url, username, password); //连接状态
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
//sql插入数据语句将读取到的数据插入数据库的表格中
String sql_insert = "insert into data(temperature, humidity, date) values ('"+temperature+"','"+humidity+"','"+now+"')";
PreparedStatement pst=conn.prepareStatement(sql_insert);
int rs=pst.executeUpdate();
if(rs!=0){
/** 跳转index.jsp **/ %>
<jsp:forward page="index.jsp"></jsp:forward>
<%
}
}
catch(Exception e){
/** 出现异常时,输出异常 **/
out.println(e);
}
%>
2.调试
2.1器件连接
2.1.1器件连接线路图
2.1.2器件功能
Arduino UNO R3开发板:作为开发平台承载实验代码,用于读取DTH11数据,向Esp8266发送AT指令实现UDP通信
DHT11温湿度传感器:系统的感知元件,用于监控室内温湿度,将数据传至Arduino开发板
Esp8266-01s:传输系统的通信组件,通过Arduino发送的AT指令来完成接入WiFi,建立UDP通信,发送数据包等操作
USB-TTL模块:连接实验器件与上位PC机,将指令打印在串口调试器中,方便调试与错误分析。不用于UDP通信
2.2调试及修正
2.2.1数据库连接操作
1)错误表现:Eclipse运行web项目,在浏览器查看时显示异常报告,浏览器报告在 [130] 行处理 [/index.jsp] 时发生异常
2)错误原因:网页显示错误根本原因为:驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。查阅相关资料了解该错误为所使用的数据库驱动程序jre版本为高版本,在较高的版本的jre中更改了相关的加密协议,禁用了低版本中的部分加密协议,导致无法建立安全连接
3)处理办法:修改jre中的安全配置协议。打开jre安装目录中\Java\jdk\jre\lib\security下的java.security中的安全配置协议,修改其jk.tls.disabledAlgorithms中的安全协议内容,删除TLSv1、TLSv1.1、3DES_EDE_CBC。启用就得加密算法。
4)处理结果:修改安全配置协议启用旧的加密算法后依旧无法建立安全连接。只能改用低版本的jre或者更换使用的数据库系统,这里将数据库系统更换为MySQL数据库,避免以后在建立连接与数据库操作上产生问题。
5)操作步骤:重新导入与MySQL相适应的数据库驱动程序,并应用于web项目中。修改连接数据库操作部分对应的代码。
①修改index.jsp中查询数据库内存的代码为
<%/** 查询数据库内容 **/ %>
<%
Class.forName("com.mysql.jdbc.Driver"); // 连接MySQL数据库的驱动程序名,使用的驱动程序与数据库相对应
String url = "jdbc:mysql://localhost:3306/mysql"; // 连接的数据库地址与数据库名
String username = "root"; // 登入数据库使用的数据库用户名
String password = "262144"; // 数据库用户密码
Connection conn = DriverManager.getConnection(url, username, password); // 连接状态
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
String sql = "select * from iot order BY date desc"; // sql查询语句,查询数据库中的表格内容
ResultSet rs=stmt.executeQuery(sql);
rs.last();
int row= rs.getRow(); // 获取数据库中保存的数据条数
int sum=row/10;
if ((row%10)!=0){
sum=sum+1;%>
<%}
②修改insert.jsp中将当前时间及温湿度数据写入数据库的代码为
<%/** 将当前时间及温湿度写入数据库 **/
try{
Class.forName("com.mysql.jdbc.Driver"); //连接数据的驱动程序名,使用的驱动程序与数据库相对应
String url = "jdbc:mysql://localhost:3306/mysql"; // 连接的数据库地址与数据库名
String username = "root"; // 登入数据库使用的数据库用户名
String password = "262144"; // 数据库用户密码
Connection conn = DriverManager.getConnection(url, username, password); //连接状态
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
//sql插入数据语句将读取到的数据插入数据库的表格中
String sql_insert = "insert into iot(temperature, humidity, date) values ('"+temperature+"','"+humidity+"','"+now+"')";
PreparedStatement pst=conn.prepareStatement(sql_insert);
int rs=pst.executeUpdate();
if(rs!=0){
/** 跳转index.jsp **/ %>
<jsp:forward page="index.jsp"></jsp:forward>
<%
}
}
catch(Exception e){
/** 出现异常时,输出异常 **/
out.println(e);
}
%>
2.2.2UDP通信建立
1)错误表现:向Arduino开发板上电后,Esp8266-01s正常接入WiFi。启动Eclipse中的web项目,打开网页发现并没有数据写入。通过窗口调试助手接收到的USB-TTL传输的信息发现Esp-8266建立UDP通信成功,通过网络调试助手对本机IP地址与Esp-8266指定的接受端口查看发现能够正常接收到通信数据。
2)错误原因:通过网络调试助手对Eclipse中建立的web项目进行由本机端口到本机端口的UDP数据通信测试,发现udpsend.java程中定义的DatagramSocket端点只使用的默认的本机IP,而并没有对于通行端口进行设置,所以每次本机发送与接受数据时都会随机选取一个新的端口进行通信,而Esp8266始终向指定的端口发送数据,造成数据无法正常接收
3)处理办法:通过DatagramSocket(int port)创建数据报套接字并将其绑定到本地主机上的指定端口。
4)处理结果:通过网络调试助手查看PC机与Esp8266之间UDP通信状态,观察到始终在指定端口间进行通信且数据接收正常。
5)操作步骤:修改udpsend.java中建立udp socket端点代码,将数据报套接字绑定的本机的指定端口。修改后的代码为:
DatagramSocket s = new DatagramSocket(8080); // 创建数据报套接字并将其绑定到本地主机上的指定端口
2.2.3数据报数据获取
错误表现:PC机与Esp8266之间通信正常,PC机端口正常收到数据报,但无法将数据写入数据库并读取展示。点击页面中更新数据按钮会出现错误。
错误原因:由于insert.jsp程序中获取温湿度操作定义接收并可正常读取的数据为4位int型数据,并定义前两位数据为温度,后两位数据为湿度。而Esp8266发送的数据包中从DHT11所获取的温湿度数据为浮点型数据,所以insert.jsp程序无法正常读取数据报中的数据并进行操作
处理办法:修改insert.jsp程序中获取温湿度操作的代码,使得能够接受float浮点型数据,并将前4位数据作为温度数据,后4位为湿度数据。
处理结果:UDP通信正常,程序能够正常读取数据报中数据,并写入数据库指定的数据表中,然后通过web页面展示。系统运转正常。
5)操作步骤:修改insert.jsp代码将收到的数据报中的数据转换为float浮点型数据并赋值个相应的变量。Float.paserFloat函数用于将字符串型数据转换为浮点型数据。
<%/** 获取温湿度 **/
String str; // 定义字符串型str用于接受UDP通信获得的数据包
float temperature=0; //定义浮点型变量temperature用于接受数据包中的温度数据
float humidity=0; //定义浮点型变量humidity用于接受数据包中的湿度数据
udpsend t=new udpsend(); // 更新UDP通信收到的数据包
str=t.udptest() ; // 将数据包中的数据赋给str
temperature=Float.parseFloat(str.substring(0, 4)); // 读取数据包中的前4位数据并转换为浮点型数据赋值给temperature
humidity=Float.parseFloat(str.substring(4, 8)); // 读取数据包中的后4位数据并转换为浮点型数据赋值给humidity
%>
[请你相信我:
我说的每一句话,
都是错的]
zijie