ftdi-0.2: A thin layer over USB to communicate with FTDI chips





data Device Source

A representation of an FTDI device.

data ChipType Source

The type of FTDI chip in a Device. The capabilities of a device depend on its chip type.



:: Device

USB device

-> ChipType 
-> Device

FTDI device

Promote a USB device to an FTDI device. You are responsible for supplying the correct USB device and specifying the correct chip type. There is no failsafe way to automatically determine whether a random USB device is an actual FTDI device.

guessChipType :: DeviceDesc -> Maybe ChipTypeSource

Tries to guess the type of the FTDI chip by looking at the USB device release number of a device's descriptor. Each FTDI chip uses a specific release number to indicate its type.


data Interface Source

A device interface. You can imagine an interface as a port or a communication channel. Some devices support communication over multiple interfaces at the same time.

Device handles

data DeviceHandle Source

You need a handle in order to communicate with a Device.

resetUSB :: DeviceHandle -> IO ()Source

Perform a USB device reset.

getTimeout :: DeviceHandle -> IntSource

Returns the USB timeout associated with a handle.

setTimeout :: DeviceHandle -> Int -> DeviceHandleSource

Modifies the USB timeout associated with a handle.

openDevice :: Device -> IO DeviceHandleSource

Open a device handle to enable communication. Only use this if you can't use withDeviceHandle for some reason.

closeDevice :: DeviceHandle -> IO ()Source

Release a device handle.

withDeviceHandle :: Device -> (DeviceHandle -> IO α) -> IO αSource

The recommended way to acquire a handle. Ensures that the handle is released when the monadic computation is completed. Even, or especially, when an exception is thrown.

Interface handles

Data transfer

runChunkedReaderT :: ChunkedReaderT m α -> ByteString -> m (α, ByteString)Source

Run the ChunkedReaderT given an initial state.

The initial state represents excess bytes carried over from a previous run. When invoking runChunkedReaderT for the first time you can safely pass the empty bytestring as the initial state.

A contrived example showing how you can manually thread the excess bytes through subsequent invocations of runChunkedReaderT:

  example ∷ InterfaceHandle → IO ()
  example ifHnd = do
    (packets1, rest1) ← runChunkedReaderT (readData ifHnd (return False) 400) empty
    print $ BS.concat packets1
    (packets2, rest2) ← runChunkedReaderT (readData ifHnd (return False) 200) rest1
    print $ BS.concat packets2

However, it is much easier to let ChunkedReaderTs monad instance handle the plumbing:

  example ∷ InterfaceHandle → IO ()
  example ifHnd =
    let reader = do packets1 ← readData ifHnd (return False) 400
                    liftIO $ print $ BS.concat packets1
                    packets2 ← readData ifHnd (return False) 200
                    liftIO $ print $ BS.concat packets1
    in runChunkedReaderT reader empty



:: forall m . MonadIO m 
=> InterfaceHandle 
-> m Bool

Check stop action

-> Int

Number of bytes to read

-> ChunkedReaderT m [ByteString] 

Reads data from the given FTDI interface by performing bulk reads.

This function produces an action in the ChunkedReaderT monad that will read exactly the requested number of bytes unless it is explicitly asked to stop early. Executing the readData action will block until either:

  • All data are read
  • The given checkStop action returns True

The result value is a list of chunks, represented as ByteStrings. This representation was choosen for efficiency reasons.

Data are read in packets. The function may choose to request more than needed in order to get the highest possible bandwidth. The excess of bytes is kept as the state of the ChunkedReaderT monad. A subsequent invocation of readData will first return bytes from the stored state before requesting more from the device itself. A consequence of this behaviour is that even when you request 100 bytes the function will actually request 512 bytes (depending on the packet size) and block until all 512 bytes are read! There is no workaround since requesting less bytes than the packet size is an error.

USB timeouts will not interrupt readData. In case of a timeout readData will simply resume reading data. A small USB timeout can degrade performance.

The FTDI latency timer can cause poor performance. If the FTDI chip can't fill a packet before the latency timer fires it is forced to send an incomplete packet. This will cause a stream of tiny packets instead of a few large packets. Performance will suffer horribly, but the request will still be completed.

If you need to make a lot of small requests then a small latency can actually improve performance.

Modem status bytes are filtered from the result. Every packet send by the FTDI chip contains 2 modem status bytes. They are not part of the data and do not count for the number of bytes read. They will not appear in the result.


  -- Read 100 data bytes from ifHnd
  (packets, rest) ← runChunkedReaderT (readData ifHnd (return False) 100) empty

Low level bulk transfers

These are low-level functions and as such they ignores things like:

  • Max packet size
  • Latency timer
  • Modem status bytes

USB timeouts are not ignored, but they will prevent the request from being completed.



:: InterfaceHandle 
-> Int

Number of bytes to read

-> IO (ByteString, Bool) 

Perform a bulk read.

Returns the bytes that where read (in the form of a ByteString) and a flag which indicates whether a timeout occured during the request.



:: InterfaceHandle 
-> ByteString

Data to be written

-> IO (Int, Bool) 

Perform a bulk write.

Returns the number of bytes that where written and a flag which indicates whether a timeout occured during the request.

Control requests

reset :: InterfaceHandle -> IO ()Source

Reset the FTDI device.

purgeReadBuffer :: InterfaceHandle -> IO ()Source

Clear the on-chip read buffer.

purgeWriteBuffer :: InterfaceHandle -> IO ()Source

Clear the on-chip write buffer.

getLatencyTimer :: InterfaceHandle -> IO Word8Source

Returns the current value of the FTDI latency timer.

setLatencyTimer :: InterfaceHandle -> Word8 -> IO ()Source

Set the FTDI latency timer. The latency is the amount of milliseconds after which the FTDI chip will send a packet regardless of the number of bytes in the packet.

data BitMode Source

MPSSE bitbang modes



Switch off bitbang mode, back to regular serial/FIFO.


Classical asynchronous bitbang mode, introduced with B-type chips.


Multi-Protocol Synchronous Serial Engine, available on 2232x chips.


Synchronous Bit-Bang Mode, available on 2232x and R-type chips.


MCU Host Bus Emulation Mode, available on 2232x chips. CPU-style fifo mode gets set via EEPROM.


Fast Opto-Isolated Serial Interface Mode, available on 2232x chips.


Bit-Bang on CBus pins of R-type chips, configure in EEPROM before use.


Single Channel Synchronous FIFO Mode, available on 2232H chips.

setBitMode :: InterfaceHandle -> Word8 -> BitMode -> IO ()Source

The bitmode controls the method of communication.

Line properties

data Parity Source



The parity bit is set to one if the number of ones in a given set of bits is even (making the total number of ones, including the parity bit, odd).


The parity bit is set to one if the number of ones in a given set of bits is odd (making the total number of ones, including the parity bit, even).


The parity bit is always 1.


The parity bit is always 0.

data BitDataFormat Source



data StopBits Source




:: InterfaceHandle 
-> BitDataFormat

Number of bits

-> StopBits

Number of stop bits

-> Maybe Parity

Optional parity mode

-> Bool


-> IO () 

Set RS232 line characteristics

newtype BaudRate α Source

Representation of a baud rate. The most interesting part is the instance for Bounded.




unBaudRate :: α


Num α => Bounded (BaudRate α) 
Enum α => Enum (BaudRate α) 
Eq α => Eq (BaudRate α) 
Fractional α => Fractional (BaudRate α) 
Integral α => Integral (BaudRate α) 
Num α => Num (BaudRate α) 
Ord α => Ord (BaudRate α) 
Read α => Read (BaudRate α) 
Real α => Real (BaudRate α) 
RealFrac α => RealFrac (BaudRate α) 
Show α => Show (BaudRate α) 

nearestBaudRate :: RealFrac α => ChipType -> BaudRate α -> BaudRate αSource

Calculates the nearest representable baud rate.

setBaudRate :: RealFrac α => InterfaceHandle -> BaudRate α -> IO (BaudRate α)Source

Sets the baud rate. Internally the baud rate is represented as a fraction. The maximum baudrate is the numerator and a special divisor is used as the denominator. The maximum baud rate is given by the BaudRate instance for Bounded. The divisor consists of an integral part and a fractional part. Both parts are limited in range. As a result not all baud rates can be accurately represented. This function returns the nearest representable baud rate relative to the requested baud rate. According to FTDI documentation the maximum allowed error is 3%. The nearest representable baud rate can be calculated with the nearestBaudRate function.

Modem status

data ModemStatus Source

Modem status information. The modem status is send as a header for each read access. In the absence of data the FTDI chip will generate the status every 40 ms.

The modem status can be explicitely requested with the pollModemStatus function.




msClearToSend :: Bool

Clear to send (CTS)

msDataSetReady :: Bool

Data set ready (DTS)

msRingIndicator :: Bool

Ring indicator (RI)

msReceiveLineSignalDetect :: Bool

Receive line signal detect (RLSD)

msDataReady :: Bool

Data ready (DR)

msOverrunError :: Bool

Overrun error (OE)

msParityError :: Bool

Parity error (PE)

msFramingError :: Bool

Framing error (FE)

msBreakInterrupt :: Bool

Break interrupt (BI)

msTransmitterHoldingRegister :: Bool

Transmitter holding register (THRE)

msTransmitterEmpty :: Bool

Transmitter empty (TEMT)

msErrorInReceiverFIFO :: Bool

Error in RCVR FIFO

pollModemStatus :: InterfaceHandle -> IO ModemStatusSource

Manually request the modem status.

Flow control

data FlowCtrl Source



Request-To-Send / Clear-To-Send


Data-Terminal-Ready / Data-Set-Ready


Transmitter on / Transmitter off

setFlowControl :: InterfaceHandle -> Maybe FlowCtrl -> IO ()Source

Set the flow control for the FTDI chip. Use Nothing to disable flow control.

setDTR :: InterfaceHandle -> Bool -> IO ()Source

Set DTR line.

setRTS :: InterfaceHandle -> Bool -> IO ()Source

Set RTS line.

setEventCharacter :: InterfaceHandle -> Maybe Word8 -> IO ()Source

Set the special event character. Use Nothing to disable the event character.

setErrorCharacter :: InterfaceHandle -> Maybe Word8 -> IO ()Source

Set the error character. Use Nothing to disable the error character.


defaultTimeout :: IntSource

Default USB timeout. The timeout can be set per device handle with the setTimeout function.