module System.Hardware.Arduino.Firmata where
import Control.Concurrent (newEmptyMVar, readMVar)
import Control.Monad (when, unless, void)
import Control.Monad.Trans (liftIO)
import Data.Bits ((.&.), shiftR)
import Data.Word (Word8)
import System.Hardware.Arduino.Data
import System.Hardware.Arduino.Comm
import qualified System.Hardware.Arduino.Utils as U
queryFirmware :: Arduino (Word8, Word8, String)
queryFirmware = do
send QueryFirmware
r <- recv
case r of
Firmware v1 v2 m -> return (v1, v2, m)
_ -> die "queryFirmware: Got unexpected response for query firmware call: " [show r]
delay :: Int -> Arduino ()
delay = liftIO . U.delay
setPinMode :: Pin -> PinMode -> Arduino ()
setPinMode p m = do
extras <- registerPinMode p m
send $ SetPinMode p m
mapM_ send extras
digitalWrite :: Pin -> Bool -> Arduino ()
digitalWrite p v = do
pd <- getPinData p
when (pinMode pd /= OUTPUT) $ 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"
]
case pinValue pd of
Just (Left b) | b == v -> return ()
_ -> do (lsb, msb) <- computePortData p v
send $ DigitalPortWrite (pinPort p) lsb msb
pullUpResistor :: Pin -> Bool -> Arduino ()
pullUpResistor p v = do
pd <- getPinData p
when (pinMode pd /= INPUT) $ die ("Invalid turnOnPullUpResistor call on pin " ++ show p)
[ "The current mode for this pin is: " ++ show (pinMode pd)
, "For turnOnPullUpResistor, it must be set to: " ++ show INPUT
, "via a proper call to setPinMode"
]
(lsb, msb) <- computePortData p v
send $ DigitalPortWrite (pinPort p) lsb msb
digitalRead :: Pin -> Arduino Bool
digitalRead p = do
pd <- getPinData p
when (pinMode pd /= INPUT) $ 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
waitFor :: Pin -> Arduino Bool
waitFor p = head `fmap` waitAny [p]
waitAny :: [Pin] -> Arduino [Bool]
waitAny ps = map snd `fmap` waitGeneric ps
waitAnyHigh :: [Pin] -> Arduino [Bool]
waitAnyHigh ps = do
curVals <- mapM digitalRead ps
when (and curVals) $ void $ waitAnyLow ps
vs <- waitGeneric ps
if (False, True) `elem` vs
then return $ map snd vs
else waitAnyHigh ps
waitAnyLow :: [Pin] -> Arduino [Bool]
waitAnyLow ps = do
curVals <- mapM digitalRead ps
unless (or curVals) $ void $ waitAnyHigh ps
vs <- waitGeneric ps
if (True, False) `elem` vs
then return $ map snd vs
else waitAnyLow ps
waitGeneric :: [Pin] -> Arduino [(Bool, Bool)]
waitGeneric ps = do
curVals <- mapM digitalRead ps
semaphore <- liftIO newEmptyMVar
let wait = do digitalWakeUp semaphore
liftIO $ readMVar semaphore
newVals <- mapM digitalRead ps
if curVals == newVals
then wait
else return $ zip curVals newVals
wait
analogRead :: Pin -> Arduino Int
analogRead p = do
pd <- getPinData p
when (pinMode pd /= ANALOG) $ die ("Invalid analogRead call on pin " ++ show p)
[ "The current mode for this pin is: " ++ show (pinMode pd)
, "For analogRead, it must be set to: " ++ show ANALOG
, "via a proper call to setPinMode"
]
return $ case pinValue pd of
Just (Right v) -> v
_ -> 0
setAnalogSamplingInterval :: Int -> Arduino ()
setAnalogSamplingInterval i
| i < 10 || i > 16383
= die ("hArduino: setAnalogSamplingInterval: Allowed interval is [10, 16383] ms, received: " ++ show i) []
| True
= send $ SamplingInterval (fromIntegral lsb) (fromIntegral msb)
where lsb = i .&. 0x7f
msb = (i `shiftR` 7) .&. 0x7f