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...
Related
I'm trying to write a program that reads the serial port using c# windows form application .I've just started to learn about threads and tasks and etc. so I'm just a little bit confused about it.So even after reading some documents, I still don't know why does the following code cause the UI to hang?So,what is wrong about this code?Do you have any suggestion?Thanks.
There is a richtextbox named txtRead which is supposed to show the data that are taken from the serial port after a button named btnReadBegin get clicked.The richtextbox get updated frequently as the serial port sends the datas,but the rest of the application hangs.
SerialPort srlPort = new
SerialPort("COM6",9600,Parity.None,8,StopBits.One);
delegate void mydel();
bool checkWhile = true;
private void BtnReadBegin_Click(object sender, EventArgs e)
{
srlPort.Open();
new Thread(() =>
{
txtRead.BeginInvoke(new mydel(ReadingSerialPortMethod));
}).Start();
}
void ReadingSerialPortMethod()
{
while (checkWhile)
{
txtRead.Text +=
Convert.ToString(Convert.ToChar(srlPort.ReadChar()));
}
}
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 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'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.
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
}
}