WIFI模块开发教程之W600网络篇6:Airkiss配网
发布日期:2021-05-15 00:02:30 浏览次数:26 分类:精选文章

本文共 7632 字,大约阅读时间需要 25 分钟。

如何使用微信Airkiss协议实现路由器配网

前言

本文探讨如何通过Airkiss协议实现设备与路由器的无线网络配网配置。配网即设备通过接收特定数据包,获取所需的路由器SSID和密码等信息。本文将从理论、实例分析以及具体配置等多个方面展开讨论。

一、理论基础

1. Airkiss协议原理

Airkiss协议是一种无线网络配网技术,核心理念为:设备处于混杂模式监听无线网络数据包,每隔一定时间发送广播包或组播包。通过对无线包的长度域(Length)进行编码传输,路由器转发数据包,当设备与路由器处于同一信道时,便可接收有效数据。与市面上的一键配网类似,Airkiss通过对length字段进行编码传输路由器信息给设备,这既是技术本质也是实现的关键。

2. Airkiss配网流程

Airkiss配网流程主要包含以下步骤:

  • 设备信道切换:设备持续切换信道监听无线数据包。
  • 信道锁定:设备接收到有效数据包后,锁定信道不再切换频道。
  • 信息编码传输:设备接收路由器发送的长度域数据包,解析其中编码的SSID和密码。
  • 配网完成:设备完成配网配置后,与路由器建立稳定连接。
  • 二、使用实例

    1. 程序分析

    1.1 设备信道切换
    static void airkiss_switch_channel(void *parameter)  
    {
    g_current_channel++;
    if (g_current_channel > MAX_CHANNEL_NUM)
    {
    g_current_channel = 1;
    }
    rt_wlan_set_channel(g_wlan_device, g_current_channel);
    airkiss_change_channel(ak_contex);
    AIRKISS_PRINTF("Switch channel %d \n", g_current_channel);
    }
    1.2 混杂包监听回调函数
    static void airkiss_monitor_callback(uint8_t *data, int len, void *user_data)  
    {
    airkiss_recv_ret = airkiss_recv(ak_contex, data, len);
    if (airkiss_recv_ret == AIRKISS_STATUS_CHANNEL_LOCKED)
    {
    rt_timer_stop(g_switch_timer);
    AIRKISS_PRINTF("Lock channel in %d \n", g_current_channel);
    rt_timer_start(g_doing_timer);
    }
    else if (airkiss_recv_ret == AIRKISS_STATUS_COMPLETE)
    {
    rt_timer_stop(g_doing_timer);
    rt_sem_release(g_cfg_done_sem);
    AIRKISS_PRINTF("AIRKISS_STATUS_COMPLETE \n");
    }
    }
    1.3 发送配网后收到的random,通知APP配网成功
    static void airkiss_send_notification_thread(void *parameter)  
    {
    int sock = -1;
    int udpbufsize = 2;
    uint8_t random = (uint32_t)parameter;
    struct sockaddr_in g_stUDPBCAddr, g_stUDPBCServerAddr;
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
    AIRKISS_PRINTF("notify create socket error!\n");
    goto _exit;
    }
    g_stUDPBCAddr.sin_family = AF_INET;
    g_stUDPBCAddr.sin_port = htons(10000);
    g_stUDPBCAddr.sin_addr.s_addr = htonl(0xffffffff);
    rt_memset(&g_stUDPBCAddr.sin_zero, 0, sizeof(g_stUDPBCAddr.sin_zero));
    g_stUDPBCServerAddr.sin_family = AF_INET;
    g_stUDPBCServerAddr.sin_port = htons(10000);
    g_stUDPBCServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    rt_memset(&g_stUDPBCServerAddr.sin_zero, 0, sizeof(g_stUDPBCServerAddr.sin_zero));
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &udpbufsize, sizeof(int)) != 0)
    {
    AIRKISS_PRINTF("notify socket setsockopt error\n");
    goto _exit;
    }
    if (bind(sock, (struct sockaddr *)&g_stUDPBCServerAddr, sizeof(g_stUDPBCServerAddr)) != 0)
    {
    AIRKISS_PRINTF("notify socket bind error\n");
    goto _exit;
    }
    for (int i = 0; i <= 20; i++)
    {
    int ret = sendto(sock, (char *)&random, 1, 0, (struct sockaddr *)&g_stUDPBCAddr, sizeof(g_stUDPBCAddr));
    rt_thread_delay(10);
    }
    AIRKISS_PRINTF("airkiss notification thread exit!\n");
    _exit:
    if (sock >= 0)
    {
    close(sock);
    }
    }
    1.4 配网入口函数
    static void airkiss_thread_entry(void *parameter)  
    {
    int result;
    g_switch_timer = rt_timer_create("switch_channel", airkiss_switch_channel, RT_NULL, AIRKISS_SWITCH_TIMER, RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_PERIODIC);
    if (!g_switch_timer)
    {
    rt_kprintf("Create airkiss swtich channel timer failed \n");
    goto _exit;
    }
    g_doing_timer = rt_timer_create("doing_timeout", airkiss_doing_timeout, RT_NULL, AIRKISS_DOING_TIMER, RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_ONE_SHOT);
    if (!g_doing_timer)
    {
    rt_kprintf("Create airkiss doing timeout timer failed \n");
    goto _exit;
    }
    g_cfg_done_sem = rt_sem_create("tlink", 0, RT_IPC_FLAG_FIFO);
    if (!g_cfg_done_sem)
    {
    rt_kprintf("Create airkiss config done sem failed! \n");
    goto _exit;
    }
    ak_contex = (airkiss_context_t *)rt_malloc(sizeof(airkiss_context_t));
    if (!ak_contex)
    {
    rt_kprintf("Malloc memory for airkiss context \n");
    goto _exit;
    }
    result = airkiss_init(ak_contex, &ak_conf);
    if (result != RT_EOK)
    {
    rt_kprintf("Airkiss init failed!!\r\n");
    goto _exit;
    }
    AIRKISS_PRINTF("Airkiss version: %s\r\n", airkiss_version());
    g_wlan_device = (struct rt_wlan_device *)rt_device_find(WIFI_DEVICE_STA_NAME);
    if (g_wlan_device == RT_NULL)
    {
    rt_kprintf("Device not found\n");
    return;
    }
    g_current_channel = 1;
    rt_wlan_set_channel(g_wlan_device, g_current_channel);
    rt_wlan_set_monitor_callback(g_wlan_device, airkiss_monitor_callback);
    rt_wlan_cfg_monitor(g_wlan_device, WIFI_MONITOR_START);
    rt_timer_start(g_switch_timer);
    if (rt_sem_take(g_cfg_done_sem, rt_tick_from_millisecond(1000 * 90)) != RT_EOK)
    {
    AIRKISS_PRINTF("Wait semaphore timeout \n");
    }
    if (airkiss_recv_ret == AIRKISS_STATUS_COMPLETE)
    {
    int8_t err;
    int8_t tick = 0;
    airkiss_result_t result;
    err = airkiss_get_result(ak_contex, &result);
    if (err == 0)
    {
    AIRKISS_PRINTF("airkiss_get_result() ok!\n");
    AIRKISS_PRINTF(" ssid = %s \n pwd = %s \n, ssid_length = %d \n pwd_length = %d \n, random = 0x%02x\r\n",
    result.ssid, result.pwd, result.ssid_length, result.pwd_length, result.random);
    }
    rt_wlan_cfg_monitor(g_wlan_device, WIFI_MONITOR_STOP);
    rt_wlan_set_monitor_callback(g_wlan_device, RT_NULL);
    station_connect(result.ssid, result.pwd);
    do
    {
    tick++;
    rt_thread_delay(rt_tick_from_millisecond(1000));
    if (tick >= 30)
    {
    rt_kprintf("GET IP Time Out!!! \n");
    goto _exit;
    }
    }
    while (!get_wifi_status(g_wlan_device->parent.netif));
    {
    rt_thread_t tid;
    tid = rt_thread_create("air_echo", airkiss_send_notification_thread, (void *)result.random, 1536, RT_THREAD_PRIORITY_MAX - 3, 20);
    if (tid != RT_NULL)
    {
    rt_thread_startup(tid);
    }
    }
    }
    _exit:
    if (g_switch_timer != RT_NULL)
    {
    rt_timer_stop(g_switch_timer);
    rt_timer_delete(g_switch_timer);
    }
    if (g_doing_timer != RT_NULL)
    {
    rt_timer_stop(g_doing_timer);
    rt_timer_delete(g_doing_timer);
    }
    if (ak_contex != RT_NULL)
    {
    rt_free(ak_contex);
    ak_contex = RT_NULL;
    }
    if (g_cfg_done_sem != RT_NULL)
    {
    rt_sem_delete(g_cfg_done_sem);
    g_cfg_done_sem = RT_NULL;
    }
    }

    2. 配置

    配置步骤一:组件下载
  • 下载smartconfig包,从GitHub下载获取。
  • smartconfig文件放在支持板的 BSP.Publish Ginger PA6/PA6X 系统路径下。
  • 打开 rtconfig.h 文件,在文件尾部添加以下宏定义:
  • #define RT_CFG_USE_AIRKISS 1  
    #define RT_CFG_AIRKISS_TX_CHANNEL 6
    #define RT_CFG_AIRKISS_RX_CHANNEL 7
    #define RT_CFG_AIRKISS_FEATURE_LOG 1
    配置步骤二:编译与配置
  • 打开项目开发环境中的 SConscript 文件,找到 2-net/6-config_airkiss 文件夹。
  • 添加文件:
  • Import('RTT_ROOT')Import('rtconfig')  
    from building import *
    cwd = GetCurrentDir()
    src = Glob('2-net/6-config_airkiss/*.c')
    CPPPATH = [cwd]
    group = DefineGroup('Applications', src, depend = [''])
    CPPPATH = CPPPATH + [group.CPPPATH]
    Return('group')

    三、下载与运行

    下载

    通过ancode build工具获取最新版 rtthread_1M.FLS Bin文件。

    烧录

    将固件烧录至开发板。

    配网流程

  • 运行配网功能:
  • 在APP中进入配网界面:
  • 待待接收设备配网结果。如果配置成功,设备将通过UDP广播发送随机数,通知APP完成配网。
  • 四、结语

    总结

  • 在实际应用中,需注意以下几点:

    • 定期更新配置文件版本,避免因版本冲突导致配网失败。
    • 此方法依赖于无线环境稳定性,安静的网络环境更有利于成功配网。
    • 建议将配网逻辑放在专用线程处理,避免影响主线程性能。
  • 如需进一步了解或调试,可访问相关技术社区或开发者论坛,如:

  • 以上内容可根据实际需求进行调整,协助解决实际场景中的配网问题。

    上一篇:RISC-V单片机快速入门07-板载LCD显示ESP8266数据
    下一篇:WIFI模块开发教程之W600网络篇3:STA模式下TCP Client通信

    发表评论

    最新留言

    逛到本站,mark一下
    [***.202.152.39]2025年05月01日 11时23分30秒