zigbee-znet25-0.1: XBee ZNet 2.5 (ZigBee) wireless modem communications

MaintainerDavid Joyner <david@joynerhome.net>
Safe HaskellSafe-Infered




XBee ZNet 2.5 (ZigBee) frame encoder/decoder functions


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 runs in the State monad. DecoderState tracks the decoder's in/out-of frame state, current frame length, and other state variables.


initDecode :: DecoderStateSource

Initial state needed to run decode in the State monad.

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 Frames 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 encodes two separate frames, runs each array of ByteStrings 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
     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 separate ByteStrings. This is a by-product of the encode implementation as described above.
  • decode was only able to produce a result once the final ByteString of each Frame was processed. In this case the result was Right Frame. If an error had occurred, we'd see Left String instead.