物联网专题39:微信智能配网
微信智能配网
当我们将8266产品,从一个 WiFi 环境,拿到另一个 WiFi 环境中,如果没有智能配网功能,我们只能在程序中修改【WiFi名 + WiFi密码】,重新烧录程序,非常麻烦。8266配网的方式有很多,包括网页配网、APP配网、微信配网等。
1 实现原理:
a) SmartConfig过程中,ESP8266开启sniffer模式,监听它可以接收到的所有网络数据(不论数据是否打算发送给8266);
b) 用户通过【手机/电脑】广播发送【加密的SSID和PassWord】信息;
c) ESP8266抓取并解密空中【SSID和PassWord】信息,从而连接WiFi;
2 要想实现一次配网,长期有效:
a) wifi_set_opmode(0X01); // 设置为 STA 模式,并保存在flash中 注意:【不要设置SSID+PASS】
b) 8266每次连接 WiFi 时,检查 WiFi 连接情况,如果 WiFi 连接成功,则正常执行程序(SNTP/UDP/TCP/DNS);
c) 如果【WiFi名】【WiFi密码】错误,则进入微信智能配网模式。微信配网成功后,将【WiFi名】【WiFi密码】保存,正常执行程序;
d) 当使用微信配网成功后,只要WiFi环境不变,即使8266重新复位,他也能成功连接配置过的WiFi,正常执行程序;
配置过程
1 设置8266为 STA 模式,从指定Flash扇区读取WiFi参数(SSID+PASS),设置连接参数,然后连接WiFi;
#define Sector_STA_INFO 0x80 // 【STA参数】保存扇区
// ssid + password = 96字节
struct station_config
{
uint8 ssid[32];
uint8 password[64];
uint8 bssid_set; // Note: If bssid_set is 1, station will just connect to the router
// with both ssid[] and bssid[] matched. Please check about this.
uint8 bssid[6];
wifi_fast_scan_threshold_t threshold;
};
struct station_config STA_INFO; // 【STA】参数结构体
// ESP8266读取【外部Flash】中的【STA参数】(SSID/PASS),作为STA,连接WIFI
os_memset(&STA_INFO, 0, sizeof(struct station_config)); // STA_INFO = 0
spi_flash_read(Sector_STA_INFO * 4096, (uint32 *)&STA_INFO, 96); // 读出【STA参数】(SSID/PASS)
STA_INFO.ssid[31] = 0; // SSID最后添'\0'
STA_INFO.password[63] = 0; // APSS最后添'\0'
os_printf("\r\nSTA_INFO.ssid=%s\r\nSTA_INFO.password=%s\r\n", STA_INFO.ssid, STA_INFO.password);
wifi_set_opmode(0x01); // 设置为STA模式,并保存到Flash
wifi_station_set_config(&STA_INFO); // 设置STA参数
wifi_station_connect(); // ESP8266连接WIFI(这里,此句可省)
2 设置软件定时器,来查询WiFi连接;
OS_Timer_IP_Init_JX(1000, 1); // 定时查询8266连接WIFI情况
在定时器回调函数中,判断 WiFi 连接情况;
// IP定时检查的回调函数
void ICACHE_FLASH_ATTR OS_Timer_IP_cb(void)
{
u8 S_WIFI_STA_Connect; // WIFI接入状态标志
// 查询STA接入WIFI状态
//-----------------------------------------------------
S_WIFI_STA_Connect = wifi_station_get_connect_status();
//---------------------------------------------------
// Station连接状态表
// 0 == STATION_IDLE -------------- STATION闲置
// 1 == STATION_CONNECTING -------- 正在连接WIFI
// 2 == STATION_WRONG_PASSWORD ---- WIFI密码错误
// 3 == STATION_NO_AP_FOUND ------- 未发现指定WIFI
// 4 == STATION_CONNECT_FAIL ------ 连接失败
// 5 == STATION_GOT_IP ------------ 获得IP,连接成功
//---------------------------------------------------
// 成功接入WIFI
if (S_WIFI_STA_Connect == STATION_GOT_IP) // 判断是否获取IP
{
wifi_get_ip_info(STATION_IF, &ST_ESP8266_IP); // 获取8266_STA的IP地址
ESP8266_IP[0] = ST_ESP8266_IP.ip.addr; // IP地址高八位 == addr低八位
ESP8266_IP[1] = ST_ESP8266_IP.ip.addr >> 8; // IP地址次高八位 == addr次低八位
ESP8266_IP[2] = ST_ESP8266_IP.ip.addr >> 16; // IP地址次低八位 == addr次高八位
ESP8266_IP[3] = ST_ESP8266_IP.ip.addr >> 24; // IP地址低八位 == addr高八位
// 显示ESP8266的IP地址
os_printf("ESP8266_IP = %d.%d.%d.%d\n", ESP8266_IP[0], ESP8266_IP[1], ESP8266_IP[2], ESP8266_IP[3]);
OLED_ShowIP(24, 2, ESP8266_IP); // OLED显示ESP8266的IP地址
OLED_ShowString(0, 4, "Connect to WIFI ");
OLED_ShowString(0, 6, "Successfully ");
//-----------------------------------------------------------------------------------------------
// 接入WIFI成功后,LED快闪3次
for (; C_LED_Flash <= 5; C_LED_Flash++)
{
GPIO_OUTPUT_SET(GPIO_ID_PIN(4), (C_LED_Flash % 2));
delay_ms(100);
}
os_printf("\r\n---- ESP8266 Connect to WIFI Successfully ----\r\n");
os_timer_disarm(&OS_Timer_IP); // 关闭定时器
//*****************************************************
// WIFI连接成功,执行后续功能。 如:SNTP/UDP/TCP/DNS等
//*****************************************************
}
// ESP8266无法连接WIFI
else if (S_WIFI_STA_Connect == STATION_NO_AP_FOUND || // 未找到指定WIFI
S_WIFI_STA_Connect == STATION_WRONG_PASSWORD || // WIFI密码错误
S_WIFI_STA_Connect == STATION_CONNECT_FAIL) // 连接WIFI失败
{
os_timer_disarm(&OS_Timer_IP); // 关闭定时器
os_printf("\r\n---- S_WIFI_STA_Connect=%d-----------\r\n", S_WIFI_STA_Connect);
os_printf("\r\n---- ESP8266 Can't Connect to WIFI-----------\r\n");
// 微信智能配网设置
//wifi_set_opmode(STATION_MODE); // 设为STA模式
smartconfig_set_type(SC_TYPE_AIRKISS); // ESP8266配网方式【AIRKISS】
smartconfig_start(smartconfig_done); // 进入【智能配网模式】,并设置回调函数
}
}
在定时器回调函数中,首先获取 WiFi 连接状态,如果连接成功,则打印WiFi参数,如果连接失败,则设置ESP8266配网模式为AIRKISS(微信硬件平台),然后进入配网模式;
3 配网模式回调函数
先看下智能配网的API函数:
下面是智能配网回调函数的函数原型:
typedef enum
{
SC_STATUS_WAIT = 0,
SC_STATUS_FIND_CHANNEL,
SC_STATUS_GETTING_SSID_PSWD,
SC_STATUS_LINK,
SC_STATUS_LINK_OVER,
} sc_status;
typedef void (*sc_callback_t)(sc_status status, void *pdata);
可以看到,这个函数传入两个参数,其中参数1为配网状态,参数2为 void 类型指针;
为什么为 void ?这是因为在不同状态时,pdata指针表示不同的含义,我们需要对其强制类型转换来获取每种状态的数据;
每种状态的意义如下说明:
下面是回调函数:
// SmartConfig状态发生改变时,进入此回调函数
// 参数1:sc_status status / 参数2:无类型指针【在不同状态下,[void *pdata]的传入参数是不同的】
void ICACHE_FLASH_ATTR smartconfig_done(sc_status status, void *pdata)
{
os_printf("\r\n------ smartconfig_done ------\r\n"); // ESP8266网络状态改变
switch (status)
{
// SmartConfig等待
case SC_STATUS_WAIT: // 初始值
os_printf("\r\nSC_STATUS_WAIT\r\n");
break;
// 发现【WIFI信号】(8266在这种状态下等待配网)
case SC_STATUS_FIND_CHANNEL:
os_printf("\r\nSC_STATUS_FIND_CHANNEL\r\n");
os_printf("\r\n---- Please Use WeChat to SmartConfig ------\r\n\r\n");
OLED_ShowString(0, 4, "Use WeChat to ");
OLED_ShowString(0, 6, "SmartConfig ");
break;
// 正在获取【SSID】【PSWD】(8266正在抓取并解密【SSID+PSWD】)
case SC_STATUS_GETTING_SSID_PSWD:
os_printf("\r\nSC_STATUS_GETTING_SSID_PSWD\r\n");
// 【SC_STATUS_GETTING_SSID_PSWD】状态下,参数2==SmartConfig类型指针
//-------------------------------------------------------------------
sc_type *type = pdata; // 获取【SmartConfig类型】指针
// 配网方式 == 【ESPTOUCH】
if (*type == SC_TYPE_ESPTOUCH)
{
os_printf("\r\nSC_TYPE:SC_TYPE_ESPTOUCH\r\n");
}
// 配网方式 == 【AIRKISS】||【ESPTOUCH_AIRKISS】
else
{
os_printf("\r\nSC_TYPE:SC_TYPE_AIRKISS\r\n");
}
break;
// 成功获取到【SSID】【PSWD】,保存STA参数,并连接WIFI
case SC_STATUS_LINK:
os_printf("\r\nSC_STATUS_LINK\r\n");
// 【SC_STATUS_LINK】状态下,参数2 == STA参数结构体指针
struct station_config *sta_conf = pdata; // 获取【STA参数】指针
// 将【SSID】【PASS】保存到【外部Flash】中
spi_flash_erase_sector(Sector_STA_INFO); // 擦除扇区
spi_flash_write(Sector_STA_INFO * 4096, (uint32 *)sta_conf, 96); // 写入扇区
wifi_station_set_config(sta_conf); // 设置STA参数【Flash】
wifi_station_disconnect(); // 断开STA连接
wifi_station_connect(); // ESP8266连接WIFI
OLED_ShowString(0, 4, "WIFI Connecting "); // OLED显示:
OLED_ShowString(0, 6, "................"); // 正在连接WIFI
break;
// ESP8266作为STA,成功连接到WIFI
case SC_STATUS_LINK_OVER:
os_printf("\r\nSC_STATUS_LINK_OVER\r\n");
smartconfig_stop(); // 停止SmartConfig,释放内存
//****************************************************************************
wifi_get_ip_info(STATION_IF, &ST_ESP8266_IP); // 获取8266_STA的IP地址
ESP8266_IP[0] = ST_ESP8266_IP.ip.addr; // IP地址高八位 == addr低八位
ESP8266_IP[1] = ST_ESP8266_IP.ip.addr >> 8; // IP地址次高八位 == addr次低八位
ESP8266_IP[2] = ST_ESP8266_IP.ip.addr >> 16; // IP地址次低八位 == addr次高八位
ESP8266_IP[3] = ST_ESP8266_IP.ip.addr >> 24; // IP地址低八位 == addr高八位
// 显示ESP8266的IP地址
os_printf("ESP8266_IP = %d.%d.%d.%d\n", ESP8266_IP[0], ESP8266_IP[1], ESP8266_IP[2], ESP8266_IP[3]);
OLED_ShowIP(24, 2, ESP8266_IP); // OLED显示ESP8266的IP地址
OLED_ShowString(0, 4, "Connect to WIFI ");
OLED_ShowString(0, 6, "Successfully ");
// 接入WIFI成功后,LED快闪3次
for (; C_LED_Flash <= 5; C_LED_Flash++)
{
GPIO_OUTPUT_SET(GPIO_ID_PIN(4), (C_LED_Flash % 2));
delay_ms(100);
}
os_printf("\r\n---- ESP8266 Connect to WIFI Successfully ----\r\n");
//*****************************************************
// WIFI连接成功,执行后续功能。 如:SNTP/UDP/TCP/DNS等
//*****************************************************
//****************************************************************************
break;
}
}
先判断微信配网的连接方式,然后当获取到SSID + PASSWORD时,将WiFi数据保存到Flash,当成功连接上WiFi时,打印ESP8266的IP地址,并可以获取SNTP、TCP通信等功能。
下载执行程序,结果如下:
提示我们无法连接WiFi,请使用微信配网,下面是配置设备上网界面;
成功连上WiFi,如下所示:
OLED显示的IP地址与串口调试助手一致。
为了验证是否一次配网长期有效,复位8266,结果如下所示:
这说明微信智能配网设置成功。我们可以在WiFi连接成功后,添加一些网络请求语句(如SNTP时间)等;
如图所示,WiFi连接成功后,获取NTP服务器时间;
上一篇: 扶着方向盘把脚抬起来了
下一篇: 微信机器人简单实现