unsafe void WriteCharacteristic(BleCharacteristic characteristic, byte[] value, int offset, int count, NativeMethods.BLUETOOTH_GATT_FLAGS flags) { Throw.If.Null(characteristic, "characteristic").Null(value, "value").OutOfRange(value, offset, count); HandleAcquireIfOpenOrFail(); try { lock (_writeSync) { int error; var wc = (WinBleCharacteristic)characteristic; var cb = stackalloc byte[NativeMethods.BTH_LE_GATT_CHARACTERISTIC_VALUE.Size + count]; var cv = (NativeMethods.BTH_LE_GATT_CHARACTERISTIC_VALUE *)cb; cv->DataSize = (uint)count; Marshal.Copy(value, offset, (IntPtr)(void *)&cv->Data[0], count); error = NativeMethods.BluetoothGATTSetCharacteristicValue(_handle, ref wc.NativeData, cv, 0, flags); if (error != 0) { var message = string.Format("Failed to write {0} bytes to characteristic {1}.", count, characteristic); throw DeviceException.CreateIOException(Device, message, error); } } } finally { HandleRelease(); } }
public override void WriteCharacteristicWithoutResponse(BleCharacteristic characteristic, byte[] value, int offset, int count, BleRequestFlags requestFlags) { Throw.If.Null(characteristic, "characteristic"); HidSharpDiagnostics.PerformStrictCheck(characteristic.IsWritableWithoutResponse, "Characteristic doesn't support Write Without Response."); var flags = GetGattFlags(requestFlags); WriteCharacteristic(characteristic, value, offset, count, flags | NativeMethods.BLUETOOTH_GATT_FLAGS.WRITE_WITHOUT_RESPONSE); }
public override void WriteCharacteristic(BleCharacteristic characteristic, byte[] value, int offset, int count, BleRequestFlags requestFlags) { Throw.If.Null(characteristic, "characteristic"); HidSharpDiagnostics.PerformStrictCheck(characteristic.IsWritable, "Characteristic doesn't support Write."); var flags = GetGattFlags(requestFlags); WriteCharacteristic(characteristic, value, offset, count, flags); }
public override unsafe byte[] ReadCharacteristic(BleCharacteristic characteristic, BleRequestFlags requestFlags) { Throw.If.Null(characteristic, "characteristic"); HidSharpDiagnostics.PerformStrictCheck(characteristic.IsReadable, "Characteristic doesn't support Read."); var flags = GetGattFlags(requestFlags); HandleAcquireIfOpenOrFail(); try { lock (_readSync) { int error; var wc = (WinBleCharacteristic)characteristic; ushort valueSize; error = NativeMethods.BluetoothGATTGetCharacteristicValue(_handle, ref wc.NativeData, 0, null, out valueSize, flags | ((requestFlags & BleRequestFlags.Cacheable) == 0 ? NativeMethods.BLUETOOTH_GATT_FLAGS.FORCE_READ_FROM_DEVICE : 0)); if (error != NativeMethods.ERROR_MORE_DATA || valueSize < NativeMethods.BTH_LE_GATT_CHARACTERISTIC_VALUE.Size) { var message = string.Format("Failed to read characteristic {0}.", characteristic); throw DeviceException.CreateIOException(Device, message, error); } var cb = stackalloc byte[valueSize]; var cv = (NativeMethods.BTH_LE_GATT_CHARACTERISTIC_VALUE *)cb; ushort valueSize2; error = NativeMethods.BluetoothGATTGetCharacteristicValue(_handle, ref wc.NativeData, valueSize, cv, out valueSize2, flags); if (error != 0 || valueSize != valueSize2 || cv->DataSize > valueSize - NativeMethods.BTH_LE_GATT_CHARACTERISTIC_VALUE.Size) { var message = string.Format("Failed to read characteristic {0}.", characteristic); throw DeviceException.CreateIOException(Device, message, error); } var bytes = new byte[cv->DataSize]; Marshal.Copy((IntPtr)(void *)&cv->Data[0], bytes, 0, checked ((int)cv->DataSize)); return(bytes); } } finally { HandleRelease(); } }
private List <BleCharacteristic> ConvertAttributesToCharacteristics(List <BleAttribute> attributes) { // *=== Service: 42 48 12 4a 7f 2c 48 47 b9 de 04 a9 02 00 06 d5 // *====== Att: 26 - 00 28 // *====== Att: 27 - 03 28 // *====== Att: 28 - 42 48 12 4a 7f 2c 48 47 b9 de 04 a9 02 04 06 d5 // *====== Att: 29 - 02 29 // *====== Att: 30 - 03 28 // *====== Att: 31 - 42 48 12 4a 7f 2c 48 47 b9 de 04 a9 02 05 06 d5 // *====== Att: 32 - 02 29 var characteristics = new List <BleCharacteristic>(); BleCharacteristic current = null; var createCharacteristic = false; foreach (var attr in attributes) { if (attr.Uuid.SequenceEqual(BleAttribute.ServiceUuid)) { // defines service - do nothing } else if (attr.Uuid.SequenceEqual(BleAttribute.CharacteristicUuid)) { // finish previous characteristic current = null; // create characteristic from next attribute createCharacteristic = true; } else if (attr.Uuid.SequenceEqual(BleAttribute.CharacteristicCccUuid)) { // add ccc capabilities to characteristic current.SetCccHandle(attr.Handle); } else { // if new characteristic begins - create it else skip and do nothing if (createCharacteristic) { current = new BleCharacteristic(BgLib, BleModuleConnection, Logger, attr.Connection, attr.Uuid, attr.Handle); createCharacteristic = false; characteristics.Add(current); } } } return(characteristics); }
static void Main(string[] args) { //Trace.Listeners.Clear(); //Trace.Listeners.Add(new ConsoleTraceListener()); HidSharpDiagnostics.EnableTracing = true; HidSharpDiagnostics.PerformStrictChecks = true; var list = DeviceList.Local; list.Changed += (sender, e) => Console.WriteLine("Device list changed."); //Console.WriteLine("Beginning discovery."); //using (list.BeginBleDiscovery()) { var allDeviceList = list.GetAllDevices().ToArray(); Console.WriteLine("All device list:"); foreach (Device dev in allDeviceList) { Console.WriteLine(dev.ToString() + " @ " + dev.DevicePath); /* * if (dev is HidDevice) * { * foreach (var serialPort in * (((HidDevice)dev).GetSerialPorts())) * { * Console.WriteLine(" " + serialPort); * } * } */ } var bleDeviceList = list.GetBleDevices().ToArray(); Console.WriteLine("BLE device list:"); foreach (BleDevice dev in bleDeviceList) { Console.WriteLine(dev.ToString() + "@" + dev.DevicePath); foreach (var service in dev.GetServices()) { Console.WriteLine(string.Format("\tService: {0}", service.Uuid)); foreach (var characteristic in service.GetCharacteristics()) { Console.WriteLine(string.Format("\t\tCharacteristic: {0} (Properties: {1})", characteristic.Uuid, characteristic.Properties)); foreach (var descriptor in characteristic.GetDescriptors()) { Console.WriteLine(string.Format("\t\t\tDescriptor: {0}", descriptor.Uuid)); } } if (service.Uuid == new BleUuid("63dc0001-fa35-4205-b09f-0fc6072ec515")) { try { using (var svc = dev.Open(service)) { Console.WriteLine("Opened!"); BleCharacteristic rx = null; foreach (var ch in service.GetCharacteristics()) { Console.WriteLine(string.Format("{0} = {1}", ch.Uuid, ch.IsReadable ? string.Join(" ", svc.ReadCharacteristic(ch)) : "N/A")); foreach (var d in ch.GetDescriptors()) { Console.WriteLine(string.Format("\t{0} = {1}", d.Uuid, string.Join(" ", svc.ReadDescriptor(d)))); } if (BleCccd.Notification != svc.ReadCccd(ch)) { svc.WriteCccd(ch, BleCccd.Notification); } if (ch.Uuid == new BleUuid("63dc0002-fa35-4205-b09f-0fc6072ec515")) { rx = ch; } } Action beginReadEvent = null; AsyncCallback endReadEvent = null; beginReadEvent = () => { svc.BeginReadEvent(endReadEvent, null); }; endReadEvent = ar => { BleEvent @event; try { @event = svc.EndReadEvent(ar); } catch (ObjectDisposedException) { Console.WriteLine("closed"); return; } catch (TimeoutException) { Console.WriteLine("timed out"); @event = default(BleEvent); } if (@event.Value != null) { Console.WriteLine(string.Format("{0} -> {1}", @event.Characteristic, string.Join(" ", @event.Value.Select(x => x.ToString())))); if (rx != null) { Console.WriteLine("writing"); svc.WriteCharacteristicWithoutResponse(rx, new[] { (byte)0xdd, (byte)1, (byte)'A' }); } } beginReadEvent(); }; beginReadEvent(); Thread.Sleep(30000); } } catch (Exception e) { Console.WriteLine(e.ToString()); } } } } Console.WriteLine(); Console.WriteLine("Press any key"); Console.ReadKey(); Console.WriteLine(); } //Console.WriteLine("Ending discovery."); /* * var serialDeviceList = list.GetSerialDevices().ToArray(); * Console.WriteLine("Serial device list:"); * foreach (SerialDevice dev in serialDeviceList) * { * Console.WriteLine(dev.DevicePath); * } * * Console.WriteLine(); */ var stopwatch = Stopwatch.StartNew(); var hidDeviceList = list.GetHidDevices().ToArray(); Console.WriteLine("Complete device list (took {0} ms to get {1} devices):", stopwatch.ElapsedMilliseconds, hidDeviceList.Length); foreach (HidDevice dev in hidDeviceList) { Console.WriteLine(dev.DevicePath); //Console.WriteLine(string.Join(",", dev.GetDevicePathHierarchy())); // TODO Console.WriteLine(dev); try { Console.WriteLine(string.Format("Max Lengths: Input {0}, Output {1}, Feature {2}", dev.GetMaxInputReportLength(), dev.GetMaxOutputReportLength(), dev.GetMaxFeatureReportLength())); } catch (UnauthorizedAccessException e) { Console.WriteLine(e); Console.WriteLine(); continue; } try { Console.WriteLine("Serial Ports: {0}", string.Join(",", dev.GetSerialPorts())); } catch { Console.WriteLine("Serial Ports: Unknown on this platform."); } try { var rawReportDescriptor = dev.GetRawReportDescriptor(); Console.WriteLine("Report Descriptor:"); Console.WriteLine(" {0} ({1} bytes)", string.Join(" ", rawReportDescriptor.Select(d => d.ToString("X2"))), rawReportDescriptor.Length); int indent = 0; foreach (var element in EncodedItem.DecodeItems(rawReportDescriptor, 0, rawReportDescriptor.Length)) { if (element.ItemType == ItemType.Main && element.TagForMain == MainItemTag.EndCollection) { indent -= 2; } Console.WriteLine(" {0}{1}", new string(' ', indent), element); if (element.ItemType == ItemType.Main && element.TagForMain == MainItemTag.Collection) { indent += 2; } } var reportDescriptor = dev.GetReportDescriptor(); // Lengths should match. Debug.Assert(dev.GetMaxInputReportLength() == reportDescriptor.MaxInputReportLength); Debug.Assert(dev.GetMaxOutputReportLength() == reportDescriptor.MaxOutputReportLength); Debug.Assert(dev.GetMaxFeatureReportLength() == reportDescriptor.MaxFeatureReportLength); foreach (var deviceItem in reportDescriptor.DeviceItems) { foreach (var usage in deviceItem.Usages.GetAllValues()) { Console.WriteLine(string.Format("Usage: {0:X4} {1}", usage, (Usage)usage)); } foreach (var report in deviceItem.Reports) { Console.WriteLine(string.Format("{0}: ReportID={1}, Length={2}, Items={3}", report.ReportType, report.ReportID, report.Length, report.DataItems.Count)); foreach (var dataItem in report.DataItems) { Console.WriteLine(string.Format(" {0} Elements x {1} Bits, Units: {2}, Expected Usage Type: {3}, Flags: {4}, Usages: {5}", dataItem.ElementCount, dataItem.ElementBits, dataItem.Unit.System, dataItem.ExpectedUsageType, dataItem.Flags, string.Join(", ", dataItem.Usages.GetAllValues().Select(usage => usage.ToString("X4") + " " + ((Usage)usage).ToString())))); } } { Console.WriteLine("Opening device for 20 seconds..."); HidStream hidStream; if (dev.TryOpen(out hidStream)) { Console.WriteLine("Opened device."); hidStream.ReadTimeout = Timeout.Infinite; using (hidStream) { var inputReportBuffer = new byte[dev.GetMaxInputReportLength()]; var inputReceiver = reportDescriptor.CreateHidDeviceInputReceiver(); var inputParser = deviceItem.CreateDeviceItemInputParser(); #if SINGLE_THREADED_WAITHANDLE_APPROACH inputReceiver.Start(hidStream); int startTime = Environment.TickCount; while (true) { if (inputReceiver.WaitHandle.WaitOne(1000)) { if (!inputReceiver.IsRunning) { break; } // Disconnected? Report report; while (inputReceiver.TryRead(inputReportBuffer, 0, out report)) { // Parse the report if possible. // This will return false if (for example) the report applies to a different DeviceItem. if (inputParser.TryParseReport(inputReportBuffer, 0, report)) { WriteDeviceItemInputParserResult(inputParser); } } } uint elapsedTime = (uint)(Environment.TickCount - startTime); if (elapsedTime >= 20000) { break; } // Stay open for 20 seconds. } #elif SINGLE_THREADED_POLLING_APPROACH inputReceiver.Start(hidStream); int startTime = Environment.TickCount; while (true) { if (!inputReceiver.IsRunning) { break; } // Disconnected? Report report; // Periodically check if the receiver has any reports. while (inputReceiver.TryRead(inputReportBuffer, 0, out report)) { // Parse the report if possible. // This will return false if (for example) the report applies to a different DeviceItem. if (inputParser.TryParseReport(inputReportBuffer, 0, report)) { WriteDeviceItemInputParserResult(inputParser); } } } uint elapsedTime = (uint)(Environment.TickCount - startTime); if (elapsedTime >= 20000) { break; } // Stay open for 20 seconds. #elif THREAD_POOL_RECEIVED_EVENT_APPROACH inputReceiver.Received += (sender, e) => { Report report; while (inputReceiver.TryRead(inputReportBuffer, 0, out report)) { // Parse the report if possible. // This will return false if (for example) the report applies to a different DeviceItem. if (inputParser.TryParseReport(inputReportBuffer, 0, report)) { // If you are using Windows Forms, you could call BeginInvoke here to marshal the results // to your main thread. WriteDeviceItemInputParserResult(inputParser); } } }; inputReceiver.Start(hidStream); Thread.Sleep(20000); #elif RAW_APPROACH IAsyncResult ar = null; int startTime = Environment.TickCount; while (true) { if (ar == null) { ar = hidStream.BeginRead(inputReportBuffer, 0, inputReportBuffer.Length, null, null); } if (ar != null) { if (ar.IsCompleted) { int byteCount = hidStream.EndRead(ar); ar = null; if (byteCount > 0) { string hexOfBytes = string.Join(" ", inputReportBuffer.Take(byteCount).Select(b => b.ToString("X2"))); Console.WriteLine(" {0}", hexOfBytes); } } else { ar.AsyncWaitHandle.WaitOne(1000); } } uint elapsedTime = (uint)(Environment.TickCount - startTime); if (elapsedTime >= 20000) { break; } // Stay open for 20 seconds. } #else #error "Choose an approach for the example." #endif } Console.WriteLine("Closed device."); } else { Console.WriteLine("Failed to open device."); } Console.WriteLine(); } } Console.WriteLine(); } catch (Exception e) { Console.WriteLine(e); } } Console.WriteLine("Press a key to exit..."); Console.ReadKey(); }