Notes on the XBee in API Mode (ZigBee Stack)

The ZigBee wireless networking protocol has become accepted for data control and acquisition in medium range applications such as Home Automation, Smart Energy control etc in small buildings and others. It has a comprehensive networking layer protocol that allows essentially automatic formation of networks of a mesh topology, with efficient and redundant routing algorithms built in. This is built on top of a standardised physical and datalink protocol IEEE 802.15.4. It also incorporates application layer protocols to ensure reliable interworking between device manufacturers.

The XBee is a device implementation by Digi International (originally by MaxStream) which supports IEEE 802.15.4 alone, or in conjunction with ZigBee. The XBee versions that support ZigBee use a so-called API mode in which serial messages are used for command and data transfer operations from a processor to an attached XBee, which then communicates with other nodes on the network.

This page describes some of the issues encountered during a project to establish a ZigBee network. The network consisted of a Raspberry Pi board running Linux (later replaced by a larger machine with a disk drive) connected to an XBee coordinator, and a set of remote battery operated nodes. The product family is the low power XBee ZB (S2), given as XB24-ZB with latest firmware version (21A7 for the coordinator and 23A7 for routers).

Remote Nodes

Digi XBee

Software Development

AT Commands

Networking Issues



Remote Nodes Requirements

Because the remote nodes are battery operated they must draw the smallest current possible. This can be assisted by using sleep modes for the XBee. An attached microcontroller is valuable for expanding the capabilities of the XBee including more comprehensive power management options. It must be used where measurements are not possible with the XBee (in particular, counting measurements). Modern microcontrollers can be put into sleep modes, drawing as little as a few microamps of current. The Atmel AVR series of microcontrollers were selected for this although many other types are possible. The microcontroller has 3.3V power shared with the XBee, a serial connection to the XBee and a pullup resistor on the reset pin. Connections via the XBee digital and analogue ports are application specific, and these need to be considered in terms of reducing power drain during sleep periods.

One additional feature to be used in this circuit is for one of the digital outputs of the XBee to be used as a reset for the microcontroller. A second digital output will be used to cause the microcontroller on reset to enter a bootloader for firmware updates. The XBee can be put into pin-sleep mode with an output of the microcontroller used to wake it up. The microcontroller can also be woken up with a third output of the XBee. This arrangement will provide a variety of recovery options in the event of hardware or software failure.

Digi XBee

For communication with the microcontroller the XBee has an AT mode  which is easy to setup and use, and is suitable for small networks of a star topology where point-point communications are dominant. This mode communicates by means of ASCII strings similar to the AT modem style commands.

Another mode referred to as API mode uses binary packets and is more flexible, being suited to mesh networks. It has the full ZigBee networking and application layer protocols and includes fault reconfiguration management capabilities. There is a second API mode in which the byte stream includes escape characters that will allow serial Xon and Xoff flow controls (for use where hardware flow controls are missing), and which can also assist in synchronization of packets in channels with high error rates.

Digi provides a configuration and test utility called X-CTU which can be used to program the devices. I found great difficulty using the original version of X-CTU as it often failed with new devices, apparently causing them to cease to respond. Instructions to recover a "bricked" XBee are messy and require resetting the device partway through the operations. As neither of my support boards boasted a reset switch this became tricky. Eventually I discovered that by setting default values in X-CTU, recycling power and programming the device several times, it seemed eventually to recover and even improve over time. The most recent version of X-CTU may improve on this but it hasn't been tested yet.

Two new XBees of the same type and recent manufacture, when set to end device mode, the associate LED would blink twice per second for a few seconds, then start to blink about four times per second. They seemed to respond to external transmissions, at which the LED would revert to the 2Hz blink rate, however they would not respond to serial communications. A check of the manual showed that for end devices the cyclic sleep mode is enabled by default and this appears to be what was happening. Apparently sleep mode cannot be disabled. Setting them to router mode resolved the issue. A suggestion has been made to change to pin sleep and ground the appropriate pin. Another possibility is to set the sleep time to the minimum value (320ms) and the sleep hold-off time to maximum (65s).

If pin sleep mode is used, then the SLEEP_RQ pin needs to be forced low to keep the devices awake. This requires an external pull-down resistor if the pin is left open.

Software Development

The first thing to do is to seek out a well established and well developed library to handle the XBee interface.
  • libxbee is a most comprehensive and flexible C/C++ library. It is strongly Unix/Windows flavoured. This worked well on a small Linux based card computer (the Raspberry Pi). It may possibly be portable to work under an RTOS, but would be unsuitable for smaller embedded microcontrollers without an OS. It uses the XBee API mode 1 (unescaped binary stream) but has mode 2 (escaped binary stream) as a compile option.
  • xbee-arduino is also appears to be a good C/C++ library that is well suited to an embedded microcontroller without OS. This uses only the XBee API mode 2 (escaped binary stream). It will require porting to other microcontroller environments.
  • xbee-api-on-arduino is a small C/C++ library written for the Maxstream XBee devices. This library was built about 2008 and has practically no documentation. The Maxstream devices differ from the Digi devices in at least some of the API message content.

  • xbee-api is a Java library. This may not be suitable for the smaller embedded microcontrollers. Although minimal Java interpreters exist for some of these they quite have low performance and flexibility. There is also a python library and even a .NET library which would be unsuitable for the same reasons.

  • An XBee C library for the dsPIC32 was built about 2009 as part of a separate project.

It is not a great idea to have two different libraries in a project, but given the different realms of applicability of the first two C/C++ libraries above it may be acceptable to make use of these.

AT Commands

In the XBee manual a number of different types of frames or packets (note the terms packet and frame seem to be used interchangeably in the manual) refer to a packetised communication between the external controller and the XBee over the serial interface. Between XBees the communication protocol is likely to be quite different.

The XBee ZB supports a large number of AT commands for querying and changing parameters on a locally attached node or in a remote node. In API mode these commands are packaged up into an API frame. The addressed node always returns an AT response frame.

Some of the AT commands that are useful for data acquisition are as follows:

Information Commands

ND requests all attached nodes to respond with basic information. This can be used to build a database of attached devices for later communication purposes.

NI asks a node, (addressed by its 64 bit network address if it is remote), to give its Node Identifier string.

DN ask a remote node, addressed by its Node Identifier string, to give its 64 bit network address.

Data Commands

The Analogue inputs are limited in range between 0 and 1.2V which is the internal voltage reference of the XBee chip (Silicon Labs EM250). They can range to a maximum of 1.4V. The EM250 specifies a maximum high voltage level as the power supply voltage plus 0.3V for all GPIO input pins. The analogue conversions are 10 bits calibrated with a factor 1200/1024 mV.

Internal pullup resistors should be enabled if the device is to be set into sleep mode and no external pullup or pulldown resistors are provided. This will reduce noise currents generated by EM induction. The EM250 appears to set a pull down resistor for enabled ADC inputs, and to disable the pullup resistors.

In the following, once a port has been enabled or periodic sampling has been set, it is not possible to revert to a disabled state (despite the manual giving the appropriate parameter to do this). The XBee needs to be software reset.

Dx where x is a numerical digit as follows: ADCx (x = 0..3) or DIOx (x = 0..7) configuration as "ADC in" (2), "digital in" (3), "digital out" high (4) or low (5).

PR set the pullup resistors (30K) on selected lines. This is a bit mask for 14 lines. All but bit 13 (DIO7 which is also CTS, an output) are enabled by default. Themask bit definitions are not in order of the I/O number. They are defined in the XBee ZB Manual. If pin sleep is to be used then the pullup for SLEEP_RQ needs to be disabled as it will have an external pull down resistor.

V% read the power supply voltage, scale by 1200/1024. The XBee should be working in the range 2100 to 3600 mV.

IR Enable periodic sampling of enabled input pins. I/O samples are sent to the coordinator on each time tick. A setting of 0 disables this, otherwise use a setting between 0x32 and 0xFFFF ms.

IS Force sampling of all enabled inputs. An I/O sample is sent immediately.

IC Set a mask of digital inputs to be monitored for changes. An I/O sample is sent when any of these change. The bit mask of 12 bits sets the DIO pins to monitor, but each bit will only take effect if the DIO pin is set to digital input.

The I/O sample message returned is formatted as follows:

  • Number of sample sets - this is always 1 (presumably for compatibility with other families?)

  • Digital channel mask (13 bits) indicating which channels have been enabled as digital I/O.
  • Analogue channel mask indicating which channels have been enabled as analogue input.

  • 2 bytes of digital I/O according to the digital channel mask. If no digital I/O is enabled, these bytes are absent.
  • 2 bytes for each enabled analogue channel, up to 4 channels and the supply voltage channel.

Sleep Mode Commands

SM Set sleep mode: Pin sleep (wake when an input changes), cyclic sleep (wake on timer), or both cyclic and pin sleep.

SP Set sleep period in 10ms increments from 0x20 to 0xAF0 (28 seconds max). Coordinators and routers buffer data for a maximum of 30 seconds and can receive commands at the end of the sleep period.

SN Set an additional number of sleep periods to extend sleep beyond the 28 second maximum. On waking the node should contact the coordinator to notify availability to receive commands.

ST Set a time after the last serial or RF reception before the device enters sleep mode.

SO Set sleep options. When waking, remain awake for the entire ST time (2). This allows external devices time to contact the node. Sleep for the entire SN*SP time (4). This must be set if extended sleep is required.

Miscellaneous Commands

FR Software reset the XBee. This clears enabled ports to their disabled values but retains the network settings saved in RO memory.

Networking Issues

After 8 weeks of operation with the PAN ID set to a fixed value, the network stopped working. The nodes (routers) all registered reception of network notification messages from each other, but no node transmitted to the coordinator (as evidenced by the silence of the RSSI LED) . Replacement of the coordinator did not change the situation. Only after each node was reprogrammed with a different PAN ID did the network restart.

In a second event of this type the routers were programmed with a PAN ID of 0 to encourage them to pick up the coordinator. However the operating PAN ID remained at the old value and nothing would shift it, except a network reset command which had to be issued over the serial interface (as the network was non-operational)..

The XBee routers can be configured to monitor the network and reset themselves. This is done by setting the channel verification (JV command) which looks for the coordinator on the current channel and will leave the network if it is not found. This happens only on power up or when joining the network. In addition the Network Watchdog Timer (NW command) can be set to cause the routers to check for a coordinator every few minutes and again to leave the network if the coordinator is not found. These settings are important to ensure that the network remains operational without the need for manual intervention. They are not applicable however to end devices.

JV Set a router to verify on power-up that a coordinator is present on the current channel, and leave the network if not found.

NW Set a timer value in minutes for a router to check for a coordinator and leave the network if not found..


This is a C/C++ library useable with Linux, Windows and OS/X for interfacing with a Digi XBee device series 1 or 2. It is well written and reasonably well documented but nevertheless it is difficult to work out how to use it. Some examples are provided but these are not as well documented as the library. The interface mode is API mode 1, that is, using unescaped binary packets (mode 2 uses escaped characters to allow control signals such as serial flow control to be inserted).

The library uses hardware flow control by default, but that may be disabled in the file. Some USB-serial adapter boards do not have the flow control connections wired in. However if that is done, care must be taken when the XBee is issued certain commands. In particular when starting up it requires a few seconds to get to the point where it accepts serial commands. It is best to ensure that any adapter board has flow controls present and in use.


In libxbee, communication with an XBee is done by setting up an XBee class instance and a set of connection class instances. Each connection is used to communicate either locally to the Xbee itself, or remotely through the Xbee remote access controls. Typically a connection would be used to send a command and wait for received responses. Connections are also used where the XBee receives unsolicited transmissions, typically carrying sensor data for processing and storage. Each of these connections is associated with a particular remote XBee address.

AT Examples

Three examples are quite easy to get going as a first step. "Simple AT" sends out local AT commands and prints the responses received from the attached XBee. "Remote AT" sends out remote AT commands to another XBee node and again prints the responses received. The address of that node must be specified in the program (it can be found from XCTU). These show how to setup a connection, send a command (with addressing for the remote commands) and receive the response via callback functions. The "Node Detect" example also uses a local AT command which queries all nodes in the network and causes them to return information about themselves.

Simple Data Example

This example is difficult to understand for a number of reasons. It sets up a connection referred to as a "listening" connection, and waits for transmission from a remote XBee. It is not immediately obvious what sort of transmission is expected or how it is generated remotely.

This "Data" connection waits for a "ZigBee Receive Packet".  At the other end a "ZigBee Transmit Request" frame must be sent containing the data to be communicated. Each XBee needs to setup two separate "Data" connections, one for transmission and the other for reception. The receiving XBee also needs to specify the address of the remote XBee from which it expects data to arrive. This means that the receiver will need a separate connection for each remote XBee that sends it data, and the data retrieved will then be associated unambiguously with the sender via the connection.

One way to run a useful test is to setup the remote XBee on a second PC with the "Broadcast" example which will send out broadcast data frames with ASCII "Hello\n\r". This is picked up by the Simple Data example and printed.

Broadcast Example

This example sends a message on a data connection as a broadcast. Once the data connection has been opened, all data packets sent from the nodes will be intercepted by the callback function.

I/O Example

This is similar to the data example except that an I/O sample Rx indication frame is expected. This is interpreted by libxbee to provide data fields related to enabled ports on the remote XBee. The XBee manual doesn't mention any corresponding frame format for the remote end. This is because the remote XBee, when appropriately configured, will transmit data regularly or in response to a triggering event. This is an autonomous operation and requires no interaction between any external controller and the XBee.

A remote XBee was setup with a potentiometer wiper connected to analogue input A1; the voltage across the potentiometer being reduced to about 1V by a series resistor (the analogue inputs are restricted to 1.25V at maximum range). A0 was connected to that voltage but was not used.

The XBee was then configured to enable the two analogue inputs with AT commands "D0"2 and "D1"2 (the two-digit AT command is in ASCII while the parameter is a raw integer 2 which sets a pin as analogue input). It was also configured for cyclic transmission of all enabled I/O port values every second with the command"IR"1000. This configuration could be done with XCTU but I used a custom utility for this. The sample code was then modified to print port A1 and the address was set to the remote XBee. In the example as at this date, the type of the variable "value" should be set to unsigned int to display the entire 10 bit range of the XBee A/D converter. The printf format can be set to %u for completeness.

NOTE: in the tests, once a parameter was set to a non-zero value (enabled) it could not be reset back to zero again, although it could be changed to another non-zero value. On reset it reverted to the default disabled state. UPDATE: this is a result of using xbee_conTx which treats the parameters as a null terminated string. To send parameters that may contain zeros use xbee_connTx which specifies a general buffer and its length.


This sets up a listening connection and waits for a remote node to join the network. It looks for identification indication messages. If a node has already joined, then it will retain that status even through power cycling.Therefore to cause an identification indication message to be sent, ensure that port D0 is set to "Commissioning Button" mode (default), and briefly connect the port to ground with a pushbutton or wire.

Forced I/O

Configure the remote XBee for analogue input as described above in the I/O example, but not for cyclic transmission. This example sends out a remote AT command to retrieve the data. The example uses libxbee calls to interpret the data format in the returned message and extract the desired I/O values. Note that callbacks are not used here, rather the received data polling call is used. In the example as at this date, the type of the variable "value" should be set to unsigned int to display the entire 10 bit range of the XBee A/D converter. The printf format can be set to %u for completeness.

C++ Interface

An instance of the xbee class provides the basis for a single locally connected XBee. More than one of these can be active at the same time on different interfaces.


This is a Java library, unfortunately as it makes interworking with C/C++ code more difficult. It is possible for Java to be compiled for use with small embedded devices with slower speed and lower memory. However it is expected that this library would be used on a larger machine.

First created 16 December 2012
Last Modified 14 July 2014
Ken Sarkies 2012