serialport 0.2.0 → 0.3.0
raw patch · 7 files changed
+266/−209 lines, 7 files
Files
- README +3/−0
- System/Hardware/Serialport.hs +13/−11
- System/Hardware/Serialport/Posix.hs +38/−68
- System/Hardware/Serialport/Types.hs +140/−0
- System/Hardware/Serialport/Windows.hs +22/−37
- System/Win32/Comm.hsc +48/−92
- serialport.cabal +2/−1
README view
@@ -41,4 +41,7 @@ - Python serial library http://pyserial.sourceforge.net/ - http://www.easysw.com/~mike/serial/serial.html - Ruby serial port library http://ruby-serialport.rubyforge.org/+ - C++ libraries + - Common cpp: http://www.gnu.org/software/commoncpp/+ - Boost asio serial: http://www.boost.org/
System/Hardware/Serialport.hs view
@@ -2,8 +2,12 @@ -- | Example usage: ----- > s <- openSerial "/dev/ttyUSB0" B9600 8 One NoParity NoFlowControl+-- > import System.Hardware.Serialport -- > +-- > s <- openSerial "/dev/ttyUSB0" defaultSerialSettings+-- > -- or a little different+-- > s <- openSerial "/dev/ttyUSB0" defaultSerialSettings { baudRate = B2400 }+-- > -- > -- Sending -- > forM_ "AT\r" $ sendChar s -- > @@ -17,18 +21,16 @@ module System.Hardware.Serialport ( -- * Types- StopBits(..)+ BaudRate(..)+ ,StopBits(..) ,Parity(..) ,FlowControl(..)- ,BaudRate(..) ,SerialPort-#if defined(linux_HOST_OS)- -- * Simple, non-portable. - --- -- | In an perfect world this would be portable but a System.IO.hWaitForInput on Windows is blocking!- ,hOpenSerial-#endif- -- * Portable methods. + -- * Configure port+ -- | You don't need the get or set functions, they are used by openSerial+ ,SerialPortSettings(..)+ ,defaultSerialSettings+ -- * Serial methods ,openSerial ,sendChar ,recvChar@@ -37,8 +39,8 @@ #if defined(mingw32_HOST_OS) import System.Hardware.Serialport.Windows-import System.Win32.Comm #elif defined(linux_HOST_OS) import System.Hardware.Serialport.Posix import System.Posix.Terminal #endif+import System.Hardware.Serialport.Types
System/Hardware/Serialport/Posix.hs view
@@ -1,51 +1,26 @@ module System.Hardware.Serialport.Posix where import System.IO-import System.Posix.Terminal import System.Posix.IO-import Data.Word---data StopBits = One | Two-data Parity = Even | Odd | NoParity-data FlowControl = Software | NoFlowControl-data SerialPort = SerialPort { handle :: Handle,- timeout :: Int }----- |Open and configure a serial port and return a Handle-hOpenSerial :: String -- ^ The filename of the serial port, such as @\/dev\/ttyS0@ or @\/dev\/ttyUSB0@- -> BaudRate - -> Word8 -- ^ The number of bits per word, typically 8- -> StopBits -- ^ Almost always @One@ unless you're talking to a printer- -> Parity -- ^ Error checking - -> FlowControl- -> IO Handle-hOpenSerial dev baud bPerB stopBits parity flow = - do setSerial dev baud bPerB stopBits parity flow- h <- openBinaryFile dev ReadWriteMode- hSetBuffering h NoBuffering - return h+import System.Posix.Terminal+import System.Hardware.Serialport.Types -- |Open and configure a serial port-openSerial :: String -- ^ The filename of the serial port, such as @\/dev\/ttyS0@ or @\/dev\/ttyUSB0@- -> BaudRate - -> Word8 -- ^ The number of bits per word, typically 8- -> StopBits -- ^ Almost always @One@ unless you're talking to a printer- -> Parity - -> FlowControl- -> Int -- ^ Receive timeout in milliseconds+openSerial :: FilePath -- ^ The filename of the serial port, such as @\/dev\/ttyS0@ or @\/dev\/ttyUSB0@+ -> SerialPortSettings -> IO SerialPort-openSerial dev baud bPerB stopBits parity flow time_ms = - do h <- hOpenSerial dev baud bPerB stopBits parity flow- return $ SerialPort h time_ms+openSerial dev settings =+ do setSerialSettings dev settings+ h <- openBinaryFile dev ReadWriteMode+ hSetBuffering h NoBuffering+ return $ SerialPort h settings -- |Possibly receive a character unless the timeout given in openSerial is exceeded. recvChar :: SerialPort -> IO (Maybe Char)-recvChar (SerialPort h time_ms) =- do have_input <- hWaitForInput h time_ms+recvChar (SerialPort h settings) =+ do have_input <- hWaitForInput h ((timeout settings) * 100) if have_input then do c <- hGetChar h return $ Just c@@ -54,44 +29,38 @@ -- |Send a character sendChar :: SerialPort -> Char -> IO ()-sendChar (SerialPort h _) =- hPutChar h+sendChar (SerialPort h _ ) c =+ hPutChar h c >> return () -- |Close the serial port closeSerial :: SerialPort -> IO ()-closeSerial (SerialPort h _) =+closeSerial (SerialPort h _ ) = hClose h --setSerial :: String - -> BaudRate - -> Word8 - -> StopBits - -> Parity - -> FlowControl- -> IO ()-setSerial dev baud bPerB stopBits parity flow = do- fd <- openFd dev ReadWrite Nothing - OpenFileFlags { append = True,- exclusive = True,- noctty = True,- nonBlock = True,- trunc = False }- termOpts <- getTerminalAttributes fd- let termOpts' = configureSettings termOpts baud bPerB stopBits parity flow- setTerminalAttributes fd termOpts' Immediately- closeFd fd+setSerialSettings :: FilePath -> SerialPortSettings -> IO ()+setSerialSettings dev settings = + do fd <- openFd dev ReadWrite Nothing + OpenFileFlags { append = True,+ exclusive = True,+ noctty = True,+ nonBlock = False,+ trunc = False }+ termOpts <- getTerminalAttributes fd+ let termOpts' = configureSettings termOpts settings+ setTerminalAttributes fd termOpts' Immediately+ closeFd fd withParity :: TerminalAttributes -> Parity -> TerminalAttributes withParity termOpts Even = termOpts `withMode` EnableParity - `withoutMode` OddParity+ `withoutMode` OddParity withParity termOpts Odd = termOpts `withMode` EnableParity- `withMode` OddParity+ `withMode` OddParity withParity termOpts NoParity = termOpts `withoutMode` EnableParity + withFlowControl :: TerminalAttributes -> FlowControl -> TerminalAttributes withFlowControl termOpts NoFlowControl = termOpts `withoutMode` StartStopInput@@ -100,19 +69,20 @@ `withMode` StartStopInput `withMode` StartStopOutput + withStopBits :: TerminalAttributes -> StopBits -> TerminalAttributes withStopBits termOpts One = termOpts `withoutMode` TwoStopBits withStopBits termOpts Two = termOpts `withMode` TwoStopBits -configureSettings :: TerminalAttributes -> BaudRate -> Word8 -> StopBits -> Parity -> FlowControl -> TerminalAttributes-configureSettings termOpts baud bPerB stopBits parity flow =- termOpts `withInputSpeed` baud- `withOutputSpeed` baud- `withBits` (fromIntegral bPerB :: Int)- `withStopBits` stopBits- `withParity` parity- `withFlowControl` flow+configureSettings :: TerminalAttributes -> SerialPortSettings -> TerminalAttributes+configureSettings termOpts settings =+ termOpts `withInputSpeed` (baudRate settings)+ `withOutputSpeed` (baudRate settings)+ `withBits` (fromIntegral (bitsPerWord settings))+ `withStopBits` (stopb settings)+ `withParity` (parity settings)+ `withFlowControl` (flowControl settings) `withoutMode` EnableEcho `withoutMode` EchoErase `withoutMode` EchoKill
+ System/Hardware/Serialport/Types.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE CPP #-}+module System.Hardware.Serialport.Types where++import Data.Word+#if defined(linux_HOST_OS)+import System.Posix.Terminal+import System.IO+#elif defined(mingw32_HOST_OS)+import System.Win32.Types++-- | Same as System.Posix.Terminal+data BaudRate+ = B0+ | B50+ | B75+ | B110+ | B134+ | B150+ | B200+ | B300+ | B600+ | B1200+ | B1800+ | B2400+ | B4800+ | B9600+ | B19200+ | B38400+ | B57600+ | B115200+#endif++fromBaudToInt :: BaudRate -> Int+fromBaudToInt b =+ case b of+ B0 -> 0+ B50 -> 50+ B75 -> 75+ B110 -> 110+ B134 -> 134+ B150 -> 150+ B200 -> 200+ B300 -> 300+ B600 -> 600+ B1200 -> 1200+ B1800 -> 1800+ B2400 -> 2400+ B4800 -> 4800+ B9600 -> 9600+ B19200 -> 19200+ B38400 -> 38400+ B57600 -> 57600+ B115200 -> 115200+++fromIntToBaud :: Int -> BaudRate+fromIntToBaud i =+ case i of+ 0 -> B0+ 50 -> B50+ 75 -> B75+ 110 -> B110+ 134 -> B134+ 150 -> B150+ 200 -> B200+ 300 -> B300+ 600 -> B600+ 1200 -> B1200+ 1800 -> B1800+ 2400 -> B2400+ 4800 -> B4800+ 9600 -> B9600+ 19200 -> B19200+ 38400 -> B38400+ 57600 -> B57600+ 115200 -> B115200+ _ -> error $ "unsupported baudrate " ++ show i+ +++data StopBits = One | Two+data Parity = Even | Odd | NoParity+data FlowControl = Software | NoFlowControl++data SerialPortSettings = SerialPortSettings {+ baudRate :: BaudRate, -- ^ baudrate+ bitsPerWord :: Word8, -- ^ Number of bits in a word+ stopb :: StopBits, -- ^ Number of stop bits+ parity :: Parity, -- ^ Type of parity+ flowControl :: FlowControl, -- ^ Type of flowcontrol+ timeout :: Int -- ^ Timeout when receiving a char in tenth of seconds+ }+++instance Show SerialPortSettings where+ show settings = baudrate ++ "/" ++ bits ++ "-" ++ par ++ "-" ++ stopbits ++ " (" ++ flow ++ ", " ++ wait ++ ")"+ where+ baudrate = show $ fromBaudToInt $ baudRate settings+ bits = show $ bitsPerWord settings+ par = case parity settings of+ Even -> "E"+ Odd -> "O"+ NoParity -> "N"+ stopbits = case stopb settings of+ One -> "1"+ Two -> "2"+ flow = case flowControl settings of+ NoFlowControl -> "no flow control"+ Software -> "software flow control"+ wait = "timeout:" ++ show ((timeout settings) * 100)+ ++data SerialPort = SerialPort { +#if defined(mingw32_HOST_OS)+ handle :: HANDLE,+#else+ handle :: Handle,+#endif+ newSettings :: SerialPortSettings+ }+++-- | Most commonly used configuration+-- +-- - 9600 baud+--+-- - 8 data bits+--+-- - 1 stop bit+--+-- - no parity+--+-- - no flow control+--+-- - 0.1 millisecond receive timeout+--+defaultSerialSettings :: SerialPortSettings+defaultSerialSettings =+ SerialPortSettings B9600 8 One NoParity NoFlowControl 1+
System/Hardware/Serialport/Windows.hs view
@@ -1,8 +1,7 @@ module System.Hardware.Serialport.Windows where -import Data.Word import Data.Bits-import System.Win32.Comm+import qualified System.Win32.Comm as Comm import System.Win32.Types import System.Win32.File import Foreign.C.Types@@ -10,24 +9,17 @@ import Foreign.Marshal.Utils import Foreign.Marshal.Alloc import Foreign.Storable+import System.Hardware.Serialport.Types -data SerialPort = SerialPort { handle :: HANDLE,- timeout :: Int }- -- | Open and configure a serial port openSerial :: String -- ^ The filename of the serial port, such as @COM5@ or @\/\/.\/CNCA0@- -> BaudRate- -> Word8- -> StopBits- -> Parity- -> FlowControl- -> Int -- ^ Receive timeout in milliseconds+ -> SerialPortSettings -> IO SerialPort-openSerial dev baud bPerB stopb par flow time_ms = +openSerial dev settings = do h <- createFile dev access_mode share_mode security_attr create_mode file_attr template_file- setSerial h baud bPerB stopb par flow time_ms- return $ SerialPort h time_ms+ setSerialSettings h settings+ return $ SerialPort h settings where access_mode = gENERIC_READ .|. gENERIC_WRITE share_mode = fILE_SHARE_NONE@@ -67,30 +59,23 @@ closeHandle h -setSerial :: HANDLE- -> BaudRate - -> Word8 - -> StopBits - -> Parity - -> FlowControl- -> Int- -> IO ()-setSerial h baud bPerB stopb parit flow time_ms = - do let ct = COMMTIMEOUTS { - readIntervalTimeout = 0, - readTotalTimeoutMultiplier = fromIntegral time_ms, - readTotalTimeoutConstant = 0, - writeTotalTimeoutMultiplier = 0, - writeTotalTimeoutConstant = 0 }- setCommTimeouts h ct+setSerialSettings :: HANDLE -> SerialPortSettings -> IO ()+setSerialSettings h settings =+ do let ct = Comm.COMMTIMEOUTS { + Comm.readIntervalTimeout = maxBound :: DWORD, + Comm.readTotalTimeoutMultiplier = maxBound :: DWORD, + Comm.readTotalTimeoutConstant = fromIntegral (timeout settings) * 100, + Comm.writeTotalTimeoutMultiplier = 0, + Comm.writeTotalTimeoutConstant = 0 }+ Comm.setCommTimeouts h ct - let dcb' = DCB {- baudRate = baud,- parity = parit,- stopBits = stopb,- flowControl = flow,- byteSize = bPerB }- setCommState h dcb'+ let dcb = Comm.DCB {+ Comm.baudRate = (baudRate settings),+ Comm.parity = (parity settings),+ Comm.stopBits = (stopb settings),+ Comm.flowControl = (flowControl settings),+ Comm.byteSize = (bitsPerWord settings) }+ Comm.setCommState h dcb return ()
System/Win32/Comm.hsc view
@@ -4,137 +4,82 @@ import System.IO import System.Win32.Types import Data.Word+import Data.Bits import Foreign.Storable import Foreign.Ptr import Foreign.Marshal.Alloc import Foreign.Marshal.Utils+import qualified System.Hardware.Serialport.Types as STypes #include <windows.h> -data BaudRate- = B0- | B50- | B75- | B110- | B134- | B150- | B200- | B300- | B600- | B1200- | B1800- | B2400- | B4800- | B9600- | B19200- | B38400- | B57600- | B115200- deriving Show -data StopBits = One | Two deriving Show-data Parity = Even | Odd | NoParity deriving Show-data FlowControl = Software | NoFlowControl deriving Show-- type LPDCB = Ptr DCB data DCB = DCB- { baudRate :: BaudRate,- parity :: Parity,- stopBits :: StopBits,- flowControl :: FlowControl,- byteSize :: Word8- } deriving Show+ { baudRate :: STypes.BaudRate,+ parity :: STypes.Parity,+ stopBits :: STypes.StopBits,+ byteSize :: Word8,+ flowControl :: STypes.FlowControl+ } --- If this member is TRUE, binary mode is enabled. Windows does not support nonbinary mode transfers, so this member must be TRUE.+-- | If this member is TRUE, binary mode is enabled. Windows does not support nonbinary mode transfers, so this member must be TRUE. fBinary :: DWORD fBinary = 0x0000000000000001 +-- | If this member is TRUE, parity checking is performed and errors are reported.+fParity :: DWORD+fParity = 0x0000000000000010++ instance Storable DCB where sizeOf = const #size DCB alignment = sizeOf poke buf dcb = do (#poke DCB, DCBlength) buf (sizeOf dcb)- (#poke DCB, BaudRate) buf (case (baudRate dcb) of- B0 -> 0- B50 -> 50- B75 -> 75- B110 -> 110- B134 -> 134- B150 -> 150- B200 -> 200- B300 -> 300- B600 -> 600- B1200 -> 1200- B1800 -> 1800- B2400 -> 2400- B4800 -> 4800- B9600 -> 9600- B19200 -> 19200- B38400 -> 38400- B57600 -> 57600- B115200 -> 115200 :: DWORD)- pokeByteOff buf 8 (fBinary :: DWORD)+ (#poke DCB, BaudRate) buf (fromIntegral (STypes.fromBaudToInt (baudRate dcb)) :: DWORD)+ pokeByteOff buf 8 (fBinary .|. fParity :: DWORD) (#poke DCB, wReserved) buf (0 :: WORD) (#poke DCB, XonLim) buf (2048 :: WORD) (#poke DCB, XoffLim) buf (512 :: WORD) (#poke DCB, ByteSize) buf (byteSize dcb :: BYTE) (#poke DCB, Parity) buf (case (parity dcb) of- NoParity -> 0- Odd -> 1- Even -> 2 :: BYTE)+ STypes.NoParity -> 0+ STypes.Odd -> 1+ STypes.Even -> 2 :: BYTE) (#poke DCB, StopBits) buf (case (stopBits dcb) of- One -> 0- Two -> 2+ STypes.One -> 0+ STypes.Two -> 2 :: BYTE) (#poke DCB, wReserved1) buf (0 :: WORD) peek buf = do _dCBlength <- (#peek DCB, DCBlength) buf :: IO DWORD _baudRate <- do _baud <- (#peek DCB, BaudRate) buf :: IO DWORD- case _baud of- 0 -> return B0- 50 -> return B50- 75 -> return B75- 110 -> return B110- 134 -> return B134- 150 -> return B150- 200 -> return B200- 300 -> return B300- 600 -> return B600- 1200 -> return B1200- 1800 -> return B1800- 2400 -> return B2400- 4800 -> return B4800- 9600 -> return B9600- 19200 -> return B19200- 38400 -> return B38400- 57600 -> return B57600- 115200 -> return B115200- _ -> fail "incorrect baudrate"+ return $ STypes.fromIntToBaud (fromIntegral _baud :: Int) _fsettings <- peekByteOff buf 8 :: IO DWORD _byteSize <- (#peek DCB, ByteSize) buf :: IO BYTE _parity <- do _par <- (#peek DCB, Parity) buf :: IO BYTE case _par of- 0 -> return NoParity- 1 -> return Odd- 2 -> return Even- --3 -> markparity- --4 -> spaceparity- _ -> fail $ "incorrect parity" ++ (show _par)+ 0 -> return STypes.NoParity+ 1 -> return STypes.Odd+ 2 -> return STypes.Even+ 3 -> fail $ "unsupported markparity"+ 4 -> fail $ "unsupported spaceparity"+ _ -> fail $ "unsupported parity" ++ (show _par) _stopBits <- do _stopb <- (#peek DCB, StopBits) buf :: IO BYTE case _stopb of- 0 -> return One- -- 1 -> one5stopbits- 2 -> return Two+ 0 -> return STypes.One+ 1 -> fail $ "unsupported one5stopbits"+ 2 -> return STypes.Two _ -> fail "unexpected stop bit count" _XonLim <- (#peek DCB, XonLim) buf :: IO WORD _XoffLim <- (#peek DCB, XoffLim) buf :: IO WORD return DCB { baudRate = _baudRate, parity = _parity, stopBits = _stopBits,- flowControl = NoFlowControl,+ flowControl = STypes.NoFlowControl, byteSize = _byteSize } @@ -156,8 +101,7 @@ setCommState :: HANDLE -> DCB -> IO () setCommState h dcb =- do with dcb (c_SetCommState h)- return ()+ failIfFalse_ "setCommState" ( with dcb (c_SetCommState h)) --BOOL WINAPI SetCommState(@@ -171,7 +115,15 @@ -+-- |+-- If an application sets ReadIntervalTimeout and ReadTotalTimeoutMultiplier to MAXDWORD and sets ReadTotalTimeoutConstant to a value greater than zero and less than MAXDWORD, one of the following occurs when the ReadFile function is called:+--+-- * If there are any bytes in the input buffer, ReadFile returns immediately with the bytes in the buffer.+--+-- * If there are no bytes in the input buffer, ReadFile waits until a byte arrives and then returns immediately.+--+-- * If no bytes arrive within the time specified by ReadTotalTimeoutConstant, ReadFile times out.+-- type LPCOMMTIMEOUTS = Ptr COMMTIMEOUTS data COMMTIMEOUTS = COMMTIMEOUTS { readIntervalTimeout :: DWORD, -- in milliseconds @@ -217,10 +169,14 @@ c_GetCommTimeouts :: HANDLE -> LPCOMMTIMEOUTS -> IO BOOL ++-- | +-- +-- On success it returns nonzero. On failure, the return value is zero and the GetLastError should be called.+-- setCommTimeouts :: HANDLE -> COMMTIMEOUTS -> IO () setCommTimeouts h ct =- do with ct (c_SetCommTimeouts h)- return ()+ failIfFalse_ "setCommTimeouts" ( with ct (c_SetCommTimeouts h) ) -- setcommtimeouts
serialport.cabal view
@@ -1,5 +1,5 @@ Name: serialport-Version: 0.2.0+Version: 0.3.0 Cabal-Version: >= 1.2 Build-Type: Simple license: BSD3@@ -23,6 +23,7 @@ Library Exposed-Modules: System.Hardware.Serialport+ System.Hardware.Serialport.Types Build-Depends: base >= 4, base < 5 ghc-options: -Wall