Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save teach310/eb61fdd764e483fac95470504220066d to your computer and use it in GitHub Desktop.
Save teach310/eb61fdd764e483fac95470504220066d to your computer and use it in GitHub Desktop.
## CoreBluetoothForUnity をカスタマイズして使いやすくする
using CoreBluetooth;
using UnityEngine;
using UnityEngine.UI;
using UniRx;
using Cysharp.Threading.Tasks;
using System.Threading;
namespace CoreBluetoothSample
{
public class CustomCentral : MonoBehaviour
{
[SerializeField] SampleButtonInformation_Log _log;
[SerializeField] SampleButtonInformation_Cube _cube;
[SerializeField] Text _stateLabel;
CBCentralManager _centralManager;
CBPeripheral _peripheral;
ObservableCentralManagerDelegate _observableCentralManagerDelegate = new ObservableCentralManagerDelegate();
ObservablePeripheralDelegate _observablePeripheralDelegate = new ObservablePeripheralDelegate();
void Start()
{
var initOptions = new CBCentralManagerInitOptions() { ShowPowerAlert = true };
_centralManager = new CBCentralManager(_observableCentralManagerDelegate, initOptions);
_centralManager.AddTo(this);
_observableCentralManagerDelegate.AddTo(this);
_observablePeripheralDelegate.AddTo(this);
AddListeners();
ConnectAsync(this.GetCancellationTokenOnDestroy()).Forget(e => Debug.LogError(e));
}
void AddListeners()
{
_observableCentralManagerDelegate.DidDisconnectPeripheralAsObservable().Subscribe(_ =>
{
ConnectAsync(this.GetCancellationTokenOnDestroy()).Forget(e => Debug.LogError(e));
}).AddTo(this);
_observablePeripheralDelegate.DidUpdateValueForCharacteristicAsObservable().Subscribe(x =>
{
if (x.error != null)
{
Debug.LogError($"[DidUpdateValueForCharacteristic] error: {x.error}");
return;
}
if (SampleButtonInformation_Data.ParseButtonInformation(x.characteristic.Value, out int buttonId, out bool isPressed))
{
_log.AppendLog(buttonId, isPressed);
_cube.Action(buttonId, isPressed);
}
}).AddTo(this);
_observablePeripheralDelegate.DidUpdateNotificationStateForCharacteristicAsObservable().Subscribe(x =>
{
if (x.error != null)
{
Debug.LogError($"[DidUpdateNotificationState] error: {x.error}");
return;
}
}).AddTo(this);
}
// 注意: この実装だと cancellationToken でキャンセルされた場合、タイミングによって Scan や Connect がキャンセルされないまま放置されます。
// Destory によるキャンセルの場合は CentralManager が Dispose されることにより Scan や Connect がキャンセルされるため問題ありません。
async UniTask ConnectAsync(CancellationToken cancellationToken)
{
if (_centralManager.State != CBManagerState.PoweredOn)
{
await UniTask.WaitUntil(() => _centralManager.State == CBManagerState.PoweredOn, cancellationToken: cancellationToken);
}
_stateLabel.text = "Scanning...";
_peripheral = await ScanAsync(cancellationToken);
_peripheral.Delegate = _observablePeripheralDelegate;
_centralManager.StopScan();
_stateLabel.text = "Connecting...";
await ConnectPeripheralAsync(cancellationToken);
_stateLabel.text = "Connected";
await DiscoverServicesAsync(cancellationToken);
await DiscoverCharacteristicsAsync(cancellationToken);
SetNotifyValue();
}
async UniTask<CBPeripheral> ScanAsync(CancellationToken cancellationToken)
{
_centralManager.ScanForPeripherals(new string[] { SampleButtonInformation_Data.ServiceUUID });
var result = await _observableCentralManagerDelegate.DidDiscoverPeripheralAsObservable().ToUniTask(useFirstValue: true, cancellationToken: cancellationToken);
return result.peripheral;
}
async UniTask ConnectPeripheralAsync(CancellationToken cancellationToken)
{
_centralManager.Connect(_peripheral);
var result = await _observableCentralManagerDelegate.DidConnectPeripheralAsObservable().ToUniTask(useFirstValue: true, cancellationToken: cancellationToken);
if (result.error != null)
{
throw result.error;
}
}
async UniTask DiscoverServicesAsync(CancellationToken cancellationToken)
{
_peripheral.DiscoverServices();
var result = await _observablePeripheralDelegate.DidDiscoverServicesAsObservable().ToUniTask(useFirstValue: true, cancellationToken: cancellationToken);
if (result.error != null)
{
throw result.error;
}
}
async UniTask DiscoverCharacteristicsAsync(CancellationToken cancellationToken)
{
foreach (var service in _peripheral.Services)
{
if (service.UUID.Equals(SampleButtonInformation_Data.ServiceUUID))
{
_peripheral.DiscoverCharacteristics(new string[] { SampleButtonInformation_Data.ButtonInformationCharacteristicUUID }, service);
}
}
var result = await _observablePeripheralDelegate.DidDiscoverCharacteristicsAsObservable().ToUniTask(useFirstValue: true, cancellationToken: cancellationToken);
if (result.error != null)
{
throw result.error;
}
}
void SetNotifyValue()
{
var service = _peripheral.Services[0];
foreach (var characteristic in service.Characteristics)
{
if (characteristic.UUID.Equals(SampleButtonInformation_Data.ButtonInformationCharacteristicUUID))
{
if (characteristic.Properties.HasFlag(CBCharacteristicProperties.Notify))
{
_peripheral.SetNotifyValue(true, characteristic);
}
}
}
}
}
}
using System;
using CoreBluetooth;
using UniRx;
public class ObservableCentralManagerDelegate : ICBCentralManagerDelegate, IDisposable
{
// NOTE: Connectの結果が成功か失敗かを判定するために、errorを追加
readonly Subject<(CBCentralManager central, CBPeripheral peripheral, CBError error)> _didConnectPeripheralSubject = new Subject<(CBCentralManager central, CBPeripheral peripheral, CBError error)>();
readonly Subject<(CBCentralManager central, CBPeripheral peripheral, CBError error)> _didDisconnectPeripheralSubject = new Subject<(CBCentralManager central, CBPeripheral peripheral, CBError error)>();
readonly Subject<(CBCentralManager central, CBPeripheral peripheral, int rssi)> _didDiscoverPeripheralSubject = new Subject<(CBCentralManager central, CBPeripheral peripheral, int rssi)>();
readonly Subject<CBCentralManager> _didUpdateStateSubject = new Subject<CBCentralManager>();
public IObservable<(CBCentralManager central, CBPeripheral peripheral, CBError error)> DidConnectPeripheralAsObservable() => _didConnectPeripheralSubject;
public IObservable<(CBCentralManager central, CBPeripheral peripheral, CBError error)> DidDisconnectPeripheralAsObservable() => _didDisconnectPeripheralSubject;
public IObservable<(CBCentralManager central, CBPeripheral peripheral, int rssi)> DidDiscoverPeripheralAsObservable() => _didDiscoverPeripheralSubject;
public IObservable<CBCentralManager> DidUpdateStateAsObservable() => _didUpdateStateSubject;
void ICBCentralManagerDelegate.DidConnectPeripheral(CBCentralManager central, CBPeripheral peripheral)
{
_didConnectPeripheralSubject.OnNext((central, peripheral, null));
}
void ICBCentralManagerDelegate.DidDisconnectPeripheral(CBCentralManager central, CBPeripheral peripheral, CBError error)
{
_didDisconnectPeripheralSubject.OnNext((central, peripheral, error));
}
void ICBCentralManagerDelegate.DidFailToConnectPeripheral(CBCentralManager central, CBPeripheral peripheral, CBError error)
{
_didConnectPeripheralSubject.OnNext((central, peripheral, error));
}
void ICBCentralManagerDelegate.DidDiscoverPeripheral(CBCentralManager central, CBPeripheral peripheral, int rssi)
{
_didDiscoverPeripheralSubject.OnNext((central, peripheral, rssi));
}
void ICBCentralManagerDelegate.DidUpdateState(CBCentralManager central)
{
_didUpdateStateSubject.OnNext(central);
}
public void Dispose()
{
_didConnectPeripheralSubject.Dispose();
_didDisconnectPeripheralSubject.Dispose();
_didDiscoverPeripheralSubject.Dispose();
_didUpdateStateSubject.Dispose();
}
}
using System;
using CoreBluetooth;
using UniRx;
public class ObservablePeripheralDelegate : ICBPeripheralDelegate, IDisposable
{
readonly Subject<(CBPeripheral peripheral, CBError error)> _didDiscoverServicesSubject = new Subject<(CBPeripheral peripheral, CBError error)>();
readonly Subject<(CBPeripheral peripheral, CBService service, CBError error)> _didDiscoverCharacteristicsSubject = new Subject<(CBPeripheral peripheral, CBService service, CBError error)>();
readonly Subject<(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)> _didUpdateValueForCharacteristicSubject = new Subject<(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)>();
readonly Subject<(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)> _didWriteValueForCharacteristicSubject = new Subject<(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)>();
readonly Subject<CBPeripheral> _isReadyToSendWriteWithoutResponseSubject = new Subject<CBPeripheral>();
readonly Subject<(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)> _didUpdateNotificationStateForCharacteristicSubject = new Subject<(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)>();
readonly Subject<(CBPeripheral peripheral, int rssi, CBError error)> _didReadRSSISubject = new Subject<(CBPeripheral peripheral, int rssi, CBError error)>();
readonly Subject<CBPeripheral> _didUpdateNameSubject = new Subject<CBPeripheral>();
readonly Subject<(CBPeripheral peripheral, CBService[] services)> _didModifyServicesSubject = new Subject<(CBPeripheral peripheral, CBService[] services)>();
public IObservable<(CBPeripheral peripheral, CBError error)> DidDiscoverServicesAsObservable() => _didDiscoverServicesSubject;
public IObservable<(CBPeripheral peripheral, CBService service, CBError error)> DidDiscoverCharacteristicsAsObservable() => _didDiscoverCharacteristicsSubject;
public IObservable<(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)> DidUpdateValueForCharacteristicAsObservable() => _didUpdateValueForCharacteristicSubject;
public IObservable<(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)> DidWriteValueForCharacteristicAsObservable() => _didWriteValueForCharacteristicSubject;
public IObservable<CBPeripheral> IsReadyToSendWriteWithoutResponseAsObservable() => _isReadyToSendWriteWithoutResponseSubject;
public IObservable<(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)> DidUpdateNotificationStateForCharacteristicAsObservable() => _didUpdateNotificationStateForCharacteristicSubject;
public IObservable<(CBPeripheral peripheral, int rssi, CBError error)> DidReadRSSIAsObservable() => _didReadRSSISubject;
public IObservable<CBPeripheral> DidUpdateNameAsObservable() => _didUpdateNameSubject;
public IObservable<(CBPeripheral peripheral, CBService[] services)> DidModifyServicesAsObservable() => _didModifyServicesSubject;
void ICBPeripheralDelegate.DidDiscoverServices(CBPeripheral peripheral, CBError error)
{
_didDiscoverServicesSubject.OnNext((peripheral, error));
}
void ICBPeripheralDelegate.DidDiscoverCharacteristics(CBPeripheral peripheral, CBService service, CBError error)
{
_didDiscoverCharacteristicsSubject.OnNext((peripheral, service, error));
}
void ICBPeripheralDelegate.DidUpdateValueForCharacteristic(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)
{
_didUpdateValueForCharacteristicSubject.OnNext((peripheral, characteristic, error));
}
void ICBPeripheralDelegate.DidWriteValueForCharacteristic(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)
{
_didWriteValueForCharacteristicSubject.OnNext((peripheral, characteristic, error));
}
void ICBPeripheralDelegate.IsReadyToSendWriteWithoutResponse(CBPeripheral peripheral)
{
_isReadyToSendWriteWithoutResponseSubject.OnNext(peripheral);
}
void ICBPeripheralDelegate.DidUpdateNotificationStateForCharacteristic(CBPeripheral peripheral, CBCharacteristic characteristic, CBError error)
{
_didUpdateNotificationStateForCharacteristicSubject.OnNext((peripheral, characteristic, error));
}
void ICBPeripheralDelegate.DidReadRSSI(CBPeripheral peripheral, int rssi, CBError error)
{
_didReadRSSISubject.OnNext((peripheral, rssi, error));
}
void ICBPeripheralDelegate.DidUpdateName(CBPeripheral peripheral)
{
_didUpdateNameSubject.OnNext(peripheral);
}
void ICBPeripheralDelegate.DidModifyServices(CBPeripheral peripheral, CBService[] services)
{
_didModifyServicesSubject.OnNext((peripheral, services));
}
public void Dispose()
{
_didDiscoverServicesSubject.Dispose();
_didDiscoverCharacteristicsSubject.Dispose();
_didUpdateValueForCharacteristicSubject.Dispose();
_didWriteValueForCharacteristicSubject.Dispose();
_isReadyToSendWriteWithoutResponseSubject.Dispose();
_didUpdateNotificationStateForCharacteristicSubject.Dispose();
_didReadRSSISubject.Dispose();
_didUpdateNameSubject.Dispose();
_didModifyServicesSubject.Dispose();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment