Line data Source code
1 : /*
2 : This program is free software: you can redistribute it and/or modify
3 : it under the terms of the GNU General Public License as published by
4 : the Free Software Foundation, either version 3 of the License, or
5 : (at your option) any later version.
6 :
7 : This program is distributed in the hope that it will be useful,
8 : but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : GNU General Public License for more details.
11 :
12 : You should have received a copy of the GNU General Public License
13 : along with this program. If not, see <http://www.gnu.org/licenses/>.
14 : */
15 :
16 : #include "AP_Arming_config.h"
17 :
18 : #if AP_ARMING_ENABLED
19 :
20 : #include "AP_Arming.h"
21 : #include <AP_HAL/AP_HAL.h>
22 : #include <AP_BoardConfig/AP_BoardConfig.h>
23 : #include <AP_BattMonitor/AP_BattMonitor.h>
24 : #include <AP_Compass/AP_Compass.h>
25 : #include <AP_Notify/AP_Notify.h>
26 : #include <GCS_MAVLink/GCS.h>
27 : #include <GCS_MAVLink/GCS_MAVLink.h>
28 : #include <AP_Mission/AP_Mission.h>
29 : #include <AP_Proximity/AP_Proximity.h>
30 : #include <AP_Rally/AP_Rally.h>
31 : #include <SRV_Channel/SRV_Channel.h>
32 : #include <AC_Fence/AC_Fence.h>
33 : #include <AP_InertialSensor/AP_InertialSensor.h>
34 : #include <AP_InternalError/AP_InternalError.h>
35 : #include <AP_GPS/AP_GPS.h>
36 : #include <AP_Declination/AP_Declination.h>
37 : #include <AP_Airspeed/AP_Airspeed.h>
38 : #include <AP_AHRS/AP_AHRS.h>
39 : #include <AP_Baro/AP_Baro.h>
40 : #include <AP_RangeFinder/AP_RangeFinder.h>
41 : #include <AP_Generator/AP_Generator.h>
42 : #include <AP_Terrain/AP_Terrain.h>
43 : #include <AP_ADSB/AP_ADSB.h>
44 : #include <AP_Scripting/AP_Scripting.h>
45 : #include <AP_Camera/AP_RunCam.h>
46 : #include <AP_GyroFFT/AP_GyroFFT.h>
47 : #include <AP_VisualOdom/AP_VisualOdom.h>
48 : #include <AP_Parachute/AP_Parachute.h>
49 : #include <AP_OSD/AP_OSD.h>
50 : #include <AP_Relay/AP_Relay.h>
51 : #include <RC_Channel/RC_Channel.h>
52 : #include <AP_Button/AP_Button.h>
53 : #include <AP_FETtecOneWire/AP_FETtecOneWire.h>
54 : #include <AP_RPM/AP_RPM.h>
55 : #include <AP_Mount/AP_Mount.h>
56 : #include <AP_OpenDroneID/AP_OpenDroneID.h>
57 : #include <AP_SerialManager/AP_SerialManager.h>
58 : #include <AP_Vehicle/AP_Vehicle_Type.h>
59 : #include <AP_Scheduler/AP_Scheduler.h>
60 : #include <AP_KDECAN/AP_KDECAN.h>
61 : #include <AP_Vehicle/AP_Vehicle.h>
62 : #include <AP_ICEngine/AP_ICEngine.h>
63 :
64 : #if HAL_MAX_CAN_PROTOCOL_DRIVERS
65 : #include <AP_CANManager/AP_CANManager.h>
66 : #include <AP_Common/AP_Common.h>
67 : #include <AP_Vehicle/AP_Vehicle_Type.h>
68 :
69 : #include <AP_PiccoloCAN/AP_PiccoloCAN.h>
70 : #include <AP_DroneCAN/AP_DroneCAN.h>
71 : #endif
72 :
73 : #include <AP_Logger/AP_Logger.h>
74 :
75 : #define AP_ARMING_COMPASS_MAGFIELD_EXPECTED 530
76 : #define AP_ARMING_COMPASS_MAGFIELD_MIN 185 // 0.35 * 530 milligauss
77 : #define AP_ARMING_COMPASS_MAGFIELD_MAX 875 // 1.65 * 530 milligauss
78 : #define AP_ARMING_BOARD_VOLTAGE_MAX 5.8f
79 : #define AP_ARMING_ACCEL_ERROR_THRESHOLD 0.75f
80 : #define AP_ARMING_MAGFIELD_ERROR_THRESHOLD 100
81 : #define AP_ARMING_AHRS_GPS_ERROR_MAX 10 // accept up to 10m difference between AHRS and GPS
82 :
83 : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
84 : #define ARMING_RUDDER_DEFAULT (uint8_t)RudderArming::ARMONLY
85 : #else
86 : #define ARMING_RUDDER_DEFAULT (uint8_t)RudderArming::ARMDISARM
87 : #endif
88 :
89 : // find a default value for ARMING_NEED_POS parameter, and determine
90 : // whether the parameter should be shown:
91 : #ifndef AP_ARMING_NEED_LOC_PARAMETER_ENABLED
92 : // determine whether ARMING_NEED_POS is shown:
93 : #if APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_Rover)
94 : #define AP_ARMING_NEED_LOC_PARAMETER_ENABLED 1
95 : #else
96 : #define AP_ARMING_NEED_LOC_PARAMETER_ENABLED 0
97 : #endif // build types
98 : #endif // AP_ARMING_NEED_LOC_PARAMETER_ENABLED
99 :
100 : // if ARMING_NEED_POS is shown, determine what its default should be:
101 : #if AP_ARMING_NEED_LOC_PARAMETER_ENABLED
102 : #if APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_Rover)
103 : #define AP_ARMING_NEED_LOC_DEFAULT 0
104 : #else
105 : #error "Unable to find value for AP_ARMING_NEED_LOC_DEFAULT"
106 : #endif // APM_BUILD_TYPE
107 : #endif // AP_ARMING_NEED_LOC_PARAMETER_ENABLED
108 :
109 : #ifndef PREARM_DISPLAY_PERIOD
110 : # define PREARM_DISPLAY_PERIOD 30
111 : #endif
112 :
113 : #ifndef AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS
114 : #define AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS 10000
115 : #endif
116 :
117 : extern const AP_HAL::HAL& hal;
118 :
119 : const AP_Param::GroupInfo AP_Arming::var_info[] = {
120 :
121 : // @Param{Plane, Rover}: REQUIRE
122 : // @DisplayName: Require Arming Motors
123 : // @Description{Plane}: Arming disabled until some requirements are met. If 0, there are no requirements (arm immediately). If 1, sends the minimum throttle PWM value to the throttle channel when disarmed. If 2, send 0 PWM (no signal) to throttle channel when disarmed. On planes with ICE enabled and the throttle while disarmed option set in ICE_OPTIONS, the motor will always get THR_MIN when disarmed. Arming will be blocked until all mandatory and non-ARMING_SKIPCHK items are satisfied; arming can then be accomplished via (eg.) rudder gesture or GCS command.
124 : // @Description{Rover}: Arming disabled until some requirements are met. If 0, there are no requirements (arm immediately). If 1, all checks not skipped by ARMING_SKIPCHK must pass before the vehicle can be armed (for example, via rudder stick or GCS command). If 3, Arm immediately once pre-arm/arm checks are satisfied, but only one time per boot up. Note that a reboot is NOT required when setting to 0 but IS require when setting to 3.
125 : // @Values{Plane}: 0:Disabled,1:Yes(minimum PWM when disarmed),2:Yes(0 PWM when disarmed)
126 : // @Values{Rover}: 0:No,1:Yes(minimum PWM when disarmed),3:No(AutoArmOnce after checks are passed)
127 : // @User: Advanced
128 : AP_GROUPINFO_FLAGS_FRAME("REQUIRE", 0, AP_Arming, require, float(Required::YES_MIN_PWM),
129 : AP_PARAM_FLAG_NO_SHIFT,
130 : AP_PARAM_FRAME_PLANE | AP_PARAM_FRAME_ROVER),
131 :
132 : // 2 was the CHECK paramter stored in a AP_Int16
133 :
134 : // @Param: ACCTHRESH
135 : // @DisplayName: Accelerometer error threshold
136 : // @Description: Accelerometer error threshold used to determine inconsistent accelerometers. Compares this error range to other accelerometers to detect a hardware or calibration error. Lower value means tighter check and harder to pass arming check. Not all accelerometers are created equal.
137 : // @Units: m/s/s
138 : // @Range: 0.25 3.0
139 : // @User: Advanced
140 : AP_GROUPINFO("ACCTHRESH", 3, AP_Arming, accel_error_threshold, AP_ARMING_ACCEL_ERROR_THRESHOLD),
141 :
142 : // index 4 was VOLT_MIN, moved to AP_BattMonitor
143 : // index 5 was VOLT2_MIN, moved to AP_BattMonitor
144 :
145 : // @Param{Plane,Rover,Copter,Blimp}: RUDDER
146 : // @DisplayName: Arming with Rudder enable/disable
147 : // @Description: Allow arm/disarm by rudder input. When enabled arming can be done with right rudder, disarming with left rudder. Rudder arming only works with throttle at zero +- deadzone (RCx_DZ). Depending on vehicle type, arming in certain modes is prevented. See the wiki for each vehicle. Caution is recommended when arming if it is allowed in an auto-throttle mode!
148 : // @Values: 0:Disabled,1:ArmingOnly,2:ArmOrDisarm
149 : // @User: Advanced
150 : AP_GROUPINFO_FRAME("RUDDER", 6, AP_Arming, _rudder_arming, ARMING_RUDDER_DEFAULT, AP_PARAM_FRAME_PLANE |
151 : AP_PARAM_FRAME_ROVER |
152 : AP_PARAM_FRAME_COPTER |
153 : AP_PARAM_FRAME_TRICOPTER |
154 : AP_PARAM_FRAME_HELI |
155 : AP_PARAM_FRAME_BLIMP),
156 :
157 : // @Param: MIS_ITEMS
158 : // @DisplayName: Required mission items
159 : // @Description: Bitmask of mission items that are required to be planned in order to arm the aircraft
160 : // @Bitmask: 0:Land,1:VTOL Land,2:DO_LAND_START,3:Takeoff,4:VTOL Takeoff,5:Rallypoint,6:RTL
161 : // @User: Advanced
162 : AP_GROUPINFO("MIS_ITEMS", 7, AP_Arming, _required_mission_items, 0),
163 :
164 : // index 8 was CHECK stored as AP_Int32, became SKIPCHK
165 :
166 : // @Param: OPTIONS
167 : // @DisplayName: Arming options
168 : // @Description: Options that can be applied to change arming behaviour
169 : // @Bitmask: 0:Disable prearm display,1:Do not send status text on state change,2:Skip IMU consistency checks when ICE motor running
170 : // @User: Advanced
171 : AP_GROUPINFO("OPTIONS", 9, AP_Arming, _arming_options, 0),
172 :
173 : // @Param: MAGTHRESH
174 : // @DisplayName: Compass magnetic field strength error threshold vs earth magnetic model
175 : // @Description: Compass magnetic field strength error threshold vs earth magnetic model. X and y axis are compared using this threhold, Z axis uses 2x this threshold. 0 to disable check
176 : // @Units: mGauss
177 : // @Range: 0 500
178 : // @User: Advanced
179 : AP_GROUPINFO("MAGTHRESH", 10, AP_Arming, magfield_error_threshold, AP_ARMING_MAGFIELD_ERROR_THRESHOLD),
180 :
181 : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
182 : // @Param: CRSDP_IGN
183 : // @DisplayName: Disable CrashDump Arming check
184 : // @Description: Must have value "1" if crashdump data is present on the system, or a prearm failure will be raised. Do not set this parameter unless the risks of doing so are fully understood. The presence of a crash dump means that the firmware currently installed has suffered a critical software failure which resulted in the autopilot immediately rebooting. The crashdump file gives diagnostic information which can help in finding the issue, please contact the ArduPIlot support team. If this crashdump data is present, the vehicle is likely unsafe to fly. Check the ArduPilot documentation for more details.
185 : // @Values: 0:Crash Dump arming check active, 1:Crash Dump arming check deactivated
186 : // @User: Advanced
187 : AP_GROUPINFO("CRSDP_IGN", 11, AP_Arming, crashdump_ack.acked, 0),
188 : #endif // AP_ARMING_CRASHDUMP_ACK_ENABLED
189 :
190 : #if AP_ARMING_NEED_LOC_PARAMETER_ENABLED
191 : // @Param: NEED_LOC
192 : // @DisplayName: Require vehicle location
193 : // @Description: Require that the vehicle have an absolute position before it arms. This can help ensure that the vehicle can Return To Launch.
194 : // @User: Advanced
195 : // @Values{Copter,Rover}: 0:Do not require location,1:Require Location
196 : AP_GROUPINFO("NEED_LOC", 12, AP_Arming, require_location, float(AP_ARMING_NEED_LOC_DEFAULT)),
197 : #endif // AP_ARMING_NEED_LOC_PARAMETER_ENABLED
198 :
199 : // @Param: SKIPCHK
200 : // @DisplayName: Arm Checks to Skip (bitmask)
201 : // @Description: Checks to skip prior to arming motor. This is a bitmask of checks before allowing arming that will be skipped. For most users it is recommended to leave this at the default of 0 (no checks skipped). In extreme circumstances, a value of -1 can be used to skip all non-mandatory current and future checks.
202 : // @Bitmask: 1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC Channels,7:Board voltage,8:Battery Level,10:Logging Available,11:Hardware safety switch,12:GPS Configuration,13:System,14:Mission,15:Rangefinder,16:Camera,17:AuxAuth,18:VisualOdometry,19:FFT
203 : // @Bitmask{Plane}: 1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC Channels,7:Board voltage,8:Battery Level,9:Airspeed,10:Logging Available,11:Hardware safety switch,12:GPS Configuration,13:System,14:Mission,15:Rangefinder,16:Camera,17:AuxAuth,19:FFT
204 : // @User: Standard
205 : AP_GROUPINFO("SKIPCHK", 13, AP_Arming, checks_to_skip, 0),
206 :
207 : AP_GROUPEND
208 : };
209 :
210 : #if HAL_WITH_IO_MCU
211 : #include <AP_IOMCU/AP_IOMCU.h>
212 : extern AP_IOMCU iomcu;
213 : #endif
214 :
215 : #pragma GCC diagnostic push
216 : #if defined(__clang_major__) && __clang_major__ >= 14
217 : #pragma GCC diagnostic ignored "-Wbitwise-instead-of-logical"
218 : #endif
219 :
220 2094 : AP_Arming::AP_Arming()
221 : {
222 2094 : if (_singleton) {
223 0 : AP_HAL::panic("Too many AP_Arming instances");
224 : }
225 2094 : _singleton = this;
226 :
227 2094 : AP_Param::setup_object_defaults(this, var_info);
228 2094 : }
229 :
230 2086 : __INITFUNC__ void AP_Arming::init(void)
231 : {
232 : // PARAMETER_CONVERSION - Added: Dec-2025 for ArduPilot-4.7
233 : // ARMING_CHECK -> ARMING_SKIPCHK
234 :
235 2086 : if (!checks_to_skip.configured()) {
236 : // new parameter is not configured (though it may be set non-zero in a
237 : // defaults table e.g. on Sub)
238 323 : int32_t skipchk_new = checks_to_skip.get(); // get param default
239 :
240 323 : uint32_t idx = 8;
241 : #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
242 123 : idx <<= 6; // the old index is shifted due to AP_NESTEDGROUPINFO in AP_Arming_Plane.cpp
243 : #endif
244 :
245 : int32_t checks_old;
246 323 : if (AP_Param::get_param_by_index(this, idx, AP_PARAM_INT32, &checks_old)) {
247 : // the old parameter was customized
248 0 : if (checks_old == 0) { // all checks disabled?
249 0 : skipchk_new = -1; // skip all current and future checks
250 0 : } else if (!(checks_old & 1)) { // ALL flag not set?
251 : // mask of known checks at the time the conversion was written
252 0 : uint32_t check_mask = ((1U << 21) - 1) & (~1); // remove ALL bit
253 : // invert bits to get checks to skip
254 0 : skipchk_new = (~checks_old) & check_mask;
255 : } else {
256 0 : skipchk_new = 0; // specifically enable all checks, ignoring param default
257 : }
258 : }
259 :
260 323 : checks_to_skip.set_and_save(skipchk_new); // set and save to finish migration
261 : }
262 2086 : }
263 :
264 : // performs pre-arm checks. expects to be called at 1hz.
265 149217 : void AP_Arming::update(void)
266 : {
267 : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
268 : // if we boot with no crashdump data present, reset the "ignore"
269 : // parameter so the user will need to acknowledge future crashes
270 : // too:
271 : crashdump_ack.check_reset();
272 : #endif
273 :
274 149217 : const uint32_t now_ms = AP_HAL::millis();
275 : // perform pre-arm checks & display failures every 30 seconds
276 149217 : bool display_fail = false;
277 149217 : if ((report_immediately && (now_ms - last_prearm_display_ms > 4000)) ||
278 148858 : (now_ms - last_prearm_display_ms > PREARM_DISPLAY_PERIOD*1000)) {
279 4599 : report_immediately = false;
280 4599 : display_fail = true;
281 4599 : last_prearm_display_ms = now_ms;
282 : }
283 : // OTOH, the user may never want to display them:
284 149217 : if (option_enabled(Option::DISABLE_PREARM_DISPLAY)) {
285 0 : display_fail = false;
286 : }
287 :
288 149217 : pre_arm_checks(display_fail);
289 149217 : }
290 :
291 : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
292 : void AP_Arming::CrashDump::check_reset()
293 : {
294 : // if there is no crash dump data then clear the crash dump ack.
295 : // This means on subsequent crash-dumps appearing the user must
296 : // re-acknowledge.
297 : if (hal.util->last_crash_dump_size() == 0) {
298 : // no crash dump data
299 : acked.set_and_save_ifchanged(0);
300 : }
301 : }
302 : #endif // AP_ARMING_CRASHDUMP_ACK_ENABLED
303 :
304 6015 : uint16_t AP_Arming::compass_magfield_expected() const
305 : {
306 6015 : return AP_ARMING_COMPASS_MAGFIELD_EXPECTED;
307 : }
308 :
309 76192184 : bool AP_Arming::is_armed() const
310 : {
311 76192184 : return armed || arming_required() == Required::NO;
312 : }
313 :
314 : /*
315 : true if armed and safety is off
316 : */
317 50621943 : bool AP_Arming::is_armed_and_safety_off() const
318 : {
319 50621943 : return is_armed() && hal.util->safety_switch_state() != AP_HAL::Util::SAFETY_DISARMED;
320 : }
321 :
322 1258475 : uint32_t AP_Arming::get_enabled_checks() const
323 : {
324 : // ignore unknown checks
325 1258475 : uint32_t check_mask = (uint32_t(Check::CHECK_LAST)-1) & (~1); // remove former ALL bit
326 1258475 : return (~checks_to_skip) & check_mask;
327 : }
328 :
329 4003307 : bool AP_Arming::check_enabled(const AP_Arming::Check check) const
330 : {
331 4003307 : return (checks_to_skip & uint32_t(check)) == 0;
332 : }
333 :
334 0 : bool AP_Arming::all_checks_enabled() const
335 : {
336 : // ignore unknown checks
337 0 : uint32_t check_mask = (uint32_t(Check::CHECK_LAST)-1) & (~1); // remove former ALL bit
338 0 : return (checks_to_skip & check_mask) == 0;
339 : }
340 :
341 243025 : void AP_Arming::check_failed(const AP_Arming::Check check, bool report, const char *fmt, ...) const
342 : {
343 243025 : if (!report) {
344 43055 : return;
345 : }
346 : char taggedfmt[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
347 :
348 : // metafmt is wrapped around the passed-in format string to
349 : // prepend "PreArm" or "Arm", depending on what sorts of checks
350 : // we're currently doing.
351 199970 : const char *metafmt = "PreArm: %s"; // it's formats all the way down
352 199970 : if (running_arming_checks) {
353 43 : metafmt = "Arm: %s";
354 : }
355 199970 : hal.util->snprintf(taggedfmt, sizeof(taggedfmt), metafmt, fmt);
356 :
357 : #if HAL_GCS_ENABLED
358 199970 : MAV_SEVERITY severity = MAV_SEVERITY_CRITICAL;
359 199970 : if (!check_enabled(check)) {
360 : // technically should be NOTICE, but will annoy users at that level:
361 0 : severity = MAV_SEVERITY_DEBUG;
362 : }
363 : va_list arg_list;
364 199970 : va_start(arg_list, fmt);
365 199970 : gcs().send_textv(severity, taggedfmt, arg_list);
366 199970 : va_end(arg_list);
367 : #endif // HAL_GCS_ENABLED
368 : }
369 :
370 13070 : void AP_Arming::check_failed(bool report, const char *fmt, ...) const
371 : {
372 : #if HAL_GCS_ENABLED
373 13070 : if (!report) {
374 12462 : return;
375 : }
376 : char taggedfmt[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
377 :
378 : // metafmt is wrapped around the passed-in format string to
379 : // prepend "PreArm" or "Arm", depending on what sorts of checks
380 : // we're currently doing.
381 608 : const char *metafmt = "PreArm: %s"; // it's formats all the way down
382 608 : if (running_arming_checks) {
383 79 : metafmt = "Arm: %s";
384 : }
385 608 : hal.util->snprintf(taggedfmt, sizeof(taggedfmt), metafmt, fmt);
386 :
387 : va_list arg_list;
388 608 : va_start(arg_list, fmt);
389 608 : gcs().send_textv(MAV_SEVERITY_CRITICAL, taggedfmt, arg_list);
390 608 : va_end(arg_list);
391 : #endif // HAL_GCS_ENABLED
392 : }
393 :
394 140495 : bool AP_Arming::barometer_checks(bool report)
395 : {
396 : #ifdef HAL_BARO_ALLOW_INIT_NO_BARO
397 : return true;
398 : #endif
399 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
400 140495 : if (AP::sitl()->baro_count == 0) {
401 : // simulate no baro boards
402 0 : return true;
403 : }
404 : #endif
405 140495 : if (check_enabled(Check::BARO)) {
406 133080 : char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] {};
407 133080 : if (!AP::baro().arming_checks(sizeof(buffer), buffer)) {
408 34 : check_failed(Check::BARO, report, "Baro: %s", buffer);
409 34 : return false;
410 : }
411 : }
412 :
413 140461 : return true;
414 : }
415 :
416 : #if AP_AIRSPEED_ENABLED
417 118879 : bool AP_Arming::airspeed_checks(bool report)
418 : {
419 118879 : if (check_enabled(Check::AIRSPEED)) {
420 116152 : const AP_Airspeed *airspeed = AP_Airspeed::get_singleton();
421 116152 : if (airspeed == nullptr) {
422 : // not an airspeed capable vehicle
423 1405 : return true;
424 : }
425 116152 : char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] {};
426 116152 : if (!airspeed->arming_checks(sizeof(buffer), buffer)) {
427 1405 : check_failed(Check::AIRSPEED, report, "Airspeed: %s", buffer);
428 1405 : return false;
429 : }
430 : }
431 :
432 117474 : return true;
433 : }
434 : #endif // AP_AIRSPEED_ENABLED
435 :
436 : #if HAL_LOGGING_ENABLED
437 140495 : bool AP_Arming::logging_checks(bool report)
438 : {
439 140495 : if (check_enabled(Check::LOGGING)) {
440 133080 : if (!AP::logger().logging_present()) {
441 : // Logging is disabled, so nothing to check.
442 0 : return true;
443 : }
444 133080 : if (AP::logger().logging_failed()) {
445 138 : check_failed(Check::LOGGING, report, "Logging failed");
446 138 : return false;
447 : }
448 132942 : if (!AP::logger().CardInserted()) {
449 0 : check_failed(Check::LOGGING, report, "No SD card");
450 0 : return false;
451 : }
452 132942 : if (AP::logger().in_log_download()) {
453 2 : check_failed(Check::LOGGING, report, "Downloading logs");
454 2 : return false;
455 : }
456 : }
457 140355 : return true;
458 : }
459 : #endif // HAL_LOGGING_ENABLED
460 :
461 : #if AP_INERTIALSENSOR_ENABLED
462 64769 : bool AP_Arming::ins_accels_consistent(const AP_InertialSensor &ins)
463 : {
464 64769 : const uint32_t now = AP_HAL::millis();
465 64769 : if (!ins.accels_consistent(accel_error_threshold)) {
466 : // accels are inconsistent:
467 565 : last_accel_pass_ms = 0;
468 565 : return false;
469 : }
470 :
471 64204 : if (last_accel_pass_ms == 0) {
472 : // we didn't return false above, so sensors are
473 : // consistent right now:
474 1884 : last_accel_pass_ms = now;
475 : }
476 :
477 : // if accels can in theory be inconsistent,
478 : // must pass for at least AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS ms before we're considered consistent:
479 : #if AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS > 0
480 64204 : if (ins.get_accel_count() > 1 && now - last_accel_pass_ms < AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS) {
481 18360 : return false;
482 : }
483 : #endif
484 :
485 45844 : return true;
486 : }
487 :
488 64769 : bool AP_Arming::ins_gyros_consistent(const AP_InertialSensor &ins)
489 : {
490 64769 : const uint32_t now = AP_HAL::millis();
491 : // allow for up to 5 degrees/s difference
492 64769 : if (!ins.gyros_consistent(5)) {
493 : // gyros are inconsistent:
494 356 : last_gyro_pass_ms = 0;
495 356 : return false;
496 : }
497 :
498 : // we didn't return false above, so sensors are
499 : // consistent right now:
500 64413 : if (last_gyro_pass_ms == 0) {
501 1886 : last_gyro_pass_ms = now;
502 : }
503 :
504 : // if gyros can in theory be inconsistent,
505 : // must pass for at least AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS ms before we're considered consistent:
506 : #if AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS > 0
507 64413 : if (ins.get_gyro_count() > 1 && now - last_gyro_pass_ms < AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS) {
508 18402 : return false;
509 : }
510 : #endif
511 :
512 46011 : return true;
513 : }
514 :
515 140495 : bool AP_Arming::ins_checks(bool report)
516 : {
517 140495 : if (check_enabled(Check::INS)) {
518 133080 : const AP_InertialSensor &ins = AP::ins();
519 133080 : if (!ins.get_gyro_health_all()) {
520 0 : check_failed(Check::INS, report, "Gyros not healthy");
521 87255 : return false;
522 : }
523 133080 : if (!ins.gyro_calibrated_ok_all()) {
524 30 : check_failed(Check::INS, report, "Gyros not calibrated");
525 30 : return false;
526 : }
527 133050 : if (!ins.get_accel_health_all()) {
528 0 : check_failed(Check::INS, report, "Accels not healthy");
529 0 : return false;
530 : }
531 133050 : if (!ins.accel_calibrated_ok_all()) {
532 68237 : check_failed(Check::INS, report, "3D Accel calibration needed");
533 68237 : return false;
534 : }
535 :
536 : //check if accelerometers have calibrated and require reboot
537 64813 : if (ins.accel_cal_requires_reboot()) {
538 44 : check_failed(Check::INS, report, "Accels calibrated requires reboot");
539 44 : return false;
540 : }
541 :
542 64769 : bool run_imu_consistency_check = true;
543 : #if AP_ICENGINE_ENABLED
544 64769 : if (option_enabled(Option::SKIP_IMU_CONSISTENCY_ICE_RUNNING)) {
545 : // ICE motors can greatly disturb the IMU, so we get arming failures
546 : // due to gyro (and sometimes accel) inconsistency. Allow this check to be
547 : // disabled while the motor is running
548 0 : auto ice = AP::ice();
549 0 : if (ice != nullptr) {
550 0 : const auto ice_state = ice->get_state();
551 0 : if (ice_state == AP_ICEngine::ICE_STARTING || ice_state == AP_ICEngine::ICE_RUNNING) {
552 0 : run_imu_consistency_check = false;
553 : }
554 : }
555 : }
556 : #endif
557 :
558 64769 : if (run_imu_consistency_check && AP_ARMING_IMU_CONSISTENCY_CHECK_TIME_MS > 0) {
559 : // check all accelerometers point in roughly same direction
560 64769 : const bool accels_consistent = ins_accels_consistent(ins);
561 :
562 : // check all gyros are giving consistent readings
563 64769 : const bool gyros_consistent = ins_gyros_consistent(ins);
564 :
565 64769 : if (!accels_consistent) {
566 18925 : check_failed(Check::INS, report, "Accels inconsistent");
567 18925 : return false;
568 : }
569 :
570 45844 : if (!gyros_consistent) {
571 12 : check_failed(Check::INS, report, "Gyros inconsistent");
572 12 : return false;
573 : }
574 : }
575 :
576 : // no arming while doing temp cal
577 45832 : if (ins.temperature_cal_running()) {
578 0 : check_failed(Check::INS, report, "temperature cal running");
579 0 : return false;
580 : }
581 :
582 : #if AP_INERTIALSENSOR_BATCHSAMPLER_ENABLED
583 : // If Batch sampling enabled it must be initialized
584 45832 : if (ins.batchsampler.enabled() && !ins.batchsampler.is_initialised()) {
585 7 : check_failed(Check::INS, report, "Batch sampling requires reboot");
586 7 : return false;
587 : }
588 : #endif
589 :
590 : // check if IMU gyro updates are greater than or equal to Ardupilot Loop rate
591 : char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
592 45825 : if (!ins.pre_arm_check_gyro_backend_rate_hz(fail_msg, ARRAY_SIZE(fail_msg))) {
593 0 : check_failed(Check::INS, report, "%s", fail_msg);
594 0 : return false;
595 : }
596 : }
597 :
598 : #if HAL_GYROFFT_ENABLED
599 : // gyros are healthy so check the FFT
600 53240 : if (check_enabled(Check::FFT)) {
601 : // Check that the noise analyser works
602 45825 : AP_GyroFFT *fft = AP::fft();
603 :
604 : char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
605 45825 : if (fft != nullptr && !fft->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
606 0 : check_failed(Check::INS, report, "%s", fail_msg);
607 0 : return false;
608 : }
609 : }
610 : #endif
611 :
612 53240 : return true;
613 : }
614 : #endif // AP_INERTIALSENSOR_ENABLED
615 :
616 140495 : bool AP_Arming::compass_checks(bool report)
617 : {
618 140495 : Compass &_compass = AP::compass();
619 :
620 : #if COMPASS_CAL_ENABLED
621 : // check if compass is calibrating
622 140495 : if (_compass.is_calibrating()) {
623 1414 : check_failed(report, "Compass calibration running");
624 1414 : return false;
625 : }
626 :
627 : // check if compass has calibrated and requires reboot
628 139081 : if (_compass.compass_cal_requires_reboot()) {
629 1024 : check_failed(report, "Compass calibrated requires reboot");
630 1024 : return false;
631 : }
632 : #endif
633 :
634 138057 : if (check_enabled(Check::COMPASS)) {
635 :
636 : // avoid Compass::use_for_yaw(void) as it implicitly calls healthy() which can
637 : // incorrectly skip the remaining checks, pass the primary instance directly
638 133325 : if (!_compass.use_for_yaw(0)) {
639 : // compass use is disabled
640 620 : return true;
641 : }
642 :
643 133276 : if (!_compass.healthy()) {
644 3 : check_failed(Check::COMPASS, report, "Compass %d not healthy", _compass.get_first_usable() + 1);
645 3 : return false;
646 : }
647 : // check compass learning is on or offsets have been set
648 : #if !APM_BUILD_COPTER_OR_HELI && !APM_BUILD_TYPE(APM_BUILD_Blimp)
649 : // check compass offsets have been set if learning is off
650 : // copter and blimp always require configured compasses
651 43005 : if (!_compass.learn_offsets_enabled())
652 : #endif
653 : {
654 133273 : char failure_msg[100] = {};
655 133273 : if (!_compass.configured(failure_msg, ARRAY_SIZE(failure_msg))) {
656 117 : check_failed(Check::COMPASS, report, "%s", failure_msg);
657 117 : return false;
658 : }
659 : }
660 :
661 : // check for unreasonable compass offsets
662 133156 : const Vector3f offsets = _compass.get_offsets();
663 133156 : if (offsets.length() > _compass.get_offsets_max()) {
664 0 : check_failed(Check::COMPASS, report, "Compass offsets too high");
665 0 : return false;
666 : }
667 :
668 : // check for unreasonable mag field length
669 133156 : const float mag_field = _compass.get_field().length();
670 133156 : if (mag_field > AP_ARMING_COMPASS_MAGFIELD_MAX || mag_field < AP_ARMING_COMPASS_MAGFIELD_MIN) {
671 49 : check_failed(Check::COMPASS, report, "Check mag field: %4.0f, max %d, min %d", (double)mag_field, AP_ARMING_COMPASS_MAGFIELD_MAX, AP_ARMING_COMPASS_MAGFIELD_MIN);
672 49 : return false;
673 : }
674 :
675 : // check all compasses point in roughly same direction
676 133107 : if (!_compass.consistent()) {
677 43 : check_failed(Check::COMPASS, report, "Compasses inconsistent");
678 43 : return false;
679 : }
680 :
681 : #if AP_AHRS_ENABLED
682 : // if ahrs is using compass and we have location, check mag field versus expected earth magnetic model
683 133064 : Location ahrs_loc;
684 133064 : AP_AHRS &ahrs = AP::ahrs();
685 133064 : if ((magfield_error_threshold > 0) && ahrs.use_compass() && ahrs.get_location(ahrs_loc)) {
686 53058 : const Vector3f veh_mag_field_ef = ahrs.get_rotation_body_to_ned() * _compass.get_field();
687 53058 : const Vector3f earth_field_mgauss = AP_Declination::get_earth_field_ga(ahrs_loc) * 1000.0;
688 53058 : const Vector3f diff_mgauss = veh_mag_field_ef - earth_field_mgauss;
689 53058 : if (MAX(fabsf(diff_mgauss.x), fabsf(diff_mgauss.y)) > magfield_error_threshold) {
690 355 : check_failed(Check::COMPASS, report, "Check mag field (xy diff:%.0f>%d)",
691 355 : (double)MAX(fabsf(diff_mgauss.x), (double)fabsf(diff_mgauss.y)), (int)magfield_error_threshold);
692 359 : return false;
693 : }
694 52703 : if (fabsf(diff_mgauss.z) > magfield_error_threshold*2.0) {
695 4 : check_failed(Check::COMPASS, report, "Check mag field (z diff:%.0f>%d)",
696 4 : (double)fabsf(diff_mgauss.z), (int)magfield_error_threshold*2);
697 4 : return false;
698 : }
699 : }
700 : #endif // AP_AHRS_ENABLED
701 : }
702 :
703 137437 : return true;
704 : }
705 :
706 : #if AP_GPS_ENABLED
707 43144 : bool AP_Arming::gps_checks(bool report)
708 : {
709 43144 : const AP_GPS &gps = AP::gps();
710 43144 : if (check_enabled(Check::GPS)) {
711 :
712 : // Any failure messages from GPS backends
713 38456 : char failure_msg[100] = {};
714 38456 : if (!AP::gps().pre_arm_checks(failure_msg, ARRAY_SIZE(failure_msg))) {
715 31 : if (failure_msg[0] != '\0') {
716 31 : check_failed(Check::GPS, report, "%s", failure_msg);
717 : }
718 9712 : return false;
719 : }
720 :
721 69841 : for (uint8_t i = 0; i < gps.num_sensors(); i++) {
722 : #if AP_GPS_BLENDED_ENABLED
723 63896 : if ((i != GPS_BLENDED_INSTANCE) &&
724 : #else
725 : if (
726 : #endif
727 31879 : (gps.get_type(i) == AP_GPS::GPS_Type::GPS_TYPE_NONE)) {
728 13 : if (gps.primary_sensor() == i) {
729 6 : check_failed(Check::GPS, report, "GPS %i: primary but TYPE 0", i+1);
730 6 : return false;
731 : }
732 7 : continue;
733 : }
734 :
735 : //GPS OK?
736 32004 : if (gps.status(i) < AP_GPS_FixType::FIX_3D) {
737 390 : check_failed(Check::GPS, report, "GPS %i: Bad fix", i+1);
738 390 : return false;
739 : }
740 :
741 : //GPS update rate acceptable
742 31614 : if (!gps.is_healthy(i)) {
743 205 : check_failed(Check::GPS, report, "GPS %i: not healthy", i+1);
744 205 : return false;
745 : }
746 : }
747 :
748 37824 : if (!AP::ahrs().home_is_set()) {
749 9063 : check_failed(Check::GPS, report, "AHRS: waiting for home");
750 9063 : return false;
751 : }
752 :
753 : // check GPSs are within 50m of each other and that blending is healthy
754 : float distance_m;
755 28761 : if (!gps.all_consistent(distance_m)) {
756 0 : check_failed(Check::GPS, report, "GPS positions differ by %4.1fm",
757 : (double)distance_m);
758 0 : return false;
759 : }
760 :
761 : // check AHRS and GPS are within 10m of each other
762 28761 : if (gps.num_sensors() > 0) {
763 28738 : const Location gps_loc = gps.location();
764 28738 : Location ahrs_loc;
765 28738 : if (AP::ahrs().get_location(ahrs_loc)) {
766 28738 : const float distance = gps_loc.get_distance(ahrs_loc);
767 28738 : if (distance > AP_ARMING_AHRS_GPS_ERROR_MAX) {
768 17 : check_failed(Check::GPS, report, "GPS and AHRS differ by %4.1fm", (double)distance);
769 17 : return false;
770 : }
771 : }
772 : }
773 : }
774 :
775 33432 : if (check_enabled(Check::GPS_CONFIG)) {
776 : uint8_t first_unconfigured;
777 28744 : if (gps.first_unconfigured_gps(first_unconfigured)) {
778 26 : check_failed(Check::GPS_CONFIG,
779 : report,
780 : "GPS %d still configuring this GPS",
781 26 : first_unconfigured + 1);
782 26 : if (report) {
783 1 : gps.broadcast_first_configuration_failure_reason();
784 : }
785 26 : return false;
786 : }
787 : }
788 :
789 33406 : return true;
790 : }
791 : #endif // AP_GPS_ENABLED
792 :
793 : #if AP_BATTERY_ENABLED
794 140495 : bool AP_Arming::battery_checks(bool report)
795 : {
796 140495 : if (check_enabled(Check::BATTERY)) {
797 :
798 137768 : char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] {};
799 137768 : if (!AP::battery().arming_checks(sizeof(buffer), buffer)) {
800 465 : check_failed(Check::BATTERY, report, "%s", buffer);
801 465 : return false;
802 : }
803 : }
804 140030 : return true;
805 : }
806 : #endif // AP_BATTERY_ENABLED
807 :
808 140495 : bool AP_Arming::hardware_safety_check(bool report)
809 : {
810 140495 : if (check_enabled(Check::SWITCH)) {
811 :
812 : // check if safety switch has been pushed
813 133080 : if (hal.util->safety_switch_state() == AP_HAL::Util::SAFETY_DISARMED) {
814 9 : check_failed(Check::SWITCH, report, "Hardware safety switch");
815 9 : return false;
816 : }
817 : }
818 :
819 140486 : return true;
820 : }
821 :
822 : #if AP_RC_CHANNEL_ENABLED
823 1030 : bool AP_Arming::rc_arm_checks(AP_Arming::Method method)
824 : {
825 : // don't check the trims if we are in a failsafe
826 1030 : if (!rc().has_valid_input()) {
827 2 : return true;
828 : }
829 :
830 : // only check if we've received some form of input within the last second
831 : // this is a protection against a vehicle having never enabled an input
832 1028 : uint32_t last_input_ms = rc().last_input_ms();
833 1028 : if ((last_input_ms == 0) || ((AP_HAL::millis() - last_input_ms) > 1000)) {
834 0 : return true;
835 : }
836 :
837 1028 : bool check_passed = true;
838 : // ensure all rc channels have different functions
839 1028 : if (rc().duplicate_options_exist()) {
840 1 : check_failed(Check::PARAMETERS, true, "Duplicate Aux Switch Options");
841 1 : check_passed = false;
842 : }
843 1028 : if (rc().flight_mode_channel_conflicts_with_rc_option()) {
844 0 : check_failed(Check::PARAMETERS, true, "Mode channel and RC%d_OPTION conflict", rc().flight_mode_channel_number());
845 0 : check_passed = false;
846 : }
847 : {
848 1028 : if (!rc().option_is_enabled(RC_Channels::Option::ARMING_SKIP_CHECK_RPY)) {
849 : const struct {
850 : const char *name;
851 : const RC_Channel *channel;
852 1028 : } channels_to_check[] {
853 1028 : { "Roll", &rc().get_roll_channel(), },
854 1028 : { "Pitch", &rc().get_pitch_channel(), },
855 1028 : { "Yaw", &rc().get_yaw_channel(), },
856 2056 : };
857 4112 : for (const auto &channel_to_check : channels_to_check) {
858 3084 : const auto *c = channel_to_check.channel;
859 3084 : if (c->get_control_in() != 0) {
860 13 : if ((method != Method::RUDDER) || (c != rc().get_arming_channel())) { // ignore the yaw input channel if rudder arming
861 0 : check_failed(Check::RC, true, "%s (RC%d) is not neutral", channel_to_check.name, c->ch());
862 0 : check_passed = false;
863 : }
864 : }
865 : }
866 : }
867 :
868 : // if throttle check is enabled, require zero input
869 1028 : if (rc().arming_check_throttle()) {
870 977 : const RC_Channel *c = &rc().get_throttle_channel();
871 977 : if (c->get_control_in() != 0) {
872 0 : check_failed(Check::RC, true, "%s (RC%d) is not neutral", "Throttle", c->ch());
873 0 : check_passed = false;
874 : }
875 977 : c = rc().find_channel_for_option(RC_Channel::AUX_FUNC::FWD_THR);
876 977 : if (c != nullptr) {
877 0 : uint8_t fwd_thr = c->percent_input();
878 : // require channel input within 2% of minimum
879 0 : if (fwd_thr > 2) {
880 0 : check_failed(Check::RC, true, "VTOL Fwd Throttle is not zero");
881 0 : check_passed = false;
882 : }
883 : }
884 : }
885 : }
886 1028 : return check_passed;
887 : }
888 :
889 133868 : bool AP_Arming::rc_calibration_checks(bool report)
890 : {
891 133868 : bool check_passed = true;
892 133868 : const uint8_t num_channels = RC_Channels::get_valid_channel_count();
893 2275756 : for (uint8_t i = 0; i < NUM_RC_CHANNELS; i++) {
894 2141888 : const RC_Channel *c = rc().channel(i);
895 2141888 : if (c == nullptr) {
896 0 : continue;
897 : }
898 2141888 : if (i >= num_channels && !(c->has_override())) {
899 4732 : continue;
900 : }
901 2137156 : const uint16_t trim = c->get_radio_trim();
902 2137156 : if (c->get_radio_min() > trim) {
903 0 : check_failed(Check::RC, report, "RC%d_MIN is greater than RC%d_TRIM", i + 1, i + 1);
904 0 : check_passed = false;
905 : }
906 2137156 : if (c->get_radio_max() < trim) {
907 11 : check_failed(Check::RC, report, "RC%d_MAX is less than RC%d_TRIM", i + 1, i + 1);
908 11 : check_passed = false;
909 : }
910 : }
911 :
912 133868 : return check_passed;
913 : }
914 :
915 140537 : bool AP_Arming::rc_in_calibration_check(bool report)
916 : {
917 140537 : if (rc().calibrating()) {
918 0 : check_failed(Check::RC, report, "RC calibrating");
919 0 : return false;
920 : }
921 140537 : return true;
922 : }
923 :
924 140495 : bool AP_Arming::manual_transmitter_checks(bool report)
925 : {
926 140495 : if (check_enabled(Check::RC)) {
927 :
928 137768 : if (AP_Notify::flags.failsafe_radio) {
929 137 : check_failed(Check::RC, report, "Radio failsafe on");
930 137 : return false;
931 : }
932 :
933 137631 : if (!rc_calibration_checks(report)) {
934 10 : return false;
935 : }
936 : }
937 :
938 140348 : return rc_in_calibration_check(report);
939 : }
940 : #endif // AP_RC_CHANNEL_ENABLED
941 :
942 : #if AP_MISSION_ENABLED
943 140495 : bool AP_Arming::mission_checks(bool report)
944 : {
945 140495 : AP_Mission &mission = AP::mission();
946 140495 : if (check_enabled(Check::MISSION) && _required_mission_items) {
947 : const struct MisItemTable {
948 : MIS_ITEM_CHECK check;
949 : MAV_CMD mis_item_type;
950 : const char *type;
951 0 : } misChecks[] = {
952 : {MIS_ITEM_CHECK_LAND, MAV_CMD_NAV_LAND, "land"},
953 : {MIS_ITEM_CHECK_VTOL_LAND, MAV_CMD_NAV_VTOL_LAND, "vtol land"},
954 : {MIS_ITEM_CHECK_DO_LAND_START, MAV_CMD_DO_LAND_START, "do land start"},
955 : {MIS_ITEM_CHECK_TAKEOFF, MAV_CMD_NAV_TAKEOFF, "takeoff"},
956 : {MIS_ITEM_CHECK_VTOL_TAKEOFF, MAV_CMD_NAV_VTOL_TAKEOFF, "vtol takeoff"},
957 : {MIS_ITEM_CHECK_RETURN_TO_LAUNCH, MAV_CMD_NAV_RETURN_TO_LAUNCH, "RTL"},
958 : };
959 0 : for (uint8_t i = 0; i < ARRAY_SIZE(misChecks); i++) {
960 0 : if (_required_mission_items & misChecks[i].check) {
961 0 : if (!mission.contains_item(misChecks[i].mis_item_type)) {
962 0 : check_failed(Check::MISSION, report, "Missing mission item: %s", misChecks[i].type);
963 0 : return false;
964 : }
965 : }
966 : }
967 0 : if (_required_mission_items & MIS_ITEM_CHECK_RALLY) {
968 : #if HAL_RALLY_ENABLED
969 0 : AP_Rally *rally = AP::rally();
970 0 : if (rally == nullptr) {
971 0 : check_failed(Check::MISSION, report, "No rally library present");
972 0 : return false;
973 : }
974 0 : Location ahrs_loc;
975 0 : if (!AP::ahrs().get_location(ahrs_loc)) {
976 0 : check_failed(Check::MISSION, report, "Can't check rally without position");
977 0 : return false;
978 : }
979 0 : RallyLocation rally_loc = {};
980 0 : if (!rally->find_nearest_rally_point(ahrs_loc, rally_loc)) {
981 0 : check_failed(Check::MISSION, report, "No sufficiently close rally point located");
982 0 : return false;
983 : }
984 : #else
985 : check_failed(Check::MISSION, report, "No rally library present");
986 : return false;
987 : #endif
988 : }
989 : }
990 :
991 : // Check there are no zero altitude takeoffs
992 : // Although technically valid in some very rare cases it's most likely that the user simply forgot to enter an altitude.
993 140495 : if (check_enabled(Check::MISSION)) {
994 133080 : const uint16_t num_commands = mission.num_commands();
995 438521 : for (uint16_t i = 1; i < num_commands; i++) {
996 305441 : if (!mission.is_takeoff_type_cmd(mission.get_command_id(i))) {
997 300534 : continue;
998 : }
999 4907 : AP_Mission::Mission_Command cmd;
1000 4907 : if (!mission.read_cmd_from_storage(i, cmd)) {
1001 0 : continue;
1002 : }
1003 4907 : if (cmd.content.location.alt == 0) {
1004 0 : check_failed(Check::MISSION, report, "Mission: Zero takeoff altitude");
1005 0 : return false;
1006 : }
1007 : }
1008 : }
1009 :
1010 : #if AP_SDCARD_STORAGE_ENABLED
1011 406655 : if (check_enabled(Check::MISSION) &&
1012 266160 : (mission.failed_sdcard_storage() || StorageManager::storage_failed())) {
1013 0 : check_failed(Check::MISSION, report, "Failed to open %s", AP_MISSION_SDCARD_FILENAME);
1014 0 : return false;
1015 : }
1016 : #endif
1017 :
1018 : #if AP_VEHICLE_ENABLED
1019 : // do not allow arming if there are no mission items and we are in
1020 : // (e.g.) AUTO mode
1021 143690 : if (AP::vehicle()->current_mode_requires_mission() &&
1022 3195 : !mission.present()) {
1023 68 : check_failed(Check::MISSION, report, "Mode requires mission");
1024 68 : return false;
1025 : }
1026 : #endif
1027 :
1028 140427 : return true;
1029 : }
1030 : #endif // AP_MISSION_ENABLED
1031 :
1032 140495 : bool AP_Arming::rangefinder_checks(bool report)
1033 : {
1034 : #if AP_RANGEFINDER_ENABLED
1035 140495 : if (check_enabled(Check::RANGEFINDER)) {
1036 133080 : RangeFinder *range = RangeFinder::get_singleton();
1037 133080 : if (range == nullptr) {
1038 80 : return true;
1039 : }
1040 :
1041 : char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
1042 133080 : if (!range->prearm_healthy(buffer, ARRAY_SIZE(buffer))) {
1043 80 : check_failed(Check::RANGEFINDER, report, "%s", buffer);
1044 80 : return false;
1045 : }
1046 : }
1047 : #endif
1048 :
1049 140415 : return true;
1050 : }
1051 :
1052 140495 : bool AP_Arming::servo_checks(bool report) const
1053 : {
1054 : #if NUM_SERVO_CHANNELS
1055 140495 : bool check_passed = true;
1056 4636335 : for (uint8_t i = 0; i < NUM_SERVO_CHANNELS; i++) {
1057 4495840 : const SRV_Channel *c = SRV_Channels::srv_channel(i);
1058 4495840 : if (c == nullptr || c->get_function() <= SRV_Channel::k_none) {
1059 3889595 : continue;
1060 : }
1061 :
1062 606245 : const uint16_t trim = c->get_trim();
1063 606245 : if (c->get_output_min() > trim) {
1064 0 : check_failed(report, "SERVO%d_MIN is greater than SERVO%d_TRIM", i + 1, i + 1);
1065 0 : check_passed = false;
1066 : }
1067 606245 : if (c->get_output_max() < trim) {
1068 0 : check_failed(report, "SERVO%d_MAX is less than SERVO%d_TRIM", i + 1, i + 1);
1069 0 : check_passed = false;
1070 : }
1071 :
1072 : // check functions using PWM are enabled
1073 606245 : if (SRV_Channels::get_disabled_channel_mask() & 1U<<i) {
1074 0 : const SRV_Channel::Function ch_function = c->get_function();
1075 :
1076 : // motors, e-stoppable functions, neopixels and ProfiLEDs may be digital outputs and thus can be disabled
1077 : // scripting can use its functions as labels for LED setup
1078 0 : const bool disabled_ok = SRV_Channel::is_motor(ch_function) ||
1079 0 : SRV_Channel::should_e_stop(ch_function) ||
1080 0 : (ch_function >= SRV_Channel::k_LED_neopixel1 && ch_function <= SRV_Channel::k_LED_neopixel4) ||
1081 0 : (ch_function >= SRV_Channel::k_ProfiLED_1 && ch_function <= SRV_Channel::k_ProfiLED_Clock) ||
1082 0 : (ch_function >= SRV_Channel::k_scripting1 && ch_function <= SRV_Channel::k_scripting16);
1083 :
1084 : // for all other functions raise a pre-arm failure
1085 0 : if (!disabled_ok) {
1086 0 : check_failed(report, "SERVO%u_FUNCTION=%u on disabled channel", i + 1, (unsigned)ch_function);
1087 0 : check_passed = false;
1088 : }
1089 : }
1090 : }
1091 :
1092 : #if HAL_WITH_IO_MCU
1093 : if (!iomcu.healthy() && AP_BoardConfig::io_enabled()) {
1094 : check_failed(report, "IOMCU is unhealthy");
1095 : check_passed = false;
1096 : }
1097 : #endif
1098 :
1099 140495 : return check_passed;
1100 : #else
1101 : return false;
1102 : #endif
1103 : }
1104 :
1105 140495 : bool AP_Arming::board_voltage_checks(bool report)
1106 : {
1107 : // check board voltage
1108 140495 : if (check_enabled(Check::VOLTAGE)) {
1109 : #if HAL_HAVE_BOARD_VOLTAGE
1110 137768 : const float bus_voltage = hal.analogin->board_voltage();
1111 137768 : const float vbus_min = AP_BoardConfig::get_minimum_board_voltage();
1112 137768 : if(((bus_voltage < vbus_min) || (bus_voltage > AP_ARMING_BOARD_VOLTAGE_MAX))) {
1113 0 : check_failed(Check::VOLTAGE, report, "Board (%1.1fv) out of range %1.1f-%1.1fv", (double)bus_voltage, (double)vbus_min, (double)AP_ARMING_BOARD_VOLTAGE_MAX);
1114 0 : return false;
1115 : }
1116 : #endif // HAL_HAVE_BOARD_VOLTAGE
1117 :
1118 : #if HAL_HAVE_SERVO_VOLTAGE
1119 137768 : const float vservo_min = AP_BoardConfig::get_minimum_servo_voltage();
1120 137768 : if (is_positive(vservo_min)) {
1121 0 : const float servo_voltage = hal.analogin->servorail_voltage();
1122 0 : if (servo_voltage < vservo_min) {
1123 0 : check_failed(Check::VOLTAGE, report, "Servo voltage to low (%1.2fv < %1.2fv)", (double)servo_voltage, (double)vservo_min);
1124 0 : return false;
1125 : }
1126 : }
1127 : #endif // HAL_HAVE_SERVO_VOLTAGE
1128 : }
1129 :
1130 140495 : return true;
1131 : }
1132 :
1133 : #if HAL_HAVE_IMU_HEATER
1134 : bool AP_Arming::heater_min_temperature_checks(bool report)
1135 : {
1136 : if (all_checks_enabled()) {
1137 : AP_BoardConfig *board = AP::boardConfig();
1138 : if (board) {
1139 : float temperature;
1140 : int8_t min_temperature;
1141 : if (board->get_board_heater_temperature(temperature) &&
1142 : board->get_board_heater_arming_temperature(min_temperature) &&
1143 : (temperature < min_temperature)) {
1144 : check_failed(Check::SYSTEM, report, "heater temp low (%0.1f < %i)", temperature, min_temperature);
1145 : return false;
1146 : }
1147 : }
1148 : }
1149 : return true;
1150 : }
1151 : #endif // HAL_HAVE_IMU_HEATER
1152 :
1153 : /*
1154 : check base system operations
1155 : */
1156 140495 : bool AP_Arming::system_checks(bool report)
1157 : {
1158 140495 : char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] {};
1159 :
1160 140495 : if (check_enabled(Check::SYSTEM)) {
1161 133080 : if (!hal.storage->healthy()) {
1162 0 : check_failed(Check::SYSTEM, report, "Param storage failed");
1163 0 : return false;
1164 : }
1165 :
1166 133080 : if (AP_Param::get_eeprom_full()) {
1167 0 : check_failed(Check::PARAMETERS, report, "parameter storage full");
1168 0 : return false;
1169 : }
1170 :
1171 : // check main loop rate is at least 90% of expected value
1172 133080 : const float actual_loop_rate = AP::scheduler().get_filtered_loop_rate_hz();
1173 133080 : const uint16_t expected_loop_rate = AP::scheduler().get_loop_rate_hz();
1174 133080 : const float loop_rate_pct = actual_loop_rate / expected_loop_rate;
1175 133080 : if (loop_rate_pct < 0.90) {
1176 66473 : check_failed(Check::SYSTEM, report, "Main loop slow (%uHz < %uHz)", (unsigned)actual_loop_rate, (unsigned)expected_loop_rate);
1177 66473 : return false;
1178 : }
1179 :
1180 : #if AP_TERRAIN_AVAILABLE
1181 66607 : const AP_Terrain *terrain = AP_Terrain::get_singleton();
1182 66607 : if ((terrain != nullptr) && terrain->init_failed()) {
1183 0 : check_failed(Check::SYSTEM, report, "Terrain out of memory");
1184 0 : return false;
1185 : }
1186 : #endif
1187 : #if AP_SCRIPTING_ENABLED
1188 66607 : const AP_Scripting *scripting = AP_Scripting::get_singleton();
1189 66607 : if ((scripting != nullptr) && !scripting->arming_checks(sizeof(buffer), buffer)) {
1190 48 : check_failed(Check::SYSTEM, report, "%s", buffer);
1191 48 : return false;
1192 : }
1193 : #endif
1194 : #if HAL_ADSB_ENABLED
1195 66559 : AP_ADSB *adsb = AP::ADSB();
1196 66559 : if ((adsb != nullptr) && adsb->enabled() && adsb->init_failed()) {
1197 0 : check_failed(Check::SYSTEM, report, "ADSB out of memory");
1198 0 : return false;
1199 : }
1200 : #endif
1201 : }
1202 73974 : if (AP::internalerror().errors() != 0) {
1203 40 : AP::internalerror().errors_as_string((uint8_t*)buffer, ARRAY_SIZE(buffer));
1204 40 : check_failed(report, "Internal errors 0x%x l:%u %s", (unsigned int)AP::internalerror().errors(), AP::internalerror().last_error_line(), buffer);
1205 40 : return false;
1206 : }
1207 :
1208 73934 : if (!hal.gpio->arming_checks(sizeof(buffer), buffer)) {
1209 0 : check_failed(report, "%s", buffer);
1210 0 : return false;
1211 : }
1212 :
1213 73934 : if (check_enabled(Check::PARAMETERS)) {
1214 : #if !AP_GPS_BLENDED_ENABLED
1215 : if (!blending_auto_switch_checks(report)) {
1216 : return false;
1217 : }
1218 : #endif
1219 : #if AP_RPM_ENABLED
1220 66519 : auto *rpm = AP::rpm();
1221 66519 : if (rpm && !rpm->arming_checks(sizeof(buffer), buffer)) {
1222 0 : check_failed(Check::PARAMETERS, report, "%s", buffer);
1223 0 : return false;
1224 : }
1225 : #endif
1226 : #if AP_RELAY_ENABLED
1227 66519 : auto *relay = AP::relay();
1228 66519 : if (relay && !relay->arming_checks(sizeof(buffer), buffer)) {
1229 0 : check_failed(Check::PARAMETERS, report, "%s", buffer);
1230 0 : return false;
1231 : }
1232 : #endif
1233 : #if HAL_PARACHUTE_ENABLED
1234 66519 : auto *chute = AP::parachute();
1235 66519 : if (chute && !chute->arming_checks(sizeof(buffer), buffer)) {
1236 38 : check_failed(Check::PARAMETERS, report, "%s", buffer);
1237 38 : return false;
1238 : }
1239 : #endif
1240 : #if HAL_BUTTON_ENABLED
1241 66481 : const auto &button = AP::button();
1242 66481 : if (!button.arming_checks(sizeof(buffer), buffer)) {
1243 0 : check_failed(Check::PARAMETERS, report, "%s", buffer);
1244 0 : return false;
1245 : }
1246 : #endif
1247 : }
1248 :
1249 73896 : return true;
1250 : }
1251 :
1252 130880 : bool AP_Arming::terrain_database_required() const
1253 : {
1254 : #if AP_MISSION_ENABLED
1255 130880 : if (AP::mission().contains_terrain_alt_items()) {
1256 310 : return true;
1257 : }
1258 : #endif
1259 : #if AP_FENCE_ENABLED
1260 130570 : const AC_Fence* fence = AP::fence();
1261 130570 : if (fence != nullptr && fence->terrain_database_required()) {
1262 189 : return true;
1263 : }
1264 : #endif
1265 130381 : return false;
1266 : }
1267 :
1268 : // check terrain database is fit-for-purpose
1269 140495 : bool AP_Arming::terrain_checks(bool report) const
1270 : {
1271 140495 : if (!check_enabled(Check::PARAMETERS)) {
1272 7415 : return true;
1273 : }
1274 :
1275 133080 : if (!terrain_database_required()) {
1276 132170 : return true;
1277 : }
1278 :
1279 : #if AP_TERRAIN_AVAILABLE
1280 :
1281 910 : const AP_Terrain *terrain = AP_Terrain::get_singleton();
1282 910 : if (terrain == nullptr) {
1283 0 : check_failed(Check::PARAMETERS, report, "terrain disabled");
1284 0 : return false;
1285 : }
1286 :
1287 910 : if (!terrain->enabled()) {
1288 4 : check_failed(Check::PARAMETERS, report, "terrain disabled");
1289 4 : return false;
1290 : }
1291 :
1292 : char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
1293 906 : if (!terrain->pre_arm_checks(fail_msg, sizeof(fail_msg))) {
1294 351 : check_failed(Check::PARAMETERS, report, "%s", fail_msg);
1295 351 : return false;
1296 : }
1297 :
1298 555 : return true;
1299 :
1300 : #else
1301 : check_failed(Check::PARAMETERS, report, "terrain required but disabled");
1302 : return false;
1303 : #endif
1304 : }
1305 :
1306 :
1307 : #if HAL_PROXIMITY_ENABLED
1308 : // check nothing is too close to vehicle
1309 140495 : bool AP_Arming::proximity_checks(bool report) const
1310 : {
1311 140495 : const AP_Proximity *proximity = AP::proximity();
1312 : // return true immediately if no sensor present
1313 140495 : if (proximity == nullptr) {
1314 27865 : return true;
1315 : }
1316 : char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
1317 112630 : if (!proximity->prearm_healthy(buffer, ARRAY_SIZE(buffer))) {
1318 235 : check_failed(report, "%s", buffer);
1319 235 : return false;
1320 : }
1321 112395 : return true;
1322 : }
1323 : #endif // HAL_PROXIMITY_ENABLED
1324 :
1325 : #if HAL_MAX_CAN_PROTOCOL_DRIVERS && HAL_CANMANAGER_ENABLED
1326 140495 : bool AP_Arming::can_checks(bool report)
1327 : {
1328 140495 : if (check_enabled(Check::SYSTEM)) {
1329 133080 : char fail_msg[100] = {};
1330 : (void)fail_msg; // might be left unused
1331 133080 : uint8_t num_drivers = AP::can().get_num_drivers();
1332 :
1333 399240 : for (uint8_t i = 0; i < num_drivers; i++) {
1334 266160 : switch (AP::can().get_driver_type(i)) {
1335 0 : case AP_CAN::Protocol::PiccoloCAN: {
1336 : #if AP_PICCOLOCAN_ENABLED
1337 0 : AP_PiccoloCAN *ap_pcan = AP_PiccoloCAN::get_pcan(i);
1338 :
1339 0 : if (ap_pcan != nullptr && !ap_pcan->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
1340 0 : check_failed(Check::SYSTEM, report, "PiccoloCAN: %s", fail_msg);
1341 0 : return false;
1342 : }
1343 :
1344 : #else
1345 : check_failed(Check::SYSTEM, report, "PiccoloCAN not enabled");
1346 : return false;
1347 : #endif
1348 0 : break;
1349 : }
1350 200 : case AP_CAN::Protocol::DroneCAN:
1351 : {
1352 : #if HAL_ENABLE_DRONECAN_DRIVERS
1353 200 : AP_DroneCAN *ap_dronecan = AP_DroneCAN::get_dronecan(i);
1354 200 : if (ap_dronecan != nullptr && !ap_dronecan->prearm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
1355 0 : check_failed(Check::SYSTEM, report, "DroneCAN: %s", fail_msg);
1356 0 : return false;
1357 : }
1358 : #endif
1359 200 : break;
1360 : }
1361 0 : case AP_CAN::Protocol::USD1:
1362 : case AP_CAN::Protocol::TOFSenseP:
1363 : case AP_CAN::Protocol::RadarCAN:
1364 : case AP_CAN::Protocol::Benewake:
1365 : {
1366 0 : for (uint8_t j = i; j; j--) {
1367 0 : if (AP::can().get_driver_type(i) == AP::can().get_driver_type(j-1)) {
1368 0 : check_failed(Check::SYSTEM, report, "Same rfnd on different CAN ports");
1369 0 : return false;
1370 : }
1371 : }
1372 0 : break;
1373 : }
1374 265960 : case AP_CAN::Protocol::EFI_NWPMU:
1375 : case AP_CAN::Protocol::None:
1376 : case AP_CAN::Protocol::Scripting:
1377 : case AP_CAN::Protocol::Scripting2:
1378 : case AP_CAN::Protocol::KDECAN:
1379 :
1380 265960 : break;
1381 : }
1382 : }
1383 : }
1384 140495 : return true;
1385 : }
1386 : #endif // HAL_MAX_CAN_PROTOCOL_DRIVERS && HAL_CANMANAGER_ENABLED
1387 :
1388 :
1389 : #if AP_FENCE_ENABLED
1390 140495 : bool AP_Arming::fence_checks(bool display_failure)
1391 : {
1392 140495 : const AC_Fence *fence = AP::fence();
1393 140495 : if (fence == nullptr) {
1394 1702 : return true;
1395 : }
1396 :
1397 : // check fence is ready
1398 : char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
1399 138793 : if (fence->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
1400 138112 : return true;
1401 : }
1402 :
1403 681 : check_failed(display_failure, "%s", fail_msg);
1404 :
1405 : #if AP_SDCARD_STORAGE_ENABLED
1406 681 : if (fence->failed_sdcard_storage() || StorageManager::storage_failed()) {
1407 0 : check_failed(display_failure, "Failed to open fence storage");
1408 0 : return false;
1409 : }
1410 : #endif
1411 :
1412 681 : return false;
1413 : }
1414 : #endif // AP_FENCE_ENABLED
1415 :
1416 : #if AP_CAMERA_RUNCAM_ENABLED
1417 140495 : bool AP_Arming::camera_checks(bool display_failure)
1418 : {
1419 140495 : if (check_enabled(Check::CAMERA)) {
1420 133080 : AP_RunCam *runcam = AP::runcam();
1421 133080 : if (runcam == nullptr) {
1422 133064 : return true;
1423 : }
1424 :
1425 : // check camera is ready
1426 : char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
1427 16 : if (!runcam->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
1428 0 : check_failed(Check::CAMERA, display_failure, "%s", fail_msg);
1429 0 : return false;
1430 : }
1431 : }
1432 7431 : return true;
1433 : }
1434 : #endif // AP_CAMERA_RUNCAM_ENABLED
1435 :
1436 : #if OSD_ENABLED
1437 140495 : bool AP_Arming::osd_checks(bool display_failure) const
1438 : {
1439 140495 : if (check_enabled(Check::OSD)) {
1440 : // if no OSD then pass
1441 133080 : const AP_OSD *osd = AP::osd();
1442 133080 : if (osd == nullptr) {
1443 1702 : return true;
1444 : }
1445 : // do osd checks for configuration
1446 : char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
1447 131378 : if (!osd->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
1448 0 : check_failed(Check::OSD, display_failure, "%s", fail_msg);
1449 0 : return false;
1450 : }
1451 : }
1452 138793 : return true;
1453 : }
1454 : #endif // OSD_ENABLED
1455 :
1456 : #if HAL_MOUNT_ENABLED
1457 140495 : bool AP_Arming::mount_checks(bool display_failure) const
1458 : {
1459 140495 : if (check_enabled(Check::CAMERA)) {
1460 133080 : AP_Mount *mount = AP::mount();
1461 133080 : if (mount == nullptr) {
1462 1741 : return true;
1463 : }
1464 131378 : char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1] = {};
1465 131378 : if (!mount->pre_arm_checks(fail_msg, sizeof(fail_msg))) {
1466 39 : check_failed(Check::CAMERA, display_failure, "Mount: %s", fail_msg);
1467 39 : return false;
1468 : }
1469 : }
1470 138754 : return true;
1471 : }
1472 : #endif // HAL_MOUNT_ENABLED
1473 :
1474 : #if AP_FETTEC_ONEWIRE_ENABLED
1475 140495 : bool AP_Arming::fettec_checks(bool display_failure) const
1476 : {
1477 140495 : const AP_FETtecOneWire *f = AP_FETtecOneWire::get_singleton();
1478 140495 : if (f == nullptr) {
1479 0 : return true;
1480 : }
1481 :
1482 : // check ESCs are ready
1483 : char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
1484 140495 : if (!f->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
1485 44 : check_failed(display_failure, "FETtec: %s", fail_msg);
1486 44 : return false;
1487 : }
1488 140451 : return true;
1489 : }
1490 : #endif // AP_FETTEC_ONEWIRE_ENABLED
1491 :
1492 : #if AP_ARMING_AUX_AUTH_ENABLED
1493 : // request an auxiliary authorisation id. This id should be used in subsequent calls to set_aux_auth_passed/failed
1494 : // returns true on success
1495 22 : bool AP_Arming::get_aux_auth_id(uint8_t& auth_id)
1496 : {
1497 22 : WITH_SEMAPHORE(aux_auth_sem);
1498 :
1499 : // check we have enough room to allocate another id
1500 22 : if (aux_auth_count >= aux_auth_count_max) {
1501 0 : aux_auth_error = true;
1502 0 : return false;
1503 : }
1504 :
1505 : // allocate buffer for failure message
1506 22 : if (aux_auth_fail_msg == nullptr) {
1507 22 : aux_auth_fail_msg = (char *)calloc(aux_auth_str_len, sizeof(char));
1508 22 : if (aux_auth_fail_msg == nullptr) {
1509 0 : aux_auth_error = true;
1510 0 : return false;
1511 : }
1512 : }
1513 22 : auth_id = aux_auth_count;
1514 22 : aux_auth_count++;
1515 22 : return true;
1516 22 : }
1517 :
1518 : // set auxiliary authorisation passed
1519 85 : void AP_Arming::set_aux_auth_passed(uint8_t auth_id)
1520 : {
1521 85 : WITH_SEMAPHORE(aux_auth_sem);
1522 :
1523 : // sanity check auth_id
1524 85 : if (auth_id >= aux_auth_count) {
1525 0 : return;
1526 : }
1527 :
1528 85 : aux_auth_state[auth_id] = AuxAuthStates::AUTH_PASSED;
1529 85 : }
1530 :
1531 : // set auxiliary authorisation failed and provide failure message
1532 67 : void AP_Arming::set_aux_auth_failed(uint8_t auth_id, const char* fail_msg)
1533 : {
1534 67 : WITH_SEMAPHORE(aux_auth_sem);
1535 :
1536 : // sanity check auth_id
1537 67 : if (auth_id >= aux_auth_count) {
1538 0 : return;
1539 : }
1540 :
1541 : // update state
1542 67 : aux_auth_state[auth_id] = AuxAuthStates::AUTH_FAILED;
1543 :
1544 : // store failure message if this authoriser has the lowest auth_id
1545 67 : for (uint8_t i = 0; i < auth_id; i++) {
1546 0 : if (aux_auth_state[i] == AuxAuthStates::AUTH_FAILED) {
1547 0 : return;
1548 : }
1549 : }
1550 67 : if (aux_auth_fail_msg != nullptr) {
1551 67 : if (fail_msg == nullptr) {
1552 0 : strncpy(aux_auth_fail_msg, "Auxiliary authorisation refused", aux_auth_str_len);
1553 : } else {
1554 67 : strncpy(aux_auth_fail_msg, fail_msg, aux_auth_str_len);
1555 : }
1556 67 : aux_auth_fail_msg_source = auth_id;
1557 : }
1558 67 : }
1559 :
1560 97 : void AP_Arming::reset_all_aux_auths()
1561 : {
1562 97 : WITH_SEMAPHORE(aux_auth_sem);
1563 :
1564 : // clear all auxiliary authorisation ids
1565 97 : aux_auth_count = 0;
1566 : // clear any previous allocation errors
1567 97 : aux_auth_error = false;
1568 :
1569 : // reset states for all auxiliary authorisation ids
1570 388 : for (uint8_t i = 0; i < aux_auth_count_max; i++) {
1571 291 : aux_auth_state[i] = AuxAuthStates::NO_RESPONSE;
1572 : }
1573 :
1574 : // free up the failure message buffer
1575 97 : if (aux_auth_fail_msg != nullptr) {
1576 1 : free(aux_auth_fail_msg);
1577 1 : aux_auth_fail_msg = nullptr;
1578 : }
1579 97 : }
1580 :
1581 140495 : bool AP_Arming::aux_auth_checks(bool display_failure)
1582 : {
1583 : // handle error cases
1584 140495 : if (aux_auth_error) {
1585 0 : if (aux_auth_fail_msg == nullptr) {
1586 0 : check_failed(Check::AUX_AUTH, display_failure, "memory low for auxiliary authorisation");
1587 : } else {
1588 0 : check_failed(Check::AUX_AUTH, display_failure, "Too many auxiliary authorisers");
1589 : }
1590 0 : return false;
1591 : }
1592 :
1593 140495 : WITH_SEMAPHORE(aux_auth_sem);
1594 :
1595 : // check results for each auxiliary authorisation id
1596 140495 : bool some_failures = false;
1597 140495 : bool failure_msg_sent = false;
1598 140495 : bool waiting_for_responses = false;
1599 141030 : for (uint8_t i = 0; i < aux_auth_count; i++) {
1600 535 : switch (aux_auth_state[i]) {
1601 282 : case AuxAuthStates::NO_RESPONSE:
1602 282 : waiting_for_responses = true;
1603 282 : break;
1604 170 : case AuxAuthStates::AUTH_FAILED:
1605 170 : some_failures = true;
1606 170 : if (i == aux_auth_fail_msg_source) {
1607 170 : check_failed(Check::AUX_AUTH, display_failure, "%s", aux_auth_fail_msg);
1608 170 : failure_msg_sent = true;
1609 : }
1610 170 : break;
1611 83 : case AuxAuthStates::AUTH_PASSED:
1612 83 : break;
1613 : }
1614 : }
1615 :
1616 : // send failure or waiting message
1617 140495 : if (some_failures) {
1618 170 : if (!failure_msg_sent) {
1619 0 : check_failed(Check::AUX_AUTH, display_failure, "Auxiliary authorisation refused");
1620 : }
1621 170 : return false;
1622 140325 : } else if (waiting_for_responses) {
1623 282 : check_failed(Check::AUX_AUTH, display_failure, "Waiting for auxiliary authorisation");
1624 282 : return false;
1625 : }
1626 :
1627 : // if we got this far all auxiliary checks must have passed
1628 140043 : return true;
1629 140495 : }
1630 : #endif // AP_ARMING_AUX_AUTH_ENABLED
1631 :
1632 : #if HAL_GENERATOR_ENABLED
1633 140495 : bool AP_Arming::generator_checks(bool display_failure) const
1634 : {
1635 140495 : const AP_Generator *generator = AP::generator();
1636 140495 : if (generator == nullptr) {
1637 0 : return true;
1638 : }
1639 140495 : char failure_msg[100] = {};
1640 140495 : if (!generator->pre_arm_check(failure_msg, sizeof(failure_msg))) {
1641 374 : check_failed(display_failure, "Generator: %s", failure_msg);
1642 374 : return false;
1643 : }
1644 140121 : return true;
1645 : }
1646 : #endif // HAL_GENERATOR_ENABLED
1647 :
1648 : #if AP_OPENDRONEID_ENABLED
1649 : // OpenDroneID Checks
1650 140684 : bool AP_Arming::opendroneid_checks(bool display_failure)
1651 : {
1652 140684 : auto &opendroneid = AP::opendroneid();
1653 :
1654 140684 : char failure_msg[100] {};
1655 140684 : if (!opendroneid.pre_arm_check(failure_msg, sizeof(failure_msg))) {
1656 0 : check_failed(display_failure, "OpenDroneID: %s", failure_msg);
1657 0 : return false;
1658 : }
1659 140684 : return true;
1660 : }
1661 : #endif // AP_OPENDRONEID_ENABLED
1662 :
1663 : //Check for multiple RC in serial protocols
1664 140684 : bool AP_Arming::serial_protocol_checks(bool display_failure)
1665 : {
1666 140684 : if (AP::serialmanager().have_serial(AP_SerialManager::SerialProtocol_RCIN, 1)) {
1667 0 : check_failed(display_failure, "Multiple SERIAL ports configured for RC input");
1668 0 : return false;
1669 : }
1670 140684 : char failure_msg[100] = {};
1671 140684 : if (!AP::serialmanager().pre_arm_checks(failure_msg, ARRAY_SIZE(failure_msg))) {
1672 0 : check_failed(display_failure, "%s", failure_msg);
1673 0 : return false;
1674 : }
1675 140684 : return true;
1676 : }
1677 :
1678 : //Check for estop
1679 140495 : bool AP_Arming::estop_checks(bool display_failure)
1680 : {
1681 140495 : if (!SRV_Channels::get_emergency_stop()) {
1682 : // not emergency-stopped, so no prearm failure:
1683 140428 : return true;
1684 : }
1685 : #if AP_RC_CHANNEL_ENABLED
1686 : // vehicle is emergency-stopped; if this *appears* to have been done via switch then we do not fail prearms:
1687 67 : const RC_Channel *chan = rc().find_channel_for_option(RC_Channel::AUX_FUNC::ARM_EMERGENCY_STOP);
1688 67 : if (chan != nullptr) {
1689 : // an RC channel is configured for arm_emergency_stop option, so estop maybe activated via this switch
1690 44 : if (chan->get_aux_switch_pos() == RC_Channel::AuxSwitchPos::LOW) {
1691 : // switch is configured and is in estop position, so likely the reason we are estopped, so no prearm failure
1692 4 : return true; // no prearm failure
1693 : }
1694 : }
1695 : #endif // AP_RC_CHANNEL_ENABLED
1696 63 : check_failed(display_failure,"Motors Emergency Stopped");
1697 63 : return false;
1698 : }
1699 :
1700 140495 : bool AP_Arming::pre_arm_checks(bool report)
1701 : {
1702 : #if !APM_BUILD_COPTER_OR_HELI
1703 47779 : if (armed || arming_required() == Required::NO) {
1704 : // if we are already armed or don't need any arming checks
1705 : // then skip the checks
1706 0 : return true;
1707 : }
1708 : #endif
1709 :
1710 140495 : bool checks_result = hardware_safety_check(report)
1711 : #if HAL_HAVE_IMU_HEATER
1712 : & heater_min_temperature_checks(report)
1713 : #endif
1714 : #if AP_BARO_ENABLED
1715 140495 : & barometer_checks(report)
1716 : #endif
1717 : #if AP_INERTIALSENSOR_ENABLED
1718 140495 : & ins_checks(report)
1719 : #endif
1720 : #if AP_COMPASS_ENABLED
1721 140495 : & compass_checks(report)
1722 : #endif
1723 : #if AP_GPS_ENABLED
1724 140495 : & gps_checks(report)
1725 : #endif
1726 : #if AP_BATTERY_ENABLED
1727 140495 : & battery_checks(report)
1728 : #endif
1729 : #if HAL_LOGGING_ENABLED
1730 140495 : & logging_checks(report)
1731 : #endif
1732 : #if AP_RC_CHANNEL_ENABLED
1733 140495 : & manual_transmitter_checks(report)
1734 : #endif
1735 : #if AP_MISSION_ENABLED
1736 140495 : & mission_checks(report)
1737 : #endif
1738 : #if AP_RANGEFINDER_ENABLED
1739 140495 : & rangefinder_checks(report)
1740 : #endif
1741 140495 : & servo_checks(report)
1742 140495 : & board_voltage_checks(report)
1743 140495 : & system_checks(report)
1744 140495 : & terrain_checks(report)
1745 : #if HAL_MAX_CAN_PROTOCOL_DRIVERS && HAL_CANMANAGER_ENABLED
1746 140495 : & can_checks(report)
1747 : #endif
1748 : #if HAL_GENERATOR_ENABLED
1749 140495 : & generator_checks(report)
1750 : #endif
1751 : #if HAL_PROXIMITY_ENABLED
1752 140495 : & proximity_checks(report)
1753 : #endif
1754 : #if AP_CAMERA_RUNCAM_ENABLED
1755 140495 : & camera_checks(report)
1756 : #endif
1757 : #if OSD_ENABLED
1758 140495 : & osd_checks(report)
1759 : #endif
1760 : #if HAL_MOUNT_ENABLED
1761 140495 : & mount_checks(report)
1762 : #endif
1763 : #if AP_FETTEC_ONEWIRE_ENABLED
1764 140495 : & fettec_checks(report)
1765 : #endif
1766 : #if HAL_VISUALODOM_ENABLED
1767 140495 : & visodom_checks(report)
1768 : #endif
1769 : #if AP_ARMING_AUX_AUTH_ENABLED
1770 140495 : & aux_auth_checks(report)
1771 : #endif
1772 : #if AP_RC_CHANNEL_ENABLED
1773 140495 : & disarm_switch_checks(report)
1774 : #endif
1775 : #if AP_FENCE_ENABLED
1776 140495 : & fence_checks(report)
1777 : #endif
1778 : #if AP_OPENDRONEID_ENABLED
1779 140495 : & opendroneid_checks(report)
1780 : #endif
1781 : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
1782 : & crashdump_checks(report)
1783 : #endif
1784 140495 : & serial_protocol_checks(report)
1785 140495 : & estop_checks(report);
1786 :
1787 140495 : if (!checks_result && last_prearm_checks_result) { // check went from true to false
1788 430 : report_immediately = true;
1789 : }
1790 140495 : last_prearm_checks_result = checks_result;
1791 :
1792 140495 : return checks_result;
1793 : }
1794 :
1795 1034 : bool AP_Arming::arm_checks(AP_Arming::Method method)
1796 : {
1797 : #if AP_RC_CHANNEL_ENABLED
1798 1034 : if (check_enabled(Check::RC)) {
1799 1030 : if (!rc_arm_checks(method)) {
1800 1 : return false;
1801 : }
1802 : }
1803 : #endif
1804 :
1805 : // Run estop check again, here in the arm checks there is no need
1806 : // bypass the check if arm emergency stop aux function is setup
1807 1033 : if (SRV_Channels::get_emergency_stop()) {
1808 0 : check_failed(true, "Motors Emergency Stopped");
1809 0 : return false;
1810 : }
1811 :
1812 : // ensure the GPS drivers are ready on any final changes
1813 1033 : if (check_enabled(Check::GPS_CONFIG)) {
1814 983 : if (!AP::gps().prepare_for_arming()) {
1815 0 : return false;
1816 : }
1817 : }
1818 :
1819 : // note that this will prepare AP_Logger to start logging
1820 : // so should be the last check to be done before arming
1821 :
1822 : // Note also that we need to PrepForArming() regardless of whether
1823 : // the arming check flag is set - disabling the arming check
1824 : // should not stop logging from working.
1825 :
1826 : #if HAL_LOGGING_ENABLED
1827 1033 : AP_Logger *logger = AP_Logger::get_singleton();
1828 1033 : if (logger->logging_present()) {
1829 : // If we're configured to log, prep it
1830 1033 : logger->PrepForArming();
1831 1033 : if (!logger->logging_started() &&
1832 0 : check_enabled(Check::LOGGING)) {
1833 0 : check_failed(Check::LOGGING, true, "Logging not started");
1834 0 : return false;
1835 : }
1836 : }
1837 : #endif // HAL_LOGGING_ENABLED
1838 :
1839 1033 : return true;
1840 : }
1841 :
1842 : #if !AP_GPS_BLENDED_ENABLED
1843 : bool AP_Arming::blending_auto_switch_checks(bool report)
1844 : {
1845 : if (AP::gps().get_auto_switch_type() == 2) {
1846 : if (report) {
1847 : check_failed(Check::GPS, true, "GPS_AUTO_SWITCH==2 but no blending");
1848 : }
1849 : return false;
1850 : }
1851 : return true;
1852 : }
1853 : #endif
1854 :
1855 : #if AP_ARMING_CRASHDUMP_ACK_ENABLED
1856 : bool AP_Arming::crashdump_checks(bool report)
1857 : {
1858 : if (hal.util->last_crash_dump_size() == 0) {
1859 : // no crash dump data
1860 : return true;
1861 : }
1862 :
1863 : // see if the user has acknowledged the failure and wants to fly anyway:
1864 : if (crashdump_ack.acked) {
1865 : // they may have acked the problem, that doesn't mean we don't
1866 : // continue to warn them they're on thin ice:
1867 : if (report) {
1868 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "CrashDump data detected");
1869 : }
1870 : return true;
1871 : }
1872 :
1873 : check_failed(Check::PARAMETERS, report, "CrashDump data detected");
1874 :
1875 : return false;
1876 : }
1877 : #endif // AP_ARMING_CRASHDUMP_ACK_ENABLED
1878 :
1879 189 : bool AP_Arming::mandatory_checks(bool report)
1880 : {
1881 189 : bool ret = true;
1882 : #if AP_OPENDRONEID_ENABLED
1883 189 : ret &= opendroneid_checks(report);
1884 : #endif
1885 189 : ret &= rc_in_calibration_check(report);
1886 189 : ret &= serial_protocol_checks(report);
1887 189 : return ret;
1888 : }
1889 :
1890 : //returns true if arming occurred successfully
1891 1247 : bool AP_Arming::arm(AP_Arming::Method method, const bool do_arming_checks)
1892 : {
1893 1247 : if (armed) { //already armed
1894 6 : return false;
1895 : }
1896 :
1897 1241 : if (method == Method::RUDDER) {
1898 54 : switch (get_rudder_arming_type()) {
1899 0 : case AP_Arming::RudderArming::IS_DISABLED:
1900 : //parameter disallows rudder arming/disabling
1901 0 : return false;
1902 54 : case AP_Arming::RudderArming::ARMONLY:
1903 : case AP_Arming::RudderArming::ARMDISARM:
1904 54 : break;
1905 : }
1906 : }
1907 :
1908 1241 : running_arming_checks = true; // so we show Arm: rather than Disarm: in messages
1909 :
1910 1241 : if ((!do_arming_checks && mandatory_checks(true)) || (pre_arm_checks(true) && arm_checks(method))) {
1911 1127 : armed = true;
1912 1127 : last_arm_time_us = AP_HAL::micros64();
1913 :
1914 1127 : _last_arm_method = method;
1915 :
1916 : #if HAL_LOGGING_ENABLED
1917 1127 : Log_Write_Arm(!do_arming_checks, method); // note Log_Write_Armed takes forced not do_arming_checks
1918 : #endif
1919 :
1920 : } else {
1921 : #if HAL_LOGGING_ENABLED
1922 114 : AP::logger().arming_failure();
1923 : #endif
1924 114 : armed = false;
1925 : }
1926 :
1927 1241 : running_arming_checks = false;
1928 :
1929 1241 : if (armed && do_arming_checks && should_skip_all_checks()) {
1930 1 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Warning: Arming Checks Disabled");
1931 : }
1932 :
1933 : #if HAL_GYROFFT_ENABLED
1934 : // make sure the FFT subsystem is enabled if arming checks have been disabled
1935 1241 : AP_GyroFFT *fft = AP::fft();
1936 1241 : if (fft != nullptr) {
1937 1241 : fft->prepare_for_arming();
1938 : }
1939 : #endif
1940 :
1941 : #if AP_TERRAIN_AVAILABLE
1942 1241 : if (armed) {
1943 : // tell terrain we have just armed, so it can setup
1944 : // a reference location for terrain adjustment
1945 1127 : auto *terrain = AP::terrain();
1946 1127 : if (terrain != nullptr) {
1947 1014 : terrain->set_reference_location();
1948 : }
1949 : }
1950 : #endif
1951 :
1952 : #if AP_FENCE_ENABLED
1953 1241 : if (armed) {
1954 1127 : auto *fence = AP::fence();
1955 1127 : if (fence != nullptr) {
1956 1127 : fence->auto_enable_fence_on_arming();
1957 : }
1958 : }
1959 : #endif
1960 : #if defined(HAL_ARM_GPIO_PIN)
1961 : update_arm_gpio();
1962 : #endif
1963 1241 : return armed;
1964 : }
1965 :
1966 : //returns true if disarming occurred successfully
1967 1417 : bool AP_Arming::disarm(const AP_Arming::Method method, bool do_disarm_checks)
1968 : {
1969 1417 : if (!armed) { // already disarmed
1970 310 : return false;
1971 : }
1972 1107 : if (method == AP_Arming::Method::RUDDER) {
1973 : // if throttle is not down, then pilot cannot rudder arm/disarm
1974 10 : if (rc().get_throttle_channel().get_control_in() > 0) {
1975 4 : return false;
1976 : }
1977 : // option must be enabled:
1978 6 : if (get_rudder_arming_type() != AP_Arming::RudderArming::ARMDISARM) {
1979 0 : gcs().send_text(MAV_SEVERITY_INFO, "Disarm: rudder disarm disabled");
1980 0 : return false;
1981 : }
1982 : }
1983 1103 : armed = false;
1984 1103 : _last_disarm_method = method;
1985 :
1986 : #if HAL_LOGGING_ENABLED
1987 1103 : Log_Write_Disarm(!do_disarm_checks, method); // Log_Write_Disarm takes "force"
1988 :
1989 1103 : check_forced_logging(method);
1990 : #endif
1991 :
1992 : #if HAL_HAVE_SAFETY_SWITCH
1993 1103 : AP_BoardConfig *board_cfg = AP_BoardConfig::get_singleton();
1994 2206 : if ((board_cfg != nullptr) &&
1995 1103 : (board_cfg->get_safety_button_options() & AP_BoardConfig::BOARD_SAFETY_OPTION_SAFETY_ON_DISARM)) {
1996 0 : hal.rcout->force_safety_on();
1997 : }
1998 : #endif // HAL_HAVE_SAFETY_SWITCH
1999 :
2000 : #if AP_COMPASS_LEARN_COPY_FROM_EKF_ENABLED
2001 : // save any compass offsets the EKF has learned. This must be done
2002 : // before the vehicle calls hal.util->set_soft_armed(false); once the
2003 : // EKF sees onGround it clears finalInflightMagInit and will no
2004 : // longer hand out learned offsets.
2005 1103 : AP::compass().save_ekf_learned_offsets();
2006 : #endif // AP_COMPASS_LEARN_COPY_FROM_EKF_ENABLED
2007 :
2008 : #if HAL_GYROFFT_ENABLED
2009 1103 : AP_GyroFFT *fft = AP::fft();
2010 1103 : if (fft != nullptr) {
2011 1103 : fft->save_params_on_disarm();
2012 : }
2013 : #endif
2014 :
2015 : #if AP_FENCE_ENABLED
2016 1103 : AC_Fence *fence = AP::fence();
2017 1103 : if (fence != nullptr) {
2018 1103 : fence->auto_disable_fence_on_disarming();
2019 : }
2020 : #endif
2021 : #if defined(HAL_ARM_GPIO_PIN)
2022 : update_arm_gpio();
2023 : #endif
2024 1103 : return true;
2025 : }
2026 :
2027 : #if defined(HAL_ARM_GPIO_PIN)
2028 : void AP_Arming::update_arm_gpio()
2029 : {
2030 : if (!AP_BoardConfig::arming_gpio_disabled()) {
2031 : hal.gpio->write(HAL_ARM_GPIO_PIN, HAL_ARM_GPIO_POL_INVERT ? !armed : armed);
2032 : }
2033 : }
2034 : #endif
2035 :
2036 2230 : void AP_Arming::send_arm_disarm_statustext(const char *str) const
2037 : {
2038 2230 : if (option_enabled(AP_Arming::Option::DISABLE_STATUSTEXT_ON_STATE_CHANGE)) {
2039 0 : return;
2040 : }
2041 2230 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "%s", str);
2042 : }
2043 :
2044 24742314 : AP_Arming::Required AP_Arming::arming_required() const
2045 : {
2046 : #if AP_OPENDRONEID_ENABLED
2047 : // cannot be disabled if OpenDroneID is present
2048 24742314 : if (AP_OpenDroneID::get_singleton() != nullptr && AP::opendroneid().enabled()) {
2049 0 : if (require != Required::YES_MIN_PWM && require != Required::YES_ZERO_PWM) {
2050 0 : return Required::YES_MIN_PWM;
2051 : }
2052 : }
2053 : #endif
2054 24742303 : return require;
2055 : }
2056 :
2057 : #if AP_RC_CHANNEL_ENABLED
2058 : // Copter and sub share the same RC input limits
2059 : // Copter checks that min and max have been configured by default, Sub does not
2060 95630 : bool AP_Arming::rc_checks_copter_sub(const bool display_failure, const RC_Channel *channels[4]) const
2061 : {
2062 : // set rc-checks to success if RC checks are disabled
2063 95630 : if (!check_enabled(Check::RC)) {
2064 9 : return true;
2065 : }
2066 :
2067 95621 : bool ret = true;
2068 :
2069 95621 : const char *channel_names[] = { "Roll", "Pitch", "Throttle", "Yaw" };
2070 :
2071 478105 : for (uint8_t i=0; i<ARRAY_SIZE(channel_names);i++) {
2072 382484 : const RC_Channel *channel = channels[i];
2073 382484 : const char *channel_name = channel_names[i];
2074 : // check if radio has been calibrated
2075 382484 : if (channel->get_radio_min() > RC_Channel::RC_CALIB_MIN_LIMIT_PWM) {
2076 0 : check_failed(Check::RC, display_failure, "%s radio min too high", channel_name);
2077 0 : ret = false;
2078 : }
2079 382484 : if (channel->get_radio_max() < RC_Channel::RC_CALIB_MAX_LIMIT_PWM) {
2080 0 : check_failed(Check::RC, display_failure, "%s radio max too low", channel_name);
2081 0 : ret = false;
2082 : }
2083 : }
2084 95621 : return ret;
2085 : }
2086 : #endif // AP_RC_CHANNEL_ENABLED
2087 :
2088 : #if HAL_VISUALODOM_ENABLED
2089 : // check visual odometry is working
2090 140495 : bool AP_Arming::visodom_checks(bool display_failure) const
2091 : {
2092 140495 : if (!check_enabled(Check::VISION)) {
2093 7415 : return true;
2094 : }
2095 :
2096 133080 : AP_VisualOdom *visual_odom = AP::visualodom();
2097 133080 : if (visual_odom != nullptr) {
2098 : char fail_msg[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1];
2099 133080 : if (!visual_odom->pre_arm_check(fail_msg, ARRAY_SIZE(fail_msg))) {
2100 39 : check_failed(Check::VISION, display_failure, "VisOdom: %s", fail_msg);
2101 39 : return false;
2102 : }
2103 : }
2104 :
2105 133041 : return true;
2106 : }
2107 : #endif
2108 :
2109 : #if AP_RC_CHANNEL_ENABLED
2110 : // check disarm switch is asserted
2111 233536 : bool AP_Arming::disarm_switch_checks(bool display_failure) const
2112 : {
2113 233536 : const RC_Channel *chan = rc().find_channel_for_option(RC_Channel::AUX_FUNC::DISARM);
2114 233536 : if (chan != nullptr &&
2115 0 : chan->get_aux_switch_pos() == RC_Channel::AuxSwitchPos::HIGH) {
2116 0 : check_failed(display_failure, "Disarm Switch on");
2117 0 : return false;
2118 : }
2119 :
2120 233536 : return true;
2121 : }
2122 : #endif // AP_RC_CHANNEL_ENABLED
2123 :
2124 : #if HAL_LOGGING_ENABLED
2125 1127 : void AP_Arming::Log_Write_Arm(const bool forced, const AP_Arming::Method method)
2126 : {
2127 1127 : const struct log_Arm_Disarm pkt {
2128 : LOG_PACKET_HEADER_INIT(LOG_ARM_DISARM_MSG),
2129 1127 : time_us : AP_HAL::micros64(),
2130 2254 : arm_state : is_armed(),
2131 1127 : arm_checks : get_enabled_checks(),
2132 1127 : forced : forced,
2133 : method : (uint8_t)method,
2134 1127 : };
2135 1127 : AP::logger().WriteCriticalBlock(&pkt, sizeof(pkt));
2136 1127 : AP::logger().Write_Event(LogEvent::ARMED);
2137 1127 : }
2138 :
2139 1103 : void AP_Arming::Log_Write_Disarm(const bool forced, const AP_Arming::Method method)
2140 : {
2141 1103 : const struct log_Arm_Disarm pkt {
2142 : LOG_PACKET_HEADER_INIT(LOG_ARM_DISARM_MSG),
2143 1103 : time_us : AP_HAL::micros64(),
2144 2206 : arm_state : is_armed(),
2145 : arm_checks : 0,
2146 1103 : forced : forced,
2147 : method : (uint8_t)method
2148 1103 : };
2149 1103 : AP::logger().WriteCriticalBlock(&pkt, sizeof(pkt));
2150 1103 : AP::logger().Write_Event(LogEvent::DISARMED);
2151 1103 : }
2152 :
2153 : // check if we should keep logging after disarming
2154 1103 : void AP_Arming::check_forced_logging(const AP_Arming::Method method)
2155 : {
2156 : // keep logging if disarmed for a bad reason
2157 1103 : switch(method) {
2158 26 : case Method::TERMINATION:
2159 : case Method::CPUFAILSAFE:
2160 : case Method::BATTERYFAILSAFE:
2161 : case Method::AFS:
2162 : case Method::ADSBCOLLISIONACTION:
2163 : case Method::PARACHUTE_RELEASE:
2164 : case Method::CRASH:
2165 : case Method::FENCEBREACH:
2166 : case Method::RADIOFAILSAFE:
2167 : case Method::GCSFAILSAFE:
2168 : case Method::TERRRAINFAILSAFE:
2169 : case Method::FAILSAFE_ACTION_TERMINATE:
2170 : case Method::TERRAINFAILSAFE:
2171 : case Method::BADFLOWOFCONTROL:
2172 : case Method::EKFFAILSAFE:
2173 : case Method::GCS_FAILSAFE_SURFACEFAILED:
2174 : case Method::GCS_FAILSAFE_HOLDFAILED:
2175 : case Method::PILOT_INPUT_FAILSAFE:
2176 : case Method::DEADRECKON_FAILSAFE:
2177 : case Method::BLACKBOX:
2178 : // keep logging for longer if disarmed for a bad reason
2179 26 : AP::logger().set_long_log_persist(true);
2180 26 : return;
2181 :
2182 1077 : case Method::RUDDER:
2183 : case Method::TOYMODE:
2184 : case Method::MAVLINK:
2185 : case Method::AUXSWITCH:
2186 : case Method::MOTORTEST:
2187 : case Method::SCRIPTING:
2188 : case Method::SOLOPAUSEWHENLANDED:
2189 : case Method::LANDED:
2190 : case Method::MISSIONEXIT:
2191 : case Method::DISARMDELAY:
2192 : case Method::MOTORDETECTDONE:
2193 : case Method::TAKEOFFTIMEOUT:
2194 : case Method::AUTOLANDED:
2195 : case Method::TOYMODELANDTHROTTLE:
2196 : case Method::TOYMODELANDFORCE:
2197 : case Method::LANDING:
2198 : case Method::DDS:
2199 : case Method::AUTO_ARM_ONCE:
2200 : case Method::TURTLE_MODE:
2201 : case Method::UNKNOWN:
2202 1077 : AP::logger().set_long_log_persist(false);
2203 1077 : return;
2204 : }
2205 : }
2206 : #endif // HAL_LOGGING_ENABLED
2207 :
2208 : AP_Arming *AP_Arming::_singleton = nullptr;
2209 :
2210 : /*
2211 : * Get the AP_Arming singleton
2212 : */
2213 8916140 : AP_Arming *AP_Arming::get_singleton()
2214 : {
2215 8916140 : return AP_Arming::_singleton;
2216 : }
2217 :
2218 : namespace AP {
2219 :
2220 8855185 : AP_Arming &arming()
2221 : {
2222 8855185 : return *AP_Arming::get_singleton();
2223 : }
2224 :
2225 : };
2226 :
2227 : #pragma GCC diagnostic pop
2228 :
2229 : #endif // AP_ARMING_ENABLED
|