0

I am working with visual studio 2019. I am sending data from microcontroller over uart/serial port to the PC and I want to read it on the PC side.

The baud rate is 9600. I am using the following code. However, its very slow. I need to be able to read the code with very high speed (comparable to the baud rate I use).

At present, I am getting 2-3 packets per second. The timer interval is set at 10ms but even if I change it to 1ms, there is no difference. I cant figure out what am I doing wrong. Help will be greatly appreciated.

Regards, Salman

    public void  readCapsule(SerialPort sp)
    {
        timer1.Enabled = false;

        string headerStart = "";
        string headerEnd = "";
        List<Int32> newCoordinates = new List<Int32>();

        headerStart = sp.ReadLine();

        if (headerStart == "START")
        {               
            for (int i = 0; i < 3; i++)
            {
                Int32 coords = sp.ReadByte();
                newCoordinates.Add(coords);
            }

            tbRead.AppendText("X: " + newCoordinates[0].ToString() + " ");
            tbRead.AppendText("Y: " + newCoordinates[1].ToString() + " ");
            tbRead.AppendText("Z: " + newCoordinates[2].ToString() + Environment.NewLine);

            headerEnd = sp.ReadLine();
            newCoordinates.Clear();                
        }
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        readCapsule(spCapsule);            
        Application.DoEvents();
        spCapsule.DiscardInBuffer();
        timer1.Enabled = true;
    }

My Microcontroller code is basically incrementing 3 variables and sending them in between the START and END header. I know that there are other lines being executed so the throughput wont be 9600 but the delay I experience is WAY too long to be contributed from the microcontroller side I think. The microcontroller code is below. I am using Atmega328p. I have verified on hyper terminal that the incoming data is way faster than the rate at which I read with my C# code:

while (1) {

    counter++; val1 = val1 + 3; val2 = val2 + 3; val3 = val3 + 3; 

    if(counter >= 100) {

        counter = 0;
        val1 = 1; val2 = 2; val3 = 3;

        }

    transmitUart0('S'); transmitUart0('T'); transmitUart0('A'); transmitUart0('R'); transmitUart0('T');transmitUart0(0x0A);
    transmitUart0(val1);
    transmitUart0(val2);
    transmitUart0(val3);
    transmitUart0('E'); transmitUart0('N'); transmitUart0('D'); transmitUart0(0x0A);

    _delay_ms(10);

}
Salman
  • 11
  • 1
  • 3
  • Do you have any reason to assume the slow rate isn't just the rate at which the microcontroller sends the data? A baud rate of 9600 is just a maximum; it doesn't mean that that's the rate at which data is transmitted continuously. Is the data coming from a sensor that just can't be read that often, for example? – Jeroen Mostert Jul 10 '19 at 15:21
  • What happens if you call `sp.ReadLine()` and the microcontroller hasn't sent anything yet? – Lasse V. Karlsen Jul 10 '19 at 15:22
  • You're calling `sp.DiscardInBuffer()`, won't that potentially lose bytes? – Lasse V. Karlsen Jul 10 '19 at 15:22
  • @JeroenMostert: Please see the MCU side code. I just included it. The data is coming from a sensor that is available at 100Hz so that shouldnt be a problem. – Salman Jul 11 '19 at 07:16
  • @LasseVågsætherKarlsen: the code waits there but the idea is that it should wait. However, I used it so that the the start header is where the data 'capture' should start. – Salman Jul 11 '19 at 07:19
  • @LasseVågsætherKarlsen: I used sp.DiscardInBuffer() because I kept getting the old data. – Salman Jul 11 '19 at 07:20

3 Answers3

1

We don't know what your microcontroller is doing but on your C# code you're introducing a huge overhead on your processing by reading bytes one by one.

The solution to reducing this overhead is, obviously, do not read bytes one by one. Just get rid of the for loop, do a second sp.ReadLine() right after you detect the header, work with the bytes you get to store the coordinates and discard the rest.

If this approach is not fixing your problem, then try to square it: read more commands in one go and process them. At this point, it might be easier to change the way the microcontroller works I guess.

EDIT: Now that you have included more details, and as you have already realized, my comments above are not really helpful. ) First off, ReadLine() is known to be very slow, see for instance here.

What you're trying to do is not really that demanding if you work out the numbers. So the solution might be to use any other method, I would advise trying the BaseStream to implement your own way of reading between CRs (something similar to what the question linked above proposes).

Otherwise you can try the DataReceived event.

Finally, note that when you say:

The timer interval is set at 10ms but even if I change it to 1ms, there is no difference...

referring to the delay on your microcontroller. You got that the other way around: if you want to see better performance reading data on your PC you need to increase and not reduce this delay (otherwise you are sending even more data than the amount you can handle). But if ReadLines() is that slow, I doubt you can improve the performance exploiting this, unless you're willing to read one data sample every 3 or 4 seconds.

Marcos G.
  • 3,371
  • 2
  • 8
  • 16
1

You should try to handle all the buffer data at once. So rather than reading byte by byte or line by line, subscribe to the event SerialPort.DataReceived and process the whole chunk at once.

See this example: https://learn.microsoft.com/en-us/dotnet/api/system.io.ports.serialport.datareceived?view=netframework-4.8

private static void DataReceivedHandler(
                    object sender,
                    SerialDataReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;
    string indata = sp.ReadExisting();
    Console.WriteLine("Data Received:");
    Console.Write(indata);
    // Use BeginInvoke for synchonization!
}

For using BeginInvoke, see this other thread.

DasKrümelmonster
  • 5,816
  • 1
  • 24
  • 45
0

I have made some changes to the code as per the feedback. The delays are the same. Please see the code below:

public void readCapsule(SerialPort sp)
{
        timer1.Enabled = false;

        string headerStart = "";
        string headerEnd = "";
        List<Int32> newCoordinates = new List<Int32>();

        headerStart = sp.ReadLine();
        tbRead.AppendText(headerStart + Environment.NewLine);

}


private void timer1_Tick(object sender, EventArgs e)
{
        readCapsule(spCapsule);
        timer1.Enabled = true;
    }
Salman
  • 11
  • 1
  • 3
  • Looking at the code on your microcontroller I can see why what I proposed is not helping much: you are sending 5 bytes + CR + 3 bytes (assuming those `val` are 1 byte each) + 3 bytes + CR. So you read 5 bytes on your first `ReadLine` and 6 bytes on your second. I'm afraid you'll have to rethink your whole implementation if you want to improve this dismal performance. I'll edit my answer with some more details. – Marcos G. Jul 12 '19 at 05:40