#include "Copter.h" /* mavlink motor test - implements the MAV_CMD_DO_MOTOR_TEST mavlink command so that the GCS/pilot can test an individual motor or flaps to ensure proper wiring, rotation. */ // motor test definitions #define MOTOR_TEST_TIMEOUT_SEC 600 // max timeout is 10 minutes (600 seconds) static uint32_t motor_test_start_ms; // system time the motor test began static uint32_t motor_test_timeout_ms; // test will timeout this many milliseconds after the motor_test_start_ms static uint8_t motor_test_seq; // motor sequence number of motor being tested static uint8_t motor_test_count; // number of motors to test static uint8_t motor_test_throttle_type; // motor throttle type (0=throttle percentage, 1=PWM, 2=pilot throttle channel pass-through) static float motor_test_throttle_value; // throttle to be sent to motor, value depends upon it's type // motor_test_output - checks for timeout and sends updates to motors objects void Copter::motor_test_output() { // exit immediately if the motor test is not running if (!ap.motor_test) { return; } EXPECT_DELAY_MS(2000); // check for test timeout uint32_t now = AP_HAL::millis(); if ((now - motor_test_start_ms) >= motor_test_timeout_ms) { if (motor_test_count > 1) { if (now - motor_test_start_ms < motor_test_timeout_ms*1.5) { // output zero for 50% of the test time motors->output_min(); } else { // move onto next motor motor_test_seq++; motor_test_count--; motor_test_start_ms = now; if (!motors->armed()) { motors->armed(true); hal.util->set_soft_armed(true); } } return; } // stop motor test motor_test_stop(); } else { int16_t pwm = 0; // pwm that will be output to the motors // calculate pwm based on throttle type switch (motor_test_throttle_type) { case MOTOR_TEST_COMPASS_CAL: compass.set_voltage(battery.voltage()); compass.per_motor_calibration_update(); FALLTHROUGH; case MOTOR_TEST_THROTTLE_PERCENT: // sanity check motor_test_throttle value #if FRAME_CONFIG != HELI_FRAME if (motor_test_throttle_value <= 100) { int16_t pwm_min = motors->get_pwm_output_min(); int16_t pwm_max = motors->get_pwm_output_max(); pwm = (int16_t) (pwm_min + (pwm_max - pwm_min) * motor_test_throttle_value * 1e-2f); } #endif break; case MOTOR_TEST_THROTTLE_PWM: pwm = (int16_t)motor_test_throttle_value; break; case MOTOR_TEST_THROTTLE_PILOT: pwm = channel_throttle->get_radio_in(); break; default: motor_test_stop(); return; } // sanity check throttle values if (pwm >= RC_Channel::RC_MIN_LIMIT_PWM && pwm <= RC_Channel::RC_MAX_LIMIT_PWM) { // turn on motor to specified pwm value motors->output_test_seq(motor_test_seq, pwm); } else { motor_test_stop(); } } } // mavlink_motor_test_check - perform checks before motor tests can begin // return true if tests can continue, false if not bool Copter::mavlink_motor_control_check(const GCS_MAVLINK &gcs_chan, bool check_rc, const char* mode) { // check board has initialised if (!ap.initialised) { gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: Board initialising", mode); return false; } // Check Motor test is allowed char failure_msg[100] {}; if (!motors->motor_test_checks(ARRAY_SIZE(failure_msg), failure_msg)) { gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: %s", mode, failure_msg); return false; } // check rc has been calibrated if (check_rc && !arming.rc_calibration_checks(true)) { gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: RC not calibrated", mode); return false; } // ensure we are landed if (!ap.land_complete) { gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: vehicle not landed", mode); return false; } // check if safety switch has been pushed if (hal.util->safety_switch_state() == AP_HAL::Util::SAFETY_DISARMED) { gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: Safety switch", mode); return false; } // check E-Stop is not active if (SRV_Channels::get_emergency_stop()) { gcs_chan.send_text(MAV_SEVERITY_CRITICAL,"%s: Motor Emergency Stopped", mode); return false; } // if we got this far the check was successful and the motor test can continue return true; } // mavlink_motor_test_start - start motor test - spin a single motor at a specified pwm // returns MAV_RESULT_ACCEPTED on success, MAV_RESULT_FAILED on failure MAV_RESULT Copter::mavlink_motor_test_start(const GCS_MAVLINK &gcs_chan, uint8_t motor_seq, uint8_t throttle_type, float throttle_value, float timeout_sec, uint8_t motor_count) { if (motor_count == 0) { motor_count = 1; } // if test has not started try to start it if (!ap.motor_test) { /* perform checks that it is ok to start test The RC calibrated check can be skipped if direct pwm is supplied */ if (!mavlink_motor_control_check(gcs_chan, throttle_type != 1, "Motor Test")) { return MAV_RESULT_FAILED; } else { // start test gcs().send_text(MAV_SEVERITY_INFO, "starting motor test"); ap.motor_test = true; EXPECT_DELAY_MS(3000); // wait for rate thread to stop running due to motor test while (using_rate_thread) { hal.scheduler->delay(1); } // enable and arm motors if (!motors->armed()) { motors->output_min(); // output lowest possible value to motors motors->armed(true); hal.util->set_soft_armed(true); } // disable throttle and gps failsafe g.failsafe_throttle.set(FS_THR_DISABLED); g.failsafe_gcs.set(FS_GCS_DISABLED); g.fs_ekf_action.set(0); // turn on notify leds AP_Notify::flags.esc_calibration = true; } } // set timeout motor_test_start_ms = AP_HAL::millis(); motor_test_timeout_ms = MIN(timeout_sec, MOTOR_TEST_TIMEOUT_SEC) * 1000; // store required output motor_test_seq = motor_seq; motor_test_count = motor_count; motor_test_throttle_type = throttle_type; motor_test_throttle_value = throttle_value; if (motor_test_throttle_type == MOTOR_TEST_COMPASS_CAL) { compass.per_motor_calibration_start(); } // return success return MAV_RESULT_ACCEPTED; } // motor_test_stop - stops the motor test void Copter::motor_test_stop() { // exit immediately if the test is not running if (!ap.motor_test) { return; } gcs().send_text(MAV_SEVERITY_INFO, "finished motor test"); // flag test is complete ap.motor_test = false; // disarm motors motors->armed(false); hal.util->set_soft_armed(false); // reset timeout motor_test_start_ms = 0; motor_test_timeout_ms = 0; // re-enable failsafes g.failsafe_throttle.load(); g.failsafe_gcs.load(); g.fs_ekf_action.load(); if (motor_test_throttle_type == MOTOR_TEST_COMPASS_CAL) { compass.per_motor_calibration_end(); } // turn off notify leds AP_Notify::flags.esc_calibration = false; }