High-Speed Serial
Optimize PySerial for high baud rates. Baud rate selection, throughput calculation, buffer sizing, and platform tuning for reliable fast serial.
Above 115200 baud, data loss becomes the main problem. This page covers baud rate selection, throughput math, and the platform-specific tweaks that keep fast serial reliable.
Baud Rate Reference
Standard Rates (universal compatibility)
| Baud Rate | Bytes/sec (8N1) | Typical Use |
|---|---|---|
| 9600 | 960 | GPS, simple sensors |
| 19200 | 1,920 | Industrial equipment |
| 38400 | 3,840 | Moderate data |
| 57600 | 5,760 | Modems |
| 115200 | 11,520 | Arduino, dev boards, fast sensors |
High-Speed Rates (hardware dependent)
| Baud Rate | Bytes/sec (8N1) | Notes |
|---|---|---|
| 230400 | 23,040 | Usually works on quality adapters |
| 460800 | 46,080 | May drop data on long cables |
| 921600 | 92,160 | Requires good USB-serial chip |
| 1000000 | 100,000 | USB-serial adapters only |
| 2000000+ | 200,000+ | Specialized hardware (FTDI, CP2102N) |
Reliability depends on: USB-serial adapter quality, cable length (shorter is better), system CPU load, and driver quality.
Throughput Calculation
def calculate_throughput(baudrate, data_bits=8, parity=0, stop_bits=1):
"""Calculate real-world serial throughput."""
bits_per_byte = 1 + data_bits + parity + stop_bits # start + data + parity + stop
max_bytes_per_sec = baudrate / bits_per_byte
# Real-world efficiency is typically 80-95%
practical = max_bytes_per_sec * 0.85
overhead_pct = (bits_per_byte - data_bits) / bits_per_byte * 100
return max_bytes_per_sec, practical, overhead_pct
for baud in [115200, 460800, 921600, 1000000]:
theoretical, practical, overhead = calculate_throughput(baud)
print(f"{baud:>8} baud: ~{practical:>7,.0f} bytes/sec usable ({overhead:.0f}% overhead)")Log serial data automatically
TofuPilot records test results from your PySerial scripts, tracks pass/fail rates, and generates compliance reports. Free to start.
Testing Your Maximum Reliable Baud Rate
This requires a loopback connection (TX wired to RX) on the device under test.
import serial
import time
def test_baud_rates(port, bauds=None):
"""Test which baud rates work reliably with loopback."""
if bauds is None:
bauds = [115200, 230400, 460800, 921600, 1000000]
test_data = b'A' * 1000
results = {}
for baud in bauds:
try:
ser = serial.Serial(port, baud, timeout=1)
ser.reset_input_buffer()
errors = 0
for _ in range(10):
ser.write(test_data)
ser.flush()
received = ser.read(len(test_data))
if received != test_data:
errors += 1
ser.close()
reliable = errors == 0
results[baud] = reliable
status = "OK" if reliable else f"{errors}/10 failed"
print(f" {baud:>8} baud: {status}")
except Exception as e:
results[baud] = False
print(f" {baud:>8} baud: FAILED ({e})")
best = max((b for b, ok in results.items() if ok), default=None)
if best:
print(f"\nBest reliable rate: {best} baud")
return results
test_baud_rates('/dev/ttyUSB0')Buffer Sizing for High Speed
At high baud rates, the default 4 KB OS buffer fills in milliseconds. Size your buffer for at least 500ms of data at your baud rate.
import serial
def configure_high_speed(port, baudrate):
"""Open a port with buffers sized for the baud rate."""
bytes_per_sec = baudrate / 10 # 8N1 = 10 bits per byte
rx_buffer = max(4096, int(bytes_per_sec * 0.5)) # 500ms of data
tx_buffer = max(2048, int(bytes_per_sec * 0.1)) # 100ms of data
ser = serial.Serial(port, baudrate, timeout=0.01)
try:
ser.set_buffer_size(rx_size=rx_buffer, tx_size=tx_buffer)
except AttributeError:
pass # Not supported on all platforms
print(f"Opened {port} at {baudrate} baud")
print(f"RX buffer: {rx_buffer} bytes ({rx_buffer / bytes_per_sec * 1000:.0f}ms)")
return ser
ser = configure_high_speed('/dev/ttyUSB0', 921600)
ser.close()Read Strategy
At high speeds, always read everything available in a tight loop. Don't use readline() for continuous data streams.
import serial
import time
ser = serial.Serial('/dev/ttyUSB0', 921600, timeout=0.001)
total_bytes = 0
start = time.time()
try:
while True:
waiting = ser.in_waiting
if waiting:
data = ser.read(waiting)
total_bytes += len(data)
# Process data here (or hand off to another thread)
else:
time.sleep(0.0005) # 500us idle sleep
elapsed = time.time() - start
if elapsed >= 5.0:
rate = total_bytes / elapsed
print(f"{rate:,.0f} bytes/sec ({total_bytes:,} total in {elapsed:.1f}s)")
total_bytes = 0
start = time.time()
except KeyboardInterrupt:
ser.close()For CPU-intensive processing, separate reading and processing into different threads. See Threading for the producer-consumer pattern.
Platform Tuning
# Enable low-latency mode on the adapter
setserial /dev/ttyUSB0 low_latency
# Increase USB memory allocation (default 16 MB, raise for multiple fast ports)
echo 64 | sudo tee /sys/module/usbcore/parameters/usbfs_memory_mb
# Verify
cat /sys/module/usbcore/parameters/usbfs_memory_mbFrom Python, set VMIN=1, VTIME=0 via termios for minimum-latency reads:
import serial
import termios
ser = serial.Serial('/dev/ttyUSB0', 921600, timeout=0.001)
fd = ser.fileno()
attrs = termios.tcgetattr(fd)
attrs[6][termios.VMIN] = 1
attrs[6][termios.VTIME] = 0
termios.tcsetattr(fd, termios.TCSANOW, attrs)import serial
ser = serial.Serial('COM3', 921600, timeout=0.001)
# Request larger OS buffers
ser.set_buffer_size(rx_size=65536, tx_size=16384)
# Disable flow control if you don't need it
ser.xonxoff = False
ser.rtscts = False
ser.dsrdtr = FalseAlso disable USB selective suspend in an elevated PowerShell:
powercfg /setacvalueindex SCHEME_CURRENT `
2a737441-1930-4402-8d77-b2bebba308a3 `
48e6b7a6-50f5-4782-a5d4-53bb8f07e226 0
powercfg /setactive SCHEME_CURRENTChecklist for Reliable High-Speed Serial
- Use a quality USB-serial adapter (FTDI FT232H or CP2102N recommended).
- Keep cables under 1 meter.
- Size RX buffer for at least 500ms of data.
- Read in a tight loop with
ser.read(ser.in_waiting). - Move processing to a separate thread if it takes more than a few milliseconds.
- Disable USB power management on both Linux and Windows.
- Test with loopback at your target baud rate before deploying.