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) exampleapplications/wifi/station; the code can be found directly in your local SDK.
Open a terminal and enter the official station example project directory:
cd ~/Ai-Thinker-WB2/applications/wifi/station
Note:
cdis the “change directory” command, entering the official example project; all subsequentmakecommands must run in this directory.
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
NULLor an empty string as the second argument.
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 in the project directory:
make -j8
Note:
makeis the “build” command, turning code into firmware (the program flashed into the board) the board can run;-j8builds with 8 parallel cores, faster.
On success a firmware build_out/station.bin is generated.
Keep the board connected via USB, confirm the serial device, and flash:
make flash p=/dev/ttyUSB0 b=921600
Note:
make flashis the “flash” command, writing the compiled firmware into the board. Afterp=comes the serial device (often/dev/ttyUSB0on Linux,COM3-like on Windows — use your computer’s actual one),b=921600is 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.
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 DONE → MGMR DONE → Connecting → connected → GOT 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
disconnect→Reconnect→GOT IPagain, proving the SDK’s auto-reconnect mechanism works. Create your business task in theGOT_IPcallback 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, usuallyNULLarg: callback argument, passNULL
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, shapedvoid cb(uint32_t event, void *val, void *arg), requiredarg: pass-through argument, passNULLif 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_WIFIcode: event code, values:CODE_WIFI_ON_INIT_DONE(init complete) etc.value: event extra value, passNULLif 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_tstruct 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 ofwifi_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, shapedvoid task(void *arg), requiredname: task name string (for debugging), e.g."main_entry"stack: task stack size (in words), values: e.g.1024arg: pointer to the argument passed to the entry function; passNULLif noneprio: task priority, values:0(lowest)~19(highest), e.g.15handle: output pointer for the task handle; passNULLif 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
/**
* @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.

