I'm trying to open and close a serial port with one button click event. But it always hangs whenever it hits the serialport.close part. Why?
private void btn_auto_Click(object sender, EventArgs e)
{
try
{
myport = new SerialPort();
myport.BaudRate = 9600;
myport.PortName = cb_portname.Text;
myport.Open();
myport.DataReceived += new SerialDataReceivedEventHandler(myport_DataReceived2);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error");
}
}
}
void myport_DataReceived2(object sender, SerialDataReceivedEventArgs e)
{
try
{
in_data = myport.ReadLine();
this.Invoke(new EventHandler(displaydata_event2));
}
catch (Exception)
{
}
}
private void displaydata_event2(object sender, EventArgs e)
{
string inStr;
inStr = in_data;
if (inStr.Length == 18)
{
int indexOfSpace = inStr.IndexOf(':');
string Temp = inStr.Substring(indexOfSpace + 1);
txtData2.Text = Temp;
}
if (txtData2.Text != "")
{
myport.Close(); //===== ALWAYS HANGS AT THIS PART =====
MessageBox.Show("STOPPED");
}
}
So, it always hangs under the if txtData2 not equals part.
Is it due to it requires a button action for a serialport to close and it cannot auto close? Thanks in advance.
Looking at the source code for the SerialPort class, and in particular for its associated SerialStream class, it appears that the Close() method will block waiting for handlers of any raised events to complete.
Your handling of the received data seems a bit suspect in any case, in that you only even bother to look at the received data if the received line is exactly 18 characters long, as well as in that you are using an instance field to pass data between two methods (very bad idea).
But most likely the biggest issue here, the one causing the deadlock, is that you are calling the SerialPort.Close() method before the DataReceived event handler has completed. Don't do that. Fix your code so that handling received data is a completely independent operation from actually closing the serial port, so that the former can complete before you attempt the latter.
Related
Here is my situation, when I check the checkbox, my application freezes but still works. Which means that it is still able to recognize data sent through the serial port; for testing purposes it just exits the application.
If I comment out line 45 ("pipe = arduino.ReadLine();" see screenshot below) meaning that it no longer has to "ReadLine()", I am able to un-check the box. However now when I try to re-check the box, I get an error message saying "Access to the port 'COM5' is denied"
I assume that the code cannot continue because it is trying to "ReadLine()" when nothing has been sent through yet. However I do not have an explanation for being denied access to the COM port; rather than me trying to open the port when its already opened.
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
SerialPort arduino = new SerialPort();
arduino.BaudRate = 9600;
arduino.PortName = comboBox1.Text;
string pipe;
if (checkBox1.Checked)
{
checkBox1.Text = "Listening...";
arduino.Open();
pipe = arduino.ReadLine();
if (pipe == "S\r")
{
//System.Diagnostics.Process.Start("shutdown", "/f /r /t 0");
System.Windows.Forms.Application.Exit();
}
else
{
checkBox1.Text = "Start";
}
}
}
The SerialPort class manages system resources and, when such sensitive objects are involved, the class usually implements the IDisposable interface to allow those system resource to be released immediately to the system.
Your code forgets to close the SerialPort, so, the next time your user action cause a call to this event handler the port is in use by your own first action.
Fortunately, there is an easy way to ensure proper closing and disposing of such objects and it is the using statement
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
checkBox1.Text = "Listening...";
using(SerialPort arduino = new SerialPort())
{
arduino.BaudRate = 9600;
arduino.PortName = comboBox1.Text;
string pipe;
arduino.Open();
pipe = arduino.ReadLine();
if (pipe == "S\r")
{
//System.Diagnostics.Process.Start("shutdown", "/f /r /t 0");
//System.Windows.Forms.Application.Exit();
}
} // Here the port will be closed and disposed.
}
else
{
checkBox1.Text = "Start";
}
}
Hello friends have a form in C # that reads data from a serial device connected, my problem is that I even changing form of the method myPort.DataReceived still running and receiving data. There's no way I close the connection with the serial port because the method does not stop excutar. I've tried a command to zip it when I change my form but it crashes when you try to run the myPort.Close, I believe that is why the myPort.DataReceived still running, so I removed the code and it continues myPort.Close open in another form. I think my solution would be to stop the myPort.DataReceived to then close connection, but can not find way to do this.Below is an excerpt from my code:
namespace EntradaFinalCliente
{
public partial class ConsultaSerial : Form
{
string SerialString;
private SerialPort myport;
public ConsultaSerial()
{
InitializeComponent();
abrirSerial();
lerDados();
}
public void abrirSerial()
{
myport = new SerialPort();
myport.BaudRate = 9600;
myport.PortName = SerialPort1;
myport.DataReceived += myport_DataReceived;
}
private void lerDados()
{
if (myport.IsOpen == false)
{
try
{
myport.Open();
}
catch
{
return;
}
}
}
private void myport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
SerialString = myport.ReadExisting();
this.Invoke(new EventHandler(Analisa));
}
private void Analisa(object sender, EventArgs e)
{
checarSerial();
}
And this is my closing the form button:
private void button1_Click (object sender, EventArgs e)
{
myPort.Close ();
this.Hide ();
var form1 = new Form1 ();
form1.Closed + = (s, args) => this.Close ();
Form1.Show ();
}
The issue you have it that once the event has been triggered, your application would have entered the function myport_DataReceived. The function will continue to execute regardless of whether the port has been closed. If the port has been closed, the function would execute for the last time. Waiting for 100ms makes it worse. So my advice is to remove the wait and put a try catch statement around the code to make the thread terminate cleanly.
Furthermore, it is better if you use the sender to read the incoming data than using the member myPort because the sender is the one that fires the event. It also helps to remove confusion when you open two or more ports.
It is also advised that the body of DataReceived event handler function should be kept to minimum. Only do what you need to get the data out. You can then store the data in memory and do more complicated handling somewhere else using the stored data.
private void myport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100); // Why do you need to wait for 100 ms? If the event reaches here, it will have the data to read. Can remove?
try
{
SerialPort sp = (SerialPort)sender;
SerialString = sp.ReadExisting();
this.Invoke(new EventHandler(Analisa));
}
catch (Exception ex)
{
// Do something else
Console.WriteLine(ex.Message);
}
}
I have methos that recieve data from opening COM port:
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
try
{
if (sp.IsOpen)
{
if (sp.BytesToRead > 0)
{
// Get data
}
}
}
}
Also I have method that does connection to COM port:
private void connectPort()
{
SerialPort mySerialPort = new SerialPort(port);
...
}
When I call method that closes port:
mySerialPort.DiscardInBuffer();
mySerialPort.DiscardOutBuffer();
mySerialPort.Close();
After I get data from device still. What is wrong?
I don't know for sure, but from the docs it sounds like the fact that the data is being raised from another thread may be buffering and/or lagging a bit behind the actual data (plus it's possible for you to receive data between when you've discarded the buffer and when you close it).
I'd probably unhooking the DataReceivedHandler first, then close the connection, finally discard the data, ex.
mySerialPort.DataReceived -= new SerialDataReceivedEventHandler(DataReceivedHandler);
I am using C# serial port controlling gsm modem. Now in Mikroelectronia USART Terminal
after sending:
AT+CUSD=1,"*778#",15
It receives:
AT+CUSD=1,"*778#",15
OK
+CUSD: 0,"Balance: 0.00 TK. Validity: 29-Jul-13. Bonus: 0.00TK. Free Min: 0. Dial *121*2092# for 3 hit songs of Ashiqui-2 as ur Caller
Tunetk.10",64
But in C# after sending data
AT+CUSD=1,"*778#",15
it returns:
AT+CUSD=1,"*778#",15
OK
+CUSD: 0,"Balance: 0.00 TK. Validity: 29-Jul-13. Bonus: 0.00TK. Free Min: 0. Dial *121*2092# for 3 hit songs of Ashiqui-2 as ur Caller Tune
That means in C# it does not receive any data after "Caller Tune". Why is that happening? Part of C# code I have used is
private void Form1_Load(object sender, EventArgs e)
{
sp1.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
}
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var valueOfPort = sp1.ReadExisting();
textBox1.AppendText(valueOfPort);
}
private void button1_Click(object sender, EventArgs e)
{
TextBox.CheckForIllegalCrossThreadCalls = false;
try
{
if (!sp1.IsOpen)
{
sp1.Open();
}
sp1.Write(textBox2.Text+"\r");
}
catch(Exception ex)
{
MessageBox.Show(string.Format("Exception : {0}", ex.Message), "Port Error");
}
}
the TextBox.CheckForIllegalCrossThreadCalls = false; makes me very suspicious, I think that is what is breaking your program, simply just invoke to update the text box properly and I think it will work. Just change the event code to the following:
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new SerialDataReceivedEventHandler(sp_DataReceived), sender, e);
}
else
{
var valueOfPort = sp1.ReadExisting();
textBox1.AppendText(valueOfPort);
}
}
What this will do is check if you are running on the correct thread, if not it restarts the function again on the UI thread with the same arguments. Also be sure to remove the TextBox.CheckForIllegalCrossThreadCalls = false;
UPDATE: After re-reading the MSDN I found this remark
This method returns the contents of the stream and internal buffer of the SerialPort object as a string. This method does not use a time-out. Note that this method can leave trailing lead bytes in the internal buffer, which makes the BytesToRead value greater than zero.
Try checking to see if there is more data to read after calling ReadExisting.
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new SerialDataReceivedEventHandler(sp_DataReceived), sender, e);
}
else
{
while(sp1.BytesToRead > 0)
{
var valueOfPort = sp1.ReadExisting();
textBox1.AppendText(valueOfPort);
}
}
}
Have you defined your device's EOL and set it correctly in your program?
C# serial port does not necessarily fire up DataReceive at the start of the message nor does it know where the message ends. It may terminate anywhere in the message. You should try explicitly set the EOL which can be uniquely identified by the port in order for it to buffer incoming message and to return it once it is completed.
// e.g. I normally used (carriage return + newline) instead of just either one
sp1.NewLine = "\r\n";
Why app is still working, when i close it.
I guess it is caused by reading of data from serial port.
Serial Port number is choosed from ComboBox.
Function WriteData update checkboxes depending on data from serial port.
Here's extract:
// Choosing of communication port from ComboBox
private void comboBoxCommunication_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (serialPort.IsOpen)
{
serialPort.DataReceived -= new System.IO.Ports.SerialDataReceivedEventHandler(Recieve);
serialPort.Close();
}
try
{
ComboBoxItem cbi = (ComboBoxItem)comboBoxKomunikacia.SelectedItem;
portCommunication = cbi.Content.ToString();
serialPort.PortName = portCommunication;
serialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(Recieve);
serialPort.BaudRate = 2400;
serialPort.Open();
serialPort.DiscardInBuffer();
}
catch (IOException ex)
{
MessageBox.Show(ex.ToString(), "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
// Close the window
private void Window_Closed(object sender, EventArgs e)
{
if (serialPort.IsOpen)
{
serialPort.DataReceived -= new System.IO.Ports.SerialDataReceivedEventHandler(Recieve);
serialPort.Close();
}
}
// Data reading
private delegate void UpdateUiTextDelegate(char text);
private void Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (serialPort.IsOpen)
{
try
{
serialPort.DiscardInBuffer();
char c = (char)serialPort.ReadChar();
Dispatcher.Invoke(DispatcherPriority.Send,
new UpdateUiTextDelegate(WriteData), c);
}
catch(IOException ex)
{
MessageBox.Show(ex.ToString(), "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
// Update of checkboxes
private void WriteData(char c) { ... }
Your code is very likely to cause deadlock, blocking your program on the Close() call. The problem statement is the Dispatcher.Invoke() call. That call cannot complete until the UI thread has dispatched the call. The deadlock occurs when you call Close() and at the same time the DataReceived event is busy executing. The Close() call cannot complete because the event is running. The event handler cannot complete because Invoke() cannot complete because the UI thread is not idle, it is stuck in the Close() call. Deadlock city.
This is especially likely to happen in your code because it has a bug. You call DiscardInBuffer() in DataReceived. That throws away the received data so the next ReadChar() call is going to block for a while, waiting for some more data to get received, possibly forever if the device isn't sending anything anymore.
Fix this problem by deleting the DiscardInBuffer() call and by using Dispatcher.BeginInvoke() instead.