Getting Started | Lesson 1 | Lesson 2 | Lesson 3 | Lesson 4 | Lesson 5 | Hardware

Lesson 2 - A Simple Application

Introduction

This is very similar to Lesson 1 in the TinyOS Tutorial Lessons with a slant towards the CotsBots.

Figure8 - A Simple Application

Let's look at a concrete example: the simple test program "Figure8" found in contrib.cotsbots.apps.Figure8. This application will run the robot in a figure8-like pattern.

Figure8 consists of two components: a module, called "Figure8M.nc", and a configuration, called "Figure8C.nc". Remember that all applications require a single top-level configuration, which is typically named after the application itself. In this case Figure8.nc is the configuration for the Figure8 application and the source file that the NesC compiler uses to generate the executable for the mote. Figure8M.nc provides the implementation of the Figure8 application. As you might guess, Figure8C.nc is used to wire the Figure8M.nc module to other components that the Figure8 application requires.

The reason for the distinction between modules and configurations is to allow a system designer to quickly "snap together" applications. For example, a designer could provide a configuration that simply wires together one or more modules, none of which she actually designed. Likewise, another developer can provide a new set of "library" modules that can be used in a range of applications. A concrete example of this is when Figure8C.nc is re-used in the TestMotorBoard application.

The Figure8M.nc Module

Let's look first at the module Figure8M.nc:

Figure8M.nc

	module Figure8M {
	  provides interface StdControl;
	  provides interface Figure8Calibration;
	  uses {
		interface Robot;
		interface Clock;
		interface Leds;
	  }
	}
	// Continued below...

The first part of the code states that this is a module called Figure8M. Figure8M provides two interfaces: StdControl and Figure8Calibration. It uses three interfaces: Robot, Clock, and Leds.

The idea of providing and requiring interfaces should be familiar to anyone who has programmed in an object-oriented language such as Java. First consider the clause "provides interface StdControl". This means that Figure8M implements the StdControl interface. StdControl is a common interface used to initialize and start TinyOS components. If we look at tos/interfaces/StdControl.nc:

StdControl.nc

	interface StdControl {
	  command result_t init();
	  command result_t start();
	  command result_t stop();
	}

We see that StdControl defines three commands, init(), start(), and stop(). init() is called when a component is first initialized, and start() when it is started, that is, actually executed for the first time. stop() is called when the component is stopped, for example, in order to power off the device that it is controlling. init() can be called multiple times, but will never be called after either start() or stop are called. Specifically, the valid call patterns of StdControl are init* (start | stop)* All three of these commands have "deep" semantics; calling init() on a component will make it call init() on all of its subcomponents. A good example is the component controlling the mote radio: init() is called when the component is initialized, start() is called to enable power to the radio, and stop() to shut off power. Because the radio power is only needed when communicating, start() and stop() may be called multiple times to power on or power off the radio.

A command is just a function that may be defined in an interface. If an interface defines a command, then any component that provides that interface must implement that function. In the case of Figure8M, then, we expect this module to implement the init(), start(), and stop() commands, which it does as we will see below.

Likewise, if an interface defines a command, then any component that uses that interface may call the function. Figure8M uses three interfaces, Robot, Clock, and Leds. (The code for these can be found in tos/interfaces.) For example, Robot.nc defines a bunch of commands like setSpeed(speed), setTurn(turn), and so forth, which set the speed and turning angle of the robot. Because Figure8M uses the Robot interface, it can invoke any of these commands. (Keep in mind, however, that Robot is just an interface: it specifies no implementation. We will see later how the configuration, Figure8C.nc, is used to wire Figure8M's use of the Robot interface to a concrete implementation of that interface.)

Let's look at the rest of Figure8M.nc to see how this all fits together:

Figure8M.nc, continued

	implementation {
	
	  uint8_t ticks;
	  uint8_t TurnRight;
	  uint8_t TurnStraight1;
	  uint8_t TurnLeft;
	  uint8_t TurnStraight2;
	  uint8_t Speed;
	
	  command result_t StdControl.init() {
	
		/* Set default constants -- could store on EEPROM for Mica*/
		Speed = 70;
		TurnRight = 3;
		TurnStraight1 = 8;
		TurnLeft = 11;
		TurnStraight2 = 16;
	
		ticks = 0;
		call Robot.init();
		return SUCCESS;
	  }
	
	  /* Start timer to fire once/sec */
	  command result_t StdControl.start() {
		call Clock.setRate(TOS_I8PS, TOS_S8PS);
		call Robot.setSpeedTurnDirection(Speed, STRAIGHT, FORWARD);
		return SUCCESS;
	  }
	
	  /* Stop timer and reset ticks, speed, direction and turn */ 
	  command result_t StdControl.stop() {
		call Clock.setRate(TOS_I0PS, TOS_S0PS);
		ticks = 0;
		call Robot.setSpeedTurnDirection(OFF,STRAIGHT,FORWARD);
		return SUCCESS;
	  }
	
	  /* Determine if I should turn */
	  event result_t Clock.fire() {
		ticks++;
		
		if (ticks == 1)
		  call Robot.setSpeedTurnDirection(Speed, STRAIGHT, FORWARD);
		else if (ticks == TurnRight)
		  call Robot.setSpeedTurnDirection(Speed, RIGHT, FORWARD);
		else if (ticks == TurnStraight1)
		  call Robot.setSpeedTurnDirection(Speed, STRAIGHT, FORWARD);
		else if (ticks == TurnLeft)
		  call Robot.setSpeedTurnDirection(Speed, LEFT, FORWARD);
		else if (ticks == TurnStraight2) {
		  call Robot.setSpeedTurnDirection(Speed, STRAIGHT, FORWARD);
		  ticks = 0;
		}
	
		return SUCCESS;
	  }
	// Continued below...

This is simple enough. As we see the Figure8M module implements the StdControl.init(), StdControl.start(), and StdControl.stop() commands, since it provides the StdControl interface. It also implements the Clock.fire() event, which is necessary since BlinkM uses the Clock interface. Several variables are also declared for use throughout the module.

init() simply initializes the internal state of the module and initializes the Robot sub-module. start() invokes Clock.setRate() to set the clock rate to 8 ticks per second and uses Robot.setSpeedTurnDirection() to initialize the state of the robot. stop() sets the clock rate to zero ticks per second, which is equivalent to disabling the clock and stops the robot as well. Each time fire() is invoked, the ticks variable is compared with the times at which we would like to to turn and the robot state is set appropriately. Voila!

If you look at the application code, you will notice that the implementation of the Figure8Calibration interface in Figure8M as well. Because the Figure8 component is open-loop (i.e. there is no feedback on what the robot's current position is -- everything is based on time only), it is often necessary to calibrate the Figure8 -- setting the proper speed and turn times. Because the Figure8Calibration interface is not actually used in Figure8.nc, we will not concern ourselves with it here.

The Figure8C.nc Configuration

Now let's look at Figure8C.nc, the configuration for this application:

Figure8C.nc

	configuration Figure8C {
	  provides interface StdControl;
	  provides interface Figure8Calibration;
	}
	implementation {
	  components Figure8M, RobotC, ClockC, LedsC;
	
	  StdControl = Figure8M.StdControl;
	  Figure8Calibration = Figure8M.Figure8Calibration;
	
	  Figure8M.Robot -> RobotC;
	  Figure8M.Clock -> ClockC;
	  Figure8M.Leds -> LedsC;
	}

The first thing to notice is that the syntax of a configuration is slightly different, in that it doesn't contain any code. The first four lines,

	  configuration Figure8C {
		provides interface StdControl;
		provides interface Figure8Calibration;
	  }
simply state that this is a configuration called Figure8C. In this case, our configuration also provides the StdControl and Figure8Calibration interfaces. This is important, because it allows a separate application (such as TestMotorBoard) to use the Figure8C.StdControl interface without knowing the components underneath it. This is important to keep in mind: a configuration can require and provide interfaces!

The "components" line specifies the set of components that this configuration references, in this case Figure8M, RobotC, ClockC, and LedsC. In many ways this line is redundant since it's obvious from the rest of the file which components are referenced by the configuration. The components line is included only for completeness, but is required by the NesC compiler.

The lines

	  StdControl = Figure8M.StdControl;
	  Figure8Calibration = Figure8M.Figure8Calibration;
are used to essentially "pass through" these interfaces from their respective implementations in Figure8M.

	  Figure8M.Robot -> RobotC;
	  Figure8M.Clock -> ClockC;
	  Figure8M.Leds -> LedsC;
are used to bind the use of the Robot, Clock, and Leds interfaces in Figure8M to their respective implementations.

There are two confusing things about these lines. First of all, the Clock on the left side of the arrow is referring to the interface called Clock (tos/interfaces/Clock.nc), while the ClockC on the right side of the arrow is refering to the implementation module called ClockC, found in tos/system/Clock.nc. Remember that the arrow always binds interfaces (on the left) to implementations (on the right). ClockC is a mnemonic for "clock component".

The second confusing point is that the line

	  Figure8M.Clock -> ClockC;
is really shorthand for S That is, because Figure8M is using the interface Clock.nc, we are trying to bind it to an implementation (in the module ClockC.nc) of the same interface. If no interface name is given on the right side of the arrow, the NesC compiler tries to bind to the same interface as on the left side of the arrow.

Compiling Figure8

If you are in the TinyOS source tree, compiling the Figure8 application is as simple as typing
	  make mica
in the contrib/cotsbots/apps/Figure8 directory. Of course this doesn't tell you anything about how the NesC compiler is invoked.

Programming the Mote and Running Figure8

Now that we've compiled the application it's time to program the mote and run it. To download your program into the mote, place the mote board (or mote and sensor stack) into the bay on the programming board.

Plug the 32-pin connector into the parallel port of a computer configured with the TOS tools, using a standard DB32 parallel port cable.

Type: make install mica. If you get the error:

	  uisp -dprog=dapa --erase 
	  pulse
	  An error has occurred during the AVR initialization.
	   * Target status:
		  Vendor Code = 0xff, Part Family = 0xff, Part Number = 0xff
	
	  Probably the wiring is incorrect or target might be `damaged'.
	  make: *** [install] Error 2
check whether the power is on. You can also get this error message if the mote is low on batteries (if you are using batteries), or if the wrong version of the uisp programming utility is installed (be sure to use the version in the NEST distribution).

If you are using an IBM ThinkPad, it may be necessary to tell the tools to use a different parallel port. You can do this by adding the line

	  HOST = THINKPAD
before the include statement in contrib/cotsbots/apps/Figure8/Makefile.

If the installation is successful you should see something like the following:

	  compiling Figure8 to a mica binary
	  ncc -board=micasb -o build/mica/main.exe -Os -target=mica  -Wall -Wshadow -DDEF_TOS_AM_GROUP=0x7d -finline-limit=200 -fnesc-cfile=build/mica/app.c  Figure8.nc -lm
	  avr-objcopy --output-target=srec build/mica/main.exe
	  build/mica/main.srec
		  compiled Figure8 to build/mica/main.srec
		  installing mica binary
	  uisp -dprog=dapa  --erase 
	  pulse
	  Atmel AVR ATmega128 is found.
	  Erasing device ...
	  pulse
	  Reinitializing device
	  Atmel AVR ATmega128 is found.
	  sleep 1              
	  uisp -dprog=dapa  --upload if=build/mica/main.srec
	  pulse
	  Atmel AVR ATmega128 is found.
	  Uploading: flash
	  sleep 1              
	  uisp -dprog=dapa  --verify if=build/mica/main.srec
	  pulse
	  Atmel AVR ATmega128 is found.
	  Verifying: flash

You can now test the program by unplugging the mote from the programming board, attaching it to the CotsBots robot as shown below, and turning on the power switch for both the robot and the Mica.

Mica mote attached to the CotsBots Platform Power Switch for the CotsBots

Typing make clean in the Figure8 directory will clean up the compiled binary files.

Go to Lesson 3