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

ナビゲーションに移動 検索に移動
806行目: 806行目:
*: Notifyと同様、確認応答あり
*: Notifyと同様、確認応答あり
<br>
<br>
Classic Bluetoothとの違いを以下に示す。<br>
==== Classic Bluetoothとの違い ====
* キャラクタリスティックの操作が基本
* キャラクタリスティックの操作が基本
* 読み取り
* 読み取り
814行目: 814行目:
* 通知
* 通知
*: characteristicChangedシグナルで受信
*: characteristicChangedシグナルで受信
<br>
==== 使用例 ====
<syntaxhighlight lang="c++">
#include <QObject>
#include <QLowEnergyController>
#include <QLowEnergyService>
#include <QLowEnergyCharacteristic>
#include <QTimer>
#include <memory>
#include <QDebug>
class BLEConnection : public QObject
{
    Q_OBJECT
private:
    std::unique_ptr<QLowEnergyController> controller;
    std::unique_ptr<QTimer> reconnectTimer;
    QBluetoothDeviceInfo currentDevice;
    bool autoReconnect = false;
    void connectControllerSignals()
    {
      connect(controller.get(), &QLowEnergyController::connected, this, &BLEConnection::onConnected);
      connect(controller.get(), &QLowEnergyController::disconnected, this, &BLEConnection::onDisconnected);
      connect(controller.get(), &QLowEnergyController::serviceDiscovered, this, &BLEConnection::onServiceDiscovered);
      connect(controller.get(), static_cast<void(QLowEnergyController::*)(QLowEnergyController::Error)>(&QLowEnergyController::error), this, &BLEConnection::onError);
      connect(controller.get(), &QLowEnergyController::stateChanged, this, &BLEConnection::onStateChanged);
    }
    void connectServiceSignals(QLowEnergyService *service)
    {
        connect(service, &QLowEnergyService::stateChanged, this, [this, service](QLowEnergyService::ServiceState newState) {
          qDebug() << "サービス状態が変更: " << getServiceStateMessage(newState);
          if (newState == QLowEnergyService::ServiceDiscovered) {
              // キャラクタリスティックの処理
              const QList<QLowEnergyCharacteristic> chars = service->characteristics();
              for (const QLowEnergyCharacteristic& ch : chars) {
                qDebug() << "キャラクタリスティック発見:";
                qDebug() << "  UUID: " << ch.
                qDebug() << "キャラクタリスティック発見:";
                qDebug() << "  UUID: " << ch.uuid().toString();
                qDebug() << "  プロパティ: " << getCharacteristicPropertiesString(ch.properties());
                // Notify対応のキャラクタリスティックの場合
                if (ch.properties() & QLowEnergyCharacteristic::Notify) {
                    QLowEnergyDescriptor notifyDesc = ch.descriptor(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration);
                    if (notifyDesc.isValid()) {
                      service->writeDescriptor(notifyDesc, QByteArray::fromHex("0100"));
                    }
                }
              }
          }
        });
        connect(service, static_cast<void(QLowEnergyService::*)(QLowEnergyService::ServiceError)>(&QLowEnergyService::error), this,
                [this](QLowEnergyService::ServiceError error) {
                  qDebug() << "サービスエラー:" << getServiceErrorMessage(error);
        });
        connect(service, &QLowEnergyService::characteristicChanged, this,
                [](const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue) {
                  qDebug() << "キャラクタリスティック値が変更:";
                  qDebug() << "  UUID:" << characteristic.uuid().toString();
                  qDebug() << "  新しい値:" << newValue.toHex();
        });
    }
    QString getErrorMessage(QLowEnergyController::Error error)
    {
      switch (error) {
          case QLowEnergyController::NoError:                      return "エラーなし";
          case QLowEnergyController::UnknownError:                return "不明なエラー";
          case QLowEnergyController::UnknownRemoteDeviceError:    return "リモートデバイスが見つかりません";
          case QLowEnergyController::NetworkError:                return "ネットワークエラー";
          case QLowEnergyController::InvalidBluetoothAdapterError: return "無効なBluetoothアダプタ";
          case QLowEnergyController::ConnectionError:              return "接続エラー";
          default:                                                return "予期せぬエラー";
      }
    }
    QString getStateMessage(QLowEnergyController::ControllerState state)
    {
      switch (state) {
          case QLowEnergyController::UnconnectedState: return "未接続";
          case QLowEnergyController::ConnectingState:  return "接続中";
          case QLowEnergyController::ConnectedState:  return "接続済み";
          case QLowEnergyController::DiscoveringState: return "探索中";
          case QLowEnergyController::DiscoveredState:  return "探索完了";
          case QLowEnergyController::ClosingState:    return "切断中";
          case QLowEnergyController::AdvertisingState: return "アドバタイジング中";
          default:                                    return "不明な状態";
      }
    }
    QString getServiceStateMessage(QLowEnergyService::ServiceState state)
    {
      switch (state) {
          case QLowEnergyService::InvalidService:      return "無効なサービス";
          case QLowEnergyService::DiscoveryRequired:  return "探索が必要";
          case QLowEnergyService::DiscoveringServices: return "サービス探索中";
          case QLowEnergyService::ServiceDiscovered:  return "サービス探索完了";
          default:                                    return "不明なサービス状態";
      }
    }
    QString getServiceErrorMessage(QLowEnergyService::ServiceError error)
    {
      switch (error) {
          case QLowEnergyService::NoError:                  return "エラーなし";
          case QLowEnergyService::OperationError:          return "操作エラー";
          case QLowEnergyService::CharacteristicReadError:  return "キャラクタリスティック読み取りエラー";
          case QLowEnergyService::CharacteristicWriteError: return "キャラクタリスティック書き込みエラー";
          case QLowEnergyService::DescriptorReadError:      return "ディスクリプタ読み取りエラー";
          case QLowEnergyService::DescriptorWriteError:    return "ディスクリプタ書き込みエラー";
          case QLowEnergyService::UnknownError:            return "不明なエラー";
          default:                                          return "予期せぬサービスエラー";
      }
    }
    QString getCharacteristicPropertiesString(QLowEnergyCharacteristic::PropertyTypes properties)
    {
      QStringList props;
      if (properties & QLowEnergyCharacteristic::Read)            props << "Read";
      if (properties & QLowEnergyCharacteristic::Write)          props << "Write";
      if (properties & QLowEnergyCharacteristic::Notify)          props << "Notify";
      if (properties & QLowEnergyCharacteristic::Indicate)        props << "Indicate";
      if (properties & QLowEnergyCharacteristic::WriteSigned)    props << "WriteSigned";
      if (properties & QLowEnergyCharacteristic::WriteNoResponse) props << "WriteNoResponse";
      return props.join(", ");
    }
public:
    explicit BLEConnection(QObject* parent = nullptr) : QObject(parent)
    {
      // 再接続タイマの初期化
      reconnectTimer = std::make_unique<QTimer>(this);
      reconnectTimer->setInterval(5000);  // 5秒間隔で再接続
      reconnectTimer->setSingleShot(true);
      connect(reconnectTimer.get(), &QTimer::timeout, this, &BLEConnection::onReconnectTimeout);
    }
    // デバイスへの接続
    void connectToDevice(const QBluetoothDeviceInfo &device)
    {
      try {
          qDebug() << "デバイスへの接続を開始:" << device.name();
          // コントローラの初期化
          controller = std::make_unique<QLowEnergyController>(device, this);
          connectControllerSignals();
          // 接続パラメータの設定
          controller->setRemoteAddressType(QLowEnergyController::PublicAddress);
          // 接続開始
          controller->connectToDevice();
          currentDevice = device;
      }
      catch (const std::exception &e) {
          QString errorMsg = QString("接続エラー: %1").arg(e.what());
          qDebug() << errorMsg;
          emit errorOccurred(errorMsg);
      }
    }
    // 切断
    void disconnect()
    {
      try {
          if (controller) {
            controller->disconnectFromDevice();
            reconnectTimer->stop();
          }
      }
      catch (const std::exception &e) {
          QString errorMsg = QString("切断エラー: %1").arg(e.what());
          qDebug() << errorMsg;
          emit errorOccurred(errorMsg);
      }
    }
    // 自動再接続の設定
    void setAutoReconnect(bool enable)
    {
      autoReconnect = enable;
      if (!enable) {
          reconnectTimer->stop();
      }
    }
signals:
    void connected();
    void disconnected();
    void errorOccurred(const QString& error);
    void connectionStateChanged(QLowEnergyController::ControllerState state);
    void serviceDiscovered(QLowEnergyService* service);
private slots:
    void onConnected()
    {
      qDebug() << "デバイスに接続";
      reconnectTimer->stop();
      emit connected();
      // サービスの探索を開始
      controller->discoverServices();
    }
    void onDisconnected()
    {
      qDebug() << "デバイスから切断";
      emit disconnected();
      // 自動再接続が有効な場合
      if (autoReconnect) {
          qDebug() << "再接続を試行...";
          reconnectTimer->start();
      }
    }
    void onServiceDiscovered(const QBluetoothUuid& uuid)
    {
      qDebug() << "サービスを発見: " << uuid.toString();
      QLowEnergyService* service = controller->createServiceObject(uuid, this);
      if (service) {
          connectServiceSignals(service);
          emit serviceDiscovered(service);
      }
    }
    void onError(QLowEnergyController::Error error)
    {
      QString errorMessage = getErrorMessage(error);
      qDebug() << "エラーが発生: " << errorMessage;
      emit errorOccurred(errorMessage);
      if (autoReconnect && error != QLowEnergyController::InvalidBluetoothAdapterError) reconnectTimer->start();
    }
    void onStateChanged(QLowEnergyController::ControllerState state)
    {
      qDebug() << "接続状態が変更: " << getStateMessage(state);
      emit connectionStateChanged(state);
    }
    void onReconnectTimeout()
    {
      if (autoReconnect && currentDevice.isValid()) {
          qDebug() << "再接続を試行...";
          controller->connectToDevice();
      }
    }
};
</syntaxhighlight>
<br><br>
<br><br>


案内メニュー