I am sending and receiving data from a device using
c# (serial communication). For receiving my setup is like this.
I am receiving on a separate thread i.e. I have something like this
comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
.....
}
To write I push a button which write data to the device.
Now I was thinking to write data automatically when ever I receive some data from device instead of me pushing
the button when ever I receive data. So my q is that can I put
comPort.WriteLine(textBox1.Text + "\r\n"); in the body of
private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
function. Becase when I put write here it stops working. Otherwise write works with separate button push as I mentioned above.
Can somebody tell me whats the correct way. Is there any issue of threads or someting
Thanks !
Any operation with UI controls (including reading and writing properties like Text) should be done in application's main thread. If you are in another thread, you should use the Invoke method to do the operation:
var text = (string)textBox1.Invoke(new Func<string>(() =>
{
return textBox1.Text;
}));
comPort.WriteLine(text + "\r\n");
Read this answer for more information.
Related
The problem is complicated, and my code is large, but let me try to explain it.
See the code below:
private void button13_Click(object sender, EventArgs e)
{
serialPort1.Write("0800"); // Write to serial port for reading
Thread.Sleep(1);
serialPort1.Write("0800"); // Write to serial port for reading
in_data = serialPort1.ReadLine();
textBox7.Text = in_data;
Console.WriteLine(in_data);
in_data = "";
serialPort1.DiscardInBuffer();
serialPort1.DiscardOutBuffer();
}
Let me explain it:
I implemented a button in C# form. When i press the button, it sends 0800 towards an arduino through serialPort1. It sends it two times. After each command sent, Arduino writes to serial port through Serial.println(receivedData, HEX). This data from Arduino is read by C#(the function above through in_data = serialPort1.ReadLine() line and is displayed in textBox7. In last of the function, I am clearing both buffers.
The problem however is, whenever I am pressing this button, some new data is being display in textbox.
And I don't understand the reason, because the circuit connected to arduino should always respond with same code. I have tested the circuit with other fixtures, and otherwise that circuit is working fine.
Writing into serialport is working just fine. For example this function:
private void button6_Click(object sender, EventArgs e)
{
serialPort1.Write("EXTADC"); // Write to serial port
Console.WriteLine("EXTADC");
}
Please note: sending two commands each time is necessary in order to get the desired output from the circuit. Also, I am pretty new to Arduino and C# (you might be having an idea from my post).
I need to read data from COM port. The data comes from Arduino, it needs to be parsed by the C# program and do certain things on the host PC. I can read the data, but only with endless "while true" loop. which is blocking the form and other tasks to be executed. So currently it looks like that:
while (true) {
oneLine = myPort.ReadLine();
this.Invoke(new EventHandler(display_data_Event));
// TBD: add analysis of data from port
// TBD: execute according to data from Arduino
}
the handler:
private void display_data_Event(object sender, EventArgs e)
{
string curr_time = DateTime.Now.ToString("h:mm:ss tt");
port_in_TextBox.AppendText(curr_time + " " + oneLine + "\n");
}
So I can display the data to a textbox, but not use it with my main program.
How can I use the "one line" string only when the event occurs, instead of the "while true"?
I tried calling functions from the handler - I guess it failed because it is another thread. So perhaps the problem is how to share the string from one thread to another.
I am lost, here is the core program:
https://learn.microsoft.com/en-us/dotnet/api/system.io.ports.serialport.datareceived?view=netframework-4.8
Now, the question is do you need one input or many. If the former, add AutoResetEvent so the main program will be blocked, and reading the data will signal it to unblock. Or if there are many lines add your "main" action in the event just after you read each line.
I am in the process of writing an application, that involves serial communication with a device among other things. (in C#). I have seen some sample code, in concrete two examples.
In the first example, the code is based on a Background control which has a while loop that checks if there is data read from the serial port (another control) and when it does do some processing
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{ serialPort1.Open();
while (backgroundWorker1.CancellationPending == false)
{
if (serialPort1.BytesToRead >= 240)
{
serialPort1.Read(RDATA, 0, 240);
//Some other process
}
}
serialPort1.Close();
}
The second example is quite different. This involves delegates and events. In this case the serial port (created in code) has an event "DataReceived". To this we add an event handler
ComPort.DataReceived +=
new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived_1);
and then the port_DataReceived_1 function is defined, in which the input data is read
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
InputData = ComPort.ReadExisting();
if (InputData != String.Empty)
{
this.BeginInvoke(new SetTextCallback(SetText), new object[] { InputData });
}
}
private void SetText(string text)
{
this.rtbIncoming.Text += text;
}
Anyway, I can see two different styles of coding serial communication here. On one hand we have a constant polling (through while) that would block the rest of the program if it weren't on another thread. It is done in a different thread though.
On the other hand we have interruptions in which the processing is done only when an event happens and not the rest of the time. It is all done on the main thread though.
My question is which of these ways would be preferable. I am imaging that the first method, even if it is on a different thread takes a lot of resources of the computer, maybe even taking the load of the CPU to 100% or something.
I am more inclined to do the second one but then again it is all in one thread.
Any recommendations, advice on this?
(My application will involve not only serial communication but maybe processing on the data received, perhaps even machine learning)
You should definitely go with the second option, which is likely handled by an I/O Completion Port thread. If you're writing a WinForms application then you'll need to make sure to check the InvokeRequired flag and perform any UI manipulation on the main thread by passing a delegate to the Invoke method.
I have a C# program that reads from two serial ports at the same time. The serial port device is a Prolific USD to 4 serial ports adapter and I plug the two hardware on separate ports of the adapter. The problem is when I read from each port one at a time, everything works fine but when I try to read from both ports at the same time, one of the port is not responding. To troubleshoot the problem, I started two instances of the application and was able to read from the two ports at a time (one from each instance of the application). Does anyone know how to read from two separate serial ports in one application at the same time? Thank you.
Adding some codes:
Port 1:
// button to start or stop reading from port 1. Because the hardware requires me to write to it before reading the response, the writing is done in the timer
private void buttonPort1_Click(object sender, EventArgs e)
{
if (buttonPort1.Text == "Start Recording")
{
if (!port1.IsOpen)
{
port1.Open();
}
timerPort1.Start();
buttonPort1.Text = "Stop Recording";
}
else
{
timerPort1.Stop();
buttonPort1.Text = "Start Recording";
}
}
// Write "D" to the hardware each time to receive back the response
private void timerPort1_Tick(object sender, EventArgs e)
{
port1.Write("D");
}
void port1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
string result = port1.ReadLine();
oneParamDelegate dg = PHandCondResult; // send back the result to the main thread
this.Invoke(dg, result);
}
catch
{
}
}
Port 2
The code for the second port is similar to the above really, the difference being different port, datareceived event and timer.
I'll try the multiple thread options suggested by Grant Thomas: I didn't try this before because I thought serial ports are already working on separate threads: the datareceived event doesn't block the main thread and you can't access controls created on the main thread but I'll still give it a go using background worker and revert back later. Thank you all for the quick response.
You're going to need to do some reading, specifically on Threading.
If you have, say, some code that looks like this:
ReadDataFromSomePort();
ReadDataFromSomeOtherPort();
Then the first will execute synchronously (blocking) and then the latter. This happens on the same thread, the main application thread. When you want to do asynchronous things, including just doing one thing while keeping a UI interactive/responsive, then you need to delegate work to other threads.
So, you end up with something like this:
var thread1 = new Thread(ReadDataFromSomePort);
var thread2 = new Thread(ReadDataFromSomeOtherPort);
thread1.Start();
thread2.Start();
There's more to it than this, rest assured, so I recommend some research on the concept before proceeding.
MSDN has a tutorial/programming reference for threading that should get you started.
Creating two different objects of SerialPort and different DataReceived events for both should work.
Part of my program uses an event handler for the receive data of my serial port. The idea is when data is received that the text received is then added to the textbox (rx). I did not used to have this problem but something has changed and I can't figure out what. So now I am re-examining the way this is handled.
During the form load of my winform the last thing I do is
if (!serialPort1.IsOpen)
{
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
Then I have the event handler
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string indata1 = serialPort1.ReadExisting();
// rx.Text = " "; accidentally posted this. it was from trial and error.
rx.AppendText(Environment.NewLine + indata1);
}
When I run the program it stops at the rx.AppendText(Environment.NewLine + indata1); and gives the error
invalidoperationexception was unhandled: Control "accessed from a
thread other than the thread it was created on.
From what I have been able to read suggests that I need to use invoke or BeginInvoke.
I have never had problems appending the text before so now I can't understand why it's a problem. Also from what I have been reading on invoking i just don't understand it.
Can someone help me understand how to use the invoke instance for my situation? or perhaps show me another way of appending the text box?
Usually the exception you're seeing occurs when you run in debug mode, and if you run your application in release mode, you're unlikely to see the exception.
However, it is best to use invoke, as you have read. Something like this:
private delegate void RefreshTextBox();
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
//this event is raised in an event separate from UI thread,
//so InvokeRequired must be checked and Invoke called to update UI controls.
if (this.InvokeRequired) {
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
} else {
RefreshTextBoxResults();
}
}
private void RefreshTextBoxResults() {
string indata1 = serialPort1.ReadExisting();
rx.Text = " ";
rx.AppendText(Environment.NewLine + indata1);
}
The first time you see this invoke stuff, it's nearly impossible to follow, but take a close look and give it some time and it will make sense. Promise. :)
Updates in GUI applications should only be done on the GUI thread. Another thread attempting to update GUI components directly will result in either the error you described or in seemingly random behavior.
The role of Invoke & friends is to enable a secondary thread to safely forward GUI updates to the GUI thread, which will then process them from a queue.
In your case (assuming WinForms here):
rx.BeginInvoke(
(Action)(() =>
{
rx.AppendText(Environment.NewLine + indata1);
}));
BeginInvoke is asynchronous, so the thread calling it will not wait for the actual updates to be processed before moving on, while Invoke is synchronous.