北京迅为IMX6ULL开发板 DHT11&网络编程实战练习

转自:【北京迅为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 断开连接\r\n").arg(hostAddress));

ui->zeroLb->setText(tr("

%2 断开连接\r\n").arg(hostPort));

break;

}

default:

{

QString hostAddress=tcpSocket->QAbstractSocket::peerAddress().toString();

quint16 hostPort = tcpSocket->QAbstractSocket::peerPort();

error = -1;

ui->messageLb->setText(tr("客户端[%1] [%1]连接错误\r\n").arg(hostAddress,hostPort));

break;

}

}

}

 

至此终结者的 Qt 服务器端编程基本结束,可以联网使用 windows 的一些小工具来调试服务器的收发功能。

 

 

http://topeetboard.com