Blocking write to Serial

Hi everyone. I'd like to ask a question related to Serial.write() and Serial.flush(). According to the documentation - Serial.write is not blocking and to wait for the data transmission you should call **Serial.flush(). **The latter must suspend the program execution until the output buffer is free. Unfortunately for me flush doesn't suspend the execution thread. Here is the code snippet:

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

void setup() {
    Serial.begin(9600);
    //Serial.setTimeout(10000);
}

void loop() {
  if (millis() - tmr >= 3000) {   // каждые 3 сек
    tmr = millis();
      digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
      delay(1000);
      int written = Serial.write("1234567890abcdefghi1234567890abcdefghi1234567890abcdefghi21345255566666666661111111112222222221234567890abcdefghi1234567890abcdefghi1234567890abcdefghi2134525556666666666111111111222222222\n");
      Serial.flush();
      Serial.write(String(written).c_str());
      Serial.write("\n");
      Serial.flush();
      if (written>=1){
     
        for (int i=0;i<10;i++){
        blinkWithBuiltInLed(300);      
        }
      }
      digitalWrite(LED_BUILTIN, LOW);
      delay(1000);

       }
  
}

I've tried to detach the USB cable and to detach the related serial port on the PC side, and to just ignore calling of bulk_transfer to simulate that the host is busy - built in LED continues to blink. So the main question - what should I do to obtain the suspension of thread?

I've tried this on 2 boards: Uno and Mega 2560. The behavior is the same. Also I've tried to analyze code of HardwareSerial but looks like the control itself is handled with the interruptions deep inside of micro-controller. At the same time if I add the artificial delay to the flush function - execution is suspended.

So any help is appreciated.

Serial.flush() just waits for the data to be clocked out. It doesn't care if there is anything reading the bits that are being sent. There is no 'flow control' so if the receiver is not reading the bits as they are being sent then the receiver will not get all of the characters. You can't detect that there is nothing receiving your characters.

1 Like

@johnwasser
thank you for the answer ! That's very interesting, so in this case on the host side you should be ready to the situation that you'll receive only "world" from the original "Hello world" message if you're too slow? :confused:

flush() is useful when TX must be enabled during transmission such as with half-duplex RS-485 communications. flush() returns after transmission is complete and it is safe to disable the transmitter output of an RS-485 chip.

as john said, there's no guarantee that the receiver was listening or accepted the transmission

And your first write likely WILL block, because the default Serial transmit buffer is typically only 64 chars, and you're printing almost 200 chars.

"blocking" usually applies to a task in a multi-tasking system that is put on the suspend queue waiting for some event to occur that is outside of its control

not some processing that requires a predictable and finite amount of time to complete.

RayLivingston:
And your first write likely WILL block, because the default Serial transmit buffer is typically only 64 chars, and you're printing almost 200 chars.

No, lamp is blinking, so there is no any delay in that. I've played with the buffer size and set it to 256mb and back to 64 - I've not seen any difference.

drongoong:
I've played with the buffer size and set it to 256mb and back to 64 - I've not seen any difference.

:o

gcjr:
"blocking" usually applies to a task in a multi-tasking system that is put on the suspend queue waiting for some event to occur that is outside of its control

not some processing that requires a predictable and finite amount of time to complete.

Yes, thank you, I understand that. I supposed that combination of Serial.write/Serial.flush provides the same behavior as in TCP/IP. So I've tried to implement some commands and responses, but looks like I have to implement one more layer for these purposes.

TheMemberFormerlyKnownAsAWOL:
:o

Sorry, I meant bytes, not Megabytes :))

...or even milli bits.

johnwasser:
Serial.flush() just waits for the data to be clocked out. It doesn't care if there is anything reading the bits that are being sent. There is no 'flow control' so if the receiver is not reading the bits as they are being sent then the receiver will not get all of the characters. You can't detect that there is nothing receiving your characters.

By the way, it would be great if somebody explain me this a little bit deeper - I thought when we send something via Serial we fill some nested TX buffer and some handler which processes this buffer using interruptions submits it to the host side. So if we don't have any ability to check that data was submitted correctly - looks like this handler doesn't follow USB protocol? Or what is going on where at this moment? I thought this is contrary to USB protocol and submitter should wait for the ACK response from the host even in case of bulk transfers? From another side AFAIK any action in USB is started on the host side. So if somebody explain me this situation I would be very pleasant.

Serial output has nothing to do with USB.

If you are using a serial to USB adapter to connect the Arduino to a PC, be aware that there is a microprocessor in that adapter doing all the work.

drongoong:
By the way, it would be great if somebody explain me this a little bit deeper

bytes are transmitted by a USART. the byte is written to an output register. the hardware prepends a start bit, outputs each bit of the byte, optionally appends a parity bit, 1 or 2 stop bits and sets a transmit complete flag after the last stop bit.

the Arduino serial bits are connected to a USB serial interface chip

Got it, thanks to everybody!

So to resume everything written - Serial.write and Serial.flush don't give any guarantee that the data was successfully transmitted to the host because they work with USART and that's not the responsibility of the latter. That's responsibility of another microcontroller tightly coupled with USB as I can see on my Arduino Mega - that's Atmel Mega 16u2. **Serial.flush **blocks execution if the interruptions has not passed the data to registers of another microcontroller. But looks like this chip is pretty "dummy" as it only works with the rate set on the USART (9600, 115200 etc.) and when we send control transfers from host to the device, these commands just set up the baud rate to transmit data from host to USART. So there is no any chance to make some custom firmware for this USB chip to enable guaranteed delivery, am I right?

lower level serial interfaces, including Ethernet are just responsible for putting data on the wires. higher level protocols such as TCP use acknowledgements to insure that the data was received correctly by retransmitting data a limited # of times due to a negative acknowledgement (NAK) or timeout.

Depending on the device you are communicating with you may be overthinking things a bit. Most serial interface devices have serial input and output buffers. So if you transmit data to the device it will be held in it's buffer while it is executing other tasks so the data will not be lost. Arduino devices have serial input buffers which can be tested with this sample code by sending data while the 10 second blocking delay is running.

#include <Arduino.h>

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println("Waiting");
  delay(10000);
  if(Serial.available()){
    char message[Serial.available()];
    memset(message, 0, sizeof(message));
    Serial.readBytesUntil('\r', message, sizeof(message));
    Serial.println(message);
  }
}

You'll see that if you send data to the device over the serial connection right after it says waiting it still receives the data, this is because the data you passed to it was held in the Serial Input buffer. So you really don't have to worry about the device missing the data unless you are exceeding it's serial input buffer before it can clear it.
The only alternative is to have the device send confirmation back via the serial connection or via IO lines that link the Arduino IC to the device.

iotrav:
Depending on the device you are communicating with you may be overthinking things a bit. Most serial interface devices have serial input and output buffers. So if you transmit data to the device it will be held in it's buffer while it is executing other tasks so the data will not be lost. Arduino devices have serial input buffers which can be tested with this sample code by sending data while the 10 second blocking delay is running.

#include <Arduino.h>

void setup() {
 Serial.begin(115200);
}

void loop() {
 Serial.println("Waiting");
 delay(10000);
 if(Serial.available()){
   char message[Serial.available()];
   memset(message, 0, sizeof(message));
   Serial.readBytesUntil('\r', message, sizeof(message));
   Serial.println(message);
 }
}



You'll see that if you send data to the device over the serial connection right after it says waiting it still receives the data, this is because the data you passed to it was held in the Serial Input buffer. So you really don't have to worry about the device missing the data unless you are exceeding it's serial input buffer before it can clear it.
The only alternative is to have the device send confirmation back via the serial connection or via IO lines that link the Arduino IC to the device.

Well I think your statement is correct for cases when you work with Arduino via something like serial COM port, but when you work with it via LibUsb or using Android standard tools like UsbDeviceConnection - looks like you should implement your own buffers. Because for case with LibUsb - I've noticed the data losses when I haven't read the data in time.

Hm, keeping in mind the thought from @iotrav I've decided to clarify one more moment - is there any possibility to specify TTL of data on host's side (hardware level)? Possibly I should just make some control transfer?

What is this device you are communicating with via Serial interface from the Arduino IC? Is it another device you are programming or is it a device you have no developmental control over? In an ideal world you would send data to the device and then it would send back an AOK acknowledgment. Personally I think it's poor design to not send back acknowledgements when data is received over Serial.

I'm not sure it's possible to set the TTL on the serial data buffer in Arduino but honestly I never looked into it.