3

I am working on code that connects to a serial port of a stepper motor. I send a command to the stepper motor via textBox2 and am attempting to read in the return data from the command to textBox3. I am able to make a connection, send the command, and receive the data. But after my GUI populates textBox3 with the returned serial data it freezes.

I believe that the code is getting stuck in the try loop but I don't know how to break out of it. Here is my code:

private void button3_Click(object sender, EventArgs e)
{
    if (isConnectedMotor)
    {
        string command = textBox2.Text;
        portMotor.Write(command + "\r\n");
        portMotor.DiscardInBuffer();

        while (true)
        {
            try
            {
                string return_data = portMotor.ReadLine();
                textBox3.AppendText(return_data);
                textBox3.AppendText(Environment.NewLine);
            }
            catch(TimeoutException)
            {
                break;
            }
        }       
    }
}

DataReceived Code:

private void connectToMotor()
{
    isConnectedMotor = true;
    string selectedPort = comboBox2.GetItemText(comboBox2.SelectedItem);
    portMotor = new SerialPort(selectedPort, 9600, Parity.None, 8, StopBits.One);
    portMotor.RtsEnable = true;
    portMotor.DtrEnable = true;
    portMotor.Open();
    portMotor.DiscardInBuffer();
    portMotor.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
    button4.Text = "Disconnect";
    enableControlsMotor();
}

private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;
    string indata = sp.ReadExisting();
    textBox3.AppendText(indata);
    textBox3.AppendText(Environment.NewLine);
}

I am getting an error saying:

An object reference is required for the non-static field, method, or property 'Form1.textBox3'

Invoke code:

private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {

        portMotor.DiscardInBuffer();
        incoming_data = portMotor.ReadExisting();
        this.Invoke(new EventHandler(displayText));
    }

    private void displayText(object o, EventArgs e)
    {
        textBox3.Text += incoming_data;
    }
Chris Catignani
  • 5,040
  • 16
  • 42
  • 49
palacetrading
  • 71
  • 1
  • 11

1 Answers1

3

Instead of looping to read data, use a DataReceived event to get at the incoming bytes asynchronously.

See the documentation and example. Also see this question for troubleshooting.

P.S. Here is a code sample to avoid locking up the UI.

private void ReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
    var incoming_data = portMotor.ReadExisting();

    // this is executing on a separate thread - so throw it on the UI thread
    Invoke(new Action(() => {
        textBox3.Text += incoming_data;
    }));
}
AngryHacker
  • 59,598
  • 102
  • 325
  • 594
  • Hey @ANGRYHACKER I am not using DataReceived to capture serial data because at the beginning of my program the stepper motor spits out extraneous information that I do not want to display in my textbox. I was purposely doing it this way to capture only the data that is returned from the command that I sent.This is why I have the portMotor.DiscardInBuffer(); line to discard all extraneous data. – palacetrading Dec 02 '19 at 19:48
  • If there is still a way to use DataReceived to do this that would be cool. Otherwise I am not sure if I should be looking for a serial data end line character or a timeout exception or IO exception etc. – palacetrading Dec 02 '19 at 19:53
  • 1
    @colinodowd Fine, then setup the DataReceived event after you've discarded extraneous information. The way your code works now, it will always lock up your user interface because you are looping non-stop. You are polling the serial port, which is not a good practice because you are wasting CPU resources. It's better to rely on the event. – AngryHacker Dec 02 '19 at 19:53
  • ...and another tip since you are new here. If you place @myname at the beginning of the comment, it appears in my inbox immediately. – AngryHacker Dec 02 '19 at 19:54
  • I appreciate your help. I actually did try that earlier and I was getting an error saying: An object reference is required for the non-static field, method, or property 'Form1.textBox3' I added my code up top – palacetrading Dec 02 '19 at 20:02
  • @colinodowd `private static void DataReceivedHandler` - remove `static`. – AngryHacker Dec 02 '19 at 20:10
  • I did try that earlier and I was getting this error when I ran the program. This was the point I came to stack overflow :) System.InvalidOperationException: 'Cross-thread operation not valid: Control 'textBox3' accessed from a thread other than the thread it was created on.' – palacetrading Dec 02 '19 at 20:21
  • @colinodowd Too long for a comment, but you are close. Now just read https://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the to fix that issue and you should be good. – Robert McKee Dec 02 '19 at 20:34
  • So I am back to being able to read in the serial data without any errors but it is freezing again which is back to my original problem! I added my DataReceived code with the invoke – palacetrading Dec 02 '19 at 21:02
  • @AngryHacker i did, its up top - under invoke code: – palacetrading Dec 02 '19 at 22:52
  • @colinodowd I updated my answer to include cross threading code. One more thing... I see that you are discarding buffer in the Received event. Don't do that - it blows away all your incoming data. Finally, your UI could still be somewhat locked if the data is coming in huge chunks non-stop and all your app is doing is updating TextBox3 with more data. If that's the case, consider listening on a timer instead. – AngryHacker Dec 02 '19 at 23:57
  • @AngryHacker i copied and pasted your code directly into mine and it still causing it to freeze. was i supposed to add anything else? when i call the same command in putty or tera term it returns perfectly and there is no error so i don't believe it is on the motor side – palacetrading Dec 03 '19 at 03:32
  • @colinodowd zip up your entire project and post it somewhere. I'll take a look. – AngryHacker Dec 03 '19 at 07:08
  • @AngryHacker here's the link: https://app.box.com/s/ndbdi8x0cnbk52f87579in2903pwtnit, thank you for all of your help – palacetrading Dec 03 '19 at 14:51
  • @colinodowd I looked at your code. I hope you are not pressing Button3 because that still has an infinite loop. That's the only thing that I see as the problem. Your speed (9600 bps) is not fast enough to cause it to lock up. – AngryHacker Dec 03 '19 at 17:54
  • @angryhacker I uncommented that code to make sure it was the code and not the motor and then i forgot to recomment it, apologies and thank you for all the help – palacetrading Dec 03 '19 at 18:56
  • @colinodowd Glad, I could help. Don't forget to accept the answer. – AngryHacker Dec 03 '19 at 19:32
  • @AngryHacker Done. Just out of curiosity, is there an Exception that would work for breaking out of a while loop used to capture serial data? What did people do before the DataReceived handler was created? – palacetrading Dec 04 '19 at 15:24
  • @colinodowd No. Using exceptions to break out of a loop is a bad idea, as is using a while loop to capture data. As far as what people used before DataReceived event handler? DataReceived event has been there on the day the PC was designed by IBM like 40 years ago. Back then it was called an interrupt. You can [read up](https://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming#x86_Processor_Interrupts) on it if interested. It works largely the same way today. – AngryHacker Dec 04 '19 at 18:14