CotsBots
A Platform for Distributed Mobile Robotics

Last updated 29 Jan. 2003

Introduction

In this lesson, you will learn to use the CotsBots distributed robot platform. The lesson assumes you are already familiar with the TinyOS toolset, and will walk you through the Figure8 application (and a bit of the nesC nomenclature). Hopefully by the end, you will be ready to start writing your own CotsBots applications!

About the CotsBots

The goal of the CotsBots project is to use commercial off-the-shelf (COTS) components to build and deploy inexpensive and modular robots. These robots provide a convenient platform to investigate algorithms, cooperation and distributed sensing in large (> 50) robot networks.

Each robot is small (5"x2.5"x3") and costs approximately $200 each (in quantity 50). Each is equipped with on-board processing and wireless radio communication (through the Mica Mote). Currently, sensing boards associated with the TinyOS/NEST project, including a board equipped with light, temperature, buzzer/microphone, 2-axis magnetometer and accelerometer, are available commercially. A common bus allows for the design and addition of robot-oriented sensor boards (for odometry, obstacle avoidance, mapping, etc.). A MotorBoard has been designed to interface boards developed through the NEST project to an off-the-shelf RC toy car. All parts are commercially available and require only minor modifications to build the robot.

The CotsBots platform consists of both a hardware and a software component. This lesson describes a very simple example of using the software. For more information, please see the CotsBotsSpecifications in the contrib/cotsbots/doc directory.

An example application: Figure8

Note that this description is based on Tutorial, Lesson 1. Apologies if it looks the same.

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
  Figure8M.Clock -> ClockC.Clock;
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 the Figure8 application

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.

Conclusion

This tutorial has just scratched the surface of NesC's syntax and CotsBots features. Please see the CotsBots Specifications in contrib/cotsbots/doc to get a more thorough introduction to CotsBots. Have fun!


Using the RobotCmdGUI >