hArduino-0.2: System/Hardware/Arduino/Firmata/Basics.hs
-------------------------------------------------------------------------------
-- |
-- 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.Concurrent (newEmptyMVar, readMVar)
import Control.Monad (when)
import Control.Monad.Trans (liftIO)
import Data.Word (Word8)
import System.Hardware.Arduino.Data
import System.Hardware.Arduino.Comm
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 $ "queryFirmware: 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 = do
extras <- registerPinMode p m
send $ SetPinMode p m
mapM_ send extras
-- | Set or clear a digital pin on the board
digitalWrite :: Pin -> Bool -> Arduino ()
digitalWrite p v = do
-- first make sure we have this pin set as output
pd <- getPinData p
when (pinMode pd /= OUTPUT) $ U.die ("Invalid digitalWrite call on pin " ++ show p)
[ "The current mode for this pin is: " ++ show (pinMode pd)
, "For digitalWrite, it must be set to: " ++ show OUTPUT
, "via a proper call to setPinMode"
]
(lsb, msb) <- computePortData p v
send $ DigitalPortWrite (pinPort p) lsb msb
-- | Read the value of a pin in digital mode; this is a non-blocking call, returning
-- the current value immediately. See 'waitFor' for a version that waits for a change
-- in the pin first.
digitalRead :: Pin -> Arduino Bool
digitalRead p = do
-- first make sure we have this pin set as input
pd <- getPinData p
when (pinMode pd /= INPUT) $ U.die ("Invalid digitalRead call on pin " ++ show p)
[ "The current mode for this pin is: " ++ show (pinMode pd)
, "For digitalWrite, it must be set to: " ++ show INPUT
, "via a proper call to setPinMode"
]
return $ case pinValue pd of
Just (Left v) -> v
_ -> False -- no (correctly-typed) value reported yet, default to False
-- | Wait for a change in the value of the digital input pin. Returns the new value.
-- Note that this is a blocking call. For a non-blocking version, see 'digitalRead', which returns the current
-- value of a pin immediately.
waitFor :: Pin -> Arduino Bool
waitFor p = do
curVal <- digitalRead p
let wait = do sleepTillDigitalMessage
newVal <- digitalRead p
if newVal == curVal
then wait
else return newVal
wait
-- | Sleep until we receive a digital message from the board
sleepTillDigitalMessage :: Arduino ()
sleepTillDigitalMessage = do
semaphore <- liftIO newEmptyMVar
digitalWakeUp semaphore
liftIO $ readMVar semaphore