--------------------------------------------------------------------------------- |-- Module : System.Hardware.Arduino.Comm-- Copyright : (c) Levent Erkok-- License : BSD3-- Maintainer : erkokl@gmail.com-- Stability : experimental---- Basic serial communication routines-------------------------------------------------------------------------------{-# LANGUAGE LambdaCase #-}{-# LANGUAGE ScopedTypeVariables #-}{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}moduleSystem.Hardware.Arduino.CommwhereimportControl.Monad(when,forever)importControl.Concurrent(MVar,ThreadId,newChan,newMVar,newEmptyMVar,putMVar,writeChan,readChan,forkIO,modifyMVar_,tryTakeMVar,killThread)importControl.Exception(tryJust,AsyncException(UserInterrupt),handle,SomeException)importControl.Monad.State(runStateT,gets,liftIO,modify)importData.Bits(testBit,(.&.))importData.List(intercalate,isInfixOf)importData.Maybe(listToMaybe)importData.Word(Word8)importSystem.Timeout(timeout)importSystem.IO(stderr,hPutStrLn)importqualifiedData.ByteStringasB(unpack,length)importqualifiedData.MapasM(empty,mapWithKey,insert,assocs,lookup)importqualifiedData.SetasS(empty)importqualifiedSystem.Hardware.SerialportasS(withSerial,defaultSerialSettings,CommSpeed(CS57600),commSpeed,recv,send)importSystem.Hardware.Arduino.Data importSystem.Hardware.Arduino.Utils importSystem.Hardware.Arduino.Protocol -- | 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 :: Bool -> FilePath -> Arduino () -> IO ()
withArduino Bool
verbose FilePath
fp Arduino ()
program =doFilePath -> IO ()
debugger <-Bool -> IO (FilePath -> IO ())
mkDebugPrinter Bool
verbose FilePath -> IO ()
debugger forall a b. (a -> b) -> a -> b
$FilePath
"Accessing arduino located at: "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showFilePath
fp MVar ThreadId
lTid <-forall a. IO (MVar a)
newEmptyMVarletArduino StateT ArduinoState IO ()
controller =doBool
initOK <-MVar ThreadId -> Arduino Bool
initialize MVar ThreadId
lTid ifBool
initOK thenArduino ()
program elseforall a. HasCallStack => FilePath -> a
errorFilePath
"Communication time-out (5s) expired."forall e a. Exception e => (e -> IO a) -> IO a -> IO a
handle(\(SomeException
e ::SomeException)->doMVar ThreadId -> IO ()
cleanUp MVar ThreadId
lTid letselfErr :: Bool
selfErr =FilePath
"*** hArduino"forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf`forall a. Show a => a -> FilePath
showSomeException
e Handle -> FilePath -> IO ()
hPutStrLnHandle
stderrforall a b. (a -> b) -> a -> b
$ifBool
selfErr thenforall a. (a -> Bool) -> [a] -> [a]
dropWhile(forall a. Eq a => a -> a -> Bool
==Char
'\n')(forall a. Show a => a -> FilePath
showSomeException
e )elseFilePath
"*** hArduino:ERROR: "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showSomeException
e forall a. [a] -> [a] -> [a]
++forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap(FilePath
"\n*** "forall a. [a] -> [a] -> [a]
++)[FilePath
"Make sure your Arduino is connected to "forall a. [a] -> [a] -> [a]
++FilePath
fp ,FilePath
"And StandardFirmata is running on it!"])forall a b. (a -> b) -> a -> b
$forall a.
FilePath -> SerialPortSettings -> (SerialPort -> IO a) -> IO a
S.withSerialFilePath
fp SerialPortSettings
S.defaultSerialSettings{commSpeed :: CommSpeed
S.commSpeed=CommSpeed
S.CS57600}forall a b. (a -> b) -> a -> b
$\SerialPort
curPort ->doletinitBoardState :: BoardState
initBoardState =BoardState {boardCapabilities :: BoardCapabilities
boardCapabilities =Map IPin PinCapabilities -> BoardCapabilities
BoardCapabilities forall k a. Map k a
M.empty,analogReportingPins :: Set IPin
analogReportingPins =forall a. Set a
S.empty,digitalReportingPins :: Set IPin
digitalReportingPins =forall a. Set a
S.empty,pinStates :: Map IPin PinData
pinStates =forall k a. Map k a
M.empty,digitalWakeUpQueue :: [MVar ()]
digitalWakeUpQueue =[],lcds :: Map LCD LCDData
lcds =forall k a. Map k a
M.empty}MVar BoardState
bs <-forall a. a -> IO (MVar a)
newMVarBoardState
initBoardState Chan Response
dc <-forall a. IO (Chan a)
newChanletinitState :: ArduinoState
initState =ArduinoState {message :: FilePath -> IO ()
message =FilePath -> IO ()
debugger ,bailOut :: FilePath -> [FilePath] -> IO ()
bailOut =forall {b}. MVar ThreadId -> FilePath -> [FilePath] -> IO b
bailOutF MVar ThreadId
lTid ,port :: SerialPort
port =SerialPort
curPort ,firmataID :: FilePath
firmataID =FilePath
"Unknown",capabilities :: BoardCapabilities
capabilities =Map IPin PinCapabilities -> BoardCapabilities
BoardCapabilities forall k a. Map k a
M.empty,boardState :: MVar BoardState
boardState =MVar BoardState
bs ,deviceChannel :: Chan Response
deviceChannel =Chan Response
dc ,listenerTid :: MVar ThreadId
listenerTid =MVar ThreadId
lTid }Either () ((), ArduinoState)
res <-forall e b a.
Exception e =>
(e -> Maybe b) -> IO a -> IO (Either b a)
tryJustAsyncException -> Maybe ()
catchCtrlC forall a b. (a -> b) -> a -> b
$forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
runStateTStateT ArduinoState IO ()
controller ArduinoState
initState caseEither () ((), ArduinoState)
res ofLeft()->FilePath -> IO ()
putStrLnFilePath
"hArduino: Caught Ctrl-C, quitting.."Either () ((), ArduinoState)
_->forall (m :: * -> *) a. Monad m => a -> m a
return()MVar ThreadId -> IO ()
cleanUp MVar ThreadId
lTid wherecatchCtrlC :: AsyncException -> Maybe ()
catchCtrlC AsyncException
UserInterrupt=forall a. a -> Maybe a
Just()catchCtrlC AsyncException
_=forall a. Maybe a
NothingcleanUp :: MVar ThreadId -> IO ()
cleanUp MVar ThreadId
tid =doMaybe ThreadId
mbltid <-forall a. MVar a -> IO (Maybe a)
tryTakeMVarMVar ThreadId
tid forall b a. b -> (a -> b) -> Maybe a -> b
maybe(forall (f :: * -> *) a. Applicative f => a -> f a
pure())ThreadId -> IO ()
killThreadMaybe ThreadId
mbltid bailOutF :: MVar ThreadId -> FilePath -> [FilePath] -> IO b
bailOutF MVar ThreadId
tid FilePath
m [FilePath]
ms =doMVar ThreadId -> IO ()
cleanUp MVar ThreadId
tid forall a. HasCallStack => FilePath -> a
errorforall a b. (a -> b) -> a -> b
$FilePath
"\n*** hArduino:ERROR: "forall a. [a] -> [a] -> [a]
++forall a. [a] -> [[a]] -> [a]
intercalateFilePath
"\n*** "(FilePath
m forall a. a -> [a] -> [a]
:[FilePath]
ms )-- | Send down a request.send ::Request ->Arduino ()send :: Request -> Arduino ()
send Request
req =doFilePath -> Arduino ()
debug forall a b. (a -> b) -> a -> b
$FilePath
"Sending: "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showRequest
req forall a. [a] -> [a] -> [a]
++FilePath
" <"forall a. [a] -> [a] -> [a]
++[FilePath] -> FilePath
unwords(forall a b. (a -> b) -> [a] -> [b]
mapWord8 -> FilePath
showByte (ByteString -> [Word8]
B.unpackByteString
p ))forall a. [a] -> [a] -> [a]
++FilePath
">"SerialPort
serial <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> SerialPort
port Int
sent <-forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIOforall a b. (a -> b) -> a -> b
$SerialPort -> ByteString -> IO Int
S.sendSerialPort
serial ByteString
p forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when(Int
sent forall a. Eq a => a -> a -> Bool
/=Int
lp )(FilePath -> Arduino ()
debug forall a b. (a -> b) -> a -> b
$FilePath
"Send failed. Tried: "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showInt
lp forall a. [a] -> [a] -> [a]
++FilePath
"bytes, reported: "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showInt
sent )wherep :: ByteString
p =Request -> ByteString
package Request
req lp :: Int
lp =ByteString -> Int
B.lengthByteString
p -- | Receive a sys-ex response. This is a blocking call.recv ::Arduino Response recv :: Arduino Response
recv =doChan Response
ch <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> Chan Response
deviceChannel forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIOforall a b. (a -> b) -> a -> b
$forall a. Chan a -> IO a
readChanChan Response
ch -- | Receive a sys-ex response with time-out. This is a blocking call, and will wait until-- either the time-out expires or the message is receivedrecvTimeOut ::Int->Arduino (MaybeResponse )recvTimeOut :: Int -> Arduino (Maybe Response)
recvTimeOut Int
n =doChan Response
ch <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> Chan Response
deviceChannel forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIOforall a b. (a -> b) -> a -> b
$forall a. Int -> IO a -> IO (Maybe a)
timeoutInt
n (forall a. Chan a -> IO a
readChanChan Response
ch )-- | Start a thread to listen to the board and populate the channel with incoming queries.setupListener ::Arduino ThreadIdsetupListener :: Arduino ThreadId
setupListener =doSerialPort
serial <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> SerialPort
port FilePath -> IO ()
dbg <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> FilePath -> IO ()
message Chan Response
chan <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> Chan Response
deviceChannel letgetBytes :: Int -> IO [Word8]
getBytes Int
n =doletgo :: Int -> [ByteString] -> IO [ByteString]
go Int
need [ByteString]
sofar |Int
need forall a. Ord a => a -> a -> Bool
<=Int
0=forall (m :: * -> *) a. Monad m => a -> m a
returnforall a b. (a -> b) -> a -> b
$forall a. [a] -> [a]
reverse[ByteString]
sofar |Bool
True=doByteString
b <-SerialPort -> Int -> IO ByteString
S.recvSerialPort
serial Int
need caseByteString -> Int
B.lengthByteString
b ofInt
0->Int -> [ByteString] -> IO [ByteString]
go Int
need [ByteString]
sofar Int
l ->Int -> [ByteString] -> IO [ByteString]
go (Int
need forall a. Num a => a -> a -> a
-Int
l )(ByteString
b forall a. a -> [a] -> [a]
:[ByteString]
sofar )[ByteString]
chunks <-Int -> [ByteString] -> IO [ByteString]
go Int
n []forall (m :: * -> *) a. Monad m => a -> m a
returnforall a b. (a -> b) -> a -> b
$forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMapByteString -> [Word8]
B.unpack[ByteString]
chunks collectSysEx :: [Word8] -> IO [Word8]
collectSysEx [Word8]
sofar =do[Word8
b ]<-Int -> IO [Word8]
getBytes Int
1ifWord8
b forall a. Eq a => a -> a -> Bool
==FirmataCmd -> Word8
firmataCmdVal FirmataCmd
END_SYSEX thenforall (m :: * -> *) a. Monad m => a -> m a
returnforall a b. (a -> b) -> a -> b
$forall a. [a] -> [a]
reverse[Word8]
sofar else[Word8] -> IO [Word8]
collectSysEx (Word8
b forall a. a -> [a] -> [a]
:[Word8]
sofar )listener :: MVar BoardState -> IO ()
listener MVar BoardState
bs =do[Word8
cmd ]<-Int -> IO [Word8]
getBytes Int
1Response
resp <-caseWord8 -> Either Word8 FirmataCmd
getFirmataCmd Word8
cmd ofLeftWord8
unknown ->forall (m :: * -> *) a. Monad m => a -> m a
returnforall a b. (a -> b) -> a -> b
$Maybe FilePath -> [Word8] -> Response
Unimplemented (forall a. a -> Maybe a
Just(forall a. Show a => a -> FilePath
showWord8
unknown ))[]RightFirmataCmd
START_SYSEX ->[Word8] -> Response
unpackageSysEx forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap`[Word8] -> IO [Word8]
collectSysEx []RightFirmataCmd
nonSysEx ->(Int -> IO [Word8]) -> FirmataCmd -> IO Response
unpackageNonSysEx Int -> IO [Word8]
getBytes FirmataCmd
nonSysEx caseResponse
resp ofUnimplemented {}->FilePath -> IO ()
dbg forall a b. (a -> b) -> a -> b
$FilePath
"Ignoring the received response: "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showResponse
resp -- NB. When Firmata sends back AnalogMessage, it uses the number in A0-A1-A2, etc., i.e., 0-1-2; which we-- need to properly interpret in our own pin mapping schema, where analogs come after digitals.AnalogMessage IPin
mp Word8
l Word8
h ->forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_MVar BoardState
bs forall a b. (a -> b) -> a -> b
$\BoardState
bst ->doletBoardCapabilities Map IPin PinCapabilities
caps =BoardState -> BoardCapabilities
boardCapabilities BoardState
bst mbP :: Maybe IPin
mbP =forall a. [a] -> Maybe a
listToMaybe[IPin
mappedPin |(IPin
mappedPin ,PinCapabilities {analogPinNumber :: PinCapabilities -> Maybe Word8
analogPinNumber =JustWord8
mp' })<-forall k a. Map k a -> [(k, a)]
M.assocsMap IPin PinCapabilities
caps ,IPin -> Word8
pinNo IPin
mp forall a. Eq a => a -> a -> Bool
==Word8
mp' ]caseMaybe IPin
mbP ofMaybe IPin
Nothing->forall (m :: * -> *) a. Monad m => a -> m a
returnBoardState
bst -- Mapping hasn't happened yetJustIPin
p ->doletv :: Int
v =(Int
128forall a. Num a => a -> a -> a
*forall a b. (Integral a, Num b) => a -> b
fromIntegral(Word8
h forall a. Bits a => a -> a -> a
.&.Word8
0x07)forall a. Num a => a -> a -> a
+forall a b. (Integral a, Num b) => a -> b
fromIntegral(Word8
l forall a. Bits a => a -> a -> a
.&.Word8
0x7f))::IntcasePinData -> Maybe (Either Bool Int)
pinValue forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap`(IPin
p forall k a. Ord k => k -> Map k a -> Maybe a
`M.lookup`BoardState -> Map IPin PinData
pinStates BoardState
bst )ofJust(Just(RightInt
v' ))|forall a. Num a => a -> a
abs(Int
v forall a. Num a => a -> a -> a
-Int
v' )forall a. Ord a => a -> a -> Bool
<Int
10->forall (m :: * -> *) a. Monad m => a -> m a
return()-- be quiet, otherwise prints too muchMaybe (Maybe (Either Bool Int))
_->FilePath -> IO ()
dbg forall a b. (a -> b) -> a -> b
$FilePath
"Updating analog pin "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showIPin
p forall a. [a] -> [a] -> [a]
++FilePath
" values with "forall a. [a] -> [a] -> [a]
++[Word8] -> FilePath
showByteList [Word8
l ,Word8
h ]forall a. [a] -> [a] -> [a]
++FilePath
" ("forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showInt
v forall a. [a] -> [a] -> [a]
++FilePath
")"forall (m :: * -> *) a. Monad m => a -> m a
returnBoardState
bst {pinStates :: Map IPin PinData
pinStates =forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insertIPin
p PinData {pinMode :: PinMode
pinMode =PinMode
ANALOG ,pinValue :: Maybe (Either Bool Int)
pinValue =forall a. a -> Maybe a
Just(forall a b. b -> Either a b
RightInt
v )}(BoardState -> Map IPin PinData
pinStates BoardState
bst )}DigitalMessage Port
p Word8
l Word8
h ->doFilePath -> IO ()
dbg forall a b. (a -> b) -> a -> b
$FilePath
"Updating digital port "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showPort
p forall a. [a] -> [a] -> [a]
++FilePath
" values with "forall a. [a] -> [a] -> [a]
++[Word8] -> FilePath
showByteList [Word8
l ,Word8
h ]forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_MVar BoardState
bs forall a b. (a -> b) -> a -> b
$\BoardState
bst ->doletupd :: IPin -> PinData -> PinData
upd IPin
o PinData
od |Port
p forall a. Eq a => a -> a -> Bool
/=IPin -> Port
pinPort IPin
o =PinData
od -- different port, no change|PinData -> PinMode
pinMode PinData
od forall a. Eq a => a -> a -> Bool
/=PinMode
INPUT =PinData
od -- not an input pin, ignore|Bool
True=PinData
od {pinValue :: Maybe (Either Bool Int)
pinValue =forall a. a -> Maybe a
Just(forall a b. a -> Either a b
LeftBool
newVal )}whereidx :: Word8
idx =IPin -> Word8
pinPortIndex IPin
o newVal :: Bool
newVal |Word8
idx forall a. Ord a => a -> a -> Bool
<=Word8
6=Word8
l forall a. Bits a => a -> Int -> Bool
`testBit`forall a b. (Integral a, Num b) => a -> b
fromIntegralWord8
idx |Bool
True=Word8
h forall a. Bits a => a -> Int -> Bool
`testBit`forall a b. (Integral a, Num b) => a -> b
fromIntegral(Word8
idx forall a. Num a => a -> a -> a
-Word8
7)letwakeUpQ :: [MVar ()]
wakeUpQ =BoardState -> [MVar ()]
digitalWakeUpQueue BoardState
bst bst' :: BoardState
bst' =BoardState
bst {pinStates :: Map IPin PinData
pinStates =forall k a b. (k -> a -> b) -> Map k a -> Map k b
M.mapWithKeyIPin -> PinData -> PinData
upd (BoardState -> Map IPin PinData
pinStates BoardState
bst ),digitalWakeUpQueue :: [MVar ()]
digitalWakeUpQueue =[]}forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_(forall a. MVar a -> a -> IO ()
`putMVar`())[MVar ()]
wakeUpQ forall (m :: * -> *) a. Monad m => a -> m a
returnBoardState
bst' Response
_->doFilePath -> IO ()
dbg forall a b. (a -> b) -> a -> b
$FilePath
"Received "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showResponse
resp forall a. Chan a -> a -> IO ()
writeChanChan Response
chan Response
resp MVar BoardState
bs <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> MVar BoardState
boardState ThreadId
tid <-forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIOforall a b. (a -> b) -> a -> b
$IO () -> IO ThreadId
forkIOforall a b. (a -> b) -> a -> b
$forall (f :: * -> *) a b. Applicative f => f a -> f b
forever(MVar BoardState -> IO ()
listener MVar BoardState
bs )FilePath -> Arduino ()
debug forall a b. (a -> b) -> a -> b
$FilePath
"Started listener thread: "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showThreadId
tid forall (m :: * -> *) a. Monad m => a -> m a
returnThreadId
tid -- | Initialize our board, get capabilities, etc. Returns True if initialization-- went OK, False if not.initialize ::MVarThreadId->Arduino Boolinitialize :: MVar ThreadId -> Arduino Bool
initialize MVar ThreadId
ltid =do-- Step 0: Set up the listener threadThreadId
tid <-Arduino ThreadId
setupListener forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIOforall a b. (a -> b) -> a -> b
$forall a. MVar a -> a -> IO ()
putMVarMVar ThreadId
ltid ThreadId
tid -- Step 1: Send a reset to get things goingRequest -> Arduino ()
send Request
SystemReset -- Step 2: Send query-firmware, and wait until we get a response-- To accommodate for the case when standard-Firmata may not be running,-- we will time out after 10 seconds of waiting, which should be plentyMaybe ()
mbTo <-forall {a}.
Request
-> Maybe Int
-> (Response -> Bool)
-> (Response -> Arduino a)
-> Arduino (Maybe a)
handshake Request
QueryFirmware (forall a. a -> Maybe a
Just(Int
5000000::Int))(\caseFirmware {}->Bool
TrueResponse
_->Bool
False)(\(Firmware Word8
v1 Word8
v2 FilePath
m )->forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify(\ArduinoState
s ->ArduinoState
s {firmataID :: FilePath
firmataID =FilePath
"Firmware v"forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showWord8
v1 forall a. [a] -> [a] -> [a]
++FilePath
"."forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showWord8
v2 forall a. [a] -> [a] -> [a]
++FilePath
"("forall a. [a] -> [a] -> [a]
++FilePath
m forall a. [a] -> [a] -> [a]
++FilePath
")"}))caseMaybe ()
mbTo ofMaybe ()
Nothing->forall (m :: * -> *) a. Monad m => a -> m a
returnBool
False-- timed outJust()->do-- Step 3: Send a capabilities requestMaybe ()
_<-forall {a}.
Request
-> Maybe Int
-> (Response -> Bool)
-> (Response -> Arduino a)
-> Arduino (Maybe a)
handshake Request
CapabilityQuery forall a. Maybe a
Nothing(\caseCapabilities {}->Bool
TrueResponse
_->Bool
False)(\(Capabilities BoardCapabilities
c )->forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify(\ArduinoState
s ->ArduinoState
s {capabilities :: BoardCapabilities
capabilities =BoardCapabilities
c }))-- Step 4: Send analog-mapping queryMaybe ()
_<-forall {a}.
Request
-> Maybe Int
-> (Response -> Bool)
-> (Response -> Arduino a)
-> Arduino (Maybe a)
handshake Request
AnalogMappingQuery forall a. Maybe a
Nothing(\caseAnalogMapping {}->Bool
TrueResponse
_->Bool
False)(\(AnalogMapping [Word8]
as )->doBoardCapabilities Map IPin PinCapabilities
m <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> BoardCapabilities
capabilities -- need to put capabilities to both outer and inner stateletcaps :: BoardCapabilities
caps =Map IPin PinCapabilities -> BoardCapabilities
BoardCapabilities (forall k a b. (k -> a -> b) -> Map k a -> Map k b
M.mapWithKey([Word8] -> IPin -> PinCapabilities -> PinCapabilities
mapAnalog [Word8]
as )Map IPin PinCapabilities
m )forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify(\ArduinoState
s ->ArduinoState
s {capabilities :: BoardCapabilities
capabilities =BoardCapabilities
caps })MVar BoardState
bs <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> MVar BoardState
boardState forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIOforall a b. (a -> b) -> a -> b
$forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_MVar BoardState
bs forall a b. (a -> b) -> a -> b
$\BoardState
bst ->forall (m :: * -> *) a. Monad m => a -> m a
returnBoardState
bst {boardCapabilities :: BoardCapabilities
boardCapabilities =BoardCapabilities
caps })-- We're done, print capabilities in debug modeBoardCapabilities
caps <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> BoardCapabilities
capabilities FilePath -> IO ()
dbg <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> FilePath -> IO ()
message forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIOforall a b. (a -> b) -> a -> b
$FilePath -> IO ()
dbg forall a b. (a -> b) -> a -> b
$FilePath
"Handshake complete. Board capabilities:\n"forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showBoardCapabilities
caps forall (m :: * -> *) a. Monad m => a -> m a
returnBool
Truewherehandshake :: Request
-> Maybe Int
-> (Response -> Bool)
-> (Response -> Arduino a)
-> Arduino (Maybe a)
handshake Request
msg Maybe Int
mbTOut Response -> Bool
isOK Response -> Arduino a
process =doFilePath -> IO ()
dbg <-forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
getsArduinoState -> FilePath -> IO ()
message Request -> Arduino ()
send Request
msg letwait :: Arduino (Maybe a)
wait =doMaybe Response
mbResp <-caseMaybe Int
mbTOut ofMaybe Int
Nothing->forall a. a -> Maybe a
Justforall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap`Arduino Response
recv JustInt
n ->Int -> Arduino (Maybe Response)
recvTimeOut Int
n caseMaybe Response
mbResp ofMaybe Response
Nothing->forall (m :: * -> *) a. Monad m => a -> m a
returnforall a. Maybe a
NothingJustResponse
resp ->ifResponse -> Bool
isOK Response
resp thenforall a. a -> Maybe a
Justforall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap`Response -> Arduino a
process Response
resp elsedoforall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIOforall a b. (a -> b) -> a -> b
$FilePath -> IO ()
dbg forall a b. (a -> b) -> a -> b
$FilePath
"Skipping unexpected response: "forall a. [a] -> [a] -> [a]
++forall a. Show a => a -> FilePath
showResponse
resp Arduino (Maybe a)
wait Arduino (Maybe a)
wait mapAnalog ::[Word8]->IPin ->PinCapabilities ->PinCapabilities mapAnalog :: [Word8] -> IPin -> PinCapabilities -> PinCapabilities
mapAnalog [Word8]
as IPin
p PinCapabilities
c |Int
i forall a. Ord a => a -> a -> Bool
<Int
rl Bool -> Bool -> Bool
&&Word8
m forall a. Eq a => a -> a -> Bool
/=Word8
0x7f=PinCapabilities
c {analogPinNumber :: Maybe Word8
analogPinNumber =forall a. a -> Maybe a
JustWord8
m }|Bool
True-- out-of-bounds, or not analog; ignore=PinCapabilities
c whererl :: Int
rl =forall (t :: * -> *) a. Foldable t => t a -> Int
length[Word8]
as i :: Int
i =forall a b. (Integral a, Num b) => a -> b
fromIntegral(IPin -> Word8
pinNo IPin
p )m :: Word8
m =[Word8]
as forall a. [a] -> Int -> a
!!Int
i 

AltStyle によって変換されたページ (->オリジナル) /