Serial.println() command should be "non blocking" ? (some questions)

from the release notes:
"Serial transmission is now asynchronous - that is, calls to Serial.print(), etc. add data to an outgoing buffer which is transmitted in the background."

As of arduino 1.0 the Serial.println() command should be "non blocking" the following code in the main loop than, right ?

The only condition that it may block is, when you write bytes into the buffer faster than the UART can put it out with its transmit speed (e.g. 9600 bps), is that right ?

so how big is this buffer ? - is it depending on my hardware (MCU) and the used serial port (USB or hardware)

or is it just a buffer in RAM which size is defined within the serial-library ?

This "transmission in the background" mentioned above is done by interrupts, right ?

(so it would be a bad idea to disable interrups right after a call of Serial.println() ?)


what about the Serial.read() (and related) -functions, are they somehow "asynchronous" (non blocking) as well ?

Dirk67:

As of arduino 1.0 the Serial.println() command should be "non blocking" the following code in the main loop than, right ?

Right.

Dirk67:

The only condition that it may block is, when you write bytes into the buffer faster than the UART can put it out with its transmit speed (e.g. 9600 bps), is that right ?

Not fully correct.
You can write into the serial output buffer as fast as you can, until the output buffer is "full". If the output buffer is "full" and you want to write additional characters, the application begins to behave like that:

  • if one character is to be sent from the application to a full output buffer
  • the application is blocked until one character is sent from the output buffer
  • then the application writes another character to the outout buffer and becomes blocked again
  • the last two steps are repeated until the application has stuffed all characters into the output buffer
  • your application is blocked, until all characters are written into the output buffer
  • then your application runs again, and the characters in the buffer will be sent in the background

Dirk67:

so how big is this buffer ? - is it depending on my hardware (MCU) and the used serial port (USB or hardware)

or is it just a buffer in RAM which size is defined within the serial-library ?

The buffer size is defined in the Serial library. I think it is 64 bytes with 8-bit Atmega controllers. But as it is a FIFO buffer, the buffer can only hold "Size minus one" characters in the buffer, that is 63 characters maximum.

Dirk67:

This "transmission in the background" mentioned above is done by interrupts, right ?

(so it would be a bad idea to disable interrups right after a call of Serial.println() ?)

Yes and yes.
While interrupts are blocked, sending bytes from the Serial output buffer is also blocked.

wow, thanks for this nice answer !

what about the Serial.read() (and related) -functions then,
are they somehow "asynchronous" (non blocking) as well ?

or do I have to make some extra efforts
e.g. like described here --> Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking

are they somehow "asynchronous" (non blocking) as well ?

What did you read on the reference page?

Dirk67:
what about the Serial.read() (and related) -functions then,
are they somehow "asynchronous" (non blocking) as well ?

Serial.read() is non blocking as well and will never wait.

But the return value is an "integer", and in case the input buffer is empty, the return value is -1.

So you can use Serial.read() with an empty input buffer, if you read the return value into an integer and compare what you get.

But while it is possible, it is not very common in Arduino programming to do so. More common is the use of the available() function and then read the value into a byte or a char.

Possibly with a code like:

if (Serial.available())
{
  byte b=Serial.read();
  // your code here
}

Instead of that you could also use:

int i= Serial.read(); // return value is -1 if input buffer is empty
if (i>=0)
{
  // your code here
}

thanks again ! very helpful @jurs

The examples in Serial Input Basics will normally read data from the input buffer far faster than it can arrive so that the 64 byte buffer size is largely irrelevant.

Note that the array in the examples is only 32 chars long - but that can be changed to suit your requirement.

...R

your Tutorial is in fact very nice and helpful, thanks ! :slight_smile:
(I think it should be put on the arduino.cc-page under "learning" -> "tutorials" or "hacking" or "playground")

so all of this Arduino functions are blocking:
Serial.parseInt()
Serial.parseFloat()
Serial.readBytes()
Serial.readBytesUntil()

OK one last question regarding your statements above,
about the possibilities when "Serial.print()" might be be blocking.

I think I got such a blocking condition with my 32U4 based board (like a micro) [serial via USB],
when the USB-cable is disconnected during the execution of the loop.
(within the loop only every 1 second it is written something to the output buffer)
Is that possible ?
does the output buffer get completely "full" when disconnecting the USB-Cable ?

if yes,
how can I "preventively" empty the output buffer before writing new chars to it (at the beginning of my 1 second interval) ?
(serial.flush doesn't do the trick I think - it only waits...)

or the other way around:
can I check with Serial.availableForWrite() if there are remaining chars in the output buffer
from the last use of Serial.print one second ago... ?

(I tried with if(Serial) but that is obviously always TRUE during runtime [?] )

Dirk67, I have read this topic, and everything was going okay, until you disconnect a ATmega32U4 board. That is indeed a bad thing to do.
What is on the other side ? I think a computer, but what program is running the serial port on the computer ? Perhaps the Arduino serial monitor ? If the connection is lost, also the program on the computer needs to be re-initialized as well.
Only a normal UART pushed out the bytes at a certain baudrate, the ATmega32U4 usb-serial is not like that, it doesn't even use a real baudrate, the speed of the data is very fast.

To be sure that your code continues, you need a usb-serial board and connect that to pin 0 (RX) and pin 1 (TX). There is probably a trick to discontinue the SerialUSB of the ATmega32U4, but I don't know if there is a valid soluation that will also work with future libraries.

Do you have a test sketch ?

Koepel,

i tried this now:

Dirk67:
can I check with Serial.availableForWrite() if there are remaining chars in the output buffer
from the last use of Serial.print one second ago... ?

by inserting the following if statement

int actual_buffersize = Serial.availableForWrite();
if (actual_buffersize >= MCU_serial_output_buffersize) {

befor outputting anything.

you can see my example program below.
there are 2 intervals within the loop (there's no "delay()" used anywhere).
the first interval is every 1 second making some adc conversion some calculating an some serial printing ...

the second independent interval is every 100 millisecond toggeling an LED.

by looking at the LED I can see when the loop is getting blocked or delayed.
The LED should blink with the same frequency under any conditions...

before inserting the "buffer-empty check" (additional if statement see above) the loop got heavily stucked/delayed, when disconnecting the USB,
meaning the LED blinking very slow and then fast again, very "irregular" or went off for one second...
(the loop is never stopped completely)

Now with the "buffer-empty check" I can disconnect and connect the USB whenever I want without a visible effect, thats great.

But there are still some glitches visible on the LED ("stuttering" LED frequency),
when I am closing the Arduino serial monitor without disconnecting the USB Cable.
So obviously there's not only the "buffer get full" issue but still another thing that causes some blocking...
(?)

// CONSTANTS ------------------
// (constants won't change during runtime)
const int TOG_Pin = 10;      // the number of the LED pin
const int analogIn_ref = A5;     
const int analogIn_2 = A3;
const long vorwiderstand_2 = 1200;
const long interval_1 = 1000;           // interval at which to analog read and print out [ms]
const long interval_2 = 100;          // TOG intervall [ms]
const int MCU_serial_output_buffersize = 64; //wie groß ist der output buffer der seriellen schnittstelle bei dieser MCU
// -----------------------------



// VARIABLES ------------------
// (Variables will change during runtime)
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long currentMillis = 0;
unsigned long previousMillis_1 = 0;        // will store last time LED was updated
unsigned long previousMillis_2 = 0; 

int sensorValue_ref = 0;       // variable to store the value read
int sensorValue_2 = 0;

long divisor_2 = 0;
long Ohm_2 = 0;
// -----------------------------
 




// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  analogReference(INTERNAL); //2,56 V bei Leonardo/micro
  pinMode(TOG_Pin, OUTPUT);   // sets the (TOG)-pin as output
}




// the loop routine runs over and over again forever:
void loop() {
  
  currentMillis = millis(); // aktuellen zeitstempel erfassen

  if (currentMillis - previousMillis_1 >= interval_1) {
    // save the last time you blinked the LED
    previousMillis_1 = currentMillis;
  
        // die 2 Eingänge einlesen (0..1023):
        sensorValue_ref = analogRead(analogIn_ref); 
        //analogRead dauert ca 0.1ms = 100µs = 0.0001s
        sensorValue_2 = analogRead(analogIn_2);
      
      
      
        // Umrechnung in Ohm (unabhängig von den Schwankungen der Sensorspannung):
        divisor_2 = ((sensorValue_ref * 2) - sensorValue_2);
        if (divisor_2 > 0){
          Ohm_2 = (sensorValue_2 * vorwiderstand_2) / divisor_2;
        } else {
          Ohm_2 = 0; // Fehler
        }  
        
        int actual_buffersize = Serial.availableForWrite();
        //nur ausgabe wenn der buffer leer ist vom letzten mal, sonst blockiert das programm !!!
        if (actual_buffersize >= MCU_serial_output_buffersize) { 
          // print out the value you read:
          Serial.print("buffersize = ");
          Serial.println(actual_buffersize);
          Serial.print("analog Input ref = ");
          Serial.println(sensorValue_ref);
          Serial.println("---------------");
          Serial.print("analog Input 2 = ");
          Serial.println(sensorValue_2);
          Serial.print("Ohm 2 = ");
          Serial.println(Ohm_2);
          Serial.println("====================");
        }
       
       
  }
  // Ende Intervall 1




  // TOG-Intervall 250ms
  currentMillis = millis(); // aktuellen zeitstempel erfassen
  
  if (currentMillis - previousMillis_2 >= interval_2) {
    // save the last time you blinked the LED
    previousMillis_2 = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(TOG_Pin, ledState);
  }
  // Ende TOG-Intervall


}

When I add all the text together, it will be about 96 characters. That means that if 64 bytes are available, you try to get 96 character into the buffer. Try to reduce the text, so the total will be less than 64.

"buffersize" -> "size" ?
"analog Input ref = " -> "ref" ?
"analog Input 2 = " -> "inp 2" ?

You could try a single currentMillis = millis() at the beginning of the loop(). I'm not sure if that has any influence, but it makes more sense to me to use just one.

The led uses a second currentMillis = millis(), and that might get delayed. When you set the new previousMillis to the currentMillis, any delay will always be added and the resulting frequency is lower.

Take care that the previousMillis never gets larger than currentMillis. But it will work okay in this example:

void loop()
{
  currentMillis = millis();          // just one currentMillis for the whole loop() function.

  if( currentMillis - previousMillis_1 >= interval_1)
  ...


  if( currentMillis - previousMillis_2 >= interval_2)
  {
    previousMillis_2 += interval_2;    // Keep steady pace, regardless of delay by code.
    ...
  }

I didn't test your sketch :frowning: because I have to find a power supply to keep the Leonardo going when it is disconnected from the USB.

thanks for your tips .

I am really not an expert in programming,
but I digged some deeper into the arduino source code and I think (just my guess)
that the statement Serial.print() is "non-blocking" is only true for normal MCU without "serial over USB" (CDC) ?

if you look into the file "CDC.cpp" one can find the following function:

size_t Serial_::write(const uint8_t *buffer, size_t size)
{
 /* 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. */
 
 // 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)
 if (_usbLineInfo.lineState > 0) {
 int r = USB_Send(CDC_TX,buffer,size);
 if (r > 0) {
 return r;
 } else {
 setWriteError();
 return 0;
 }
 }
 setWriteError();
 return 0;
}

you can see from the comments that the case of unexpected detachment of an endpoint is IMHO not properly solved...

In addition to that the used function "USB_Send()" itself is unfortunately blocking (!)
(and it has a huge "hard-wired" time-out of 250ms ...
you can see it in the file "USBCore.cpp":

// Blocking Send of data to an endpoint
int USB_Send(u8 ep, const void* d, int len)
{
 if (!_usbConfiguration)
 return -1;

 int r = len;
 const u8* data = (const u8*)d;
 u8 timeout = 250; // 250ms timeout on send? TODO
 while (len)
 {
 u8 n = USB_SendSpace(ep);
 if (n == 0)
 {
 if (!(--timeout))
 return -1;
 delay(1);
 continue;
 }

 if (n > len)
 n = len;
 {
 LockEP lock(ep);
 // Frame may have been released by the SOF interrupt handler
 if (!ReadWriteAllowed())
 continue;
 len -= n;
 if (ep & TRANSFER_ZERO)
 {
 while (n--)
 Send8(0);
 }
 else if (ep & TRANSFER_PGM)
 {
 while (n--)
 Send8(pgm_read_byte(data++));
 }
 else
 {
 while (n--)
 Send8(*data++);
 }
 if (!ReadWriteAllowed() || ((len == 0) && (ep & TRANSFER_RELEASE))) // Release full buffer
 ReleaseTX();
 }
 }
 TXLED1; // light the TX LED
 TxLEDPulse = TX_RX_LED_PULSE_MS;
 return r;
}

the next thing is:
the statement that Serial.flush() is just waiting of outgoing serial data to complete is IMHO wrong for MCU with "serial over USB" (CDC).
as you can see in the file "CDC.cpp":

void Serial_::flush(void)
{
 USB_Flush(CDC_TX);
}

and then in the file "USBCore.cpp"

void USB_Flush(u8 ep)
{
 SetEP(ep);
 if (FifoByteCount())
 ReleaseTX();
}

I think Serial.flush() does in fact flushing the TX Buffer immediately when using arduino boards with "serial over USB" (?)

All the code is online.

CDC.cpp : https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/CDC.cpp

USBCore.cpp : https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/USBCore.cpp

At least it is in the source code that it is not fully tested what happens when the cable is yanked out.
As far as I can tell from the source code, the flush does indeed clear the TX buffer, but I did not test it.

If you want it to be very reliable, you have no other choice but to use the normal UART and a usb-serial converter. The USB part of the ATmega32U4 is very complex. I don't understand it. Perhaps it might take a few years before it is updated to be more reliable.

I made some progress by controlling the USB pad status
to see if the USB cable is connected
and the Serial.dtr() status
to see if the PC/Terminal is ready or not.

The only remaining thing that I don't know is
how to stop the serial Serial.print() command continuing to try to fill up the USB output buffer by firing interrupts in the background ?

(So this question is about to use the Serial.print() command with a varibale lenght string that can have more then 64 bytes sometimes)

From my understanding I can give one or several Serial.print() commands (much) more bytes then the 64bytes of the TX output buffer,
and then it will fill up an internal buffer (?) (which / where / how to empty ?)
and then in the background via interrupts it will fill / try to fill up the USB TX output buffer (on a scheduled basis).

is that right ? --> compare here --> Teensyduino: Using USB Serial with Teensy on the Arduino IDE --> "USB Buffering and Timing Details" -> "Transmit Buffering"

This works pretty good and in case of USB CDC really fast (only some ms for every 64 bit)
as long as the USB stays connected an the PC/terminal program is ready.

If I want to stop this background sending activities immediately (in case of USB disconnect/not ready), how can I manage that ?
I tried Serial.flush() and Serial.end() but obviously it is not enough, it only clears the 64bit TX output buffer,
but I think in the background some interrupt are still firing* and trying to put the remaining bytes from the last Serial.print() commands into the USB TX output buffer (?)

*(I think so, because my timed loops (see my example sketch above) are heavily disturbed when closing a terminal program on the PC...
but they are not disturbed when simply unplugging the USB-Cable, obviously this second case is handled correctly)

So how can I stop this interrupts or empty the "internal buffer of the Serial.print() command" immediately ?

You encountered yet another problem, the Serial.end() should stop everything at that very moment, but the function is empty :frowning:
Therefor I don't know how to stop SerialUSB.

I don't know if the ATmega32U4 has a large buffer. Most Teensy boards use a ARM processor.
The buffer should be created when the USB descriptor is used to create a new USB device, but I can't find it.

I can find issues like yours : Leonardo fails to send exactly 64 bytes. · Issue #3946 · arduino/Arduino · GitHub

I doubt if SerialUSB can be reliable when the connection fails, and if you find a way, it might change with an update of the library.

I THINK (and I haven't looked at the code) that with USB, all interrupts are triggered by the host (PC.) The host USB controller essentially polls the device (Arduino) every ms or so, and if there is data buffered up, it is copied from the "Serial" buffer to the USB buffer and sent off in a packet (essentially "instantly.")

If you pull out the USB cable, obviously these USB polls will stop. I'm not sure if the Arduino chip can detect that the USB has disconnected - it can certainly decide that nothing has happened in a while and time out, but that's not quite the desirable behavior. The 32u4 CDC basic code looks like:

size_t Serial_::write(const uint8_t *buffer, size_t size)
{
    /* 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. */
    
    // 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)
    if (_usbLineInfo.lineState > 0)    {
        int r = USB_Send(CDC_TX,buffer,size);
  :

and USB_Send looks like:

//    Blocking Send of data to an endpoint
int USB_Send(u8 ep, const void* d, int len)
{
	if (!_usbConfiguration)
		return -1;

	int r = len;
	const u8* data = (const u8*)d;
	u8 timeout = 250;		// 250ms timeout on send? TODO
	while (len)
	{
		u8 n = USB_SendSpace(ep);  //get space available
	:
		if (n > len)
			n = len;
		{
		:
		
			{
				while (n--)
					Send8(*data++);
			}
			if (!ReadWriteAllowed() || ((len == 0) && (ep & TRANSFER_RELEASE)))	// Release full buffer
				ReleaseTX();
		}
	:
	return r;
}

There are a lot of "TODO" comments in there, so I suspect that detecting failure of an established USB connection is ... not done yet.

westfw:
...and if there is data buffered up, it is copied from the "Serial" buffer to the USB buffer and sent off in a packet (essentially "instantly.")...

yes, this is what I am talking about,
but where is this "Serial" buffer and can I empty it immediately from my running sketch,
this is all I need to know...

I never knew, that all this USB stuff is implemented so poor within the arduino core :frowning:

It looks like it is using the dedicated (?) USB buffers. (the send8() function mentioned above writes to an endpoint data fifo, which it looks like is automatically maintained by the HW.) I assume that when it's full, USB_SendSpace() will return 0, which causes (in code that I didn't quote) USB_Send() to block for up to 250ms (using delay() !)
That in turn will cause the write() function to call setWriteError(), but I can't see that anything actually checks the error status of a device, so it will abort that print OK, but try again with the next print (blocking for another 250ms.)

I don't know if actually flushing the USB buffers is important - presumably if the cable is ever plugged back in, the whole port will re-enumerate and all the FIFOs will get reset. What you DO need to do is have the write() function recognize that things have gone wrong, and have it stop trying to write more data. That might be as easy as using getWriteError() (if you can put up with one 250ms timeout.)

@westfw: very interesting

westfw:
I assume that when it's full, USB_SendSpace() will return 0, which causes (in code that I didn't quote) USB_Send() to block for up to 250ms (using delay() !)
That in turn will cause the write() function to call setWriteError(), but I can't see that anything actually checks the error status of a device, so it will abort that print OK, but try again with the next print (blocking for another 250ms.)

maybe that would explain, why my blink loop with millis() get so disturbed.
The more bytes (> 64byte) I write with Serial.print() and then interrupt* the connection, the more and longer blocking problems occur in my loop with millis()...

westfw:
...presumably if the cable is ever plugged back in, the whole port will re-enumerate and all the FIFOs will get reset.

*as I wrote before: plugging in and out the cable is not a problem any more.
(astonishingly this works without any problem with my example sketch on the page before)
The problem occurs, when stopping a terminal on the PC (Win10).
And really bad blocking problems occur (and remain), when I close the arduino terminal and let the arduino IDE opened.

westfw:
What you DO need to do is have the write() function recognize that things have gone wrong, and have it stop trying to write more data. That might be as easy as using getWriteError() (if you can put up with one 250ms timeout.)

yes OK,
but when getWriteError() throws an error there may be still a lot of bytes in the pipe
(because of using Serial.print() with more than 64 bytes (TX buffer size)),
so how can I "stop it trying to write more data" since this is IMHO still done (or tried) by interrupts in the background (?)
That leads directly to my question before

but where is this "Serial" buffer and can I empty it immediately from my running sketch,
this is all I need to know...

where I ask not flushing the TX-Buffer (64byte) but some "intermediate buffer" of the stream class
(when using Serial.print() with much more than 64 bytes)

or you think there is no such "intermediate buffer" at all ?
(there's still a lack of knowledge/understanding the code right on my side ... :-/ )

[edit]
I just took a look on the teensy solution (teensy 2.0 with 32U4),
and it looks much more sophisticated to me ...
--> cores/usb_serial at master · PaulStoffregen/cores · GitHub
see for example the usb_serial_class::write() function
--> cores/usb_serial/usb_api.cpp at master · PaulStoffregen/cores · GitHub

b.t.w. his TRANSMIT_TIMEOUT is 25ms instead of 250ms
and it seems that all the USB stuff is terminated in a proper way then ...