/// <summary> /// This function reads data from the specific BLE characteristic /// </summary> /// <param name="param"></param> //static async Task<int> ReadCharacteristic(string param) //{ // int retVal = 0; // if (_selectedDevice != null) // { // if (!string.IsNullOrEmpty(param)) // { // List<BluetoothLEAttributeDisplay> chars = new List<BluetoothLEAttributeDisplay>(); // string charName = string.Empty; // var parts = param.Split('/'); // // Do we have parameter is in "service/characteristic" format? // if (parts.Length == 2) // { // string serviceName = Utilities.GetIdByNameOrNumber(_services, parts[0]); // charName = parts[1]; // // If device is found, connect to device and enumerate all services // if (!string.IsNullOrEmpty(serviceName)) // { // var attr = _services.FirstOrDefault(s => s.Name.Equals(serviceName)); // IReadOnlyList<GattCharacteristic> characteristics = new List<GattCharacteristic>(); // try // { // // Ensure we have access to the device. // var accessStatus = await attr.service.RequestAccessAsync(); // if (accessStatus == DeviceAccessStatus.Allowed) // { // var result = await attr.service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); // if (result.Status == GattCommunicationStatus.Success) // characteristics = result.Characteristics; // } // } // catch (Exception ex) // { // Console.WriteLine($"Restricted service. Can't read characteristics: {ex.Message}"); // retVal += 1; // } // foreach (var c in characteristics) // chars.Add(new BluetoothLEAttributeDisplay(c)); // } // } // else if (parts.Length == 1) // { // if (_selectedService == null) // { // if(!Console.IsOutputRedirected) // Console.WriteLine("No service is selected."); // } // chars = new List<BluetoothLEAttributeDisplay>(_characteristics); // charName = parts[0]; // } // // Read characteristic // if (chars.Count > 0 && !string.IsNullOrEmpty(charName)) // { // string useName = Utilities.GetIdByNameOrNumber(chars, charName); // var attr = chars.FirstOrDefault(c => c.Name.Equals(useName)); // if (attr != null && attr.characteristic != null) // { // // Read characteristic value // GattReadResult result = await attr.characteristic.ReadValueAsync(BluetoothCacheMode.Uncached); // if (result.Status == GattCommunicationStatus.Success) // Console.WriteLine(Utilities.FormatValue(result.Value, _dataFormat)); // else // { // Console.WriteLine($"Read failed: {result.Status}"); // retVal += 1; // } // } // else // { // Console.WriteLine($"Invalid characteristic {charName}"); // retVal += 1; // } // } // else // { // Console.WriteLine("Nothing to read, please specify characteristic name or #."); // retVal += 1; // } // } // else // { // Console.WriteLine("Nothing to read, please specify characteristic name or #."); // retVal += 1; // } // } // else // { // Console.WriteLine("No BLE device connected."); // retVal += 1; // } // return retVal; //} /// <summary> /// This function writes data from the specific BLE characteristic /// </summary> /// <param name="param"> /// parameters should be: /// [char_name] or [service_name/char_name] - specific characteristics /// [data_value] - data to write; data will be interpreted depending of current display format, /// wrong data format will cause write fail /// </param> /// <param name="userInput"> /// we need whole user input (trimmed from spaces on beginning) in case of text input with spaces at the end //static async Task<int> WriteCharacteristic(string param) //{ // int retVal = 0; // if (_selectedDevice != null) // { // if (!string.IsNullOrEmpty(param)) // { // List<BluetoothLEAttributeDisplay> chars = new List<BluetoothLEAttributeDisplay>(); // string charName = string.Empty; // // First, split data from char name (it should be a second param) // var parts = param.Split(' '); // if (parts.Length < 2) // { // Console.WriteLine("Insufficient data for write, please provide characteristic name and data."); // retVal += 1; // return retVal; // } // // Now try to convert data to the byte array by current format // string data = param.Substring(parts[0].Length + 1); // if (string.IsNullOrEmpty(data)) // { // Console.WriteLine("Insufficient data for write."); // retVal += 1; // return retVal; // } // var buffer = Utilities.FormatData(data, _dataFormat); // if (buffer != null) // { // // Now process service/characteristic names // var charNames = parts[0].Split('/'); // // Do we have parameter is in "service/characteristic" format? // if (charNames.Length == 2) // { // string serviceName = Utilities.GetIdByNameOrNumber(_services, charNames[0]); // charName = charNames[1]; // // If device is found, connect to device and enumerate all services // if (!string.IsNullOrEmpty(serviceName)) // { // var attr = _services.FirstOrDefault(s => s.Name.Equals(serviceName)); // IReadOnlyList<GattCharacteristic> characteristics = new List<GattCharacteristic>(); // try // { // // Ensure we have access to the device. // var accessStatus = await attr.service.RequestAccessAsync(); // if (accessStatus == DeviceAccessStatus.Allowed) // { // var result = await attr.service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); // if (result.Status == GattCommunicationStatus.Success) // characteristics = result.Characteristics; // } // foreach (var c in characteristics) // chars.Add(new BluetoothLEAttributeDisplay(c)); // } // catch (Exception ex) // { // Console.WriteLine($"Restricted service. Can't read characteristics: {ex.Message}"); // retVal += 1; // return retVal; // } // } // } // else if (charNames.Length == 1) // { // if (_selectedService == null) // { // if(!Console.IsOutputRedirected) // Console.WriteLine("No service is selected."); // retVal += 1; // } // chars = new List<BluetoothLEAttributeDisplay>(_characteristics); // charName = parts[0]; // } // // Write characteristic // if (chars.Count > 0 && !string.IsNullOrEmpty(charName)) // { // string useName = Utilities.GetIdByNameOrNumber(chars, charName); // var attr = chars.FirstOrDefault(c => c.Name.Equals(useName)); // if (attr != null && attr.characteristic != null) // { // // Write data to characteristic // GattWriteResult result = await attr.characteristic.WriteValueWithResultAsync(buffer); // if (result.Status != GattCommunicationStatus.Success) // { // if (!Console.IsOutputRedirected) // Console.WriteLine($"Write failed: {result.Status}"); // retVal += 1; // } // } // else // { // if (!Console.IsOutputRedirected) // Console.WriteLine($"Invalid characteristic {charName}"); // retVal += 1; // } // } // else // { // if (!Console.IsOutputRedirected) // Console.WriteLine("Please specify characteristic name or # for writing."); // retVal += 1; // } // } // else // { // if (!Console.IsOutputRedirected) // Console.WriteLine("Incorrect data format."); // retVal += 1; // } // } // } // else // { // if (!Console.IsOutputRedirected) // Console.WriteLine("No BLE device connected."); // retVal += 1; // } // return retVal; ////} /// <summary> /// This function used to add "ValueChanged" event subscription /// </summary> /// <param name="param"></param> static async Task <int> SubscribeToCharacteristic(string param) { int retVal = 0; if (_selectedDevice != null) { if (!string.IsNullOrEmpty(param)) { List <BluetoothLEAttributeDisplay> chars = new List <BluetoothLEAttributeDisplay>(); string charName = string.Empty; var parts = param.Split('/'); // Do we have parameter is in "service/characteristic" format? if (parts.Length == 2) { string serviceName = Utilities.GetIdByNameOrNumber(_services, parts[0]); charName = parts[1]; // If device is found, connect to device and enumerate all services if (!string.IsNullOrEmpty(serviceName)) { var attr = _services.FirstOrDefault(s => s.Name.Equals(serviceName)); IReadOnlyList <GattCharacteristic> characteristics = new List <GattCharacteristic>(); try { // Ensure we have access to the device. var accessStatus = await attr.service.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { var result = await attr.service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { characteristics = result.Characteristics; } } } catch (Exception ex) { if (!Console.IsOutputRedirected) { Console.WriteLine($"Restricted service. Can't subscribe to characteristics: {ex.Message}"); } retVal += 1; } foreach (var c in characteristics) { chars.Add(new BluetoothLEAttributeDisplay(c)); } } } else if (parts.Length == 1) { if (_selectedService == null) { if (!Console.IsOutputRedirected) { Console.WriteLine("No service is selected."); } retVal += 1; return(retVal); } chars = new List <BluetoothLEAttributeDisplay>(_characteristics); charName = parts[0]; } // Read characteristic if (chars.Count > 0 && !string.IsNullOrEmpty(charName)) { string useName = Utilities.GetIdByNameOrNumber(chars, charName); var attr = chars.FirstOrDefault(c => c.Name.Equals(useName)); if (attr != null && attr.characteristic != null) { // First, check for existing subscription if (!_subscribers.Contains(attr.characteristic)) { var status = await attr.characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify); if (status == GattCommunicationStatus.Success) { _subscribers.Add(attr.characteristic); attr.characteristic.ValueChanged += Characteristic_ValueChanged; } else { if (!Console.IsOutputRedirected) { Console.WriteLine($"Can't subscribe to characteristic {useName}"); } retVal += 1; } } else { if (!Console.IsOutputRedirected) { Console.WriteLine($"Already subscribed to characteristic {useName}"); } retVal += 1; } } else { if (!Console.IsOutputRedirected) { Console.WriteLine($"Invalid characteristic {useName}"); } retVal += 1; } } else { if (!Console.IsOutputRedirected) { Console.WriteLine("Nothing to subscribe, please specify characteristic name or #."); } retVal += 1; } } else { if (!Console.IsOutputRedirected) { Console.WriteLine("Nothing to subscribe, please specify characteristic name or #."); } retVal += 1; } } else { if (!Console.IsOutputRedirected) { Console.WriteLine("No BLE device connected."); } retVal += 1; } return(retVal); }
/// <summary> /// Connect to the specific device by name or number, and make this device current /// </summary> /// <param name="deviceName"></param> /// <returns></returns> static async Task <int> OpenDevice(string deviceName) { int retVal = 0; if (!string.IsNullOrEmpty(deviceName)) { var devs = _deviceList.OrderBy(d => d.Name).Where(d => !string.IsNullOrEmpty(d.Name)).ToList(); string foundId = Utilities.GetIdByNameOrNumber(devs, deviceName); // If device is found, connect to device and enumerate all services if (!string.IsNullOrEmpty(foundId)) { _selectedCharacteristic = null; _selectedService = null; _services.Clear(); try { // only allow for one connection to be open at a time if (_selectedDevice != null) { CloseDevice(); } _selectedDevice = await BluetoothLEDevice.FromIdAsync(foundId).AsTask().TimeoutAfter(_timeout); if (!Console.IsInputRedirected) { Console.WriteLine($"Connecting to {_selectedDevice.Name}."); } var result = await _selectedDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { if (!Console.IsInputRedirected) { Console.WriteLine($"Found {result.Services.Count} services:"); } for (int i = 0; i < result.Services.Count; i++) { var serviceToDisplay = new BluetoothLEAttributeDisplay(result.Services[i]); _services.Add(serviceToDisplay); if (!Console.IsInputRedirected) { Console.WriteLine($"#{i:00}: {_services[i].Name}"); } } } else { Console.WriteLine($"Device {deviceName} is unreachable."); retVal += 1; } } catch { Console.WriteLine($"Device {deviceName} is unreachable."); retVal += 1; } } else { retVal += 1; } } else { Console.WriteLine("Device name can not be empty."); retVal += 1; } return(retVal); }
/// <summary> /// Set active service for current device /// </summary> /// <param name="parameters"></param> static async Task <int> SetService(string serviceName) { int retVal = 0; if (_selectedDevice != null) { if (!string.IsNullOrEmpty(serviceName)) { string foundName = Utilities.GetIdByNameOrNumber(_services, serviceName); // If device is found, connect to device and enumerate all services if (!string.IsNullOrEmpty(foundName)) { var attr = _services.FirstOrDefault(s => s.Name.Equals(foundName)); IReadOnlyList <GattCharacteristic> characteristics = new List <GattCharacteristic>(); try { // Ensure we have access to the device. var accessStatus = await attr.service.RequestAccessAsync(); if (accessStatus == DeviceAccessStatus.Allowed) { // BT_Code: Get all the child characteristics of a service. Use the cache mode to specify uncached characterstics only // and the new Async functions to get the characteristics of unpaired devices as well. var result = await attr.service.GetCharacteristicsAsync(BluetoothCacheMode.Uncached); if (result.Status == GattCommunicationStatus.Success) { characteristics = result.Characteristics; _selectedService = attr; _characteristics.Clear(); if (!Console.IsInputRedirected) { Console.WriteLine($"Selected service {attr.Name}."); } if (characteristics.Count > 0) { for (int i = 0; i < characteristics.Count; i++) { var charToDisplay = new BluetoothLEAttributeDisplay(characteristics[i]); _characteristics.Add(charToDisplay); if (!Console.IsInputRedirected) { Console.WriteLine($"#{i:00}: {charToDisplay.Name}\t{charToDisplay.Chars}"); } } } else { if (!Console.IsOutputRedirected) { Console.WriteLine("Service don't have any characteristic."); } retVal += 1; } } else { if (!Console.IsOutputRedirected) { Console.WriteLine("Error accessing service."); } retVal += 1; } } // Not granted access else { if (!Console.IsOutputRedirected) { Console.WriteLine("Error accessing service."); } retVal += 1; } } catch (Exception ex) { if (!Console.IsOutputRedirected) { Console.WriteLine($"Restricted service. Can't read characteristics: {ex.Message}"); } retVal += 1; } } else { if (!Console.IsOutputRedirected) { Console.WriteLine("Invalid service name or number"); } retVal += 1; } } else { if (!Console.IsOutputRedirected) { Console.WriteLine("Invalid service name or number"); } retVal += 1; } } else { if (!Console.IsOutputRedirected) { Console.WriteLine("Nothing to use, no BLE device connected."); } retVal += 1; } return(retVal); }
/// <summary> /// Event handler for ValueChanged callback /// </summary> /// <param name="sender"></param> /// <param name="args"></param> static void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args) { if (_primed) { // reference on parsing HR values: https://developer.polar.com/wiki/H6,_H7,_H10_and_OH1_Heart_rate_sensors#Bluetooth_LE_API_for_Windows_10 //var newValue = Utilities.FormatValue(args.CharacteristicValue, _dataFormat); var newValue = Utilities.FormatValue(args.CharacteristicValue, DataFormat.Dec); // read just the second byte for HR value byte[] data; CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data); var hrValue = (int)data[1];// + (data[2] << 8); bool rrPresent = ((data[0] & 0x10) >> 4) != 0; //if (Console.IsInputRedirected) Console.Write($"{newValue}"); //else Console.Write($"Value changed for {sender.Uuid}: {newValue}\n"); Console.Write($"HR {hrValue}"); // to hold hrSample for LSL outlet double[] hrSample = { hrValue, -1.0, -1.0 }; if (rrPresent) { int rrValue = data[2] + (data[3] << 8); Console.Write($"\trr interval: {rrValue}"); rrMovingAverage.AddObservation(rrValue); hrSample[1] = rrValue; if (rrMovingAverage.HasFullPeriod) { var rrStdDev = rrMovingAverage.StandardDeviation; Console.Write($"\tstd dev rr: {rrStdDev}"); hrSample[2] = rrStdDev; } // push data to LSL outlet } outlet.push_sample(hrSample); Console.Write("\n"); //if (_notifyCompleteEvent != null) //{ // _notifyCompleteEvent.Set(); // _notifyCompleteEvent = null; //} } else { _primed = true; } }