How to create a "DataReceived" Event for SerialDevice? - c#

I am using the SerialDevice class to use RS-485 with my serial ports. It currently does not have an event for when data is received. And I currently have to poll a Listener with a DataReader which doesn't always capture all the data in its buffer.
I am looking for a way to create a receiver event for the SerialDevice class so that I don't have to poll and so that it can be triggered right when the correct data is received.
My current method:
async void Initialize()
{
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync();
if (devices.Count > 0)
{
for (int i = 0; i < devices.Count; i++)
{
if (devices[i].Id.Contains("PNP0501#2"))
{
GpioStatus = string.Format("Connecting... {0} found", devices[i].Id);
DeviceInformation deviceInfo = devices[i];
serialDevice = await SerialDevice.FromIdAsync(deviceInfo.Id);
serialDevice.BaudRate = 9600;
serialDevice.DataBits = 8;
serialDevice.StopBits = SerialStopBitCount.Two;
serialDevice.Parity = SerialParity.None;
serialDevice.Handshake = SerialHandshake.None;
}
}
}
Listen();
}
Initialize();
DataReader dataReader;
SerialDevice serialDevice;
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
const uint ReadBufferLength = 1024;
async void Listen()
{
try
{
dataReader = new DataReader(serialDevice.InputStream);
dataReader.InputStreamOptions = InputStreamOptions.Partial;
while (true)
{
await ReadAsync(cancellationTokenSource.Token);
}
}
catch
{
GpioStatus = "Failed to listen";
}
}
private async Task ReadAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
Task<UInt32> loadAsyncTask = dataReader.LoadAsync(ReadBufferLength).AsTask(cancellationToken);
UInt32 bytesRead = await loadAsyncTask;
GpioStatus = dataReader.ReadString(bytesRead);
}
Any ideas would be much appreciated!

As it's a serial protocol, you need to interpret as things come in. If the first thing expected is (say) a 4 byte length, followed by the rest of the packet, then:
while (dataReader.UnconsumedBufferLength > 0)
{
// Note that the call to readString requires a length of "code units"
// to read. This is the reason each string is preceded by its length
// when "on the wire".
uint bytesToRead = dataReader.ReadUInt32();
receivedStrings += dataReader.ReadString(bytesToRead) + "\n";
}
Code snippet taken from : https://learn.microsoft.com/en-us/uwp/api/windows.storage.streams.datareader

Related

async await in UWP windows 10 IOT core running in Raspberry Pi 3B and memory leak

First of all I am a complete begineer in C# and literally don't know how to use async/await/cancellation/Tasks, I come from a embedded systems background with basic-moderate C/C++ knowledge. So i just get the idea what those async functions do here. the async task codes are copied from samples from microsoft, they simply keep on reading the serial port for new bytes. I made some animations for the UI through Blend, and that's just it nothing more fancy.
This app runs on a raspberry with IOT core, and its sole job is to provide a UI for user to control a microcontroller with plain text instructions send through serial port. All these buttons simply send some strings to serial port which my microcontroller reads and acts accordingly.
Problem 1: I modified a line in the ReadAsync function uint ReadBufferLength = 1; the value was 128 in sample code, my microcontroller sends a complete instruction or data ending with "\r\n". This meant it will keep on reading bytes in serial that come from the microcontroller until the buffer is full and call the ReadAsync function again. since it was reading until the buffer (128) was full, multiple instructions come and fill the buffer. So i had no way to figure out what is the instruction in the buffer. So my work around here was to change the buffer to 1 and append a string with new bytes from serial port until it finds Environment.NewLine and then build logic based on the complete instruction in the string.
Question 1: How do I modify the code to not change the uint ReadBufferLength = 1; and make the ReadAsync function store my serial incoming data until it finds \r\n ?
MAJOR Problem 2: As soon as I enter Manual/Auto mode by clicking the front panel button, my microcontroller starts sending temperature sensor value through serial port as fast as possible without any delay between instructions. I know the raspberry pi (win10 iot core) is fast enough to not miss any instruction or the physical serial port buffer of the PI getting overfilled. It works fine, there is no problem at this point in the app, no crash or slowdowns. But overtime I can see that the app is consuming more and more RAM. the behavior is same in PC if I build it in x86 mode. after running it for 1 hour it will consume over 120 megabytes whereas it starts at 30-50 megabytes initially.
To recreate the problem, please change string qFilter = SerialDevice.GetDeviceSelector("UART0"); UART0 to appropriate com port in windows. otherwise the app will not run. Probably a Virtual COM port needs to be installed or a usb to ttl module might be required.
Problem 3: Cannot build the app in release mode using the option "Compile with .NET Native tool chain", without this it runs fine in the raspberry.
Internal compiler error: MCG0024:UnresolvableTypeReference Unresolvable type reference 'System.Runtime.InteropServices.WindowsRuntime.IRestrictedErrorInfo' in 'Assembly(Name=System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)' found. Please check the references in your build system. A reference is either missing or an assembly is missing an expected type. hvac_version_4
github project file
using Windows.UI;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.Devices.SerialCommunication;
using Windows.Storage.Streams;
using System.Threading;
using Windows.Devices.Enumeration;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Animation;
using Microsoft.Toolkit.Uwp.UI.Animations.Behaviors;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace hvac_version_2
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
SolidColorBrush greenBrush = new SolidColorBrush(Colors.Green);
SolidColorBrush redBrush = new SolidColorBrush(Colors.Red);
SolidColorBrush greyBrush = new SolidColorBrush(Colors.DarkSlateGray);
//private bool updatingValues;
private CancellationTokenSource ReadCancellationTokenSource;
private SerialDevice serialPort = null;
DataWriter dataWriteObject = null;
DataReader dataReaderObject = null;
public string strFromPort;
private string final_string;
private int compressorAkey=0;
private int compressorBkey=0;
private int pump_entry = 0;
private int compAentry=0;
private int compBentry=0;
private int fan1entry = 0;
private int fan2entry = 0;
private double Blur_amount = 0;
public MainPage()
{
this.InitializeComponent();
stop_button.IsEnabled = false;
set_temperature.IsEnabled = false;
set_switching_time.IsEnabled = false;
auto_mode.IsEnabled = false;
manual_mode.IsEnabled = false;
compressorA_button.IsEnabled = false;
compressorB_button.IsEnabled = false;
fanA_button.IsEnabled = false;
fanB_button.IsEnabled = false;
init.IsEnabled = false;
init_timing.IsEnabled = false;
pump_button.IsEnabled = false;
load_animation.Begin();
}
private async void stop_button_Click(object sender, RoutedEventArgs e)
{
//do something
start_animation.Stop();
if (serialPort == null) return;
await sendToPort("end");
set_temperature.IsEnabled = false;
set_switching_time.IsEnabled = false;
manual_mode.IsEnabled = true;
auto_mode.IsEnabled = true;
stop_button.IsEnabled = false;
compressorA_button.IsEnabled = false;
compressorB_button.IsEnabled = false;
fanA_button.IsEnabled = false;
fanB_button.IsEnabled = false;
init.IsEnabled = false;
init_timing.IsEnabled = false;
pump_button.IsEnabled = false;
start_button.IsEnabled = true;
pump_entry = 0;
compAentry = 0;
compBentry = 0;
fan1entry = 0;
fan2entry = 0;
stop_animation.Begin();
start_button.Width = 162;
GC.Collect();
}
private async void set_temperature_changed(object sender, EventArgs e)
{
//System.Diagnostics.Debug.WriteLine("entered the async void set_temp.............");
}
private async void set_switching_time_value_changed(object sender, EventArgs e)
{
//System.Diagnostics.Debug.WriteLine("entered the async void switching time.............");
}
private async void manual_mode_Click(object sender, RoutedEventArgs e)
{
if (serialPort == null) return;
await sendToPort("manual");
auto_mode.IsEnabled = false;
manual_mode.IsEnabled = false;
stop_button.IsEnabled = true;
set_temperature.IsEnabled = true;
set_switching_time.IsEnabled = true;
pump_button.IsEnabled = true;
fanA_button.IsEnabled = true;
fanB_button.IsEnabled = true;
compressorA_button.IsEnabled = true;
compressorB_button.IsEnabled = true;
init.IsEnabled = true;
init_timing.IsEnabled = true;
}
private async void auto_mode_Click(object sender, RoutedEventArgs e)
{
set_switching_time.IsEnabled = true;
set_temperature.IsEnabled = true;
init.IsEnabled = true;
stop_button.IsEnabled = true;
init_timing.IsEnabled = true;
if (serialPort == null) return;
await sendToPort("auto");
}
private async void start_button_Click(object sender, RoutedEventArgs e)
{
stop_animation.Stop();
await sendToPort("ready");
auto_mode.IsEnabled = true;
manual_mode.IsEnabled = true;
start_button.IsEnabled = false;
start_animation.Begin();
System.Diagnostics.Debug.WriteLine("sending ready command");
}
private async void compressorA_button_Click(object sender, RoutedEventArgs e)
{
if (compAentry == 0)
{
if (serialPort == null) return;
await sendToPort("compressorAON"); //sending key
compAentry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("compressorAoff"); //sending key
compAentry = 0;
}
}
private async void compressorB_button_Click(object sender, RoutedEventArgs e)
{
if (compBentry == 0)
{
if (serialPort == null) return;
await sendToPort("compressorBON"); //sending key
compBentry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("compressorBoff"); //sending key
compBentry = 0;
}
}
private async void fanA_button_Click(object sender, RoutedEventArgs e)
{
if (fan1entry == 0)
{
if (serialPort == null) return;
await sendToPort("fan1on");
fan1entry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("fan1off");
fan1entry = 0;
}
}
private async void fanB_button_Click(object sender, RoutedEventArgs e)
{
if (fan2entry == 0)
{
if (serialPort == null) return;
await sendToPort("fan2on");
fan2entry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("fan2off");
fan2entry = 0;
}
}
private async void pump_button_Click(object sender, RoutedEventArgs e)
{
if (pump_entry == 0)
{
if (serialPort == null) return;
await sendToPort("pumpon");
pump_entry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("pumpoff");
pump_entry = 0;
}
}
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
string qFilter = SerialDevice.GetDeviceSelector("UART0");
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(qFilter);
if (devices.Any())
{
string deviceId = devices.First().Id;
await OpenPort(deviceId);
}
ReadCancellationTokenSource = new CancellationTokenSource();
while (true)
{
//System.Diagnostics.Debug.WriteLine("program came before await listen");
await Listen();
}
}
private async Task OpenPort(string deviceId)
{
serialPort = await SerialDevice.FromIdAsync(deviceId);
if (serialPort != null)
{
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(50);
serialPort.BaudRate = 115200;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
//Console.WriteLine("Serial port configured successfully");
//txtStatus.Text = "Serial port configured successfully";
}
}
private async Task Listen()
{
try
{
if (serialPort != null)
{
dataReaderObject = new DataReader(serialPort.InputStream);
await ReadAsync(ReadCancellationTokenSource.Token);
}
}
catch (Exception ex)
{
txtStatus.Text = ex.Message;
}
finally
{
if (dataReaderObject != null) // Cleanup once complete
{
dataReaderObject.DetachStream();
dataReaderObject = null;
}
}
}
private async Task ReadAsync(CancellationToken cancellationToken)
{
Task<UInt32> loadAsyncTask;
uint ReadBufferLength = 1; // only when this buffer would be full next code would be executed
dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken); // Create a task object
//string debug = dataReaderObject.ReadString(bytesRead2);
//System.Diagnostics.Debug.WriteLine("writing string debug"+ debug);
UInt32 bytesRead = await loadAsyncTask; // Launch the task and wait until buffer would be full
if (bytesRead > 0)
{
strFromPort = dataReaderObject.ReadString(bytesRead);
final_string = final_string + strFromPort;
//txtStatus2.Text = final_string;
if (final_string.Contains(Environment.NewLine))
{
string logicstring = final_string;
final_string = "";
//System.Diagnostics.Debug.WriteLine("writing logic string " + logicstring);
if (logicstring.StartsWith("A"))
{
if(logicstring.Contains("-"))
{
//it is a negative number
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge1_pass = int.Parse(logicstring) * -1;
tempgauge1.Value = gauge1_pass;
}
else
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge1_pass = int.Parse(logicstring);
tempgauge1.Value = gauge1_pass;
}
}
if (logicstring.StartsWith("B"))
{
if (logicstring.Contains("-"))
{
//it is a negative number
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge2_pass = int.Parse(logicstring) * -1;
tempgauge1.Value = gauge2_pass;
}
else
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge2_pass = int.Parse(logicstring);
tempgauge2.Value = gauge2_pass;
}
}
if (logicstring.StartsWith("setting"))
{
if (logicstring.Contains("-"))
{
//it is a negative number
logicstring = Regex.Match(logicstring, #"\d+").Value;
int previous_temp_setting = int.Parse(logicstring) * -1;
set_temperature.Value = previous_temp_setting;
}
else
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int previous_temp_setting = int.Parse(logicstring);
set_temperature.Value = previous_temp_setting;
}
}
if (logicstring.StartsWith("pumpon"))
{
pump_status_light.Fill = greenBrush;
Storyboard_Pump_Led.RepeatBehavior = RepeatBehavior.Forever;
Storyboard_Pump_Led.Begin();
}
if (logicstring.StartsWith("pumpoff"))
{
pump_status_light.Fill = redBrush;
Storyboard_Pump_Led.Stop();
}
if (logicstring.StartsWith("pumpfail"))
{
if (!FailPopup.IsOpen)
{
FailPopup.IsOpen = true;
}
}
if (logicstring.StartsWith("clear the popup"))
{
if (FailPopup.IsOpen)
{
FailPopup.IsOpen = false;
}
}
if (logicstring.StartsWith("switch_interval"))
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int previous_interval_setting = int.Parse(logicstring);
set_switching_time.Value = previous_interval_setting;
}
if (logicstring.StartsWith("timer"))
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int remaining_time = int.Parse(logicstring);
remaining_time = remaining_time / 1000;
timer_box.Text = remaining_time.ToString();
}
if (logicstring.StartsWith("Free RAM = "))
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int remaining_memory = int.Parse(logicstring);
free_memory.Text = remaining_memory.ToString();
}
if (logicstring.StartsWith("fan1on"))
{
fan1_status_light.Fill = greenBrush;
storyboard_fan1_led.RepeatBehavior = RepeatBehavior.Forever;
storyboard_fan1_led.Begin();
}
if (logicstring.StartsWith("fan1off"))
{
fan1_status_light.Fill = redBrush;
storyboard_fan1_led.Stop();
}
if (logicstring.StartsWith("fan2on"))
{
fan2_status_light.Fill = greenBrush;
storyboard_fan_2.RepeatBehavior = RepeatBehavior.Forever;
storyboard_fan_2.Begin();
}
if (logicstring.StartsWith("fan2off"))
{
fan2_status_light.Fill = redBrush;
storyboard_fan_2.Stop();
}
if (logicstring.StartsWith("compressorAON")) //key
{
compressorA_status_light.Fill = greyBrush;
compressorAkey = 1;
}
if (logicstring.StartsWith("compressorAoff")) //key
{
compressorA_status_light.Fill = redBrush;
compressorAkey = 0;
}
if (logicstring.StartsWith("compressorBON")) //key
{
compressorB_status_light.Fill = greyBrush;
compressorBkey = 1;
}
if (logicstring.StartsWith("compressorBoff")) //key
{
compressorB_status_light.Fill = redBrush;
compressorBkey = 0;
}
if (logicstring.StartsWith("compressorA on")) // actually compressor on
{
compressorA_status_light.Fill = greenBrush;
storyboard_compA_Led.RepeatBehavior = RepeatBehavior.Forever;
storyboard_compA_Led.Begin();
}
if (logicstring.StartsWith("compressorB on")) // actually compressor on
{
compressorB_status_light.Fill = greenBrush;
storyboard_compB_led.RepeatBehavior = RepeatBehavior.Forever;
storyboard_compB_led.Begin();
}
if (logicstring.StartsWith("compressor off")) // actually compressor off
{
if (compressorBkey == 1)
{
compressorB_status_light.Fill = greyBrush;
storyboard_compB_led.Stop();
}
if (compressorAkey == 1)
{
compressorA_status_light.Fill = greyBrush;
storyboard_compA_Led.Stop();
}
if (compressorBkey == 0)
{
compressorB_status_light.Fill = redBrush;
storyboard_compB_led.Stop();
}
if (compressorAkey == 0)
{
compressorA_status_light.Fill = redBrush;
storyboard_compA_Led.Stop();
}
}
if (logicstring.StartsWith("compressorA off")) // actually compressor off
{
compressorA_status_light.Fill = redBrush;
storyboard_compA_Led.Stop();
}
if (logicstring.StartsWith("compressorB off")) // actually compressor off
{
compressorB_status_light.Fill = redBrush;
storyboard_compB_led.Stop();
}
}
}
}
private async Task WriteAsync(string text2write)
{
Task<UInt32> storeAsyncTask;
if (text2write.Length != 0)
{
dataWriteObject.WriteString(text2write);
storeAsyncTask = dataWriteObject.StoreAsync().AsTask(); // Create a task object
UInt32 bytesWritten = await storeAsyncTask; // Launch the task and wait
if (bytesWritten > 0)
{
//txtStatus.Text = bytesWritten + " bytes written at " + DateTime.Now.ToString(System.Globalization.CultureInfo.CurrentUICulture.DateTimeFormat.LongTimePattern);
}
}
else { }
}
private async Task sendToPort(string sometext)
{
try
{
if (serialPort != null)
{
dataWriteObject = new DataWriter(serialPort.OutputStream);
await WriteAsync(sometext);
}
else { }
}
catch (Exception ex)
{
txtStatus.Text = ex.Message;
}
finally
{
if (dataWriteObject != null) // Cleanup once complete
{
dataWriteObject.DetachStream();
dataWriteObject = null;
}
}
}
private void CancelReadTask()
{
if (ReadCancellationTokenSource != null)
{
if (!ReadCancellationTokenSource.IsCancellationRequested)
{
ReadCancellationTokenSource.Cancel();
}
}
}
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
CancelReadTask();
if (serialPort != null)
{
serialPort.Dispose();
}
serialPort = null;
}
private async void init_Click(object sender, RoutedEventArgs e)
{
double sendtempvalue = (double)set_temperature.Value;
if (serialPort == null) return;
await sendToPort("settemp," + sendtempvalue);
//System.Diagnostics.Debug.WriteLine("EEPROM " + sendtempvalue);
}
private async void init_timing_Click(object sender, RoutedEventArgs e)
{
double switching_time_minute = (double)set_switching_time.Value;
if (serialPort == null) return;
await sendToPort("switch_interval," + switching_time_minute);
//System.Diagnostics.Debug.WriteLine("EEPROM " + switching_time_minute);
}
/*
private void Page_Unloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
}*/
}
}
Question 1: How do I modify the code to not change the uint
ReadBufferLength = 1; and make the ReadAsync function store my serial
incoming data until it finds \r\n ?
Please try to use following code. When using ReadBufferLength = 1, the transmission efficiency will be lower.
private async Task ReadAsync(CancellationToken cancellationToken)
{
Task<UInt32> loadAsyncTask;
uint ReadBufferLength = 128; // only when this buffer would be full next code would be executed
dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken); // Create a task object
//string debug = dataReaderObject.ReadString(bytesRead2);
//System.Diagnostics.Debug.WriteLine("writing string debug"+ debug);
UInt32 bytesRead = await loadAsyncTask; // Launch the task and wait until buffer would be full
if (bytesRead > 0)
{
strFromPort = dataReaderObject.ReadString(bytesRead);
final_string = final_string + strFromPort;
//txtStatus2.Text = final_string;
if (final_string.Contains(Environment.NewLine))
{
string logicstring = final_string;
final_string = final_string.Substring(final_string.IndexOf(Environment.NewLine) + 1);
//System.Diagnostics.Debug.WriteLine("writing logic string " + logicstring);
}
}
}
Problem 2: after running it for 1 hour it will consume over 120
megabytes whereas it starts at 30-50 megabytes initially.
Please try to build the app in release but not using the .NET native toolchain.It's a known issue. Please see here.
Problem 3: Cannot build the app in release mode using the option
"Compile with .NET Native tool chain", without this it runs fine in
the raspberry.
Please remove the references runtime.win10-arm-aot.Microsoft.NETCore.UniversalWindowsPlatform and runtime.win10-arm.Microsoft.NETCore.UniversalWindowsPlatform in your project. The project will be compiled with .Net native tool chain successfully without the two libraries, since that these two libs depends on the native lib of System.Runtime.InteropServices.

Networkstream multiple threads and transferring data

I have a application that uses a Networkstream to communicate with a server. Reading and writing takes place on two different threads. One thread is always listening for data coming from the server and the other thread writes data to the stream to send a request. The server then processes the data and then sends back a order number, but since I am writing and reading on two threads I canĀ“t read on the thread on which I initially send the data to the sever.
My Problem is that I create a listviewitem for each request I send to the server and I somehow need to identify the listviewitem that corresponds to the received order number.
private void SendCheckoutData()
{
//Start sending data
manager.SendInt(TableId);
Thread.Sleep(80);
manager.SendInt(ClientId);
Thread.Sleep(80);
manager.SendInt(ListViewCheckoutTable.Items.Count);
Thread.Sleep(80);
manager.SendInt(GetCombinedPrice());
Thread.Sleep(80);
int NumberOfItems = 0;
for (int i = 0; i < ListViewCheckoutTable.Items.Count; i++)
{
OrderMenuItem item = (OrderMenuItem)ListViewCheckoutTable.Items.GetItemAt(i);
manager.SendInt(item.Id);
Thread.Sleep(80);
manager.SendInt(item.Quantity);
Thread.Sleep(80);
NumberOfItems += item.Quantity;
}
int OrderNumber; //The variable I somehow need to assign the value to
ListViewPendingOrders.Items.Add(new PendingOrderMenuItem { OrderNumber = OrderNumber, Quantity = NumberOfItems, TotalPrice = GetCombinedPrice() });
}
TCPManager
class TCPManager
{
private readonly string ip;
private readonly int port;
TcpClient client;
NetworkStream stream;
Task ListenerTask;
CancellationTokenSource cancelToken = new CancellationTokenSource();
public TCPManager(string ip, int port)
{
this.ip = ip;
this.port = port;
}
public void Connect()
{
client = new TcpClient(ip, port);
stream = client.GetStream();
}
public void Disconnect()
{
cancelToken.Cancel();
while (!ListenerTask.IsCompleted)
{
Thread.Sleep(10);
}
stream.Close();
client.Close();
}
public void ReadData()
{
ListenerTask = new Task(() =>
{
while (true)
{
while (!stream.DataAvailable)
{
Thread.Sleep(100);
}
Thread.Sleep(100);
StartNewTask(GetInt());
}
}, cancelToken.Token, TaskCreationOptions.LongRunning);
}
private void StartNewTask(int request)
{
switch (request)
{
case RequestIds.REQUEST_ORDERNUMBER:
break;
case RequestIds.REQUEST_ORDER_COMPLETED:
break;
}
}
public int GetInt()
{
byte[] bData = new byte[sizeof(int)];
stream.Read(bData, 0, bData.Length);
int Data = BitConverter.ToInt32(bData, 0);
return Data;
}

UWP app slows, then crashes after certain number of serial writes

I am making an application using Windows IoT Core on the Raspberry Pi 3.
I am trying to communicate with an Arduino using serial communication. I have used most of the methods shown in this example on GitHub for creating the serial device and writing to it: https://github.com/ms-iot/samples/tree/develop/SerialSample/CS.
The program runs fine for the first 130 bytes or so, but then will begin to slow down dramatically after that and crash around the 135th byte or so.
Sometimes it causes the Raspberry Pi to display the Windows 10 "blue screen of death" saying that it needs to restart.
I've tried rewriting the code many ways and using different variations of async, task, and await, but can't seem to get it to work.
Here's the code I use to initialize the serial device:
private async void setup()
{
serialPort = await initializeSerial();
}
private async Task<SerialDevice> initializeSerial()
{
try
{
string aqs = SerialDevice.GetDeviceSelector();
var dis = await DeviceInformation.FindAllAsync(aqs);
for (int i = 0; i < dis.Count; i++)
{
if (dis[i].Name.Contains("Arduino Uno"))
{
var serialPort = await SerialDevice.FromIdAsync(dis[i].Id);
/* Configure serial settings */
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.BaudRate = 9600;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
// Display configured settings
//Debug.WriteLine("Serial port configured successfully.");
// Create cancellation token object to close I/O operations when closing the device
ReadCancellationTokenSource = new CancellationTokenSource();
return serialPort;
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return null;
}
and here's the code I use to write to the serial device:
private async void sendString(string stringToSend)
{
try
{
if (serialPort != null)
{
// Create the DataWriter object and attach to OutputStream
dataWriteObject = new DataWriter(serialPort.OutputStream);
//Launch the WriteAsync task to perform the write
await WriteAsync(stringToSend);
}
else
{
status.Text = "Select a device and connect";
}
}
catch (Exception ex)
{
status.Text = "sendString: " + ex.Message;
}
finally
{
// Cleanup once complete
if (dataWriteObject != null)
{
dataWriteObject.DetachStream();
dataWriteObject = null;
}
}
}
/// <summary>
/// WriteAsync: Task that asynchronously writes data from the input text box 'sendText' to the OutputStream
/// </summary>
/// <returns></returns>
private async Task WriteAsync(string stringToWrite)
{
Task<UInt32> storeAsyncTask;
dataWriteObject.WriteString(stringToWrite);
// Launch an async task to complete the write operation
storeAsyncTask = dataWriteObject.StoreAsync().AsTask();
UInt32 bytesWritten = await storeAsyncTask;
if (bytesWritten > 0)
{
counter++;
status.Text = stringToWrite + ", ";
status.Text += "bytes written successfully! (" + counter + ")";
}
}
Any help/advice would be appreciated.
Update
I rewrote my sendString method as this and simplified it using a using statement:
private async void sendString(string stringToSend)
{
using (DataWriter dataWriter = new DataWriter(serialPort.OutputStream))
{
dataWriter.WriteString(stringToSend);
await dataWriter.StoreAsync();
//await dataWriter.FlushAsync();
dataWriter.DetachStream();
counter++;
status.Text = stringToSend + ", ";
status.Text += "bytes written successfully! (" + counter + ")";
}
}
However, the problem still persists.
I tried using the UART pins instead of the USB cable to write data, and experienced no more issues. Here's the code I used to initialize the UART bus on the Pi and I used the same function to write as I used above:
private async Task<SerialDevice> initializeSerial()
{
try
{
string aqs = SerialDevice.GetDeviceSelector("UART0");
var dis = await DeviceInformation.FindAllAsync(aqs);
SerialDevice serialPort = await SerialDevice.FromIdAsync(dis[0].Id);
/* Configure serial settings */
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.BaudRate = 9600;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
status.Text = "Serial port configured succesfully.";
return serialPort;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
status.Text = "Serial port not configured successfully. Are you sure a serial device is connected?";
return null;
}
This doesn't exactly answer the problem with sending data over the USB cable, but I'm posting it here in case anyone has a similar problem with using the USB port and would like to try this instead.

Windows 10 Universal App - Socket connection recurrent readings

I am following the Socket Example about StreamSockets. I can read from the server as a client. The example is https://social.msdn.microsoft.com/Forums/es-ES/79eda064-473d-4398-8fe3-72369e686c3c/comunicacion-tcpcip-con-streamsocket-y-datawriterdatareader?forum=esdevwindows
I'm building a Windows 10 Universal Application and I need continuous readings from a socket connection. I'm getting the first reading but then only empty strings.
Thanks!
Image capture response
Private StreamSocket socketForServer;
Private DataWriter cfaStreamWriter;
Private DataReader cfaStreamReader;
// Read socket data
Private Async void ReadData()
{
If (socketForServer == null) Then Return;
uint s = await cfaStreamReader.LoadAsync(1024);
String Data = cfaStreamReader.ReadString(s);
PrintData(Data);
var ignore = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () >=
ReadData());
}
// Connect to the bid controller through TCP Socket.
Public Async Task<bool> Connect()
{
socketForServer = New StreamSocket();
var hostname = New HostName(deviceSPACS_IP);
await socketForServer.ConnectAsync(hostname, deviceSPACS_PORT.ToString());
// send login
SendRawMessage("WELCOME TO SERVER");
cfaStreamReader = New DataReader(socketForServer.InputStream)
{
InputStreamOptions = InputStreamOptions.Partial
};
ReadData();
Return True;
}
// Disconnect from bid Controller
Public bool Disconnect()
{
If (socketForServer == null) Then Return False;
SendRawMessage("GOOD BYE AL SERVER");
cfaStreamReader.Dispose();
cfaStreamReader = null;
socketForServer.Dispose();
socketForServer = null;
Return True;
}
Private Async void SendRawMessage(String message)
{
cfaStreamWriter = New DataWriter(socketForServer.OutputStream); cfaStreamWriter.WriteString(message + "\r\n");
await cfaStreamWriter.StoreAsync();
await cfaStreamWriter.FlushAsync();
cfaStreamWriter.DetachStream();
cfaStreamWriter.Dispose();
cfaStreamWriter = null;
}

Loop with async wait operation weirdness

I have the following client listener that pass the client to HandleStationClients. The constructor of HandleStationClients starts a Task with the connection in other thread for listening.
The code below runs on the main thread with async function. When a client connects the awaiting part below will continue and passes the client to new created HandleStationClients and hook the events.
Normally after wiring the events the loop will start over and wait for a new connection at the await.
The problem is that this code loop twice for each connection. So client connects and HandleStationClients will created and events will be hooked and while loop starts again and then continue to run the same process creating again a new HandleStationClients and event hooks.
After the client is processed the awaiter is not waiting but continuing for a second time.The events are fired twice. I dont know whats wrong. Any one have a clue?
while (true)
{
counter += 1;
// Wait for new connection then do rest
stationsClientSocket = await stationsServerSocket.AcceptTcpClientAsync();
stationClients.Add(stationsClientSocket, 0);
Debug.WriteLine("Client toegevoegd " + counter);
HandleStationClients stationClient = new HandleStationClients(stationsClientSocket);
stationClient.ConnectionEstabilished += stationClient_ConnectionEstabilished;
stationClient.ConnectionClosed += stationClient_ConnectionClosed;
stationClient.NewDataReceived += stationClient_NewDataReceived;
}
The HandleClient looks like
class HandleStationClients
{
public HandleStationClients(TcpClient client)
{
Task.Factory.StartNew(() => { ProcessConnection(client); });
}
#region Event definitions
public delegate void NewDataReceivedEventHandler(string newData);
public event NewDataReceivedEventHandler NewDataReceived;
public delegate void ConnectionClosedEventHandler();
public event ConnectionClosedEventHandler ConnectionClosed;
public delegate void ConnectionEstabilishedEventHandler(IPEndPoint endpoint);
public event ConnectionEstabilishedEventHandler ConnectionEstabilished;
#endregion
public async void ProcessConnection(TcpClient stationsClientSocket)
{
byte[] message = new byte[1024];
int bytesRead;
NetworkStream networkStream = stationsClientSocket.GetStream();
if (this.ConnectionEstabilished != null)
{
this.ConnectionEstabilished((IPEndPoint)stationsClientSocket.Client.RemoteEndPoint);
}
while ((true))
{
bytesRead = 0;
try
{
bytesRead = await networkStream.ReadAsync(message, 0, 1024);
}
catch (Exception ex)
{
// some error hapens here catch it
Debug.WriteLine(ex.Message);
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
ASCIIEncoding encoder = new ASCIIEncoding();
if (this.NewDataReceived != null)
{
byte[] buffer = null;
string incomingMessage = encoder.GetString(message, 0, bytesRead);
this.NewDataReceived(incomingMessage);
}
}
stationsClientSocket.Close();
// Fire the disconnect Event
this.ConnectionClosed();
}
}
It's a bad idea to start the task in the constructor. This means your task is running while you are registering the event handlers. There is a decent chance that sometime you won't get an event registered before it needs to be fired.
What you should do is wait to start the task until the event handlers are all registered. You will need to create a Start method to take care of starting the task, and have the code call it once the events are registered.
The updated class:
class HandleStationClients
{
// Added a field to store the value until the Start method
TcpClient _client;
public HandleStationClients(TcpClient client)
{
this._client = client;
// Moved the line from here...
}
public void Start()
{
// ...to here.
Task.Factory.StartNew(() => { ProcessConnection(_client); });
}
#region Event definitions
// ...
#endregion
public async void ProcessConnection(TcpClient stationsClientSocket)
{
byte[] message = new byte[1024];
int bytesRead;
NetworkStream networkStream = stationsClientSocket.GetStream();
if (this.ConnectionEstabilished != null)
{
this.ConnectionEstabilished((IPEndPoint)stationsClientSocket.Client.RemoteEndPoint);
}
while ((true))
{
bytesRead = 0;
try
{
bytesRead = await networkStream.ReadAsync(message, 0, 1024);
}
catch (Exception ex)
{
// some error hapens here catch it
Debug.WriteLine(ex.Message);
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
ASCIIEncoding encoder = new ASCIIEncoding();
if (this.NewDataReceived != null)
{
byte[] buffer = null;
string incomingMessage = encoder.GetString(message, 0, bytesRead);
this.NewDataReceived(incomingMessage);
}
}
stationsClientSocket.Close();
// Fire the disconnect Event
// I added a line to check that ConnectionClosed isn't null
if (this.ConnectionClosed != null)
{
this.ConnectionClosed();
}
}
}
Then you need to change the calling code as follows.
while (true)
{
counter += 1;
// Wait for new connection then do rest
stationsClientSocket = await stationsServerSocket.AcceptTcpClientAsync();
stationClients.Add(stationsClientSocket, 0);
Debug.WriteLine("Client toegevoegd " + counter);
HandleStationClients stationClient = new HandleStationClients(stationsClientSocket);
stationClient.ConnectionEstabilished += stationClient_ConnectionEstabilished;
stationClient.ConnectionClosed += stationClient_ConnectionClosed;
stationClient.NewDataReceived += stationClient_NewDataReceived;
// Call Start manually
stationClient.Start();
}
I moved the Task start away from the constructor to a Start method. problem is solved.

Categories