This lesson discusses the basic concept of interfaces in the nesC programming language. It includes an easy to understand example using the Blink application introduced in the TinyOS Lesson 1 Tutorial.
Bug-free, maintainable software is rarely written
first try. As code evolves, pieces are rewritten,
refactored and improved. One large component may
turn into several smaller components. Given
enough common functionality between components,
the behavior of a set of components may be defined
independently of any particular implementation.
nesC
encourages such abstraction by
the use of interfaces. In this tutorial,
we will define an interface (BlinkHandler.nc
)
for handling blinks, and wrap the Blink application
with a program that implements the BlinkHandler
interface.
Let's start with a quote from the nesC reference manual:
Interfaces in nesC are bidirectional: they specify a multi-function interaction channel between two components, the provider and the user. The interface specifies a set of named functions, called commands, to be implemented by the interface's provider and a set of named functions, called events, to be implemented by the interface's user.
For our application, Blink
will be the
interface provider, and Blinker
will be
the interface user.
To provide independence between the component that
wants to blink an LED and the program that provides
the actual blinking behavior, the Blink
configuration needs to be slightly modified to
remove Main:
Blink.nc
configuration Blink { provides { interface StdControl; interface BlinkHandler; } } implementation { components BlinkM, TimerC, LedsC; BlinkM.StdControl = StdControl; StdControl = TimerC; BlinkM.Timer -> TimerC.Timer[unique("Timer")]; BlinkM.Leds -> LedsC; BlinkM.BlinkHandler = BlinkHandler; } |
In addition to removing the Main, we also specify that the BlinkHandler interface is implemented. Since this tutorial is being written during the holiday season, the Leds interface is retained so that Blink can fire a red LED while the BlinkHandler implementation fires a green LED.
In the Blink module file (BlinkM.nc) we need to add a few lines of code:
BlinkM.nc
module BlinkM { provides { interface StdControl; interface BlinkHandler; } uses { interface Timer; interface Leds; } } implementation { ... event result_t Timer.fired() { signal BlinkHandler.Blink(); call Leds.redToggle(); return SUCCESS; } ... default event result_t BlinkHandler.Blink() { return SUCCESS; } ... } |
Now when the timer fires, the BlinkHandler is signaled as well as toggling the red LED. Since the BlinkHandler interface is provided, a default Blink event is implemented.
BlinkHandler.nc
interface BlinkHandler { event result_t Blink (); } |
Since Blink is a component, not a program, it can't run by itself: something, somewhere with a Main needs to wire to it. The Blinker program fits the bill:
Blinker.nc
configuration Blinker { } implementation { components Main, Blink, LedsC, BlinkerM; Main.StdControl -> BlinkerM.StdControl; Main.StdControl -> Blink.StdControl; BlinkerM.Leds -> LedsC; BlinkerM.BlinkHandler -> Blink.BlinkHandler; } |
The final bit is to provide an overriding event for for catching the BlinkHandler.Blink signal. This is implemented like so:
BlinkerM.nc
module BlinkerM { provides { interface StdControl; } uses { interface BlinkHandler; interface StdControl as Blink; interface Leds; } } implementation { // StdControl implementation. ... event result_t BlinkHandler.Blink() { call Leds.greenToggle(); return SUCCESS; } } |
nesC interfaces are indispenable for rapidly and accurately implementing reusable components. This tutorial and accompanying code is intended to help clarify some of the implementation details for creating and implementing interfaces.
Use a parameterized interface to make the yellow LED blink. Hint: Remember to parameterize the default implementation of the Blink event:
BlinkerM.nc
... default event result_t BlinkHandler.Blink[uint8_t id]() { return SUCCESS; } ... |
Comments and corrections to: doolin at ce dot berkeley dot edu.