Serial write() question.

In the documentation I read..

Serial.write(buf, len)

write() will return the number of bytes written, though reading that number is optional.

If I drop in a buffer of say.. 100 bytes and it returns 20, what does that mean?
It sent the first 20 and to bad that's all you get?
Or I seen the first 20, come back later and it'll be all sent?
Or do I wait for a "long time" and it tells me it send all 100?
Where do I find out what's going on?

I ask because I'l like to write some code that sends up to 256 bytes in the background. It would be nice to not wait around for the complete send. If I can send what I can now, and come back later to send some more, that would be perfect.

Thanks!

-jim lee

I ask because I'l like to write some code that sends up to 256 bytes in the background.

You'll have to increase the size of the transmit buffer, otherwise the write will block, and not send in "background"

How do I find out how many bite are in the transmit buffer? I can then just top it off and be on my way.

-jim lee

The TX buffer size is 64 bytes.

64 bytes, ok. Is there any call I can make to know how many are left? Is this what the write() function is telling me? How many went into the transmit buffer?

Thanks everyone.

-jim lee

Have you ever come across a situation where the number of bytes returned by "write" didn't equal the number of bytes you sent it?

I haven't written the code yet so no. I was trying to figure out how it all works so I could write it. But I'm and not getting it from the reference stuff. For example, the Stream class reference seems to think you only ever want to read from streams, there's no mention of write() in there. At least not that I saw.

So I ask you guys. I wonder how did larryd knew there is a 64 byte buffer? I've not seen any mention of any output buffers in the reference.

-jim lee

So I ask you guys. I wonder how did larryd knew there is a 64 byte buffer?

Probably because larryd went for a stroll around the source code.

Most other people don't care enough.

jimLee:
I haven't written the code yet so no. I was trying to figure out how it all works so I could write it. But I'm and not getting it from the reference stuff. For example, the Stream class reference seems to think you only ever want to read from streams, there's no mention of write() in there. At least not that I saw.

So I ask you guys. I wonder how did larryd knew there is a 64 byte buffer? I've not seen any mention of any output buffers in the reference.

-jim lee

It always sends what you tell it to send. The reason it returns the number of bytes is just as a convenience. It has nothing to do with possibly not sending it all. If you are calling some function or accessing some variable to get the length of the buffer then you don't have to do that twice. write will just pass the information through to you.

99.99% of the time it is totally useless because you already know how many bytes you told it to send. But what else you gonna have it return?

Say you have some class that has a buffer and a method to get the number of bytes currently in the buffer. Then in your code you need to send that buffer and you also need to know how many bytes you sent.

howMany = Serial.write(myInstance.getBuffer(), myInstance.getLength());

is nicer than.

howMany = myInstance.getLength();
Serial.write(myInstance.getBuffer(), howMany);

Especially if this buffer is coming from something interrupt driven and we're trying to keep this short in some critical section.

So write blocks 'till its all sent. This is why I would like to know how much room is left in the buffer so I could just top it off and be on my way.

Is there any way to find out how much room is left in the tx buffer?

-jim lee

Did you consider that your question might have already been answered?

RTFM early, RTFM often.

availableForWrite()

I'm blind, I must be. I read through that over and over, never seen that. Just blew right past. Or maybe someone added it while we were talking?

Anyway.. Thank you!

Better to feel dumb and satisfied than smart and frustrated!

-jim lee

Delta_G:
It always sends what you tell it to send. The reason it returns the number of bytes is just as a convenience. It has nothing to do with possibly not sending it all.

This is true for the HardwareSerial class which uses the built in UART, but there can be write() errors for other types of h/w like Wire/I2C. Also, I'm not sure what happens on other interfaces like the virtual serial connection on boards like the Leonardo or Teensy that use the USB interface for the Serial object.
I'm guessing that certain errors can cause a pre-mature return before all the characters are sent.

--- bill

bperrybap:
This is true for the HardwareSerial class which uses the built in UART, but there can be write() errors for other types of h/w like Wire/I2C. Also, I’m not sure what happens on other interfaces like the virtual serial connection on boards like the Leonardo or Teensy that use the USB interface for the Serial object.
I’m guessing that certain errors can cause a pre-mature return before all the characters are sent.

— bill

It could work that way. And it might be cool if it did. BUT :frowning:

From Wire.cpp:

size_t TwoWire::write(const uint8_t *data, size_t quantity)
{
  if(transmitting){
  // in master transmitter mode
    for(size_t i = 0; i < quantity; ++i){
      write(data[i]);
    }
  }else{
  // in slave send mode
    // reply to master
    twi_transmit(data, quantity);
  }
  return quantity;
}

It just returns whatever you passed in to it with no regard for whether or not there were errors.

Though it is true that different classes all implement their own write() method so you could create a blocking write which waits to see if there is any error in sending. But it would have to be a blocking function. It couldn’t just be loading a buffer for an interrupt like Serial or Wire.

And just for completeness since Serial was the actual topic in the OP...

From HardwareSerial.cpp :

size_t HardwareSerial::write(uint8_t c)
{
  _written = true;
  // If the buffer and the data register is empty, just write the byte
  // to the data register and be done. This shortcut helps
  // significantly improve the effective datarate at high (>
  // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown.
  if (_tx_buffer_head == _tx_buffer_tail && bit_is_set(*_ucsra, UDRE0)) {
    *_udr = c;
    sbi(*_ucsra, TXC0);
    return 1;
  }
  tx_buffer_index_t i = (_tx_buffer_head + 1) % SERIAL_TX_BUFFER_SIZE;
	
  // If the output buffer is full, there's nothing for it other than to 
  // wait for the interrupt handler to empty it a bit
  while (i == _tx_buffer_tail) {
    if (bit_is_clear(SREG, SREG_I)) {
      // Interrupts are disabled, so we'll have to poll the data
      // register empty flag ourselves. If it is set, pretend an
      // interrupt has happened and call the handler to free up
      // space for us.
      if(bit_is_set(*_ucsra, UDRE0))
	_tx_udr_empty_irq();
    } else {
      // nop, the interrupt handler will free up space for us
    }
  }

  _tx_buffer[_tx_buffer_head] = c;
  _tx_buffer_head = i;
	
  sbi(*_ucsrb, UDRIE0);
  
  return 1;
}

And we can see at the end there it just returns 1 for write no matter what. And then from Print.cpp :

size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    if (write(*buffer++)) n++;
    else break;
  }
  return n;
}

The return value from write gets treated like a boolean. So 0 and 1 are the only options there. Otherwise the buffer version of write is just adding up the return values from all the individual calls to the single byte version.

Delta_G:
It could work that way. And it might be cool if it did. BUT :frowning:

oops. Totally forgot that Wire buffers everthing in write() and then does a blocking transmit in endTransmission()

And just for completeness since Serial was the actual topic in the OP...

But we don't really know what board/core is being used.
Boards like the Leonardo or Teensy don't use HardwareSerial for the Serial object as the serial interface uses USB.

If using Leonardo, write() can fail. It uses the USB CDC code which is much more complex than a simple uart.

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;
}

The behavior of the Serial object write() code can vary depending on what hardware is being used for the Serial object which depends on the Arduino board being used.

--- bill