/***************************************************************************
 * Copyright (c) 2009 Digi International Inc., All Rights Reserved
 * 
 * This software contains proprietary and confidential information of Digi
 * International Inc.  By accepting transfer of this copy, Recipient agrees
 * to retain this software in confidence, to prevent disclosure to others,
 * and to make no use of this software other than that for which it was
 * delivered.  This is an unpublished copyrighted work of Digi International
 * Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 * prohibited.
 * 
 * Restricted Rights Legend
 *
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 * Technical Data and Computer Software clause at DFARS 252.227-7031 or
 * subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 * Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 ***************************************************************************
 *
 * @Descr: XBeeDeviceManager Class
 *
 * @History:
 *    10/01/09:   gsarabia      initial version.
 *
 ***************************************************************************/

#region Namespace Inclusions

using System;
using System.IO;
using System.Text;
using System.IO.Ports;
using System.Threading;

#endregion

namespace Digi.ZigBee
{
    public class XBeeDeviceManager : XBeeSerialPort
    {
        AuxMethods Aux = new AuxMethods();

        #region Variables

        private int InternalTimeout = 1000; // 1000 ms

        internal ulong InternalDestIEEEAddr = 0x00;
        private ushort InternalDestNwkAddr = 0xFFFE;
        private byte InternalSourceEP = 0xE8;
        private byte InternalDestEP = 0xE8;
        private ushort InternalClusterId = 0x11;
        private ushort InternalProfileId = 0xC105;
        private byte InternalBroadcastRadius = 0x00;
        private byte InternalTxOptions = 0x00;  // Unicast Transmission
        private bool InternalRemoteATCmdEnable = false;
        private byte InternalRemoteATCmdOptions = 0x02; // Apply Changes
        private bool InternalApplyChangesEnable = true; // Apply Changes
        private bool InternalExplicitAddressingEnable = false;
        private bool InternalAPIOptions = false;

        private Node_Information[] Network;
        private Host_Information Host;

        private Frame_Id_Mode InternalFrameIdMode = Frame_Id_Mode.Autoincrement;
        private byte InternalFrameId = 0xFF;
        private Random InternalRandom = new Random();
        private Encoding InternalEncoding = Encoding.ASCII;

        private const byte StartDelimiter = 0x7E;   // Start Delimiter
        private const byte NoAck = 0x00;    // No Acknowledgement
        private const byte CR = 0x0D;   // "\r"
        private static byte[] OK = Encoding.ASCII.GetBytes("OK\r"); // "OK"
        private static byte[] ERROR = Encoding.ASCII.GetBytes("ERROR\r");   // "ERROR"

        private byte[] XBeeRspFrame;

        private bool WaitXBeeRsp = false;
        public const int InfiniteTimeout = 0;   // Infinite Timeout
        
        public struct TxStatus 
        {
            public ushort? NetworkAddress;
            public byte? RetryCount;
            public Delivery? DeliveryStatus;
            public Discovery? DiscoveryStatus;
        }

        public struct ATCmdRsp
        {
            public Status_Rsp CmdStatus;
            public byte[] CmdData;
        }

        public struct Node_Discovered
        {
            public ulong? IEEEAddress;
            public ushort? NetworkAddress;
            public string Identifier;
            public ushort? ParentNetworkAddress;
            public Node_Type? NodeType;
            public ushort? ProfileId;
            public ushort? ManufacturerId;
            public uint? DeviceTypeId;
        }

        public struct Node_Information
        {
            public ulong IEEEAddress;
            public ushort NetworkAddress;
            public string Identifier;
            public Node_Type NodeType;
            public Hardware? Hardware;
        }

        public struct Host_Information
        {
            public ulong IEEEAddress;
            public ushort NetworkAddress;
            public string Identifier;
            public Node_Type NodeType;
            public Hardware? Hardware;
            public Firmware? Firmware;
        }

        public struct Sample
        {
            public byte Samples;
            public ushort? DigitalMask;
            public byte? AnalogMask;
            public ushort? DigitalSamples;
            public ushort[] AnalogSamples;
        }

        public struct FormatDeviceTypeId
        {
            public ModuleType ModuleId;
            public ProductType ProductId;
        }

        private bool Loopback = false;
        private bool InternalUploading = false;

        static TimerCallback Callback = new TimerCallback(ATCmdMode);
        private int InternalInterval = 9700;
        private static Timer InternalCommandModeTimeoutTimer = new Timer(Callback, null, Timeout.Infinite, Timeout.Infinite);

        private const byte NULL = 0x00; // Null 
        private const byte SOH = 0x01;  // Start of Header 
        private const byte EOT = 0x04;  // End of Transmission 
        private const byte ACK = 0x06;  // Acknowledgment
        private const byte NAK = 0x15;  // Negative Acknowledgement
        private const byte CAN = 0x18;  // Cancel
        private const byte C = 0x43;    // Start of a CRC request
        private const byte Padding = 0xFF;  //Padding

        #endregion

        #region Constructors

        /// <summary>Initializes a new instance of the XBeeDevice class using the specified port name, baud rate, and parity bit.</summary>
        /// <param name="MyPortName">The port to use.</param>
        public XBeeDeviceManager(string MyPortName)
            : base(MyPortName)
        {
            XBeeSerialPortOpen += new XBeeSerialPortOpenEventHandler(DetectingOperationMode);
            XBeeFrameReceived += new XBeeFrameReceivedEventHandler(XBeeRxFrame);
        }

        /// <summary>Initializes a new instance of the XBeeDevice class using the specified port name, baud rate, and parity bit.</summary>
        /// <param name="MyPortName">The port to use.</param>
        /// <param name="MyBaudRate">The serial baud rate.</param>
        public XBeeDeviceManager(string MyPortName, BaudRateEnum MyBaudRate)
            : base(MyPortName, MyBaudRate)
        {
            XBeeSerialPortOpen += new XBeeSerialPortOpenEventHandler(DetectingOperationMode);
            XBeeFrameReceived += new XBeeFrameReceivedEventHandler(XBeeRxFrame);
        }

        /// <summary>Initializes a new instance of the XBeeDevice class using the specified port name, baud rate, and parity bit.</summary>
        /// <param name="MyPortName">The port to use.</param>
        /// <param name="MyBaudRate">The serial baud rate.</param>
        /// <param name="MyParity">The parity-checking protocol.</param>
        public XBeeDeviceManager(string MyPortName, BaudRateEnum MyBaudRate, ParityEnum MyParity)
            : base(MyPortName, MyBaudRate, MyParity)
        {
            XBeeSerialPortOpen += new XBeeSerialPortOpenEventHandler(DetectingOperationMode);
            XBeeFrameReceived += new XBeeFrameReceivedEventHandler(XBeeRxFrame);
        }

        #endregion

        #region Properties

            #region Timeout XBee

            /// <summary>Gets or sets the Timeout in milliseconds.</summary>
            public int TimeoutXBee
            {
                get
                {
                    return InternalTimeout;
                }
                set 
                {
                    if (value < 0)
                        throw new ArgumentOutOfRangeException("Timeout", "Timeout. The Timeout value is less than zero.");
                    InternalTimeout = value; 
                }
            }

            #endregion

            #region Frame ID Mode

            /// <summary>Gets or sets the Frame ID mode. Default Autoincrement Mode.</summary>
            public Frame_Id_Mode FrameIdMode
            {
                get
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Frame ID Mode. This command is not supported in Transparent Operation Mode.");
                    return InternalFrameIdMode;
                }
                set
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Frame ID Mode. This command is not supported in Transparent Operation Mode.");
                    if (Enum.IsDefined(typeof(Frame_Id_Mode), value))
                    {
                        InternalFrameIdMode = value;
                        if (value != Frame_Id_Mode.Constant)
                            InternalFrameId = (byte)InternalRandom.Next(0x01, 0x100);
                    }
                    else
                        throw new ArgumentOutOfRangeException("Frame ID Mode", "Frame ID Mode is out of legal range.");
                }
            }

            #endregion

            #region Frame ID

            /// <summary>
            /// Gets or sets the Frame ID.
            /// Identifies the UART data frame for the host to correlate with 
            /// a subsequent acknowledgement. If set to 0x00, no response is sent.
            /// </summary>
            public byte FrameId
            {
                get
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Frame ID. This command is not supported in Transparent Operation Mode.");
                    return InternalFrameId;
                }
                set
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Frame ID. This command is not supported in Transparent Operation Mode.");
                    if (InternalFrameIdMode == Frame_Id_Mode.Constant)
                        InternalFrameId = value;
                    else
                        throw new InvalidOperationException("Frame ID. The specified operation is only available with Constant Frame ID Mode.");
                }
            }

            #endregion

            #region Remote AT Command Enable

            /// <summary>Gets or sets the Remote AT Command enable.</summary>
            internal bool RemoteATCmdEnable
            {
                get
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Remote AT Command Enable. This command is not supported in Transparent Operation Mode.");
                    return InternalRemoteATCmdEnable;
                }
                set
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Remote AT Command Enable. This command is not supported in Transparent Operation Mode.");
                    InternalRemoteATCmdEnable = value;
                }
            }

            #endregion

            #region Explicit Addressing Enable

            /// <summary>Gets or sets the Explicit Addressing enable.</summary>
            public bool ExplicitAddressingEnable
            {
                get
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Explicit Addressing Enable. This command is not supported in Transparent Operation Mode.");
                    return InternalExplicitAddressingEnable;
                }
                set
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Explicit Addressing Enable. This command is not supported in Transparent Operation Mode.");
                    InternalExplicitAddressingEnable = value;
                }
            }

            #endregion

            #region Apply Changes Enable

            /// <summary>
            /// Gets or sets the Apply Changes enable. 
            /// New parameter values are applied after executing the AT Command or are queued and not applied 
            /// until either an AT Command with Apply Changes enable or the Apply Changes command is issued.
            /// </summary>
            public bool ApplyChangesEnable
            {
                get
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Apply Changes Enable. This command is not supported in Transparent Operation Mode.");
                    return InternalApplyChangesEnable;
                }
                set
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Apply Changes Enable. This command is not supported in Transparent Operation Mode.");
                    InternalApplyChangesEnable = value;
                    if (InternalApplyChangesEnable)
                        InternalRemoteATCmdOptions = 0x02;    // Apply Changes.
                    else
                        InternalRemoteATCmdOptions = 0x00;    // No Apply Changes.
                }
            }

            #endregion

            #region Broadcast Radius

            /// <summary>
            /// Gets or Sets maximum number of hops a broadcast transmission can traverse. 
            /// If sets to 0x00, the TX radius will be set to the maximum hop value. 
            /// </summary>
            public byte BroadcastRadius
            {
                get
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Broadcast Radius. This command is not supported in Transparent Operation Mode.");
                    return InternalBroadcastRadius;
                }
                set
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Broadcast Radius. This command is not supported in Transparent Operation Mode.");
                    InternalBroadcastRadius = value;
                }
            }

            #endregion

            #region Multicast Transmission Enable

            /// <summary>
            /// Gets or Sets Multicast Transmission Enable.
            /// If multicast transmission is disabled send unicast transmission. 
            /// Default multicast transmission disabled. 
            /// </summary>
            public bool MulticastTransmissionEnable
            {
                get
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Multicast Transmission Enable. This command is not supported in Transparent Operation Mode.");
                    if (InternalTxOptions == 0x08)
                        return true;    // Multicast Transmission.
                    else
                        return false;   // Unicast Transmission.
                }
                set
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new NotSupportedException("Multicast Transmission Enable. This command is not supported in Transparent Operation Mode.");
                    if (value)
                        InternalTxOptions = 0x08;    // Multicast Transmission.
                    else
                        InternalTxOptions = 0x00;    // Unicast Transmission.
                }
            }

            #endregion

            #region Network Devices

            /// <summary>
            /// Gets all nodes joined with the host. 
            /// The following information is reported for each node:
            /// -.IEEE 64-bit Address.
            /// -.16-bit Network Address.
            /// -.Node Identifier.
            /// -.Node Type. (Coordinator, Router, End Device).
            /// -.Hardware. (XBee, XBee-PRO).
            /// </summary>
            public Node_Information[] NetworkDevices
            {
                get
                {
                    return Network;
                }
            }

            #endregion

            #region Host Device

            /// <summary>
            /// Gets the following information for Host:
            /// -.IEEE 64-bit Address.
            /// -.16-bit Network Address.
            /// -.Node Identifier.
            /// -.Node Type. (Coordinator, Router, End Device).
            /// -.Hardware. (XBee, XBee-PRO).
            /// -.Firmware. (ZB, ZNET).
            /// </summary>
            public Host_Information HostDevice
            {
                get
                {
                    return Host;
                }
            }

            #endregion

        #endregion

        #region Methods

            #region Detecting Operation Mode

            /// <summary>Detecting operation mode.</summary>
            private void DetectingOperationMode()
            {
                try
                {
                    OperationMode = OperationModeEnum.API;
                    APIMode();
                    APIOptions();
                }
                catch
                {
                    try
                    {
                        OperationMode = OperationModeEnum.Transparent;
                        if (EnterCommandMode())
                        {
                            ushort CT = CommandModeTimeout();
                            InternalInterval = CT - 500;
                            InternalCommandModeTimeoutTimer.Change(Timeout.Infinite, InternalInterval);
                        }
                        else
                            throw new Exception("Error initializing the library.");
                    }
                    catch
                    {
                        throw new Exception("Error initializing the library.");
                    }
                }
                try
                {
                    RegisterDevices();
                }
                catch
                {
                    throw new Exception("Error initializing the library.");
                }
            }

            #endregion

            #region Register Devices

            /// <summary>Register devices.</summary>
            private void RegisterDevices()
            {
                try
                {
                    byte Options = NetworkDiscoveryOptions();
                    if ((Options & 0x02) != 0x02)
                        NetworkDiscoveryOptions((byte)(Options | 0x02));
                    Node_Discovered[] NodesDiscovered = NodeDiscover();
                    Node_Information[] AuxNetwork = Network;
                    Network = new Node_Information[NodesDiscovered.Length - 1];
                    Host.IEEEAddress = (ulong)NodesDiscovered[0].IEEEAddress;
                    Host.NetworkAddress = (ushort)NodesDiscovered[0].NetworkAddress;
                    Host.Identifier = NodesDiscovered[0].Identifier;
                    Host.NodeType = (Node_Type)NodesDiscovered[0].NodeType;
                    if (Host.Hardware == null || Host.Firmware == null)
                    {
                        Host.Hardware = (Hardware)(HardwareVersion() & 0xFF00);
                        Host.Firmware = (Firmware)(FirmwareVersion() & 0xF000);
                    }

                    for (int i = 1; i < NodesDiscovered.Length; i++)
                    {
                        Network[i - 1].IEEEAddress = (ulong)NodesDiscovered[i].IEEEAddress;
                        Network[i - 1].NetworkAddress = (ushort)NodesDiscovered[i].NetworkAddress;
                        Network[i - 1].Identifier = NodesDiscovered[i].Identifier;
                        Network[i - 1].NodeType = (Node_Type)NodesDiscovered[i].NodeType;
                    }

                    if (OperationMode != OperationModeEnum.Transparent)
                    {
                        if (AuxNetwork == null)
                        {
                            for (int i = 0; i < Network.Length; i++)
                            {
                                InternalDestIEEEAddr = Network[i].IEEEAddress;
                                InternalDestNwkAddr = Network[i].NetworkAddress;
                                InternalRemoteATCmdEnable = true;
                                Network[i].Hardware = (Hardware)(HardwareVersion() & 0xFF00);
                                InternalRemoteATCmdEnable = false;
                            }
                        }
                        else
                        {
                            for (int i = 0; i < Network.Length; i++)
                            {
                                foreach (Node_Information Node in AuxNetwork)
                                {
                                    if (Network[i].IEEEAddress == Node.IEEEAddress)
                                    {
                                        Network[i].Hardware = Node.Hardware;
                                        break;
                                    }
                                }
                                if (Network[i].Hardware == null)
                                {
                                    InternalDestIEEEAddr = Network[i].IEEEAddress;
                                    InternalDestNwkAddr = Network[i].NetworkAddress;
                                    InternalRemoteATCmdEnable = true;
                                    Network[i].Hardware = (Hardware)(HardwareVersion() & 0xFF00);
                                    InternalRemoteATCmdEnable = false;
                                }
                            }
                        }
                    }
                    DestinationAddress();
                    InternalDestNwkAddr = 0xFFFE;
                    if ((Options & 0x02) != 0x02)
                        NetworkDiscoveryOptions(Options);
                }
                catch
                {
                    DestinationAddress();
                    InternalDestNwkAddr = 0xFFFE;
                    Host = new Host_Information();
                    Network = new Node_Information[0];
                    throw new Exception("Refresh Network. Error refresh network nodes.");
                }
            }

            #endregion

            #region New Frame ID

            /// <summary>Sets the new Frame ID value.</summary>
            /// <returns>The new Frame ID value.</returns>
            private byte NewFrameID()
            {
                if (InternalFrameIdMode == Frame_Id_Mode.Autoincrement)
                {
                    if (InternalFrameId == 0xFF)
                        InternalFrameId = 0x00;
                    InternalFrameId++;
                    return InternalFrameId;
                }
                else if (InternalFrameIdMode == Frame_Id_Mode.Random)
                    InternalFrameId = (byte)InternalRandom.Next(0x01, 0x100);
                return InternalFrameId;
            }

            #endregion

            #region Get Frame

            /// <summary>Gets the Frame with Start Delimiter + Length + Frame Data + Checksum.</summary>
            /// <param name="FrameData">Frame Data to get Frame.</param>
            /// <returns>The Frame with Start Delimiter + Length + Frame Data + Checksum.</returns>
            private byte[] GetFrame(byte[] FrameData)
            {
                if (FrameData == null)
                    throw new ArgumentNullException("FrameData", "Get Frame. FrameData is a null reference.");
                if (FrameData.Length == 0)
                    throw new ArgumentException("Get Frame. FrameData cannot be empty.");
                byte[] Frame = new byte[1 + 2 + FrameData.Length + 1];  // Start Delimiter + Length + Frame Data + Checksum
                Frame[0] = StartDelimiter;  // Start Delimiter
                Frame[1] = (byte)(FrameData.Length / 256);  // Length MSB
                Frame[2] = (byte)(FrameData.Length % 256);  // Length LSB
                Array.Copy(FrameData, 0, Frame, 3, FrameData.Length);   // Frame
                Frame[Frame.Length - 1] = CalculateChecksum(Frame); // Calculate Checksum
                return Frame;
            }

            #endregion

            #region Get Frame Data

            /// <summary>Gets the Frame Data.</summary>
            /// <param name="Frame">Frame to get Frame Data.</param>
            /// <returns>The Frame without Start Delimiter, Length and Checksum.</returns>
            private byte[] GetFrameData(byte[] Frame)
            {
                if (Frame == null)
                    throw new ArgumentNullException("Frame", "Get Frame Data. Frame is a null reference.");
                if (Frame.Length == 0)
                    throw new ArgumentException("Get Frame Data. Frame cannot be empty.");
                byte[] FrameData = new byte[-1 - 2 + Frame.Length - 1]; // Start Delimiter + Length + Frame Data + Checksum
                Array.Copy(Frame, 3, FrameData, 0, FrameData.Length);   // Frame Data
                return FrameData;
            }

            #endregion

            #region Checksum

            /// <summary>Calculate the Checksum of Frame.</summary>
            /// <param name="Frame">Frame to calculate the Checksum.</param>
            /// <returns>Checksum of Frame.</returns>
            private byte CalculateChecksum(byte[] Frame)
            {
                if (Frame == null)
                    throw new ArgumentNullException("Frame", "Calculate Checksum. Frame is a null reference.");
                if (Frame.Length == 0)
                    throw new ArgumentException("Calculate Checksum. Frame cannot be empty.");
                if (Frame.Length < 4)
                    throw new ArgumentException("Calculate Checksum. Frame length must be 4 or more.");
                byte Checksum = 0;
                for (int i = 3; i < Frame.Length - 1; i++)
                {
                    Checksum += Frame[i];
                }
                return (byte)(0xFF - Checksum);
            }

            /// <summary>Verify the Checksum of Frame.</summary>
            /// <param name="Frame">Frame to verify the Checksum.</param>
            /// <returns>True if successful.</returns>
            private bool VerifyChecksum(byte[] Frame)
            {
                if (Frame == null)
                    throw new ArgumentNullException("Frame", "Verify Checksum. Frame is a null reference.");
                if (Frame.Length == 0)
                    throw new ArgumentException("Verify Checksum. Frame cannot be empty.");
                if (Frame.Length < 3)
                    throw new ArgumentException("Verify Checksum. Frame length must be 3 or more.");
                byte Checksum = 0;
                for (int i = 3; i < Frame.Length; i++)
                {
                    Checksum += Frame[i];
                }
                if (Checksum == 0xFF)
                    return true;
                else
                    return false;
            }

            #endregion

            #region XBee Rx Frame

            /// <summary>Received data from the XBee module.</summary>
            private void XBeeRxFrame(XBeeSerialPort sender, byte[] Frame)
            {
                if (InternalUploading)
                {
                    XBeeRspFrame = Frame;
                    WaitXBeeRsp = false;
                }
                else if (OperationMode == OperationModeEnum.Transparent)
                {
                    if (ATCommandMode)  // AT Command Mode Enable
                    {
                        XBeeRspFrame = Frame;
                        WaitXBeeRsp = false;
                    }
                    else if (ReceivePacket != null)
                        ReceivePacket(this, null, null, null, Frame);
                }
                else
                {
                    if (!VerifyChecksum(Frame))
                        return;
                    byte[] FrameData = GetFrameData(Frame);
                    Frame_Type InternalFrameType = (Frame_Type)FrameData[0];
                    switch (InternalFrameType)
                    {
                        case Frame_Type.AT_Cmd_Rsp:
                            XBeeRspFrame = FrameData;
                            WaitXBeeRsp = false;
                            break;

                        case Frame_Type.Modem_Status:
                            if (ModemStatus != null)
                                ModemStatus(this, (Modem_Status)FrameData[1]);
                            break;

                        case Frame_Type.Tx_Status:
                            XBeeRspFrame = FrameData;
                            WaitXBeeRsp = false;
                            break;

                        case Frame_Type.Rx_Packet:
                            if (ReceivePacket != null)
                            {
                                byte[] IEEEAdd = new byte[8];
                                Array.Copy(FrameData, 1, IEEEAdd, 0, 8);
                                byte[] NwkAdd = new byte[2];
                                Array.Copy(FrameData, 9, NwkAdd, 0, 2);
                                Rx_Options Options = (Rx_Options)FrameData[11];
                                byte[] ReceivedData = new byte[FrameData.Length - 12];
                                Array.Copy(FrameData, 12, ReceivedData, 0, ReceivedData.Length);
                                ReceivePacket(this, Aux.ByteToUlong(IEEEAdd), Aux.ByteToUshort(NwkAdd), Options, ReceivedData);
                            }
                            break;

                        case Frame_Type.Explicit_Rx_Packet:
                            if (Loopback)
                            {
                                byte[] ClusterId = new byte[2];
                                Array.Copy(FrameData, 13, ClusterId, 0, 2);
                                if (Aux.ByteToUshort(ClusterId) == 0x0011) // Serial data
                                {
                                    byte[] ReceivedData = new byte[FrameData.Length - 18];
                                    Array.Copy(FrameData, 18, ReceivedData, 0, ReceivedData.Length);
                                    XBeeRspFrame = ReceivedData;
                                    WaitXBeeRsp = false;
                                }
                            }
                            else if (ExplicitReceivePacket != null)
                            {
                                byte[] IEEEAdd = new byte[8];
                                Array.Copy(FrameData, 1, IEEEAdd, 0, 8);
                                byte[] NwkAdd = new byte[2];
                                Array.Copy(FrameData, 9, NwkAdd, 0, 2);
                                byte SourceEP = FrameData[11];
                                byte DestEP = FrameData[12];
                                byte[] ClusterId = new byte[2];
                                Array.Copy(FrameData, 13, ClusterId, 0, 2);
                                byte[] ProfileId = new byte[2];
                                Array.Copy(FrameData, 15, ProfileId, 0, 2);
                                Rx_Options Options = (Rx_Options)FrameData[17];
                                byte[] ReceivedData = new byte[FrameData.Length - 18];
                                Array.Copy(FrameData, 18, ReceivedData, 0, ReceivedData.Length);
                                ExplicitReceivePacket(this, Aux.ByteToUlong(IEEEAdd), Aux.ByteToUshort(NwkAdd), SourceEP, DestEP, Aux.ByteToUshort(ClusterId), Aux.ByteToUshort(ProfileId), Options, ReceivedData);
                            }
                            break;

                        case Frame_Type.IO_Sample:
                            if (IOSample != null)
                            {
                                byte[] IEEEAdd = new byte[8];
                                Array.Copy(FrameData, 1, IEEEAdd, 0, 8);
                                byte[] NwkAdd = new byte[2];
                                Array.Copy(FrameData, 9, NwkAdd, 0, 2);
                                Rx_Options Options = (Rx_Options)FrameData[11];
                                byte Samples = FrameData[12];
                                byte[] DIOMask = new byte[2];
                                Array.Copy(FrameData, 13, DIOMask, 0, 2);
                                byte? AnalogMask = FrameData[15];
                                ushort? DigitalMask = Aux.ByteToUshort(DIOMask);
                                ushort? DigitalSamples = null;
                                ushort[] AnalogSamples = null;
                                int Index = 16;
                                if (DigitalMask == 0x00)
                                    DigitalMask = null;
                                else
                                {
                                    byte[] DIOSamples = new byte[2];
                                    Array.Copy(FrameData, 16, DIOSamples, 0, 2);
                                    DigitalSamples = Aux.ByteToUshort(DIOSamples);
                                    Index = 18;
                                }
                                if (AnalogMask == 0x00)
                                    AnalogMask = null;
                                else
                                {
                                    AnalogSamples = new ushort[(FrameData.Length - Index) / 2];
                                    byte[] AnalogSample = new byte[2];
                                    for (int i = 0; i < AnalogSamples.Length; i++)
                                    {
                                        Array.Copy(FrameData, (Index + i * 2), AnalogSample, 0, 2);
                                        AnalogSamples[i] = Aux.ByteToUshort(AnalogSample);
                                    }
                                }
                                IOSample(this, Aux.ByteToUlong(IEEEAdd), Aux.ByteToUshort(NwkAdd), Options, Samples, DigitalMask, AnalogMask, DigitalSamples, AnalogSamples);
                            }
                            break;

                        case Frame_Type.Sensor_Sample:
                            if (SensorSample != null)
                            {

                                byte[] IEEEAdd = new byte[8];
                                Array.Copy(FrameData, 1, IEEEAdd, 0, 8);
                                byte[] NwkAdd = new byte[2];
                                Array.Copy(FrameData, 9, NwkAdd, 0, 2);
                                Rx_Options Options = (Rx_Options)FrameData[11];
                                MemoryStream InternalSensorsStream = new MemoryStream();
                                byte WireSensors = FrameData[12];
                                if ((WireSensors & 0x01) == 0x01)
                                    InternalSensorsStream.WriteByte(0x01);
                                if ((WireSensors & 0x02) == 0x02)
                                    InternalSensorsStream.WriteByte(0x02);
                                if ((WireSensors & 0x60) == 0x60)
                                    InternalSensorsStream.WriteByte(0x60);
                                Sensor_Type[] Sensors = new Sensor_Type[InternalSensorsStream.Length];
                                InternalSensorsStream.Position = 0;
                                for (int i = 0; i < InternalSensorsStream.Length; i++)
                                {
                                    Sensors[i] = (Sensor_Type)InternalSensorsStream.ReadByte();
                                }
                                ushort[] Samples = new ushort[4];
                                byte[] Sample = new byte[2];
                                for (int i = 0; i < 4; i++)
                                {
                                    Array.Copy(FrameData, (13 + i * 2), Sample, 0, 2);
                                    Samples[i] = Aux.ByteToUshort(Sample);
                                }
                                if ((WireSensors & 0x01) == 0x01)
                                {
                                    byte[] Temperature = new byte[2];
                                    Array.Copy(FrameData, 21, Temperature, 0, 2);
                                    SensorSample(this, Aux.ByteToUlong(IEEEAdd), Aux.ByteToUshort(NwkAdd), Options, Sensors, Samples, Aux.ByteToUshort(Temperature));
                                }
                                else
                                    SensorSample(this, Aux.ByteToUlong(IEEEAdd), Aux.ByteToUshort(NwkAdd), Options, Sensors, Samples, null);
                            }
                            break;

                        case Frame_Type.Node_Ident:
                            if (NodeIdentification != null)
                            {
                                Rx_Options Options = (Rx_Options)FrameData[11];
                                byte[] NwkAdd = new byte[2];
                                Array.Copy(FrameData, 12, NwkAdd, 0, 2);
                                byte[] IEEEAdd = new byte[8];
                                Array.Copy(FrameData, 14, IEEEAdd, 0, 8);
                                int i = 22;
                                bool End = false;
                                while (!End)
                                {
                                    if (FrameData[i] == 0x00)
                                        End = true;
                                    i++;
                                }
                                byte[] NI = new byte[i - 23];
                                Array.Copy(FrameData, 22, NI, 0, NI.Length);
                                string NIString = InternalEncoding.GetString(NI, 0, (int)NI.Length);
                                byte[] Parent = new byte[2];
                                Array.Copy(FrameData, i, Parent, 0, 2);
                                Node_Type DeviceType = (Node_Type)FrameData[i + 2];
                                Source_Event SourceEvent = (Source_Event)FrameData[i + 3];
                                byte[] ProfileId = new byte[2];
                                Array.Copy(FrameData, i + 4, ProfileId, 0, 2);
                                byte[] ManufacturerId = new byte[2];
                                Array.Copy(FrameData, i + 6, ManufacturerId, 0, 2);
                                byte[] DeviceTypeId = new byte[4];
                                if (FrameData.Length > i + 8)
                                {
                                    Array.Copy(FrameData, i + 8, DeviceTypeId, 0, 4);
                                    NodeIdentification(this, Options, Aux.ByteToUlong(IEEEAdd), Aux.ByteToUshort(NwkAdd), NIString, Aux.ByteToUshort(Parent), DeviceType, SourceEvent, Aux.ByteToUshort(ProfileId), Aux.ByteToUshort(ManufacturerId), Aux.ByteToUint(DeviceTypeId));
                                }
                                else
                                    NodeIdentification(this, Options, Aux.ByteToUlong(IEEEAdd), Aux.ByteToUshort(NwkAdd), NIString, Aux.ByteToUshort(Parent), DeviceType, SourceEvent, Aux.ByteToUshort(ProfileId), Aux.ByteToUshort(ManufacturerId), null);
                            }
                            break;

                        case Frame_Type.Remote_AT_Cmd_Rsp:
                            XBeeRspFrame = FrameData;
                            WaitXBeeRsp = false;
                            break;

                        case Frame_Type.Firmware_Update_Status:
                            XBeeRspFrame = FrameData;
                            WaitXBeeRsp = false;
                            break;

                        case Frame_Type.Route_Record:
                            if (RouteRecord != null)
                            {
                                byte[] IEEEAdd = new byte[8];
                                Array.Copy(FrameData, 1, IEEEAdd, 0, 8);
                                byte[] NwkAdd = new byte[2];
                                Array.Copy(FrameData, 9, NwkAdd, 0, 2);
                                Rx_Options Options = (Rx_Options)FrameData[11];
                                byte NumberAddresses = FrameData[12];
                                ushort[] Addresses = new ushort[(FrameData.Length - 13) / 2];
                                byte[] Address = new byte[2];
                                for (int i = 0; i < Addresses.Length; i++)
                                {
                                    Array.Copy(FrameData, (13 + i * 2), Address, 0, 2);
                                    Addresses[i] = Aux.ByteToUshort(Address);
                                }
                                RouteRecord(this, Aux.ByteToUlong(IEEEAdd), Aux.ByteToUshort(NwkAdd), Options, NumberAddresses, Addresses);
                            }
                            break;

                        default:
                            throw new NotSupportedException(InternalFrameType.ToString() + " Frame Type received is not supported.");
                    }
                }
            }

            #endregion

            #region Firmware Update

                /// <summary>Firmware Update.</summary>
                /// <param name="Path">Path file name.</param>
                /// <returns>True if successful.</returns>
                public bool FirmwareUpdate(string Path)
                {
                    if (!InternalRemoteATCmdEnable)
                        return FirmwareUpdateDeviceManager(Path);
                    else
                        return FirmwareUpdateRemote(Path);
                }

                #region Firmware Update Device Manager

                    /// <summary>Firmware Update.</summary>
                    /// <param name="Path">Path file name.</param>
                    /// <returns>True if successful.</returns>
                    private bool FirmwareUpdateDeviceManager(string Path)
                    {
                        ParityEnum SerialPortParity = Parity;
                        HandshakeEnum SerialPortHandshake = Handshake;
                        bool Complete = false;
                        DateTime Timeout;
                        try
                        {
                            // *** Step 1 – Invoke Bootloader ***
                            if (InvokeBootloader())
                            {
                                Parity = ParityEnum.None;
                                Handshake = HandshakeEnum.None;
                                BaudRate = BaudRateEnum._115200;
                                InternalUploading = true;
                                Uploading = true;
                                if (!Prompt())
                                    throw new Exception("Failed to update firmware.");
                            }
                            else
                                throw new Exception("Failed to update firmware.");

                            // *** Step 2 – Upload .ebl ***
                            bool BeginUpload = false;
                            WaitXBeeRsp = true;
                            Timeout = DateTime.Now.AddMilliseconds(1000);  // Set Timeout
                            XBeeSerialPortTx(new byte[] { 0x31 });
                            while (WaitXBeeRsp && (DateTime.Now < Timeout))
                            {
                                Thread.Sleep(10);
                                if (!WaitXBeeRsp)
                                {
                                    if (XBeeRspFrame[XBeeRspFrame.Length - 1] == C)
                                    {
                                        BeginUpload = true;
                                        break;
                                    }
                                    WaitXBeeRsp = true; // Wait Response
                                }
                            }
                            if (!BeginUpload)
                                throw new Exception("Failed to update firmware.");

                            // *** Step 3 – Firmware Update ***
                            Stream InternalStream = File.OpenRead(Path);  // Firmware update .ebl file 
                            byte[] FirmwareUpdateFrame = new byte[133];
                            int n = (int)(Math.Ceiling((double)(InternalStream.Length / 128)));
                            int BlockNumber;
                            ushort CRC;
                            for (BlockNumber = 1; BlockNumber <= n; BlockNumber++)
                            {
                                FirmwareUpdateFrame[0] = SOH;
                                FirmwareUpdateFrame[1] = (byte)BlockNumber;
                                FirmwareUpdateFrame[2] = (byte)(~BlockNumber);
                                InternalStream.Read(FirmwareUpdateFrame, 3, 128);
                                CRC = CalculateCRC(FirmwareUpdateFrame);
                                FirmwareUpdateFrame[131] = (byte)(CRC >> 8);
                                FirmwareUpdateFrame[132] = (byte)(CRC & 0xFF);
                                if (!XModemCRC(FirmwareUpdateFrame))
                                    throw new Exception("Failed to update firmware.");
                            }
                            if (InternalStream.Length > n * 128)
                            {
                                FirmwareUpdateFrame[0] = SOH;
                                FirmwareUpdateFrame[1] = (byte)BlockNumber;
                                FirmwareUpdateFrame[2] = (byte)(~BlockNumber);
                                InternalStream.Read(FirmwareUpdateFrame, 3, (int)(InternalStream.Length - n * 128));
                                for (int Byte = (3 + (int)(InternalStream.Length - n * 128)); Byte < 131; Byte++)
                                {
                                    FirmwareUpdateFrame[Byte] = Padding;
                                }
                                CRC = CalculateCRC(FirmwareUpdateFrame);
                                FirmwareUpdateFrame[131] = (byte)(CRC >> 8);
                                FirmwareUpdateFrame[132] = (byte)(CRC & 0xFF);
                                if (!XModemCRC(FirmwareUpdateFrame))
                                    throw new Exception("Failed to update firmware.");
                            }

                            int Retry = 0;
                            bool Success = false;
                            while (!Success && (Retry < 10))
                            {
                                WaitXBeeRsp = true; // Wait Response
                                Timeout = DateTime.Now.AddMilliseconds(1000);   // Set Timeout
                                XBeeSerialPortTx(new byte[] { EOT });   // End of Transmission
                                while (WaitXBeeRsp && (DateTime.Now < Timeout))
                                {
                                    Thread.Sleep(10);
                                    if (!WaitXBeeRsp)
                                    {
                                        if (XBeeRspFrame[XBeeRspFrame.Length - 1] == 0x0A)
                                        {
                                            Success = true;
                                            break;
                                        }
                                        else if (XBeeRspFrame[XBeeRspFrame.Length - 1] == CAN)
                                            throw new Exception("Failed to update firmware.");
                                    }
                                }
                                Retry++;
                            }
                            if (!Success)
                                throw new Exception("Failed to update firmware.");

                            // *** Step 4 – Run ***
                            if (!Prompt())
                                throw new Exception("Failed to update firmware.");

                            bool Run = false;
                            WaitXBeeRsp = true;
                            XBeeSerialPortTx(new byte[] { 0x32 });
                            Timeout = DateTime.Now.AddMilliseconds(1000);  // Set Timeout
                            while (WaitXBeeRsp && (DateTime.Now < Timeout))
                            {
                                Thread.Sleep(10);
                                if (!WaitXBeeRsp)
                                {
                                    if (XBeeRspFrame[XBeeRspFrame.Length - 1] == 0x32)
                                    {
                                        Run = true;
                                        break;
                                    }
                                    WaitXBeeRsp = true; // Wait Response
                                }
                            }
                            if (!Run)
                                throw new Exception("Failed to update firmware.");

                            Complete = true;
                            Thread.Sleep(2000);
                        }
                        catch
                        {
                            Complete = false;
                        }
                        finally
                        {
                            // *** Step 5 – Configuration ***
                            InternalUploading = false;
                            Uploading = false;
                            Parity = SerialPortParity;
                            Handshake = SerialPortHandshake;
                            BaudRate = BaudRateEnum._9600;
                            DetectingOperationMode();
                        }
                        return Complete;
                    }

                    #region Prompt

                    /// <summary>
                    /// After the XBee Module receives a carriage return, it displays a menu,
                    /// after listing the menu options, the bootloader's BL > prompt displays.
                    /// </summary>
                    /// <returns>True if successful.</returns>
                    private bool Prompt()
                    {
                        WaitXBeeRsp = true;
                        DateTime Timeout = DateTime.Now.AddMilliseconds(3000);  // Set Timeout
                        XBeeSerialPortTx(new byte[] { 0x0D });
                        while (WaitXBeeRsp && (DateTime.Now < Timeout))
                        {
                            Thread.Sleep(10);
                            if (!WaitXBeeRsp)
                            {
                                if (XBeeRspFrame[XBeeRspFrame.Length - 1] == NULL)
                                return true;
                                WaitXBeeRsp = true; // Wait Response
                            }
                        }
                        return false;
                    }

                    #endregion

                    #region XModem CRC

                    /// <summary>Sends Xmodem CRC.</summary>
                    /// <param name="Data">XModem CRC Packet.</param>
                    /// <returns>True if successful.</returns>
                    private bool XModemCRC(byte[] Packet)
                    {
                        int Retry = 0;
                        while (Retry < 10)
                        {
                            WaitXBeeRsp = true; // Wait Response
                            DateTime Timeout = DateTime.Now.AddMilliseconds(1000);   // Set Timeout
                            XBeeSerialPortTx(Packet); // Xmodem CRC
                            while (WaitXBeeRsp && (DateTime.Now < Timeout))
                            {
                                Thread.Sleep(10);
                                if (!WaitXBeeRsp)
                                {
                                    if (XBeeRspFrame[XBeeRspFrame.Length - 1] == ACK)
                                        return true;
                                    else if (XBeeRspFrame[XBeeRspFrame.Length - 1] == CAN)
                                        throw new Exception("Failed to update firmware.");
                                }
                            }
                            Retry++;
                        }
                        return false;
                    }

                    #endregion

                    #region Calculate CRC

                    /// <summary>Calculate the CRC of packet.</summary>
                    /// <param name="Packet">Packet to calculate the CRC.</param>
                    /// <returns>CRC of packet.</returns>
                    private ushort CalculateCRC(byte[] Packet)
                    {
                        if (Packet == null)
                            throw new ArgumentNullException("Packet", "Calculate CRC. Packet is a null reference.");
                        if (Packet.Length == 0)
                            throw new ArgumentException("Calculate CRC. Packet cannot be empty.");
                        if (Packet.Length < 4)
                            throw new ArgumentException("Calculate CRC. Packet length must be 4 or more.");
                        ushort CRC = 0;
                        for (int i = 3; i < 131; i++)
                        {
                            byte Byte = Packet[i];
                            CRC ^= (ushort)((ushort)Byte << 8);
                            for (int j = 1; j <= 8; j++)
                            {
                                if ((CRC & 0x8000) != 0)
                                    CRC = (ushort)((CRC << 1) ^ (ushort)0x1021);
                                else
                                    CRC <<= 1;
                            }
                        }
                        return CRC;
                    }

                    #endregion

                #endregion

                #region Firmware Update Remote ZB

                    /// <summary>Firmware Update.</summary>
                    /// <param name="Path">Path file name.</param>
                    /// <returns>True if successful.</returns>
                    private bool FirmwareUpdateRemote(string Path)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                            throw new NotSupportedException("Firmware Update. This command is not supported in Transparent Operation Mode.");
                        if (Host.Firmware != Firmware.ZB)
                            throw new NotSupportedException("Firmware Update. This command is not supported in devices running ZNET firmware.");
                        InternalRemoteATCmdEnable = false;
                        byte SourceEP = InternalSourceEP;
                        byte DestEP = InternalDestEP;
                        ushort ClusterId = InternalClusterId;
                        ushort ProfileId = InternalProfileId;
                        ulong TargetIEEEAddr = InternalDestIEEEAddr;    // Target IEEE Address
                        ushort TargetNetworkAddr = InternalDestNwkAddr; // Target Network Address
                        ulong UpdaterIEEEAddr = 0x00;   // Updater IEEE Address
                        ushort UpdaterNetworkAddr = 0xFFFE; // Updater Network Address
                        bool XBeeExplicitAddressingEnable = InternalExplicitAddressingEnable;
                        bool APIOpt = InternalAPIOptions;
                        bool XBeeApplyChanges = ApplyChangesEnable;
                        byte XBeeBroadcastRadius = InternalBroadcastRadius;
                        bool XBeeMulticastTransmissionEnable = MulticastTransmissionEnable;
                        bool SourceIsUpdater = false;
                        XBee_BaudRate XBeeInterfaceDataRate = XBee_BaudRate._9600;
                        bool Complete = false;
                        try
                        {
                            // *** Step 1 – Source Configuration ***
                            ApplyChangesEnable = true;
                            InternalExplicitAddressingEnable = false;   // Disable explicit addressing frames
                            if (APIOpt)
                                APIOptions(false);  // Disable explicit receive frames
                            XBeeInterfaceDataRate = InterfaceDataRate();
                            if (XBeeInterfaceDataRate != XBee_BaudRate._115200)
                                InterfaceDataRate(XBee_BaudRate._115200);

                            // *** Step 2 – Verify the target has good links to one or more neighbors ***
                            BroadcastRadius = 0x01; // 1-hop neighbors
                            MulticastTransmissionEnable = false;
                            InternalRemoteATCmdEnable = true;
                            Node_Discovered[] Updaters = NodeDiscover();    // List of potential updaters
                            InternalRemoteATCmdEnable = false;
                            InternalBroadcastRadius = 0x00;

                            // *** Step 3 – Qualify the Neighbors ***
                            bool QualifyNeighbor = false;
                            APIOptions(true);   // Enable explicit receive frames
                            foreach (Node_Discovered Node in Updaters)
                            {
                                if (Node.IEEEAddress == Host.IEEEAddress)
                                {
                                    bool LoopbackRspRx = true;
                                    for (int Packet = 1; Packet < 11; Packet++)
                                    {
                                        LoopbackRspRx = LoopbackTransmission("# " + Convert.ToString(Packet) + ". Loopback Transmission");
                                        if (!LoopbackRspRx)
                                            break;
                                    }
                                    if (LoopbackRspRx)
                                    {
                                        QualifyNeighbor = true;
                                        SourceIsUpdater = true;
                                        break;
                                    }
                                }
                            }
                            if (!QualifyNeighbor)
                            {
                                for (int i = 0; i < Updaters.Length; i++)
                                {
                                    if (Updaters[i].IEEEAddress != TargetIEEEAddr)
                                    {
                                        InternalDestIEEEAddr = (ulong)Updaters[i].IEEEAddress;
                                        InternalDestNwkAddr = (ushort)Updaters[i].NetworkAddress;
                                        bool LoopbackRspRx = true;
                                        for (int Packet = 1; Packet < 11; Packet++)
                                        {
                                            LoopbackRspRx = LoopbackTransmission("# " + Convert.ToString(Packet) + ". Loopback Transmission");
                                            if (!LoopbackRspRx)
                                                break;
                                        }
                                        if (LoopbackRspRx)
                                        {
                                            QualifyNeighbor = true;
                                            break;
                                        }
                                    }
                                }
                            }
                            APIOptions(false);  // Disable explicit receive frames
                            if (!QualifyNeighbor)
                                throw new Exception("Failed to update firmware.");

                            // *** Step 4 – Query Target ***
                            if (!SourceIsUpdater)    // Selected updater is not the source
                            {
                                UpdaterIEEEAddr = InternalDestIEEEAddr;
                                UpdaterNetworkAddr = InternalDestNwkAddr;
                                InternalDestIEEEAddr = TargetIEEEAddr;
                                InternalDestNwkAddr = TargetNetworkAddr;
                            }
                            InternalRemoteATCmdEnable = true;
                            if (!InvokeBootloader())
                                throw new Exception("Failed to update firmware.");
                            InternalRemoteATCmdEnable = false;

                            // *** Step 5 – Configure Addressing for the Update ***
                            if (SourceIsUpdater)    // Selected updater is the source
                            {
                                InternalDestIEEEAddr = Host.IEEEAddress;    // Source Destination IEEE Address
                                InternalDestNwkAddr = Host.NetworkAddress;  // Source Destination IEEE Address
                            }
                            else
                            {
                                InternalDestIEEEAddr = UpdaterIEEEAddr;
                                InternalDestNwkAddr = UpdaterNetworkAddr;
                                InternalRemoteATCmdEnable = true;
                                DestinationAddress(TargetIEEEAddr); // Updater Destination IEEE Address
                                InternalRemoteATCmdEnable = false;
                            }
                            InternalSourceEP = 0xE8;    // Data Endpoint
                            InternalDestEP = 0xE8;  // Data Endpoint 
                            if (SourceIsUpdater)    // Selected updater is the source
                                InternalClusterId = 0x71FE;    // Query and file upload Cluster ID
                            else
                                InternalClusterId = 0x71FF; // Query and file upload Cluster ID
                            InternalProfileId = 0xC105; // Profile ID
                            InternalExplicitAddressingEnable = true;    // Enable explicit addressing frames
                            if (XModemQuery())  // XModem query to the target
                            {
                                // *** Step 6 – Over-the-Air Firmware Update ***
                                Stream InternalStream = File.OpenRead(Path);  // Firmware update .ebl file 
                                byte[] FirmwareUpdateFrame = new byte[66];
                                int n = (int)(Math.Ceiling((double)(InternalStream.Length / 64)));
                                int BlockNumber;
                                for (BlockNumber = 1; BlockNumber <= n; BlockNumber++)
                                {
                                    FirmwareUpdateFrame[0] = 0x01;
                                    FirmwareUpdateFrame[1] = (byte)BlockNumber;
                                    InternalStream.Read(FirmwareUpdateFrame, 2, 64);
                                    if (!FirmwareUpdatePacket(FirmwareUpdateFrame))
                                        throw new Exception("Failed to update firmware.");
                                }
                                if (InternalStream.Length > n * 64)
                                {
                                    FirmwareUpdateFrame[0] = 0x01;
                                    FirmwareUpdateFrame[1] = (byte)BlockNumber;
                                    InternalStream.Read(FirmwareUpdateFrame, 2, (int)(InternalStream.Length - n * 64));
                                    for (int i = (2 + (int)(InternalStream.Length - n * 64)); i < 66; i++)
                                    {
                                        FirmwareUpdateFrame[i] = Padding;
                                    }
                                    if (!FirmwareUpdatePacket(FirmwareUpdateFrame))
                                        throw new Exception("Failed to update firmware.");
                                }
                                if (!FirmwareUpdatePacket(new byte[] { 0x01, 0x04 }))   // End of transmission to the target
                                    throw new Exception("Failed to update firmware.");
                            }
                            else
                                throw new Exception("Failed to update firmware.");

                            // *** Step 7 – Finishing the Update ***
                            Thread.Sleep(5000);
                            Complete = true;
                        }
                        catch
                        {
                            Complete = false;
                        }
                        finally
                        {
                            if (!SourceIsUpdater) // Selected updater is not the source
                            {
                                InternalRemoteATCmdEnable = true;
                                DestinationAddress(UpdaterIEEEAddr); // Updater Destination IEEE Address
                                InternalRemoteATCmdEnable = false;
                            }
                            APIOptions(APIOpt);
                            if (XBeeInterfaceDataRate != XBee_BaudRate._115200)
                                InterfaceDataRate(XBeeInterfaceDataRate);
                            InternalBroadcastRadius = XBeeBroadcastRadius;
                            DestinationAddress(TargetIEEEAddr);
                            InternalExplicitAddressingEnable = XBeeExplicitAddressingEnable;
                            MulticastTransmissionEnable = XBeeMulticastTransmissionEnable;
                            InternalSourceEP = SourceEP;
                            InternalDestEP = DestEP;
                            InternalClusterId = ClusterId;
                            InternalProfileId = ProfileId;
                            InternalRemoteATCmdEnable = true;
                            ApplyChangesEnable = XBeeApplyChanges;
                        }
                        return Complete;
                    }

                    #region XModem Query

                    /// <summary>XModem query.</summary>
                    /// <returns>True if successful.</returns>
                    private bool XModemQuery()
                    {
                        Thread.Sleep(1000);
                        int Retry = 0;
                        while (Retry < 10)
                        {
                            Frame_Id_Mode XBeeFrameIdMode = InternalFrameIdMode;
                            InternalFrameIdMode = Frame_Id_Mode.Constant;
                            InternalFrameId = NoAck;
                            WaitXBeeRsp = true;    // Wait Response
                            DateTime TimeOut = DateTime.Now.AddMilliseconds(4000); // Set Timeout
                            Transmit(0x0151);
                            InternalFrameIdMode = XBeeFrameIdMode;
                            while (WaitXBeeRsp && (DateTime.Now < TimeOut))
                            {
                                Thread.Sleep(10);
                            }
                            if (!WaitXBeeRsp)
                            {
                                byte[] RspFrameData = XBeeRspFrame;
                                Frame_Type RspFrameType = (Frame_Type)RspFrameData[0];
                                if (RspFrameType == Frame_Type.Firmware_Update_Status)
                                {
                                    Bootloader_Type Bootloader = (Bootloader_Type)RspFrameData[12];
                                    if (Bootloader == Bootloader_Type.Query_Rsp)
                                        return true;
                                    else if (Bootloader == Bootloader_Type.Query)
                                        Retry++;
                                    else
                                        throw new Exception("Failed to update firmware.");
                                }
                                else
                                    throw new Exception("Failed to update firmware.");
                            }
                            else
                                Retry++;
                        }
                        return false;
                    }

                    #endregion

                    #region Firmware Update Packet

                    /// <summary>Sends packets to update firmware.</summary>
                    /// <param name="Packet">Data that is sent to the destination device. Up to 72 bytes.</param>
                    /// <returns>True if successful.</returns>
                    private bool FirmwareUpdatePacket(byte[] Packet)
                    {
                        int Retry = 0;
                        while (Retry < 10)
                        {
                            Frame_Id_Mode XBeeFrameIdMode = InternalFrameIdMode;
                            InternalFrameIdMode = Frame_Id_Mode.Constant;
                            InternalFrameId = NoAck;
                            WaitXBeeRsp = true;    // Wait Response
                            DateTime TimeOut = DateTime.Now.AddMilliseconds(1000); // Set Timeout
                            Transmit(Packet);
                            InternalFrameIdMode = XBeeFrameIdMode;
                            while (WaitXBeeRsp && (DateTime.Now < TimeOut))
                            {
                                Thread.Sleep(10);
                            }
                            if (!WaitXBeeRsp)
                            {
                                byte[] RspFrameData = XBeeRspFrame;
                                Frame_Type RspFrameType = (Frame_Type)RspFrameData[0];
                                if (RspFrameType == Frame_Type.Firmware_Update_Status)
                                {
                                    Bootloader_Type Bootloader = (Bootloader_Type)RspFrameData[12];
                                    if ((Bootloader == Bootloader_Type.Acknowledged) && (Packet[1] == RspFrameData[13]))
                                        return true;
                                    else if ((Bootloader == Bootloader_Type.No_Acknowledged) || (Packet[1] != RspFrameData[13]))
                                        Retry++;
                                    else
                                        throw new Exception("Failed to update firmware.");
                                }
                                else
                                    throw new Exception("Failed to update firmware.");
                            }
                            else
                                Retry++;
                        }
                        return false;
                    }

                    #endregion

                    #region Loopback Transmission
            
                    /// <summary>
                    /// Address the packets to the loopback cluster ID (0x0012) on 
                    /// the data endpoint (0xE8), the loopback data responses will 
                    /// be received on serial data cluster ID (0x0011).
                    /// </summary>
                    /// <param name="RFData">Data that is sent to the destination device.</param>
                    /// <returns>True if loopback response is received.</returns>
                    private bool LoopbackTransmission(string RFData)
                    {
                        Loopback = true;
                        bool XBeeRemoteATCmdEnable = InternalRemoteATCmdEnable;
                        InternalRemoteATCmdEnable = false;
                        byte XBeeBroadcastRadius = InternalBroadcastRadius;
                        bool XBeeMulticastTransmissionEnable = MulticastTransmissionEnable;
                        bool XBeeExplicitAddressingEnable = InternalExplicitAddressingEnable;
                        InternalSourceEP = 0xE8;    // Data Endpoint
                        InternalDestEP = 0xE8;  // Data Endpoint
                        InternalClusterId = 0x0012; // Loopback Cluster ID
                        InternalProfileId = 0xC105; // Profile ID
                        InternalBroadcastRadius = 0x00;
                        MulticastTransmissionEnable = false;
                        bool Status = false;
                        try
                        {
                            InternalExplicitAddressingEnable = true;    // Enable explicit addressing frames
                            Frame_Id_Mode XBeeFrameIdMode = InternalFrameIdMode;
                            InternalFrameIdMode = Frame_Id_Mode.Constant;
                            InternalFrameId = NoAck;
                            WaitXBeeRsp = true;    // Wait Loopback Response
                            DateTime TimeOut = DateTime.Now.AddMilliseconds(InternalTimeout); // Set Timeout
                            Transmit(RFData);
                            InternalFrameIdMode = XBeeFrameIdMode;
                            while (WaitXBeeRsp && (DateTime.Now < TimeOut || InternalTimeout == InfiniteTimeout))
                            {
                                Thread.Sleep(10);
                            }
                            if (!WaitXBeeRsp)
                                Status = RFData.Equals(InternalEncoding.GetString(XBeeRspFrame, 0, (int)XBeeRspFrame.Length));
                            else
                                throw new TimeoutException();
                        }
                        catch
                        {
                            Status = false;
                        }
                        finally
                        {
                            InternalBroadcastRadius = XBeeBroadcastRadius;
                            MulticastTransmissionEnable = XBeeMulticastTransmissionEnable;
                            InternalExplicitAddressingEnable = XBeeExplicitAddressingEnable;
                            InternalRemoteATCmdEnable = XBeeRemoteATCmdEnable;
                            Loopback = false;
                        }
                        return Status;
                    }

                    #endregion

                #endregion

            #endregion

            #region Transparent Operation Mode

                #region Send

                /// <summary>
                /// Send a Command using Transparent Operation Mode and wait the response.
                /// </summary>
                /// <param name="ReqData">Data to send.<param>
                /// <returns>Received Data.</returns>
                private byte[] Send(byte[] ReqData)
                {
                    WaitXBeeRsp = true;    // Wait Response
                    DateTime Timeout = DateTime.Now.AddMilliseconds(InternalTimeout);   // Set Timeout
                    XBeeSerialPortTx(ReqData);  // Send Data
                    while (WaitXBeeRsp && (DateTime.Now < Timeout || InternalTimeout == InfiniteTimeout))
                    {
                        Thread.Sleep(10);
                    }
                    if (!WaitXBeeRsp)
                        return XBeeRspFrame;  // Return Data
                    else
                        throw new TimeoutException("Timeout exceed with no response.");
                }

                #endregion

                #region AT Command

                /// <summary>Used to query or set module parameters on the local device.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <param name="Parameter">Value to set command to.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp TransparentATCommand(string Command, byte[] Parameter)
                {
                    byte[] ReqData = new byte[2 + 2 + (Parameter.Length * 2) + 1];   // "AT" prefix + AT Command + Parameter + "\r"
                    if (Command.Length == 2)
                        Array.Copy((Encoding.ASCII.GetBytes("AT" + Command)), ReqData, 4);  // "AT" prefix + AT Command
                    else
                        throw new ArgumentException("Command must be Two ASCII characters.");
                    string StringParameter = BitConverter.ToString(Parameter);
                    StringParameter = StringParameter.Replace("-", "");
                    Array.Copy(Encoding.ASCII.GetBytes(StringParameter), 0, ReqData, 4, StringParameter.Length);  // Parameter
                    ReqData[ReqData.Length - 1] = CR;    // "\r"
                    ATCmdRsp InternalATCmdRsp = new ATCmdRsp();
                    if (!ATCommandMode)
                    {
                        try
                        {
                            EnterCommandMode();
                        }
                        catch
                        {
                            InternalATCmdRsp.CmdStatus = Status_Rsp.Error;
                            return InternalATCmdRsp;
                        }
                    }
                    byte[] RspData = Send(ReqData);
                    if (RspData[RspData.Length - 1] != CR)
                    {
                        InternalATCmdRsp.CmdStatus = Status_Rsp.Error;
                        return InternalATCmdRsp;
                    }
                    if (Equals(RspData, ERROR))
                    {
                        InternalATCmdRsp.CmdStatus = Status_Rsp.Error;
                    }
                    else
                    {
                        InternalATCmdRsp.CmdStatus = Status_Rsp.Ok;
                        if (!Equals(RspData, OK))
                        {
                            if (!Command.Equals("NI"))
                                InternalATCmdRsp.CmdData = AsciiToByte(RspData);
                            else
                            {
                                byte[] RspDataString = new byte[RspData.Length - 1];
                                Array.Copy(RspData, RspDataString, RspDataString.Length);
                                InternalATCmdRsp.CmdData = RspDataString;
                            }
                        }
                    }
                    InternalCommandModeTimeoutTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);   // Disable Command Mode Timeout timer
                    InternalCommandModeTimeoutTimer.Change(System.Threading.Timeout.Infinite, InternalInterval);    // Enable Command Mode Timeout timer
                    return InternalATCmdRsp;
                }

                /// <summary>Used to query or set module parameters on the local device.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <param name="Parameter">Value to set command to.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp TransparentATCommand(string Command, string Parameter)
                {
                    byte[] ReqData = new byte[2 + 2 + (Parameter.Length * 2) + 1];   // "AT" prefix + AT Command + Parameter + "\r"
                    if (Command.Length == 2)
                        Array.Copy((Encoding.ASCII.GetBytes("AT" + Command)), ReqData, 4);  // "AT" prefix + AT Command
                    else
                        throw new ArgumentException("Command must be Two ASCII characters.");
                    Array.Copy(Encoding.ASCII.GetBytes(Parameter), 0, ReqData, 4, Parameter.Length);   // Parameter
                    ReqData[ReqData.Length - 1] = CR;    // "\r"
                    ATCmdRsp InternalATCmdRsp = new ATCmdRsp();
                    if (!ATCommandMode)
                    {
                        try
                        {
                            EnterCommandMode();
                        }
                        catch
                        {
                            InternalATCmdRsp.CmdStatus = Status_Rsp.Error;
                            return InternalATCmdRsp;
                        }
                    }
                    byte[] RspData = Send(ReqData);
                    if (Equals(RspData, ERROR))
                    {
                        InternalATCmdRsp.CmdStatus = Status_Rsp.Error;
                    }
                    else
                    {
                        InternalATCmdRsp.CmdStatus = Status_Rsp.Ok;
                        if (!Equals(RspData, OK))
                        {
                            if (RspData[RspData.Length - 1] != CR)
                            {
                                InternalATCmdRsp.CmdStatus = Status_Rsp.Error;
                                return InternalATCmdRsp;
                            }
                            InternalATCmdRsp.CmdData = AsciiToByte(RspData);
                        }
                    }
                    InternalCommandModeTimeoutTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);   // Disable Command Mode Timeout timer
                    InternalCommandModeTimeoutTimer.Change(System.Threading.Timeout.Infinite, InternalInterval);    // Enable Command Mode Timeout timer
                    return InternalATCmdRsp;
                }

                #endregion

                #region AT Command Mode Enable

                /// <summary>Used to automatically exits AT Command Mode.</summary>
                private static void ATCmdMode(object sender)
                {
                    InternalCommandModeTimeoutTimer.Change(Timeout.Infinite, Timeout.Infinite); // Disable Command Mode Timeout timer
                    ATCommandMode = false;
                }

                #endregion

            #endregion

            #region API Operation Mode

                #region Send Frame

                /// <summary>
                /// Send a Frame using API Operation Mode and wait the response if Frame ID is not 0x00.
                /// </summary>
                /// <param name="ReqFrame">Frame to send.<param>
                /// <returns>Received Frame Data.</returns>
                private byte[] SendFrame(byte[] ReqFrame)
                {
                    WaitXBeeRsp = true;    // Wait Response
                    DateTime Timeout = DateTime.Now.AddMilliseconds(InternalTimeout);   // Set Timeout
                    XBeeSerialPortTx(ReqFrame);  // Send Frame
                    if (ReqFrame[4] != NoAck)
                    {
                        while (WaitXBeeRsp && (DateTime.Now < Timeout || InternalTimeout == InfiniteTimeout))
                        {
                            Thread.Sleep(10);
                        }
                        if (!WaitXBeeRsp)
                            return XBeeRspFrame;  // Return Frame Data
                        else
                            throw new TimeoutException("Timeout exceed with no response.");
                    }
                    else
                        return null;    // Ok
                }

                #endregion

                #region AT Command

                /// <summary>Used to query or set module parameters on the local device.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <param name="Parameter">Value to set command to.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp APIATCommand(string Command, byte[] Parameter)
                {
                    byte[] ReqFrameData = new byte[1 + 1 + 2 + Parameter.Length]; // API Identifier + Frame ID + AT Command + Parameter Value
                    if (InternalApplyChangesEnable)
                        ReqFrameData[0] = (byte)Frame_Type.AT_Cmd_Req;   // API Identifier
                    else
                        ReqFrameData[0] = (byte)Frame_Type.AT_Cmd_Queue_Req;    // API Identifier
                    ReqFrameData[1] = (byte)InternalRandom.Next(0x01, 0x100);   // Frame ID
                    if (Command.Length == 2)
                        Array.Copy(Encoding.ASCII.GetBytes(Command), 0, ReqFrameData, 2, 2);   // AT Command
                    else
                        throw new ArgumentException("Command must be Two ASCII characters.");
                    Array.Copy(Parameter, 0, ReqFrameData, 4, Parameter.Length);   // Parameter
                    byte[] ReqFrame = GetFrame(ReqFrameData); // Frame
                    byte[] RspFrameData = SendFrame(ReqFrame);    // Send Frame
                    Frame_Type RspFrameType = (Frame_Type)RspFrameData[0];
                    if (RspFrameType == Frame_Type.AT_Cmd_Rsp)
                    {
                        if (ReqFrameData[1] != RspFrameData[1])
                            throw new Exception("The Frame ID sent does not correlate with the Frame ID received.");
                        if (ReqFrameData[2] != RspFrameData[2] || ReqFrameData[3] != RspFrameData[3])
                            throw new Exception("The AT Command sent does not correlate with the AT Command received.");
                        ATCmdRsp InternalATCmdRsp = new ATCmdRsp();
                        InternalATCmdRsp.CmdStatus = (Status_Rsp)RspFrameData[4];
                        if (InternalATCmdRsp.CmdStatus == Status_Rsp.Ok)
                        {
                            if (RspFrameData.Length > 5)
                            {
                                InternalATCmdRsp.CmdData = new byte[RspFrameData.Length - 5];
                                Array.Copy(RspFrameData, 5, InternalATCmdRsp.CmdData, 0, InternalATCmdRsp.CmdData.Length);
                                return InternalATCmdRsp;
                            }
                            else
                                return InternalATCmdRsp;
                        }
                        else
                            return InternalATCmdRsp;
                    }
                    else
                        throw new Exception("Invalid Frame Type received.");
                }

                /// <summary>Used to query or set module parameters on a remote device.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <param name="Parameter">Value to set command to.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp APIRemoteATCommand(string Command, byte[] Parameter)
                {
                    byte[] ReqFrameData = new byte[1 + 1 + 8 + 2 + 1 + 2 + Parameter.Length];   // API Identifier + Frame ID + Destination Address + Destination Network + Remote Command Options + AT Command + Parameter value
                    ReqFrameData[0] = (byte)Frame_Type.Remote_AT_Cmd_Req;   // API Identifier
                    ReqFrameData[1] = (byte)InternalRandom.Next(0x01, 0x100);   // Frame ID
                    Array.Copy(Aux.UlongToByte(InternalDestIEEEAddr), 0, ReqFrameData, 2, 8);    // Destination Address
                    Array.Copy(Aux.UshortToByte(InternalDestNwkAddr), 0, ReqFrameData, 10, 2);  // Destination Network
                    ReqFrameData[12] = InternalRemoteATCmdOptions; // Remote Command Options
                    if (Command.Length == 2)
                        Array.Copy(Encoding.ASCII.GetBytes(Command), 0, ReqFrameData, 13, 2);    // AT Command
                    else
                        throw new ArgumentException("Command must be Two ASCII characters.");
                    Array.Copy(Parameter, 0, ReqFrameData, 15, Parameter.Length);    // Parameter
                    byte[] ReqFrame = GetFrame(ReqFrameData); // Frame
                    byte[] RspFrameData = SendFrame(ReqFrame);    // Send Frame
                    Frame_Type RspFrameType = (Frame_Type)RspFrameData[0];
                    if (RspFrameType == Frame_Type.Remote_AT_Cmd_Rsp)
                    {
                        if (ReqFrameData[1] != RspFrameData[1])
                            throw new Exception("The Frame ID sent does not correlate with the Frame ID received.");
                        if (ReqFrameData[13] != RspFrameData[12] || ReqFrameData[14] != RspFrameData[13])
                            throw new Exception("The AT Command sent does not correlate with the AT Command received.");
                        ATCmdRsp InternalATCmdRsp = new ATCmdRsp();
                        InternalATCmdRsp.CmdStatus = (Status_Rsp)RspFrameData[14];
                        if (InternalATCmdRsp.CmdStatus == Status_Rsp.Ok)
                        {
                            if (RspFrameData.Length > 15)
                            {
                                InternalATCmdRsp.CmdData = new byte[RspFrameData.Length - 15];
                                Array.Copy(RspFrameData, 15, InternalATCmdRsp.CmdData, 0, InternalATCmdRsp.CmdData.Length);
                                return InternalATCmdRsp;
                            }
                            else
                                return InternalATCmdRsp;
                        }
                        else
                            return InternalATCmdRsp;
                    }
                    else
                        throw new Exception("Invalid Frame Type received.");
                }

                #endregion

                #region Transmit

                /// <summary>Send data as an RF packet to the specified destination.</summary>
                /// <param name="RFData">Data that is sent to the destination device. Up to 72 bytes.</param>
                /// <returns>ZigBee Transmit Status.</returns>
                private TxStatus APITransmit(byte[] RFData)
                {
                    byte[] ReqFrameData = new byte[1 + 1 + 8 + 2 + 1 + 1 + RFData.Length];  // API Identifier + Frame ID + Destination Address + Destination Network + Broadcast Radius + Options + RF Data
                    ReqFrameData[0] = (byte)Frame_Type.Tx_Req;  // API Identifier
                    if (InternalDestIEEEAddr == 0xFFFF && InternalDestNwkAddr == 0xFFFE)
                        ReqFrameData[1] = NoAck;    // Frame ID
                    else
                        ReqFrameData[1] = NewFrameID();  // Frame ID
                    Array.Copy(Aux.UlongToByte(InternalDestIEEEAddr), 0, ReqFrameData, 2, 8);    // Destination Address
                    Array.Copy(Aux.UshortToByte(InternalDestNwkAddr), 0, ReqFrameData, 10, 2);   // Destination Network
                    ReqFrameData[12] = InternalBroadcastRadius;    // Broadcast Radius
                    ReqFrameData[13] = InternalTxOptions;  // Options
                    Array.Copy(RFData, 0, ReqFrameData, 14, RFData.Length);  // RF Data
                    byte[] ReqFrame = GetFrame(ReqFrameData);   // Frame
                    byte[] RspFrameData = SendFrame(ReqFrame);  // Send Frame
                    if (ReqFrameData[1] != NoAck)
                    {
                        Frame_Type RspFrameType = (Frame_Type)RspFrameData[0];
                        if (RspFrameType == Frame_Type.Tx_Status)
                        {
                            if (ReqFrameData[1] != RspFrameData[1])
                                throw new Exception("The Frame ID sent does not correlate with the Frame ID received.");
                            TxStatus InternalTxStatus = new TxStatus();
                            byte[] Network_Address = new byte[2];
                            Array.Copy(RspFrameData, 2, Network_Address, 0, 2);
                            InternalTxStatus.NetworkAddress = Aux.ByteToUshort(Network_Address);
                            InternalTxStatus.RetryCount = RspFrameData[4];
                            InternalTxStatus.DeliveryStatus = (Delivery)RspFrameData[5];
                            InternalTxStatus.DiscoveryStatus = (Discovery)RspFrameData[6];
                            return InternalTxStatus;
                        }
                        else
                            throw new Exception("Invalid Frame Type received.");
                    }
                    else
                    {
                        TxStatus InternalTxStatus = new TxStatus();
                        return InternalTxStatus;  // Ok
                    }
                }

                /// <summary>
                /// Send data as an RF packet to the specified destination.
                /// Allows ZigBee application layer fields to be specified for a data transmission.
                /// </summary>
                /// <param name="RFData">Data that is sent to the destination device. Up to 72 bytes.</param>
                /// <returns>ZigBee Transmit Status.</returns>
                private TxStatus APIExplicitTransmit(byte[] RFData)
                {
                    byte[] ReqFrameData = new byte[1 + 1 + 8 + 2 + 1 + 1 + 2 + 2 + 1 + 1 + RFData.Length];  // API Identifier + Frame ID + Destination Address + Destination Network + Source Endpoint + Destination Endpoint + Cluster ID + Profile ID + Broadcast Radius + Options + RF Data
                    ReqFrameData[0] = (byte)Frame_Type.Explicit_Tx_Req;  // API Identifier
                    if (InternalDestIEEEAddr == 0xFFFF && InternalDestNwkAddr == 0xFFFE)
                        ReqFrameData[1] = NoAck;    // Frame ID
                    else
                        ReqFrameData[1] = NewFrameID(); // Frame ID
                    Array.Copy(Aux.UlongToByte(InternalDestIEEEAddr), 0, ReqFrameData, 2, 8);    // Destination Address
                    Array.Copy(Aux.UshortToByte(InternalDestNwkAddr), 0, ReqFrameData, 10, 2);   // Destination Network
                    ReqFrameData[12] = InternalSourceEP;    // Source Endpoint
                    ReqFrameData[13] = InternalDestEP;  // Destination Endpoint
                    Array.Copy(Aux.UshortToByte(InternalClusterId), 0, ReqFrameData, 14, 2);   // Cluster ID
                    Array.Copy(Aux.UshortToByte(InternalProfileId), 0, ReqFrameData, 16, 2);   // Profile ID
                    ReqFrameData[18] = InternalBroadcastRadius;    // Broadcast Radius
                    ReqFrameData[19] = InternalTxOptions;  // Options
                    Array.Copy(RFData, 0, ReqFrameData, 20, RFData.Length);  // RF Data
                    byte[] ReqFrame = GetFrame(ReqFrameData);   // Frame
                    byte[] RspFrameData = SendFrame(ReqFrame);  // Send Frame
                    if (ReqFrameData[1] != NoAck)
                    {
                        Frame_Type RspFrameType = (Frame_Type)RspFrameData[0];
                        if (RspFrameType == Frame_Type.Tx_Status)
                        {
                            if (ReqFrameData[1] != RspFrameData[1])
                                throw new Exception("The Frame ID sent does not correlate with the Frame ID received.");
                            TxStatus InternalTxStatus = new TxStatus();
                            byte[] NwkAdd = new byte[2];
                            Array.Copy(RspFrameData, 2, NwkAdd, 0, 2);
                            InternalTxStatus.NetworkAddress = Aux.ByteToUshort(NwkAdd);
                            InternalTxStatus.RetryCount = RspFrameData[4];
                            InternalTxStatus.DeliveryStatus = (Delivery)RspFrameData[5];
                            InternalTxStatus.DiscoveryStatus = (Discovery)RspFrameData[6];
                            return InternalTxStatus;
                        }
                        else
                            throw new Exception("Invalid Frame Type received.");
                    }
                    else
                    {
                        TxStatus InternalTxStatus = new TxStatus();
                        return InternalTxStatus;  // Ok
                    }
                }

                #endregion

                #region Create Source Route

                /// <summary>
                /// Creates a source route in the module. A source route specifies the complete 
                /// route a packet should traverse to get from source to destination.
                /// </summary>
                /// <param name="Addresses">
                /// Intermediate hop addresses must be ordered starting with the 
                /// neighbor of the destination, and working closer to the source.
                /// </param>
                public void CreateSourceRoute(ushort[] Addresses)
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                        throw new InvalidOperationException("Create Source Route. This command is not supported in Transparent Operation Mode.");
                    if (Host.Firmware != Firmware.ZB)
                        throw new InvalidOperationException("Create Source Route. This command is not supported in devices running ZNET firmware.");
                    byte[] ReqFrameData = new byte[1 + 1 + 8 + 2 + 1 + 1 + (Addresses.Length * 2)]; // API Identifier + Frame ID + Destination Address + Destination Network + Route Options + Number Addresses + Addresses
                    ReqFrameData[0] = (byte)Frame_Type.Source_Route; // API Identifier
                    ReqFrameData[1] = 0x00; // Frame ID
                    Array.Copy(Aux.UlongToByte(InternalDestIEEEAddr), 0, ReqFrameData, 2, 8);   // Destination Address
                    Array.Copy(Aux.UshortToByte(InternalDestNwkAddr), 0, ReqFrameData, 10, 2);  // Destination Network
                    ReqFrameData[12] = 0x00;    // Route Options
                    ReqFrameData[13] = (byte)Addresses.Length;  // Number Addresses
                    for (int i = 0; i < Addresses.Length; i++)
                    {
                        Array.Copy(Aux.UshortToByte(Addresses[i]), 0, ReqFrameData, (14 + i * 2), 2);  // Addresses
                    }
                    byte[] ReqFrame = GetFrame(ReqFrameData);   // Frame
                    XBeeSerialPortTx(ReqFrame); // Send Frame
                }

                #endregion

            #endregion

            #region AT Commands

                #region AT Command

                /// <summary>Used to query module parameters.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp ATCommand(string Command)
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                    {
                        return TransparentATCommand(Command, new byte[0]);
                    }
                    else
                    {
                        if (!InternalRemoteATCmdEnable)
                            return APIATCommand(Command, new byte[0]);
                        else
                            return APIRemoteATCommand(Command, new byte[0]);
                    }
                }

                /// <summary>Used to set module parameters.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <param name="Parameter">Value to set command to.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp ATCommand(string Command, byte[] Parameter)
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                    {
                        return TransparentATCommand(Command, Parameter);
                    }
                    else
                    {
                        if (!InternalRemoteATCmdEnable)
                            return APIATCommand(Command, Parameter);
                        else
                            return APIRemoteATCommand(Command, Parameter);
                    }
                }

                /// <summary>Used to set module parameters.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <param name="Parameter">Value to set command to.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp ATCommand(string Command, string Parameter)
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                    {
                        return TransparentATCommand(Command, Parameter);
                    }
                    else
                    {
                        if (!InternalRemoteATCmdEnable)
                            return APIATCommand(Command, Encoding.ASCII.GetBytes(Parameter));
                        else
                            return APIRemoteATCommand(Command, Encoding.ASCII.GetBytes(Parameter));
                    }
                }

                /// <summary>Used to set module parameters.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <param name="Parameter">Value to set command to.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp ATCommand(string Command, byte Parameter)
                {
                    byte[] ParameterByte = { Parameter };
                    if (OperationMode == OperationModeEnum.Transparent)
                    {
                        return TransparentATCommand(Command, ParameterByte);
                    }
                    else
                    {
                        if (!InternalRemoteATCmdEnable)
                            return APIATCommand(Command, ParameterByte);
                        else
                            return APIRemoteATCommand(Command, ParameterByte);
                    }
                }

                /// <summary>Used to set module parameters.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <param name="Parameter">Value to set command to.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp ATCommand(string Command, ushort Parameter)
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                    {
                        return TransparentATCommand(Command, Aux.UshortToByte(Parameter));
                    }
                    else
                    {
                        if (!InternalRemoteATCmdEnable)
                            return APIATCommand(Command, Aux.UshortToByte(Parameter));
                        else
                            return APIRemoteATCommand(Command, Aux.UshortToByte(Parameter));
                    }
                }

                /// <summary>Used to set module parameters.</summary>
                /// <param name="Command">Two ASCII characters that identify the AT Command.</param>
                /// <param name="Parameter">Value to set command to.</param>
                /// <returns>AT Command Response.</returns>
                private ATCmdRsp ATCommand(string Command, ulong Parameter)
                {
                    if (OperationMode == OperationModeEnum.Transparent)
                    {
                        return TransparentATCommand(Command, Aux.UlongToByte(Parameter));
                    }
                    else
                    {
                        if (!InternalRemoteATCmdEnable)
                            return APIATCommand(Command, Aux.UlongToByte(Parameter));
                        else
                            return APIRemoteATCommand(Command, Aux.UlongToByte(Parameter));
                    }
                }

                #endregion

                #region Special Commands

                    #region Write ZB / ZNet

                    /// <summary>
                    /// Write parameter values to non-volatile memory so that parameter modifications 
                    /// persist through subsequent resets.
                    /// </summary>
                    /// <returns>True if successful.</returns>
                    public bool Write()
                    {
                        ATCmdRsp WR;
                        try
                        {
                            WR = ATCommand("WR");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Write. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Write. " + ex.Message);
                        }
                        if (WR.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Write Binding Table znet

                    /// <summary>Writes the current binding table to non-volative memory.</summary>
                    /// <returns>True if successful.</returns>
                    public bool WriteBinding()
                    {
                        if (Host.Firmware == Firmware.ZB)
                            throw new Exception("Binding Table Index. This Command is not supported in devices running ZB firmware.");
                        ATCmdRsp WB;
                        try
                        {
                            WB = ATCommand("WB");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Write Binding Table. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Write Binding Table. " + ex.Message);
                        }
                        if (WB.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Restore Defaults ZB / znet

                    /// <summary>
                    /// Restore module parameters to factory defaults. 
                    /// Restore command does not reset the ID parameter.        
                    /// </summary>
                    /// <returns>True if successful.</returns>
                    public bool RestoreDefaults()
                    {
                        ATCmdRsp RE;
                        try
                        {
                            RE = ATCommand("RE");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Restore Defaults. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Restore Defaults. " + ex.Message);
                        }
                        if (RE.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Software Reset ZB / znet

                    /// <summary>Reset module.</summary>
                    /// <returns>True if successful.</returns>
                    public bool SoftwareReset()
                    {
                        ATCmdRsp FR;
                        try
                        {
                            FR = ATCommand("FR");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Software Reset. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Software Reset. " + ex.Message);
                        }
                        if (FR.CmdStatus == Status_Rsp.Ok)
                        {
                            Thread.Sleep(2500);
                            return true;
                        }
                        else
                            return false;
                    }

                    #endregion

                    #region Network Reset ZB / znet

                    /// <summary>
                    /// Reset network layer parameters on one or more modules within a PAN.
                    /// All network configuration and routing information is consequently lost.
                    /// </summary>
                    /// <param name="Options">
                    /// Resets network layer parameters on the node issuing the command or 
                    /// on all nodes in the PAN.
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool NetworkReset(Nwk_Rst_Options Options)
                    {
                        if (!Enum.IsDefined(typeof(Nwk_Rst_Options), Options))
                            throw new ArgumentOutOfRangeException("Options", "Network Reset. Options is out of legal range.");
                        ATCmdRsp NR;
                        try
                        {
                            NR = ATCommand("NR", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Network Reset. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Network Reset. " + ex.Message);
                        }
                        if (NR.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Apply Changes ZB / znet

                    /// <summary>
                    /// Applies changes to all command registers causing queued command
                    /// register values to be applied.
                    /// </summary>
                    /// <returns>True if successful.</returns>
                    public bool ApplyChanges()
                    {
                        ATCmdRsp AC;
                        try
                        {
                            AC = ATCommand("AC");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Apply Changes. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Apply Changes. " + ex.Message);
                        }
                        if (AC.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                #endregion

                #region Addressing Commands
                    
                    #region Destination Address ZB / ZNET
                    
                    /// <summary>
                    /// Gets the 64 bits destination address for data transmissions.
                    /// 0x0000000000000000 = Coordinator’s destination address. 
                    /// 0x000000000000FFFF = Broadcast address for the PAN. 
                    /// </summary>
                    /// <returns>Return Destination Address value.</returns>
                    public ulong DestinationAddress()
                    {
                        ATCmdRsp DH;
                        ATCmdRsp DL;
                        try
                        {
                            DH = ATCommand("DH");
                            DL = ATCommand("DL");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Destination Address. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Destination Address. " + ex.Message);
                        }
                        if (DH.CmdStatus == Status_Rsp.Ok && DL.CmdStatus == Status_Rsp.Ok)
                        {
                            if (!InternalRemoteATCmdEnable)
                                 InternalDestIEEEAddr = (ulong)(Aux.ByteToUint(DH.CmdData) * 0x100000000 + Aux.ByteToUint(DL.CmdData));
                            return InternalDestIEEEAddr;
                        }
                        else
                        {
                            if (DH.CmdStatus != Status_Rsp.Ok)
                                throw new Exception("Destination Address. " + DH.CmdStatus.ToString());
                            else
                                throw new Exception("Destination Address. " + DL.CmdStatus.ToString());
                        }
                    }

                    /// <summary>
                    /// Sets the 64 bits destination address for data transmissions.
                    /// 0x0000000000000000 = Coordinator’s destination address. 
                    /// 0x000000000000FFFF = Broadcast address for the PAN. 
                    /// <summary>
                    /// <param name="IEEEAddress">64 bits destination address.</param>
                    /// <returns>True if successful.</returns>
                    public bool DestinationAddress(ulong IEEEAddress)
                    {
                        byte[] Address64 = Aux.UlongToByte(IEEEAddress);
                        byte[] AddressHigh = new byte[4];
                        byte[] AddressLow = new byte[4];
                        ushort Address16 = 0x00;
                        Array.Copy(Address64, 0, AddressHigh, 0, 4);
                        Array.Copy(Address64, 4, AddressLow, 0, 4);
                        if (IEEEAddress == 0x00)
                            Address16 = 0x00;
                        else if (IEEEAddress == 0xFFFF)
                            Address16 = 0xFFFE;
                        else
                        {
                            bool Found = false;
                            if (Host.IEEEAddress == IEEEAddress)
                            {
                                Address16 = Host.NetworkAddress;
                                Found = true;
                            }
                            else
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (Node.IEEEAddress == IEEEAddress)
                                    {
                                        Address16 = Node.NetworkAddress;
                                        Found = true;
                                        break;
                                    }
                                }
                            }
                            if (!Found)
                                throw new ArgumentOutOfRangeException("Address.", "Destination Address. Address is not valid.");
                        }
                        ATCmdRsp DH;
                        ATCmdRsp DL;
                        try
                        {
                            DH = ATCommand("DH", AddressHigh);
                            DL = ATCommand("DL", AddressLow);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Destination Address. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Destination Address. " + ex.Message);
                        }
                        if (DH.CmdStatus == Status_Rsp.Ok && DL.CmdStatus == Status_Rsp.Ok)
                        {
                            if (!InternalRemoteATCmdEnable)
                            {
                                InternalDestIEEEAddr = IEEEAddress;
                                InternalDestNwkAddr = Address16;
                            }
                            return true;
                        }
                        else
                            return false;
                    }

                    #endregion

                    #region Network Address ZB / ZNET

                    /// <summary>Get the 16-bit network address of the module.</summary>
                    /// <returns>Returns the 16-bit network address of the module.</returns>
                    public ushort NetworkAddress()
                    {
                        ATCmdRsp MY;
                        try
                        {
                            MY = ATCommand("MY");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Network Address. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Network Address. " + ex.Message);
                        }
                        if (MY.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(MY.CmdData);
                        else
                            throw new Exception("Network Address. " + MY.CmdStatus.ToString());
                    }

                    #endregion

                    #region Parent Network Address ZB / ZNET

                    /// <summary>Get the 16-bit parent network address of the module.</summary>
                    /// <returns>Returns the 16-bit parent network address of the module.</returns>
                    public ushort ParentNetworkAddress()
                    {
                        ATCmdRsp MP;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType != Node_Type.End_Device)
                                            throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType != Node_Type.End_Device)
                                    throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                            }
                            MP = ATCommand("MP");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Parent Network Address. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Parent Network Address. " + ex.Message);
                        }
                        if (MP.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(MP.CmdData);
                        else
                            throw new Exception("Parent Network Address. " + MP.CmdStatus.ToString());
                    }

                    #endregion

                    #region Number of Children ZB / ZNET

                    /// <summary>
                    /// If XBee Firmware is ZB:
                    /// Gets the number of end device children that can join to the device. 
                    /// If returns 0 the device can not allow any more device children to join.
                    /// If XBee Firmware is ZNET 2.5:
                    /// Gets the number of end device children that have joined to the device. 
                    /// </summary>
                    /// <returns>Returns the number of children that can join or have joined to the device.</returns>
                    public byte NumberChildren()
                    {
                        ATCmdRsp NC;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType == Node_Type.End_Device)
                                            throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.End_Device)
                                    throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                            }
                            NC = ATCommand("NC");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Number of Children. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Number of Children. " + ex.Message);
                        }
                        if (NC.CmdStatus == Status_Rsp.Ok)
                            return NC.CmdData[0];
                        else
                            throw new Exception("Number of Children. " + NC.CmdStatus.ToString());
                    }

                    #endregion

                    #region Serial Number ZB / ZNET

                    /// <summary>Gets the 64 bits of the RF module's unique IEEE 64-bit address.</summary>
                    /// <returns>Returns the 64 bits of the RF module's unique IEEE 64-bit address.</returns>
                    public ulong SerialNumber()
                    {
                        ATCmdRsp SH;
                        ATCmdRsp SL;
                        try
                        {
                            SH = ATCommand("SH");
                            SL = ATCommand("SL");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Serial Number. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Serial Number. " + ex.Message);
                        }
                        if (SH.CmdStatus == Status_Rsp.Ok && SL.CmdStatus == Status_Rsp.Ok)
                            return (ulong)(Aux.ByteToUint(SH.CmdData) * 0x100000000 + Aux.ByteToUint(SL.CmdData));
                        else
                        {
                            if (SH.CmdStatus != Status_Rsp.Ok)
                                throw new Exception("Serial Number. " + SH.CmdStatus.ToString());
                            else
                                throw new Exception("Serial Number. " + SL.CmdStatus.ToString());
                        }
                    }

                    #endregion

                    #region Node Identifier ZB / ZNET

                    /// <summary>
                    /// Gets a string identifier. The register only accepts printable ASCII data. 
                    /// </summary>
                    /// <returns>Returns the Node Identifier.</returns>
                    public string NodeIdentifier()
                    {
                        ATCmdRsp NI;
                        try
                        {
                            NI = ATCommand("NI");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Node Identifier. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Node Identifier. " + ex.Message);
                        }
                        if (NI.CmdStatus == Status_Rsp.Ok)
                            return InternalEncoding.GetString(NI.CmdData, 0, (int)NI.CmdData.Length);
                        else
                            throw new Exception("Node Identifier. " + NI.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets a string identifier. The register only accepts printable ASCII data. 
                    /// </summary>
                    /// <param name="Identifier">String identifier.</param>
                    /// <returns>True if successful.</returns>
                    public bool NodeIdentifier(string Identifier)
                    {
                        if (Identifier == null)
                            throw new ArgumentNullException("Identifier", "Node Identifier. Identifier is a null reference.");
                        if (Identifier.Length == 0)
                            throw new ArgumentException("Node Identifier. Identifier cannot be empty.");
                        if (Identifier.Length > 20)
                            throw new ArgumentException("Node Identifier. Identifier can only have a maximum of 20 characters.");
                        ATCmdRsp NI;
                        try
                        {
                            NI = ATCommand("NI", Identifier);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Node Identifier. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Node Identifier. " + ex.Message);
                        }
                        if (NI.CmdStatus == Status_Rsp.Ok)
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                for (int i = 0; i < Network.Length; i++)
                                {
                                    if (Network[i].IEEEAddress == InternalDestIEEEAddr)
                                    {
                                        Network[i].Identifier = Identifier;
                                        break;
                                    }
                                }
                            }
                            else
                                Host.Identifier = Identifier;
                            return true;
                        }
                        else
                            return false;
                    }

                    #endregion

                    #region Device Type Identifier ZB / ZNET

                    /// <summary>
                    /// Gets a device type value. This value can be used 
                    /// to differentiate multiple XBee-based products.
                    /// </summary>
                    /// <returns>Returns Device Type Identifier value.</returns>
                    public uint DeviceTypeIdentifier()
                    {
                        ATCmdRsp DD;
                        try
                        {
                            DD = ATCommand("DD");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Device Type Identifier. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Device Type Identifier. " + ex.Message);
                        }
                        if (DD.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUint(DD.CmdData);
                        else
                            throw new Exception("Device Type Identifier. " + DD.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets a device type value. This value can be used 
                    /// to differentiate multiple XBee-based products.
                    /// </summary>
                    /// <param name="Identifier">Device type value.</param>
                    /// <returns>True if successful.</returns>
                    public bool DeviceTypeIdentifier(uint Identifier)
                    {
                        ATCmdRsp DD;
                        try
                        {
                            DD = ATCommand("DD", Identifier);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Device Type Identifier. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Device Type Identifier. " + ex.Message);
                        }
                        if (DD.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Source Endpoint ZB / ZNET

                    /// <summary>
                    /// Gets ZigBee application layer source endpoint value. If ZigBee 
                    /// application layer addressing is enabled , this value will be used as the 
                    /// source endpoint for all data transmissions. 
                    /// </summary>
                    /// <returns>Return ZigBee application layer source endpoint value.</returns>
                    public byte SourceEndpoint()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp SE;
                            try
                            {
                                SE = ATCommand("SE");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Source Endpoint. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Source Endpoint. " + ex.Message);
                            }
                            if (SE.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalSourceEP = SE.CmdData[0];
                                return InternalSourceEP;
                            }
                            else
                                throw new Exception("Source Endpoint. " + SE.CmdStatus.ToString());
                        }
                        else
                        {
                            if (InternalRemoteATCmdEnable)
                                throw new Exception("Source Endpoint. This Command is not supported for remotes devices.");
                            else
                                return InternalSourceEP;
                        }
                    }

                    /// <summary>
                    /// Sets ZigBee application layer source endpoint value. If ZigBee application layer 
                    /// addressing is enabled , this value will be used as the source endpoint for all data 
                    /// transmissions. 
                    /// </summary>
                    /// <param name="Endpoint">ZigBee application layer source endpoint value.</param>
                    /// <returns>True if successful.</returns>
                    public bool SourceEndpoint(byte Endpoint)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp SE;
                            try
                            {
                                SE = ATCommand("SE", Endpoint);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Source Endpoint. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Source Endpoint. " + ex.Message);
                            }
                            if (SE.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalSourceEP = Endpoint;
                                return true;
                            }
                            else
                                return false;
                        }
                        else
                        {
                            if (InternalRemoteATCmdEnable)
                                throw new Exception("Source Endpoint. This Command is not supported for remotes devices.");
                            else
                            {
                                InternalSourceEP = Endpoint;
                                return true;
                            }
                        }
                    }

                    #endregion

                    #region Destination Endpoint ZB / ZNET

                    /// <summary>
                    /// Gets Zigbee application layer destination endpoint value. If ZigBee application layer 
                    /// addressing is enabled, this value will be used as the destination endpoint for all data 
                    /// transmissions.
                    /// </summary>
                    /// <returns>Return Zigbee application layer destination endpoint value.</returns>
                    public byte DestinationEndpoint()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp DE;
                            try
                            {
                                DE = ATCommand("DE");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Destination Endpoint. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Destination Endpoint. " + ex.Message);
                            }
                            if (DE.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalDestEP = DE.CmdData[0];
                                return InternalDestEP;
                            }
                            else
                                throw new Exception("Destination Endpoint. " + DE.CmdStatus.ToString());
                        }
                        else
                        {
                            if (InternalRemoteATCmdEnable)
                                throw new Exception("Destination Endpoint. This Command is not supported for remotes devices.");
                            else
                                return InternalDestEP;
                        }
                    }

                    /// <summary>
                    /// Sets Zigbee application layer destination endpoint value. If ZigBee application layer 
                    /// addressing is enabled, this value will be used as the destination endpoint for all data 
                    /// transmissions.
                    /// </summary>
                    /// <param name="Endpoint">Zigbee application layer destination endpoint value.</param>
                    /// <returns>True if successful.</returns>
                    public bool DestinationEndpoint(byte Endpoint)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp DE;
                            try
                            {
                                DE = ATCommand("DE", Endpoint);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Destination Endpoint. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Destination Endpoint. " + ex.Message);
                            }
                            if (DE.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalDestEP = Endpoint;
                                return true;
                            }
                            else
                                return false;
                        }
                        else
                        {
                            if (InternalRemoteATCmdEnable)
                                throw new Exception("Destination Endpoint. This Command is not supported for remotes devices.");
                            else
                            {
                                InternalDestEP = Endpoint;
                                return true;
                            }
                        }
                    }

                    #endregion

                    #region Cluster Identifier ZB / ZNET

                    /// <summary>
                    /// Gets Zigbee application layer cluster ID value. If ZigBee application layer 
                    /// addressing is enabled, this value will be used as the cluster ID for all data 
                    /// transmissions. The default value 0x11 (Transparent data cluster ID).
                    /// </summary>
                    /// <returns>Return Zigbee application layer cluster ID value.</returns>
                    public ushort ClusterIdentifier()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp CI;
                            try
                            {
                                CI = ATCommand("CI");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Cluster Identifier. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Cluster Identifier. " + ex.Message);
                            }
                            if (CI.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalClusterId = Aux.ByteToUshort(CI.CmdData);
                                return InternalClusterId;
                            }
                            else
                                throw new Exception("Cluster Identifier. " + CI.CmdStatus.ToString());
                        }
                        else
                        {
                            if (InternalRemoteATCmdEnable)
                                throw new Exception("Cluster Identifier. This Command is not supported for remotes devices.");
                            else
                                return InternalClusterId;
                        }
                    }

                    /// <summary>
                    /// Sets Zigbee application layer cluster ID value. If ZigBee application layer 
                    /// addressing is enabled, this value will be used as the cluster ID for all data 
                    /// transmissions.
                    /// </summary>
                    /// <param name="ClusterID">Zigbee application layer cluster ID value.</param>
                    /// <returns>True if successful.</returns>
                    public bool ClusterIdentifier(ushort ClusterID)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp CI;
                            try
                            {
                                if (Host.Firmware == Firmware.ZNet)
                                    ClusterID = (ushort)(ClusterID & 0x00FF);
                                CI = ATCommand("CI", ClusterID);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Destination Endpoint. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Destination Endpoint. " + ex.Message);
                            }
                            if (CI.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalClusterId = ClusterID;
                                return true;
                            }
                            else
                                return false;
                        }
                        else
                        {
                            if (InternalRemoteATCmdEnable)
                                throw new Exception("Cluster Identifier. This Command is not supported for remotes devices.");
                            else
                            {
                                if (Host.Firmware == Firmware.ZNet)
                                    ClusterID = (ushort)(ClusterID & 0x00FF);
                                InternalClusterId = ClusterID;
                                return true;
                            }
                        }
                    }

                    #endregion

                    #region Profile ID ZB

                    /// <summary>
                    /// Gets Zigbee application layer profile ID value. If ZigBee application layer 
                    /// addressing is enabled, this value will be used as the profile ID for all data 
                    /// transmissions. The default value 0xC105 (Transparent data cluster ID).
                    /// </summary>
                    /// <returns>Return Zigbee application layer cluster ID value.</returns>
                    public ushort ProfileIdentifier()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                            throw new Exception("Profile Identifier. This Command is not supported in Transparent Operation Mode.");
                        if (InternalRemoteATCmdEnable)
                            throw new Exception("Profile Identifier. This Command is not supported for remotes devices.");
                        else
                            return InternalProfileId;
                    }

                    /// <summary>
                    /// Sets Zigbee application layer profile ID value. If ZigBee application layer 
                    /// addressing is enabled, this value will be used as the profile ID for all data 
                    /// transmissions.
                    /// </summary>
                    /// <param name="ProfileID">Zigbee application layer profile ID value.</param>
                    /// <returns>True if successful.</returns>
                    public bool ProfileIdentifier(ushort ProfileID)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                            throw new Exception("Profile Identifier. This Command is not supported in Transparent Operation Mode.");
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Profile Identifier. This Command is not supported in devices running ZNET Firmware.");
                        else
                        {
                            if (InternalRemoteATCmdEnable)
                                throw new Exception("Profile Identifier. This Command is not supported for remotes devices.");
                            else
                            {
                                InternalProfileId = ProfileID;
                                return true;
                            }
                        }
                    }

                    #endregion

                    #region Max RF Payload Bytes ZB

                    /// <summary>
                    /// Gets the maximum number of RF payload bytes 
                    /// that can be sent in a unicast transmission.
                    /// </summary>
                    /// <returns>Returns the maximum number of RF payload bytes.</returns>
                    public ushort MaxRFPayloadBytes()
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Max RF Payload Bytes. This Command is not supported in devices running ZNET Firmware.");
                        ATCmdRsp NP;
                        try
                        {
                            NP = ATCommand("NP");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Max RF Payload Bytes. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Max RF Payload Bytes. " + ex.Message);
                        }
                        if (NP.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(NP.CmdData);
                        else
                            throw new Exception("Max RF Payload Bytes. " + NP.CmdStatus.ToString());
                    }

                    #endregion

                    #region ZigBee Application Layer Addressing ZNET

                    /// <summary>
                    /// Gets the ZigBee application layer addressing enable attribute. If ZigBee 
                    /// application layer addressing is enabled, data packets will use Zigbee 
                    /// application layer source and destination endpoints, and the cluster ID 
                    /// fields in all data transmissions. 
                    /// </summary>
                    /// <returns>Return if ZigBee Application Layer Addressing is enabled.</returns>
                    public bool ZigBeeApplicationLayerAddressing()
                    {
                        if (Host.Firmware == Firmware.ZB)
                            throw new Exception("ZigBee Application Layer Addressing. This Command is not supported in devices running ZB Firmware.");
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp ZA;
                            try
                            {
                                ZA = ATCommand("ZA");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("ZigBee Application Layer Addressing. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("ZigBee Application Layer Addressing. " + ex.Message);
                            }
                            if (ZA.CmdStatus == Status_Rsp.Ok)
                            {
                                if (ZA.CmdData[0] == 0x00)
                                    return false;
                                return true;
                            }
                            else
                                throw new Exception("ZigBee Application Layer Addressing. " + ZA.CmdStatus.ToString());
                        }
                        else
                            throw new Exception("ZigBee Application Layer Addressing. This Command is only supported in Transparent Operation Mode.");
                    }

                    /// <summary>
                    /// Sets the ZigBee application layer addressing enable attribute. If ZigBee 
                    /// application layer addressing is enabled, data packets will use Zigbee 
                    /// application layer source and destination endpoints, and the cluster ID 
                    /// fields in all data transmissions. 
                    /// </summary>
                    /// <param name="Enable">Enable ZigBee application layer addressing.</param>
                    /// <returns>True if successful.</returns>
                    public bool ZigBeeApplicationLayerAddressing(bool Enable)
                    {
                        if (Host.Firmware == Firmware.ZB)
                            throw new Exception("ZigBee Application Layer Addressing. This Command is not supported in devices running ZB Firmware.");
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp ZA;
                            byte EnableByte;
                            if (!Enable)
                                EnableByte = 0x00;
                            else
                                EnableByte = 0x01;
                            try
                            {
                                ZA = ATCommand("ZA", EnableByte);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("ZigBee Application Layer Addressing. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("ZigBee Application Layer Addressing. " + ex.Message);
                            }
                            if (ZA.CmdStatus == Status_Rsp.Ok)
                                return true;
                            else
                                return false;
                        }
                        else
                            throw new Exception("ZigBee Application Layer Addressing. This Command is only supported in Transparent Operation Mode.");
                    }

                    #endregion

                    #region Binding Table Index ZNET

                    /// <summary>
                    /// Gets the binding table index value. The addressing information at that index in the 
                    /// binding table will be used for all data transmissions.
                    /// </summary>
                    /// <returns>Return the binding table index value.</returns>
                    public byte BindingTableIndex()
                    {
                        if (Host.Firmware == Firmware.ZB)
                            throw new Exception("Binding Table Index. This Command is not supported in devices running ZB Firmware.");
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp BI;
                            try
                            {
                                BI = ATCommand("BI");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Binding Table Index. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Binding Table Index. " + ex.Message);
                            }
                            if (BI.CmdStatus == Status_Rsp.Ok)
                                return BI.CmdData[0];
                            else
                                throw new Exception("Binding Table Index. " + BI.CmdStatus.ToString());
                        }
                        else
                            throw new Exception("Binding Table Index. This Command is only supported in Transparent Operation Mode.");
                    }

                    /// <summary>
                    /// Sets the binding table index value. The addressing information at that index in the 
                    /// binding table will be used for all data transmissions.
                    /// </summary>
                    /// <param name="Index">Binding table index value.</param>
                    /// <returns>True if successful.</returns>
                    public bool BindingTableIndex(byte Index)
                    {
                        if (Host.Firmware == Firmware.ZB)
                            throw new Exception("Binding Table Index. This Command is not supported in devices running ZB Firmware.");
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp BI;
                            try
                            {
                                BI = ATCommand("BI", Index);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Binding Table Index. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Binding Table Index. " + ex.Message);
                            }
                            if (BI.CmdStatus == Status_Rsp.Ok)
                                return true;
                            else
                                return false;
                        }
                        else
                            throw new Exception("Binding Table Index. This Command is only supported in Transparent Operation Mode.");
                    }

                    #endregion

                #endregion

                #region Networking Commands

                    #region Operating Channel ZB / ZNET

                    /// <summary>
                    /// Gets the channel number used for transmitting and receiving between RF modules. 
                    /// A value of 0 means the device has not joined a PAN and is not operating on any channel.
                    /// </summary>
                    /// <returns>Returns the channel number used for communications.</returns>
                    public byte OperatingChannel()
                    {
                        ATCmdRsp CH;
                        try
                        {
                            CH = ATCommand("CH");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Operating Channel. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Operating Channel. " + ex.Message);
                        }
                        if (CH.CmdStatus == Status_Rsp.Ok)
                            return CH.CmdData[0];
                        else
                            throw new Exception("Operating Channel. " + CH.CmdStatus.ToString());
                    }

                    #endregion

                    #region PAN ID ZNET

                    /// <summary>
                    /// Gets the PAN ID. If set to 0xFFFF, the coordinator will select a random 
                    /// PAN ID, and the router/end device will join any PAN ID. 
                    /// </summary>
                    /// <returns>Returns the PAN ID.</returns>
                    public ushort PanID()
                    {
                        if (Host.Firmware == Firmware.ZB)
                            throw new Exception("PAN ID. This Command is not supported in devices running ZB firmware.");
                        ATCmdRsp ID = new ATCmdRsp();
                        try
                        {
                            ID = ATCommand("ID");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("PAN ID. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("PAN ID. " + ex.Message);
                        }
                        if (ID.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(ID.CmdData);
                        else
                            throw new Exception("PAN ID. " + ID.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the PAN ID. If set to 0xFFFF, the coordinator will select a random 
                    /// PAN ID, and the router/end device will join any PAN ID. 
                    /// </summary>
                    /// <param name="PanId">Parameter value to configure Command.</param>
                    /// <returns>True if successful.</returns>
                    public bool PanID(ushort PanId)
                    {
                        if (Host.Firmware == Firmware.ZB)
                            throw new Exception("PAN ID. This Command is not supported in devices running ZB firmware.");
                        if (PanId > 0x3FFF && PanId != 0xFFFF)
                            throw new ArgumentOutOfRangeException("PanId", "PAN ID. PanId range is XBee 0x0000-0x3FFF, 0xFFFF.");
                        ATCmdRsp ID = new ATCmdRsp();
                        try
                        {
                            ID = ATCommand("ID", PanId);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("PAN ID. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("PAN ID. " + ex.Message);
                        }
                        if (ID.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Extended PAN ID ZB

                    /// <summary>
                    /// Gets the 64-bit PAN ID. If set to 0, the coordinator will select a random 
                    /// extended PAN ID, and the router/end device will join any extended PAN ID. 
                    /// </summary>
                    /// <returns>Returns the 64-bit PAN ID.</returns>
                    public ulong ExtendedPanID()
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Extended PAN ID. This Command is not supported in devices running ZNET firmware.");
                        ATCmdRsp ID = new ATCmdRsp();
                        try
                        {
                            ID = ATCommand("ID");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Extended PAN ID. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Extended PAN ID. " + ex.Message);
                        }
                        if (ID.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUlong(ID.CmdData);
                        else
                            throw new Exception("Extended PAN ID. " + ID.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the 64-bit PAN ID. If set to 0, the coordinator will select a random 
                    /// extended PAN ID, and the router/end device will join any extended PAN ID. 
                    /// </summary>
                    /// <param name="PanId">64-bit PAN ID.</param>
                    /// <returns>True if successful.</returns>
                    public bool ExtendedPanID(ulong PanId)
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Extended PAN ID. This Command is not supported in devices running ZNET firmware.");
                        ATCmdRsp ID = new ATCmdRsp();
                        try
                        {
                            ID = ATCommand("ID", PanId);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Extended PAN ID. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Extended PAN ID. " + ex.Message);
                        }
                        if (ID.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Operating Extended PAN ID ZB
        
                    /// <summary>
                    /// Gets the 64-bit Extended PAN ID. Reflects the operating extended PAN ID 
                    /// that the module is running on. If Extended PAN ID > 0, Operating Extended 
                    /// PAN ID will equal Extended PAN ID.
                    /// </summary>
                    /// <returns>Returns the Operating Extended PAN ID.</returns>
                    public ulong OperatingExtendedPanID()
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Operating Extended PAN ID. This Command is not supported in devices running ZNET firmware.");
                        ATCmdRsp OP;
                        try
                        {
                            OP = ATCommand("OP");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Operating Extended PAN ID. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Operating Extended PAN ID. " + ex.Message);
                        }
                        if (OP.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUlong(OP.CmdData);
                        else
                            throw new Exception("Operating Extended PAN ID. " + OP.CmdStatus.ToString());
                    }

                    #endregion

                    #region Max Unicast Hops ZB

                    /// <summary>
                    /// Gets the maximum hops limit. This limit sets the maximum 
                    /// broadcast hops value and determines the unicast timeout. 
                    /// The default unicast timeout of 1600 ms is enough time for 
                    /// data and the acknowledgment to traverse about 8 hops.
                    /// </summary>
                    /// <returns>Returns the unicast timeout in milliseconds.</returns>
                    public ushort MaxUnicastHops()
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Max Unicast Hops. This Command is not supported in devices running ZNET firmware.");
                        ATCmdRsp NH;
                        try
                        {
                            NH = ATCommand("NH");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Max Unicast Hops. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Max Unicast Hops. " + ex.Message);
                        }
                        if (NH.CmdStatus == Status_Rsp.Ok)
                            return (ushort)(NH.CmdData[0] * 50 + 100);
                        else
                            throw new Exception("Max Unicast Hops. " + NH.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the maximum hops limit. This limit sets the maximum 
                    /// broadcast hops value and determines the unicast timeout. 
                    /// The default unicast timeout of 1600 ms is enough time for 
                    /// data and the acknowledgment to traverse about 8 hops.
                    /// </summary>
                    /// <param name="UnicastTimeout">Unicast timeout in milliseconds.</param>
                    /// <returns>True if successful.</returns>
                    public bool MaxUnicastHops(ushort UnicastTimeout)
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Max Unicast Hops. This Command is not supported in devices running ZNET firmware.");
                        if (UnicastTimeout < 100 || UnicastTimeout > 12850)
                            throw new ArgumentOutOfRangeException("UnicastTimeout", "Max Unicast Hops. UnicastTimeout range is 100-12850 ms.");
                        ATCmdRsp NH;
                        try
                        {
                            NH = ATCommand("NH", (byte)(Math.Ceiling((double)UnicastTimeout / 50) - 2));
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Max Unicast Hops. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Max Unicast Hops. " + ex.Message);
                        }
                        if (NH.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Max Broadcast Hops ZB / ZNET

                    /// <summary>
                    /// Gets the maximum number of hops for each broadcast data transmission. 
                    /// </summary>
                    /// <returns>Returns the maximum number of hops in broadcast transmission.</returns>
                    public byte MaxBroadcastHops()
                    {
                        ATCmdRsp BH;
                        try
                        {
                            BH = ATCommand("BH");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Max Broadcast Hops. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Max Broadcast Hops. " + ex.Message);
                        }
                        if (BH.CmdStatus == Status_Rsp.Ok)
                            return BH.CmdData[0];
                        else
                            throw new Exception("Max Broadcast Hops. " + BH.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the maximum number of hops for each broadcast data transmission. 
                    /// Setting this to 0 will use the maximum number of hops.
                    /// </summary>
                    /// <param name="Hops">
                    /// Maximum number of hops for each broadcast data transmission.
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool MaxBroadcastHops(byte Hops)
                    {
                        if ((Hops > 0x1E && Host.Firmware == Firmware.ZB) || Hops > 0x20)
                            throw new ArgumentOutOfRangeException("Hops", "Max Broadcast Hops. Hops range is 0x00-0x1E in ZB firmware, and 0x00-0x20 in ZNET firmware.");
                        ATCmdRsp BH;
                        try
                        {
                            BH = ATCommand("BH", Hops);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Max Broadcast Hops. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Max Broadcast Hops. " + ex.Message);
                        }
                        if (BH.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Operating PAN ID ZB / ZNET

                    /// <summary>
                    /// Gets the Operating PAN ID.Reflects the Operating 
                    /// PAN ID that the module is running on. 
                    /// </summary>
                    /// <returns>Returns the Operating PAN ID.</returns>
                    public ushort OperatingPanID()
                    {
                        ATCmdRsp OI;
                        try
                        {
                            if (Host.Firmware == Firmware.ZB)
                            {
                                OI = ATCommand("OI");
                            }
                            else
                            {
                                OI = ATCommand("OP");
                            }
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Operating PAN ID. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Operating PAN ID. " + ex.Message);
                        }
                        if (OI.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(OI.CmdData);
                        else
                            throw new Exception("Operating PAN ID. " + OI.CmdStatus.ToString());
                    }

                    #endregion

                    #region Node Discovery Timeout ZB / ZNET

                    /// <summary>
                    /// Gets the amount of time a node will spend discovering 
                    /// other nodes when NodeDiscover or DestinationNode is issued.
                    /// </summary>
                    /// <returns>Returns the Timeout in milliseconds.</returns>
                    public ushort NodeDiscoveryTimeout()
                    {
                        ATCmdRsp NT;
                        try
                        {
                            NT = ATCommand("NT");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Node Discover Timeout. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Node Discover Timeout. " + ex.Message);
                        }
                        if (NT.CmdStatus == Status_Rsp.Ok)
                            return (ushort)(Aux.ByteToUshort(NT.CmdData) * 100);
                        else
                            throw new Exception("Node Discover Timeout. " + NT.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the amount of time a node will spend discovering other 
                    /// nodes when Node Discover or Destination Node is issued.
                    /// </summary>
                    /// <param name="Timeout">Timeout in milliseconds.</param>
                    /// <returns>True if successful.</returns>
                    public bool NodeDiscoveryTimeout(ushort Timeout)
                    {
                        if (Timeout < 3200 || Timeout > 25500)
                            throw new ArgumentOutOfRangeException("Timeout", "Node Discover Timeout. Timeout range is 3200-25500 ms.");
                        ATCmdRsp NT;
                        try
                        {
                            NT = ATCommand("NT", (byte)Math.Ceiling((double)Timeout / 100));
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Node Discover Timeout. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Node Discover Timeout. " + ex.Message);
                        }
                        if (NT.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Network Discovery Options ZB / ZNET

                    /// <summary>
                    /// Gets the options value for the network discovery command. 
                    /// The options bitfield value can change the behavior of the NodeDiscover 
                    /// command and/or change what optional values are returned in any received 
                    /// NodeDiscover responses or API node identification frames. 
                    /// </summary>
                    /// <returns>Returns the Network Discovery Options.</returns>
                    public byte NetworkDiscoveryOptions()
                    {
                        ATCmdRsp NO;
                        try
                        {
                            NO = ATCommand("NO");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Network Discovery Options. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Network Discovery Options. " + ex.Message);
                        }
                        if (NO.CmdStatus == Status_Rsp.Ok)
                            return NO.CmdData[0];
                        else
                            throw new Exception("Network Discovery Options. " + NO.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the options value for the network discovery command. 
                    /// The options bitfield value can change the behavior of the Node Discover 
                    /// command and/or change what optional values are returned in any received 
                    /// Node Discover responses or API node identification frames. 
                    /// </summary>
                    /// <param name="Options">Options bitfield value:
                    /// 0x01 = Append DeviceIdentifier value.  
                    /// 0x02 = Local device sends NodeDiscover reponse frame when NodeDiscover is issued.
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool NetworkDiscoveryOptions(byte Options)
                    {
                        if (Options >0x03)
                            throw new ArgumentOutOfRangeException("Options", "Network Discovery Options. Options range is 0x00-0x03.");
                        ATCmdRsp NO;
                        try
                        {
                            NO = ATCommand("NO", Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Network Discovery Options. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Network Discovery Options. " + ex.Message);
                        }
                        if (NO.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Node Discover ZB / ZNET

                    /// <summary>
                    /// Discovers and reports all RF modules found. 
                    /// Only a module that matches the supplied Node Identifier will respond.
                    /// </summary>
                    /// <param name="Identifier">String Node Identifier.</param>
                    /// <returns>Returns node information.</returns>
                    public Node_Discovered NodeDiscover(string Identifier)
                    {
                        bool Found = false;
                        foreach (Node_Information Node in Network)
                        {
                            if (Node.Identifier == Identifier)
                            {
                                Found = true;
                                break;
                            }
                        }
                        if (!Found)
                            throw new ArgumentOutOfRangeException("Identifier.", "Node Discover. Identifier is not valid.");
                        ATCmdRsp ND;
                        Node_Discovered NodesDiscovered = new Node_Discovered();
                        ushort InternalNodeDiscoveryTimeout;
                        try
                        {
                            InternalNodeDiscoveryTimeout = NodeDiscoveryTimeout();
                        }
                        catch
                        {
                            return NodesDiscovered;
                        }
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            byte[] ReqData = new byte[2 + 2 + Identifier.Length + 1];   // "AT" prefix + AT Command + Identifier + "\r"
                            Array.Copy((Encoding.ASCII.GetBytes("ATND")), ReqData, 4);  // "AT" prefix + AT Command
                            Array.Copy(Encoding.ASCII.GetBytes(Identifier), 0, ReqData, 4, Identifier.Length); // Identifier
                            ReqData[ReqData.Length - 1] = CR; // "\r"
                            if (!ATCommandMode)
                            {
                                try
                                {
                                    EnterCommandMode();
                                }
                                catch
                                {
                                    return NodesDiscovered;
                                }
                            }
                            MemoryStream InternalMemoryStream = new MemoryStream();
                            DateTime Timeout = DateTime.Now.AddMilliseconds(InternalNodeDiscoveryTimeout * 1.2);  // Set Time Out
                            WaitXBeeRsp = true;    // Wait Response
                            XBeeSerialPortTx(ReqData);  // Send Data
                            while (DateTime.Now < Timeout)
                            {
                                if (!WaitXBeeRsp)
                                {
                                    InternalMemoryStream.Write(XBeeRspFrame, 0, XBeeRspFrame.Length);
                                    WaitXBeeRsp = true;    // Wait Response
                                    Thread.Sleep(10);
                                }
                            }
                            ND.CmdData = InternalMemoryStream.ToArray();
                            if (ND.CmdData != null && !Equals(ND.CmdData, ERROR))
                            {
                                InternalCommandModeTimeoutTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);   // Disable Command Mode Timeout timer
                                InternalCommandModeTimeoutTimer.Change(System.Threading.Timeout.Infinite, InternalInterval);    // Enable Command Mode Timeout timer
                                byte[] NwkAdd = new byte[5];
                                Array.Copy(ND.CmdData, 0, NwkAdd, 0, 5);
                                NodesDiscovered.NetworkAddress = Aux.ByteToUshort(AsciiToByte(NwkAdd));
                                byte[] IEEEAdd = new byte[17];
                                Array.Copy(ND.CmdData, 5, IEEEAdd, 0, 8);
                                Array.Copy(ND.CmdData, 14, IEEEAdd, 8, 9);
                                NodesDiscovered.IEEEAddress = Aux.ByteToUlong(AsciiToByte(IEEEAdd));
                                int i = 23;
                                bool End = false;
                                while (!End)
                                {
                                    if (ND.CmdData[i] == 0x0D)
                                        End = true;
                                    i++;
                                }
                                byte[] NI = new byte[i - 24];
                                Array.Copy(ND.CmdData, 23, NI, 0, NI.Length);
                                NodesDiscovered.Identifier = InternalEncoding.GetString(NI, 0, (int)NI.Length);
                                byte[] ParentNwkAdd = new byte[5];
                                Array.Copy(ND.CmdData, i, ParentNwkAdd, 0, 5);
                                NodesDiscovered.ParentNetworkAddress = Aux.ByteToUshort(AsciiToByte(ParentNwkAdd));
                                byte[] NodeType = new byte[3];
                                Array.Copy(ND.CmdData, i + 5, NodeType, 0, 3);
                                NodesDiscovered.NodeType = (Node_Type)AsciiToByte(NodeType)[0];
                                byte[] ProfileId = new byte[5];
                                Array.Copy(ND.CmdData, i + 11, ProfileId, 0, 5);
                                NodesDiscovered.ProfileId = Aux.ByteToUshort(AsciiToByte(ProfileId));
                                byte[] ManufacturerId = new byte[5];
                                Array.Copy(ND.CmdData, i + 16, ManufacturerId, 0, 5);
                                NodesDiscovered.ManufacturerId = Aux.ByteToUshort(AsciiToByte(ManufacturerId));
                                byte ByteDeviceTypeId = ND.CmdData[i + 21];
                                if (ByteDeviceTypeId != CR)
                                {
                                    byte[] DeviceTypeId = new byte[9];
                                    Array.Copy(ND.CmdData, i + 21, DeviceTypeId, 0, 9);
                                    NodesDiscovered.DeviceTypeId = Aux.ByteToUint(AsciiToByte(DeviceTypeId));
                                }                                
                            }
                            return NodesDiscovered;
                        }
                        else
                        {
                            int InternalOldTimeoutXBee = InternalTimeout;
                            InternalTimeout = (int)(InternalNodeDiscoveryTimeout * 1.2);
                            try
                            {
                                ND = ATCommand("ND", Identifier);
                            }
                            catch (TimeoutException ex)
                            {
                                InternalTimeout = InternalOldTimeoutXBee;
                                throw new TimeoutException("Node Discover. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                InternalTimeout = InternalOldTimeoutXBee;
                                throw new Exception("Node Discover. " + ex.Message);
                            }
                            if (ND.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalTimeout = InternalOldTimeoutXBee;
                                byte[] NwkAdd = new byte[2];
                                Array.Copy(ND.CmdData, 0, NwkAdd, 0, 2);
                                NodesDiscovered.NetworkAddress = Aux.ByteToUshort(NwkAdd);
                                byte[] IEEEAdd = new byte[8];
                                Array.Copy(ND.CmdData, 2, IEEEAdd, 0, 8);
                                NodesDiscovered.IEEEAddress = Aux.ByteToUlong(IEEEAdd);
                                int i = 10;
                                bool End = false;
                                while (!End)
                                {
                                    if (ND.CmdData[i] == 0x00)
                                        End = true;
                                    i++;
                                }
                                byte[] NI = new byte[i - 11];
                                Array.Copy(ND.CmdData, 10, NI, 0, NI.Length);
                                NodesDiscovered.Identifier = InternalEncoding.GetString(NI, 0, (int)NI.Length);
                                byte[] ParentNwkAdd = new byte[2];
                                Array.Copy(ND.CmdData, i, ParentNwkAdd, 0, 2);
                                NodesDiscovered.ParentNetworkAddress = Aux.ByteToUshort(ParentNwkAdd);
                                NodesDiscovered.NodeType = (Node_Type)ND.CmdData[i + 2];
                                byte[] ProfileId = new byte[2];
                                Array.Copy(ND.CmdData, i + 4, ProfileId, 0, 2);
                                NodesDiscovered.ProfileId = Aux.ByteToUshort(ProfileId);
                                byte[] ManufacturerId = new byte[2];
                                Array.Copy(ND.CmdData, i + 6, ManufacturerId, 0, 2);
                                NodesDiscovered.ManufacturerId = Aux.ByteToUshort(ManufacturerId);
                                if (ND.CmdData.Length > i + 8)
                                {
                                    byte[] DeviceTypeId = new byte[4];
                                    Array.Copy(ND.CmdData, i + 8, DeviceTypeId, 0, 4);
                                    NodesDiscovered.DeviceTypeId = Aux.ByteToUint(DeviceTypeId);
                                }
                                return NodesDiscovered;
                            }
                            else
                            {
                                InternalTimeout = InternalOldTimeoutXBee;
                                throw new Exception("Node Discover. " + ND.CmdStatus.ToString());
                            }
                        }
                    }
       
                    /// <summary>Discovers and reports all RF modules found.</summary>
                    /// <returns>Returns node information for each module discovered.</returns>
                    public Node_Discovered[] NodeDiscover()
                    {
                        if (InternalRemoteATCmdEnable)
                        {
                            byte Options;
                            ushort InternalNodeDiscoveryTimeout;
                            try
                            {
                                InternalRemoteATCmdEnable = false;
                                InternalNodeDiscoveryTimeout = NodeDiscoveryTimeout();
                                Options = NetworkDiscoveryOptions();
                                InternalRemoteATCmdEnable = true;
                            }
                            catch
                            {
                                InternalRemoteATCmdEnable = true;
                                Node_Discovered[] NodesDiscovered = new Node_Discovered[0];
                                return NodesDiscovered;
                            }
                            byte[] ReqFrameData = new byte[1 + 1 + 8 + 2 + 1 + 2];   // API Identifier + Frame ID + Destination Address + Destination Network + Remote Command Options + AT Command
                            ReqFrameData[0] = (byte)Frame_Type.Remote_AT_Cmd_Req;     // API Identifier
                            ReqFrameData[1] = NewFrameID();  // Frame ID
                            Array.Copy(Aux.UlongToByte(InternalDestIEEEAddr), 0, ReqFrameData, 2, 8);    // Destination Address
                            Array.Copy(Aux.UshortToByte(InternalDestNwkAddr), 0, ReqFrameData, 10, 2);  // Destination Network
                            ReqFrameData[12] = (byte)InternalRemoteATCmdOptions;    // Remote Command Options
                            Array.Copy(Encoding.ASCII.GetBytes("ND"), 0, ReqFrameData, 13, 2);    // AT Command
                            byte[] ReqFrame = GetFrame(ReqFrameData); // Frame
                            MemoryStream InternalMemoryStream = new MemoryStream();
                            int Nodes = 0;
                            DateTime Timeout = DateTime.Now.AddMilliseconds(InternalNodeDiscoveryTimeout * 1.2);  // Set Time Out
                            WaitXBeeRsp = true;    // Wait Response
                            XBeeSerialPortTx(ReqFrame);  // Send Frame
                            while (DateTime.Now < Timeout)
                            {
                                if (!WaitXBeeRsp)
                                {
                                    InternalMemoryStream.Write(XBeeRspFrame, 0, XBeeRspFrame.Length);
                                    WaitXBeeRsp = true;    // Wait Response
                                    Nodes++;
                                    Thread.Sleep(10);
                                }
                            }
                            if (Nodes > 0)
                            {
                                Node_Discovered[] NodesDiscovered = new Node_Discovered[Nodes];
                                InternalMemoryStream.Position = 0;
                                for (int i = 0; i < Nodes; i++)
                                {
                                    Frame_Type RspFrameType = (Frame_Type)InternalMemoryStream.ReadByte();
                                    if (RspFrameType == Frame_Type.Remote_AT_Cmd_Rsp)
                                    {
                                        if (InternalFrameId != InternalMemoryStream.ReadByte())
                                            throw new Exception("Node Discover. The Frame ID sent does not correlate with the Frame ID received.");
                                        byte[] Addresses = new byte[10];
                                        InternalMemoryStream.Read(Addresses, 0, 10);
                                        if (ReqFrameData[13] != InternalMemoryStream.ReadByte() || ReqFrameData[14] != InternalMemoryStream.ReadByte())
                                            throw new Exception("Node Discover. The AT Command sent does not correlate with the AT Command received.");
                                        Status_Rsp Status = (Status_Rsp)InternalMemoryStream.ReadByte();
                                        if (Status != Status_Rsp.Ok)
                                            throw new Exception("Node Discover. " + Status.ToString());
                                        byte[] NwkAdd = new byte[2];
                                        InternalMemoryStream.Read(NwkAdd, 0, 2);
                                        NodesDiscovered[i].NetworkAddress = Aux.ByteToUshort(NwkAdd);
                                        byte[] IEEEAdd = new byte[8];
                                        InternalMemoryStream.Read(IEEEAdd, 0, 8);
                                        NodesDiscovered[i].IEEEAddress = Aux.ByteToUlong(IEEEAdd);
                                        MemoryStream InternalNodeId = new MemoryStream();
                                        byte Byte = (byte)InternalMemoryStream.ReadByte();
                                        while (Byte != 0x00)
                                        {
                                            InternalNodeId.WriteByte(Byte);
                                            Byte = (byte)InternalMemoryStream.ReadByte();
                                        }
                                        NodesDiscovered[i].Identifier = InternalEncoding.GetString(InternalNodeId.ToArray(), 0, (int)InternalNodeId.Length);
                                        byte[] ParentNwkAdd = new byte[2];
                                        InternalMemoryStream.Read(ParentNwkAdd, 0, 2);
                                        NodesDiscovered[i].ParentNetworkAddress = Aux.ByteToUshort(ParentNwkAdd);
                                        NodesDiscovered[i].NodeType = (Node_Type)InternalMemoryStream.ReadByte();
                                        byte Reserved = (byte)InternalMemoryStream.ReadByte();
                                        byte[] ProfileId = new byte[2];
                                        InternalMemoryStream.Read(ProfileId, 0, 2);
                                        NodesDiscovered[i].ProfileId = Aux.ByteToUshort(ProfileId);
                                        byte[] ManufacturerId = new byte[2];
                                        InternalMemoryStream.Read(ManufacturerId, 0, 2);
                                        NodesDiscovered[i].ManufacturerId = Aux.ByteToUshort(ManufacturerId);
                                        if ((Options & 0x01) == 0x01)
                                        {
                                            byte[] DeviceTypeId = new byte[4];
                                            InternalMemoryStream.Read(DeviceTypeId, 0, 4);
                                            NodesDiscovered[i].DeviceTypeId = Aux.ByteToUint(DeviceTypeId);
                                        }
                                    }
                                    else
                                        throw new Exception("Node Discover. Invalid API Identifier Frame received.");
                                }
                                return NodesDiscovered;
                            }
                            else
                            {
                                Node_Discovered[] NodesDiscovered = new Node_Discovered[0];
                                return NodesDiscovered;
                            }
                        }
                        else
                        {
                            ushort InternalNodeDiscoveryTimeout;
                            try
                            {
                                InternalNodeDiscoveryTimeout = NodeDiscoveryTimeout();
                            }
                            catch
                            {
                                Node_Discovered[] NodesDiscovered = new Node_Discovered[0];
                                return NodesDiscovered;
                            }
                            if (OperationMode == OperationModeEnum.Transparent)
                            {
                                byte[] ReqData = new byte[2 + 2 + 1];   // "AT" prefix + AT Command + "\r"
                                Array.Copy((Encoding.ASCII.GetBytes("ATND")), ReqData, 4);  // "AT" prefix + AT Command
                                ReqData[4] = CR;  // "\r"
                                if (!ATCommandMode)
                                {
                                    try
                                    {
                                        EnterCommandMode();
                                    }
                                    catch
                                    {
                                        Node_Discovered[] NodesDiscovered = new Node_Discovered[0];
                                        return NodesDiscovered;
                                    }
                                }
                                MemoryStream InternalMemoryStream = new MemoryStream();
                                int Nodes = 0;
                                DateTime Timeout = DateTime.Now.AddMilliseconds(InternalNodeDiscoveryTimeout * 1.2);  // Set Timeout
                                WaitXBeeRsp = true;    // Wait Response
                                XBeeSerialPortTx(ReqData);  // Send Data
                                while (DateTime.Now < Timeout)
                                {
                                    if (!WaitXBeeRsp)
                                    {
                                        InternalMemoryStream.Write(XBeeRspFrame, 0, XBeeRspFrame.Length);
                                        WaitXBeeRsp = true;    // Wait Response
                                        if (XBeeRspFrame[0] == CR)
                                            Nodes++;
                                        Thread.Sleep(10);
                                    }
                                }
                                Nodes--;
                                if (Nodes > 0)
                                {
                                    InternalCommandModeTimeoutTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);   // Disable Command Mode Timeout timer
                                    InternalCommandModeTimeoutTimer.Change(System.Threading.Timeout.Infinite, InternalInterval);    // Enable Command Mode Timeout timer
                                    Node_Discovered[] NodesDiscovered = new Node_Discovered[Nodes];
                                    InternalMemoryStream.Position = 0;
                                    for (int i = 0; i < Nodes; i++)
                                    {
                                        byte[] NwkAdd = new byte[5];
                                        InternalMemoryStream.Read(NwkAdd, 0, 5);
                                        NodesDiscovered[i].NetworkAddress = Aux.ByteToUshort(AsciiToByte(NwkAdd));
                                        byte[] IEEEAdd = new byte[17];
                                        InternalMemoryStream.Read(IEEEAdd, 0, 8);
                                        byte InternalCR = (byte)InternalMemoryStream.ReadByte();
                                        InternalMemoryStream.Read(IEEEAdd, 8, 9);
                                        NodesDiscovered[i].IEEEAddress = Aux.ByteToUlong(AsciiToByte(IEEEAdd));
                                        MemoryStream Identifier = new MemoryStream();
                                        byte ByteIdentifier = (byte)InternalMemoryStream.ReadByte();
                                        while (ByteIdentifier != 0x0D)
                                        {
                                            Identifier.WriteByte(ByteIdentifier);
                                            ByteIdentifier = (byte)InternalMemoryStream.ReadByte();
                                        }
                                        NodesDiscovered[i].Identifier = InternalEncoding.GetString(Identifier.ToArray(), 0, (int)Identifier.Length);
                                        byte[] ParentNwkAdd = new byte[5];
                                        InternalMemoryStream.Read(ParentNwkAdd, 0, 5);
                                        NodesDiscovered[i].ParentNetworkAddress = Aux.ByteToUshort(AsciiToByte(ParentNwkAdd));
                                        byte[] NodeType = new byte[3];
                                        InternalMemoryStream.Read(NodeType, 0, 3);
                                        NodesDiscovered[i].NodeType = (Node_Type)AsciiToByte(NodeType)[0];
                                        byte[] Reserved = new byte[3];
                                        InternalMemoryStream.Read(Reserved, 0, 3);
                                        byte[] ProfileId = new byte[5];
                                        InternalMemoryStream.Read(ProfileId, 0, 5);
                                        NodesDiscovered[i].ProfileId = Aux.ByteToUshort(AsciiToByte(ProfileId));
                                        byte[] ManufacturerId = new byte[5];
                                        InternalMemoryStream.Read(ManufacturerId, 0, 5);
                                        NodesDiscovered[i].ManufacturerId = Aux.ByteToUshort(AsciiToByte(ManufacturerId));
                                        byte ByteDeviceTypeId = (byte)InternalMemoryStream.ReadByte();
                                        if (ByteDeviceTypeId != CR)
                                        {
                                            byte[] Device_Type_Id = new byte[9];
                                            InternalMemoryStream.Position--;
                                            InternalMemoryStream.Read(Device_Type_Id, 0, 9);
                                            NodesDiscovered[i].DeviceTypeId = Aux.ByteToUint(AsciiToByte(Device_Type_Id));
                                            InternalCR = (byte)InternalMemoryStream.ReadByte();
                                        }
                                    }
                                    return NodesDiscovered;
                                }
                                else
                                {
                                    Node_Discovered[] NodesDiscovered = new Node_Discovered[0];
                                    return NodesDiscovered;
                                }
                            }
                            else
                            {
                                byte Options;
                                try
                                {
                                    Options = NetworkDiscoveryOptions();
                                }
                                catch
                                {
                                    Node_Discovered[] NodesDiscovered = new Node_Discovered[0];
                                    return NodesDiscovered;
                                }
                                byte[] ReqFrameData = new byte[1 + 1 + 2];   // API Identifier + Frame ID + AT Command
                                ReqFrameData[0] = (byte)Frame_Type.AT_Cmd_Req;   // API Identifier
                                ReqFrameData[1] = NewFrameID();  // Frame ID
                                Array.Copy(Encoding.ASCII.GetBytes("ND"), 0, ReqFrameData, 2, 2);    // AT Command
                                byte[] ReqFrame = GetFrame(ReqFrameData); // Frame
                                MemoryStream InternalMemoryStream = new MemoryStream();
                                int Nodes = 0;
                                DateTime Timeout = DateTime.Now.AddMilliseconds(InternalNodeDiscoveryTimeout * 1.2);  // Set Timeout
                                WaitXBeeRsp = true;    // Wait Response
                                XBeeSerialPortTx(ReqFrame);    // Send Frame
                                while (DateTime.Now < Timeout)
                                {
                                    if (!WaitXBeeRsp)
                                    {
                                        InternalMemoryStream.Write(XBeeRspFrame, 0, XBeeRspFrame.Length);
                                        WaitXBeeRsp = true;    // Wait Response
                                        Nodes++;
                                        Thread.Sleep(10);
                                    }
                                }
                                if (Nodes > 0)
                                {
                                    Node_Discovered[] NodesDiscovered = new Node_Discovered[Nodes];
                                    InternalMemoryStream.Position = 0;
                                    for (int i = 0; i < Nodes; i++)
                                    {
                                        Frame_Type RspFrameType = (Frame_Type)InternalMemoryStream.ReadByte();
                                        if (RspFrameType == Frame_Type.AT_Cmd_Rsp)
                                        {
                                            if (InternalFrameId != InternalMemoryStream.ReadByte())
                                                throw new Exception("Node Discover. The Frame ID sent does not correlate with the Frame ID received.");
                                            if (ReqFrameData[2] != InternalMemoryStream.ReadByte() || ReqFrameData[3] != InternalMemoryStream.ReadByte())
                                                throw new Exception("Node Discover. The AT Command sent does not correlate with the AT Command received.");
                                            Status_Rsp Status = (Status_Rsp)InternalMemoryStream.ReadByte();
                                            if (Status != Status_Rsp.Ok)
                                                throw new Exception("Node Discover. " + Status.ToString());
                                            byte[] NwkAdd = new byte[2];
                                            InternalMemoryStream.Read(NwkAdd, 0, 2);
                                            NodesDiscovered[i].NetworkAddress = Aux.ByteToUshort(NwkAdd);
                                            byte[] IEEEAdd = new byte[8];
                                            InternalMemoryStream.Read(IEEEAdd, 0, 8);
                                            NodesDiscovered[i].IEEEAddress = Aux.ByteToUlong(IEEEAdd);
                                            MemoryStream InternalNodeId = new MemoryStream();
                                            byte Byte = (byte)InternalMemoryStream.ReadByte();
                                            while (Byte != 0x00)
                                            {
                                                InternalNodeId.WriteByte(Byte);
                                                Byte = (byte)InternalMemoryStream.ReadByte();
                                            }
                                            NodesDiscovered[i].Identifier = InternalEncoding.GetString(InternalNodeId.ToArray(), 0, (int)InternalNodeId.Length);
                                            byte[] ParentNwkAdd = new byte[2];
                                            InternalMemoryStream.Read(ParentNwkAdd, 0, 2);
                                            NodesDiscovered[i].ParentNetworkAddress = Aux.ByteToUshort(ParentNwkAdd);
                                            NodesDiscovered[i].NodeType = (Node_Type)InternalMemoryStream.ReadByte();
                                            byte Reserved = (byte)InternalMemoryStream.ReadByte();
                                            byte[] ProfileId = new byte[2];
                                            InternalMemoryStream.Read(ProfileId, 0, 2);
                                            NodesDiscovered[i].ProfileId = Aux.ByteToUshort(ProfileId);
                                            byte[] ManufacturerId = new byte[2];
                                            InternalMemoryStream.Read(ManufacturerId, 0, 2);
                                            NodesDiscovered[i].ManufacturerId = Aux.ByteToUshort(ManufacturerId);
                                            if ((Options & 0x01) == 0x01)
                                            {
                                                byte[] DeviceTypeId = new byte[4];
                                                InternalMemoryStream.Read(DeviceTypeId, 0, 4);
                                                NodesDiscovered[i].DeviceTypeId = Aux.ByteToUint(DeviceTypeId);
                                            }
                                        }
                                        else
                                            throw new Exception("Node Discover. Invalid API Identifier Frame received.");
                                    }
                                    return NodesDiscovered;
                                }
                                else
                                {
                                    Node_Discovered[] NodesDiscovered = new Node_Discovered[0];
                                    return NodesDiscovered;
                                }
                            }
                        }
                    }

                    #endregion

                    #region Destination Node ZB / ZNET

                    /// <summary>
                    /// Resolves an Node Identifier string to a physical address.
                    /// Destination Address and Destination Network Address are 
                    /// set to the 64-bit address and 16-bit network address of 
                    /// the module with the matching Node Identifier respectively.
                    /// The radius of the DestinationNode command is set by the BH command.
                    /// </summary>
                    /// <param name="Identifier">String Node Identifier.</param>
                    /// <returns>True if successful.</returns>
                    public bool DestinationNode(string Identifier)
                    {
                        bool Found = false;
                        foreach (Node_Information Node in Network)
                        {
                            if (Node.Identifier == Identifier)
                            {
                                Found = true;
                                break;
                            }
                        }
                        if (!Found)
                            throw new ArgumentOutOfRangeException("Identifier.", "Destination Node. Identifier is not valid.");
                        ATCmdRsp DN;
                        int InternalOldTimeoutXBee = InternalTimeout;
                        try
                        {
                            ushort InternalNodeDiscoveryTimeout = NodeDiscoveryTimeout();
                            InternalTimeout = (int)(InternalNodeDiscoveryTimeout * 1.2);
                            DN = ATCommand("DN", Identifier);
                            if (DN.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalTimeout = InternalOldTimeoutXBee;
                                if (OperationMode != OperationModeEnum.Transparent)
                                {
                                    byte[] NwkAdd = new byte[2];
                                    Array.Copy(DN.CmdData, 0, NwkAdd, 0, 2);
                                    byte[] IEEEAddress = new byte[8];
                                    Array.Copy(DN.CmdData, 2, IEEEAddress, 0, 8);
                                    bool DestIEEEAddr = DestinationAddress(Aux.ByteToUlong(IEEEAddress));
                                    InternalDestNwkAddr = Aux.ByteToUshort(NwkAdd);
                                    if (DestIEEEAddr)
                                        return true;
                                    else
                                        return false;
                                }
                                return true;
                            }
                            InternalTimeout = InternalOldTimeoutXBee;
                            return false;
                        }
                        catch (TimeoutException ex)
                        {
                            InternalTimeout = InternalOldTimeoutXBee;
                            throw new TimeoutException("Destination Node. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            InternalTimeout = InternalOldTimeoutXBee;
                            throw new Exception("Destination Node. " + ex.Message);
                        }
                    }

                    #endregion

                    #region Scan Channels ZB / ZNET

                    /// <summary>Gets the list of channels to scan.</summary>
                    /// <returns>Returns the list of channels to scan.</returns>
                    public ushort ScanChannels()
                    {
                        ATCmdRsp SC;
                        try
                        {
                            SC = ATCommand("SC");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Scan Channels. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Scan Channels. " + ex.Message);
                        }
                        if (SC.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(SC.CmdData);
                        else
                            throw new Exception("Scan Channels. " + SC.CmdStatus.ToString());
                    }

                    /// <summary>Sets the list of channels to scan.</summary>
                    /// <param name="Channels">XBee 0x0001-0xFFFF / XBee-Pro 0x0001-0x3FFF.
                    /// Bit (Channel): 
                    /// 0 (0x0B)  4 (0x0F)  8 (0x13) 12 (0x17)
                    /// 1 (0x0C)  5 (0x10)  9 (0x14) 13 (0x18)
                    /// 2 (0x0D)  6 (0x11) 10 (0x15) 14 (0x19)
                    /// 3 (0x0E)  7 (0x12) 11 (0x16) 15 (0x1A)
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool ScanChannels(ushort Channels)
                    {
                        if (Channels < 0x01)
                            throw new ArgumentOutOfRangeException("Channels", "Channels range is 0x0001-0xFFFF for XBee devices, and 0x0001-0x3FFF for XBee-PRO running ZB firmware and 0x0001-0xFFFF for XBee devices, and 0x0002-0x3FFE for XBee-PRO running ZNET Firmware.");
                        ATCmdRsp SC;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.Hardware == Hardware.XBee_PRO && ((Host.Firmware == Firmware.ZB && Channels > 0x3FFF) || (Host.Firmware == Firmware.ZNet && (Channels > 0x3FFE || Channels < 0x02))))
                                            throw new ArgumentOutOfRangeException("Channels", "Channels range is 0x0001-0xFFFF for XBee devices, and 0x0001-0x3FFF for XBee-PRO running ZB firmware and 0x0001-0xFFFF for XBee devices, and 0x0002-0x3FFE for XBee-PRO running ZNET Firmware.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Hardware == Hardware.XBee_PRO && ((Host.Firmware == Firmware.ZB && Channels > 0x3FFF) || (Host.Firmware == Firmware.ZNet && (Channels > 0x3FFE || Channels < 0x02))))
                                    throw new ArgumentOutOfRangeException("Channels", "Channels range is 0x0001-0xFFFF for XBee devices, and 0x0001-0x3FFF for XBee-PRO running ZB firmware and 0x0001-0xFFFF for XBee devices, and 0x0002-0x3FFE for XBee-PRO running ZNET Firmware.");
                            }
                            SC = ATCommand("SC", Channels);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Scan Channels. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Scan Channels. " + ex.Message);
                        }
                        if (SC.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Scan Duration ZB / ZNET

                    /// <summary>Gets the scan channel duration.</summary>
                    /// <returns>Returns the scan time.</returns>
                    public Scan_Times ScanDuration()
                    {
                        ATCmdRsp SD;
                        try
                        {
                            SD = ATCommand("SD");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Scan Duration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Scan Duration. " + ex.Message);
                        }
                        if (SD.CmdStatus == Status_Rsp.Ok)
                            return (Scan_Times)(SD.CmdData[0]);
                        else
                            throw new Exception("Scan Duration. " + SD.CmdStatus.ToString());
                    }

                    /// <summary>Sets the scan channel duration.</summary>
                    /// <param name="ScanTime">Scan time.</param>
                    /// <returns>True if successful.</returns>
                    public bool ScanDuration(Scan_Times ScanTime)
                    {
                        if (!Enum.IsDefined(typeof(Scan_Times), ScanTime))
                            throw new ArgumentOutOfRangeException("ScanTime", "Scan Duration. ScanTime is out of legal range.");
                        ATCmdRsp SD;
                        try
                        {
                            SD = ATCommand("SD", (byte)ScanTime);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Scan Duration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Scan Duration. " + ex.Message);
                        }
                        if (SD.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region ZigBee Stack Profile ZB

                    /// <summary>
                    /// Gets the ZigBee stack profile value. This must be set the
                    /// same on all devices that should join the same network.
                    /// </summary>
                    /// <returns>Returns the ZigBee stack profile value.</returns>
                    public byte ZigBeeStackProfile()
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("ZigBee Stack Profile. This Command is not supported in devices running ZNET firmware.");
                        ATCmdRsp ZS;
                        try
                        {
                            ZS = ATCommand("ZS");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("ZigBee Stack Profile. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("ZigBee Stack Profile. " + ex.Message);
                        }
                        if (ZS.CmdStatus == Status_Rsp.Ok)
                            return ZS.CmdData[0];
                        else
                            throw new Exception("ZigBee Stack Profile. " + ZS.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the ZigBee stack profile value. This must be set the
                    /// same on all devices that should join the same network.
                    /// </summary>
                    /// <param name="Profile">ZigBee stack profile value.</param>
                    /// <returns>True if successful.</returns>
                    public bool ZigBeeStackProfile(byte Profile)
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("ZigBee Stack Profile. This Command is not supported in devices running ZNET firmware.");
                        if (Profile > 0x02)
                            throw new ArgumentOutOfRangeException("StackProfile", "ZigBee Stack Profile. StackProfile range is 0x00-0x02.");
                        ATCmdRsp ZS;
                        try
                        {
                            ZS = ATCommand("ZS", Profile);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("ZigBee Stack Profile. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("ZigBee Stack Profile. " + ex.Message);
                        }
                        if (ZS.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Node Join Time ZB / ZNET

                    /// <summary>
                    /// Gets the time that a Coordinator/Router allows nodes to join.
                    /// </summary>
                    /// <returns>Returns the Join Time in seconds.</returns>
                    public byte NodeJoinTime()
                    {
                        ATCmdRsp NJ;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType == Node_Type.End_Device)
                                            throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.End_Device)
                                    throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                            }
                            NJ = ATCommand("NJ");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Node Join Time. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Node Join Time. " + ex.Message);
                        }
                        if (NJ.CmdStatus == Status_Rsp.Ok)
                            return NJ.CmdData[0];
                        else
                            throw new Exception("Node Join Time. " + NJ.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the time that a Coordinator/Router allows nodes to join.
                    /// 0xFF = Always allows joining.
                    /// </summary>
                    /// <param name="JoinTime">Join Time in seconds.</param>
                    /// <returns>True if successful.</returns>
                    public bool NodeJoinTime(byte JoinTime)
                    {
                        ATCmdRsp NJ;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType == Node_Type.End_Device)
                                            throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.End_Device)
                                    throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                            }
                            NJ = ATCommand("NJ", JoinTime);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Node Join Time. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Node Join Time. " + ex.Message);
                        }
                        if (NJ.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Channel Verification ZB / ZNET

                    /// <summary>Gets the channel verification.
                    /// If channel verification is enabled, a router will verify the 
                    /// coordinator is on its operating channel when joining or coming 
                    /// up from a power cycle. If a coordinator is not detected, the 
                    /// router will leave its current channel and attempt to join a new 
                    /// PAN. If channel verification is disabled, the router will continue 
                    /// operating on its current channel even if a coordinator is not detected.
                    /// </summary>
                    /// <returns>Returns if the channel verification is enabled.</returns>
                    public bool ChannelVerification()
                    {
                        ATCmdRsp JV;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType != Node_Type.Router)
                                            throw new InvalidOperationException("Invalid Device Type, it must be a Router.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType != Node_Type.Router)
                                    throw new InvalidOperationException("Invalid Device Type, it must be a Router.");
                            }
                            JV = ATCommand("JV");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Channel Verification. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Channel Verification. " + ex.Message);
                        }
                        if (JV.CmdStatus == Status_Rsp.Ok)
                        {
                            if (JV.CmdData[0] == 0x00)
                                return false;
                            else
                                return true;
                        }
                        else
                            throw new Exception("Channel Verification. " + JV.CmdStatus.ToString());
                    }

                    /// <summary>Sets the channel verification enabled or disabled.
                    /// If channel verification is enabled, a router will verify the 
                    /// coordinator is on its operating channel when joining or coming 
                    /// up from a power cycle. If a coordinator is not detected, the 
                    /// router will leave its current channel and attempt to join a new 
                    /// PAN. If channel verification is disabled, the router will continue 
                    /// operating on its current channel even if a coordinator is not detected.
                    /// </summary>
                    /// <param name="Verification">Channel verification.</param>
                    /// <returns>True if successful.</returns>
                    public bool ChannelVerification(bool Verification)
                    {
                        ATCmdRsp JV;
                        byte VerificationByte;
                        if (!Verification)
                            VerificationByte = 0x00;
                        else
                            VerificationByte = 0x01;

                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType != Node_Type.Router)
                                            throw new InvalidOperationException("Invalid Device Type, it must be a Router.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType != Node_Type.Router)
                                    throw new InvalidOperationException("Invalid Device Type, it must be a Router.");
                            }
                            JV = ATCommand("JV", VerificationByte);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Channel Verification. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Channel Verification. " + ex.Message);
                        }
                        if (JV.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Join Notification ZB / ZNET

                    /// <summary>Gets the join notification.</summary>
                    /// <returns>Returns the join notification setting.</returns>
                    public bool JoinNotification()
                    {
                        ATCmdRsp JN;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType == Node_Type.Coordinator)
                                            throw new InvalidOperationException("Invalid Device Type, it must not be a Coordinator.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.Coordinator)
                                    throw new InvalidOperationException("Invalid Device Type, it must not be a Coordinator.");
                            }
                            JN = ATCommand("JN");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Join Notification. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Join Notification. " + ex.Message);
                        }
                        if (JN.CmdStatus == Status_Rsp.Ok)
                        {
                            if (JN.CmdData[0] == 0x00)
                                return false;
                            else
                                return true;
                        }
                        else
                            throw new Exception("Join Notification. " + JN.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the join notification enabled or disabled.
                    /// If enabled, the module will transmit a broadcast node 
                    /// identification packet on power up and when joining. This
                    /// action blinks the Associate LED rapidly on all devices 
                    /// that receive the transmission, and sends an API frame 
                    /// out the UART of API devices.
                    /// </summary>
                    /// <param name="Notification">Join notification.</param>
                    /// <returns>True if successful.</returns>
                    public bool JoinNotification(bool Notification)
                    {
                        ATCmdRsp JN;
                        byte NotificationByte;
                        if (!Notification)
                            NotificationByte = 0x00;
                        else
                            NotificationByte = 0x01;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType == Node_Type.Coordinator)
                                            throw new InvalidOperationException("Invalid Device Type, it must not be a Coordinator.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.Coordinator)
                                    throw new InvalidOperationException("Invalid Device Type, it must not be a Coordinator.");
                            }
                            JN = ATCommand("JN", NotificationByte);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Join Notification. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Join Notification. " + ex.Message);
                        }
                        if (JN.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Aggregate Routing Notification ZB / ZNET

                    /// <summary>
                    /// Gets time between consecutive aggregate route broadcast messages.
                    /// </summary>
                    /// <returns>Returns the time between consecutive aggregate route broadcast.</returns>
                    public byte AggregateRoutingNotification()
                    {
                        ATCmdRsp AR;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType == Node_Type.End_Device)
                                            throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.End_Device)
                                    throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                            }
                            AR = ATCommand("AR");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Aggregate Routing Notification. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Aggregate Routing Notification. " + ex.Message);
                        }
                        if (AR.CmdStatus == Status_Rsp.Ok)
                            return AR.CmdData[0];
                        else
                            throw new Exception("Aggregate Routing Notification. " + AR.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets time between consecutive aggregate route broadcast messages.
                    /// </summary>
                    /// <param name="Time">Time between consecutive aggregate route broadcast.</param>
                    /// <returns>True if successful.</returns>
                    public bool AggregateRoutingNotification(byte Time)
                    {
                        ATCmdRsp AR;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType == Node_Type.End_Device)
                                            throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.End_Device)
                                    throw new InvalidOperationException("Invalid Device Type, it must not be an End Device.");
                            }
                            AR = ATCommand("AR", Time);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Aggregate Routing Notification. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Aggregate Routing Notification. " + ex.Message);
                        }
                        if (AR.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Association Indication ZB / ZNET

                    /// <summary>Gets information regarding last node join request.</summary>
                    /// <returns>Returns the information regarding last node join request.</returns>
                    public Association_Indications AssociationIndication()
                    {
                        ATCmdRsp AI;
                        try
                        {
                            AI = ATCommand("AI");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Association Indication. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Association Indication. " + ex.Message);
                        }
                        if (AI.CmdStatus == Status_Rsp.Ok)
                            return (Association_Indications)AI.CmdData[0];
                        else
                            throw new Exception("Association Indication. " + AI.CmdStatus.ToString());
                    }

                    #endregion

                #endregion

                #region Security Commands

                    #region Encryption Enable ZB / ZNET

                    /// <summary>Gets the encryption enable setting. </summary>
                    /// <returns>Returns the encryption enable setting.</returns>
                    public bool EncryptionEnable()
                    {
                        ATCmdRsp EE;
                        try
                        {
                            EE = ATCommand("EE");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Encryption Enable. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Encryption Enable. " + ex.Message);
                        }
                        if (EE.CmdStatus == Status_Rsp.Ok)
                        {
                            if (EE.CmdData[0] == 0x00)
                                return false;
                            else
                                return true;
                        }
                        else
                            throw new Exception("Encryption Enable. " + EE.CmdStatus.ToString());
                    }

                    /// <summary>Sets the encryption enable setting. </summary>
                    /// <param name="Encryption">Encryption.</param>
                    /// <returns>True if successful.</returns>
                    public bool EncryptionEnable(bool Encryption)
                    {
                        ATCmdRsp EE;
                        byte EncryptionByte;
                        if (!Encryption)
                            EncryptionByte = 0x00;
                        else
                            EncryptionByte = 0x01;
                        try
                        {
                            EE = ATCommand("EE", EncryptionByte);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Encryption Enable. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Encryption Enable. " + ex.Message);
                        }
                        if (EE.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Encryption Options ZB / ZNET

                    /// <summary>
                    /// Gets options for encryption.
                    /// 0x01 = Send the security key unsecured over-the-air during joins.
                    /// 0x02 = Use trust center (coordinator only).
                    /// </summary>
                    /// <returns>Returns the options for encryption.</returns>
                    public byte EncryptionOptions()
                    {
                        ATCmdRsp EO;
                        try
                        {
                            EO = ATCommand("EO");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Encryption Options. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Encryption Options. " + ex.Message);
                        }
                        if (EO.CmdStatus == Status_Rsp.Ok)
                            return EO.CmdData[0];
                        else
                            throw new Exception("Encryption Options. " + EO.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets options for encryption. Unused option bits should be set to 0.
                    /// 0x01 = Send the security key unsecured over-the-air during joins.
                    /// 0x02 = Use trust center (coordinator only).
                    /// </summary>
                    /// <param name="Options">Options for encryption.</param>
                    /// <returns>True if successful.</returns>
                    public bool EncryptionOptions(byte Options)
                    {
                        if (Options > 0x03)
                            throw new ArgumentOutOfRangeException("Options", "Encryption Options. Options range is 0x00-0x03.");
                        ATCmdRsp EO;
                        try
                        {
                            EO = ATCommand("EO", Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Encryption Options. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Encryption Options. " + ex.Message);
                        }
                        if (EO.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Network Encryption Key ZB

                    /// <summary>Sets the 128-bit AES encryption key.</summary>
                    /// <param name="KeyHigh">64-bit high AES encryption key.</param>
                    /// <param name="KeyLow">64-bit low AES encryption key.</param>
                    /// <returns>True if successful.</returns>
                    public bool NetworkEncryptionKey(ulong KeyHigh, ulong KeyLow)
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Network Encryption Key. This Command is not supported in devices running ZNET firmware.");
                        ATCmdRsp NK;
                        byte[] Key = new byte[16];
                        Array.Copy(Aux.UlongToByte(KeyHigh),0 , Key, 0, 8);
                        Array.Copy(Aux.UlongToByte(KeyLow), 0, Key, 8, 8);
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType != Node_Type.Coordinator)
                                            throw new InvalidOperationException("Invalid Device Type, it must be a Coordinator.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType != Node_Type.Coordinator)
                                    throw new InvalidOperationException("Invalid Device Type, it must be a Coordinator.");
                            }
                            NK = ATCommand("NK", Key);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Network Encryption Key. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Network Encryption Key. " + ex.Message);
                        }
                        if (NK.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Link Key ZB / ZNET

                    /// <summary>Sets the 128-bit AES link key.</summary>
                    /// <param name="KeyHigh">64-bit high AES link key.</param>
                    /// <param name="KeyLow">64-bit low AES link key.</param>
                    /// <returns>True if successful.</returns>
                    public bool LinkKey(ulong KeyHigh, ulong KeyLow)
                    {
                        ATCmdRsp KY;
                        byte[] Key = new byte[16];
                        Array.Copy(Aux.UlongToByte(KeyHigh), 0, Key, 0, 8);
                        Array.Copy(Aux.UlongToByte(KeyLow), 0, Key, 8, 8);
                        try
                        {
                            KY = ATCommand("KY", Key);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Link Key. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Link Key. " + ex.Message);
                        }
                        if (KY.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                #endregion

                #region RF Interfacing Commands

                    #region Power Level ZB / ZNET

                    /// <summary>
                    /// Gets the power level at which the RF module transmits conducted power.
                    /// </summary>
                    /// <returns>Returns the power level.</returns>
                    public byte PowerLevel()
                    {
                        ATCmdRsp PL;
                        try
                        {
                            PL = ATCommand("PL");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Power Level. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Power Level. " + ex.Message);
                        }
                        if (PL.CmdStatus == Status_Rsp.Ok)
                            return PL.CmdData[0];
                        else
                            throw new Exception("Power Level. " + PL.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the power level at which the RF module transmits conducted power.
                    /// </summary>
                    /// <param name="Level">Power level.
                    ///    XBee   
                    /// 0 = -8 dBm
                    /// 1 = -4 dBm           XBee-PRO
                    /// 2 = -2 dBm          4 = 17 dBm
                    /// 3 =  0 dBm
                    /// 4 =  2 dBm
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool PowerLevel(byte Level)
                    {
                        if (Level > 0x04)
                            throw new ArgumentOutOfRangeException("Level", "Power Level. Level range is 0-4 for XBee and 4 for XBee-PRO.");
                        ATCmdRsp PL;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.Hardware == Hardware.XBee_PRO && Level != 0x4)
                                            throw new ArgumentOutOfRangeException("Level", "Level range is 0-4 for XBee and 4 for XBee-PRO.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Hardware == Hardware.XBee_PRO && Level != 0x4)
                                    throw new ArgumentOutOfRangeException("Level", "Level range is 0-4 for XBee and 4 for XBee-PRO.");
                            }
                            PL = ATCommand("PL", Level);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Power Level. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Power Level. " + ex.Message);
                        }
                        if (PL.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Power Mode ZB / ZNET

                    /// <summary>
                    /// Gets the power mode of the device. Enabling boost mode will improve
                    /// the receive sensitivity by 1dB and increase the transmit power by 2dB. 
                    /// </summary>
                    /// <returns>Returns the boost mode.</returns>
                    public bool PowerMode()
                    {
                        ATCmdRsp PM;
                        try
                        {
                            PM = ATCommand("PM");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Power Mode. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Power Mode. " + ex.Message);
                        }
                        if (PM.CmdStatus == Status_Rsp.Ok)
                        {
                            if (PM.CmdData[0] == 0x00)
                                return false;
                            else
                                return true;
                        }
                        else
                            throw new Exception("Power Mode. " + PM.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the power mode of the device. Enabling boost mode will improve
                    /// the receive sensitivity by 1dB and increase the transmit power by 2dB. 
                    /// </summary>
                    /// <param name="BoostMode">Boost mode.</param>
                    /// <returns>True if successful.</returns>
                    public bool PowerMode(bool BoostMode)
                    {
                        ATCmdRsp PM;
                        byte BoostModeByte;
                        if (!BoostMode)
                            BoostModeByte = 0x00;
                        else
                            BoostModeByte = 0x01;
                        try
                        {
                            PM = ATCommand("PM", BoostModeByte);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Power Mode. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Power Mode. " + ex.Message);
                        }
                        if (PM.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Clear Signal Strength ZB / ZNET

                    /// <summary>
                    /// Clears the received signal strength of the last received RF data packet. 
                    /// </summary>
                    /// <returns>True if successful.</returns>
                    public bool ClearSignalStrength()
                    {
                        ATCmdRsp DB;
                        try
                        {
                            DB = ATCommand("DB", 0x00);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Clear Signal Strength. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Clear Signal Strength. " + ex.Message);
                        }
                        if (DB.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Received Signal Strength ZB / ZNET

                    /// <summary>
                    /// Gets the received signal strength of the last received RF data packet. 
                    /// </summary>
                    /// <returns>Returns the received signal strength in -dBm.</returns>
                    public byte ReceivedSignalStrength()
                    {
                        ATCmdRsp DB;
                        try
                        {
                            DB = ATCommand("DB");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Received Signal Strength. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Received Signal Strength. " + ex.Message);
                        }
                        if (DB.CmdStatus == Status_Rsp.Ok)
                            return DB.CmdData[0];
                        else
                            throw new Exception("Received Signal Strength. " + DB.CmdStatus.ToString());
                    }

                    #endregion

                #endregion

                #region Serial Interfacing Commands

                    #region API Mode ZB / ZNET

                    /// <summary>Gets API Mode.</summary>
                    /// <returns>Returns the API mode.</returns>
                    public API_Mode APIMode()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                            throw new Exception("API Mode. This Command is not supported in Transparent Operation Mode.");
                        else
                        {
                            ATCmdRsp AP;
                            try
                            {
                                AP = ATCommand("AP");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("API Mode. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("API Mode. " + ex.Message);
                            }
                            if (AP.CmdStatus == Status_Rsp.Ok)
                            {
                                if (!InternalRemoteATCmdEnable)
                                {
                                    switch ((API_Mode)AP.CmdData[0])
                                    {
                                        case API_Mode.Enabled:
                                            OperationMode = OperationModeEnum.API;
                                            break;
                                        case API_Mode.Enabled_With_Escaped:
                                            OperationMode = OperationModeEnum.API_With_Escaped;
                                            break;
                                    }
                                }
                                return (API_Mode)AP.CmdData[0];
                            }
                            else
                                throw new Exception("API Mode. " + AP.CmdStatus.ToString());
                        }
                    }

                    /// <summary>Sets API Mode.</summary>
                    /// <param name="Mode">API mode.</param>
                    /// <returns>True if successful.</returns>
                    public bool APIMode(API_Mode Mode)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                            throw new Exception("API Mode. This Command is not supported in Transparent Operation Mode.");
                        else
                        {
                            if (!Enum.IsDefined(typeof(API_Mode), Mode))
                                throw new ArgumentOutOfRangeException("Mode", "API Mode. Mode is out of legal range.");
                            ATCmdRsp AP;
                            try
                            {
                                AP = ATCommand("AP", (byte)Mode);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("API Mode. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("API Mode. " + ex.Message);
                            }
                            if (AP.CmdStatus == Status_Rsp.Ok)
                            {
                                if (!InternalRemoteATCmdEnable)
                                {
                                    switch (Mode)
                                    {
                                        case API_Mode.Enabled:
                                            OperationMode = OperationModeEnum.API;
                                            break;
                                        case API_Mode.Enabled_With_Escaped:
                                            OperationMode = OperationModeEnum.API_With_Escaped;
                                            break;
                                    }
                                } 
                                return true;
                            }
                            else
                                return false;
                        }
                    }

                    #endregion

                    #region API Options ZB / ZNET

                    /// <summary>
                    /// Gets the type of receive API frame to send out the Uart for received RF data packets.
                    /// </summary>
                    /// <returns>Returns if ZigBee explicit Rx is enabled.</returns>
                    public bool APIOptions()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                            throw new Exception("API Options. This Command is not supported in Transparent Operation Mode.");
                        else
                        {
                            ATCmdRsp AO;
                            try
                            {
                                AO = ATCommand("AO");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("API Options. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("API Options. " + ex.Message);
                            }
                            if (AO.CmdStatus == Status_Rsp.Ok)
                            {
                                if (AO.CmdData[0] == 0x00)
                                {
                                    if (!InternalRemoteATCmdEnable)
                                        InternalAPIOptions = false;
                                    return false;
                                }
                                else
                                {
                                    if (!InternalRemoteATCmdEnable)
                                        InternalAPIOptions = true;
                                    return true;
                                }
                            }
                            else
                                throw new Exception("API Options. " + AO.CmdStatus.ToString());
                        }
                    }

                    /// <summary>
                    /// Sets the type of receive API frame to send out the Uart for received RF data packets.
                    /// </summary>
                    /// <param name="Options">Enable ZigBee explicit Rx.</param>
                    /// <returns>True if successful.</returns>
                    public bool APIOptions(bool Options)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                            throw new Exception("API Options. This Command is not supported in Transparent Operation Mode.");
                        else
                        {
                            ATCmdRsp AO;
                            byte OptionsByte;
                            if (!Options)
                                OptionsByte = 0x00;
                            else
                                OptionsByte = 0x01;
                            try
                            {
                                AO = ATCommand("AO", OptionsByte);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("API Options. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("API Options. " + ex.Message);
                            }
                            if (AO.CmdStatus == Status_Rsp.Ok)
                            {
                                if (!InternalRemoteATCmdEnable)
                                    InternalAPIOptions = Options;
                                return true;
                            }
                            else
                                return false;
                        }
                    }

                    #endregion

                    #region Interface Data Rate ZB / ZNET

                    /// <summary>
                    /// Gets the serial interface data rate for communication between the XBee and host.
                    /// </summary>
                    /// <returns>Returns the serial interface data rate.</returns>
                    public XBee_BaudRate InterfaceDataRate()
                    {
                        ATCmdRsp BD;
                        try
                        {
                            BD = ATCommand("BD");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Interface Data Rate. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Interface Data Rate. " + ex.Message);
                        }
                        if (BD.CmdStatus == Status_Rsp.Ok)
                            return (XBee_BaudRate)BD.CmdData[BD.CmdData.Length - 1];
                        else
                            throw new Exception("Interface Data Rate. " + BD.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the serial interface data rate for communication between the XBee and host.
                    /// </summary>
                    /// <param name="DataRate">Serial interface data rate.</param>
                    /// <returns>True if successful.</returns>
                    public bool InterfaceDataRate(XBee_BaudRate DataRate)
                    {
                        if (!Enum.IsDefined(typeof(XBee_BaudRate), DataRate))
                            throw new ArgumentOutOfRangeException("DataRate", "Interface Data Rate. DataRate is out of legal range.");
                        ATCmdRsp BD;
                        try
                        {
                            BD = ATCommand("BD", (byte)DataRate);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Interface Data Rate. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Interface Data Rate. " + ex.Message);
                        }
                        if (BD.CmdStatus == Status_Rsp.Ok)
                        {
                            if (!InternalRemoteATCmdEnable)
                            {
                                if (OperationMode == OperationModeEnum.Transparent)
                                    ApplyChanges();
                                switch (DataRate)
                                {
                                    case XBee_BaudRate._1200:
                                        BaudRate = BaudRateEnum._1200;
                                        break;
                                    case XBee_BaudRate._2400:
                                        BaudRate = BaudRateEnum._2400;
                                        break;
                                    case XBee_BaudRate._4800:
                                        BaudRate = BaudRateEnum._4800;
                                        break;
                                    case XBee_BaudRate._9600:
                                        BaudRate = BaudRateEnum._9600;
                                        break;
                                    case XBee_BaudRate._19200:
                                        BaudRate = BaudRateEnum._19200;
                                        break;
                                    case XBee_BaudRate._38400:
                                        BaudRate = BaudRateEnum._38400;
                                        break;
                                    case XBee_BaudRate._57600:
                                        BaudRate = BaudRateEnum._57600;
                                        break;
                                    case XBee_BaudRate._115200:
                                        BaudRate = BaudRateEnum._115200;
                                        break;
                                }
                            }                            
                            return true;
                        }
                        else
                            return false;
                    }

                    #endregion

                    #region Serial Parity ZB / ZNET

                    /// <summary>
                    /// Gets the serial parity setting on the XBee.
                    /// </summary>
                    /// <returns>Returns the serial parity setting.</returns>
                    public XBee_Parity SerialParity()
                    {
                        ATCmdRsp NB;
                        try
                        {
                            NB = ATCommand("NB");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Serial Parity. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Serial Parity. " + ex.Message);
                        }
                        if (NB.CmdStatus == Status_Rsp.Ok)
                            return (XBee_Parity)NB.CmdData[NB.CmdData.Length - 1];
                        else
                            throw new Exception("Serial Parity. " + NB.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the serial parity setting on the XBee.
                    /// </summary>
                    /// <param name="Parity">Serial parity setting.</param>
                    /// <returns>True if successful.</returns>
                    public bool SerialParity(XBee_Parity parity)
                    {
                        if (!Enum.IsDefined(typeof(XBee_Parity), parity))
                            throw new ArgumentOutOfRangeException("Parity", "Serial Parity. Parity is out of legal range.");
                        ATCmdRsp NB;
                        try
                        {
                            NB = ATCommand("NB", (byte)parity);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Serial Parity. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Serial Parity. " + ex.Message);
                        }
                        if (NB.CmdStatus == Status_Rsp.Ok)
                        {
                            if (!InternalRemoteATCmdEnable)
                            {
                                if (OperationMode == OperationModeEnum.Transparent)
                                    ApplyChanges();
                                switch (parity)
                                {
                                    case XBee_Parity.None:
                                        Parity = ParityEnum.None;
                                        break;
                                    case XBee_Parity.Even:
                                        Parity = ParityEnum.Even;
                                        break;
                                    case XBee_Parity.Odd:
                                        Parity = ParityEnum.Odd;
                                        break;
                                    case XBee_Parity.Mark:
                                        Parity = ParityEnum.Mark;
                                        break;
                                }
                            }
                            return true;
                        }
                        else
                            return false;
                    }

                    #endregion

                    #region Packetization Timeout ZB / ZNET

                    /// <summary>
                    /// Gets number of character times of inter-character silence required before packetization. 
                    /// </summary>
                    /// <returns>Returns the number of character times.</returns>
                    public byte PacketizationTimeout()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp RO;
                            try
                            {
                                RO = ATCommand("RO");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Packetization Timeout. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Packetization Timeout. " + ex.Message);
                            }
                            if (RO.CmdStatus == Status_Rsp.Ok)
                                return RO.CmdData[0];
                            else
                                throw new Exception("Packetization Timeout. " + RO.CmdStatus.ToString());
                        }
                        else
                            throw new Exception("Packetization Timeout. This Command is only supported in Transparent Operation Mode.");
                    }

                    /// <summary>
                    /// Sets number of character times of inter-character silence required before packetization. 
                    /// 0x00 = Transmit characters as they arrive instead of buffering them into one RF packet.
                    /// </summary>
                    /// <param name="Characters">Number of character times.</param>
                    /// <returns>True if successful.</returns>
                    public bool PacketizationTimeout(byte Characters)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp RO;
                            try
                            {
                                RO = ATCommand("RO", Characters);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Packetization Timeout. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Packetization Timeout. " + ex.Message);
                            }
                            if (RO.CmdStatus == Status_Rsp.Ok)
                                return true;
                            else
                                return false;
                        }
                        else
                            throw new Exception("Packetization Timeout. This Command is only supported in Transparent Operation Mode.");
                    }

                    #endregion

                    #region DIO6 Configuration ZB / ZNET

                    /// <summary>Gets options for the DIO6 line of the XBee.</summary>
                    /// <returns>Returns the options for the DIO6 line.</returns>
                    public DIO6 DIO6Configuration()
                    {
                        ATCmdRsp D6;
                        try
                        {
                            D6 = ATCommand("D6");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO6 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO6 Configuration. " + ex.Message);
                        }
                        if (D6.CmdStatus == Status_Rsp.Ok)
                            return (DIO6)D6.CmdData[D6.CmdData.Length - 1];
                        else
                            throw new Exception("DIO6 Configuration. " + D6.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the DIO6 line of the XBee.</summary>
                    /// <param name="Options">Options for the DIO6 line.</param>
                    /// <returns>True if successful.</returns>
                    public bool DIO6Configuration(DIO6 Options)
                    {
                        if (!Enum.IsDefined(typeof(DIO6), Options))
                            throw new ArgumentOutOfRangeException("Options", "DIO6 Configuration. Options is out of legal range.");
                        ATCmdRsp D6;
                        try
                        {
                            D6 = ATCommand("D6", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO6 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO6 Configuration. " + ex.Message);
                        }
                        if (D6.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region DIO7 Configuration ZB / ZNET

                    /// <summary>Gets options for the DIO7 line of the XBee.</summary>
                    /// <returns>Returns the options for the DIO7 line.</returns>
                    public DIO7 DIO7Configuration()
                    {
                        ATCmdRsp D7;
                        try
                        {
                            D7 = ATCommand("D7");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO7 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO7 Configuration. " + ex.Message);
                        }
                        if (D7.CmdStatus == Status_Rsp.Ok)
                            return (DIO7)D7.CmdData[D7.CmdData.Length - 1];
                        else
                            throw new Exception("DIO7 Configuration. " + D7.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the DIO7 line of the XBee.</summary>
                    /// <param name="Options">Options for the DIO7 line.</param>
                    /// <returns>True if successful.</returns>
                    public bool DIO7Configuration(DIO7 Options)
                    {
                        if (!Enum.IsDefined(typeof(DIO7), Options))
                            throw new ArgumentOutOfRangeException("Options", "DIO7 Configuration. Options is out of legal range.");
                        ATCmdRsp D7;
                        try
                        {
                            D7 = ATCommand("D7", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO7 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO7 Configuration. " + ex.Message);
                        }
                        if (D7.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                #endregion

                #region I/O Commands

                    #region Force Sample ZB / ZNET

                    /// <summary>
                    /// Forces a read of all enabled digital and analog input lines. 
                    /// </summary>
                    /// <returns>Returns a read of all enabled digital and analog input lines.</returns>
                    public Sample ForceSample()
                    {
                        ATCmdRsp IS;
                        try
                        {
                            IS = ATCommand("IS");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Force Sample. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Force Sample. " + ex.Message);
                        }
                        if (IS.CmdStatus == Status_Rsp.Ok)
                        {
                            Sample SampleRead = new Sample();
                            SampleRead.Samples = IS.CmdData[0];
                            byte[] DigitalMask = new byte[2];
                            Array.Copy(IS.CmdData, 1, DigitalMask, 0, 2);
                            SampleRead.DigitalMask = Aux.ByteToUshort(DigitalMask);
                            SampleRead.AnalogMask = IS.CmdData[3];
                            int Index = 4;
                            if (SampleRead.DigitalMask == 0x00)
                                SampleRead.DigitalMask = null;
                            else
                            {
                                byte[] DigitalSamples = new byte[2];
                                Array.Copy(IS.CmdData, 4, DigitalSamples, 0, 2);
                                SampleRead.DigitalSamples = Aux.ByteToUshort(DigitalSamples);
                                Index = 6;
                            }
                            if (SampleRead.AnalogMask == 0x00)
                                SampleRead.AnalogMask = null;
                            else
                            {
                                SampleRead.AnalogSamples = new ushort[(IS.CmdData.Length - Index) / 2];
                                byte[] AnalogSample = new byte[2];
                                for (int i = 0; i < SampleRead.AnalogSamples.Length; i++)
                                {
                                    Array.Copy(IS.CmdData, (Index + i * 2), AnalogSample, 0, 2);
                                    SampleRead.AnalogSamples[i] = Aux.ByteToUshort(AnalogSample);
                                }
                            }
                            return SampleRead;
                        }
                        else
                            throw new Exception("Force Sample. " + IS.CmdStatus.ToString());
                    }

                    #endregion

                    #region XBee Sensor Sample ZB / ZNET

                    /// <summary>
                    /// Forces a sample to be taken on an XBee Sensor device. 
                    /// </summary>
                    /// <returns>Returns a sample taken on an XBee Sensor Device.</returns>
                    public byte[] XBeeSensorSample()
                    {
                        ATCmdRsp IS;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType == Node_Type.Coordinator)
                                            throw new InvalidOperationException("Invalid Device Type, it must not be a Coordinator.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.Coordinator)
                                    throw new InvalidOperationException("Invalid Device Type, it must not be a Coordinator.");
                            }
                            IS = ATCommand("1S");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("XBee Sensor Sample. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("XBee Sensor Sample. " + ex.Message);
                        }
                        if (IS.CmdStatus == Status_Rsp.Ok)
                            return IS.CmdData;
                        else
                            throw new Exception("XBee Sensor Sample. " + IS.CmdStatus.ToString());
                    }

                    #endregion

                    #region IO Sample Rate ZB / ZNET

                    /// <summary>Gets the IO Sample Rate to enable periodic sampling.</summary>
                    /// <returns>Returns the IO Sample Rate in milliseconds.</returns>
                    public ushort IOSampleRate()
                    {
                        ATCmdRsp IR;
                        try
                        {
                            IR = ATCommand("IR");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("IO Sample Rate. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("IO Sample Rate. " + ex.Message);
                        }
                        if (IR.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(IR.CmdData);
                        else
                            throw new Exception("IO Sample Rate. " + IR.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the IO Sample Rate to enable periodic sampling. For periodic sampling 
                    /// to be enabled, IO Sample Rate must be set to a non-zero value, and at least 
                    /// one module pin must have analog or digital IO functionality enabled.
                    /// IO Samples are sent to the Destination Address.
                    /// </summary>
                    /// <param name="SampleRate">Sample rate in milliseconds.</param>
                    /// <returns>True if successful.</returns>
                    public bool IOSampleRate(ushort SampleRate)
                    {
                        ATCmdRsp IR;
                        try
                        {
                            IR = ATCommand("IR", SampleRate);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("IO Sample Rate. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("IO Sample Rate. " + ex.Message);
                        }
                        if (IR.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region IO Digital Change Detection ZB / ZNET

                    /// <summary>
                    /// Gets the digital IO pins to monitor for changes in the IO state. 
                    /// IO Digital Change Detection is a bitmask that can be used to 
                    /// enable or disable edge detection on individual channels. 
                    /// Bit (IO pin): 
                    /// 0 (DIO0)    4 (DIO4)     8 (DIO8)
                    /// 1 (DIO1)    5 (DIO5)     9 (DIO9)
                    /// 2 (DIO2)    6 (DIO6)    10 (DIO10)
                    /// 3 (DIO3)    7 (DIO7)    11 (DIO11)
                    /// </summary>
                    /// <returns>
                    /// Returns a bitmask used to enable or disable edge detection on individual channels.
                    /// </returns>
                    public ushort IODigitalChangeDetection()
                    {
                        ATCmdRsp IC;
                        try
                        {
                            IC = ATCommand("IC");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("IO Digital Change Detection. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("IO Digital Change Detection. " + ex.Message);
                        }
                        if (IC.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(IC.CmdData);
                        else
                            throw new Exception("IO Digital Change Detection. " + IC.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the digital IO pins to monitor for changes in the IO state.
                    /// IO changes are sent to the Destination Address.
                    /// </summary>
                    /// <param name="Mask">
                    /// Mask is a bitmask that can be used to enable or disable edge detection on individual channels.
                    /// Bit (IO pin): 
                    /// 0 (DIO0)    4 (DIO4)     8 (DIO8)
                    /// 1 (DIO1)    5 (DIO5)     9 (DIO9)
                    /// 2 (DIO2)    6 (DIO6)    10 (DIO10)
                    /// 3 (DIO3)    7 (DIO7)    11 (DIO11)
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool IODigitalChangeDetection(ushort Mask)
                    {
                        ATCmdRsp IC;
                        try
                        {
                            IC = ATCommand("IC", Mask);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("IO Digital Change Detection. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("IO Digital Change Detection. " + ex.Message);
                        }
                        if (IC.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region PWM0 Configuration ZB / ZNET

                    /// <summary>Gets options for the PWM0 of the XBee.</summary>
                    /// <returns>Returns the options for the PWM0.</returns>
                    public PWM0 PWM0Configuration()
                    {
                        ATCmdRsp P0;
                        try
                        {
                            P0 = ATCommand("P0");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("PWM0 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("PWM0 Configuration. " + ex.Message);
                        }
                        if (P0.CmdStatus == Status_Rsp.Ok)
                            return (PWM0)P0.CmdData[P0.CmdData.Length - 1];
                        else
                            throw new Exception("PWM0 Configuration. " + P0.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the PWM0 of the XBee.</summary>
                    /// <param name="Options">Options for the PWM0.</param>
                    /// <returns>True if successful.</returns>
                    public bool PWM0Configuration(PWM0 Options)
                    {
                        if (!Enum.IsDefined(typeof(PWM0), Options))
                            throw new ArgumentOutOfRangeException("Options", "PWM0 Configuration. Options is out of legal range.");
                        ATCmdRsp P0;
                        try
                        {
                            P0 = ATCommand("P0", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("PWM0 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("PWM0 Configuration. " + ex.Message);
                        }
                        if (P0.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region AD0/DIO0 Configuration ZB / ZNET

                    /// <summary>Gets options for the AD0/DIO0 of the XBee.</summary>
                    /// <returns>Returns the options for the AD0/DIO0.</returns>
                    public AD0DIO0 AD0DIO0Configuration()
                    {
                        ATCmdRsp D0;
                        try
                        {
                            D0 = ATCommand("D0");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("AD0/DIO0 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("AD0/DIO0 Configuration. " + ex.Message);
                        }
                        if (D0.CmdStatus == Status_Rsp.Ok)
                            return (AD0DIO0)D0.CmdData[D0.CmdData.Length - 1];
                        else
                            throw new Exception("AD0/DIO0 Configuration. " + D0.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the AD0/DIO0 of the XBee.</summary>
                    /// <param name="Options">Options for the AD0/DIO0.</param>
                    /// <returns>True if successful.</returns>
                    public bool AD0DIO0Configuration(AD0DIO0 Options)
                    {
                        if (!Enum.IsDefined(typeof(AD0DIO0), Options))
                            throw new ArgumentOutOfRangeException("Options", "AD0/DIO0 Configuration. Options is out of legal range.");
                        ATCmdRsp D0;
                        try
                        {
                            D0 = ATCommand("D0", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("AD0/DIO0 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("AD0/DIO0 Configuration. " + ex.Message);
                        }
                        if (D0.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region AD1/DIO1 Configuration ZB / ZNET

                    /// <summary>Gets options for the AD1/DIO1 of the XBee.</summary>
                    /// <returns>Returns the options for the AD1/DIO1.</returns>
                    public AD1DIO1 AD1DIO1Configuration()
                    {
                        ATCmdRsp D1;
                        try
                        {
                            D1 = ATCommand("D1");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("AD1/DIO1 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("AD1/DIO1 Configuration. " + ex.Message);
                        }
                        if (D1.CmdStatus == Status_Rsp.Ok)
                            return (AD1DIO1)D1.CmdData[D1.CmdData.Length - 1];
                        else
                            throw new Exception("AD1/DIO1 Configuration. " + D1.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the AD1/DIO1 of the XBee.</summary>
                    /// <param name="Options">Options for the AD1/DIO1.</param>
                    /// <returns>True if successful.</returns>
                    public bool AD1DIO1Configuration(AD1DIO1 Options)
                    {
                        if (!Enum.IsDefined(typeof(AD1DIO1), Options))
                            throw new ArgumentOutOfRangeException("Options", "AD1/DIO1 Configuration. Options is out of legal range.");
                        ATCmdRsp D1;
                        try
                        {
                            D1 = ATCommand("D1", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("AD1/DIO1 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("AD1/DIO1 Configuration. " + ex.Message);
                        }
                        if (D1.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region AD2/DIO2 Configuration ZB / ZNET

                    /// <summary>Gets options for the AD2/DIO2 of the XBee.</summary>
                    /// <returns>Returns the options for the AD2/DIO2.</returns>
                    public AD2DIO2 AD2DIO2Configuration()
                    {
                        ATCmdRsp D2;
                        try
                        {
                            D2 = ATCommand("D2");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("AD2/DIO2 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("AD2/DIO2 Configuration. " + ex.Message);
                        }
                        if (D2.CmdStatus == Status_Rsp.Ok)
                            return (AD2DIO2)D2.CmdData[D2.CmdData.Length - 1];
                        else
                            throw new Exception("AD2/DIO2 Configuration. " + D2.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the AD2/DIO2 of the XBee.</summary>
                    /// <param name="Options">Options for the AD2/DIO2.</param>
                    /// <returns>True if successful.</returns>
                    public bool AD2DIO2Configuration(AD2DIO2 Options)
                    {
                        if (!Enum.IsDefined(typeof(AD2DIO2), Options))
                            throw new ArgumentOutOfRangeException("Options", "AD2/DIO2 Configuration. Options is out of legal range.");
                        ATCmdRsp D2;
                        try
                        {
                            D2 = ATCommand("D2", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("AD2/DIO2 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("AD2/DIO2 Configuration. " + ex.Message);
                        }
                        if (D2.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region AD3/DIO3 Configuration ZB / ZNET

                    /// <summary>Gets options for the AD3/DIO3 of the XBee.</summary>
                    /// <returns>Returns the options for the AD3/DIO3.</returns>
                    public AD3DIO3 AD3DIO3Configuration()
                    {
                        ATCmdRsp D3;
                        try
                        {
                            D3 = ATCommand("D3");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("AD3/DIO3 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("AD3/DIO3 Configuration. " + ex.Message);
                        }
                        if (D3.CmdStatus == Status_Rsp.Ok)
                            return (AD3DIO3)D3.CmdData[D3.CmdData.Length - 1];
                        else
                            throw new Exception("AD3/DIO3 Configuration. " + D3.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the AD3/DIO3 of the XBee.</summary>
                    /// <param name="Options">Options for the AD3/DIO3.</param>
                    /// <returns>True if successful.</returns>
                    public bool AD3DIO3Configuration(AD3DIO3 Options)
                    {
                        if (!Enum.IsDefined(typeof(AD3DIO3), Options))
                            throw new ArgumentOutOfRangeException("Options", "AD3/DIO3 Configuration. Options is out of legal range.");
                        ATCmdRsp D3;
                        try
                        {
                            D3 = ATCommand("D3", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("AD3/DIO3 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("AD3/DIO3 Configuration. " + ex.Message);
                        }
                        if (D3.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region DIO4 Configuration ZB / ZNET

                    /// <summary>Gets options for the DIO4 of the XBee.</summary>
                    /// <returns>Returns the options for the DIO4.</returns>
                    public DIO4 DIO4Configuration()
                    {
                        ATCmdRsp D4;
                        try
                        {
                            D4 = ATCommand("D4");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO4 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO4 Configuration. " + ex.Message);
                        }
                        if (D4.CmdStatus == Status_Rsp.Ok)
                            return (DIO4)D4.CmdData[D4.CmdData.Length - 1];
                        else
                            throw new Exception("DIO4 Configuration. " + D4.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the DIO4 of the XBee.</summary>
                    /// <param name="Options">Options for the DIO4.</param>
                    /// <returns>True if successful.</returns>
                    public bool DIO4Configuration(DIO4 Options)
                    {
                        if (!Enum.IsDefined(typeof(DIO4), Options))
                            throw new ArgumentOutOfRangeException("Options", "DIO4 Configuration. Options is out of legal range.");
                        ATCmdRsp D4;
                        try
                        {
                            D4 = ATCommand("D4", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO4 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO4 Configuration. " + ex.Message);
                        }
                        if (D4.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region DIO5 Configuration ZB / ZNET

                    /// <summary>Gets options for the DIO5 of the XBee.</summary>
                    /// <returns>Returns the options for the DIO5.</returns>
                    public DIO5 DIO5Configuration()
                    {
                        ATCmdRsp D5;
                        try
                        {
                            D5 = ATCommand("D5");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO5 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO5 Configuration. " + ex.Message);
                        }
                        if (D5.CmdStatus == Status_Rsp.Ok)
                            return (DIO5)D5.CmdData[D5.CmdData.Length - 1];
                        else
                            throw new Exception("DIO5 Configuration. " + D5.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the DIO5 of the XBee.</summary>
                    /// <param name="Options">Options for the DIO5.</param>
                    /// <returns>True if successful.</returns>
                    public bool DIO5Configuration(DIO5 Options)
                    {
                        if (!Enum.IsDefined(typeof(DIO5), Options))
                            throw new ArgumentOutOfRangeException("Options", "DIO5 Configuration. Options is out of legal range.");
                        ATCmdRsp D5;
                        try
                        {
                            D5 = ATCommand("D5", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO5 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO5 Configuration. " + ex.Message);
                        }
                        if (D5.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region DIO8 Configuration ZB / ZNET Not supported

                    /// <summary>Gets options for the DIO8 of the XBee.</summary>
                    /// <returns>Returns the options for the DIO8.</returns>
                    public DIO8 DIO8Configuration()
                    {
                        throw new Exception("DIO8 Configuration. This Command is not yet supported.");
                    }

                    /// <summary>Sets options for the DIO8 of the XBee.</summary>
                    /// <param name="Options">Options for the DIO8.</param>
                    /// <returns>True if successful.</returns>
                    public bool DIO8Configuration(DIO8 Options)
                    {
                        throw new Exception("DIO13 Configuration. This Command is not yet supported.");
                    }

                    #endregion

                    #region DIO11 Configuration ZB / ZNET

                    /// <summary>Gets options for the DIO11 line of the XBee.</summary>
                    /// <returns>Returns the options for the DIO11 line.</returns>
                    public DIO11 DIO11Configuration()
                    {
                        ATCmdRsp P1;
                        try
                        {
                            P1 = ATCommand("P1");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO11 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO11 Configuration. " + ex.Message);
                        }
                        if (P1.CmdStatus == Status_Rsp.Ok)
                            return (DIO11)P1.CmdData[P1.CmdData.Length - 1];
                        else
                            throw new Exception("DIO11 Configuration. " + P1.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the DIO11 line of the XBee.</summary>
                    /// <param name="Options">Options for the DIO11 line.</param>
                    /// <returns>True if successful.</returns>
                    public bool DIO11Configuration(DIO11 Options)
                    {
                        if (!Enum.IsDefined(typeof(DIO11), Options))
                            throw new ArgumentOutOfRangeException("Options", "DIO11 Configuration. Options is out of legal range.");
                        ATCmdRsp P1;
                        try
                        {
                            P1 = ATCommand("P1", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO11 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO11 Configuration. " + ex.Message);
                        }
                        if (P1.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region DIO12 Configuration ZB / ZNET

                    /// <summary>Gets options for the DIO12 line of the XBee.</summary>
                    /// <returns>Returns the options for the DIO12 line.</returns>
                    public DIO12 DIO12Configuration()
                    {
                        ATCmdRsp P2;
                        try
                        {
                            P2 = ATCommand("P2");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO12 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO12 Configuration. " + ex.Message);
                        }
                        if (P2.CmdStatus == Status_Rsp.Ok)
                            return (DIO12)P2.CmdData[P2.CmdData.Length - 1];
                        else
                            throw new Exception("DIO12 Configuration. " + P2.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for the DIO12 line of the XBee.</summary>
                    /// <param name="Options">Options for the DIO12 line.</param>
                    /// <returns>True if successful.</returns>
                    public bool DIO12Configuration(DIO12 Options)
                    {
                        if (!Enum.IsDefined(typeof(DIO12), Options))
                            throw new ArgumentOutOfRangeException("Options", "DIO12 Configuration. Options is out of legal range.");
                        ATCmdRsp P2;
                        try
                        {
                            P2 = ATCommand("P2", (byte)Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("DIO12 Configuration. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("DIO12 Configuration. " + ex.Message);
                        }
                        if (P2.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region DIO13 Configuration ZB / ZNET Not supported

                    /// <summary>Gets options for the DIO13 line of the RF module.</summary>
                    /// <returns>Returns the options for the DIO13 line.</returns>
                    public DIO13 DIO13Configuration()
                    {
                        throw new Exception("DIO13 Configuration. This Command is not yet supported.");
                    }

                    /// <summary>Sets options for the DIO13 line of the RF module.</summary>
                    /// <param name="Options">Options for the DIO13 line.</param>
                    /// <returns>True if successful.</returns>
                    public bool DIO13Configuration(DIO13 Options)
                    {
                        throw new Exception("DIO13 Configuration. This Command is not yet supported.");
                    }

                    #endregion

                    #region Associate LED Blink Time ZB / ZNET

                    /// <summary>
                    /// Gets the Associate LED blink time. If the Associate LED functionality 
                    /// is enabled, this value determines the on and off blink times for the 
                    /// LED when the module has joined a network. 
                    /// </summary>
                    /// <returns>Returns the Associate LED blink time in milliseconds.</returns>
                    public ushort AssociateLEDBlinkTime()
                    {
                        ATCmdRsp LT;
                        try
                        {
                            LT = ATCommand("LT");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Associate LED Blink Time. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Associate LED Blink Time. " + ex.Message);
                        }
                        if (LT.CmdStatus == Status_Rsp.Ok)
                            return (ushort)(LT.CmdData[0] * 10);
                        else
                            throw new Exception("Associate LED Blink Time. " + LT.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the Associate LED blink time. If the Associate LED functionality 
                    /// is enabled, this value determines the on and off blink times for the 
                    /// LED when the module has joined a network. 
                    /// </summary>
                    /// <param name="BlinkTime">
                    /// Associate LED blink time in milliseconds. If Blink Time = 0, the default 
                    /// blink rate will be used (500 ms coordinator, 250 ms router/end device).
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool AssociateLEDBlinkTime(ushort BlinkTime)
                    {
                        if (BlinkTime !=0x00 && (BlinkTime < 200 || BlinkTime > 2550))
                            throw new ArgumentOutOfRangeException("BlinkTime", "Associate LED Blink Time. BlinkTime range is 200-2550 ms.");
                        ATCmdRsp LT;
                        try
                        {
                            LT = ATCommand("LT", (byte)Math.Ceiling((double)BlinkTime / 10));
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Associate LED Blink Time. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Associate LED Blink Time. " + ex.Message);
                        }
                        if (LT.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Pull-up Resistor ZB / ZNET

                    /// <summary>
                    /// Gets the bit field that configures the internal pull-up resistor 
                    /// status for the I/O lines (30k pull-up resistors).
                    /// </summary>
                    /// <returns>Returns the internal pull-up resistor status.</returns>
                    public ushort PullUpResistor()
                    {
                        ATCmdRsp PR;
                        try
                        {
                            PR = ATCommand("PR");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Pull-up Resistor. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Pull-up Resistor. " + ex.Message);
                        }
                        if (PR.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(PR.CmdData);
                        else
                            throw new Exception("Pull-up Resistor. " + PR.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the bit field that configures the internal pull-up resistor 
                    /// status for the I/O lines (30k pull-up resistors).
                    /// </summary>
                    /// <param name="Status">
                    /// "1" specifies the pull-up resistor is enabled. 
                    /// "0" specifies no pull-up.
                    /// 0 - DIO4 (Pin 11)
                    /// 1 - AD3/DIO3 (Pin 17)
                    /// 2 - AD2/DIO2 (Pin 18)
                    /// 3 - AD1/DIO1 (Pin 19)
                    /// 4 - AD0/DIO0 (Pin 20)
                    /// 5 - RTS/DIO6 (Pin 16)
                    /// 6 - DTR/Sleep Request/DIO8 (Pin 9)
                    /// 7 - DIN/Config (Pin 3)
                    /// 8 - Associate/DIO5 (Pin 15)
                    /// 9 - On/Sleep/DIO9 (Pin 13)
                    /// 10 - DIO12 (Pin 4)
                    /// 11 - PWM0/RSSI/DIO10 (Pin 6)
                    /// 12 - PWM1/DIO11 (Pin 7)
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool PullUpResistor(ushort Status)
                    {
                        if (Status > 0x1FFF)
                            throw new ArgumentOutOfRangeException("Status", "Pull-up Resistor. Status range is 0x0000-0x1FFF.");
                        ATCmdRsp PR;
                        try
                        {
                            PR = ATCommand("PR", Status);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Pull-up Resistor. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Pull-up Resistor. " + ex.Message);
                        }
                        if (PR.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region RSSI PWM Timer ZB / ZNET

                    /// <summary>
                    /// Gets the time RSSI signal will be output after last transmission.
                    /// If Time = 25500, output will always be on.
                    /// </summary>
                    /// <returns>Returns the Time in milliseconds.</returns>
                    public ushort RSSIPWMTimer()
                    {
                        ATCmdRsp RP;
                        try
                        {
                            RP = ATCommand("RP");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("RSSI PWM Timer. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("RSSI PWM Timer. " + ex.Message);
                        }
                        if (RP.CmdStatus == Status_Rsp.Ok)
                            return (ushort)(RP.CmdData[0] * 100);
                        else
                            throw new Exception("RSSI PWM Timer. " + RP.CmdStatus.ToString());
                    }

                    /// <summary>Sets the time RSSI signal will be output after last transmission.</summary>
                    /// <param name="Time">Time in milliseconds. When Time = 25500, output will always be on.</param>
                    /// <returns>True if successful.</returns>
                    public bool RSSIPWMTimer(ushort Time)
                    {
                        if (Time > 25500)
                            throw new ArgumentOutOfRangeException("Timeout", "RSSI PWM Timer. Timeout range is 0-25500 ms.");
                        ATCmdRsp RP;
                        try
                        {
                            RP = ATCommand("RP", (byte)Math.Ceiling((double)Time / 100));
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("RSSI PWM Timer. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("RSSI PWM Timer. " + ex.Message);
                        }
                        if (RP.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Commissioning Pushbutton ZB / ZNET

                    /// <summary>
                    /// This command can be used to simulate commissioning button presses in software. 
                    /// </summary>
                    /// <param name="Button">
                    /// Button should be set to the number of button presses to be simulated. 
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool CommissioningPushbutton(byte Button)
                    {
                        if (Button != 0x01 && Button != 0x02 && Button != 0x04)
                            throw new ArgumentOutOfRangeException("Button", "Commissioning Pushbutton. Button is out of legal range.");
                        ATCmdRsp CB;
                        try
                        {
                            CB = ATCommand("CB", Button);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Commissioning Pushbutton. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Commissioning Pushbutton. " + ex.Message);
                        }
                        if (CB.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Supply Voltage ZB / ZNET

                    /// <summary>Reads the voltage on the Vcc pin. </summary>
                    /// <returns>Returns voltage in mV.</returns>
                    public ushort SupplyVoltage()
                    {
                        ATCmdRsp V;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    foreach (Node_Information Node in Network)
                                    {
                                        if (InternalDestIEEEAddr == Node.IEEEAddress)
                                        {
                                            if (Node.NodeType != Node_Type.End_Device)
                                                throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    if (Host.NodeType != Node_Type.End_Device)
                                        throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                }
                            }
                            V = ATCommand("%V");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Supply Voltage. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Supply Voltage. " + ex.Message);
                        }
                        if (V.CmdStatus == Status_Rsp.Ok)
                            return (ushort)(((float)(Aux.ByteToUshort(V.CmdData) / 1023.0)) * 1200);
                        else
                            throw new Exception("Supply Voltage. " + V.CmdStatus.ToString());
                    }

                    #endregion

                    #region Supply Voltage Threshold ZB / ZNET

                    /// <summary>
                    /// Gets the voltage supply threshold, if the meassured 
                    /// supply voltage falls bellow or equal to this threshold, the 
                    /// supply voltage will be included in the IO sample set. 
                    /// </summary>
                    /// <returns>Voltage supply threshold in mV.</returns>
                    public ushort SupplyVoltageThreshold()
                    {
                        ATCmdRsp V;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    foreach (Node_Information Node in Network)
                                    {
                                        if (InternalDestIEEEAddr == Node.IEEEAddress)
                                        {
                                            if (Node.NodeType != Node_Type.End_Device)
                                                throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    if (Host.NodeType != Node_Type.End_Device)
                                        throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                }
                            }
                            V = ATCommand("V+");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Supply Voltage Threshold. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Supply Voltage Threshold. " + ex.Message);
                        }
                        if (V.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(V.CmdData);
                        else
                            throw new Exception("Supply Voltage Threshold. " + V.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the voltage supply threshold, if the meassured 
                    /// supply voltage falls bellow or equal to this threshold, the 
                    /// supply voltage will be included in the IO sample set. 
                    /// </summary>
                    /// <param name="Threshold">Voltage supply threshold in mV.</param>
                    /// <returns>True if successful.</returns>
                    public bool SupplyVoltageThreshold(ushort Threshold)
                    {
                        ATCmdRsp V;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    foreach (Node_Information Node in Network)
                                    {
                                        if (InternalDestIEEEAddr == Node.IEEEAddress)
                                        {
                                            if (Node.NodeType != Node_Type.End_Device)
                                                throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    if (Host.NodeType != Node_Type.End_Device)
                                        throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                }
                            }
                            V = ATCommand("V+", Threshold);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Supply Voltage Threshold. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Supply Voltage Threshold. " + ex.Message);
                        }
                        if (V.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                #endregion

                #region Diagnostics Commands

                    #region Firmware Version ZB / ZNET

                    /// <summary>Gets firmware version of the XBee.</summary>
                    /// <returns>Returns firmware version</returns>
                    public ushort FirmwareVersion()
                    {
                        ATCmdRsp VR;
                        try
                        {
                            VR = ATCommand("VR");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Firmware Version. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Firmware Version. " + ex.Message);
                        }
                        if (VR.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(VR.CmdData);
                        else
                            throw new Exception("Firmware Version. " + VR.CmdStatus.ToString());
                    }

                    #endregion

                    #region Hardware Version ZB / ZNET

                    /// <summary>Gets hardware version of the XBee.</summary>
                    /// <returns>Returns hardware version</returns>
                    public ushort HardwareVersion()
                    {
                        ATCmdRsp HV;
                        try
                        {
                            HV = ATCommand("HV");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Hardware Version. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Hardware Version. " + ex.Message);
                        }
                        if (HV.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(HV.CmdData);
                        else
                            throw new Exception("Hardware Version. " + HV.CmdStatus.ToString());
                    }

                    #endregion

                #endregion

                #region AT Command Options Commands

                    #region Enter AT Command Mode ZB / ZNET

                    /// <summary>Enter into AT Command Mode.</summary>
                    /// <returns>True if successful.</returns>
                    private bool EnterCommandMode()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            Thread.Sleep(1000); // Guard Times
                            WaitXBeeRsp = true;    // Wait Response
                            DateTime TimeOut = DateTime.Now.AddMilliseconds(InternalTimeout); // Set Time Out
                            ATCommandMode = true;
                            XBeeSerialPortTx(Encoding.ASCII.GetBytes("+++"));   // Send Command Sequence Characters
                            Thread.Sleep(1000); // Guard Times
                            while (WaitXBeeRsp && (DateTime.Now < TimeOut))
                            {
                                Thread.Sleep(10);
                            }
                            if (!WaitXBeeRsp)
                            {
                                if (Equals(XBeeRspFrame, OK))
                                {
                                    InternalCommandModeTimeoutTimer.Change(Timeout.Infinite, InternalInterval); // Enable Command Mode Timeout timer
                                    return true;
                                }
                                else
                                {
                                    ATCommandMode = false;
                                    return false;
                                }
                            }
                            else
                                throw new TimeoutException("Enter Command Mode. The operation has timeout.");
                        }
                        else
                            throw new NotSupportedException("Enter Command Mode. This Command is only supported in Transparent API Mode.");
                    }

                    #endregion

                    #region Command Mode Timeout ZB / ZNET

                    /// <summary>
                    /// Gets the period of inactivity after which the RF module automatically 
                    /// exits AT Command Mode and returns to Idle Mode.
                    /// </summary>
                    /// <returns>Returns the number of character times.</returns>
                    public ushort CommandModeTimeout()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp CT;
                            try
                            {
                                CT = ATCommand("CT");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Command Mode Timeout. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Command Mode Timeout. " + ex.Message);
                            }
                            if (CT.CmdStatus == Status_Rsp.Ok)
                            {
                                return (ushort)(Aux.ByteToUshort(CT.CmdData) * 100);
                            }
                            else
                                throw new Exception("Command Mode Timeout. " + CT.CmdStatus.ToString());
                        }
                        else
                            throw new Exception("Command Mode Timeout. This Command is only supported in Transparent Operation Mode.");
                    }

                    /// <summary>
                    /// Sets the period of inactivity after which the RF module automatically 
                    /// exits AT Command Mode and returns to Idle Mode.
                    /// </summary>
                    /// <param name="Timeout">Number of character times.</param>
                    /// <returns>True if successful.</returns>
                    public bool CommandModeTimeout(ushort Timeout)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            if (Timeout < 200 || Timeout > 65500)
                                throw new ArgumentOutOfRangeException("Timeout", "Command Mode Timeout. Timeout range is 200-65500 ms.");
                            ATCmdRsp CT;
                            try
                            {
                                CT = ATCommand("CT", (ushort)(Timeout / 100));
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Command Mode Timeout. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Command Mode Timeout. " + ex.Message);
                            }
                            if (CT.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalInterval = Timeout - 500;
                                InternalCommandModeTimeoutTimer.Change(System.Threading.Timeout.Infinite, InternalInterval);
                                return true;
                            }
                            else
                                return false;
                        }
                        else
                            throw new Exception("Command Mode Timeout. This Command is only supported in Tranparent Operation Mode.");
                    }

                    #endregion

                    #region Exit Command Mode ZB / ZNET

                    /// <summary>Explicitly exit the module from AT Command Mode.</summary>
                    /// <returns>True if successful.</returns>
                    private bool ExitCommandMode()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp CN;
                            try
                            {
                                CN = ATCommand("CN");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Exit Command Mode. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Exit Command Mode. " + ex.Message);
                            }
                            if (CN.CmdStatus == Status_Rsp.Ok)
                            {
                                InternalCommandModeTimeoutTimer.Change(Timeout.Infinite,Timeout.Infinite);  // Disable Command Mode Timeout timer
                                ATCommandMode = false;
                                return true;
                            }
                            else
                                return false;
                        }
                        else
                            throw new NotSupportedException("Exit Command Mode. This Command is only supported in Transparent Operation Mode.");
                    }

                    #endregion

                    #region Guard Times ZB / ZNET

                    /// <summary>
                    /// Gets required period of silence before and after enter the module into AT Command Mode. 
                    /// The period of silence is used to prevent inadvertent entrance into AT Command Mode.
                    /// </summary>
                    /// <returns>Returns the guard time in milliseconds.</returns>
                    private ushort GuardTimes()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp GT;
                            try
                            {
                                GT = ATCommand("GT");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Guard Times. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Guard Times. " + ex.Message);
                            }
                            if (GT.CmdStatus == Status_Rsp.Ok)
                            {
                                return (ushort)(Aux.ByteToUshort(GT.CmdData));
                            }
                            else
                                throw new Exception("Guard Times. " + GT.CmdStatus.ToString());
                        }
                        else
                            throw new Exception("Guard Times. This Command is only supported in Transparent Operation Mode.");
                    }

                    /// <summary>
                    /// Sets required period of silence before and after enter the module into AT Command Mode. 
                    /// The period of silence is used to prevent inadvertent entrance into AT Command Mode.
                    /// </summary>
                    /// <param name="Time">Guard time in milliseconds.</param>
                    /// <returns>True if successful.</returns>
                    private bool GuardTimes(ushort Time)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            if (Time < 1 || Time > 3300)
                                throw new ArgumentOutOfRangeException("Time", "Guard Times. Time range is 1-3300 ms.");
                            ATCmdRsp GT;
                            try
                            {
                                GT = ATCommand("GT", Time);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Guard Times. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Guard Times. " + ex.Message);
                            }
                            if (GT.CmdStatus == Status_Rsp.Ok)
                                return true;
                            else
                                return false;
                        }
                        else
                            throw new Exception("Guard Times. This Command is only supported in Transparent Operation Mode.");
                    }

                    #endregion

                    #region Command Sequence Character ZB / ZNET

                    /// <summary>
                    /// Gets the ASCII character value to be used to enter the module into AT Command Mode. 
                    /// </summary>
                    /// <returns>Returns ASCII character.</returns>
                    private char CommandSequenceCharacter()
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp CC;
                            try
                            {
                                CC = ATCommand("CC");
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Command Sequence Character. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Command Sequence Character. " + ex.Message);
                            }
                            if (CC.CmdStatus == Status_Rsp.Ok)
                            {
                                return (char)(CC.CmdData[0]);
                            }
                            else
                                throw new Exception("Command Sequence Character. " + CC.CmdStatus.ToString());
                        }
                        else
                            throw new Exception("Command Sequence Character. This Command is only supported in Transparent Operation Mode.");
                    }

                    /// <summary>
                    /// Sets the ASCII character value to be used to enter the module into AT Command Mode. 
                    /// </summary>
                    /// <param name="Character">ASCII character.</param>
                    /// <returns>True if successful.</returns>
                    private bool CommandSequenceCharacter(char Character)
                    {
                        if (OperationMode == OperationModeEnum.Transparent)
                        {
                            ATCmdRsp CC;
                            try
                            {
                                CC = ATCommand("CC", Character);
                            }
                            catch (TimeoutException ex)
                            {
                                throw new TimeoutException("Command Sequence Character. " + ex.Message);
                            }
                            catch (Exception ex)
                            {
                                throw new Exception("Command Sequence Character. " + ex.Message);
                            }
                            if (CC.CmdStatus == Status_Rsp.Ok)
                                return true;
                            else
                                return false;
                        }
                        else
                            throw new Exception("Command Sequence Character. This Command is only supported in Transparent Operation Mode.");
                    }

                    #endregion

                #endregion

                #region Sleep Commands

                    #region Sleep Mode ZB / ZNET

                    /// <summary>Gets the sleep mode on the XBee.</summary>
                    /// <returns>Returns the sleep mode.</returns>
                    public Sleep_Mode SleepMode()
                    {
                        ATCmdRsp SM;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType == Node_Type.Coordinator)
                                            throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.Coordinator)
                                    throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                            }
                            SM = ATCommand("SM");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Sleep Mode. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Sleep Mode. " + ex.Message);
                        }
                        if (SM.CmdStatus == Status_Rsp.Ok)
                            return (Sleep_Mode)SM.CmdData[SM.CmdData.Length - 1];
                        else
                            throw new Exception("Sleep Mode. " + SM.CmdStatus.ToString());
                    }

                    /// <summary>Sets the sleep mode on the XBee.</summary>
                    /// <param name="Mode">Sleep mode.</param>
                    /// <returns>True if successful.</returns>
                    public bool SleepMode(Sleep_Mode Mode)
                    {
                        if (!Enum.IsDefined(typeof(Sleep_Mode), Mode))
                            throw new ArgumentOutOfRangeException("Mode", "Sleep Mode. Mode is out of legal range.");
                        ATCmdRsp SM;
                        int Node = 0;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                for (int i = 0; i < Network.Length; i++)
                                {
                                    if (InternalDestIEEEAddr == Network[i].IEEEAddress)
                                    {
                                        Node = i;
                                        if (Network[i].NodeType == Node_Type.Coordinator)
                                            throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType == Node_Type.Coordinator)
                                    throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                            }
                            SM = ATCommand("SM", (byte)Mode);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Sleep Mode. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Sleep Mode. " + ex.Message);
                        }
                        if (SM.CmdStatus == Status_Rsp.Ok)
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                if (Mode == Sleep_Mode.Disabled)
                                    Network[Node].NodeType = Node_Type.Router;
                                else
                                    Network[Node].NodeType = Node_Type.End_Device;
                            }
                            else
                            {
                                if (Mode == Sleep_Mode.Disabled)
                                    Host.NodeType = Node_Type.Router;
                                else
                                    Host.NodeType = Node_Type.End_Device;
                            }
                            return true;
                        }
                        else
                            return false;
                    }

                    #endregion

                    #region Number of Sleep Periods ZB / ZNET

                    /// <summary>
                    /// Gets the number of sleep periods to not assert the On/Sleep
                    /// pin on wakeup if no RF data is waiting for the end device.
                    /// </summary>
                    /// <returns>Returns the number of sleep periods.</returns>
                    public ushort NumberSleepPeriods()
                    {
                        ATCmdRsp SN;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Host.Firmware == Firmware.ZNet)
                                        {
                                            if (Node.NodeType == Node_Type.Coordinator)
                                                throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Firmware == Firmware.ZNet)
                                {
                                    if (Host.NodeType == Node_Type.Coordinator)
                                        throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                }
                            }
                            SN = ATCommand("SN");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Number Sleep Periods. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Number Sleep Periods. " + ex.Message);
                        }
                        if (SN.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(SN.CmdData);
                        else
                            throw new Exception("Number Sleep Periods. " + SN.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the number of sleep periods to not assert the On/Sleep
                    /// pin on wakeup if no RF data is waiting for the end device.
                    /// </summary>
                    /// <param name="Periods">Number of sleep periods.</param>
                    /// <returns>True if successful.</returns>
                    public bool NumberSleepPeriods(ushort Periods)
                    {
                        if (Periods < 0x0001)
                            throw new ArgumentOutOfRangeException("Periods", "Number Sleep Periods. Periods range is 0x0001-0xFFFF.");
                        ATCmdRsp SN;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Host.Firmware == Firmware.ZNet)
                                        {
                                            if (Node.NodeType == Node_Type.Coordinator)
                                                throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Firmware == Firmware.ZNet)
                                {
                                    if (Host.NodeType == Node_Type.Coordinator)
                                        throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                }
                            }
                            SN = ATCommand("SN", Periods);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Number Sleep Periods. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Number Sleep Periods. " + ex.Message);
                        }
                        if (SN.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Sleep Period ZB / ZNET

                    /// <summary>
                    /// Gets the value that determines how long the end device will sleep at a time.
                    /// </summary>
                    /// <returns>Returns the period time in milliseconds.</returns>
                    public ushort SleepPeriod()
                    {
                        ATCmdRsp SP;
                        try
                        {
                            SP = ATCommand("SP");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Sleep Period. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Sleep Period. " + ex.Message);
                        }
                        if (SP.CmdStatus == Status_Rsp.Ok)
                            return (ushort)(Aux.ByteToUshort(SP.CmdData) * 10);
                        else
                            throw new Exception("Sleep Period. " + SP.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the value that determines how long the end device will sleep at a time.
                    /// On the parent, this value determines how long the parent will buffer a message 
                    /// for the sleeping end device. It should be set at least equal to the longest SP
                    /// time of any child end device.
                    /// </summary>
                    /// <param name="Time">Period time in milliseconds.</param>
                    /// <returns>True if successful.</returns>
                    public bool SleepPeriod(ushort Time)
                    {
                        if (Time < 320 || Time > 28000)
                            throw new ArgumentOutOfRangeException("Time", "Sleep Period. Time range is 320-28000 ms.");
                        ATCmdRsp SP;
                        try
                        {
                            SP = ATCommand("SP", (ushort)Math.Ceiling((double)Time / 10));
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Sleep Period. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Sleep Period. " + ex.Message);
                        }
                        if (SP.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Time Before Sleep ZB / ZNET

                    /// <summary>Gets the time before sleep timer on an end device.</summary>
                    /// <returns>Returns the time before sleep in milliseconds.</returns>
                    public ushort TimeBeforeSleep()
                    {
                        ATCmdRsp ST;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Host.Firmware == Firmware.ZB)
                                        {
                                            if (Node.NodeType != Node_Type.End_Device)
                                                throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                            break;
                                        }
                                        else
                                        {
                                            if (Node.NodeType == Node_Type.Coordinator)
                                                throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    if (Host.NodeType != Node_Type.End_Device)
                                        throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                }
                                else
                                {
                                    if (Host.NodeType == Node_Type.Coordinator)
                                        throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                }
                            }
                            ST = ATCommand("ST");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Time Before Sleep. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Time Before Sleep. " + ex.Message);
                        }
                        if (ST.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(ST.CmdData);
                        else
                            throw new Exception("Time Before Sleep. " + ST.CmdStatus.ToString());
                    }
                    /// <summary>
                    /// Sets the time before sleep timer on an end device. Once the timer expires, an end 
                    /// device may enter low power operation. Applicable for cyclic sleep end devices only.
                    /// </summary>
                    /// <param name="Time">Time before sleep in milliseconds.</param>
                    /// <returns>True if successful.</returns>
                    public bool TimeBeforeSleep(ushort Time)
                    {
                        if (Time < 0x0001 || Time > 0xFFFE)
                            throw new ArgumentOutOfRangeException("Time", "Time Before Sleep. Time range is 1-65534 ms.");
                        ATCmdRsp ST;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Host.Firmware == Firmware.ZB)
                                        {
                                            if (Node.NodeType != Node_Type.End_Device)
                                                throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                            break;
                                        }
                                        else
                                        {
                                            if (Node.NodeType == Node_Type.Coordinator)
                                                throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    if (Host.NodeType != Node_Type.End_Device)
                                        throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                }
                                else
                                {
                                    if (Host.NodeType == Node_Type.Coordinator)
                                        throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                }
                            }
                            ST = ATCommand("ST", Time);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Time Before Sleep. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Time Before Sleep. " + ex.Message);
                        }
                        if (ST.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Sleep Options ZB / ZNET

                    /// <summary>Gets options for sleep.</summary>
                    /// <returns>Returns the options for sleep.</returns>
                    public byte SleepOptions()
                    {
                        ATCmdRsp SO;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Host.Firmware == Firmware.ZB)
                                        {
                                            if (Node.NodeType != Node_Type.End_Device)
                                                throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                            break;
                                        }
                                        else
                                        {
                                            if (Node.NodeType == Node_Type.Coordinator)
                                                throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    if (Host.NodeType != Node_Type.End_Device)
                                        throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                }
                                else
                                {
                                    if (Host.NodeType == Node_Type.Coordinator)
                                        throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                }
                            }
                            SO = ATCommand("SO");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Sleep Options. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Sleep Options. " + ex.Message);
                        }
                        if (SO.CmdStatus == Status_Rsp.Ok)
                            return SO.CmdData[0];
                        else
                            throw new Exception("Sleep Options. " + SO.CmdStatus.ToString());
                    }

                    /// <summary>Sets options for sleep.</summary>
                    /// <param name="Options">
                    /// 0x02 = Always wake for Time Before Sleep time
                    /// 0x04 = Sleep entire Number of Sleep Periods * Sleep Periods time
                    /// </param>
                    /// <returns>True if successful.</returns>
                    public bool SleepOptions(byte Options)
                    {
                        if (Options != 0x00 && Options != 0x02 && Options != 0x04 && Options != 0x06)
                            throw new ArgumentOutOfRangeException("Options", "Sleep Options. Options value was out of legal range.");
                        ATCmdRsp SO;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Host.Firmware == Firmware.ZB)
                                        {
                                            if (Node.NodeType != Node_Type.End_Device)
                                                throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                            break;
                                        }
                                        else
                                        {
                                            if (Node.NodeType == Node_Type.Coordinator)
                                                throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                            break;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (Host.Firmware == Firmware.ZB)
                                {
                                    if (Host.NodeType != Node_Type.End_Device)
                                        throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                }
                                else
                                {
                                    if (Host.NodeType == Node_Type.Coordinator)
                                        throw new InvalidOperationException("Invalid Device Type, it must be a Router or an End Device.");
                                }
                            }
                            SO = ATCommand("SO", Options);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Sleep Options. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Sleep Options. " + ex.Message);
                        }
                        if (SO.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Wake Host ZB

                    /// <summary>Gets the wake host timer value.</summary>
                    /// <returns>Returns the wake host timer in milliseconds.</returns>
                    public ushort WakeHost()
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Wake Host. This Command is not supported in devices running ZNET firmware.");
                        ATCmdRsp WH;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType != Node_Type.End_Device)
                                            throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType != Node_Type.End_Device)
                                    throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                            }
                            WH = ATCommand("WH");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Wake Host. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Wake Host. " + ex.Message);
                        }
                        if (WH.CmdStatus == Status_Rsp.Ok)
                            return Aux.ByteToUshort(WH.CmdData);
                        else
                            throw new Exception("Wake Host. " + WH.CmdStatus.ToString());
                    }

                    /// <summary>
                    /// Sets the wake host timer value. If the wake host timer is set to a nonzero value, 
                    /// this timer specifies a time in millisecond that the device should allow after waking 
                    /// from sleep before sending data out the UART or transmitting an IO sample.
                    /// If serial characters are received, the wake host timer is stopped immediately.
                    /// </summary>
                    /// <param name="Time">Wake host timer in milliseconds.</param>
                    /// <returns>True if successful.</returns>
                    public bool WakeHost(ushort Time)
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Wake Host. This Command is not supported in devices running ZNET firmware.");
                        ATCmdRsp WH;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType != Node_Type.End_Device)
                                            throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType != Node_Type.End_Device)
                                    throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                            }
                            WH = ATCommand("WH", Time);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Wake Host. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Wake Host. " + ex.Message);
                        }
                        if (WH.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                    #region Sleep Immediately ZB

                    /// <summary>
                    /// Cause a cyclic sleep module to sleep immediately rather than wait
                    /// for the ST timer to expire.
                    /// </summary>
                    /// <returns>True if successful.</returns>
                    public bool SleepImmediately()
                    {
                        if (Host.Firmware == Firmware.ZNet)
                            throw new Exception("Sleep Immediately. This Command is not supported in devices running ZNET firmware.");
                        ATCmdRsp SI;
                        try
                        {
                            if (InternalRemoteATCmdEnable)
                            {
                                foreach (Node_Information Node in Network)
                                {
                                    if (InternalDestIEEEAddr == Node.IEEEAddress)
                                    {
                                        if (Node.NodeType != Node_Type.End_Device)
                                            throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (Host.NodeType != Node_Type.End_Device)
                                    throw new InvalidOperationException("Invalid Device Type, it must be an End Device.");
                            }
                            SI = ATCommand("SI");
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Sleep Immediately. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Sleep Immediately. " + ex.Message);
                        }
                        if (SI.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                #endregion

                #region Bootloader Commands

                    #region Invoke Bootloader ZB

                    /// <summary>Invoke the bootloader.</summary>
                    /// <returns>True if successful.</returns>
                    private bool InvokeBootloader()
                    {
                        ATCmdRsp P;
                        try
                        {
                            P = ATCommand("%P", 0x5441);
                        }
                        catch (TimeoutException ex)
                        {
                            throw new TimeoutException("Invoke Bootloader. " + ex.Message);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Invoke Bootloader. " + ex.Message);
                        }
                        if (P.CmdStatus == Status_Rsp.Ok)
                            return true;
                        else
                            return false;
                    }

                    #endregion

                #endregion

            #endregion

            #region Transmit

            /// <summary>Send data as an RF packet to the specified destination.</summary>
            /// <param name="RFData">Data that is sent to the destination device.</param>
            /// <returns>ZigBee Transmit Status.</returns>
            public TxStatus Transmit(byte[] RFData)
            {
                TxStatus InternalTxStatus = new TxStatus();
                if (OperationMode == OperationModeEnum.Transparent)
                {
                    if (ATCommandMode)
                        ExitCommandMode();
                    XBeeSerialPortTx(RFData);
                    Thread.Sleep(200);
                    return InternalTxStatus;
                }
                else
                {
                    if (RFData.Length <= 72)
                    {
                        if (InternalExplicitAddressingEnable)
                            return APIExplicitTransmit(RFData);
                        else
                            return APITransmit(RFData);
                    }
                    int n;
                    byte[] Packet = new byte[72];
                    n = (int)(Math.Ceiling((double)(RFData.Length / 72)));
                    for (int i = 0; i < n; i++)
                    {
                        Array.Copy(RFData, i * 72, Packet, 0, 72);
                        if (InternalExplicitAddressingEnable)
                            InternalTxStatus = APIExplicitTransmit(Packet);
                        else
                            InternalTxStatus = APITransmit(Packet);
                    }
                    if (RFData.Length > n * 72)
                    {
                        Packet = new byte[RFData.Length - n * 72];
                        Array.Copy(RFData, n * 72, Packet, 0, Packet.Length);
                        if (InternalExplicitAddressingEnable)
                            InternalTxStatus = APIExplicitTransmit(Packet);
                        else
                            InternalTxStatus = APITransmit(Packet);
                    }
                    return InternalTxStatus;
                }
            }

            /// <summary>Send data as an RF packet to the specified destination.</summary>
            /// <param name="RFData">Data that is sent to the destination device.</param>
            /// <returns>ZigBee Transmit Status.</returns>
            public TxStatus Transmit(string RFData)
            {
                return Transmit(Encoding.ASCII.GetBytes(RFData));
            }

            /// <summary>Send data as an RF packet to the specified destination.</summary>
            /// <param name="RFData">Data that is sent to the destination device.</param>
            /// <returns>ZigBee Transmit Status.</returns>
            public TxStatus Transmit(byte RFData)
            {
                byte[] RFDataByte = { RFData };
                return Transmit(RFDataByte);
            }

            /// <summary>Send data as an RF packet to the specified destination.</summary>
            /// <param name="RFData">Data that is sent to the destination device.</param>
            /// <returns>ZigBee Transmit Status.</returns>
            public TxStatus Transmit(ushort RFData)
            {
                return Transmit(Aux.UshortToByte(RFData));
            }

            /// <summary>Send data as an RF packet to the specified destination.</summary>
            /// <param name="RFData">Data that is sent to the destination device.</param>
            /// <returns>ZigBee Transmit Status.</returns>
            public TxStatus Transmit(ulong RFData)
            {
                return Transmit(Aux.UlongToByte(RFData));
            }

            #endregion

            #region Equals

            /// <summary>Determines whether two Array instances are equal.</summary>
            /// <param name="Array1">Array 1.</param>
            /// <param name="Array2">Array 2.</param>
            /// <returns>True if successful.</returns>
            private bool Equals(byte[] Array1, byte[] Array2)
            {
                if (Array1 == null || Array2 == null || Array1.Length != Array2.Length)
                    return false;
                for (int i = 0; i < Array1.Length; i++)
                {
                    if (!Array1[i].Equals(Array2[i]))
                        return false;
                }
                return true;
            }

            #endregion

            #region ASCII To Byte

            /// <summary>Convert ASCII array to Byte array.</summary>
            /// <param name="Ascii">ASCII array.</param>
            /// <returns>Byte array.</returns>
            private byte[] AsciiToByte(byte[] Ascii)
            {
                byte[] ByteArray;
                if ((Ascii.Length - 1) % 2 == 1)
                    ByteArray = new byte[((Ascii.Length - 1) / 2) + 1];
                else
                    ByteArray = new byte[(Ascii.Length - 1) / 2];
                for (int i = (Ascii.Length - 2); i >= 0; i -= 2)
                {
                    if (i != 0)
                    {
                        if (Ascii[i - 1] > 0x40)
                            ByteArray[i / 2] = (byte)((Ascii[i - 1] - 0x37) << 4);
                        else
                            ByteArray[i / 2] = (byte)((Ascii[i - 1] - 0x30) << 4);
                        if (Ascii[i] > 0x40)
                            ByteArray[i / 2] += (byte)(Ascii[i] - 0x37);
                        else
                            ByteArray[i / 2] += (byte)(Ascii[i] - 0x30);
                    }
                    else
                    {
                        if (Ascii[i] > 0x40)
                            ByteArray[i / 2] += (byte)(Ascii[i] - 0x37);
                        else
                            ByteArray[i / 2] += (byte)(Ascii[i] - 0x30);
                    }
                }
                return ByteArray;
            }

            #endregion

        #endregion

        #region Events & Delegates

        public event ModemStatusEventHandler ModemStatus;
        public delegate void ModemStatusEventHandler(XBeeDeviceManager sender, Modem_Status Status);

        public event ReceivePacketEventHandler ReceivePacket;
        public delegate void ReceivePacketEventHandler(XBeeDeviceManager sender, ulong? IEEEAddress, ushort? NwkAddress, Rx_Options? Options, byte[] ReceivedData);

        public event ExplicitReceivePacketEventHandler ExplicitReceivePacket;
        public delegate void ExplicitReceivePacketEventHandler(XBeeDeviceManager sender, ulong IEEEAddress, ushort NwkAddress, byte SourceEndPoint, byte DestinationEndPoint, ushort ClusterID, ushort ProfileID, Rx_Options Options, byte[] ReceivedData);

        public event IOSampleEventHandler IOSample;
        public delegate void IOSampleEventHandler(XBeeDeviceManager sender, ulong IEEEAddress, ushort NwkAddress, Rx_Options Options, byte Samples, ushort? DigitalMask, byte? AnalogMask, ushort? DigitalSamples, ushort[] AnalogSamples);

        public event SensorSampleEventHandler SensorSample;
        public delegate void SensorSampleEventHandler(XBeeDeviceManager sender, ulong IEEEAddress, ushort NwkAddress, Rx_Options Options, Sensor_Type[] Sensors, ushort[] Samples, ushort? Temperature);

        public event NodeIdentificationEventHandler NodeIdentification;
        public delegate void NodeIdentificationEventHandler(XBeeDeviceManager sender, Rx_Options Options, ulong IEEEAddress, ushort NwkAddress, string NodeIdentifier, ushort Parent, Node_Type DeviceType, Source_Event SourceEvent, ushort ProfileID, ushort ManufacturerID, uint? DeviceTypeID);

        public event RouteRecordEventHandler RouteRecord;
        public delegate void RouteRecordEventHandler(XBeeDeviceManager sender, ulong IEEEAddress, ushort NwkAddress, Rx_Options Options, byte NumberAddresses, ushort[] Addresses);

        #endregion
    }
}
