一、什么是Web配网
就是我们需要联网的设备自己临时创建一个WIFI热点,用户连接该热点之后可以访问特定网页,在网页中输入想要连接的目标WIFI的名称和密码,提交后设备会自动连接到目标网络,如此一来我们就不需要每次通过程序调整要连接的WIFI名称和密码,十分的方便!
二、使用STM32实现Web配网步骤
初始化串口;
将ESP-01S配置成AP模式;
配置自身WIFI名称和密码;
设置DNS重定向;
启动HTTP服务器;
等待用户访问网页;
发送网页内容;
等待用户提交目标WIFI信息;
ESP-01S连接目标WIFI;
我们来看一下网页
三、固件烧录
我们使用的ESP-01S是需要烧录固件的,安信可官网上面的一些固件可能不支持DNS,所以博主这里选择的是烧录V2.2.1的固件,关于如何烧录固件以及固件烧录工具,大家可以到STM32外设系列WIFI模块查看,这里不再赘述了。
四、AT指令
这里介绍一下用到的一些AT指令
AT+CWMODE=2
配置为AP模式
AT+CWSAP=“ssid”,“password”,11,0
配置自身热点名称密码,这里密码其实是无效的,即使配置也不需要输入密码就可以直接连接
AT+CIPMUX=1
允许多连接
AT+CIPDNS=1,“ip”
设置DNS重定向,这里的IP就是后面在配网时手机端要访问的IP
AT+CIPSERVER=1,端口号
启动TCP服务
AT+CIPSEND=0,字节长度
发送网页
其他用到的指令就是一些比较常规的,比如配置成STA模式,连接WIFI,外设系列WIFI篇也有介绍,这里就不再赘述。
五、程序设计逻辑
程序设计时我们摒弃之前使用While循环来等待串口解析到正确指令的方法,使用switch状态机来实现,另外在解析指令时不再使用固定位置匹配的方法,使得程序更加稳定可靠,我们可以看一下状态机大概的样子
switch (gServerConfigStep)
{
// 配置为AP模式
case SETAPMODE:
break;
// 设置自身热点名称和密码
case SETSELFWIFI:
break;
// 允许多连接
case SETMULTIPLECONNECT:
break;
// DNS重定向
case DNSREDIRECT:
break;
// 启动TCP服务
case STARTTCPSER:
break;
// 等待手机端连接Wifi
case WAUTCONNECT:
break;
// 等待探测消息结束
case WAITPROBEEND:
break;
// 等待手机端请求
case WAITREQUEST:
break;
// 准备发送网页
case PREPARESEND:
break;
// 发送网页
case SENDHTML:
break;
// 等待获取输入的WIFI信息
case GETWIFIINFO:
break;
// 配置为STA模式
case SETSTAMODE:
break;
// 连接WIFI
case CONNECTWIFI:
break;
}
六、探测消息
在上面的状态机中我们可以看到有一个等待探测消息结束的状态,博主使用苹果手机、VIVO、小米三个品牌的手机进行测试,发现除了小米手机外,别的手机均不会发出探测消息,那什么是探测消息呢?
当手机连接到一个热点时会主动发送试探请求,用于检测网络是否可用,检测是否需要认证或者预加载浏览器主页。经过测试,小米手机在连接到ESP-01S的热点后几秒内手机端会弹出一个页面告知该网络无法上网,是否要保持连接,此时点击保持连接会发送出第一条探测消息,内容如下
0,CONNECT
+IPD,0,165:GET / HTTP/1.1
User-Agent: Dalvik/xxx (Linux; U; Android xx; xxxx Build/xxxxxxx)
Host: xxxxxx
Connection: Keep-Alive
Accept-Encoding: gzip
之后会重复不断地发送,发送大概四五次后停止发送,而苹果和VIVO手机则不会发出探测消息。
我们既然拿出一小节来单独介绍探测消息,那肯定说明他比较重要,实测在探测消息没有全部发送完成时,如果手机端访问IP,ESP-01S请求发送网页,即使发送成功,有时候手机端是无法显示网页内容的,所以博主选择等待探测消息全部发送完成后再开始发送网页等操作。
虽然解决了小米手机探测消息的问题,但是有些品牌的手机却没有发送探测消息这一步骤,所以程序设计上必须要灵活处理。
七、程序设计
本次设计只有一个业务函数,也就是Web配网,实际也就是填充我们上面介绍的状态机中每一部分的内容,我们来一起看一下
7.1 常规指令程序设计
这里的常规指令指的是比如发送一条指令,ESP-01S会回复OK的指令,通常指令不会很长,回复内容也比较简单,我们拿配置成AP模式来举例,看一下常规指令的发送和回复检测程序设计
// 配置为AP模式
case SETAPMODE:
if (gCommSendFlag == NOSEND)
{
sprintf ((char*)string,"AT+CWMODE=2\r\n");
USART2_Send(string,strlen((char*)string));
gCommSendFlag = SENT;
delay_ms(50);
}
// 判断是否接收到回复消息
if (gCommSendFlag == SENT && gUsart2EndFlag == 1)
{
if (strstr((char*)&USART2_RECEFIFO,"OK"))
{
gServerConfigStep = SETSELFWIFI;
// 复位指令发送标志位
gCommSendFlag = NOSEND;
// 清零时间计数变量
timeCunt = 0;
}
// 清除接收完成标志位
gUsart2EndFlag = 0;
// 清零接收计数变量
gReceCunt2 = 0;
}
// 计时
timeCunt = timeCunt + 1;
delay_ms(50);
// 超时未收到OK回复处理(3s)
if (timeCunt >= 60)
{
// 复位指令发送标志位
gCommSendFlag = NOSEND;
// 清零时间计数变量
timeCunt = 0;
}
break;
在程序设计时使用strlen来计算要发送的指令的长度,在接收检测中使用strstr来匹配其中的关键字,使得整个程序更加健壮。另外加入超时自动发送,在检测到3s内没有收到正确回复时会重新发送一次指令。当然如果在后续设计时想要更加友好,可以再加一个超时退出并提示的功能,设计思路是一样的。
7.2 检测WIFI连接程序设计
在检测手机端是否连接ESP-01S的热点时我么不需要发送指令,只需要等待接收消息即可,一旦有设备连接了我们的WIFI,我们就会收到提示
+STA_CONNECTED:"xxxxx"
+DIST_STA_IP:"xxxxx"
在程序设计时我们选择匹配+STA_CONNECTED来检测是否有设备连接到我们的WIFI
// 等待手机端连接Wifi
case WAUTCONNECT:
if (gUsart2EndFlag == 1)
{
delay_ms(100);
if (strstr((char*)&USART2_RECEFIFO,"+STA_CONNECTED:"))
{
gServerConfigStep = WAITPROBEEND;
// 复位指令发送标志位
gCommSendFlag = NOSEND;
// 清零时间计数变量
timeCunt = 0;
}
// 清除接收完成标志位
gUsart2EndFlag = 0;
// 清零接收计数变量
gReceCunt2 = 0;
}
break;
7.3 等待探测消息结束程序设计
终于来到了我们的重头戏,如何设计等待探测消息结束的程序
7.3.1 有探测消息的手机
有的手机比如博主测试的小米,会发送探测消息,针对会发送探测消息的手机我们设计一个计时程序,在规定时间内接收到探测消息就清零计时变量重新开始计时,如果在规定时间内没有接收到探测消息,则认为探测消息发送结束,至于如何确认检测到了探测消息,我们可以通过检测探测消息中的User-Agent: Dalvik来实现
7.3.2 没有探测消息的手机
针对不会发送探测消息的手机,我们再增加一个控制逻辑,当连续两次超时没有检测到探测消息,我们就认为该手机不会发送探测消息,直接进行下一步即可
7.3.3 等待探测消息结束程序源码
// 等待探测消息结束
case WAITPROBEEND:
// 判断是否接收到试探消息
if (gUsart2EndFlag == 1)
{
delay_ms(300);
if (strstr((char*)&USART2_RECEFIFO,"User-Agent: Dalvik"))
{
// 清零时间计数变量
timeCunt = 0;
// 收到探测消息标志位置1
prboeMassageFlag = 1;
}
// 清除接收完成标志位
gUsart2EndFlag = 0;
// 清零接收计数变量
gReceCunt2 = 0;
}
// 计时
timeCunt = timeCunt + 1;
delay_ms(50);
// 超时未收到探测消息(10s)
if (timeCunt >= 200)
{
// 已经收到一次探测消息
if (prboeMassageFlag)
{
// LED点亮提示试探消息已经结束
PCout(13) = 0;
// 跳转到等待访问网页
gServerConfigStep = WAITREQUEST;
// 复位指令发送标志位
gCommSendFlag = NOSEND;
// 清零接收到探测消息的标志位
prboeMassageFlag = 0;
}
// 超时2次未收到探测消息认为该手机端不发送探测消息或者没有点保持连接
else if (timeOutFlag == 1)
{
// LED点亮提示试探消息已经结束
PCout(13) = 0;
delay_ms(500);
PCout(13) = 1;
delay_ms(500);
PCout(13) = 0;
// 跳转到等待访问网页
gServerConfigStep = WAITREQUEST;
// 复位指令发送标志位
gCommSendFlag = NOSEND;
// 清零接收到探测消息的标志位
prboeMassageFlag = 0;
// 清零超时标志位
timeOutFlag = 0;
}
else
{
// 清零时间计数变量
timeCunt = 0;
timeOutFlag = 1;
}
}
break;
7.4 等待手机端请求程序设计
我们必须要等到手机端通过浏览器访问我们DNS设置的IP时才可以发送网页,手机端是无法自动弹出网页的,即使发送成功也不行。我们可以通过匹配以下的关键字来判断手机端是否正在主动通过浏览器访问我们的IPUpgrade-Insecure-Requests: 1,程序设计可以直接参考7.2小节检测WIFI连接程序设计,这里就不再赘述了。
7.5 发送网页程序设计
发送网页时我们需要先通过AT+CIPSEND=0,字节长度指令来告诉手机端我们准备发送多少字节的数据,之后再发送网页代码网页代码必须要符合串口发送的格式,我们贴一下网页的源码
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP8266 Wi-Fi Config</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #0066cc; }
form { max-width: 300px; margin: 0 auto; }
input, button { width: 100%; padding: 10px; margin: 8px 0; }
button { background: #0066cc; color: white; border: none; }
</style>
</head>
<body>
<h1>Wi-Fi Config</h1>
<form action="/connect" method="GET">
<input type="text" name="ssid" placeholder="Wi-Fi Name" required>
<input type="password" name="pass" placeholder="Password" required>
<button type="submit">Connect</button>
</form>
</body>
</html>
发送网页程序设计如下,在计算长度时要减去四,目前不清楚为什么,大家注意一下
// 发送网页
case SENDHTML:
if (gCommSendFlag == NOSEND)
{
USART2_Send((u8 *)gHtmlPage, strlen(gHtmlPage) - 4);
gCommSendFlag = SENT;
}
if (gUsart2EndFlag == 1)
{
delay_ms(100);
if (strstr((char*)&USART2_RECEFIFO,"SEND OK"))
{
gServerConfigStep = GETWIFIINFO;
// 复位指令发送标志位
gCommSendFlag = NOSEND;
// 清零时间计数变量
timeCunt = 0;
}
// 清除接收完成标志位
gUsart2EndFlag = 0;
// 清零接收计数变量
gReceCunt2 = 0;
}
break;
7.6 解析WIFI信息程序设计
当我们在网页中输入目标WIFI信息并点击连接时,我们会收到一条回复,其中就包含了我们输入的目标WIFI信息,我们需要解析出来,解析时使用strstr匹配关键字符,搭配memchr寻找索引,最后通过memcpy截取出WIFI名称和密码,关于这几个函数的介绍在外设系列GPS篇有介绍,我们就不再赘述了,直接看程序设计
// 等待获取输入的WIFI信息
case GETWIFIINFO:
if (gUsart2EndFlag == 1)
{
delay_ms(300);
// 匹配字符串
if (strstr((char*)&USART2_RECEFIFO,"ssid"))
{
// 解析WIFI名称和密码
index1 = memchr(USART2_RECEFIFO,'?',gReceCunt2);
index2 = memchr(USART2_RECEFIFO,'H',gReceCunt2);
memcpy(string,index1 + 1,index2 - index1);
// WIFI名称
index1 = memchr(string,'=',gReceCunt2);
index2 = memchr(string,'&',gReceCunt2);
memcpy(gWifiInfor.ssid,index1 + 1,index2 - index1 - 1);
// WIFI密码
index1 = memchr(index2,'=',gReceCunt2);
index2 = memchr(string,'H',gReceCunt2);
memcpy(gWifiInfor.password,index1 + 1,index2 - index1 - 2);
gServerConfigStep = SETSTAMODE;
// 复位指令发送标志位
gCommSendFlag = NOSEND;
// 清零时间计数变量
timeCunt = 0;
}
// 清除接收完成标志位
gUsart2EndFlag = 0;
// 清零接收计数变量
gReceCunt2 = 0;
}
break;
7.7 连接WIFI程序设计
最后就是连接目标WIFI了,比较简单,先配置成STA模式之后使用AT指令连接即可,这里不再赘述。
7.8 LED程序设计
本次设计使用的是STM32F103C8T6核心板搭配ESP-01S实现的Web配网,没有外加任何显示设备,为了能够有一个提示,我们使用板子自带的LED,如果你的手机会发送探测消息,当探测消息结束时LED会被点亮,此时访问IP即可,如果你的手机不会发送探测消息,LED会闪烁一下之后常亮,提示可以访问IP,最后连接到目标WIFI后会以500ms为间隔闪烁。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_45217381/article/details/147078325
|