diff --git a/Sample Apps/BLE Explorer/Robotics.Mobile.BtLEExplorer.Core/Pages/ServiceList.xaml.cs b/Sample Apps/BLE Explorer/Robotics.Mobile.BtLEExplorer.Core/Pages/ServiceList.xaml.cs index 24716a9..8c98b63 100644 --- a/Sample Apps/BLE Explorer/Robotics.Mobile.BtLEExplorer.Core/Pages/ServiceList.xaml.cs +++ b/Sample Apps/BLE Explorer/Robotics.Mobile.BtLEExplorer.Core/Pages/ServiceList.xaml.cs @@ -21,29 +21,8 @@ public ServiceList (IAdapter adapter, IDevice device) this.device = device; this.services = new ObservableCollection (); listView.ItemsSource = services; - - // when device is connected - adapter.DeviceConnected += (s, e) => { - device = e.Device; // do we need to overwrite this? - - // when services are discovered - device.ServicesDiscovered += (object se, EventArgs ea) => { - Debug.WriteLine("device.ServicesDiscovered"); - //services = (List)device.Services; - if (services.Count == 0) - Device.BeginInvokeOnMainThread(() => { - foreach (var service in device.Services) { - services.Add(service); - } - }); - }; - // start looking for services - device.DiscoverServices (); - - }; - // TODO: add to IAdapter first - //adapter.DeviceFailedToConnect += (sender, else) => {}; - + adapter.DeviceConnected += this.OnDeviceConnected; + adapter.DeviceDisconnected += this.OnDeviceDisconnected; DisconnectButton.Activated += (sender, e) => { adapter.DisconnectDevice (device); Navigation.PopToRootAsync(); // disconnect means start over @@ -71,6 +50,44 @@ public void OnItemSelected (object sender, SelectedItemChangedEventArgs e) { ((ListView)sender).SelectedItem = null; // clear selection } + + public void OnDeviceConnected(object sender, DeviceConnectionEventArgs args) + { + if (args.Device == this.device) { + this.adapter.DeviceConnected -= this.OnDeviceConnected; + this.device.ServicesDiscovered += this.ServicesDiscovered; + this.device.DiscoverServices (); + } + } + + public void OnDeviceDisconnected(object sender, DeviceConnectionEventArgs args) + { + if (args.Device == this.device) { + this.adapter.DeviceDisconnected -= this.OnDeviceDisconnected; + // For robustness reason, its possible to be disconnected from device + // right after adding the OnConnected delgate in constructor. + // We do not want DeviceConnected delegate become a hidden bomb. + this.adapter.DeviceConnected -= this.OnDeviceConnected; + // For robustness reason, its possible we go from device + // connected to device disconnected without going through + // service discovered. We do not want the service discover + // delegate become a hidden bomb. + this.device.ServicesDiscovered -= this.ServicesDiscovered; + } + } + + public void ServicesDiscovered(object sender, EventArgs args) + { + this.device.ServicesDiscovered -= this.ServicesDiscovered; + Debug.WriteLine("device.ServicesDiscovered"); + if (services.Count == 0) { + Device.BeginInvokeOnMainThread (() => { + foreach (var service in device.Services) { + services.Add (service); + } + }); + } + } } } diff --git a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Adapter.cs b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Adapter.cs index 62d9010..17dd8be 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Adapter.cs +++ b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Adapter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Android.Bluetooth; using System.Threading.Tasks; +using Java.Util; namespace Robotics.Mobile.Core.Bluetooth.LE { @@ -19,7 +20,6 @@ public class Adapter : Java.Lang.Object, BluetoothAdapter.ILeScanCallback, IAdap // class members protected BluetoothManager _manager; protected BluetoothAdapter _adapter; - protected GattCallback _gattCallback; public bool IsScanning { get { return this._isScanning; } @@ -44,38 +44,30 @@ public Adapter () // get a reference to the bluetooth system service this._manager = (BluetoothManager) appContext.GetSystemService("bluetooth"); this._adapter = this._manager.Adapter; - - this._gattCallback = new GattCallback (this); - - this._gattCallback.DeviceConnected += (object sender, DeviceConnectionEventArgs e) => { - this._connectedDevices.Add ( e.Device); - this.DeviceConnected (this, e); - }; - - this._gattCallback.DeviceDisconnected += (object sender, DeviceConnectionEventArgs e) => { - // TODO: remove the disconnected device from the _connectedDevices list - // i don't think this will actually work, because i'm created a new underlying device here. - //if(this._connectedDevices.Contains( - this.DeviceDisconnected (this, e); - }; } - //TODO: scan for specific service type eg. HeartRateMonitor - public async void StartScanningForDevices (Guid serviceUuid) - { - StartScanningForDevices (); -// throw new NotImplementedException ("Not implemented on Android yet, look at _adapter.StartLeScan() overload"); - } - public async void StartScanningForDevices () + public async void StartScanningForDevices () + { + this.StartScanningForDevices(Guid.Empty); + } + + public async void StartScanningForDevices (Guid serviceUuid) { Console.WriteLine ("Adapter: Starting a scan for devices."); // clear out the list this._discoveredDevices = new List (); + UUID[] serviceUuids = null; + if (serviceUuid != Guid.Empty) + { + serviceUuids = new UUID[1]; + serviceUuids[0] = UUID.FromString(serviceUuid.ToString()); + } + // start scanning this._isScanning = true; - this._adapter.StartLeScan (this); + this._adapter.StartLeScan (serviceUuids, this); // in 10 seconds, stop the scan await Task.Delay (10000); @@ -125,9 +117,31 @@ protected bool DeviceExistsInDiscoveredList(BluetoothDevice device) public void ConnectToDevice (IDevice device) { - // returns the BluetoothGatt, which is the API for BLE stuff - // TERRIBLE API design on the part of google here. - ((BluetoothDevice)device.NativeDevice).ConnectGatt (Android.App.Application.Context, true, this._gattCallback); + var androidBleDevice = (Device)device; + if (androidBleDevice._gatt == null) { + var gattCallback = new GattCallback (androidBleDevice); + gattCallback.DeviceConnected += (object sender, DeviceConnectionEventArgs e) => { + this._connectedDevices.Add (e.Device); + this.DeviceConnected (this, e); + }; + + gattCallback.DeviceDisconnected += (object sender, DeviceConnectionEventArgs e) => { + this._connectedDevices.Remove (e.Device); + this.DeviceDisconnected (this, e); + }; + + androidBleDevice.GattCallback = gattCallback; + androidBleDevice._gatt = ((BluetoothDevice)device.NativeDevice).ConnectGatt (Android.App.Application.Context, false, gattCallback); + var success = androidBleDevice._gatt.Connect (); + Console.WriteLine(string.Format("Initial connection attempt is {0}", success)); + } else { + switch (androidBleDevice.State) { + case DeviceState.Disconnected: + androidBleDevice.Disconnect (); + this.ConnectToDevice (androidBleDevice); + break; + } + } } public void DisconnectDevice (IDevice device) diff --git a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Characteristic.cs b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Characteristic.cs index ecbe337..e08d73b 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Characteristic.cs +++ b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Characteristic.cs @@ -12,30 +12,20 @@ namespace Robotics.Mobile.Core.Bluetooth.LE public class Characteristic : ICharacteristic { public event EventHandler ValueUpdated = delegate {}; - + public event EventHandler ValueWritten = delegate {}; protected BluetoothGattCharacteristic _nativeCharacteristic; - /// - /// we have to keep a reference to this because Android's api is weird and requires - /// the GattServer in order to do nearly anything, including enumerating services - /// - protected BluetoothGatt _gatt; - /// - /// we also track this because of gogole's weird API. the gatt callback is where - /// we'll get notified when services are enumerated - /// - protected GattCallback _gattCallback; - - - public Characteristic (BluetoothGattCharacteristic nativeCharacteristic, BluetoothGatt gatt, GattCallback gattCallback) + protected Device _device; + + + public Characteristic (BluetoothGattCharacteristic nativeCharacteristic, Device device) { this._nativeCharacteristic = nativeCharacteristic; - this._gatt = gatt; - this._gattCallback = gattCallback; + this._device = device; - if (this._gattCallback != null) { + if (this._device.GattCallback != null) { // wire up the characteristic value updating on the gattcallback - this._gattCallback.CharacteristicValueUpdated += (object sender, CharacteristicReadEventArgs e) => { + this._device.GattCallback.CharacteristicValueUpdated += (object sender, CharacteristicReadEventArgs e) => { // it may be other characteristics, so we need to test if(e.Characteristic.ID == this.ID) { // update our underlying characteristic (this one will have a value) @@ -104,7 +94,6 @@ public object NativeCharacteristic { //NOTE: why this requires Apple, we have no idea. BLE stands for Mystery. public bool CanWrite {get{return (this.Properties & CharacteristicPropertyType.WriteWithoutResponse | CharacteristicPropertyType.AppleWriteWithoutResponse) != 0; }} - // HACK: UNTESTED - this API has only been tested on iOS public void Write (byte[] data) { if (!CanWrite) { @@ -113,11 +102,16 @@ public void Write (byte[] data) var c = _nativeCharacteristic; c.SetValue (data); - this._gatt.WriteCharacteristic (c); + this._device.GattCallback.CharacteristicValueWritten += this.OnWritten; + this._device._gatt.WriteCharacteristic (c); Console.WriteLine(".....Write"); } - + public void OnWritten(object sender, CharacteristicWrittenEventArgs args) + { + this._device.GattCallback.CharacteristicValueWritten -= this.OnWritten; + this.ValueWritten (sender, args); + } // HACK: UNTESTED - this API has only been tested on iOS public Task ReadAsync() @@ -132,20 +126,20 @@ public Task ReadAsync() // it may be other characteristics, so we need to test var c = e.Characteristic; tcs.SetResult(c); - if (this._gattCallback != null) { + if (this._device.GattCallback != null) { // wire up the characteristic value updating on the gattcallback - this._gattCallback.CharacteristicValueUpdated -= updated; + this._device.GattCallback.CharacteristicValueUpdated -= updated; } }; - if (this._gattCallback != null) { + if (this._device.GattCallback != null) { // wire up the characteristic value updating on the gattcallback - this._gattCallback.CharacteristicValueUpdated += updated; + this._device.GattCallback.CharacteristicValueUpdated += updated; } Console.WriteLine(".....ReadAsync"); - this._gatt.ReadCharacteristic (this._nativeCharacteristic); + this._device._gatt.ReadCharacteristic (this._nativeCharacteristic); return tcs.Task; } @@ -156,12 +150,12 @@ public void StartUpdates () bool successful = false; if (CanRead) { Console.WriteLine ("Characteristic.RequestValue, PropertyType = Read, requesting updates"); - successful = this._gatt.ReadCharacteristic (this._nativeCharacteristic); + successful = this._device._gatt.ReadCharacteristic (this._nativeCharacteristic); } if (CanUpdate) { Console.WriteLine ("Characteristic.RequestValue, PropertyType = Notify, requesting updates"); - successful = this._gatt.SetCharacteristicNotification (this._nativeCharacteristic, true); + successful = this._device._gatt.SetCharacteristicNotification (this._nativeCharacteristic, true); // [TO20131211@1634] It seems that setting the notification above isn't enough. You have to set the NOTIFY // descriptor as well, otherwise the receiver will never get the updates. I just grabbed the first (and only) @@ -177,7 +171,7 @@ public void StartUpdates () if (_nativeCharacteristic.Descriptors.Count > 0) { BluetoothGattDescriptor descriptor = _nativeCharacteristic.Descriptors [0]; descriptor.SetValue (BluetoothGattDescriptor.EnableNotificationValue.ToArray ()); - _gatt.WriteDescriptor (descriptor); + this._device._gatt.WriteDescriptor (descriptor); } else { Console.WriteLine ("RequestValue, FAILED: _nativeCharacteristic.Descriptors was empty, not sure why"); } @@ -190,7 +184,7 @@ public void StopUpdates () { bool successful = false; if (CanUpdate) { - successful = this._gatt.SetCharacteristicNotification (this._nativeCharacteristic, false); + successful = this._device._gatt.SetCharacteristicNotification (this._nativeCharacteristic, false); //TODO: determine whether Console.WriteLine ("Characteristic.RequestValue, PropertyType = Notify, STOP updates"); } diff --git a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Device.cs b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Device.cs index 0e8cb5a..14667be 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Device.cs +++ b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Device.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Android.Bluetooth; using System.Linq; +using System.Threading; namespace Robotics.Mobile.Core.Bluetooth.LE { @@ -16,32 +17,21 @@ public class Device : DeviceBase /// /// TODO: consider wrapping the Gatt and Callback into a single object and passing that around instead. /// - protected BluetoothGatt _gatt; + internal BluetoothGatt _gatt; /// /// we also track this because of gogole's weird API. the gatt callback is where /// we'll get notified when services are enumerated /// - protected GattCallback _gattCallback; + private GattCallback _gattCallback; + internal ProfileState _profileState; public Device (BluetoothDevice nativeDevice, BluetoothGatt gatt, GattCallback gattCallback, int rssi) : base () { this._nativeDevice = nativeDevice; this._gatt = gatt; - this._gattCallback = gattCallback; + this.GattCallback = gattCallback; this._rssi = rssi; - - // when the services are discovered on the gatt callback, cache them here - if (this._gattCallback != null) { - this._gattCallback.ServicesDiscovered += (s, e) => { - var services = this._gatt.Services; - this._services = new List (); - foreach (var item in services) { - this._services.Add (new Service (item, this._gatt, this._gattCallback)); - } - this.ServicesDiscovered (this, e); - }; - } } public override Guid ID { @@ -70,7 +60,7 @@ public override int Rssi { get { return this._rssi; } - } protected int _rssi; + } internal int _rssi; public override object NativeDevice { @@ -79,9 +69,6 @@ public override object NativeDevice } } - // TODO: investigate the validity of this. Android API seems to indicate that the - // bond state is available, rather than the connected state, which are two different - // things. you can be bonded but not connected. public override DeviceState State { get { return this.GetState (); @@ -103,8 +90,17 @@ public override void DiscoverServices () public void Disconnect () { - this._gatt.Disconnect (); - this._gatt.Dispose (); + if (this._gatt != null) { + this._gatt.Disconnect (); + // From empirical results, simply gatt.disconnect follow by gatt.connect is not sufficient + // to reconnect to deviece (on Nexus 7 2013 with Adnroid 5.1.1) + // Calling gatt.Close() has more chance on the next connection attempt being successful. + // Being said then, you should avoid using the same gatt client and gatt callback for more + // than one device. + this._gatt.Close (); + this.GattCallback = null; + this._gatt = null; + } } #endregion @@ -113,18 +109,40 @@ public void Disconnect () protected DeviceState GetState() { - switch (this._nativeDevice.BondState) { - case Bond.Bonded: + switch (this._profileState) { + case ProfileState.Connected: return DeviceState.Connected; - case Bond.Bonding: + case ProfileState.Connecting: return DeviceState.Connecting; - case Bond.None: + case ProfileState.Disconnected: default: return DeviceState.Disconnected; } } + internal GattCallback GattCallback + { + get + { + return this._gattCallback; + } + set + { + this._gattCallback = value; + // when the services are discovered on the gatt callback, cache them here + if (this._gattCallback != null) { + this._gattCallback.ServicesDiscovered += (s, e) => { + var services = this._gatt.Services; + this._services = new List (); + foreach (var item in services) { + this._services.Add (new Service (item, this)); + } + this.ServicesDiscovered (this, e); + }; + } + } + } #endregion } } diff --git a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/GattCallback.cs b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/GattCallback.cs index 8f6d8a6..06ce585 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/GattCallback.cs +++ b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/GattCallback.cs @@ -3,6 +3,9 @@ namespace Robotics.Mobile.Core.Bluetooth.LE { + /// + /// GattCallback has a 1 to 1 relation to each Device instance. + /// public class GattCallback : BluetoothGattCallback { @@ -10,27 +13,27 @@ public class GattCallback : BluetoothGattCallback public event EventHandler DeviceDisconnected = delegate {}; public event EventHandler ServicesDiscovered = delegate {}; public event EventHandler CharacteristicValueUpdated = delegate {}; + public event EventHandler CharacteristicValueWritten = delegate {}; - protected Adapter _adapter; + private Device _device; - public GattCallback (Adapter adapter) + public GattCallback(Device device) { - this._adapter = adapter; + this._device = device; } public override void OnConnectionStateChange (BluetoothGatt gatt, GattStatus status, ProfileState newState) { - Console.WriteLine ("OnConnectionStateChange: "); base.OnConnectionStateChange (gatt, status, newState); - //TODO: need to pull the cached RSSI in here, or read it (requires the callback) - Device device = new Device (gatt.Device, gatt, this, 0); + Console.WriteLine ("OnConnectionStateChange: "); + this._device._profileState = newState; switch (newState) { // disconnected case ProfileState.Disconnected: Console.WriteLine ("disconnected"); - this.DeviceDisconnected (this, new DeviceConnectionEventArgs () { Device = device }); + this.DeviceDisconnected (this, new DeviceConnectionEventArgs () { Device = this._device }); break; // connecting case ProfileState.Connecting: @@ -39,7 +42,7 @@ public override void OnConnectionStateChange (BluetoothGatt gatt, GattStatus sta // connected case ProfileState.Connected: Console.WriteLine ("Connected"); - this.DeviceConnected (this, new DeviceConnectionEventArgs () { Device = device }); + this.DeviceConnected (this, new DeviceConnectionEventArgs () { Device = this._device }); break; // disconnecting case ProfileState.Disconnecting: @@ -65,6 +68,15 @@ public override void OnDescriptorRead (BluetoothGatt gatt, BluetoothGattDescript } + public override void OnCharacteristicWrite (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, GattStatus status) + { + base.OnCharacteristicWrite(gatt, characteristic, status); + Console.WriteLine (string.Format("OnCharacteristicWrite: Status: {0}", status)); + this.CharacteristicValueWritten (this, new CharacteristicWrittenEventArgs () { + Characteristic = new Characteristic (characteristic, this._device) } + ); + } + public override void OnCharacteristicRead (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, GattStatus status) { base.OnCharacteristicRead (gatt, characteristic, status); @@ -72,7 +84,7 @@ public override void OnCharacteristicRead (BluetoothGatt gatt, BluetoothGattChar Console.WriteLine ("OnCharacteristicRead: " + characteristic.GetStringValue (0)); this.CharacteristicValueUpdated (this, new CharacteristicReadEventArgs () { - Characteristic = new Characteristic (characteristic, gatt, this) } + Characteristic = new Characteristic (characteristic, this._device) } ); } @@ -83,9 +95,15 @@ public override void OnCharacteristicChanged (BluetoothGatt gatt, BluetoothGattC Console.WriteLine ("OnCharacteristicChanged: " + characteristic.GetStringValue (0)); this.CharacteristicValueUpdated (this, new CharacteristicReadEventArgs () { - Characteristic = new Characteristic (characteristic, gatt, this) } + Characteristic = new Characteristic (characteristic, this._device) } ); } + + public override void OnReadRemoteRssi (BluetoothGatt gatt, int rssi, GattStatus status) + { + base.OnReadRemoteRssi (gatt, rssi, status); + this._device._rssi = rssi; + } } } diff --git a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Service.cs b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Service.cs index a4db02a..8236170 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Service.cs +++ b/Source/Platform Stacks/Robotics.Mobile.Core.Droid/Bluetooth/LE/Service.cs @@ -7,22 +7,12 @@ namespace Robotics.Mobile.Core.Bluetooth.LE public class Service : IService { protected BluetoothGattService _nativeService; - /// - /// we have to keep a reference to this because Android's api is weird and requires - /// the GattServer in order to do nearly anything, including enumerating services - /// - protected BluetoothGatt _gatt; - /// - /// we also track this because of gogole's weird API. the gatt callback is where - /// we'll get notified when services are enumerated - /// - protected GattCallback _gattCallback; + protected Device _device; - public Service (BluetoothGattService nativeService, BluetoothGatt gatt, GattCallback _gattCallback) + public Service (BluetoothGattService nativeService, Device device) { this._nativeService = nativeService; - this._gatt = gatt; - this._gattCallback = _gattCallback; + this._device = device; } public Guid ID { @@ -54,7 +44,7 @@ public IList Characteristics { if (this._characteristics == null) { this._characteristics = new List (); foreach (var item in this._nativeService.Characteristics) { - this._characteristics.Add (new Characteristic (item, this._gatt, this._gattCallback)); + this._characteristics.Add (new Characteristic (item, this._device)); } } return this._characteristics; @@ -66,7 +56,7 @@ public ICharacteristic FindCharacteristic (KnownCharacteristic characteristic) //TODO: why don't we look in the internal list _chacateristics? foreach (var item in this._nativeService.Characteristics) { if ( string.Equals(item.Uuid.ToString(), characteristic.ID.ToString()) ) { - return new Characteristic(item, this._gatt, this._gattCallback); + return new Characteristic(item, this._device); } } return null; diff --git a/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Adapter.cs b/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Adapter.cs index b3d4ebf..863b729 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Adapter.cs +++ b/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Adapter.cs @@ -63,8 +63,8 @@ protected Adapter () _central.DiscoveredPeripheral += (object sender, CBDiscoveredPeripheralEventArgs e) => { Console.WriteLine ("DiscoveredPeripheral: " + e.Peripheral.Name); - Device d = new Device(e.Peripheral); - if(!ContainsDevice(this._discoveredDevices, e.Peripheral ) ){ + if(!ContainsDevice(this._discoveredDevices, e.Peripheral ) ){ + Device d = new Device(e.Peripheral); this._discoveredDevices.Add (d); this.DeviceDiscovered(this, new DeviceDiscoveredEventArgs() { Device = d }); } @@ -82,7 +82,7 @@ protected Adapter () // when a peripheral gets connected, add that peripheral to our running list of connected peripherals if(!ContainsDevice(this._connectedDevices, e.Peripheral ) ){ Device d = new Device(e.Peripheral); - this._connectedDevices.Add (new Device(e.Peripheral)); + this._connectedDevices.Add (d); // raise our connected event this.DeviceConnected ( sender, new DeviceConnectionEventArgs () { Device = d } ); } @@ -101,7 +101,12 @@ protected Adapter () this._connectedDevices.Remove(foundDevice); // raise our disconnected event - this.DeviceDisconnected (sender, new DeviceConnectionEventArgs() { Device = new Device(e.Peripheral) }); + IDevice deviceForEvent = foundDevice; + if (deviceForEvent == null) + { + deviceForEvent = new Device(e.Peripheral); + } + this.DeviceDisconnected (sender, new DeviceConnectionEventArgs() { Device = deviceForEvent }); }; _central.FailedToConnectPeripheral += (object sender, CBPeripheralErrorEventArgs e) => { diff --git a/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Characteristic.cs b/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Characteristic.cs index 9829e74..5490b88 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Characteristic.cs +++ b/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Characteristic.cs @@ -17,6 +17,7 @@ namespace Robotics.Mobile.Core.Bluetooth.LE public class Characteristic : ICharacteristic { public event EventHandler ValueUpdated = delegate {}; + public event EventHandler ValueWritten = delegate {}; protected CBCharacteristic _nativeCharacteristic; CBPeripheral _parentDevice; @@ -123,6 +124,11 @@ public void Write (byte[] data) CBCharacteristicWriteType.WithoutResponse : CBCharacteristicWriteType.WithResponse; + if (t == CBCharacteristicWriteType.WithResponse) + { + _parentDevice.WroteCharacteristicValue += this.UpdatedWrite; + } + _parentDevice.WriteValue (nsdata, descriptor, t); // Console.WriteLine ("** Characteristic.Write, Type = " + t + ", Data = " + BitConverter.ToString (data)); @@ -176,6 +182,15 @@ void UpdatedNotify(object sender, CBCharacteristicEventArgs e) { }); } + public void UpdatedWrite (object sender, CBCharacteristicEventArgs e) + { + this.ValueWritten(this, new CharacteristicWrittenEventArgs() + { + Characteristic = new Characteristic(e.Characteristic, _parentDevice) + }); + _parentDevice.WroteCharacteristicValue -= this.UpdatedWrite; + } + //TODO: this is the exact same as ServiceUuid i think public static Guid CharacteristicUuidToGuid ( CBUUID uuid) { diff --git a/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Device.cs b/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Device.cs index e06ff84..73a26ed 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Device.cs +++ b/Source/Platform Stacks/Robotics.Mobile.Core.iOS/Bluetooth/LE/Device.cs @@ -21,20 +21,6 @@ public Device (CBPeripheral nativeDevice) { this._nativeDevice = nativeDevice; - this._nativeDevice.DiscoveredService += (object sender, NSErrorEventArgs e) => { - // why we have to do this check is beyond me. if a service has been discovered, the collection - // shouldn't be null, but sometimes it is. le sigh, apple. - if (this._nativeDevice.Services != null) { - foreach (CBService s in this._nativeDevice.Services) { - Console.WriteLine ("Device.Discovered Service: " + s.Description); - if(!ServiceExists(s)) { - this._services.Add (new Service(s, this._nativeDevice)); - } - } - this.ServicesDiscovered(this, new EventArgs()); - } - }; - #if __UNIFIED__ // fixed for Unified https://bugzilla.xamarin.com/show_bug.cgi?id=14893 this._nativeDevice.DiscoveredCharacteristic += (object sender, CBServiceEventArgs e) => { @@ -115,8 +101,25 @@ public override IList Services #region public methods + public void DiscoveredService(object sender, NSErrorEventArgs e) + { + this._nativeDevice.DiscoveredService -= this.DiscoveredService; + // why we have to do this check is beyond me. if a service has been discovered, the collection + // shouldn't be null, but sometimes it is. le sigh, apple. + if (this._nativeDevice.Services != null) { + foreach (CBService s in this._nativeDevice.Services) { + Console.WriteLine ("Device.Discovered Service: " + s.Description); + if(!ServiceExists(s)) { + this._services.Add (new Service(s, this._nativeDevice)); + } + } + this.ServicesDiscovered(this, new EventArgs()); + } + } + public override void DiscoverServices () { + this._nativeDevice.DiscoveredService += this.DiscoveredService; this._nativeDevice.DiscoverServices(); } diff --git a/Source/Platform Stacks/Robotics.Mobile.Core/Bluetooth/LE/CharacteristicWrittenEventArgs.cs b/Source/Platform Stacks/Robotics.Mobile.Core/Bluetooth/LE/CharacteristicWrittenEventArgs.cs new file mode 100644 index 0000000..266c9d1 --- /dev/null +++ b/Source/Platform Stacks/Robotics.Mobile.Core/Bluetooth/LE/CharacteristicWrittenEventArgs.cs @@ -0,0 +1,13 @@ +using System; + +namespace Robotics.Mobile.Core.Bluetooth.LE +{ + public class CharacteristicWrittenEventArgs : EventArgs + { + public ICharacteristic Characteristic { get; set; } + + public CharacteristicWrittenEventArgs () + { + } + } +} \ No newline at end of file diff --git a/Source/Platform Stacks/Robotics.Mobile.Core/Bluetooth/LE/ICharacteristic.cs b/Source/Platform Stacks/Robotics.Mobile.Core/Bluetooth/LE/ICharacteristic.cs index d60a3dd..0950b8c 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core/Bluetooth/LE/ICharacteristic.cs +++ b/Source/Platform Stacks/Robotics.Mobile.Core/Bluetooth/LE/ICharacteristic.cs @@ -8,6 +8,7 @@ public interface ICharacteristic { // events event EventHandler ValueUpdated; + event EventHandler ValueWritten; // properties Guid ID { get; } diff --git a/Source/Platform Stacks/Robotics.Mobile.Core/Robotics.Mobile.Core.csproj b/Source/Platform Stacks/Robotics.Mobile.Core/Robotics.Mobile.Core.csproj index b917abc..262ea64 100644 --- a/Source/Platform Stacks/Robotics.Mobile.Core/Robotics.Mobile.Core.csproj +++ b/Source/Platform Stacks/Robotics.Mobile.Core/Robotics.Mobile.Core.csproj @@ -54,6 +54,7 @@ +