QT上位机串口实时温湿度显示

STM32与上位机通信协议——UART协议:
串行通讯需要有通信协议
通信协议:规定发送与接收方,通信的方式与要求,数据的格式
由RXD和TXD两条线,由于没有时钟线,所以需要规定波特率
数据传输速率就是波特率
UART(串行异步全双工)
采用的是串行通信,也就是一条传输线,一位一位的顺序发送(可以远距离传输,传输较慢)
异步通信是以一个字符为传输单位,每个字符为10位(1个起始位,7个数据位,1个校验位,1个停止位)
通信中两个字符之间的时间间隔不固定,但是同一个字符相邻位之间时间间隔是固定的:
数据通信格式
1、起始位
2、数据位
3、校验位
4、停止位
5、空闲位
STM32需要通过串口向上位机完成数据上传,然后完成实时显示。
所以需要先完成上位机的一个开发,上位机开发工具选择QT
创建QT工程:
在这里插入图片描述

选择QT Widgets 修改名称为:serialdisplay,路径自己选择
在这里插入图片描述

直接点击下一步,到这个界面:
在这里插入图片描述
修改一下类名为:SerialDisplay
在这里插入图片描述

直接点击下一步,直到点击完成 创建工程就成功了,创建完成可以运行一下;
接下来就是添加空间,找到设计界面,点击ui
在这里插入图片描述
就转到这个界面,可以开始添加控件,左边就是控件栏:
在这里插入图片描述
添加完控件如下:
在这里插入图片描述
修改右边对应控件的名字,便于代码里使用;
在这里插入图片描述
然后就是代码部分:
右击你要实现功能的控件,比如说打开串口按钮如下:
在这里插入图片描述
点击转到槽函数:
在这里插入图片描述
选择点击触发,点击ok,就会跳转到以下界面:
在这里插入图片描述
对应实现该函数即可。其他我就不一一介绍了:
直接上源码:
serialdisplay.cpp文件

#include "serialdisplay.h"
#include "ui_serialdisplay.h"
#include <QDebug>
#include <QMessageBox>
#include <QFontDialog>

#include <QJsonObject>
#include <QJsonDocument>

SerialDisplay::SerialDisplay(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::SerialDisplay)
{
    ui->setupUi(this); 
    serialflag = 0;//表示串口关闭
    setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint);// 禁止最大化按钮
    serialport = new QSerialPort();//分配内存
    ui->dateTimeEdit->setDisabled(true);//
    timerid = startTimer(1000);//开启定时器,每隔1秒
}

SerialDisplay::~SerialDisplay()
{
    killTimer(timerid);//关闭
    delete ui;
    delete serialport;
}

//串口获取
void SerialGetData::Serial_read()
{
    int temp = 0;//
    int humi = 0;
    QByteArray recvData;
    try {
        recvData = SerialGetData::serial_recv_Data();//从串口接收数据
    } catch (MyExcption &err) {
        QMessageBox::warning(NULL , "提示", err.what());
        return;
    }
    QString receive = QString::fromLocal8Bit(recvData.constData());
    //json格式数据解析 比如:{"温度":"10℃","湿度":"20%"}
    QJsonParseError err;
    QByteArray arr ;
    arr.append(receive);
    QJsonDocument doc = QJsonDocument::fromJson(arr,&err);
    if(err.error != QJsonParseError::NoError){
        qDebug() << "转换失败";
        return;
    }
    QJsonObject obj = doc.object();
    temp = SerialGetData::getNumsFromStr(obj.value("温度").toString());
    humi = SerialGetData::getNumsFromStr(obj.value("湿度").toString());
   // qDebug() << receive;

    /*分割字符串的方法*/
    /*//自定义数据格式(温度:10-湿度:20或者10℃-20%)
    QStringList list = receive.split("-");//以:分割字符串
    if(list[0].contains("温度") || list[0].contains("℃")){
        temp = SerialGetData::getNumsFromStr(list[0]);
        humi = SerialGetData::getNumsFromStr(list[1]);
    }
    else if(list[0].contains("湿度") || list[0].contains("%")){
        temp = SerialGetData::getNumsFromStr(list[1]);
        humi = SerialGetData::getNumsFromStr(list[0]);
    }
    else{
        QMessageBox::information(NULL,"提示","数据格式错误",QMessageBox::Ok);
        return;
    }
    */
    ui->lcdTemp->display(temp);
    ui->lcdHuim->display(humi);
}
//串口接收
QByteArray SerialGetData::serial_recv_Data() throw(MyExcption)
{
    //读取串口收到的数据
    QByteArray buffer = serialport->readAll();
    //为空的话
    if(buffer.isEmpty()){
        throw("收到数据为空");
        return NULL;
    }
    return buffer;
}
//获取字符串中数字部分
int SerialGetData::getNumsFromStr(QString data)
{
    QString num;
    int j = 0;
    for(int i = 0 ; i < data.length();i++){
        if(data[i] >= '0' && data[i] <= '9'){
            num[j] = data[i];
            j++;
        }
    }
    return num.toInt();
}
//扫描串口
void SerialGetData::on_scanSerialBtn_clicked()
{
    // 清除当前显示的端口号
     ui->serialPortBox->clear();
     //检索端口号
     foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
     {
         ui->serialPortBox->addItem(info.portName());
     }
}
//打开串口
void SerialGetData::on_openSerialBtn_clicked()
{
    if(ui->openSerialBtn->text() == QString("打开串口"))  //串口未打开
    {
        //设置端口号
        //qDebug() << ui->serialPortBox->currentText();
        serialport->setPortName(ui->serialPortBox->currentText());
        //设置波特率
        serialport->setBaudRate(ui->baudRateBox->currentText().toInt());
        //设置数据位
        switch (ui->dataBitBox->currentText().toInt())
        {
            case 8: serialport->setDataBits(QSerialPort::Data8); break;
            case 7: serialport->setDataBits(QSerialPort::Data7); break;
            case 6: serialport->setDataBits(QSerialPort::Data6); break;
            case 5: serialport->setDataBits(QSerialPort::Data5); break;
            default: break;
        }
        //设置停止位
        switch (ui->stopBitBox->currentText().toInt())
        {
            case 1: serialport->setStopBits(QSerialPort::OneStop);break;
            case 2: serialport->setStopBits(QSerialPort::TwoStop);break;
            default:break;
        }
        //设置校验方式
        switch (ui->chekBitBox->currentIndex())
        {
            case 0: serialport->setParity(QSerialPort::NoParity);break;
            default:break;
        }
        //设置流控制模式
        serialport->setFlowControl(QSerialPort::NoFlowControl);
        //打开串口
        if(serialport->open(QIODevice::ReadWrite)==false)
        {
            QMessageBox::warning(NULL , "提示", "串口打开失败!");
            return;
        }
        // 失能串口设置控件
        ui->serialPortBox->setEnabled(false);
        ui->chekBitBox->setEnabled(false);
        ui->baudRateBox->setEnabled(false);
        ui->dataBitBox->setEnabled(false);
        ui->stopBitBox->setEnabled(false);
        ui->scanSerialBtn->setEnabled(false);
        //调整串口控制按钮的文字提示
        ui->openSerialBtn->setText(QString("关闭串口"));
        ui->openSerialBtn->setStyleSheet("background-color: rgb(255, 255, 255);\
                                       color: rgb(255,0,0);\
                                       border-color: rgb(255, 0, 0);");
        serialflag = 1;//串口打开
    }
    else       //串口已经打开
    {
        serialport->close();
        // 使能串口设置控件
        ui->serialPortBox->setEnabled(true);
        ui->chekBitBox->setEnabled(true);
        ui->baudRateBox->setEnabled(true);
        ui->dataBitBox->setEnabled(true);
        ui->stopBitBox->setEnabled(true);
        ui->scanSerialBtn->setEnabled(true);
        //调整串口控制按钮的文字提示
        ui->openSerialBtn->setText(QString("打开串口"));
        ui->openSerialBtn->setStyleSheet("background-color: rgb(0, 255, 255);\
                                       color: rgb(0,0,255);\
                                       border-color: rgb(255, 0, 0);");
        serialflag = 0;//串口关闭
    }
}
//建立连接
void SerialGetData::on_eConnectBtn_clicked()
{
   if(serialflag == 0){
       QMessageBox::information(NULL,"提示","串口未打开",QMessageBox::Ok);
       return;
   }
   ui->eConnectBtn->setEnabled(false);
   ui->eDisconnectBtn->setEnabled(true);
   ui->eConnectBtn->setStyleSheet("background-color: rgb(255, 255, 255);\
                                  color: rgb(255, 0, 0);\
                                  border-color: rgb(255, 0, 0);");
   ui->eDisconnectBtn->setStyleSheet("background-color: rgb(0, 255, 255);\
                                  color: rgb(0, 0, 255);\
                                  border-color: rgb(255, 0, 0);");
   /*
   if(connectflag == 1){
       QMessageBox::information(NULL,"提示","已建立连接",QMessageBox::Ok);
       return;
   }*/
   connect(serialport,&QSerialPort::readyRead,this,&SerialGetData::Serial_read);
   //connectflag = 1;//已为信号绑定槽函数
}
//关闭连接
void SerialGetData::on_eDisconnectBtn_clicked()
{
    if(serialflag == 0){
        QMessageBox::information(NULL,"提示","串口未打开",QMessageBox::Ok);
        return;
    }
    ui->eDisconnectBtn->setEnabled(false);
    ui->eConnectBtn->setEnabled(true);
    ui->eConnectBtn->setStyleSheet("background-color: rgb(0, 255, 255);\
                                   color: rgb(0, 0, 255);\
                                   border-color: rgb(255, 0, 0);");
    ui->eDisconnectBtn->setStyleSheet("background-color: rgb(255, 255, 255);\
                                   color: rgb(255, 0, 0);\
                                   border-color: rgb(255, 0, 0);");
    /*
    if(connectflag == 0){
        QMessageBox::information(NULL,"提示","未建立连接",QMessageBox::Ok);
        return;
    }*/
    disconnect(serialport,&QSerialPort::readyRead,this,&SerialGetData::Serial_read);
    //connectflag = 0;//已解除绑定
    ui->lcdTemp->display(0);
    ui->lcdHuim->display(0);
}
//定时器,定时获取时间展示
void SerialGetData::timerEvent(QTimerEvent *event)
{
    //获取当前时间
    QDate currentdate = dateTime.currentDateTime().date();
    QTime currenttime = dateTime.currentDateTime().time();
    if(event->timerId() == timerid){
        ui->dateTimeEdit->setDate(currentdate);
        ui->dateTimeEdit->setTime(currenttime);
    }
}

serialdisplay.h文件:

#ifndef SERIALDISPLAY_H
#define SERIALDISPLAY_H

#include <QMainWindow>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QDateTime>
#include "TOOL/myexcption.h"

QT_BEGIN_NAMESPACE
namespace Ui { class SerialDisplay; }
QT_END_NAMESPACE

class SerialDisplay : public QMainWindow
{
    Q_OBJECT

public:
    SerialGetData(QWidget *parent = nullptr);
    ~SerialGetData();

    QByteArray serial_recv_Data() throw(MyExcption);

    bool serial_sendData(QString data);

    int getNumsFromStr(QString data);//获取字符串中数字部分

private slots:
    void on_scanSerialBtn_clicked();

    void Serial_read();

    void on_openSerialBtn_clicked();

    void on_eConnectBtn_clicked();

    void on_eDisconnectBtn_clicked();

    void timerEvent(QTimerEvent *event)override;

private:
    Ui::SerialGetData *ui;

    QSerialPort *serialport;

    int timerid;

    int serialflag;

    QDateTime dateTime;
};
#endif // SERIALDISPLAY_H

上面两个文件用到了,自定义异常处理:
myexcption.cpp文件:

#include "myexcption.h"

MyExcption::MyExcption(const char *err)
{
    this->error = err;//
}

myexcption.h文件:

#ifndef MYEXCPTION_H
#define MYEXCPTION_H


class MyExcption
{
public:
    MyExcption(const char *err);
    const char* what() throw(){
        return this->error;
    }
private:
    const char *error;
};

#endif // MYEXCPTION_H

代码中完成了实时时间的显示,和温湿度显示,温湿度部分用到了json格式数据的处理,要求下位机传上来的数据必须是json格式的数据,接下来说说json格式数据:
json格式大概分为三种:
第一种,也是最简单的(JSON对象):

{
	"name":"smith",
	"age":30,
	"sex":}

第二种 (对象的属性也可以是JSON对象):

{
	"name":"smith",
	"age":18
	"sex":"school":{
		"sname":"南京大学",
		"address":"南京市鼓楼区汉口路22号"
	}
}

第三种(对象数组):

[
	{
		"title":"Java实战经典开发",
		"edition":3,
		"author":["smith","尼古拉斯","斯巴达"]
	},
	{
		"title":"Oracle实战经典开发",
		"edition":3,
		"author":["smith","尼古拉斯","斯巴达"]
	},
	{
		"title":"Vue实战经典开发",
		"edition":5,
		"author":["smith","尼古拉斯","斯巴达"]
	}
]

其实JSON数据就是一段字符串而已,只不过有不同意义的分隔符将其分割开来而已,我们看上面的符号,里面有[] ,{}等符号,其中

1 []中括号代表的是一个数组;
2 {}大括号代表的是一个对象
3 双引号“”表示的是属性值
4 冒号:代表的是前后之间的关系,冒号前面是属性的名称,后面是属性的值,这个值可以是基本数据类型,也可以是引用数据类型。
而我们只是简单的上传温湿度,所以只需要用到第一种:
可以规定数据格式为:

{
	"温度":"18℃",
	"湿度":"70%"
}

而下位机只需要按照这种格式发数据到串口即可。

实现效果图:

在这里插入图片描述
对之前的控件进行了颜色修改和美化。

问题

2024-4-8修改:
针对大家评论区或者私信提出的一直转换失败的原因:由于串口是按流接收,会在长数据发送时出现一帧数据不完整的情况,比如发送:1dabnxaiuhnsxius这个字符串,会在第一帧数据发送:1dabnxaiu第二帧发送:hnsxius,而我那里没有做完整性判断,所以会出问题,只需要将串口读取部分做一个约定,以回车换行结尾即可解决该问题,在接收到回车换行之前的数据保存,每一帧数据拼接起来,直到获取到回车换行为止,修改部分函数如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ddd58d335ec4496da73286a5f860772a.png

完整工程链接:
请根据资源链接下载

  • 45
    点赞
  • 296
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
### 回答1: 树莓派是一种单板计算机系统,可以通过各种传感器采集到不同类型的数据。要在Qt上位机实时显示树莓派采集到的数据,可以按照以下步骤进行: 1. 在树莓派上编写数据采集程序:使用合适的编程语言(如Python)编写程序,在程序中连接传感器并采集数据。可以使用相关库(如RPi.GPIO或sense-hat库)来方便地进行IO控制和数据采集。 2. 在树莓派上设置数据传输通信:将采集到的数据通过适合的通信方式传输给Qt上位机。常见的通信方式有串口通信、网络通信等。可以使用相应的通信库(如pySerial)来实现数据的传输。 3. 在Qt上位机中编写数据接收程序:使用C++编写Qt程序,在程序中接收树莓派传输的数据。可以使用Qt串口通信库或网络通信库来接收数据。 4. 在Qt上位机实时显示数据:使用Qt的界面设计工具,在界面中添加显示数据的控件(如文本框、图表等)。在接收到新的数据时,可以通过信号与槽机制将数据更新到界面上的相应控件中。 5. 运行程序并连接树莓派与Qt上位机:确保树莓派和Qt上位机通过相应通信方式正确连接。启动树莓派上的数据采集程序和Qt上位机中的数据接收程序,即可实时显示从树莓派采集到的数据。 总结起来,要在Qt上位机实时显示树莓派采集到的数据,需要在树莓派和Qt上位机分别编写相应的程序,并通过合适的通信方式进行数据传输和接收。 ### 回答2: 树莓派是一种功能强大的微型计算机,可以用于数据采集和处理。如果想要将树莓派采集到的数据实时显示Qt上位机上,可以按照以下步骤进行操作: 1. 首先,确保树莓派和Qt上位机能够进行通信。可以使用网络连接、串口通信或其他方式进行通信。 2. 在树莓派上编写数据采集的代码。根据具体需求,可以使用传感器、模块或其他硬件设备采集数据,并将数据存储在树莓派上。 3. 编写与Qt上位机通信的代码。在Qt上位机上,通过网络连接或串口通信,与树莓派建立连接并发送指令进行数据通信。 4. 在Qt上位机中创建图形界面,用于显示树莓派采集到的数据。可以使用Qt提供的控件,如图表控件或文本框控件,将数据实时显示出来。 5. 在Qt上位机中设置定时器,定时从树莓派获取数据,并更新显示数据的界面。 6. 编写数据处理的代码。根据具体需求,对从树莓派采集到的数据进行处理、计算或分析,并在Qt上位机的界面上显示处理结果。 7. 调试和测试。确保树莓派和Qt上位机之间的通信正常,数据能够准确地被获取和实时显示。 总而言之,树莓派采集到的数据在Qt上位机实时显示需要建立通信连接、编写数据采集和处理的代码,并在Qt界面中实时更新数据显示。 ### 回答3: 在树莓派采集数据并在Qt上位机实时显示,可以通过以下步骤实现: 1. 在树莓派上编写一个数据采集程序,使用传感器或其他设备获取数据。该程序可以使用Python或其他编程语言编写,并以一定的频率读取传感器数据。 2. 将采集到的数据通过网络传输到Qt上位机。可以使用网络通信协议,如TCP/IP或UDP,编写树莓派上的数据发送程序。 3. 在Qt上位机中编写一个数据接收程序,通过网络接收来自树莓派的数据。可以使用Qt提供的网络编程类库,如QTcpServer和QTcpSocket,从树莓派接收数据。 4. 在Qt上位机的界面中创建一个用于实时显示数据的窗口或控件。可以使用Qt的图形化界面设计工具,如Qt Designer,设计好界面布局。 5. 在Qt上位机的数据接收程序中,解析接收到的数据,并将其显示在界面上的窗口或控件中。可以使用Qt提供的图表类库,如QChart或QCustomPlot,将数据绘制成实时曲线图或其他形式的图表。 6. 在数据接收程序中,使用定时器或其他机制,定期更新界面上的数据显示,以实现实时更新。 7. 编译和部署Qt上位机程序,并将其运行在Qt支持的操作系统上,如Windows、Linux或Mac OS。 通过以上步骤,树莓派采集到的数据可以实时传输到Qt上位机,并通过图形界面实时显示。这样,用户可以方便地监测和分析采集到的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值