Skip to content

Overview

Going online is the first step for IoT devices. The Ai-WB2 acts as a client in STA (Station) mode (making the board the "client", connecting to a router like a phone), and once it gets an IP (the "house number" on the network) it can do TCP/UDP/HTTP/MQTT network communication. This tutorial demonstrates the complete connection flow: Wi-Fi initialization → connect to router → get IP, observing each stage's state through event callbacks (like setting an alarm that rings automatically at the set time).

In plain words: STA mode makes the board connect to your home router like a phone. The Wi-Fi's name is called SSID (the network's name); you can only connect with the right password; after connecting, the router gives the board an IP address (the "house number" on the network), and only with it can the board talk to other devices. The whole process is exactly like a phone connecting to Wi-Fi: search → enter password → connected → the Wi-Fi icon appears in the status bar.

This tutorial is based on the official Ai-Thinker SDK (Ai-Thinker-Open/Ai-Thinker-WB2, version release_bl_iot_sdk_1.6.40) example applications/wifi/station; the code can be found directly in your local SDK.

🎯Page GoalObserve the whole STA connection flow through event callbacks (INIT → MGMR → CONNECTED → GOT IP), mastering the Wi-Fi event-driven framework and connection APIs.
🧰Prerequisites① Ai-WB2 development board (Type-C data cable) ② A 2.4GHz router (note the SSID and password) ③ Environment set up per [SDK Installation](../sdk/sdk_intro).
🔗RelatedEvent framework concepts: [Wi-Fi Introduction](./wifi_intro); network communication after connecting: [TCP Client](./tcp_client).

Enter the Example Project

Open a terminal and enter the official station example project directory:

cd ~/Ai-Thinker-WB2/applications/wifi/station

Note: cd is the “change directory” command, entering the official example project; all subsequent make commands must run in this directory.

Modify the Router Parameters

Open station/main.c and change the SSID (the network’s name) and password at the top to your router’s:

#define ROUTER_SSID "your ssid"
#define ROUTER_PWD "your password"

For example:

#define ROUTER_SSID "my_wifi"
#define ROUTER_PWD "12345678"

💡 Only the 2.4GHz band is supported; for an open network (empty password) pass NULL or an empty string as the second argument.

Write the Code

Open station/main.c. The full code for this step has been moved to the end of this page:

📜 Full Code — in the “Full Code” section below, collapsed by default — click to expand, identical to the official example (applications/wifi/station/station/main.c).

Code highlights:

Code Purpose
tcpip_init(NULL, NULL) Initialize the network stack; without it none of the network functions work
aos_register_event_filter(EV_WIFI, cb, NULL) Register the event callback; without it you receive no Wi-Fi notifications at all
hal_wifi_start_firmware_task() Start the Wi-Fi firmware task; no wireless hardware, no internet
aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE, 0) Post the initialization event, kicking off the following connection flow automatically
wifi_mgmr_start_background(&conf) Start the Wi-Fi management module; without it the connect function fails outright
wifi_mgmr_sta_enable() Put the board in “client” position, ready to connect to the router
wifi_mgmr_sta_connect(if, ssid, pwd, ...) Actually initiates the connection; a wrong SSID/password never connects
CODE_WIFI_ON_GOT_IP callback Networked only after getting the IP; all network applications should start here

💡 The official SDK also supports provisioning events (CODE_WIFI_ON_PROV_SSID / PASSWD / CONNECT) for auto-connecting after app/web provisioning — commonly used in real provisioning products.

Build the Project

Build in the project directory:

make -j8

Note: make is the “build” command, turning code into firmware (the program flashed into the board) the board can run; -j8 builds with 8 parallel cores, faster.

On success a firmware build_out/station.bin is generated.

Flash the Firmware

Keep the board connected via USB, confirm the serial device, and flash:

make flash p=/dev/ttyUSB0 b=921600

Note: make flash is the “flash” command, writing the compiled firmware into the board. After p= comes the serial device (often /dev/ttyUSB0 on Linux, COM3-like on Windows — use your computer’s actual one), b=921600 is the flash baud rate (serial transfer speed), keep the default.

⏳ During flashing, press and hold the EN button on the board when prompted to enter download mode; wait for the progress bar to complete — that means the flash succeeded.

Run and Verify

After flashing, the board automatically restarts and runs. Open a serial assistant (baud rate 921600, the “speech speed” of serial data — both ends must match) and observe the event logs:

[OS] Starting TCP/IP Stack...
[OS] proc_main_entry task...
[APP] [EVT] INIT DONE 1
[APP] [EVT] MGMR DONE 2
[APP] [EVT] Connecting 3
[APP] [EVT] connected 4
[APP] [EVT] GOT IP 5
[SYS] Memory left is 267232 Bytes

The event order is the connection flow: INIT DONEMGMR DONEConnectingconnectedGOT IP. GOT IP appearing means networked; if it stays stuck at Connecting or keeps repeating disconnect, the router wasn’t connected (SSID/password/2.4GHz band issue) — see the FAQ at the end; if there’s no serial output at all, check the serial port and flashing first (see the FAQ at the end).

💡 Advanced verification: power off the router and restore it; the serial prints disconnectReconnectGOT IP again, proving the SDK’s auto-reconnect mechanism works. Create your business task in the GOT_IP callback to start network applications (see TCP Client).


API Summary for This Tutorial

tcpip_init(callback, arg)

Initializes the lwIP TCP/IP stack (socket, DNS, netif etc. depend on it); called first in main.

Parameters:

  • callback: initialization-complete callback function pointer, usually NULL
  • arg: callback argument, pass NULL

Return: none

aos_register_event_filter(evt, cb, arg)

Registers an event filter callback, called when the networking state changes.

Parameters:

  • evt: event type, values: EV_WIFI (Wi-Fi events)
  • cb: callback function pointer, shaped void cb(uint32_t event, void *val, void *arg), required
  • arg: pass-through argument, pass NULL if none

Return: 0 on success; negative error code on failure

aos_post_event(evt, code, value)

Posts an event to the event loop (kicking off the Wi-Fi initialization flow).

Parameters:

  • evt: event type, values: EV_WIFI
  • code: event code, values: CODE_WIFI_ON_INIT_DONE (init complete) etc.
  • value: event extra value, pass NULL if none

Return: 0 on success; negative error code on failure

hal_wifi_start_firmware_task

Starts the Wi-Fi firmware task, enabling the wireless hardware (users generally don't call it directly).

Return: none

wifi_mgmr_start_background(conf)

Starts the Wi-Fi manager (the scheduling core of connecting/hotspot/scanning).

Parameters:

  • conf: wifi_conf_t struct pointer, optional fields: country_code (country code, "CN" for China)

Return: 0 on success; negative error code on failure

wifi_mgmr_sta_enable

Enables station (STA) mode, ready to connect to a router.

Return: interface handle (wifi_interface_t) on success; NULL on failure

wifi_mgmr_sta_connect(if, ssid, pwd, NULL, NULL, 0, 0)

Connects to a router by SSID/password (asynchronous; the result is notified via event callback).

Parameters:

  • if: STA interface handle (return of wifi_mgmr_sta_enable())
  • ssid: router name string, required (e.g. "FAE@Seahi")
  • pwd: Wi-Fi password string, required (e.g. "fae12345678")
  • Args 4~7: optional args (BSSID, channel, security type, index), pass NULL, NULL, 0, 0

Return: 0 on success; negative error code on failure

aos_now_ms

Returns the milliseconds elapsed since system power-on (for timeout judgment/timestamps).

Return: system uptime in milliseconds (uint64_t, never fails)

xPortGetFreeHeapSize

Returns the current free heap bytes (for diagnosing insufficient memory).

Return: remaining heap bytes (never fails)

xTaskCreate(fn, name, stack, arg, prio, handle)

Creates a task and adds it to the ready queue; the scheduler runs it according to priority.

Parameters:

  • fn: pointer to the task entry function, shaped void task(void *arg), required
  • name: task name string (for debugging), e.g. "main_entry"
  • stack: task stack size (in words), values: e.g. 1024
  • arg: pointer to the argument passed to the entry function; pass NULL if none
  • prio: task priority, values: 0 (lowest)~19 (highest), e.g. 15
  • handle: output pointer for the task handle; pass NULL if not needed

Return: pdPASS on success; pdFAIL on failure


Full Code

Below is the complete station/main.c source, identical to the official example (applications/wifi/station/station/main.c):

📜 Click to expand the full station/main.c code
c
/**
 * @file main.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2022-10-09
 *
 * @copyright Copyright (c) 2022
 *
 */
#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include <string.h>
#include "blog.h"
#include <aos/yloop.h>
#include <aos/kernel.h>
#include <lwip/tcpip.h>
#include <wifi_mgmr_ext.h>
#include <hal_wifi.h>

#define ROUTER_SSID "your ssid"
#define ROUTER_PWD "your password"

static wifi_conf_t conf =
{
    .country_code = "CN",
};

static void wifi_sta_connect(char* ssid, char* password)
{
    wifi_interface_t wifi_interface;

    wifi_interface = wifi_mgmr_sta_enable();
    wifi_mgmr_sta_connect(wifi_interface, ssid, password, NULL, NULL, 0, 0);
}

static void event_cb_wifi_event(input_event_t* event, void* private_data)
{
    static char* ssid;
    static char* password;

    switch (event->code)
    {
        case CODE_WIFI_ON_INIT_DONE:
        {
            blog_info("[APP] [EVT] INIT DONE %lld", aos_now_ms());
            wifi_mgmr_start_background(&conf);
        }
        break;
        case CODE_WIFI_ON_MGMR_DONE:
        {
            blog_info("[APP] [EVT] MGMR DONE %lld", aos_now_ms());
            //_connect_wifi();

            wifi_sta_connect(ROUTER_SSID, ROUTER_PWD);
        }
        break;
        case CODE_WIFI_ON_SCAN_DONE:
        {
            blog_info("[APP] [EVT] SCAN Done %lld", aos_now_ms());
            // wifi_mgmr_cli_scanlist();
        }
        break;
        case CODE_WIFI_ON_DISCONNECT:
        {
            blog_info("[APP] [EVT] disconnect %lld", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_CONNECTING:
        {
            blog_info("[APP] [EVT] Connecting %lld", aos_now_ms());
        }
        break;
        case CODE_WIFI_CMD_RECONNECT:
        {
            blog_info("[APP] [EVT] Reconnect %lld", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_CONNECTED:
        {
            blog_info("[APP] [EVT] connected %lld", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_PRE_GOT_IP:
        {
            blog_info("[APP] [EVT] connected %lld", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_GOT_IP:
        {
            blog_info("[APP] [EVT] GOT IP %lld", aos_now_ms());
            blog_info("[SYS] Memory left is %d Bytes", xPortGetFreeHeapSize());
        }
        break;
        case CODE_WIFI_ON_PROV_SSID:
        {
            blog_info("[APP] [EVT] [PROV] [SSID] %lld: %s",
                   aos_now_ms(),
                   event->value ? (const char*)event->value : "UNKNOWN");
            if (ssid)
            {
                vPortFree(ssid);
                ssid = NULL;
            }
            ssid = (char*)event->value;
        }
        break;
        case CODE_WIFI_ON_PROV_BSSID:
        {
            blog_info("[APP] [EVT] [PROV] [BSSID] %lld: %s",
                   aos_now_ms(),
                   event->value ? (const char*)event->value : "UNKNOWN");
            if (event->value)
            {
                vPortFree((void*)event->value);
            }
        }
        break;
        case CODE_WIFI_ON_PROV_PASSWD:
        {
            blog_info("[APP] [EVT] [PROV] [PASSWD] %lld: %s", aos_now_ms(),
                   event->value ? (const char*)event->value : "UNKNOWN");
            if (password)
            {
                vPortFree(password);
                password = NULL;
            }
            password = (char*)event->value;
        }
        break;
        case CODE_WIFI_ON_PROV_CONNECT:
        {
            blog_info("[APP] [EVT] [PROV] [CONNECT] %lld", aos_now_ms());
            blog_info("connecting to %s:%s...", ssid, password);
            wifi_sta_connect(ssid, password);
        }
        break;
        case CODE_WIFI_ON_PROV_DISCONNECT:
        {
            blog_info("[APP] [EVT] [PROV] [DISCONNECT] %lld", aos_now_ms());
        }
        break;
        default:
        {
            blog_info("[APP] [EVT] Unknown code %u, %lld", event->code, aos_now_ms());
            /*nothing*/
        }
    }
}

static void proc_main_entry(void* pvParameters)
{

    aos_register_event_filter(EV_WIFI, event_cb_wifi_event, NULL);
    hal_wifi_start_firmware_task();
    aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE, 0);
    vTaskDelete(NULL);
}

static void system_thread_init()
{
    /*nothing here*/
}

void main()
{
    system_thread_init();
    puts("[OS] Starting TCP/IP Stack...");
    tcpip_init(NULL, NULL);
    puts("[OS] proc_main_entry task...");
    xTaskCreate(proc_main_entry, (char*)"main_entry", 1024, NULL, 15, NULL);
}

FAQ & Troubleshooting

⚠️ Keeps printing Connecting, never succeeds
Cause: wrong SSID/password, the router is on the 5GHz band, or the signal is too weak
Fix: double-check ROUTER_SSID/ROUTER_PWD; confirm the router broadcasts 2.4GHz; test with the board closer to the router

⚠️ Connected but no GOT IP
Cause: the router's DHCP is off, or the IP is taken
Fix: confirm DHCP is enabled on the router; routers with limited connections (like phone hotspots) limit the number of joined devices

⚠️ Prints disconnect then reconnects repeatedly
Cause: a wrong password triggers endless reconnect loops, or the router's MAC filter rejects the board
Fix: confirm the password is correct; check whether the router has MAC filtering enabled (allow the Ai-WB2's MAC)

⚠️ Build errors after changing the SSID
Cause: encoding issues with a Chinese SSID, or the macro string lacks quotes
Fix: use ASCII characters for the SSID (a Chinese SSID needs UTF-8 encoding); confirm the macro format is #define ROUTER_SSID "xxx"

⚠️ Serial device not found / can't open
Cause: USB-to-serial driver not installed, insufficient permission, or the cable only charges and can't transfer data
Fix: on Linux check the device with lsusb/dmesg; if permission denied run sudo chmod 666 /dev/ttyUSB0; on Windows install the driver and check the COM port in Device Manager; try a data-capable cable

⚠️ Flashing keeps waiting / fails
Cause: download mode wasn't entered, wrong baud rate, or a wrong serial number
Fix: press and hold EN during flashing to enter download mode as prompted; change p=/dev/ttyUSB0 to your actual serial port; try another USB port or cable

Self-Check

The serial prints events in the order INIT DONE → MGMR DONE → Connecting → connected → GOT IP — STA connection is verified.

Released under the MIT License. Build Time 2026-09-11 14:52:23