diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index 317da760b6..39f46ca954 100644 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -281,6 +281,8 @@ main_sources(COMMON_SRC fc/firmware_update.h fc/firmware_update_common.c fc/firmware_update_common.h + fc/multifunction.c + fc/multifunction.h fc/rc_smoothing.c fc/rc_smoothing.h fc/rc_adjustments.c diff --git a/src/main/drivers/osd_symbols.h b/src/main/drivers/osd_symbols.h index adcb595ce2..47340d82a0 100644 --- a/src/main/drivers/osd_symbols.h +++ b/src/main/drivers/osd_symbols.h @@ -174,6 +174,8 @@ #define SYM_FLIGHT_MINS_REMAINING 0xDA // 218 Flight time (mins) remaining #define SYM_FLIGHT_HOURS_REMAINING 0xDB // 219 Flight time (hours) remaining #define SYM_GROUND_COURSE 0xDC // 220 Ground course +#define SYM_ALERT 0xDD // 221 General alert symbol + #define SYM_CROSS_TRACK_ERROR 0xFC // 252 Cross track error #define SYM_LOGO_START 0x101 // 257 to 297, INAV logo diff --git a/src/main/fc/fc_core.c b/src/main/fc/fc_core.c index 729439aa5a..898c624bf0 100755 --- a/src/main/fc/fc_core.c +++ b/src/main/fc/fc_core.c @@ -365,7 +365,7 @@ static bool emergencyArmingCanOverrideArmingDisabled(void) static bool emergencyArmingIsEnabled(void) { - return emergencyArmingUpdate(IS_RC_MODE_ACTIVE(BOXARM)) && emergencyArmingCanOverrideArmingDisabled(); + return emergencyArmingUpdate(IS_RC_MODE_ACTIVE(BOXARM), false) && emergencyArmingCanOverrideArmingDisabled(); } static void processPilotAndFailSafeActions(float dT) @@ -457,7 +457,7 @@ disarmReason_t getDisarmReason(void) return lastDisarmReason; } -bool emergencyArmingUpdate(bool armingSwitchIsOn) +bool emergencyArmingUpdate(bool armingSwitchIsOn, bool forceArm) { if (ARMING_FLAG(ARMED)) { return false; @@ -486,6 +486,10 @@ bool emergencyArmingUpdate(bool armingSwitchIsOn) toggle = true; } + if (forceArm) { + counter = EMERGENCY_ARMING_MIN_ARM_COUNT + 1; + } + return counter >= EMERGENCY_ARMING_MIN_ARM_COUNT; } @@ -838,7 +842,7 @@ void taskMainPidLoop(timeUs_t currentTimeUs) if (!ARMING_FLAG(ARMED)) { armTime = 0; - + processDelayedSave(); } diff --git a/src/main/fc/fc_core.h b/src/main/fc/fc_core.h index 37d0bbda79..c66a0050ba 100644 --- a/src/main/fc/fc_core.h +++ b/src/main/fc/fc_core.h @@ -42,7 +42,7 @@ timeUs_t getLastDisarmTimeUs(void); void tryArm(void); disarmReason_t getDisarmReason(void); -bool emergencyArmingUpdate(bool armingSwitchIsOn); +bool emergencyArmingUpdate(bool armingSwitchIsOn, bool forceArm); bool areSensorsCalibrating(void); float getFlightTime(void); diff --git a/src/main/fc/fc_msp_box.c b/src/main/fc/fc_msp_box.c index 80458c3ac3..b8b220fdba 100644 --- a/src/main/fc/fc_msp_box.c +++ b/src/main/fc/fc_msp_box.c @@ -97,6 +97,7 @@ static const box_t boxes[CHECKBOX_ITEM_COUNT + 1] = { { .boxId = BOXPLANWPMISSION, .boxName = "WP PLANNER", .permanentId = 55 }, { .boxId = BOXSOARING, .boxName = "SOARING", .permanentId = 56 }, { .boxId = BOXCHANGEMISSION, .boxName = "MISSION CHANGE", .permanentId = 59 }, + { .boxId = BOXMULTIFUNCTION, .boxName = "MULTI FUNCTION", .permanentId = 60 }, { .boxId = CHECKBOX_ITEM_COUNT, .boxName = NULL, .permanentId = 0xFF } }; @@ -178,6 +179,7 @@ void initActiveBoxIds(void) RESET_BOX_ID_COUNT; ADD_ACTIVE_BOX(BOXARM); ADD_ACTIVE_BOX(BOXPREARM); + ADD_ACTIVE_BOX(BOXMULTIFUNCTION); if (sensors(SENSOR_ACC) && STATE(ALTITUDE_CONTROL)) { ADD_ACTIVE_BOX(BOXANGLE); @@ -410,6 +412,7 @@ void packBoxModeFlags(boxBitmask_t * mspBoxModeFlags) #ifdef USE_MULTI_MISSION CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCHANGEMISSION)), BOXCHANGEMISSION); #endif + CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXMULTIFUNCTION)), BOXMULTIFUNCTION); memset(mspBoxModeFlags, 0, sizeof(boxBitmask_t)); for (uint32_t i = 0; i < activeBoxIdCount; i++) { diff --git a/src/main/fc/multifunction.c b/src/main/fc/multifunction.c new file mode 100644 index 0000000000..5772d84923 --- /dev/null +++ b/src/main/fc/multifunction.c @@ -0,0 +1,87 @@ +/* + * This file is part of INAV Project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License Version 3, as described below: + * + * This file is free software: you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include "platform.h" +#include "build/debug.h" +#include "drivers/time.h" + +#include "fc/fc_core.h" +#include "fc/multifunction.h" +#include "fc/rc_modes.h" + +#include "io/osd.h" +#include "navigation/navigation.h" + +static void multiFunctionApply(multi_function_e selectedItem) +{ + switch (selectedItem) { + case MULTI_FUNC_NONE: + return; + case MULTI_FUNC_1: + resetOsdWarningMask(); + break; + case MULTI_FUNC_2: + emergencyArmingUpdate(true, true); + break; + case MULTI_FUNC_COUNT: + break; + } +} + +bool multiFunctionSelection(multi_function_e * returnItem) +{ + static timeMs_t startTimer; + static timeMs_t selectTimer; + static int8_t selectedItem = 0; + static bool toggle = true; + const timeMs_t currentTime = millis(); + + if (IS_RC_MODE_ACTIVE(BOXMULTIFUNCTION)) { + if (selectTimer) { + if (currentTime - selectTimer > 3000) { + *returnItem = selectedItem; + multiFunctionApply(selectedItem); + selectTimer = 0; + selectedItem = 0; + return true; + } + } else if (toggle) { + selectedItem++; + selectedItem = selectedItem == MULTI_FUNC_COUNT ? 1 : selectedItem; + selectTimer = currentTime; + } + startTimer = currentTime; + toggle = false; + } else if (startTimer) { + selectTimer = 0; + if (currentTime - startTimer > 2000) { + startTimer = 0; + selectedItem = 0; + } + toggle = true; + } + + *returnItem = selectedItem; + return false; +} diff --git a/src/main/fc/multifunction.h b/src/main/fc/multifunction.h new file mode 100644 index 0000000000..b302dcf986 --- /dev/null +++ b/src/main/fc/multifunction.h @@ -0,0 +1,34 @@ +/* + * This file is part of INAV Project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License Version 3, as described below: + * + * This file is free software: you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#pragma once + +typedef enum { + MULTI_FUNC_NONE, + MULTI_FUNC_1, + MULTI_FUNC_2, + MULTI_FUNC_COUNT, +} multi_function_e; + +bool multiFunctionSelection(multi_function_e * returnItem); diff --git a/src/main/fc/rc_controls.c b/src/main/fc/rc_controls.c index 8391f6bebf..101e6098ae 100644 --- a/src/main/fc/rc_controls.c +++ b/src/main/fc/rc_controls.c @@ -223,7 +223,7 @@ void processRcStickPositions(bool isThrottleLow) rcDisarmTimeMs = currentTimeMs; tryArm(); } else { - emergencyArmingUpdate(armingSwitchIsActive); + emergencyArmingUpdate(armingSwitchIsActive, false); // Disarming via ARM BOX // Don't disarm via switch if failsafe is active or receiver doesn't receive data - we can't trust receiver // and can't afford to risk disarming in the air diff --git a/src/main/fc/rc_modes.h b/src/main/fc/rc_modes.h index a72be3ddb8..be8ca599e8 100644 --- a/src/main/fc/rc_modes.h +++ b/src/main/fc/rc_modes.h @@ -77,6 +77,7 @@ typedef enum { BOXUSER3 = 48, BOXUSER4 = 49, BOXCHANGEMISSION = 50, + BOXMULTIFUNCTION = 51, CHECKBOX_ITEM_COUNT } boxId_e; diff --git a/src/main/io/osd.c b/src/main/io/osd.c index a671b56210..0415858dac 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -77,6 +77,7 @@ FILE_COMPILE_FOR_SPEED #include "fc/controlrate_profile.h" #include "fc/fc_core.h" #include "fc/fc_tasks.h" +#include "fc/multifunction.h" #include "fc/rc_adjustments.h" #include "fc/rc_controls.h" #include "fc/rc_modes.h" @@ -183,6 +184,9 @@ static bool fullRedraw = false; static uint8_t armState; static uint8_t statsPagesCheck = 0; +textAttributes_t osdGetMultiFunctionMessage(char *buff); +static osd_warnings_status_flags_e osdWarningsMask = 0; + typedef struct osdMapData_s { uint32_t scale; char referenceSymbol; @@ -1684,8 +1688,10 @@ static bool osdDrawSingleElement(uint8_t item) buff[1] = SYM_SAT_R; tfp_sprintf(buff + 2, "%2d", gpsSol.numSat); if (!STATE(GPS_FIX)) { - if (getHwGPSStatus() == HW_SENSOR_UNAVAILABLE || getHwGPSStatus() == HW_SENSOR_UNHEALTHY) { - strcpy(buff + 2, "X!"); + hardwareSensorStatus_e sensorStatus = getHwGPSStatus(); + if (sensorStatus == HW_SENSOR_UNAVAILABLE || sensorStatus == HW_SENSOR_UNHEALTHY) { + buff[2] = SYM_ALERT; + buff[3] = '\0'; } TEXT_ATTRIBUTES_ADD_BLINK(elemAttr); } @@ -3267,6 +3273,12 @@ static bool osdDrawSingleElement(uint8_t item) } #endif // USE_ADC #endif // USE_POWER_LIMITS + case OSD_MULTI_FUNCTION: + { + displayWrite(osdDisplayPort, elemPosX, elemPosY, " "); + elemAttr = osdGetMultiFunctionMessage(buff); + break; + } default: return false; @@ -3969,7 +3981,7 @@ static void osdShowStatsPage1(void) displayWrite(osdDisplayPort, statNameX, top, "DISARMED BY :"); displayWrite(osdDisplayPort, statValuesX, top++, disarmReasonStr[getDisarmReason()]); - + if (savingSettings == true) { displayWrite(osdDisplayPort, statNameX, top++, OSD_MESSAGE_STR(OSD_MSG_SAVING_SETTNGS)); } else if (notify_settings_saved > 0) { @@ -3979,7 +3991,7 @@ static void osdShowStatsPage1(void) displayWrite(osdDisplayPort, statNameX, top++, OSD_MESSAGE_STR(OSD_MSG_SETTINGS_SAVED)); } } - + displayCommitTransaction(osdDisplayPort); } @@ -4656,4 +4668,77 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter return elemAttr; } +void resetOsdWarningMask(void) +{ + osdWarningsMask = 0; +} + +bool checkOsdWarning(bool condition, osd_warnings_status_flags_e warningType) +{ + static timeMs_t newWarningStartTime = 0; + const timeMs_t currentTimeMs = millis(); + + if (condition) { + if (!(osdWarningsMask & warningType)) { + newWarningStartTime = currentTimeMs; + osdWarningsMask |= warningType; + } + if (currentTimeMs - newWarningStartTime < 10000) { // Display new warnings for 10s + return true; + } + } else if (osdWarningsMask & warningType) { + osdWarningsMask ^= warningType; + } + + return false; +} + +textAttributes_t osdGetMultiFunctionMessage(char *buff) +{ + textAttributes_t elemAttr = TEXT_ATTRIBUTES_NONE; + uint8_t warningCount = BITCOUNT(osdWarningsMask); + + multi_function_e multiFuncItem; + multiFunctionSelection(&multiFuncItem); + if (multiFuncItem) { + switch (multiFuncItem) { + case MULTI_FUNC_NONE: + case MULTI_FUNC_1: + strcpy(buff, warningCount ? "WARNINGS " : "0 WARNINGS"); + break; + case MULTI_FUNC_2: + strcpy(buff, "EMERG ARM "); + break; + case MULTI_FUNC_COUNT: + break; + } + + return elemAttr; + } + +/* WARNINGS --------------------------------------------- */ + const char *messages[2]; + const char *message = NULL; + uint8_t messageCount = 0; + + if (checkOsdWarning(!STATE(GPS_FIX), OSD_WARN_1)) { + hardwareSensorStatus_e sensorStatus = getHwGPSStatus(); + bool gpsFailed = sensorStatus == HW_SENSOR_UNAVAILABLE || sensorStatus == HW_SENSOR_UNHEALTHY; + messages[messageCount++] = gpsFailed ? "GPS FAILED" : "NO GPS FIX"; + } + + if (messageCount) { + message = messages[OSD_ALTERNATING_CHOICES(2000, messageCount)]; // display each warning for 2s + strcpy(buff, message); + TEXT_ATTRIBUTES_ADD_BLINK(elemAttr); + return elemAttr; + } else if (warningCount) { + buff[0] = SYM_ALERT; + tfp_sprintf(buff + 1, "%u", warningCount); + return elemAttr; + } +/* WARNINGS --------------------------------------------- */ + + return elemAttr; +} #endif // OSD diff --git a/src/main/io/osd.h b/src/main/io/osd.h index ba5b26321a..19071ac1e2 100644 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -270,6 +270,7 @@ typedef enum { OSD_NAV_WP_MULTI_MISSION_INDEX, OSD_GROUND_COURSE, // 140 OSD_CROSS_TRACK_ERROR, + OSD_MULTI_FUNCTION, OSD_ITEM_COUNT // MUST BE LAST } osd_items_e; @@ -328,6 +329,12 @@ typedef enum { OSD_CRSF_LQ_TYPE3 } osd_crsf_lq_format_e; +typedef enum { + OSD_WARN_NONE = 0, + OSD_WARN_1 = 1 << 0, + OSD_WARN_2 = 1 << 1, +} osd_warnings_status_flags_e; + typedef struct osdLayoutsConfig_s { // Layouts uint16_t item_pos[OSD_LAYOUT_COUNT][OSD_ITEM_COUNT]; @@ -473,6 +480,8 @@ int32_t osdGetAltitude(void); void osdStartedSaveProcess(void); void osdShowEEPROMSavedNotification(void); +void resetOsdWarningMask(void); + void osdCrosshairPosition(uint8_t *x, uint8_t *y); bool osdFormatCentiNumber(char *buff, int32_t centivalue, uint32_t scale, int maxDecimals, int maxScaledDecimals, int length); void osdFormatAltitudeSymbol(char *buff, int32_t alt);