Qt on Android 藍牙開發

来源:http://www.cnblogs.com/sigma0/archive/2016/08/14/5769527.html
-Advertisement-
Play Games

版權聲明:本文為MULTIBEANS ORG研發跟隨文章,未經MLT ORG允許不得轉載。 最近做項目,需要開發安卓應用,實現串口的收發,目測CH340G在安卓手機上非常麻煩,而且驅動都是Java版本的, 就沒選擇,博主在大二的時候學習過Java SE基本的語法,寫過一些小程式就放棄了Java的道路 ...


  版權聲明:本文為MULTIBEANS ORG研發跟隨文章,未經MLT ORG允許不得轉載。

 

  最近做項目,需要開發安卓應用,實現串口的收發,目測CH340G在安卓手機上非常麻煩,而且驅動都是Java版本的, 就沒選擇,博主在大二的時候學習過Java SE基本的語法,寫過一些小程式就放棄了Java的道路。最後選擇了藍牙無線透傳模塊,實現串口通信。現在Qt跨平臺支持安卓,是在是令人欣喜。在網上找資料,用Qt on Android做藍牙驅動的幾乎沒有,也沒有相關常式,所以準備撰寫此文,獻給廣大嵌入式程式員們

一、軟硬體平臺

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()
{
    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!!!\nThis message is sended via bluetooth of android device!\n");
    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 + "\n");
        comStr.clear();
    }

}

 

我這裡是這樣處理的,當然了,你有你自己的處理方法,意思就是那麼個意思。  

四、結束語

完成對藍牙的開發,實現了最基本的功能,這裡為了講述用Qt開發藍牙在安卓設備上,用了最簡單最簡單的例子,給你一個思路框架方法,如果追求極高的穩定性,好需要自 己深入研究,這裡不做討論。歡迎批評指正,我也是一個求學者,大家共同交流,共同進步。最後,貼上源碼,僅供大家參考。     ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 百度雲鏈接:http://pan.baidu.com/s/1dEB5LoX 提取碼: zykk ------------------------------------------------------------------------------------------------------------------------------------------------------------------------  
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要:與關係資料庫相比,每個NoSQL都有自己不同的適用場景,這裡帶大家盤點文檔資料庫、圖資料庫、鍵值數據存儲、列存儲資料庫與記憶體數據網路等領域的常用的NoSQL。 在幾年內,NoSQL資料庫一直以性能、可擴展性、靈活的模式和分析能力聚焦著人們的註意力。儘管關係型資料庫對於某些用例來說仍是一個不錯的... ...
  • 1.jar包安裝到MVN本地庫 mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0.1.0 -Dpackaging=jar -Dfile=ojdbc6.jar 2. oracle 12c... ...
  • R 是一門擁有統計分析及作圖功能的免費軟體,主要用於數學建模、統計計算、數據處理、可視化等方向。據 IEEE Spectrum發佈的2016年編程語言前10位排名來看,R語言由2015年排名第6位上升級2016年的第5位。目前在CRAN 上發佈的演算法包已經超過8000+多個。R體系涉及到高等數據、概 ...
  • 結構體源代碼如下: typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // +18h WORD Magic; // 標誌字, ROM 映像(0107h),普通可執行文件(010Bh) +1Ah BYTE MajorLinke... ...
  • 1、簡述TCP三次握手四次揮手過程及各過程中客戶端和伺服器端的狀態。 1 2 3 4 5 6 7 8 9 10 11 12 13 #三次握手 客戶端向伺服器端發送SYN包,客戶端進入SYN_SEND狀態 伺服器端收到客戶端發送的包返回ACK+SYN包,伺服器端進入SYN_RECV狀態 客戶端收到服務 ...
  • 源代碼如下: typedef struct _IMAGE_FILE_HEADER { +04h WORD Machine; // 運行平臺 +06h WORD NumberOfSections; // 文件的區塊數目 +08h DWORD TimeDateStamp; // 文件創... ...
  • 問題 一般Android手機用usb數據線連接到windows操作系統的電腦上後,會自動將手機存儲卡以移動存儲的方式顯示在電腦里. 但是如果操作系統是Mac的,就沒有這個存儲設備.問題來了,Mac電腦如何讀取手機上的文件呢? 解決方式 從Android的網站下載一個傳輸工具,地址是, 安裝打開後,會 ...
  • 比如 ssh 10.0.1.23,出現以下情況: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @@@@@@@@@@@@@@@ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...