2

I have a nice and fancy C# program but the processing of the serially received bytes is way too slow. And I am thinking I can perhaps solve this using a background worker but I have no experience with it and all my earlier attemps failed.

private void serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            rxBuffer += serial.ReadExisting(); // add new bytes to the string 'rxBuffer'

            this.Invoke(new EventHandler(serialReceived));
        }

        private void serialReceived(object s, EventArgs e)
        {
            while (rxBuffer.Length > 0) // keep processing bytes until the string is empty
            {
                // textBox1.Text += rxBuffer[0];
                byte b = Convert.ToByte(rxBuffer[0]);
                string hexValue = b.ToString("X");

                rxBuffer = rxBuffer.Remove(0, 1); // removes the 'captured' byte from the string
                ..... 
                lots of code

This while loop is essentially 75% of my entire program. Incomming bytes
contoll UI elements, they can store or read from and to text files and send bytes back over the serial port, it simply can do about everything.

This systems works but it is far too slow. So I think moving the while loop in a background worker could solve it.

How can I implement a background worker in my current code setup? All my attempts so far failed.

EDIT: I want to add something which I just commented below. I thought that serial_DataReceived would be called with every single byte. There happens to be a delay of 2ms between 2 following received bytes in my application. ReadExisting() sometimes return more than 1 byte. So there is some kind of delay in the serial event. I have had this also in Qt but this was never a problem, I think it is ment as a feature to receive entire strings at once.

I personally think that the invoke() makes everything slow, but I have no prior experience with C# so I am just guessing, anyways I am open for any solutions.

bask185
  • 377
  • 1
  • 3
  • 12
  • Here is link which tells how can use background worker; https://stackoverflow.com/questions/6481304/how-to-use-a-backgroundworker – Önder Öğretmen Sep 07 '17 at 08:11
  • 1
    what is the reason behind using `ReadExisting` ? especially if you are actually interested in the bytes?=! [SerialPort.Read](https://msdn.microsoft.com/en-us/library/ms143549(v=vs.110).aspx) will read the bytes directly into a buffer. It looks like the entire string processing is actually the slow process, if you put it in the background, it will not be faster. Since you already implemented the `serial_DataReceived` event your process is already on a background thread. – Mong Zhu Sep 07 '17 at 08:15
  • What do you do in `lots of code` so that it requires an `Invoke` call? The call is probably making it so slow ( and my former assumption about the string processing is wrong) – Mong Zhu Sep 07 '17 at 08:17
  • @MongZhu It was my understanding that the event 'serial_DataReceived' get called with every received byte. This was not entirely the case as ReadExisting() often returns more than 1 byte. With ReadExisting I add all new data to the buffer string with the convenience of the += operator. The string is the buffer. – bask185 Sep 07 '17 at 08:33
  • @MongZhu I explained in the OP what happens in the lots of code. UI elements are controlled (text being placed on the screen) and I write to and from text files. I also send bytes over the serial port back to the machine. I needed the invoke to prevent a thread related crash. I cannot directly modify UI elements from a thread which originates out a serial event – bask185 Sep 07 '17 at 08:40
  • the event is called not at every incoming byte. ReadExisting reads the entire IncommingBuffer. So it can make your life miserable depending on the way that your device is streaming data. How exactly is your device communicating? – Mong Zhu Sep 07 '17 at 08:43
  • Well by adding the incommingbuffer to the string rxBuffer, I don't miss bytes and I can process them in the while loop 1 byte at the time. But I have succesfully implemented the background worker and it has a good effect. The isBusy() didn't seem to work for me so I used a try and catch blocks to call the runWorkerAsynch(); which does the trick. – bask185 Sep 07 '17 at 09:27

2 Answers2

1

this code seems to be the solution. I notice that my GUI runs signicantly smoother

private void serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        rxBuffer += serial.ReadExisting();

        try { backgroundWorker1.RunWorkerAsync(); } catch { }
    }


    private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
    {
        //private void serialReceived(object s, EventArgs e)
        //{
        while (rxBuffer.Length > 0)
        {
            // textBox1.Text += rxBuffer[0];
            byte b = Convert.ToByte(rxBuffer[0]);
            string hexValue = b.ToString("X");

            rxBuffer = rxBuffer.Remove(0, 1);

I tried using the isBusy() flag but it didn't work so I use the try and catch blocks instead.

The serial_Datareceived nicely keeps adding new bytes to the string rxBuffer and the backgroundworker makes sure that every single byte of the rxString gets processed.

EDIT: I have a problem, I cannot control my UI elements with the exception of a panel. I just found out. I am trying to acces the UI elements when I am still in a different thread.

bask185
  • 377
  • 1
  • 3
  • 12
1

I see this question a lot, where people are sending and receiving commands via the serial port. I have a great example here https://stackoverflow.com/a/38536373/2009197 which talks about using a background worker and a state machine to achieve asynchronous communication between computers/devices serially. You'll want to invoke updating your UI thread as you'll be receiving data on a separate thread.

Baddack
  • 1,947
  • 1
  • 24
  • 33