Last active
November 14, 2023 14:44
-
-
Save teach310/eb61fdd764e483fac95470504220066d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## CoreBluetoothForUnity をカスタマイズして使いやすくする |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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