目标:用嵌入式开发板采集信息,用WiFi传输到云平台。 开发手机App或者微信小程序,实现云平台数据和信息的读取。可以开发Web页面,对采集的数据进行显示。
本组目标:设计并制造一个物联网温湿度及智能安防,可以实现页面或移动端实时查看传感器信息。
总体思路:硬件端采用stm32f103或者是arduino作为开发板,esp8266为通讯模块,温湿度传感器,超声波传感器,软件端采用Java监听,嵌入式C语言,JavaScript微信小程序。
器材:stm32f103c8开发板一个,arduino开发板一个,温湿度传感器,超声波传感器,服务器,杜邦线,面包板
为了实现这一目的,我分成了两个方案,第一个方案,arduino方案,第二个是stm32方案
我用word画了一个简单的结构图,有助于下面的理解。
仔细研读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=
第四步,数据接收及存储。服务器端设置端口监听,获取esp8266发送过来的数据,此处可以用java或者python写。建立端口监听之后,可以看到发送过来的数据。接下来进行数据处理,首先从发送的数据包中提取有用信息并存到数据库。
第五步,应用端显示数据。jsp界面调用数据库并实时显示温湿度及距离。移动端采用微信小程序调用数据库并实时显示。
这五步基本上完成了整个应用系统的开发。
接下来我贴上这次程序的源码。仅供参考,一些关键代码需要自己更改。
arduino源码
#include
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包--%>
距离及温湿度
历史距离及温湿度
编号
距离
湿度
温度
时间
<%
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("
");
%>
<%
Statement stmt2 = null;
ResultSet rs2 = null;
String sql2 = "SELECT * FROM test_post.esp8266;"; //查询语句
stmt2 = conn2.createStatement();
rs2 = stmt2.executeQuery(sql2);
out.print("距离及温湿度:");
out.print("
");
while (rs2.next()) {%>
<%=rs2.getString(1) %>
<%=rs2.getString(2) %>
<%=rs2.getString(3) %>
<%=rs2.getString(4) %>
<%=rs2.getString(5) %>
<%
}
}else{
out.print("连接失败!");
}
}catch (Exception e) {
//e.printStackTrace();
out.print("数据库连接异常!");
}
%>
移动端我采用的是微信小程序,这里只展示出关键代码,就是云函数连接数据库,这个问题解决了,剩下的页面显示就很简单了。
// 云函数入口文件
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页面图
移动端效果图
页面优化没有做好,咱们不需要花里胡哨,科技的简约美。
这就是方案一的全部步骤了。
方案二就是换了一种开发板,比起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传输代码。
服务器的设置和方案一都一样,这里就不再赘述,应用端也一样,参考方案一。
到这里,整个物联网实训结束。
以上仅供参考。
转载请说明出处。