public async Task Init(GattLocalService gatt) { var ch = await gatt.CreateCharacteristicAsync( this.Uuid, new GattLocalCharacteristicParameters { CharacteristicProperties = this.ToNative(this.Properties), ReadProtectionLevel = this.Permissions.HasFlag(GattPermissions.ReadEncrypted) ? GattProtectionLevel.EncryptionAndAuthenticationRequired : GattProtectionLevel.Plain, WriteProtectionLevel = this.Permissions.HasFlag(GattPermissions.WriteEncrypted) ? GattProtectionLevel.EncryptionAndAuthenticationRequired : GattProtectionLevel.Plain, } ); foreach (var descriptor in this.Descriptors.OfType <IUwpGattDescriptor>()) { await descriptor.Init(ch.Characteristic); } this.native = ch.Characteristic; this.nativeReady.OnNext(ch.Characteristic); }
/// <summary> /// Initializes a new instance of the <see cref="MSFTReadLongCharacteristic" /> class. /// </summary> /// <param name="characteristic">Characteristic this wraps</param> public MicrosoftReadLongCharacteristic(GattLocalCharacteristic characteristic, GenericGattService service) : base(characteristic, service) { for (int i = 0; i < longCharacteristicData.Length; i++) { longCharacteristicData[i] = (byte)(i % 10); } }
/// <summary> /// Base implementation for the read callback /// </summary> /// <param name="sender"></param> /// <param name="args"></param> protected virtual void Characteristic_ReadRequested(GattLocalCharacteristic sender, GattReadRequestedEventArgs args) { // Grab the event deferral before performing any async operations in the handler. var deferral = args.GetDeferral(); Characteristic_ReadRequested(sender, args, deferral); }
/// <summary> /// Starts the Heart rate service /// </summary> public override async Task Init() { await CreateServiceProvider(GattServiceUuids.HeartRate); // Preparing the Blood pressure characteristics var heartRateCharacteristics = PlainNotifyParameters; heartRateCharacteristics.UserDescription = "Heart Rates in Beats per Minute"; heartRateCharacteristics.PresentationFormats.Add( GattPresentationFormat.FromParts( Convert.ToByte(PresentationFormats.FormatTypes.Unsigned16BitInteger), PresentationFormats.Exponent, Convert.ToUInt16(PresentationFormats.Units.PeriodBeatsPerMinute), Convert.ToByte(PresentationFormats.NamespaceId.BluetoothSigAssignedNumber), PresentationFormats.Description)); // Create the heart rate characteristic for the service GattLocalCharacteristicResult result = await ServiceProvider.Service.CreateCharacteristicAsync( GattCharacteristicUuids.HeartRateMeasurement, PlainNotifyParameters); // Grab the characterist object from the service set it to the HeartRate property which is of a specfic Characteristic type GattLocalCharacteristic baseHeartRateMeasurement = null; GattServicesHelper.GetCharacteristicsFromResult(result, ref baseHeartRateMeasurement); if (baseHeartRateMeasurement != null) { HeartRateMeasurement = new Characteristics.HeartRateMeasurementCharacteristic(baseHeartRateMeasurement, this); } }
/// <summary> /// handles Reads on the STDOUT characteristic. If the command thread is done, restart command thread. /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private async void WriteCharacteristic_WriteRequested(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args) { var deferral = args.GetDeferral(); var request = await args.GetRequestAsync(); var reader = DataReader.FromBuffer(request.Value); //string data = reader.ReadString(reader.UnconsumedBufferLength); byte[] data = new byte[reader.UnconsumedBufferLength]; reader.ReadBytes(data); if (cmd.getCmdRunning() == true) { cmd.writeStdin(data); Indicate(); } else { cmd.startCommand(); Indicate(); } if (request.Option == GattWriteOption.WriteWithResponse) { request.Respond(); } deferral.Complete(); }
/// <summary> /// Base implementation for the read callback /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private async void Characteristic_ReadRequested(GattLocalCharacteristic sender, GattReadRequestedEventArgs args) { // Grab the event deferral before performing any async operations in the handler. var deferral = args.GetDeferral(); Debug.WriteLine($"({this.GetType()})Entering base.Characteristic_ReadRequested"); // In order to get the remote request, access to the device must be provided by the user. // This can be accomplished by calling BluetoothLEDevice.RequestAccessAsync(), or by getting the request on the UX thread. // // Note that subsequent calls to RequestAccessAsync or GetRequestAsync for the same device do not need to be called on the UX thread. await CoreApplication.MainView.CoreWindow.Dispatcher.RunTaskAsync( async() => { var request = await args.GetRequestAsync(); if (request != null) { Debug.WriteLine($"Characteristic_ReadRequested - Length {request.Length}, State: {request.State}, Offset: {request.Offset}"); if (!ReadRequested(args.Session, request)) { request.RespondWithValue(Value); } } else { var Msg = new MessageDialog("There was a communication issue. As a temporary workaround, please try pairing the device with Windows first.", "BLE HackMe Comm. Issue"); await Msg.ShowAsync(); } deferral.Complete(); }); }
/// <summary> /// Starts the Heart rate service /// </summary> public override async Task Init() { await CreateServiceProvider(GattServiceUuids.HeartRate); // Preparing the heart rate characteristics var heartRateCharacteristicsParameters = PlainReadNotifyParameters; //random 4 digits added to manufacturer specific advertisement text Random rnd = new Random(); randomForDescriptor = Convert.ToString(rnd.Next(1000, 9999)); heartRateCharacteristicsParameters.UserDescription = $"{Constants.heartRateDescriptorText}{randomForDescriptor}"; // Create the heart rate characteristic for the service GattLocalCharacteristicResult result = await ServiceProvider.Service.CreateCharacteristicAsync( GattCharacteristicUuids.HeartRateMeasurement, heartRateCharacteristicsParameters); // Grab the characteristic object from the service set it to the HeartRate property which is of a specfic Characteristic type GattLocalCharacteristic baseHeartRateMeasurement = null; BLEServices.Helpers.GetCharacteristicsFromResult(result, ref baseHeartRateMeasurement); if (baseHeartRateMeasurement != null) { HeartRateMeasurement = new BLEServices.HeartRateMeasurementCharacteristic(baseHeartRateMeasurement, this); } }
/// <summary> /// Asynchronous initialization /// </summary> /// <returns>Initialization Task</returns> public override async Task Init() { await CreateServiceProvider(GattServiceUuids.Battery); // Preparing the Battery Level characteristics GattLocalCharacteristicParameters batteryCharacteristicsParameters = PlainReadParameters; // Set the user descriptions // batteryCharacteristicsParameters.UserDescription = "Battery Level percentage remaining"; // Create the characteristic for the service GattLocalCharacteristicResult result = await ServiceProvider.Service.CreateCharacteristicAsync( GattCharacteristicUuids.BatteryLevel, batteryCharacteristicsParameters); // Grab the characterist object from the service set it to the BatteryLevel property which is of a specfic Characteristic type GattLocalCharacteristic baseBatteryLevel = null; BLEServices.Helpers.GetCharacteristicsFromResult(result, ref baseBatteryLevel); if (baseBatteryLevel != null) { BatteryLevel = new BLEServices.BatteryLevelCharacteristic(baseBatteryLevel, this); } }
//--------------------------------------------------------------------- // Event callback for Write //--------------------------------------------------------------------- protected virtual void Characteristic_WriteRequested( GattLocalCharacteristic sender, GattWriteRequestedEventArgs args ) { Characteristic_WriteRequested(sender, args, args.GetDeferral()); }
// Helper function for async Read callback protected virtual async void Characteristic_ReadRequested( GattLocalCharacteristic sender, GattReadRequestedEventArgs args, Deferral deferral = null ) { Debug.WriteLine($"base.Characteristic_ReadRequested Entry"); // Get an event deferral for async operations if we don't have one if (deferral == null) { deferral = args.GetDeferral(); } // // Normally, GetRequestAsync() is recommended to run on a UI thread // because, even with paired devices, the device may prompt the // user for consent. But, we're running in a background service so // we won't run this on a UI thread. According to one of the devs // on the core Bluetooth team, because this is for a "test // application," consent prompts are not currently part of MS's // policy and it will be auto-accepted. // var request = await args.GetRequestAsync(); request.RespondWithValue(Value); Debug.WriteLine($"Characteristic ReadRequested- Length: " + $"{request.Length}, State: {request.State}, " + $"Offset: {request.Offset}" ); deferral.Complete(); Debug.WriteLine("base.Characteristic_ReadRequested Exit"); }
//--------------------------------------------------------------------- // Event callback for when number of subscribers to this characteristic // change //--------------------------------------------------------------------- protected virtual void Characteristic_SubscribedClientsChanged( GattLocalCharacteristic sender, object args ) { Debug.WriteLine("Subscribers: {0}", sender.SubscribedClients.Count()); }
/// <summary> /// Initializes a new instance of the <see cref="MSFTNotifyCharacteristic" /> class. /// </summary> /// <param name="characteristic">Characteristic this wraps</param> public MicrosoftNotifyCharacteristic(GattLocalCharacteristic characteristic, GenericGattService service) : base(characteristic, service) { DataWriter writer = new DataWriter(); writer.WriteString("Hello World!"); Value = writer.DetachBuffer(); }
private void CleanupInternal() { if (mStatus == Status.Advertise) { mServiceProvider.StopAdvertising(); } SetStatus(Status.Invalid); if (mInitializationCancelTokenSource != null) { mInitializationCancelTokenSource.Dispose(); mInitializationCancelTokenSource = null; } mUploadCharacteristic = null; mDownloadCharacteristic = null; mServiceProvider = null; mSubscribedCentrals.Clear(); // TODO mCallback = null; mFinished = true; }
/// <summary> /// Asynchronous initialization /// </summary> /// <returns>Initialization Task</returns> public override async Task Init() { await CreateServiceProvider(GattServiceUuids.Battery); // Preparing the Battery Level characteristics GattLocalCharacteristicParameters batteryCharacteristicsParameters = PlainReadNotifyParameters; // Set the user descriptions batteryCharacteristicsParameters.UserDescription = "Battery Level percentage remaining"; // Add presentation format - 16-bit integer, with exponent 0, the unit is percentage, defined per Bluetooth SIG with Microsoft as descriptor batteryCharacteristicsParameters.PresentationFormats.Add( GattPresentationFormat.FromParts( Convert.ToByte(PresentationFormats.FormatTypes.Unsigned8BitInteger), PresentationFormats.Exponent, Convert.ToUInt16(PresentationFormats.Units.Percentage), Convert.ToByte(PresentationFormats.NamespaceId.BluetoothSigAssignedNumber), PresentationFormats.Description)); // Create the characteristic for the service GattLocalCharacteristicResult result = await ServiceProvider.Service.CreateCharacteristicAsync( GattCharacteristicUuids.BatteryLevel, batteryCharacteristicsParameters); // Grab the characterist object from the service set it to the BatteryLevel property which is of a specfic Characteristic type GattLocalCharacteristic baseBatteryLevel = null; GattServicesHelper.GetCharacteristicsFromResult(result, ref baseBatteryLevel); if (baseBatteryLevel != null) { BatteryLevel = new Characteristics.BatteryLevelCharacteristic(baseBatteryLevel, this); } }
private async void ResultCharacteristic_ReadRequestedAsync(GattLocalCharacteristic sender, GattReadRequestedEventArgs args) { // BT_Code: Process a read request. using (args.GetDeferral()) { // Get the request information. This requires device access before an app can access the device's request. GattReadRequest request = await args.GetRequestAsync(); if (request == null) { // No access allowed to the device. Application should indicate this to the user. rootPage.NotifyUser("Access to device not allowed", NotifyType.ErrorMessage); return; } var writer = new DataWriter(); writer.ByteOrder = ByteOrder.LittleEndian; writer.WriteInt32(resultVal); // Can get details about the request such as the size and offset, as well as monitor the state to see if it has been completed/cancelled externally. // request.Offset // request.Length // request.State // request.StateChanged += <Handler> // Gatt code to handle the response request.RespondWithValue(writer.DetachBuffer()); } }
private async Task CreateCharacteristics(GattServiceProvider gattServiceProvider) { var statusCharacteristicParams = new GattLocalCharacteristicParameters { CharacteristicProperties = GattCharacteristicProperties.Read | GattCharacteristicProperties.Notify, WriteProtectionLevel = GattProtectionLevel.Plain, UserDescription = "Status Characteristic" }; statusCharacteristicParams.PresentationFormats.Add(GattPresentationFormat.FromParts(GattPresentationFormatTypes.Utf8, GattPresentationFormatExponent, GattPresentationFormatUnitless, GattPresentationFormatNamespaceId, GattPresentationFormatDescription)); var statusCharacteristicResult = await gattServiceProvider.Service.CreateCharacteristicAsync( BluetoothConstants.StatusCharacteristicUuid, statusCharacteristicParams); if (statusCharacteristicResult.Error != BluetoothError.Success) { throw new InvalidOperationException($"Failed to create GATT status characteristic with error {statusCharacteristicResult.Error}"); } _statusCharacteristic = statusCharacteristicResult.Characteristic; var commandCharacteristicResult = await gattServiceProvider.Service.CreateCharacteristicAsync( BluetoothConstants.CommandCharacteristicUuid, new GattLocalCharacteristicParameters { CharacteristicProperties = GattCharacteristicProperties.Write | GattCharacteristicProperties.WriteWithoutResponse, WriteProtectionLevel = GattProtectionLevel.Plain, UserDescription = "Command Characteristic" }); if (commandCharacteristicResult.Error != BluetoothError.Success) { throw new InvalidOperationException($"Failed to create GATT command characteristic with error {commandCharacteristicResult.Error}"); } var brightnessCharacteristicResult = await gattServiceProvider.Service.CreateCharacteristicAsync( BluetoothConstants.BrightnessCharacteristicUuid, new GattLocalCharacteristicParameters { CharacteristicProperties = GattCharacteristicProperties.Write | GattCharacteristicProperties.WriteWithoutResponse, WriteProtectionLevel = GattProtectionLevel.Plain, UserDescription = "Brightness Characteristic" }); if (brightnessCharacteristicResult.Error != BluetoothError.Success) { throw new InvalidOperationException($"Failed to create GATT brightness characteristic with error {brightnessCharacteristicResult.Error}"); } _statusCharacteristic.ReadRequested += StatusCharacteristic_ReadRequested; commandCharacteristicResult.Characteristic.WriteRequested += CommandCharacteristic_WriteRequested; brightnessCharacteristicResult.Characteristic.WriteRequested += BrightnessCharacteristic_WriteRequested; }
private async void WifiListCharacteristic_ReadRequestedAsync(GattLocalCharacteristic sender, GattReadRequestedEventArgs args) { using (args.GetDeferral()) { // Get the request information. This requires device access before an app can access the device's request. GattReadRequest request = await args.GetRequestAsync(); if (request == null) { // No access allowed to the device. Application should indicate this to the user. return; } var writer = new DataWriter(); var result = JsonConvert.SerializeObject( new WIfiNetworkPayload { AvailableAdapters = this.WiFiAdapter.NetworkReport.AvailableNetworks.Select(x => new WifiNetwork { Ssid = x.Ssid }) }); writer.WriteString(result); //var wifiAdapters = //writer.WriteString("{ \"AvailableAdapters\": [{ \"Ssid\": \"ilab\" }, { \"Ssid\": \"uqconnect\" }] }"); // Gatt code to handle the response request.RespondWithValue(writer.DetachBuffer()); } }
private async void writeRemoteCanStop(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args) { var deferral = args.GetDeferral(); var request = await args.GetRequestAsync(); if (request == null) { Debug.WriteLine("Write of Can Stop had a null request! Make sure the application manifest allows Bluetooth access."); deferral.Complete(); return; } // The "Can Stop" characteristic is a single byte of data - 0 for false, and 1 for true. var reader = DataReader.FromBuffer(request.Value); if (reader.UnconsumedBufferLength > 0) { var value = reader.ReadByte(); await eventDispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { canStop = value > 0; Debug.WriteLine("Can Stop is now: {0}", canStop); StateChanged?.Invoke(this, null); }); } if (request.Option == GattWriteOption.WriteWithResponse) { request.Respond(); } deferral.Complete(); }
public void Dispose() { // Windows BLE seems to get unreliable if services etc aren't disposed correctly. if (continueButtonCharacteristic != null) { continueButtonCharacteristic.ReadRequested -= readContinueButtonValue; continueButtonCharacteristic.SubscribedClientsChanged -= subscribedClientsChanged; continueButtonCharacteristic = null; } if (stopButtonCharacteristic != null) { stopButtonCharacteristic.ReadRequested -= readStopButtonValue; stopButtonCharacteristic = null; } if (remoteCanContinueCharacteristic != null) { remoteCanContinueCharacteristic.WriteRequested -= writeRemoteCanContinue; remoteCanContinueCharacteristic = null; } if (remoteCanStopCharacteristic != null) { remoteCanStopCharacteristic.WriteRequested -= writeRemoteCanStop; remoteCanStopCharacteristic = null; } if (userMessageCharacteristic != null) { userMessageCharacteristic.WriteRequested -= writeUserMessage; userMessageCharacteristic = null; } if (cascableService != null) { cascableService.StopAdvertising(); cascableService = null; } }
private void OnCharacteristicSubscribedClientsChanged(GattLocalCharacteristic characteristic, object args) { Utils.Info("OnCharacteristicSubscribedClientsChanged"); // TODO var unsubscribedCentrals = mSubscribedCentrals.Where(ctx => !characteristic.SubscribedClients.Contains(ctx.client)).ToList(); // Unsubscribed foreach (var context in unsubscribedCentrals) { Utils.Info("unsubscribed: {0}", context.client.Session.DeviceId.Id); RemoveCentral(context); } foreach (var client in characteristic.SubscribedClients) { if (!mSubscribedCentrals.Exists(ctx => { return(ctx.client == client); })) // Subscribed { Utils.Info("subscribed: {0}", client.Session.DeviceId.Id); client.Session.SessionStatusChanged += OnSessionStatusChanged; var context = new CentralContext(client); // TODO: timeout mSubscribedCentrals.Add(context); } } }
protected override void Characteristic_SubscribedClientsChanged(GattLocalCharacteristic sender, object args) { lock (this) { if (sender.SubscribedClients.Count == 0) { if (m_timeUpdate != null) { m_timeUpdate.Cancel(); m_timeUpdate = null; } } else if (m_timeUpdate == null) { m_timeUpdate = ThreadPoolTimer.CreatePeriodicTimer( (source) => { UpdateCurrentTimeValue(); NotifyValue(); }, TimeSpan.FromMinutes(15)); } } base.Characteristic_SubscribedClientsChanged(sender, args); }
private async void OnWriteMessage(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args) { var deferral = args.GetDeferral(); try { var req = await args.GetRequestAsync(); var reader = DataReader.FromBuffer(req.Value); var buf = new byte[reader.UnconsumedBufferLength]; reader.ReadBytes(buf); var xml = DMessenger.MessageEncoder.Decode(buf); var msg = new DMessenger.Message(xml); if (req.Option == GattWriteOption.WriteWithResponse) { req.Respond(); } } finally { deferral.Complete(); } }
private void HandleError() { mUploadCharacteristic = null; mDownloadCharacteristic = null; mServiceProvider = null; mCallback.OnFail(); }
private async void subscribedClientsChanged(GattLocalCharacteristic sender, object args) { await eventDispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { Debug.WriteLine("Subscribed clients changed!!"); StateChanged?.Invoke(this, null); }); }
private async void Op1Characteristic_WriteRequestedAsync(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args) { // BT_Code: Processing a write request. using (args.GetDeferral()) { PlayMusic(); } }
//--------------------------------------------------------------------- // Write request callback to receive the header length of an IPv6 // packet that had its header compressed //--------------------------------------------------------------------- protected override void Characteristic_WriteRequested( GattLocalCharacteristic sender, GattWriteRequestedEventArgs args ) { // Receive the Write request into this characteristic's Value buffer base.Characteristic_WriteRequested(sender, args); }
/// <summary> /// Initializes a new instance of the <see cref="BatteryLevelCharacteristic" /> class. /// </summary> /// <param name="characteristic">The characteristic that this wraps</param> public BatteryLevelCharacteristic(GattLocalCharacteristic characteristic, GenericGattService service) : base(characteristic, service) { // generate random value 1-100% Random rnd = new Random(); batteryLevelIndication = rnd.Next(1, 100); Value = BLEServices.Helpers.ToIBuffer(batteryLevelIndication); }
internal async Task <bool> Initialize() { try { GattServiceProviderResult result = await GattServiceProvider.CreateAsync(_serviceId); if (result.Error != BluetoothError.Success) { Robot.Message("can't create GATT BlueTooth service with id " + _serviceId.ToString(), MessageType.Error); Robot.Message("without bluetooth service, remote controller won't work", MessageType.Warning); return(_init); } _service = result.ServiceProvider; byte[] value = new byte[] { 0x21 }; var constantParameters = new GattLocalCharacteristicParameters { CharacteristicProperties = (GattCharacteristicProperties.Read), StaticValue = value.AsBuffer(), ReadProtectionLevel = GattProtectionLevel.Plain, }; GattLocalCharacteristicResult characteristicResult = await _service.Service.CreateCharacteristicAsync(_notifyId, new GattLocalCharacteristicParameters { CharacteristicProperties = GattCharacteristicProperties.Notify, ReadProtectionLevel = GattProtectionLevel.Plain, StaticValue = value.AsBuffer() }); if (characteristicResult.Error != BluetoothError.Success) { Robot.Message("can't create GATT BlueTooth service with id " + _serviceId.ToString(), MessageType.Error); Robot.Message("without bluetooth service, remote controller won't work", MessageType.Warning); return(_init); } _notifyCharacteristic = characteristicResult.Characteristic; //_notifyCharacteristic.SubscribedClientsChanged += _btNotify_SubscribedClientsChanged; GattServiceProviderAdvertisingParameters advParameters = new GattServiceProviderAdvertisingParameters { IsDiscoverable = true, IsConnectable = true }; _service.StartAdvertising(advParameters); Robot.Message("created Bluetooth GATT service with id " + _serviceId.ToString(), MessageType.Success); _init = true; } catch (Exception x) { while (x != null) { Robot.Message(x.Message, MessageType.Error); x = x.InnerException; } Robot.Message("without bluetooth service, remote controller won't work", MessageType.Warning); } return(_init); }
/// <summary> /// Base implementation for the write callback /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private async void Characteristic_WriteRequested(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args) { Debug.WriteLine("Characteristic_WriteRequested: Write Requested"); // Grab the event deferral before performing any async operations in the handler. var deferral = args.GetDeferral(); // In order to get the remote request, access to the device must be provided by the user. // This can be accomplished by calling BluetoothLEDevice.RequestAccessAsync(), or by getting the request on the UX thread. // // Note that subsequent calls to RequestAccessAsync or GetRequestAsync for the same device do not need to be called on the UX thread. await CoreApplication.MainView.CoreWindow.Dispatcher.RunTaskAsync( async() => { // Grab the request var request = await args.GetRequestAsync(); Debug.WriteLine($"Characteristic_WriteRequested - Length {request.Value.Length}, State: {request.State}, Offset: {request.Offset}"); // WriteRequested overriden if needed to set up the Value // if not - set the Value here if (!WriteRequested(args.Session, request)) { // Set the characteristic Value Value = request.Value; } else { Debug.WriteLine("Characteristic_WriteRequested: Looks like write completed in derived class"); } // Complete request (respond with confirmation in case of WRITE REQ) even if the overriden WriteRequested did not change the Value. // As side effect this will show in nRF Connect that the value has changed even if it hasn't. if (request.Option == GattWriteOption.WriteWithResponse) { Debug.WriteLine("Characteristic_WriteRequested: Completing request with responds"); request.Respond(); } else { Debug.WriteLine("Characteristic_WriteRequested: Completing request without responds"); } byte[] data; CryptographicBuffer.CopyToByteArray(Value, out data); if (data == null) { Debug.WriteLine("Characteristic_WriteRequested: Value after write complete was NULL"); } else { Debug.WriteLine($"Characteristic_WriteRequested: New Value: {data.BytesToString()}"); } deferral.Complete(); }); }
/// <summary> /// Read request callback to update the value /// </summary> /// <param name="sender"></param> /// <param name="args"></param> protected override async void Characteristic_ReadRequested(GattLocalCharacteristic sender, GattReadRequestedEventArgs args) { Deferral deferral = args.GetDeferral(); Debug.WriteLine($"MSFTLongReadCharacteristic: ReadRequested - MaxPduSize {args.Session.MaxPduSize}"); await UpdateValue(args); base.Characteristic_ReadRequested(sender, args, deferral); }