在这篇文章中我将从头教大家如何自己做一个物联网的demo出来。这个demo对于初学者来说如果是一步步去探索的话真的挺费时间的,期间也会遇到各种各样的问题,这里呢我就把我自己探索的路子分享给大家(demo基于TCP/IP协议,只要配置正确不存在连不上网的问题,因为所有的网络都是支持TCP/IP的)
1.材料准备工作:
购买一块STM32开发板,一个ESP8266模块,杜邦线若干,租用一台阿里云服务器。
2.硬件连接:
ESP8266的VCC接3.3v(5v也还凑合,不至于烧坏)
ESP8266的GND接GND
ESP8266的TXD接板子USART的RXD引脚
ESP8266的RXD接板子USART的TXD引脚
RST引脚与IO_0引脚不需要控制,悬空即可
3.服务器准备
第一步:登陆阿里云官网租用一台服务器,我选用的是学生轻量应用服务器(9.5元每月),操作系统windows server2012R2
第二步:购买完成后会跳转轻量应用服务器的控制台页面,设置远程桌面:
我选用的是win10自带的远程连接工具:
通过账号密码连接桌面。
第三步:端口设置
如图,点击防火墙,为了方便我把TCP和UDP的所有端口都开放了。点击添加规则即可开放端口。
第四步:服务器端安装软件
我把我电脑的D盘通过映射网络驱动器的方式映射到服务器(配置很简单,百度一下你就知道),在服务器内可以直接把D盘的软件复制过去。这里我们需要JDK,eclips,如果后期想运行java web项目的话建议再安装一个tomcat,然后再安装一个MySQL数据库,安装的时候要勾上允许远程访问,端口默认3306就OK了。关于java环境的配置以及软件安装我就不写了,网上到处都是这方面的文章。
4.硬件端的代码编写
基本的原理参见我的上一篇博客(https://blog.csdn.net/naruhina/article/details/104211705),今天的代码与上篇博客的代码略有不同,加入了防TCP自己断掉的心跳包机制,每隔十秒发送一次,由定时器驱动。代码与说明详见上传的资源,这里只放主要代码.
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "usart3.h"
#include "common.h"
#include "StaConfig.h"
#include "timer3.h"
/************************************************
第一次测试完成日期:2020/2/7
第一次描述:通过USART3连接ESP8266的TX与RX。相关AT指令以及数据发送均通过USART3进行
主函数调用setClient设置为STAClient透传模式与阿里云服务器取得通信,通过在电脑上使用
串口助手发送字符串到串口1并在串口1中断服务函数内通过调用发送数据函数通过串口3发送给
ESP8266,然后再经由路由器发送到云服务器。云服务器端通过网络调试助手创建一个TCP SERVER
然后就可以向STM32发送数据了,接收到的数据由相应函数处理回显到串口调试助手内。
备注:发送成功后atk_8266_send_data函数返回了失败,但实际上发送成功,应该是第二个参数不对应
导致的,但无关紧要。并且程序内并没有调用退出透传的函数,重新烧录程序完成后掉一次电恢复一下。
心跳包机制尚未加入
************************************************/
/************************************************
第二次测试完成日期:2020/2/9
本次主要修改了发送数据受缓存影响的bug,以及与向服务器发送的时候没有0x0a,0x0d的bug。新增防止因长时间无操作的定时发送心跳包的功能,以及确定
后atk_8266_send_data函数无误,在透传模式下的返回值1是第二个参数不对应的误报,实际没任何错误。新增TCP Client 非透传模式
************************************************/
int main(void)
{
u8 ipbuf[50];
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
usart3_init(115200); //初始化串口3
TIM3_Int_Init(49999,14399);//5Khz的计数频率,计数到5000为1s
setClient(ipbuf);//TCP Client 透传模式
//setClientNonPierce(ipbuf);
while(1)
{
atk_8266_at_response(1);
delay_ms(100);
}
}
#include "StaConfig.h"
#include "common.h"
/***********************************************************/
//第一次测试完工日期:2020/2/7
//作者:lht
//2020/2/9 新增TCP Client 非透传模式
/***********************************************************/
void setClient(u8 *ipbuf)//设置ESP8266工作在STA模式的CLIENT设置并开启透传
{
//printf("AT恢复出厂设置:%d\n",atk_8266_send_cmd("AT+RESTORE","OK",1000));
printf("AT测试:%d\n",atk_8266_send_cmd("AT","OK",500));
printf("设置为STA模式:%d\n",atk_8266_send_cmd("AT+CWMODE=1","OK",500));//设置为STA模式
printf("重启:%d\n",atk_8266_send_cmd("AT+RST","OK",500));//重启生效
delay_ms(1000); //延时3S等待重启成功
delay_ms(1000);
delay_ms(1000);
printf("连接到路由器:%d\n",atk_8266_send_cmd("AT+CWJAP=\"FAST666\",\"你的密码\"","OK",1000));//连接路由器
printf("设置单链接:%d\n",atk_8266_send_cmd("AT+CIPMUX=0","OK",3500));
atk_8266_get_wanip(ipbuf);
printf("IP:%s\n",ipbuf); //端口号不要带引号,否则会有意想不到的错误。
printf("连接到服务器:%d\n",atk_8266_send_cmd("AT+CIPSTART=\"TCP\",\"服务器公网IP\",8086","OK",3000));//连接到SERVER
printf("开启透传模式:%d\n",atk_8266_send_cmd("AT+CIPMODE=1","OK",3500));//开启透传模式
printf("开始透传:%d\n",atk_8266_send_cmd("AT+CIPSEND","OK",550));//开始透
}
void setServer(u8 *ipbuf)//设置ESP8266工作在STA模式的SERVER设置
{
//printf("AT恢复出厂设置:%d\n",atk_8266_send_cmd("AT+RESTORE","OK",1000));
printf("AT测试:%d\n",atk_8266_send_cmd("AT","OK",500));
printf("设置为STA模式:%d\n",atk_8266_send_cmd("AT+CWMODE=1","OK",500));//设置为STA模式
printf("重启:%d\n",atk_8266_send_cmd("AT+RST","OK",500));//重启生效
delay_ms(1000); //延时3S等待重启成功
delay_ms(1000);
delay_ms(1000);
printf("连接到路由器:%d\n",atk_8266_send_cmd("AT+CWJAP=\"FAST666\",\"你的密码\"","OK",1000));//连接路由器
printf("设置多链接:%d\n",atk_8266_send_cmd("AT+CIPMUX=1","OK",3500));
printf("开启服务器:%d\n",atk_8266_send_cmd("AT+CIPSERVER=1,8086","OK",2000));
atk_8266_get_wanip(ipbuf);
printf("IP:%s\n",ipbuf);
printf("开始传输:%d\n",atk_8266_send_cmd("AT+CIPSEND=3,25","OK",550));//开始透传
}
void setClientNonPierce(u8 *ipbuf)//STA Client 非透传模式
{
printf("AT测试:%d\n",atk_8266_send_cmd("AT","OK",500));
printf("设置为STA模式:%d\n",atk_8266_send_cmd("AT+CWMODE=1","OK",500));//设置为STA模式
printf("重启:%d\n",atk_8266_send_cmd("AT+RST","OK",500));//重启生效
delay_ms(1000); //延时3S等待重启成功
delay_ms(1000);
delay_ms(1000);
printf("连接到路由器:%d\n",atk_8266_send_cmd("AT+CWJAP=\"FAST666\",\"你的密码\"","OK",1000));//连接路由器
printf("设置单链接:%d\n",atk_8266_send_cmd("AT+CIPMUX=0","OK",3500));
atk_8266_get_wanip(ipbuf);
printf("IP:%s\n",ipbuf); //端口号不要带引号,否则会有意想不到的错误。
printf("连接到服务器:%d\n",atk_8266_send_cmd("AT+CIPSTART=\"TCP\",\"服务器公网IP\",8086","OK",3000));//连接到SERVER
//printf("开启透传模式:%d\n",atk_8266_send_cmd("AT+CIPMODE=1","OK",3500));//开启透传模式
printf("允许数据传输:%d\n",atk_8266_send_cmd("AT+CIPSEND=8","OK",550));//允许数据发送,每次最大8字节
}
#include "common.h"
//4个网络模式
const u8 *ATK_ESP8266_CWMODE_TBL[3]={"STA模式 ","AP模式 ","AP&STA模式 "}; //ATK-ESP8266,3种网络模式,默认为路由器(ROUTER)模式
//4种工作模式
const u8 *ATK_ESP8266_WORKMODE_TBL[3]={"TCP服务器","TCP客户端"," UDP 模式"}; //ATK-ESP8266,4种工作模式
//5种加密方式
const u8 *ATK_ESP8266_ECN_TBL[5]={"OPEN","WEP","WPA_PSK","WPA2_PSK","WPA_WAP2_PSK"};
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//usmart支持部分
//将收到的AT指令应答数据返回给电脑串口
//mode:0,不清零USART3_RX_STA;
// 1,清零USART3_RX_STA;
void atk_8266_at_response(u8 mode)
{
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
printf("%s",USART3_RX_BUF); //发送到串口
if(mode)USART3_RX_STA=0;
}
}
//ATK-ESP8266发送命令后,检测接收到的应答
//str:期待的应答结果
//返回值:0,没有得到期待的应答结果
// 其他,期待应答结果的位置(str的位置)
u8* atk_8266_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;
}
//向ATK-ESP8266发送命令
//cmd:发送的命令字符串
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//waittime:等待时间(单位:10ms)
//返回值:0,发送成功(得到了期待的应答结果)
// 1,发送失败
u8 atk_8266_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(atk_8266_check_cmd(ack))
{
printf("ack:%s\r\n",(u8*)ack);
break;//得到有效数据
}
USART3_RX_STA=0;
}
}
if(waittime==0)res=1;
}
return res;
}
//向ATK-ESP8266发送指定数据
//data:发送的数据
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//waittime:等待时间(单位:10ms)
//返回值:0,发送成功(得到了期待的应答结果)
u8 atk_8266_send_data(u8 *data,u8 *ack,u16 waittime)
{
u8 res=0;
USART3_RX_STA=0;
u3_printf("%s\r\n",data); //发送数据,为了方便云服务器接收加入了回车换行符
if(ack&&waittime) //需要等待应答
{
while(--waittime) //等待倒计时
{
delay_ms(10);
if(USART3_RX_STA&0X8000)//接收到期待的应答结果
{
if(atk_8266_check_cmd(ack))break;//得到有效数据
USART3_RX_STA=0;
}
}
if(waittime==0)res=1;
}
return res;
}
//ATK-ESP8266退出透传模式
//返回值:0,退出成功;
// 1,退出失败
u8 atk_8266_quit_trans(void)
{
while((USART3->SR&0X40)==0); //等待发送空
USART3->DR='+';
delay_ms(15); //大于串口组帧时间(10ms)
while((USART3->SR&0X40)==0); //等待发送空
USART3->DR='+';
delay_ms(15); //大于串口组帧时间(10ms)
while((USART3->SR&0X40)==0); //等待发送空
USART3->DR='+';
delay_ms(500); //等待500ms
return atk_8266_send_cmd("AT","OK",20);//退出透传判断.
}
//获取ATK-ESP8266模块的AP+STA连接状态
//返回值:0,未连接;1,连接成功
u8 atk_8266_apsta_check(void)
{
if(atk_8266_quit_trans())return 0; //退出透传
atk_8266_send_cmd("AT+CIPSTATUS",":",50); //发送AT+CIPSTATUS指令,查询连接状态
if(atk_8266_check_cmd("+CIPSTATUS:0")&&
atk_8266_check_cmd("+CIPSTATUS:1")&&
atk_8266_check_cmd("+CIPSTATUS:2")&&
atk_8266_check_cmd("+CIPSTATUS:4"))
return 0;
else return 1;
}
//获取ATK-ESP8266模块的连接状态
//返回值:0,未连接;1,连接成功.
u8 atk_8266_consta_check(void)
{
u8 *p;
u8 res;
if(atk_8266_quit_trans())return 0; //退出透传
atk_8266_send_cmd("AT+CIPSTATUS",":",50); //发送AT+CIPSTATUS指令,查询连接状态
p=atk_8266_check_cmd("+CIPSTATUS:");
res=*p; //得到连接状态
return res;
}
//获取Client ip地址
//ipbuf:ip地址输出缓存区
void atk_8266_get_wanip(u8* ipbuf)
{
u8 *p,*p1;
if(atk_8266_send_cmd("AT+CIFSR","OK",500))//获取WAN IP地址失败
{
ipbuf[0]=0;
return;
}
p=atk_8266_check_cmd("\"");
p1=(u8*)strstr((const char*)(p+1),"\"");
*p1=0;
sprintf((char*)ipbuf,"%s",p+1);
}
u8 atk_8266_chaxun(u8 *cmd,u16 waittime)
{
u8 res=0;
USART3_RX_STA=0;
u3_printf("%s\r\n",cmd); //发送命令
if(waittime) //需要等待应答
{
while(--waittime) //等待倒计时
{
delay_ms(10);
if(USART3_RX_STA&0X8000)//接收到期待的应答结果
{
USART3_RX_STA=0;
printf("查询指令ack:%s\r\n",USART3_RX_BUF);
break;//得到有效数据
}
}
if(waittime==0)res=1;
}
return res;
}
5.服务器端
服务器端则是用Socket建立TCP连接并监听指定端口的信息,然后连接MySQL数据库把数据写进去。这里只放Socket部分,jdbc以及其他部分见上传的资源
package main;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import DB.DBTool;
import changeCode.ChangeToUtf8;
/**
*
*双向通信,首先硬件端向服务器发送数据,然后服务器端在控制台输入响应敲回车发送给硬件端完成一次应答。
*
*/
public class DuplexTrans {
public static void main(String[] args){
String sql;
Socket socket = null;
BufferedReader in = null;
BufferedWriter out = null;
BufferedReader br = null;
try {
//创建服务器端套接字:指定监听端口
ServerSocket server = new ServerSocket(8086);
//监听客户端的连接
socket = server.accept();
//获取socket的输入输出流接收和发送信息
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
//接收客户端发送的信息
String str = in.readLine();
str = ChangeToUtf8.toUTF8(str);//转换编码防止乱码
String str2 = "";
//如果客户端发送的是“end”则终止连接
if (str.equals("end")){
break;
}else if(str.equals("heartBeat")) {
}else {//非心跳包,发送反馈信息并写入数据库
System.out.println("客户端发来:" + str);
sql = "insert into iottest(content) values(?)";
DBTool.update(sql, str);
str2 = br.readLine(); // 读到\n为止,因此一定要输入换行符!
out.write(str2 + "\n");
out.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
6.测试
在左边远程连接窗口内可以看到控制台中输出了fromStm32,它是从本地的串口调试助手输入到USART1然后在到USART3,而USART3连接我的ESP8266。右边串口调试助手收到的fromServer是在服务器的eclipse的控制台输入的,数据到达ESP8266后发送给串口3,再发送到串口1让串口调试助手显示信息。
我们再来看看此时云端数据库内有没有被写入信息:
可以看到服务器收到的信息已经被写入了云端的数据库
完整代码:https://download.csdn.net/download/naruhina/12146142
顺带一提,可以建一个web项目,2.5版本即可,代码调试完成后导出一个war包放到服务器端的Tomcat里运行,这样就不用在服务器端启动eclipse来看控制台输出了。