欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

物联网专题39:微信智能配网

程序员文章站 2022-06-04 10:00:23
...

微信智能配网

当我们将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】

物联网专题39:微信智能配网

物联网专题39:微信智能配网

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函数:

物联网专题39:微信智能配网

下面是智能配网回调函数的函数原型:

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指针表示不同的含义,我们需要对其强制类型转换来获取每种状态的数据;

每种状态的意义如下说明:

物联网专题39:微信智能配网

下面是回调函数:

// 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通信等功能。

下载执行程序,结果如下:

物联网专题39:微信智能配网

提示我们无法连接WiFi,请使用微信配网,下面是配置设备上网界面;

物联网专题39:微信智能配网

成功连上WiFi,如下所示:

物联网专题39:微信智能配网

OLED显示的IP地址与串口调试助手一致。

物联网专题39:微信智能配网

为了验证是否一次配网长期有效,复位8266,结果如下所示:

物联网专题39:微信智能配网

这说明微信智能配网设置成功。我们可以在WiFi连接成功后,添加一些网络请求语句(如SNTP时间)等;

物联网专题39:微信智能配网

如图所示,WiFi连接成功后,获取NTP服务器时间;

物联网专题39:微信智能配网

 

 

相关标签: IoT