ブルートゥース・ロー・エナジーの概要

Qt Bluetooth Low Energy API は、ペリフェラル/サーバーおよびセントラル/クライアントの役割をサポートします。すべての主要な Qt プラットフォームでサポートされています。唯一の例外は、Windows でペリフェラルの役割がサポートされていないことです。

Bluetooth Low Energyとは

Bluetooth Smartとしても知られるBluetooth Low Energyは、2011年に正式に発表されたワイヤレス・コンピュータ・ネットワーク技術です。従来の」Bluetoothと同じ2.4GHzの周波数で動作する。主な違いは、技術名にもあるように、エネルギー消費が少ないことだ。この技術により、Bluetooth Low Energyデバイスは、コイン電池で数ヶ月、あるいは数年間動作する機会を得ることができる。この技術はBluetooth v4.0で導入された。この技術をサポートする機器はBluetooth Smart Ready機器と呼ばれる。この技術の主な特徴は以下の通り:

  • 超低ピーク、平均、アイドルモード消費電力
  • 標準的なコイン電池で数年間動作可能
  • 低コスト
  • マルチベンダーの相互運用性
  • 拡張レンジ

Bluetooth Low Energyはクライアント・サーバー・アーキテクチャを採用しています。サーバー(周辺機器とも呼ばれる)は、温度や心拍数などのサービスを提供し、それらを広告します。クライアント(中央デバイスとして知られる)はサーバーに接続し、サーバーが広告した値を読み取ります。例えば、サーモスタット、湿度センサー、圧力センサーなどのBluetooth Smart Readyセンサーが設置されたアパートがある。これらのセンサーは、アパートの環境値を広告する周辺機器である。同時に、携帯電話やコンピューターがこれらのセンサーに接続し、その値を取得し、より大きな環境制御アプリケーションの一部としてユーザーに提示するかもしれない。

基本サービス構造

Bluetooth Low Energyは2つのプロトコルに基づいています:ATT(Attribute Protocol)とGATT(Generic Attribute Profile)です。これらは、すべてのBluetooth Smart Readyデバイスで使用される通信レイヤーを規定しています。

ATTプロトコル

ATTの基本構成要素は属性です。各属性は3つの要素で構成されています:

  • 値 - ペイロードあるいは望ましい情報の一部
  • UUID - 属性のタイプ(GATTによって使われます)。
  • 16ビットのハンドル-属性の一意識別子

サーバーは属性を保存し、クライアントはATTプロトコルを使ってサーバー上の値を読み書きする。

GATTプロファイル

GATTは、事前に定義されたUUIDに意味を適用することによって、一連の属性のグループ化を定義します。下の表は、特定の日の心拍数を公開するサービスの例を示している。実際の値は2つの特性の中に格納される:

ハンドルUUID説明
0x00010x2800UUID 0x180D心拍数サービス開始
0x00020x2803UUID 0x2A37、値ハンドル:0x0003心拍計測(HRM)タイプの特性
0x00030x2A3765 bpm心拍数値
0x00040x2803UUID 0x2A08、値ハンドル:0x0005タイプ特性 日付 時間
0x00050x2A0818/08/2014 11:00測定日時
0x00060x2800UUID xxxxxx次のサービスを開始する
............

GATTは、上記で使用されたUUID0x2800 がサービス定義の開始を示すと規定している。0x2800 に続くすべての属性は、次の0x2800 または終了に出会うまで、サービスの一部です。よく知られたUUID0x2803 は、同様の方法で、特性が見つかることを示し、特性の各々は、値の性質を定義する型を持つ。上記の例では、0x2A08 (Date Time)と0x2A37 (Heart Rate Measurement)というUUIDを使用している。上記の各UUIDはBluetooth Special Interest Groupによって定義されており、GATT仕様に記載されています。利用可能な場合は、事前に定義されたUUIDを使用することが望ましいですが、特性やサービスタイプに新しいUUIDやまだ使用されていないUUIDを使用することは十分に可能です。

一般に、各サービスは1つ以上の特性から構成される。特性はデータを含み、追加情報または特性を操作する手段を提供する記述子によってさらに記述することができる。すべてのサービス、特性、および記述子は、128ビットのUUIDによって認識される。最後に、サービスの中にサービスを含めることが可能です(下図参照)。

Qt Bluetooth 低エネルギーAPIの使用

このセクションでは、Qt が提供する Bluetooth Low Energy API の使用方法について説明します。クライアント側では、API は周辺機器への接続を作成し、そのサービスを検出し、機器に保存されたデータを読み書きすることができます。サーバー側では、サービスのセットアップ、広告、クライアントが特性を書き込んだときの通知を受けることができます。以下のコード例は、Heart Rate Gameと Heart Rate Serverの例から引用しています。

接続の確立

Bluetooth Low Energy周辺デバイスの特性を読み書きできるようにするには、デバイスを見つけて接続する必要があります。これには周辺デバイスがその存在とサービスをアドバタイズする必要があります。QBluetoothDeviceDiscoveryAgent クラスの助けを借りてデバイス検出を開始します。QBluetoothDeviceDiscoveryAgent::deviceDiscovered() シグナルに接続し、start() で検索を開始します:

m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
m_deviceDiscoveryAgent->setLowEnergyDiscoveryTimeout(15000);

connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
        this, &DeviceFinder::addDevice);
connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred,
        this, &DeviceFinder::scanError);

connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished,
        this, &DeviceFinder::scanFinished);
connect(m_deviceDiscoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled,
        this, &DeviceFinder::scanFinished);
m_deviceDiscoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);

低エネルギー・デバイスにしか興味がないので、受信スロット内のデバイス・タイプをフィルタリングする。デバイス・タイプは、QBluetoothDeviceInfo::coreConfigurations ()フラグで確認できる。deviceDiscovered() シグナルは、詳細が発見されると、同じデバイスに対して複数回発せられることがある。ここでは、ユーザーが個々のデバイスだけを見ることができるように、これらのデバイスの発見を照合します:

void DeviceFinder::addDevice(const QBluetoothDeviceInfo &device)
{
    // If device is LowEnergy-device, add it to the list
    if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
        auto devInfo = new DeviceInfo(device);
        auto it = std::find_if(m_devices.begin(), m_devices.end(),
                               [devInfo](DeviceInfo *dev) {
                                   return devInfo->getAddress() == dev->getAddress();
                               });
        if (it == m_devices.end()) {
            m_devices.append(devInfo);
        } else {
            auto oldDev = *it;
            *it = devInfo;
            delete oldDev;
        }
        setInfo(tr("Low Energy device found. Scanning more..."));
        setIcon(IconProgress);
    }
    //...
}

周辺デバイスのアドレスがわかったら、QLowEnergyController クラスを使用します。このクラスはすべてのBluetooth Low Energy開発のエントリーポイントです。このクラスのコンストラクタは、リモート・デバイスのQBluetoothAddress を受け入れます。最後に、恒例のスロットを設定し、connectToDevice ()を使用してデバイスに直接接続します:

m_control = QLowEnergyController::createCentral(m_currentDevice->getDevice(), this);
connect(m_control, &QLowEnergyController::serviceDiscovered,
        this, &DeviceHandler::serviceDiscovered);
connect(m_control, &QLowEnergyController::discoveryFinished,
        this, &DeviceHandler::serviceScanDone);

connect(m_control, &QLowEnergyController::errorOccurred, this,
        [this](QLowEnergyController::Error error) {
            Q_UNUSED(error);
            setError("Cannot connect to remote device.");
            setIcon(IconError);
        });
connect(m_control, &QLowEnergyController::connected, this, [this]() {
    setInfo("Controller connected. Search services...");
    setIcon(IconProgress);
    m_control->discoverServices();
});
connect(m_control, &QLowEnergyController::disconnected, this, [this]() {
    setError("LowEnergy controller disconnected");
    setIcon(IconError);
});

// Connect
m_control->connectToDevice();

上記のコード・スニペットは、接続が確立された後、アプリケーションがどのようにサービス・ディスカバリーを開始するかを示している。

以下のserviceDiscovered() スロットは、QLowEnergyController::serviceDiscovered ()シグナルの結果としてトリガーされ、断続的な進捗レポートを提供します。ここでは、近隣の HeartRate デバイスを監視するハートリスナーアプリについて話しているため、QBluetoothUuid::ServiceClassUuid::HeartRate 型以外のサービスは無視します。

void DeviceHandler::serviceDiscovered(const QBluetoothUuid &gatt)
{
    if (gatt == QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::HeartRate)) {
        setInfo("Heart Rate service discovered. Waiting for service scan to be done...");
        setIcon(IconProgress);
        m_foundHeartRateService = true;
    }
}

最終的に、QLowEnergyController::discoveryFinished ()シグナルが発行され、サービス発見が正常に完了したことを示します。HeartRateサービスが見つかった場合、そのサービスを表すQLowEnergyService インスタンスが作成されます。返されたサービスオブジェクトは、更新通知に必要なシグナルを提供し、QLowEnergyService::discoverDetails() を使用してサービス詳細の発見がトリガーされる:

    // If heartRateService found, create new service
    if (m_foundHeartRateService)
        m_service = m_control->createServiceObject(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::HeartRate), this);

    if (m_service) {
        connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged);
        connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateHeartRateValue);
        connect(m_service, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::confirmedDescriptorWrite);
        m_service->discoverDetails();
    } else {
        setError("Heart Rate Service not found.");
        setIcon(IconError);
    }

詳細検索中、サービスのstate() はRemoteService からRemoteServiceDiscovering へと遷移し、最終的にRemoteServiceDiscovered で終了する:

void DeviceHandler::serviceStateChanged(QLowEnergyService::ServiceState s)
{
    switch (s) {
    case QLowEnergyService::RemoteServiceDiscovering:
        setInfo(tr("Discovering services..."));
        setIcon(IconProgress);
        break;
    case QLowEnergyService::RemoteServiceDiscovered:
    {
        setInfo(tr("Service discovered."));
        setIcon(IconBluetooth);

        const QLowEnergyCharacteristic hrChar =
                m_service->characteristic(QBluetoothUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement));
        if (!hrChar.isValid()) {
            setError("HR Data not found.");
            setIcon(IconError);
            break;
        }

        m_notificationDesc = hrChar.descriptor(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration);
        if (m_notificationDesc.isValid())
            m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));

        break;
    }
    default:
        //nothing for now
        break;
    }

    emit aliveChanged();
}

周辺機器とのインタラクション

上記のコード例では、必要な特性はHeartRateMeasurement 型です。アプリケーションは心拍数の変化を測定するため、特性の変更通知を有効にする必要があります。すべての特性が変更通知を提供するわけではないことに注意。HeartRate特性は標準化されているので、通知を受け取ることができると仮定することができる。最終的にQLowEnergyCharacteristic::properties ()は、QLowEnergyCharacteristic::Notify フラグが設定されていなければならず、適切な通知が利用可能であることを確認するために、QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration 型の記述子が存在しなければならない。

最後に、Bluetooth Low Energy規格に従って、HeartRate特性の値を処理する:

void DeviceHandler::updateHeartRateValue(const QLowEnergyCharacteristic &c, const QByteArray &value)
{
    // ignore any other characteristic change -> shouldn't really happen though
    if (c.uuid() != QBluetoothUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement))
        return;

    auto data = reinterpret_cast<const quint8 *>(value.constData());
    quint8 flags = *data;

    //Heart Rate
    int hrvalue = 0;
    if (flags & 0x1) // HR 16 bit? otherwise 8 bit
        hrvalue = static_cast<int>(qFromLittleEndian<quint16>(data[1]));
    else
        hrvalue = static_cast<int>(data[1]);

    addMeasurement(hrvalue);
}

一般的に、特性値は一連のバイトである。これらのバイトの正確な解釈は、特性のタイプと値の構造によって異なります。一般的に、特性値は一連のバイトです。これらのバイトの正確な解釈は、特性のタイプと値の構造によって異なります上記のコード・スニペットは、標準化されたHeartRate値を読み取る方法を示しています。

広告サービス

周辺機器にGATTサーバー・アプリケーションを実装する場合、セントラル・デバイスに提供したいサービスを定義し、それをアドバタイズする必要があります:

QLowEnergyAdvertisingDataadvertisingData; advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral); advertisingData.setIncludePowerLevel(true); advertisingData.setLocalName("HeartRateServer"); advertisingData.setServices(QList<QBluetoothUuid>() <<QBluetoothUuid::ServiceClassUuid::HeartRate);boolerrorOccurred= false;conststd::unique_ptr<QLowEnergyController> leController(QLowEnergyController::createPeripheral());autoerrorHandler= [&leController, &errorOccurred](QLowEnergyController::Error errorCode) { エラーコード        qWarning().noquote().nospace() << errorCode << " occurred: "
           <<  leController->errorString();if(errorCode!= QLowEnergyController:RemoteHostClosedError){。            qWarning("Heartrate-server quitting due to the error.");
            errorOccurred= trueQCoreApplication::quit(); } };QObject::connect(leController.get(), &QLowEnergyController::errorOccurred,errorHandler); std::unique_ptr<QLowEnergyService> service(leController->addService(serviceData)); leController->startAdvertising(QLowEnergyAdvertisingParameters(),advertisingData,advertisingData);if(errorOccurred)return -1;

これで、潜在的なクライアントはデバイスに接続し、提供されるサービスを検出し、特性値の変更の通知を受けるために自分自身を登録することができる。APIのこの部分は、上記のセクションですでにカバーされている。

周辺機器へのサービスの実装

最初のステップは、サービス、その特性、および記述子を定義することである。これは、QLowEnergyServiceDataQLowEnergyCharacteristicDataQLowEnergyDescriptorData クラスを使用して実現されます。これらのクラスは、定義するBluetooth Low Energyサービスを構成する重要な情報のコンテナまたはビルディング・ブロックとして機能します。以下のコード・スニペットは、測定された1分間の拍動を公表する単純なHeartRateサービスを定義しています。このようなサービスが使用される例としては、腕時計があります。

QLowEnergyCharacteristicData charData;
charData.setUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement);
charData.setValue(QByteArray(2, 0));
charData.setProperties(QLowEnergyCharacteristic::Notify);
const QLowEnergyDescriptorData clientConfig(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
                                            QByteArray(2, 0));
charData.addDescriptor(clientConfig);

QLowEnergyServiceData serviceData;
serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
serviceData.setUuid(QBluetoothUuid::ServiceClassUuid::HeartRate);
serviceData.addCharacteristic(charData);

結果のserviceData オブジェクトは、上記の広告サービスのセクションで説明したように公開することができます。QLowEnergyServiceDataQLowEnergyAdvertisingData によってラップされた情報の間に部分的な情報の重複があるにもかかわらず、2つのクラスは2つの非常に異なるタスクに対応する。広告データは近隣のデバイスにパブリッシュされ、29バイトのサイズ制限のため、しばしば範囲が制限される。そのため、それらは常に100%完全ではない。それに比べて、QLowEnergyServiceData の中に含まれるサービスデータは、サービスデータの完全なセットを提供し、アクティブなサービス発見との接続が実行されたときにのみ、接続クライアントに見えるようになります。

次のセクションでは、サービスが心拍数の値を更新する方法を示します。サービスの性質によっては、https://www.bluetooth.org で定義されている公式サービス定義に準拠する必要があります他のサービスは完全にカスタムかもしれない。心拍数サービスは採用され、その仕様はhttps://www.bluetooth.com/specifications/adopted-specifications。

QTimer heartbeatTimer;
quint8 currentHeartRate = 60;
enum ValueChange { ValueUp, ValueDown } valueChange = ValueUp;
const auto heartbeatProvider = [&service, &currentHeartRate, &valueChange]() {
    QByteArray value;
    value.append(char(0)); // Flags that specify the format of the value.
    value.append(char(currentHeartRate)); // Actual value.
    QLowEnergyCharacteristic characteristic
            = service->characteristic(QBluetoothUuid::CharacteristicType::HeartRateMeasurement);
    Q_ASSERT(characteristic.isValid());
    service->writeCharacteristic(characteristic, value); // Potentially causes notification.
    if (currentHeartRate == 60)
        valueChange = ValueUp;
    else if (currentHeartRate == 100)
        valueChange = ValueDown;
    if (valueChange == ValueUp)
        ++currentHeartRate;
    else
        --currentHeartRate;
};
QObject::connect(&heartbeatTimer, &QTimer::timeout, heartbeatProvider);
heartbeatTimer.start(1000);

一般的に、周辺デバイス上の特性および記述子値の更新は、Bluetooth Low Energyデバイスの接続と同じ方法を使用します。

Qt Bluetooth(セントラルとペリフェラルの両方の役割で)iOS上で使用するには、使用説明を含むInfo.plistファイルを提供する必要があります。CoreBluetoothのドキュメントによると:Info.plistにアクセスする必要があるデータタイプの使用説明キーが含まれていない場合、アプリはクラッシュします。iOS 13以降にリンクされたアプリでCore Bluetooth APIにアクセスするには、NSBluetoothAlwaysUsageDescriptionキーをインクルードしてください。iOS 12 以前では、Bluetooth 周辺機器データにアクセスするために NSBluetoothPeripheralUsageDescription をインクルードします。

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.