zoukankan      html  css  js  c++  java
  • Qt on Android 蓝牙通信开发

      版权声明:本文为MULTIBEANS ORG研发跟随文章,未经MLT ORG允许不得转载。

      最近做项目,需要开发安卓应用,实现串口的收发,目测CH340G在安卓手机上非常麻烦,而且驱动都是Java版本的, 就没选择,博主在大二的时候学习过Java SE基本的语法,写过一些小程序就放弃了Java的道路。最后选择了蓝牙无线透传模块,实现串口通信。现在Qt跨平台支持安卓,是在是令人欣喜。在网上找资料,用Qt on Android做蓝牙驱动的几乎没有,也没有相关例程,所以准备撰写此文,献给广大嵌入式程序员们

      2018/6/27更新:

           增加Java版本的蓝牙通信,文章地址:https://www.cnblogs.com/sigma0/p/9234478.html

    一、软硬件平台

    1.1 硬件平台

    1. 蓝牙:HC-05,(淘宝上有卖),它的接口就是跟串口一样的,我们用到了TX,RX,GND,VCC四个引脚。跟下位机或者用CH340G TTL转USB模块接到PC机上。蓝牙工作在串口模式可以通过AT指令调节。具体参考蓝牙配套的说明文档,最主要的就是请将蓝牙设定为从机模式,否则安卓手机搜寻链接不上。
    2.安卓手机:我这里测试用了2台安卓手机,一台是小米4移动版,安卓版本6.0.1;一台是MOTO MT887,安卓版本4.1.2。

    1.2 软件平台

    本项目Qt版本是5.7,系统是windows 8.1 x64

    二、软件基本介绍

      因为第一次做蓝牙,就做一个非常简单的雏形,实现蓝牙状态检测、蓝牙的开关、蓝牙的扫描和蓝牙配对链接,并且能像串口助手一样完成数据收发。如图,就是本一开始做的最简单的软件界面,本软件基于QWidget控件制作,当然你可以选择mainwinodw,更可以自己定义类。

    软件界面

     

      我不用介绍每个部位是什么了,都会明白吧?蓝牙打开后通过扫描,会将蓝牙的MAC地址还有名字显示在List中,我们双击List列表中的蓝牙,就会进入actived信号连接的槽函数,执行蓝牙的配对连接。建立连接之后,就类似串口一样可以进行数据通信了。另外,点击send按钮之后会发送一堆字符串。

    三、 蓝牙开发

    3.1 项目文件准备

    需要用到蓝牙就需要在.pro文件中引入库,我没有用Qt quick,用的是纯C++写的代码,你需要在.pro文件中加入这句话:

    QT += bluetooth
    如果没有这句话的话,包含蓝牙目录下的头文件,会提示找不到该文件。
     
    之后就是要包含一些蓝牙用到的头文件:
     
    #include <QtBluetooth/qbluetoothglobal.h>
    #include <QtBluetooth/qbluetoothlocaldevice.h>
    #include <qbluetoothaddress.h>
    #include <qbluetoothdevicediscoveryagent.h>
    #include <qbluetoothlocaldevice.h>
    #include <qbluetoothsocket.h>
      一会儿介绍每个都是做什么的。
     
    请在类中声明定义蓝牙相关句柄:
    QBluetoothDeviceDiscoveryAgent *discoveryAgent;
    QBluetoothLocalDevice *localDevice;
    QBluetoothSocket *socket;
     
      第一个discoveryAgent是用来对周围蓝牙进行搜寻,localDevice顾名思义,就是对本地设备进行操作,比如进行设备的打开,设备的关闭等等。socket就是用来进行蓝牙配对链接和数据传输的。这里要用到这三个。
     

    3.2 蓝牙开关和可见性设定

    在构造函数中,请为localDevice使用new运算符分配内存。
    localDevice = new QBluetoothLocalDevice();

    1) 蓝牙开关

    本设计在运行APP的时候,会检测一下我们本地设备的蓝牙是否打开,如果判断是开启状态,我们可以将打开蓝牙的按钮disable掉,将关闭蓝牙的按钮enable,所以在APP运行的时候需要进行蓝牙状态检测。检测方法如下:
     
    进行一个这样的检测,对本地设备模式进行判断。
    if( localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff )  {
            ui->pushButton_openBluetooth->setEnabled(true);
            ui->pushButton_closeDevice->setEnabled(false);
    }else {
            ui->pushButton_openBluetooth->setEnabled(false);
            ui->pushButton_closeDevice->setEnabled(true);
    }

    在构造函数中

    那么,我们如何来对蓝牙进行打开和关闭呢?我在open按钮和close按钮的槽函数中对蓝牙进行开关操作。

    open按钮的槽函数:

    void Widget::on_pushButton_openBluetooth_clicked()
    {
        localDevice->powerOn();
        ui->pushButton_closeDevice->setEnabled(true);
        ui->pushButton_openBluetooth->setEnabled(false);
        ui->pushButton_scan->setEnabled(true);
    }
    localDevice->powerOn();方法调用打开本地的蓝牙设备,然后你可以根据自己的喜好完成对按钮的使能和禁止操作。
     
    close按钮的槽函数:
    void Widget::on_pushButton_closeDevice_clicked()
    {
        localDevice->setHostMode(QBluetoothLocalDevice::HostPoweredOff);
        ui->pushButton_closeDevice->setEnabled(false);
        ui->pushButton_openBluetooth->setEnabled(true);
        ui->pushButton_scan->setEnabled(false);
    }
    close设备和我们的open设备的方法在形式上不一样,我还以为他们两个是对称的,但是事实上不是,只能用这样的方法对蓝牙进行关闭。

    2) 蓝牙可见性

      同样地,在蓝牙使用过程中,安卓手机提供了蓝牙是否可以被其他蓝牙搜索到这样的功能,也就是蓝牙可见,我们也可以用localDevice下的HostMode()方法,对这个状态进行检测。如下:
    if( localDevice->hostMode() == QBluetoothLocalDevice::HostDiscoverable ) {
            ui->checkBox_discoverable->setChecked(true);
    }else {
            ui->checkBox_discoverable->setChecked(false);
    }
    我的设计中,蓝牙可见如界面图用的是checkBox空间完成的,通过setChecked()方法,一开机对是否可见进行。
    在翻转checkBox的时候,会激发进入checkBox的槽函数,我们在checkBox的槽函数中,完成对蓝牙可见性的设定。代码如下:
    localDevice->setHostMode( QBluetoothLocalDevice::HostDiscoverable);
    同理,不可见你也能想到对吧。
     

    3.3 蓝牙设备的查找

      使用蓝牙设备的查找,就要用到 discoveryAgent 这个类的实例化。我们需要在构造函数中对discoveryAgent =new QBluetoothDeviceDiscoveryAgent();分配内存。然后就可以使用这个类的方法来对蓝牙进行查找了。除此之外,还要进行一个信号和槽的链接。
        connect(discoveryAgent,
                SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
                this,
                SLOT(addBlueToothDevicesToList(QBluetoothDeviceInfo))
                );
      在我们发现设备的时候,这个deviceDiscovered信号被触发,进入到addBlueToothDevicesToList的函数中。在上面的软件界面,我们的最上面蓝牙列表下的控件是ListIte控件,这里做一个槽函数,将发现的设备打印到这个列表中列出来。
    void Widget::addBlueToothDevicesToList( const QBluetoothDeviceInfo &info )
    {
        QString label = QString("%1 %2").arg(info.address().toString()).arg(info.name());
    
        QList<QListWidgetItem *> items = ui->list->findItems(label, Qt::MatchExactly);
    
        if (items.empty()) {
            QListWidgetItem *item = new QListWidgetItem(label);
            QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(info.address());
            if (pairingStatus == QBluetoothLocalDevice::Paired || pairingStatus == QBluetoothLocalDevice::AuthorizedPaired )
                item->setTextColor(QColor(Qt::green));
            else
                item->setTextColor(QColor(Qt::black));
            ui->list->addItem(item);
        }
    
    }
      这里给出这个函数,每一句话十分的好理解,这里增加点选操作,当点击listItem中的项目的时候,背景颜色会翻转,双击这个项目就会和这个蓝牙设备建立连接,这里有个actived槽函数,在这个槽函数里面就会进行蓝牙的链接。下一章节写这个如何连接。
     

    3.4 蓝牙设备的建立连接

      在说蓝牙设备连接之前,不得不提一个非常重要的概念,就是蓝牙的Uuid,引用一下百度的:
     
      在蓝牙中,每个服务和服务属性都唯一地由"全球唯一标识符" (UUID)来校验。正如它的名字所暗示的,每一个这样的标识符都要在时空上保证唯一。UUID类可表现为短整形(16或32位)和长整形(128位)UUID。他提供了分别利用String和16位或32位数值来创建类的构造函数,提供了一个可以比较两个UUID(如果两个都是128位)的方法,还有一个可以转换一个UUID为一个字符串的方法。UUID实例是不可改变的(immutable),只有被UUID标示的服务可以被发现。
    在Linux下你用一个命令uuidgen -t可以生成一个UUID值;在Windows下则执行命令uuidgen 。UUID看起来就像如下的这个形式:2d266186-01fb-47c2-8d9f-10b8ec891363。当使用生成的UUID去创建一个UUID对象,你可以去掉连字符。
     
    在我们的项目中,用到的模式是串口模式,我们需要建立一个存储Uuid的机制,如下:
    static const QLatin1String serviceUuid("00001101-0000-1000-8000-00805F9B34FB");
    这个字符串里面的内容就是串口模式的Uuid,如果你开发的蓝牙也是要使用串口,你直接Copy过去就可以了,如果你使用其他模式,自己去找这个Uuid码是多少。
     
    在使用蓝牙建立连接,需要建立蓝牙socket服务。请在构造函数中增加对socket的分配内存,要注意的是构造函数中的参数需要给定模式。
     
    socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
      
     
      在Qt文档中,给了3中模式,具体如何这里不做引申,读者需要请自己查询文档。但RfcommProtocol,属于模拟RS232模式,我就叫串口模式了。
      在上一节中说了,当双击ItemList控件中的项目时候,会进入到actived槽函数和蓝牙进行链接,那么如何连接呢?在itemList中会打印一个蓝牙的MAC地址信息,我们会将这个Mac地址保存在QBluetoothAddress这个类的实例化中,并将这个address传递给socket,作为链接依据。
    void Widget::itemActivated(QListWidgetItem *item)
    {
        QString text = item->text();
    
        int index = text.indexOf(' ');
    
        if (index == -1)
            return;
    
        QBluetoothAddress address(text.left(index));
        QString name(text.mid(index + 1));
        qDebug() << "You has choice the bluetooth address is " << address;
        qDebug() << "The device is connneting.... ";
        QMessageBox::information(this,tr("Info"),tr("The device is connecting..."));
        socket->connectToService(address, QBluetoothUuid(serviceUuid) ,QIODevice::ReadWrite);
    
    }

      我们通过对字符串的处理,将得到address信息。通过socket->connectToService(....),把地址,Uuid,和蓝牙模式传递进去,当执行完这句话的时候,安卓手机开始和你

       选择的蓝牙设备进行链接。

      同样在socket中也提供了丰富的槽函数,比如成功建立连接信号,成功断开信号,这里在槽函数中可以做一些例子,这里给出例子:

        connect(socket,
                SIGNAL(connected()),
                this,
                SLOT(bluetoothConnectedEvent())
                );
        connect(socket,
                SIGNAL(disconnected()),
                this,
                SLOT(bluetoothDisconnectedEvent())
                );
    void Widget::bluetoothConnectedEvent()
    {
      // 2017/10/8 更新一下,请在这里插入关闭蓝牙查找服务,否则数据会断。
    // 具体语句是什么我忘记了,反正使用discoveryAgent的一个什么close,或者stop的方法
    qDebug()
    << "The android device has been connected successfully!"; QMessageBox::information(this,tr("Info"),tr("successful connection!")); } void Widget::bluetoothDisconnectedEvent() { qDebug() << "The android device has been disconnected successfully!"; QMessageBox::information(this,tr("Info"),tr("successful disconnection!")); }

    最后,还有一个断开连接函数。通过断开连接按钮的槽函数实现。

    void Widget::on_pushButton_disconnect_clicked()
    {
        socket->disconnectFromService();
    
    }

    3.5 发送和接收数据

      蓝牙发送和接收数据,也是通过socket进行。发送数据十分简单:
    void Widget::on_pushButton_send_clicked()
    {
        QByteArray arrayData;
        QString s("Hello Windows!!!
    This message is sended via bluetooth of android device!
    ");
        arrayData = s.toUtf8();
        socket->write(arrayData);
    }
    这里通过socket->write函数,完成发送。发送之后,上位机,我用的串口助手会显示该信息。
    串口助手接受到信息
     
    那么接收数据呢?
    我们在构造函数中,需要建立这样的一个信号和槽的链接:
        connect(socket,
                SIGNAL(readyRead()),
                this,
                SLOT(readBluetoothDataEvent())
                );
    readyRead()信号触发,跳进readBluetoothDataEvent中。
    void Widget::readBluetoothDataEvent()
    {
    
        QByteArray line = socket->readAll();
        QString strData = line.toHex();
        comStr.append(strData);
        qDebug() <<"rec data is: "<< comStr;
        qDebug() <<"The comStr length is: " << comStr.length();
        if(comStr.length() >= 30) {
    
            ui->textBrowser_info->append(comStr + "
    ");
            comStr.clear();
        }
    
    }
    我这里是这样处理的,当然了,你有你自己的处理方法,意思就是那么个意思。
     

    四、结束语

    完成对蓝牙的开发,实现了最基本的功能,这里为了讲述用Qt开发蓝牙在安卓设备上,用了最简单最简单的例子,给你一个思路框架方法,如果追求极高的稳定性,好需要自
    己深入研究,这里不做讨论。欢迎批评指正,我也是一个求学者,大家共同交流,共同进步。最后,贴上源码,仅供大家参考。
     
     
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    提取码: zykk
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     
  • 相关阅读:
    Unity3D Shader入门指南(二)
    Unity3D Shader入门指南(一)
    NGUI 减少drawcall规则
    linux系统date命令详解
    使用Installutil安装系统服务方法
    各大视频网站广告屏蔽代码
    C#--无边框窗体实现拖动、最大化、最小化、关闭(转)
    C# 如何在winform窗体自定义一个扁平化控件(转)
    C# winform 界面美化技巧(扁平化设计) (转)
    VC6插件安装--Unable to register this add-in because its DllRegisterServer returns an error (转)
  • 原文地址:https://www.cnblogs.com/sigma0/p/5769527.html
Copyright © 2011-2022 走看看