TLDR: Creating a UWP app for Raspberry Pi 3 Model B running Windows 10 IoT and StreamSockets.ConnectAsync()
(RFCOMM) sometimes finishes and sometimes it takes an extraordinarily long time to fail ("No more data is available. (Exception from HRESULT: 0x80070103)") or just never finishes. Why is that? The debugger in Visual Studio has been used without success.
I am currently creating a UWP app using Visual Studio 2017 and writing in C#. The app is being tested on my Raspberry Pi 3 Model B running Windows 10 IoT, and the purpose of the app is to connect to Arduino's that have the HC-06 Bluetooth module using RFCOMM.
I have a button for testing the connection between the Raspberry Pi and the Arduino, which will try connecting (in turn) to all the Bluetooth devices I have defined.
I have, however, encountered a 'bug'; sometimes when I click the button, the connection test works fine, a connection is established and I can also see the data sent printed in the Serial Monitor on my Arduino.
The problem is that sometimes the call to StreamSockets.ConnectAsync()
just never finishes or uses an extraordinarily long time to fail with the exception "No more data is available. (Exception from HRESULT: 0x80070103)".
My question is what could be causing such a behavior?
All of the code below is located in Settings.xaml.cs
.
public sealed partial class Settings : Page
{
private MainPage rootPage = Current;
private StreamSocket socket = null;
private DataWriter writer = null;
private RfcommDeviceService service = null;
private BluetoothDevice bluetoothDevice;
public Settings()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
rootPage = Current;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
Disconnect();
}
void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
{
Disconnect("App suspension discconects");
}
// Rest of the code is below here //
}
When the button is clicked, ConnectionTestButton_Click
is called:
private void ConnectionTestButton_Click(object sender, RoutedEventArgs e)
{
ConnectionTest();
}
Which in turn calls ConnectionTest()
:
private async void ConnectionTest()
{
rootPage.NotifyUser("Connection test in progress...", NotifyType.StatusMessage);
Disconnect(); // Ensures that there is no residue data from previous attempts
// This connects to the Bluetooth devices I have specified in MainPage.xaml.cs in turn
// There is no problem with the declaration of the devices as this works correctly
foreach (KeyValuePair<string, string> device in Constants.BtDevices)
{
await Task.Run(() => ConnectToBtDevice(device.Value, true));
await Task.Delay(5000);
Disconnect("Connection test");
await Task.Delay(5000);
}
}
Which calls ConnectToBtDevice()
x times, where x is defined by how many Bluetooth devices I have declared in MainPage.xaml.cs
:
private async Task ConnectToBtDevice(string btId, bool connectionTest = false)
{
try
{
bluetoothDevice = await BluetoothDevice.FromIdAsync(btId);
rootPage.NotifyUser(
Constants.BtDevices.FirstOrDefault(x => x.Value == btId).Key
+ " found, trying to connect...",
NotifyType.StatusMessage);
}
catch (Exception ex)
{
rootPage.NotifyUser(
"An exception was thrown when trying to receive the Bluetooth device "
+ ex.Message,
NotifyType.ErrorMessage);
return;
}
if (bluetoothDevice == null)
{
rootPage.NotifyUser("Bluetooth device returned null",
NotifyType.ErrorMessage);
}
var rfcommServices = await bluetoothDevice.GetRfcommServicesAsync();
if (rfcommServices.Services.Count > 0)
{
service = rfcommServices.Services[0];
}
else
{
rootPage.NotifyUser("No RFCOMM services found on the device",
NotifyType.ErrorMessage);
return;
}
lock (this)
{
socket = new StreamSocket();
}
try
{
// This call is what is causing my headache
await socket.ConnectAsync(service.ConnectionHostName,
service.ConnectionServiceName,
SocketProtectionLevel
.BluetoothEncryptionAllowNullAuthentication);
rootPage.NotifyUser("Hostname: "
+ service.ConnectionHostName
+ " Servicename: "
+ service.ConnectionServiceName,
NotifyType.StatusMessage);
// Ensure utf8 is used for the reader and writer
// Not sure if this is actually necessary?
writer = new DataWriter(socket.OutputStream)
{
UnicodeEncoding = UnicodeEncoding.Utf8
};
DataReader reader = new DataReader(socket.InputStream)
{
UnicodeEncoding = UnicodeEncoding.Utf8
};
rootPage.NotifyUser("Connected to "
+ Constants.BtDevices.FirstOrDefault(x => x.Value == btId).Key,
NotifyType.SuccessMessage);
ReceiveData(reader, connectionTest);
// Send some data if the connection is for test purposes only
if (connectionTest)
{
await SendData("Test");
}
}
catch (Exception ex)
{
rootPage.NotifyUser("An exception was thrown when trying to connect to "
+ Constants.BtDevices.FirstOrDefault(x => x.Value == btId).Key
+ " "
+ ex.Message,
NotifyType.ErrorMessage);
Disconnect();
}
}
It also calls Disconnect
if a connection has been established and data has been sent:
private void Disconnect(string disconnectReason = null)
{
if (writer != null)
{
writer.DetachStream();
writer = null;
}
if (service != null)
{
service.Dispose();
service = null;
}
lock (this)
{
if (socket != null)
{
socket.Dispose();
socket = null;
}
}
if (disconnectReason != null)
{
rootPage.NotifyUser("Disconnected. Reason: "
+ disconnectReason,
NotifyType.CautionMessage);
}
}
(If any of the code is weirdly indented/formatted it is probably because I tried to limit the line length for this question, so I quickly formatted it in the question text editor)
I have tried the debugger in Visual Studio but without success in locating what is wrong, as everything seems normal. And by normal I mean that every variable is what it is supposed to be, e.g. service.ConnectionHostName
and service.ConnectionServiceName
are both set to the correct values.
{(XX:XX:XX:XX:XX:XX)}
and "Bluetooth#BluetoothbX:XX:XX:XX:XX:XX-XX:XX:XX:XX:XX:XX#RFCOMM:XXXXXXXX:{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
respectively ( "X'ed" for privacy ( not that it really matters that much, but better safe than sorry ;) ))
Neither SendData()
nor ReceiveData()
is really relevant for this question, as the problem lies before any of those methods are called, but I will happily include them if requested.
I can also add that I have used the BluetoothRfcommChat sample as a reference because I am very new to UWP development, networking/Bluetooth and C# in general.
( Also, let me know if there is anything wrong with the formatting of the question itself )
Sometimes when the connection takes too long to establish, I can encounter the exception: "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond."
I added a CancellationToken
to have a timeout function of the ConnectAsync
method, however, ConnectAsync
only sometimes registers cancellation requests, which means that sometimes it is still stuck.
The way I have it implemented:
// Called in constructor
private CancellationTokenSource connectionCts = null;
// Called in 'ConnectoToBtDevice' (same place as original)
try
{
// public const int ConnectionTimeout = 5000;
connectionCts = new CancellationTokenSource();
connectionCts.CancelAfter(Constants.ConnectionTimeout);
await socket.ConnectAsync(service.ConnectionHostName,
service.ConnectionServiceName)
.AsTask(connectionCts.Token);
...
}
catch (TaskCanceledException tEx)
{
rootPage.NotifyUser("Connection timed out.", NotifyType.ErrorMessage);
Disconnect();
}
// Called in 'Disconnect'
if (connectionCts != null)
{
connectionCts.Dispose();
connectionCts = null;
}
It appears that if I try using:
await bluetoothDevice.GetRfcommAsync(BluetoothCacheMode.Uncached);
(Using the BluetoothCacheMode.Uncached
) It gets stuck at that line instead. I have no idea if this has a connection with the above problem, but it might.
Okay, so I have now tested a bit and found out that it has no problem connecting to my phone. This makes it look like there is a problem connecting using the serial port service that is on my HC-06 modules (UUID = "{00001101-0000-1000-8000-00805F9B34FB}'"). The problem is that this is the only RFCOMM service my HC-06 provides.
User contributions licensed under CC BY-SA 3.0