CosmoSerialPort 1.1.0

dotnet add package CosmoSerialPort --version 1.1.0
                    
NuGet\Install-Package CosmoSerialPort -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CosmoSerialPort" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CosmoSerialPort" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="CosmoSerialPort" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CosmoSerialPort --version 1.1.0
                    
#r "nuget: CosmoSerialPort, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CosmoSerialPort@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CosmoSerialPort&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=CosmoSerialPort&version=1.1.0
                    
Install as a Cake Tool

CosmoSerialPort

A modern, cross-platform serial port library for .NET 10 — a semantic port of serialport-rs built for industrial automation, robotics, SCADA, telemetry and other long-running, latency-sensitive workloads.

  • Windows (Win32 overlapped I/O + IOCP), Linux (termios2/BOTHER, poll), macOS (termios + IOSSIOSPEED, IOKit enumeration)
  • SerialPort derives from Stream: works with StreamReader, System.IO.Pipelines, etc.
  • Real async: pooled IValueTaskSource operations over a poll reactor (Unix) / IOCP (Windows) — no Task.Run wrappers, zero steady-state allocations
  • Deterministic disposal: Dispose() wakes blocked readers, even with infinite timeouts
  • Port enumeration with USB VID/PID, serial number, manufacturer and product strings
  • Modem pin control (RTS/DTR out, CTS/DSR/RI/CD in), break, buffer discard, XON/XOFF and RTS/CTS flow control
  • Opt-in text layer (ReadLine/WriteLine, NewLine, Encoding) and a race-free DataReceived event — the binary hot path stays untouched until you use them
  • RS-485 direction control (Rs485Settings: TIOCSRS485 on Linux, RTS_CONTROL_TOGGLE on Windows), inter-byte timeouts (InterByteTimeout, pySerial's inter_byte_timeout), and in-memory SerialPort.OpenLoopback() / SerialPort.OpenPair() transports for hardware-free testing on every platform
  • URL-style ports, pySerial-compatible: rfc2217://host:port (Telnet COM-port control, works with ser2net), socket://host:port (raw TCP) and loop:// — just pass the URL as the port name; plus DSR/DTR flow control and 1.5 stop bits (Windows/RFC 2217), and an interactive terminal sample (samples/CosmoSerialPort.Terminal, the miniterm equivalent)
  • Nullable, analyzer-clean, AOT- and trimming-compatible
  • Targets net10.0 and netstandard2.0 — usable from .NET Framework 4.7.2+, validated on .NET Framework 4.8 (see samples/CosmoSerialPort.Probe48). The ns2.0 build uses classic DllImport interop and a small polyfill layer; behavior is identical, though async continuations there always dispatch via the thread pool

Documentation

Full guides live in docs/: getting started, configuration, reading & writing, events, signals & status, enumeration, virtual & network ports, error handling, API reference, platform notes, migration, examples, and the FAQ.

Quick start

using CosmoSerialPort;

// Discover ports
foreach (SerialPortInfo info in SerialPort.GetPortInfos())
    Console.WriteLine($"{info.PortName} [{info.PortType}] {info.UsbInfo?.Product}");

// Open with the builder (an immutable record — share and reconfigure freely)
using SerialPort port = SerialPort.Create("/dev/ttyUSB0", 115200)
    .WithParity(Parity.None)
    .WithStopBits(StopBits.One)
    .WithFlowControl(FlowControl.None)
    .WithReadTimeout(TimeSpan.FromSeconds(2))
    .Open();

// Sync: returns as soon as at least one byte is available,
// throws SerialPortTimeoutException after ReadTimeout.
byte[] buffer = new byte[256];
int read = port.Read(buffer);

// Async with cancellation
int n = await port.ReadAsync(buffer.AsMemory(), cancellationToken);
await port.WriteAsync("AT\r\n"u8.ToArray());

// Pipelines for protocol parsing
var reader = port.CreatePipeReader();

// Control surface
port.SetRts(true);
ModemPins pins = port.GetModemPins();
port.DiscardBuffers(ClearBuffer.Input);

// Text layer (opt-in; UTF-8 and "\n" by default)
port.WriteLine("AT");
string? reply = port.ReadLine();
string? reply2 = await port.ReadLineAsync(cancellationToken);

// DataReceived: a background watcher owns the receive side while subscribed,
// so reading inside the handler is safe (unlike System.IO.Ports).
port.DataReceived += (_, e) =>
{
    if (!e.IsEndOfStream && port.ReadLine() is { } line)
        Console.WriteLine($"got: {line}");
};

Feature tour

Network and URL-style ports

Port names can be URLs (pySerial's serial_for_url scheme set) — the builder and every feature above work unchanged:

// RFC 2217 / Telnet COM-port control (ser2net, terminal servers):
// line settings, RTS/DTR/break and purges are forwarded to the remote UART.
using SerialPort remote = SerialPort.Create("rfc2217://192.168.1.50:4001", 115200)
    .WithParity(Parity.Even)
    .WithReadTimeout(TimeSpan.FromSeconds(5))
    .Open();
remote.WriteLine("STATUS?");
Console.WriteLine(remote.ReadLine());
Console.WriteLine(remote.GetModemPins()); // served from NOTIFY_MODEMSTATE

// Raw TCP bridge (no control channel):
using SerialPort tcp = SerialPort.Open("socket://10.0.0.7:7777", 9600);

Hardware-free testing

// loop://—everything written comes back on the same port; RTS/DTR reflect as CTS/DSR.
using SerialPort loop = SerialPort.OpenLoopback();
loop.WriteLine("echo");
Assert.Equal("echo", loop.ReadLine());

// A cross-connected null-modem pair — drive both ends of your protocol in one process.
(SerialPort device, SerialPort host) = SerialPort.OpenPair();
device.Write(response);
host.Read(buffer);          // reads what "device" wrote
device.Dispose();           // "host" now reads end-of-stream, like a hot unplug

Status events

port.PinChanged += (_, e) =>
    Console.WriteLine($"changed: {e.ChangedPins}, now: {e.CurrentPins}");
port.ErrorReceived += (_, e) =>
    Console.WriteLine($"line errors: {e.Errors}"); // Frame | Overrun | RxParity | RxOver | Break

Both are driven by a ~50 ms sampling watcher that starts with the first subscription and stops with the last; pin pulses shorter than the interval can be missed, and ErrorReceived has no macOS source. DataReceived supports ReceivedBytesThreshold to batch notifications.

Industrial extras

// RS-485 half-duplex direction control (Linux TIOCSRS485 / Windows RTS toggle).
using SerialPort bus = SerialPort.Create("/dev/ttyS2", 19200)
    .WithRs485Mode(new Rs485Settings { DelayBeforeSend = TimeSpan.FromMilliseconds(1) })
    .Open();

// Inter-byte timeout: Read collects a burst and returns when the line goes quiet,
// instead of returning the first fragment — ideal for framed RTU-style protocols.
port.InterByteTimeout = 50;   // ms
int frame = port.Read(buffer);

// Parity beyond N/E/O, plus error-byte replacement and null stripping.
using SerialPort legacy = SerialPort.Create("COM2", 9600)
    .WithParity(Parity.Mark)              // Mark/Space: Windows & Linux
    .WithParityReplace((byte)'?')         // substitute bytes received in error
    .WithDiscardNull(true)
    .WithStopBits(StopBits.OnePointFive)  // Windows & RFC 2217
    .Open();

Timeouts

Value Sync behavior Async behavior
Timeout.Infinite (default) Block until data Wait until data or cancellation
> 0 SerialPortTimeoutException after the timeout Same
0 Fail immediately if nothing buffered (the serialport-rs default) Same

SerialPortTimeoutException derives from TimeoutException for System.IO.Ports compatibility; all other errors derive from SerialPortException (an IOException) with the native error code preserved.

Thread safety

One thread may read while another writes on the same instance. Multiple concurrent readers (or writers) are rejected. Control operations are individually thread-safe.

CosmoSerialPort vs System.IO.Ports

How this library compares to the classic System.IO.Ports.SerialPort:

Feature System.IO.Ports CosmoSerialPort
API model Mutable component: set properties, then Open() Immutable SerialPortBuilder record → Open() returns the port
Stream integration Indirect, via the BaseStream property SerialPort is a Stream
Async I/O BaseStream.ReadAsync wraps the old APM pattern; not truly asynchronous Real async: ValueTask + CancellationToken; poll reactor on Unix, IOCP on Windows
Cancellation Not supported — close the port to abort CancellationToken on every async operation
Allocations Internal buffering and event machinery allocate per operation Zero steady-state allocations; Span/Memory overloads
Dispose while reading Notorious hangs and ObjectDisposedException crashes on hot unplug Deterministic: Dispose() wakes blocked readers; hot-unplug tested
DataReceived event Fires on a pool thread; reading from the handler races other reads Serialized invocations; a watcher owns the receive side, so handler reads are race-free with natural backpressure
Text layer ReadLine/WriteLine/ReadExisting/Write(string) (ASCII default) Same surface, opt-in with zero cost when unused (UTF-8 default)
Port enumeration GetPortNames() — names only (registry SERIALCOMM) Names plus USB VID/PID, serial number, manufacturer, product, transport type (SetupAPI / sysfs / IOKit)
Baud rates Standard rates; non-standard rates fail on Linux/macOS Arbitrary rates: BOTHER (Linux), IOSSIOSPEED (macOS), any DCB value (Windows)
Exclusive access None on Unix — two processes can open the same tty flock + TIOCEXCL by default, opt-out via WithExclusive(false)
Modem pins CtsHolding/DsrHolding/CDHolding — three calls, no Ring Indicator GetModemPins() — all four input pins in one native call
Error model Mixed IOException/InvalidOperationException/UnauthorizedAccessException; native codes lost Typed hierarchy (PortNotFound, AccessDenied, PortInUse, InvalidConfiguration, NativeIO) with NativeErrorCode preserved
Timeout exception TimeoutException SerialPortTimeoutException : TimeoutException — existing catch blocks keep working
Pipelines Manual wiring over BaseStream CreatePipeReader() / CreatePipeWriter() built in
Parity options Mark/Space parity, ParityReplace, DiscardNull Same — Mark/Space on Windows and Linux (not macOS); ParityReplace and DiscardNull everywhere
Handshake options RequestToSendXOnXOff combined mode Same — FlowControl.HardwareAndSoftware
PinChanged/ErrorReceived events Available Available — sampling-based (~50ms); richer args (all pins + change mask, error flags); ErrorReceived not on macOS
ReceivedBytesThreshold Available Available
AOT / trimming Not annotated AOT- and trim-compatible (net10.0 target)
Frameworks .NET Framework and .NET (Windows focus; limited Unix fidelity) net10.0 + netstandard2.0 (.NET Framework 4.7.2+), first-class Windows/Linux/macOS

Migration notes. Most call sites map one-to-one: IsOpenCanRead/CanWrite, Close()Dispose(), DiscardInBuffer()/DiscardOutBuffer()DiscardBuffers(ClearBuffer.…), DtrEnable/RtsEnableWithDtrOnOpen(…)/SetRts(…), HandshakeFlowControl. Write(string) and ReadExisting() exist with the same semantics (mind the UTF-8 vs ASCII default Encoding). Ports open at construction time via the builder, so code that configured a closed port and opened it later should defer creation to the point of first use.

CosmoSerialPort vs pySerial

For teams coming from Python, the feature sets now largely mirror each other:

Feature pySerial 3.5 CosmoSerialPort
Async model Blocking + threads; pyserial-asyncio is separate and experimental First-class ValueTask async with CancellationToken
Cancellation cancel_read()/cancel_write() (may cancel a future call on POSIX) Deterministic per-operation tokens
Data/parity/stop 5–8 bits, N/E/O/Mark/Space, stop bits 1 / 1.5 / 2 Same (Mark/Space not on macOS; 1.5 on Windows/RFC 2217)
Flow control XON/XOFF, RTS/CTS, DSR/DTR Same, plus the combined RTS/CTS+XON/XOFF mode
inter_byte_timeout Yes Yes — InterByteTimeout
Custom baud rates BOTHER / IOSSIOSPEED Same mechanisms
Exclusive access Opt-in, POSIX only Default-on (flock+TIOCEXCL); inherent on Windows
RS-485 mode rs485_mode + software rs485.RS485 Rs485Settings (Linux TIOCSRS485, Windows RTS toggle) plus Emulated software mode on any platform
URL handlers rfc2217://, socket://, loop://, spy://, hwgrep://, alt://, cp2110:// rfc2217://, socket://, loop://, spy:// + OpenPair() + custom scheme registration; hwgrep:// → query over GetPortInfos(); alt:///cp2110:// not built in
Enumeration fields device, hwid, vid, pid, serial, location, manufacturer, product, interface name, type, vid, pid, serial, manufacturer, product, location, interface
Timed break / buffer size send_break(duration), set_buffer_size() SendBreak(TimeSpan) / BreakState, SetBufferSize() (Windows)
Events None — poll or the experimental ReaderThread Native DataReceived (thresholded), PinChanged, ErrorReceived
Framing helpers read_until, Packetizer ReadLine/NewLine, System.IO.Pipelines
Terminal tool miniterm samples/CosmoSerialPort.Terminal
Error model SerialException + SerialTimeoutException Typed hierarchy with preserved native error codes
Performance Interpreter-bound Native code, zero steady-state allocations

Repository layout

Path Contents
src/CosmoSerialPort The library
tests/CosmoSerialPort.Tests xunit suite: PTY loopback on Unix, in-memory and in-process network servers everywhere
bench/CosmoSerialPort.Benchmarks BenchmarkDotNet loopback benchmarks
samples/CosmoSerialPort.Probe Hardware smoke test / enumeration demo for a real or virtual port
samples/CosmoSerialPort.Probe48 The same probe on .NET Framework 4.8, consuming the netstandard2.0 build
samples/CosmoSerialPort.Terminal Interactive terminal (miniterm equivalent); accepts device names and URLs
ARCHITECTURE.md Analysis of serialport-rs and the porting decisions
dotnet build -c Release        # warning-clean, analyzers on
dotnet test  -c Release        # 71 tests; PTY suite on macOS/Linux, virtual transports everywhere
dotnet run --project samples/CosmoSerialPort.Probe -c Release -- COM1        # against an echo peer
dotnet run --project samples/CosmoSerialPort.Terminal -c Release -- loop://  # try it without hardware
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 112 7/20/2026
1.0.0 93 7/20/2026
0.2.0 104 7/19/2026
0.1.1 99 7/19/2026
0.1.0 107 7/18/2026