MAC protocols in ContikiOS

From Contiki
Jump to: navigation, search

Back to Contiki Tutorials

Introduction

This tutorial covers the main features of Medium Access Control (MAC) protocols available in ContikiOS 2.7. Medium Access Control protocols take care of the organization of medium access in wireless networks. They can be interpreted as rules that coordinate when each node is going to transmit/receive packets.

There are a large number of protocols available in the literature, they can usually be classified as contention-based or reservation-based protocols. The first group is based on Carrier Sensing for detecting medium activity and are prone to collisions and lower efficiency; however, they are of easy implementation. The second group is more efficient in terms of throughput and energy, but require precise synchronization and is less adaptable to dynamic traffic. ContikiOS only includes MAC protocols of the first category.

There are about 5 types of MAC layers that can be used in ContikiOS. They are usually implementation of well-known MAC protocols for Wireless Sensor Networks; a few of them are implementations of protocols developed exclusively for ContikiOS, but that were based on previous work present in the literature.

You will learn

Here you are going to be to be more familiar with all MAC protocols implemented on ContikiOS. You are going to understand the organization of MAC protocols, where all files are located and where you should start when you want to modify a protocol. You will also learn the steps you need to take in order to implement your own MAC protocol. And finally, we evaluate two famous MAC protocols: ContikiMAC and X-MAC, showing a few statistics regarding their performance.

MAC, RDC and Framer drivers

The network stack implemented in ContikiOS is a little bit different than the usual 5-layers model typically adopted in TCP/IP. In-between the Physical and the Network layers, where usually is located the MAC, we have 3 different layers: Framer, Radio Duty-Cycle (RDC) and Medium Access Control (MAC). The figure below shows the organization of layers in ContikiOS.

Contiki-layers.jpg

The network layers can be accessed through the global variables NETSTACK_FRAMER, NETSTACK_RDC and NETSTACK_MAC, which are defined in compilation time. All the variables can be found in core/net/netstack.h. Every network layer is defined by the pragmas #define in the beginning of this file.

Framer layer is not a regular layer implementation; it is actually a collection of auxiliary functions that are called for creating a frame with data to be transmited and parsing of data being received. Examples of Framer types can be found in core/net/mac; there are two types of Framer layers: framer-802154.c and framer-nullmac.c.

Radio Duty-Cycle (RDC) layer takes care of the sleep period of nodes. This is the most important layer because it is the one responsible for deciding exactly when the packets will be transmitted and it is responsible for making sure that the node is awake when packets are to be received. RDC protocols' source codes are also located in core/net/mac. Examples of RDC layers that are implemented: contikimac.c, xmac.c, lpp.c, nullrdc-noframer.c, nullrdc.c and sicslowmac.c.

Finally, the MAC layer takes care of addressing and retransmission of lost packets. The source files can be located in core/net/mac; we have only two types of MAC layers available: csma.c and nullmac.c.

Framers

A Framer consists of a collection of auxiliary functions that are called before transmitting a packet and after their reception. The Framer structure, called struct framer can be found in file core/net/mac/framer.h. It is shown below.

struct framer {

  int (* create)(void);
  int (* parse)(void);

};

Function create should create a new frame to be transmitted. While function parse will be called for parsing a received frame.

The Framer type framer-nullmac is defined by files core/net/mac/framer-nullmac.c and core/net/mac/framer-nullmac.h. It should be used together with nullmac (MAC layer). The auxiliary function simply fill in the 2 fields of nullmac_hdr, which are: receiver address and sender address. This is the very basic type of Framer that can be used on ContikiOS.

The Framer type framer-802154 is defined by core/net/mac/framer-802154.c and core/net/mac/framer-802154.h. It creates and parses frames compatible with standard IEEE 802.15.4 (2003). The create and parse functions manipulate all header information abstractedly through the packetbuf structure. The real work is done by functions located in the file core/net/mac/frame802154.c, which are responsible for reading all information from receive/transmit buffer and inserting/extracting them appropriately into packetbuf structure.

RDC protocols

The RDC structure, called struct rdc_driver can be found in file core/net/mac/rdc.h. It is shown below.

/**
 * The structure of a RDC (radio duty cycling) driver in Contiki.
 */
struct rdc_driver {
  char *name;

  /** Initialize the RDC driver */
  void (* init)(void);

  /** Send a packet from the Rime buffer  */
  void (* send)(mac_callback_t sent_callback, void *ptr);

  /** Send a packet list */
  void (* send_list)(mac_callback_t sent_callback, void *ptr, struct rdc_buf_list *list);

  /** Callback for getting notified of incoming packet. */
  void (* input)(void);

  /** Turn the MAC layer on. */
  int (* on)(void);

  /** Turn the MAC layer off. */
  int (* off)(int keep_radio_on);

  /** Returns the channel check interval, expressed in clock_time_t ticks. */
  unsigned short (* channel_check_interval)(void);
};

Function input is called when a new packet has arrived and need to be processed by RDC layer. And functions send and send_list are called for transmitting out-going packets.

There are 2 very basic types of RDC layers implemented in ContikiOS: nullrdc-noframer (core/net/mac/nullrdc-noframer.c) and nullrdc (core/net/mac/nullrdc.c). The first one does not use Framer functions, and transmits/receives data directly to Radio layer (Physical). The developer can take total control over the format of the packets being transmitted.

The second RDC layer (nullrdc) uses the Framer functions for header creation/parsing. It does not save energy and works as a pass-through layer that only transmits a packet and returns the results of such transmission (success or collision).

A third simple RDC layer is sicslowmac.c (core/net/mac/sicslowmac.c). This is a RDC layer with no energy savings and that uses IEEE 8021.5.4 frames.

The three last protocols available in ContikiOS are implementations of popular asynchronous MAC protocols that aim at energy saving; namely X-MAC, ContikiMac and LPP.

X-MAC is a short-preamble protocol from 2006 that was ported to ContikiOS [1]. X-MAC has two different implementation in ContikiOS, which can be found in files core/net/mac/cxmac.c and core/net/mac/xmac.c; the implementation in cxmac.c is a relaxed implementation that does not require stringent timing precision and work on larger number of platforms.

ContikiMac [2] is a protocol that proposes enhancement over X-MAC. The implementation can be found in file core/net/mac/contikimac.c. Finally, LPP (Low Power Probing) is a receiver-initiated low power protocol from 2008 that was also ported to ContikiOS [3]; the code can be found in core/net/mac/lpp.c.

Each of the last three RDC protocols can be used for energy saving, since they operate in duty-cycle mode. The details of each of these protocols is outside the scope of this tutorial and can be found in the references. One important parameter that need to be defined for the operation of these protocols is the channel check rate. This parameter defines the frequency that nodes will listen to the medium to eventually receive data from their neighbors. When some activity is detected the nodes stay awake to receive data; when there is no activity the nodes go back to sleep mode for another duty-cycling period. This parameter that defines the channel check rate is NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE, and it should be set to a power of 2 (2, 4, 8, 16, 32, etc.); its unit is Hz (how many times the nodes will check medium in second). Below we show how to change this parameter as well as others.

MAC protocols

The MAC structure, called struct mac_driver can be found in file core/net/mac/mac.h. It is shown below.

/**
 * The structure of a MAC protocol driver in Contiki.
 */
struct mac_driver {
  char *name;

  /** Initialize the MAC driver */
  void (* init)(void);

  /** Send a packet from the Rime buffer  */
  void (* send)(mac_callback_t sent_callback, void *ptr);

  /** Callback for getting notified of incoming packet. */
  void (* input)(void);
  
  /** Turn the MAC layer on. */
  int (* on)(void);

  /** Turn the MAC layer off. */
  int (* off)(int keep_radio_on);

  /** Returns the channel check interval, expressed in clock_time_t ticks. */
  unsigned short (* channel_check_interval)(void);
};

There are only two types of MAC protocols in ContikiOS: nullmac (core/net/mac/nullmac.c) and csma (core/net/mac/csma). The first one is a simple pass-through protocol that simply calls the appropriate RDC functions. And the second one implements addressing, sequence number and retransmissions. CSMA protocol keep a list of packets to each of the neighbors and calculate statistics such as number of retransmissions, collisions, deferrals, etc. Even though the name, CSMA mean Carrier-Sense Medium Access, the implementation in ContikiOS does not rely on the carrier sensing, because the medium access is performed by RDC protocol.

How to choose and change your protocols

All three stack layers (Framer, RDC and MAC) need to be defined during compilation time. They are define using pragma #define either in the Makefile or in the project-conf.h file. The definitions of which protocols should be used are used by file core/net/netstack.h that effectively binds the NETSTACK_FRAMER, NETSTACK_RDC and NETSTACK_MAC to the protocols that are supposed to be used.

Below it is shown the piece of code responsible for setting the MAC layer.

#ifndef NETSTACK_NETWORK
#ifdef NETSTACK_CONF_NETWORK
#define NETSTACK_NETWORK NETSTACK_CONF_NETWORK
#else /* NETSTACK_CONF_NETWORK */
#define NETSTACK_NETWORK rime_driver
#endif /* NETSTACK_CONF_NETWORK */
#endif /* NETSTACK_NETWORK */

Initially the code checks if NETSTACK_NETWORK was already define (#ifndef NETSTACK_NETWORK). If it was not, the code then checks if NETSTACK_CONF_NETWORK was define. If NETSTACK_CONF_NETWORK was defined, the desired network protocol is used (#define NETSTACK_NETWORK NETSTACK_CONF_NETWORK); otherwise a default protocol is used, which in this case is rime_driver.

Hence, if you want to use a particular network protocol, you can either define NETSTACK_NETWORK directly or define NETSTACK_CONF_NETWORK.

If no definitions are made, by default ContikiOS stack layers will be:

  1. Network layer - rime_driver
  2. MAC layer - nullmac_driver
  3. RDC - nullrdc_driver
  4. Framer - framer_nullmac
  5. Radio - nullradio_driver

The stack layers are usually define inside file contiki-conf.h, which overwrites the configuration of core/net/netstack.h. There is one contiki-conf.h file for each platform; for instance, platform sky has contiki-conf.h located inside folder platform/sky. Below we show the excerpt where stack layers are define:

#ifndef NETSTACK_CONF_MAC
#define NETSTACK_CONF_MAC     csma_driver
#endif /* NETSTACK_CONF_MAC */

#ifndef NETSTACK_CONF_RDC
#define NETSTACK_CONF_RDC     contikimac_driver
#endif /* NETSTACK_CONF_RDC */

#ifndef NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE
#define NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE 8
#endif /* NETSTACK_CONF_RDC_CHANNEL_CHECK_RATE */

#ifndef NETSTACK_CONF_RADIO
#define NETSTACK_CONF_RADIO   cc2420_driver
#endif /* NETSTACK_CONF_RADIO */

#ifndef NETSTACK_CONF_FRAMER
#define NETSTACK_CONF_FRAMER  framer_802154
#endif /* NETSTACK_CONF_FRAMER */

We can see that, if we are using Tmote Sky hardware, by default the stack layer will be composed by csma_driver, contikimac_driver, framer_802154 and cc2420_driver.

Changing the protocols

If you want to change the protocols to be used you define them either inside the Makefile or creating a project configuration file.

In the first case you should change Makefile, inputing the definitions in one single line, as follows:

CONTIKI = /home/user/contiki
DEFINES = NETSTACK_CONF_RDC=cxmac_driver,NETSTACK_CONF_MAC=null_mac

All the protocols that you want to use should be defined in the DEFINES line, using commas to separate them.

The second option is to create a project configuration file. Let's assume the file create was named project-conf.h. This file should be inserted into your Makefile, as follows:

CONTIKI = /home/user/contiki
CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\"

Inside project-conf.h file you can define all protocols you want to use, as follows:

#define NETSTACK_CONF_MAC nullmac_driver
#define NETSTACK_CONF_RDC nullrc_driver
#define NETSTACK_CONF_FRAMER framer_nullmac

If you choose to use projet-conf.h file, you will need to modifed the file contiki-conf.h inside your platform folder. If you are using sky platform, for example, you need to edit file platform/sky/contiki-conf.h. The last part of contiki-conf.h file should be moved to the beginning of the file, as shown below

/* -*- C -*- */

#ifndef CONTIKI_CONF_H
#define CONTIKI_CONF_H

/* include the project config */
/* PROJECT_CONF_H might be defined in the project Makefile */
#ifdef PROJECT_CONF_H
#include PROJECT_CONF_H
#endif /* PROJECT_CONF_H */

How to code your own protocol

We will explain here how to create a new protocol and integrate it into ContikiOS. The protocol can be a Network, MAC, RDC, Framer or Radio protocol; the steps for creating any one of these protocols are similar. We are going to show in details how to create a new MAC, called Modified MAC.

First step is to create both header and c files for Modified MAC protocol. Both files need to be created inside core/net/mac. Our files will be named modifiedmac.c and modifiedmac.h.

The file modifiedmac.h is shown below. In this file we only declare an external variable that will reference our MAC protocols, called modifiedmac_driver.

#ifndef __MODIFIEDMAC_H__
#define __MODIFIEDMAC_H__

#include "net/mac/mac.h"

extern const struct mac_driver modifiedmac_driver;

#endif /* __MODIFIEDMAC_H__ */

An initial version of file modifiedrdc.c is shown below. It only contains the declaration of all functions that must be implemented in a MAC driver.

#include "net/mac/modifiedmac.h"
#include "net/netstack.h"

static void
send_packet(mac_callback_t sent, void *ptr)
{
}

static void
packet_input(void)
{
}

static int
on(void)
{
  return 0;
}

static int
off(int keep_radio_on)
{
  return 0;
}

static unsigned short
channel_check_interval(void)
{
  return 0;
}

static void
init(void)
{
}

const struct mac_driver modifiedmac_driver = {
  "modifiedmac",
  init,
  send_packet,
  packet_input,
  on,
  off,
  channel_check_interval,
};

The next step is to include our source code into ContikiOS make system. We just need to edit the file core/net/mac/Makefile.mac and include the file modifiedmac.c in the list of Contiki source files. After editing, Makefile.mac must look like this:

CONTIKI_SOURCEFILES += cxmac.c xmac.c nullmac.c lpp.c frame802154.c sicslowmac.c nullrdc.c nullrdc-noframer.c mac.c
CONTIKI_SOURCEFILES += framer-nullmac.c framer-802154.c csma.c contikimac.c phase.c modifiedmac.c

Below we show the functions that should be changed in order to make our MAC protocol functional. It is going to work exactly as a Null MAC protocol and we are counting the number of packets received and transmitted.

uint16_t n_pkts_transmitted = 0;
uint16_t n_pkts_received = 0;

static void
send_packet(mac_callback_t sent, void *ptr)
{
  n_pkts_transmitted++;
  NETSTACK_RDC.send(sent, ptr);
}

static void
packet_input(void)
{
  n_pkts_received++;
  NETSTACK_NETWORK.input();
}

static int
on(void)
{
  return NETSTACK_RDC.on();
}

static int
off(int keep_radio_on)
{
  return NETSTACK_RDC.off(keep_radio_on);
}

Now you are able to use your create your own rules for addressing, framezation and all functions that should be performed by a MAC protocol.

If you want to use your new protocol, you need to change the MAC layer that will be used by ContikiOS, following the instructions above, and setting definition NETSTACK_CONF_MAC as modifiedmac_driver (#define NETSTACK_CONF_MAC modifiedmac_driver).

Evaluating ContikiMAC and XMAC protocols

We are going to evaluate the performance of the two most famous RDC protocols in ContikiOS, the ContikiMAC and X-MAC. We want to evaluate the Packet Reception Rate (PRR), which is the percentage of received packets in an interval; and also the average duty-cycle of sensor nodes. The PRR is related to the maximum throughput the network is able to handle; with lower PRR more packets are dropped and the total amount of data that can be transfered to the sink node is small. Also, the duty-cycle impacts the average power consumption and the lifetime of nodes, since the largest energy savings are achieved when nodes are sleeping; the lower the duty-cycle the longest will be the node's lifetime.

We considered the firmware in the folder examples/ipv6/rpl-udp/, which is a firmware based on IPv6 and RPL for UDP packet transmission. In the same folder there is one simulation file called rpl-udp-powertrace.csc that is composed of 1 sink node and 30 sensor nodes and traces the energy consumption of all nodes, and also keeps track of PRR values throughout the simulation. We considered the firmwares udp-server.c and udp-client.c and reduced the data transmission interval in udl-client.c to 1 second. The line below shows where the code of udp-client.c was changed:

#ifndef PERIOD
#define PERIOD 1
#endif

By default the simulation is executed with ContikiMAC. We let the simulation run for 5 minutes before extracting the desired statistics. After simulation for ContikiMAC, we changed the RDC protocol, as described before, to X-MAC. And the same statistics were obtained. The figures below show the average duty-cycle of each node for both protocols. The figure on the left shows results of ContikiMAC and the one on the right shows results of X-MAC.

Power contikimac.png Power xmac.png

We can see that ContikiMAC reduces the average duty-cycle from 17% to 9%, which drastically impacts the energy consumption of the nodes.

The PRR statistics were also analysed for all 30 sensor nodes. The average PRR of ContikiMAC was 50.48% and the PRR of X-MAC was 30.38%. We can conclude that ContikiMAC implementation is more energy and throughput efficient than X-MAC.

References

1- Buettner, Michael, Gary V. Yee, Eric Anderson, and Richard Han. "X-MAC: a short preamble MAC protocol for duty-cycled wireless sensor networks." In Proceedings of the 4th international conference on Embedded networked sensor systems, pp. 307-320. ACM, 2006.

2 - Dunkels, Adam. "The contikimac radio duty cycling protocol." (2011).

3 - Liang, Chieh-Jan Mike, and Andreas Terzis. "Koala: Ultra-low power data retrieval in wireless sensor networks." In Proceedings of the 7th international conference on Information processing in sensor networks, pp. 421-432. IEEE Computer Society, 2008.



Back to Contiki Tutorials

Edited by: Pedro