StoRC
Autonomous Payload Delivery System
|
00001 /*********************************************************** 00002 * StoRC - Search to Rescue Craft 00003 * MEAM Senior Design 2011-2012 00004 * 00005 * Authors: Michael Posner (CIS, MEAM '12) 00006 * Timothy Hennelly (ESE '12) 00007 * Jacob Orloff (MEAM '12) 00008 * 00009 * (some code from ArduPilot Mega project, found here: 00010 * http://code.google.com/p/ardupilot-mega/ ) 00011 * 00012 * Licensing: 00013 * 00014 * This program is free software: you can redistribute it and/or modify 00015 * it under the terms of the GNU General Public License as published by 00016 * the Free Software Foundation, either version 3 of the License, or 00017 * (at your option) any later version. 00018 * 00019 * This program is distributed in the hope that it will be useful, 00020 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00021 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00022 * GNU General Public License for more details. 00023 * 00024 * You should have received a copy of the GNU General Public License 00025 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00026 * 00027 ***********************************************************/ 00028 00029 /** 00030 * @file storc.pde 00031 * @brief Main file, contains Arduino setup() and loop() functions 00032 * @author Michael Posner (CIS, MEAM '12) 00033 * @author Timothy Hennelly (ESE '12) 00034 * @author Jacob Orloff (MEAM '12) 00035 * 00036 */ 00037 00038 // Libraries -------------------------------------------------------- 00039 // (they have to be included by this main storc.pde file, not storc.h) 00040 00041 //ArduPilot Mega headers 00042 #include <FastSerial.h> //ArduPilot Mega library for better serial communication (used by common and GPS library, MUST BE FIRST #include ) 00043 #include <AP_Common.h> //ArduPilot Mega common library 00044 #include <DataFlash.h> //ArduPilot Mega flash memory library 00045 #include <APM_RC.h> //ArduPilot Mega library for reading RC inputs and generating PWM pulses on output channels 00046 #include <AP_GPS.h> //ArduPilot Mega library for reading GPS data from GPS 00047 #include <APM_BMP085.h> //ArduPilot Mega library for reading the barometer (abs. pressure + temp) 00048 #include <AP_ADC.h> //ArduPilot Mega library for using ADC 00049 #include <AP_IMU.h> //ArduPilot Mega general header for including all relevant IMU headers 00050 #include <AP_Compass.h> //ArduPilot Mega magnetometer library 00051 #include <AP_DCM.h> //ArduPilot Mega dcm library 00052 00053 //AVR headers 00054 #include <stdio.h> 00055 00056 //Arduino headers 00057 #include <SPI.h> // Arduino SPI lib (for flash memory communication) 00058 #include <Wire.h> //Arduino I2C library (used by barometer) 00059 00060 //Project Headers -------------------------------------------------- 00061 #include "storc.h" //Main storc header file 00062 #include "controls.h" //Controls header file 00063 00064 //Functions -------------------------------------------------------- 00065 00066 /** 00067 * Function that runs every so often, as determined by {@link SLOW_LOOP_CLOCK} 00068 * @see loop 00069 */ 00070 void slow_loop(void) { 00071 00072 //GPS 00073 if(use_gps == true) { 00074 00075 gps->update(); 00076 00077 //debug_gps(); 00078 00079 if(LOG && (logging_sw > 512)) { 00080 if(gps->new_data) { //If the GPS has new data 00081 //adjust the GPS time: GPS Millisecond time of week -> HH:MM:SS 00082 long day = floor((gps->time/1000)/SEC_PER_DAY); 00083 long hour = floor(((gps->time/1000) - day*SEC_PER_DAY)/SEC_PER_HOUR); 00084 long minute = floor(((gps->time/1000) - day*SEC_PER_DAY - hour*SEC_PER_HOUR)/SEC_PER_MIN); 00085 long second = floor((gps->time/1000) - day*SEC_PER_DAY - hour*SEC_PER_HOUR - minute*SEC_PER_MIN); 00086 00087 //convert to EST 00088 hour -= 5; 00089 if(hour < 0) { 00090 hour += 24; 00091 } 00092 00093 //log gps data in flash memory (these values are sometimes *10^7 or *100, but we fix that in the read function) 00094 Log_Write_GPS(day,hour,minute,second,gps->latitude,gps->longitude,gps->altitude,gps->ground_speed,gps->ground_course); 00095 00096 gps->new_data = 0; // We have read the data 00097 00098 new_ALT = gps->altitude/100.0; // New altitude reading 00099 updated_ALT = 1; // Updated altitude is true 00100 } 00101 } 00102 00103 00104 00105 } 00106 00107 //Barometer 00108 if(barometer.Read() == 1) { //if the barometer has new data 00109 //debug_barometer(); 00110 if(LOG && (logging_sw > 512)) { 00111 Log_Write_Barometer((float)(barometer.Temp)*0.1 + 273.15, (float)(barometer.Press)/1000.0); //convert temp to K, press to kPa 00112 } 00113 00114 } 00115 00116 //Log state 00117 if(LOG) { 00118 Log_Write_State(drop_state, logging_sw, get_wind_profile, avg_wind_ns, avg_wind_ew, drift_ns, drift_ew); 00119 } 00120 00121 //Controls ---------------------------------------------------------- 00122 if(CONTROLS){ 00123 00124 if (rc_5 > 1500){ 00125 00126 //Navigation update 00127 if(NAVIGATION){ 00128 00129 /* 00130 // Figure out where to go 00131 if(drop_state == false){ // If still have not delivered payload, go to target 00132 LAT_C = target_LAT; 00133 LON_C = target_LON; 00134 }else{ // If already delivered, go to start 00135 LAT_C = gps_start_lat;b 00136 LON_C = gps_start_lon; 00137 } 00138 */ 00139 00140 00141 // Check if within Tolerance Radius Squared of the drop site 00142 //dist2drop = (del_LAT*del_LAT)+(del_LON*del_LON); 00143 //if(dist2drop < TRS){ 00144 if((abs(del_LAT) < DROP_THRESH) && (abs(del_LON) < DROP_THRESH)) { 00145 drop_payload(); 00146 drop_state = true; 00147 LAT_C = gps_start_lat; 00148 LON_C = gps_start_lon; 00149 Serial.println("----IN THE SACK----"); 00150 } 00151 else{ 00152 reset_drop(); 00153 Serial.println("----OUTTA THE SACK----"); 00154 } 00155 00156 //calculate desired heading to align with destination 00157 del_LAT = LAT_C - (gps->latitude); 00158 del_LON = LON_C - (gps->longitude); 00159 psi_c = atan2(del_LAT,del_LON); 00160 00161 //convert psi_c to same reference frame as GPS Heading is in (atan2 returns in the range [-pi,pi]) 00162 if(psi_c < 0) { 00163 psi_c += 2.0*PI; 00164 } 00165 00166 psi_c = 2.0*PI-(psi_c-PI/2.0); 00167 00168 psi_c = RAD2DEG(psi_c); //convert to degrees 00169 00170 //calculate difference in desired from current heading 00171 psi_error = psi_c - gps->ground_course/100.0; 00172 00173 //wrap if error is over 180 or less than -180 00174 if(psi_error > 180) { 00175 psi_error -= 360; 00176 } 00177 else if (psi_error < -180) { 00178 psi_error += 360; 00179 } 00180 00181 //calculate bank command based on difference 00182 if( psi_error < 0 && psi_error > -180 ) { //psi_c is to the left of the plane, turn left 00183 00184 phi_c = -KP_PSI2PHI*abs(psi_error); 00185 } 00186 else { //psi_c is to the right of the plane, turn right 00187 00188 phi_c = KP_PSI2PHI*abs(psi_error); 00189 } 00190 00191 phi_c = constrain(phi_c,-45,45); 00192 00193 /* 00194 Serial.print("H: "); 00195 Serial.print(gps->ground_course/100.0); 00196 Serial.print(", Psi_c: "); 00197 Serial.print(psi_c); 00198 Serial.print(", Phi_c: "); 00199 Serial.print(phi_c); 00200 Serial.print(", d: "); 00201 if(drop_state) { 00202 Serial.print("1"); 00203 } 00204 else { 00205 Serial.print("0"); 00206 } 00207 00208 Serial.print(", LAT_C: "); 00209 Serial.print(LAT_C); 00210 Serial.print(", LON_C: "); 00211 Serial.print(LON_C); 00212 Serial.print(", real lat: "); 00213 Serial.print(gps->latitude); 00214 Serial.print(", real lon: "); 00215 Serial.print(gps->longitude); 00216 Serial.print(", DROP_THRESH: "); 00217 Serial.print(DROP_THRESH); 00218 Serial.print(", del_LAT: "); 00219 Serial.print(del_LAT); 00220 Serial.print(", del_LON: "); 00221 Serial.println(del_LON); 00222 */ 00223 00224 if(LOG && (logging_sw > 512)) { 00225 Log_Write_Navigation(LAT_C, LON_C, del_LAT, del_LON, psi_c, psi_error, phi_c, DROP_THRESH); 00226 } 00227 } 00228 00229 } 00230 00231 if ((rc_4 > 1500) && (updated_ALT)){ // Throttle Control 00232 00233 // If just switched on 00234 if(sw1 == 0){ 00235 I_THR = 0; // Reset integrator to zero 00236 rc_thr_trim = rc_throttle; // Remember throttle starting position 00237 } 00238 00239 sw1 = 1; // Set switch variable to high 00240 00241 del_THR= 0; // Reset to zero 00242 THR_control(1.0/(float)(SLOW_LOOP_CLOCK)); // Run the throttle controls algorithm. Use slow_loop timer since that's the sampling rate. 00243 updated_ALT = 0; 00244 00245 } 00246 else{ 00247 sw1 = 0; // Keep switch variable low 00248 } 00249 00250 } 00251 00252 } 00253 00254 /** 00255 * Function that runs every so often, as determined by {@link MED_LOOP_CLOCK} 00256 * @see loop 00257 */ 00258 void medium_loop(void) { 00259 00260 //Read logging switch 00261 logging_sw = analogRead(LOGGING_SWITCH); //analogRead returns 0-1023 00262 00263 //test slide switch 00264 //slide_sw = digitalRead(SLIDE_SWITCH); 00265 //Serial.print("slide sw: "); Serial.println(slide_sw); 00266 00267 //IMU --------------------------------------------------------------- 00268 imu.update(); 00269 accel = imu.get_accel(); 00270 gyro = imu.get_gyro(); 00271 00272 //DCM --------------------------------------------------------------- 00273 //debug_dcm(); 00274 00275 //Magnetometer ------------------------------------------------------ 00276 compass.read(); 00277 compass.calculate(dcm.get_dcm_matrix()); //use the current dcm matrix to adjust calculations 00278 compass.null_offsets(dcm.get_dcm_matrix()); //re-evaluate offsets 00279 00280 //debug_magnetometer(); 00281 00282 //Airspeed ---------------------------------------------------------- 00283 update_airspeed(); 00284 //debug_airspeed(); 00285 00286 if(gps->altitude/100.0 > 38+20) { //if we get high enough, profile over 00287 get_wind_profile = false; 00288 } 00289 00290 if(get_wind_profile) { 00291 //update average wind stuff while we're ascending 00292 //get current wind speed, split into directions 00293 float curr_wind_ns = (gps->ground_speed/100.0 - airspeed)*sin(DEG2RAD(gps->ground_course/100.0)); 00294 float curr_wind_ew = (gps->ground_speed/100.0 - airspeed)*cos(DEG2RAD(gps->ground_course/100.0)); 00295 00296 //add to running sum 00297 sum_wind_ns += curr_wind_ns; 00298 sum_wind_ew += curr_wind_ew; 00299 00300 //increment total number of samples 00301 total_ns++; 00302 total_ew++; 00303 00304 //calculate new average 00305 avg_wind_ns = sum_wind_ns/(float)(total_ns); 00306 avg_wind_ew = sum_wind_ew/(float)(total_ew); 00307 } 00308 else { //do offset calculations 00309 00310 int s_ns = 1; //sign of ns wind 00311 int s_ew = -1; //sign of ew wind 00312 00313 if(avg_wind_ns < 0) { 00314 s_ns = -1; 00315 } 00316 if(avg_wind_ew < 0) { 00317 s_ew = -1; 00318 } 00319 00320 drift_ns = K_DRIFT*(gps->altitude/100.0)*avg_wind_ns+(s_ns*OFFSET_DRIFT); //drift [m] positive means will drift north 00321 drift_ew = K_DRIFT*(gps->altitude/100.0)*avg_wind_ew+(s_ew*OFFSET_DRIFT); //drift [m] positive means will drift east 00322 00323 //drift is in meters, constrain to not be huge 00324 drift_ns = constrain(drift_ns, -500,500); 00325 drift_ew = constrain(drift_ew, -500,500); 00326 00327 //convert drift to GPS 00328 float drift_ns_gps = DELT_M2GPS_LAT(drift_ns); 00329 float drift_ew_gps = DELT_M2GPS_LON(drift_ew); 00330 00331 //adjust target 00332 LAT_C = target_LAT - drift_ns_gps*10000000; 00333 LON_C = target_LON - drift_ew_gps*10000000; 00334 00335 } 00336 00337 //RC ---------------------------------------------------------------- 00338 //debug_rc(); 00339 00340 00341 //Controls ---------------------------------------------------------- 00342 if(CONTROLS){ 00343 00344 /* No Longer Doing Elevator Control 00345 00346 if (rc_4 > 1500){ // Elevator Control 00347 00348 // If just switched on 00349 if(sw1 == 0){ 00350 I_ELE = 0; // Reset integrator to zero 00351 rc_ele_trim = rc_elevator; // Remember servo neutral position 00352 } 00353 00354 sw1 = 1; // Set switch variable to high 00355 del_ELE = 0; // Reset to zero 00356 q_c = (float)(KP_RP*(abs(phi_c))); // Commanded pitch rate based on rc input 00357 ELE_control(1.0/(float)(MED_LOOP_CLOCK)); // Run the elevator controls algorithm 00358 00359 } 00360 else{ 00361 sw1 = 0; // Keep switch variable low 00362 } 00363 */ 00364 00365 if (rc_5 > 1500){ // Rudder Control 00366 00367 // If just switched on 00368 if(sw2 == 0){ 00369 I_RUD = 0; // Reset integrator to zero 00370 rc_rud_trim = rc_rudder; // Remember servo neutral position 00371 } 00372 00373 sw2 = 1; // Set switch variable to high 00374 del_RUD = 0; // Reset to zero 00375 // Get phi_c from navigation (outer loop) 00376 phi_c = (rc_rudder - rc_rud_trim)/10.0f; // Commanded bank based on rc input (inner loop) 00377 RUD_control(1.0/(float)(MED_LOOP_CLOCK)); // Run the rudder controls algorithm 00378 00379 } 00380 else{ 00381 sw2 = 0; // Keep switch variable low 00382 phi_c = 0.0; 00383 } 00384 00385 //debug_controls(); 00386 00387 } 00388 else{ //PAYLOAD DROP ---------------------------------------------------------------- 00389 00390 //re-zero controls variables incase we are switching mid-flight 00391 del_THR = 0; 00392 del_RUD = 0; 00393 del_ELE = 0; 00394 00395 //alternate conditions for dropping (auto-based on GPS, or manual) based on whether GPS is active 00396 boolean condition = false; 00397 if(use_gps == true) { //if GPS is on 00398 condition = rc_4 > 1500; 00399 //condition = gps->latitude > gps_start_lat + 0.0008*10000000; //farther than about 90 m north of start point //bug, lose rc control 00400 //condition = gps->latitude < 399518950; 00401 } 00402 else { //else do manual 00403 condition = rc_4 > 1500; 00404 } 00405 00406 //drop condition 00407 if( condition ) { 00408 drop_payload(); 00409 drop_state = true; 00410 } 00411 else { 00412 reset_drop(); 00413 } 00414 00415 } 00416 00417 } 00418 00419 /** 00420 * Function that runs every so often, as determined by {@link FAST_LOOP_CLOCK} 00421 * @see loop 00422 */ 00423 void fast_loop(void) { 00424 00425 //update DCM 00426 dcm.update_DCM(GYRO_INT_TIME); 00427 00428 } 00429 00430 00431 //Setup and Loop --------------------------------------------------- 00432 00433 /** 00434 * Arduino setup() function. This gets called first when the board is powered 00435 * on. It sets up the timers and initializes the serial connection as well as 00436 * any library initialization. 00437 */ 00438 void setup(void){ 00439 00440 //Setup timers for fast, medium, slow loops (Arduino millis() command returns 00441 // current time in milliseconds in an unsigned long) 00442 fast_timer = millis(); 00443 medium_timer = millis(); 00444 slow_timer = millis(); 00445 telemetry_timer = millis(); 00446 00447 //Initialize LED pins, set for output 00448 pinMode(A_LED,OUTPUT); 00449 pinMode(B_LED,OUTPUT); 00450 pinMode(C_LED,OUTPUT); 00451 00452 //start LEDs ON, then turn them off as initialization progresses 00453 digitalWrite(A_LED,HIGH); 00454 digitalWrite(B_LED,HIGH); 00455 digitalWrite(C_LED,HIGH); 00456 00457 00458 //Initialize Serial --------------------------------------- 00459 Serial.begin(115200); // Initialize Serial Communication (FTDI on Serial 0) 00460 Serial1.begin(38400, 128, 16); // standard gps running 00461 Serial3.begin(115200); // Initialize Serial3 for telemetry communication 00462 Serial.println("---------- StoRC Debug ----------"); 00463 00464 //Initialize Slide switch for input 00465 pinMode(SLIDE_SWITCH,INPUT); 00466 slide_sw = digitalRead(SLIDE_SWITCH); 00467 00468 //slide switch determines whether to use gps or not 00469 if(slide_sw == 1) { 00470 use_gps = true; 00471 } 00472 else { 00473 use_gps = false; 00474 } 00475 00476 Serial.print("SLIDE SWITCH: "); 00477 Serial.println(slide_sw); 00478 00479 //Initialize sensors and libraries --------------------------------------- 00480 00481 adc.Init(); //initialize adc 00482 00483 //Only initialize GPS if slide switch has determined we should 00484 if(use_gps == true) { 00485 //Set up GPS 00486 gps = &gps_driver; 00487 gps->init(); 00488 gps->update(); //force the driver to detect the GPS before checking its status 00489 00490 //Wait for full GPS lock before proceeding 00491 Serial.println("GPS Init..."); 00492 while(gps->status() < 2) { 00493 gps->update(); 00494 } 00495 Serial.println(" GPS Locked, waiting for altitude to settle..."); 00496 long count = 0; 00497 while(gps->altitude/100.0 < 20 || gps->altitude/100.0 > 50 ) { 00498 if(count == 100000) { //(int)(gps->altitude) % 10 == 0) 00499 Serial.print(" "); 00500 Serial.println(gps->altitude/100.0); 00501 count = 0; 00502 } 00503 count++; 00504 00505 gps->update(); 00506 } 00507 Serial.println(" Altitude between 30 and 50 meters."); 00508 } 00509 else { 00510 Serial.println("Screw GPS, man..."); 00511 } 00512 00513 digitalWrite(A_LED,LOW); //done with GPS intialization 00514 00515 barometer.Init(); //initialize the barometer 00516 00517 zero_pitot(); //initialize the airspeed measurement (must come after adc init!) 00518 00519 imu.init(IMU::COLD_START, delay); //intialize IMU on ground, stationary and level 00520 Serial.println(); 00521 00522 digitalWrite(B_LED,LOW); //done with IMU initialization 00523 00524 //Initialize Magnetometer stuff 00525 Wire.begin(); //used to communicate with the magnetometer pins 00526 00527 boolean result = compass.init(); 00528 if(result == true) { 00529 Serial.print("Compass found: "); 00530 } 00531 else { 00532 Serial.println("Compass initialization failed"); 00533 while (1) { 00534 }; 00535 } 00536 00537 compass.set_orientation(AP_COMPASS_COMPONENTS_DOWN_PINS_FORWARD); // set compass's orientation on aircraft. 00538 compass.set_offsets(0,0,0); // set offsets to account for surrounding interference 00539 compass.set_declination(0.213221057); // set local difference between magnetic north and true north (Philadelphia: 12 deg, 13 min) 00540 00541 switch( compass.product_id ) { 00542 case AP_COMPASS_TYPE_HIL: 00543 Serial.println("HIL"); 00544 break; 00545 case AP_COMPASS_TYPE_HMC5843: 00546 Serial.println("HMC5843"); 00547 break; 00548 case AP_COMPASS_TYPE_HMC5883L: 00549 Serial.println("HMC5883L"); 00550 break; 00551 default: 00552 Serial.println("unknown"); 00553 break; 00554 } 00555 00556 //DCM 00557 dcm.set_compass(&compass); 00558 dcm.set_centripetal(1); 00559 00560 //initialize the flash memory 00561 DataFlash.Init(); 00562 00563 if(LOG) { 00564 start_new_log(get_num_logs()); //start a fresh log if logging is turned on 00565 } 00566 00567 APM_RC.Init(); // Initialize RC library 00568 00569 //initialize code parameters ----------------------------------- 00570 00571 //Initialize the drop timer 00572 //drop_timer = -1; 00573 reset_drop(); 00574 00575 00576 //Initialize accel and gyro readings 00577 imu.update(); 00578 accel = imu.get_accel(); 00579 gyro = imu.get_gyro(); 00580 00581 //save starting GPS location 00582 if(use_gps == true) { 00583 gps->update(); 00584 gps_start_lat = gps->latitude; 00585 gps_start_lon = gps->longitude; 00586 } 00587 00588 digitalWrite(C_LED,LOW); //done with initialization 00589 } 00590 00591 /** 00592 * Arduino loop() function. This is called immediately after {@link setup} and 00593 * it loops as long as the board is powered. This function calls everything else 00594 * that runs. Using timers and periods set by {@link SLOW_LOOP_CLOCK}, 00595 * {@link MED_LOOP_CLOCK}, and {@link FAST_LOOP_CLOCK}, it determines when to call 00596 * each subfunction and also calls what needs to run at full speed. 00597 */ 00598 void loop(void){ 00599 00600 //Full speed loop (things that are done every iteration of loop() ) 00601 00602 read_RC(); //Get RC commands from controller 00603 00604 // Slow Loop check 00605 if( millis() - slow_timer >= 1000/SLOW_LOOP_CLOCK ) { 00606 slow_timer = millis(); 00607 slow_loop(); 00608 } 00609 00610 // Medium Loop check 00611 if( millis() - medium_timer >= 1000/MED_LOOP_CLOCK ) { 00612 medium_timer = millis(); 00613 medium_loop(); 00614 } 00615 00616 // Fast Loop check 00617 if( millis() - fast_timer >= 1000/FAST_LOOP_CLOCK ) { 00618 fast_timer = millis(); 00619 fast_loop(); 00620 } 00621 00622 //telemetry 00623 if(TELEMETRY) { 00624 if( millis() - telemetry_timer >= 1000/TELEMETRY_CLOCK ) { 00625 telemetry_timer = millis(); 00626 transmit_data(); 00627 } 00628 } 00629 00630 //important, higher-speed logging 00631 if(LOG) { 00632 if( millis() - logging_timer >= 1000/LOGGING_CLOCK ) { 00633 logging_timer = millis(); 00634 00635 if (logging_sw > 512) { 00636 Log_Write_IMU(gyro.x, gyro.y, gyro.z, accel.x, accel.y, accel.z); 00637 Log_Write_Magnetometer(RAD2DEG(compass.heading), dcm.roll_sensor/100.0, dcm.pitch_sensor/100.0, dcm.yaw_sensor/100.0); 00638 Log_Write_Airspeed(airspeed); 00639 Log_Write_RC(rc_elevator,rc_rudder,rc_throttle, rc_4, rc_5, rc_6, rc_7, rc_8); 00640 Log_Write_Controls_Rudder(KP_RUD, kp_rud_gm, KD_RUD, kd_rud_gm, KI_RUD, ki_rud_gm, I_RUD, del_RUD); 00641 //Log_Write_Controls_Elevator(KP_ELE, kp_ele_gm, KP_RP, KI_ELE, ki_ele_gm, I_ELE, del_ELE); 00642 Log_Write_Controls_Throttle(KP_THR, kp_thr_gm, KI_THR, ki_thr_gm, I_THR, del_ALT, del_THR); 00643 } 00644 00645 } 00646 } 00647 00648 //decide whether to add controls 00649 if(CONTROLS){ 00650 00651 if (rc_5 > 1500){ // Rudder Control 00652 rc_rudder = rc_rud_trim + del_RUD; 00653 } 00654 if (rc_4 > 1500) { // Throttle Control 00655 rc_throttle = rc_thr_trim + del_THR; 00656 } 00657 00658 } 00659 00660 //debug_controls(); 00661 send_RC(); //Send RC commands to outputs 00662 00663 } 00664 00665 00666 00667 00668 00669 00670 00671 00672 00673 00674 00675 00676 00677 00678 00679 00680 00681 00682 00683 00684 00685 00686 00687 00688 00689 00690 00691