LoCmdMain.c

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/irccm/LoCmdMain.c
00003    \brief Robolocust control program for the iRobot Create's Command
00004    Module.
00005 
00006    This file defines the main function for a control program meant to be
00007    run on the iRobot Create's Command Module. This program is designed to
00008    listen to the Command Module's USB port for incoming motor commands
00009    from the higher (C++) layers of the lobot controller and convert them
00010    into the equivalent sequence of Open Interface opcode and operand
00011    bytes to be sent to the iRobot Create.
00012 */
00013 
00014 /*
00015  ************************************************************************
00016  * The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   *
00017  * by the University of Southern California (USC) and the iLab at USC.  *
00018  * See http://iLab.usc.edu for information about this project.          *
00019  *                                                                      *
00020  * Major portions of the iLab Neuromorphic Vision Toolkit are protected *
00021  * under the U.S. patent ``Computation of Intrinsic Perceptual Saliency *
00022  * in Visual Environments, and Applications'' by Christof Koch and      *
00023  * Laurent Itti, California Institute of Technology, 2001 (patent       *
00024  * pending; application number 09/912,225 filed July 23, 2001; see      *
00025  * http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     *
00026  ************************************************************************
00027  * This file is part of the iLab Neuromorphic Vision C++ Toolkit.       *
00028  *                                                                      *
00029  * The iLab Neuromorphic Vision C++ Toolkit is free software; you can   *
00030  * redistribute it and/or modify it under the terms of the GNU General  *
00031  * Public License as published by the Free Software Foundation; either  *
00032  * version 2 of the License, or (at your option) any later version.     *
00033  *                                                                      *
00034  * The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  *
00035  * that it will be useful, but WITHOUT ANY WARRANTY; without even the   *
00036  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      *
00037  * PURPOSE.  See the GNU General Public License for more details.       *
00038  *                                                                      *
00039  * You should have received a copy of the GNU General Public License    *
00040  * along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   *
00041  * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   *
00042  * Boston, MA 02111-1307 USA.                                           *
00043  ************************************************************************
00044 */
00045 
00046 /*
00047    Primary maintainer for this file: mviswana usc edu
00048    $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/irccm/LoCmdMain.c $
00049    $Id: LoCmdMain.c 13769 2010-08-08 01:34:02Z mviswana $
00050 */
00051 
00052 /*------------------------------ HEADERS ------------------------------*/
00053 
00054 // lobot headers
00055 #include "LoRemote.h"
00056 #include "LoBumps.h"
00057 #include "LoCliffs.h"
00058 #include "LoWheelDrops.h"
00059 #include "LoDrive.h"
00060 #include "LoSensors.h"
00061 #include "LoBeep.h"
00062 #include "LoUtils.h"
00063 #include "LoIO.h"
00064 #include "LoTimer.h"
00065 #include "LoCMInterface.h"
00066 #include "LoOpenInterface.h"
00067 
00068 // AVR headers
00069 #include <avr/io.h>
00070 #include <avr/interrupt.h>
00071 
00072 /*------------------------------- TYPES -------------------------------*/
00073 
00074 // Rather than use a large switch-case construct to respond to high-level
00075 // commands, we use a dispatch table containing function pointers. This
00076 // type defines a command handler, i.e., a function that implements the
00077 // code required to handle a high-level command. Such a function will
00078 // take one parameter: a 16-bit word. If a particular command doesn't
00079 // need any operands, its handler is free to ignore this input parameter.
00080 typedef void (*CommandHandler)(int op) ;
00081 
00082 // Each high-level command is identified by a single character. We use the
00083 // following type to map these command IDs to their corresponding
00084 // handlers.
00085 typedef struct {
00086    char cmd ;
00087    CommandHandler function ;
00088 } CommandInfo ;
00089 
00090 /*------------------------------ GLOBALS ------------------------------*/
00091 
00092 // As mentioned earlier, we use a dispatch table rather than a
00093 // switch-case construct to handle the different high-level commands
00094 // supported by the Robolocust controller.
00095 //
00096 // The following array is that dispatch table.
00097 //
00098 // DEVNOTE: To add new commands to the system, implement an appropriate
00099 // handler function and then add its function pointer and other relevant
00100 // info to this table.
00101 static const CommandInfo g_command_map[] = {
00102    {LOBOT_CMD_NOP,      &lo_nop},
00103    {LOBOT_CMD_FORWARD,  &lo_forward},
00104    {LOBOT_CMD_REVERSE,  &lo_reverse},
00105    {LOBOT_CMD_STOP,     &lo_stop},
00106    {LOBOT_CMD_LEFT,     &lo_left},
00107    {LOBOT_CMD_RIGHT,    &lo_right},
00108    {LOBOT_CMD_STRAIGHT, &lo_straight},
00109    {LOBOT_CMD_SPIN,     &lo_cmd_spin},
00110    {LOBOT_CMD_ENABLE_REAR_BUMPS,  &lo_enable_rear_bumps},
00111    {LOBOT_CMD_DISABLE_REAR_BUMPS, &lo_disable_rear_bumps},
00112 } ;
00113 
00114 /*----------------------------- CONSTANTS -----------------------------*/
00115 
00116 // How many different commands can this control program handle?
00117 static const char LOBOT_NUM_COMMANDS =
00118    sizeof(g_command_map)/sizeof(g_command_map[0]) ;
00119 
00120 // Number of milliseconds to wait for iRobot Create to boot
00121 #ifndef LOBOT_CREATE_BOOT_DELAY
00122    #define LOBOT_CREATE_BOOT_DELAY 3500
00123 #endif
00124 
00125 // This control program continuously listens to the Command Module's USB
00126 // port for the higher layers of the Robolocust controller to send it
00127 // motor (and other commands).
00128 //
00129 // If it doesn't hear from the higher layers for more than some
00130 // predefined amount of time, it will assume that the higher layers
00131 // aren't functioning and will shut off the robot's motors to ensure that
00132 // nothing catastrophic happens.
00133 //
00134 // The following constant specifies the number of milliseconds that can
00135 // elapse before this control program will consider the higher layers of
00136 // the system non-functional. That is, we will shut off the robot's
00137 // motors if we don't receive any high-level commands within these many
00138 // milliseconds since the previous command came in.
00139 #ifndef LOBOT_COMPUTER_TIMEOUT
00140    #define LOBOT_COMPUTER_TIMEOUT 250
00141 #endif
00142 
00143 // When the control program starts up, it plays a little diddy after
00144 // initialization to indicate that it is up and ready to start processing
00145 // high-level commands. This value specifies the amount of time (in ms)
00146 // to wait while the startup diddy plays.
00147 #ifndef LOBOT_STARTUP_BEEP_DELAY
00148    #define LOBOT_STARTUP_BEEP_DELAY 5000
00149 #endif
00150 
00151 // Number of milliseconds to wait between consecutive iterations of
00152 // this control program's main loop.
00153 #ifndef LOBOT_UPDATE_DELAY
00154    #define LOBOT_UPDATE_DELAY 50
00155 #endif
00156 
00157 // Another useful auditory feedback: at the end of each iteration of the
00158 // main loop play a little beep to let users know that the control
00159 // program is still alive.
00160 //
00161 // NOTE: Playing the heartbeat beep in every iteration of the main loop
00162 // will result in a one very long and annoying beep (because the update
00163 // delay is usually very small). Therefore we emit the heartbeat sound
00164 // every couple of iterations.
00165 //
00166 // The following number specifies the number of iterations between
00167 // heartbeat beeps.
00168 //
00169 // DEVNOTE: To get the (approximate) number of milliseconds between
00170 // heartbeats, multiply the following number by the main loop's update
00171 // delay specified above.
00172 #ifndef LOBOT_HEARTBEAT_INTERVAL
00173    #define LOBOT_HEARTBEAT_INTERVAL 25
00174 #endif
00175 
00176 /*-------------------------- INITIALIZATION ---------------------------*/
00177 
00178 /*
00179    Function: init_command_module
00180     Purpose: Initialize Command Module's ATmega168 microcontroller
00181 
00182    Mostly, the initialization is simply a matter of configuring various
00183    pins. The ATmega168 has 3 I/O ports, viz., B, C and D. Pins B0-B3 and
00184    C0-C5 are exposed for end-user applications. The remaining pins are
00185    for internal use by the Command Module.
00186 
00187    For lobot, we don't use any of the command module's I/O pins. So we
00188    simply set them all to zero, i.e., to act as inputs and enable their
00189    pull-ups.
00190 
00191    B4 is used to redirect the internal UART towards either the USB port
00192    or the Create's serial port. This pin *must* be an output. Since this
00193    control program needs to listen to the USB port for incoming motor
00194    commands from the higher (C++) layers of the lobot controller, we
00195    initialize it to the high state. Whenever required, this pin will be
00196    cleared to redirect I/O to the Create's serial port and then set high
00197    again to continue listening for incoming commands.
00198 
00199    B5 is used to detect whether or not the Create is powered up; it
00200    *must* be an input.
00201 
00202    B6, B7, C6 and C7 are hard-wired and cannot be changed in software.
00203 
00204    Port D is also for internal use by the Command Module. However, it is
00205    available to end-user applications. Nonetheless, the port D pins are
00206    required to be configured in a specific way.
00207 */
00208 static void init_command_module(void)
00209 {
00210    // Configure the I/O pins
00211    DDRB  = 0x10 ; // B0-3 = input,   B4 = output, B5 = input
00212    PORTB = 0xCF ; // B0-3 = pull-up, B4 = Create, B5 = floating
00213 
00214    DDRC  = 0x00 ; // C0-5 = input
00215    PORTC = 0xFF ; // C0-5 = pull-up
00216 
00217    DDRD  = 0xE6 ; // reserved by Command Module; must be set like this
00218    PORTD = 0x7D ; // reserved by Command Module; must be set like this
00219 }
00220 
00221 // Power on the robot if required
00222 static void init_robot(void)
00223 {
00224    if (PINB & 0x20) // B5 = 1 ==> robot powered up
00225       return ;      // no need to do anything then
00226 
00227    while (! (PINB & 0x20)) // continue till B5 (Create power detect) is high
00228    {
00229       PORTD &= ~0x80 ; // clear D7 (the power control pin)
00230       lo_wait(500) ;   // delay to ensure Create sees new state
00231 
00232       PORTD |= 0x80 ;  // set D7 to turn power on
00233       lo_wait(500) ;   // delay to ensure Create sees new state
00234 
00235       PORTD &= ~0x80 ; // clear D7
00236    }
00237    lo_wait(LOBOT_CREATE_BOOT_DELAY) ;
00238 }
00239 
00240 // Initialize the Open Interface protocol
00241 static void init_open_interface(void)
00242 {
00243    lo_tx(LOBOT_OI_CMD_START) ;
00244    lo_reset_baud(LOBOT_OI_BAUD_57600) ;
00245    lo_tx(LOBOT_OI_CMD_FULL) ;
00246    lo_wait(100) ;
00247 }
00248 
00249 // Light the Play and Power LEDs on the Create as visual
00250 // feedback/confirmation that the robot is up and the control program
00251 // ready for action.
00252 static void init_leds(void)
00253 {
00254    lo_tx(LOBOT_OI_CMD_LEDS)  ;
00255    lo_tx(LOBOT_OI_LED_PLAY)  ; // turn Play LED on and Advance LED off
00256    lo_tx(0x00) ; lo_tx(0xFF) ; // Power LED: green at full intensity
00257    lo_wait(50) ;
00258 }
00259 
00260 /*-------------- LOW-LEVEL ACTIONS AND ACKNOWLEDGEMENTS ---------------*/
00261 
00262 // Take care of wheel drops, cliffs, bumps and remote control commands in
00263 // that order of preference.
00264 static void react_to_sensors(void)
00265 {
00266    lo_wheel_drops() ;
00267    if (lo_is_wheel_dropped()) // don't bother with the other sensors
00268       ;
00269    else // no wheel drops ==> look at cliffs, bumps and the remote control
00270    {
00271       lo_cliffs() ;
00272       if (lo_is_cliff_detected()) // don't bother with bumps
00273          ;
00274       else // no wheel drops and no cliffs ==> check bumps and remote
00275       {
00276          lo_bumps() ;
00277          if (lo_bumps_pending()) // don't bother with remote
00278             ;
00279          else
00280             lo_remote() ;
00281       }
00282    }
00283 }
00284 
00285 // If we reacted to low-level/built-in Roomba/Create sensors here, let
00286 // the high-level controller know.
00287 //
00288 // NOTE: Order of preference ==> wheel drops, cliffs, bumps.
00289 static void send_pending_acks(void)
00290 {
00291    if (lo_wheel_drops_pending())
00292       lo_send_wheel_drops() ;
00293    else if (lo_cliffs_pending())
00294       lo_send_cliffs() ;
00295    else if (lo_bumps_pending())
00296       lo_send_bumps() ;
00297    else if (lo_remote_pending())
00298       lo_send_remote() ;
00299 
00300    if (lo_sensors_pending())
00301       lo_send_sensors() ;
00302 }
00303 
00304 /*------------------ COMMAND VALIDATION AND DESPATCH ------------------*/
00305 
00306 // Check the parity of the high-level 4-byte command sequence
00307 static char bad_parity(char cmd[LOBOT_CMD_SIZE])
00308 {
00309    char xor = 0 ;
00310    for (unsigned char i = 0; i < LOBOT_CMD_SIZE; ++i)
00311       xor ^= cmd[i] ;
00312    return xor ; // should be all zeros; otherwise, bad parity
00313 }
00314 
00315 // Execute the high-level command sent in by lobot controller by looking
00316 // up its handler in the command map and then invoking that function with
00317 // the supplied parameter.
00318 static void execute(char cmd, int param)
00319 {
00320    for (unsigned char i = 0; i < LOBOT_NUM_COMMANDS; ++i)
00321       if (cmd == g_command_map[i].cmd) {
00322          (*g_command_map[i].function)(param) ;
00323          return ;
00324       }
00325 
00326    // Bogus command! High-level has gone bonkers ==> stop the robot
00327    lo_stop(0) ;
00328 }
00329 
00330 /*----------------------- MISCELLANEOUS HELPERS -----------------------*/
00331 
00332 // Quick helper to check if any unsafe conditions have been detected or
00333 // if the user is remote controlling the robot. If this is true, then the
00334 // low-level controller will not accept any high-level commands.
00335 #define ignore_high_level() \
00336    (lo_is_wheel_dropped() || lo_is_cliff_detected() || lo_is_remote_control())
00337 
00338 // Quick helper to check if the Play button has been pressed
00339 #define play_quit() \
00340    ((lo_get_sensor(LOBOT_SENSORS_BUTTONS) & LOBOT_OI_BUTTON_PLAY) == \
00341      LOBOT_OI_BUTTON_PLAY)
00342 
00343 /*------------------------------- MAIN --------------------------------*/
00344 
00345 int main(void)
00346 {
00347    // Initialization steps
00348    cli() ; // don't want to be interrupted during initialization
00349    init_command_module() ;
00350    lo_init_timer() ;
00351    lo_init_comm() ;
00352    lo_init_sensors() ;
00353    lo_init_drive() ;
00354    sei() ; // okay (in fact, need) to start responding to interrupts now
00355    init_robot() ;
00356    init_open_interface() ;
00357    init_leds() ;
00358    lo_init_beeps() ;
00359    lo_stop(0) ; // just as a precaution
00360 
00361    // Play the startup signal to indicate that the program is ready to go
00362    lo_beep(LOBOT_BEEP_STARTUP) ;
00363    lo_wait(LOBOT_STARTUP_BEEP_DELAY) ;
00364 
00365    // Main loop: listen for high-level commands and execute them
00366    int heartbeat = LOBOT_HEARTBEAT_INTERVAL ;
00367    for(;;)
00368    {
00369       // Retrieve sensor data and react as required, sending
00370       // acknowledgements to the high level if necessary. When the user
00371       // presses the 'P' button on the remote or the Play button on the
00372       // robot, quit this low-level control program.
00373       lo_sensors() ;
00374       if (lo_sensors_pending()) {
00375          react_to_sensors() ;
00376          if (lo_remote_quit() || play_quit())
00377             break ;
00378       }
00379 
00380       // Send any pending acknowledgements and sensor data to high level.
00381       // But before we switch the USART to talk to the high level, we
00382       // need to ensure that the drive module's acceleration/decelartion
00383       // functionality doesn't try to issue drive commands while the
00384       // USART is switched. Otherwise, it will send drive commands to the
00385       // wrong destination and the high level will receive what it will
00386       // construe as bogus acknowledgement messages.
00387       lo_clear_buffers() ;
00388       lo_suspend_ramping() ;
00389       lo_io_to_usb() ;
00390       send_pending_acks() ;
00391 
00392       // If conditions are safe (e.g., no wheels dropped) and the user
00393       // isn't remote controlling the robot, then retrieve the next
00394       // high-level command and execute it. If no command is available,
00395       // stop the robot.
00396       char cmd[LOBOT_CMD_SIZE] = {0} ;
00397       if (ignore_high_level()) // unsafe conditions or remote control
00398          ; // don't send ACK_READY
00399       else // no unsafe conditions detected ==> okay to execute high-level cmds
00400       {
00401          lo_tx(LOBOT_ACK_READY) ;
00402 
00403          lo_rx(cmd, LOBOT_CMD_SIZE, LOBOT_COMPUTER_TIMEOUT) ;
00404          if (lo_io_error() || bad_parity(cmd))
00405             cmd[0] = LOBOT_CMD_STOP ;
00406       }
00407 
00408       lo_clear_buffers() ;
00409       lo_io_to_create() ;
00410       lo_resume_ramping() ;
00411       if (cmd[0])
00412          execute(cmd[0], lo_make_word(cmd[1], cmd[2])) ;
00413 
00414       // Short wait before the next iteration
00415       lo_wait(LOBOT_UPDATE_DELAY) ;
00416 
00417       // Emit heartbeat sound as auditory feedback to users that the
00418       // low-level control program is still alive and kicking...
00419       if (heartbeat-- == 0) {
00420          lo_beep(LOBOT_BEEP_HEARTBEAT) ;
00421          heartbeat = LOBOT_HEARTBEAT_INTERVAL ;
00422       }
00423    }
00424 
00425    // User pressed the 'P' button on the Roomba Remote or the Play
00426    // button on the robot to quit the Robolocust program and
00427    // automatically dock the robot with the home base.
00428    //
00429    // NOTE: Skip automatic docking. With the mini-ITX system + Li-Ion
00430    // batteries, the robot is quite heavy. Auto-docking can damage the
00431    // robot, the Roomba Home Base, or both. Best to lift robot and dock
00432    // it manually. Therefore, only put the robot back in passive mode.
00433    lo_beep(LOBOT_BEEP_QUITTING) ;
00434    lo_wait(LOBOT_STARTUP_BEEP_DELAY) ;
00435    //lo_tx(LOBOT_OI_CMD_DEMO) ;
00436    //lo_tx(LOBOT_OI_DEMO_COVER_AND_DOCK) ;
00437    lo_tx(LOBOT_OI_CMD_PASSIVE) ;
00438 
00439    return 0 ;
00440 }
Generated on Sun May 8 08:41:30 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3