ブルートゥース・ロー・エナジー・スキャナー

Bluetooth Low Energy 周辺機器のコンテンツをブラウズするためのアプリケーションです。このサンプルでは、すべてのQt BluetoothLow Energy クラスの使用を示しています。

Bluetooth Low Energy Scanner Example は、Qt Bluetooth API を使用して Bluetooth Low Energy アプリケーションを開発する方法を示しています。 このアプリケーションでは、Low Energy デバイスのスキャン、サービスのスキャン、サービスの特性と記述子の読み取りを行います。

この例では、以下の Qt クラスを紹介します:

このサンプルは、任意の Bluetooth Low Energy 周辺デバイスで使用できます。すべてのサービス、特性、記述子のスナップショットを作成し、ユーザーに提示します。したがって、アプリケーションは周辺機器によって提供されるコンテンツを閲覧する簡単な方法を提供します。

例の実行

Qt Creator からサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、Building and Running an Example を参照してください。

Bluetooth の使用許可のリクエスト

特定のプラットフォームでは、Bluetooth の使用許可を明示的に与える必要があります。この例では、BluetoothPermission QMLオブジェクトを使用し、必要であればパーミッションのチェックとリクエストを行っています:

BluetoothPermission {
    id: permission
    communicationModes: BluetoothPermission.Access
    onStatusChanged: {
        if (permission.status === Qt.PermissionStatus.Denied)
            Device.update = "Bluetooth permission required"
        else if (permission.status === Qt.PermissionStatus.Granted)
            devicesPage.toggleDiscovery()
    }
}

許可要求ダイアログは、ユーザーがデバイス探索を開始しようとしたときにトリガーされ、許可ステータスはUndetermined です:

onButtonClick: {
    if (permission.status === Qt.PermissionStatus.Undetermined)
        permission.request()
    else if (permission.status === Qt.PermissionStatus.Granted)
        devicesPage.toggleDiscovery()
}

ユーザーによって許可が与えられた場合、デバイスの検出が開始されます。それ以外の場合、アプリケーションは機能しません。

デバイスのスキャン

最初のステップは、すべての周辺デバイスを見つけることです。デバイスは、QBluetoothDeviceDiscoveryAgent クラスを使用して見つけることができます。検出プロセスはstart() を使って開始される。各新規デバイスはdeviceDiscovered() シグナルで通知される:

discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
discoveryAgent->setLowEnergyDiscoveryTimeout(25000);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
        this, &Device::addDevice);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred,
        this, &Device::deviceScanError);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished,
        this, &Device::deviceScanFinished);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled,
        this, &Device::deviceScanFinished);
discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);

以下のaddDevice() スロットは、新しいデバイスの発見に対する反応としてトリガーされる。これは、QBluetoothDeviceInfo::LowEnergyCoreConfiguration フラグを持つすべての発見されたデバイスをフィルターし、ユーザーに表示されるリストに追加する。deviceDiscovered() シグナルは、詳細が発見されるにつれて、同じデバイスに対して複数回発せられることがある。ここでは、ユーザーが個々のデバイスだけを見ることができるように、これらのデバイスの発見を照合する:

void Device::addDevice(const QBluetoothDeviceInfo &info)
{
    if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
        auto devInfo = new DeviceInfo(info);
        auto it = std::find_if(devices.begin(), devices.end(),
                               [devInfo](DeviceInfo *dev) {
                                   return devInfo->getAddress() == dev->getAddress();
                               });
        if (it == devices.end()) {
            devices.append(devInfo);
        } else {
            auto oldDev = *it;
            *it = devInfo;
            delete oldDev;
        }
        emit devicesUpdated();
    }
}

デバイスのリストは以下の画像のようになります。

注: リモート・デバイスが積極的にその存在をアドバタイズすることが前提条件です。

サービスへの接続

ユーザーがリストからデバイスを選択すると、アプリケーションはデバイスに接続し、すべてのサービスをスキャンします。デバイスへの接続にはQLowEnergyController クラスが使用されます。QLowEnergyController::connectToDevice ()関数が接続プロセスのトリガーとなり、QLowEnergyController::connected ()シグナルを受信するか、エラーが発生するまで続きます:

if (!controller) {
    // Connecting signals and slots for connecting to LE services.
    controller = QLowEnergyController::createCentral(currentDevice.getDevice(), this);
    connect(controller, &QLowEnergyController::connected,
            this, &Device::deviceConnected);
    connect(controller, &QLowEnergyController::errorOccurred, this, &Device::errorReceived);
    connect(controller, &QLowEnergyController::disconnected,
            this, &Device::deviceDisconnected);
    connect(controller, &QLowEnergyController::serviceDiscovered,
            this, &Device::addLowEnergyService);
    connect(controller, &QLowEnergyController::discoveryFinished,
            this, &Device::serviceScanDone);
}

if (isRandomAddress())
    controller->setRemoteAddressType(QLowEnergyController::RandomAddress);
else
    controller->setRemoteAddressType(QLowEnergyController::PublicAddress);
controller->connectToDevice();

connected() シグナルによってトリガされたスロットは、直ちにQLowEnergyController::discoverServices() を呼び出し、接続された周辺デバイスのサービス検出を開始する。

controller->discoverServices();

下の画像は、SensorTagデバイスが選択されたときの結果を表示しています。ビューには、サービス名、プライマリサービスかセカンダリサービスか、サービスタイプを決定するUUIDが一覧表示されます。

サービスが選択されるとすぐに、関連するQLowEnergyService インスタンスが作成され、サービスとの対話が可能になります:

QLowEnergyService *service = controller->createServiceObject(serviceUuid);
if (!service) {
    qWarning() << "Cannot create service for uuid";
    return;
}

サービスオブジェクトは、サービスの詳細、特性の読み取りと書き込み、記述子を発見し、データ変更通知を受信するために必要な信号と関数を提供します。変更通知は、値の書き込みの結果、または内部ロジックによって潜在的にトリガされるデバイス上の更新によってトリガされることがあります。最初の詳細検索中、サービスのstate ()はRemoteService からRemoteServiceDiscovering に遷移し、最終的にRemoteServiceDiscovered で終了する:

connect(service, &QLowEnergyService::stateChanged,
        this, &Device::serviceDetailsDiscovered);
service->discoverDetails();
setUpdate(u"Back\n(Discovering details...)"_s);

サービスデータの読み込み

サービスを選択すると、サービスの詳細が表示されます。各特性は、名前、UUID、値、ハンドル、プロパティとともにリストされます。

QLowEnergyService::characteristics ()でサービスの特性を取得することができ、QLowEnergyCharacteristic::descriptors ()で各ディスクリプタを取得することができる。

const QList<QLowEnergyCharacteristic> chars = service->characteristics();
for (const QLowEnergyCharacteristic &ch : chars) {
    auto cInfo = new CharacteristicInfo(ch);
    m_characteristics.append(cInfo);
}

サンプルアプリケーションは記述子を表示しないが、UUIDに基づいて名前が識別できない場合、個々の特性の名前を取得するために記述子を使用する。名前を取得する2つ目の方法は、QBluetoothUuid::DescriptorType::CharacteristicUserDescription 型の記述子の存在である。以下のコードは、これを実現する方法を示している:

QString name = m_characteristic.name();
if (!name.isEmpty())
    return name;

// find descriptor with CharacteristicUserDescription
const QList<QLowEnergyDescriptor> descriptors = m_characteristic.descriptors();
for (const QLowEnergyDescriptor &descriptor : descriptors) {
    if (descriptor.type() == QBluetoothUuid::DescriptorType::CharacteristicUserDescription) {
        name = descriptor.value();
        break;
    }
}

プロジェクト例 @ code.qt.io

本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。