转自:【北京迅为i.mx6ull终结者开发板使用手册】第六十三章 QT 实战项目
硬件平台:IMX6ULL开发板
63.1 DHT11&网络编程实战练习
本项目为阶段性练习,内容简洁实用,目的在于让初学者能够巩固前面知识,学以致用,逐步提升 C++ 和 Qt 水平,为学习下面的教程做铺垫。本章节使用的资料已经放到了开发板网盘资料中,路径为“11_Linux 系统开发进阶Qt 开发_章节使用资料”。
63.1.1 项目前准备
工具及环境:
1. 开发环境:Ubuntu16.04
2. 工具:Qt creator
3. 迅为电子 i.MX6ULL 终结者开发板
MX6ULL 终结者开发板已板载了 DHT11 传感器,且提供的 Linux 镜像和设备树已配置好了 dht11 接口, dht11 驱动程序使用高版本 Linux 内核自带的稳定驱动程序,根文件系统使用网盘资料里的 Yocto 镜像,使用手册有详细的系统移植和 Qt5 移植步骤。开发板要保证 iio 接口正常,如图 63.1.1.1:
cd /sys/bus/iio/devices/iio:device1
多 cat 几次, cat in_temp_input ,出现 00 为结尾的数字为正常
63.1.2 软件流程图
本实验目的是练习 Qt 的使用。把 dht11 采集的温度湿度显示在触摸屏的 Qt 上,并通过 TCP 传输到 PC 机的 Qt 界面上。应用程序将采集到的数据利用 UDP 网络套接字传输给终结者的 Qt 进程,终结者的 Qt 将数据转发给 PC 端的 Qt 显示界面。流程图如图 63.1.2.1:
63.1.3 Linux 应用程序
功能描述:
1. 获取温湿度数据。
2. 将温湿度封装成消息通过 UDP 发送到 i.MX6ULL 服务器。
高版本 Linux 内核自带了 dht11 的驱动,使用者不用再去关心它的时序问题,可以用标准 IO 读取 sysfs
的/sys/bus/iio/devices/iio:device1/下的温湿度端口,测试读取成功后将数据发送到 Qt 进程。
为了让读者更好地理解 Qt 的 UDP,所以此处没有用到操作系统进程间通信机制,直接使用 UDP 网络
套接字,代码在 readDHT11.c:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
struct sockaddr_in serveraddr;
63.1.3 Linux 应用程序
功能描述:
1. 获取温湿度数据。
2. 将温湿度封装成消息通过 UDP 发送到 i.MX6ULL 服务器。
高版本 Linux 内核自带了 dht11 的驱动,使用者不用再去关心它的时序问题,可以用标准 IO 读取 sysfs
的/sys/bus/iio/devices/iio:device1/下的温湿度端口,测试读取成功后将数据发送到 Qt 进程。
为了让读者更好地理解 Qt 的 UDP,所以此处没有用到操作系统进程间通信机制,直接使用 UDP 网络
套接字,代码在 readDHT11.c:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
struct sockaddr_in serveraddr;
63.1.3 Linux 应用程序
功能描述:
1. 获取温湿度数据。
2. 将温湿度封装成消息通过 UDP 发送到 i.MX6ULL 服务器。
高版本 Linux 内核自带了 dht11 的驱动,使用者不用再去关心它的时序问题,可以用标准 IO 读取 sysfs
的/sys/bus/iio/devices/iio:device1/下的温湿度端口,测试读取成功后将数据发送到 Qt 进程。
为了让读者更好地理解 Qt 的 UDP,所以此处没有用到操作系统进程间通信机制,直接使用 UDP 网络
套接字,代码在 readDHT11.c:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
struct sockaddr_in serveraddr;
fread( buf, 1, sizeof(buf), fp );
fclose(fp);
sscanf( buf, "%d", &value );
*tem_Value = value;
return 0;
}
int mathDHT(int value)
{
return (value/1000);
}
/*
* 获取 UDP socket
*/
int udpNet(void)
{
int sockfd;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd <0){
perror("socket");
return -1;
}
return sockfd;
}
int main(int argc, const char *argv[])
{
int hum,tem,temNew,humiNew;
int temOld=0,humOld=-10;
int fdSocket = udpNet();//获取 UDP 套接字
char ts[6];
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi("7089")); //目标端口号
serveraddr.sin_addr.s_addr = 0; //本机 IP
while(1){
sleep(1);
read_Temp(&tem);//获取温度
fread( buf, 1, sizeof(buf), fp );
fclose(fp);
sscanf( buf, "%d", &value );
*tem_Value = value;
return 0;
}
int mathDHT(int value)
{
return (value/1000);
}
/*
* 获取 UDP socket
*/
int udpNet(void)
{
int sockfd;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd <0){
perror("socket");
return -1;
}
return sockfd;
}
int main(int argc, const char *argv[])
{
int hum,tem,temNew,humiNew;
int temOld=0,humOld=-10;
int fdSocket = udpNet();//获取 UDP 套接字
char ts[6];
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi("7089")); //目标端口号
serveraddr.sin_addr.s_addr = 0; //本机 IP
while(1){
sleep(1);
read_Temp(&tem);//获取温度
fread( buf, 1, sizeof(buf), fp );
fclose(fp);
sscanf( buf, "%d", &value );
*tem_Value = value;
return 0;
}
int mathDHT(int value)
{
return (value/1000);
}
/*
* 获取 UDP socket
*/
int udpNet(void)
{
int sockfd;
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd <0){
perror("socket");
return -1;
}
return sockfd;
}
int main(int argc, const char *argv[])
{
int hum,tem,temNew,humiNew;
int temOld=0,humOld=-10;
int fdSocket = udpNet();//获取 UDP 套接字
char ts[6];
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi("7089")); //目标端口号
serveraddr.sin_addr.s_addr = 0; //本机 IP
while(1){
sleep(1);
read_Temp(&tem);//获取温度
63.1.4 终结者 Qt 服务器
Qt 服务器功能描述:
1.接收应用程序发来的数据并解析;
2.界面显示出:温湿度,PC 客户端的连接状态,系统时间;
3.通过 TCP 向 PC 客户端发送数据包。
63.1.4.1 界面布局
温度显示:LCD Number
湿度显示:LCD Number
系统时间,连接状态提示: Label
关闭按钮:push Button
Mainwindow.ui :
在属性栏设置主窗口尺寸,宽度为 1024,高度为 600,添加两个 LCD Number 组件,设置最小宽度 500,最小高度为 290,然后垂直布局两个 LCD Number,如:图 63.1.4.1.2
给 LCD Number 添加颜色,右击此组件,选择“改变样式表”,
在弹出的界面选择“添加颜色”,点击“color”,
在弹出的颜色选择框中选择想要添加的颜色,然后点击“OK”。
自动回到样式表编辑器,可以看到添加的 color,点击”apply”,然后点击”OK”,发现 LCD Number 的颜色已经改变,另外一个 LCD Number 的操作方法不再赘述。
在主界面右上角添加两个 Label,一个用来显示时间,一个显示 TCP 连接状态,宽度 500,高度 130,垂直布局,如图 63.1.4.1.7:
添加退出按钮,设置最小高度 80,最小宽度 100,修改字体大小:
在按钮左侧添加 Label,最小宽度 300,最小高度 290,在按钮和 Label 间添加弹簧,然后水平布局,效果如图 63.1.4.1.9:
点击主窗口,然后点击栅格布局,效果如图 63.1.4.1.10:
最后改变各控件的名称,方便在代码中调用。
到此,服务器端基本 ui 界面组装完成,读者可以在此基础上美化。
63.1.4.2 UDP 服务器端
步骤一:在工程下创建设计师界面类 datafrom,自动生成 .h .cpp .ui 文件。Ui 界面 Label 显示连接状态,close 按钮自动关联槽函数(右键转到槽),槽函数内关闭此界面。
void datafrom::on_pushButton_clicked()
{
this->close();
}
步骤二:创建 UDP socket 套接字,绑定数据信号和读取数据槽函数。
步骤三:读取数据槽函数内读取解析数据。
datafrom.h:
class datafrom : public QWidget
{
Q_OBJECT
public:
explicit datafrom(QWidget *parent = nullptr);
~datafrom();
QUdpSocket * udpSocket;
QString tempt;//保存温度值
QString humid;//保存湿度值
private slots:
void readyRead_Slot(void);
void on_pushButton_clicked();
private:
Ui::datafrom *ui;
};
datafrom.cpp :
#include <QDebug>
#include <QUdpSocket>
datafrom::datafrom(QWidget *parent) :
QWidget(parent),
ui(new Ui::datafrom)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
//绑定本端口的端口号
if(udpSocket->bind(7089) == true){
ui->message->setText("Udp create successful!");
}else{
ui->message->setText("Udp create failed");
}
//绑定数据信号和槽函数
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_Slot()));
}
datafrom::~datafrom()
{
delete ui;
}
/*
*读取数据槽函数
*/
void datafrom::readyRead_Slot()
{
QString buf;
QByteArray array;
//hasPendingDatagrams()返回 true 时表示至少有一个数据报在等待被读取
while(udpSocket->hasPendingDatagrams()){
//获取数据
array.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(array.data(),array.size());
buf = array.data();
QString t = buf.mid(0,2);
QString h = buf.mid(3,4);
tempt = t;
humid = h;
qDebug()<<tempt << " udp "<< humid <<endl;
}
}
//关闭此窗口
void datafrom::on_pushButton_clicked()
{
this->close();
}
Mainwindow.cpp :
udpcli = new datafrom(this);
//获取 UDP 对象的温湿度成员
temp = udpcli->tempt;
humid = udpcli->humid;
在开发板终端可看到对应的打印信息。
63.1.4.3 TCP 服务器端
步骤一:创建 TCP server, socket 套接字对象,绑定端口号,开始监听,等待客户端连接,绑定newConnection()和 newConnection_Slot()槽函数。
步骤二:新连接 newConnection_Slot()槽函数内开启定时器,定时时间(刷新时间)到后向 TCP 客户端发送消息,
void MainWindow::newConnection_Slot(void)
{
tcpSocket = tcpServer->nextPendingConnection();
connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_Solt()));
ui->messageLb->setText("连接成功");
timer.start(1000);
connect(&timer,SIGNAL(timeout()),this,SLOT(send_Solt()));//定时
//将客户端连接异常信号与槽函数绑定。
connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),this,
SLOT(clientError_Solt(QAbstractSocket::SocketError)));//客户端状态异常
}
//发送数据
void MainWindow::send_Solt()
{
//显示时间
ui->timeLb->setText(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
//从 UDP 对象获取数据
temp = udpcli->tempt;
humid = udpcli->humid;
//显示到 Qt 的 LCDNumber 界面
ui->tempLcd->display(temp);
ui->humidLcd->display(humid);
//消息封装,将消息推送到客户端
QString post = "Post::" +temp + "," + humid;
tcpSocket->write(post.toUtf8());
}
步骤三:将客户端连接状态显示到界面上。
//连接异常处理
void MainWindow::clientError_Solt(QAbstractSocket::SocketError)
{
int error = tcpSocket->error();
switch(error)
{
case QAbstractSocket::RemoteHostClosedError://客户端断开
{
//获取客户端的信息
QString hostAddress=tcpSocket->QAbstractSocket::peerAddress().toString();
quint16 hostPort = tcpSocket->QAbstractSocket::peerPort();
ui->messageLb->setText(tr("客户端 %2 断开连接 ").arg(hostAddress));
ui->zeroLb->setText(tr("
%2 断开连接 ").arg(hostPort));
break;
}
default:
{
QString hostAddress=tcpSocket->QAbstractSocket::peerAddress().toString();
quint16 hostPort = tcpSocket->QAbstractSocket::peerPort();
error = -1;
ui->messageLb->setText(tr("客户端[%1] [%1]连接错误 ").arg(hostAddress,hostPort));
break;
}
}
}
至此终结者的 Qt 服务器端编程基本结束,可以联网使用 windows 的一些小工具来调试服务器的收发功能。
http://topeetboard.com