Due native USB bidirectional I/O performance [Now with specific example]

Ok. I'm fine with ugly code and having to change some names of stuff. Teensy does indeed have native USB, which is part of the reason I'd like to try running your benchmark. Like Leonardo's native USB, it's called "Serial", not "SerialUSB". That's pretty easy to change.

It does take a LOT of time to do good benchmarks, make the code nice to use, and write up the results well. I've done a lot of benchmarks, but published very few so far because of how much work it is to go from ugly but useful info to something worth of publishing to a wide audience.

Don't worry... I'm only interested to run it and see how simultaneously data flow, perhaps in ways I've not anticipated, actually works. So ugly code is fine, really.

Linux is not an issue. I use all 3 systems and Ubuntu is my primary desktop.

But if you're got a lot of complex stuff going on with timers, perhaps this isn't a good benchmark that shows Due's actual native USB performance? Maybe the problem is unrelated to the USB speed at all. Complicated timing issues are pretty common with programs that directly manipulate the timers and other hardware.

If you do decide to post some code, I'll take a look.

If you post some code, I'll give it a try here on a couple other boards. I have a collection of every Arduino compatible board which currently supports native USB: Arduino Leonardo & Due, every Teensy, Maple, and Fubarino Mini.

It could at least be a sanity check. But until you actually post something, nobody could even begin to guess why you're experiencing these speed issues.

I'm about to give this a try, but first, other than one bad result at the end, I'm failing to see what is so unexpected?

exedor:
Tweaking the above code produces the following times when running test program above on Linux
Time to read 2400 packets of 64 bytes from native USB only: 1 second (or less)
Time to write 2400 packets of 64 bytes to native USB only: 1 second (or less)
Time to read 2400 packets of 32 bytes from native USB only: 1 second (or less)
Time to write 2400 packets of 32 bytes to native USB only: 1 second (or less)

This seems to confirm the native port is indeed fast. Is that right? Where is the problem you mentioned?

Time to write 2400 packets of 64 bytes to programming port: 13 seconds
Time to read 2400 packets of 64 bytes from programming port: 13 seconds

13 seconds is exactly as expected. The programming port is a hardware serial port that communicates to the 16u2 chip. You set the baud rate to 115200, which gives 11.52 kbytes/sec. Sending 153600 bytes at that speed should take 13.3 seconds.

Time to write 2400 packets of 32 bytes to programming port: 77 seconds
Time to read 2400 packets of 32 bytes from programming port: 77 seconds

This is the one result I do not understand. You're transmitting half as much data in exactly the same way, but the time increases by a factor of 6.

I've tried changing port speed to 230400. It makes no difference whatsoever.

Did you change it on the Arduino side? On the programming port, the rate Arduino is sending controls the pace.

I would use the native USB port for both read and write but as soon as I start doing async read/write against the native USB port, I'm seeing extreme data corruption and data loss.

Again, I would give this a try here, but you didn't post any code which attempts to demonstrate the problem.

I've run the test. I can confirm similar results.

paul@preston:/tmp/t > ./test 
Succfully opened /dev/ttyACM0 for reading
Total time to run test with 2400 packets of size 64 was 13 seconds
paul@preston:/tmp/t > ./test 
Succfully opened /dev/ttyACM0 for reading
Total time to run test with 2400 packets of size 32 was 81 seconds

Something is clearly very wrong with the 32 byte test.

I believe the problems you've been seeing are caused by incorrect baud rate settings on the programming port.

Using "stty" to set the baud rate is not reliable. Here is a copy of your code using the proper termios functions to set the baud rate after the port is open. This version always receives the 2400 32-byte packets in 6 or 7 seconds.

#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>

#define PKTS           2400
#define PKT_SIZE       32
#define SER_PORT_IN    "/dev/ttyACM0"  // On my system this is programming                                                                   
#define SER_PORT_OUT   "/dev/ttyACM1"  // On my system this is native USB                                                                    
#define SER_PORT_SPEED 115200

#define TEST_READ     1
#define TEST_WRITE    0

int main() {

  time_t deltaT = 0;
  int i = 0, bytes = 0, totalBytes = 0, fdIn = 0, fdOut = 0;
  char buf[PKT_SIZE];
  struct termios t;

#if !(TEST_READ || TEST_WRITE)
  printf("Nothing to test, change TEST_READ to 1 or TEST_WRITE to 1\n");
  return(0);
#endif

#if TEST_READ
  // this stty command does not seem to set the baud rate in some cases
  snprintf(buf, sizeof(buf)-1, "stty -F %s sane raw -echo %d", SER_PORT_IN, SER_PORT_SPEED);
  errno = 0;
  fdIn = open(SER_PORT_IN, O_RDWR | O_NOCTTY);
  if (fdIn < 0)  {
    printf("ERROR: Unable to open port %s, err(%d): %s\n",
           SER_PORT_IN, errno, strerror(errno));
    return(-1);
  }
  // using the termios functions after the port is open is very reliable
  tcgetattr(fdIn, &t);
  cfsetispeed(&t, B115200);
  tcsetattr(fdIn, TCSANOW, &t);
  printf("Succfully opened %s for reading\n", SER_PORT_IN);
#endif

#if TEST_WRITE
  snprintf(buf, sizeof(buf)-1, "stty -F %s sane raw -echo", SER_PORT_OUT);
  system(buf);

  // Time for device to reset and native USB dev node to show up again                                                                       
  sleep(4);

  errno = 0;
  fdOut = open(SER_PORT_OUT, O_RDWR | O_NOCTTY);
  if (fdOut < 0)  {
    printf("ERROR: Unable to open port %s, err(%d): %s\n",
           SER_PORT_OUT, errno, strerror(errno));
    return(-1);
  }
  printf("Succfully opened %s for writing\n", SER_PORT_OUT);
#endif

  deltaT = time(NULL);
  for (i = 0; i < PKTS; i++) {
#if TEST_WRITE
    // Ensure full packet is delivered                                                                                                       
    bytes = 0; totalBytes = 0;
    while (totalBytes < sizeof(buf)) {
      errno = 0;
      bytes = write(fdOut, buf+totalBytes, sizeof(buf) - totalBytes);
      if (bytes < 0) {
        printf("ERROR: Unable to write to  port %s, err(%d): %s\n",
               SER_PORT_OUT, errno, strerror(errno));
        return(-1);
      }
      totalBytes += bytes;
    }
#endif

#if TEST_READ
    bytes = 0; totalBytes = 0;
    while (totalBytes < sizeof(buf)) {
      errno = 0;
      bytes = read(fdIn, buf+totalBytes, sizeof(buf) - totalBytes);
      if (bytes < 0) {
        printf("ERROR: Unable to read from port %s, err(%d): %s\n",
               SER_PORT_IN, errno, strerror(errno));
        return(-2);
      }
      //printf("read %d\n", bytes);
      totalBytes += bytes;
    }
#endif
  }
  printf("Total time to run test with %d packets of size %d was %d seconds\n",
         PKTS, (int)sizeof(buf), (int)(time(NULL) - deltaT));
  return 0;
}

exedor: Try packet sizes of 36 and 43, they might help you see where your bug is :wink:

When using that stty hack I find it better having open(...) before system("stty ..."). It's always been reliable for me but even so I wouldn't use it in production code or code for profiling.

I really don't think I can keep following this thread. You posted code, I tried to help. You could have posted code demonstrating the actual bidirectional problem, but didn't. I just can't keep wasting time on this.

I have had a similar problem MavLink problem with certain Arduino boards - Networking, Protocols, and Devices - Arduino Forum

I thought it might have been caused by binary data, but reading this thread, I see that I also was doing bidirectional comms. The exact same sketch works 100% if I switch to the Programming port, at 57600 baud. It seems the SerialUSB on the Due, and the Serial on the Leonardo, are too flaky to be used for real life two way comms. :roll_eyes:

My sketch is a biggish system, and uses MavLink to third party software, so its hard to extract a simple test. Exedor - did you get a stripped down test written?

Does just sending data TO the PC using SerialUSB.print
at speeds 115200 or higher work flawlessly?
Thanks, Gary

I guess that needs a Due sketch sending out incrementing values and a processing sketch to read and verify. I don't have the time to develop this right now unfortunately...

garygid:
Does just sending data TO the PC using SerialUSB.print
at speeds 115200 or higher work flawlessly?

It did work when I tested several months ago. The speed was in the hundreds of kbytes/sec range, which is dramatically faster than 11 kbytes/sec, but far less than Due's hardware ought to be capable of providing.

Maybe I ought to re-run these tests? Now I have a nice collection of all the Arduino compatible boards with native USB (each with their own USB stacks that have vastly different performance): Arduino Due & Leonardo, Maple, Teensy 2.0 & 3.0, and Fubarino Mini.

EDIT: I had hoped to adapt the OP's test into a bidirectional bandwidth benchmark..... but after many messages he never actually posted even a test program that demonstrated the problem, and has since deleted almost all his prior writings.

I just dug out an old transmit benchmark program, ported it to Arduino and ran it.

Using Arduino 1.5.4, Due's speed has improved about 10X since I tested many months ago. On my Linux machine, I measured 4700.86 kbyte/sec transmit-only speed from Arduino Due. That's 408 times faster than using hardware serial at 115200 baud.

Here's the benchmark test, if you'd like to try running it on your own system:

// USB Serial Transmit Bandwidth Test
// Written by Paul Stoffregen, paul@pjrc.com
// This benchmark code is in the public domain.
//
// Within 5 seconds of opening the port, this program
// will send a message as rapidly as possible, for 10 seconds.
//
// To run this benchmark test, use serial_read.exe (Windows) or
// serial_listen (Mac, Linux) program can read the data efficiently
// without saving it.
// http://www.pjrc.com/teensy/serial_listen.c
// http://www.pjrc.com/teensy/serial_read.c
// http://www.pjrc.com/teensy/serial_read.exe
//
// You can also run a terminal emulator and select the option
// to capture all text to a file. However, some terminal emulators
// may limit the speed, depending upon how they update the screen
// and how efficiently their code processes the imcoming data. The
// Arduino Serial Monitor is particularly slow. Only use it to
// verify this sketch works. For actual benchmarks, use the
// efficient receive tests above.
//
// Full disclosure: Paul is the author of Teensyduino.
//
// Results can vary depending on the number of other USB devices
// connected. For fastest results, disconnect all others.

//#define USBSERIAL Serial // for Leonardo, Teensy, Fubarino
#define USBSERIAL SerialUSB // for Due, Maple

void setup()
{
USBSERIAL.begin(115200);
}

void loop()
{
// wait for serial port to be opened
while (!USBSERIAL) ;

// give the user 5 seconds to enable text capture in their
// terminal emulator, or do whatever to get ready
for (int n=5; n; n--) {
USBSERIAL.print("10 second speed test begins in ");
USBSERIAL.print(n);
USBSERIAL.println(" seconds.");
if (!USBSERIAL) break;
delay(1000);
}

// send a string as fast as possible, for 10 seconds
unsigned long beginMillis = millis();
do {
USBSERIAL.print("USB Fast Serial Transmit Bandwidth Test, capture this text.\r\n");
} while (millis() - beginMillis < 10000);
USBSERIAL.println("done!");

// after the test, wait forever doing nothing,
// well, at least until the terminal emulator quits
while (USBSERIAL) ;
}

Hi Paul, I upload it , then I run in command line "serial_read.exe COM8:"
my result :
on Win 7 64

C:\Users\Administrador\Downloads>serial_read.exe COM8:
Reading from COM8:
timeout reading from COM8:
Total bytes read: 0
Speed 0.00 kbytes/sec

C:\Users\Administrador\Downloads>

What I do wrong?

blackmaster:
What I do wrong?

I don't know from only this info. Maybe your Due isn't running the test program? Maybe you're opening the wrong COM port? Maybe you're using the programming port instead of the native port? Maybe the Windows serial driver is messed up, as it tends to be sometimes? (Mac and Linux are much more reliable than Windows)

Did you try opening COM8 from the Arduino Serial Monitor? You should see the 5 messages about the test beginning, then 10 seconds of rapid data, and "done" at the end. Try opening the serial monitor a few times, letting the test run all 15 seconds each time. Every time you open the serial monitor, the test should run again. Once that's working, then close the serial monitor and run the serial_read.exe on COM8.

To use the IDE Serial Monitor, one needs to output
using the Programming USB Port on the Due, right?

And, to direct the output to the Programming USB Port,
one needs to re-compile with Serial instead of SerialUSB,
right?

Or, can the Serial Monitor be used to monitor any Comm
Port?
If so, how?
Perhaps by re-selecting a Comm Port after the program
download is finished, and before opening the Serial
Monitor?

Thanks, Gary

Does the
while( !Serial ) ;
just wait for a Comm Port to be created in the PC,
or does it also wait for some program in
the PC to Open the port?

Thanks, Gary

garygid:
Or, can the Serial Monitor be used to monitor any Comm
Port?

Yes, it can open any serial port.

If so, how?

Select the port in Tools > Serial Ports, then open the serial monitor.

Does the
while( !Serial ) ;
just wait for a Comm Port to be created in the PC,
or does it also wait for some program in
the PC to Open the port?

It waits for a program to actually open the port.

Perhaps by re-selecting a Comm Port after the program
download is finished, and before opening the Serial
Monitor?

One simple approach is to program this sketch using the programming port. After it's fully uploaded, physically unplug the USB cable from the programming port. Look at the Tools > Serial ports menu while it's unplugged. Then plug the cable into the native port. When you look at the Tools > Serial Ports list again, the newly appearing port is the one for the native port, so choose it.

Then open the serial monitor. You should see the test run in the serial monitor window for 15 seconds. If that works, close the window and open it again. Each time you reopen the serial monitor window, the test will run again. Get that working before you try running serial_read.exe.

Did you ever get the benchmark to run?

I'm curious to hear how it performs on other systems. Not curious enough to actually go to the trouble of setting up and running those tests myself, but curious enough to ask here.....