packages feed

hArduino (empty) → 0.1

raw patch · 16 files changed

+598/−0 lines, 16 filesdep +basedep +bytestringdep +mtlsetup-changed

Dependencies added: base, bytestring, mtl, process, serialport

Files

@@ -0,0 +1,5 @@+Copyright (c) 2013, Levent Erkok (erkokl@gmail.com)+All rights reserved.++The usbArduino program is distributed with the BSD3 license. See the LICENSE file+for details.
+ INSTALL view
@@ -0,0 +1,9 @@+The usbArduino library can be installed simply by issuing cabal install+like this:++     cabal install sbv++The package depends on the usb library itself, which in turn relies on+the availability of the libusb package on your platform. See notes+at https://github.com/basvandijk/bindings-libusb for details on how+to get the latter.
+ LICENSE view
@@ -0,0 +1,26 @@+usbArduino: Communicate with your Arduino board over USB++Copyright (c) 2013, Levent Erkok (erkokl@gmail.com)+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:+    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.+    * Redistributions in binary form must reproduce the above copyright+      notice, this list of conditions and the following disclaimer in the+      documentation and/or other materials provided with the distribution.+    * Neither the name of the developer (Levent Erkok) nor the+      names of its contributors may be used to endorse or promote products+      derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL LEVENT ERKOK BE LIABLE FOR ANY+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README view
@@ -0,0 +1,1 @@+Please see http://leventerkok.github.com/hArduino.
+ RELEASENOTES view
@@ -0,0 +1,11 @@+Hackage: http://hackage.haskell.org/package/hArduino+GitHub:  http://leventerkok.github.com/hArduino ++Latest Hackage released version: 0.1++======================================================================+Version 0.1, 2013-01-14++ - Initial design+ - Blink example operational+ - Home page at: http://leventerkok.github.com/hArduino 
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ System/Hardware/Arduino.hs view
@@ -0,0 +1,42 @@+-------------------------------------------------------------------------------+-- |+-- Module      :  System.Hardware.Arduino+-- Copyright   :  (c) Levent Erkok+-- License     :  BSD3+-- Maintainer  :  erkokl@gmail.com+-- Stability   :  experimental+--+-- (The hArduino library is hosted at <http://leventerkok.github.com/hArduino/>.+-- Comments, bug reports, and patches are always welcome.)+--+-- hArduino: Control Arduino from Haskell, using the Firmata protocol.+--+-- The hArduino library allows construction of Haskell programs that control+-- Arduino boards that are running the (freely available) Firmata program. Note+-- that hArduino does /not/ allow you to run arbitrary Haskell code on the+-- Arduino! It simply allows you to control a board from Haskell, where you+-- can exchange information with the board, send/receive commands from other+-- peripherals connected, etc.+-------------------------------------------------------------------------------+module System.Hardware.Arduino (+  -- * Running the controller+  withArduino, Arduino+  -- * Programming the Arduino+  -- ** Basic handshake with the board+  , queryFirmware+  -- ** Controlling the pins+  , setPinMode+  -- ** Reading and Writing digital values+  , digitalRead, digitalWrite+  -- ** Misc utilities+  , delay+  -- * Hardware components on the board+  -- ** Pins+  , pin, PinMode(..)+ )+ where++import System.Hardware.Arduino.Data+import System.Hardware.Arduino.Comm+import System.Hardware.Arduino.Firmata+import System.Hardware.Arduino.Parts
+ System/Hardware/Arduino/Comm.hs view
@@ -0,0 +1,84 @@+-------------------------------------------------------------------------------+-- |+-- Module      :  System.Hardware.Arduino.Comm+-- Copyright   :  (c) Levent Erkok+-- License     :  BSD3+-- Maintainer  :  erkokl@gmail.com+-- Stability   :  experimental+--+-- Basic serial communication routines+-------------------------------------------------------------------------------++{-# LANGUAGE NamedFieldPuns #-}+module System.Hardware.Arduino.Comm where++import Control.Monad.State (modify, runStateT, when)++import qualified Data.ByteString            as B (pack, unpack, concat, length)+import qualified System.Hardware.Serialport as S (withSerial, defaultSerialSettings, CommSpeed(CS57600), commSpeed, recv, send)++import System.Hardware.Arduino.Data+import System.Hardware.Arduino.Utils+import System.Hardware.Arduino.Protocol+import System.Hardware.Arduino.Firmata++-- | Run the Haskell program to control the board:+--+--    * The file path argument should point to the device file that is+--      associated with the board. ('COM1' on Windows,+--      '/dev/cu.usbmodemfd131' on Mac, etc.)+--+--    * The boolean argument controls verbosity. It should remain+--      'False' unless you have communication issues. The print-out+--      is typically less-than-useful, but it might point to the root+--      cause of the problem.+--+-- See "System.Hardware.Arduino.Examples.Blink" for a simple example.+withArduino :: Bool       -- ^ If 'True', debugging info will be printed+            -> FilePath   -- ^ Path to the USB port+            -> Arduino () -- ^ The Haskell controller program to run+            -> IO ()+withArduino verbose fp program =+        do debugger <- mkDebugPrinter verbose+           debugger $ "Accessing arduino located at: " ++ show fp+           let Arduino controller = do (v1, v2, m) <- queryFirmware+                                       modify (\b -> b{firmataID = "Firmware v" ++ show v1 ++ "." ++ show v2 ++ "(" ++ m ++ ")"})+                                       program+           S.withSerial fp S.defaultSerialSettings{S.commSpeed = S.CS57600} $ \port -> do+                _ <- runStateT controller (mkState debugger port)+                return ()+ where mkState debugger port = ArduinoState debugger port "ID: Uninitialized" (Just (ArduinoChannel recvChan recvNChan sendChan))+        where extract b = do let resp = unpackage b+                             debugger $ "Received: " ++ show resp+                             return resp+              recvChan = do debugger "Waiting for a Sysex response.."+                            let skip = do b <- S.recv port 1+                                          case B.unpack b of+                                            []     -> skip+                                            [0xF0] -> return ()+                                            bs     -> do debugger $ "Skipping bytes <" ++ unwords (map showByte bs) ++ ">"+                                                         skip+                                collect sofar = do b <- S.recv port 1+                                                   let rmsg = b : sofar+                                                   if b == B.pack [0xF7] -- end message+                                                      then return $ reverse rmsg+                                                      else collect rmsg+                            skip+                            chunks <- collect [B.pack [0xF0]]+                            extract $ B.concat chunks+              recvNChan n = do debugger $ "Waiting for a non-Sysex response of " ++ show n ++ " bytes"+                               let go need sofar+                                    | need <= 0  = return sofar+                                    | True       = do b <- S.recv port need+                                                      case B.length b of+                                                        0 -> go need sofar+                                                        l -> do when (need < l) $ debugger $ "Received partial response: <" ++ unwords (map showByte (B.unpack b)) ++ ">"+                                                                go (need - l) (b : sofar)+                               chunks <- go n []+                               extract $ B.concat $ reverse chunks+              sendChan msg = do let p  = package msg+                                    lp = B.length p+                                debugger $ "Sending: " ++ show msg ++ " <" ++ unwords (map showByte (B.unpack p)) ++ ">"+                                sent <- S.send port p+                                when (sent /= lp)+                                     (debugger $ "Send failed. Tried: " ++ show lp ++ "bytes, reported: " ++ show sent)
+ System/Hardware/Arduino/Data.hs view
@@ -0,0 +1,65 @@+-------------------------------------------------------------------------------+-- |+-- Module      :  System.Hardware.Arduino.Data+-- Copyright   :  (c) Levent Erkok+-- License     :  BSD3+-- Maintainer  :  erkokl@gmail.com+-- Stability   :  experimental+--+-- Underlying data structures+-------------------------------------------------------------------------------++{-# LANGUAGE DeriveFunctor               #-}+{-# LANGUAGE GeneralizedNewtypeDeriving  #-}+{-# LANGUAGE NamedFieldPuns              #-}+module System.Hardware.Arduino.Data where++import Control.Monad.State              (StateT, MonadIO, MonadState, gets, liftIO)+import Data.Maybe                       (fromMaybe)+import System.Hardware.Serialport       (SerialPort)++import System.Hardware.Arduino.Protocol++-- | Basic communication channel with the board.+data ArduinoChannel = ArduinoChannel {+                  recvChan  :: IO Response              -- ^ Receive a sys-ex response.+                , recvNChan :: Int -> IO Response       -- ^ Receive a non-sys-ex response that has /n/ bytes in it+                , sendChan  :: Request -> IO ()         -- ^ Send a request down to Arduino+                }++-- | State of the board and other machinery.+data ArduinoState = ArduinoState {+                message       :: String -> IO ()        -- ^ Current debugging routine+              , port          :: SerialPort             -- ^ Port we are communicating on+              , firmataID     :: String                 -- ^ The ID of the board (as identified by the Board itself)+              , deviceChannel :: Maybe ArduinoChannel   -- ^ Communication channel+              }++-- | The Arduino monad.+newtype Arduino a = Arduino (StateT ArduinoState IO a)+                  deriving (Functor, Monad, MonadIO, MonadState ArduinoState)++-- | Retrieve the current channel.+getChannel :: Arduino ArduinoChannel+getChannel = fromMaybe die `fmap` gets deviceChannel+  where die = error "Cannot communicate with the board!"++-- | Send down a request.+send :: Request -> Arduino ()+send r = do ArduinoChannel{sendChan} <- getChannel+            liftIO $ sendChan r++-- | Receive a sys-ex response.+recv :: Arduino Response+recv = do ArduinoChannel{recvChan} <- getChannel+          liftIO recvChan++-- | Receive a non-sys-ex response, that has /n/ bytes+recvN :: Int -> Arduino Response+recvN n = do ArduinoChannel{recvNChan} <- getChannel+             liftIO $ recvNChan n++-- | Debugging only: print it on stdout.+debug :: String -> Arduino ()+debug s = do f <- gets message+             liftIO $ f s
+ System/Hardware/Arduino/Examples/Blink.hs view
@@ -0,0 +1,33 @@+-------------------------------------------------------------------------------+-- |+-- Module      :  System.Hardware.Arduino.Examples.Blink+-- Copyright   :  (c) Levent Erkok+-- License     :  BSD3+-- Maintainer  :  erkokl@gmail.com+-- Stability   :  experimental+--+-- The /hello world/ of the arduino world, blinking the led.+-------------------------------------------------------------------------------++module System.Hardware.Arduino.Examples.Blink where++import Control.Monad           (forever)+import Control.Monad.Trans     (liftIO)+import System.Hardware.Arduino++-- | Blink the led connected to port 13 on the Arduino UNO board.+-- The blinking will synchronize with the printing of a dot on stdout.+--+-- Depending on your set-up, you will need to change the path to the+-- USB board. If you have problems, try changing the first argument+-- to 'True' in the call to 'withArduino', which will hopefully print+-- a useful diagnostic message.+blink :: IO ()+blink = withArduino False "/dev/cu.usbmodemfd131" $ do+           let led = pin 13+           setPinMode led OUTPUT+           forever $ do liftIO $ putStr "."+                        digitalWrite led True+                        delay 1000+                        digitalWrite led False+                        delay 1000
+ System/Hardware/Arduino/Firmata.hs view
@@ -0,0 +1,16 @@+-------------------------------------------------------------------------------+-- |+-- Module      :  System.Hardware.Arduino.Firmata+-- Copyright   :  (c) Levent Erkok+-- License     :  BSD3+-- Maintainer  :  erkokl@gmail.com+-- Stability   :  experimental+--+-- The Firmata protocol, as implemented in Haskell.+-- See: www.firmata.org+-------------------------------------------------------------------------------+module System.Hardware.Arduino.Firmata+  (module System.Hardware.Arduino.Firmata.Basics)+  where++import System.Hardware.Arduino.Firmata.Basics
+ System/Hardware/Arduino/Firmata/Basics.hs view
@@ -0,0 +1,65 @@+-------------------------------------------------------------------------------+-- |+-- Module      :  System.Hardware.Arduino.Firmata.Basics+-- Copyright   :  (c) Levent Erkok+-- License     :  BSD3+-- Maintainer  :  erkokl@gmail.com+-- Stability   :  experimental+--+-- Basic components of the firmata protocol+-------------------------------------------------------------------------------++{-# LANGUAGE NamedFieldPuns #-}+module System.Hardware.Arduino.Firmata.Basics where++import Control.Monad.Trans (liftIO)+import Data.Word           (Word8)+import Data.Bits           (shiftL)++import System.Hardware.Arduino.Data+import System.Hardware.Arduino.Parts+import System.Hardware.Arduino.Protocol+import qualified System.Hardware.Arduino.Utils as U++-- | Retrieve the Firmata firmware version running on the Arduino. The first+-- component is the major, second is the minor. The final value is a human+-- readable identifier for the particular board.+queryFirmware :: Arduino (Word8, Word8, String)+queryFirmware = do+        send QueryFirmware+        r <- recv+        case r of+          Firmware v1 v2 m -> return (v1, v2, m)+          _                -> error $ "Got unexpected response for query firmware call: " ++ show r++-- | Delay the computaton for a given number of milli-seconds.+delay :: Int -> Arduino ()+delay = liftIO . U.delay++-- | Set the mode on a particular pin on the board.+setPinMode :: Pin -> PinMode -> Arduino ()+setPinMode p m = send $ SetPinMode p m++-- | Read the value of a pin in digital mode.+digitalRead :: Pin -> Arduino (PinMode, Bool)+digitalRead p = do+        send $ DigitalRead p+        r <- recv+        case r of+          DigitalPinState p' m b -> if p == p'+                                    then return (m, b)+                                    else error $ "Got unexpected response for " ++ show p' ++ " instead of " ++ show p+          _                -> error $ "Got unexpected response for query DigitalRead: " ++ show r++-- | Set or clear a particular digital pin on the board.+digitalWrite :: Pin -> Bool -> Arduino ()+digitalWrite p v = do+        let (port, idx) = pinPort p+            dr n+             | n > 2 && n < 14 = digitalRead (pin n)+             | True            = return (OUTPUT, False)+        oldVal <- map snd `fmap` mapM dr [port * 8 + i | i <- [0 .. 7]]+        let [b0, b1, b2, b3, b4, b5, b6, b7] = [if i == idx then v else old | (old, i) <- zip oldVal [0 ..]]+            lsb = sum [1 `shiftL` i | (True, i) <- zip [b0, b1, b2, b3, b4, b5, b6, False] [0 .. 7]]+            msb = if b7 then 1 else 0+        send $ DigitalPortWrite port lsb msb
+ System/Hardware/Arduino/Parts.hs view
@@ -0,0 +1,45 @@+-------------------------------------------------------------------------------+-- |+-- Module      :  System.Hardware.Arduino.Firmata.Parts+-- Copyright   :  (c) Levent Erkok+-- License     :  BSD3+-- Maintainer  :  erkokl@gmail.com+-- Stability   :  experimental+--+-- Basic Arduino hardware abstractions, pins etc.+-------------------------------------------------------------------------------++module System.Hardware.Arduino.Parts(PinMode(..), Pin, pin, pinNo, pinPort) where++-- | A pin on the Arduino+data Pin = Pin { pinNo :: Int   -- ^ The pin number+               }+         deriving Eq++instance Show Pin where+  show p | i < 10 = "Pin0" ++ show i+         | True   = "Pin"  ++ show i+   where i = pinNo p++-- | Smart constructor for a pin. The input should be between 1 and 13:+--+--     * Pins 0-1 are reserved for TX/RX; so can't be directly used.+--+--     * 13 pins is UNO specific, we will need to expand this definition if+--       we start supporting other boards.+pin :: Int -> Pin+pin i | i < 2 || i > 13 = error $ "Invalid pin number: " ++ show i+      | True            = Pin i++-- | On the Arduino, pins are grouped into banks of 8.+-- Given a pin, this function determines which port/index it belongs to+pinPort :: Pin -> (Int, Int)+pinPort p = pinNo p `quotRem` 8++-- | The mode for a pin.+data PinMode = INPUT+             | OUTPUT+             | ANALOG+             | PWM+             | SERVO+             deriving (Show, Enum)
+ System/Hardware/Arduino/Protocol.hs view
@@ -0,0 +1,97 @@+-------------------------------------------------------------------------------+-- |+-- Module      :  System.Hardware.Arduino.Protocol+-- Copyright   :  (c) Levent Erkok+-- License     :  BSD3+-- Maintainer  :  erkokl@gmail.com+-- Stability   :  experimental+--+-- Internal representation of the firmata protocol.+-------------------------------------------------------------------------------++module System.Hardware.Arduino.Protocol(Request(..), Response(..), package, unpackage) where++import Data.Bits ((.|.), (.&.), shiftL)+import Data.Char (chr)+import Data.List (intercalate)+import Data.Word (Word8, Word16)++import qualified Data.ByteString as B++import System.Hardware.Arduino.Parts+import System.Hardware.Arduino.Utils++-- | A request, as sent to Arduino+data Request = QueryFirmware                     -- ^ Query the Firmata version installed+             | SetPinMode      Pin PinMode       -- ^ Set a pin to a particular mode+             | DigitalRead     Pin               -- ^ Read the value of a given pin+             | DigitalPortWrite Int Word8 Word8  -- ^ Write the values of pins on the given port; 2 bytes lo/hi++instance Show Request where+   show QueryFirmware            = "QueryFirmWare"+   show (SetPinMode p m)         = "SetPinMode "   ++ show p ++ " to " ++ show m+   show (DigitalRead p)          = "DigitalRead "  ++ show p+   show (DigitalPortWrite p l h) = "DigitalWrite " ++ show p ++ " to " ++ showBin l ++ "_" ++ showBin h++-- | A response, as returned from the Arduino+data Response = Firmware  Word8 Word8 String       -- ^ Firmware version (maj/min and indentifier+              | DigitalPinState Pin PinMode Bool   -- ^ State of a given pin+              | DigitalPortState Int Word16        -- ^ State of a given port+              | Unknown [Word8]                    -- ^ Represents messages currently unsupported++instance Show Response where+  show (Firmware majV minV n)  = "Firmware v" ++ show majV ++ "." ++ show minV ++ " (" ++ n ++ ")"+  show (DigitalPinState p m v) = "DigitalPinState " ++ show p ++ "(" ++ show m ++ ") = " ++ if v then "HIGH" else "LOW"+  show (DigitalPortState p w)  = "DigitalPortState " ++ show p ++ " = " ++ show w+  show (Unknown bs)            = "Unknown [" ++ intercalate ", " (map showByte bs) ++ "]"++-- | Marker for the start of a sys-ex message+cdStart :: Word8+cdStart = 0xf0++-- | Marker for the end of a sys-ex message+cdEnd :: Word8+cdEnd   = 0xf7++-- | Wrap a sys-ex message to be sent to the board+sysEx :: [Word8] -> B.ByteString+sysEx bs = B.pack $ cdStart : bs ++ [cdEnd]++-- | Package a request as a sequence of bytes to be sent to the board+-- using the Firmata protocol.+package :: Request -> B.ByteString+package QueryFirmware            = sysEx  [0x79]+package (SetPinMode p m)         = B.pack [0xf4, fromIntegral (pinNo p), fromIntegral (fromEnum m)]+package (DigitalRead p)          = sysEx  [0x6d, fromIntegral (pinNo p)]+package (DigitalPortWrite p l m) = B.pack [0x90 .|. fromIntegral p, l, m]++-- | Unpackage a series of bytes as received from the board into a Response+unpackage :: B.ByteString -> Response+unpackage inp+  | length bs < 2 || head bs /= cdStart || last bs /= cdEnd+  = Unknown bs+  | True+  = getResponse (init (tail bs))+  where bs = B.unpack inp++-- | Parse a response message. TBD: Use a proper (cereal based?) parser.+getResponse :: [Word8] -> Response+getResponse (rf : majV : minV : rest)+  | rf == 0x79+  = Firmware majV minV (getString rest)+getResponse (pr : curPin : pinMode : pinState : [])+  | pr == 0x6e+  = DigitalPinState (pin (fromIntegral curPin)) (toEnum (fromIntegral pinMode)) (pinState /= 0)+getResponse (dpr : vL : vH : [])+  | dpr .&. 0xF0 == 0x90+  = let port = fromIntegral (dpr .&. 0x0F)+        w    = fromIntegral ((vH .&. 0x7F) `shiftL` 8) .|. fromIntegral (vL .&. 0x7F)+    in DigitalPortState port w+getResponse bs = Unknown bs++-- | Turn a lo/hi encoded Arduino string constant into a Haskell string+getString :: [Word8] -> String+getString []         = ""+getString [a]        = [chr (fromIntegral a)]  -- shouldn't happen, but no need to error out either+getString (l:h:rest) = c : getString rest+  where c = chr $ fromIntegral $ h `shiftL` 8 .|. l
+ System/Hardware/Arduino/Utils.hs view
@@ -0,0 +1,52 @@+-------------------------------------------------------------------------------+-- |+-- Module      :  System.Hardware.Arduino.Utils+-- Copyright   :  (c) Levent Erkok+-- License     :  BSD3+-- Maintainer  :  erkokl@gmail.com+-- Stability   :  experimental+--+-- Internal utilities+-------------------------------------------------------------------------------+module System.Hardware.Arduino.Utils where++import Control.Concurrent (threadDelay)+import Control.Monad      (void)+import Data.Char          (isAlphaNum, isAscii, isSpace, chr)+import Data.IORef         (newIORef, readIORef, writeIORef)+import Data.Word          (Word8)+import System.Process     (system)+import System.Info        (os)+import Numeric            (showHex, showIntAtBase, showFFloat)++-- | Delay (wait) for the given number of milli-seconds+-- NB. The 'threadDelay' function is broken on Mac, see <http://hackage.haskell.org/trac/ghc/ticket/7299>+-- Until there's a new GHC release that fixes this issue, we temporarily use system/sleep on Mac.+delay :: Int -> IO ()+delay n+  | os == "darwin"+  = void $ system $ "sleep " ++ showFFloat (Just 2) (fromIntegral n / (1000 :: Double)) ""+  | True+  = threadDelay (n*1000)++-- | A simple printer that can keep track of sequence numbers. Used for debugging purposes.+mkDebugPrinter :: Bool -> IO (String -> IO ())+mkDebugPrinter False = return (const (return ()))+mkDebugPrinter True  = do+        cnt <- newIORef (1::Int)+        let f s = do i <- readIORef cnt+                     writeIORef cnt (i+1)+                     putStrLn $ "[" ++ show i ++ "] hArduino: " ++ s+        return f++-- | Show a byte in a visible format.+showByte :: Word8 -> String+showByte i | isVisible = [c]+           | i <= 0xf  = '0' : showHex i ""+           | True      = showHex i ""+  where c = chr $ fromIntegral i+        isVisible = isAscii c && isAlphaNum c && isSpace c++-- | Show a number as a binary value+showBin :: (Integral a, Show a) => a -> String+showBin n = showIntAtBase 2 (head . show) n ""
+ hArduino.cabal view
@@ -0,0 +1,45 @@+Name:          hArduino+Version:       0.1+Category:      Hardware+Synopsis:      Control your Arduino board from Haskell.+Description:   Control Arduino from Haskell, using the Firmata protocol.+               .+               The hArduino library allows construction of Haskell programs that control+               Arduino boards that are running the (freely available) Firmata program. Note+               that hArduino does /not/ allow you to run arbitrary Haskell code on the+               Arduino! It simply allows you to control a board from Haskell, where you+               can exchange information with the board, send/receive commands from other+               peripherals connected, etc.+               .+               hArduino is work-in-progress. Comments, bug-reports, and patches are welcome.+Copyright:     Levent Erkok, 2013+License:       BSD3+License-file:  LICENSE+Stability:     Experimental+Author:        Levent Erkok+Homepage:      http://leventerkok.github.com/hArduino+Bug-reports:   http://github.com/LeventErkok/hArduino/issues+Maintainer:    Levent Erkok (erkokl@gmail.com)+Build-Type:    Simple+Cabal-Version: >= 1.14+Extra-Source-Files: INSTALL, README, COPYRIGHT, RELEASENOTES++source-repository head+    type:       git+    location:   git://github.com/LeventErkok/hArduino.git++Library+  default-language: Haskell2010+  ghc-options     : -Wall+-- NB. process is only needed for "sleep", since+-- threaddelay is broken on Mac. See: http://hackage.haskell.org/trac/ghc/ticket/7299+  Build-depends   : base  >= 4 && < 5, serialport, bytestring, mtl, process+  Exposed-modules : System.Hardware.Arduino+                  , System.Hardware.Arduino.Examples.Blink+  Other-modules   : System.Hardware.Arduino.Comm+                  , System.Hardware.Arduino.Data+                  , System.Hardware.Arduino.Firmata+                  , System.Hardware.Arduino.Firmata.Basics+                  , System.Hardware.Arduino.Protocol+                  , System.Hardware.Arduino.Parts+                  , System.Hardware.Arduino.Utils