0

I read/write data to serial port and I want to see reading on listbox right away. I created a new thread to send command to serial port. I keep the main thread empty, so, it can update the UI and also serial port event handler wont be interrupted with something else.(I am not sure is it right approach?)

The following code works with while (!dataRecieved) { Thread.Sleep(4000); } but does not works with while (!dataRecieved) { Thread.Sleep(100); }. The problem is if I use 100ms sleep, serial port event handler fire only once and then program stops!(If I debug with breakpoint 100ms works because I create additional time when stepping into the code.) If I wait 4000ms the program works. Also, I check the time between sending data and receiving data from serial port is 200ms. So, 100ms is reasonable.

Here is the code:

public bool dataRecieved = false;

public Form1()
        {
            InitializeComponent();
        }

        public void AppendTextBox(string value)
        {
            this.Invoke((MethodInvoker)delegate { richTextBox1.Text += value + "\n";});
        }

private void button1_Click(object sender, EventArgs e)
        {
            serialPort1.Open();
            Thread testThread = new Thread(() => sendThread());
            testThread.Start();
        }

public void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            data = serialPort1.ReadLine();
            dataRecieved = true;
        }

public void sendThread()
        {
            for(int i = 0; i<10; i++) 
            {
              serialPort1.WriteLine("AT" + i);
              // Following line creates odd situation:
              // if Thread.Sleep(100), I receive only first data, then program stops(serial port handler doesnt fire!).
              // if Thread.Sleep(4000), I receive all data, successfuly works.
              // But I do not want to wait 4000ms, because I receive answer from device in 200ms.
              while (!dataRecieved) { Thread.Sleep(100); }
              AppendTextBox("Received" + "AT" + i);
              dataRecieved = false;
            }
        } 

Where I am wrong? Can you please provide a solution?

Akif
  • 41
  • 4
  • You can't touch UI components in a thread. Presumably "AppendTextBox" is touching UI components. – J... Apr 13 '21 at 16:44
  • Do not use sleep. You have to wait for the receive message before sending Use a waitone to synchronize the reads and writes: https://learn.microsoft.com/en-us/dotnet/api/system.threading.waithandle.waitone?force_isolation=true&view=net-5.0 The timer is not needed in the sample code and is only used to demonstrate the method. – jdweng Apr 13 '21 at 16:45
  • Suggest you also read the docs for the SerialPort class. It already runs on its own thread and the DataReceived event fires on that thread, so using that handler to feed data to another thread is simply the wrong way to do it. If you're going to write a thread to manage the port directly then have the thread do that. Don't use the event. Just have the thread block until data comes in - that's the nice thing about threads - you don't have to worry about them blocking if there's nothing for them to do. – J... Apr 13 '21 at 16:46
  • The following may be helpful: https://stackoverflow.com/questions/65957066/serial-to-usb-cable-from-a-scale-to-pc-some-values-are-just-question-marks/65971845#65971845 – Tu deschizi eu inchid Apr 13 '21 at 16:52

2 Answers2

0

I even didn't use a new Thead for write and read on SerialPort. You just need use update control in Invoke() is ok. Below is my update on richTextBox. You can change form richTextBox to your listbox.

 public void update_RichTextBox(string message)
        {
            Invoke(new System.Action(() =>
            {
                txtReceivedData.Text += message;
                txtReceivedData.Refresh();
                txtReceivedData.SelectionStart = txtReceivedData.Text.Length;
                txtReceivedData.ScrollToCaret();
            }));
            
        }

and the way to use above void:

if (ComPort.IsOpen)
  {
                ComPort.Write(_inputdata + "\r");
                Form1._Form1.update_RichTextBox(_inputdata + "\r");
                string _receviedData = ComPort.ReadExisting();
                Form1._Form1.update_RichTextBox(respond);
                ComPort.DiscardInBuffer();//delete all data in device's received buffer
                ComPort.DiscardOutBuffer();// delete all data in transmit buffer
  }
else
            {
                MessageBox.Show("haven't yet open COM port");
                return "FLASE";
            }
0

I use something I call "Cross Thread Linker"

#region Cross Thread Linker
public bool ControlInvokeRequired(Control c, Action a)
{
    if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate { a(); }));
    else return false;
    return true;
}

void Update_RichTextBox(RichTextBox rtb, string Text)
{
    if (ControlInvokeRequired(rtb, () => Update_RichTextBox(rtb, Text))) return;
    rtb.AppendText(Text + Environment.NewLine);
}

#endregion

Then:

Update_RichTextBox(richTextBox1, "Text to append");