Serial Transport (USB / UART)
Shiny.Obd.Serial connects to any ELM327-compatible adapter that presents as a serial port —
OBDLink SX/EX, ELM327 clones on CH340/FTDI/CP210x bridges, and adapters wired directly to a board’s
TX/RX pins.
It is built on System.IO.Ports, which is supported on Windows, Linux, macOS and Mac Catalyst.
Platform support
Section titled “Platform support”| Platform | Supported | Notes |
|---|---|---|
| Windows, Linux, macOS | Yes | |
| Mac Catalyst | Yes | Marked [SupportedOSPlatform] on the assembly |
| Android | No | Compiles and loads, but cannot open a port — see below |
| iOS, tvOS, Browser/WASM | No | [UnsupportedOSPlatform]; throws PlatformNotSupportedException |
Installation
Section titled “Installation”<PackageReference Include="Shiny.Obd" /><PackageReference Include="Shiny.Obd.Serial" />Registration
Section titled “Registration”services.AddShinyObdSerial(config =>{ config.PortNameFilter = "OBDLink"; config.BaudRate = 115200;});
// or, against a known portservices.AddShinyObdSerial("/dev/ttyUSB0");IObdTransport, IObdConnection and IObdDeviceScanner are registered as singletons. An OBD
adapter is a single physical resource; a scoped or transient registration would leave two consumers
fighting over one serial port.
Direct construction
Section titled “Direct construction”var transport = new SerialObdTransport(new SerialObdConfiguration{ PortName = null, // null discovers a port PortNameFilter = "OBDLink", AutoDetectBaudRate = true, CommandTimeout = TimeSpan.FromSeconds(10)});
var connection = new ObdConnection(transport);await connection.Connect();
Console.WriteLine($"Opened {transport.ConnectedPortName} at {transport.ConnectedBaudRate} baud");Configuration
Section titled “Configuration”| Property | Default | Notes |
|---|---|---|
PortName |
null |
Null discovers one. Prefer a /dev/serial/by-id/... path on Linux |
BaudRate |
38400 |
The ELM327 default. OBDLink/STN adapters run happily at 115200 and up |
AutoDetectBaudRate |
true |
Probes BaudRateCandidates at connect |
BaudRateCandidates |
38400, 115200, 9600, 500000 |
|
PortNameFilter |
null |
Substring match against the port name or description |
DtrEnable / RtsEnable |
true |
Most USB bridges hold the adapter in reset until DTR is raised |
OpenSettleDelay |
500ms |
Bridges that reset on DTR swallow anything sent during the reset |
CommandTimeout |
10s |
Per command |
Baud rate probing
Section titled “Baud rate probing”A UART at the wrong baud rate does not go quiet — it returns framing garbage. The probe therefore
checks the shape of the ATI reply (a known adapter string, or at least mostly-printable ASCII)
rather than merely that a reply arrived. It costs a second or two at connect and removes an entire
category of “it connects but returns nonsense” problem.
Discovery
Section titled “Discovery”var scanner = new SerialObdDeviceScanner();using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await scanner.Scan(device =>{ var info = (SerialPortInfo)device.NativeDevice; Console.WriteLine($"{info.PortName} — {info.Description} (likely: {info.IsLikelyAdapter})");}, cts.Token);Unlike a raw SerialPort.GetPortNames(), discovery is platform-aware:
| Platform | Enumerated | Why |
|---|---|---|
| Linux | /dev/serial/by-id/*, then ttyUSB* / ttyACM* / ttyAMA* / serial* |
by-id names are built from the USB descriptor, so they carry the vendor and serial number and survive a reboot |
| macOS | /dev/cu.* only |
Opening the matching tty. node blocks until the device asserts carrier detect, which a USB-serial bridge never does |
| Windows | SerialPort.GetPortNames() |
Backed by the SERIALCOMM device map, and accurate |
Candidates are returned likely-adapter first, matched against known OBD brands (OBDLink, Veepeak, Vgate, ScanTool) and the USB-serial bridge chips they are built on (FTDI, CH340, CP210x, PL2303). The bridges are included deliberately: a genuine OBDLink SX presents as a stock FTDI device with no OBD branding anywhere in its USB descriptor, so matching only on “OBD” would skip the best adapter on the list.
Linux permissions
Section titled “Linux permissions”Opening a serial port requires the dialout group:
sudo usermod -aG dialout $USER # log out and back inModemManager also probes every serial device it sees and will hold an OBD adapter open for several seconds sending AT commands at it, which makes connects fail intermittently and unreproducibly. Tell it to ignore the bridges:
ATTRS{idVendor}=="0403", ENV{ID_MM_DEVICE_IGNORE}="1" # FTDIATTRS{idVendor}=="1a86", ENV{ID_MM_DEVICE_IGNORE}="1" # CH340ATTRS{idVendor}=="10c4", ENV{ID_MM_DEVICE_IGNORE}="1" # CP210x

