Serial Flush returns before last character is sent

I am using a serial port on a multipoint (RS485) line. This requires that the transmitter be turned on before sending data and off after the last byte is sent. I have found that serial1.flush() returns after the last byte has been removed from the buffer but before the byte is actually sent by the hardware. This results in the transmitter being turned off before the last byte is actually sent, and so the last byte gets corrupted. Is there some way to determine that the last byte has physically been sent by the hardware? BTW, I determined this behavior by adding a delay after the flush instruction. When I make the delay exactly equal to one character time, if works fine, but this is kind of a mickey-mouse solution. The is on a Raspberry Pi Pico board.

That's one of the reasons why flush() returns when the buffer is empty. flush() is a method of the Stream class which does not know about the sending hardware and consequently can not wait for the end of the hardware transmission.

Waiting for a character send time after flush() is a valid solution.

It would be, if it were coded inline. But the bus timing should be encapsulated in a function or method.

Meeting hardware limitations can only be "mickey mouse" in the implementation.

Or send one additional character before flush

flush seems to be implemented correctly, it waits for the TX shift register to be empty by waiting a BUSY bit to be clear, there is no better way to do it in software, so it might be a hardware issue

Hmmmm. I wonder, does the SR show empty before, or after the stop bit is sent?

From datasheet (p.432):

If this bit is set to 1, the UART is busy
transmitting data. This bit remains set until the complete
byte, including all the stop bits, has been sent from the
shift register

Well that seems ironclad. Guess there is a bug in the code...

Just use a delay based on the selected baud. Don't make "Better" the enemy of "Good Enough".

I never had an issue with this when using the Teensy processors, and when I was using AVR processors with C++ I would always wait for the transmission register to be empty, and it worked fine. I'm using the "Arduino" version for the pico, not the EarlePhilhower version, which may be a difference. I don't like the timing idea because it will vary with baud rate, which is subject to change in my implementation. I'm a newbie with Arduino, so I'm not sure how to go about getting at the hardware registers in this environment.

A guess, maybe because serial is interrupt driven, the ISR is just a client of flush(), so it may return before the ISR has completed the flush.

??

Of course it would. And if flush() behaved like you imagined and didn't return until the last stop bit was on the wire, then that would also depend on the baud. So, adding a one-character, baud-dependent delay doesn't really seem like much of a difference.

Well, not really. If it worked as intended, it would not return until the transmit register was empty, which means the last stop bit had been completely sent. That's how it's supposed to work. It doesn't depend on baud rate at all because the hardware takes care of that. Sending an extra character, as some have suggested isn't viable, because the receiver would then get a character fragment that it had to dispose of somehow. Since the receiving system has already been implemented, forcing it to change is not really an option. So, the options are to add a baud-dependent delay or test for the transmission complete in the hardware.

Is there some way for me to test this bit myself? If the flush function isn't doing it correctly, maybe I could test it directly???

Please post a reference for "how it's supposed to work".

Anyway, what's the big deal? Just write a wrapper function or class to implement the baud-dependent delay. Or, if using delay really bothers you, have your wrapper class poll the hardware per @guix 's suggestion. Then it would return as quickly as possible based on the hardware UART's status.

So, based on your suggestion, I wrote some code to go fetch the UART flag register directly. However, no matter how much data I send, I never see the BUSY bit being on. Perhaps there's something else that needs to be done, other than just reading that memory location. Don't know if memory caching may have some effect on it, or perhaps I need to disable interrupts while I'm reading it. Not sure at this point. In any case, I've decided to punt at this point and just build in a baud-rate based delay to account for one extra character time. Seems to work, although it may not be optimal.

After working this issue for a while I thought I would comment on what the solution was. In case you were wondering, the reason I need to turn the transmitter off as rapidly as possible is because I need to be able to receive the next incoming message, and the host driving this system has very fast turnaround time between receiving a message and transmitting the next one. This was the concern with adding extra delays. But after reviewing all the suggestions, it occurred to me that the problem was in the hardware design, which used a single output to both enable the transmitter and disable the receiver, and vice versa. The solution kind of slapped me in the face, which was: Just leave the receiver on all the time! A small change to the circuit board tied the receiver enable line to ground so it could receive constantly. Now I no longer have to be concerned about turning the transmitter off quickly, as long as it is off before the next message is completely received. Because this is a multipoint link, there may be other boards wanting to transmit, and only one can transmit at a time. Also, In order to avoid an in-line wait, I just started a timer and then turn the transmitter off when the timer expires. Thanks to all for the suggestions!

But then any characters you send, will also appear in your receive stream...

and allow to detect when the last byte of a message has been sent and the transmitter can be turned off :slight_smile:

Actually, they don't because the transmit and receive wires are separate pairs.

Don't what? Loopback is done on one pair.