UART RTS/CTS software flow control
This was brought up by somebody I am working with that has several UARTS implemented that have large packets of information and network communication. This causes potential for blocking functionality and has lead to buffer overflows on the UART. Flow control helps but not all UARTs have flow control and I was asked if that flow control could be done low level in Micropython. That sounded like an interesting enough idea to bring up here.
ESP32/UART: Added XON/XOFF flow control.
Summary
I have an existing ESP32 application that is written in C++ that consists of a display panel with embedded ESP32, plus a realtime CNC controller that runs on another ESP32. The two ends communicate over a UART link at high speed - between 1 Mbps and 5 Mbps. To prevent overrun, the communication link uses XON/XOFF software flow control. I am porting the display end to another ESP32-S3 display, replacing the C++ UI code with MicroPython and LVGL. MicroPython currently does not support XON/XOFF flow control. Due to hardware pinout and cabling limitations, it is not feasible to use RTS/CTS flow control. The low-level UART driver in ESP-IDF supports XON/XOFF flow control, so it quite easy to expose that capability via the MicroPython UART class. The API change simply adds a UART.XONXOFF option to the "flow" parameter.
XON/XOFF, of course, is a is a well-known flow control method with long history. It is supported by many serial drivers and terminal emulators.
This PR implements the feature only for the ESP32 port, although it is probably possible to do a similar thing for many other ports. The API change - namely the addition of the UART.XONXOFF value - should not conflict with any existing usage on other ports.
Testing
I tested it on an ESP32-S3 system, namely an Elecrow 7" HMI panel. With the UART configured as follows:
from machine import UART
u = UART(1, 1000000, rx=44, tx=43, flow=UART.XONXOFF)
I connected to the device using TeraTermPro on a PC, setting the speed to 1000000 and enabling XonXoff flow control in TeraTerm. The test application sends characters periodically and receives anything that is sent. I verified that, when I sent CTRL-S via TeraTerm, the application paused its transmission, resuming it on receipt of CTRL-Q. In the other direction, I pasted large blocks of text into TeraTerm and verified that there were periodic pauses as flow control occurred, and that no data was lost during the transmission of those large blocks of data at high speed.
I also tested with my existing C++ application running on the other end, which depends on flow control to avoid overrunning the receiver. The system works correctly, without data loss, when XONXOFF flow control is enabled.
The ESP32 API for enabling XON/XOFF flow control is the same on all ESP32 variants, so there is no reason to expect differences across the ESP32 family.
Trade-offs and Alternatives
The code size increase is 384 bytes of FLASH.
The only alternative that would work in my application (short of going back to C++) would be to revamp the serial line protocol to wrap all of the messages in limited-length packets and apply some form of request/acknowledge flow control at the packet level. That would require a lot of design and implementation on an existing stable product at the other end.
Trying to do the XON/XOFF at the Python level is unlikely to work, considering the high speed of the communication link, the limited size of the ESP32's hardware FIFO, and hard-to-control latencies introduced by the display driver and the LVGL stack.
As previously mentioned, RTS/CTS hardware flow control is not feasible because of severe pin limitations at both ends of the communications channel, as well as existing cabling techniques that are deployed in existing variants of this new device.