当前位置: 首页 > news >正文

3. QML实现蓝牙通信

1. 说明:

复杂的软件系统里面,可能不止包括一种通讯协议,可能是多种通讯的结合才能实现软件控制的整个流程。目前,使用蓝牙通讯在短距离传输信号是比较常见的一种方式,特别是在安卓端开发软件时,使用蓝牙通讯很常见的。本篇文章记录一下在QML中使用蓝牙通讯的小案例。

2. 实现步骤:

在QML中使用蓝牙通讯有两种方案,一种是使用quick自带的三个控件BluetoothDiscoveryModel,BluetoothService,BluetoothSocket来实现,也可以使用qwidget的三个对应的类QBluetoothDeviceDiscoveryAgent,QBluetoothLocalDevice,QBluetoothSocket来实现。
目前,第一种方式貌似qt支持的还不是很完善,某种情况下会出现bug,导致无法连接到蓝牙设备,第二种方式实现起来还是可以的。
所以,本篇文章使用的是QML搭建界面,蓝牙通讯的功能在Qt中利用上面的三个类进行实现。
效果展示:
在这里插入图片描述

2.1 类功能说明:

前提:在***.Pro文件中加入蓝牙模块QT += bluetooth***,在自定义的蓝牙协议类文件中加入以下头文件:

#include <QBluetoothSocket>		//用于数据通信
#include <QBluetoothLocalDevice>	//用于控制本机蓝牙状态(开启/关闭)
#include <QBluetoothDeviceDiscoveryAgent>	//用于搜寻附近的蓝牙设备
#include <QBluetoothAddress>	//用于提取蓝牙设备的地址

在连接蓝牙设备时,需要知道目标蓝牙设备的地址,这个可以通过QBluetoothDeviceDiscoveryAgent这个类搜索到的信息进行提取,还需要知道UUID标识号,一般都是使用串口类的uuid,具体使用什么类型可以自己去查找,参考网址:https://www.jianshu.com/p/eb85cb690e86

2.2 自定义蓝牙协议类:

因为要在QML当中调用蓝牙配置的相关函数,需要QML与C++进行交互,可以将蓝牙服务模块封装成一个单独类,然后注册的QML中进行使用,类中函数的相关解释请看代码注释,都挺详细的。核心代码如下:
BluetoothProxy.h:

#ifndef BLUETOOTHPROXY_H
#define BLUETOOTHPROXY_H

#include <QObject>

#include <QBluetoothSocket>
#include <QBluetoothLocalDevice>
#include <QBluetoothDeviceDiscoveryAgent>
#include <QBluetoothAddress>


class BluetoothProxy : public QObject
{
    Q_OBJECT
public:
    explicit BluetoothProxy(QObject *parent = nullptr);
    ~BluetoothProxy();

public slots:

    void addBlueToothDevicesToList(const QBluetoothDeviceInfo&);

    void readBluetoothDataEvent();

    void bluetoothConnectedEvent();

    void bluetoothDisconnectedEvent();

    void onError(QBluetoothSocket::SocketError error);

    //启动搜寻蓝牙
    Q_INVOKABLE void startSearch();
    //发送数据
    Q_INVOKABLE void sendData(QString mData);
    //断开蓝牙
    Q_INVOKABLE void disconnectBth();

signals:
    //每次搜寻到蓝牙设备,发送此信号
    void deviceFind(QString mDeviceInfo);
    //配对状态更新时,发送此信号
    void connectProcess(QString mProcessInfo);


private:
    //用于搜寻附近的蓝牙设备
    QBluetoothDeviceDiscoveryAgent *discoveryAgent;
    //用于控制本机蓝牙状态(开启/关闭)
    QBluetoothLocalDevice *localDevice;
    //用于数据通信
    QBluetoothSocket *socket;

};
#endif // BLUETOOTHPROXY_H

BluetoothProxy.cpp:

#include "bluetoothproxy.h"


//这个UUID要根据自己的使用情况来确定,我使用的是串口类的UUID,具体可https://www.jianshu.com/p/eb85cb690e86
static const QLatin1String serviceUuid("00001101-0000-1000-8000-00805F9B34FB");

BluetoothProxy::BluetoothProxy(QObject *parent) : QObject(parent)
{
    //在构造函数中初始化一些成员变量
    discoveryAgent = new QBluetoothDeviceDiscoveryAgent();  // 否则搜寻周围的蓝牙设备
    localDevice = new QBluetoothLocalDevice();      // 负责控制设备上的蓝牙(比如开启/关闭等操作)
    socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);   // 负责通信数据传输

    //搜寻到蓝牙设备后,将启动设备信息存储函数
    connect(discoveryAgent,SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),this,SLOT(addBlueToothDevicesToList(QBluetoothDeviceInfo)));

    //接收到有数据传输过来时,将启动读取信息的函数
    connect(socket,SIGNAL(readyRead()),this,SLOT(readBluetoothDataEvent()));

    //与目标设备成功连接够,启动提示函数
    connect(socket,SIGNAL(connected()),this,SLOT(bluetoothConnectedEvent()));

    //与目标设备断开连接时,启动顿开蓝牙提示函数
    connect(socket,SIGNAL(disconnected()),this,SLOT(bluetoothDisconnectedEvent()));

    //出西安错误时,启动错误响应函数
    connect(socket,SIGNAL(error(QBluetoothSocket::SocketError)),this,SLOT(onError(QBluetoothSocket::SocketError)));

}

BluetoothProxy::~BluetoothProxy()
{

}


void BluetoothProxy::onError(QBluetoothSocket::SocketError error)
{
    QString str;
    if(QBluetoothSocket::UnknownSocketError == error){
        str = "UnknownSocketError";
    }else if(QBluetoothSocket::NoSocketError == error){
        str = "NoSocketError";
    }else if(QBluetoothSocket::HostNotFoundError == error){
        str = "HostNotFoundError";
    }else if(QBluetoothSocket::ServiceNotFoundError == error){
        str = "ServiceNotFoundError";
    }else if(QBluetoothSocket::NetworkError == error){
        str = "NetworkError";
    }else if(QBluetoothSocket::UnsupportedProtocolError == error){
        str = "UnsupportedProtocolError";
    }else if(QBluetoothSocket::OperationError == error){
        str = "OperationError";
    }else if(QBluetoothSocket::RemoteHostClosedError == error){
        str = "RemoteHostClosedError";
    }
    qDebug()<<error;
}

//开始搜寻蓝牙设备
void BluetoothProxy::startSearch()
{
//    if(localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff){
//        localDevice->powerOn();//调用打开本地的蓝牙设备
//        discoveryAgent->start();
//    }
    discoveryAgent->start();
}

//发送数据到蓝牙设备
void BluetoothProxy::sendData(QString mData)
{
    socket->write(mData.toUtf8());
}

//断开蓝牙
void BluetoothProxy::disconnectBth()
{
    socket->close();
}

//显示蓝牙设备信息
void BluetoothProxy::addBlueToothDevicesToList( const QBluetoothDeviceInfo &info )
{
    QString deviceInfo = QString("%1 %2").arg(info.address().toString()).arg(info.name());

    qDebug()<<deviceInfo;
    emit deviceFind(deviceInfo);

    //如果发现目标蓝牙,则进行连接
    if(deviceInfo.contains("这里换成自己蓝牙设备的名称")){

        int index = deviceInfo.indexOf(' ');
        if(index == -1){
            qDebug()<<"index";
            return;
        }
        //获取目标蓝牙设备地址
        QBluetoothAddress address(deviceInfo.left(index));
        QString name(deviceInfo.mid(index + 1));
        //开始连接
        socket->connectToService(address, QBluetoothUuid(serviceUuid) ,QIODevice::ReadWrite);
        //向qml端发送信号
        emit connectProcess(QString("蓝牙连接中,请稍后....(%1)").arg(name));
    }
}


//接收蓝牙设备发送过来的数据
void BluetoothProxy::readBluetoothDataEvent()
{
    QByteArray receiceData = socket->readAll();
    if(receiceData.size() > 4)return;

}

void BluetoothProxy::bluetoothConnectedEvent()
{
    emit connectProcess("蓝牙连接成功....");
}

void BluetoothProxy::bluetoothDisconnectedEvent()
{
    emit connectProcess("蓝牙已断开....");
}

2.3 具体使用:

main.cpp文件中将上述自定义类注册到QML中:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQmlContext>

#include "bluetoothproxy.h"


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
	
	//自定义蓝牙类注册到QML中
    BluetoothProxy blueTooth;
    engine.rootContext()->setContextProperty("myBlueTooth",&blueTooth);

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

将上面的自定义蓝牙类注册到QML当中后,即可在.qml文件中进行调用,主界面代码如下:
main.qml:

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15


ApplicationWindow {
    id:root
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Connections{
        target: myBlueTooth
        function onDeviceFind(mDeviceInfo){
            blueToothList.append({info:mDeviceInfo})
        }
        function onConnectProcess(mProcessInfo){
            popText.text = mProcessInfo
            pop.open()
        }
    }

    Text{
        id:titleName
        anchors.horizontalCenter: btView.horizontalCenter
        anchors.top: parent.top
        anchors.topMargin: 15
        text: "此处显示蓝牙设备信息"
        font.pixelSize: 20
    }

    ListView{
        id:btView
        width: 300
        height: 400
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.top: titleName.bottom
        anchors.topMargin: 15
        spacing: 10
        focus:true
        enabled: true
        highlight: Rectangle {
            width: parent.width
            height: 20
            color: "lightsteelblue"
            radius: 5
            y: btView.currentItem.y
            Behavior on y {
                SpringAnimation {
                    spring: 3
                    damping: 0.2
                }
            }
        }
        model: ListModel{
            id:blueToothList
            //            ListElement{
            //                info:""
            //            }
        }
        delegate: Rectangle{
            color: Qt.rgba(0,0,0,0)
            width: parent.width
            height: 20
            anchors.horizontalCenter: parent.horizontalCenter
            Text{
                id:deviceInfo
                anchors.centerIn: parent
                text: info
                font.pixelSize: 15
                color: "black"
            }
            MouseArea{
                anchors.fill: parent
                onClicked: {
                    btView.currentIndex = index
                }
            }
        }

    }

    Button{
        id:searchBtn
        width: 100
        height: 50
        text: "搜寻蓝牙"
        onClicked: {
            myBlueTooth.startSearch()
        }
    }
    Button{
        id:clearBtn
        width: 100
        height: 50
        anchors.top:searchBtn.bottom
        anchors.topMargin: 10
        text:"清空信息"
        onClicked: {
            blueToothList.clear()
        }
    }
    Button{
        id:sendBtn
        width: 100
        height: 50
        anchors.top:clearBtn.bottom
        anchors.topMargin: 10
        text:"发送数据"
        onClicked: {
            myBlueTooth.sendData("1")
        }
    }
    Button{
        id:discBtn
        width: 100
        height: 50
        anchors.top:sendBtn.bottom
        anchors.topMargin: 10
        text:"断开蓝牙"
        onClicked: {
            myBlueTooth.disconnectBth()
        }
    }

    Popup{
        id:pop
        width: 300
        height: 200
        x:(root.width-pop.width)/2
        y:(root.height-pop.height)/2
        //visible: true// the default value is false
        background: Rectangle {
            anchors.fill: parent
            border.width: 0
            radius: 20
            color: Qt.rgba(0,0,0,0.4)
        }
        contentItem: Rectangle{
            anchors.fill: parent
            color: Qt.rgba(0,0,0,0.4)
            border.width: 0
            radius: 20
            Text{
                id:popText
                anchors.centerIn: parent
                font.pixelSize: 20
                color: "white"
            }
            Row{
                id:btnRow
                anchors.bottom: parent.bottom
                anchors.left: parent.left
                anchors.right: parent.right
                height: 50
                Button{
                    id:cancelBtn
                    width: 60
                    height: 25
                    anchors.right: parent.right
                    anchors.rightMargin: 15
                    anchors.bottom: parent.bottom
                    anchors.bottomMargin: 15
                    text: "Yes"
                    onClicked: {
                        pop.close()
                    }
                }
            }
        }
    }

}

界面设计比较简单,以实现功能为主,界面效果如下:
在这里插入图片描述

2.4 功能测试:

我是在安卓手机上下载了一个SPP蓝牙串口软件,这个可以在手机的应用商店里下载到,软件打开后选择页面右上角的***+号,再选择服务端模式***,如下图所示:
在这里插入图片描述
启动服务端后,即可运行Qt程序,然后点击界面的搜寻蓝牙按钮,即可自动搜寻附近的蓝牙设备,注意:将代码中蓝牙设备名称修改成自己设备的名称,否则连接不上蓝牙。
如果碰到检测不到蓝牙的情况,可点击手机蓝牙助手右上角的设置按钮,选择启用可检测性,注意将数据的接收格式改成utf-8,因为上面代码中蓝牙端发送的数据格式定义为了utf-8的格式。如下图所示:
在这里插入图片描述
然后就可以愉快的传输数据啦。。。。

持续更新中,请大家多多关注…

相关文章:

  • 算法第二十期——FLoyd算法的入门与应用
  • VBA之正则表达式(41)-- 替换函数声明
  • python get方法及常用的代码
  • Vue——插槽
  • uni-app的基本使用(二)
  • kubeSphere / k8s中master、worker节点启停命令操作
  • Vue内容分发
  • MySQL主从复制、读写分离(MayCat2)实现数据同步
  • C#,码海拾贝(04)——拉格朗日(Lagrange)曲线插值算法及其拓展,《C#数值计算算法编程》源代码升级改进版
  • 解决使用WinScp连接Ubantu系统失败的问题---SSH无法连接
  • ChatGPT会干掉测试吗?
  • 使用Go语言编写命令行实用程序
  • rk3568-AD按键驱动调试
  • linux操作系统基础(含C编译,make编译,shell脚本)
  • 首次,第五轮学科评估结果不公开
  • @jsonView过滤属性
  • 2017-09-12 前端日报
  • Android 架构优化~MVP 架构改造
  • git 常用命令
  • golang 发送GET和POST示例
  • HTTP--网络协议分层,http历史(二)
  • isset在php5.6-和php7.0+的一些差异
  • JavaScript标准库系列——Math对象和Date对象(二)
  • Map集合、散列表、红黑树介绍
  • oschina
  • PV统计优化设计
  • vue 个人积累(使用工具,组件)
  • 从0实现一个tiny react(三)生命周期
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 前端自动化解决方案
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • 阿里云重庆大学大数据训练营落地分享
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • ​你们这样子,耽误我的工作进度怎么办?
  • # 数论-逆元
  • # 透过事物看本质的能力怎么培养?
  • #git 撤消对文件的更改
  • #Z2294. 打印树的直径
  • (03)光刻——半导体电路的绘制
  • (7)STL算法之交换赋值
  • (Forward) Music Player: From UI Proposal to Code
  • (Oracle)SQL优化技巧(一):分页查询
  • (二)hibernate配置管理
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (简单) HDU 2612 Find a way,BFS。
  • (三十五)大数据实战——Superset可视化平台搭建
  • (五)MySQL的备份及恢复
  • (转)jdk与jre的区别
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .net 7 上传文件踩坑