I have a Visual Studio 2008 C# .net 2.0 CF application that reads from a serial port using the System.IO.Ports.SerialPort class. Unfortunately, the SerialDataReceivedEventHandler is never called.
I open the port like this:
private SerialPort serial_port_;
protected void OpenSerialPort(string port, int baud)
{
if (serial_port_ == null)
{
serial_port_ = new SerialPort(port,
baud,
Parity.None,
8,
StopBits.One);
}
else
{
serial_port_.BaudRate = baud;
serial_port_.PortName = port;
}
if (!serial_port_.IsOpen)
{
serial_port_.Open();
serial_port_.DataReceived += new SerialDataReceivedEventHandler(OnSerialDataReceived);
}
}
private void OnSerialDataReceived(object sender, SerialDataReceivedEventArgs args)
{
Debug.WriteLine("Serial data received");
}
If, however, I add a Debug.WriteLine(serial_port_.ReadLine()); right after the port is opened, I see in the output window a line of text from the port just as I would expect.
So, why does ReadLine work, but the DataReceived event does not?
Thanks,
PaulH
Edit: Further testing shows this code works on some devices, but not others. What does the DataReceived event require to work properly?
Further Frustration: On this device, ReadExisting always returns null and BytesToRead always returns 0.
ReadLine() and Read() both work perfectly, though.
Don't put blocking reads in the data received handler. If the handler fires and there is NOT an entire line to be read it will block. In the code you posted there is not a read of any kind.
From MSDN: "PinChanged , DataReceived, and ErrorReceived events may be called out of order, and there may be a slight delay between when the underlying stream reports the error and when the event handler is executed. Only one event handler can execute at a time."
Related
I am trying to do a serial communication between my micro processor and c# form application.
The problem is, data which comes from micro processor can come in 2 seconds or 5 seconds or 10 seconds. I mean there is no specific time and i would like to listen port and get data if it is come in 2 seconds if it is not, wait for the data until it comes.
I tried to this with serialport.readline(); but form hangs on while readline blocking, so i tried to do with backgroundworkers when i do this, i cant close form while backgroundworker is busy because readline command blocks the whole program.
All i am saying is, please give me some clue about listening the port while coming data time is not specific.
Thank you for your time (sorry for english it is not well)
You can use the SerialPort.DataReceived Event to get the data async. After you created an instance of the SerialPort class, you are able to add event handlers to the SerialPort. Thes event handlers are called if data was received.
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
Inside the handler you can read the data from the input buffer and do what ever you want with it.
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
//Do what ever you want with the data
}
This is a very common solution to get data in unregular time steps which runs your application without blocking it.
You can use the DataReceived event. It will be fired everytime new data arrives at your port. You need to register to it like this:
SerialPort port = new SerialPort(/*your specification*/);
port.DataReceived += Port_DataReceived;
In the event handler you would then read out the incoming data
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = sender as SerialPort;
if (port != null)
{
var incoming_message = port.ReadExisting();
}
}
Now you just need to open the port and it will listen automatically. NOTE! the incoming data will arrive on a different thread than the main thread. So if you want to use controls of your form for display you need to use BeginInvoke
If your data is marked at the end with \n you could try using the ReadLine method:
var incoming_message = port.ReadLine();
Or you could try ReadTo
var incoming_message = port.ReadTo("\n");
EDIT:
If it is such a long time, than you should read it in batches. You could also try to handle it in a while loop.
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = sender as SerialPort;
string message = "";
if (port != null)
{
while(port.BytesToRead > 0)
{
message += port.ReadExisting();
System.Threading.Thread.Sleep(500); // give the device time to send data
}
}
}
EDIT 2:
If you want to store the data declare a List<string> outside of the event handler and add the string when it is entirely read.
List<string> dataStorage = new List<string>();
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort port = sender as SerialPort;
string message = "";
if (port != null)
{
while(port.BytesToRead > 0)
{
message += port.ReadExisting();
System.Threading.Thread.Sleep(500); // give the device time to send data
}
// add now the entire read string to the list
dataStorage(message);
}
}
Since the event handler does not know whether you have send A or B just collect the entire received messages in one list. You know the order in which you have send your commands, so later you can take out the corresponding message and use Split to get the 400 entries in an array:
string [] A_array_data = dataStorage[0].Split(" ");
I am quite new with C# programming and know very little about serial ports. I am following this method provided by Microsoft to continuously read information entering multiple serial ports. Overall, my application is to bring data in from multiple COM ports and perform computational tasks on the resulting data.
1) I want to use multiple serial ports. Unfortunately, I do not currently have enough USB-RS232 adapters to test multiple ports. I am not sure if creating a second DataReceivedHandler method is the correct way to do this. This is what I currently have:
// Receive data on COM Port A
private static void DataReceivedHandlerA(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string inDataA = sp.ReadExisting();
}
// Receive data on COM Port B
private static void DataReceivedHandlerB(object sender, SerialDataReceivedEventArgs e)
{
SerialPort spB = (SerialPort)sender;
string inDataB = spB.ReadExisting();
}
2) Using the received data from within the DataReceivedHandler method elsewhere in the Main() loop. Since the method is private, I am not able to use the inDataA within the Main() loop. Whenever I make this method public, it seems to fail. I want to be able to return the string inDataB. Is this possible, or is there another better way to do this.
Any reason you can't have those two variables as global ones outside of main so they are available to all functions?
Static String inDataA, inDataB;
// Receive data on COM Port A
private static void DataReceivedHandlerA(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
inDataA = sp.ReadExisting();
Console.Write(inDataA);
}
// Receive data on COM Port B
private static void DataReceivedHandlerB(object sender, SerialDataReceivedEventArgs e)
{
SerialPort spB = (SerialPort)sender;
inDataB = spB.ReadExisting();
Console.Write(inDataB);
}
//use those variables here as well in some other functions
When you call Console.Write(inDataxx) from main, the inDataxx is null or empty because the event handler has most likely not yet been triggered. Since you have not created a notification event or polling loop, this print command is executed once and only once.
In your main loop of the sample you provided there are three sections. The COM port setup, the console setup, and the COM tear down. This is all executed sequentially and there is not logic to continue to printing your public (now static variables). This example was designed to print directly from the event handlers. To get your design working, you will need to modify the main loop to poll or use events to print the data. Try some polling like this:
int keyIn = 0;
do
{
// Check if any key pressed, read it into while-controlling variable
if (Console.KeyAvailable)
keyIn = Console.Read();
// Poll our channel A data
if (!string.IsNullOrEmpty(inDataA))
{
Console.WriteLine(String.Format("Received data {0} on channel A", inDataA));
inDataA = "";
}
// Poll our channel B data
if (!string.IsNullOrEmpty(inDataB))
{
Console.WriteLine(String.Format("Received data {0} on channel B", inDataB));
inDataB = "";
}
// Stop looping when keyIn is no longer 0
}while (keyIn == 0);
Please note, that if you plan to use this is production code do not use polling. Polling is very inefficient.
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.
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
}
}
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.