Introduction to Programming Mica2 Motes Using TinyOS by Valliappan Annamalai
Mica2 mote is one of the most popular and commercially available sensors which is marketed by CrossBow technologies. Applications for Mica2 motes are developed on an operating system called TinyOS. TinyOS is an Event based OS (i.e.) it waits for a stimuli and upon occurrence it executes a set of functions that are specific to that stimuli. It is written using a language called Nested C (NesC). NesC is similar to the C programming language. Sensors are application specific. Therefore, only one application runs on a sensor. Since single application runs on a sensor TinyOS does not support memory management or process management. Since memory manager is not part of TinyOS it discourages applications from allocating or using dynamic memory. Sample sensor application: Each sensor sends a message packet once every 1000ms during tree construction. The mica2 mote has a single processor and a transceiver that works in the 900 Mhz range. Each sensor has a hardware clock. The granularity of this clock is 1ms. Every time the clock ticks, an interrupt is generated. In a traditional OS when an interrupt is generated by the device the kernel executes a function called an Interrupt Service Routine (ISR) that is specific to the interrupt. During kernel initialization this ISR is tied to the interrupt. So when this interrupt is triggered the kernel takes care of transferring control to this ISR. Since sensors are application specific only a single application runs on it at any given time. Therefore, the application should also handle the responsibilities of the kernel, which in this case is to take care of tying up the interrupts to their respective ISRs. In our example, the clock will generate an interrupt once every 1ms and the application has to call its respective ISR. The ISR increments a counter and when the counter reaches 1000 then it will send a tree construction message packet out. Let us assume that the ISR for handling clock ticks is named fired. In almost all applications such kind of timer based interrupts are used in carrying out operations and the transceiver will be used for transmitting message packets. Therefore, instead of writing the code for timer and transceiver in every application it would be better to create separate modules for counting clock ticks and for transmitting packets. Modularization will help in reducing the time taken during application development. Therefore, instead of writing the code in each application for registering the ISRs, definition for the ISRs, and defining functions that interact with the hardware it would be more efficient to have separate modules which will handle them. Like C++ classes in NesC there is a logical way of grouping functions and data together which is called a component. Each component will provide a service. In our example we will have a Timer component that takes care of keeping track of number of clock ticks, a GenericComm component that is used for transmitting packets, and an Application component that has code for calling the send function in GenericComm for sending a packet and for processing message packets it receives (in general Application component has the functionality defined by the user). Each of these components will provide functions for using the services provided by them. The Timer component provides a function called start through which the Application sets the interval after which it would like to perform an activity. In our example this interval is 1000ms. The Timer component is now responsible for counting till 1000 and then signaling the application after its count reaches a multiple of 1000. When the
Application receives the signal it will send a packet by calling a send function in the GenericComm component. To signal the expiry of 1000ms the timer calls the fired function which is implemented by the Application. In this example Timer component provides a service for counting specific number of clock ticks and the application uses the service provided by the Timer. In traditional operating systems, the kernel in conjunction with device drivers will provide a set of services such as sending data through wireless and wired interface, getting input from the user, displaying output, etc. Operating systems like Linux provides a set of basic services and it also has provision for adding new functionality in the form of loadable modules. Based on this concept, TinyOS also has a set of basic services and all other services are added as modules (e.g. Timer, GenericComm, Temperature Sensor, etc.). In our application the application code will consist of the Timer, GenericComm and Application modules in addition to the basic set of services provided by TinyOS. Hardware
1ms 2ms Interrupt
How does the application know which function (signature of the function) it has to implement for handling the interrupt generated by the timer? This can be avoided by ensuring that component Timer publishes the signature of the fired function that it will call in the Application. In C this is accomplished using header files. Now each component will be associated with two header files. One header file will define the functions for which timer has implementation. In our example the Timer component’s start function will be defined in the first header file. The second header file will have the definitions of functions for which the component calling the functions defined in the first header file will have to have the definition. In our example this header file will have the definition for the fired function. In this solution the total number of header files will be twice the total number of components. NesC provides a construct called interface that will help in reducing the total number of header files. An interface defines both the functions that can be called on a service provider component and the service provider component will call on the service user component. In NesC to distinguish between these two functions there are special keywords. The functions implemented by the provider of a service are prefixed with a keyword called command and the functions implemented by the service user and prefixed with an event keyword.
In our example when the Timer calls the fired event handler in the Application, the Application is responsible for calling the send command in the GenericComm. Usually the time taken by hardware devices to complete an operation is high. Therefore, the CPU will be wasted when the hardware sensor is sensing temperature. And so the GenericComm component sends a packet to the transceiver which schedules a function for transmitting the packet at a later time when the sensor is not busy. These functions that can be scheduled for later execution are prefixed with a task keyword. So where is the scheduler? One of the basic functionality provided by TinyOS is a simple FIFO schedule. When the task scheduled by the transceiver completes transmission of data packet it triggers an interrupt to indicate that it has completed transmission. This form of splitting the execution of time consuming operation related to hardware is called split-phase operation. In TinyOS components are built one on top of the other in other words a component will provide a base set of functionality. And components that are built on top of it will use these services and will provide other higher level services. From the figure it is clear that the Hardware is the lowest level and all of the other components are built one on top of the other. As defined earlier a component that uses a service will call command functions in the component that provides the service and the component that provides the service will call the event functions in the component that uses the service (i.e.) a higher level component will call commands in the lower level component upon which it is built and a lower level component will signal event to the component that is built on top of it. In C programming language the entry point or the first function that is called when the executable is loaded into memory is called the main function. Similarly in TinyOS an application’s entry point is the start function in a component called Main. The Main component is the one that gets initial control as soon as the device is switched ON. When creating an application the Main component is connected to the application component. The main component is responsible for passing control to all the components that are connected to it. It passes control through a function called start. In our example the application’s start function will pass control to the timer by calling the timer’s start function and will pass the number of ticks after which it has to call the fired function. The Main component is also responsible for starting and initializing the FIFO scheduler. Hardware
Register ISR 1ms 2ms Interrupt
Figure 2: Hierarchical arrangement of modules and the functions they exchange How do we attach components together (i.e.) how do we connect Application component with
the Timer component? In each application the components are connected in a top level component (i.e.) the Main, Application, Timer and GenericComm components are connected together in a component called a configuration. This way of connecting components to each other is called wiring. This configuration component will have only the wiring code. Figure 3 show how control flows through components in our sample application.
Start Start Start s FIFO Fired event handler posts sendMsg task Schedules The task SendDone Command Event Function Tasks
Fired Start Send
Fired . . . . . Fired . . . . .
Send . . . . . . . . . . SendDone
Figure 3: Control flow in our sample application. BadgeM is the application module