For reasons (explained at the end of the post) I need to have a pin change state immediately as the UART finishes outputting a set of bytes.
I'm using pyserial & pigpiod on a RPi3B+ and bluetooth is disabled so I'm using the 'good' serial chip at the moment.
The 'obvious' solution is simply
serial.write("my_bytes") pi.write(ASSERT_PIN, 1)
but as the write function simply triggers an asynchronous process, the pin is changed mid message, about 2 bytes in. Adding a sleep isn't an option as the message length varies and the pin's state change must be as close to the end of the message as possible.
So far I haven't found a callback or other way of directly checking the status of the serial buffer. Are there any?
As I couldn't see anything in software I've tried playing with the RTS/CTS pins.
My first attempt has been to wire another of the GPIO pins to the CTS pin and toggle it as part of the send function. The theory is:
- CTS is held high via a jumper cable by software, UART (basically) thinks the line is busy.
- serial.write("my_bytes") out, CTS still high so UART holds the bytes in it's buffer and sets RTS high as it wants to send but can't.
- Toggle CTS low and then high again, UART sees the line as open briefly and takes the opportunity to send the bytes out. RTS goes low as the request appears to have been approved.
- serial.write("dummy_bytes") to add something to the output buffer again after CTS has been toggled.
- There are now bytes to be sent that arrived after the CTS was toggled, once the current write command is complete it will want to send again but see the line as busy. RTS will go high immediately after the bytes in the current send command are gone which I can then use.
I've actually (somewhat surprisingly) got this to mostly work. I can keep bytes in the UART buffer by holding CTS high and trigger it to send by pulling it low.
The problem is that I still can't guarantee the order in which the GPIOs/serial buffers are handled and I have a horrible feeling that the write() commands aren't handled atomically. Sometimes the dummy bytes just get added to the real bytes, they are output and with nothing left to send RTS never goes high. Other times the dummy bytes don't get sent but it takes between 30 and 300ms before RTS goes high, implying the lag is on the CPU side getting the data to the buffer.
Any ideas are welcome, I'm going to keep stabbing at it.
The Reasons: I'm making a SDI-12 interface for the raspberry Pi, an archaic half-duplex, 1 wire, 5 volt protocol used almost exclusively by expensive weather monitoring equipment.
I have a circuit based on this https://daycounter.com/Circuits/SDI-12/SDI-12-Interface.phtml (which again, surprisingly, actually works well).
UART must be inverted, level shifted to 5v and have the RX and TX lines routed to a single data line.
- When the Pi is transmitting, the output is inverted and level shifted by the tri-state buffer but is otherwise just sent straight to the SDI-12 bus at 1200 BAUD (7bit, even parity).
- As there is only a single data line, the line must be put into a high impedence state for the sensor to respond, this is done by disabling the output of the tri-state buffer (effectively disconnecting the TX pin from the data line).
- With SDI-12, once the device receives a command, it immediately replies. 20ms delay at the most.
- This means that the tri-state buffer's output must be disabled AFTER the message is sent (otherwise it is truncated) but BEFORE the sensor starts to reply, leaving a small 5-10ms window.
tl;dr : I'm trying to convince two devices to talk to each other in a way they weren't really designed to do and want to rope you into helping me.