搜索
bottom↓
回复: 0

《I.MX6U嵌入式Qt开发指南》第二十章 USB Bluetooth

[复制链接]

出0入234汤圆

发表于 2021-7-19 16:12:53 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2021-8-11 12:25 编辑

1)实验平台:正点原子i.MX6ULL Linux阿尔法开发板
2)  章节摘自【正点原子】《I.MX6U嵌入式Qt开发指南》
3)购买链接:https://item.taobao.com/item.htm?&id=603672744434
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)
正点原子Linux技术交流群:1027879335    1.png


2.jpg


3.png

第二十章 USB Bluetooth

          Qt官方提供了蓝牙的相关类和API函数,也提供了相关的例程给我们参考。编者根据Qt官方的例程编写出适合我们Ubuntu和正点原子I.MX6U开发板的例程。注意Windows上不能使用Qt的蓝牙例程,因为底层需要有BlueZ协议栈,而Windows没有。Windows可能需要去移植。编者就不去探究了。确保我们正点原子I.MX6U开发板与Ubuntu可用即可,所以大家还是老实的用Ubuntu来开发吧!

20.1 资源简介
       在正点原子IMX6U开发板上虽然没有带板载蓝牙,但是可以外接免驱USB蓝牙,直接在USB接口插上一个USB蓝牙模块就可以进行本小节的实验了。详细请看【正点原子】I.MX6U用户快速体验V1.x.pdf的第3.29小节蓝牙测试,先了解蓝牙是如何在Linux上如何使用的,切记先看正点原子快速体验文档,了解用哪种蓝牙芯片,和怎么测试蓝牙的。本Qt教程就不再介绍了。
20.2 应用实例
       项目简介:Qt蓝牙聊天。将蓝牙设置成一个服务器,或者用做客户端,连接手机即可通信。
       例06_bluetooth_chat,Qt蓝牙聊天(难度:难)。项目路径为Qt/3/06_bluetooth_chat。
       Qt使用蓝牙,需要在项目文件加上相应的蓝牙模块。添加的代码如下红色加粗部分。06_bluetooth_chat.pro文件代码如下。
  1. 1   QT       += core gui bluetooth
  2. 2
  3. 3   greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  4. 4
  5. 5   CONFIG += c++11
  6. 6
  7. 7   # The following define makes your compiler emit warnings if you use
  8. 8   # any Qt feature that has been marked deprecated (the exact warnings
  9. 9   # depend on your compiler). Please consult the documentation of the
  10. 10  # deprecated API in order to know how to port your code away from it.
  11. 11  DEFINES += QT_DEPRECATED_WARNINGS
  12. 12
  13. 13  # You can also make your code fail to compile if it uses deprecated APIs.
  14. 14  # In order to do so, uncomment the following line.
  15. 15  # You can also select to disable deprecated APIs only up to a certain version of Qt.
  16. 16  #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
  17. 17
  18. 18  SOURCES += \
  19. 19      chatclient.cpp \
  20. 20      chatserver.cpp \
  21. 21      main.cpp \
  22. 22      mainwindow.cpp \
  23. 23      remoteselector.cpp
  24. 24
  25. 25  HEADERS += \
  26. 26      chatclient.h \
  27. 27      chatserver.h \
  28. 28      mainwindow.h \
  29. 29      remoteselector.h
  30. 30
  31. 31  # Default rules for deployment.
  32. 32  qnx: target.path = /tmp/${TARGET}/bin
  33. 33  else: unix:!android: target.path = /opt/${TARGET}/bin
  34. 34  !isEmpty(target.path): INSTALLS += target
复制代码

        第18~29行,可以看到我们的项目组成文件。一个客户端,一个服务端,一个主界面和一个远程选择蓝牙的文件。总的看起来有四大部分,下面就介绍这四大部分的文件。
        chatclient.h的代码如下。
  1.     /******************************************************************
  2.     Copyright (C) 2015 The Qt Company Ltd.
  3.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  4.     * @projectName   06_bluetooth_chat
  5.     * @brief         chatclient.h
  6.     * @author        Deng Zhimao
  7.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  8.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9.     * @date           2021-03-20
  10.     *******************************************************************/
  11. 1   #ifndef CHATCLIENT_H
  12. 2   #define CHATCLIENT_H
  13. 3
  14. 4   #include <qbluetoothserviceinfo.h>
  15. 5   #include <QBluetoothSocket>
  16. 6   #include <QtCore/QObject>
  17. 7
  18. 8   QT_FORWARD_DECLARE_CLASS(QBluetoothSocket)
  19. 9
  20. 10  class ChatClient : public QObject
  21. 11  {
  22. 12      Q_OBJECT
  23. 13
  24. 14  public:
  25. 15      explicit ChatClient(QObject *parent = nullptr);
  26. 16      ~ChatClient();
  27. 17
  28. 18      /* 开启客户端 */
  29. 19      void startClient(const QBluetoothServiceInfo &remoteService);
  30. 20
  31. 21      /* 停止客户端 */
  32. 22      void stopClient();
  33. 23
  34. 24  public slots:
  35. 25      /* 发送消息 */
  36. 26      void sendMessage(const QString &message);
  37. 27
  38. 28      /* 主动断开连接 */
  39. 29      void disconnect();
  40. 30
  41. 31  signals:
  42. 32      /* 接收到消息信号 */
  43. 33      void messageReceived(const QString &sender, const QString &message);
  44. 34
  45. 35      /* 连接信号 */
  46. 36      void connected(const QString &name);
  47. 37
  48. 38      /* 断开连接信号 */
  49. 39      void disconnected();
  50. 40
  51. 41  private slots:
  52. 42      /* 从socket里读取消息 */
  53. 43      void readSocket();
  54. 44
  55. 45      /* 连接 */
  56. 46      void connected();
  57. 47
  58. 48  private:
  59. 49      /* socket通信 */
  60. 50      QBluetoothSocket *socket;
  61. 51  };
  62. 52
  63. 53  #endif // CHATCLIENT_H
复制代码

        chatclient.h文件主要是客户端的头文件,其中写一些接口,比如开启客户端,关闭客户端,接收信号与关闭信号等等。
chatclient.cpp的代码如下。        
  1.    /******************************************************************
  2.     Copyright (C) 2015 The Qt Company Ltd.
  3.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  4.     * @projectName   06_bluetooth_chat
  5.     * @brief         chatclient.cpp
  6.     * @author        Deng Zhimao
  7.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  8.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9.     * @date           2021-03-20
  10.     *******************************************************************/

  11. 1   #include "chatclient.h"
  12. 2   #include <qbluetoothsocket.h>
  13. 3
  14. 4   ChatClient::ChatClient(QObject *parent)
  15. 5       :   QObject(parent), socket(0)
  16. 6   {
  17. 7   }
  18. 8
  19. 9   ChatClient::~ChatClient()
  20. 10  {
  21. 11      stopClient();
  22. 12  }
  23. 13
  24. 14  /* 开启客户端 */
  25. 15  void ChatClient::startClient(const QBluetoothServiceInfo &remoteService)
  26. 16  {
  27. 17      if (socket)
  28. 18          return;
  29. 19      
  30. 20      // Connect to service
  31. 21      socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
  32. 22      qDebug() << "Create socket";
  33. 23      socket->connectToService(remoteService);
  34. 24      qDebug() << "ConnectToService done";
  35. 25      
  36. 26      connect(socket, SIGNAL(readyRead()),
  37. 27              this, SLOT(readSocket()));
  38. 28      connect(socket, SIGNAL(connected()),
  39. 29              this, SLOT(connected()));
  40. 30      connect(socket, SIGNAL(disconnected()),
  41. 31              this, SIGNAL(disconnected()));
  42. 32  }
  43. 33
  44. 34  /* 停止客户端 */
  45. 35  void ChatClient::stopClient()
  46. 36  {
  47. 37      delete socket;
  48. 38      socket = 0;
  49. 39  }
  50. 40
  51. 41  /* 从Socket读取消息 */
  52. 42  void ChatClient::readSocket()
  53. 43  {
  54. 44      if (!socket)
  55. 45          return;
  56. 46      
  57. 47      while (socket->canReadLine()) {
  58. 48          QByteArray line = socket->readLine();
  59. 49          emit messageReceived(socket->peerName(),
  60. 50                               QString::fromUtf8(line.constData(),
  61. 51                                                 line.length()));
  62. 52      }
  63. 53  }
  64. 54
  65. 55  /* 发送的消息 */
  66. 56  void ChatClient::sendMessage(const QString &message)
  67. 57  {
  68. 58      qDebug()<<"Sending data in client: " + message;
  69. 59      
  70. 60      QByteArray text = message.toUtf8() + '\n';
  71. 61      socket->write(text);
  72. 62  }
  73. 63
  74. 64  /* 主动连接 */
  75. 65  void ChatClient::connected()
  76. 66  {
  77. 67      emit connected(socket->peerName());
  78. 68  }
  79. 69
  80. 70  /* 主动断开连接*/
  81. 71  void ChatClient::disconnect() {
  82. 72      qDebug()<<"Going to disconnect in client";
  83. 73      if (socket) {
  84. 74          qDebug()<<"diconnecting...";
  85. 75          socket->close();
  86. 76      }
  87. 77  }
复制代码

        chatclient.cpp文件主要是客户端的chatclient.h头文件的实现。代码参考Qt官方btchat例子,代码比较长,也有相应的注释了,大家自由查看。主要我们关注的是下面的代码。
        第15~32行,我们需要开启客户端模式,那么我们需要将扫描服务器(手机蓝牙)的结果,实例化一个蓝牙socket,使用socket连接传入来的服务器信息,即可将本地蓝牙当作客户端,实现了客户端创建。
chatserver.h代码如下。
  1.     /******************************************************************
  2.     Copyright (C) 2015 The Qt Company Ltd.
  3.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  4.     * @projectName   06_bluetooth_chat
  5.     * @brief         chatserver.h
  6.     * @author        Deng Zhimao
  7.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  8.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9.     * @date           2021-03-20
  10.     *******************************************************************/

  11. 1   #ifndef CHATSERVER_H
  12. 2   #define CHATSERVER_H
  13. 3
  14. 4   #include <qbluetoothserviceinfo.h>
  15. 5   #include <qbluetoothaddress.h>
  16. 6   #include <QtCore/QObject>
  17. 7   #include <QtCore/QList>
  18. 8   #include <QBluetoothServer>
  19. 9   #include <QBluetoothSocket>
  20. 10
  21. 11
  22. 12  class ChatServer : public QObject
  23. 13  {
  24. 14      Q_OBJECT
  25. 15
  26. 16  public:
  27. 17      explicit ChatServer(QObject *parent = nullptr);
  28. 18      ~ChatServer();
  29. 19
  30. 20      /* 开启服务端 */
  31. 21      void startServer(const QBluetoothAddress &localAdapter = QBluetoothAddress());
  32. 22
  33. 23      /* 停止服务端 */
  34. 24      void stopServer();
  35. 25
  36. 26  public slots:
  37. 27      /* 发送消息 */
  38. 28      void sendMessage(const QString &message);
  39. 29
  40. 30      /* 服务端主动断开连接 */
  41. 31      void disconnect();
  42. 32
  43. 33  signals:
  44. 34      /* 接收到消息信号 */
  45. 35      void messageReceived(const QString &sender, const QString &message);
  46. 36
  47. 37      /* 客户端连接信号 */
  48. 38      void clientConnected(const QString &name);
  49. 39
  50. 40      /* 客户端断开连接信号 */
  51. 41      void clientDisconnected(const QString &name);
  52. 42
  53. 43  private slots:
  54. 44
  55. 45      /* 客户端连接 */
  56. 46      void clientConnected();
  57. 47
  58. 48      /* 客户端断开连接 */
  59. 49      void clientDisconnected();
  60. 50
  61. 51      /* 读socket */
  62. 52      void readSocket();
  63. 53
  64. 54  private:
  65. 55      /* 使用rfcomm协议 */
  66. 56      QBluetoothServer *rfcommServer;
  67. 57
  68. 58      /* 服务器蓝牙信息 */
  69. 59      QBluetoothServiceInfo serviceInfo;
  70. 60
  71. 61      /* 用于保存客户端socket */
  72. 62      QList<QBluetoothSocket *> clientSockets;
  73. 63
  74. 64      /* 用于保存客户端的名字 */
  75. 65      QList<QString> socketsPeername;
  76. 66  };
  77. 67
  78. 68  #endif // CHATSERVER_H
复制代码

        chatserver.h文件主要是服务端的头文件,其中写一些接口,比如开启服务端,关闭服务端,接收信号与关闭信号等等。
chatserver.cpp代码如下。
  1.    /******************************************************************
  2.     Copyright (C) 2015 The Qt Company Ltd.
  3.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  4.     * @projectName   06_bluetooth_chat
  5.     * @brief         chatserver.cpp
  6.     * @author        Deng Zhimao
  7.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  8.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9.     * @date           2021-03-20
  10.     *******************************************************************/

  11. 1   #include "chatserver.h"
  12. 2  
  13. 3   #include <qbluetoothserver.h>
  14. 4   #include <qbluetoothsocket.h>
  15. 5   #include <qbluetoothlocaldevice.h>
  16. 6  
  17. 7   static const QLatin1String serviceUuid("e8e10f95-1a70-4b27-9ccf-02010264e9c8");
  18. 8   ChatServer::ChatServer(QObject *parent)
  19. 9       :   QObject(parent), rfcommServer(0)
  20. 10  {
  21. 11  }
  22. 12
  23. 13  ChatServer::~ChatServer()
  24. 14  {
  25. 15      stopServer();
  26. 16  }
  27. 17
  28. 18  /* 开启服务端,设置服务端使用rfcomm协议与serviceInfo的一些属性 */
  29. 19  void ChatServer::startServer(const QBluetoothAddress& localAdapter)
  30. 20  {
  31. 21      if (rfcommServer)
  32. 22          return;
  33. 23
  34. 24      rfcommServer = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this);
  35. 25      connect(rfcommServer, SIGNAL(newConnection()), this, SLOT(clientConnected()));
  36. 26      bool result = rfcommServer->listen(localAdapter);
  37. 27      if (!result) {
  38. 28          qWarning()<<"Cannot bind chat server to"<<localAdapter.toString();
  39. 29          return;
  40. 30      }
  41. 31
  42. 32      //serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceRecordHandle, (uint)0x00010010);
  43. 33
  44. 34      QBluetoothServiceInfo::Sequence classId;
  45. 35
  46. 36      classId<<QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
  47. 37      serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList,
  48. 38                               classId);
  49. 39
  50. 40      classId.prepend(QVariant::fromValue(QBluetoothUuid(serviceUuid)));
  51. 41
  52. 42      serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId);
  53. 43      serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList,classId);
  54. 44
  55. 45      serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceName, tr("Bt Chat Server"));
  56. 46      serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceDescription,
  57. 47                               tr("Example bluetooth chat server"));
  58. 48      serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceProvider, tr("qt-project.org"));
  59. 49
  60. 50      serviceInfo.setServiceUuid(QBluetoothUuid(serviceUuid));
  61. 51
  62. 52      QBluetoothServiceInfo::Sequence publicBrowse;
  63. 53      publicBrowse<< QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup));
  64. 54      serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList,
  65. 55                               publicBrowse);
  66. 56
  67. 57      QBluetoothServiceInfo::Sequence protocolDescriptorList;
  68. 58      QBluetoothServiceInfo::Sequence protocol;
  69. 59      protocol<< QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap));
  70. 60      protocolDescriptorList.append(QVariant::fromValue(protocol));
  71. 61      protocol.clear();
  72. 62      protocol<< QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm))
  73. 63              << QVariant::fromValue(quint8(rfcommServer->serverPort()));
  74. 64      protocolDescriptorList.append(QVariant::fromValue(protocol));
  75. 65      serviceInfo.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList,
  76. 66                               protocolDescriptorList);
  77. 67
  78. 68      serviceInfo.registerService(localAdapter);
  79. 69  }
  80. 70
  81. 71  /* 停止服务端 */
  82. 72  void ChatServer::stopServer()
  83. 73  {
  84. 74      // Unregister service
  85. 75      serviceInfo.unregisterService();
  86. 76
  87. 77      // Close sockets
  88. 78      qDeleteAll(clientSockets);
  89. 79
  90. 80      // Close server
  91. 81      delete rfcommServer;
  92. 82      rfcommServer = 0;
  93. 83  }
  94. 84
  95. 85  /* 主动断开连接 */
  96. 86  void ChatServer::disconnect()
  97. 87  {
  98. 88      qDebug()<<"Going to disconnect in server";
  99. 89
  100. 90      foreach (QBluetoothSocket *socket, clientSockets) {
  101. 91          qDebug()<<"sending data in server!";
  102. 92          socket->close();
  103. 93      }
  104. 94  }
  105. 95
  106. 96  /* 发送消息 */
  107. 97  void ChatServer::sendMessage(const QString &message)
  108. 98  {
  109. 99      qDebug()<<"Going to send message in server: " << message;
  110. 100     QByteArray text = message.toUtf8() + '\n';
  111. 101
  112. 102     foreach (QBluetoothSocket *socket, clientSockets) {
  113. 103         qDebug()<<"sending data in server!";
  114. 104         socket->write(text);
  115. 105     }
  116. 106     qDebug()<<"server sending done!";
  117. 107 }
  118. 108
  119. 109 /* 客户端连接 */
  120. 110 void ChatServer::clientConnected()
  121. 111 {
  122. 112     qDebug()<<"clientConnected";
  123. 113
  124. 114     QBluetoothSocket *socket = rfcommServer->nextPendingConnection();
  125. 115     if (!socket)
  126. 116         return;
  127. 117
  128. 118     connect(socket, SIGNAL(readyRead()), this, SLOT(readSocket()));
  129. 119     connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
  130. 120     clientSockets.append(socket);
  131. 121     socketsPeername.append(socket->peerName());
  132. 122     emit clientConnected(socket->peerName());
  133. 123 }
  134. 124
  135. 125 /* 客户端断开连接 */
  136. 126 void ChatServer::clientDisconnected()
  137. 127 {
  138. 128     QBluetoothSocket *socket = qobject_cast<QBluetoothSocket *>(sender());
  139. 129     if (!socket)
  140. 130         return;
  141. 131
  142. 132     if (clientSockets.count() != 0) {
  143. 133         QString peerName;
  144. 134
  145. 135         if (socket->peerName().isEmpty())
  146. 136             peerName = socketsPeername.at(clientSockets.indexOf(socket));
  147. 137         else
  148. 138             peerName = socket->peerName();
  149. 139
  150. 140         emit clientDisconnected(peerName);
  151. 141
  152. 142         clientSockets.removeOne(socket);
  153. 143         socketsPeername.removeOne(peerName);
  154. 144     }
  155. 145
  156. 146     socket->deleteLater();
  157. 147
  158. 148 }
  159. 149
  160. 150 /* 从Socket里读取数据 */
  161. 151 void ChatServer::readSocket()
  162. 152 {
  163. 153     QBluetoothSocket *socket = qobject_cast<QBluetoothSocket *>(sender());
  164. 154     if (!socket)
  165. 155         return;
  166. 156
  167. 157     while (socket->bytesAvailable()) {
  168. 158         QByteArray line = socket->readLine().trimmed();
  169. 159         qDebug()<<QString::fromUtf8(line.constData(), line.length())<<endl;
  170. 160         emit messageReceived(socket->peerName(),
  171. 161                              QString::fromUtf8(line.constData(), line.length()));
  172. 162         qDebug()<<QString::fromUtf8(line.constData(), line.length())<<endl;
  173. 163     }
  174. 164 }
复制代码

        chatserver.cpp文件主要是服务端的chatserver.h头文件的实现。代码也是参考Qt官方btchat例子,代码比较长,也有相应的注释了,大家自由查看。主要我们关注的是下面的代码。
        第19~69行,我们需要开启服务端模式,那么我们需要将本地的蓝牙localAdapter的地址传入,创建一个QBluetoothServer对象rfcommServer。在19至69行代码很长,其中使用了serviceInfo.setAttribute()设置了许多参数,这个流程是官方给出的流程,我们只需要了解下就可以了。大体流程:使用了QBluetoothServiceInfo类允许访问服务端蓝牙服务的属性,其中有设置蓝牙的UUID为文件开头定义的serviceUuid,设置serviceUuid的目的是为了区分其他蓝牙,用于搜索此类型蓝牙,但是作用并不是很大,因为我们的手机并不一定开启了这个uuid标识。最后必须用registerService()启动蓝牙。
第36行,转换串行端口(SerialPort),转换成classId,然后再设置串行端口服务。通信原理就是串行端口连接到RFCOMM server channel。(PS:蓝牙使用的协议多且复杂,本教程并不能清晰解释这种原理,如果有错误,欢迎指出)。
remoteselector.h代码如下。
  1.    /******************************************************************
  2.     Copyright (C) 2015 The Qt Company Ltd.
  3.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  4.     * @projectName   06_bluetooth_chat
  5.     * @brief         remoteselector.h
  6.     * @author        Deng Zhimao
  7.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  8.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9.     * @date           2021-03-20
  10.     *******************************************************************/

  11. 1   #ifndef REMOTESELECTOR_H
  12. 2   #define REMOTESELECTOR_H
  13. 3
  14. 4   #include <qbluetoothuuid.h>
  15. 5   #include <qbluetoothserviceinfo.h>
  16. 6   #include <qbluetoothservicediscoveryagent.h>
  17. 7   #include <QListWidgetItem>
  18. 8
  19. 9   /* 声明一个蓝牙适配器类 */
  20. 10  class RemoteSelector : public QObject
  21. 11  {
  22. 12      Q_OBJECT
  23. 13
  24. 14  public:
  25. 15      explicit RemoteSelector(QBluetoothAddress&,
  26. 16                              QObject *parent = nullptr);
  27. 17      ~RemoteSelector();
  28. 18
  29. 19      /* 开启发现蓝牙 */
  30. 20      void startDiscovery(const QBluetoothUuid &uuid);
  31. 21
  32. 22      /* 停止发现蓝牙 */
  33. 23      void stopDiscovery();
  34. 24
  35. 25      /* 蓝牙服务 */
  36. 26      QBluetoothServiceInfo service() const;
  37. 27
  38. 28  signals:
  39. 29      /* 找到新服务 */
  40. 30      void newServiceFound(QListWidgetItem*);
  41. 31
  42. 32      /* 完成 */
  43. 33      void finished();
  44. 34
  45. 35  private:
  46. 36      /* 蓝牙服务代理,用于发现蓝牙服务 */
  47. 37      QBluetoothServiceDiscoveryAgent *m_discoveryAgent;
  48. 38
  49. 39      /* 服务信息 */
  50. 40      QBluetoothServiceInfo m_serviceInfo;
  51. 41
  52. 42  private slots:
  53. 43      /* 服务发现完成 */
  54. 44      void serviceDiscovered(const QBluetoothServiceInfo &serviceInfo);
  55. 45
  56. 46      /* 蓝牙发现完成 */
  57. 47      void discoveryFinished();
  58. 48
  59. 49  public:
  60. 50      /* 键值类容器 */
  61. 51      QMap<QString, QBluetoothServiceInfo> m_discoveredServices;
  62. 52  };
  63. 53
  64. 54  #endif // REMOTESELECTOR_H
  65. 55
复制代码

        remoteselector.h翻译成远程选择器,代码也是参考Qt官方btchat例子,这个头文件定义了开启蓝牙发现模式,蓝牙关闭发现模式,还有服务完成等等,代码有注释,请自由查看。
remoteselector.cpp代码如下。
  1.    /******************************************************************
  2.     Copyright (C) 2015 The Qt Company Ltd.
  3.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  4.     * @projectName   06_bluetooth_chat
  5.     * @brief         remoteselector.cpp
  6.     * @author        Deng Zhimao
  7.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  8.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9.     * @date           2021-03-20
  10.     *******************************************************************/
  11. 1   #include "remoteselector.h"
  12. 2
  13. 3   /* 初始化本地蓝牙 */
  14. 4   RemoteSelector::RemoteSelector(QBluetoothAddress &localAdapter, QObject *parent)
  15. 5       : QObject(parent)
  16. 6   {
  17. 7       m_discoveryAgent = new QBluetoothServiceDiscoveryAgent(localAdapter);
  18. 8
  19. 9       connect(m_discoveryAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)),
  20. 10              this, SLOT(serviceDiscovered(QBluetoothServiceInfo)));
  21. 11      connect(m_discoveryAgent, SIGNAL(finished()), this, SLOT(discoveryFinished()));
  22. 12      connect(m_discoveryAgent, SIGNAL(canceled()), this, SLOT(discoveryFinished()));
  23. 13  }
  24. 14
  25. 15  RemoteSelector::~RemoteSelector()
  26. 16  {
  27. 17      delete m_discoveryAgent;
  28. 18  }
  29. 19
  30. 20  /* 开启发现模式,这里无需设置过滤uuid,否则搜索不到手机
  31. 21   * uuid会过滤符合条件的uuid服务都会返回相应的蓝牙设备
  32. 22   */
  33. 23  void RemoteSelector::startDiscovery(const QBluetoothUuid &uuid)
  34. 24  {
  35. 25      Q_UNUSED(uuid);
  36. 26      qDebug()<<"startDiscovery";
  37. 27      if (m_discoveryAgent->isActive()) {
  38. 28          qDebug()<<"stop the searching first";
  39. 29          m_discoveryAgent->stop();
  40. 30      }
  41. 31
  42. 32      //m_discoveryAgent->setUuidFilter(uuid);
  43. 33      m_discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);
  44. 34  }
  45. 35
  46. 36  /* 停止发现 */
  47. 37  void RemoteSelector::stopDiscovery()
  48. 38  {
  49. 39      qDebug()<<"stopDiscovery";
  50. 40      if (m_discoveryAgent){
  51. 41          m_discoveryAgent->stop();
  52. 42      }
  53. 43  }
  54. 44
  55. 45  QBluetoothServiceInfo RemoteSelector::service() const
  56. 46  {
  57. 47      return m_serviceInfo;
  58. 48  }
  59. 49
  60. 50  /* 扫描蓝牙服务信息 */
  61. 51  void RemoteSelector::serviceDiscovered(const QBluetoothServiceInfo &serviceInfo)
  62. 52  {
  63. 53  #if 0
  64. 54      qDebug() << "Discovered service on"
  65. 55               << serviceInfo.device().name() << serviceInfo.device().address().toString();
  66. 56      qDebug() << "\tService name:" << serviceInfo.serviceName();
  67. 57      qDebug() << "\tDescription:"
  68. 58               << serviceInfo.attribute(QBluetoothServiceInfo::ServiceDescription).toString();
  69. 59      qDebug() << "\tProvider:"
  70. 60               << serviceInfo.attribute(QBluetoothServiceInfo::ServiceProvider).toString();
  71. 61      qDebug() << "\tL2CAP protocol service multiplexer:"
  72. 62               << serviceInfo.protocolServiceMultiplexer();
  73. 63      qDebug() << "\tRFCOMM server channel:" << serviceInfo.serverChannel();
  74. 64  #endif
  75. 65
  76. 66      QMapIterator<QString, QBluetoothServiceInfo> i(m_discoveredServices);
  77. 67      while (i.hasNext()){
  78. 68          i.next();
  79. 69          if (serviceInfo.device().address() == i.value().device().address()){
  80. 70              return;
  81. 71          }
  82. 72      }
  83. 73
  84. 74      QString remoteName;
  85. 75      if (serviceInfo.device().name().isEmpty())
  86. 76          remoteName = serviceInfo.device().address().toString();
  87. 77      else
  88. 78          remoteName = serviceInfo.device().name();
  89. 79
  90. 80      qDebug()<<"adding to the list....";
  91. 81      qDebug()<<"remoteName: "<< remoteName;
  92. 82      QListWidgetItem *item =
  93. 83              new QListWidgetItem(QString::fromLatin1("%1%2")
  94. 84                                  .arg(remoteName, serviceInfo.serviceName()));
  95. 85      m_discoveredServices.insert(remoteName, serviceInfo);
  96. 86      emit newServiceFound(item);
  97. 87  }
  98. 88
  99. 89  /* 发现完成 */
  100. 90  void RemoteSelector::discoveryFinished()
  101. 91  {
  102. 92      qDebug()<<"discoveryFinished";
  103. 93      emit finished();
  104. 94  }
复制代码

        remoteselector .cpp是remoteselector.h的实现代码。主要看以下几点。
        第4~13行,初始化本地蓝牙,实例化对象discoveryAgent(代理对象),蓝牙主要通过本地代理对象去扫描其他蓝牙。
        第32行,这里官方Qt代码设计是过滤uuid。只有符合对应的uuid的蓝牙,才会返回结果。因为我们要扫描我们的手机,所以这里我们要把他注释掉。手机的uuid没有设置成设定的uuid,如果设置了uuid过滤,手机就扫描不出了。(uuid指的是唯一标识,手机蓝牙有很多uuid,不同的uuid有不同的作用,指示着不同的服务)。
        其他代码都是一些逻辑性的代码,比较简单,请自由查看。
mainwindow.h代码如下。
  1.    /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   06_bluetooth_chat
  4.     * @brief         mainwindow.h
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-03-19
  9.     *******************************************************************/
  10. 1   #ifndef MAINWINDOW_H
  11. 2   #define MAINWINDOW_H
  12. 3  
  13. 4   #include <QMainWindow>
  14. 5   #include <qbluetoothserviceinfo.h>
  15. 6   #include <qbluetoothsocket.h>
  16. 7   #include <qbluetoothhostinfo.h>
  17. 8   #include <QDebug>
  18. 9   #include <QTabWidget>
  19. 10  #include <QHBoxLayout>
  20. 11  #include <QVBoxLayout>
  21. 12  #include <QPushButton>
  22. 13  #include <QListWidget>
  23. 14  #include <QTextBrowser>
  24. 15  #include <QLineEdit>
  25. 16
  26. 17  class ChatServer;
  27. 18  class ChatClient;
  28. 19  class RemoteSelector;
  29. 20
  30. 21  class MainWindow : public QMainWindow
  31. 22  {
  32. 23      Q_OBJECT
  33. 24
  34. 25  public:
  35. 26      MainWindow(QWidget *parent = nullptr);
  36. 27      ~MainWindow();
  37. 28
  38. 29  public:
  39. 30      /* 暴露的接口,主动连接设备*/
  40. 31      Q_INVOKABLE void connectToDevice();
  41. 32
  42. 33  signals:
  43. 34      /* 发送消息信号 */
  44. 35      void sendMessage(const QString &message);
  45. 36
  46. 37      /* 连接断开信号 */
  47. 38      void disconnect();
  48. 39
  49. 40      /* 发现完成信号 */
  50. 41      void discoveryFinished();
  51. 42
  52. 43      /* 找到新服务信号 */
  53. 44      void newServicesFound(const QStringList &list);
  54. 45
  55. 46  public slots:
  56. 47      /* 停止搜索 */
  57. 48      void searchForDevices();
  58. 49
  59. 50      /* 开始搜索 */
  60. 51      void stopSearch();
  61. 52
  62. 53      /* 找到新服务 */
  63. 54      void newServiceFound(QListWidgetItem*);
  64. 55
  65. 56      /* 已连接 */
  66. 57      void connected(const QString &name);
  67. 58
  68. 59      /* 显示消息 */
  69. 60      void showMessage(const QString &sender, const QString &message);
  70. 61
  71. 62      /* 发送消息 */
  72. 63      void sendMessage();
  73. 64
  74. 65      /* 作为客户端断开连接 */
  75. 66      void clientDisconnected();
  76. 67
  77. 68      /* 主动断开连接 */
  78. 69      void toDisconnected();
  79. 70
  80. 71      /* 作为服务端时,客户端断开连接 */
  81. 72      void disconnected(const QString &name);
  82. 73
  83. 74  private:
  84. 75      /* 选择本地蓝牙,些方法未没使用 */
  85. 76      int adapterFromUserSelection() const;
  86. 77
  87. 78      /* 本地蓝牙的Index */
  88. 79      int currentAdapterIndex;
  89. 80
  90. 81      /* 蓝牙本地适配器初始化 */
  91. 82      void localAdapterInit();
  92. 83
  93. 84      /* 布局初始化 */
  94. 85      void layoutInit();
  95. 86
  96. 87      /* 服务端*/
  97. 88      ChatServer *server;
  98. 89
  99. 90      /* 多个客户端 */
  100. 91      QList<ChatClient *> clients;
  101. 92
  102. 93      /* 远程选择器,使用本地蓝牙去搜索蓝牙,可过滤蓝牙等 */
  103. 94      RemoteSelector *remoteSelector;
  104. 95
  105. 96      /* 本地蓝牙 */
  106. 97      QList<QBluetoothHostInfo> localAdapters;
  107. 98
  108. 99      /* 本地蓝牙名称 */
  109. 100     QString localName;
  110. 101
  111. 102     /* tabWidget视图,用于切换页面 */
  112. 103     QTabWidget *tabWidget;
  113. 104
  114. 105     /* 3个按钮,扫描按钮,连接按钮,发送按钮 */
  115. 106     QPushButton *pushButton[5];
  116. 107
  117. 108     /* 2个垂直布局,一个用于页面一,另一个用于页面二 */
  118. 109     QVBoxLayout *vBoxLayout[2];
  119. 110
  120. 111     /* 2个水平布局,一个用于页面一,另一个用于页面二 */
  121. 112     QHBoxLayout *hBoxLayout[2];
  122. 113
  123. 114     /* 页面一和页面二容器 */
  124. 115     QWidget *pageWidget[2];
  125. 116
  126. 117     /* 用于布局, pageWidget包含subWidget */
  127. 118     QWidget *subWidget[2];
  128. 119
  129. 120     /* 蓝牙列表 */
  130. 121     QListWidget *listWidget;
  131. 122
  132. 123     /* 显示对话的内容 */
  133. 124     QTextBrowser *textBrowser;
  134. 125
  135. 126     /* 发送消息输入框 */
  136. 127     QLineEdit *lineEdit;
  137. 128
  138. 129 };
  139. 130 #endif // MAINWINDOW_H
复制代码

        mainwindow.h是整个代码重要的文件,这里使用了客户端类,服务端类和远程服务端类。前面介绍的客户端类,服务端类和远程服务端类都是为mainwindow.h服务的。我们在编程的时候可以不用改动客户端类,服务端类和远程服务端类了,直接像mainwindow.h一样使用它们的接口就可以编程了。
        其中编者还在mainwindow.h使用了很多控件,这些控件都是界面组成的重要元素。并不复杂,如果看不懂界面布局,或者理解不了界面布局,请回到本教程的第七章学习基础,本教程不再一一说明这种简单的布局了。
mainwindow.cpp代码如下。
  1.    /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   06_bluetooth_chat
  4.     * @brief         mainwindow.cpp
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-03-19
  9.     *******************************************************************/
  10. 1   #include "mainwindow.h"
  11. 2   #include "remoteselector.h"
  12. 3   #include "chatserver.h"
  13. 4   #include "chatclient.h"
  14. 5   #include <qbluetoothuuid.h>
  15. 6   #include <qbluetoothserver.h>
  16. 7   #include <qbluetoothservicediscoveryagent.h>
  17. 8   #include <qbluetoothdeviceinfo.h>
  18. 9   #include <qbluetoothlocaldevice.h>
  19. 10  #include <QGuiApplication>
  20. 11  #include <QScreen>
  21. 12  #include <QRect>
  22. 13  #include <QTimer>
  23. 14  #include <QDebug>
  24. 15  #include <QTabBar>
  25. 16  #include <QHeaderView>
  26. 17  #include <QTableView>
  27. 18
  28. 19
  29. 20  static const QLatin1String
  30. 21  serviceUuid("e8e10f95-1a70-4b27-9ccf-02010264e9c8");
  31. 22
  32. 23  MainWindow::MainWindow(QWidget *parent)
  33. 24      : QMainWindow(parent)
  34. 25  {
  35. 26      /* 本地蓝牙初始化 */
  36. 27      localAdapterInit();
  37. 28
  38. 29      /* 界面布局初始化 */
  39. 30      layoutInit();
  40. 31  }
  41. 32
  42. 33  MainWindow::~MainWindow()
  43. 34  {
  44. 35      qDeleteAll(clients);
  45. 36      delete server;
  46. 37  }
  47. 38
  48. 39  /* 初始化本地蓝牙,作为服务端 */
  49. 40  void MainWindow::localAdapterInit()
  50. 41  {
  51. 42      /* 查找本地蓝牙的个数 */
  52. 43      localAdapters = QBluetoothLocalDevice::allDevices();
  53. 44      qDebug() << "localAdapter: " << localAdapters.count();
  54. 45
  55. 46      QBluetoothLocalDevice localDevice;
  56. 47      localDevice.setHostMode(QBluetoothLocalDevice::HostDiscoverable);
  57. 48
  58. 49      QBluetoothAddress adapter = QBluetoothAddress();
  59. 50      remoteSelector = new RemoteSelector(adapter, this);
  60. 51      connect(remoteSelector,
  61. 52              SIGNAL(newServiceFound(QListWidgetItem*)),
  62. 53              this, SLOT(newServiceFound(QListWidgetItem*)));
  63. 54
  64. 55      /* 初始化服务端 */
  65. 56      server = new ChatServer(this);
  66. 57
  67. 58      connect(server, SIGNAL(clientConnected(QString)),
  68. 59              this, SLOT(connected(QString)));
  69. 60
  70. 61      connect(server, SIGNAL(clientDisconnected(QString)),
  71. 62              this, SLOT(disconnected(QString)));
  72. 63
  73. 64      connect(server, SIGNAL(messageReceived(QString, QString)),
  74. 65              this, SLOT(showMessage(QString, QString)));
  75. 66
  76. 67      connect(this, SIGNAL(sendMessage(QString)),
  77. 68              server, SLOT(sendMessage(QString)));
  78. 69
  79. 70      connect(this, SIGNAL(disconnect()),
  80. 71              server, SLOT(disconnect()));
  81. 72
  82. 73      server->startServer();
  83. 74
  84. 75      /* 获取本地蓝牙的名称 */
  85. 76      localName = QBluetoothLocalDevice().name();
  86. 77  }
  87. 78
  88. 79  void MainWindow::layoutInit()
  89. 80  {
  90. 81      /* 获取屏幕的分辨率,Qt官方建议使用这
  91. 82       * 种方法获取屏幕分辨率,防上多屏设备导致对应不上
  92. 83       * 注意,这是获取整个桌面系统的分辨率
  93. 84       */
  94. 85      QList <QScreen *> list_screen =  QGuiApplication::screens();
  95. 86
  96. 87      /* 如果是ARM平台,直接设置大小为屏幕的大小 */
  97. 88  #if __arm__
  98. 89      /* 重设大小 */
  99. 90      this->resize(list_screen.at(0)->geometry().width(),
  100. 91                   list_screen.at(0)->geometry().height());
  101. 92  #else
  102. 93      /* 否则则设置主窗体大小为800x480 */
  103. 94      this->resize(800, 480);
  104. 95  #endif
  105. 96
  106. 97      /* 主视图 */
  107. 98      tabWidget = new QTabWidget(this);
  108. 99
  109. 100     /* 设置主窗口居中视图为tabWidget */
  110. 101     setCentralWidget(tabWidget);
  111. 102
  112. 103     /* 页面一对象实例化 */
  113. 104     vBoxLayout[0] = new QVBoxLayout();
  114. 105     hBoxLayout[0] = new QHBoxLayout();
  115. 106     pageWidget[0] = new QWidget();
  116. 107     subWidget[0] = new QWidget();
  117. 108     listWidget = new QListWidget();
  118. 109     /* 0为扫描按钮,1为连接按钮 */
  119. 110     pushButton[0] = new QPushButton();
  120. 111     pushButton[1] = new QPushButton();
  121. 112     pushButton[2] = new QPushButton();
  122. 113     pushButton[3] = new QPushButton();
  123. 114     pushButton[4] = new QPushButton();
  124. 115
  125. 116     /* 页面二对象实例化 */
  126. 117     hBoxLayout[1] = new QHBoxLayout();
  127. 118     vBoxLayout[1] = new QVBoxLayout();
  128. 119     subWidget[1] = new QWidget();
  129. 120     textBrowser = new QTextBrowser();
  130. 121     lineEdit = new QLineEdit();
  131. 122     pushButton[2] = new QPushButton();
  132. 123     pageWidget[1] = new QWidget();
  133. 124
  134. 125
  135. 126     tabWidget->addTab(pageWidget[1], "蓝牙聊天");
  136. 127     tabWidget->addTab(pageWidget[0], "蓝牙列表");
  137. 128
  138. 129     /* 页面一 */
  139. 130     vBoxLayout[0]->addWidget(pushButton[0]);
  140. 131     vBoxLayout[0]->addWidget(pushButton[1]);
  141. 132     vBoxLayout[0]->addWidget(pushButton[2]);
  142. 133     vBoxLayout[0]->addWidget(pushButton[3]);
  143. 134     subWidget[0]->setLayout(vBoxLayout[0]);
  144. 135     hBoxLayout[0]->addWidget(listWidget);
  145. 136     hBoxLayout[0]->addWidget(subWidget[0]);
  146. 137     pageWidget[0]->setLayout(hBoxLayout[0]);
  147. 138     pushButton[0]->setMinimumSize(120, 40);
  148. 139     pushButton[1]->setMinimumSize(120, 40);
  149. 140     pushButton[2]->setMinimumSize(120, 40);
  150. 141     pushButton[3]->setMinimumSize(120, 40);
  151. 142     pushButton[0]->setText("开始扫描");
  152. 143     pushButton[1]->setText("停止扫描");
  153. 144     pushButton[2]->setText("连接");
  154. 145     pushButton[3]->setText("断开");
  155. 146
  156. 147     /* 页面二 */
  157. 148     hBoxLayout[1]->addWidget(lineEdit);
  158. 149     hBoxLayout[1]->addWidget(pushButton[4]);
  159. 150     subWidget[1]->setLayout(hBoxLayout[1]);
  160. 151     vBoxLayout[1]->addWidget(textBrowser);
  161. 152     vBoxLayout[1]->addWidget(subWidget[1]);
  162. 153     pageWidget[1]->setLayout(vBoxLayout[1]);
  163. 154     pushButton[4]->setMinimumSize(120, 40);
  164. 155     pushButton[4]->setText("发送");
  165. 156     lineEdit->setMinimumHeight(40);
  166. 157     lineEdit->setText("正点原子论坛网址<a href="www.openedv.com" target="_blank">www.openedv.com</a>");
  167. 158
  168. 159     /* 设置表头的大小 */
  169. 160     QString str = tr("QTabBar::tab {height:40; width:%1};")
  170. 161             .arg(this->width()/2);
  171. 162     tabWidget->setStyleSheet(str);
  172. 163
  173. 164     /* 开始搜寻蓝牙 */
  174. 165     connect(pushButton[0], SIGNAL(clicked()),
  175. 166             this, SLOT(searchForDevices()));
  176. 167
  177. 168     /* 停止搜寻蓝牙 */
  178. 169     connect(pushButton[1], SIGNAL(clicked()),
  179. 170             this, SLOT(stopSearch()));
  180. 171
  181. 172     /* 点击连接按钮,本地蓝牙作为客户端去连接外界的服务端 */
  182. 173     connect(pushButton[2], SIGNAL(clicked()),
  183. 174             this, SLOT(connectToDevice()));
  184. 175
  185. 176     /* 点击断开连接按钮,断开连接 */
  186. 177     connect(pushButton[3], SIGNAL(clicked()),
  187. 178             this, SLOT(toDisconnected()));
  188. 179
  189. 180     /* 发送消息 */
  190. 181     connect(pushButton[4], SIGNAL(clicked()),
  191. 182             this, SLOT(sendMessage()));
  192. 183 }
  193. 184
  194. 185 /* 作为客户端去连接 */
  195. 186 void MainWindow::connectToDevice()
  196. 187 {
  197. 188     if (listWidget->currentRow() == -1)
  198. 189         return;
  199. 190
  200. 191     QString name = listWidget->currentItem()->text();
  201. 192     qDebug() << "Connecting to " << name;
  202. 193
  203. 194     // Trying to get the service
  204. 195     QBluetoothServiceInfo service;
  205. 196     QMapIterator<QString,QBluetoothServiceInfo>
  206. 197             i(remoteSelector->m_discoveredServices);
  207. 198     bool found = false;
  208. 199     while (i.hasNext()){
  209. 200         i.next();
  210. 201
  211. 202         QString key = i.key();
  212. 203
  213. 204         /* 判断连接的蓝牙名称是否在发现的设备里 */
  214. 205         if (key == name) {
  215. 206             qDebug() << "The device is found";
  216. 207             service = i.value();
  217. 208             qDebug() << "value: " << i.value().device().address();
  218. 209             found = true;
  219. 210             break;
  220. 211         }
  221. 212     }
  222. 213
  223. 214     /* 如果找到,则连接设备 */
  224. 215     if (found) {
  225. 216         qDebug() << "Going to create client";
  226. 217         ChatClient *client = new ChatClient(this);
  227. 218         qDebug() << "Connecting...";
  228. 219
  229. 220         connect(client, SIGNAL(messageReceived(QString,QString)),
  230. 221                 this, SLOT(showMessage(QString,QString)));
  231. 222         connect(client, SIGNAL(disconnected()),
  232. 223                 this, SLOT(clientDisconnected()));;
  233. 224         connect(client, SIGNAL(connected(QString)),
  234. 225                 this, SLOT(connected(QString)));
  235. 226         connect(this, SIGNAL(sendMessage(QString)),
  236. 227                 client, SLOT(sendMessage(QString)));
  237. 228         connect(this, SIGNAL(disconnect()),
  238. 229                 client, SLOT(disconnect()));
  239. 230
  240. 231         qDebug() << "Start client";
  241. 232         client->startClient(service);
  242. 233
  243. 234         clients.append(client);
  244. 235     }
  245. 236 }
  246. 237
  247. 238 /* 本地蓝牙选择,默认使用第一个蓝牙 */
  248. 239 int MainWindow::adapterFromUserSelection() const
  249. 240 {
  250. 241     int result = 0;
  251. 242     QBluetoothAddress newAdapter = localAdapters.at(0).address();
  252. 243     return result;
  253. 244 }
  254. 245
  255. 246 /* 开始搜索 */
  256. 247 void MainWindow::searchForDevices()
  257. 248 {
  258. 249     /* 先清空 */
  259. 250     listWidget->clear();
  260. 251     qDebug() << "search for devices!";
  261. 252     if (remoteSelector) {
  262. 253         delete remoteSelector;
  263. 254         remoteSelector = NULL;
  264. 255     }
  265. 256
  266. 257     QBluetoothAddress adapter = QBluetoothAddress();
  267. 258     remoteSelector = new RemoteSelector(adapter, this);
  268. 259
  269. 260     connect(remoteSelector,
  270. 261             SIGNAL(newServiceFound(QListWidgetItem*)),
  271. 262             this, SLOT(newServiceFound(QListWidgetItem*)));
  272. 263
  273. 264     remoteSelector->m_discoveredServices.clear();
  274. 265     remoteSelector->startDiscovery(QBluetoothUuid(serviceUuid));
  275. 266     connect(remoteSelector, SIGNAL(finished()),
  276. 267             this, SIGNAL(discoveryFinished()));
  277. 268 }
  278. 269
  279. 270 /* 停止搜索 */
  280. 271 void MainWindow::stopSearch()
  281. 272 {
  282. 273     qDebug() << "Going to stop discovery...";
  283. 274     if (remoteSelector) {
  284. 275         remoteSelector->stopDiscovery();
  285. 276     }
  286. 277 }
  287. 278
  288. 279 /* 找到蓝牙服务 */
  289. 280 void MainWindow::newServiceFound(QListWidgetItem *item)
  290. 281 {
  291. 282     /* 设置项的大小 */
  292. 283     item->setSizeHint(QSize(listWidget->width(), 50));
  293. 284
  294. 285     /* 添加项 */
  295. 286     listWidget->addItem(item);
  296. 287
  297. 288     /* 设置当前项 */
  298. 289     listWidget->setCurrentRow(listWidget->count() - 1);
  299. 290
  300. 291     qDebug() << "newServiceFound";
  301. 292
  302. 293     // get all of the found devices
  303. 294     QStringList list;
  304. 295
  305. 296     QMapIterator<QString, QBluetoothServiceInfo>
  306. 297             i(remoteSelector->m_discoveredServices);
  307. 298     while (i.hasNext()){
  308. 299         i.next();
  309. 300         qDebug() << "key: " << i.key();
  310. 301         qDebug() << "value: " << i.value().device().address();
  311. 302         list << i.key();
  312. 303     }
  313. 304
  314. 305     qDebug() << "list count: "  << list.count();
  315. 306
  316. 307     emit newServicesFound(list);
  317. 308 }
  318. 309
  319. 310 /* 已经连接 */
  320. 311 void MainWindow::connected(const QString &name)
  321. 312 {
  322. 313     textBrowser->insertPlainText(tr("%1:已连接\n").arg(name));
  323. 314     tabWidget->setCurrentIndex(0);
  324. 315     textBrowser->moveCursor(QTextCursor::End);
  325. 316 }
  326. 317
  327. 318 /* 接收消息 */
  328. 319 void MainWindow::showMessage(const QString &sender,
  329. 320                              const QString &message)
  330. 321 {
  331. 322     textBrowser->insertPlainText(QString::fromLatin1("%1: %2\n")
  332. 323                                  .arg(sender, message));
  333. 324     tabWidget->setCurrentIndex(0);
  334. 325     textBrowser->moveCursor(QTextCursor::End);
  335. 326 }
  336. 327
  337. 328 /* 发送消息 */
  338. 329 void MainWindow::sendMessage()
  339. 330 {
  340. 331     showMessage(localName, lineEdit->text());
  341. 332     emit sendMessage(lineEdit->text());
  342. 333 }
  343. 334
  344. 335 /* 作为客户端断开连接 */
  345. 336 void MainWindow::clientDisconnected()
  346. 337 {
  347. 338     ChatClient *client = qobject_cast<ChatClient *>(sender());
  348. 339     if (client) {
  349. 340         clients.removeOne(client);
  350. 341         client->deleteLater();
  351. 342     }
  352. 343
  353. 344     tabWidget->setCurrentIndex(0);
  354. 345     textBrowser->moveCursor(QTextCursor::End);
  355. 346 }
  356. 347
  357. 348 /* 主动断开连接 */
  358. 349 void MainWindow::toDisconnected()
  359. 350 {
  360. 351     emit disconnect();
  361. 352     textBrowser->moveCursor(QTextCursor::End);
  362. 353     tabWidget->setCurrentIndex(0);
  363. 354 }
  364. 355
  365. 356 /* 作为服务端时,客户端断开连接 */
  366. 357 void MainWindow::disconnected(const QString &name)
  367. 358 {
  368. 359     textBrowser->insertPlainText(tr("%1:已断开\n").arg(name));
  369. 360     tabWidget->setCurrentIndex(0);
  370. 361     textBrowser->moveCursor(QTextCursor::End);
  371. 362 }
复制代码

        mainwindow.cpp则是整个项目的核心文件,包括处理界面点击的事件,客户端连接,服务端连接,扫描蓝牙,断开蓝牙和连接蓝牙等。设计这样的一个逻辑界面并不难,只要我们前面第七章Qt控件打下了基础。上面的代码注释详细,请自由查看。
20.3 程序运行效果
       本例程运行后,默认开启蓝牙的服务端模式,可以用手机安装蓝牙调试软件(安卓手机如蓝牙调试宝、蓝牙串口助手)。当我们点击蓝牙列表页面时,点击扫描后请等待扫描的结果,选中需要连接的蓝牙再点击连接。
       下面程序效果是Ubuntu虚拟机上连接USB蓝牙模块,用手机连接后运行的蓝牙聊天第一页效果图。
USB Bluetooth36527.png

       下面程序效果是Ubuntu虚拟机上连接USB蓝牙模块运行的蓝牙聊天第二页效果图。
USB Bluetooth36571.png

        安卓手机可以用蓝牙调试宝等软件进行配对连接。IOS手机请下载某些蓝牙调试软件测试即可。手机接收到的消息如下。
USB Bluetooth36629.png

        在编者测试的过程中,发现在Ubuntu上运行蓝牙聊天程序不太好用,需要开启扫描后,才能连接的上,而且接收的消息反应比较慢,有可能是虚拟机的原因吧。不过在正点原子I.MX6U开发板上运行没有问题。先按照详细请看【正点原子】I.MX6U用户快速体验V1.x.pdf的第3.29小节蓝牙测试开启蓝牙,启用蓝牙被扫描后,先进行配对,手机用蓝牙调试软件就可以连接上进行聊天了。
        注意:本程序需要确保蓝牙能正常使用的情况下才能运行,默认使用第一个蓝牙,如果Ubuntu上查看有两个蓝牙,请不要插着USB蓝牙启动电脑,先等Ubuntu启动后再插蓝牙模块。连接前应先配对,连接不上的原因可能或者蓝牙质量问题,或者系统里的软件没有开启蓝牙,或者使用的手机蓝牙调试软件不支持SPP(串行端口)蓝牙调试等,请退出重试等。程序仅供学习与参考。

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-25 21:51

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表