杰理 SDK 文档中心
首页
首页
  • 概述与快速开始

    • SDK 总览与芯片能力
    • 环境搭建与编译构建
    • 烧录与固件升级
    • 文档与版本资源
  • 应用与示例方案

    • demo 示例工程
    • WiFi 摄像头方案 (wifi_camera)
    • WiFi 音箱方案 (wifi_soundbox)
    • WiFi 婴儿监护方案 (wifi_bbm)
    • 公共应用模块库
    • 示例代码库 (example)
  • 系统架构与平台

    • 总体架构与工程分层
    • 系统启动与运行框架
    • 芯片驱动与板级适配
    • 设备管理与文件系统
    • 系统工具库与算法
  • 音频子系统

    • 音频框架与处理节点
    • 音频编解码与音效
    • 播放器与录音器
    • 语音交互与 AI 唤醒
    • LE Audio 与蓝牙音频
    • 音频调试与歌词
  • 视频与显示子系统

    • 摄像头驱动与 ISP
    • 视频编码与图像处理
    • 显示与 GPU 加速
    • 屏幕镜像 (screen_mirror)
  • 无线连接与网络

    • 蓝牙协议栈 (双模蓝牙)
    • WiFi 协议栈与配网
    • 网络协议栈
    • 云平台与 IoT 协议
  • UI 子系统

    • LVGL 集成与应用
    • UI 工程与工具链
  • 配置系统

    • 功能配置
    • 板级配置
    • 网络与蓝牙配置
    • 音频配置与提示音
  • 工具与测试

    • 产测与射频测试工具
    • 固件升级与更新机制
    • 调试与日志工具
  • 硬件参考设计

    • 原理图参考设计
    • 芯片数据手册

WiFi 协议栈与配网

AC792N SDK 的 WiFi 子系统从应用层到硬件的完整技术参考:涵盖 STA / AP / P2P / Monitor / SmartConfig 多种工作模式、UART 命令行与 SmartConfig 等配网途径、SSID 记忆与自动重连、lwIP 协议栈集成、静态 IP 记忆以及底层 802.11 收包过滤适配,帮助开发者快速完成设备联网并理解各层之间的调用关系。

Purpose and Scope

本文档系统性地介绍 AC792N(WL83 系列)单芯片 WiFi 方案的协议栈架构与配网机制,覆盖以下内容:

  • WiFi 工作模式(STA、AP、P2P GO/GC、Monitor、SmartConfig)的切换机制与 API 入口;
  • 三种典型配网途径:UART 命令行配网、SmartConfig(smp_cfg)、AP 热点配网;
  • SSID 记忆存储(VM)与信号最优自动重连逻辑;
  • lwIP 网络协议栈集成:DHCP、静态 IP 记忆、AP 侧 DHCP Server;
  • WiFi 扫描机制(同步信号量封装)与底层 wifi_adapter 收包过滤/硬件寄存器适配。

以下主题属于兄弟页面,不在本文展开:WiFi 低功耗(sdk/apps/common/example/wifi/low_power)、Raw TX/RX 透传测试(raw_txrx / wifi_raw_test)、以及网络事件订阅的通用机制(event/net_event.h)。本文聚焦"如何让设备连上网、记住网、按需切换模式"这一能力边界。

Overview

AC792N 是杰理(Jieli)面向物联网场景的 WiFi SoC,SDK 采用单芯片双协议栈架构:上层应用通过 wifi/wifi_connect.h 提供的统一封装 API 控制 WiFi 协议栈,网络数据面接入 lwIP(lwip.h),实现 TCP/IP、DHCP、DNS 等能力;WiFi 协议栈内部按模式(STA/AP/P2P/Monitor/SmartConfig)驱动 MAC/PHY 硬件。

配网(Provisioning)是物联网设备接入路由器的关键环节。SDK 提供多条配网路径,其设计意图在于适应不同量产与调试场景:

  • UART 命令行配网(uart_cmd.c):调试与产测最直接的手段,串口输入 sta <ssid> <pwd> 即可入网,输入 smp_cfg 进入 SmartConfig;
  • SmartConfig 配网(wifi_enter_smp_cfg_mode):手机 App 广播加密报文,设备监听并解析出 SSID/密码,适合无屏幕设备;
  • AP 配网:设备自建热点(默认 192.168.4.1,内嵌 DHCP Server),手机直连后通过本地页面/协议下发目标路由信息;
  • SSID 记忆与自动重连:wifi_store_mode_info / wifi_get_stored_sta_info / wifi_set_sta_connect_best_ssid 组合实现"记住最优网络、开机自动回连"。

Architecture

flowchart TD
    subgraph sg_App["应用层 (apps)"]
        UartCmd["uart_cmd.c<br/>UART 命令行配网入口"]
        WifiTask["wifi_demo_task.c<br/>模式切换 / SSID 记忆 / 静态IP"]
        ScanList["examples/scan_list.c<br/>扫描同步封装"]
    end

    subgraph sg_Api["WiFi 统一 API 层"]
        WifiConnect["wifi/wifi_connect.h<br/>enter_sta/ap/p2p/smp_cfg · scan · store"]
    end

    subgraph sg_Stack["WiFi 协议栈"]
        StaMode["STA_MODE"]
        ApMode["AP_MODE"]
        P2pMode["P2P GO / GC"]
        MonitorMode["MONITOR_MODE"]
        SmpCfg["SMART_CONFIG"]
        StackEvent["WIFI_EVENT_* / net_event"]
    end

    subgraph sg_Net["网络协议栈 lwIP"]
        Netif["WIFI_NETIF netif"]
        DhcpSrv["dhcp_srv (AP侧DHCP Server)"]
        DhcpClient["DHCP Client (STA侧)"]
    end

    subgraph sg_Storage["持久化 syscfg/VM"]
        VmSsid["SSID 记忆存储"]
        VmIp["VM_STA_IPADDR_INDEX<br/>静态 IP 记忆"]
    end

    subgraph sg_Hw["硬件适配"]
        Adapter["wifi_adapter.h<br/>RX 过滤 DROP_* / 寄存器"]
        MacPhy["MAC / PHY 硬件"]
    end

    UartCmd --> WifiConnect
    WifiTask --> WifiConnect
    ScanList --> WifiConnect
    WifiConnect --> StaMode
    WifiConnect --> ApMode
    WifiConnect --> P2pMode
    WifiConnect --> MonitorMode
    WifiConnect --> SmpCfg
    StaMode --> Netif
    ApMode --> DhcpSrv
    StaMode --> DhcpClient
    StaMode --> VmSsid
    WifiTask --> VmIp
    WifiTask --> VmSsid
    StackEvent --> WifiTask
    Netif --> Adapter
    Adapter --> MacPhy

架构图说明:应用层通过 wifi_connect.h 统一 API 下发模式切换与扫描命令,协议栈内部按模式驱动硬件;STA 模式数据面接入 lwIP WIFI_NETIF,通过 DHCP Client 获取地址;AP 模式启用 dhcp_srv 为手机分配 192.168.4.x 地址;SSID 与 IP 信息通过 syscfg VM 持久化,保证掉电重连;Monitor/Raw 模式下应用可经 wifi_adapter.h 直接配置 RX 过滤规则与硬件 MAC/BSSID 寄存器。

核心 API 与数据流总览

wifi_connect.h 是应用访问协议栈的唯一窗口,其 API 按职责分为五组:

职责组代表 API说明
模式切换wifi_enter_sta_mode / wifi_enter_ap_mode / wifi_enter_p2p_mode / wifi_enter_smp_cfg_mode进入对应工作模式
扫描wifi_scan_req / wifi_get_scan_result / wifi_clear_scan_result发起扫描、取结果、清缓存
SSID 记忆wifi_store_mode_info / wifi_get_stored_sta_info / wifi_del_stored_sta_info / wifi_set_sta_connect_best_ssid存取/删除已保存网络
状态查询wifi_is_on / wifi_get_mode_cur_info / wifi_get_mode_stored_info / wifi_get_mac / wifi_get_bssid / wifi_get_channel当前模式、地址、信道
认证解析get_wifi_auth_mode将认证模式枚举转为可读字符串

典型配网数据流:uart_cmd 收到 sta <ssid> <pwd> → wifi_sta_connect(ssid, pwd, save) → wifi_enter_sta_mode → 协议栈扫描目标 BSSID 并完成 802.11 关联/握手 → lwIP WIFI_NETIF 启动 DHCP → 事件回调通知应用 → 应用按需调用 wifi_store_mode_info 保存网络 → 下次开机 wifi_return_sta_mode 自动回连。

说明:以上 API 签名均来自实际调用点(wifi_demo_task.c、uart_cmd.c、scan_list.c),具体参数与返回语义以 SDK 中 wifi/wifi_connect.h 头文件为准。

配网方式详解

1. UART 命令行配网

uart_cmd.c 是 demo_wifi 工程的串口配网入口。它注册一个 uart_test_main 任务,打开 uart1(注释标明 Rx 为 PA1),配置循环接收缓冲并阻塞读取,每收到一行命令就交给 cmd_do 解析执行:

static u8 buf[1 * 1024] __attribute__((aligned(32))); //用于串口接收缓存数据的循环buf
static void uart_test_main(void *priv)
{
    char recv_buf[256];
    int len;
    void *hdl = dev_open("uart1", NULL);
    ...
    /* 1 . 设置串口接收缓存数据的循环buf地址 */
    dev_ioctl(hdl, IOCTL_UART_SET_CIRCULAR_BUFF_ADDR, (int)buf);
    /* 2 . 设置串口接收缓存数据的循环buf长度 */
    dev_ioctl(hdl, IOCTL_UART_SET_CIRCULAR_BUFF_LENTH, sizeof(buf));
    /* 3 . 设置接收数据为阻塞方式,需要非阻塞可以去掉,建议加上超时设置 */
    dev_ioctl(hdl, IOCTL_UART_SET_RECV_BLOCK, 1);
    ...
    while (1) {
        len = dev_read(hdl, recv_buf, sizeof(recv_buf));
        if (len <= 0) {
            if (len == UART_CIRCULAR_BUFFER_WRITE_OVERLAY) {
                dev_ioctl(hdl, IOCTL_UART_FLUSH, 0); //回卷覆盖则冲掉循环buf重新接收
            } else if (len == UART_RECV_TIMEOUT) {
                puts("UART_RECV_TIMEOUT...\r\n");
            }
            continue;
        }
        recv_buf[len] = '\0';
        cmd_do(recv_buf);
    }
}

Source: uart_cmd.c

设计意图:阻塞式 dev_read 配合 1KB 循环缓冲,既能保证命令按行完整接收,又通过 UART_CIRCULAR_BUFFER_WRITE_OVERLAY 错误码处理"用户长期不取数据导致缓冲回卷"的边界情况——此时直接 IOCTL_UART_FLUSH 丢弃积压数据,避免解析错乱。

cmd_do 是配网命令的分发核心,支持的命令覆盖了所有入网方式:

void cmd_do(char *buf)
{
    char *cmd = buf;
    ...
    if (0 == memcmp(buf, "sta", strlen("sta"))) {  //串口输入 sta <ssid> <pwd> 进入STA模式。uart1 Rx为PA1
        if (sscanf(buf, "sta %s %s", ssid, pwd) == 2) {
            printf("Enter sta mode, ssid : %s, pwd: %s\n", ssid, pwd);
            wifi_sta_connect(ssid, pwd, 1);
        } else if (sscanf(buf, "sta %s", ssid) == 1) {
            wifi_sta_connect(ssid, "", 1);
        }
    } else if (0 == memcmp(buf, "ap", strlen("ap"))) {	//ap <ssid> <pwd> 进入AP模式
        if (sscanf(buf, "ap  %s %s", ssid, pwd) == 2) {
            wifi_enter_ap_mode(ssid, pwd);
        } else if (sscanf(buf, "ap %s", ssid) == 1) {
            wifi_enter_ap_mode(ssid, "");
        }
    } else if (0 == memcmp(buf, "go", strlen("go"))) {	//go <name> 进入GO模式
        ...
        wifi_enter_p2p_mode(P2P_GO_MODE, P2P_Device_Name);
    } else if (0 == memcmp(buf, "gc", strlen("gc"))) {	//gc <name> 进入GC模式
        ...
        wifi_enter_p2p_mode(P2P_GC_MODE, P2P_Device_Name);
    } else if (0 == memcmp(buf, "scan", strlen("scan"))) {	//scan
        void wifi_get_list_example(void);
        wifi_get_list_example();
    } else if (0 == memcmp(buf, "get_mac", strlen("get_mac"))) {	//get_mac 获取mac地址
        wifi_get_mac(mac_addr);
        printf("Mac addr: %02X:%02X:%02X:%02X:%02X:%02X", ...);
    } else if (0 == memcmp(buf, "smp_cfg", strlen("smp_cfg"))) {	//smp_cfg 进入smp_cfg模式
        wifi_enter_smp_cfg_mode();
    } else if (0 == memcmp(buf, "ssid_store_test", strlen("ssid_store_test"))) {
        ... // 见"SSID 记忆与自动重连"
    }
}

Source: uart_cmd.c

值得注意的细节:sta 命令带 save=1 参数调用 wifi_sta_connect,说明通过串口配网成功后会自动保存 SSID;而 ap 命令直接调用 wifi_enter_ap_mode,AP 模式下设备不保存任何网络信息,这与 AP 配网的"临时热点"定位一致。smp_cfg 则一键进入 SmartConfig 监听状态。

2. SmartConfig 配网(smp_cfg)

SmartConfig 是面向无屏幕设备的经典配网方案:手机 App 将 SSID/密码编码进 UDP 广播报文的长度或序列中,设备处于混杂监听状态(SmartConfig 模式)捕获报文并解码。SDK 通过 wifi_enter_smp_cfg_mode() 一个 API 进入该模式(见上节 smp_cfg 命令),协议栈内部完成监听与解析,解析成功后通常以事件形式通知应用层,应用层再调用 wifi_sta_connect 完成入网。该模式适合量产阶段手机 App 一键配网,但要求手机与设备处于同一信道,且对信道环境有一定依赖。

3. AP 配网(热点配网)

设备先进入 AP 模式自建热点,手机连接该热点后即可访问设备(默认网关 192.168.4.1)。wifi_demo_task.c 中的 wifi_set_lan_setting_info 配置了 AP 侧完整的局域网参数:

static void wifi_set_lan_setting_info(void)
{
    struct lan_setting lan_setting_info = {
        .WIRELESS_IP_ADDR0  = 192, .WIRELESS_IP_ADDR1  = 168,
        .WIRELESS_IP_ADDR2  = 4,   .WIRELESS_IP_ADDR3  = 1,
        .WIRELESS_NETMASK0  = 255, .WIRELESS_NETMASK1  = 255,
        .WIRELESS_NETMASK2  = 255, .WIRELESS_NETMASK3  = 0,
        .WIRELESS_GATEWAY0  = 192, .WIRELESS_GATEWAY1  = 168,
        .WIRELESS_GATEWAY2  = 4,   .WIRELESS_GATEWAY3  = 1,
        .SERVER_IPADDR1  = 192, .SERVER_IPADDR2  = 168,
        .SERVER_IPADDR3  = 4,   .SERVER_IPADDR4  = 1,
        .CLIENT_IPADDR1  = 192, .CLIENT_IPADDR2  = 168,
        .CLIENT_IPADDR3  = 4,   .CLIENT_IPADDR4  = 2,
        .SUB_NET_MASK1   = 255, .SUB_NET_MASK2  = 255,
        .SUB_NET_MASK3   = 255, .SUB_NET_MASK4  = 0,
    };
    net_set_lan_info(&lan_setting_info);
}

Source: wifi_demo_task.c

该配置将 AP 网关固定为 192.168.4.1、网段 /24,DHCP Server 从 192.168.4.2 起分配地址——这正是"设备作为热点被手机访问"的标准拓扑。AP 配网的完整链路是:ap <ssid> <pwd> → wifi_enter_ap_mode → lwIP 配置 lan_setting → dhcp_srv 为连入的手机分配 IP。手机端通常再通过本地 HTTP/TCP 服务下发目标路由器信息,设备随后切回 STA 模式入网。

4. STA 连接与 SSID 记忆、自动重连

wifi_demo_task.c 封装了 STA 模式的入口函数 wifi_sta_connect,并设计了一套"请求-保存"标志机制:

static char save_ssid_flag, request_connect_flag;

void wifi_sta_connect(char *ssid, char *pwd, char save)
{
    save_ssid_flag = save;
    request_connect_flag = 1;
    wifi_set_sta_connect_best_ssid(0);
    wifi_enter_sta_mode(ssid, pwd);
}

Source: wifi_demo_task.c

wifi_set_sta_connect_best_ssid(0) 关闭"信号最优选择",强制连接指定 SSID;之后在连接成功事件中由 wifi_sta_save_ssid() 根据 save_ssid_flag 决定是否落盘保存:

static void wifi_sta_save_ssid(void)
{
    if (save_ssid_flag) {
        save_ssid_flag = 0;
        struct wifi_mode_info info;
        info.mode = STA_MODE;
        wifi_get_mode_cur_info(&info);
        wifi_store_mode_info(STA_MODE, info.ssid, info.pwd);
    }
}

Source: wifi_demo_task.c

而开机自动回连由 wifi_return_sta_mode 实现:优先读取记忆的 SSID,无记忆时回退到编译期默认 STA_SSID/STA_PWD,同时打开"连接信号最优已存网络"开关:

void wifi_return_sta_mode(void)
{
    if (!wifi_is_on()) {
        return;
    }
    struct wifi_mode_info info;
    info.mode = STA_MODE;
    int ret = wifi_get_mode_stored_info(&info);
    if (ret) {//如果没保存过SSID
        info.ssid = STA_SSID;
        info.pwd = STA_PWD;
    }
    wifi_clear_scan_result(); //根据扫描结果连接信号最优ssid之前先清除之前结果,防止之前最优信号的ssid已下线
    wifi_set_sta_connect_best_ssid(1); //自动连接保存过的最佳WIFI
    save_ssid_flag = 0;
    wifi_enter_sta_mode(info.ssid, info.pwd);
}

Source: wifi_demo_task.c

设计意图:wifi_clear_scan_result 在"按扫描结果挑选最优 SSID"前先清空缓存,防止上次扫描中已下线的热点残留导致误连;FORCE_DEFAULT_MODE 宏(默认 0)决定 wifi_on 之后进入"最后记忆模式"还是强制默认模式,取值 3~200 时作为 STA 连接超时秒数,超时后回退连接记忆/最优网络——这是对"路由器暂时不可达"场景的容错设计。

SSID 记忆的增删查由 ssid_store_test 命令完整演示:

u8 ssid_stored_cnt;
struct wifi_stored_sta_info wifi_stored_sta_info[32];
os_time_dly(5 * 100); //假设等待已经连接上路由器
wifi_store_mode_info(STA_MODE, "GJ12", "123456789");
wifi_store_mode_info(STA_MODE, "WL83", "12345678");
wifi_store_mode_info(STA_MODE, "WIFI_PS_TEST", "12345678");
ssid_stored_cnt = wifi_get_stored_sta_info(wifi_stored_sta_info);
for (int i = 0; i < ssid_stored_cnt; i++) {
    printf("1:wifi_get_stored_sta_info[%d]= %s \r\n", i, wifi_stored_sta_info[i].ssid);
}
wifi_del_stored_sta_info("WIFI_PS_TEST"); //中途删除掉其中一个存储过的SSID

Source: uart_cmd.c

该示例表明:协议栈支持多网络记忆(数组上限 32 条,ssid[33] / pwd[65] 长度约束),并且删除操作按 SSID 精确匹配。多记忆 + 最优信号选择(wifi_set_sta_connect_best_ssid(1))构成了"设备在多环境间移动自动切换最优网络"的能力。

WiFi 扫描机制

扫描是配网的前置步骤(发现可用热点)与自动重连的关键(评估信号强度)。SDK 将异步扫描封装为同步阻塞接口,供应用以简单方式获取结果。scan_list.c 使用 OS 信号量 + 互斥锁实现"请求-等待-取结果"三步:

struct wifi_scan_ssid_info *wifi_scan_list_get(u32 *total_scan_num)
{
    u32 sta_ssid_num;
    struct wifi_scan_ssid_info *sta_ssid_info;

    if (!os_sem_valid(&pscan_list->scan_sem)) {
        os_sem_create(&pscan_list->scan_sem, 0);
    }
    if (!os_mutex_valid(&pscan_list->scan_mutex)) {
        os_mutex_create(&pscan_list->scan_mutex);
    }

    scan_list_mutex_pend();
    wifi_scan_req();
    //等待WIFI_EVENT_STA_SCAN_COMPLETED事件
    if (scan_list_sem_pend(500)) { //单位10ms
        printf("get_scan_list timeout");
        pscan_list->sem_flag = 0;
        scan_list_mutex_post();
        return NULL;
    }

    sta_ssid_num = 0;
    sta_ssid_info = wifi_get_scan_result(&sta_ssid_num);
    if (total_scan_num) {
        *total_scan_num = sta_ssid_num;
    }
    wifi_clear_scan_result();
    scan_list_mutex_post();
    return sta_ssid_info;
}

void wifi_scan_list_release(struct wifi_scan_ssid_info *list)
{
    if (list) {
        free(list);
    }
}

Source: scan_list.c

关键机制拆解:

  • 信号量等待:wifi_scan_req() 发起扫描后,协议栈完成扫描会触发 WIFI_EVENT_STA_SCAN_COMPLETED 事件,事件处理回调中调用 scan_list_sem_post() 释放信号量(sem_flag 作为"是否真的在等待"的守卫,防止事件迟到时误触发);
  • 超时保护:scan_list_sem_pend(500) 以 10ms 为单位,即最长等待 5 秒;超时返回 NULL,调用方需自行处理失败;
  • 互斥串行:scan_mutex 保证同一时刻只有一个任务发起扫描,避免协议栈扫描状态机被并发请求破坏;
  • 内存所有权:wifi_get_scan_result 返回堆内存,使用完必须 wifi_scan_list_release 释放,而 wifi_clear_scan_result 清理协议栈内部缓存。

扫描结果的消费示例(scan 命令入口):

void wifi_get_list_example(void)
{
    u32 scan_num = 0;
    struct wifi_scan_ssid_info *list = wifi_scan_list_get(&scan_num);
    if (!list) {
        printf("wifi_scan_list_get fail\n");
        return;
    }
    printf("sta_ssid_num: %d\n", scan_num);
    for (int i = 0; i < scan_num; i++) {
        log_info("wifi_sta_scan_channel_test ssid = [%s],rssi = %d,snr = %d, auth_mode = %s",
                 list[i].ssid, list[i].rssi, list[i].snr, get_wifi_auth_mode(list[i].auth_mode));
    }
    wifi_scan_list_release(list);
}

Source: scan_list.c

扫描结果结构体 wifi_scan_ssid_info 至少包含 ssid、rssi(信号强度)、snr(信噪比)、auth_mode(认证方式)四个字段,get_wifi_auth_mode 将认证枚举转为可读字符串——RSSI/SNR 正是"信号最优 SSID 选择"(wifi_set_sta_connect_best_ssid)的排序依据。

lwIP 协议栈集成与 IP 管理

网络接口与 DHCP

STA 模式入网后,协议栈将数据面挂接到 lwIP 的 WIFI_NETIF 网络接口;应用通过 lwip_get_netif_info(WIFI_NETIF, ...) 读取当前 IP/掩码/网关,通过 net_set_lan_info 写入局域网配置。AP 模式由内置 dhcp_srv 承担地址分配,STA 模式由 lwIP DHCP Client 向路由器获取地址。

静态 IP 记忆(WiFi 重连加速)

wifi_demo_task.c 在 CONFIG_STATIC_IPADDR_ENABLE 下实现了"DHCP 地址记忆复用":连接成功后把 IP/掩码/网关/DNS、关联的 SSID、本机 MAC、网关 MAC、信道一起写入 VM_STA_IPADDR_INDEX;下次连接时若 SSID、本机 MAC、网关 MAC 全部匹配,则跳过 DHCP 直接使用记忆地址,从而显著缩短重连上电时间:

static int compare_dhcp_ipaddr(void)
{
    use_static_ipaddr_flag = 0;
    u8 local_mac[6];
    u8 gw_mac[6];
    struct sta_ip_info  sta_ip_info;
    struct netif_info netif_info;
    int ret = syscfg_read(VM_STA_IPADDR_INDEX, (char *)&sta_ip_info, sizeof(struct sta_ip_info));
    if (ret < 0) {
        log_info("compare_dhcp_ipaddr NO VM_STA_IPADDR_INDEX");
        return -1;
    }
    lwip_get_netif_info(WIFI_NETIF, &netif_info);
    struct wifi_mode_info info;
    info.mode = STA_MODE;
    wifi_get_mode_cur_info(&info);
    wifi_get_bssid(gw_mac);
    wifi_get_mac(local_mac);
    if (!strcmp(info.ssid, (const char *)sta_ip_info.ssid)
        && !memcmp(local_mac, sta_ip_info.local_mac, 6)
        && !memcmp(gw_mac, sta_ip_info.gw_mac, 6)) {
        use_static_ipaddr_flag = 1;
        log_info("compare_dhcp_ipaddr Match");
        return 0;
    }
    return -1;
}

Source: wifi_demo_task.c

static void store_dhcp_ipaddr(void)
{
    struct sta_ip_info  sta_ip_info = {0};
    u8 sta_channel, local_mac[6], gw_mac[6];
    if (use_static_ipaddr_flag) { //记忆IP匹配成功,不需要重新保存
        return;
    }
    struct netif_info netif_info;
    lwip_get_netif_info(WIFI_NETIF, &netif_info);
    ...
    strcpy((char *)sta_ip_info.ssid, info.ssid);
    memcpy(sta_ip_info.gw_mac, gw_mac, 6);
    memcpy(sta_ip_info.local_mac, local_mac, 6);
    sta_ip_info.ip = netif_info.ip;
    sta_ip_info.netmask = netif_info.netmask;
    sta_ip_info.gw = netif_info.gw;
    sta_ip_info.chanel = sta_channel;
    sta_ip_info.dns = *(u32 *)dns_getserver(0);
    syscfg_write(VM_STA_IPADDR_INDEX, (char *) &sta_ip_info, sizeof(struct sta_ip_info));
    log_info("store_dhcp_ipaddr");
}

Source: wifi_demo_task.c

compare_dhcp_ipaddr 的匹配条件刻意包含了网关 MAC(BSSID)与本机 MAC:只有"同一路由器 + 同一台设备"才复用 IP,防止换了路由器后误用旧网段的地址;注释还指出若路由器未联网(DHCP 拿不到公网 DNS),会放弃复用重新走 DHCP。dns_set_server 从记忆块中恢复 DNS,保证静态 IP 场景下域名解析可用。

底层硬件适配层(wifi_adapter.h)

wifi_bbm 工程中的 wifi_adapter.h 暴露了协议栈与硬件之间的底层适配接口,主要面向 Monitor/Raw 模式的高级用户(如协议分析、私有协议透传):

#define DROP_CRC_ERR   BIT(0) //drop CRC error frame
#define DROP_PHY_ERR   BIT(1) //drop physical error frame
#define DROP_UC_NOME   BIT(2) //drop not to me unicast frame
#define DROP_NOT_MYBSS BIT(3) //drop frame that is not my BSSID
#define DROP_VER_ERR   BIT(4) //drop 802.11 version error frame
#define DROP_DROP_MC   BIT(5) //drop multicast frame
#define DROP_BC        BIT(6) //drop broadcast frame
#define DROP_DUPL      BIT(7) //drop duplicated frame
#define DROP_CFACK     BIT(8) //drop CF-END + CF-ACK
#define DROP_CFEND     BIT(9) //drop CF-END
#define DROP_ACK       BIT(10) //drop ACK
#define DROP_CTS       BIT(11) //drop CTS
#define DROP_RTS       BIT(12) //drop RTS
#define DROP_PSPOLL    BIT(13) //drop PS-POLL
#define DROP_BA        BIT(14) //drop BA
#define DROP_BAR       BIT(15) //drop BAR
#define DROP_CTRL_RSV  BIT(16) //drop reserve control subtype

//复位对应的接收过滤包类型
void wf_rx_filt_set(unsigned int reg);
//设置对应的接收过滤包类型
void wf_rx_filt_reset(unsigned int reg);
//设置硬件MAC_BSSID寄存器 ,需要在wifi_raw_on后调用
void wf_asic_set_bssid(unsigned char *bssid);
//设置本机硬件MAC, 需要在wifi_raw_on后调用
void wf_asic_set_mac(unsigned char *mac);
//使能channel power检测,需要在wifi_raw_on后调用
void wf_channel_power_detect_enable(void);
//获取当前channel power
char get_current_channel_power(void);

Source: wifi_adapter.h

该层按位定义 802.11 帧类型的硬件 RX 过滤开关:wf_rx_filt_set/reset 分别复位/置位对应过滤位,例如开启 DROP_BC 后广播帧直接由硬件丢弃、不进协议栈,降低 CPU 负载;wf_asic_set_bssid/mac 在 wifi_raw_on 之后配置硬件匹配寄存器,用于 Raw 模式下精确收包;wf_channel_power_detect_enable 与 get_current_channel_power 提供信道功率检测,服务于产测与信道选择。这些接口属于协议栈对硬件的"后门",普通配网场景无需调用。

核心流程:STA 配网全链路

sequenceDiagram
    participant U as 用户/手机App
    participant UC as uart_cmd 任务
    participant WT as wifi_demo_task
    participant W as WiFi 协议栈 (wifi_connect)
    participant LW as lwIP / DHCP
    participant VM as syscfg VM 存储

    U->>UC: "sta <ssid> <pwd>"
    UC->>WT: wifi_sta_connect(ssid, pwd, save=1)
    WT->>W: wifi_set_sta_connect_best_ssid(0)
    WT->>W: wifi_enter_sta_mode(ssid, pwd)
    W->>W: 扫描/关联/802.11 握手
    W-->>WT: 连接成功事件 (net_event)
    WT->>LW: 启动 DHCP 获取地址
    LW-->>WT: IP/掩码/网关/DNS
    WT->>WT: wifi_sta_save_ssid() 检查 save_ssid_flag
    WT->>VM: wifi_store_mode_info(STA_MODE, ssid, pwd)
    WT->>VM: store_dhcp_ipaddr() 写 VM_STA_IPADDR_INDEX
    WT-->>U: 配网成功 (日志/状态指示)

    Note over U,W: 下次上电
    U->>WT: wifi_return_sta_mode()
    WT->>VM: wifi_get_mode_stored_info() 读记忆 SSID
    WT->>W: wifi_set_sta_connect_best_ssid(1)
    WT->>W: wifi_enter_sta_mode(记忆SSID)
    W->>W: 自动重连最优网络
flowchart TD
    Start([上电 wifi_on]) --> Mode{"配置 FORCE_DEFAULT_MODE?"}
    Mode -->|"0 最后记忆模式"| Last["恢复记忆模式"]
    Mode -->|"1 强制默认"| Default["进入默认模式"]
    Mode -->|"3-200 秒超时"| Timeout["STA 连接超时计时"]
    Last --> Sta["STA 模式"]
    Default --> Sta
    Timeout -->|"超时未连上"| Fallback["回退连接记忆/最优网络"]
    Sta --> Conn{"连接成功?"}
    Conn -->|"否"| Timeout
    Conn -->|"是"| Dhcp["DHCP / 静态IP复用"]
    Dhcp --> Save["wifi_sta_save_ssid / store_dhcp_ipaddr"]
    Save --> Done([联网就绪])

Configuration Options

以下配置集中在 sdk/apps/demo/demo_wifi/include/app_config.h 与 wifi_demo_task.c 顶部宏定义,按工程裁剪:

配置项类型默认值说明
TCFG_WIFI_ENABLE宏开关1(demo_wifi)使能 WiFi 功能及相应任务/内存分配
WIFI_AP_SSID / WIFI_AP_PWD字符串工程定义AP 模式默认热点名/密码(AP_SSID/AP_PWD)
WIFI_STA_SSID / WIFI_STA_PWD字符串工程定义无记忆 SSID 时的默认 STA 目标(STA_SSID/STA_PWD)
FORCE_DEFAULT_MODEint0wifi_on 后的初始模式:0=最后记忆模式,1=强制默认模式,3~200=STA 连接超时秒数,超时回退记忆/最优网络
CONNECT_BEST_SSIDint0启动后 STA 模式是否挑选记忆过的信号最优 WiFi
WIFI_MODE_CYCLE_TEST宏开关关循环测试模式切换:AP→STA→MONITOR→AP…(从默认或记忆模式开始)
CONFIG_STATIC_IPADDR_ENABLE宏开关工程定义使能静态 IP 记忆复用(VM_STA_IPADDR_INDEX)
CONFIG_ASSIGN_MACADDR_ENABLE宏开关工程定义使能 MAC 地址分配(mac_addr_succ_flag)
运行期常量类型值说明
扫描等待超时10ms 单位500scan_list_sem_pend(500),即 5 秒
扫描结果条数上限int32wifi_stored_sta_info[32] 记忆网络上限
SSID / 密码缓冲bytes33 / 65ssid[33]、pwd[65],含结尾 \0
UART 接收缓冲bytes1 * 1024buf[1*1024],循环缓冲,对齐 32
UART 命令任务栈bytes4 * 1024os_task_create(..., 4*1024, ...)

API Reference

以下 API 为 wifi/wifi_connect.h 在 demo 代码中的实际用法(签名以头文件为准),按调用场景组织:

模式切换

void wifi_enter_sta_mode(char *ssid, char *pwd) 进入 STA 工作站模式并尝试连接指定网络。

  • 参数:ssid 目标热点名;pwd 密码(开放网络传空串)。
  • 说明:由 wifi_sta_connect / wifi_return_sta_mode 调用,是配网的最终落点。

void wifi_enter_ap_mode(char *ssid, char *pwd) 进入 AP 热点模式,设备自建网络供手机接入(配合 net_set_lan_info + dhcp_srv)。

  • 参数:ssid 热点名;pwd 密码(可为空)。

void wifi_enter_p2p_mode(int mode, char *devname) 进入 P2P(Wi-Fi Direct)模式。

  • 参数:mode 取值 P2P_GO_MODE 或 P2P_GC_MODE;devname 设备名(缺省 JL_GO_TEST / JL_GC_TEST)。

void wifi_enter_smp_cfg_mode(void) 进入 SmartConfig 监听模式,等待手机 App 广播配网报文并解析 SSID/密码。

扫描

int wifi_scan_req(void):发起异步扫描,完成后协议栈上报 WIFI_EVENT_STA_SCAN_COMPLETED 事件。 struct wifi_scan_ssid_info *wifi_get_scan_result(u32 *num):获取扫描结果(堆内存),num 返回条数。 void wifi_clear_scan_result(void):清空协议栈内扫描缓存,防止陈旧结果干扰最优选择。

  • 返回的内存须用 free 释放(封装为 wifi_scan_list_release)。

SSID 记忆

void wifi_store_mode_info(int mode, char *ssid, char *pwd):保存网络到 VM(多网络,上限 32 条)。 u8 wifi_get_stored_sta_info(struct wifi_stored_sta_info *info):读出全部已存网络,返回条数。 int wifi_del_stored_sta_info(char *ssid):按 SSID 精确删除一条记忆。 void wifi_set_sta_connect_best_ssid(int enable):开关"自动连接信号最优已存网络"。

状态查询

int wifi_is_on(void):WiFi 是否已上电使能。 int wifi_get_mode_cur_info(struct wifi_mode_info *info):读取当前模式及所连 SSID/密码(info.mode 置为 STA_MODE)。 int wifi_get_mode_stored_info(struct wifi_mode_info *info):读取记忆的模式信息,返回非 0 表示无记忆。 void wifi_get_mac(u8 *mac):读取本机 MAC(6 字节)。 void wifi_get_bssid(u8 *bssid):读取当前关联 AP 的 BSSID。 u8 wifi_get_channel(void):读取当前信道。 const char *get_wifi_auth_mode(int auth_mode):认证模式枚举转可读字符串。

底层适配(wifi_adapter.h,Raw/Monitor 场景)

  • void wf_rx_filt_set(unsigned int reg) / void wf_rx_filt_reset(unsigned int reg):复位/置位 RX 硬件过滤位(DROP_* 位域)。
  • void wf_asic_set_bssid(unsigned char *bssid) / void wf_asic_set_mac(unsigned char *mac):配置硬件 BSSID/MAC 寄存器,需在 wifi_raw_on 后调用。
  • void wf_channel_power_detect_enable(void) / char get_current_channel_power(void):信道功率检测。

Failure Modes、Edge Cases 与并发

扫描超时与失败

  • scan_list_sem_pend(500) 5 秒超时返回 NULL,调用方必须判空(wifi_get_list_example 即先 if (!list) return;);
  • 事件迟到时由 sem_flag 守卫,避免悬空信号量污染下一次扫描;
  • 扫描结果内存必须释放,否则堆泄漏(wifi_scan_list_release)。

连接超时与回退

  • FORCE_DEFAULT_MODE 取 3~200 时作为 STA 连接超时;超时后自动回连最后记忆或最优网络,保证"路由器重启/暂时不可达"时设备不长期挂起;
  • 无记忆 SSID 时回退编译期 STA_SSID/STA_PWD,保证开箱即有默认网络可连。

并发与互斥

  • 扫描接口用 scan_mutex 串行化,多任务并发调用 wifi_scan_list_get 会互斥等待;
  • 协议栈事件回调与业务任务之间通过信号量解耦(扫描完成事件 → scan_list_sem_post),回调内不做耗时操作;
  • save_ssid_flag / request_connect_flag 为单任务内标志位,模式切换与保存动作由 wifi_demo_task 串行处理,避免竞态。

UART 边界

  • 循环缓冲回卷(UART_CIRCULAR_BUFFER_WRITE_OVERLAY)时冲掉全部数据重新接收,代价是可能丢命令,换取状态机不混乱;
  • 命令解析用 sscanf 按空格分隔,SSID 含空格或超长(>32)会解析失败或截断,属于已知限制。

IP 复用边界

  • 静态 IP 复用要求 SSID、本机 MAC、网关 MAC 三者全部匹配,任一不匹配则重新 DHCP;
  • 路由器无外网(DHCP 拿不到有效 DNS)时放弃复用,重新走完整 DHCP 流程(见源码注释)。

Performance、运维与扩展点

性能与资源

  • 扫描为阻塞 5 秒同步接口,业务设计上应避免在 UI/交互关键路径直接调用;批量命令(ssid_store_test)前先 os_time_dly(5*100) 等连接稳定;
  • RX 过滤(DROP_*)可在 Raw 模式下由硬件直接丢弃无关帧,是降低 CPU 中断负载的主要手段;
  • compare_dhcp_ipaddr 的记忆复用显著缩短重连上电时间(跳过 DHCP 交换)。

运维建议

  • 产测/调试优先走 UART 命令(sta/ap/scan/get_mac),日志标签 [WIFI](log_tag_const_*_WIFI_AP_STA、WIFI_RAW 等)可单独开关;
  • WIFI_MODE_CYCLE_TEST 用于回归测试模式切换状态机,量产固件应关闭;
  • SSID 记忆满 32 条后写入策略需按产品需求补充(示例代码未体现淘汰策略)。

扩展点

  • 配网协议扩展:在 cmd_do 中追加命令分支,复用 wifi_sta_connect 即可接入自有配网协议(如厂商 App 的 UDP/TCP 下发);
  • Raw 协议栈:wifi_bbm 的 wifi_adapter + raw_txrx(raw_test.c / kcp_test.c)支持私有无线协议开发,与标准 TCP/IP 数据面并存;
  • 网络事件:订阅 net_event(event/net_event.h)可在连接/断线/扫描完成等时机挂接业务逻辑(demo 中扫描完成事件即由此驱动信号量)。

Tests 与验证手段

  • ssid_store_test:验证 wifi_store_mode_info / wifi_get_stored_sta_info / wifi_del_stored_sta_info 的增删查闭环;
  • WIFI_MODE_CYCLE_TEST:循环切换 AP→STA→MONITOR 模式,回归模式状态机;
  • scan 命令:验证扫描、事件上报、结果解析(RSSI/SNR/认证方式)与内存释放;
  • wifi_raw_test(demo_wifi/examples)与 raw_txrx:Raw 收发与 KCP 传输测试,覆盖 wifi_adapter 底层接口。

Related Links

  • wifi_demo_task.c — 模式切换/SSID记忆/静态IP核心实现
  • uart_cmd.c — UART 配网命令入口
  • scan_list.c — 扫描同步封装
  • wifi_adapter.h — 底层 RX 过滤与硬件寄存器适配
  • demo_wifi 应用配置 app_config.h
  • 兄弟页面:WiFi 低功耗(low_power)、Raw TX/RX 透传(raw_txrx / wifi_raw_test)、网络事件机制(event/net_event.h)
Prev
蓝牙协议栈 (双模蓝牙)
Next
网络协议栈