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(" ");
Related
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...
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 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."
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.