SerialPort in WPF threw I/O Exception - c#

I am work on a very simple program (C# WPF application in VS2010) that display data from SerialPort and display on a TextBox. The program works fine in normal circumstances. But when user open a connection, collect some data, close it, and open it again, and do this for several cycles, the program will eventually throw an exception:
"The I/O operation has been aborted because of either a thread exit or an application request."
[The I/O Exception occurred in the ReadLine()]
Sometime the program would throw me an exception; sometimes the program just hangs.
Below is my code:
/* Click to Open ComPort */
private void PortOpen_Click(object sender, RoutedEventArgs e)
{
if (!serialPort1.IsOpen)
{
serialPort1.PortName = "COM1";
serialPort1.BaudRate = 9600;
serialPort1.ReceivedBytesThreshold = 1;
serialPort1.NewLine = "\r\n";
serialPort1.Parity = Parity.None;
serialPort1.StopBits = StopBits.One;
serialPort1.DataBits = 8;
serialPort1.Handshake = Handshake.None;
serialPort1.Open();
serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(Receive);
}
}
/* Receive data from ComPort */
private void Receive(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (serialPort1.IsOpen)
{
try
{
string1 = serialPort1.ReadLine(); /* This is where I/O Exception occurred */
Dispatcher.Invoke(DispatcherPriority.Send, new UpdateUiTextDelegate(DisplayText), string1);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
private void DisplayText(string string1)
{
textBox1.Text = string1;
}
/* Close ComPort */
private void PortClose_Click(object sender, RoutedEventArgs e)
{
if (serialPort1.IsOpen)
{
serialPort1.Close();
}
}
Following summarize my head-banging-against-table attempts in the past 40 hours which result in no progress:
I have tried add Thread.Sleep(3000) before and after Open() and Close(). And I start to get so frustrated that I put Thread.Sleep in between every single line. I though that would allow enough time for some unfinished working in the background. Doesn't solve the problem.
I tried Zach Saw's Post. A lot of comments left on the blog post are very positive. I tried the approach and even copy and paste the exact code to mine. Doesn't solve the problem. A very long post that wasted half of my day.
Kim Hamilton address the issues here. which suggest using BeginInvoke instead of Invoke. Tried and still persist the same problem.
There was a very nice commercial SerialPort library Franson SerialTools which is fairly cheap and works fantastic with no bugs regardless of how many time and how quick I Open() or Close() the serialPort. However, they have discontinue their development and the library they have only works on Form application, not WPF. Some of their argument in their API only accepts Forms.Control. too bad. There are other Commercial product out there but either they are overly priced or do not offer free trail so I wouldn't know whether it works or not before purchase
Does anyone get the .NET SerialPort to work and actually check for bugs (Open() and Close() many times - even when there are no incoming data)?

When you open port you add event handler, when closing i think you should remove it.
Try to do this:
private void PortClose_Click(object sender, RoutedEventArgs e)
{
if (serialPort1.IsOpen)
{
serialPort1.DataReceived -= Receive;
serialPort1.Close();
}
}
Hope this solves problem.

I imagine the blocking call to ReadLine is interrupted by a call to Close from the UI thread. What's wrong with catching this error? I would expect it to happen.

#Reniuz solution didn't help me also.
Actually I couldn't understand how it could help at all, obviously as #Hans Passant comment, the DataReceived could be already in progress while unsubscribing the event.
My solution for this was to unsubscribe within the DataReceived event, just raise a flag whenever unsubscribe is needed and check for it in the DataReceived event.
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
// Brief: Handle data received event
{
// Read the data
// ~~~~~~~~~~~~~
// Check for unsubscribe
// ~~~~~~~~~~~~~~~~~~~~~
if (bStopDataRequested)
{
serialPort1.DataReceived -= DataReceivedHandler; // Unsubscribe to DataReceived event
// Only then close the port
}
}

Related

Serial monitor, DataReceived handler misunderstanding C#, WPF

I'm developing a serial monitor for my application in WPF, programming in C#.
I have trouble managing the DataReceived event, because I want a real time monitor like HyperTerminal or TeraTerm for example (I'm not using them because I want my terminal to be a part of an ethernet communication tool, which I already developed using winPcap).
I have to read some data from my microcontroller, display it on the textBox (It just prints a menu and the list of commands available) and when it finishes the loading sequence I would like to interact with it, nothing special, just send a "flash-" command to program the fpga of the board.
My application goes in exception when I try to update the textbox.text with the data received. I tried to search everywhere but despite a lot of examples, I didn't catch something which is explaining the code properly.
Here is the code, thanks in advance
namespace WpfApplication1 {
/// <summary>
/// Interaction logic for SerialMonitor.xaml
/// </summary>
public partial class SerialMonitor : Window {
//VARIABLES
public SerialPort comPort = new SerialPort();
public SerialMonitor() {
//initialization
InitializeComponent();
scanPorts();
}
private void scanPorts() {
textBoxIndata.Clear();
string[] ports = SerialPort.GetPortNames();
foreach (string port in ports) {
comboBoxPorts.Items.Add(port);
}
}
private void openComBtn_Click(object sender , RoutedEventArgs e) {
comPort.Parity = Parity.None;
comPort.DataBits = 8;
comPort.ReadTimeout = 500;
comPort.StopBits = StopBits.One;
if (comboBoxPorts.SelectedItem != null && comboBoxPorts.SelectedItem != null) {
comPort.PortName = comboBoxPorts.SelectedItem.ToString();
comPort.BaudRate = Convert.ToInt32(comboBoxBaud.Text);
try {
//Open port and add the event handler on datareceived
comPort.Open();
comPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
catch (Exception ex) {
MessageBox.Show(ex.ToString());
}
}
if (comPort.IsOpen) {
label1.Content = "COM PORT OPEN";
}
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
}
//function to update the textBox, didn't manage to get it working
private void updateUI (string s) {
}
//CLOSE AND EXIT BUTTONS
private void closeComBtn_Click(object sender , RoutedEventArgs e) {
if (comPort.IsOpen) {
comPort.Close();
label1.Content = "COM PORT CLOSED";
}
}
private void exitBtn_Click(object sender , RoutedEventArgs e) {
if (comPort.IsOpen) {
comPort.Close();
}
this.Close();
}
}
}
I got now the problem that when I send my command using SerialPort.Write(string cmd), I can't read back the answer...
EDIT: Fixed everything, I will post the code if anyone is interested in programming a tool like this one
DataReceived event returns on another/secondary thread, which means you will have to marshal back to the UI thread to update your TextBox
SerialPort.DataReceived Event
The DataReceived event is raised on a secondary thread when data is
received from the SerialPort object. Because this event is raised on a
secondary thread, and not the main thread, attempting to modify some
elements in the main thread, such as UI elements, could raise a
threading exception. If it is necessary to modify elements in the main
Form or Control, post change requests back using Invoke, which will do
the work on the proper thread.
You can use Dispatcher.BeginInvoke or Dispatcher.Invoke Method to marshal back to the main thread
Exmaple
Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));
or
someControl.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));
I got now the problem that when i send my command using SerialPort.Write(string cmd), I can't read back the answer...

is AutoResetEvent waitOne() function what I am looking for?

I am new on this forum but I have always checked it out and I appreciate it very much.
I must say in the beginning that I am not a very skilled or knowledgable software programmer and for the past days I am stuck on a problem. I have a barcode reader on my COM1 port. I connect, I write "TRIGGER", it returns with(hopefully) "OK", when I have the OK, then I send a "READ", and it comes back with a big line with some fields. So here is right now how my code looks like; (the code is mainly like this. I didn`t write it all but the serialPort and waitHandle stuff is like below)
in SerialPortExample class,
public AutoResetEvent waitHandle = new AutoResetEvent(false);
public string Read()
{
for (int i=0; i<1000; i++) {
this._serialPort.Write("TRIGGER" + Convert.ToChar(0));
waitHandle.WaitOne();
// a couple lines of stuff
this._serialPort.Write("GETSTRINGRESULTS" + Convert.ToChar(0));
waitHandle.WaitOne();
if (OBarcode.ReadStatus == ReadStatus.SUCCESS) {
return OBarcode.BarcodeString;
}
}
return "";
}
this is raised when some answer from serialPort
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try {
string value = _serialPort.ReadExisting();
...
// stuff
waitHandle.Set();
} catch(Exception ex) {
// stuff
}
}
in the form with behind a button click, I open the connection, do the Read(), Close() the connection and so on. I have seen this AutoResetEvent online and decided to use. When I click the button for the first time, everything runs smooth, but, if I click the button again. which reoopens the closed connection and does the read again, this time the WaitOne() doesn`t wait.
I would appreciate very much appreciate your help. How should I normally handle this? I am doing something seriously wrong.

receiving serial port data in a while loop within startbutton_Click() function

I have created a wpf from and in it each time I get two byte from serial port and find the difference between them and then in a while loop I show the difference by a textbox:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
SerialPort port;
private void StartButton_Click(object sender, RoutedEventArgs e)
{
port = new SerialPort("COM3");
port.BaudRate = 9600;
port.DtrEnable = true;
port.RtsEnable = true;
port.Handshake = Handshake.None;
port.Open();
try
{
if (!port.IsOpen)
throw new Exception();
}
catch (Exception)
{
Console.Out.WriteLine("port is not open!!");
}
while (port.IsOpen)
{
var b1 = port.ReadByte();
var b2 = port.ReadByte();
double t1 = b1 * 1e-9;
double t2 = b2 * 1e-9; ;
var dift = t2 - t1;
if (dift == 0)
{
this.SpeedTextBox.Text = "0";
continue;
}
this.SpeedTextBox.Text = dift;
}
}
private void StopButton_Click(object sender, RoutedEventArgs e)
{
if (port != null)
{
if (port.IsOpen) port.Close();
port.Dispose();
}
}
}
but when I execute it and click on StartButton the form will be stoped working.I know that program receives data( I tested it with another simple program) . but I don't know what goes wrong here!!
can anyone help me?
thanks in advance.
ReadByte() is a blocking method, it won't return until a byte was received. This is why SerialPort has the DataReceived event.
First use another program like Hyperterminal or Putty to test the connection and eliminate simple mistakes like bad wiring, picking the wrong port number or baud rate and getting the Parity, DataBits and StopBits settings wrong. Which you don't set so there are non-zero odds that you'll get framing errors. You must implement the ErrorReceived event to ensure these kind of errors do not go unobserved and leave you mystified why it doesn't work.
If you don't use DataReceived then it is also important that you use the ReadTimeout property to ensure your program doesn't hang forever without any way to diagnose the cause if there's something wrong with the connection. Be prepared to catch the TimeoutException.
SysInternals' PortMon is a very useful utility to compare good vs bad, it shows you exactly what's going on inside the serial port driver. Beware however that it doesn't work on a 64-bit operating system.
Hans has covered the serial port cases, but another reason why your program will lock up is that your click handler uses an infinite wait loop. The way Windows applications work is that they have a main loop that gets messages (like click events) from a queue. For each message, your event handler is called, and it is only when your event handler returns control to the main loop that it can process the next message (e.g. to redraw your window and show the new text you have set on your control). So you can't use a long loop or blocking calls in your event handler if you want your program to remain responsive to user input.

WPF threading C#

I'm very new to threading. I hope someone can give me some example.
I'm trying to start a thread when user click on start button and do the following process:
private void btnStart_Click(object sender, RoutedEventArgs e)
{
if (serialPort.IsOpen)
serialPort.Close();
try
{
//To set all the parameters for Serial Comm
serialPort.PortName = "COM14";
serialPort.BaudRate = int.Parse("38400");
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.Encoding = System.Text.Encoding.ASCII;
serialPort.DataReceived += new SerialDataReceivedEventHandler(GotRawData);
serialPort.Open();
//To show that Com Port is Opened
txtboxOutput.AppendText(DateTime.Now.ToString("hh:mm:ss tt") + " - COM14 is opened." + Environment.NewLine);
txtboxOutput.ScrollToEnd();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
private void GotRawData() is a method where i do something to get some raw data from a hardware.
You might find the System.ComponentModel.BackgroundWorker class rather useful which in my understanding is the simplest way to execute an operation on a separate thread.
I do not know if I understood the question correctly.
Once the user clicks a button, you want to start a seperate thread and receive data from the serial port there.
I think this should that:
private void btnStart_Click(object sender, RoutedEventArgs e)
{
Thread GetData = new Thread(thFunctionToRun);
GetData.Start();
}
You're not making any blocking calls in btnStart_Click, so it's fine to just run this on the main UI thread.
A couple of points:
Remember that GotRawData will be called on a worker thread, so if you are accessing any UI controls, you will have to marshal those calls back onto the UI thread.
From MSDN SerialPort.Open :
The best practice for any application is to wait for some amount of time after calling the Close method before attempting to call the Open method, as the port may not be closed instantly.

datareceived Event Handler in Serial C#

I am trying to implement the datarecieved based event handler, I think I am able to receive data from the port, but having difficulties executing the event.. I have tried both ReadLine and ReadExisting.. can you please comment on my code.. Thanks,
private void Form1_Load( object sender, EventArgs e )
{
// graphing stuff
portname = "COM1";
parity = Parity.None;
BaudRate = 115200;
stopbits = StopBits.One;
databits = 8;
port = new System.IO.Ports.SerialPort(portname);
port.Parity = parity;
port.BaudRate = BaudRate;
port.StopBits = stopbits;
port.DataBits = databits;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
port.Open();
count = 0;
}
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
line = port.ReadLine();
count++;
this.BeginInvoke(new LineReceivedEvent(LineReceived),line);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private delegate void LineReceivedEvent(string text);
private void LineReceived(string text)
{
if (zedGraphControl1.GraphPane.CurveList.Count <= 0)
return;
LineItem curve = zedGraphControl1.GraphPane.CurveList[0] as LineItem;
if (curve == null)
return;
IPointListEdit list = curve.Points as IPointListEdit;
double value = double.Parse(text);
list.Add(count, value);
// graphing stuff
}
// graphing stuff
}
I have done a lot of work with Serial communications, and DataReceived never works like I want it to. There is a property on SerialPort called ReceivedBytesThreshold that is supposed to change when the event fires, but I have hit and miss luck with it. Do some googling on this event, and you'll have thousands of results reporting problems with it. The event can work sometimes, but I wouldn't rely on it for mission critical operation.
The better way I have found to do it if you're looking for line endings is to simply have a tight loop that continually reads bytes into a buffer if they are available, and then invokes the LineReceived method on the buffer when it encounters a line ending. Put this on its own thread, and it should do the trick. Add a few Thread.Sleep() inside the loop to keep it from taking over.
If you're not looking for instantaneous reactions to the serial stream, then run it on a threaded timer every second or half second. Each tick of the timer, read all the existing bytes into the buffer, and whenever you encounter a line ending invoke LineReceived.
I am also using the DataReceived event with great success in one of our products. The protocol I am implementing mandates a minimum packet size of 6 bytes, so I use that are my receive threshold.
I make sure to keep any orphaned data, and reconstruct it the next time the event occurs if I get an incomplete read or a malformed packet. I really have had very few issues with this implementation. I would suggest locking in the event handler so you don't end up with a race on the serial port, but that may not be needed.

Categories

Resources