上篇文章:嵌入式Linux之wifi配网脚本分析,介绍了嵌入式Linux开发板中,通过sh脚本调用wpa_supplicant等工具进行配网。
本篇,来介绍如何通过C++编程 ,来实现同样的功能。
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 主函数
参考sh脚本的配网流程,编写对应的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:%dn", __func__, argc);
if (argc > 1)
{
if (argc == 3)
{
ssid = argv[1];
password = argv[2];
}
else
{
printf("[%s] please input para: ssid passwordn", __func__);
return -1;
}
}
printf("[%s] ssid:%s password:%sn", __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名称和密码,也支持运行时传入参数,如果有对应的参数,则使用参数中的wifi名称和密码先执行S80dnsmasq stop(可参考上篇文章中的介绍)然后创建一个wpa的配置文件最后进行联网
2.2 执行Linux指令
在C++中实现Linux指令的执行,可使用std::system,为了便于分析程序的执行,在每次执行Linux指令时,打印出对应执行的指令内容,这里做了一个函数封装以及宏定义封装,用于打印是哪个函数调用了哪个指令:
#define SYS_CMD(cmd) SysCmd(__func__, cmd)
int SysCmd(const char *funcName, std::string cmd)
{
printf("[%s] do %sn", funcName, cmd.c_str());
return std::system(cmd.c_str());
}
2.3 创建wpa配置文件
#define WPA_CONF_FILE "/etc/wpa_supplicant.conf"
void CreateWpaConfig(const std::string &ssid, const std::string &password)
{
printf("[%s] inn", __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 errn");
}
printf("[%s] outn", __func__);
}
主要功能就是打开"/etc/wpa_supplicant.conf",并写入对应的配置项。
2.4 配网处理
调用各种Linux指令,进行配网
void ConnectWiFi()
{
printf("[%s] inn", __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] outn", __func__);
}
处理流程可归纳为如下流程图:
这里只描述了正常的情况,中间环节若出错,则需要异常退出。
3 实测验证
先不加参数运行测试程序,使用的默认的wifi名称和密码进行连接,可以看到最后的IP是192.168.5.111。
使用手机再开一个热点(模拟另一个wifi),然后指定手机热点的名称和密码作为参数,运行测试程序,可以看到最后的IP是192.168.174.243
4 总结
本篇介绍了如何使用C++编程,来实现嵌入式Linux开发中,调用wpa_supplicant等工具进行wifi配网,通过编写测试例程,并进行实际测试,验证代码功能。