[solved] How do I detect when TRANSMIT (TX) buffer is full (or empty)?

My sketch is sending (not receiving) far more data than will fit into the 64 byte _tx_ring_buffer within HardwareSerial, and the baud rate is slow enough that I'll easily overrun the buffer. I cannot change the baud rate, modifying the Arduino core to bump up the buffer size is not an option.

I'd like to know when _tx_ring_buffer is full (and empty) so my app can go do other work, instead of blocking or overrunning. It seems like such a simple task, but I don't see any public methods, members, or events that tell me the state of the TX buffer. There is lots of methods for checking available data, contents, etc of the RX buffer, but none that I can find for the TX buffer. Surely I'm overlooking something.

Right now my kludge work-around is to shove 64 bytes into the buffer and then sleep the current thread (technically letting other threads do their thing) for an estimated time for HardwareSerial to pump the TX buffer dry at the given baud rate. Repeat until all data is transmitted.

So, how do I detect when the TX buffer is full (or empty)?

Any help would be appreciated.

Kindly share your sketch, so it could be easier to understand.

Try this:

struct ring_buffer
{
  unsigned char buffer[64];
  volatile unsigned int head;
  volatile unsigned int tail;
};

extern ring_buffer tx_buffer;

void setup() {
    Serial.begin(2400);
    Serial.println("here's some stuff to print! Have fun!");
    Serial.print("head: "); Serial.println(tx_buffer.head);
    Serial.print("tail: "); Serial.println(tx_buffer.tail);
    int diff = (tx_buffer.head - tx_buffer.tail) % 64;
    Serial.print("diff: "); Serial.println(diff);
}

void loop() {

}

I don't think there is a method to detect transmit buffer being full, that is why it's a blocking function when full so as to 'throttle' your serial write commands making it impossible to 'overrun' the transmit buffer. Short of hacking the serial library to add a function to see how full the transmit buffer is on command I can't think of anyway other then keeping track in your thread of how much you are sending out per relative to the character rate of the baud rate your using.

Lefty

WizenedEE, your example works great for ring_buffers that I instantiate, but the problem is that there is a private ring_buffer (within HardwareSerial) that I can't access without invasive hacks.

Lefty, thanks for that. I was sort of coming to that conclusion, but hoping to be wrong. It seems a tremendous oversight to not have a way to detect a potential block or overrun.

Is it possible I could "sense" the empty buffer indirectly by monitoring the enabled state of the TX interrupt? If so, where is the wiring source that (de)attaches the hardware tx interrupt? Would be nice to use the same #ifdef logic so my code is portable to other 'duinos.

zoidicus:
WizenedEE, your example works great for ring_buffers that I instantiate, but the problem is that there is a private ring_buffer (within HardwareSerial) that I can't access without invasive hacks.

Did you actually try it? If you run this sketch:

struct ring_buffer
{
  unsigned char buffer[64];
  volatile unsigned int head;
  volatile unsigned int tail;
};

extern ring_buffer tx_buffer;

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

void loop() {
    unsigned long starttime = millis();
    Serial.print("Starting at: "); Serial.println(starttime);
    Serial.println("here's some stuff to print! Have fun!");
    int diff = (tx_buffer.head - tx_buffer.tail) % 64;
    Serial.print("diff: "); Serial.println(diff);
    unsigned long midtime = millis();
    while (tx_buffer.head != tx_buffer.tail);
    unsigned long endtime = millis();
    Serial.print("before the while: "); Serial.println(midtime);
    Serial.print("after the while: "); Serial.println(endtime);
    delay(2000);
}

it waits for about 160 milliseconds in the while loop, which is about how long it takes to print to my screen.
The "extern" means it's accessing the "tx_buffer" in HardwareSerial.cpp
If you're on a mega, it has a different name though.

It would be useful for HardwareSerial to have a method that does this though.

WizenedEE:
Did you actually try it?
It would be useful for HardwareSerial to have a method that does this though.

um....no :blush: I thought I had, but I didn't have the extern. I blame "anti-knowledge" bleeding over from c#. :roll_eyes:
...
and yes

And on another tact I found I should be able to query the interrupt enable flag like this...
bool USART_1_DataRegisterEmptyInterruptEnabled = ((UCSR1B) & (1 << (UDRIE1))) >> (UDRIE1);
...but of course that is specific to serial 1 on the mega1280 family.

I'm not fond of either, because both are prone to breakage if the library changes. At least I have something better than "well just wait a while".

Thank you