diff --git a/sbp.cabal b/sbp.cabal
--- a/sbp.cabal
+++ b/sbp.cabal
@@ -1,5 +1,5 @@
 name:                  sbp
-version:               0.50.6
+version:               0.50.7
 synopsis:              SwiftNav's SBP Library
 homepage:              https://github.com/swift-nav/libsbp
 license:               LGPL-3
@@ -50,10 +50,13 @@
                      , bytestring
                      , data-binary-ieee754
                      , monad-loops
+                     , scientific
                      , text
+                     , unordered-containers
   default-extensions:  RecordWildCards
                        TemplateHaskell
                        NoImplicitPrelude
+                       OverloadedStrings
 
 test-suite test
   type:                exitcode-stdio-1.0
diff --git a/src/SwiftNav/SBP.hs b/src/SwiftNav/SBP.hs
--- a/src/SwiftNav/SBP.hs
+++ b/src/SwiftNav/SBP.hs
@@ -5,16 +5,21 @@
 -- Maintainer:  Mark Fine <dev@swiftnav.com>
 -- Stability:   experimental
 -- Portability: portable
+--
+-- SBP message containers and serialization utilities.
 
 module SwiftNav.SBP where
 
-import BasicPrelude
+import BasicPrelude hiding (lookup)
+import Data.Aeson hiding (decode)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.Put
 import Data.ByteString
 import Data.ByteString.Lazy hiding (ByteString)
 import Data.ByteString.Builder
+import Data.HashMap.Strict
+import Data.Scientific
 import Data.Word
 import SwiftNav.CRC16
 import SwiftNav.SBP.Acquisition
@@ -30,9 +35,14 @@
 import SwiftNav.SBP.System
 import SwiftNav.SBP.Tracking
 
-msgSBPPreamble :: Word8
-msgSBPPreamble = 0x55
+msgPreamble :: Word8
+msgPreamble = 0x55
 
+-- | Default sender ID. Intended for messages sent from the host to
+-- the device.
+defaultSenderID :: Word16
+defaultSenderID = 0x42
+
 data Msg = Msg
   { msgSBPType    :: Word16
   , msgSBPSender  :: Word16
@@ -65,7 +75,24 @@
     word8 msgSBPLen       <>
     byteString msgSBPPayload
 
+instance FromJSON Msg where
+  parseJSON (Object v) = do
+    Msg <$> v .: "msg_type"
+        <*> v .: "sender"
+        <*> v .: "length"
+        <*> v .: "payload"
+        <*> v .: "crc"
+  parseJSON _ = mzero
 
+instance ToJSON Msg where
+  toJSON Msg {..} = object [ "preamble" .= msgPreamble
+                           , "msg_type" .= msgSBPType
+                           , "sender" .= msgSBPSender
+                           , "length" .= msgSBPLen
+                           , "payload" .= msgSBPPayload
+                           , "crc" .= msgSBPCrc
+                           ]
+
 data SBPMsg =
      SBPMsgAcqResult MsgAcqResult Msg
    | SBPMsgAcqResultDepA MsgAcqResultDepA Msg
@@ -140,81 +167,81 @@
 instance Binary SBPMsg where
   get = do
     preamble <- getWord8
-    if preamble /= msgSBPPreamble then get else do
+    if preamble /= msgPreamble then get else do
       sbp <- get
       return $ decode' sbp where
-        decode' sbp
-          | checkCrc sbp /= msgSBPCrc sbp = SBPMsgBadCrc sbp
-          | msgSBPType sbp == msgAcqResult = SBPMsgAcqResult (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgAcqResultDepA = SBPMsgAcqResultDepA (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgAlmanac = SBPMsgAlmanac (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgBasePos = SBPMsgBasePos (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgBaselineEcef = SBPMsgBaselineEcef (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgBaselineNed = SBPMsgBaselineNed (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgBootloaderHandshakeDepA = SBPMsgBootloaderHandshakeDepA (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgBootloaderHandshakeReq = SBPMsgBootloaderHandshakeReq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgBootloaderHandshakeResp = SBPMsgBootloaderHandshakeResp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgBootloaderJumpToApp = SBPMsgBootloaderJumpToApp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgCwResults = SBPMsgCwResults (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgCwStart = SBPMsgCwStart (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgDops = SBPMsgDops (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgEphemeris = SBPMsgEphemeris (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgEphemerisDepA = SBPMsgEphemerisDepA (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgEphemerisDepB = SBPMsgEphemerisDepB (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgExtEvent = SBPMsgExtEvent (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFileioReadDirReq = SBPMsgFileioReadDirReq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFileioReadDirResp = SBPMsgFileioReadDirResp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFileioReadReq = SBPMsgFileioReadReq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFileioReadResp = SBPMsgFileioReadResp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFileioRemove = SBPMsgFileioRemove (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFileioWriteReq = SBPMsgFileioWriteReq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFileioWriteResp = SBPMsgFileioWriteResp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFlashDone = SBPMsgFlashDone (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFlashErase = SBPMsgFlashErase (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFlashProgram = SBPMsgFlashProgram (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFlashReadReq = SBPMsgFlashReadReq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgFlashReadResp = SBPMsgFlashReadResp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgGpsTime = SBPMsgGpsTime (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgHeartbeat = SBPMsgHeartbeat (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgIarState = SBPMsgIarState (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgInitBase = SBPMsgInitBase (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgLog = SBPMsgLog (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgMaskSatellite = SBPMsgMaskSatellite (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgObs = SBPMsgObs (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgObsDepA = SBPMsgObsDepA (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgPosEcef = SBPMsgPosEcef (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgPosLlh = SBPMsgPosLlh (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgPrintDep = SBPMsgPrintDep (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgReset = SBPMsgReset (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgResetFilters = SBPMsgResetFilters (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgSetTime = SBPMsgSetTime (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgSettingsReadByIndexDone = SBPMsgSettingsReadByIndexDone (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgSettingsReadByIndexReq = SBPMsgSettingsReadByIndexReq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgSettingsReadByIndexResp = SBPMsgSettingsReadByIndexResp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgSettingsReadReq = SBPMsgSettingsReadReq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgSettingsReadResp = SBPMsgSettingsReadResp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgSettingsSave = SBPMsgSettingsSave (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgSettingsWrite = SBPMsgSettingsWrite (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgStartup = SBPMsgStartup (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgStmFlashLockSector = SBPMsgStmFlashLockSector (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgStmUniqueIdResp = SBPMsgStmUniqueIdResp (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgThreadState = SBPMsgThreadState (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgTrackingIq = SBPMsgTrackingIq (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgTrackingState = SBPMsgTrackingState (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgTrackingStateDepA = SBPMsgTrackingStateDepA (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgTweet = SBPMsgTweet (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgUartState = SBPMsgUartState (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgVelEcef = SBPMsgVelEcef (decode (fromStrict (msgSBPPayload sbp))) sbp
-          | msgSBPType sbp == msgVelNed = SBPMsgVelNed (decode (fromStrict (msgSBPPayload sbp))) sbp
+        decode' sbp@Msg {..}
+          | checkCrc sbp /= msgSBPCrc = SBPMsgBadCrc sbp
+          | msgSBPType == msgAcqResult = SBPMsgAcqResult (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgAcqResultDepA = SBPMsgAcqResultDepA (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgAlmanac = SBPMsgAlmanac (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgBasePos = SBPMsgBasePos (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgBaselineEcef = SBPMsgBaselineEcef (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgBaselineNed = SBPMsgBaselineNed (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgBootloaderHandshakeDepA = SBPMsgBootloaderHandshakeDepA (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgBootloaderHandshakeReq = SBPMsgBootloaderHandshakeReq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgBootloaderHandshakeResp = SBPMsgBootloaderHandshakeResp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgBootloaderJumpToApp = SBPMsgBootloaderJumpToApp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgCwResults = SBPMsgCwResults (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgCwStart = SBPMsgCwStart (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgDops = SBPMsgDops (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgEphemeris = SBPMsgEphemeris (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgEphemerisDepA = SBPMsgEphemerisDepA (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgEphemerisDepB = SBPMsgEphemerisDepB (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgExtEvent = SBPMsgExtEvent (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFileioReadDirReq = SBPMsgFileioReadDirReq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFileioReadDirResp = SBPMsgFileioReadDirResp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFileioReadReq = SBPMsgFileioReadReq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFileioReadResp = SBPMsgFileioReadResp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFileioRemove = SBPMsgFileioRemove (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFileioWriteReq = SBPMsgFileioWriteReq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFileioWriteResp = SBPMsgFileioWriteResp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFlashDone = SBPMsgFlashDone (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFlashErase = SBPMsgFlashErase (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFlashProgram = SBPMsgFlashProgram (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFlashReadReq = SBPMsgFlashReadReq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgFlashReadResp = SBPMsgFlashReadResp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgGpsTime = SBPMsgGpsTime (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgHeartbeat = SBPMsgHeartbeat (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgIarState = SBPMsgIarState (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgInitBase = SBPMsgInitBase (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgLog = SBPMsgLog (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgMaskSatellite = SBPMsgMaskSatellite (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgObs = SBPMsgObs (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgObsDepA = SBPMsgObsDepA (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgPosEcef = SBPMsgPosEcef (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgPosLlh = SBPMsgPosLlh (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgPrintDep = SBPMsgPrintDep (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgReset = SBPMsgReset (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgResetFilters = SBPMsgResetFilters (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgSetTime = SBPMsgSetTime (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgSettingsReadByIndexDone = SBPMsgSettingsReadByIndexDone (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgSettingsReadByIndexReq = SBPMsgSettingsReadByIndexReq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgSettingsReadByIndexResp = SBPMsgSettingsReadByIndexResp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgSettingsReadReq = SBPMsgSettingsReadReq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgSettingsReadResp = SBPMsgSettingsReadResp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgSettingsSave = SBPMsgSettingsSave (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgSettingsWrite = SBPMsgSettingsWrite (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgStartup = SBPMsgStartup (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgStmFlashLockSector = SBPMsgStmFlashLockSector (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgStmUniqueIdResp = SBPMsgStmUniqueIdResp (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgThreadState = SBPMsgThreadState (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgTrackingIq = SBPMsgTrackingIq (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgTrackingState = SBPMsgTrackingState (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgTrackingStateDepA = SBPMsgTrackingStateDepA (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgTweet = SBPMsgTweet (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgUartState = SBPMsgUartState (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgVelEcef = SBPMsgVelEcef (decode (fromStrict msgSBPPayload)) sbp
+          | msgSBPType == msgVelNed = SBPMsgVelNed (decode (fromStrict msgSBPPayload)) sbp
           | otherwise = SBPMsgUnknown sbp
 
   put msg = do
-    putWord8 msgSBPPreamble
+    putWord8 msgPreamble
     put $ encode' msg
     where
       encode' (SBPMsgAcqResult _ sbp) = sbp
@@ -284,3 +311,154 @@
       encode' (SBPMsgVelEcef _ sbp) = sbp
       encode' (SBPMsgVelNed _ sbp) = sbp
       encode' (SBPMsgUnknown sbp) = sbp
+      encode' (SBPMsgBadCrc  sbp) = sbp
+
+instance FromJSON SBPMsg where
+  parseJSON (Object o) = case lookup "msg_type" o of
+    Nothing         -> mzero
+    Just (Number msgType) -> decode' (toBoundedInteger msgType) where
+                               decode' Nothing = mzero
+                               decode' (Just t)
+                                 | t == msgAcqResult = SBPMsgAcqResult <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgAcqResultDepA = SBPMsgAcqResultDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgAlmanac = SBPMsgAlmanac <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgBasePos = SBPMsgBasePos <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgBaselineEcef = SBPMsgBaselineEcef <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgBaselineNed = SBPMsgBaselineNed <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgBootloaderHandshakeDepA = SBPMsgBootloaderHandshakeDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgBootloaderHandshakeReq = SBPMsgBootloaderHandshakeReq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgBootloaderHandshakeResp = SBPMsgBootloaderHandshakeResp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgBootloaderJumpToApp = SBPMsgBootloaderJumpToApp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgCwResults = SBPMsgCwResults <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgCwStart = SBPMsgCwStart <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgDops = SBPMsgDops <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgEphemeris = SBPMsgEphemeris <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgEphemerisDepA = SBPMsgEphemerisDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgEphemerisDepB = SBPMsgEphemerisDepB <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgExtEvent = SBPMsgExtEvent <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFileioReadDirReq = SBPMsgFileioReadDirReq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFileioReadDirResp = SBPMsgFileioReadDirResp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFileioReadReq = SBPMsgFileioReadReq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFileioReadResp = SBPMsgFileioReadResp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFileioRemove = SBPMsgFileioRemove <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFileioWriteReq = SBPMsgFileioWriteReq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFileioWriteResp = SBPMsgFileioWriteResp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFlashDone = SBPMsgFlashDone <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFlashErase = SBPMsgFlashErase <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFlashProgram = SBPMsgFlashProgram <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFlashReadReq = SBPMsgFlashReadReq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgFlashReadResp = SBPMsgFlashReadResp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgGpsTime = SBPMsgGpsTime <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgHeartbeat = SBPMsgHeartbeat <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgIarState = SBPMsgIarState <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgInitBase = SBPMsgInitBase <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgLog = SBPMsgLog <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgMaskSatellite = SBPMsgMaskSatellite <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgObs = SBPMsgObs <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgObsDepA = SBPMsgObsDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgPosEcef = SBPMsgPosEcef <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgPosLlh = SBPMsgPosLlh <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgPrintDep = SBPMsgPrintDep <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgReset = SBPMsgReset <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgResetFilters = SBPMsgResetFilters <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgSetTime = SBPMsgSetTime <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgSettingsReadByIndexDone = SBPMsgSettingsReadByIndexDone <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgSettingsReadByIndexReq = SBPMsgSettingsReadByIndexReq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgSettingsReadByIndexResp = SBPMsgSettingsReadByIndexResp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgSettingsReadReq = SBPMsgSettingsReadReq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgSettingsReadResp = SBPMsgSettingsReadResp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgSettingsSave = SBPMsgSettingsSave <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgSettingsWrite = SBPMsgSettingsWrite <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgStartup = SBPMsgStartup <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgStmFlashLockSector = SBPMsgStmFlashLockSector <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgStmUniqueIdResp = SBPMsgStmUniqueIdResp <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgThreadState = SBPMsgThreadState <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgTrackingIq = SBPMsgTrackingIq <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgTrackingState = SBPMsgTrackingState <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgTrackingStateDepA = SBPMsgTrackingStateDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgTweet = SBPMsgTweet <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgUartState = SBPMsgUartState <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgVelEcef = SBPMsgVelEcef <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | t == msgVelNed = SBPMsgVelNed <$> parseJSON (Object o) <*> parseJSON (Object o)
+                                 | otherwise = SBPMsgUnknown <$> parseJSON (Object o)
+    Just _ -> mzero
+  parseJSON (_) = mzero
+
+merge :: Value -> Value -> Value
+merge (Object one) (Object two) = Object $ one <> two
+merge _ _ = undefined
+
+instance ToJSON SBPMsg where
+   toJSON (SBPMsgAcqResult msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgAcqResultDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgAlmanac msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgBasePos msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgBaselineEcef msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgBaselineNed msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgBootloaderHandshakeDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgBootloaderHandshakeReq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgBootloaderHandshakeResp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgBootloaderJumpToApp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgCwResults msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgCwStart msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgDops msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgEphemeris msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgEphemerisDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgEphemerisDepB msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgExtEvent msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFileioReadDirReq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFileioReadDirResp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFileioReadReq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFileioReadResp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFileioRemove msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFileioWriteReq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFileioWriteResp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFlashDone msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFlashErase msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFlashProgram msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFlashReadReq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgFlashReadResp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgGpsTime msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgHeartbeat msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgIarState msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgInitBase msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgLog msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgM25FlashWriteStatus msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgMaskSatellite msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgNapDeviceDnaReq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgNapDeviceDnaResp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgObs msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgObsDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgPosEcef msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgPosLlh msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgPrintDep msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgReset msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgResetFilters msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgSetTime msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgSettingsReadByIndexDone msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgSettingsReadByIndexReq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgSettingsReadByIndexResp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgSettingsReadReq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgSettingsReadResp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgSettingsSave msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgSettingsWrite msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgStartup msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgStmFlashLockSector msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgStmFlashUnlockSector msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgStmUniqueIdReq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgStmUniqueIdResp msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgThreadState msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgTrackingIq msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgTrackingState msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgTrackingStateDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgTweet msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgUartState msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgVelEcef msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgVelNed msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgBadCrc sbp) = toJSON sbp
+   toJSON (SBPMsgUnknown sbp) = toJSON sbp
diff --git a/src/SwiftNav/SBP/Bootload.hs b/src/SwiftNav/SBP/Bootload.hs
--- a/src/SwiftNav/SBP/Bootload.hs
+++ b/src/SwiftNav/SBP/Bootload.hs
@@ -45,6 +45,9 @@
   put MsgBootloaderHandshakeReq =
     return ()
 
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgBootloaderHandshakeReq_" . stripPrefix "msgBootloaderHandshakeReq_"}
+             ''MsgBootloaderHandshakeReq)
+
 msgBootloaderHandshakeResp :: Word16
 msgBootloaderHandshakeResp = 0x00B4
 
@@ -115,6 +118,9 @@
 
   put MsgNapDeviceDnaReq =
     return ()
+
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgNapDeviceDnaReq_" . stripPrefix "msgNapDeviceDnaReq_"}
+             ''MsgNapDeviceDnaReq)
 
 msgNapDeviceDnaResp :: Word16
 msgNapDeviceDnaResp = 0x00DD
diff --git a/src/SwiftNav/SBP/Flash.hs b/src/SwiftNav/SBP/Flash.hs
--- a/src/SwiftNav/SBP/Flash.hs
+++ b/src/SwiftNav/SBP/Flash.hs
@@ -254,6 +254,9 @@
   put MsgStmUniqueIdReq =
     return ()
 
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgStmUniqueIdReq_" . stripPrefix "msgStmUniqueIdReq_"}
+             ''MsgStmUniqueIdReq)
+
 msgStmUniqueIdResp :: Word16
 msgStmUniqueIdResp = 0x00E5
 
diff --git a/src/SwiftNav/SBP/Piksi.hs b/src/SwiftNav/SBP/Piksi.hs
--- a/src/SwiftNav/SBP/Piksi.hs
+++ b/src/SwiftNav/SBP/Piksi.hs
@@ -44,6 +44,9 @@
   put MsgAlmanac =
     return ()
 
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgAlmanac_" . stripPrefix "msgAlmanac_"}
+             ''MsgAlmanac)
+
 msgSetTime :: Word16
 msgSetTime = 0x0068
 
@@ -61,6 +64,9 @@
   put MsgSetTime =
     return ()
 
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSetTime_" . stripPrefix "msgSetTime_"}
+             ''MsgSetTime)
+
 msgReset :: Word16
 msgReset = 0x00B2
 
@@ -77,6 +83,9 @@
   put MsgReset =
     return ()
 
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgReset_" . stripPrefix "msgReset_"}
+             ''MsgReset)
+
 msgCwResults :: Word16
 msgCwResults = 0x00C0
 
@@ -95,6 +104,9 @@
   put MsgCwResults =
     return ()
 
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgCwResults_" . stripPrefix "msgCwResults_"}
+             ''MsgCwResults)
+
 msgCwStart :: Word16
 msgCwStart = 0x00C1
 
@@ -113,6 +125,9 @@
   put MsgCwStart =
     return ()
 
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgCwStart_" . stripPrefix "msgCwStart_"}
+             ''MsgCwStart)
+
 msgResetFilters :: Word16
 msgResetFilters = 0x0022
 
@@ -154,6 +169,9 @@
 
   put MsgInitBase =
     return ()
+
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgInitBase_" . stripPrefix "msgInitBase_"}
+             ''MsgInitBase)
 
 msgThreadState :: Word16
 msgThreadState = 0x0017
diff --git a/src/SwiftNav/SBP/Settings.hs b/src/SwiftNav/SBP/Settings.hs
--- a/src/SwiftNav/SBP/Settings.hs
+++ b/src/SwiftNav/SBP/Settings.hs
@@ -46,6 +46,9 @@
   put MsgSettingsSave =
     return ()
 
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSettingsSave_" . stripPrefix "msgSettingsSave_"}
+             ''MsgSettingsSave)
+
 msgSettingsWrite :: Word16
 msgSettingsWrite = 0x00A0
 
@@ -185,3 +188,6 @@
 
   put MsgSettingsReadByIndexDone =
     return ()
+
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSettingsReadByIndexDone_" . stripPrefix "msgSettingsReadByIndexDone_"}
+             ''MsgSettingsReadByIndexDone)
