找回密码
立即注册
搜索
发回帖 发新帖

6557

积分

0

好友

829

主题
发表于 昨天 23:23 | 查看: 1| 回复: 0

之前分享过 嵌入式Linux之wifi配网脚本分析,里面通过 shell 脚本调用 wpa_supplicant 等工具完成开发板配网。这次换一种实现方式:用 C++ 把同样的流程写一遍。

先看整体连接关系:

wpa_supplicant 连接开发板 WiFi 示意图

1 准备工作

我这个开发板之前配置了开机自动启动配网脚本。为了方便调试 C++ 程序,需要先把开机自启的配网脚本关掉。

打开 /etc/init.d 目录下的 S99myinit,把启动配网脚本那行注释掉:

#!/bin/sh

#fltest_wifi.sh -i wlan0 -s "wifi_name" -p wifi_password

如果临时需要联网,可以手动执行:

fltest_wifi.sh -i mlan0 -s "MERCURY_3394" -p "2H2+O2=2H2O"

另外,之前在飞凌开发板上已经搭好了 Ubuntu 下的交叉编译环境。编译时我习惯通过下面的方式指定交叉编译工具链,进行交叉编译:

export PATH=/home/xxpcb/myTest/OK3568/gcc_aarch64/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH
aarch64-linux-gnu-g++ test_wifi.cpp -o test_wifi

2 编写代码

2.1 主函数

参考之前 shell 脚本的配网流程,C++ 版的主函数如下:

#include <unistd.h>
#include <cstdlib> // 用于调用系统命令
#include <fstream>
#include <iostream>
#include <string>

#define NET_PORT "mlan0"

int main(int argc, char *argv[])
{
    std::string ssid = "MERCURY_3394";
    std::string password = "2H2+O2=2H2O";
    printf("[%s] argc:%d\n", __func__, argc);
    if (argc > 1)
    {
        if (argc == 3)
        {
            ssid = argv[1];
            password = argv[2];
        }
        else
        {
            printf("[%s] please input para: ssid password\n", __func__);
            return -1;
        }
    }
    printf("[%s] ssid:%s password:%s\n", __func__, ssid.c_str(), password.c_str());

    SYS_CMD(std::string("/etc/init.d/S80dnsmasq stop > /dev/null"));
    CreateWpaConfig(ssid, password);
    ConnectWiFi();

    return 0;
}

主函数里做了几件事:

  • 内置一个默认 WiFi 名称和密码,也支持通过命令行参数传入新的 SSID 和密码。
  • 先执行 S80dnsmasq stop,关闭可能影响连接的服务。
  • 然后创建 wpa_supplicant 配置文件。
  • 最后执行完整的连接流程。

2.2 执行 Linux 指令

在 C++ 中执行 Linux 命令,最直接的方式就是 std::system。为了方便观察程序执行到哪一步、具体调用了哪条命令,这里做了一层函数封装和宏定义:

#define SYS_CMD(cmd) SysCmd(__func__, cmd)

int SysCmd(const char *funcName, std::string cmd)
{
    printf("[%s] do %s\n", funcName, cmd.c_str());
    return std::system(cmd.c_str());
}

这样每次执行系统命令时,都能在终端里看到是哪个函数发起了这条命令。

2.3 创建 wpa 配置文件

CreateWpaConfig() 的作用是打开 /etc/wpa_supplicant.conf,并写入对应的连接参数:

#define WPA_CONF_FILE "/etc/wpa_supplicant.conf"

void CreateWpaConfig(const std::string &ssid, const std::string &password)
{
    printf("[%s] in\n", __func__);
    std::ofstream configFile(WPA_CONF_FILE);
    if (configFile.is_open())
    {
        configFile << "ctrl_interface=/var/run/wpa_supplicant" << std::endl;
        configFile << "ctrl_interface_group=0" << std::endl;
        configFile << "update_config=1" << std::endl;

        configFile << "network={" << std::endl;
        configFile << "    ssid=\"" << ssid << "\"" << std::endl;
        configFile << "    psk=\"" << password << "\"" << std::endl;
        configFile << "key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE" << std::endl;
        configFile << "group=CCMP TKIP WEP104 WEP40" << std::endl;
        configFile << "}" << std::endl;

        configFile.close();
    }
    else
    {
        printf("write config file err\n");
    }
    printf("[%s] out\n", __func__);
}

主要逻辑就是写入 ctrl_interface 等全局参数,再把 network={...} 段拼出来,其中 SSID 和密码用传入的参数填充。

2.4 配网处理

ConnectWiFi() 负责逐步执行 ifconfig、wpa_supplicant、wpa_cli 和 udhcpc 等命令:

void ConnectWiFi()
{
    printf("[%s] in\n", __func__);
    std::string tmpCmd;
    // down NET_PORT
    tmpCmd = "ifconfig " + std::string(NET_PORT) + " down > /dev/null";
    if (-1 == SYS_CMD(tmpCmd.c_str()))
    {
        printf("[%s] do %s failed!\n", __func__, tmpCmd.c_str());
        goto END;
    }

    // stop wpa_supplication
    tmpCmd = "ps -fe|grep wpa_supplicant |grep -v grep > /dev/null";
    if (0 == SYS_CMD(tmpCmd.c_str()))
    {
        tmpCmd = "kill -9 $(pidof wpa_supplicant)";
        if (-1 == SYS_CMD(tmpCmd.c_str()))
        {
            printf("[%s] do %s failed!\n", __func__, tmpCmd.c_str());
            goto END;
        }
    }

    // up NET_PORT
    tmpCmd = "ifconfig " + std::string(NET_PORT) + " up > /dev/null";
    if (-1 == SYS_CMD(tmpCmd.c_str()))
    {
        printf("[%s] do %s failed!\n", __func__, tmpCmd.c_str());
        goto END;
    }

    // start wpa_supplication
    tmpCmd = "(wpa_supplicant -Dnl80211,wext -i" + std::string(NET_PORT) + " -c " +
             std::string(WPA_CONF_FILE) + " >/dev/null) &";
    if (-1 == SYS_CMD(tmpCmd.c_str()))
    {
        printf("[%s] do %s failed!\n", __func__, tmpCmd.c_str());
        goto END;
    }
    sleep(3);

    // check status
    tmpCmd =
        "wpa_cli -i" + std::string(NET_PORT) + " status |grep COMPLETED |grep -v grep >/dev/null";
    if (-1 == SYS_CMD(tmpCmd.c_str()))
    {
        printf("[%s] do %s failed!\n", __func__, tmpCmd.c_str());
        goto END;
    }

    // udhcpc
    tmpCmd = "udhcpc -i" + std::string(NET_PORT);
    if (-1 == SYS_CMD(tmpCmd.c_str()))
    {
        printf("[%s] do %s failed!\n", __func__, tmpCmd.c_str());
        goto END;
    }

END:
    printf("[%s] out\n", __func__);
}

整体处理流程可以整理成下面的流程图:

C++ WiFi 配网处理流程图

这里只展示了正常情况下的流程,中间任意一个环节出错,都需要异常退出。

3 实测验证

先不加参数运行测试程序,程序会使用默认的 WiFi 名称和密码进行连接。从日志可以看到,最终拿到的 IP 是 192.168.5.111。

接着用手机开一个热点,模拟另一个 WiFi 网络,然后把手机热点的名称和密码作为参数传给程序再次运行。这次实际分配到的 IP 是 192.168.174.243:

test_wifi 程序实际运行日志与 IP 分配结果

4 总结

本篇介绍了如何使用 C++ 编程,来实现嵌入式Linux开发中,调用wpa_supplicant等工具进行wifi配网,通过编写测试例程,并进行实际测试,验证代码功能。

整体来看,C++ 版本和 shell 脚本版本的思路是一致的,只是把命令拼装和执行过程交给 C++ 来完成。如果后续要集成到自己的应用程序里,可以进一步把 CreateWpaConfig() 和 ConnectWiFi() 封装成接口使用。




上一篇:嵌入式Linux WiFi配网脚本分析:hostapd与wpa_supplicant自动联网实战
下一篇:Bazel 构建工具入门:Ubuntu 安装配置与 C++ 编译实例详解
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-22 06:36 , Processed in 0.075303 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表