public async void Connect() { // Repair the device if needed if (!devInfo.Pairing.IsPaired) { Debug.WriteLine("Pairing..."); DeviceInformationCustomPairing customPairing = devInfo.Pairing.Custom; customPairing.PairingRequested += PairingRequested; DevicePairingResult result = await customPairing.PairAsync(DevicePairingKinds.ProvidePin, DevicePairingProtectionLevel.None); customPairing.PairingRequested -= PairingRequested; Debug.WriteLine("Pair status: " + result.Status); } else { Debug.WriteLine("Already Paired"); } // Get the actual device try { device = await BluetoothDevice.FromIdAsync(devInfo.Id); device.ConnectionStatusChanged += ConnectionStatusChanged; } catch (Exception ex) { Debug.WriteLine("Bluetooth Not Available"); return; } //try { var services = await device.GetRfcommServicesAsync(); if (services.Services.Count > 0) { var service = services.Services[0]; stream = new StreamSocket(); //try { await stream.ConnectAsync(service.ConnectionHostName, service.ConnectionServiceName); //} catch (Exception ex) { //Debug.WriteLine("Could not connect to device"); //} Debug.WriteLine("Stream Connected"); rx = new DataReader(stream.InputStream); rx.InputStreamOptions = InputStreamOptions.Partial; tx = new DataWriter(stream.OutputStream); OnConnected(); } /*} catch (Exception ex) { * Debug.WriteLine("Failed to get services"); * return; * }*/ }
private async void Button_Click_GetRfcommServices(object sender, RoutedEventArgs e) { BluetoothDevice bt = await BluetoothDevice.FromIdAsync(ViewModel.SelectedDevice.Id); RfcommDeviceServicesResult result = await bt.GetRfcommServicesAsync(); foreach (var b in bt.SdpRecords) { Debug.WriteLine(Encoding.UTF8.GetString(b.ToArray())); } foreach (var service in result.Services) { Debug.WriteLine(service.ServiceId.AsString() + " " + service.ConnectionHostName + " " + service.ConnectionServiceName); } }
/// <summary>Get extra info for connection and other not gathered at discovery to save time</summary> /// <param name="deviceInfo">The device information data model to populate</param> /// <param name="forceRetrieve">Force re-reading of all extra information</param> /// <returns>An asynchronous task result</returns> private async Task GetExtraInfo(BTDeviceInfo deviceInfo, bool forceRetrieve) { this.log.InfoEntry("GetExtraInfo"); // 0 length remote host name indicates that we require more information for connection if (deviceInfo.RemoteHostName.Length == 0 || forceRetrieve) { this.log.Info("GetExtraInfo", () => string.Format("Getting info to fill in host name for address:{0}", deviceInfo.Address)); using (BluetoothDevice device = await BluetoothDevice.FromIdAsync(deviceInfo.Address)) { // TODO - defer this to before connection or info request // SDP records only after services // Must use uncached RfcommDeviceServicesResult serviceResult = await device.GetRfcommServicesAsync(BluetoothCacheMode.Uncached); this.log.Info("GetExtraInfo", () => string.Format("Success. Number of services:{0}", serviceResult.Services.Count)); //WrapErr.ChkTrue(serviceResult.Services.Count > 0, 9999, () => string.Format("No services for BT:{0}", deviceInfo.Name)); if (serviceResult.Services.Count == 0) { throw new Exception(string.Format("No services for BT:{0}", deviceInfo.Name)); } if (serviceResult.Error == BluetoothError.Success) { foreach (var service in serviceResult.Services) { BT_ServiceType serviceType = BT_ParseHelpers.GetServiceType(service.ConnectionServiceName); this.log.Info("GetExtraInfo", () => string.Format("Device {0} Connection host name {1} Service name {2} Type {3}", deviceInfo.Name, service.ConnectionHostName, service.ConnectionServiceName, serviceType.ToString())); if (serviceType == BT_ServiceType.SerialPort) { // TODO get extra info on attributes //var sdpAttr = await service.GetSdpRawAttributesAsync(BluetoothCacheMode.Uncached); //foreach (var attr in sdpAttr) { // this.log.Info("HarvestInfo", () => string.Format(" SDP Attribute:{0} Capacity:{1} Length:{2}", attr.Key, attr.Value.Capacity, attr.Value.Length)); //} // Sample output. See: https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/ //SDP Attribute id | Capacity | Length | Description(?) // 256 7 7 // 0 5 5 // 6 11 11 // 4 14 14 // 1 5 5 (service class ID list deviceInfo.ServiceType = BT_ServiceType.SerialPort; deviceInfo.RemoteHostName = service.ConnectionHostName.ToString(); deviceInfo.RemoteServiceName = service.ConnectionServiceName; // TODO info on access //service.DeviceAccessInformation.CurrentStatus == DeviceAccessStatus.Allowed this.log.Info("****", () => string.Format("Device:{0} Host Name:{1} Service:{2}", deviceInfo.Name, deviceInfo.RemoteHostName, deviceInfo.RemoteServiceName)); } else { // Not used. } } } else { this.log.Error(9999, () => string.Format("Get Service result:{0}", serviceResult.Error.ToString())); } } } }
/// <summary>Get extra info for connection and other not gathered at discovery to save time</summary> /// <param name="deviceInfo">The device information data model to populate</param> /// <param name="forceRetrieve">Force re-reading of all extra information</param> /// <returns>An asynchronous task result</returns> private async Task GetExtraInfo(BTDeviceInfo deviceInfo, bool forceRetrieve, bool display) { try { this.log.InfoEntry("GetExtraInfo"); // TODO Cannot call this after the services have already been retrieved. So do not force // 0 length remote host name indicates that we require more information for connection if (deviceInfo.RemoteHostName.Length == 0 || forceRetrieve) { this.log.Info("GetExtraInfo", () => string.Format("Getting info to fill in host name for address:{0}", deviceInfo.Address)); using (BluetoothDevice device = await BluetoothDevice.FromIdAsync(deviceInfo.Address)) { // TODO - defer this to before connection or info request // SDP records only after services // Must use uncached RfcommDeviceServicesResult serviceResult = await device.GetRfcommServicesAsync(BluetoothCacheMode.Uncached); this.log.Info("GetExtraInfo", () => string.Format("Success. Number of services:{0}", serviceResult.Services.Count)); //WrapErr.ChkTrue(serviceResult.Services.Count > 0, 9999, () => string.Format("No services for BT:{0}", deviceInfo.Name)); if (serviceResult.Services.Count == 0) { throw new Exception(string.Format("No services for BT:{0}", deviceInfo.Name)); } if (serviceResult.Error == BluetoothError.Success) { foreach (var service in serviceResult.Services) { BT_ServiceType serviceType = BT_ParseHelpers.GetServiceType(service.ServiceId.AsShortId()); this.log.Info("GetExtraInfo", () => string.Format("Device {0} Connection host name {1} Service name {2} Type {3}", deviceInfo.Name, service.ConnectionHostName, service.ConnectionServiceName, serviceType.ToString())); if (serviceType == BT_ServiceType.SerialPort) { //await this.GetListSDPAttributes(service); deviceInfo.ServiceType = serviceType; deviceInfo.RemoteHostName = service.ConnectionHostName.ToString(); deviceInfo.RemoteServiceName = service.ConnectionServiceName; deviceInfo.ServiceClassName = serviceType.ToString().CamelCaseToSpaces(); deviceInfo.ServiceClassInt = (int)service.ServiceId.AsShortId(); // TODO info on access //service.DeviceAccessInformation.CurrentStatus == DeviceAccessStatus.Allowed this.log.Info("****", () => string.Format("Device:{0} Host Name:{1} Service:{2}", deviceInfo.Name, deviceInfo.RemoteHostName, deviceInfo.RemoteServiceName)); //deviceInfo.Strength = 0; this.ListDeviceInfoProperties(device); this.log.Info("****", () => string.Format("device.BluetoothAddress: {0}", device.BluetoothAddress)); this.log.Info("****", () => string.Format("device.BluetoothDeviceId.id: {0}", device.BluetoothDeviceId.Id)); this.log.Info("****", () => string.Format("device.DeviceId: {0}", device.DeviceId)); this.log.Info("****", () => string.Format("device.DeviceInformation.Id: {0}", device.DeviceInformation.Id)); this.log.Info("****", () => string.Format("device.ClassOfDevice: {0}", device.ClassOfDevice.RawValue)); //this.log.Info("****", () => string.Format(":{0}", )); //this.log.Info("****", () => string.Format(":{0}", )); // Experimental. See the note on the method //await this.GetRadioInfo(device, deviceInfo, service.ConnectionServiceName); //await this.GetRadioInfo(device, deviceInfo, deviceInfo.RemoteHostName); // List of radios available on current device //await this.ListRadios(); if (display) { this.BT_DeviceInfoGathered?.Invoke(this, deviceInfo); } } else { //Not used. } service.Dispose(); } } else { this.log.Error(9999, () => string.Format("Get Service result:{0}", serviceResult.Error.ToString())); } } } else if (deviceInfo.RemoteHostName.Length > 0 && display) { this.BT_DeviceInfoGathered?.Invoke(this, deviceInfo); } } catch (Exception e) { this.log.Exception(9999, "GetExtraInfo", "", e); } }
public async Task <bool> BtConnect(string deviceID) { try { //System.Diagnostics.Debug.Assert(Thread.CurrentThread.IsBackground, "SerialComm:BtConnect() cannot be called from the UI thread."); DeviceAccessStatus accessStatus = DeviceAccessInformation.CreateFromId(deviceID).CurrentStatus; if (accessStatus == DeviceAccessStatus.DeniedByUser) { //await OBD2Xam.MainPage.Instance.DisplayAlert("Error", // "This app does not have access to connect to the remote device. Please grant access in Settings > Privacy > Other Devices.", // "Cancel"); await Xamarin.Forms.Device.InvokeOnMainThreadAsync( () => { OBD2Xam.MainPage.Instance.DisplayAlert("Error", "This app does not have access to connect to the remote device. Please grant access in Settings > Privacy > Other Devices.", "Cancel"); } ); return(false); } BluetoothDevice device = await BluetoothDevice.FromIdAsync(deviceID); if (device == null) { //rootPage.NotifyUser("Bluetooth Device returned null. Access Status = " + accessStatus.ToString(), NotifyType.ErrorMessage); System.Diagnostics.Debug.WriteLine("Bluetooth Device returned null. Access Status = " + accessStatus.ToString()); System.Diagnostics.Debugger.Break(); return(false); } //System.Diagnostics.Debug.WriteLine(device.ConnectionStatus); DeviceAccessStatus das; das = await device.RequestAccessAsync(); // might not always work... //System.Diagnostics.Debugger.Break(); /* * // RequestAccessAsync() needs to executed on the UI thread, which means the UI thread cannot be blocked * // while waiting for all this other crap to run. So this code needs to be executed in backround, * // WITHOUT an await, because that would cause the UI thread to block. * bool invokeComplete = false; * Xamarin.Forms.Device.BeginInvokeOnMainThread( async () => { * das = await device.RequestAccessAsync(); * invokeComplete = true; * }); * * // now we wait for the UI thread to finish it's task, without await * // because BeginInvokeOnMainThread() isn't awaitable. * while (!invokeComplete) * { * System.Diagnostics.Debug.WriteLine("waiting..."); * System.Threading.Thread.Sleep(100); * } */ if (das == DeviceAccessStatus.Allowed) { RfcommDeviceServicesResult rfResultList = await device.GetRfcommServicesAsync().AsTask().ConfigureAwait(false); logRfTypes(rfResultList); // https://blog.j2i.net/2018/07/29/connecting-to-bluetooth-rfcomm-with-uwp/ if (rfResultList.Services.Count > 0) { foreach (var service in rfResultList.Services) { if (service.ServiceId.AsString() == Constants.BT_SERIAL_PORT_INTERFACE) { streamSocket = new StreamSocket(); await streamSocket.ConnectAsync(service.ConnectionHostName, service.ConnectionServiceName); dw = new DataWriter(streamSocket.OutputStream); sr = new StreamReader(streamSocket.InputStream.AsStreamForRead(256)); break; } } if (!IsOpen()) { throw new Exception("Service not found)"); } } } } catch (Exception exc) { if (exc.Message == "Element not found. (Exception from HRESULT: 0x80070490)") { //System.Diagnostics.Debug.WriteLine("Device not listening."); await Xamarin.Forms.Device.InvokeOnMainThreadAsync( () => { OBD2Xam.MainPage.Instance.DisplayAlert("Error", "Device not listening.", "Cancel"); } ); this.Close(); } else { System.Diagnostics.Debug.WriteLine(exc.Message); System.Diagnostics.Debugger.Break(); } } return(IsOpen()); }