This package contains a library that allows you .net app to communicate to a FTDI USB chip. It wraps the D2XX API into a nice OO .net library. The Library can also be used on Linux and MacOS.
$ dotnet add package Essy.FTDIWrapperA small, opinionated .NET wrapper around the FTDI D2XX (ftd2xx) native library.
It lets you enumerate FTDI devices, open them by serial number, configure the UART,
read/write data, work with events, and access EEPROM/user area – all from managed C# code.
Namespace:
Essy.FTDIWrapper
IDisposable.The native interop is centralized in a single internal FTDIFunctions class that P/Invokes into ftd2xx.
Once published to NuGet:
dotnet add package Essy.FTDIWrapper
Or via the NuGet Package Manager UI in Visual Studio: search for Essy.FTDIWrapper.
ftd2xx native library (ftd2xx.dll / libftd2xx.so / libftd2xx.dylib) available on the library load path.Some features (port cycling, driver version, reset pipe retry count) are only supported on Windows and are no-ops or short‑circuit on Unix-like platforms.
using System;
using System.Collections.Generic;
using Essy.FTDIWrapper;
class Program
{
static void Main()
{
List<FTDI_DeviceInfo> devices = FTDI_DeviceInfo.EnumerateDevices();
if (devices.Count == 0)
{
Console.WriteLine("No FTDI devices found.");
return;
}
foreach (var dev in devices)
{
Console.WriteLine(dev.ToString());
}
}
}
FTDI_DeviceInfo.EnumerateDevices() calls FT_CreateDeviceInfoList and FT_GetDeviceInfoDetail under the hood and converts the C strings to managed strings.
A device is opened by serial number via FT_OpenEx and wrapped in FTDI_Device, which is IDisposable.
using System;
using System.Text;
using Essy.FTDIWrapper;
class Program
{
static void Main()
{
var devices = FTDI_DeviceInfo.EnumerateDevices();
if (devices.Count == 0)
{
Console.WriteLine("No FTDI devices found.");
return;
}
var info = devices[0];
using var dev = new FTDI_Device(info);
// Basic UART configuration
dev.SetBaudrate(115200);
dev.SetParameters(DataLength.EightBits, Parity.None, StopBits.OneStopBit);
dev.SetFlowControl(FlowControl.None);
// Clear buffers (optional)
dev.ClearReceiveBuffer();
dev.ClearSendBuffer();
// Send some data
var payload = Encoding.ASCII.GetBytes("Hello FTDI!");
dev.WriteBytes(payload);
// Read back anything available
uint available = dev.NumberOfBytesInReceiveBuffer();
if (available > 0)
{
byte[] response = dev.ReadBytes(available);
Console.WriteLine("Read: " + BitConverter.ToString(response));
}
}
}
The WriteBytes and ReadBytes methods validate that the requested number of bytes were actually transmitted/received, and throw a FTDIDeviceNotAllBytesTransmittedException otherwise.
FTDI_Device can use event notifications to wait for RX data. On EnableWaitForDataEvent, it sets up an event with FT_SetEventNotification and waits on it via EventWaitHandle. fileciteturn0file0L168-L191turn0file3L60-L73
using System;
using System.Text;
using System.Threading.Tasks;
using Essy.FTDIWrapper;
class Program
{
static async Task Main()
{
var devInfo = FTDI_DeviceInfo.EnumerateDevices()[0];
using var dev = new FTDI_Device(devInfo);
dev.SetBaudrate(115200);
dev.EnableWaitForDataEvent();
Console.WriteLine("Waiting for data...");
// Asynchronously wait until the RXCHAR event is signalled
await dev.WaitForDataAsync();
uint available = dev.NumberOfBytesInReceiveBuffer();
if (available > 0)
{
byte[] data = dev.ReadBytes(available);
Console.WriteLine("Received: " + BitConverter.ToString(data));
}
}
}
There is also a synchronous WaitForData(int timeoutMs) plus equivalent methods for modem/pin‑change events (EnableWaitForPinChangeEvent, WaitForPinChange, WaitForPinChangeAsync).
The library exposes helpers to read/write the user area (UA) EEPROM as either text or raw bytes:
using System;
using Essy.FTDIWrapper;
class Program
{
static void Main()
{
var info = FTDI_DeviceInfo.EnumerateDevices()[0];
using var dev = new FTDI_Device(info);
// Get capacity
uint capacity = dev.GetSizeOfUAEEProm();
Console.WriteLine($"User EEPROM size: {capacity} bytes");
// Store a label
dev.WriteToUAEEProm("My FTDI Device");
// Read it back as string
string label = dev.ReadFromUAEEProm();
Console.WriteLine($"Label: {label}");
// Or as raw bytes
byte[] raw = dev.ReadBinaryFromUAEEProm(16);
Console.WriteLine("Raw: " + BitConverter.ToString(raw));
}
}
The implementation ensures that the requested size fits within the available user area and throws an FTDIDeviceLowLevelException if not.
Most failures wrap the FT_STATUS code returned from ftd2xx in a typed exception:
FTDIException – base type, exposes the Status property.FTDIEnumerationException – for enumeration problems.FTDIDeviceException and its children:
FTDIDeviceLowLevelExceptionFTDIDeviceNotAllBytesTransmittedExceptionExample:
try
{
using var dev = new FTDI_Device(info);
dev.SetBaudrate(9600);
dev.WriteBytes(new byte[] { 0x01, 0x02, 0x03 });
}
catch (FTDIException ex)
{
Console.Error.WriteLine($"FTDI error: {ex.Message}");
Console.Error.WriteLine($"Status: {ex.Status}");
}
Some additional APIs you can call on FTDI_Device:
Reset() / TryReset() – reset the device.ResetPort(), TryResetPort() – reset the USB port (Windows only).CyclePort(), TryCyclePort() – cycle the USB port (Windows only).SetResetPipeRetryCount(uint) – configure retry behaviour when resetting pipes.SetBufferFlushTimer(byte) – configure latency timer.SetBufferSizes(uint tx, uint rx) – change USB transfer sizes.GetStatusPins() – read modem pins (CTS, DSR, RI, DCD).SetRTS(bool) – assert/deassert RTS.GetDriverVersion() – retrieve FTDI driver version (Windows only).GetDeviceProductID() / SetDeviceProductID(ushort) – read or reprogram the Product ID in EEPROM, preserving the user area data.FTDIFunctions, while public, user‑friendly APIs live in FTDI_Device and FTDI_DeviceInfo.FT_STATUS codes; instead it surfaces them via exceptions so advanced users can still reason about exact driver failures.This library is licensed under the MIT License See the source files for the full license text.