QtでBluetooth(基礎編)

# はじめに
昨日は、 @task_jp さんによる 「[AGL の開発に今年はちょっとだけ関われました](https://qiita.com/task_jp/items/1658ca7dd5bf4c829ec4)」でした。色々あったようですが、落ち着く所に落ち着けたようで何よりです。

@task_jpさんは、Qtの不具合修正をいくつも行なっていますが、再現から不具合を見つけて直した時の事を書かれています。こちらを参考に気になる不具合を見つけたらなおしてみて、 @helicalgear さんの「[コントリビューションしてみたよ!](https://qiita.com/helicalgear/items/ba97890ac7b5ddf8d9ed)」を参考にパッチを送ってみましょう。

なお、修正はちょっと難しいかもと思ったらバグレポートを出してみましょう。

バグレポートのURLは以下に変更になっていますが、「[バグレポートの方法](https://blog.qt.io/jp/2010/03/23/how-to-report-bugs-3/)」あたりを参考にすると良いかと思います。

https://bugreports.qt.io/secure/Dashboard.jspa

まぁ、どれも面倒だなって人は商用ライセンス買ってサポートに依頼するって手もありますけどね。


# Bluetoothの基礎知識

さて、最近では、無線式のマウスやキーボード、イヤホンをはじめとして、活動量計やカーナビによる携帯の無線通話等、様々な場所にBluetoothが使われるようになりました。日本ではまだ赤外線の利用が多いですが、リモコンにも採用されはじめています。

Qtには、Bluetoothのためのクラス群が用意されています。そこで、アドベントカレンダーの機会を使って、少しQtのBluetooth周りについて調べておきたいと思います。普段、Bluetooth関連のアプリを書いたりしているわけではなく、単純に記事のために調べてる程度の知識レベルですので、誤りもあるかもしれません。気がつかれた方は、コメント下さい。

## Bluetoothとは
Bluetoothは、デジタル機器向けの近距離無線通信規格の1つです。もともとエリクソン社の社内プロジェクトとして開発が開始され、その後エリクソンとインテル・IBM・ノキア・東芝の5社によるBluetooth SIGが発足。このグループにより最初のバージョンが策定されました。

2018年12月某日現在のプロモーターは、エリクソン、ノキア、インテル、アップル、レノボ、マイクロソフト、東芝の7社となっています。他に、609社がアソシエート会員として、33769社がアダプター会員としてこのグループに参加しています。

## Bluetooth ClassicとBluetooth Low Energy

このBluetoothには2種類の系統が存在しています。
Bluetooth 3.0までのBluetooth ClassicとBluetooth 4.0以降のBluetooth Low Energy(BLE)です。

Bluetoothは3.0までの進化の過程では、高速化、大容量化が行なわれ、様々なプロファイルが追加され、複雑化していきました。

これに対し、ノキアでは体組織計や歩数計等のデバイスで大容量通信を必要としないデバイス向けとして、プロトコルをシンプルにして通信の処理手順を省き、弱い出力の電波でも通信できるような制御を盛り込んだWibreeというBluetoothとは別の規格を制定しました。Wirebeeでは音声や映像など大容量データの送受信はBluetoothなど他の手段で行なえば良いという割り切りった考えで、とにかく省電力とシンプルさを主眼に置いていました。

このWirebeeをBluetooth SIGが引き継ぎ、Bluetooth 4.0として仕様したのが、Bluetooth Low Energyです。

細々とした違いや歴史は他にいくらでも記事があるので、置いておいて、Qtでの対応状況を見ていきましょう。

# Qt Bluetoothの対応状況

## API
Bluetoothはアーキテクチャによって対応状況が異なっています。

参考) https://doc.qt.io/qt-5.12/qtbluetooth-index.html


|                | Android | iOS  | Linux | macOS | WinRT | Win32 |
|----------------|---------|------|-------|-------|-------|-------|
| Classic        | ○       |      |  ○    |  ○    |  ○    |       |
| BLE Central    | ○       | ○    |  ○    |  ○    |  ○    |       |
| BLE Peripheral | ○       | ○    |  ○    |  ○    |       |       |

- Linuxは、BlueZ 4.x/5.xに対応している必要があります。
- Windows 10 1507以降はWinRT API経由である程度サポートされている模様(1607以降でさらに改善）
- Windows7, Windows8等、WinRT APIがサポートされないOSは未対応です。

## 提供プロトコル

プロトコルはBluetoothデバイス間の通信を制御する通信規約です。

Qt Bluetoothでは、Classicのプロトコルとして機器同士のデータ伝送路を設定するL2CAPと、その上に構築されるRS-232Cシリアルポートの転送機能をエミュレーションプロトコルであるRFCOMMをサポートしています。また、L2CAPの上に構築されるサービス発見プロトコルであるSDPもサポートしています。

なお、現状、AndroidとWinRTではRFCOMMのみをサポートしていると記載されています。また、iOSではこれらを指定できるクラスが利用できないとされているので、直接これらのプロトコルを使ったやりとりを制御できないものと思われます。

BLEとしては、ATTというGATTプロファイル用のプロトコルがサポートされています。

|            | Android | iOS  | Linux | macOS | WinRT | Win32 |
|------------|---------|------|-------|-------|-------|-------|
| L2CAP      |         |      |  ○    |  ○    |       |       |
| RFCOMM     | ○       | ○    |  ○    |  ○    |  ○    |       |
| SDP        | ○       | ○    |  ○    |  ○    |  ○    |       |
| ATT        | ◯       | ○    |  ○    |  ○    |  ○    |       |
 
## 提供プロファイル

プロファイルは、プロトコルをどのように組み合わせて機能を提供するかの
厳密にはプロファイルが提供されているというべきなのか微妙なところもありますが、以下のことが可能となっています。

- OPP(OBEX Object Push Profile)を使ったファイル転送
- SPP(Serial Port Profile)を使ったシリアル通信エミュレート
- GATT(Generic Attribute Profile)を使った通信


## Qt Bluetoothのクラス群

Qt Bluetoothのクラス群は、.pro ファイルのQT変数に以下を追加することで利用できます。

```:
QT += bluetooth
```

提供されるクラスは以下の通りです。

|     クラス                       | 概要                                        |
|---------------------------------|---------------------------------------------|
| QBluetoothAddress               | Bluetooth Addressの保持クラス                 |
| QBluetoothDeviceDiscoveryAgent  | デバイス検出エージェント。ClassicかBLEかを指定可能 |
| QBluetoothDeviceInfo            | デバイス情報保持クラス                          |
| QBluetoothHostInfo              | ローカルBluetooth情報保持クラス                 |
| QBluetoothLocalDevice           | ローカルBluetoothデバイスへのアクセスクラス       |
| QBluetoothServer                | L2CAPまたはRFCOMM上でのサービス実装用クラス       |
| QBluetoothServiceDiscoveryAgent | SDPを使ったサービス照会クラス                    |
| QBluetoothServiceInfo           | サービス情報保持クラス                          |
| QBluetoothSocket                | L2CAPまたはRFCOMMを利用する通信クラス           |
| QBluetoothTransferManager       | Object push profile(OPP)を使った転送管理クラス |
| QBluetoothTransferReply         | データ転送応答クラス                           |
| QBluetoothTransferRequest       | データ転送要求クラス                           |
| QBluetoothUuid                  | Bluetoothで利用するUUID生成クラス              |
| QLowEnergyAdvertisingData       | BLE アドバタイジングデータクラス                 |
| QLowEnergyAdvertisingParameters | BLE アドバタイジングパラメータクラス              |
| QLowEnergyCharacteristic        | GATT キャラクタリスティック情報                 |
| QLowEnergyCharacteristicData    | GATT キャラクタリスティックデータ               |
| QLowEnergyConnectionParameters  | BLE接続パラメータ要求・通知クラス                |
| QLowEnergyController            | BLE制御クラス                                 |
| QLowEnergyDescriptor            | GATT ディスクリプタクラス　                     |
| QLowEnergyDescriptorData        | GATT ディスクリプタデータクラス                 |
| QLowEnergyService               | GATT サービスクラス                           |
| QLowEnergyServiceData           | GATT サービスデータクラス                      |

### 使用例
#### ローカルのBluetoothデバイスから情報を取得する

利用するのは[QBluetoothLocalDevice](http://doc.qt.io/qt-5/qbluetoothlocaldevice.html)クラスです。

```cpp:
#include <QCoreApplication>
#include <QBluetoothLocalDevice>
#include <QString>
#include <QBluetoothAddress>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QBluetoothLocalDevice localDevice;
    QString localDeviceName;

    // 有効なBluetooth deviceがあるかチェックする
    if (localDevice.isValid()) {

        // 電源を投入する
        localDevice.powerOn();

        // ローカルデバイス名を取得する
        localDeviceName = localDevice.name();

        // デバイスを検出可能にする
        localDevice.setHostMode(QBluetoothLocalDevice::HostDiscoverable);

        // 接続済みデバイスのアドレス一覧
        QList<QBluetoothAddress> remotes;
        remotes = localDevice.connectedDevices();

        qDebug() << localDeviceName;
    } else {
        qDebug() << "invalid";
    }
    return 0;
}
```
私の自宅の環境は以下のような状態です。

![スクリーンショット 2018-12-14 11.07.12.png](https://qiita-image-store.s3.amazonaws.com/0/58828/0eee168c-841a-5f18-0813-a9326841d98a.png)

この場合、プログラムを実行すると以下の結果が取得できます。

```:
"hermit4 の Mac Pro"
3
```

### Bluetoothデバイスを探す
接続可能なデバイスを探すには、[QBluetoothDeviceDiscoveryAgent](http://doc.qt.io/qt-5/qbluetoothdevicediscoveryagent.html)を利用します。発見すると、[QBluetoothDeviceInfo](http://doc.qt.io/qt-5/qbluetoothdeviceinfo.html)が取得できます。

```cpp:
#include <QCoreApplication>
#include <QBluetoothDeviceDiscoveryAgent>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QBluetoothDeviceDiscoveryAgent discovery;

    QObject::connect(&discovery, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
                     [](const QBluetoothDeviceInfo &device) {
                         qDebug() << "Found new device:" << device.name();
                     });
    discovery.start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
    return a.exec();
}
```

とりあえず、BLE対応のデバイスを探してみました。名前が無い子がいますが、４つほど見つかっています。

```:
Found new device: ""
Found new device: ""
Found new device: "LE-Bose Revolve+ R"
Found new device: "LE-Bose Revolve+ R"
```

アドバタイジングやファイルの転送例なども用意したいところですが、対象のデバイスなども用意しなくてはなりませんし、今回は公開までの時間の都合で厳しいため、この先は明日の[Qt勉強会@Tokyo](https://qt-users.connpass.com/event/112460/)で色々試して、実践編ということで別途公開したいと思います。

# まとめ
本日は、QtにBluetooth関連のクラスがあること、Classic、BLEとも対応しているもののアーキテクチャ依存で対応状況が異なること、とりあえずローカルデバイスへのアクセスとリモートデバイスのスキャン方法をかなり簡単にですが学習してみました。続きは、12/24のクリスマスイブにぼっちを極めつつ書き付けたいと思います。

明日は @kanryu さんによる「Qtで始める画像処理」です。お楽しみに。

なお、明日は新宿と名古屋でQtのもくもく勉強会があります。
お時間のある方はぜひ遊びに来てください。

- [Qt 勉強会 @ Tokyo #66](https://qt-users.connpass.com/event/112460/)
- [Qt 勉強会 @ Nagoya # 23](https://qt-users.connpass.com/event/112273/)