Due - SerialUSB.write() issue

Hello,

I am trying to do something similar to this:

I tried to simply run the code as-is, but I don't get any output.

After some investigation, it would appear that there is a limit to the amount of data that can be sent with SerialUSB.write() => if the length of the data is greater than 64, I am not getting any output.

I am using the very simple code below for testing:

#include <Arduino.h>

const int buf_size = 65;
uint16_t buf[buf_size];

void setup(){
    SerialUSB.begin(0);
    while(!SerialUSB);
  
    for (int i = 0; i < buf_size; i++) {
        buf[i] = 2000 + i;
    }

}

void loop(){
    SerialUSB.write((uint8_t *)buf, buf_size); 
}

When buf_size is set to 64, I get the expected output. Try any value > 64, and there is no output whatsoever.

Is this the expected behavior? Apparently, this used to work in the past (the code linked at the beginning has been used by a lot of people).
Or am I doing something wrong?

NOTE: I am using an Arduino Due. In general, I code in vscode + pio, but I also tried with the Arduino IDE v2, same results.

Thanks!

You create array of buf_size * 2 bytes:

but send buf_size bytes only:

SerialUSB.write larger as 64 bytes

My guess:
the 64 comes from the maximum size of an USB VCP packet (EP size).
Sending larger amount of data needs a loop, every USB chunk is max. 64 bytes.

@b707 :

You create array of buf_size * 2 bytes
but send buf_size bytes only
Yes, I am well aware of this, but that is not the point here. My goal was just to illustrate the fact that I cannot send more than 64 bytes. The code is not meant to do anything useful (or even correct). But thanks for pointing this out anyway!

@tjaekel : do you know if this is documented somewhere (I couldn't find any information on this)?
I also find this difficult to accept since this was working in the past (cf. arduino-due_high-speed-ADC.ino, first link that I provided). Shouldn't this be considered a regression instead?

USB Bulk transfer - EP size 64 bytes
USB CDC (VCP UART) - EP size 64 bytes

See in USB Enumeration, text etc. - max. packet size for data endpoint (EP) on USB CDC (VCP UART) is 64 bytes.

Thanks.
But I was thinking about documentation on SerialUSB :slight_smile:
What you sent is fairly low level, and I would expect this level of details to be hidden by the SerialUSB interface.

That said, you are certainly right, this might well be the root cause of the issue I am facing.

Looking at the code here (I hope that I am looking at the right one):

it looks like the SerialUSB.write() is calling USBD_Send() here:

And this snippet appears to take care of the EP max packet size:

    while (len)
    {
        if(ep==0) n = EP0_SIZE;
        else n =  EPX_SIZE;
        if (n > len)
            n = len;
        len -= n;

        UDD_Send(ep & 0xF, data, n);
        data += n;
    }

So unless I am completely wrong, it should be possible to send more than 64 bytes with SerialUSB.write()

Potentially yes:
See this code: it sends in chunks, here the EPX_SIZE. This is potentially set to 64 bytes (as max. for VCP UART).

But: I do not trust the UDD_Send() function:
if this one does not wait for the previous chunk sent - just 64 bytes go through and next is "too early" and lost.

Try to do this (more for debug):

  • send max. 64 bytes
  • after sending 64 bytes and some more still to send- wait, e.g. 1 ms
  • try to send again the next (64 max. byte) chunk (or remaining part)
    Or:
  • make sure never to send more as 64 bytes (entire string length to send)
  • have a delay somewhere, before you send the next string from somewhere else

USB VCP is a device: it just transmits the 64 bytes as one USB packet when host has requested it. So, if device sends faster and larger as requested - it is lost.
Just split into 64 byte chunks, wait a bit between two sends (e.g. 1 ms) ...

Just make your strings smaller, do not send so often ... and let's see if this works.
If it does, but with larger and faster - a bug in the LIB code.

You can also "stress the system" and test:

  • send a string with 128 characters, e.g. an incrementing pattern
  • send this in a fast loop
  • if it comes as all on PC (host) - all fine
  • if you lose parts, e.g. an USB packet - the implementation for USB VCP in LIB is wrong
    (it does not wait for next available 64 byte chunk possible to send - actually requested by host)

You can "relax" the timing:

  • if you put your USB VCP sender into a thread/task
  • you send max. 64 bytes per chunk
  • but it has a FIFO: it stores the other additional characters and tries to send (a bit later) again
  • you need a criteria if last chunk was delivered, otherwise, wait 1 ms between chunks
  • so, the USB sender task sends your FIFO as 64 byte chunks, waits in between ...
    but you can be faster as this thread - you need a "back pressure" (and stop your sender)

Just test the max. throughput you could get without lost data...
I assume: the LIB will not handle properly a "too fast" send (or too large string size).

Thanks for the suggestions.
I will investigate further as soon as I can, but I won't have access to my Due anymore for a few months. I won't be able to check how SerialUSB.write() behaves before long.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.