Home | Support | Hardware | Software | Download | Media and Pubs | Related Work

Software

Software may be downloaded from the Download Page. To contribute to the CotsBots software, check out the directions here.

TinyOS

TinyOS is an event-driven operating system designed for sensor network nodes with very limited resources. In an event-driven operating system the software responds to events which can be propagated from the interrupt level to much higher levels in the system. For example, contact made by a whisker sensor can signal a hardware interrupt which can further signal an obstacleDetect event in a higher (abstracted) level. Many robot architectures used to describe behavior are event-driven, making TinyOS an excellent operating system for robot algorithms.

TinyOS offers several other advantages including the ability to abstract software modules. For example, much of the software described below is written so that the application writer doesn't need to know what hardware they are driving and the specifics on how to drive it. Instead, abstracted functionality such as setSpeed, setDir and setTurn can be provided to simplify the software writing process.

TinyOS uses NesC, which is a modified version of C. The extensions primarily support the structural aspects of TinyOS.

CotsBots Software

Software for the CotsBots can be divided into two major sub-sections: software that runs on the Mica mote, and software that runs on the MotorBoard.

Mica Mote Software

In the nesC language, the primary means of abstraction is through interfaces. An interface defines commands that may be called and events that may be signaled. A component can implement or use interfaces. Hopefully, this will be demonstrated more fully in the descriptions below.
Interfaces
The primary interface of importance in writing CotsBots code on the NEST mote is Robot.nc.
interface Robot {
  command result_t init();
  command result_t setSpeed(uint8_t speed);
  command result_t setDir(uint8_t direction);
  command result_t setTurn(uint8_t turn);
  command result_t setSpeedTurnDirection(uint8_t speed, uint8_t turn, uint8_t dir);
}
This interface provides the basic features required to drive the robot from an application. Robot.init() is required to setup the communication components that will be described later. Robot.setSpeed(speed) takes an argument from 0-255 (where 0 is OFF and 255 is the top speed). A typical, controllable speed lies from 20-100. Robot.setDir(direction) takes a direction argument (FORWARD = 1, REVERSE = 0). Robot.setTurn(turn) takes an argument from 0-60, where 0 is full left and 60 is full right (STRAIGHT = 30).

Because an application writer will often wish to change all three commands at once, a Robot.setSpeedTurnDirection(speed, turn, direction) command is also provided. This is a preferred method over using three separate commands in turn as will be described in the implementation below.

Components
The contrib\cotsbots\tos\system\RobotC.nc configuration and tos\system\RobotM.nc implementation provide the Robot interface to the application developer. In this case, RobotC.nc uses whichever means of communication is desired (UART, I2C, etc) to communicate the above commands in the Robot interface to the MotorBoard. More on this communication is described below.

An important troubleshooting component to note in this implementation is the time required to send a message over the UART/I2C channel. Buffering is provided for two messages, but if the user tries to send three commands simultaneously, it is likely that the last one will be lost.

See the README in contrib\cotsbots\tos\system for more information about this and other components.

Motorboard Software

The MotorBoard uses an Atmel ATmega8L microcontroller to interface the NEST mote with the motor drivers. To make use of pre-written code in the NEST community, the processor runs TinyOS. Several driver components and interfaces have been written specifically to abstract the MotorBoard functionality and are listed below.

The contrib/cotsbots/tos/lib/MotorBoard.h file sets up many of the definitions used in applications and in the messaging system that will be described later.

Interfaces
interface HPLMotor {
  command result_t init();
  command result_t setSpeed(uint8_t speed);
  command result_t setDir(uint8_t direction);
  command uint8_t getSpeed();
  command uint8_t getDir();
}
The HPLMotor interface provides that basic features with which one can drive a motor. An initialization using HPLMotor.init() is required. HPLMotor.setSpeed(speed) takes an argument from 0-255 (where 0 is OFF and 255 is the top speed). A typical, controllable speed lies from 20-100. HPLMotor.setDir(direction) takes a direction argument (FORWARD = 1, REVERSE = 0). Get functions are also given to retrieve the current speed and direction.
				
interface Servo {
  command result_t init();
  command result_t setTurn(uint8_t turn);
  event result_t debug(uint16_t data);
}
The Servo interface provides the features with which to drive a servo motor. Notice that the interface abstracts the implementation details and I could therefore drive a standard hobby servo using this interface or the servo on the Mini-Z Racer. Servo.setTurn(turn) takes an argument from 0-60, where 0 is full left and 60 is full right (STRAIGHT = 30) for the implementation that will be described below in ?6.3.2. However, an 8-bit argument is provided so that positions from 0 to 255 might be possible in a different implementation. An initialization using Servo.init() is required.

In addition, an interesting feature is provided in the Servo interface: the debug event. Because it is often necessary to review the control signals given to the servo for calibration, etc. the debug event is provided to push whatever information is desired to a higher level component. This may then be sent back to the radio, or used directly in a higher component.

interface EEPROM {
  command result_t init();
  command uint8_t read(uint8_t address);
  command result_t write(uint8_t address, uint8_t data);
  event result_t writeDone();
}
This EEPROM interface differs from the EEPROMRead and EEPROMWrite interfaces provided in the standard TinyOS-1.x release. This is done for simplicity in accessing the on-chip EEPROM v. the off-chip EEPROM that those interfaces are designed to handle. EEPROM.read(address) will return the byte read from a particular memory address. EEPROM.write(address) will write a value to this address. EEPROM.init() is required.

An important use for this interface is in the RealMain.nc component. Because the internal RC oscillator on the ATmega8L, it is necessary to calibrate it well enough to send messages over the UART and I2C lines. This calibration value is always stored in address 0 of the EEPROM and is read and stored into the calibration register as the first thing in RealMain.nc.

interface ServoCalibration {
  command result_t setKp(uint8_t Kp);
  command result_t setKi(uint8_t Ki);
  command result_t setStraight(uint8_t straight);
  command result_t setDebug(uint8_t state);
}
ServoCalibration provides an interface for which to calibrate the Mini-Z Racer servo. ServoCalibration.setStraight(straight) writes a new "straight" reference value to the EEPROM. ServoCalibration.setKp(Kp) and ServoCalibration.setKi(Ki) allow a user to set the gain parameters for the PI control loop controlling the Mini-Z Racer servo. ServoCalibration.setDebug(state) allows the user to decide whether the Servo.debug() event is fired or not. These functions may be accessed through the RobotCmdGUI.
Components
The HPLMotor1/2 components implement the HPLMotor interface on two separate channels. Motors are controlled using the PWM lines on the ATmega8L's Timer1. PWM is currently set up at 8-bit, phase correct, TOP mode and the frequency is currently set at approximately 500Hz. This may be slowed down or sped up as applications require. HPLMotor.setSpeed(speed) takes an argument from 0-255 (where 0 is OFF and 255 is the top speed). A typical, controllable speed lies from 20-100. HPLMotor.setDir(direction) takes a direction argument (FORWARD = 1, REVERSE = 0).

MZServo implements both the Servo and ServoCalibration interfaces. MZServo uses a PI control loop (the parameters for which can be set using the ServoCalibration interface) to match the current position read from the ADC to the desired turn radius. Servo.setTurn(turn) takes an argument from 0-60, where 0 is full left and 60 is full right (STRAIGHT = 30).

HPLEEPROM implements the EEPROM interface. For more information on how this is done, please see the ATmega8L datasheet.

The MotorClock component implements the standard Clock interface (although not with all of the features provided). The MotorClock is very rudimentary and is run off of Timer0 which is only equipped with an overflow interrupt. Documentation is provided within the MotorClock.nc component itself on achieving different speeds.

MotorTest runs through a set sequence of speeds to test each motor. It implements a StdControl interface and may be started and stopped through the RobotCmdGUI. See the component file for more information.

MotorBoard Messaging

Because the MotorBoard controls the motor features through the ATmega8L, it is necessary to define a messaging protocol for communication between the NEST mote and MotorBoard.
Message Structure
The message structure used to communicate to and from the motor board is defined as follows:
				
typedef struct MOTOR_Msg {
  uint8_t addr;
  uint8_t type;
  uint8_t data[MOTORDataLength];
} MOTOR_Msg;
Each motor board may be assigned a specific address using the TOS_LOCAL_ADDRESS field (or make motor install.x). This allows the motor boards to be stacked to achieve more than two channels if required.

The type of message could include anything from accelerometer data to a setSpeed command, etc. Some commonly used commands are defined in the MotorBoard.h file for convienence.

The data array provides arguments for commands or other data corresponding to the message type. The length of this array is variable dependent on the MOTORDataLength field also in MotorBoard.h. For example, if MOTORDataLength = 2 (Default), the data array is two bytes. Two bytes has been enough for everything used thus far, although a variable length packet structure is being considered for a future CotsBots release.

Because the data array can be cast to whatever types and size the programmer wishes, this provides the most flexible means of sending data

Interfaces
interface MotorReceiveMsg {
  event MOTOR_MsgPtr receive(MOTOR_MsgPtr m);
}
The MotorReceiveMsg interface provides only one event - MotorReceiveMsg.receive(m). This interface is very similar to the standard TinyOS-1.x ReceiveMsg interface, but uses MOTOR_Msg's instead of TOS_Msg's. It is required that a pointer to a MOTOR_Msg buffer be returned on completion of this event.
interface MotorSendMsg {
  command result_t send(MOTOR_MsgPtr msg);
  event result_t sendDone(MOTOR_MsgPtr msg, result_t success);
}
MotorSendMsg is also analogous to its SendMsg cousin and provides both a MotorSendMsg.send(msg) command and a MotorSendMsg.sendDone(msg, success) event for split-phase operation. Notice that these interfaces are abstract from whichever bus is being used to send and receive messages.
Components
MotorPacket provides the MotorReceive/SendMsg interfaces as well as a StdControl interface from which the communication subsystems must be initialized. Please see comments in the component code to get a better idea of what this component does.

The UARTMotorPacket component is the configuration component above the MotorPacket module. It sets up the use of the UART instead of another ByteComm interface such as I2C.

UART v. I2C
Unfortunately, as of the initial release, only the UART communication is available. However, an I2CMotorPacket component should be available shortly.

Some CotsBots Applications

Some of the CotsBots Applications available in the contrib\cotsbots\apps directory on Sourceforge.

Figure8:

Figure8 runs the tos/lib/Robot/Figure8M component with the default values. The robot should move in a figure8 pattern, although this depends tremendously on surface, battery voltage, etc. Because there is no position feedback currently being used, this pattern is completely open-loop and must be calibrated.

MotorBoardTop:

It is expected that this component run on the UC Berkeley motor board equipped with an Atmel ATmega8L microcontroller. MotorBoardTop provides access to all of the robot-specific hardware drivers as well as the communication interface to the master mote.

MotorBoardTopM provides a top-level message parser to implement the commands provided by tos/lib/Robot/MotorBoard.h. It also has the ability to send messages to the master mote (debug or otherwise).

If a platform other than the Mini-Z is used (differential drive robot for example), the implementation details of how the robot turns should be provided here. For example, the current setTurn message sets the turning angle of the Mini-Z servo. However, for a differential-drive robot, setting the turning angle would involve moving the two motors at different speeds and directions. It might also be better to write a "controller" interface here instead that takes in velocity and rate of rotation (and then map this to control of the motors).

Obstacle:

This is an obstacle-avoidance program that utilizes the accelerometer on the mica sensorboard to determine the presence of an obstacle. When an obstacle is detected, the robot will back up, turn, and go in a new direction.

TestMotorBoard:

TestMotorBoard tests the interface to the motorboard. It receives and sends messages over the radio interface as well as over the motor board interface. Incoming radio packets are transformed into motor packets. In addition, packets sent from the motor board (which may contain accelerometer or servo debugging information) are relayed back over the radio.

TestNavigation:

TestNavigation tests the NavigationC/M component. It accepts a radio message that tells the robot its current x-y position in cm, its current heading in radians (-pi,pi) and the position it should go to. TestNavigation calls the Navigation.navigate command to move the robot from point A to point B. In addition, debugging capability is provided in the Navigation component to check the calculations at various steps.

BeepDiffusion:

The goal of BeepDiffusion is to spread robots out in a confined space. Each robot beeps in turn and if the other robots hear it, they move forward. It uses obstacle avoidance through the accelerometer component (Obstacle above). In this way, the robots act as gas particles diffusing throughout a space -- bouncing off walls. In this case though, if no robots can hear the others, they stop and consider themselves spread far enough. There are still some problems with this application -- namely getting consistent beep radii. BeepDiffusion has two components -- BeepDiffusionC and StartDiffusion. BeepDiffusion is the main application to be run on the robots and StartDiffusion injects packets into the network to start the application.

A CotsBots Simulator

TOSSIM is a TinyOS simulator available in the standard TinyOS release. However, components have not yet been written to adapt it for use with the CotsBots. It would also be interesting to integrate CotsBots with Player/Stage.