There is mainly 2 different ways to manage USB transmissions if the host is not processing data at the rate the MCU is sending it:
On one hand SerialUSB.write () can just discard packets and not block after a set timeout period, returning a value that indicates how many bytes could be fit in the TX buffer. The timeout period can be hard set, or could be modified to match a certain baud rate, so timeout only if the host is acknowledging packets at a slower rate.
On the other hand the SerialUSB.write()can block indifinitely until data can be queued for TX, and return only if it was successfully queued.
Please vote below, and add any extra comment you want to add in the thread.
https://github.com/rogerclarkmelbourne/ … .h#L66-L67
May it could simply be extented by providing an additional timeout to enabledBlockingTx() …
In other words, I would prefer an universal solution which cover all possible cases !
For applications where high Tx throughput is needed, it must be granted that the host is capable of that high speed.
Otherwise one should be able to select the desired behavior, blocking or not.
The program hangs..
The same potentially applies if the terminal is not open.
I chose the second option, I.e don’t block but return how much space is available, as it would allow the program to choose to block its self if necessary
However in hindsight, I should look to see what Serial.print returns in the rest of the Arduino world. Eg.on AVR and Due.
Please vote again as the change cleared the voting.
But so far it seems the best option is to make it configurable.
I will look at teensy and due code to see what they do, and that could be default, and then configurable to act the opposite.
I know I’ve mentioned this before, but ….
Can you also look at what happens in regard to DTR, beause AFIK our SerialUSB implementation does not currently behave the same way as the Due or the Teensy.
I think the difference is that we don’t put any data into the USB TX buffer is DTR is not set, (Host terminal is not active), but the other boards still put stuff into the TX buffer
Currently people have issues with Libmaple not working correctly with some terminal’s, who don’t set DTR by default.
i.e Normal Arduino boards work with those terminals but LibMaple does not.
SerialUSB TX behavior
https://github.com/arduino/ArduinoCore- … C.cpp#L264
/* only try to send bytes if the high-level CDC connection itself
is open (not just the pipe) – the OS should set lineState when the port
is opened and clear lineState when the port is closed.
bytes sent before the user opens the connection or after
the connection is closed are lost – just like with a UART. */
LineState contains 2 flags, DTR and RTS, besides baud rate and some other information.
The code checks if line rate != 0. In that case, it sends.
If lineState is 0, it doesn’t even buffer the data, just returns 0, and sets a flag.
If lineState !=0, then tries to send data, and returns a value indicating what how much it sent. I need to dig more in the code to see if it can successfully send part of what’s requested, but looks like it.
OK it seems like the low level driver will block until the ep is ready to take a packet. So the class is the one that will return 0 if the line is closed, but it the line is open, it blocks.
It doesn’t seem to do any kind of buffering like we do for TX, it goes straight to the endpoint buffer, which for bulk transmission and USB FS is max 64 bytes.
There is no timeout or anything like that. If the line is open, it will block, if the line is closed, it returns right away. But line open is not just having a host connected to USB, but rather having an application open the port.
I’m not sure what would happen if the line is setup, application open the port, then cable is removed, and from the comments in the code, whoever wrote the library doesn’t know either.
// TODO – ZE – check behavior on different OSes and test what happens if an
// open connection isn’t broken cleanly (cable is yanked out, host dies
// or locks up, or host virtual serial port hangs)
Even if we manage to detect the line open and close correctly, we may want to be able to not block for too long.
EDIT:
The RX behavior:
It will not ACK RX packets until it’s able to write them to the class buffer. So if the application is not reading, or not reading fast enough, it will block the host until there is space in the buffer. So on RX there shouldn’t be any drop, which I think is the most appropriate.
Configurable at runtime (blocking or non-blocking with small timeout)
+
if not set at runtime use a default configuration like: It should not block and return inmediately if there is no space in the buffer (return value indicates number of bytes queued for TX).
Any more votes or different opinions on the expected behaviour?
So, I have the same trouble than you and wait the end of the poll
-If USB is disconnected, just drop and return with no timeout, and not saving to buffer either. Return indicates 0 bytes were sent. This is how Arduino DUE works.
-If USB is connected, 2 possibly outcomes:
- by default try to queue to the buffer, and wait for a timeout period. Such timeout simulating the rate at which the port was opened (115200 if nothing was provided). If the bytes can’t be sent at that pace, return indicating how many were queued. If the sketch cares to check the return fine, if not, then data is just lost. This is more or less how a USART port would work. If puts the data in the output line, but doesn’t care if anyone listen.
- At runtime you can set the timeout to max (let’s say 0xFFFF), or bps rate to 0, and if so, the sending function will block indefinitely waiting until it can send the data. I think that still if the port gets disconnected from the host after the sketch is running, the TX function should just return, dont see a need to block in this case unless someone can explain it to me.
Does that fit everyone needs?
[victor_pv – Mon Sep 11, 2017 1:14 am] –
Does that fit everyone needs?
I am ok with that.
BTW my experiments with windows10/usbser.sys driver show that it has a 16K buffer. So if the host PC program opens the COM port, but does not read anything at all, the STM32 USB will be able to send 16K data instantly anyway, bypassing your device timeout.
[stevestrong – Mon Sep 11, 2017 8:29 am] –
Victor, I would not use timeouts at all, just return the nr of bytes successfully sent, because if systick is not used, then the timeout won’t work.
The possible problem with that is if you do a lot of Serial.print in a row, filling up the buffer. I don’t think anyone checks return values when they are trying to print information, but at the same time they expect that to be sent, and not lose a line here or there.
As I said if we allow to change it at runtime, we can make it that if timeout is 0, it doesn’t even look at systick, just runs once trying to send the data and return what’s successfull. That would allow for not using systick.
Let’s try to agree on how to manage the possibilities. I suggest using begin() since it’s already there. Imagine the following case (and add or modify to it until we all like it).
Serial.begin(); //No timeout, and no blocking. No guarantee of delivery. We can include some repeat loop in case the sketch is sending data at very fast pace. currently this results in a set timeout of 50ms in the libmaple F1.
Serial.begin(115200); // Use a timeout to approximate 115200 speed.
Serial.begin(0); // Block until it can write data to buffer (no timeout, just blocking), except if the port is totally disconnected (which you should have checked for with !Serial)
+1
Serial.begin(<baudrate>, bool blocking = 1 or 0); // 0 per default
basically use second parameter to specify the blocking/non-blocking option.
BTW, libmaple F4 USB serial has these 2 functions:
void usbEnableBlockingTx(void);
void usbDisableBlockingTx(void);
That is what I’ve said in the other thread several weeks ago.
usbEnableBlockingTx() could have a new argument for the timeout value, and 0 or -1 can indicate blocking forever…
Serial.begin(<baudrate>, bool blocking = 1 or 0); // 0 per default
begin(115200, 1);


