「Qtの基礎 - Bluetooth Low Energy」の版間の差分

ナビゲーションに移動 検索に移動
編集の要約なし
90行目: 90行目:
* パッシブスキャン
* パッシブスキャン
<br>
<br>
BLEデバイスのスキャン中は、<u>アドバタイズメント</u>と呼ばれる特別なブロードキャストパケットを発信する。<br>
BLEデバイスのスキャン中は、<br>
このアドバタイズメントパケットには、以下に示す情報が含まれる。<br>
<u>BLEペリフェラル (アドバタイザ) デバイスがアドバタイズメントと呼ばれる特別なブロードキャストパケットを定期的に発信して</u>、セントラル (スキャナ) デバイスがそれをスキャンする。<br>
スキャンを行うのはセントラル側、アドバタイズメントパケットを発信するのはペリフェラル側である。<br>
<br>
このアドバタイジングパケットには、以下に示す情報が含まれる。<br>
* デバイス名
* デバイス名
* メーカー固有データ
* メーカー固有データ
* 提供するサービスのUUID
* 提供するサービスのUUID
* 電波強度 (RSSI)
* MACアドレス (デバイスアドレス)
* その他のカスタムデータ
* その他のカスタムデータ
<br>
<br>
また、スキャン時間や範囲を設定可能であり、バッテリー消費とスキャン精度のバランスを取ることができる。<br>
また、スキャン時間や範囲を設定可能であり、バッテリー消費とスキャン精度のバランスを取ることができる。<br>
アドバタイジング間隔は、20ミリ秒~10.24秒の範囲で設定可能である。<br>
<br>
<br>
<u>BLEでは、同様の<code>QBluetoothDeviceDiscoveryAgent</code>クラスを使用するが、<code>LowEnergyDiscoveryTimeout</code>の設定が必要となる。</u><br>
<u>BLEでは、同様の<code>QBluetoothDeviceDiscoveryAgent</code>クラスを使用するが、<code>LowEnergyDiscoveryTimeout</code>の設定が必要となる。</u><br>
また、フィルタリングでBLEデバイスのみを検出する。<br>
また、フィルタリングでBLEデバイスのみを検出する。<br>
<br>
==== アドバタイジングパケットの種類 ====
* ADV_IND
*: 一般的な接続可能なアドバタイジング
* ADV_DIRECT_IND
*: 特定のデバイスのみに向けたアドバタイジング
* ADV_NONCONN_IND
*: 接続不可の通知専用
* ADV_SCAN_IND
*: スキャン応答が可能な通知専用
* ADV_EXT_IND
*: 拡張アドバタイジング (Bluetooth 5.0以降)
<br>
==== データ構造 ====
* PDU Header (2バイト)
** アドバタイジングタイプ
** TxAddressタイプ
** RxAddressタイプ
* アドバタイジングアドレス (6バイト)
* アドバタイジングデータ (最大31バイト)
<br>
==== アドバタイジングデータの主要要素 ====
* Length (1バイト)
* AD Type (1バイト)
** 0x01
**: Flags
** 0x09
**: Complete Local Name
** 0xFF
**: Manufacturer Specific Data
** 0x03
**: Complete List of 16-bit Service UUIDs
* AD Data (可変長)
<br>
==== その他 (通信特性 / セキュリティ等) ====
* 通信特性
** アドバタイジング間隔
**: 20ミリ秒~10.24秒
** チャンネル
**: 37, 38, 39の3チャンネル
** 送信電力
**: -20[dBm]~+4[dBm] (地域規制による)
<br>
* セキュリティ
** プライバシー保護のためのランダムアドレス
** アドバタイジングデータの暗号化オプション
** ホワイトリストによるフィルタリング機能
<br>
* スキャン応答
** SCAN_REQ
**: スキャナからの追加情報要求
** SCAN_RSP
**: アドバタイザーからの応答 (最大31バイト)
<br>
==== 使用例 ====
<syntaxhighlight lang="c++">
#include <QObject>
#include <QBluetoothDeviceDiscoveryAgent>
#include <QBluetoothDeviceInfo>
#include <memory>
#include <QTimer>
#include <QDebug>
class BLEDeviceScanner : public QObject
{
    Q_OBJECT
private:
    std::unique_ptr<QBluetoothDeviceDiscoveryAgent> discoveryAgent;
    std::unique_ptr<QTimer> rescanTimer;
    bool isContinuousScan = false;
    void connectSignals()
    {
      // デバイス探索エージェントのシグナル接続
      connect(discoveryAgent.get(), &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BLEDeviceScanner::onDeviceDiscovered);
      connect(discoveryAgent.get(), &QBluetoothDeviceDiscoveryAgent::finished, this, &BLEDeviceScanner::onScanFinished);
      connect(discoveryAgent.get(), static_cast<void(QBluetoothDeviceDiscoveryAgent::*)(QBluetoothDeviceDiscoveryAgent::Error)>(&QBluetoothDeviceDiscoveryAgent::error),
              this, &BLEDeviceScanner::onError);
      // 再スキャンタイマのシグナル接続
      connect(rescanTimer.get(), &QTimer::timeout, this, [this]() {
          if (isContinuousScan) {
            discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
          }
      });
    }
    QString getErrorMessage(QBluetoothDeviceDiscoveryAgent::Error error)
    {
      switch (error) {
          case QBluetoothDeviceDiscoveryAgent::NoError:                      return "エラーなし";
          case QBluetoothDeviceDiscoveryAgent::InputOutputError:            return "Bluetooth IOエラー";
          case QBluetoothDeviceDiscoveryAgent::PoweredOffError:              return "Bluetoothがオフ";
          case QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError: return "無効なBluetoothアダプタ";
          case QBluetoothDeviceDiscoveryAgent::UnsupportedPlatformError:    return "プラットフォームがサポートされていない";
          case QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod:  return "未対応の探索方法";
          default:                                                          return "不明なエラー";
      }
    }
public:
    explicit BLEDeviceScanner(QObject* parent = nullptr) : QObject(parent)
    {
      // デバイス探索エージェントの初期化
      discoveryAgent = std::make_unique<QBluetoothDeviceDiscoveryAgent>(this);
      // BLEデバイスのみをスキャンするように設定
      discoveryAgent->setLowEnergyDiscoveryTimeout(10000);  // 10秒のタイムアウト
      // 自動再スキャンタイマの設定
      rescanTimer = std::make_unique<QTimer>(this);
      rescanTimer->setInterval(30000);  // 30秒間隔で再スキャン
      connectSignals();
    }
    // スキャンを開始
    void startScan(bool continuous = false)
    {
      try {
          qDebug() << "BLEデバイススキャンを開始...";
          isContinuousScan = continuous;
          discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
          if (continuous) rescanTimer->start();
          emit scanStarted();
      }
      catch (const std::exception &e) {
          QString errorMsg = QString("スキャン開始エラー: %1").arg(e.what());
          qDebug() << errorMsg;
          emit errorOccurred(errorMsg);
      }
    }
    // スキャンを停止
    void stopScan()
    {
      try {
          qDebug() << "BLEデバイススキャンを停止...";
          discoveryAgent->stop();
          rescanTimer->stop();
          isContinuousScan = false;
          emit scanStopped();
      }
      catch (const std::exception &e) {
          QString errorMsg = QString("スキャン停止エラー: %1").arg(e.what());
          qDebug() << errorMsg;
          emit errorOccurred(errorMsg);
      }
    }
signals:
    void deviceDiscovered(const QBluetoothDeviceInfo& device);
    void scanStarted();
    void scanStopped();
    void scanFinished();
    void errorOccurred(const QString& error);
private slots:
    void onDeviceDiscovered(const QBluetoothDeviceInfo& device)
    {
      // BLEデバイスのみを処理
      if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
          qDebug() << "BLEデバイスを発見:";
          qDebug() << "  名前:" << device.name();
          qDebug() << "  アドレス:" << device.address().toString();
          qDebug() << "  RSSI:" << device.rssi();
          // アドバタイズメントデータの解析
          const QList<QBluetoothUuid> serviceUuids = device.serviceUuids();
          if (!serviceUuids.isEmpty()) {
            qDebug() << "  提供サービス:";
            for (const QBluetoothUuid& uuid : serviceUuids) {
                qDebug() << "    -" << uuid.toString();
            }
          }
          // マニファクチャラーデータの解析
          const QMap<quint16, QByteArray> manufacturerData = device.manufacturerData();
          if (!manufacturerData.isEmpty()) {
            qDebug() << "  マニファクチャラーデータ:";
            QMap<quint16, QByteArray>::const_iterator i = manufacturerData.constBegin();
            while (i != manufacturerData.constEnd()) {
                qDebug() << "    ID: " << i.key() << "データ: " << i.value().toHex();
                ++i;
            }
          }
          emit deviceDiscovered(device);
      }
    }
    void onScanFinished()
    {
      qDebug() << "スキャンが完了";
      emit scanFinished();
      // 継続的スキャンの場合は再開
      if (isContinuousScan) {
          QTimer::singleShot(1000, this, [this]() {
            discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
          });
      }
    }
    void onError(QBluetoothDeviceDiscoveryAgent::Error error)
    {
      QString errorMessage = getErrorMessage(error);
      qDebug() << "スキャンエラー: " << errorMessage;
      emit errorOccurred(errorMessage);
      // エラーからの自動復帰を試みる (5秒後にBLEデバイスの検出を開始)
      if (isContinuousScan) {
          QTimer::singleShot(5000, this, [this]() {
            discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
          });
      }
    }
};
</syntaxhighlight>
<br><br>
<br><br>


案内メニュー