📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

 
(同じ利用者による、間の17版が非表示)
37行目: 37行目:
<br>
<br>
Qt Bluetoothモジュールは、これらの状況を適切に検出して、シグナル / スロットメカニズムを通じて通知する仕組みを提供している。<br>
Qt Bluetoothモジュールは、これらの状況を適切に検出して、シグナル / スロットメカニズムを通じて通知する仕組みを提供している。<br>
<br>
<u>※Classic Bluetoothを使用した場合の注意</u><br>
* <u>ソケットベースの通信に関する知識が重要である。</u>
* <u>大きなデータ転送に適しているが、電力消費も大きい。</u>
<br>
<u>ソフトウェアの要件 (データ量、通信頻度、電力消費等) に応じて適切な方式を選択する必要がある。</u><br>
<u>また、多くのモダンなBluetoothデバイスはBLEを採用する傾向にあり、特に省電力性が重要な場合はBLEが推奨される。</u><br>
<br><br>
== Classic Bluetoothの特徴 ==
Classic Bluetoothは、<code>QBluetoothSocket</code>クラスを使用した直接的なソケット通信である。<br>
<br>
RFCOMMプロトコルベースの通信であり、主に、シリアル通信のような双方向データストリームに適している。<br>
Bluetooth Low Energyと比較して、より大きなデータ転送が可能である。<br>
<br><br>
== Bluetoothの設定 (接続先) ==
接続先のLinuxにおいて、Bluetoothの設定および状態を確認する。<br>
bluetoothctl show
# 出力例:
Controller 00:E0:4C:23:99:87 (public)
        Name: mobian
        Alias: mobian
        Class: 0x00000110
        Powered: yes
        Discoverable: no
        DiscoverableTimeout: 0x000000b4
        Pairable: no
        UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
        UUID: Generic Access Profile    (00001800-0000-1000-8000-00805f9b34fb)
        UUID: PnP Information          (00001200-0000-1000-8000-00805f9b34fb)
        UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)
        UUID: A/V Remote Control        (0000110e-0000-1000-8000-00805f9b34fb)
        UUID: Device Information        (0000180a-0000-1000-8000-00805f9b34fb)
        Modalias: usb:xxxxxxxxxxxxxxx
        Discovering: no
        Roles: central
        Roles: peripheral
Advertising Features:
        ActiveInstances: 0x00 (0)
        SupportedInstances: 0x05 (5)
        SupportedIncludes: tx-power
        SupportedIncludes: appearance
        SupportedIncludes: local-name
<br>
接続先デバイスが<u>"Discoverable: no"</u>となっている場合は、一時的にデバイスを検出を有効にする。<br>
または、既知のMACアドレスを使用して直接接続を試みる。<br>
bluetoothctl discoverable on
<br>
接続先デバイスが<u>"Pairable: no"</u>となっている場合は、一時的にペアリングを有効にする。<br>
bluetoothctl pairable on
<br>
"Roles"が<u>"central"</u>と<u>"peripheral"</u>の両方をサポートしている場合は、接続モードを明示的に指定することを推奨する。<br>
<br>
上記の設定は、システムを再起動すると元の状態に戻る、あるいは、一定時間 (通常は180秒) が経過すると自動的に無効になる。<br>
<br>
永続的に設定を変更する場合は、Bluetoothの設定ファイルである/etc/bluetooth/main.confファイルを編集する必要がある。<br>
<u>ただし、セキュリティの観点から、必要な時だけ一時的に有効にすることが推奨される。</u><br>
sudo vi /etc/bluetooth/main.conf
<br>
<syntaxhighlight lang="ini">
# /etc/bluetooth/main.confファイル
[General]
# デバイスは永続的に検出可能な状態となる
DiscoverableTimeout = 0
# 常にペアリング可能な状態となる
AlwaysPairable = true
</syntaxhighlight>
<br>
デバイスを検出を有効にする。<br>
bluetoothctl discoverable on
<br>
設定を反映させるため、BlueZサービスを再起動する。<br>
sudo systemctl restart bluetooth
<br>
また、セキュリティ要件に応じて適切な認証方法を設定する。<br>
<br><br>
<br><br>


47行目: 126行目:
スキャン中は、各デバイスの基本情報 (デバイス名、MACアドレス、デバイスクラス、信号強度等) を取得できる。<br>
スキャン中は、各デバイスの基本情報 (デバイス名、MACアドレス、デバイスクラス、信号強度等) を取得できる。<br>
また、スキャン時間や範囲を設定可能であり、バッテリー消費とスキャン精度のバランスを取ることができる。<br>
また、スキャン時間や範囲を設定可能であり、バッテリー消費とスキャン精度のバランスを取ることができる。<br>
<br>
Bluetooth Classicでは、<code>QBluetoothDeviceDiscoveryAgent</code>を使用して、全てのBluetoothデバイスを検出する。<br>
<br>
==== Bluetoothアダプタの初期化 / デバイス検出用エージェントの生成 ====
Bluetoothアダプタを初期化する。<br>
デバイス検出用エージェントの生成する。<br>
<br>
また、BLEデバイスは検出対象から除外する。<br>
<br>
<syntaxhighlight lang="c++">
#include <QBluetoothDeviceDiscoveryAgent>
#include <QBluetoothDeviceInfo>
// Bluetoothアダプタの初期化とデバイス検出エージェントの生成
QBluetoothDeviceDiscoveryAgent discoveryAgent;
// Bluetooth Classicのみをスキャン (BLEデバイスを除外)
discoveryAgent.setDiscoveryModes(QBluetoothDeviceDiscoveryAgent::ClassicMethod);
// 検出時にもBLEデバイスをフィルタリング
connect(&discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, [](const QBluetoothDeviceInfo &device) {
    // BLEデバイスの場合
    if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
      return;
    }
    // Bluetooth Classicデバイスの場合
    // ...略
});
</syntaxhighlight>
<br>
==== デバイス検出イベントの設定 ====
デバイスが見つかった時のイベントする。<br>
スキャン完了時のイベント処理を設定する。<br>
エラー発生時のイベント処理を設定する。<br>
<br>
<syntaxhighlight lang="c++">
// デバイス発見時のイベント
connect(&discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, [](const QBluetoothDeviceInfo &device) {
    // デバイス発見時の処理
});
// スキャン完了時のイベント
connect(&discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, []() {
    // スキャン完了時の処理
});
// エラー発生時のイベント
connect(&discoveryAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred, [](QBluetoothDeviceDiscoveryAgent::Error error) {
    // エラー発生時の処理
});
</syntaxhighlight>
<br>
==== スキャンの開始 ====
スキャン中の場合は停止する。<br>
新しいスキャンを開始する。<br>
<br>
<br>
<syntaxhighlight lang="c++">
// アクティブなスキャンがある場合は停止
if (discoveryAgent.isActive()) {
    discoveryAgent.stop();
}
// スキャン開始
discoveryAgent.start();
</syntaxhighlight>
<br>
==== デバイス検出時 ====
検出したデバイスがBLEかどうかを確認する。
Bluetooth Classicの場合は、デバイス名、MACアドレスを取得する。
<br>
<syntaxhighlight lang="c++">
void handleDeviceDiscovered(const QBluetoothDeviceInfo &device)
{
    // BLEデバイスをスキップ
    if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
      return;
    }
    // デバイス情報の取得
    QString name    = device.name();
    QString address = device.address().toString();
    qDebug() << "Found device : " << name << address;
}
</syntaxhighlight>
<br>
==== スキャン完了時 ====
スキャンの完了を通知する。<br>
必要に応じてスキャンを再開または終了する。<br>
<br>
<syntaxhighlight lang="c++">
void handleScanFinished()
{
    qDebug() << "スキャン完了";
    // 必要に応じてスキャンを再開
    // discoveryAgent.start();
}
</syntaxhighlight>
<br>
==== エラー発生時 ====
Bluetooth Classicでは、以下に示すようなエラー処理がある。<br>
* Bluetoothの電源が入っていない。
* Bluetoothアダプタが見つからない。
* 入出力エラー
* その他のエラー
<br>
<syntaxhighlight lang="c++">
void handleError(QBluetoothDeviceDiscoveryAgent::Error error)
{
    switch (error) {
      case QBluetoothDeviceDiscoveryAgent::PoweredOffError:
          qDebug() << "Bluetoothの電源が入っていません";
          break;
      case QBluetoothDeviceDiscoveryAgent::InputOutputError:
          qDebug() << "I/Oエラー";
          break;
      case QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError:
          qDebug() << "Bluetoothアダプタが無効です";
          break;
      default:
          qDebug() << "不明なエラー";
          break;
    }
}
</syntaxhighlight>
<br>
==== 組み合わせ ====
  <syntaxhighlight lang="c++">
  <syntaxhighlight lang="c++">
  #include <QObject>
  #include <QObject>
  #include <QBluetoothDeviceDiscoveryAgent>
  #include <QBluetoothDeviceDiscoveryAgent>
  #include <memory>
  #include <QBluetoothDeviceInfo>
  #include <stdexcept>
  #include <stdexcept>
  #include <QDebug>
  #include <QDebug>
60行目: 267行目:
   
   
  private:
  private:
     std::unique_ptr<QBluetoothDeviceDiscoveryAgent> discoveryAgent;
     QBluetoothDeviceDiscoveryAgent discoveryAgent;
   
   
     // デバイスタイプを文字列に変換するヘルパー関数
     // デバイスタイプ
     QString deviceTypeToString(QBluetoothDeviceInfo::MajorDeviceClass type)
     QString deviceTypeToString(QBluetoothDeviceInfo::MajorDeviceClass type)
     {
     {
80行目: 287行目:
     }
     }
   
   
     // エラーコードを文字列に変換するヘルパー関数
     // エラーコード
     QString errorToString(QBluetoothDeviceDiscoveryAgent::Error error)
     QString errorToString(QBluetoothDeviceDiscoveryAgent::Error error)
     {
     {
95行目: 302行目:
   
   
  public:
  public:
     explicit BluetoothScanner(QObject* parent = nullptr) : QObject(parent)
     explicit BluetoothScanner(QObject *parent = nullptr) : QObject(parent)
     {
     {
       try {
       try {
113行目: 320行目:
     }
     }
   
   
     // スキャン開始メソッド
     // スキャンの開始
     void startScan()
     void startScan()
     {
     {
       try {
       try {
           qDebug() << "デバイススキャンを開始...";
           qDebug() << "デバイススキャンを開始...";
           discoveryAgent->start();
           discoveryAgent.start();
       }
       }
       catch (const std::exception &e) {
       catch (const std::exception &e) {
126行目: 333行目:
     }
     }
   
   
     // スキャン停止メソッド
     // スキャンの停止
     void stopScan()
     void stopScan()
     {
     {
       try {
       try {
           qDebug() << "デバイススキャンを停止...";
           qDebug() << "デバイススキャンを停止...";
           discoveryAgent->stop();
           discoveryAgent.stop();
       }
       }
       catch (const std::exception &e) {
       catch (const std::exception &e) {
141行目: 348行目:
  private slots:
  private slots:
     // デバイスが存在する場合のスロット
     // デバイスが存在する場合のスロット
     void onDeviceDiscovered(const QBluetoothDeviceInfo& device)
     void onDeviceDiscovered(const QBluetoothDeviceInfo &device)
     {
     {
       qDebug() << "デバイスの探索に成功:";
       qDebug() << "デバイスの探索に成功:";
       qDebug() << "  名前: "         << device.name();
       qDebug() << "  名前: "           << device.name();
       qDebug() << "  アドレス: "     << device.address().toString();
       qDebug() << "  アドレス: "       << device.address().toString();
       qDebug() << "  RSSI: "         << device.rssi();
       qDebug() << "  RSSI: "           << device.rssi();
       qDebug() << "  デバイスタイプ: " << deviceTypeToString(device.majorDeviceClass());
       qDebug() << "  デバイスタイプ: " << deviceTypeToString(device.majorDeviceClass());
     }
     }
158行目: 365行目:
     // エラー発生時のスロット
     // エラー発生時のスロット
     void onError(QBluetoothDeviceDiscoveryAgent::Error error)
     void onError(QBluetoothDeviceDiscoveryAgent::Error error)
    {
    {
         qDebug() << "エラーが発生: " << errorToString(error);
         qDebug() << "エラーが発生: " << errorToString(error);
     }
     }
172行目: 379行目:
各サービスはUUIDで識別されており、標準的なサービスには予め定義されたUUIDが割り当てられている。<br>
各サービスはUUIDで識別されており、標準的なサービスには予め定義されたUUIDが割り当てられている。<br>
また、カスタムサービスの場合は独自のUUIDを使用する。<br>
また、カスタムサービスの場合は独自のUUIDを使用する。<br>
<br>
Classic Bluetoothでは、<code>QBluetoothServiceInfo</code>クラスを使用して、単純なサービス情報を管理する。<br>
<br>
==== Bluetoothサービスディスカバリの準備 ====
<code>QBluetoothServiceDiscoveryAgent</code>クラスのインスタンスを生成する。<br>
<br>
以下に示すシグナルを接続する。
* QBluetoothServiceDiscoveryAgent::serviceDiscovered
*: 発見したサービスを通知する。
* QBluetoothServiceDiscoveryAgent::finished
*: 探索完了を通知する。
* QBluetoothServiceDiscoveryAgent::error
*: エラー発生時の通知
<br>
<br>
<syntaxhighlight lang="c++">
#include <QBluetoothServiceDiscoveryAgent>
#include <QDebug>
QBluetoothServiceDiscoveryAgent discoveryAgent;
// シグナルとスロットの接続
connect(&discoveryAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered, [](const QBluetoothServiceInfo &info) {
    qDebug() << "サービス発見";
});
connect(&discoveryAgent, &QBluetoothServiceDiscoveryAgent::finished, []() {
    qDebug() << "探索完了";
});
connect(&discoveryAgent, &QBluetoothServiceDiscoveryAgent::error, [](QBluetoothServiceDiscoveryAgent::Error error) {
    qDebug() << "エラー発生";
});
</syntaxhighlight>
<br>
==== サービスディスカバリの開始 ====
<code>QBluetoothAddress</code>クラスを使用して、対象デバイスのBluetoothアドレスを指定する。<br>
<br>
<code>QBluetoothServiceDiscoveryAgent::setRemoteAddress</code>メソッドを実行して、探索対象のデバイスを設定する。<br>
<code>QBluetoothServiceDiscoveryAgent::start</code>メソッドを実行して、ディスカバリを開始する。<br>
<br>
<syntaxhighlight lang="c++">
#include <QBluetoothAddress>
// デバイスのBluetoothアドレスを設定
QBluetoothAddress address("XX:XX:XX:XX:XX:XX");
discoveryAgent.setRemoteAddress(address);
// ディスカバリ開始
discoveryAgent.start(QBluetoothServiceDiscoveryAgent::FullDiscovery);
</syntaxhighlight>
<br>
==== サービスの発見時 ====
<code>QBluetoothServiceDiscoveryAgent::serviceDiscovered</code>シグナルで通知された<code>QBluetoothServiceInfo</code>クラスから必要な情報を取得する。<br>
<br>
QBluetoothServiceInfoクラスでは、以下に示す情報が取得できる。<br>
<br>
これらの情報は、Bluetoothデバイスとの接続やサービスの詳細な把握に役立つ。<br>
ただし、全ての情報が必ず使用可能とは限らないため、デバイスやサービスの種類によっては取得できる情報が異なることに注意する。<br>
<br>
* サービスの基本情報
** serviceDescription()
**: サービスの説明文
** serviceName()
**: サービス名
** serviceProvider()
**: サービスプロバイダ名
** serviceUuid()
**: サービスのUUID
** serviceAvailability()
**: サービスの可用性 (0x00~0xFF)
*: <br>
* プロトコル関連
** protocolServiceMultiplexer()
**: プロトコルサービス多重化子 (PSM) 値
** serverChannel()
**: RFCOMMサーバチャンネル番号
** socketProtocol()
**: 使用されているソケットプロトコル (RFCOMM / L2CAP)
*: <br>
* サービスクラス情報
** serviceClassUuids()
**: サービスクラスのUUIDリスト
** serviceProvider()
**: サービスプロバイダ情報
*: <br>
* 接続関連
** device()
**: サービスを提供しているデバイス情報
** isComplete()
**: サービス情報が完全かどうか
** isRegistered()
**: サービスが登録されているかどうか
** isValid()
**: サービス情報が有効かどうか
*: <br>
* 属性関連
** attributes()
**: 全ての属性のリスト
** contains(quint16 attributeId)
**: 特定の属性の存在確認
** attribute(quint16 attributeId)
**: 特定の属性値の取得
*: <br>
* プロファイル関連
** majorServiceClass()
**: メジャーサービスクラス (オーディオ、電話等)
** minorServiceClass()
**: マイナーサービスクラス (詳細なサービス種別)
<br>
<syntaxhighlight lang="c++">
connect(&discoveryAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered, [](const QBluetoothServiceInfo &info) {
    // サービス名を取得
    QString name = info.serviceName();
    // UUIDを取得
    QBluetoothUuid uuid = info.serviceUuid();
    // プロトコルを取得
    int protocol = info.protocolServiceMultiplexer();
    qDebug() << "サービス名: " << name;
    qDebug() << "UUID: "    << uuid.toString();
    qDebug() << "プロトコル: " << protocol;
});
</syntaxhighlight>
<br>
==== 探索完了 または エラー時 ====
<code>QBluetoothServiceDiscoveryAgent::finished</code>シグナルで探索完了を検知する。<br>
<br>
<code>QBluetoothServiceDiscoveryAgent::error</code>シグナルでエラーを検知する。<br>
<br>
必要に応じて、<code>QBluetoothServiceDiscoveryAgent::stop</code>メソッドで探索を終了することも可能である。<br>
<br>
<syntaxhighlight lang="c++">
// 完了時の処理
connect(&discoveryAgent, &QBluetoothServiceDiscoveryAgent::finished, []() {
    qDebug() << "探索が完了しました";
});
// エラー時の処理
connect(&discoveryAgent, &QBluetoothServiceDiscoveryAgent::error, [](QBluetoothServiceDiscoveryAgent::Error error) {
    switch (error) {
      case QBluetoothServiceDiscoveryAgent::NoError:
          qDebug() << "エラーなし";
          break;
      case QBluetoothServiceDiscoveryAgent::PoweredOffError:
          qDebug() << "Bluetoothがオフです";
          break;
      default:
          qDebug() << "その他のエラー";
    }
});
// 探索を途中で停止する場合
discoveryAgent.stop();
</syntaxhighlight>
<br>
==== 組み合わせ ====
  <syntaxhighlight lang="c++">
  <syntaxhighlight lang="c++">
  #include <QBluetoothServiceDiscoveryAgent>
  #include <QBluetoothServiceDiscoveryAgent>
  #include <QBluetoothAddress>
  #include <QBluetoothAddress>
  #include <QDebug>
  #include <QDebug>
#include <memory>
   
   
  class ServiceDiscovery : public QObject
  class ServiceDiscovery : public QObject
184行目: 547行目:
   
   
  private:
  private:
     std::unique_ptr<QBluetoothServiceDiscoveryAgent> discoveryAgent;
     QBluetoothServiceDiscoveryAgent discoveryAgent;
   
   
     // プロトコルタイプを文字列に変換するヘルパー関数
     // プロトコルタイプ
     QString protocolToString(int protocol)
     QString protocolToString(int protocol)
     {
     {
198行目: 561行目:
     }
     }
   
   
     // エラーコードを文字列に変換するヘルパー関数
     // エラーコード
     QString errorToString(QBluetoothServiceDiscoveryAgent::Error error)
     QString errorToString(QBluetoothServiceDiscoveryAgent::Error error)
     {
     {
212行目: 575行目:
   
   
  public:
  public:
     explicit ServiceDiscovery(QObject* parent = nullptr) : QObject(parent)
     explicit ServiceDiscovery(QObject* parent = nullptr) : QObject(parent), discoveryAgent(this)
     {
     {
       try {
       // 各種シグナルとスロットの接続
          // サービス探索エージェントの作成
      connect(&discoveryAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered, this, &ServiceDiscovery::onServiceDiscovered);
          discoveryAgent = std::make_unique<QBluetoothServiceDiscoveryAgent>(this);
      connect(&discoveryAgent, &QBluetoothServiceDiscoveryAgent::finished, this, &ServiceDiscovery::onScanFinished);
      connect(&discoveryAgent, static_cast<void(QBluetoothServiceDiscoveryAgent::*)(QBluetoothServiceDiscoveryAgent::Error)>(&QBluetoothServiceDiscoveryAgent::error),
          // 各種シグナルとスロットの接続
              this, &ServiceDiscovery::onError);
          connect(discoveryAgent.get(), &QBluetoothServiceDiscoveryAgent::serviceDiscovered, this, &ServiceDiscovery::onServiceDiscovered);
          connect(discoveryAgent.get(), &QBluetoothServiceDiscoveryAgent::finished, this, &ServiceDiscovery::onScanFinished);
          connect(discoveryAgent.get(), static_cast<void(QBluetoothServiceDiscoveryAgent::*)(QBluetoothServiceDiscoveryAgent::Error)>(&QBluetoothServiceDiscoveryAgent::error),
                  this, &ServiceDiscovery::onError);
      }
      catch (const std::exception &e) {
          qDebug() << "初期化エラー: " << e.what();
          throw;
      }
     }
     }
   
   
233行目: 587行目:
     void startDiscovery(const QBluetoothAddress& address)
     void startDiscovery(const QBluetoothAddress& address)
     {
     {
       try {
       qDebug() << "サービス探索を開始...";
          qDebug() << "サービス探索を開始...";
      discoveryAgent.setRemoteAddress(address);
          discoveryAgent->setRemoteAddress(address);
      discoveryAgent.start(QBluetoothServiceDiscoveryAgent::FullDiscovery);
          discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);
      }
      catch (const std::exception &e) {
          qDebug() << "探索開始エラー:" << e.what();
          throw;
      }
     }
     }
   
   
247行目: 595行目:
     void stopDiscovery()
     void stopDiscovery()
     {
     {
       try {
       qDebug() << "サービス探索を停止...";
          qDebug() << "サービス探索を停止...";
      discoveryAgent.stop();
          discoveryAgent->stop();
      }
      catch (const std::exception &e) {
          qDebug() << "探索停止エラー:" << e.what();
          throw;
      }
     }
     }
   
   
  private slots:
  private slots:
     // サービスの探索に成功した場合のスロット
     // サービスの探索に成功した場合
     void onServiceDiscovered(const QBluetoothServiceInfo& service)
     void onServiceDiscovered(const QBluetoothServiceInfo& service)
     {
     {
267行目: 609行目:
   
   
       // サービスの詳細情報を表示
       // サービスの詳細情報を表示
       if (service.serviceUuid().isNull()) {
       if (service.serviceUuid().isNull()) qDebug() << "  UUID: カスタムUUID";
          qDebug() << "  UUID: カスタムUUID";
       else                               qDebug() << "  UUID: " << service.serviceUuid().toString();
      }
       else {
      // サービスクラスを表示
          qDebug() << "  UUID: " << service.serviceUuid().toString();
      QList<QBluetoothUuid> serviceClasses = service.serviceClassUuids();
      if (!serviceClasses.isEmpty()) {
          qDebug() << "  サービスクラス:";
          for (const QBluetoothUuid& uuid : serviceClasses) {
            qDebug() << "    -" << uuid.toString();
          }
       }
       }
   
   
284行目: 631行目:
     }
     }
   
   
     // 探索完了時のスロット
     // 探索完了時
     void onScanFinished()
     void onScanFinished()
     {
     {
290行目: 637行目:
     }
     }
   
   
     // エラー発生時のスロット
     // エラー発生時
     void onError(QBluetoothServiceDiscoveryAgent::Error error)
     void onError(QBluetoothServiceDiscoveryAgent::Error error)
     {
     {
296行目: 643行目:
     }
     }
  };
  };
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// 使用方法
ServiceDiscovery discovery;
// 例: MACアドレスを指定してサービス探索を開始
QBluetoothAddress address("XX:XX:XX:XX:XX:XX");
discovery.startDiscovery(address);
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>
308行目: 665行目:
* ペアリング状態の管理 (新規ペアリング、ペアリング解除、ペアリング状態の確認等) が含まれる。
* ペアリング状態の管理 (新規ペアリング、ペアリング解除、ペアリング状態の確認等) が含まれる。
* 1度ペアリングされたデバイスは、特別な設定がない限り、自動的に再接続可能になる。
* 1度ペアリングされたデバイスは、特別な設定がない限り、自動的に再接続可能になる。
<br>
==== ペアリングの準備 ====
<code>QBluetoothLocalDevice</code>クラスのインスタンスを生成して、ローカルのBluetoothアダプタを初期化する。<br>
<br>
以下に示すペアリング関連のシグナルに対するスロットを接続する。<br>
* QBluetoothLocalDevice::pairingFinished
* QBluetoothLocalDevice::pairingDisplayConfirmation
* QBluetoothLocalDevice::pairingDisplayPinCode
* QBluetoothLocalDevice::error
<br>
これらの処理は非同期で行われており、シグナル / スロットメカニズムを通じて状態の変更が通知される。<br>
そのため、ユーザインターフェースと組み合わせる場合は、各シグナルに応じて適切なUIの更新を行う必要がある。<br>
<br>
<syntaxhighlight lang="c++">
#include <QBluetoothLocalDevice>
#include <QDebug>
QBluetoothLocalDevice localDevice;
// シグナルとスロットの接続
connect(&localDevice, &QBluetoothLocalDevice::pairingFinished, [](const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing status) {
    qDebug() << "ペアリング完了";
});
connect(&localDevice, &QBluetoothLocalDevice::pairingDisplayConfirmation, [](const QBluetoothAddress &address, QString pin) {
    qDebug() << "ペアリング確認:" << pin;
});
connect(&localDevice, &QBluetoothLocalDevice::pairingDisplayPinCode, [](const QBluetoothAddress &address, QString pin) {
    qDebug() << "PINコード:" << pin;
});
connect(&localDevice, &QBluetoothLocalDevice::error, []() {
    qDebug() << "エラー発生";
});
</syntaxhighlight>
<br>
==== ペアリング要求 ====
リモートデバイスのBluetoothアドレス (QBluetoothAddressクラス) を指定して、<code>QBluetoothLocalDevice::requestPairing</code>メソッドを実行する。<br>
この時、ペアリングモードとして<code>QBluetoothLocalDevice::Paired</code>を指定する。<br>
<br>
<syntaxhighlight lang="c++">
#include <QBluetoothAddress>
// デバイスのBluetoothアドレスを設定
QBluetoothAddress address("XX:XX:XX:XX:XX:XX");
// ペアリング開始
localDevice.requestPairing(address, QBluetoothLocalDevice::Paired);
</syntaxhighlight>
<br>
==== ペアリング処理中 ====
Bluetoothデバイスによっては、PINコードの表示や確認が必要になる。<br>
<br>
<code>QBluetoothLocalDevice::pairingDisplayPinCode</code>シグナルを受信した場合、ユーザにPINコードを表示する。<br>
<br>
<code>QBluetoothLocalDevice::pairingDisplayConfirmation</code>シグナルを受信した場合は、ユーザに確認を求めて、<code>QBluetoothLocalDevice::pairingConfirmation</code>メソッドで応答する。<br>
<br>
<syntaxhighlight lang="c++">
// ペアリング確認に応答
localDevice.pairingConfirmation(true);  // 承認する場合
localDevice.pairingConfirmation(false);  // 拒否する場合
</syntaxhighlight>
<br>
==== ペアリング完了 ====
<code>QBluetoothLocalDevice::pairingFinished</code>シグナルを受信して処理結果を確認する。<br>
<br>
エラーが発生した場合は、<code>QBluetoothLocalDevice::error</code>シグナルを受信する。<br>
<br>
<syntaxhighlight lang="c++">
// ペアリング結果の確認
#include <QBluetoothLocalDevice>
#include <QDebug>
// ペアリング完了シグナルの処理
connect(&localDevice, &QBluetoothLocalDevice::pairingFinished, [](const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing status) {
    switch (status) {
      case QBluetoothLocalDevice::Paired:
          qDebug() << "ペアリング成功:" << address.toString();
          break;
      case QBluetoothLocalDevice::Unpaired:
          qDebug() << "ペアリング解除:" << address.toString();
          break;
      default:
          qDebug() << "不明な状態:" << address.toString();
          break;
    }
});
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// エラーの確認
// QBluetoothLocalDevice::errorシグナルを受信してエラー発生を検知する
// エラーシグナルの処理
connect(&localDevice, &QBluetoothLocalDevice::error, []() {
    qDebug() << "ペアリングでエラーが発生しました";
});
</syntaxhighlight>
<br>
==== ペアリング管理 ====
<code>QBluetoothLocalDevice::connectedDevices</code>メソッドを実行して、ペアリング済みデバイスの一覧を取得することが可能である。<br>
また、<code>QBluetoothLocalDevice::pairingStatus</code>メソッドを実行して、特定デバイスのペアリング状態を確認できる。<br>
<br>
ペアリングを解除する場合は、ペアリングモードを<code>QBluetoothLocalDevice::Unpaired</code>に指定して<code>QBluetoothLocalDevice::requestPairing</code>メソッドを実行する。<br>
<br>
<syntaxhighlight lang="c++">
// ペアリング状態の確認
QBluetoothLocalDevice::Pairing status = localDevice.pairingStatus(address);
// ペアリング済みデバイスの取得
QList<QBluetoothAddress> devices = localDevice.connectedDevices();
</syntaxhighlight>
<br>
==== 組み合わせ ====
<syntaxhighlight lang="c++">
#include <QBluetoothLocalDevice>
#include <QDebug>
class PairingManager : public QObject
{
    Q_OBJECT
private:
    QBluetoothLocalDevice localDevice;
    // ペアリング状態
    QString pairingStatusToString(QBluetoothLocalDevice::Pairing status)
    {
      switch (status) {
          case QBluetoothLocalDevice::Unpaired:        return "未ペアリング";
          case QBluetoothLocalDevice::Paired:          return "ペアリング済み";
          case QBluetoothLocalDevice::AuthorizedPaired: return "認証済みペアリング";
          default:                                      return "不明な状態";
      }
    }
public:
    explicit PairingManager(QObject *parent = nullptr) : QObject(parent), localDevice(this)
    {
        // 各種シグナルとスロットの接続
        connect(&localDevice, &QBluetoothLocalDevice::pairingFinished, this, &PairingManager::onPairingFinished);
        connect(&localDevice, &QBluetoothLocalDevice::error, this, &PairingManager::onError);
        connect(&localDevice, &QBluetoothLocalDevice::pairingDisplayConfirmation, this, &PairingManager::onPairingConfirmationRequest);
        connect(&localDevice, &QBluetoothLocalDevice::pairingDisplayPinCode, this, &PairingManager::onPairingDisplayPinCode);
    }
    // ペアリングを開始
    void requestPairing(const QBluetoothAddress& address)
    {
      qDebug() << "ペアリングを開始..." << address.toString();
      localDevice.requestPairing(address, QBluetoothLocalDevice::Paired);
    }
    // ペアリングの解除
    void removePairing(const QBluetoothAddress& address)
    {
      qDebug() << "ペアリングを解除..." << address.toString();
      localDevice.requestPairing(address, QBluetoothLocalDevice::Unpaired);
    }
    // ペアリング状態の確認
    QBluetoothLocalDevice::Pairing getPairingStatus(const QBluetoothAddress &address)
    {
      return localDevice.pairingStatus(address);
    }
    // ペアリング済みデバイスの一覧を取得
    QList<QBluetoothAddress> getPairedDevices()
    {
      return localDevice.connectedDevices();
    }
private slots:
    // ペアリング完了時
    void onPairingFinished(const QBluetoothAddress& address, QBluetoothLocalDevice::Pairing status)
    {
      qDebug() << "ペアリング処理が完了:";
      qDebug() << "  デバイス:" << address.toString();
      qDebug() << "  状態:" << pairingStatusToString(status);
    }
    // エラー発生時
    void onError()
    {
        qDebug() << "ペアリング処理でエラーが発生";
    }
    // ペアリング確認要求時
    void onPairingConfirmationRequest(const QBluetoothAddress& address, QString pin)
    {
      qDebug() << "ペアリング確認要求を受信:";
      qDebug() << "  デバイス:" << address.toString();
      qDebug() << "  確認コード:" << pin;
      // ここに、ユーザに確認を求めるUIを表示する
      // 以下の例では、自動的に確認している
      localDevice.pairingConfirmation(true);
    }
    // PINコード表示要求時
    void onPairingDisplayPinCode(const QBluetoothAddress &address, QString pin)
    {
      qDebug() << "PINコード表示要求を受信:";
      qDebug() << "  デバイス:" << address.toString();
      qDebug() << "  PINコード:" << pin;
    }
};
</syntaxhighlight>
<br><br>
<br><br>


323行目: 890行目:
* 接続パラメータの最適化
* 接続パラメータの最適化
*: 電力消費、通信速度、接続の安定性等のバランスを取る。
*: 電力消費、通信速度、接続の安定性等のバランスを取る。
<br>
Classic Bluetoothでは、<code>QBluetoothSocket</code>クラスを使用して、<br>
<code>connect</code>メソッドでソケット接続、<code>write</code>メソッド / <code>read</code>メソッドでデータの送受信を行う。<br>
<br>
接続の確立と維持を行う処理は、全ての操作が非同期で行われる。<br>
そのため、各段階でのシグナル / スロット接続による状態管理が重要になる。<br>
<br>
==== 接続の確立 ====
<code>QBluetoothSocket</code>クラスのインスタンスを生成する。<br>
この時、RFCOMMプロトコルを指定する。<br>
<br>
デバイスのMACアドレスとポート番号を指定して、<code>QBluetoothSocket::connectToService</code>メソッドを実行する。<br>
接続状態の変化を監視するためのシグナル / スロット接続を設定する。<br>
<br>
<syntaxhighlight lang="c++">
#include <QBluetoothSocket>
#include <QDebug>
QBluetoothSocket socket(QBluetoothServiceInfo::RfcommProtocol);
connect(&socket, &QBluetoothSocket::connected, []() {
    qDebug() << "接続成功";
});
connect(&socket, &QBluetoothSocket::stateChanged, [](QBluetoothSocket::SocketState state) {
    qDebug() << "状態変更:" << state;
});
connect(&socket, &QBluetoothSocket::errorOccurred, [](QBluetoothSocket::SocketError error) {
    qDebug() << "エラー発生:" << error;
});
</syntaxhighlight>
<br>
RFCOMM (Radio Frequency Communication) とは、Bluetooth Classicで使用される重要なプロトコルの1つである。<br>
RFCOMMプロトコルの特徴を以下に示す。<br>
* シリアルポート通信をエミュレートするプロトコル
* RS-232C通信の代替として設計
* 信頼性の高い双方向通信を提供
* 最大60個の同時接続をサポート
<br>
Qtで使用可能な他のBluetoothプロトコルを以下に示す。<br>
* L2CAP (Logical Link Control and Adaptation Protocol)
*: 低レイヤーのプロトコル
*: RFCOMMの基盤となるプロトコル
*: より高速なデータ転送が可能
*: 生のデータパケット送受信に使用
*: <br>
* RFCOMM
*: 最も使用される。
*: シリアルポートエミュレーション
*: 多くのBluetooth機器で採用されている。
*: 多くのBluetooth通信では、使いやすさと互換性の高さからRFCOMMが選択される。
<br>
<syntaxhighlight lang="c++">
// RFCOMMプロトコルを指定する場合
// シリアル通信が必要な場合 あるいは 安定性が重要な場合
QBluetoothSocket socket(QBluetoothServiceInfo::RfcommProtocol);
// L2CAPプロトコルを指定する場合
// 高速な通信が必要な場合 あるいは 低レベルの制御が必要な場合
QBluetoothSocket socket(QBluetoothServiceInfo::L2capProtocol);
</syntaxhighlight>
<br>
==== 接続の維持 ====
<code>QBluetoothSocket::connected</code>シグナルを受信して、接続成功を確認する。<br>
<br>
<code>QBluetoothSocket::stateChanged</code>シグナルを監視して、接続状態の変化を検知する。<br>
<br>
<code>QBluetoothSocket::errorOccurred</code>シグナルを監視してエラーを検知する。<br>
<br>
また、必要に応じて定期的なキープアライブメッセージを送信する。<br>
<br>
<syntaxhighlight lang="c++">
QBluetoothAddress address("XX:XX:XX:XX:XX:XX");
socket.connectToService(address, <ポート番号  例: 1>);
</syntaxhighlight>
<br>
==== データの送信 ====
<code>QBluetoothSocket::write</code>メソッドを使用してデータを送信する。<br>
<br>
<syntaxhighlight lang="c++">
QByteArray sendData = "Hello";
socket.write(sendData);
</syntaxhighlight>
<br>
==== データの受信 ====
# <code>QBluetoothSocket::readyRead</code>シグナルを受信してデータの到着を検知する。
# <code>QBluetoothSocket::readAll</code>メソッド、あるいは、<code>QBluetoothSocket::read</code>メソッドでデータを読む。
<br>
<syntaxhighlight lang="c++">
connect(&socket, &QBluetoothSocket::readyRead, []() {
    QByteArray data = socket.readAll();
    qDebug() << "受信データ: " << data;
});
</syntaxhighlight>
<br>
==== 切断処理 ====
# <code>QBluetoothSocket::disconnectFromService</code>メソッドを実行して、接続を終了する。
# <code>QBluetoothSocket::disconnected</code>シグナルを受信して切断完了を確認する。
# 必要に応じて、リソースの解放を実施する。
<br>
<syntaxhighlight lang="c++">
connect(&socket, &QBluetoothSocket::disconnected, []() {
    qDebug() << "切断完了";
});
// 切断を実行
socket.disconnectFromService();
</syntaxhighlight>
<br>
==== 組み合わせ ====
<syntaxhighlight lang="c++">
#include <QBluetoothSocket>
#include <QDebug>
class BluetoothConnection : public QObject
{
    Q_OBJECT
private:
    QBluetoothSocket socket;
    // エラーコード
    QString errorToString(QBluetoothSocket::SocketError error)
    {
      switch (error) {
          case QBluetoothSocket::NoSocketError:        return "エラーなし";
          case QBluetoothSocket::UnknownSocketError:  return "不明なエラー";
          case QBluetoothSocket::HostNotFoundError:    return "ホストが見つかりません";
          case QBluetoothSocket::ServiceNotFoundError: return "サービスが見つかりません";
          case QBluetoothSocket::NetworkError:        return "ネットワークエラー";
          case QBluetoothSocket::OperationError:      return "操作エラー";
          default:                                    return "予期せぬエラー";
      }
    }
    // 接続状態
    QString stateToString(QBluetoothSocket::SocketState state)
    {
      switch (state) {
          case QBluetoothSocket::UnconnectedState:  return "未接続";
          case QBluetoothSocket::ConnectingState:    return "接続中";
          case QBluetoothSocket::ConnectedState:    return "接続済み";
          case QBluetoothSocket::BoundState:        return "バインド済み";
          case QBluetoothSocket::ClosingState:      return "切断中";
          case QBluetoothSocket::ServiceLookupState: return "サービス検索中";
          default:                                  return "不明な状態";
      }
    }
public:
    explicit BluetoothConnection(QObject *parent = nullptr) : QObject(parent), socket(QBluetoothServiceInfo::RfcommProtocol, this)
    {
      // 各種シグナルとスロットの接続
      connect(&socket, &QBluetoothSocket::connected, this, &BluetoothConnection::onConnected);
      connect(&socket, &QBluetoothSocket::disconnected, this, &BluetoothConnection::onDisconnected);
      connect(&socket, &QBluetoothSocket::errorOccurred, this, &BluetoothConnection::onError);
      connect(&socket, &QBluetoothSocket::readyRead, this, &BluetoothConnection::onDataReceived);
      connect(&socket, &QBluetoothSocket::stateChanged, this, &BluetoothConnection::onStateChanged);
    }
    ~BluetoothConnection()
    {
      disconnect();
    }
    // デバイスに接続
    void connectToDevice(const QBluetoothAddress &address, quint16 port)
    {
      if (socket.state() == QBluetoothSocket::ConnectedState) {
          qDebug() << "既に接続済み";
          return;
      }
      qDebug() << "デバイスに接続します: " << address.toString();
      qDebug() << "ポート: " << port;
      socket.connectToService(address, port);
    }
    // 接続を切断
    void disconnect()
    {
      if (socket.state() != QBluetoothSocket::UnconnectedState) {
          qDebug() << "接続を切断...";
          socket.disconnectFromService();
      }
    }
    // データを送信
    bool sendData(const QByteArray &data)
    {
      if (socket.state() != QBluetoothSocket::ConnectedState) {
          qDebug() << "送信エラー: 接続されていません";
          return false;
      }
      qint64 bytesWritten = socket.write(data);
      if (bytesWritten == -1) {
          qDebug() << "送信エラー: データの書き込みに失敗";
          return false;
      }
      qDebug() << bytesWritten << "バイトデータの送信完了";
      return true;
    }
private slots:
    // 接続確立時
    void onConnected()
    {
      qDebug() << "接続が確立";
      qDebug() << "  ローカルアドレス:" << socket.localAddress().toString();
      qDebug() << "  リモートアドレス:" << socket.peerAddress().toString();
    }
    // 切断時
    void onDisconnected()
    {
      qDebug() << "接続が切断";
    }
    // エラー発生時
    void onError(QBluetoothSocket::SocketError error)
    {
      qDebug() << "エラーが発生: " << errorToString(error);
    }
    // データ受信時
    void onDataReceived()
    {
      QByteArray data = socket.readAll();
      qDebug() << "データを受信: " << data.size() << "バイト";
      // ここでデータを処理する
      // ...略
    }
    // 接続状態変更時
    void onStateChanged(QBluetoothSocket::SocketState state)
    {
      qDebug() << "接続状態が変更: " << stateToString(state);
    }
};
</syntaxhighlight>
<br><br>
<br><br>