{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} import BGLib.Commands import BGLib.Types import Control.Concurrent import Control.Concurrent.Async import Control.Concurrent.STM import Control.Monad.IO.Class import Control.Monad.Reader import qualified Data.ByteString.Char8 as BSS import Data.Semigroup ((<>)) import Options.Applicative import Prelude hiding (print, putStrLn) import qualified Prelude as P import System.Exit import System.Hardware.Serialport -- This is our monad stack, most of the application runs inside this. type AppM env a = ReaderT env IO a -- We store the command line options here data AppOptions = AppOptions { appOptSerialPort :: String , appOptDebug :: Bool } -- The data structure will be our "env", the environment stored in the -- ReaderT env IO monad stack data App = App { appOptions :: AppOptions , appSerialPort :: SerialPort , appBGChan :: TChan BgPacket } -- Instances for our environment to properly serve the library -- functions. instance HasSerialPort App where getSerialPort = appSerialPort instance HasBGChan App where getBGChan = appBGChan instance HasDebug App where getDebug = appOptDebug . appOptions -- Command line parser optParser :: Parser AppOptions optParser = AppOptions <$> argument str ( metavar "PORT" <> help "Serial port" ) <*> switch ( long "debug" <> short 'd' <> help "Whether to be quiet" ) -- Takes an environment and runs a program with it inside the IO monad. execApp :: env -> AppM env a -> IO a execApp = flip runReaderT -- RUns a program in a new thread inside out AppM stack forkApp :: AppM env () -> AppM env ThreadId forkApp act = do env <- ask liftIO $ forkIO $ execApp env act -- Can be used to wait for an event handler to return a value. -- Waiting for a specific BLE device advertisement to appear for -- example. Timeout is in microseconds. withTimeOut :: Int -> AppM env a -> AppM env (Maybe a) withTimeOut t a = do env <- ask res <- liftIO $ race (threadDelay t) (execApp env a) return $ case res of Left () -> Nothing Right x -> Just x -- A few lifted functions putStrLn :: MonadIO m => String -> m () putStrLn = liftIO . P.putStrLn print :: (MonadIO m, Show a) => a -> m () print = liftIO . P.print main :: IO () main = do -- Run the command line parser appOpts <- execParser $ info ( optParser <**> helper ) ( fullDesc <> progDesc "Execute a short battery of test on port PORT" <> header "bgapitest - a short text / example for haskell-bglib" ) -- Build the application environment app <- App <$> return appOpts <*> openSerial (appOptSerialPort appOpts) (SerialPortSettings CS115200 8 One NoParity NoFlowControl 1000) <*> atomically newBroadcastTChan -- Run the application execApp app $ do -- Register an event handler for protocol errors. -- Event handlers are blocking. We use forkApp to make it -- "run in the background". _ <- forkApp $ evtSystemProtocolError $ \reason -> do liftIO $ die $ "*** PROTOCOL ERROR " ++ show reason -- Starts a thread that keeps reading packets from the serial port, -- pushing them to the broadcast TChan startPacketReader putStrLn "Running hello" systemHello putStrLn "If you can read this, we're fine. :)" putStrLn "" putStrLn "Getting system information:" (major, minor, patch, build, llVersion, protocolVersion, hw) <- systemGetInfo putStrLn $ "Major version: " ++ show major putStrLn $ "Minor version: " ++ show minor putStrLn $ "Patch version: " ++ show patch putStrLn $ "Build Version: " ++ show build putStrLn $ "Link Layer version: " ++ show llVersion putStrLn $ "Protocol version: " ++ show protocolVersion putStrLn $ "Hardware version: " ++ show hw putStrLn "" putStrLn "We should get a \"not connected\" error:" attclientAttributeWrite 0 0 "e" >>= print putStrLn "" putStrLn "Getting Bluetooth Address:" systemAddressGet >>= print putStrLn "" putStrLn "Running some encryption-decription tests" putStrLn "" let aeskey = "abcdefgh12345678" putStrLn $ "Setting AES key to " ++ aeskey systemAesSetkey $ toUInt8Array $ BSS.pack $ aeskey putStrLn "" let plaintext = "This is plain" putStrLn $ "Encrypting: " ++ plaintext encrypted <- systemAesEncrypt $ toUInt8Array $ BSS.pack $ plaintext putStrLn $ "Encrypted: " ++ bsShowHex (fromUInt8Array encrypted) putStrLn "" putStrLn $ "Decrypting" decrypted <- systemAesDecrypt encrypted putStrLn $ "Decrypted: " ++ BSS.unpack (fromUInt8Array decrypted) putStrLn "" _ <- gapDiscover GapDiscoverGeneric -- Register an event handler for scan responses. Can be done anywhere. -- The handler forks a thread that runs forever, and can be terminated -- later if necessary. _ <- withTimeOut 5000000 $ evtGapScanResponse $ \rssi _ sender _ _ _ -> do print rssi print sender putStrLn "" return $ Nothing -- We'd like to listen to further events. _ <- gapEndProcedure putStrLn "Let's cause trouble:" s <- askSerialPort _ <- liftIO $ send s $ BSS.pack "a" liftIO $ threadDelay 5000000