module System.Hardware.Arduino.Protocol(Request(..), Response(..), package, unpackage) where
import Data.Bits ((.|.), (.&.), shiftL)
import Data.Char (chr)
import Data.List (intercalate)
import Data.Word (Word8, Word16)
import qualified Data.ByteString as B
import System.Hardware.Arduino.Parts
import System.Hardware.Arduino.Utils
data Request = QueryFirmware
| SetPinMode Pin PinMode
| DigitalRead Pin
| DigitalPortWrite Int Word8 Word8
instance Show Request where
show QueryFirmware = "QueryFirmWare"
show (SetPinMode p m) = "SetPinMode " ++ show p ++ " to " ++ show m
show (DigitalRead p) = "DigitalRead " ++ show p
show (DigitalPortWrite p l h) = "DigitalWrite " ++ show p ++ " to " ++ showBin l ++ "_" ++ showBin h
data Response = Firmware Word8 Word8 String
| DigitalPinState Pin PinMode Bool
| DigitalPortState Int Word16
| Unknown [Word8]
instance Show Response where
show (Firmware majV minV n) = "Firmware v" ++ show majV ++ "." ++ show minV ++ " (" ++ n ++ ")"
show (DigitalPinState p m v) = "DigitalPinState " ++ show p ++ "(" ++ show m ++ ") = " ++ if v then "HIGH" else "LOW"
show (DigitalPortState p w) = "DigitalPortState " ++ show p ++ " = " ++ show w
show (Unknown bs) = "Unknown [" ++ intercalate ", " (map showByte bs) ++ "]"
cdStart :: Word8
cdStart = 0xf0
cdEnd :: Word8
cdEnd = 0xf7
sysEx :: [Word8] -> B.ByteString
sysEx bs = B.pack $ cdStart : bs ++ [cdEnd]
package :: Request -> B.ByteString
package QueryFirmware = sysEx [0x79]
package (SetPinMode p m) = B.pack [0xf4, fromIntegral (pinNo p), fromIntegral (fromEnum m)]
package (DigitalRead p) = sysEx [0x6d, fromIntegral (pinNo p)]
package (DigitalPortWrite p l m) = B.pack [0x90 .|. fromIntegral p, l, m]
unpackage :: B.ByteString -> Response
unpackage inp
| length bs < 2 || head bs /= cdStart || last bs /= cdEnd
= Unknown bs
| True
= getResponse (init (tail bs))
where bs = B.unpack inp
getResponse :: [Word8] -> Response
getResponse (rf : majV : minV : rest)
| rf == 0x79
= Firmware majV minV (getString rest)
getResponse (pr : curPin : pinMode : pinState : [])
| pr == 0x6e
= DigitalPinState (pin (fromIntegral curPin)) (toEnum (fromIntegral pinMode)) (pinState /= 0)
getResponse (dpr : vL : vH : [])
| dpr .&. 0xF0 == 0x90
= let port = fromIntegral (dpr .&. 0x0F)
w = fromIntegral ((vH .&. 0x7F) `shiftL` 8) .|. fromIntegral (vL .&. 0x7F)
in DigitalPortState port w
getResponse bs = Unknown bs
getString :: [Word8] -> String
getString [] = ""
getString [a] = [chr (fromIntegral a)]
getString (l:h:rest) = c : getString rest
where c = chr $ fromIntegral $ h `shiftL` 8 .|. l