Portability | portable |
---|---|
Stability | experimental |
Maintainer | David Joyner <david@joynerhome.net> |
Safe Haskell | Safe-Infered |
XBee ZNet 2.5 (ZigBee) frame encoder/decoder functions
- encode :: Frame -> [ByteString]
- data DecoderState
- initDecode :: DecoderState
- decode :: MonadState DecoderState m => ByteString -> m [Either String Frame]
Frame encoder
encode :: Frame -> [ByteString]Source
Serialize a Frame
, escape control characters, and wrap the result with
framing bytes. Return an array of ByteString
suitable for transmission
to the XBee modem.
Note that this function returns an array of ByteString
. Encoding
takes place in a piece-wise manner and for efficiency's sake the individual
bits are not concatenated to form a single ByteString
. Typically this is
a non-issue however if you need a single ByteString
representation of the
Frame
you can always obtain it by calling concat
.
Here's an example that illustrates encode
usage as well as the
on-the-wire frame format:
import qualified Data.ByteString as B import Network.Protocol.ZigBee.ZNet25 import Text.Printf main = hexdump $ B.concat $ encode (ATCommand 0 (commandName "ND") B.empty) hexdump = mapM_ (putStr . printf "%02x ") . B.unpack
This prints:
7e 00 04 08 00 4e 44 65
The leading 7e
byte is the frame delimiter. This is followed by the 16-bit
frame length (4 bytes in this case), that many bytes of data (the
serialized ATCommand
frame), and the final checksum byte.
Stateful frame decoder
data DecoderState Source
decode :: MonadState DecoderState m => ByteString -> m [Either String Frame]Source
Decode a ByteString
in the State
monad, reversing the encode
process. Once a frame delimiter byte is found, the inner frame payload is
unescaped, the checksum is verified, and finally a Frame
is deserialized.
Note that this function may produce zero or more errors or Frame
s depending
on the DecoderState
and input byte string. Errors will be reported for
checksum errors and Frame
deserialization failures.
Here's a slightly more complex example that encode
s two separate frames,
runs each array of ByteString
s through decode
and prints the result
after running the State
monad:
import Control.Monad.State import qualified Data.ByteString as B import Network.Protocol.ZigBee.ZNet25 main = putStrLn $ show $ evalState (mapM decode bs) initDecode where bs = concat $ map encode [atndCommand, txRequest] atndCommand = ATCommand 1 (commandName "ND") B.empty txRequest = ZigBeeTransmitRequest 2 addr nwaddr 0 0 $ B.singleton 0x55 addr = address $ B.pack [ 0xde, 0xad, 0xbe, 0xef, 0xba, 0xda, 0xba, 0xda ] nwaddr = networkAddress $ B.pack [ 0x55, 0xaa ]
This prints:
[[],[],[],[Right (ATCommand 1 "ND" "")],[],[],[],[Right (ZigBeeTransmitRequest 2 de:ad:be:ef:ba:da:ba:da 55:aa 0 0 "U")]]
Note a few things:
- Each call to
encode
apparently produced four separateByteString
s. This is a by-product of theencode
implementation as described above. -
decode
was only able to produce a result once the finalByteString
of eachFrame
was processed. In this case the result wasRight
Frame
. If an error had occurred, we'd seeLeft
String
instead.