如何使用 Wi-Fi station 模式

總覽

本文件會說明如何在 MediaTek LinkIt 7687 開發板上使用 LinkIt SDK 裡的 Wi-Fi station 模式。同時也會介紹使用的 API 和使用上的順序。

簡介

本教學文件將會介紹二個常用的場景:

  • 場景 1:已知的Wi-Fi配置,且不會再改變
    • 此場景比較適合應用在開發階段,當開發者和使用者就是同一個人時,它可以簡化設定的複雜度
    • LinkIt SDK 針對這種場景提供了一條捷徑,可以直接指定配置以簡化初始化流程
  • 場景 2 : 使用 "Smart Connection" 來接收從移動裝置發出來的 Wi-Fi 配置
    • 此場景適用於產品階段,由於使用者和其Wi-Fi 配置是未知的,需要讓使用者自行提供 Wi-Fi 配置
    • 流程上會複雜一些,7687裝置必須先進到 Wi-Fi ready 狀態後才能開始接收 smart connection 信息

前置作業

請先安裝以下的列表以進行後續操作:

開始之前

若您之前不曾使用 7687 HDK 來開發專案,請先安裝 LinkIt SDK v3 並進行開發環境設定,相關細節請參考 LinkIt development platform for RTOS 入門手冊

Wi-Fi 基礎知識

Wi-Fi 事件

LinkIt SDK 使用事件處理函數來接收 Wi-Fi 狀態變化。開發者應利用 wifi_connection_register_event_handler() 來註冊自己的處理函數。

通過此處理函數可以接收到以下各種事件,包括:

  • WIFI_EVENT_IOT_INIT_COMPLETE:最重要的事件之一。它表示 Wi-Fi 已經初始化完成,可以開始使用 
  • WiFI_EVENT_IOT_CONNECTED:表示裝置已經成功的找到 AP (但還沒成功連接上)
  • WIFI_EVENT_IOT_PORT_SECURE:表示裝置已經成功的通過認證流程且連接上 AP。有關認證流程細節請參照 Wireless_supplicant 。

Station 模式的階段

Wi-Fi station 模式總共有 3 個階段,分別為:

  1. 初始化完成:從 wifi_init() 開始。到收到 WIFI_EVENT_IOT_INIT_COMPLETE 事件為止。
  2. 連接上AP:從設置 SSID/password 開始,到收到 WIFI_EVENT_IOT_PORT_SECURE 事件為止。
  3. 取得 IP 地址:從 dhcp_start() 開始,到取得 IP 地址為止。

所有階段都完成後,裝置即可訪問網路。

 

場景 1 - 預先設置好的 Wi-Fi 配置 

初始化 Wi-Fi 並連接 AP

Linkit SDK 提供了能同時初始化 Wi-Fi 和連接 AP 的捷徑,可以在 wifi_init() 直接帶入 SSID和密碼。他們會被暫時記下來,等到 Wi-Fi 初始化完成後再自動用來連接 AP。

不管是初始化完成或者連接AP成功的事件,都可以經由 wifi_connection_register_event_handler() 來註冊處理函數。針對不同的事件可以註冊相同或者不同的處理函數。在此教學文件使用同一個函數來註冊所有事件。由於 wifi_init() 會自動連接 AP,你也可以選擇忽略初始化完成的 WIFI_EVENT_IOT_INIT_COMPLETE 事件,只關注 WIFI_EVENT_IOT_PORT_SECURE 事件即可。

#define SSID "Your_AP"
#define password "Your_password"
 
void main()
{
    wifi_config_t wifi_config = {0};

    wifi_connection_register_event_handler(WIFI_EVENT_IOT_INIT_COMPLETE , _wifi_event_handler);
    wifi_connection_register_event_handler(WIFI_EVENT_IOT_PORT_SECURE, _wifi_event_handler);

    wifi_config.opmode = WIFI_MODE_STA_ONLY;
 
    strcpy((char *)wifi_config.sta_config.ssid, SSID);
    wifi_config.sta_config.ssid_length = strlen(SSID);
    strcpy((char *)wifi_config.sta_config.password, password);
    wifi_config.sta_config.password_length = strlen(password);

    wifi_init(&wifi_config, NULL);

    // register callback for DHCP
    sta_if = netif_find_by_type(NETIF_TYPE_STA);
    netif_set_status_callback(sta_if, _ip_ready_callback);
    dhcp_start(sta_if);
}
 
static int32_t _wifi_event_handler(wifi_event_t event,
        uint8_t *payload,
        uint32_t length)
{
    switch(event)
    {
    case WIFI_EVENT_IOT_INIT_COMPLETE:
        LOG_I(common, "wifi inited");
        break;
    case WIFI_EVENT_IOT_PORT_SECURE:
        LOG_I(common, "wifi inited");
        break;
    }
    return 1;
}

通知 DHCP 服務來取得 IP 地址

收到 WIFI_EVENT_IOT_PORT_SECURE 事件之後,表示 7687裝置已經成功連線上網路。下一步是通知DHCP服務來從網路取得動態 IP 地址。

呼叫 netif_set_link_up(), 系統會呼叫之前利用 netif_set_status_callback() 註冊的處理函數來通知 IP 地址已經成功取得。

{
    // after WIFI_EVENT_IOT_PORT_SECURE
    ...
    struct netif *sta_if;

    sta_if = netif_find_by_type(NETIF_TYPE_STA);
    netif_set_link_up(sta_if);
}

static void _ip_ready_callback(struct netif *netif)
{
    if (!ip4_addr_isany_val(netif->ip_addr)) 
    {
        if (NULL != inet_ntoa(netif->ip_addr)) 
        {
            char ip_addr[17] = {0};
            strcpy(ip_addr, inet_ntoa(netif->ip_addr));
            LOG_I(common, "DHCP got IP: %s", ip_addr);
        } else {
            LOG_E(common, "DHCP got IP failed");
        }
    }
    LOG_I(common, "ip ready");
}
 

取得 IP 地址之後,裝置就可以進行各種網路工作,包括 HTTP 瀏覽,TCP/UDP socket建立等等

場景 2 - 利用 Smart connection 來取得 Wi-Fi 配置

在無法預先知道 SSID/密碼的情況下,流程可以分為四個部分:

  1. Wi-Fi 初始化.
  2. 利用 Smart Connection 取得 SSID/密碼
  3. 連線到 AP
  4. 利用 DHCP 取得 IP 地址

初始化Wi-Fi

把 opmode 參數設定成 WIFI_MODE_STA_ONLY 後,呼叫 wifi_init(),再等到 WIFI_EVENT_IOT_INIT_COMPLETE 事件發生就表示 Wi-Fi 已經初始化完成

void main()
{
    wifi_config_t wifi_config = {0};

    wifi_connection_register_event_handler(WIFI_EVENT_IOT_INIT_COMPLETE , _wifi_event_handler);
    wifi_connection_register_event_handler(WIFI_EVENT_IOT_PORT_SECURE, _wifi_event_handler);

    wifi_config.opmode = WIFI_MODE_STA_ONLY;
    wifi_init(&wifi_config, NULL);
 
    // register callback for DHCP
    sta_if = netif_find_by_type(NETIF_TYPE_STA);
    netif_set_status_callback(sta_if, _ip_ready_callback);
    dhcp_start(sta_if);
}
 
static int32_t _wifi_event_handler(wifi_event_t event,
        uint8_t *payload,
        uint32_t length)
{
    switch(event)
    {
    case WIFI_EVENT_IOT_INIT_COMPLETE:
        LOG_I(common, "wifi inited");
        // continue to do smart connection
        break;
    }
    return 1;
}

開始 Smart Connection 來接收 SSID/密碼資訊

Wi-Fi 初始化完成後,就可以使用 wifi_smart_connection_init() 和 wifi_smart_connection_start() 來啟動 smart connection。

wifi_smart_connection_init() 的第一和第二個參數分別是加密的金鑰和金鑰的長度,此金鑰是一組 ASCII 字串,目的是加密 smart connection 廣播的訊息,只有使用相同金鑰的發送者和接收者才能彼此溝通。此金鑰可以為 NULL (使用系統預設金鑰)。第三個參數是事件處理函數,用來處理過程中的事件。

Smart connection有三個事件:

  • WIFI_SMART_CONNECTION_EVENT_INFO_COLLECTED: 成功的接收到 SSID/密碼配置
  • WIFI_SMART_CONNECTION_EVENT_TIMEOUT: 逾時仍無法收到SSID/密碼
  • WIFI_SMART_CONNECTION_EVENT_CHANNEL_LOCKED: 在接收過程中的一個階段,可作為除錯分析使用

在 WIFI_SMART_CONNECTION_EVENT_INFO_COLLECTED 事件發生後,就可以使用 wifi_smart_connection_get_result() 來取得收到的 SSID 和密碼,並使用它們來連線。

{
    // after WIFI_EVENT_IOT_INIT_COMPLETE
    ...
    wifi_smart_connection_init(key, strlen(key), _smtcn_handler);
    wifi_smart_connection_start(0);
}
 
static void _smtcn_handler(wifi_smart_connection_event_t event, void *data)
{
    uint8_t ssid[WIFI_MAX_LENGTH_OF_SSID + 1];
    uint8_t pwd[WIFI_LENGTH_PASSPHRASE + 1]; 
    uint8_t ssid_len = 0;
    uint8_t pwd_len = 0;

    switch (event)
    {
        case WIFI_SMART_CONNECTION_EVENT_CHANNEL_LOCKED:
            break;
        case WIFI_SMART_CONNECTION_EVENT_TIMEOUT:
            LOG_E(common, "Smart connection failed - timeout");
            break;
        case WIFI_SMART_CONNECTION_EVENT_INFO_COLLECTED:
		    wifi_smart_connection_get_result(ssid, &ssid_len, pwd, &pwd_len, NULL, NULL);
  	        ssid[ssid_len] = 0;
   	        pwd[pwd_len] = 0;
            wifi_smart_connection_deinit();
            break;
    }
}

連線到 AP

取得 SSID/密碼之後,就可以使用 wifi_config_set_ssid(),wifi_config_set_wpa_psk_key() 和 wifi_config_reload_setting() 來更新 Wi-Fi 配置。裝置會立刻開始嘗試連線到 AP。若一切順利,在成功連線後就會收到 WIFI_EVENT_IOT_PORT_SECURE 事件。

Note. 若 AP 使用 WEP 加密協定,請呼叫 wifi_config_set_wep_key() 來設定密碼

{
    // after wifi_smart_connection_get_result()
    ...
    wifi_config_set_ssid(WIFI_PORT_STA, ssid, strlen(ssid));
    wifi_config_set_wpa_psk_key(WIFI_PORT_STA, pwd, strlen(pwd));
    wifi_config_reload_setting();
}
 
static int32_t _wifi_event_handler(wifi_event_t event,
        uint8_t *payload,
        uint32_t length)
{
    struct netif *sta_if;
    switch(event)
    {
    case WIFI_EVENT_IOT_PORT_SECURE:
        LOG_I(common, "wifi connected");
        // connected and authenticated to AP
        break;
    }
    return 1;
}
 

利用 DHCP 來取得 IP 地址

最後一個步驟和之前場景 1 時相同,當裝置已經成功連線到網路(收到 WIFI_EVENT_IOT_PORT_SECURE 事件)後,下一步就是通知 DHCP 服務來取得動態 IP 地址。呼叫 netif_set_link_up(),等到順利取得 IP 地址時,系統會呼叫之前在 netif_set_status_callback() 裡註冊的回調函數。

{
    // after WIFI_EVENT_IOT_PORT_SECURE
    ...
    struct netif *sta_if;

    sta_if = netif_find_by_type(NETIF_TYPE_STA);
    netif_set_link_up(sta_if);}
 
static void _ip_ready_callback(struct netif *netif)
{
    if (!ip4_addr_isany_val(netif->ip_addr)) 
    {
        if (NULL != inet_ntoa(netif->ip_addr)) 
        {
            char ip_addr[17] = {0};
            strcpy(ip_addr, inet_ntoa(netif->ip_addr));
            LOG_I(common, "DHCP got IP: %s", ip_addr);
        } else {
            LOG_E(common, "DHCP got IP failed");
        }
    }
    LOG_I(common, "ip ready");
}

取得 IP 地址之後,裝置就可以進行各種網路工作,包括 HTTP 瀏覽,TCP/UDP socket建立等等

結論

透過此教學,您可以學到如何在 LinkIt 7687 開發板上使用 Wi-Fi station 模式。您可以選擇使用預先定義好的 Wi-Fi 配置,或者利用 "Smart Connection" 來從行動裝置接收 Wi-Fi 配置。祝愉快!