Serial.Lib
1.2.81
dotnet add package Serial.Lib --version 1.2.81
NuGet\Install-Package Serial.Lib -Version 1.2.81
<PackageReference Include="Serial.Lib" Version="1.2.81" />
<PackageVersion Include="Serial.Lib" Version="1.2.81" />
<PackageReference Include="Serial.Lib" />
paket add Serial.Lib --version 1.2.81
#r "nuget: Serial.Lib, 1.2.81"
#:package Serial.Lib@1.2.81
#addin nuget:?package=Serial.Lib&version=1.2.81
#tool nuget:?package=Serial.Lib&version=1.2.81
Serial.Lib
Serial port I/O for .NET that does one job well: cancellable, deadline-honouring reads and writes that never
abort spuriously, a bounded open and close, typed failures, and port enumeration with a stable hardware
identity. Namespace SharpAstro.Serial, net10.0, AOT and trim compatible. Written for the astronomy devices
TianWen drives (mounts, focusers, flat panels, filter wheels), where
"it mostly works" is not good enough.
Why
System.IO.Ports.SerialPort async reads are not trustworthy:
- On a CH34x USB bridge (very common on cheap devices) the first
BaseStream.ReadAsyncsucceeds and every later one aborts withERROR_OPERATION_ABORTEDwhile the reply still arrives, so replies land one frame late. - The "async" is a blocking read on a pool thread anyway (dotnet/runtime#28968),
and it ignores
ReadTimeout, so a timeout built fromTask.WhenAnyleaves the read hanging. - A write to a Bluetooth serial port with nobody on the far end never completes and ignores its token.
This library drives the port only through blocking calls, each bounded, and builds every guarantee above them once.
Use
using SharpAstro.Serial;
await using var port = await SerialPorts.OpenAsync("COM3", new SerialSettings(9600) { AssertDtr = true, AssertRts = true });
await port.WriteAsync(":00#"u8.ToArray());
var reply = new byte[32];
var n = await port.ReadTerminatedAsync(reply, "#"u8.ToArray()); // throws SerialTimeoutException, never returns a default
The contract
- A read completes, or throws
SerialTimeoutExceptionat its deadline (carrying the bytes that did arrive), orOperationCanceledExceptionfor the caller's token, or anotherSerialException. Cancelling never leaves a read pending that could eat the next reply. - Bytes after a reply's terminator are kept for the next read; a reply longer than the buffer is refused
(
SerialFramingException), never truncated. - A write still pending at its deadline marks the port (
HasAbandonedIo); every later write throwsSerialIoAbandonedExceptionat once instead of stranding another thread. - A fault on a port that is no longer enumerated is
SerialPortRemovedException, distinct from a timeout. - Open and close are bounded; a close that cannot finish abandons the handle, not the caller.
- Every exception derives from
SerialException, which derives fromIOException. OpenedAtsays when the open finished: opening resets many boards (every CH340 one), and some firmware saves state on a delay.
Testing without hardware
SerialLoopback.CreatePair(settings) returns two ports wired to each other in memory: what one writes, the
other reads. They are real ports in every respect but the wire, so a test through them gets the same deadlines,
framing and carry-over as a COM port.
Identity
SerialPorts.Enumerate() lists each port with what the OS knows about it: USB vendor and product id, serial
number, device instance id, and the USB socket's location path (Windows device tree; Linux sysfs and
/dev/serial/by-path / by-id). SerialPortInfo.Identity says what a saved configuration can key on:
Identity |
keyed on | survives |
|---|---|---|
Device |
vendor, product, serial number | moving the device to another socket |
Socket |
the USB location path | renames and re-enumeration, but two identical devices swapped between sockets swap identities |
PortName |
the OS name | nothing; a COM name follows the socket, a /dev/ttyUSBn name follows enumeration order |
IdentityKey renders the strongest one as a string (usb:1a86:7523:SERIAL, socket:..., name:COM3).
A Bluetooth serial port on Windows also says what it leads to (SerialPortInfo.Bluetooth): the paired device's address,
its name and its Class of Device as Windows recorded them at pairing (MajorClass), or IsIncoming for Windows' own
incoming port, which nothing dials. That is what tells a paired headset (AudioVideo), whose serial channel takes every
write and answers none, from a serial module (an HC-05 reports Uncategorized), before a probe spends its budget on it.
Status
1.0 is the managed backend (the blocking half of System.IO.Ports); 1.1 adds reads with no deadline
(Timeout.InfiniteTimeSpan, the token alone ends them) and the loopback pair; 1.2 the Bluetooth device behind a port. A native Win32 backend (overlapped I/O
driven correctly) is planned as 2.0, behind the same API. Design notes: docs/plans/serial-lib.md in tianwen.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- System.IO.Ports (>= 10.0.12)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Serial.Lib:
| Package | Downloads |
|---|---|
|
TianWen.Lib
Astronomical imaging and device-control library for .NET: cameras, mounts, focusers, filter wheels, cover/calibrators and guiders over ASCOM, Alpaca, ZWO, QHYCCD, Meade LX200, Skywatcher, OnStep and PHD2, plus plate solving, deep-sky and planetary stacking, auto-focus and unattended session automation. First-class multi-OTA (dual rig) support. AOT compatible. |
GitHub repositories
This package is not used by any popular GitHub repositories.