First commit
This commit is contained in:
commit
622168b3a8
|
@ -0,0 +1,73 @@
|
|||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.autosave
|
||||
*.a
|
||||
*.core
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.app
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
*.res
|
||||
*.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
|
||||
# xemacs temporary files
|
||||
*.flc
|
||||
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
*.pyc
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
QT += core gui widgets serialport
|
||||
|
||||
# 添加包含路径配置
|
||||
INCLUDEPATH += $$[QT_INSTALL_HEADERS]
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
widget.cpp
|
||||
|
||||
HEADERS += \
|
||||
widget.h
|
||||
|
||||
TRANSLATIONS += \
|
||||
QT_Micro_Weather_Station_zh_CN.ts
|
||||
|
||||
# Default rules for deployment.
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="QT_Micro_Weather_Station_zh_CN"></TS>
|
|
@ -0,0 +1,11 @@
|
|||
#include "widget.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
Widget w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
|
@ -0,0 +1,458 @@
|
|||
#include "widget.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout> // 添加网格布局头文件
|
||||
#include <QLabel>
|
||||
#include <QSerialPortInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QByteArray>
|
||||
#include <QTime>
|
||||
#include <QDateTime> // 添加日期时间头文件
|
||||
#include <QTimer>
|
||||
#include <QSerialPort>
|
||||
#include <QPushButton>
|
||||
#include <QRegExpValidator>
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
|
||||
// 根据 Qt 版本选择编码处理方式
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <QTextCodec>
|
||||
#else
|
||||
#include <QStringConverter>
|
||||
#endif
|
||||
|
||||
Widget::Widget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
// 初始化串口对象
|
||||
serialPort = new QSerialPort(this);
|
||||
|
||||
// 创建UI控件
|
||||
openButton = new QPushButton(tr("打开串口"), this);
|
||||
closeButton = new QPushButton(tr("关闭串口"), this);
|
||||
sendButton = new QPushButton(tr("发送"), this);
|
||||
autoSendButton = new QPushButton(tr("自动查询数据"), this);
|
||||
autoIntervalEdit = new QLineEdit("5", this); // 初始化时间间隔输入框
|
||||
autoIntervalEdit->setFixedWidth(50); // 设置固定宽度
|
||||
autoIntervalEdit->setValidator(new QIntValidator(1, 3600, this)); // 1秒-1小时
|
||||
autoSendTimer = new QTimer(this);
|
||||
isAutoSending = false;
|
||||
|
||||
// 初始化状态标签和定时器(简洁样式)
|
||||
statusLabel = new QLabel(this);
|
||||
statusLabel->setAlignment(Qt::AlignCenter);
|
||||
statusLabel->setMinimumHeight(24);
|
||||
statusLabel->setStyleSheet("font-weight: bold; font-size: 12pt;");
|
||||
statusLabel->setMargin(5);
|
||||
statusTimer = new QTimer(this);
|
||||
statusTimer->setSingleShot(true);
|
||||
connect(statusTimer, &QTimer::timeout, this, &Widget::clearStatus);
|
||||
|
||||
// 连接定时器信号
|
||||
connect(autoSendTimer, &QTimer::timeout, this, [this]() {
|
||||
if (serialPort->isOpen()) {
|
||||
// 发送固定数据: 30 03 00 00 00 09 81ED
|
||||
QByteArray autoData = QByteArray::fromHex("30030000000981ED");
|
||||
serialPort->write(autoData);
|
||||
}
|
||||
});
|
||||
|
||||
portComboBox = new QComboBox(this);
|
||||
baudRateComboBox = new QComboBox(this);
|
||||
sendLineEdit = new QLineEdit(this);
|
||||
// 设置输入验证器:只允许16进制字符和空格
|
||||
QRegExp hexRegExp("^[0-9A-Fa-f\\s]*$");
|
||||
QValidator *validator = new QRegExpValidator(hexRegExp, this);
|
||||
sendLineEdit->setValidator(validator);
|
||||
|
||||
receiveTextEdit = new QTextEdit(this);
|
||||
receiveTextEdit->setReadOnly(true);
|
||||
|
||||
// 设置下拉框选项
|
||||
QStringList baudRates = {"9600", "19200", "38400", "57600", "115200"};
|
||||
baudRateComboBox->addItems(baudRates);
|
||||
baudRateComboBox->setCurrentText("9600"); // 默认波特率改为9600
|
||||
|
||||
// 获取可用串口并显示描述信息
|
||||
QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
|
||||
for (const QSerialPortInfo &port : ports) {
|
||||
QString portInfo = port.portName() + " - " + port.description();
|
||||
portComboBox->addItem(portInfo, port.portName());
|
||||
}
|
||||
|
||||
// 初始按钮状态
|
||||
closeButton->setEnabled(false);
|
||||
autoSendButton->setEnabled(false); // 初始禁用自动发送按钮
|
||||
|
||||
// 连接信号槽
|
||||
connect(openButton, &QPushButton::clicked, this, &Widget::openPort);
|
||||
connect(closeButton, &QPushButton::clicked, this, &Widget::closePort);
|
||||
connect(sendButton, &QPushButton::clicked, this, &Widget::sendData);
|
||||
connect(autoSendButton, &QPushButton::clicked, this, &Widget::autoSendData);
|
||||
connect(serialPort, &QSerialPort::readyRead, this, &Widget::handleReadyRead);
|
||||
|
||||
// 创建布局
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
QHBoxLayout *portLayout = new QHBoxLayout();
|
||||
portLayout->addWidget(new QLabel("串口:", this));
|
||||
portLayout->addWidget(portComboBox);
|
||||
portLayout->addWidget(new QLabel("波特率:", this));
|
||||
portLayout->addWidget(baudRateComboBox);
|
||||
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout();
|
||||
buttonLayout->addWidget(openButton);
|
||||
buttonLayout->addWidget(closeButton);
|
||||
|
||||
// 添加自动查询控制区域(右侧)- 优化布局
|
||||
QVBoxLayout *autoLayout = new QVBoxLayout();
|
||||
|
||||
// 时间间隔控制行
|
||||
QHBoxLayout *intervalLayout = new QHBoxLayout();
|
||||
intervalLayout->addWidget(new QLabel("间隔(秒):", this));
|
||||
intervalLayout->addWidget(autoIntervalEdit);
|
||||
|
||||
autoLayout->addLayout(intervalLayout);
|
||||
autoLayout->addWidget(autoSendButton);
|
||||
|
||||
// 初始状态:禁用输入框
|
||||
autoIntervalEdit->setEnabled(false);
|
||||
|
||||
buttonLayout->addLayout(autoLayout);
|
||||
|
||||
mainLayout->addLayout(portLayout);
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
|
||||
// 添加接收框(调整到上方)
|
||||
mainLayout->addWidget(new QLabel("接收数据:", this));
|
||||
mainLayout->addWidget(receiveTextEdit);
|
||||
|
||||
// 添加气象数据显示区域(调整到下方)
|
||||
QGridLayout *dataLayout = new QGridLayout();
|
||||
minWindDirectionLabel = new QLabel("最小风向: -- °/s", this);
|
||||
avgWindDirectionLabel = new QLabel("平均风向: -- °/s", this);
|
||||
maxWindDirectionLabel = new QLabel("最大风向: -- °/s", this);
|
||||
minWindSpeedLabel = new QLabel("最小风速: -- m/s", this);
|
||||
avgWindSpeedLabel = new QLabel("平均风速: -- m/s", this);
|
||||
maxWindSpeedLabel = new QLabel("最大风速: -- m/s", this);
|
||||
temperatureLabel = new QLabel("大气温度: -- °", this);
|
||||
humidityLabel = new QLabel("大气湿度: -- %", this);
|
||||
pressureLabel = new QLabel("大气压强: -- hPa", this);
|
||||
|
||||
dataLayout->addWidget(minWindDirectionLabel, 0, 0);
|
||||
dataLayout->addWidget(avgWindDirectionLabel, 0, 1);
|
||||
dataLayout->addWidget(maxWindDirectionLabel, 0, 2);
|
||||
dataLayout->addWidget(minWindSpeedLabel, 1, 0);
|
||||
dataLayout->addWidget(avgWindSpeedLabel, 1, 1);
|
||||
dataLayout->addWidget(maxWindSpeedLabel, 1, 2);
|
||||
dataLayout->addWidget(temperatureLabel, 2, 0);
|
||||
dataLayout->addWidget(humidityLabel, 2, 1);
|
||||
dataLayout->addWidget(pressureLabel, 2, 2);
|
||||
|
||||
mainLayout->addLayout(dataLayout);
|
||||
|
||||
// 添加发送框和按钮
|
||||
QHBoxLayout *sendLayout = new QHBoxLayout();
|
||||
sendLayout->addWidget(new QLabel("发送数据(16进制):", this));
|
||||
sendLayout->addWidget(sendLineEdit);
|
||||
sendLayout->addWidget(sendButton);
|
||||
mainLayout->addLayout(sendLayout);
|
||||
|
||||
// 添加状态标签
|
||||
mainLayout->addWidget(statusLabel);
|
||||
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
Widget::~Widget()
|
||||
{
|
||||
if (serialPort->isOpen()) {
|
||||
serialPort->close();
|
||||
}
|
||||
delete serialPort;
|
||||
}
|
||||
|
||||
// CRC16-MODBUS计算函数
|
||||
quint16 Widget::calculateCRC(const QByteArray &data)
|
||||
{
|
||||
quint16 crc = 0xFFFF; // 初始值
|
||||
for (int i = 0; i < data.size(); ++i) {
|
||||
crc ^= static_cast<quint8>(data.at(i)); // 异或当前字节
|
||||
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
if (crc & 0x0001) {
|
||||
crc = (crc >> 1) ^ 0xA001; // 多项式 0x8005 反转后为 0xA001
|
||||
} else {
|
||||
crc = crc >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 交换高8位和低8位(转换为大端序)
|
||||
return (crc << 8) | (crc >> 8);
|
||||
}
|
||||
|
||||
void Widget::openPort()
|
||||
{
|
||||
// 获取实际端口名(去除描述部分)
|
||||
QString portName = portComboBox->currentData().toString();
|
||||
serialPort->setPortName(portName);
|
||||
|
||||
// 更新按钮状态
|
||||
openButton->setEnabled(false);
|
||||
closeButton->setEnabled(true);
|
||||
|
||||
if (serialPort->open(QIODevice::ReadWrite)) {
|
||||
statusLabel->setText("串口已打开");
|
||||
autoSendButton->setEnabled(true); // 启用自动发送按钮
|
||||
autoIntervalEdit->setEnabled(true); // 启用时间输入框
|
||||
} else {
|
||||
statusLabel->setText("无法打开串口");
|
||||
// 恢复按钮状态
|
||||
openButton->setEnabled(true);
|
||||
closeButton->setEnabled(false);
|
||||
autoSendButton->setEnabled(false); // 确保禁用自动发送按钮
|
||||
}
|
||||
statusTimer->start(800); // 0.8秒后清除状态消息(进一步缩短显示时间)
|
||||
}
|
||||
|
||||
void Widget::closePort()
|
||||
{
|
||||
if (serialPort->isOpen()) {
|
||||
serialPort->close();
|
||||
statusLabel->setText("串口已关闭");
|
||||
}
|
||||
|
||||
// 更新按钮状态
|
||||
openButton->setEnabled(true);
|
||||
closeButton->setEnabled(false);
|
||||
autoSendButton->setEnabled(false); // 禁用自动发送按钮
|
||||
|
||||
// 如果正在自动发送,停止定时器并重置状态
|
||||
if (isAutoSending) {
|
||||
autoSendTimer->stop();
|
||||
isAutoSending = false;
|
||||
autoSendButton->setText("自动查询数据");
|
||||
// 注意:这里不设置输入框状态,由调用方处理
|
||||
}
|
||||
autoIntervalEdit->setEnabled(false); // 关闭串口时禁用输入框
|
||||
statusTimer->start(800); // 0.8秒后清除状态消息(进一步缩短显示时间)
|
||||
}
|
||||
|
||||
void Widget::clearStatus()
|
||||
{
|
||||
statusLabel->clear();
|
||||
}
|
||||
|
||||
void Widget::sendData()
|
||||
{
|
||||
if (!serialPort->isOpen()) {
|
||||
QMessageBox::warning(this, "警告", "请先打开串口");
|
||||
return;
|
||||
}
|
||||
|
||||
QString text = sendLineEdit->text().trimmed().remove(' ');
|
||||
if (text.isEmpty()) return;
|
||||
|
||||
// 处理奇数长度输入:在最后一位前补零
|
||||
if (text.length() % 2 != 0) {
|
||||
text = text.left(text.length() - 1) + "0" + text.right(1);
|
||||
}
|
||||
|
||||
// 将16进制字符串转换为字节数组
|
||||
QByteArray data;
|
||||
for (int i = 0; i < text.length(); i += 2) {
|
||||
QString hex = text.mid(i, 2);
|
||||
bool ok;
|
||||
char byte = static_cast<char>(hex.toInt(&ok, 16));
|
||||
if (ok) {
|
||||
data.append(byte);
|
||||
} else {
|
||||
QMessageBox::warning(this, "错误", "无效的16进制数据: " + hex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 发送数据
|
||||
serialPort->write(data);
|
||||
serialPort->flush(); // 确保数据立即发送
|
||||
}
|
||||
|
||||
// 保存数据到CSV文件(使用UTF-8编码)
|
||||
void Widget::saveToCSV(const QVector<quint16>& values)
|
||||
{
|
||||
QFile file(csvFileName);
|
||||
if (!file.exists()) {
|
||||
// 创建文件并写入带BOM的UTF-8表头
|
||||
if (file.open(QIODevice::WriteOnly)) {
|
||||
// 直接写入UTF-8 BOM头
|
||||
file.write("\xEF\xBB\xBF");
|
||||
// 写入UTF-8编码的表头(转换为QString后调用toUtf8)
|
||||
file.write(QString("序号,时间,最小风向,平均风向,最大风向,最小风速,平均风速,最大风速,温度,湿度,压强\n").toUtf8());
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 追加数据(使用UTF-8编码)
|
||||
if (file.open(QIODevice::Append)) {
|
||||
// 构建数据行(使用用户要求的时间格式:YYYY/MM/DD-HH/MM/SS)
|
||||
QString line = QString("%1,%2,").arg(dataCount++).arg(QDateTime::currentDateTime().toString("yyyy/MM/dd-HH/mm/ss"));
|
||||
|
||||
// 添加9个数据值
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
line += QString::number(values[i]);
|
||||
if (i < values.size() - 1) line += ",";
|
||||
}
|
||||
line += "\n";
|
||||
|
||||
// 直接写入UTF-8编码的数据
|
||||
file.write(line.toUtf8());
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::handleReadyRead()
|
||||
{
|
||||
if (!serialPort->isOpen()) return;
|
||||
|
||||
// 读取数据并追加到缓冲区
|
||||
receiveBuffer += serialPort->readAll();
|
||||
|
||||
// 循环处理缓冲区中的数据帧
|
||||
while (receiveBuffer.size() >= 5) { // 最小帧长度:帧头(2) + 长度(1) + 数据(0) + CRC(2) = 5字节
|
||||
// 查找帧头 (0x30, 0x03)
|
||||
int startIndex = receiveBuffer.indexOf("\x30\x03");
|
||||
if (startIndex == -1) {
|
||||
// 没有找到有效帧头,清空缓冲区
|
||||
receiveBuffer.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
// 移除帧头前的无效数据
|
||||
if (startIndex > 0) {
|
||||
receiveBuffer = receiveBuffer.mid(startIndex);
|
||||
}
|
||||
|
||||
// 检查是否包含完整帧
|
||||
if (receiveBuffer.size() < 3) break; // 帧头(2) + 长度(1)
|
||||
|
||||
quint8 length = static_cast<quint8>(receiveBuffer.at(2)); // 数据长度
|
||||
int frameSize = 3 + length + 2; // 完整帧大小
|
||||
|
||||
if (receiveBuffer.size() < frameSize) {
|
||||
// 数据不完整,等待更多数据
|
||||
break;
|
||||
}
|
||||
|
||||
// 提取完整帧
|
||||
QByteArray frame = receiveBuffer.left(frameSize);
|
||||
receiveBuffer = receiveBuffer.mid(frameSize); // 移除已处理帧
|
||||
|
||||
// 获取当前时间
|
||||
QString timestamp = QTime::currentTime().toString("[HH:mm:ss] ");
|
||||
|
||||
// 转换为16进制字符串
|
||||
QString hexData;
|
||||
for (char byte : frame) {
|
||||
hexData += QString("%1 ").arg(static_cast<unsigned char>(byte), 2, 16, QLatin1Char('0')).toUpper();
|
||||
}
|
||||
|
||||
// 解析数据帧并校验
|
||||
QString statusText = timestamp + hexData.trimmed();
|
||||
QString statusColor = "black";
|
||||
|
||||
// 提取数据部分(包括帧头和长度)
|
||||
QByteArray frameData = frame.left(3 + length);
|
||||
|
||||
// 计算CRC
|
||||
quint16 calculatedCRC = calculateCRC(frameData);
|
||||
|
||||
// 提取接收到的CRC(设备发送顺序:高字节在前,低字节在后)
|
||||
quint8 firstByte = static_cast<quint8>(frame.at(3 + length)); // 先接收的字节(高字节)
|
||||
quint8 secondByte = static_cast<quint8>(frame.at(3 + length + 1)); // 后接收的字节(低字节)
|
||||
quint16 receivedCRC = (firstByte << 8) | secondByte; // 组合为16位CRC值
|
||||
|
||||
// 比较CRC
|
||||
if (calculatedCRC == receivedCRC) {
|
||||
statusColor = "green";
|
||||
statusText += " <span style='color:green;'>✓ 校验通过</span>";
|
||||
|
||||
// 解析寄存器数据并更新显示标签
|
||||
QVector<quint16> dataValues;
|
||||
for (int i = 3; i < 3 + length; i += 2) {
|
||||
if (i + 1 < 3 + length) {
|
||||
int regIndex = (i - 3) / 2;
|
||||
quint16 value = static_cast<quint8>(frame.at(i)) << 8 |
|
||||
static_cast<quint8>(frame.at(i + 1));
|
||||
|
||||
dataValues.append(value); // 保存数据值
|
||||
|
||||
// 更新对应的标签
|
||||
switch (regIndex) {
|
||||
case 0: minWindDirectionLabel->setText(QString("最小风向: %1 °/s").arg(value)); break;
|
||||
case 1: avgWindDirectionLabel->setText(QString("平均风向: %1 °/s").arg(value)); break;
|
||||
case 2: maxWindDirectionLabel->setText(QString("最大风向: %1 °/s").arg(value)); break;
|
||||
case 3: minWindSpeedLabel->setText(QString("最小风速: %1 m/s").arg(value)); break;
|
||||
case 4: avgWindSpeedLabel->setText(QString("平均风速: %1 m/s").arg(value)); break;
|
||||
case 5: maxWindSpeedLabel->setText(QString("最大风速: %1 m/s").arg(value)); break;
|
||||
case 6: temperatureLabel->setText(QString("大气温度: %1 °").arg(value)); break;
|
||||
case 7: humidityLabel->setText(QString("大气湿度: %1 %").arg(value)); break;
|
||||
case 8: pressureLabel->setText(QString("大气压强: %1 hPa").arg(value)); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存数据到CSV
|
||||
saveToCSV(dataValues);
|
||||
} else {
|
||||
statusColor = "red";
|
||||
statusText += QString(" <span style='color:red;'>✗ 校验不通过 (计算值: %1, 接收值: %2)</span>")
|
||||
.arg(calculatedCRC, 4, 16, QChar('0').toUpper())
|
||||
.arg(receivedCRC, 4, 16, QChar('0').toUpper());
|
||||
}
|
||||
|
||||
// 添加带样式的文本
|
||||
receiveTextEdit->append("<span style='color:" + statusColor + ";'>" + statusText + "</span>");
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::autoSendData()
|
||||
{
|
||||
if (!serialPort->isOpen()) {
|
||||
QMessageBox::warning(this, "警告", "请先打开串口");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAutoSending) {
|
||||
// 停止自动发送
|
||||
autoSendTimer->stop();
|
||||
autoSendButton->setText("自动查询数据");
|
||||
isAutoSending = false;
|
||||
autoIntervalEdit->setEnabled(true); // 停止后启用输入框
|
||||
} else {
|
||||
// 立即发送一次查询指令
|
||||
if (serialPort->isOpen()) {
|
||||
QByteArray autoData = QByteArray::fromHex("30030000000981ED");
|
||||
serialPort->write(autoData);
|
||||
}
|
||||
|
||||
// 获取时间间隔(秒),默认为5秒
|
||||
int interval = 5;
|
||||
bool ok;
|
||||
int inputInterval = autoIntervalEdit->text().toInt(&ok);
|
||||
if (ok && inputInterval > 0) {
|
||||
interval = inputInterval * 1000; // 转换为毫秒
|
||||
} else {
|
||||
autoIntervalEdit->setText("5"); // 重置为默认值
|
||||
interval = 5000;
|
||||
}
|
||||
|
||||
// 开始自动发送
|
||||
autoSendTimer->start(interval);
|
||||
autoSendButton->setText("停止自动查询");
|
||||
isAutoSending = true;
|
||||
autoIntervalEdit->setEnabled(false); // 开始后禁用输入框
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef WIDGET_H
|
||||
#define WIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSerialPort>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QComboBox>
|
||||
#include <QTextEdit>
|
||||
#include <QLabel>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui { class Widget; }
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class Widget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Widget(QWidget *parent = nullptr);
|
||||
~Widget();
|
||||
|
||||
private slots:
|
||||
void openPort();
|
||||
void closePort();
|
||||
void sendData();
|
||||
void autoSendData();
|
||||
void handleReadyRead();
|
||||
void clearStatus();
|
||||
|
||||
private:
|
||||
// UI 控件
|
||||
QPushButton *openButton;
|
||||
QPushButton *closeButton;
|
||||
QPushButton *sendButton;
|
||||
QPushButton *autoSendButton;
|
||||
QLineEdit *autoIntervalEdit;
|
||||
QComboBox *portComboBox;
|
||||
QComboBox *baudRateComboBox;
|
||||
QLineEdit *sendLineEdit;
|
||||
QTextEdit *receiveTextEdit;
|
||||
QLabel *statusLabel;
|
||||
QLabel *minWindDirectionLabel;
|
||||
QLabel *avgWindDirectionLabel;
|
||||
QLabel *maxWindDirectionLabel;
|
||||
QLabel *minWindSpeedLabel;
|
||||
QLabel *avgWindSpeedLabel;
|
||||
QLabel *maxWindSpeedLabel;
|
||||
QLabel *temperatureLabel;
|
||||
QLabel *humidityLabel;
|
||||
QLabel *pressureLabel;
|
||||
|
||||
// 串口和定时器
|
||||
QSerialPort *serialPort;
|
||||
QTimer *autoSendTimer;
|
||||
QTimer *statusTimer;
|
||||
|
||||
// 状态标志和缓冲区
|
||||
bool isAutoSending;
|
||||
QByteArray receiveBuffer;
|
||||
|
||||
// 数据记录
|
||||
QString csvFileName = "weather_data.csv";
|
||||
int dataCount = 1;
|
||||
|
||||
// 辅助函数
|
||||
quint16 calculateCRC(const QByteArray &data);
|
||||
void saveToCSV(const QVector<quint16>& values);
|
||||
};
|
||||
#endif // WIDGET_H
|
Loading…
Reference in New Issue