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 }