物联网工程实训——智慧家居开发

物联网工程实训

目标:用嵌入式开发板采集信息,用WiFi传输到云平台。 开发手机App或者微信小程序,实现云平台数据和信息的读取。可以开发Web页面,对采集的数据进行显示。

本组目标:设计并制造一个物联网温湿度及智能安防,可以实现页面或移动端实时查看传感器信息。

总体思路:硬件端采用stm32f103或者是arduino作为开发板,esp8266为通讯模块,温湿度传感器,超声波传感器,软件端采用Java监听,嵌入式C语言,JavaScript微信小程序。

器材:stm32f103c8开发板一个,arduino开发板一个,温湿度传感器,超声波传感器,服务器,杜邦线,面包板

为了实现这一目的,我分成了两个方案,第一个方案,arduino方案,第二个是stm32方案

我用word画了一个简单的结构图,有助于下面的理解。

方案一——arduino开发板

仔细研读esp8266技术手册之后发现,esp8266可以独立的连接WiFi并与标服务器建立TCP连接,开启透传模式传递信息。arduino的每个端口都可以设置为软串口,这样我就有了第一种想法。发送信息全部交给esp8266,arduino开发板的作用就是,收集并处理传感器信息给esp8266。我设想是硬件端可以仅用一百行代码就可以解决。arduino作为串口工具,直接给esp8266发送AT指令,这样也不需要安装esp8266的库文件,而且arduino的成本远远低于stm32.

第一步,准备工作。设置arduino12和13端口为软串口(此处随便设置,除A0和A1)。设置工作频率为9600HZ,分别把esp8266的rx和tx接到12和13端口,相应的把12端口设置为软tx,13端口设置为rx端口。接下来用串口测试命令,输入AT指令,出现OK,则说明正常。

第二步,数据收集。连接温湿度传感器和超声波传感器,温湿度传感器最好接5v电压(有可能3.3v电压不太够,温湿度传感器无法正常工作),单片机收集数据并传给esp8266

第三步,数据发送。esp8266先于可以上网的WiFi建立连接,先设置esp8266工作模式,mode=1 :Station模式(接收模式),设置命令为AT+CWMODE=<mode>;然后连接wifi命令为AT+ CWSAP= <ssid>,<pwd>。然后与目标服务器建立TCP连接,命令为AT+CIPSTART=2,"TCP","IP地址",端口。TCP连接成功之后,建立透传模式,命令为AT+CIPSEND。这样就可以把上一步收集的传感器信息发送到服务器。

第四步,数据接收及存储。服务器端设置端口监听,获取esp8266发送过来的数据,此处可以用java或者python写。建立端口监听之后,可以看到发送过来的数据。接下来进行数据处理,首先从发送的数据包中提取有用信息并存到数据库。

第五步,应用端显示数据。jsp界面调用数据库并实时显示温湿度及距离。移动端采用微信小程序调用数据库并实时显示。

这五步基本上完成了整个应用系统的开发。

接下来我贴上这次程序的源码。仅供参考,一些关键代码需要自己更改。

arduino源码

#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(13, 12); // RX, TX  通过软串口连接esp8266
 
void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
 
  mySerial.begin(115200);
  mySerial.println("AT+RST");   // 初始化重启一次esp8266
  delay(1500);
  echo();
  mySerial.println("AT");
  echo();
  delay(500);
  mySerial.println("AT+CWMODE=3");  // 设置Wi-Fi模式
  echo();
  mySerial.println("AT+CWJAP=\"WiFiSSID\",\"password\"");  // 连接Wi-Fi
  echo();
  delay(10000);
}
 
void loop() {
 
  if (mySerial.available()) {
    Serial.write(mySerial.read());
  }
  if (Serial.available()) {
    mySerial.write(Serial.read());
  }
  post();
}
 
void echo(){
  delay(50);
  while (mySerial.available()) {
    Serial.write(mySerial.read());
  }
}
 
void post(){
  String temp = "POST data";
  mySerial.println("AT+CIPMODE=1");
  echo();
  mySerial.println("AT+CIPSTART=\"TCP\",\"ip地址\",80");  // 连接服务器的80端口
  delay(1000);
  echo();
  mySerial.println("AT+CIPSEND"); // 进入TCP透传模式,接下来发送的所有消息都会发送给服务器
  echo();
  mySerial.print("POST /IP地址"); // 开始发送post请求
  mySerial.print(" HTTP/1.1\r\nIP地址\r\nUser-Agent: arduino-ethernet\r\nConnection:close\r\nContent-Length:"); // post请求的报文格式
  mySerial.print(temp.length()); // 需要计算post请求的数据长度
  mySerial.print("\r\n\r\n"); 
  mySerial.println(temp); // 结束post请求
  delay(3000);
  echo();
  mySerial.print("+++"); // 退出tcp透传模式,用println会出错
  delay(2000);
}

这只是关键代码,不包括传感器收集数据的代码。

Java监听代码

 
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.ServerSocket;
import java.net.Socket;
 
public class test {
 
	       public static void main(String args[]) throws IOException {
	          //为了简单起见,所有的异常信息都往外抛
	          int port = 702;
	          //定义一个ServerSocket监听在端口上
	          ServerSocket server = new ServerSocket(port);
	          System.out.println("start");
	          //server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的
	          Socket socket = server.accept();
	          //跟客户端建立好连接之后,我们就可以获取socket的InputStream,并从中读取客户端发过来的信息了。
	          Reader reader = new InputStreamReader(socket.getInputStream());
	          //char chars[] = new char[60]; 
	          int len;
                  int k=1;
	          StringBuilder sb = new StringBuilder();
	          while (k>0 ) {
       		     char chars[] = new char[60]; 
                     len=reader.read(chars);
	             //sb.append(new String(chars, 0, len)); 
                     //reader.read(chars);
                     //k+=2;
                     System.out.println(chars);
                     System.out.println("-----------------");
                     //System.out.println("from client: " + sb);
	          }
	          
	          
	          
	          reader.close();
	          socket.close();
	          server.close();
	       }
	       
	    
}

此处只是Java监听代码,并没有连接并调用数据库的部分,数据库部分可以自己写,很简单。

jsp显示代码

因为当时时间比较紧,就没建专门的servlet类,直接把调用数据库加到了jsp代码中。

<%@ page contentType="text/html"%>  
<%@page pageEncoding="GB2312"%>  
<%@page import="java.sql.*" %>  <%--导入java.sql包--%>
<html>
<head>
<title>距离及温湿度</title>
</head>
<body>
<h1 align="center">历史距离及温湿度</h1>
<table border="2">
<tr>
<td width="100" id="title">编号</td>
<td width="100" dis="title">距离</td>
<td width="100" hum="title">湿度</td>
<td width="100" temp="title">温度</td>
<td width="200" time="title">时间</td>
</tr>
<%  
        try {  
            Class.forName("com.mysql.cj.jdbc.Driver");  ////驱动程序名
            String url = "jdbc:mysql://localhost:3306/test_post?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false"; //数据库名
            String username = "";  //数据库用户名
            String password = "";  //数据库用户密码
            Connection conn2 = DriverManager.getConnection(url, username, password);  //连接状态

            if(conn2 != null){  
               // out.print("Connected");  
                out.print("<br />"); 
%>
<%
                Statement stmt2 = null;  
                ResultSet rs2 = null;  
                String sql2 = "SELECT * FROM test_post.esp8266;";  //查询语句
                stmt2 = conn2.createStatement();  
                rs2 = stmt2.executeQuery(sql2);  
                out.print("距离及温湿度:");  
				out.print("<br/>");
                while (rs2.next()) {%>
  <tr>  
    <td width="100" ><%=rs2.getString(1) %></td>  
    <td width="100" ><%=rs2.getString(2) %></td>  
    <td width="100"><%=rs2.getString(3) %></td>  
    <td width="100"><%=rs2.getString(4) %></td>  
    <td width="200"><%=rs2.getString(5) %></td>  
  </tr>
  <%
            }  
            }else{  
                out.print("连接失败!");  
            }  
        }catch (Exception e) {        
            //e.printStackTrace();  
            out.print("数据库连接异常!");  
        }  
%> 
</table>
</body>
</html>

移动端我采用的是微信小程序,这里只展示出关键代码,就是云函数连接数据库,这个问题解决了,剩下的页面显示就很简单了。

// 云函数入口文件
const cloud = require('wx-server-sdk')
//引入mysql操作模块
const mysql = require('mysql2/promise')
cloud.init()

// 云函数入口函数
exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  try {
    const connection = await mysql.createConnection({
      host: "IP",
      database: "test_post",
      user: "user",
      password: "pwd"
    })
    const [rows, fields] = await connection.execute('SELECT * from test_post.esp8266 where id = (SELECT max(id) FROM test_post.esp8266);')
    return rows;
  } catch (err) {
    console.log("链接错误", err)
    return err
  }
}

放几张图片展示以下效果

硬件图

jsp页面图

移动端效果图

页面优化没有做好,咱们不需要花里胡哨,科技的简约美。

这就是方案一的全部步骤了。

方案二——stm32开发板

方案二就是换了一种开发板,比起arduino,stm32的普及率更高,个人觉得stm32比较繁琐,但是其功能和性能比arduino强。

和方案一,软件端是一样的,那我只说一下硬件端。

正点原子给了一套模板,以及库文件,直接调用库文件,很简单的就解决了这一问题。

思路和arduino差不多,也是单片机收集数据,esp8266传数据库。

这里只简单概述一下,贴一下关键代码

esp8266库

#include "esp8266.h"
#include "string.h"
#include "usart.h"
#include "usart3.h"
#include "stm32f10x.h"
#include "sys.h" 
#include "delay.h"

//ESP8266Ä£¿éºÍPC½øÈë͸´«Ä£Ê½
void esp8266_start_trans(void)
{
	//ÉèÖù¤×÷ģʽ 1£ºstationģʽ   2£ºAPģʽ  3£º¼æÈÝ AP+stationģʽ
	esp8266_send_cmd("AT+CWMODE=1","OK",50);
	//ÈÃWifiÄ£¿éÖØÆôµÄÃüÁî
	esp8266_send_cmd("AT+RST","ready",20);
	
	delay_ms(1000);         //ÑÓʱ3SµÈ´ýÖØÆô³É¹¦
	delay_ms(1000);
	delay_ms(1000);
	delay_ms(1000);
	
	//ÈÃÄ£¿éÁ¬½ÓÉÏ×Ô¼ºµÄ·ÓÉ
	while(esp8266_send_cmd("AT+CWJAP=\"test\",\"12345678\"","WIFI GOT IP",600));
	
	//=0£ºµ¥Â·Á¬½Óģʽ     =1£º¶à·Á¬½Óģʽ
	esp8266_send_cmd("AT+CIPMUX=0","OK",20);
	
	//½¨Á¢TCPÁ¬½Ó  ÕâËÄÏî·Ö±ð´ú±íÁË ÒªÁ¬½ÓµÄIDºÅ0~4   Á¬½ÓÀàÐÍ  Ô¶³Ì·þÎñÆ÷IPµØÖ·   Ô¶³Ì·þÎñÆ÷¶Ë¿ÚºÅ
	//while(esp8266_send_cmd("AT+CIPSTART=\"TCP\",\"116.62.235.218\",5479","CONNECT",200));
	while(esp8266_send_cmd("AT+CIPSTART=\"TCP\",\"IP\",702","CONNECT",200));
	//ÊÇ·ñ¿ªÆô͸´«Ä£Ê½  0£º±íʾ¹Ø±Õ 1£º±íʾ¿ªÆô͸´«
	esp8266_send_cmd("AT+CIPMODE=1","OK",200);
	
	//͸´«Ä£Ê½Ï ¿ªÊ¼·¢ËÍÊý¾ÝµÄÖ¸Áî Õâ¸öÖ¸ÁîÖ®ºó¾Í¿ÉÒÔÖ±½Ó·¢Êý¾ÝÁË
	esp8266_send_cmd("AT+CIPSEND","OK",50);
}

//ESP8266Í˳ö͸´«Ä£Ê½   ·µ»ØÖµ:0,Í˳ö³É¹¦;1,Í˳öʧ°Ü
//ÅäÖÃwifiÄ£¿é£¬Í¨¹ýÏëwifiÄ£¿éÁ¬Ðø·¢ËÍ3¸ö+£¨Ã¿¸ö+ºÅÖ®¼ä ³¬¹ý10ms,ÕâÑùÈÏΪÊÇÁ¬ÐøÈý´Î·¢ËÍ+£©
u8 esp8266_quit_trans(void)
{
	u8 result=1;
	u3_printf("+++");
	delay_ms(1000);					//µÈ´ý500msÌ«ÉÙ Òª1000ms²Å¿ÉÒÔÍ˳ö
	result=esp8266_send_cmd("AT","OK",20);//Í˳ö͸´«ÅжÏ.
	if(result)
		printf("quit_trans failed!");
	else
		printf("quit_trans success!");
	return result;
}


//ÏòESP8266·¢ËÍÃüÁî
//cmd:·¢Ë͵ÄÃüÁî×Ö·û´®;ack:ÆÚ´ýµÄÓ¦´ð½á¹û,Èç¹ûΪ¿Õ,Ôò±íʾ²»ÐèÒªµÈ´ýÓ¦´ð;waittime:µÈ´ýʱ¼ä(µ¥Î»:10ms)
//·µ»ØÖµ:0,·¢Ëͳɹ¦(µÃµ½ÁËÆÚ´ýµÄÓ¦´ð½á¹û);1,·¢ËÍʧ°Ü
u8 esp8266_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
	u8 res=0; 
	USART3_RX_STA=0;
	u3_printf("%s\r\n",cmd);	//·¢ËÍÃüÁî
	if(ack&&waittime)		//ÐèÒªµÈ´ýÓ¦´ð
	{
		while(--waittime)	//µÈ´ýµ¹¼Æʱ
		{
			delay_ms(10);
			if(USART3_RX_STA&0X8000)//½ÓÊÕµ½ÆÚ´ýµÄÓ¦´ð½á¹û
			{
				if(esp8266_check_cmd(ack))
				{
					printf("ack:%s\r\n",(u8*)ack);
					break;//µÃµ½ÓÐЧÊý¾Ý 
				}
					USART3_RX_STA=0;
			} 
		}
		if(waittime==0)res=1; 
	}
	return res;
} 


//ESP8266·¢ËÍÃüÁîºó,¼ì²â½ÓÊÕµ½µÄÓ¦´ð
//str:ÆÚ´ýµÄÓ¦´ð½á¹û
//·µ»ØÖµ:0,ûÓеõ½ÆÚ´ýµÄÓ¦´ð½á¹û;ÆäËû,ÆÚ´ýÓ¦´ð½á¹ûµÄλÖÃ(strµÄλÖÃ)
u8* esp8266_check_cmd(u8 *str)
{
	char *strx=0;
	if(USART3_RX_STA&0X8000)		//½ÓÊÕµ½Ò»´ÎÊý¾ÝÁË
	{ 
		USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//Ìí¼Ó½áÊø·û
		strx=strstr((const char*)USART3_RX_BUF,(const char*)str);
	} 
	return (u8*)strx;
}


//ÏòESP8266·¢ËÍÊý¾Ý
//cmd:·¢Ë͵ÄÃüÁî×Ö·û´®;waittime:µÈ´ýʱ¼ä(µ¥Î»:10ms)
//·µ»ØÖµ:·¢ËÍÊý¾Ýºó£¬·þÎñÆ÷µÄ·µ»ØÑéÖ¤Âë
u8* esp8266_send_data(u8 *cmd,u16 waittime)
{
	char temp[5];
	char *ack=temp;
	USART3_RX_STA=0;
	u3_printf("%s",cmd);	//·¢ËÍÃüÁî
	if(waittime)		//ÐèÒªµÈ´ýÓ¦´ð
	{
		while(--waittime)	//µÈ´ýµ¹¼Æʱ
		{
			delay_ms(10);
			if(USART3_RX_STA&0X8000)//½ÓÊÕµ½ÆÚ´ýµÄÓ¦´ð½á¹û
			{
				USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//Ìí¼Ó½áÊø·û
				ack=(char*)USART3_RX_BUF;
				printf("ack:%s\r\n",(u8*)ack);
				USART3_RX_STA=0;
				break;//µÃµ½ÓÐЧÊý¾Ý 
			} 
		}
	}
	return (u8*)ack;
} 

注释乱码了,将就着看吧,很简单就能看明白。

DHT11温湿度传感器代码

#include "dht11.h"
#include "delay.h"
      
//¸´Î»DHT11
void DHT11_Rst(void)	   
{                 
	DHT11_IO_OUT(); 	//SET OUTPUT
    DHT11_DQ_OUT=0; 	//À­µÍDQ
    delay_ms(20);    	//À­µÍÖÁÉÙ18ms
    DHT11_DQ_OUT=1; 	//DQ=1 
	delay_us(30);     	//Ö÷»úÀ­¸ß20~40us
}
//µÈ´ýDHT11µÄ»ØÓ¦
//·µ»Ø1:δ¼ì²âµ½DHT11µÄ´æÔÚ
//·µ»Ø0:´æÔÚ
u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	DHT11_IO_IN();//SET INPUT	 
    while (DHT11_DQ_IN&&retry<100)//DHT11»áÀ­µÍ40~80us
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=100)return 1;
	else retry=0;
    while (!DHT11_DQ_IN&&retry<100)//DHT11À­µÍºó»áÔÙ´ÎÀ­¸ß40~80us
	{
		retry++;
		delay_us(1);
	};
	if(retry>=100)return 1;	    
	return 0;
}
//´ÓDHT11¶ÁÈ¡Ò»¸öλ
//·µ»ØÖµ£º1/0
u8 DHT11_Read_Bit(void) 			 
{
 	u8 retry=0;
	while(DHT11_DQ_IN&&retry<100)//µÈ´ý±äΪµÍµçƽ
	{
		retry++;
		delay_us(1);
	}
	retry=0;
	while(!DHT11_DQ_IN&&retry<100)//µÈ´ý±ä¸ßµçƽ
	{
		retry++;
		delay_us(1);
	}
	delay_us(40);//µÈ´ý40us
	if(DHT11_DQ_IN)return 1;
	else return 0;		   
}
//´ÓDHT11¶ÁÈ¡Ò»¸ö×Ö½Ú
//·µ»ØÖµ£º¶Áµ½µÄÊý¾Ý
u8 DHT11_Read_Byte(void)    
{        
    u8 i,dat;
    dat=0;
	for (i=0;i<8;i++) 
	{
   		dat<<=1; 
	    dat|=DHT11_Read_Bit();
    }						    
    return dat;
}
//´ÓDHT11¶ÁÈ¡Ò»´ÎÊý¾Ý
//temp:ζÈÖµ(·¶Î§:0~50¡ã)
//humi:ʪ¶ÈÖµ(·¶Î§:20%~90%)
//·µ»ØÖµ£º0,Õý³£;1,¶Áȡʧ°Ü
u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
{        
 	u8 buf[5];
	u8 i;
	DHT11_Rst();
	if(DHT11_Check()==0)
	{
		for(i=0;i<5;i++)//¶ÁÈ¡40λÊý¾Ý
		{
			buf[i]=DHT11_Read_Byte();
		}
		if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
		{
			*humi=buf[0];
			*temp=buf[2];
		}
	}else return 1;
	return 0;	    
}
//³õʼ»¯DHT11µÄIO¿Ú DQ ͬʱ¼ì²âDHT11µÄ´æÔÚ
//·µ»Ø1:²»´æÔÚ
//·µ»Ø0:´æÔÚ    	 
u8 DHT11_Init(void)
{	 
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //ʹÄÜPG¶Ë¿ÚʱÖÓ
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				 //PG11¶Ë¿ÚÅäÖÃ
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //ÍÆÍìÊä³ö
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);				 //³õʼ»¯IO¿Ú
 	GPIO_SetBits(GPIOA,GPIO_Pin_0);						 //PG11 Êä³ö¸ß
			    
	DHT11_Rst();  //¸´Î»DHT11
	return DHT11_Check();//µÈ´ýDHT11µÄ»ØÓ¦
} 

同样乱码。。。。。

主函数部分

esp8266_start_trans();//esp8266½øÐгõʼ»¯
	while(1)
	{	    	    
 		if(t%1000==0)			//ÿ100ms¶ÁÈ¡Ò»´Î
		{									  
			DHT11_Read_Data(&temperature,&humidity);	//¶ÁÈ¡ÎÂʪ¶ÈÖµ					    
			LCD_ShowNum(30+40,150,temperature,2,16);	//ÏÔʾζÈ	   		   
			LCD_ShowNum(30+40,170,humidity,2,16);		//ÏÔʾʪ¶È	
	
			u3_printf("tem %d hum %d",temperature,humidity);
	
		}				   
	 	delay_ms(10);
		t++;
		if(t==20)
		{
			t=0;
			LED0=!LED0;
		}
	}
	esp8266_quit_trans();

部分代码,初始化那地方我没贴出来,这是esp8266传输代码。

服务器的设置和方案一都一样,这里就不再赘述,应用端也一样,参考方案一。

到这里,整个物联网实训结束。

以上仅供参考。

转载请说明出处。

发布了12 篇原创文章 · 获赞 19 · 访问量 5496

猜你喜欢

转载自blog.csdn.net/Hao_ge_666/article/details/103808563