SerialPortItem
cpp
#pragma once
#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <functional>
enum SerialPortState {
CloseState,
Opened,
OpenFail,
PortNameError
};
Q_DECLARE_METATYPE(SerialPortState)
class SerialPortItem : public QObject
{
Q_OBJECT
public:
using DataCallback = std::function<void(const QByteArray&)>;
explicit SerialPortItem(DataCallback callback = nullptr, QObject* parent = nullptr);
~SerialPortItem() override;
// 参数设置
void setPortName(const QString& portName);
QString portName() const;
void setBaudRate(int baudRate);
int baudRate() const;
void setDataBits(QSerialPort::DataBits bits);
QSerialPort::DataBits dataBits() const;
void setStopBits(QSerialPort::StopBits bits);
QSerialPort::StopBits stopBits() const;
void setParity(QSerialPort::Parity parity);
QSerialPort::Parity parity() const;
// 状态查询
bool isOpen() const;
SerialPortState state() const;
// 打开 / 关闭
void start();
void stop();
// 发送
void send(const QByteArray& data);
void send(const char* data, int len);
// 底层对象
QSerialPort* serialPort() const;
// 列出当前系统可用串口
static QStringList availablePortNames();
signals:
void RecData(const QByteArray& data, SerialPortItem* self);
void StateChanged(SerialPortState state);
private:
void onReadyRead();
void onError(QSerialPort::SerialPortError err);
void updateState(SerialPortState s);
QSerialPort* m_SerialPort = nullptr;
DataCallback m_datacallback;
QString m_portName;
int m_baudRate = 115200;
QSerialPort::DataBits m_dataBits = QSerialPort::Data8;
QSerialPort::StopBits m_stopBits = QSerialPort::OneStop;
QSerialPort::Parity m_parity = QSerialPort::NoParity;
SerialPortState m_state = CloseState;
};
SerialPortItem.cpp
cpp
#include "SerialPortItem.h"
#include <QDebug>
SerialPortItem::SerialPortItem(DataCallback callback, QObject* parent)
: QObject(parent)
, m_SerialPort(new QSerialPort(this))
, m_datacallback(std::move(callback))
{
// 默认选第一个可用串口
const auto ports = QSerialPortInfo::availablePorts();
if (!ports.isEmpty()) {
m_portName = ports.first().portName();
}
connect(m_SerialPort, &QSerialPort::readyRead,
this, &SerialPortItem::onReadyRead);
connect(m_SerialPort, &QSerialPort::errorOccurred,
this, &SerialPortItem::onError);
qRegisterMetaType<SerialPortState>("SerialPortState");
}
SerialPortItem::~SerialPortItem()
{
stop();
}
// ---------------- getters / setters ----------------
void SerialPortItem::setPortName(const QString& portName) { m_portName = portName; }
QString SerialPortItem::portName() const { return m_portName; }
void SerialPortItem::setBaudRate(int baudRate) { m_baudRate = baudRate; }
int SerialPortItem::baudRate() const { return m_baudRate; }
void SerialPortItem::setDataBits(QSerialPort::DataBits bits) { m_dataBits = bits; }
QSerialPort::DataBits SerialPortItem::dataBits() const { return m_dataBits; }
void SerialPortItem::setStopBits(QSerialPort::StopBits bits) { m_stopBits = bits; }
QSerialPort::StopBits SerialPortItem::stopBits() const { return m_stopBits; }
void SerialPortItem::setParity(QSerialPort::Parity parity) { m_parity = parity; }
QSerialPort::Parity SerialPortItem::parity() const { return m_parity; }
bool SerialPortItem::isOpen() const
{
return m_SerialPort && m_SerialPort->isOpen();
}
SerialPortState SerialPortItem::state() const
{
return m_state;
}
QSerialPort* SerialPortItem::serialPort() const
{
return m_SerialPort;
}
QStringList SerialPortItem::availablePortNames()
{
QStringList names;
const auto ports = QSerialPortInfo::availablePorts();
for (const auto& info : ports) {
names << info.portName();
}
return names;
}
// ---------------- 打开 / 关闭 ----------------
void SerialPortItem::start()
{
if (isOpen()) {
return;
}
// 1. 校验串口号
bool found = false;
const auto ports = QSerialPortInfo::availablePorts();
for (const auto& info : ports) {
if (info.portName() == m_portName) {
found = true;
break;
}
}
if (!found) {
qWarning() << "[Serial] 串口号不存在:" << m_portName;
updateState(PortNameError);
return;
}
// 2. 统一设置参数
m_SerialPort->setPortName(m_portName);
m_SerialPort->setBaudRate(m_baudRate);
m_SerialPort->setDataBits(m_dataBits);
m_SerialPort->setParity(m_parity);
m_SerialPort->setStopBits(m_stopBits);
m_SerialPort->setFlowControl(QSerialPort::NoFlowControl);
// 3. 打开
if (!m_SerialPort->open(QIODevice::ReadWrite)) {
qWarning() << "[Serial] 打开失败:" << m_SerialPort->errorString();
updateState(OpenFail);
} else {
qInfo() << "[Serial] 打开成功:" << m_portName << "@" << m_baudRate;
updateState(Opened);
}
}
void SerialPortItem::stop()
{
if (m_SerialPort->isOpen()) {
m_SerialPort->close();
qInfo() << "[Serial] 已关闭:" << m_portName;
}
updateState(CloseState);
}
// ---------------- 收数据 ----------------
void SerialPortItem::onReadyRead()
{
const QByteArray buffer = m_SerialPort->readAll();
if (buffer.isEmpty()) {
return;
}
emit RecData(buffer, this);
if (m_datacallback) {
m_datacallback(buffer);
}
}
// ---------------- 发送 ----------------
void SerialPortItem::send(const QByteArray& data)
{
if (!isOpen() || data.isEmpty()) {
return;
}
m_SerialPort->write(data);
}
void SerialPortItem::send(const char* data, int len)
{
if (!isOpen() || data == nullptr || len <= 0) {
return;
}
m_SerialPort->write(data, len);
}
// ---------------- 错误 / 状态 ----------------
void SerialPortItem::onError(QSerialPort::SerialPortError err)
{
if (err == QSerialPort::NoError) {
return;
}
qWarning() << "[Serial] 错误:" << err << m_SerialPort->errorString();
// 断线 / 设备被拔掉,自动关闭并通知
if (err == QSerialPort::ResourceError ||
err == QSerialPort::DeviceNotFoundError ||
err == QSerialPort::PermissionError) {
if (m_SerialPort->isOpen()) {
m_SerialPort->close();
}
updateState(CloseState);
}
}
void SerialPortItem::updateState(SerialPortState s)
{
if (m_state == s) {
return;
}
m_state = s;
emit StateChanged(m_state);
}
main.cpp(示范用法)
cpp
#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
#include "SerialPortItem.h"
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
// 1. 打印当前可用串口
const QStringList ports = SerialPortItem::availablePortNames();
qInfo() << "可用串口:" << ports;
if (ports.isEmpty()) {
qWarning() << "没有找到串口设备,程序退出";
return 0;
}
// 2. 创建串口对象,传入收数据回调
auto* sp = new SerialPortItem([](const QByteArray& data) {
qInfo() << "[回调] 收到数据:" << data;
}, &app);
// 3. 配置参数(默认就是 115200/8/1/无校验,这里只改串口名)
sp->setPortName(ports.first());
sp->setBaudRate(115200);
// 4. 监听状态变化
QObject::connect(sp, &SerialPortItem::StateChanged,
[](SerialPortState s) {
switch (s) {
case CloseState: qInfo() << "状态: 已关闭"; break;
case Opened: qInfo() << "状态: 已打开"; break;
case OpenFail: qInfo() << "状态: 打开失败"; break;
case PortNameError: qInfo() << "状态: 串口号错误"; break;
}
});
// 5. 监听数据信号(和回调二选一即可,这里都示范一下)
QObject::connect(sp, &SerialPortItem::RecData,
[](const QByteArray& data, SerialPortItem*) {
qInfo() << "[信号] 收到数据:" << data;
});
// 6. 打开串口
sp->start();
// 7. 定时发送测试数据
int counter = 0;
QTimer sendTimer;
QObject::connect(&sendTimer, &QTimer::timeout, [&]() {
if (sp->isOpen()) {
const QByteArray msg = QString("Hello %1\n").arg(counter++).toUtf8();
sp->send(msg);
qInfo() << "[发送]" << msg.trimmed();
}
});
sendTimer.start(1000);
// 8. 10 秒后关闭串口并退出
QTimer::singleShot(10000, [&]() {
qInfo() << "10 秒到,关闭串口并退出";
sp->stop();
sendTimer.stop();
app.quit();
});
return app.exec();
}