{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
module System.Hardware.Arduino.Firmata where
import Control.Concurrent (newEmptyMVar, readMVar, withMVar, modifyMVar_, threadDelay)
import Control.Monad (when, unless, void)
import Control.Monad.State (StateT(..), gets)
import Control.Monad.Trans (liftIO)
import Data.Bits ((.&.), shiftR, setBit)
import Data.Maybe (fromMaybe)
import Data.Time (getCurrentTime, utctDayTime)
import System.Timeout (timeout)
import Data.Word (Word8)
import qualified Data.Map as M
import System.Hardware.Arduino.Data
import System.Hardware.Arduino.Comm
import qualified System.Hardware.Arduino.Utils as U
queryFirmware :: Arduino (Word8, Word8, String)
queryFirmware :: Arduino (Word8, Word8, String)
queryFirmware = do
Request -> Arduino ()
send Request
QueryFirmware
r <- Arduino Response
recv
case r of
Firmware Word8
v1 Word8
v2 String
m -> (Word8, Word8, String) -> Arduino (Word8, Word8, String)
forall a. a -> Arduino a
forall (m :: * -> *) a. Monad m => a -> m a
return (Word8
v1, Word8
v2, String
m)
Response
_ -> String -> [String] -> Arduino (Word8, Word8, String)
forall a. String -> [String] -> Arduino a
die String
"queryFirmware: Got unexpected response for query firmware call: " [Response -> String
forall a. Show a => a -> String
show Response
r]
delay :: Int -> Arduino ()
delay :: Int -> Arduino ()
delay = IO () -> Arduino ()
forall a. IO a -> Arduino a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Arduino ()) -> (Int -> IO ()) -> Int -> Arduino ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> IO ()
U.delay
time :: Arduino a -> Arduino (Int, a)
time :: forall a. Arduino a -> Arduino (Int, a)
time Arduino a
a = do start <- Arduino Integer
tick
r <- a
end <- r `seq` tick
return (toMicroSeconds (end - start), r)
where
tick :: Arduino Integer
tick = do t <- IO DiffTime -> Arduino DiffTime
forall a. IO a -> Arduino a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO DiffTime -> Arduino DiffTime)
-> IO DiffTime -> Arduino DiffTime
forall a b. (a -> b) -> a -> b
$ UTCTime -> DiffTime
utctDayTime (UTCTime -> DiffTime) -> IO UTCTime -> IO DiffTime
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` IO UTCTime
getCurrentTime
let precision = Integer
1000000000000 :: Integer
return . round . (fromIntegral precision *) . toRational $ t
toMicroSeconds :: Integer -> Int
toMicroSeconds :: Integer -> Int
toMicroSeconds Integer
t = Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Int) -> Integer -> Int
forall a b. (a -> b) -> a -> b
$ Integer
t Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`quot` Integer
1000000
timeOut :: Int -> Arduino a -> Arduino (Maybe a)
timeOut :: forall a. Int -> Arduino a -> Arduino (Maybe a)
timeOut Int
to (Arduino (StateT ArduinoState -> IO (a, ArduinoState)
f)) = StateT ArduinoState IO (Maybe a) -> Arduino (Maybe a)
forall a. StateT ArduinoState IO a -> Arduino a
Arduino ((ArduinoState -> IO (Maybe a, ArduinoState))
-> StateT ArduinoState IO (Maybe a)
forall s (m :: * -> *) a. (s -> m (a, s)) -> StateT s m a
StateT (\ArduinoState
st -> do
mbRes <- Int -> IO (a, ArduinoState) -> IO (Maybe (a, ArduinoState))
forall a. Int -> IO a -> IO (Maybe a)
timeout Int
to (ArduinoState -> IO (a, ArduinoState)
f ArduinoState
st)
case mbRes of
Maybe (a, ArduinoState)
Nothing -> (Maybe a, ArduinoState) -> IO (Maybe a, ArduinoState)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe a
forall a. Maybe a
Nothing, ArduinoState
st)
Just (a
a, ArduinoState
st') -> (Maybe a, ArduinoState) -> IO (Maybe a, ArduinoState)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (a -> Maybe a
forall a. a -> Maybe a
Just a
a, ArduinoState
st')))
setPinMode :: Pin -> PinMode -> Arduino ()
setPinMode :: Pin -> PinMode -> Arduino ()
setPinMode Pin
p' PinMode
m = do
p <- Pin -> Arduino IPin
getInternalPin Pin
p'
extras <- registerPinMode p m
send $ SetPinMode p m
mapM_ send extras
digitalWrite :: Pin -> Bool -> Arduino ()
digitalWrite :: Pin -> Bool -> Arduino ()
digitalWrite Pin
p' Bool
v = do
(p, pd) <- String -> Pin -> PinMode -> Arduino (IPin, PinData)
convertAndCheckPin String
"digitalWrite" Pin
p' PinMode
OUTPUT
case pinValue pd of
Just (Left Bool
b) | Bool
b Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
v -> () -> Arduino ()
forall a. a -> Arduino a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Maybe (Either Bool Int)
_ -> do (lsb, msb) <- IPin -> Bool -> Arduino (Word8, Word8)
computePortData IPin
p Bool
v
send $ DigitalPortWrite (pinPort p) lsb msb
pullUpResistor :: Pin -> Bool -> Arduino ()
pullUpResistor :: Pin -> Bool -> Arduino ()
pullUpResistor Pin
p' Bool
v = do
(p, _) <- String -> Pin -> PinMode -> Arduino (IPin, PinData)
convertAndCheckPin String
"pullUpResistor" Pin
p' PinMode
INPUT
(lsb, msb) <- computePortData p v
send $ DigitalPortWrite (pinPort p) lsb msb
digitalRead :: Pin -> Arduino Bool
digitalRead :: Pin -> Arduino Bool
digitalRead Pin
p' = do
(_, pd) <- String -> Pin -> PinMode -> Arduino (IPin, PinData)
convertAndCheckPin String
"digitalRead" Pin
p' PinMode
INPUT
return $ case pinValue pd of
Just (Left Bool
v) -> Bool
v
Maybe (Either Bool Int)
_ -> Bool
False
waitFor :: Pin -> Arduino Bool
waitFor :: Pin -> Arduino Bool
waitFor Pin
p = [Bool] -> Bool
forall a. HasCallStack => [a] -> a
head ([Bool] -> Bool) -> Arduino [Bool] -> Arduino Bool
forall a b. (a -> b) -> Arduino a -> Arduino b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` [Pin] -> Arduino [Bool]
waitAny [Pin
p]
waitAny :: [Pin] -> Arduino [Bool]
waitAny :: [Pin] -> Arduino [Bool]
waitAny [Pin]
ps = ((Bool, Bool) -> Bool) -> [(Bool, Bool)] -> [Bool]
forall a b. (a -> b) -> [a] -> [b]
map (Bool, Bool) -> Bool
forall a b. (a, b) -> b
snd ([(Bool, Bool)] -> [Bool])
-> Arduino [(Bool, Bool)] -> Arduino [Bool]
forall a b. (a -> b) -> Arduino a -> Arduino b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` [Pin] -> Arduino [(Bool, Bool)]
waitGeneric [Pin]
ps
waitAnyHigh :: [Pin] -> Arduino [Bool]
waitAnyHigh :: [Pin] -> Arduino [Bool]
waitAnyHigh [Pin]
ps = do
curVals <- (Pin -> Arduino Bool) -> [Pin] -> Arduino [Bool]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM Pin -> Arduino Bool
digitalRead [Pin]
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 :: [Pin] -> Arduino [Bool]
waitAnyLow [Pin]
ps = do
curVals <- (Pin -> Arduino Bool) -> [Pin] -> Arduino [Bool]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM Pin -> Arduino Bool
digitalRead [Pin]
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 :: [Pin] -> Arduino [(Bool, Bool)]
waitGeneric [Pin]
ps = do
curVals <- (Pin -> Arduino Bool) -> [Pin] -> Arduino [Bool]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM Pin -> Arduino Bool
digitalRead [Pin]
ps
semaphore <- liftIO newEmptyMVar
let wait = do MVar () -> Arduino ()
digitalWakeUp MVar ()
semaphore
IO () -> Arduino ()
forall a. IO a -> Arduino a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Arduino ()) -> IO () -> Arduino ()
forall a b. (a -> b) -> a -> b
$ MVar () -> IO ()
forall a. MVar a -> IO a
readMVar MVar ()
semaphore
newVals <- (Pin -> Arduino Bool) -> [Pin] -> Arduino [Bool]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM Pin -> Arduino Bool
digitalRead [Pin]
ps
if curVals == newVals
then wait
else return $ zip curVals newVals
wait
pulse :: Pin -> Bool -> Int -> Maybe Int -> Arduino (Maybe Int)
pulse :: Pin -> Bool -> Int -> Maybe Int -> Arduino (Maybe Int)
pulse Pin
p' Bool
v Int
duration Maybe Int
mbTo = do
(p, _) <- String -> Pin -> PinMode -> Arduino (IPin, PinData)
convertAndCheckPin String
"pulse" Pin
p' PinMode
INPUT
let to = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
maxAllowed Maybe Int
mbTo
maxAllowed = Int
2147483647
bad Int
x = Int
x Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 Bool -> Bool -> Bool
|| Int
x Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
maxAllowed
when (any bad [duration, to]) $ die ("Invalid duration/time-out values for pulse on pin " ++ show p)
[ "Values should be between 0 and " ++ show maxAllowed
, "Received: " ++ show (duration, to)
]
send $ Pulse p v (fromIntegral duration) (fromIntegral to)
r <- recv
case r of
PulseResponse IPin
pOut Word32
d | IPin
p IPin -> IPin -> Bool
forall a. Eq a => a -> a -> Bool
== IPin
pOut -> case Word32
d of
Word32
0 -> Maybe Int -> Arduino (Maybe Int)
forall a. a -> Arduino a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Int
forall a. Maybe a
Nothing
Word32
i -> Maybe Int -> Arduino (Maybe Int)
forall a. a -> Arduino a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Maybe Int
forall a. a -> Maybe a
Just (Word32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
i))
Response
_ -> String -> [String] -> Arduino (Maybe Int)
forall a. String -> [String] -> Arduino a
die (String
"pulseIn: Got unexpected response for Pulse call on pin: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Pin -> String
forall a. Show a => a -> String
show Pin
p') [Response -> String
forall a. Show a => a -> String
show Response
r]
pulseOut_hostTiming :: Pin
-> Bool
-> Int
-> Int
-> Arduino ()
pulseOut_hostTiming :: Pin -> Bool -> Int -> Int -> Arduino ()
pulseOut_hostTiming Pin
p' Bool
pulseValue Int
dBefore Int
dAfter
| Int
dBefore Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 Bool -> Bool -> Bool
|| Int
dAfter Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0
= String -> [String] -> Arduino ()
forall a. String -> [String] -> Arduino a
die (String
"pulseOut: Invalid delay amounts: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Int, Int) -> String
forall a. Show a => a -> String
show (Int
dBefore, Int
dAfter))
[ String
"Pre-delay and pulse-amounts must be non-negative."]
| Bool
True
= do (p, pd) <- String -> Pin -> PinMode -> Arduino (IPin, PinData)
convertAndCheckPin String
"pulseOut_hostTiming" Pin
p' PinMode
OUTPUT
let curPort = IPin -> Port
pinPort IPin
p
curIndex = IPin -> Word8
pinPortIndex IPin
p
bs <- gets boardState
(setMask, resetMask) <- liftIO $ withMVar bs $ \BoardState
bst -> do
let values :: [(Word8, Maybe (Either Bool Int))]
values = [(IPin -> Word8
pinPortIndex IPin
sp, PinData -> Maybe (Either Bool Int)
pinValue PinData
spd) | (IPin
sp, PinData
spd) <- Map IPin PinData -> [(IPin, PinData)]
forall k a. Map k a -> [(k, a)]
M.assocs (BoardState -> Map IPin PinData
pinStates BoardState
bst), Port
curPort Port -> Port -> Bool
forall a. Eq a => a -> a -> Bool
== IPin -> Port
pinPort IPin
sp, PinData -> PinMode
pinMode PinData
pd PinMode -> [PinMode] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [PinMode
INPUT, PinMode
OUTPUT]]
getVal :: Bool -> Word8 -> Bool
getVal Bool
nv Word8
i
| Word8
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
curIndex = Bool
nv
| Just (Just (Left Bool
ov)) <- Word8
i Word8
-> [(Word8, Maybe (Either Bool Int))]
-> Maybe (Maybe (Either Bool Int))
forall a b. Eq a => a -> [(a, b)] -> Maybe b
`lookup` [(Word8, Maybe (Either Bool Int))]
values = Bool
ov
| Bool
True = Bool
False
mkMask :: Bool -> (a, b)
mkMask Bool
val = let [Bool
b0, Bool
b1, Bool
b2, Bool
b3, Bool
b4, Bool
b5, Bool
b6, Bool
b7] = (Word8 -> Bool) -> [Word8] -> [Bool]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> Word8 -> Bool
getVal Bool
val) [Word8
0 .. Word8
7]
lsb :: a
lsb = ((Int, Bool) -> a -> a) -> a -> [(Int, Bool)] -> a
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(Int
i, Bool
b) a
m -> if Bool
b then a
m a -> Int -> a
forall a. Bits a => a -> Int -> a
`setBit` Int
i else a
m) a
0 ([Int] -> [Bool] -> [(Int, Bool)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0..] [Bool
b0, Bool
b1, Bool
b2, Bool
b3, Bool
b4, Bool
b5, Bool
b6])
msb :: b
msb = ((Int, Bool) -> b -> b) -> b -> [(Int, Bool)] -> b
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(Int
i, Bool
b) b
m -> if Bool
b then b
m b -> Int -> b
forall a. Bits a => a -> Int -> a
`setBit` (Int
iInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
7) else b
m) b
0 ([Int] -> [Bool] -> [(Int, Bool)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
7..] [Bool
b7])
in (a
lsb, b
msb)
((Word8, Word8), (Word8, Word8))
-> IO ((Word8, Word8), (Word8, Word8))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> (Word8, Word8)
forall {a} {b}. (Bits a, Bits b, Num a, Num b) => Bool -> (a, b)
mkMask Bool
pulseValue, Bool -> (Word8, Word8)
forall {a} {b}. (Bits a, Bits b, Num a, Num b) => Bool -> (a, b)
mkMask (Bool -> Bool
not Bool
pulseValue))
let writeThrough (Word8
lsb, Word8
msb) = Request -> Arduino ()
send (Request -> Arduino ()) -> Request -> Arduino ()
forall a b. (a -> b) -> a -> b
$ Port -> Word8 -> Word8 -> Request
DigitalPortWrite Port
curPort Word8
lsb Word8
msb
fst setMask `seq` snd setMask `seq` fst resetMask `seq` snd resetMask `seq` writeThrough resetMask
liftIO $ threadDelay dBefore
writeThrough setMask
liftIO $ threadDelay dAfter
writeThrough resetMask
liftIO $ modifyMVar_ bs $ \BoardState
bst -> BoardState -> IO BoardState
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return BoardState
bst{pinStates = M.insert p PinData{pinMode = OUTPUT, pinValue = Just (Left (not pulseValue))}(pinStates bst)}
{-# ANN pulseOut_hostTiming "HLint: ignore Use camelCase" #-}
pulseIn_hostTiming :: Pin -> Bool -> Maybe Int -> Arduino (Maybe Int)
pulseIn_hostTiming :: Pin -> Bool -> Maybe Int -> Arduino (Maybe Int)
pulseIn_hostTiming Pin
p Bool
v Maybe Int
mbTo = case Maybe Int
mbTo of
Maybe Int
Nothing -> Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Arduino Int -> Arduino (Maybe Int)
forall a b. (a -> b) -> Arduino a -> Arduino b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Arduino Int
measure
Just Int
to -> Int -> Arduino Int -> Arduino (Maybe Int)
forall a. Int -> Arduino a -> Arduino (Maybe a)
timeOut Int
to Arduino Int
measure
where waitTill :: (Bool -> Bool) -> Arduino ()
waitTill Bool -> Bool
f = do curVal <- Pin -> Arduino Bool
digitalRead Pin
p
unless (f curVal) $ waitTill f
measure :: Arduino Int
measure = do (Bool -> Bool) -> Arduino ()
waitTill (Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
v)
(t, _) <- Arduino () -> Arduino (Int, ())
forall a. Arduino a -> Arduino (Int, a)
time (Arduino () -> Arduino (Int, ()))
-> Arduino () -> Arduino (Int, ())
forall a b. (a -> b) -> a -> b
$ (Bool -> Bool) -> Arduino ()
waitTill (Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
/= Bool
v)
return $ fromIntegral t
{-# ANN pulseIn_hostTiming "HLint: ignore Use camelCase" #-}
analogRead :: Pin -> Arduino Int
analogRead :: Pin -> Arduino Int
analogRead Pin
p' = do
(_, pd) <- String -> Pin -> PinMode -> Arduino (IPin, PinData)
convertAndCheckPin String
"analogRead" Pin
p' PinMode
ANALOG
return $ case pinValue pd of
Just (Right Int
v) -> Int
v
Maybe (Either Bool Int)
_ -> Int
0
analogWrite :: Pin -> Int -> Arduino ()
analogWrite :: Pin -> Int -> Arduino ()
analogWrite Pin
p' Int
dc = do
(p, _) <- String -> Pin -> PinMode -> Arduino (IPin, PinData)
convertAndCheckPin String
"analogWrite" Pin
p' PinMode
PWM
when (dc < 0 || dc > 255) $ die ("Invalid duty-cycle value for PWM write on pin " ++ show p)
[ "Values should be between 0 and 255"
, "Received: " ++ show dc
]
send $ AnalogPinWrite p (fromIntegral lsb) (fromIntegral msb)
where lsb :: Int
lsb = Int
dc Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0x7f
msb :: Int
msb = (Int
dc Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftR` Int
7) Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0x7f
setAnalogSamplingInterval :: Int -> Arduino ()
setAnalogSamplingInterval :: Int -> Arduino ()
setAnalogSamplingInterval Int
i
| Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
10 Bool -> Bool -> Bool
|| Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
16383
= String -> [String] -> Arduino ()
forall a. String -> [String] -> Arduino a
die (String
"hArduino: setAnalogSamplingInterval: Allowed interval is [10, 16383] ms, received: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
i) []
| Bool
True
= Request -> Arduino ()
send (Request -> Arduino ()) -> Request -> Arduino ()
forall a b. (a -> b) -> a -> b
$ Word8 -> Word8 -> Request
SamplingInterval (Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
lsb) (Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
msb)
where lsb :: Int
lsb = Int
i Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0x7f
msb :: Int
msb = (Int
i Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftR` Int
7) Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0x7f