title: Communication speed over USB (PC/Arduino) using SerialPort in C# seems to "clip"
body: <p>Prepare for a lengthy explanation - sorry.</p>
  <p>I'm building an application in C#, using Visual Studio 2019, that will communicate with some Arduino MEGA 2560 R3 devices. The connection is through USB Serial (if I'm not mistaken, this is USB CDC, right?).</p>
  <p>Communication runs fine over all kinds of baudrates, even up to 2000000.</p>
  <p>To make sure that my Arduino can &quot;follow&quot; the command rate sent by my PC, I'm using an ACK-protocol, which is as simple as my Arduino confirming each command with the sequence &quot;A\n&quot;.</p>
  <p>In an attempt to stress test the communication, I let the application send &quot;0123456789\n&quot; commands as fast as possible, waiting for the &quot;A\n&quot; after each command sent. This means sending 11 characters, and receiving 2 characters, a total of 13 characters.</p>
  <p><strong>And here is where the mystery starts.</strong></p>
  <p>Starting from 38400 baud, the Tx/Rx cycle seems to clip at 244 per second. If I increase the baudrate up to 2000000, it remains at 244 Tx/Rx cycles per second. <strong>What is going here!</strong></p>
  <p>Below is the PC code, which is a simple Console application written in Visual Studio 2019:</p>
  <pre><code>using System;
  using System.Diagnostics;
  using System.IO.Ports;
  
  
  namespace BasicComTest
  {
      class Program
      {
          public static SerialPort _serialPort;
  
  
          static void Main(string[] args)
          {
              _serialPort = new SerialPort();
              _serialPort.BaudRate = 115200;
              _serialPort.PortName = &quot;COM4&quot;;
              _serialPort.Handshake = Handshake.None;
              _serialPort.NewLine = &quot;\n&quot;;
  
  
              _serialPort.ReadTimeout = 2000;
              _serialPort.WriteTimeout = 2000;
  
  
              _serialPort.Open();
  
  
              _serialPort.WriteLine(&quot;&quot;);
  
  
              Stopwatch sw = new Stopwatch();
              sw.Start();
  
  
              ulong count = 0;
  
  
              while (!Console.KeyAvailable)
              {
                  _serialPort.WriteLine($&quot;0123456789&quot;);
                  string ack = _serialPort.ReadLine();
  
  
                  count++;
                  if (count == 1000)
                  {
                      sw.Stop();
                      Debug.WriteLine($&quot;1000 commands in {sw.ElapsedMilliseconds} - {1000000/sw.ElapsedMilliseconds} commands/sec&quot;);
                      count = 0;
                      sw.Restart();
                  }
              }
  
  
              _serialPort.Close();
          }
      }
  }
  </code></pre>
  <p>And below is the simple Arduino sketch (I'm using an ISR routine in an attempt to increase speed):</p>
  <pre><code>volatile uint8_t rcvd = 0;
  
  String sCommandRx;
  String sCommand;
  bool bCommandRx;
  
  ISR(USART0_RX_vect)
  {
    // received data
    rcvd = UDR0;
  
    if (rcvd == '\n')
    {
      sCommandRx = sCommand;
      bCommandRx = true;
      sCommand = &quot;&quot;;
    }
    else
      sCommand += (char)rcvd;
  }
  
  void setup()
  {
    UBRR0H = 0;
    UBRR0L = 0;
  
    // USART initialization with 16MHz system clock, 8-none-1, RX interrupt
    UCSR0A = 1&lt;&lt;U2X0 | 0&lt;&lt;MPCM0;
    UCSR0B = 1&lt;&lt;RXCIE0 | 0&lt;&lt;TXCIE0 | 0&lt;&lt;UDRIE0 | 1&lt;&lt;RXEN0 | 1&lt;&lt;TXEN0 | 0&lt;&lt;UCSZ02;
    UCSR0C = 0&lt;&lt;UMSEL01 | 0&lt;&lt;UMSEL00 | 0&lt;&lt;UPM01 | 0&lt;&lt;UPM00 | 0&lt;&lt;USBS0 | 1&lt;&lt;UCSZ01 | 1&lt;&lt;UCSZ00 | 0&lt;&lt;UCPOL0;
  
    sCommand = &quot;&quot;;
  }
  
  void loop()
  {
    if (bCommandRx)
    {
      if (sCommandRx == &quot;0123456789&quot;)
      {
        UDR0 = 'A';
        while((UCSR0A &amp; 1&lt;&lt;TXC0)==0);
        UCSR0A |= 1&lt;&lt;TXC0;
       
        UDR0 = '\n';
        while((UCSR0A &amp; 1&lt;&lt;TXC0)==0);
        UCSR0A |= 1&lt;&lt;TXC0;
      }
  
      bCommandRx = false;
    }
  }
  </code></pre>
  <p>I can already guarantee that the Arduino code works perfect, because I tested it with 2 Arduino's back to back to rule out that they are causing the clipping. If you want to read the full story, you will find it on the Arduino forum here:</p>
  <p><a href="https://forum.arduino.cc/index.php?topic=719361.0" rel="nofollow noreferrer">https://forum.arduino.cc/index.php?topic=719361.0</a></p>
  <p>If I measure the communication speed with the 2 back-to-back Arduino's, then it goes as fast as 6060 Tx/Rx sets per second at 2000000 baud (you have to count some overhead, which is measured at about 100 msec for 1000 commands - you can read the full report in the Arduino forum).</p>
  <p>But using my console application, I get maximum <strong>244 Tx/Rx sets per second</strong>. Except (and this makes it even weirder), if I build the &quot;A\n&quot; in the ISR routing in my Arduino, then I'm getting exactly the double of <strong>488 Tx/Rx sets per second</strong>:</p>
  <pre><code>volatile uint8_t rcvd = 0;
  
  
  ISR(USART0_RX_vect)
  {
      // received data
      rcvd = UDR0;
      
      // reply with &quot;A\n&quot; after receiving &quot;\n&quot;
      if(rcvd == '\n')
      {
          UDR0 = 'A';
          while((UCSR0A &amp; 1&lt;&lt;TXC0)==0);
          UCSR0A |= 1&lt;&lt;TXC0;
          
          UDR0 = '\n';
          while((UCSR0A &amp; 1&lt;&lt;TXC0)==0);
          UCSR0A |= 1&lt;&lt;TXC0;
      }
  }
  
  void setup()
  {
      // set baud rate (500 Kbps)
      UBRR0H = 0;
      UBRR0L = 3;
  
      // USART initialization with 16MHz system clock, 8-none-1, RX interrupt
      UCSR0A = 1&lt;&lt;U2X0 | 0&lt;&lt;MPCM0;
      UCSR0B = 1&lt;&lt;RXCIE0 | 0&lt;&lt;TXCIE0 | 0&lt;&lt;UDRIE0 | 1&lt;&lt;RXEN0 | 1&lt;&lt;TXEN0 | 0&lt;&lt;UCSZ02;
      UCSR0C = 0&lt;&lt;UMSEL01 | 0&lt;&lt;UMSEL00 | 0&lt;&lt;UPM01 | 0&lt;&lt;UPM00 | 0&lt;&lt;USBS0 | 1&lt;&lt;UCSZ01 | 1&lt;&lt;UCSZ00 | 0&lt;&lt;UCPOL0;
  }
  
  void loop(){
  }
  </code></pre>
  <p>But whether I put the transmission of &quot;A\n&quot; in the loop() or in the ISR, the results of back-to-back Arduino's remains the same.</p>
  <p><strong>Sorry for this very long and detailed description. But my question is obviously very simple. What is the reason of this clipping?</strong></p>
  <p>I have a theory. Although I have read a lot of &quot;bad comments&quot; about SerialPort, I think that the reason has to be found in the fact that this goes over USB (Arduino works with USB 2.0). I have explained the theory on the Arduino forum (I even corrected my explanation slightly below):</p>
  <p><em>If I'm not mistaken, USB 2.0 HID devices (and I guess CDC is the same) have a polling rate of 1 msec. I experienced with some PIC microcontrollers, and built my own USB stack on my PC to communicate with it, and I had some limit of 1000 commands/sec, or 500 Tx/Rx sets per second. As you can read above, the fastest results I could get with SerialPort were 488 Tx/Rx sets per second, which is slightly slower but pretty close. There might of course be some &quot;conversion-lag&quot;, creating some de-synchronization between serial and USB, hence missing some USB frames. Putting the &quot;A\n&quot; answer in the loop() (not in the ISR) gave me only the half (244) commands. I think that putting the answer in the ISR made it react so fast, that the data could still &quot;piggy back&quot; on the next frame. If I put the answer in the loop(), it missed the train, and took the some later 1msec frames. Anyway, these are just WILD GUESSES for now, so I could be COMPLETELY wrong of course.</em></p>
  <p><strong>Update:</strong></p>
  <p>Just tried to work with some lower level file handling in my C# program, using Kernel32.CreateFile, Kernel32.WriteFile and Kernel32.ReadFile, but got exactly the same results which means 244 Tx/Rx sets per second. Even tried to play with the timings (COMMTIMEOUTS), but nothing changed.</p>
  <p>I also tried with sending and receiving larger strings of characters, and it seems that if I use up to 72 + '\n' for both the Tx and Rx portion, it remains stable. Using more characters starts dropping the Tx/Rx sets per second number, until I use 76 + '\n' for both the Tx and Rx portion. Then it remains stable again at 162 Tx/Rx sets per second.</p>
  <p>For my application, 244 Tx/Rx sets per second is good enough, but still am curious to understand from where that limitation comes.</p>