✨✨ Rqtz 个人主页 : 点击✨✨
🎈PyQt系列专栏:点击🎈
🎈Qt智能车上位机专栏: 点击🎈
🎈Qt串口助手专栏:点击🎈
💫宗旨:共享IT之美,共创机器未来
目录
编辑
编辑
项目背景
相关参数
软件下载连接
软件图片
动图展示
shell命令配置串口名称过程
命令过程
shell命令解释
编辑
转为Qt框架实现
串口扫描识别
判断是USB还是ACM并查询串口内核编号
printshell函数实现精准获取串口内核
函数参数
使用C语言函数fopen()与fgets()获取输出
输出udev命令
输入管理员密码打开usb.rules编辑
代码结构
项目背景
由于在ubuntu电脑上配置多传感器(如雷达,陀螺仪,单片机)的串口,为了防止每次使用时都要对串口名称进行修改,因此需要重命名。如果按照终端的方法配置,不仅要打开文件,还要记住复杂的shell命令,十分的繁琐与复杂。基于这个问题,我使用C++Qt写了一款ubuntu串口重命名助手,全程不需要输入一条命令,直接点击按钮即可轻松配置串口,5到6秒钟就可以配置完成,十分好用。
相关参数
设备:ubuntu20.04 noetic
开发语言:C++/面向对象
软件框架:Qt
IDE:Qt Creater
核心内容:shell命令输出到管道
我是基于这位大佬的博客中描述的串口配置方法来实现的
[005] ubuntu下绑定USB设备的串口名称(KERNELS硬件端口号绑定)_串口名是ttyacm0是什么kernel-CSDN博客
软件下载连接
https://download.csdn.net/download/m0_75192474/89535177?spm=1001.2014.3001.5503
源码连接: 源码连接-githup
软件图片
比较简单
动图展示
shell命令配置串口名称过程
命令过程
- 查看串口详细信息:该命令查询的是ttyUSB0
udevadm info --attribute-walk --name=/dev/ttyUSB0
- 将内核输入到指定文件中
sudo gedit /etc/udev/rules.d/usb.rules
- 重启udev服务
sudo udevadm trigger
- 查询串口更改情况
ls -l /dev |grep ttyUSB
shell命令解释
-
udevadm
: 这是一个用户空间工具,用于查询和操作udev设备管理器。udev是一个动态设备管理器,它负责在Linux系统中管理设备节点的创建、删除和属性更新。 -
info
: 这是udevadm的一个子命令,用于显示设备的详细信息。 -
--attribute-walk
: 这是一个选项,表示要遍历设备的所有属性并显示它们的值。这将提供关于设备的各种属性的信息,如制造商、型号、序列号等。 -
--name=/dev/ttyUSB0
: 这是一个选项,指定要查询的设备的名称。在这个例子中,我们查询的是名为/dev/ttyUSB0
的设备。这个设备通常是一个串行端口设备,例如USB转串口适配器。 -
sudo gedit /etc/udev/rules.d/usb.rules:该文件用于定义USB设备的自定义规则。
-
ls -l /dev
: 这部分命令会列出/dev
目录下的所有文件和目录,以及它们的详细信息。-l
选项表示长格式输出,会显示每个文件的类型、权限、所有者、组、大小、最后修改时间和名称。 -
| :将命令输出传给管道。
-
grep ttyUSB
: 这部分命令是一个过滤工具,它会从前面的命令的输出中筛选出包含ttyUSB
字符串的行。换句话说,它会显示所有以ttyUSB
开头的设备文件。 -
udevadm info --attribute-walk --name=/dev/ttyUSB0 | grep 'KERNELS' | awk '{print $1}' :将串口设备的详细信息传入管道
-
| grep 'KERNELS'
:这部分命令会从上述命令的输出中筛选出包含KERNELS
字符串的行。 -
| awk '{print $1}'
:这部分命令会处理grep
命令的输出,并使用awk
命令仅提取每行的第一个字段(即KERNELS
的值)。
转为Qt框架实现
串口扫描识别
.pro文件中添加 serialport模块
遍历串口,count为显示串口的数量,并添加到serial_list中,其为一个Qcombox.
QList<QSerialPortInfo> serialPorts = QSerialPortInfo::availablePorts();
//遍历串口信息列表并输出串口号
foreach (const QSerialPortInfo &serialPortInfo, serialPorts) {
count++;
ui->serial_list->addItem(serialPortInfo.portName());
}
判断是USB还是ACM并查询串口内核编号
//判断是USB还是ACM并查询串口内核编号
void port::on_see_kernel_clicked()
{
for(int i=0;i<ui->serial_list->currentText().length();i++)
{
if(ui->serial_list->currentText()[i]=="U"&&ui->serial_list->currentText()[i+1]=="S"&&ui->serial_list->currentText()[i+2]=="B")
{
cr1=ui->serial_list->currentText()[i+3];
usbflag=true;
}
if(ui->serial_list->currentText()[i]=="A"&&ui->serial_list->currentText()[i+1]=="C"&&ui->serial_list->currentText()[i+2]=="M")
{
cr2=ui->serial_list->currentText()[i+3];
acmflag=true;
}
}
if(usbflag)
printshell(QString(cr1).toStdString(),"udevadm info --attribute-walk --name=/dev/ttyUSB","KERNELS",1);
else if(acmflag)
printshell(QString(cr2).toStdString(),"udevadm info --attribute-walk --name=/dev/ttyUSB","KERNELS",1);
}
- for循环:为遍历当前的combox的内容,判断是否为USB或ACM,并把当前的串口索引号放到cr1/cr2中
- printshell函数:将索引号和相关命令参数传入,精准获取所需要的内容。
printshell函数实现精准获取串口内核
//将shell命令输出到管道并按规则打印
QString port::printshell(std::string data,std::string comm,std::string findstr,int num)
{
std::array<char,128> buffer;
std::string res;
//字符串拼接
std::stringstream str;
str<<comm<<data<<" | grep '"<<findstr<<"' | awk '{print $"<<num<<"}'";
qDebug()<<str.str().c_str();
//创建一个文件对象指针
FILE* pipe = popen(str.str().c_str(),"r");
if (!pipe) {
std::cerr << "popen() failed!" << std::endl;
}
//fgets函数直到有一个换行符或文件末尾才终止
int temp = 0;
while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
temp++;
//第二个为需要的那一个
if(temp==2)
{
ui->KERENEL->setText(buffer.data());
qDebug()<<buffer.data();
}
res+=buffer.data();
}
auto returnpope = pclose(pipe);
return QString::fromStdString(res);
}
函数参数
- std::string data : 为串口设备索引号,如ttyUSB0中的0
- std::string comm: 为查询串口设备的命令 即udevadm info --attribute-walk --name=/dev/ttyUSB
- std::string findstr:为grep命令查找的内容 如KERNELS
- int num:awk命令所打印的字段
- 使用std::stringstream拼接得到完整的命令 udevadm info --attribute-walk --name=/dev/ttyUSB0 | grep 'KERNELS' | awk '{print $2}'
- 效果
但是此时我们需要的是第二个数据,红框中的
使用C语言函数fopen()与fgets()获取输出
- fopen()函数:参数1 :传入shell命令的c风格字符串,参数2: 访问方式
- fgets()函数 原型解析:
fgets()
函数的原型为char *fgets(char *str, int n, FILE *stream)
。 - 参数详解:
char *str
:这是一个指向字符数组的指针,用于存储从流中读取的字符串。int n
:这是要读取的最大字符数(包括最后的空字符'\0')。通常使用数组长度作为此参数。FILE *stream
:这是指向FILE
对象的指针,标识了要从中读取字符的流。
while (fgets(buffer.data(), buffer.size(), pipe) != nullptr)
这段代码用于从管道或文件中逐行读取数据,直到读取到文件末尾或者发生错误返回空指针为止。- if(temp==2) 时刚好读取到第二行也就是我们需要的数据,把他添加到输入框中
输出udev命令
//输出串口内核命令kernels
void port::on_pushButton_2_clicked()
{
if(ui->KERENEL->text()==""||ui->set_portname->text() == "")
{
ui->textEdit->append("串口内核或串口别名不能为空..................");
}
else {
//除去最后那个换行符
QString aimker = "";
for (int i = 0;i<ui->KERENEL->text().size();i++) {
if(ui->KERENEL->text()[i]=="\n")
break;
aimker+=ui->KERENEL->text()[i];
}
//串口内核命令
QString com=aimker+", MODE:=\"0777\", GROUP:=\"dialout\", SYMLINK+=\"" +ui->set_portname->text()+"\"";
ui->rule_edit->setText(com);
if(usbflag)
{
ui->textEdit->insertHtml(QString("<strong><font color= 'red' >%1</font> </strong> ").arg("/dev/ttyUSB"+QString(cr1)));
ui->textEdit->insertHtml(QString("<font color= 'blue' >%1</font><hr> ").arg(com));
}
else if(acmflag) {
ui->textEdit->insertHtml(QString("<strong><font color= 'red' >%1</font> </strong>").arg("/dev/ttyACM"+QString(cr2)));
ui->textEdit->insertHtml(QString("<font color= 'blue' >%1</font><hr> ").arg(com));
}
}
}
核心:
QString com=aimker+", MODE:=\"0777\", GROUP:=\"dialout\", SYMLINK+=\"" +ui->set_portname->text()+"\"";
- aimker 为串口设备的kERNELS
- ui->set_portname->text() 为设置的串口别名
- 生成命令 KERNELS=="3-3:1.0", MODE:="0777", GROUP:="dialout", SYMLINK+="rplidar"
- 使用富文本将内容按照不同颜色插入信息框.
输入管理员密码打开usb.rules编辑
//打开usb.rules文件修改
void port::on_change_btn_clicked()
{
if(ui->password->text()=="")
{
ui->textEdit->insertPlainText("请输入管理员密码...............\n");
}
else {
//使用&使其在后台运行不妨碍主线程
std::string command = "echo '"+ui->password->text().toStdString()+"' | sudo -S gedit /etc/udev/rules.d/usb.rules &";
system(command.c_str());
ui->udev_btn->setDisabled(false);
}
}
使用的是
echo "your_password" | sudo -S command
- 将管理员密码与shell命令相结合,用-S连接,可以不用在终端输入密码,直接用用户在输入框中输入的密码。
- comand 指的是终端shell命令 --> gedit /etc/udev/rules.d/usb.rules &
- 使用system函数启动并使用 "&"在后台运行,注意有部分电脑可能打开这个文件有点慢,请耐心等待,如果一直没有打开,有可能是密码输入错误.
- 将命令复制到文件中点击保存.
代码结构
如有错误,请大佬指正批评!