PySerial
PySerialDocs

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 RateBytes/sec (8N1)Typical Use
9600960GPS, simple sensors
192001,920Industrial equipment
384003,840Moderate data
576005,760Modems
11520011,520Arduino, dev boards, fast sensors

High-Speed Rates (hardware dependent)

Baud RateBytes/sec (8N1)Notes
23040023,040Usually works on quality adapters
46080046,080May drop data on long cables
92160092,160Requires good USB-serial chip
1000000100,000USB-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_mb

From 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 = False

Also 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_CURRENT

Checklist for Reliable High-Speed Serial

  1. Use a quality USB-serial adapter (FTDI FT232H or CP2102N recommended).
  2. Keep cables under 1 meter.
  3. Size RX buffer for at least 500ms of data.
  4. Read in a tight loop with ser.read(ser.in_waiting).
  5. Move processing to a separate thread if it takes more than a few milliseconds.
  6. Disable USB power management on both Linux and Windows.
  7. Test with loopback at your target baud rate before deploying.