How do you perform Arduino actions in reactimate ?
I could get them to execute indirectly by performing an IO action that has an observable side effect. Then inside withArduino I would observe this side effect and run the corresponding Arduino command.
Here is a sample code. First, let me get the import aside.
{-# LANGUAGE GeneralizedNewtypeDeriving, ScopedTypeVariables #-} import Control.Monad.IO.Class import Data.IORef import Data.Word import Reactive.Banana import Reactive.Banana.Frameworks import Text.Printf
Since I don’t have arduino, I will have to prototype several methods from hArduino.
newtype Arduino a = Arduino (IO a) deriving (Functor, Applicative, Monad, MonadIO) newtype Pin = Pin Word8 pin :: Word8 -> Pin pin = Pin digitalWrite :: Pin -> Bool -> Arduino () digitalWrite (Pin n) v = Arduino $ do printf "Pretend pin %d on the arduino just got turned %s.\n" n (if v then "on" else "off") digitalRead :: Pin -> Arduino Bool digitalRead (Pin n) = Arduino $ do printf "We need to pretend we read a value from pin %d.\n" n putStrLn "Should we return True or False?" readLn withArduino :: Arduino () -> IO () withArduino (Arduino body) = do putStrLn "Pretend we're initializing the arduino." body
In the rest of the code, I will pretend that the Arduino and Pin types are opaque.
We need a network of events to convert input events representing signals received from arduino into output events that describe what we want to send to arduino. To make things extremely simple, let me get data from one output and output the same data to another output.
eventNetwork :: forall t. Event t Bool -> Event t Bool eventNetwork = id
Then connect our network of events to the outside world. When the output events occur, I simply write this value to the IORef, which I can watch later.
main :: IO () main = do (inputPinAddHandler, fireInputPin) <- newAddHandler outputRef <- newIORef False let networkDescription :: forall t. Frameworks t => Moment t () networkDescription = do -- input inputPinE <- fromAddHandler inputPinAddHandler -- output let outputPinE = eventNetwork inputPinE reactimate $ writeIORef outputRef <$> outputPinE network <- compile networkDescription actuate network withArduino $ do let inputPin = pin 1 let outputPin = pin 2 -- initialize pins here... -- main loop loop inputPin outputPin fireInputPin outputRef
Note that reactimate and compile called only once, outside the main loop. These functions configure your network of events; you do not want to name them in each cycle.
Finally, we start the main loop.
loop :: Pin -> Pin -> (Bool -> IO ()) -> IORef Bool -> Arduino () loop inputPin outputPin fireInputPin outputRef = do -- read the input from the arduino inputValue <- digitalRead inputPin -- send the input to the event network liftIO $ fireInputPin inputValue -- read the output from the event network outputValue <- liftIO $ readIORef outputRef -- send the output to the arduino digitalWrite outputPin outputValue loop inputPin outputPin fireInputPin outputRef
Notice how we use liftIO to interact with the event network from within Arduino computing. We call fireInputPin to trigger an input event, the event network writeIORef an output response event, and the writeIORef we gave reactimate causes the value of the output event to be written to IORef. If the event network was more complex and the input event did not raise any output event, the contents of the IORef would remain unchanged. Regardless, we can observe this content and use it to determine which Arduino calculations should be performed. In this case, we simply send the output value to the predefined output.