Is there a known speed limitation on the Uno USB UART?

but with this batch are you setting the right values? if no, try do do it.
maybe there is a different initialization by udev for USBx and ACMx devicies

The atmega328p on the UNO running at 16MHz has a theoretical limit of (I can't decide which off hand) either 1Mbps or 2Mbps (depending on if you can set the baud rate generator to 0 and have it still work - something I'm not sure of).

The BASH code I use

#!/bin/bash
arduinoport=`cat /root/arduinoport` # sets arduinoport to suit UNO or Duemilanove which is stored in /root/arduinoport

stty -F $arduinoport cs8 115200 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -clocal

cat $arduinoport|head -n 6|tail -n 4 > /root/arduino2

other than setting the device name (/dev/ttyUSB0 for the Duemilanove) in /root/arduinoport that is the only change between the Uno and the Duemilanove. It has worked 100% once a minute over several years. Although I do use the Duemilanove most of the time.

I have used two different programs:

  1. A C++ program that configures the port with tcsetattr(), and then runs a recv() loop in a thread. I have tried with both O_NONBLOCK, and MIN=1/TIME=1 tty settings.
  2. Simply "dd if=/dev/ttyACM2 of=foo bs=X" on the command line, with values of X of 1 and 16 and 64.

Note that latency is important -- data sent from the board is packetized in chunks of 7 - 18 bytes, and as soon as there is data, I want to be able to poll it.
The actual data rate is only about 2 kB/second, so at packet size 64, that's 32 calls to read() per second.

How big is the receive/USB buffer in the 8u2 on the Uno? Is it double-buffered? Maybe the firmware is available somewhere so I can look at it to answer this question myself?
I would expect that there would be sufficient buffering in the Linux ttyACM driver so that it could keep up with this simple data rate, but somehow, it's not yet working right.

After seeing this thread and posting in it, I thought I'd hook up my Uno in place of the Duemilanove in my long running home monitoring sketch. All appeared well so I left it to it, came back to it several hours later to find numerous holes in my graphs and log files. Not many, but not the 100% perfect record the Duemilanove has been turning in. I've put the Duemilanove back in place. I've had numerous doubts about the Uno since I bought one, this hasn't exactly inspired my confidence..............

It is an early Uno BTW - A genuine Italian one.

The Duemilanove I took out and put back is a Chinese knock off I bought on Ebay.

Yes, my Uno is early, too -- it's a R1, and it has the 8u2, not the 16u2 for serial conversion.
Maybe I should try the R3 and see if it's any better...

I tried with an R3, and it has the same problem.

I also tried with two stop bits (to give it a bit more breathing room) and with 38400 bps instead of 115200 bps, with about the same result.

Additionally, after a few hundred bytes, the receiving just stops, even though I can verify on the logic analyzer that the data is still being sent out the TX of the 328p. This happens no matter whether I use "cat," "dd," or my own program.

I'm going to have to try this on Windows now, to see if it really can be a Linux driver problem.

I took a look at the firmware for the 8u2 in the arduino-1.0.1 distribution.

I'm re-building the arduino-usbserial using the latest version of LUFA. Some changes were needed, because some symbols had changed names, but that worked out alright.

Now, I'm trying to program the 8u2 chip directly. After flashing the chip, the Linux USB stack sees it as ttyACM2, but any attempt to read from or write to it just hangs. This is most likely because of the update to a newer LUFA, as I didn't spend a ton of time on that. Does anyone else have experience with this? Or is the fact that I didn't combine with the DFU firmware the problem? If so, how do I do that combinification step?

Flashing back to the UNO-dfu_and_usbserial_combined.hex firmware goes back to the old broken behavior.

jwatte:
I'm getting all kinds of corrupted data in the Uno -> Host direction.

Can you give more detail? I had a sketch sending MIDI data in quite large volumes at 115200 baud to the serial monitor on a Mac. It didn't "seem" to lose any, although it might be hard to tell for sure. What I can say though is that the data lined up. See here for example:

If bytes were being dropped, the text wouldn't line up the way it did.

How big is the receive/USB buffer in the 8u2 on the Uno? Is it double-buffered?

According to the datasheet:

Programmable maximum packet size from 8 to 64 bytes Programmable single or double buffer

I read through the code in the usbserial firmware, and it uses a 128 byte ring buffer going each direction, with a "high water mark" at 96 bytes.
At 10 bytes per millisecond, that's only 3 ms to drain a packet worst case, which may be hard to get with a 1 ms USB bus frame length. There may be other things on the USB bus, or the host may be temporarily busy or something...

Also, the ring buffer uses two pointers and one byte for count, when all it really needs is two bytes; one each for head and tail counter. Such waste :wink:

Right now, I'd like for my updated re-build of the firmware to actually work. I believe the problem is that the fuses are set to use the bootloader, which I have not programmed into the chip, and thus it hangs when I start it. But, that could be wrong, because the bus will at least see it ("lsusb" shows the Arduino device, etc.) If I can make this firmware work, then I can experiment enough to figure out where the loss is -- on the Linux side or the 8u2 side.
(I do not need or use the DFU functionality.)

Another option is perhaps to look at the VirtualSerial project from the LUFA distribution, and see if that will work any better...

It wouldn't surprise me if the fuses needed tweaking, but you said the board identified itself, which implies the software was running.

Can you reduce the data rate? eg. use binary rather than ASCII text, or employ some form of data compression?

maybe because cat will read the buffer but without ascking for protocol initialization (like using Serial.read witout a Serial.print before).
maybe is just a trick with the lock file
it almost work because all setting are there, but almost is not always. can you try to use a real Serial program (like serial monitor) and look if it work correctly?

I don't know if this helps, but I have had problems sending files over a serial link at 115.2kbps without using an Arduino. I have had this problem on more than one machine. In both cases, I used what appeared to be a good, machine-made, less-than 3 foot (1m) cable connected between two computers with built-in serial ports with a null-modem adapter, of course. You can easily conduct this experiment on Windows by running two instances of HyperTerminal and use the Send File feature. I use the Zmodem protocol. When I do this, I occasionally get the status "bad packet" and HyperTerminal does a re-transmit. I'm sure this experiment can be done on Linux. I just don't know how. I have never investigated the cause. I'm curious if others have encountered this problem.

A hardware-based UART on a PC is different from the USB stack. Depending on the depth of the buffer, it may need to be able to service an interrupt in less than a millisecond if it's to avoid dropping data, and Windows does not in general give such timing guarantees.

The thing here is that the "serial" part is the trace between the AVR 328p and the 8u2 on the Arduino circuit board. That's a very short trace :slight_smile: Also, the code for the 8u2 (in the bootloader serial firmware) seems like it would not disable interrupts for 100 microseconds at a time, so it shouldn't be dropping data.

Also, the signal on the TX pin on the 328p is super clean -- the logic analyzer has no problem decoding it, and there are no framing errors or anything.

Thus, the corruption is either in the 8u2 firmware, or on the USB bus, or in the Linux cdc_acm USB driver. I have a hard time teasing those thee things apart, so it's hard to know where exactly to look now.
My current approach is to write some other firmware for the 8u2, and flash that, to try to control what exactly gets sent over the USB, so I can then test whether it makes it all the way to the Linux box.
Unfortunately, because the firmware I flashed didn't start, I haven't gotten to that part yet. Probably, I need to disable bootloader start-up in the fuses; or I need to actually provide the DFU bootloader together with the serial firmware.

So, the question is: How do I combine the existing DFU firmware hex file with my own usbserial hex file? It's some incantation of objcopy, I'm sure, telling it to put the DFU input at point A and the usbserial input at point B; I'd just like an actual example command line of how our Arduino friends are doing this, because that seems to have dropped out of the available documentation...

jwatte:
How do I combine the existing DFU firmware hex file with my own usbserial hex file? It's some incantation of objcopy, I'm sure, telling it to put the DFU input at point A and the usbserial input at point B; ...

I don't really understand that question. However an alternative way of getting hex files onto the chip is presented here:

If you have an SD card reader lying around you could use that. That program also lets you tweak the fuses.

I'm not sure what you want is achievable. Most things in the computer world have a protocol of "are you ready?" and "retransmit that, I didn't get it". You are trying to shove data to the host at 87 uS per byte, continually, regardless of what the host is doing.

jwatte:
A hardware-based UART on a PC is different from the USB stack. Depending on the depth of the buffer, it may need to be able to service an interrupt in less than a millisecond if it's to avoid dropping data, and Windows does not in general give such timing guarantees.

The computers UART has a hardware based FIFO buffer. Old (<100 MHz) computers could handle this bandwidth. It seems highly unlikely that a modern computer UART buffer is going to fill up.

jwatte:
The thing here is that the "serial" part is the trace between the AVR 328p and the 8u2 on the Arduino circuit board. That's a very short trace :slight_smile: Also, the code for the 8u2 (in the bootloader serial firmware) seems like it would not disable interrupts for 100 microseconds at a time, so it shouldn't be dropping data.

Also, the signal on the TX pin on the 328p is super clean -- the logic analyzer has no problem decoding it, and there are no framing errors or anything.

I agree that the short serial link between the 328p and the 8u2 is probably not causing a problem but a logic analyzer won't show you how "clean" it is.

How long does it take before you see corruption in the data? Have you tried capturing the data directly (without the 8u2) from the 328p over that seem amount of time?

mkwired:
How long does it take before you see corruption in the data? Have you tried capturing the data directly (without the 8u2) from the 328p over that seem amount of time?

The data is almost immediately corrupted -- as in, within the first 50 bytes, say.

I already captured the data directly from the 328p, using the logic analyzer, which is a Saleae USB connected device and another Linux program, and it captures the data just fine (using the asynchronous serial analyzer.)
This should tell me that the USB bus itself is probably OK, because that Saleae box works just fine, at a much higher rate (1 byte per bit assuming RLE compression.)

The computers UART has a hardware based FIFO buffer. Old (<100 MHz) computers could handle this bandwidth. It seems highly unlikely that a modern computer UART buffer is going to fill up.

Those UARTs are often just 16 bytes long. The interrupt could come in at half-way, or could even come in at a high water mark at 12 or 15 bytes full.
If half-way, then the kernel has (8/11520) seconds, or about 0.7 milliseconds to answer the interrupt. If the high water mark is at 15 bytes, it has less than 0.1 milliseconds. And, while computers are a lot faster now, they are not necessarily a lot lower latency.