写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
SmartConfig是什么?就是人们常说一键智能配网,通常用的最多的就是我们用手机去连 WiFi,而 8266 SDK开发上也有智能配网这个功能,那么,它是怎么实现的呢?首先我们需要了解在 8266中有两种智能配网方式,一个是用 ESPTouch,另一个是用AirKiss(飞吻),在开发中是可以两种同时使用的,然后下面我们来分析一下代码吧!
/******************************************************************************
* FunctionName : smartconfig_task
* Description : smartconfig任务
* Parameters : none
* Returns : none
*******************************************************************************/
void ICACHE_FLASH_ATTR smartconfig_task(void *pvParameters)
{
wifi_station_disconnect(); // 先关闭连接
os_printf("\n>>>>>>>>>> enter smartconfig mode\n");
// os_timer_disarm(&WifiLED_250ms);
// os_timer_setfn(&WifiLED_250ms, (os_timer_func_t *)wifiled_state_cb, NULL);
// os_timer_arm(&WifiLED_250ms, 250, 1);
smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS); // 设置快连模式类型(ESPTOUCH和AIRKISS同时使用),必须在 smartconfig_start之前调用
smartconfig_start(smartconfig_done);
os_printf("\n>>>>>>>>>> smartconfig distribution network wait.....\n");
while(1)
{
if (wifi_station_get_connect_status() == STATION_GOT_IP) // 等待连接到 AP
{
// GLED_ON();
break;
}
vTaskDelay(200 / portTICK_RATE_MS);
}
os_printf("\n>>>>>>>>>> esp8266 distribution network success\n\n");
vTaskDelete(NULL);
}
简单说下上面的流程:首先,额,先关闭 WiFi的连接状态(不管有没有连,因为不知道在开机的时候有没有自动连上,连上了就没必要进行这个配网了,除非你想换个 WiFi连接,额这个不深究了);然后配置它的模式,有三种(选择你需要的),在程序里面自己可以定位去看看;最后不用说了,一直等待,直到连接完成,然后状态什么的,可以在串口打印数据里面可以观察,当然,你一可以添加物理上的指示,例如我注释掉的那些 LED对应状态的代码;值得注意的是 smartconfig_start(smartconfig_done); 这个式子里面,我们要配置 smartconfig_done函数,这个函数是用来分析在连接过程中所处的状态,代码如下
/******************************************************************************
* FunctionName : smartconfig_done
* Description : smartconfig 状态发生改变时,进入该函数
* Parameters : status
* pdata
* Returns : none
*******************************************************************************/
void ICACHE_FLASH_ATTR smartconfig_done(sc_status status, void *pdata)
{
switch(status) {
/* 连接未开始,请勿在此阶段开始连接 */
case SC_STATUS_WAIT:
printf("SC_STATUS_WAIT\n");
break;
/* 发现信道,请在此阶段开启 APP进行配对 */
case SC_STATUS_FIND_CHANNEL:
printf("SC_STATUS_FIND_CHANNEL\n");
break;
/* 获取到 Wi-Fi名称和密码 */
case SC_STATUS_GETTING_SSID_PSWD:
printf("SC_STATUS_GETTING_SSID_PSWD\n");
sc_type *type = pdata;
/* 判断类型,在这一步发来的数据 pdata中应该有配置类型 */
if (*type == SC_TYPE_ESPTOUCH) {
printf("SC_TYPE:SC_TYPE_ESPTOUCH\n");
} else {
printf("SC_TYPE:SC_TYPE_AIRKISS\n");
}
break;
/* 开始连接 Wi-Fi */
case SC_STATUS_LINK:
printf("SC_STATUS_LINK\n");
struct station_config *sta_conf = pdata;
wifi_station_set_config(sta_conf); // 设置配置并保存到 flash
wifi_station_disconnect();
wifi_station_connect();
break;
/* 获取到IP,连接路由完成 */
case SC_STATUS_LINK_OVER:
printf("SC_STATUS_LINK_OVER\n");
/* 如果使用的是 SmartConfig,此时手机会将自己的 IP地址发给ESP8266 */
if (pdata != NULL) {
//SC_TYPE_ESPTOUCH
uint8 phone_ip[4] = {0};
memcpy(phone_ip, (uint8*)pdata, 4);
printf("Phone ip: %d.%d.%d.%d\n",phone_ip[0],phone_ip[1],phone_ip[2],phone_ip[3]);
}
/* 如果是使用的 Airkiss方式,到这一步还没有完成,还会跟微信进行数据交互,应该告知微信配网完成之类的 */
else {
//SC_TYPE_AIRKISS - support airkiss v2.0
airkiss_start_discover(); // 开始 Airkiss内网发现
}
smartconfig_stop(); // SmartConfig 完成,停止配置
break;
}
}
好了,到这里,如果是用 ESPTOUCH进行连接的话,基本没问题了,若是用 Airkiss的话,还需要以下这些代码(即调用 airkiss_start_discover(void) 函数)
/******************************************************************************
* FunctionName : airkiss_start_discover
* Description : airkiss内网发现
* Parameters : none
* Returns : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR airkiss_start_discover(void)
{
ssdp_udp.local_port = DEFAULT_LAN_PORT; // 设置本地端口
pssdpudpconn.type = ESPCONN_UDP; // 设置通信方式为 UDP
pssdpudpconn.proto.udp = &(ssdp_udp);
espconn_regist_recvcb(&pssdpudpconn, airkiss_wifilan_recv_callback); // 注册收到数据回调函数
espconn_create(&pssdpudpconn); // 创建一个UDP传输
os_timer_disarm(&ssdp_time_serv);
os_timer_setfn(&ssdp_time_serv, (os_timer_func_t *)airkiss_wifilan_time_callback, NULL); //注册airkiss定时回调函数
os_timer_arm(&ssdp_time_serv, 1000, 1); // 1s
}
然后,这里有个注册回调函数和定时回调函数需要处理
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "espressif/espconn.h"
#include "espressif/airkiss.h"
#define DEVICE_TYPE "gh_9e2cff3dfa51" //wechat public number
#define DEVICE_ID "122475" //model ID
#define DEFAULT_LAN_PORT 12476
LOCAL esp_udp ssdp_udp;
LOCAL struct espconn pssdpudpconn;
LOCAL os_timer_t ssdp_time_serv;
uint8 lan_buf[200];
uint16 lan_buf_len;
uint8 udp_sent_cnt = 0;
/*===================================
* Smartconfig Airkiss FUNCTIONS *
====================================*/
const airkiss_config_t akconf =
{
(airkiss_memset_fn)&memset,
(airkiss_memcpy_fn)&memcpy,
(airkiss_memcmp_fn)&memcmp,
0,
};
/******************************************************************************
* FunctionName : airkiss_wifilan_time_callback
* Description : airkiss定时回调
* Parameters : arg
* pdata
* len
* Returns : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR airkiss_wifilan_time_callback(void)
{
uint16 i;
airkiss_lan_ret_t ret;
/* airkiss_wifilan_time_callback运行三十次后停止运行 */
if ((udp_sent_cnt++) >30) {
udp_sent_cnt = 0;
os_timer_disarm(&ssdp_time_serv);//s
//return;
}
/* 设置本地udp发送端口号,IP地址设置成255.255.255.255是进行广播,UDP有单播,多播和广播三种模式 */
ssdp_udp.remote_port = DEFAULT_LAN_PORT;
ssdp_udp.remote_ip[0] = 255;
ssdp_udp.remote_ip[1] = 255;
ssdp_udp.remote_ip[2] = 255;
ssdp_udp.remote_ip[3] = 255;
lan_buf_len = sizeof(lan_buf);
ret = airkiss_lan_pack(AIRKISS_LAN_SSDP_NOTIFY_CMD,
DEVICE_TYPE, DEVICE_ID, 0, 0, lan_buf, &lan_buf_len, &akconf);
if (ret != AIRKISS_LAN_PAKE_READY) {
os_printf("Pack lan packet error!");
return;
}
ret = espconn_sendto(&pssdpudpconn, lan_buf, lan_buf_len);
if (ret != 0) {
os_printf("UDP send error!");
}
os_printf("Finish send notify!\n"); // UDP发送完成
}
/******************************************************************************
* FunctionName : airkiss_wifilan_recv_callback
* Description : airkiss无线局域网接收回调
* Parameters : arg
* pdata
* len
* Returns : none
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR airkiss_wifilan_recv_callback(void *arg, char *pdata, unsigned short len)
{
uint16 i;
remot_info* pcon_info = NULL;
airkiss_lan_ret_t ret = airkiss_lan_recv(pdata, len, &akconf);
airkiss_lan_ret_t packret;
switch (ret){
case AIRKISS_LAN_SSDP_REQ:
espconn_get_connection_info(&pssdpudpconn, &pcon_info, 0);
os_printf("remote ip: %d.%d.%d.%d \r\n",pcon_info->remote_ip[0],pcon_info->remote_ip[1],
pcon_info->remote_ip[2],pcon_info->remote_ip[3]);
os_printf("remote port: %d \r\n",pcon_info->remote_port);
pssdpudpconn.proto.udp->remote_port = pcon_info->remote_port;
memcpy(pssdpudpconn.proto.udp->remote_ip,pcon_info->remote_ip,4);
ssdp_udp.remote_port = DEFAULT_LAN_PORT;
lan_buf_len = sizeof(lan_buf);
packret = airkiss_lan_pack(AIRKISS_LAN_SSDP_RESP_CMD,
DEVICE_TYPE, DEVICE_ID, 0, 0, lan_buf, &lan_buf_len, &akconf);
if (packret != AIRKISS_LAN_PAKE_READY) {
os_printf("Pack lan packet error!");
return;
}
os_printf("\r\n\r\n");
for (i=0; i
最后把代码从下往上添加就可以了
嗯嗯,到这里,一个完整的 SmartConfig配置程序就分析完了,最后说下要注意的是,如果使用 Airkiss 进行配网,那么DEVICE_TYPE跟 DEVICE_ID这两个宏要根据你要用的微信公众号所提供的信息去填写,具体看一下官方的解释及使用(戳这里);接着,还得注意的是,这次我们是不是用了 SmartConfig所提供的库,那么,嘿嘿,知道怎么做了吧,在 Makefile里要添加进去哦,不懂的前面的篇章
噢,忘了给出 ESP-TOUCH的资料了