Communication between various 802.15.4 sensor motes : iMote2 (CC2420, .NET & TinyOS),  TelosB & MicaZ (CC2420, TinyOS), WaspMote (Digi XBee, Libelium) and Arduino (Mega 2560+XBee shield+Digi XBee)

C. Pham, LIUPPA laboratory, University of Pau, France.

last update: June 14th, 2013.


For those who have several sensor platforms communications, interoperability between various sensor boards is an issue. Commonly found sensors boards a few years ago were Mica2, MicaZ and TelosB then came the iMote2 which is much more powerful than the previous motes (you can see this page from the TinyOS project or this one from U. Washington.). Most of these boards run under the TinyOS system. Their radio module is a TI CC2420 which is also quite powerfull and flexible. There are lots of documention on the web. AdvanticSys also propose TelosB-based sensor motes with many extension boards. Recenty, Waspmote board from Libelium became quite attractive because of their ease of programming based on the open-source Arduino IDE. These WaspMote motes have been deployed in large-scale in the Santander city (Spain, see deployment map) as part of the SmartSantander EU project. Arduino hardware is also very well-known from the electronic community and quite interesting sensor boards could be realized on an Arduino basis. Both Waspmote and Arduino boards use the Digi XBee modules for 802.15.4 connectivity. Both CC2420 and Digi XBee are 802.15.4 compliant therefore it is possible to make them communicate in order to build sensor networks with a mixture of hardware platforms. Although the CC2420 is much more flexible than the XBee because many parameters can be tuned, the XBee module is very versatile and easy to use. Also, an XBee module has an IEEE 64-bit address while the CC2420 still do not have such feature, at least to the best of my knowledge.

Figure 1: from left to right:  iMote2, WaspMote, TelosB, MicaZ

The iMote2 from Crossbow came either with the .NET firmware or the TinyOS support. The development environment shipped with the .NET version is based on Windows with Visual Studio 5 and the Micro .NET framework. The librairies are quite complete and also support limited TinyOS compatibility. If you have the TinyOS version of the iMote2, the information provided in this page also apply to you. With TinyOS, you can either use the default ActiveMessage layer or a fully 802.15.4 compliant protocol stack (tkn154, see tinyos-2.x/apps/tests/tkn154, only on MicaZ and TelosB, not iMote2. For iMote2, use Ieee154 component instead with limitation on using only 16-bit addresses). Arduino proposes an open-source IDE based on Processing which offer a very simple way to rapidly develop prototypes. Waspmote boards from Libelium are shipped with a custom version of the Arduino IDE. As iMote2, TelosB, MicaZ, Waspmote and Arduino boards are quite representative, this document presents our preliminary tests and solutions for making them able to communicate.

Connecting with USB

In most of the operations described in this page, the various sensor boards will need to be connected to a computer (for uploading program, for reading the serial data,...). If you are using MAC OS X, you will probably have no problem as the USB port will be detected quite easily by the Libelium or Arduino IDE. Same thing if you are using the serial tool Zterm on MAC OS X. If you are using Linux, the new versions do have built-in  FTDI support so you should not having problem. However, sometime the name of the serial port can change. For instance, Libelium Waspmote are usually accessible under ttyUSB0. The Arduino board usually are accessible under ttyACM0. For the iMote2, you may need to have the debug board and use the USB port of the debug board to connect to the computer. These debug boards offer 2 FTDI ports, usually ttyUSB0 and ttyUSB1. In all cases, you can use the dmesg program to determine the name of the port under Linux: plug the USB cable and type dmesg to see the name under which the system has detected your USB device. I'm not using Windows platform, except for programming the .NET iMote2 so check on the web fo further information.

iMote2 under .NET: communicating with an XBee gateway

First of all, in order for the 2 radios to be able to communicate, you have to ensure that they are both in the same PAN ID and in the same channel. See this page for how to set this on the XBee. Here, let's assume that both are on PAN ID 0x1234 and channel 0x0D.

In addition to these basic settings, on the XBee, you have to set the Mac mode to either 1 or 2, but 2 is better with acknowledgment. The Libelium come with a Mac mode of 0 by default, which inserts/removes transparently 2 additional bytes in the payload portion of the standard 802.15.4 frame structure. Also, I prefer to use 64-bit addressing instead of the 16-bit because the latter is independant from the hardware MAC address and is therefore more difficult to debug. Finally, the right settings for the XBee should be, using AT command syntax:


It is also advised to disable encryption and to use API mode 2 with escaped characters:


In a first step, we can use the Libelium gateway (a USB-serial converter for the XBee module) to sniff all packets from an iMote2. The gateway will be inserted into a PC under Windows, MACOSX or Linux running any serial tool such as HyperTerminal, Zterm or minicom respectively. The Libelium XBee serial connection configuration is:

Figure 2. XBee USB-Serial gateway from Libelium

With the gateway, there is no software needed on the Libelium side: an iMote2 will be used to broadcast 802.15.4 packets that will be received by the Libelium XBee gateway and send on the serial line of the gateway. The iMote2 runs a simple C# test program that send 'AAAAABBBBB' as the payload. Here is the code of this simple program:

using Crossbow.lib.utils;
using Crossbow.platform.imote2;

    public class myTest

        static Leds m_Leds = new Leds();

        public static void Main()
            const ushort _rfChannel = (ushort)RadioChannel.Ch13; // channel 13
            const ushort _rfPower = (ushort)RadioPower.M10DBM;  // -10dbm

            SerialDump.print("App [myTest]: Started."); // Print the applicaiton identifier
            Radio radio = new Radio(_rfChannel, _rfPower, 0x1234, 0x5678);
            byte[] packet = new byte[10];


            for (; ; )
                    for (int i = 0; i < 5; i++)
                        packet[i] = (byte)'A';

                    for (int i = 5; i < 10; i++)
                        packet[i] = (byte)'B';

                    // payload is AAAAABBBBB



                    radio.Send((ushort)0xFFFF, (ushort)0xFFFF, packet);


                catch (System.Exception ex)
                    SerialDump.print("App [myTest]: " + ex.Message);

The line Radio radio = new Radio(_rfChannel, _rfPower, 0x1234, 0x5678); defines a radio configured in PAN ID 0x1234, channel 0x0D (13 in decimal) with a 16-bit address of 0x5678. Actually, you can put anything you want for the 16-bit address here since we are going to broadcast the packet with the line radio.Send((ushort)0xFFFF, (ushort)0xFFFF, packet);.

With a serial tool such as minicom you will get the raw data from the XBee module which should look like:

Figure 3.

The hex form is something like:

7e 00 19 80 62 af 2c d1 7d c3 1a a8 3f 06 34 12 78 56 41 41 41 41 41 42 42 42 42 42 87

which could be decoded as follows with the API frame structure used by the XBee for XBee->Application communications.

7E: frame start delimiter
00: Length msb
19: Length lsb
80: RX packet (64bits)
Source address : 62 af 2c d1 7d c3 1a a8
RSSI   : 3f
Option : 06 Address broadcast PAN broadcast
Data : 34 12 78 56 41 41 41 41 41 42 42 42 42 42
Checksum: 87

Figure 4.

Actually, the 64-bit address field is varying so this information seems not to be reliable. The length is 0x0019 so 25 in decimal. This is correct as it counts from the 0x80 to the last data byte 0x42. According to the API frame, we can see that the PAN ID 0x1234 and the 16-bit address of 0x5678 becomes part of the payload. If you look at the .NET implementation (RadioCC2420.cs) then you will see that the BuildFrame() function, prior to sending the frame, does insert the source PAN ID and the source short address in the payload. Here, on the XBee, the important thing is that we actually received something and that the data part 'AAAAABBBBB' can be identified somewhere in the frame.

iMote2 under .NET: communicating with a Waspmote

The next step is to realize a communication between an iMote2 and a Waspmote (with the XBee module). For this purpose, the program on the iMote2 side is the same but we need a program on the Waspmote to realize the function of the USB-serial gateway. In addition, we do not want the raw data but only the real data part, i.e. 'AAAAABBBBB'.

It is not possible to use the traditional Libelium API to receive the packets from the iMote since the Libelium API performs a lot of additional steps that rely on additional headers specific to the Libelium API. We recall here the traditional Libelium API for receiving a packet:

    if( XBee.available() )
      if( !xbee802.error_RX )
        // Writing the parameters of the packet received
          for(int f=0;f<xbee802.packet_finished[xbee802.pos-1]->data_length;f++)

In our case, we will directly use the to get all the raw data received by the XBee module. Actually,  xbee802.treatData() calls xbee802.parseMessage() which exactely does the same Our code is largely based on the code found in xbee802.parseMessage()but in a much simpler version that does not take into account multiple fragments.  Here is a typical statement to be inserted in a loop to get all the data from the XBee:

    if (XBee.available()) {

You will get in memory all the raw data that you can process later on. The whole code can be found here but I advised you to have a look at it later. The next step is to enable the reception of both iMote2 data and data from other Waspmote that are using the Libelium API. We will describe this step in the next section and will provide more information on the code to explain what we did.

Receiving both iMote2 data and Waspmote data on a Waspmote

The iMote2 program is the same: it sends 'AAAAABBBBB' as the payload. We still have our first Waspmote (noted WA) with the previous receiving code. We use a second Waspmote (noted WB) with the WaspXBee802_2_sending_receiving program shipped by the IDE practically unchanged. The slightly modified code can be found here.

In order to be able to receive from both an iMote and a Waspmote using the Libelium API, we have to find a way to determine from the raw data its origin. We did this this way, it may be not the best way, a Waspmote is required to
  1. use a predefined application ID (for instance 0x52 as used in the Libelium example)
  2. use a source ID string set to "WASP"
Here is the code for sending a packet with the Libelium API that satisfies these 2 constraints:

  // Set params to send
  paq_sent=(packetXBee*) calloc(1,sizeof(packetXBee));
  xbee802.setOriginParams(paq_sent, "WASP", NI_TYPE);
  xbee802.setDestinationParams(paq_sent, "000000000000FFFF", data, MAC_TYPE, DATA_ABSOLUTE);

A packet sent from a Waspmote with the Libelium API is typically longer because of the application header inserted by the Libelium API. However, we can use the 2 constraints to determine whether this is a packet from a Waspmote or not. The figure below show the output of the WA connected to a computer under Linux/minicom. The iMote2 and WB are sending data that will be received and displayed by WA.

Figure 5.

The first packet comes from the iMote2. The length is 0x19 (25 décimal) including the 0x80 command code and excluding the last byte which is the checksum. The 14th byte is the option byte (see the XBee RX API structure). Here its value is 0x6 so it is both a broadcast address and a broadcast PAN because it is the way we sent it in the C# code. Then, you can recognize in the next 4 bytes the PAN ID 0x1234 and the 16-bit address 0x5678 on the sending iMote2. Then it is easy to identify the real payload, i.e. 'AAAAABBBBB' which is 41414141414242424242 in hex format.

The second packet comes from WB that is using the Libelium API to send the packet. The 14th byte is again the option byte that indicates an address broadcast mode because we used the broadcast 64-bit address "000000000000FFFF" in the xbee802.setDestinationParams call. Then we can recognize the application ID 0x52, the fragment number 0x01, the first fragment indicator '#' and source type 0x02 (because we use NI_TYPE) and then the source ID "WASP" (0x57 0x41 0x53 0x50). Then we have a '#' delimiter that is not documented but it is inserted by the Libelium API and we have to take it into account before we find the real data "Test from -mac:0013A20040762056". The application header is from the Libelium 802.15.4 programming guide.

Figure 6.

Therefore the procedure for receiving a packet is quite simple. Suppose that we have in memory buffer all the raw data, the code in WA (getRawData()) is then:

  // set the myRawData pointer, one could extract additional information from the XBee header if required
  int dataStart;
  // test whether the packet commes from a WaspMote with the Libelium API
  if (memory[14]==0x52 && memory[18]=='W' && memory[19]=='A' && memory[20]=='S' && memory[21]=='P') {
  else {
  // set the length of the real data
  // allocate a new buffer to copy the real data, plus one char for the null-terminated character 
  myData = (uint8_t*) calloc(myData_len+1,sizeof(uint8_t));
  for (int j=0; j<myData_len; j++)
  // set the null-terminated chaacter so one could diectly pint the real data if it is a string

We provide 2 pointers: myRawData will point to the beginning of the raw data, i.e. the frame delimiter 0x7E and myData will point to the real data, which position depends on whether it is a packet from an iMote or another Waspmote with the Libelium API. We simple have to test for the application ID 0x52 and the source ID string "WASP" in order to point to the correct position. The reception of packets in WA is then performed as follows:

    if (XBee.available()) {

      // free myRawData
      // myData, note that both memory buffers are independent

Don't forget to free both myRawData and myData when you don't need then anymore. We also replace the checksum by a '\0' character so that you could print the real data in ASCII form if required. If you are using non ASCII data, then you have to cast appropriately myData into whatever data structure you used at the sending side. Beware of different size type and little or big endian mode! Now, you can have a look at the whole reception code in WA.

Note that there is a light version of the Libelium API that simply send the payload without any additional information. In this case, you have a pure 802.15.4 frame so if you receive it on an other XBee, data should start at byte 14 if 64-bit address is used and you normally do not have anything to do to get the payload.

iMote2 under .NET using the TinyOS format

The .NET framework for the iMote2 has some (limited) form of TinyOS compatibility: the radio could be a TOSRadio in order to be compatible with Mica2, MicaZ or TelosB platforms that mainly run under TinyOS system. In the previous C# code for the iMote2, just change the following lines:

Radio radio = new Radio(_rfChannel, _rfPower, 0x1234, 0x5678);


TOSRadio radio = new TOSRadio(_rfChannel, _rfPower, 0x1234, 0x5678);

and adds the following line before the radio.Send call:

radio.amType = 50;

The value is not important, you just have to pick a value to specify the Active Message (AM) type of TinyOS. This value will help us identify where the real data starts in the received frame.

The same procedure than previously, connecting the XBee gateway to get the raw data (after processing escaped characters however), gives the following dump:

7e 00 17 80 62 af 00 d1 7d af 1a a8 2e 06 32 7d 41 41 41 41 41 42 42 42 42 42 29

which could be decoded as follows with the API frame structure used by the XBee for XBee->Application communications.

7E: frame start delimiter
00: Length msb
17: Length lsb
80: RX packet (64bits)
Source address : 62 af 00 d1 7d af 1a a8
RSSI   : 2e
Option : 06 Address broadcast PAN broadcast
Data : 32 7d 41 41 41 41 41 42 42 42 42 42
Checksum: 29

We can see that the payload part comes again with 2 additional bytes (and not 4 without the TOSRadio). The first byte is the amType value (0x32 is 50 in decimal) and the second byte is the default AM group ID used by the .NET framework (0x7d is 125 in decimal).

It is quite possible to modify the Waspmote reception code to take into account both iMote with and without TOS radio. One solution could be to always use the default group ID of 0x7d and track this value. If you avoid a PAN ID having 0x7d byte then a non-TOS radio will not have 0x7d at byte 15. If you have 0x7d at byte 15, then you can say that it is from an iMote2 with TOSRadio and then the data start at position 16 and not position 18 when the non-TOS radio is used. You can add more safety by also tracking the amType byte if once again you use a predefined value (such as 0x32). This is a bit tricky and not 100% reliable but it works as a workaround to make iMote2 and Waspmote communicating altogether in the same network.

It is possible to use a mote under TinyOS (I've tested with a TelosB and MicaZ) using the alternative tkn154 IEEE 802.15.4 compliant protocol stack to receive/sniff packets sent by an iMote2 because the default ActiveMessage layer will probably not give the frame to the application layer because of internal filtering mechanisms. To build such a sniffer, just follow the instruction in TinyOS to build for a TelosB the tinyos-2.x/apps/tests/tkn154/nonbeacon-enabled/TestPromiscuous application. This will act as a sniffer. Be sure to be on the same channel (change in the RADIO_CHANNEL parameter). Output obtained with the Java PrintfClient is shown below:

> java -comm serial@/dev/ttyUSB0:telosb
Frametype: Data
SrcAddrMode: 0
DstAddrMode: 2
DstAddr: 0xFFFF
DestPANId: 0x1234
DSN: 210
MHRLen: 7
MHR: 0x41 0x08 0xD2 0x34 0x12 0xFF 0xFF
PayloadLen: 12
Payload: 0x32 0x7D 0x41 0x41 0x41 0x41 0x41 0x42 0x42 0x42 0x42 0x42
MpduLinkQuality: 108
Timestamp: 1807868

As before, you can see in the payload the first byte which is the amType value (0x32 is 50 in decimal) and the second byte is the default AM group ID used by TinyOS (0x7d is 125 in decimal).

Note that you can use a TelosB mote under TinyOS with the tkn154 protocol stack to then communicate with WaspMote and Arduino boards also. This is explained below.

Communication the other way: Waspmote to iMote2

Communications from a Waspmote to an iMote2 is also possible. We will demonstrate this by using the same second Waspmote WB (the one which sends "Test from -mac...") and a iMote2 connected to a Windows PC. To dump the serial port on the PC, we use the SerialDump program provided by Crossbow in their iMote2 .NET bundle. The iMote runs a modified version of the XSniffer program (XSniffer is one of the sample program of the Crossbow .NET bundle). This modified version, we called myXSniffer, does not use TOS messages and adds 2 additional dumps of the serial port: an hex and an ASCII version (we decided to use this solution in oder to not change the SerialDump code). The C# code for myXSniffer can be seen here. The following screenshot shows the reception by the attached iMote2 of packets from the Waspmote WB. Click on the image to get the full resolution image.

Figure 7.

You can see that the hex dump is :

76 40 00 A2 13 00 52 01 23 02 57 41 53 50 23 54 ...

The first 6 bytes is part of the source MAC address: 00 13 A2 00 40 76. Don't know exactely why the last 2 bytes (20 56) cannot be obtained. The next 9 bytes, is the Libelium application header: 52 01 23 02 57 41 53 50 23. Once again, we can recognize the application ID 0x52, the fragment number 0x01, the first fragment indicator '#' and source type 0x02 (because we use NI_TYPE) and then the source ID "WASP" (0x57 0x41 0x53 0x50). Again, we have the terminating '#' delimiter that is not documented but it is inserted by the Libelium API. We have to take it into account before we find the real data "Test from -mac:0013A20040762056". Therefore, it is quite possible on the iMote2 to point to the real data by skipping the first 15 bytes when receiving the data. Once again, if you are using non ASCII data, then you have to cast appropriately the received data into whatever data structure you used at the sending side. Be sure to know how the data are coded: little endian or big endian!

Communication with Arduino board: (1) Arduino to Waspmote

We also have the Mega 2560 board from Arduino to test with. This board can have 802.15.4 connectivity by plugging an XBee shield (seems that the shield is from Libelium) with a Digi Xbee module (serie 1). Arduino also have the equivalent of the USB-serial gateway that work just like the Libelium gateway, and is fully compatible with Waspmote node.


Figure 8.                                                                                                                                                Figure 9.

Like previously, make sure that the XBee module is set to the correct configuration to allow physical connectivity:


Note that once you issued the atbd5 command that put the XBee module to work at 38400 bauds you may have to reconfigue your serial tool (minicom for instance) to that baud rate.

We will use a library for using the XBee module with API mode which is more convenient than the AT command mode that is used by the original Arduino library. This XBee API library can be donwloaded from Current version is 0.3. You can also check this page to see how this library offers communication primitives to Arduino board using the XBee module.

This XBee API library uses NewSoftSerial library from which is now included in the Arduino IDE core library (as SoftwareSerial) if the version is equal or higher than 1.0, which should be the case if you get the latest Arduino IDE which is 1.0.1 released on May 21st, 2012.

Using this API, here is a simple code taken from to send a packet:

// Create an XBee object at the top of your sketch
XBee xbee = XBee();

// Tell XBee to start Serial

// Create an array for holding the data you want to send.
uint8_t payload[] = { 'H', 'i' };

// Specify the address of the remote XBee (this is the SH + SL)
XBeeAddress64 addr64 = XBeeAddress64(0x0013a200, 0x403e0f30);

// Create a TX Request
ZBTxRequest zbTx = ZBTxRequest(addr64, payload, sizeof(payload));

// Send your request

We will then use this API to have a simple sending program which does roughly the same thing than previously (i.e. sending "Test from -mac:0013A20040762056"). For the moment, the MAC address is statically fixed but it is probably possible to get it from the XBee module using the XBee API to send the appropriate AT commands (atsh and atsl to get respectively the msb and lsb of the 64-bit address). The complete code for this test program to be compiled and uploaded from the Arduino IDE is here. Here is a .zip file that you copy into your Aduino's IDE sketch folder and unzip it. As you can see in the source code, we use AT command to read the MAC address of the XBee module. This part is not important for the communication issue that we are addressing here, but if you are not familiar to AT command and want to know more on this, you could read the following XBee page.   Important note, taken from

The Arduino has only one serial port which must be connected to USB (FTDI) for uploading sketches and to the XBee for running sketches. The Arduino XBee Shield provides a set of jumpers to direct Serial communication to either the USB (Arduino IDE) or the XBee. When using the XBee Shield you will need to place both the jumpers in the USB position prior to uploading your sketch. Then after a successful upload, place the jumpers in the "XBEE" position to run your sketch. Always remember to power off the Arduino before moving the jumpers.

Figure 10.

Once again, we can use minicom (or alternatively cutecom, as shown in figure 10, which can output the hex values) to see the raw data received by an XBee gateway. Since there is no extra header introduced by the Xbee API (nothing like the full Libelium API for instance) the raw data strictly follows the structure of the Digi XBee receive frame illustrated in figure 4. Note that a WaspMote with the light Libelium API is fully compatible with an Arduino since both use the XBee module without introducing any additional overheads.

It is possible to modify the previously Waspmote reception code to take into account also data sent from Arduino boards that strictly follow the Digi XBee frame structure. In the following version, we check whether byte 14th and byte 15th have respectively the value of 0x34 and 0x12 (the PAN ID) in which case it is most probably a packet from an iMote2 (without TOS header), otherwise it is a packet from a pure XBee module such as the Arduino XBee API without any additional header. In the figure below, we can see that we can successfully receive simultaneously from an iMote2 (the 'AAAAABBBBB' payload), a Waspmote with Libelium API (the 'Test from -mac:0013A20040762056' payload) and from a Mega 2560 Arduino board (the 'Arduino test from -mac:0013A20040762056' payload).

Figure 11.

Communication with Arduino board: (2) Waspmote and iMote2 to Arduino

Normally, data from a Waspmote to an Arduino should carry the application header of the Libelium API. So, if we use the same convention then previously of using 'WASP' as the node identifier for Waspmote node you could just skip the application header that should look like:

... 52 01 23 02 57 41 53 50 23 ...

To verify this statement, we wrote a simple sniffer program for the Arduino using the XBee API. The simple sniffer code can be find here. Once again, here is a .zip file that you can copy into your Arduino's IDE sketch folder and unzip it. Compile and upload the sniffer program, then open the serial monitor (or any serial tools such as minicom) to get the following information:

Figure 12.

The sniffer program prints 3 informations: (1) data (hex format) got with getFramedata() function, (2) data (hex format) got with getData() function and (3) data (ASCII format) got with getData() function.

The first packet is received from our Waspmote WB. You can see that the "raw data" section includes the source MAC address followed by the RSSI and the option byte (that indicate an address boadcast here 0x2). Actually, this is the cmdData part of figure 4 from the XBee module. Then you have the data from the application layer that you can also see in the "App data" section. Before the real payload, we can recognize the Libelium application header.  So our suggestion was true and you just have to skip this header to get the real payload.

The packet is received from our iMote2 that again contineously sends the 'AAAAABBBBB' payload. Very similarly to the iMote2->Waspmote case (except that with the XBee API on the Arduino the raw data does not include the frame start delimiter, the frame length and the XBee API identifier) we can recognize in the first 6 bytes of the raw data something similar to an 64-bit address field but it is varying so this information seems not to be reliable. Then we can see that the PAN ID 0x1234 and the 16-bit address of 0x5678 becomes part of the payload so you just have to skip these bytes to get the real payload.

As part of a master project, a group of students of University of Pau (year 2012-2013) wrote this reception code that allows an Arduino to receive the real payload from Libelium, Imote and, of course, other Arduino.

Communication with Arduino board: (3) Arduino to iMote2

Communication from an Arduino to an iMote2 is very similar to the Waspmote to iMote2 case. The only difference is that the application header is not present therefore the real data starts right after the first 6 bytes that, once again, represent part of the source MAC address. Once again, I don't know exactely why the last 2 bytes cannot be obtained. Therefore, for an Arduino board, you can just skip the first 6 bytes. Remember that for an Waspmote, we found that we have to skip 15 bytes. It is possible to distinguish between data from an Arduino and data from a Waspmote if we use the same convention then previously of using 'WASP' as the node identifier for Waspmote node. In this case, 'WASP' should starts at byte 10, otherwise it is most probably a packet from an Arduino.

CC2420 Motes (iMote2, TelosB, MicaZ,...) under TinyOS to XBee (WapsMote or Arduino), see text in red for the opposite way (XBee to TinyOS)

TinyOS with ActiveMessage

The default TinyOS configuration uses ActiveMessage to communicate and the current version of TinyOS uses by default interoperable frames (IFRAME) to be able to interoperate with non-TinyOS network. By default, TinyOS proposes the TINYOS-6LOWPAN network identifier which has value 0x3F (see TEP125 and TEP126, as well as tinyos-2.x/tos/chips/cc2420/cc2420.h).

In order to communicate between motes, at the 802.15.4 level the motes themselves must be in the same PANID. The fact is that under the ActiveMessage stack, the correspondance between the ActiveMessage various addresses and the 802.15.4 addresses is a bit tricky. After several tests with a promiscuous sniffer (the one described previously with the tkn154 stack) here are our conclusions:
  1. the DEFAULT_LOCAL_GROUP variable will define both the source and destination PANID, because it seems that TinyOS only support intra-PAN communication to the best of our knowledge, but it has to be defined at the highest level
  2. the TinyOS node id will be mapped to the 16-bit 802.15.4 source address
So if you want your mote to be on PANID 0x3332 and having a 16-bit address of 0x6287 then you have to issue the following command for compiling and installing the program on the motes, here a TelosB mote:

> DEFAULT_LOCAL_GROUP=0x3332 make telosb
> make telosb reinstall.0x6287 bsl,/dev/ttyUSB0

Unfortunately, our first tests shown that the 16-bit PANID address is not correctly interpreted by TinyOS and it appeared that only the lower part of the 16-bit address is taken into account by TinyOS. This has been verified with the sniffer that showed that the source PANID is set to 0x32 and not 0x3332. After verification, this is confirmed in tinyos-2.x/tos/types/AM.h where nx_am_group_t and am_group_t are defined as nx_uint8_t and uint8_t respectively. So in order to take into account the 16-bit PANID, our first solution was to change these types to nx_uint16_t and uint16_t respectively. After these changes and recompilation, the upload of the program on the mote will assign to the CC2420 module the requested 16-bit PANID. Now, if we look at what is captured by the sniffer, we have something like:

Frametype: Data
SrcAddrMode: 2
SrcAddr: 0x6287
SrcPANId: 0x3332
DstAddrMode: 2
DstAddr: 0xFFFF
DestPANId: 0x3332
DSN: 170
MHRLen: 9
MHR: 0x41 0x88 0xAA 0x32 0x33 0xFF 0xFF 0x87 0x62
PayloadLen: 12
0x3F 0x02 0x41 0x41 0x41 0x41 0x41 0x42 0x42 0x42 0x42 0x42
MpduLinkQuality: 108
Timestamp: 58237590

In the payload we can see that there are 2 extra bytes compared to a pure 802.15.4 payload. The first byte 0x3F is the TINYOS-6LOWPAN network id as explained above. The second byte 0x02 is the AM_ID that you give to your application when writing new AMSendC(MY_AM_ID) to define the ActiveMessage sender component. In summary, if you wants to correctly interpret a frame sent by a mote under TinyOS ActiveMessage, you could track the TINYOS-6LOWPAN network id and check whether the AM_ID is one value that you have defined for your application.

Unfortunately, changing the width of nx_am_group_t and am_group_t introduces some incompatibility issues with tools such as PrintfClient. So if you just want to stick with the default TinyOS distribution, you can just use only 8-bit PANID for your XBee modules. In this way, the PANID value for TinyOS will be the same than the one of your XBee. If you still want to have 16-bit PANID, which is the standard in 802.15.4 networks, here is our final solution that avoids changing the width of nx_am_group_t and am_group_t therefore preserving the compatibility with the other TinyOS tools. Our solution consists in changing in the tinyos-2.x/tos/chips/cc2420/control/ file the following statement in the Init.init() function:

m_pan = call ActiveMessageAddress.amGroup();


    m_pan =
    m_pan = call ActiveMessageAddress.amGroup();

Then in the Makefile you can add the compilation flag in the usual manner:


It has been tested on both TelosB and MicaZ platforms.

Actually, it is also possible to communicate from a WaspMote or Arduino to a mote under TinyOS ActiveMessaging system. To do so, just insert 0x3F and an 8-bit value for an ActiveMessage identifier before your payload to make the TinyOS mote with ActiveMessage being able to retrieve the packet. For instance, if you want to send "hello" then build the following payload: 0x3F 0x02 'h' 'e' 'l' 'l' 'o'. On the XBee module you MUST be in Mac Mode 2 (AT MM2) and MUST have a 16-bit network address (AT MY) that is different from 0xFFFF because otherwise TinyOS with the CC2420 (MicaZ, TelosB, iMote2,...) will incorrectly understand the packet. In my case, I'm using the last 4 digit of the 64-bit address of the XBee module to define the 16-bit address. If you do so, you will be able to correctly receive the payload on the TinyOS mote which is "hello", here is a portion of the slightly modified TinyOS

event message_t *RadioReceive.receive[am_id_t id](message_t *msg,
                            void *payload,
                            uint8_t len) {

    id = call RadioAMPacket.type(msg);

    if (id==0x02) {     
        uint8_t k;
        printf("Got: ");

        for (k=0; k<len; k++)
            printf("0x%02X ", *(uint8_t*)(payload+k));

        printf("Rcv(AMID %u) not from expected AMID 0x02\n", id);

    return msg;

So if you forge a packet
0x3F 0x02 'h' 'e' 'l' 'l' 'o' you will see:

Got: 0x68 0x65 0x6C 0x6C 0x6F

and if you send 0x3F 0x05 'h' 'e' 'l' 'l' 'o' you will get:

Rcv(AMID 5) not from expected AMID 0x02

We can sniff what is actually sent on the air (when the AM ID is 0x02) with the promiscuous sniffer where the 16-bit address of my XBee module is set to 0x205B and I'm sending to network address 0x0001:

Frametype: Data
SrcAddrMode: 2
SrcAddr: 0x205B
SrcPANId: 0x3332
DstAddrMode: 2
DstAddr: 0x01
DestPANId: 0x3332
DSN: 131
MHRLen: 9
MHR: 0x61 0x88 0x83 0x32 0x33 0x01 0x00 0x5B 0x20
PayloadLen: 7
Payload: 0x3F 0x02 0x68 0x65 0x6C 0x6C 0x6F
MpduLinkQuality: 107
Timestamp: 72145824


TinyOS with 802.15.4 stack

Now, it is also possible to have a pure 802.15.4 behavior with TinyOS without the extra 2 bytes. As said previously, TinyOS can have 802.15.4 MAC behavior if the tkn154 protocol stack is used. Here we are going to flash one TelosB (noted telos1) mote with the previously tinyos-2.x/apps/tests/tkn154/nonbeacon-enabled/TestPromiscuous application. An other TelosB mote (noted telos2) will be flashed with the tinyos-2.x/apps/tests/tkn154/nonbeacon-enabled/TestIndirectData/Coordinator application. Normally this application use indirect transmission but in our case we will use direct tranmission by removing the TX_OPTIONS_INDIRECT flag in the SendIndirectData() function of In app_profile.h, we use RADIO_CHANNEL= 0x0C and PAN_ID=0x3332. We set DEVICE_ADDRES to 0xFFFF for broadcast and left the COORDINATOR_ADDRESS unchanged. It is possible to indicate a 16-bit destination short address here and you will have to configure the receiving XBee module accordingly (ATMY6288 for instance).

To built the executable, just type in tinyos-2.x/apps/tests/tkn154/nonbeacon-enabled/TestIndirectData/Coordinator:

> make telosb
> make telosb reinstall.0x0001 bsl,/dev/ttyUSB0

Note that it has also been tested with a MicaZ sender. Now, assuming that telos1, the sniffer, is now connected on /dev/ttyUSB0, run:

> java -comm serial@/dev/ttyUSB0:telosb

to have the sniffer running. At the same time, use a WaspMote with the sniffer program with an XBee configured on radio channel 0x0C and PANID 0x3332 to get the frames sent by telos2. Figure below shows a screenshoot of a serial tool (Serial Tools on MACOSX) connected to the WaspMote to get the output of the sniffer WaspMote.

If we look at the last line for instance, the XBee raw data is:

7E 00 2B 81 62 87 2B 02 54 65 73 ...

This is interpreted as:

7E: frame start delimiter
00: Length msb
2B: Length lsb
81: RX packet (16bits)
Source address : 62 87
RSSI   : 2B
Option : 02 Address broadcast
Data : 54 65 73 ...
Checksum: 27

The payload is captured correctly by the WaspMote as it is a pure 802.15.4 payload. If we look at what is captured by telos1 (the promiscuous sniffer), we can see the following information:

Frametype: Data
SrcAddrMode: 2
SrcAddr: 0x6287
SrcPANId: 0x3332
DstAddrMode: 2
DstAddr: 0xFFFF
DestPANId: 0x3332
DSN: 170
MHRLen: 9
MHR: 0x61 0x88 0xAA 0x32 0x33 0xFF 0xFF 0x87 0x62
PayloadLen: 38
Payload: 0x54 0x65 0x73 0x74 0x49 0x6E 0x64 0x69 0x72 0x65 0x63 0x74 0x2C 0x20 0x43 0x6F 0x6F 0x72 0x64 0x69 0x6E 0x61 0x74 0x6F 0x72 0x20 0x74 0x61 0x6C 0x6B 0x69 0x6E 0x67 0x20 0x6E 0x6F 0x77 0x21
MpduLinkQuality: 108
Timestamp: 58237590

Frametype: Acknowledgement
SrcAddrMode: 0
DstAddrMode: 0
DSN: 170
MHRLen: 3
MHR: 0x02 0x00 0xAA
PayloadLen: 0
MpduLinkQuality: 107
Timestamp: 58237720

It is quite interesting to see that the CC2420 of the TelosB in promiscuous mode can be used as a radio sniffer since we also captured the ACK sent back by the XBee module. This is not possible with an XBee because the XBee firmware does not give access to these ACK frames.

When using the TKN154 stack, communicating from a WaspMote or Arduino to a mote under TinyOS becomes straightforward. You can either use 16-bit or 64-bit source address (by setting MY to 0xFFFF) and use either 16-bit or 64-bit destination address. Once again, in any case, you need to set your XBee module in Mac Mode 2 which is the pure 802.15.4 mode (no Digi header).


In summary, we have been able to communicate between various sensor motes: .NET iMote2, TinyOS TelosB & MicaZ, Waspmote and Arduino. It is a bit tricky to make all of them communicating simultaneously but it is possible if some conventions are used to identify the provenance of data packets. With TinyOS, the usage of the tkn154 protocol stack will allow easy bi-directional commnication, i.e. TinyOS<->XBee, otherwise, if ActiveMessage is used, you need to take into account the 2 additional bytes used by the ActiveMessaging system to generate then for XBee->TinyOS and to remove them for TinyOS->XBee.


Here is a nice tutorial on how to install the latest Arduino IDE on Ubuntu Linux.


It may be possible to use the Libelium XBee library with Arduino boards by importing (and probably modifying some lines) the appropriate code into the Arduino IDE.

Hope this helps. Enjoy!
C. Pham.