12,796
回編集
編集の要約なし |
|||
90行目: | 90行目: | ||
* パッシブスキャン | * パッシブスキャン | ||
<br> | <br> | ||
BLEデバイスのスキャン中は、<u> | BLEデバイスのスキャン中は、<br> | ||
<u>BLEペリフェラル (アドバタイザ) デバイスがアドバタイズメントと呼ばれる特別なブロードキャストパケットを定期的に発信して</u>、セントラル (スキャナ) デバイスがそれをスキャンする。<br> | |||
スキャンを行うのはセントラル側、アドバタイズメントパケットを発信するのはペリフェラル側である。<br> | |||
<br> | |||
このアドバタイジングパケットには、以下に示す情報が含まれる。<br> | |||
* デバイス名 | * デバイス名 | ||
* メーカー固有データ | * メーカー固有データ | ||
* 提供するサービスのUUID | * 提供するサービスのUUID | ||
* | * 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> | ||