WIFI module development tutorial W600 network 6: Airkiss distribution network

preface

This paper studies how to use wechat Airkiss protocol to distribute the network of the module. The so-called distribution network is a mechanism to notify the router ssid and password that the module needs to connect.

1, Theoretical basis

1.Airkiss principle

The device is in the hybrid mode to monitor wireless packets. The APP sends broadcast packets / multicast packets every short period of time and forwards them through the router. When the device and the router are in the same channel, the device can receive valid data, while the visible field in the wireless packet is only length. Therefore, Airkiss and the so-called one key distribution network in the market are actually a way of transmitting router ssid and password to the device by encoding length.

2.Airkiss distribution network process

The main steps are as follows:

The device is in hybrid mode, monitors wireless packets, and switches channels every 100ms.

After the device channel is locked, it will not be switched

APP issues ssid and password encoded by length

The device parses the package content according to the same rules and obtains the router ssid and password

2, Use example

1. Program analysis

1.1 equipment channel switching

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 mixed packet listening callback function

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 send the random received after the distribution network, and notify APP that the distribution network is successful

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 distribution network entry function

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_timer_stop(g_switch_timer);
        rt_timer_delete(g_switch_timer);
    }
    if (g_doing_timer)
    {
        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_sem_delete(g_cfg_done_sem);
        g_cfg_done_sem = 0;
    }
}

2. Configuration

(1) Component download

Download smartconfig package (github download), put smartconfig in bsp/w60x / path, and then rtconfig Modify in H and add the macro definition as shown in the figure below:

(2) Compile configuration

Create a new folder in the applications/2-net Directory: 6-config_airkiss, and then you need to modify the applications / sconscript script.

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)

Return('group')

3. Download wechat official debugging tool

Download address: https://iot.weixin.qq.com/wiki/new/index.html?page=6-1

3, Download run

On the ENV console, enter the scons command and generate rtthread in the build/Bin directory_ 1M. FLS,

After the burning operation, operate the APP distribution network:

Debugging serial port information is as follows:

In the debugging serial port information, receive the password of the router, and then connect the router successfully, indicating that the distribution network is OK.

4, Conclusion

1. Summary:

At the end of this section, the following points need to be paid attention to in the actual operation process:

(1) Problems introduced by component update

Call rt_smartconfig_start starts the distribution network. After success, enter smartconfig_ The result callback function cannot directly send UDP broadcast, because the callback function should not do operations that take too long. The correct operation is to create a sending thread and send the content to be broadcast to the thread through parameter transmission.

rt_smartconfig_start(SMARTCONFIG_TYPE_AIRKISS, SMARTCONFIG_ENCRYPT_NONE, RT_NULL, smartconfig_result);

2. Data acquisition

If you have any questions during use, please add QQ group for further communication.

QQ communication group: 906015840 (Note: Internet of things project communication)

Official account: Internet of things Inn, scan the code to pay attention, and reply to w600.

One leaf solitary sand product: one sand, one world, one leaf, one Bodhi

Posted by The Silent on Tue, 24 May 2022 08:44:09 +0300