Bluetooth Communication from Xamarin to C# Console App - c#

I am developing an Xamarin application (currently focused on Android but will eventually support iOS) that communicates via bluetooth to a nearby C# console application. They both act as a client/server as they need to send messages back and forth.
The read code for the Android implementation is as follows:
byte[] buffer = new byte[1024];
int bytes;
// Listen to the input stream while connected
while (true)
{
try
{
// Read from the InputStream
bytes = this.InStream.Read(buffer, 0, buffer.Length);
var message = new Java.Lang.String(buffer, 0, bytes);
// TODO: Notify the UI of the message
}
catch (Java.IO.IOException e)
{
// TODO: Show some error
}
}
The problem with the above code is that since I am only reading a buffer size of 1024 I am only getting partial messages for anything over 1024 bytes. Using a DataWriter (as shown below in the console application) would be ideal for me, but it is not available for an Android implementation using Xamarin.
The write code for the Android application is as follows:
try
{
this.OutStream.Write(buffer, 0, buffer.Length);
// TODO: Notify the UI
}
catch (Java.IO.IOException e)
{
// TODO: Show some error
}
Now, for the console application the read code is as follows:
// socket here is a Windows.Networking.Sockets.StreamSocket
var reader = new DataReader(socket.InputStream);
while (true)
{
try
{
uint readLength = await reader.LoadAsync(sizeof(uint));
uint currentLength = reader.ReadUInt32();
await reader.LoadAsync(currentLength);
string message = reader.ReadString(readLength);
Console.WriteLine("Message received: " + message);
}
catch (Exception e)
{
// TODO: Show some error
}
}
The problem here, is the the await reader.LoadAsync(currentLength) is never resolved. I am guessing that is because the Android app is only sending the message and not the length, then the message. I can get rid of the second await and use a buffer size of 1024 as I am in the Android application, but I will again be getting partial messages. Simply increasing the buffer size to get a bigger message is another option, but that sounds incorrect to me.
And the write code for the console application:
// socket here is a Windows.Networking.Sockets.StreamSocket
var writer = new DataWriter(socket.OutputStream)
writer.WriteUInt32((uint)message.Length);
writer.WriteString(message);
try
{
await writer.StoreAsync();
}
catch (Exception exception)
{
// TODO: Show some error
}
I am unsure of the changes I need to make to be able to send and receive complete messages back and forth on both applications. As I stated before, the implementation of the console application will need to support iOS bluetooth communication as well.

Related

UWP C# UART RS485 DetachBuffer

I have previously posted regarding the issue but haven't really found the problem.
Recently, I found this post regarding detachbuffer and wonder if this could be the reason i encounter the problem.
I have my UART for RS485 using a FTDI USB to 485 cable connected to Raspberry Pi on Windows IoT Core.
I set a dispatchertimer at every 1second to transmit polling to respective field devices.
I am able to tx and rx the 485 data with no problem.
However, after the polling loop to about 20 times it just crash and exited the debug mode.
I used try & catch to trap the exception but could not get it. However, i manage to read the error message at the debug output pane - The program [0xBB0] PwD.exe' has exited with code -1073741811 (0xc000000d).
I wonder if i repeatedly transmit polling, dataWriteObject.DetachBuffer(); would cause the problem?
Thanks.
snippets of my code are as follow;
private void PollTimer_Tick(object sender, object e)
{
if (pollCounter <= maxDevice)
{
var a = pollCounter | 0x80;
pollCounter++;
TxAdr = Convert.ToByte(a);
TxCmd = TxPoll;
TxPollCard();
}
else pollCounter = 0;
}
private async void TxPollCard()
{
if (serialPort != null)
{
List<byte> data = new List<byte>();
data.Add(TxHeader);
data.Add(TxAdr);
data.Add(TxCmd);
TxChkSum = 0;
foreach (byte a in data)
{
TxChkSum += a;
}
TxChkSum = (byte)(TxChkSum - 0x80);
data.Add(TxChkSum);
try
{
// Create the DataWriter object and attach to OutputStream
dataWriteObject = new DataWriter(serialPort.OutputStream);
dataWriteObject.WriteBytes(data.ToArray());
Task<UInt32> storeAsyncTask;
// Launch an async task to complete the write operation
storeAsyncTask = dataWriteObject.StoreAsync().AsTask();
UInt32 bytesWritten = await storeAsyncTask;
dataWriteObject.DetachBuffer();
}
catch (Exception ex)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
MainStatusDisplay.Text = ex.Message;
});
}
}
else
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
MainStatusDisplay.Text = "No UART port found";
});
}
}
Update:
Additional test i did which i disconnect the devices & keep transmitting without expecting response, it didn't crash.
On the other hand, i stop transmit and only listen to the 485 bus, it didn't crash either.

How can I cancel LoadAsync() without closing the underlying socket?

I have an app that communicates to some hardware over rfcomm on bluetooth. My app works on Android and am in the process of getting things working on UWP. Here's how I set up the stream reader/writers in the UWP code:
var btDevice = await BluetoothDevice.FromIdAsync(devId);
var services = await btDevice.GetRfcommServicesAsync();
if (services.Services.Count > 0)
{
// We only have one service so use the first one...
var service = services.Services[0];
// Create a stream...
_bluetoothStream = new StreamSocket();
await _bluetoothStream.ConnectAsync(service.ConnectionHostName, service.ConnectionServiceName);
_dataReader = new DataReader(_bluetoothStream.InputStream);
_dataWriter = new DataWriter(_bluetoothStream.OutputStream);
_dataReader.InputStreamOptions = InputStreamOptions.Partial;
My hardware only sends data to my app after the app sends it data so I've set up a send/receive mechanism. Everything works great except for a specific use case where my device is restarting (but bluetooth connection is still active) and is unable to send a response. In this case my upper level code is setup to attempt a retry, however the bluetooth connection gets closed when the receive times out.
_dataWriter.WriteBytes(comm.TransmitData);
Task<UInt32> writeAysncTask = _dataWriter.StoreAsync().AsTask();
UInt32 bytesWritten = await writeAysncTask;
:
try
{
using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(comm.TimeoutMs))) // _receiveTimeoutMs)))
{
// When this times out, exception gets thrown and socket is closed
// How do I prevent the socket from closing so I can do a retry???
var loadTask = _dataReader.LoadAsync(comm.ReceiveCount).AsTask(cts.Token);
bytesRead = await loadTask;
if (bytesRead > 0)
{
rxData = new byte[bytesRead];
_dataReader.ReadBytes(rxData);
}
else
{
System.Diagnostics.Debug.WriteLine("Received 0!");
}
}
}
catch (Exception ex)
{
// The bluetooth connection is closed automatically if the
// caancellationToken fires...In my case, I need the connection
// to stay open...How do I achieve this???
// Update: When this code is executed with _dataReader/Writer
// that was created with SerialDevice class (see below), the
// timeout exception does not cause the Serial connection to
// close so my calling code can then issue a retry.
System.Diagnostics.Debug.WriteLine(ex.Message) ;
}
UPDATE: It should be noted that when I use the exact same code with streams created from a SerialDevice everything works as I would expect...When the receive times out the socket is NOT closed. Seems like maybe I'm up against something in the Bluetooth Implementation in UWP. Ugh. Here's how I create the _dataReader/_dataWriter with the SerialDevice class:
_serialDevice = await SerialDevice.FromIdAsync(devId);
// Configure the port
_serialDevice.BaudRate = _baudrate;
_serialDevice.Parity = SerialParity.None;
_serialDevice.DataBits = 8;
_serialDevice.StopBits = SerialStopBitCount.One;
_dataReader = new DataReader(_serialDevice.InputStream);
_dataWriter = new DataWriter(_serialDevice.OutputStream);
I've come up with a work around to the problem I was facing. Unfortunately, I can't use the same code for SerialDevice and BluetoothDevice. I have to say, it really stinks that the bluetooth socket gets closed when a cancellation token times out. The code would be so much cleaner if it didn't close! Shouldn't it be up to me to decide if the socket should be closed? Now I'm stuck with this:
using (var cts = new CancellationTokenSource())
{
Task.Run(async () =>
{
try
{
await Task.Delay((int)comm.TimeoutMs, cts.Token);
System.Diagnostics.Debug.WriteLine("Canceling async read");
// If we make it this far, then the read as failed...cancel the async io
// which will cause the bytesRead below to be 0.
await _bluetoothStream.CancelIOAsync();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}, cts.Token);
var loadTask = _dataReader.LoadAsync(comm.ReceiveCount).AsTask();
bytesRead = await loadTask;
if (bytesRead > 0)
{
// SIgnal the delay task to cancel...
cts.Cancel(true);
if (bytesRead > comm.ReceiveCount)
System.Diagnostics.Debug.WriteLine("Received too much!!");
rxData = new byte[bytesRead];
_dataReader.ReadBytes(rxData);
}
else
{
System.Diagnostics.Debug.WriteLine("Received 0!");
}
}
After implementing this, I did notice that after I've paired my BT device, Windows returns it as a SerialDevice in the following query:
string aqs = SerialDevice.GetDeviceSelector();
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(aqs);
// My bluetooth device is included in the 'devices' collection
So if I connect to it as a serial device, I guess I won't need the work around after all. Oh well, hopefully this post will help someone else.

How to communicate between two Unity apps with TCP?

Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...who knows for what reason. If anyone else stumbles upon this problem (in a program like Unity, where functions are looping constantly and data is always being processed), consider that you're moving too much irrelevant data.
Original Post
I've run into quite the problem, and I'm hoping I can receive some guidance.
In short, I'm wondering how to use TCP to communicate two Unity apps over the same computer. I've gotten it functioning in editor, but when both apps are built, communication quickly breaks down.
This is really stumping me, because I don't understand why an app would work in the Editor environment, but not in the official build.
When I use TCP to communicate between two Unity apps (on the same computer), it works so long as one of them is kept in the Unity environment. That is, if I build one app, and open the other in the Unity editor, TCP communication works flawlessly.
Here is some more background: One of my apps is functioning as a User Interface, and the other is interfacing with a Looking Glass to provide a holographic display of in-game objects. Originally, they were combined into one App - but I had a lot of trouble getting Unity's multidisplay support to function between two monitors of different resolutions. Looking Glass factory even provides a prefab to do just this, but it is broken in the current SDK. So I have resorted to using sockets to interface between two apps, one for each monitor.
I'm using C#'s TCP listener class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=netframework-4.8
And TCP client class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcpclient?view=netframework-4.8
Presently, the UI is acting as the TCPListener, and the application that produces holograms is the TCPClient. Within each of these applications, I'm using two Queues - an IncomingMessages queue and an Outgoing Messages queue - which are global variables shared between the main thread and the networking thread.
TCP Listener:
private void Start()
{
incomingMessages = new Queue();
outgoingMessages = new Queue();
Application.runInBackground = true;
thread = new Thread(new ThreadStart(Receive));
thread.Start();
//stuff happens that's irrelevant to this question. And then...
}
void Receive()
{
TcpListener server = null;
try
{
// Set the TcpListener on port 13000.
Int32 port = 13000;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
Debug.Log("About to reenter main while in Server...");
while (threadContinue)
{
Debug.Log("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Debug.Log("Connected!");
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Debug.Log("Received from Client: " + data);
lock (this)
incomingMessages.Enqueue(data);
string response = supplyData();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(response);
// Send back a response.
stream.Write(msg, 0, msg.Length);
Debug.Log("Sent to Client: " + response);
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Debug.Log("Exiting 'Receive'");
}
And here is the TCP Client. It attempts to connect a regular intervals, and also whenever new data is available. This is so that it can receive information from the server regularly and share new data whenever it is available:
void Start()
{
//prepare networking
Application.runInBackground = true;
outgoingMessages = new Queue();
incomingMessages = new Queue();
thread = new Thread(new ThreadStart(Connect));
thread.Start();
//stuff happens that's irrelevant to this question...
}
private void Connect()
{
String server = "127.0.0.1";
Int32 port = 13000;
string message = "";
while (threadContinue == true)
{
if (timeToConnect())
{
lastConnection = ourTime;
if (outgoingMessages.Count > 0)
message = outgoingMessages.Dequeue().ToString();
else
message = "Nothing to report.";
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Debug.Log("Sent to Server: " + message);
// Buffer to store the response bytes.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
lock (this)
incomingMessages.Enqueue(responseData);
Debug.Log("Received from Server: " + responseData);
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
Debug.Log("ArgumentNullException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
}
}
}
private bool timeToConnect()
{
if ((ourTime - lastConnection > NETWORK_DELAY) || (outgoingMessages.Count > 0))
return true;
return false;
}
Instantiated in separate threads so that Unity's main thread can continue unhindered.
Again - it works in Editor, but when I build it, it breaks.
Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...just in the exported app. Who knows for what reason. If anyone else stumbles upon this problem...where you're bypassing Unity's multidisplay functionality by building multiple apps that communicate over network...consider that you're burdening your queues with too much data.

C# ClientWebsocket throwing Exception on incoming binary message

I implemented a websocket-client using System.Net.WebSockets to communicate with an embedded device. On the device the libwebsocket library is in use.
The main part of the protocol is implemented using JSON strings, which works perfectly but some binary transmission is also needed. Outgoing binary message from Windows are received correctly on the device, but in the case of incoming binary messages a exception is thrown at ReceiveAsync().
I implemented the receive part as a asynchronous loop that calls ReceiveAsync(). In the case of an incoming binary message the WebSocketException with the following error code is thrown:
HResult 0x83760002
E_INVALID_PROTOCOL_FORMAT Protocol data had invalid format.
I don't know what causes it, it throws before i can look at the data i receive. It already worked to receive binary data from the device, but it was an early implementation and only looped back the data that i sent.
private async void ReceiveLoop(CancellationToken cancellationToken)
{
try
{
List<byte> receivedBytes = new List<byte>();
var buffer = new byte[c_bufferSize];
while (!cancellationToken.IsCancellationRequested)
{
var receiveBuffer = new ArraySegment<Byte>(buffer);
WebSocketReceiveResult result;
result = await _webSocket.ReceiveAsync(receiveBuffer, cancellationToken);
receivedBytes.AddRange(receiveBuffer.Array);
// message is complete, return it
if (result.EndOfMessage)
{
if (result.MessageType == WebSocketMessageType.Text)
{
String receivedString = Encoding.UTF8.GetString(receivedBytes.ToArray());
Debug.Print($"{receivedString}");
Debug.Print($"{receivedString.Length.ToString()}");
WebsocketMessage response = new WebsocketMessage(receivedString);
MessagesSubject.OnNext(response);
}
if (result.MessageType == WebSocketMessageType.Binary)
{
WebsocketMessage response = new WebsocketMessage(receivedBytes.ToArray());
MessagesSubject.OnNext(response);
}
receivedBytes.Clear();
buffer = new byte[c_bufferSize];
}
}
}
catch(OperationCanceledException ex)
{
return;
}
catch(Exception ex)
{
MessagesSubject.OnError(ex);
}
}

UWP TCP server memory overflow exception

I'm building a TCP server in UWP to receive and send data from an NodeMCU (Arduino based) to an windows PC (later it will be a Raspberry Pi with Windows 10 IOT).
When setting up and startig the server there is no problem, and also the NodeMCU is changing the status to "connected" - it seems that the connection between PC and NodeMCU works just fine.
When receiving the data, I get an exception in the event handler, becaust the size (variable size in the code below) of the package is read as "1414743380" which is way too much and so I get an exception "Insufficient memory to continue the execution of the program." at line "await reader.LoadAsync((uint)size);" which seems just right.
The variable payloadsize contains this:
The question is why do I get such a package size and where does it come from?
Is there something wrong on the NodeMCU / Arduino side?
I used the code for the TCP Server from the Microsoft Example.
Starting the server:
public async Task<bool> StartListeningAsync()
{
if (_localSocket == null)
{
_localSocket = new StreamSocketListener();
_localSocket.ConnectionReceived += LocalSocketConnectionReceived;
await _localSocket.BindServiceNameAsync(CommunicationPort);
return true;
}
return false;
}
Event handler for TCP messages:
private async void LocalSocketConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
using (var reader = new DataReader(args.Socket.InputStream))
{
reader.InputStreamOptions = InputStreamOptions.None;
// Read the length of the payload that will be received.
byte[] payloadSize = new byte[(uint)BitConverter.GetBytes(0).Length];
await reader.LoadAsync((uint)payloadSize.Length);
reader.ReadBytes(payloadSize);
// Read the payload.
int size = BitConverter.ToInt32(payloadSize, 0);
byte[] payload = new byte[size];
await reader.LoadAsync((uint)size);
reader.ReadBytes(payload);
}
}
the message from the Arduino is this (from Wireshark):
EDIT: Full message description

Categories

Resources