Qt5 gui 蓝牙 不能自动接收数据

Qt.5.15
Windows 10
蓝牙设备会以周期为120Hz, 向外发送数据

基于QT GUI 没收到这个信号 characteristicChanged, 换了几台电脑都一样,不能自动收取数据,不得不用一个Timer来读,但偶尔会错过数据

connect(service, &QLowEnergyService::stateChanged, this,&MainWindow::serviceStateChanged);
connect(service, &QLowEnergyService::characteristicChanged, this,&MainWindow::BLEServiceCharacteristicChanged);
connect(service, &QLowEnergyService::characteristicRead, this,&MainWindow::BLEServiceCharacteristicRead);

不得不用一个Timer来读,但偶尔会错过数据
service->readCharacteristic(m_Characteristic[2]);

但是基于 C# .net GUI 能够捕获这个 signal ,C#gui 和安卓都可以自动收到蓝牙设备发出的数据。
private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)

该回答引用ChatGPT
根据问题描述,可以看出在使用Qt5 GUI开发蓝牙应用时,无法自动接收数据,需要使用Timer来读取数据,但是偶尔会错过数据。而在使用C# .net GUI开发蓝牙应用时,可以自动接收数据。下面我将从以下几个方面进行分析:
1. QLowEnergyService::characteristicChanged信号
根据代码,可以看出已经连接了QLowEnergyService::characteristicChanged信号,但是无法自动接收数据。这可能是由于QLowEnergyService::characteristicChanged信号的触发机制不同导致的。在Qt中,QLowEnergyService::characteristicChanged信号只有在蓝牙设备的特征值发生变化时才会触发,而在C# .net中,GattCharacteristic的ValueChanged事件会在蓝牙设备发送数据时触发。因此,建议在Qt中使用QLowEnergyService::characteristicRead信号来读取数据。
2. Timer读取数据
根据代码,可以看出使用了Timer来读取数据,但是偶尔会错过数据。这可能是由于Timer的精度不够高导致的。建议使用Qt的QTimer类,它提供了更高的精度和更好的稳定性。另外,可以使用QTimer::start()函数来启动定时器,使用QTimer::stop()函数来停止定时器。
3. 蓝牙设备发送数据的频率
根据问题描述,蓝牙设备会以周期为120Hz向外发送数据。这可能会导致数据丢失。建议在Qt中使用QBluetoothSocket类来连接蓝牙设备,并使用QIODevice::readyRead()信号来读取数据。这样可以保证数据不会丢失。
下面是一个使用QBluetoothSocket类读取蓝牙设备数据的示例代码:
cpp
QBluetoothSocket *socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
socket->connectToService(QBluetoothAddress("00:11:22:33:44:55"), 1); // 连接蓝牙设备
connect(socket, SIGNAL(readyRead()), this, SLOT(readBluetoothData())); // 读取数据

cpp
void MainWindow::readBluetoothData()
{
QByteArray data = socket->readAll();
// 处理数据
}

希望以上分析对您有所帮助。

该回答引用ChatGPT
您好,我认为可能是因为QT GUI在接收数据时的线程不够稳定,或者在接收数据时的处理速度不够快。建议优化线程和处理速度,或者使用C#或安卓的GUI来接收数据。另外,可以尝试调整Timer的时间间隔,或者使用其他方法接收数据,比如使用Notification或Indication来接收数据。

基于最新版ChatGPT4的回答,望采纳!!!有其他问题也可以询问我哦💕(最新版更智能,功能更加强大):
您遇到的问题可能是因为 Qt 的 QLowEnergyCharacteristic 类不会在 Windows 平台上自动接收数据更新通知。相反,它需要手动读取数据,或使用一个计时器轮询。这可能会导致数据丢失或延迟。

为了解决这个问题,您可以考虑使用 Windows 平台特定的 BLE 库,例如 WinRT BLE API。通过使用该库,您可以访问更多平台特定功能,并且可以更直接地与 Windows 平台上的 BLE 设备进行通信。

如果您希望继续使用 Qt BLE 库,您可以考虑使用 QLowEnergyController::readCharacteristic() 函数来读取数据。但是,这将需要您手动控制读取频率和数据处理,以确保不会丢失或延迟数据。

另一个选择是使用 Qt 的 QTimer 类来定期检查是否有新数据可用。这样可以减少不必要的轮询,并使数据接收更加及时。但请注意,这仍然可能会导致数据丢失或延迟,因此请确保在代码中处理这些情况。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
问题可能是由于 Qt 的事件和线程机制所导致的。QLowEnergyService::characteristicChanged 信号是由 BLE 设备发射出来的,它在后台线程调用,因此您的 GUI 线程可能会错过该信号。在 Android 平台上,这个问题得到了解决,因为 Android 主线程处理事件的速度很快,因此 GATT 事件不会错过。

解决方案是使用 Qt 的事件循环机制来读取 BLE 数据。将 QLowEnergyController 和 QLowEnergyService 对象分别分配到一个线程中,以便它们可以异步执行。在主线程中创建一个 QT 事件队列,并调用 exec() 函数开始事件循环。在 QLowEnergyService::characteristicChanged 信号发射时,它将被排入事件队列,该事件将触发一个处理函数,该函数将读取新的 GATT 特性值。这可以确保事件不会错过,并且可以及时读取和处理新的数据。

下面是示例代码:

#include <QCoreApplication>
#include <QDebug>
#include <QLowEnergyController>
#include <QLowEnergyService>

class BLEController : public QObject {
    Q_OBJECT
public:
    BLEController(QObject *parent = nullptr) : QObject(parent) {
        m_controller = QLowEnergyController::createCentral(this);
        m_controller->setRemoteAddressType(QLowEnergyController::RandomAddress);
        QObject::connect(m_controller, &QLowEnergyController::connected, this, &BLEController::onConnected);
        QObject::connect(m_controller, &QLowEnergyController::disconnected, this, &BLEController::onDisconnected);
        QObject::connect(m_controller, &QLowEnergyController::discoveryFinished, this, &BLEController::onDiscoveryFinished);
        QObject::connect(m_controller, &QLowEnergyController::errorOccurred, this, &BLEController::onError);
    }
    void connectToDevice(const QBluetoothDeviceInfo& device) {
        m_controller->connectToDevice(device);
    }
signals:
    void connected();
    void disconnected();
    void discoveryFinished();
    void error(QString error);
private:
    QLowEnergyController *m_controller;
private slots:
    void onConnected() {
        qDebug() << "Connected to GATT server.";
        emit connected();
    }
    void onDisconnected() {
        qDebug() << "Disconnected from GATT server.";
        emit disconnected();
    }
    void onDiscoveryFinished() {
        qDebug() << "Service discovery finished.";
        emit discoveryFinished();
    }
    void onError(QLowEnergyController::Error error) {
        QString errorStr = QString("Error occurred: %1").arg(error);
        qDebug() << errorStr;
        emit error(errorStr);
    }
};

class BLEService : public QObject {
    Q_OBJECT
public:
    BLEService(QObject *parent = nullptr) : QObject(parent), m_service(nullptr) {}
    void setController(QLowEnergyController *controller) {
        m_service = controller->createServiceObject(QBluetoothUuid(<SERVICE_UUID>), this);
        QObject::connect(m_service, &QLowEnergyService::stateChanged, this, &BLEService::onStateChanged);
    }
signals:
    void characteristicChanged(const QByteArray &newValue);
    void characteristicRead(const QByteArray &value);
private:
    QLowEnergyService *m_service;
private slots:
    void onStateChanged(QLowEnergyService::ServiceState state) {
        if (state == QLowEnergyService::ServiceDiscovered) {
            qDebug() << "BLE service discovered.";
            QLowEnergyCharacteristic characteristic = m_service->characteristic(QBluetoothUuid(<CHARACTERISTIC_UUID>));
            if (characteristic.isValid()) {
                QObject::connect(m_service, &QLowEnergyService::characteristicChanged, this, &BLEService::onCharacteristicChanged);
                m_service->readCharacteristic(characteristic);
            } else {
                qDebug() << "Characteristic not found!";
            }
        }
    }
    void onCharacteristicChanged(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) {
        if (characteristic.uuid() == QBluetoothUuid(<CHARACTERISTIC_UUID>)) {
            emit characteristicChanged(newValue);
            m_service->readCharacteristic(characteristic);
        }
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    BLEController controller;
    BLEService service;
    QObject::connect(&controller, &BLEController::connected, [&service]() {
        qDebug() << "Connected to device.";
        service.setController(controller.m_controller);
    });
    QObject::connect(&service, &BLEService::characteristicChanged, [](const QByteArray &newValue) {
        qDebug() << "New value: " << newValue;
    });
    QBluetoothDeviceInfo device(<DEVICE_ADDRESS>);
    controller.connectToDevice(device);
    return app.exec();
}

请注意,在这个示例代码中,使用了 QLowEnergyController 对象来处理连接和发现的过程,使用 QLowEnergyService 对象来处理 GATT 特性的读取和写入。QBluetoothUuid 类用于表示 BLE 设备中使用的 UUID。您需要将其替换为您的特定 UUID。假设您的 BLE 设备已经处于配对状态,需要将 替换为该设备的 MAC 地址。

这个示例代码将执行一个事件循环,并在 QLowEnergyService::characteristicChanged 信号触发时读取新的 GATT 特性值。这保证了事件不会因为Qt主线程的执行速度而超时。
如果我的回答解决了您的问题,请采纳!

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 帮你找了个相似的问题, 你可以看下: https://ask.csdn.net/questions/230927
  • 你也可以参考下这篇文章:Qt工作笔记-三种方式解决Qt5中文编码问题
  • 除此之外, 这篇博客: Qt创建动态库给C#调用,通过回调完成交互中的 3.2 编写代码调用Qt接口 部分也许能够解决你的问题, 你可以仔细阅读以下内容或者直接跳转源博客中阅读:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    
    namespace ConsoleApp1
    {
        class Program
        {
            [DllImport("QtClassLibrary1.dll", EntryPoint = "ImageZoom", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
            public extern static int ImageZoom(int w,int h,IntPtr Path, CallbackDelegate callback);
    
    
            //定义委托
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            public delegate void CallbackDelegate(IntPtr Path);
    
            //接收C++回调数据
            static void CallBackFunction(IntPtr Path)
            {
                Console.WriteLine("C++传出来的回调:" + Marshal.PtrToStringAnsi(Path));
            }
    
    
            static void Main(string[] args)
            {
                string text = "C:\\Users\\11266\\Pictures\\20220425103841.png";
                int r_code = ImageZoom(100,100,Marshal.StringToHGlobalAnsi(text), CallBackFunction);
                Console.WriteLine("执行状态:" + r_code);
    
                Console.ReadKey();
            }
        }
    }
    

    写完代码,直接运行,会报错找不到模块。很正常,因为代码里填写的库是当前程序运行路径,现在路径下并没有库文件,接下来需要拷贝库到运行目录下即可。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^