Last active

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

Monitoring a serial connection with a Threepenny UI frontend.

View SerialThreepenny.hs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
-- Imports for serial port
import qualified Data.ByteString.Char8 as B
import System.Hardware.Serialport
(openSerial, recv, closeSerial, defaultSerialSettings)
 
-- Imports for threading and stuff
import Control.Monad (void, forever, mapM_)
import Control.Concurrent (forkIO, killThread)
import Control.Concurrent.Chan
(Chan, newChan, dupChan, writeChan, getChanContents)
 
-- Threepenny
import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core hiding (text)
 
import System.Cmd (system)
 
main = do
-- Start the serial read, which will just send all bytes into a Chan.
bus <- newChan
s <- openSerial "COM4" defaultSerialSettings
t <- forkIO $ forever $ recv s 1 >>= writeChan bus
 
-- Start the Threepenny GUI (and, under Windows, launch the webpage automatically)
let port = 10000
system $ "start \"\" \"http://localhost:" ++ show port ++ "\""
startGUI defaultConfig { tpPort = port } $ setup bus
 
-- Runs after the TP UI ends. I assume.
killThread t
closeSerial s
 
setup :: Bus -> Window -> IO ()
setup globalBus window = void $ do
-- Bus clone lets us listen in
bus <- dupChan globalBus
 
-- Page elements
updateList <- UI.div #. "updates"
return window # set title "Serial"
getBody window #+
[ UI.h1 #+ [string "Serial thing"]
, element updateList
]
 
-- Actual interesting bit. This process runs when you open the page.
-- It listens to the bus and creates a new element for each message.
listener <- forkIO $ listen window bus updateList
on UI.disconnect window $ const $ killThread listener
 
-- Hear a byte on the bus -> create a new div.
listen window bus elem = getChanContents bus >>= mapM_ add
where add u = atomic window $ element elem #+ [mkUpdate u]
 
-- Another utility function.
mkUpdate u = UI.div #. "update" #+ [string $ B.unpack u]
 
-- Bus type, if you hadn't guessed.
type Bus = Chan B.ByteString

Cool! Could use bracket for open/closeSerial and forkIO/killThread to get automatic cleanup.

Owner

@robinp Thanks! I've never used bracket before. I'll have a look!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.