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
- COPYRIGHT +5/−0
- INSTALL +9/−0
- LICENSE +26/−0
- README +1/−0
- RELEASENOTES +11/−0
- Setup.hs +2/−0
- System/Hardware/Arduino.hs +42/−0
- System/Hardware/Arduino/Comm.hs +84/−0
- System/Hardware/Arduino/Data.hs +65/−0
- System/Hardware/Arduino/Examples/Blink.hs +33/−0
- System/Hardware/Arduino/Firmata.hs +16/−0
- System/Hardware/Arduino/Firmata/Basics.hs +65/−0
- System/Hardware/Arduino/Parts.hs +45/−0
- System/Hardware/Arduino/Protocol.hs +97/−0
- System/Hardware/Arduino/Utils.hs +52/−0
- hArduino.cabal +45/−0
+ COPYRIGHT view
@@ -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