Go Down

Topic: SerialUSB can block upon write, has no availableForWrite() method (Read 2577 times) previous topic - next topic

elegans

To avoid a blocking call to Serial.write() it is possible to call availableForWrite() to check if there is enough space in the serial buffer before attempting the write. This works great. Now I'm trying to so the same thing with the Due native USB port.

I'm try to avoid a blocking call to SerialUSB.write() but encounter the compiler error of:
error: 'class Serial_' has no member named 'availableForWrite'

Why is availableForWrite not provided? Are there any work arounds?

Thanks.


MorganS

I've only found this to be a problem when unplugging the USB from a Due that was previously writing to SerialUSB. Then it just gets stuck. In all other cases it simply isn't possible to fill up the SerialUSB buffer fast enough to cause a problem.

I seem to remember that there is a way to tell if SerialUSB is still connected but it's not as simple or easy as you might hope for. The usual while(!Serial); only works once and doesn't let you know if it's lost the connection.
"The problem is in the code you didn't post."

pjrc

Use Tools > Boards > Boards Manager...

Select the Arduino SAM Boards, and then look for the "Select version" drop down.  Use it to upgrade your SAM core library to version 1.6.9.

Then you'll magically find you do have availableForWrite().  You've simply been using an old version.

MartinYoung

Replying here because this is were a google search leads one.

If you continually try to write to SerialUSB and the data is not read by the host then eventually the write will block indefinitely.

The availableForWrite() call that's provided for SAMD always returns 63 (the size of the USB packet, minus 1). It doesn't matter if the write will block or not, always 63.

The SerialUSB class provides access to a method called dtr() while will normally be 1, but 0 is there is no serial connection. This is useful if the console is present on boot (so wait-for-connection worked okay) but it is later disconneced. You can use this to avoid making a blocking write.

Unfortunately there seems to be no way to detect that there is a connection but that the host is not consuming the data. In this case availableForWrite() will always return 63, and dtr() will always return 1, but the write will block regardless.

I cannot find any workaround for this, but would be happy to learn of one.

MorganS

So, plugged in to a PC, you open SerialMonitor and then close it - your Due is blocking after that?

I have been able to get availableForWrite() to show different numbers. It usually sends the packet so quickly that you don't get a chance to observe a half-filled packet.
"The problem is in the code you didn't post."

jrickard

We're having the same problem.

Run the code below on any Arduino Due.  It is basically BLINKY with some USBPrints to stuff some data out the serial port.

Power your board with 12v.  It will blink the LED of course.

You can plug USB into native port and remove it at will with no ill effect.

But IF you peek at the USB stream with Serial Monitor, then unplug the USB cable, the program locks up.  No more blinky.

I've tried Serial.dtr() and Serial.availableForWrite() to no avail.

Anyone have a solution for this?

Jack Rickard



Code: [Select]

char Buffer[500];
void setup() {
   pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(50);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(50);                       // wait for a second

  sprintf(Buffer, "This is a whole lot of shit.This is a whole lot of shit.This is a whole lot of shit.This is a whole lot of shit.\n");
  SerialUSB.print(Buffer);
  SerialUSB.print(Buffer);
  SerialUSB.print(Buffer);
  SerialUSB.print(Buffer);
}

ard_newbie


AdderD

A workaround: Insert a watchdog reset in your code. :)
Yes, that works but it's lame. The USB stack on the Due has no business locking up when you try to write and the  USB cord has gotten disconnected or the terminal program has closed. This is not really acceptable.

But, far from being just a complainer, I also fixed the problem. The issue is in LIBSAM. Which sucks because libsam is precompiled and everyone is using the compiled library that comes with board files. At any rate, UDD_Send which is a function in uotghs_device.c contains this little jem:

Code: [Select]

while( UOTGHS_DEVEPTISR_TXINI != (UOTGHS->UOTGHS_DEVEPTISR[ep] & UOTGHS_DEVEPTISR_TXINI )) {}


Yes, an infinite loop in the core USB sending function. NAUGHTY! If you close the terminal or pull the USB cord (obviously while also powering it from another source) this line will NEVER complete. The quick fix is to give it only a finite amount of time to wait and then give up. This works and the sketch can now try writing all it wants. But, depending on how long you set the timeout here it can waste a lot of time. Setting the timeout too long slows the program down. Setting it too short can cause writes to silently disappear.

I'm going to look to see if there is another register or status flag somewhere that I can read to see if things seem to be disconnected.

ard_newbie


In UOTGHS_CTRL  (datasheet page 1088):

VBUSHWC: VBus Hardware Control
The UOTGHS resets the UOTGVBOF output pin (PB10) when a VBUS problem occurs.


When you test the status register UOTGHS->UOTGHS_DEVEPTISR[ep], you could write instead:

while( UOTGHS_DEVEPTISR_TXINI != (UOTGHS->UOTGHS_DEVEPTISR[ep] & UOTGHS_DEVEPTISR_TXINI )) {
TimeOut++;

if ((TimeOut == XXX) || !(PIOB->PIO_PDSR & PIO_PDSR_P10)) break;
}
TimeOut = 0;

Go Up