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.
Related
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'm having a strange problem. I'm using SocketIO4Net Client for my program written in C#. The program communicates with server written in NodeJS & SocketIO. I'm able to send & receive data between my program & server using 'socket.emit()' & 'socket.On()' methods in SocketIO4NET Client. However, when i try to update a textbox in the GUI with the data i received from the server, nothing happens. But, if i use 'MessageBox.Show()' method, i can display the data i received. I'm using the following code for receiving data:
public Form1()
{
InitializeComponent();
socket = new Client("http://127.0.0.1:80/");
socket.On("message", (data) =>
{
String msg = data.Json.Args[0].ToString();
MessageBox.Show(msg,"Received Data");
rxtxtbox.Text = msg;
});
socket.Connect();
}
For sending data:
private void sendbtn_Click(object sender, EventArgs e)
{
String msg = msgtxtbox.Text.ToString();
socket.Emit("private message", msg);
}
The above code works fine. But its not updating the TextBox 'rxtxtbox'. If I move the line "rxtxtbox.Text = msg;" above "MessageBox.Show();", then nothing will happen on receiving the "message" event. I tried setting breakpoints & watching the value of the variable "msg" & its fine. I tried declaring another function just to update the textbox & passing "msg" to it & still getting no results! I think this has something to do with waiting for "message" event? I tried "Application.DoEvents()" and "rxtxtbox.Refresh()" methods & still no luck! I'm new to C#. Please help!
You mention that if you place the MessageBox.Show in front of the rxtxtbox.Text - you see the payload data, but having the rxtxtbox.Text first - nothing at all happens.
The event handers from socketIO4Net definitely run on a background thread, so I'd bet you are throwing an exception updating the UI control - from this non-UI thread. My understanding of MessageBox is that it is not tied to the UI, so it could be called from non-ui threads w/o issue.
Try this in place of your rxtxtbox.Text = msg line:
rxtxtbox.Invoke(new Action(() => rxtxtbox.Text = msg)));
This uses something known as a lambda express to create an anonymous delegate that will be executed on the thread that owns the control (textbox in this case) underlying handle.
You could also place this line right before you update the Text property, and inspect it (if true, you are indeed on a non-ui thread, and have found your problem):
bool state = rxtxtbox.InvokeRequired;
There are various ways to deal with updates to the GUI from non-ui threads. Search multithreading winforms gui C# etc here on SO. Any messages raised from socketio4net will need to be handled appropriately, or you will throw UI thread exceptions.
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.
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.
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.