diff --git a/obd.cabal b/obd.cabal
--- a/obd.cabal
+++ b/obd.cabal
@@ -1,5 +1,5 @@
 name:                obd
-version:             0.2.0.2
+version:             0.2.1.0
 synopsis:            Communicate to OBD interfaces over ELM327
 description:
     Haskell library to communicate with OBD-II over ELM327,
diff --git a/src/System/Hardware/ELM327.hs b/src/System/Hardware/ELM327.hs
--- a/src/System/Hardware/ELM327.hs
+++ b/src/System/Hardware/ELM327.hs
@@ -25,11 +25,11 @@
                                , stopb = One
                                , parity = NoParity
                                , flowControl = NoFlowControl
-                               , timeout = 5 {- 500ms -} }
+                               , timeout = 50 {- 5000ms -} }
     port <- openSerial fp s
     is <- makeInputStream (produce port)
     os <- makeOutputStream (consume port)
-    initialize $ Con is os
+    initialize $ Con is os (const $ return ())
   where
     produce port = do
         b <- Port.recv port 8
diff --git a/src/System/Hardware/ELM327/Connection.hs b/src/System/Hardware/ELM327/Connection.hs
--- a/src/System/Hardware/ELM327/Connection.hs
+++ b/src/System/Hardware/ELM327/Connection.hs
@@ -1,9 +1,36 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE FlexibleContexts #-}
 -- | Basic interface for communication with the ELM327.
-module System.Hardware.ELM327.Connection where
+module System.Hardware.ELM327.Connection (
+  -- * Basic types
+  Con(..)
+, ConT(..)
+, withCon
+, ConError(..)
 
+  -- * Interacting with connections
+, close
+, close'
+, send
+, sendBytes
+, sendString
+, recvRaw
+, recv
+, flushOutputStream
+
+  -- * Sending commands
+, at
+, obd
+
+  -- * Logging
+, fileLog
+)
+where
+
+import Prelude hiding (log)
+
 import Control.Lens (re, (^.), (^?))
+import Control.Monad (void)
 import Control.Monad.Except (ExceptT, MonadError, runExceptT, throwError)
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import Control.Monad.Reader (ReaderT, MonadReader, runReaderT, ask)
@@ -15,6 +42,7 @@
 import Data.Word (Word8)
 import qualified Data.ByteString.Char8 as Char8
 
+import System.IO (openFile, IOMode(..), hPutStrLn, hFlush, hClose)
 import System.IO.Streams (InputStream, OutputStream)
 import qualified System.IO.Streams as Streams
 
@@ -31,8 +59,10 @@
 import System.Hardware.ELM327.Utils.Monad (maybeToExcept, orThrow)
 
 -- | A connection to an ELM327 device that can be closed.
-data Con = Con { conInput :: InputStream ByteString
-               , conOutput :: OutputStream ByteString }
+data Con = Con { conInput :: InputStream ByteString   -- ^ Input stream of the connection
+               , conOutput :: OutputStream ByteString -- ^ Output stream of the connection
+               , conLog :: Maybe ByteString -> IO ()  -- ^ Function that will be called with every byte sent or received, 'Nothing' indicates connection closed.
+               }
 
 -- | Monad transformer for connection operations.
 newtype ConT m a = ConT { runConT :: ReaderT Con (ExceptT ConError m) a }
@@ -51,9 +81,12 @@
 close :: MonadIO m => ConT m ()
 close = ask >>= close'
 
+
 -- | Close the connection
 close' :: MonadIO m => Con -> m ()
-close' =  liftIO . Streams.write Nothing . conOutput
+close' c = do
+    liftIO . Streams.write Nothing . conOutput $ c
+    liftIO $ conLog c Nothing
 
 -- | Send an ELM327 command.
 send :: MonadIO m => Command -> ConT m ()
@@ -64,7 +97,7 @@
 
 -- | Send all bytes to the ELM327.
 sendBytes :: MonadIO m => ByteString -> ConT m ()
-sendBytes x = ask >>= liftIO . Streams.write (Just x) . conOutput
+sendBytes x = ask >>= liftIO . Streams.write (Just x) . conOutput >> void (log $ Just x)
 
 -- | Send a string to the ELM327.
 sendString :: MonadIO m => String -> ConT m ()
@@ -72,7 +105,7 @@
 
 -- | Receive available ELM327 caracters as a byte string
 recvRaw :: MonadIO m => ConT m (Maybe ByteString)
-recvRaw = ask >>= liftIO . Streams.read . conInput
+recvRaw = ask >>= liftIO . Streams.read . conInput >>= log
 
 -- | Receive an ELM327 response as a byte string.
 recv :: MonadIO m => ConT m (Maybe ByteString)
@@ -81,7 +114,7 @@
     if Char8.null s then return Nothing else return (Just s)
   where
     recv' xs = do
-        bs <- ask >>= liftIO . Streams.read . conInput
+        bs <- ask >>= liftIO . Streams.read . conInput >>= log
         case bs of Nothing -> throwError ConTimeoutError
                    Just bs' -> handle' xs bs'
     handle' xs bs =
@@ -118,3 +151,21 @@
     removeStatusPrefixes x = foldl stripPrefix' x statusPrefixes
     stripPrefix' x pref = fromMaybe x (stripPrefix pref x)
     stripHeader = stripPrefix [0x40 + obdMode cmd, obdPID cmd]
+
+-- | Log sent or received bytes
+log :: MonadIO m => Maybe ByteString -> ConT m (Maybe ByteString)
+log Nothing = return Nothing
+log (Just x) = do
+    log' <- conLog <$> ask
+    liftIO $ log' (Just x)
+    return $ Just x
+
+-- | Create a file logger that can be used with 'conLog'.
+fileLog :: FilePath -> IO (Maybe ByteString -> IO ())
+fileLog fp = do
+    fh <- openFile fp AppendMode
+    hPutStrLn fh "Connecting..."
+    return $ log' fh
+  where
+    log' fh Nothing = hClose fh
+    log' fh (Just x) = Char8.hPut fh x >> hFlush fh
diff --git a/src/System/Hardware/ELM327/Simulator.hs b/src/System/Hardware/ELM327/Simulator.hs
--- a/src/System/Hardware/ELM327/Simulator.hs
+++ b/src/System/Hardware/ELM327/Simulator.hs
@@ -83,7 +83,7 @@
     is <- makeInputStream (produce c)
     os <- makeOutputStream (consume c)
     _ <- forkIO . void $ runStateT (runSimulator c) s
-    return $ Con is os
+    return $ Con is os (const $ return ())
   where
     produce = atomically . readTMChan . _conOutputBuffer
     consume c Nothing = atomically $ closeTMChan (_conInputBuffer c)
