/// <summary>Build the GATT service data model</summary> /// <param name="service">The OS GATT service object</param> /// <param name="deviceDataModel">The portable GATT session data model</param> /// <returns>The async task</returns> public async Task BuildServiceDataModel(GattDeviceService service, BluetoothLEDeviceInfo deviceDataModel) { this.log.Info("BuildServiceDataModel", () => string.Format("Gatt Service:{0} Uid:{1}", BLE_DisplayHelpers.GetServiceName(service), service.Uuid.ToString())); // New service data model to add to device info BLE_ServiceDataModel serviceDataModel = new BLE_ServiceDataModel() { Characteristics = new Dictionary <string, BLE_CharacteristicDataModel>(), AttributeHandle = service.AttributeHandle, DeviceId = deviceDataModel.Id, DisplayName = BLE_DisplayHelpers.GetServiceName(service), Uuid = service.Uuid, SharingMode = (BLE_SharingMode)service.SharingMode, }; if (service.DeviceAccessInformation != null) { serviceDataModel.DeviceAccess = (BLE_DeviceAccessStatus)service.DeviceAccessInformation.CurrentStatus; } this.BuildSessionDataModel(service.Session, serviceDataModel.Session); // TODO //service.ParentServices // Get the characteristics for the service GattCharacteristicsResult characteristics = await service.GetCharacteristicsAsync(); if (characteristics.Status == GattCommunicationStatus.Success) { if (characteristics.Characteristics != null) { if (characteristics.Characteristics.Count > 0) { foreach (GattCharacteristic ch in characteristics.Characteristics) { await this.BuildCharacteristicDataModel(ch, serviceDataModel); } } else { this.log.Info("ConnectToDevice", () => string.Format("No characteristics")); } } } else { this.log.Error(9999, "HarvestDeviceInfo", () => string.Format("Failed to get Characteristics result {0}", characteristics.Status.ToString())); } // Add the service data model to the device info data model deviceDataModel.Services.Add(serviceDataModel.Uuid.ToString(), serviceDataModel); }
private void Ch_BatteryLevelValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args) { // TODO temp to read the changed value in the characteristic //args.CharacteristicValue.Length; byte[] b = args.CharacteristicValue.FromBufferToBytes(); // Will be length 1- value is Hex byte uint8Data = b[0]; // My Arduino maps it 0-100 // TODO - must be hex between 0x00 - 0x64 //int level = Convert.ToInt32(uint8Data.ToString(), 16); int level = Convert.ToInt32(uint8Data.ToString()); this.log.Info("Ch_ValueChanged", () => string.Format(" Characteristic:{0} Value:0x{1} - {2}% Handle:{3}", BLE_DisplayHelpers.GetCharacteristicName(sender), uint8Data, level, sender.AttributeHandle)); }
private async Task BuildCharacteristicDataModel(GattCharacteristic ch, BLE_ServiceDataModel service) { try { BLE_CharacteristicDataModel characteristic = new BLE_CharacteristicDataModel(); characteristic.Uuid = ch.Uuid; characteristic.UserDescription = ch.UserDescription; characteristic.AttributeHandle = ch.AttributeHandle; characteristic.Service = service; characteristic.CharName = BLE_DisplayHelpers.GetCharacteristicName(ch); characteristic.PropertiesFlags = ch.CharacteristicProperties.ToUInt().ToEnum <CharacteristicProperties>(); characteristic.ProtectionLevel = (BLE_ProtectionLevel)ch.ProtectionLevel; characteristic.PresentationFormats = this.BuildPresentationFormats(ch); await this.BuildDescriptors(ch, characteristic); service.Characteristics.Add(characteristic.Uuid.ToString(), characteristic); } catch (Exception e) { this.log.Exception(9999, "Failed during build of characteristic", e); } }
private async Task BuildDescriptors(GattCharacteristic ch, BLE_CharacteristicDataModel dataModel) { dataModel.Descriptors = new Dictionary <string, BLE_DescriptorDataModel>(); GattDescriptorsResult descriptors = await ch.GetDescriptorsAsync(); if (descriptors.Status == GattCommunicationStatus.Success) { if (descriptors.Descriptors.Count > 0) { foreach (GattDescriptor desc in descriptors.Descriptors) { GattReadResult r = await desc.ReadValueAsync(); if (r.Status == GattCommunicationStatus.Success) { // New characteristic data model to add to service BLE_DescriptorDataModel descDataModel = new BLE_DescriptorDataModel() { Uuid = desc.Uuid, AttributeHandle = desc.AttributeHandle, ProtectionLevel = (BLE_ProtectionLevel)desc.ProtectionLevel, DisplayName = BLE_ParseHelpers.GetDescriptorValueAsString(desc.Uuid, r.Value.FromBufferToBytes()) }; dataModel.Descriptors.Add(descDataModel.Uuid.ToString(), descDataModel); this.log.Info("ConnectToDevice", () => string.Format(" Descriptor:{0} Uid:{1} Value:{2}", BLE_DisplayHelpers.GetDescriptorName(desc), desc.Uuid.ToString(), descDataModel.DisplayName)); } ; } } } else { this.log.Error(9999, "BuildDescriptors", () => string.Format("Get Descriptors result:{0}", descriptors.Status.ToString())); } }
private async Task DumpCharacteristic(GattCharacteristic ch) { try { if (ch.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Read)) { GattReadResult readResult = await ch.ReadValueAsync(); if (readResult.Status == GattCommunicationStatus.Success) { //if (ch.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Read)) { byte[] b = readResult.Value.FromBufferToBytes(); switch (BLE_DisplayHelpers.GetCharacteristicEnum(ch)) { case GattNativeCharacteristicUuid.String: case GattNativeCharacteristicUuid.DeviceName: case GattNativeCharacteristicUuid.ManufacturerNameString: string strVal = Encoding.ASCII.GetString(b, 0, (int)readResult.Value.Length); this.log.Info("DumpCharacteristic", () => string.Format(" Characteristic:{0} Value:{1} Handle:{2}", BLE_DisplayHelpers.GetCharacteristicName(ch), strVal, ch.AttributeHandle)); break; case GattNativeCharacteristicUuid.BatteryLevel: #region Battery Level // This works until we get another add existing again // Setting it to notify so I can pick up the event var c = await ch.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify); ch.ValueChanged += Ch_BatteryLevelValueChanged; // Will be length 1- value is Hex byte uint8Data = b[0]; // My Arduino maps it 0-100 // TODO - must be hex between 0x00 - 0x64 //int level = Convert.ToInt32(uint8Data.ToString(), 16); int level = Convert.ToInt32(uint8Data.ToString()); this.log.Info("DumpCharacteristic", () => string.Format(" Characteristic:{0} Value:0x{1} - {2}% Handle:{3}", BLE_DisplayHelpers.GetCharacteristicName(ch), uint8Data, level, ch.AttributeHandle)); #endregion break; case GattNativeCharacteristicUuid.PnPID: #region PPnPID // 7 bytes //0x02,0x5E,0x04,0x17,0x08,0x31,0x01 // field1 - 1 byte uint8 source of Vendor ID // field2 - 2 byte Uint16 - product vendor namespace // field3 - 2 byte Uint16 - manufacturer ID // field4 - 2 byte Uint16 - manufacturer version of product StringBuilder sb = new StringBuilder(); sb .Append("Vendor ID:").Append(b[0]).Append(",") .Append("Vendor Namespace:").Append(BitConverter.ToInt16(b, 1)).Append(",") .Append("Manufacturer ID:").Append(BitConverter.ToInt16(b, 3)).Append(",") .Append("Manufacturer Namespace:").Append(BitConverter.ToInt16(b, 5)); this.log.Info("DumpCharacteristic", () => string.Format(" Characteristic:{0} Value:{1} Handle:{2}", BLE_DisplayHelpers.GetCharacteristicName(ch), sb.ToString(), ch.AttributeHandle)); #endregion break; case GattNativeCharacteristicUuid.Appearance: #region Appearance this.log.Info("DumpCharacteristic", () => string.Format(" Characteristic:{0} Value:{1} Handle:{2}", BLE_DisplayHelpers.GetCharacteristicName(ch), b.ToGattAppearanceEnum().ToString().CamelCaseToSpaces(), ch.AttributeHandle)); #endregion break; case GattNativeCharacteristicUuid.PeripheralPreferredConnectionParameters: #region //Peripheral Preferred Connection Parameters Value:0x14,0x00,0x24,0x00,0x04,0x00,0xC8,0x00 - 8 bytes // 8 bytes in 4 uint16 fields // 1. Minimum Connect interval 6-3200 // 2. Maximum Connect interval 6-3200 // 3. Slave latency 0-1000 // 4. Connection Supervisor Timeout Multiplier 10-3200 StringBuilder sb2 = new StringBuilder(); sb2 .Append("Min Connect Interval:").Append(BitConverter.ToInt16(b, 0)).Append(",") .Append("Max Connect Interval:").Append(BitConverter.ToInt16(b, 2)).Append(",") .Append("Slave Latency:").Append(BitConverter.ToInt16(b, 4)).Append(",") .Append("Connect Supervisor Timout multiplier:").Append(BitConverter.ToInt16(b, 6)); this.log.Info("DumpCharacteristic", () => string.Format(" Characteristic:{0} Value:{1} Handle:{2}", BLE_DisplayHelpers.GetCharacteristicName(ch), sb2.ToString(), ch.AttributeHandle)); #endregion break; default: byte[] data = new byte[readResult.Value.Length]; Array.Copy(b, data, data.Length); if (BLE_DisplayHelpers.GetCharacteristicName(ch) == "39320") { var xx = await ch.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify); ch.ValueChanged += Ch_ValueChangedSerialReturn; } this.log.Info("DumpCharacteristic", () => string.Format( " Characteristic: ** NOT ENUMERATED ** {0} Value:{1} Length:{2} Handle:{3}", BLE_DisplayHelpers.GetCharacteristicName(ch), data.ToFormatedByteString(), data.Length, ch.AttributeHandle)); break; } } else { this.log.Info("ConnectToDevice", () => string.Format(" Characteristic:{0} Read FAILED result:{1} Enum:{2}", BLE_DisplayHelpers.GetCharacteristicName(ch), readResult.Status, BLE_DisplayHelpers.GetCharacteristicEnum(ch))); } } else if (ch.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Write)) { if (BLE_DisplayHelpers.GetCharacteristicName(ch) == "39319") { try { //WrapErr.ToErrReport(9999, () => { this.log.Info("LKDJFKLJSDFLK:JSDKLF", "GOT 39319 (output to device)"); // Test message //using (var ms = new DataWriter()) { // // do it this way when we have a multi byte block // ms.WriteBytes(Encoding.ASCII.GetBytes("Blipo message\n\r")); // var result = await ch.WriteValueAsync(ms.DetachBuffer()); //} for (int ii = 0; ii < 5; ii++) { //byte[] bytes = Encoding.ASCII.GetBytes("Blipo message\n\r"); byte[] bytes = Encoding.ASCII.GetBytes("Blipo message being somewhat long and convaluted just to test the return of the entire thing\n\r"); int blockLimit = 20; int count = bytes.Length / blockLimit; int rest = (bytes.Length % blockLimit); int lastIndex = 0; for (int i = 0; i < count; i++) { lastIndex = i * blockLimit; using (var ms = new DataWriter()) { byte[] part = new byte[blockLimit]; Array.Copy(bytes, lastIndex, part, 0, part.Length); this.log.Error(9191, part.ToFormatedByteString()); ms.WriteBytes(part); var result = await ch.WriteValueAsync(ms.DetachBuffer()); } } if (lastIndex > 0) { if (lastIndex > 0) { lastIndex += blockLimit; } using (var ms = new DataWriter()) { byte[] part = new byte[rest]; Array.Copy(bytes, lastIndex, part, 0, part.Length); this.log.Error(9192, part.ToFormatedByteString()); ms.WriteBytes(part); var result = await ch.WriteValueAsync(ms.DetachBuffer()); } } } //for (int i = 0; i < bytes.Length; i++) { // using (var ms = new DataWriter()) { // ms.WriteByte(bytes[i]); // var result = await ch.WriteValueAsync(ms.DetachBuffer()); // } //} //}); } catch (Exception e) { this.log.Exception(9999, "Fail on write to descriptor", e); } } } } catch (Exception e) { this.log.Exception(9999, "Failed during dump info", e); } }
private async Task ConnectToDevice(BluetoothLEDeviceInfo deviceInfo) { this.log.Info("ConnectToDevice", () => string.Format("Attempting connection to {0}: FromIdAsync({1})", deviceInfo.Name, deviceInfo.Id)); try { // https://github.com/microsoft/Windows-universal-samples/blob/master/Samples/BluetoothLE/cs/Scenario2_Client.xaml.cs this.log.Info("ConnectToDevice", () => string.Format("--------------------------------------------------------------------")); this.log.Info("ConnectToDevice", () => string.Format(" Param Device Info ID {0}", deviceInfo.Id)); this.currentDevice = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id); if (this.currentDevice == null) { this.log.Info("ConnectToDevice", "Connection failed"); } else { this.log.Info("ConnectToDevice", "Connection ** OK **"); this.currentDevice.NameChanged += CurrentDevice_NameChanged; } // This just does the easy serial communications - this is using a regular HC-05 Classic (RFCOMM) board //RfcommDeviceService s = await RfcommDeviceService.FromIdAsync(this.id); //BluetoothDevice.GetRfcommServicesAsync(); this.log.Info("ConnectToDevice", () => string.Format("Device:{0} Connection status {1}", this.currentDevice.Name, this.currentDevice.ConnectionStatus.ToString())); GattDeviceServicesResult services = await this.currentDevice.GetGattServicesAsync(); if (services.Status == GattCommunicationStatus.Success) { if (services.Services != null) { if (services.Services.Count > 0) { foreach (GattDeviceService serv in services.Services) { // Service this.log.Info("ConnectToDevice", () => string.Format("Gatt Service:{0} Uid:{1}", BLE_DisplayHelpers.GetServiceName(serv), serv.Uuid.ToString())); GattCharacteristicsResult characteristics = await serv.GetCharacteristicsAsync(); if (characteristics.Status == GattCommunicationStatus.Success) { if (characteristics.Characteristics != null) { if (characteristics.Characteristics.Count > 0) { foreach (GattCharacteristic ch in characteristics.Characteristics) { await this.DumpCharacteristic(ch); GattDescriptorsResult descriptors = await ch.GetDescriptorsAsync(); if (descriptors.Status == GattCommunicationStatus.Success) { if (descriptors.Descriptors.Count > 0) { foreach (GattDescriptor desc in descriptors.Descriptors) { GattReadResult r = await desc.ReadValueAsync(); string descName = "N/A"; if (r.Status == GattCommunicationStatus.Success) { //descName = Encoding.ASCII.GetString(r.Value.FromBufferToBytes()); descName = BLE_ParseHelpers.GetDescriptorValueAsString(desc.Uuid, r.Value.FromBufferToBytes()); } // descriptors have read and write this.log.Info("ConnectToDevice", () => string.Format(" Descriptor:{0} Uid:{1} Value:{2}", BLE_DisplayHelpers.GetDescriptorName(desc), desc.Uuid.ToString(), descName)); } } } else { this.log.Info("ConnectToDevice", () => string.Format(" Get Descriptors result:{0}", descriptors.Status.ToString())); } } } else { this.log.Info("ConnectToDevice", () => string.Format("No characteristics")); } } } else { this.log.Info("ConnectToDevice", () => string.Format(" Characteristics result {0}", characteristics.Status.ToString())); } } } else { this.log.Info("ConnectToDevice", "No services exposed"); } } else { this.log.Error(9999, "Null services"); } } else { this.log.Info("ConnectToDevice", () => string.Format(" Get Services result {0}", services.Status.ToString())); } this.log.Info("ConnectToDevice", () => string.Format("--------------------------------------------------------------------")); } catch (Exception e) { this.log.Exception(9999, "BLE Connect Exception", e); } try { if (this.currentDevice == null) { // report error this.log.Info("ConnectToDevice", () => string.Format("NULL device returned for {0}", deviceInfo.Id)); return; } else { // Note: BluetoothLEDevice.GattServices property will return an empty list for unpaired devices. For all uses we recommend using the GetGattServicesAsync method. // BT_Code: GetGattServicesAsync returns a list of all the supported services of the device (even if it's not paired to the system). // If the services supported by the device are expected to change during BT usage, subscribe to the GattServicesChanged event. //GattDeviceServicesResult result = // await this.currentDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached); ////GattDeviceServicesResult result = await BluetoothLEDevice.FromIdAsync(this.currentDevice.DeviceId); //System.Diagnostics.Debug.WriteLine("Device Connected {0}", this.currentDevice.BluetoothAddress); this.log.Info("ConnectToDevice", () => string.Format("Device Connected {0}", this.currentDevice.BluetoothAddress)); } } catch (Exception ex) { this.log.Exception(9999, "on main task", ex); } }