/// <summary> /// Used for writing blobs /// </summary> /// <param name="ch">The characteristic to write on</param> /// <param name="stream">The stream to send</param> /// <param name="reliableWrite">Use reliable write atomic writing if available (windows and android)</param> public static IObservable <BleWriteSegment> BlobWrite(this IGattCharacteristic ch, Stream stream, bool reliableWrite) => Observable.Create <BleWriteSegment>(async ob => { var cts = new CancellationTokenSource(); var trans = reliableWrite ? ch.Service.Device.BeginReliableWriteTransaction() : new VoidGattReliableWriteTransaction(); using (trans) { var mtu = ch.Service.Device.MtuSize; var buffer = new byte[mtu]; var read = stream.Read(buffer, 0, buffer.Length); var pos = read; var len = Convert.ToInt32(stream.Length); while (!cts.IsCancellationRequested && read > 0) { await trans .Write(ch, buffer) .ToTask(cts.Token) .ConfigureAwait(false); //if (this.Value != buffer) //{ // trans.Abort(); // throw new GattReliableWriteTransactionException("There was a mismatch response"); //} var seg = new BleWriteSegment(buffer, pos, len); ob.OnNext(seg); read = stream.Read(buffer, 0, buffer.Length); if (read > 0 && read < buffer.Length) { for (var index = read; index < buffer.Length; index++) { buffer[index] = 0; } } pos += read; } await trans.Commit(); } ob.OnCompleted(); return(() => { cts.Cancel(); trans.Dispose(); }); });
public WriteRequest(IGattCharacteristic characteristic, IPeripheral peripheral, byte[] data, int offset, bool isReplyNeeded) { this.Characteristic = characteristic; this.Peripheral = peripheral; this.Offset = offset; this.IsReplyNeeded = isReplyNeeded; this.Data = data; }
public static void AssertWrite(this IGattCharacteristic characteristic, bool withResponse) { if (!characteristic.CanWrite()) { throw new ArgumentException($"This characteristic '{characteristic.Uuid}' does not support writes"); } if (withResponse && !characteristic.CanWriteWithResponse()) { throw new ArgumentException($"This characteristic '{characteristic.Uuid}' does not support writes with response"); } }
public static IObservable <byte[]> ReadInterval(this IGattCharacteristic character, TimeSpan timeSpan) { return(Observable.Create <byte[]>(ob => Observable .Interval(timeSpan) .Subscribe(async _ => { var read = await character.Read(); ob.OnNext(read); }) )); }
public override IObservable <GattCharacteristicResult> Write(IGattCharacteristic characteristic, byte[] value) { this.AssertAction(); if (!(characteristic is GattCharacteristic platform)) { throw new ArgumentException("Characteristic must be UWP type"); } this.native.WriteValue(platform.Native, value.AsBuffer()); return(Observable.Return(new GattCharacteristicResult(characteristic, value, GattCharacteristicResultType.Write))); }
public override IObservable <CharacteristicGattResult> Write(IGattCharacteristic characteristic, byte[] value) { this.AssertAction(); if (!(characteristic is GattCharacteristic platform)) { throw new ArgumentException("Characteristic must be UWP type"); } // TODO: need write observable this.native.WriteValue(platform.Native, null); return(null); }
protected override async Task <bool> ValidateServicesAsync(IEnumerable <IGattService> services, CancellationToken token) { var service = services?.FirstOrDefault(s => s.Uuid == SERVICE_UUID); _characteristic = service?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID); if (_characteristic != null) { return(await _bleDevice?.EnableNotificationAsync(_characteristic, token)); } return(false); }
protected override Task <bool> ValidateServicesAsync(IEnumerable <IGattService> services, CancellationToken token) { var service = services?.FirstOrDefault(s => s.Uuid == SERVICE_UUID); _characteristic = service?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID); var deviceInformationService = services?.FirstOrDefault(s => s.Uuid == SERVICE_UUID_DEVICE_INFORMATION); _firmwareRevisionCharacteristic = deviceInformationService?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_FIRMWARE_REVISION); _modelNumberCharacteristic = deviceInformationService?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_MODEL_NUMBER); return(Task.FromResult(_characteristic != null && _firmwareRevisionCharacteristic != null && _modelNumberCharacteristic != null)); }
public bool WriteNoResponse(IGattCharacteristic characteristic, byte[] data) { if (State != BluetoothLEDeviceState.Connected) { return(false); } var nativeCharacteristic = ((GattCharacteristic)characteristic).Characteristic; var nativeData = NSData.FromArray(data); _peripheral.WriteValue(nativeData, nativeCharacteristic, CBCharacteristicWriteType.WithoutResponse); return(true); }
private static async Task SendChallengeResponse(IGattCharacteristic authCharacteristic, byte[] challenge) { var challengePayload = new byte[ChallengePayloadLength]; Buffer.BlockCopy(challenge, ChallengeHeaderLength, challengePayload, 0, challengePayload.Length); var responsePayload = Encrypt(StaticEncryptionKey, challengePayload); var responseHeader = new byte[] { 0x03, 0x00 }; var response = new byte[responseHeader.Length + responsePayload.Length]; Buffer.BlockCopy(responseHeader, 0, response, 0, responseHeader.Length); Buffer.BlockCopy(responsePayload, 0, response, responseHeader.Length, responsePayload.Length); await authCharacteristic.WriteWithoutResponse(response); }
/// <summary> /// Called when a new characteristic is discovered /// </summary> /// <param name="characteristic">The characteristic</param> private void OnCharacteristicDiscovered(IGattCharacteristic characteristic) { var id = characteristic.Uuid; if (id == Constants.ImageCharacteristic) { this.imageCharacteristic = characteristic; this.NotifyCanSendImages?.Invoke(); } else if (id == Constants.ImageReceivedCharacteristic) { this.imageReceivedCharacteristic = characteristic; } }
public static IObservable <byte[]> WhenReadOrNotify(this IGattCharacteristic character, TimeSpan readInterval) { if (character.CanNotify()) { return(character.SubscribeToNotifications()); } if (character.CanRead()) { return(character.ReadInterval(readInterval)); } throw new ArgumentException($"Characteristic {character.Uuid} does not have read or notify permissions"); }
public override IObservable <CharacteristicResult> Write(IGattCharacteristic characteristic, byte[] value) { this.AssertAction(); var platform = characteristic as GattCharacteristic; if (platform == null) { throw new ArgumentException(""); } // TODO: need write observable this.native.WriteValue(platform.Native, null); return(null); }
private async Task <IGattCharacteristic> GetGattCharacteristicAsync(Tuple <Guid, Guid> gattChar) { IGattCharacteristic characteristic = null; if (gattChar.Item1 == MbientLab.MetaWear.Constants.METAWEAR_GATT_SERVICE && _advertisementCharacteristics != null) { characteristic = _advertisementCharacteristics.FirstOrDefault(x => x.Uuid == gattChar.Item2); } else { characteristic = await device.GetKnownCharacteristics(gattChar.Item1, gattChar.Item2).FirstAsync(); } return(characteristic); }
public Task <bool> WriteNoResponseAsync(IGattCharacteristic characteristic, byte[] data, CancellationToken token) { lock (_lock) { if (State != BluetoothLEDeviceState.Connected) { return(Task.FromResult(false)); } var nativeCharacteristic = ((GattCharacteristic)characteristic).Characteristic; var nativeData = NSData.FromArray(data); _peripheral.WriteValue(nativeData, nativeCharacteristic, CBCharacteristicWriteType.WithoutResponse); return(Task.FromResult(true)); } }
/// <summary> /// Enables notifications and hooks it for discovered characteristic. When subscription is disposed, it will also clean up. /// </summary> /// <param name="characteristic"></param> /// <param name="useIndicationIfAvailable"></param> /// <returns></returns> public static IObservable <CharacteristicGattResult> RegisterAndNotify(this IGattCharacteristic characteristic, bool useIndicationIfAvailable = false) => characteristic .EnableNotifications(useIndicationIfAvailable) .Select(x => x.Characteristic.WhenNotificationReceived()) .Switch() .Finally(() => characteristic .DisableNotifications() .Where(x => x.Characteristic.Service.Device.Status == ConnectionStatus.Connected) .Subscribe( _ => {}, ex => Log.Warn( BleLogCategory.Characteristic, "Could not cleanly disable notifications in RegisterAndNotify - " + ex ) ) );
protected override Task <bool> ValidateServicesAsync(IEnumerable <IGattService> services, CancellationToken token) { var deviceInformationService = services?.FirstOrDefault(s => s.Uuid == SERVICE_UUID_DEVICE_INFORMATION); _firmwareRevisionCharacteristic = deviceInformationService?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_FIRMWARE_REVISION); _hardwareRevisionCharacteristic = deviceInformationService?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_HARDWARE_REVISION); var remoteControlService = services?.FirstOrDefault(s => s.Uuid == SERVICE_UUID_REMOTE_CONTROL); _remoteControlCharacteristic = remoteControlService?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_REMOTE_CONTROL); _quickDriveCharacteristic = remoteControlService?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_QUICK_DRIVE); return(Task.FromResult( _firmwareRevisionCharacteristic != null && _hardwareRevisionCharacteristic != null && _remoteControlCharacteristic != null && _quickDriveCharacteristic != null)); }
public static IObservable <CharacteristicResult> WhenReadOrNotify(this IGattCharacteristic character, TimeSpan readInterval) { if (character.CanNotify()) { return(character .EnableNotifications() .Where(x => x) .Select(x => character.WhenNotificationReceived()) .Switch()); } if (character.CanRead()) { return(character.ReadInterval(readInterval)); } throw new ArgumentException($"Characteristic {character.Uuid} does not have read or notify permissions"); }
/// <summary> /// Write bytes through communication interface /// </summary> public override void WriteBytes(byte[] buffer, int offset, int count) { if (_bleDevice == null) { throw new ArgumentNullException("_bleDevice"); } _cmdTx = buffer[2]; int loopCnt = 0; while (loopCnt <= _maxAutoRetries) { loopCnt++; try { IGattCharacteristic characteristic = _bleDevice.GetCharacteristicsForService(ServiceUuid) .FirstOrDefaultAsync(x => x.Uuid == TxCharacteristicUuid).Wait(); if (null != characteristic) { Stopwatch watch = Stopwatch.StartNew(); characteristic.WriteWithoutResponse(buffer).Wait(); watch.Stop(); _stopWatch.Restart(); lock (_lockDebugInfo) { if (_writeTimeMs.Count >= 1000) { _writeTimeMs.RemoveAt(0); } _writeTimeMs.Add(watch.ElapsedMilliseconds); } break; } } catch (Exception) { Thread.Sleep(75); } } }
protected override async Task <bool> ValidateServicesAsync(IEnumerable <IGattService> services, CancellationToken token) { var service = services?.FirstOrDefault(s => s.Uuid == SERVICE_UUID); _writeCharacteristic = service?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_WRITE); var deviceInformationService = services?.FirstOrDefault(s => s.Uuid == SERVICE_UUID_DEVICE_INFORMATION); _firmwareRevisionCharacteristic = deviceInformationService?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_FIRMWARE_REVISION); _hardwareRevisionCharacteristic = deviceInformationService?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_HARDWARE_REVISION); _notifyCharacteristic = service?.Characteristics?.FirstOrDefault(c => c.Uuid == CHARACTERISTIC_UUID_NOTIFY); if (_notifyCharacteristic != null) { await _bleDevice?.EnableNotificationAsync(_notifyCharacteristic, token); } return(_writeCharacteristic != null && _firmwareRevisionCharacteristic != null && _hardwareRevisionCharacteristic != null); }
public void ConnectPrinter(IPeripheral selectedPeripheral) { if (!selectedPeripheral.IsConnected()) { selectedPeripheral.Connect(); } _perifDisposable = selectedPeripheral.WhenAnyCharacteristicDiscovered().Subscribe((characteristic) => { if (characteristic.CanWrite() && !characteristic.CanRead() && !characteristic.CanNotify()) { _savedCharacteristic = characteristic; IsReady = true; _perifDisposable.Dispose(); } }); }
public void Disconnect() { if (ConnectState != DeviceState.Disconnecting || ConnectState != DeviceState.Disconnected) { try { readCharacteristicObserver?.Dispose(); } catch (Exception e) { // Crashes.TrackError(e); } try { chararacteristicListener?.Dispose(); } catch (Exception e) { // Crashes.TrackError(e); } try { stateListener?.Dispose(); } catch (Exception e) { // Crashes.TrackError(e); } try { device.CancelConnection(); } catch (Exception e) { // Crashes.TrackError(e); } readCharacteristic = null; writeCharacteristic = null; } }
/// <summary> /// Set Packet Receipt Notification (PRN) value. /// Sets the number of packets to be sent before receiving a Packet Receipt Notification. The default is 0. /// </summary> /// <param name="device"></param> /// <param name="prn"></param> /// <returns></returns> private async Task SetPRN(IGattCharacteristic controlPoint, int prn) { // PRN value should be LSB byte[] data = new byte[] { CSetPRN, 0, 0 }; SetUint16LSB(data, 1, prn); var notif = GetTimedNotification(controlPoint); await controlPoint.Write(data).Timeout(OperationTimeout); var result = await notif.Task; Debug.WriteLineIf(LogLevelDebug, String.Format("Received PRN set response: {0}", BitConverter.ToString(result.Data))); AssertSuccess(result); }
private void Button_ScanService(object sender, RoutedEventArgs e) { currdev.DiscoverServices().Subscribe(mService => { IGattService findServ = mService; findServ.DiscoverCharacteristics().Subscribe(mCharacter => { Debug.WriteLine("FindCharacter OK:" + mCharacter.Uuid); if (mCharacter.Uuid == (new Guid("48EB9002-F352-5FA0-9B06-8FCAA22602CF"))) { Debug.WriteLine("找到了 OK:"); FindCharacter = mCharacter; } } ); //Debug.WriteLine("mService OK"); }); }
public async Task <bool> WriteAsync(IGattCharacteristic characteristic, byte[] data, CancellationToken token) { using (token.Register(() => { lock (_lock) { _writeCompletionSource?.TrySetResult(false); } })) { lock (_lock) { if (_bluetoothGatt == null || State != BluetoothLEDeviceState.Connected) { return(false); } var nativeCharacteristic = ((GattCharacteristic)characteristic).BluetoothGattCharacteristic; nativeCharacteristic.WriteType = GattWriteType.Default; if (!nativeCharacteristic.SetValue(data)) { return(false); } _writeCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); if (!_bluetoothGatt.WriteCharacteristic(nativeCharacteristic)) { _writeCompletionSource = null; return(false); } } var result = await _writeCompletionSource.Task.ConfigureAwait(false); lock (_lock) { _writeCompletionSource = null; return(result); } } }
public Device(Plugin.BluetoothLE.IDevice dev) { device = dev; Name = dev.Name; chararacteristicListener = device.WhenAnyCharacteristicDiscovered().Subscribe(characteristic => { if (characteristic.Uuid.ToString() == BleService.CharacteristictReadUuid) { readCharacteristic = characteristic; OnStateChanged(DeviceState.Connected); RaisePropertyChanged(nameof(ConnectState)); if (readCharacteristic.CanNotify()) { readCharacteristicObserver = characteristic.RegisterAndNotify(true).Subscribe(r => { Read?.Invoke(r.Data); }, exception => { // Crashes.TrackError(exception); }); } } if (characteristic.Uuid.ToString() == BleService.CharacteristicWriteUuid) { if (characteristic.CanWrite()) { writeCharacteristic = characteristic; OnStateChanged(DeviceState.Connected); RaisePropertyChanged(nameof(ConnectState)); } } }, exception => { // Crashes.TrackError(exception); }); stateListener = device.WhenStatusChanged().Subscribe(f => { OnStateChanged(ConnectState); }); }
public static IObservable <BleWriteSegment> WriteBlobTransaction(this IGattCharacteristic ch, Stream stream) => Observable.Create <BleWriteSegment>(async(ob, ct) => { var trans = ch.Service.Peripheral.TryBeginTransaction(); using (trans) { var mtu = ch.Service.Peripheral.MtuSize; var buffer = new byte[mtu]; var read = stream.Read(buffer, 0, buffer.Length); var pos = read; var len = Convert.ToInt32(stream.Length); var remaining = 0; while (!ct.IsCancellationRequested && read > 0) { await trans .Write(ch, buffer) .ToTask(ct) .ConfigureAwait(false); //if (this.Value != buffer) //{ // trans.Abort(); // throw new GattReliableWriteTransactionException("There was a mismatch response"); //} var seg = new BleWriteSegment(buffer, pos, len); ob.OnNext(seg); remaining = len - pos; if (remaining > 0 && remaining < mtu) { // readjust buffer -- we don't want to send extra garbage buffer = new byte[remaining]; } read = stream.Read(buffer, 0, buffer.Length); pos += read; } await trans.Commit(); } ob.OnCompleted(); return(trans); });
void ConnectPrinter(IPeripheral selectedPeripheral) { if (!selectedPeripheral.IsConnected()) { selectedPeripheral.Connect(); } _perifDisposable = selectedPeripheral.WhenAnyCharacteristicDiscovered().Subscribe((characteristic) => { //System.Diagnostics.Debug.WriteLine(characteristic.Description); //this is not suppported at this momment, and no neccesary I guess if (characteristic.CanWrite() && !characteristic.CanRead() && !characteristic.CanNotify()) { IsReadyToPrint = true; _savedCharacteristic = characteristic; System.Diagnostics.Debug.WriteLine($"Writing {characteristic.Uuid} - {characteristic.CanRead()} - {characteristic.CanIndicate()} - {characteristic.CanNotify()}"); _perifDisposable.Dispose(); } }); }
/// <summary> /// Request and read DFU checksum from control point after data have been sent /// </summary> /// <param name="device"></param> /// <returns></returns> private async Task <ObjectChecksum> ReadChecksum(IGattCharacteristic controlPoint) { Debug.WriteLineIf(LogLevelDebug, String.Format("Begin read checksum")); ObjectChecksum checksum = new ObjectChecksum(); CharacteristicGattResult result = null; for (var retry = 0; retry < MaxRetries; retry++) { try { // Request checksum var notif = GetTimedNotification(controlPoint); var re = await controlPoint.Write(CCalculateCRC).Timeout(OperationTimeout); Debug.WriteLine(re); result = await notif.Task; break; } catch (Exception) { await Task.Delay(100); } finally { if (retry == MaxRetries) { throw new DFUTimeout(); } } } Debug.WriteLineIf(LogLevelDebug, String.Format("Check Sum Response {0}", BitConverter.ToString(result.Data))); AssertSuccess(result); SetChecksum(checksum, result.Data); Debug.WriteLineIf(LogLevelDebug, String.Format("End read checksum")); return(checksum); }
private void ListView_ItemTapped(object sender, ItemTappedEventArgs e) { var select = (CharacteristicsList)e.Item; foreach (var c in AllCharacteristics) { if (c.Uuid == select.Uuid) { SelectCharacteristic = c; info_uuid.Text = "UUID:" + SelectCharacteristic.Uuid; info_read.Text = "CallBack UUID:"; notify_btn.Text = "Notify"; if (SelectCharacteristic.CanRead()) { read_btn.IsVisible = true; } else { read_btn.IsVisible = false; } if (SelectCharacteristic.CanWrite()) { write_btn.IsVisible = true; } else { write_btn.IsVisible = false; } if (SelectCharacteristic.CanNotify()) { notify_btn.IsVisible = true; } else { notify_btn.IsVisible = false; } background.IsVisible = true; info.IsVisible = true; break; } } }