diff --git a/locales/en/messages.json b/locales/en/messages.json index 50a08b33fd8..374e69cf402 100755 --- a/locales/en/messages.json +++ b/locales/en/messages.json @@ -1414,6 +1414,9 @@ "configurationFeatures": { "message": "Other Features" }, + "configurationFeatureHelp": { + "message": "Help" + }, "configurationReceiver": { "message": "Receiver" }, @@ -1444,8 +1447,14 @@ "configurationSpiRxHelp": { "message": "Note: The SPI RX provider will only work if the required hardware is on board or connected to an SPI bus." }, - "configurationOtherFeaturesHelp": { - "message": "Note: Not all features are supported by all flight controllers. If you enable a specific feature, and it is disabled after you hit 'Save and Reboot', it means that this feature is not supported on your board." + "configurationActiveImu": { + "message": "Active IMU" + }, + "configurationSensors": { + "message": "Sensor Configuration" + }, + "configurationSensorsHelp": { + "message": "Configure additional sensors and advanced features." }, "configurationMagAlignmentHelp": { "message": "The magnetometer alignment is the orientation of the magnetometer sensor on the flight controller board. The default is usually correct, but if you have a custom build or a flight controller with a different magnetometer orientation, you may need to adjust this setting." @@ -1534,6 +1543,30 @@ "message": "$t(configurationBoardAlignmentYaw.message)", "description": "Don't translate!!!" }, + "configurationGyro1AlignmentRoll": { + "message": "Roll Degrees", + "description": "Don't translate!!!" + }, + "configurationGyro1AlignmentPitch": { + "message": "Pitch Degrees", + "description": "Don't translate!!!" + }, + "configurationGyro1AlignmentYaw": { + "message": "Yaw Degrees", + "description": "Don't translate!!!" + }, + "configurationGyro2AlignmentRoll": { + "message": "Roll Degrees", + "description": "Don't translate!!!" + }, + "configurationGyro2AlignmentPitch": { + "message": "Pitch Degrees", + "description": "Don't translate!!!" + }, + "configurationGyro2AlignmentYaw": { + "message": "Yaw Degrees", + "description": "Don't translate!!!" + }, "configurationSensorAlignmentGyro": { "message": "GYRO Alignment" }, @@ -1570,6 +1603,10 @@ "configurationSensorAlignmentDefaultOption": { "message": "Default" }, + "configurationSensorAlignmentCustom": { + "message": "Custom" + }, + "configurationAccelTrims": { "message": "Accelerometer Trim" }, @@ -1600,6 +1637,9 @@ "configurationGyroCalOnFirstArm": { "message": "Calibrate Gyro on first arm" }, + "configurationGyroCalOnFirstArmHelp": { + "message": "When enabled, the gyroscope will be calibrated on the first arm after power on. This can help reduce gyro drift during flight." + }, "configurationMotorIdle": { "message": "Motor Idle (%)" }, @@ -7105,6 +7145,9 @@ "configurationGyroSyncDenom": { "message": "Gyro update frequency" }, + "configurationGyroFrequency": { + "message": "Gyro update frequency" + }, "configurationPidProcessDenom": { "message": "PID loop frequency" }, diff --git a/src/components/tabs/ConfigurationTab.vue b/src/components/tabs/ConfigurationTab.vue new file mode 100644 index 00000000000..7a6b599f6c8 --- /dev/null +++ b/src/components/tabs/ConfigurationTab.vue @@ -0,0 +1,1384 @@ + + + + + diff --git a/src/css/tabs/configuration.less b/src/css/tabs/configuration.less index 27a62dd8f73..c0e6b9a667c 100644 --- a/src/css/tabs/configuration.less +++ b/src/css/tabs/configuration.less @@ -27,8 +27,12 @@ gap: 0.5rem; .sensor_align_inputs { display: flex; + align-items: center; label { white-space: nowrap; + display: flex; + align-items: center; + gap: 0.5rem; } } } diff --git a/src/js/fc.js b/src/js/fc.js index e761a5ec0de..1105aca6d03 100644 --- a/src/js/fc.js +++ b/src/js/fc.js @@ -481,6 +481,14 @@ const FC = { gyro_to_use: 0, gyro_1_align: 0, gyro_2_align: 0, + + gyro_1_align_roll: 0, + gyro_1_align_pitch: 0, + gyro_1_align_yaw: 0, + gyro_2_align_roll: 0, + gyro_2_align_pitch: 0, + gyro_2_align_yaw: 0, + mag_align_roll: 0, mag_align_pitch: 0, mag_align_yaw: 0, diff --git a/src/js/main.js b/src/js/main.js index f375122735b..9298a303a87 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -13,7 +13,7 @@ import CliAutoComplete from "./CliAutoComplete.js"; import DarkTheme, { setDarkTheme } from "./DarkTheme.js"; import { isExpertModeEnabled } from "./utils/isExpertModeEnabled.js"; import { updateTabList } from "./utils/updateTabList.js"; -import { mountVueTab } from "./vue_tab_mounter.js"; +import { mountVueTab, unmountVueTab } from "./vue_tab_mounter.js"; import * as THREE from "three"; import NotificationManager from "./utils/notifications.js"; @@ -27,12 +27,6 @@ import("./msp/debug/msp_debug_tools.js") console.warn("Failed to load MSP debug tools:", err); }); -if (typeof String.prototype.replaceAll === "undefined") { - String.prototype.replaceAll = function (match, replace) { - return this.replace(new RegExp(match, "g"), () => replace); - }; -} - $(document).ready(function () { appReady(); }); @@ -216,6 +210,7 @@ function startProcess() { // detach listeners and remove element data const content = $("#content"); + unmountVueTab(); content.empty(); // display loading screen @@ -291,9 +286,7 @@ function startProcess() { import("./tabs/setup_osd").then(({ setup_osd }) => setup_osd.initialize(content_ready)); break; case "configuration": - import("./tabs/configuration").then(({ configuration }) => - configuration.initialize(content_ready), - ); + mountVueTab("configuration", content_ready); break; case "pid_tuning": import("./tabs/pid_tuning").then(({ pid_tuning }) => pid_tuning.initialize(content_ready)); diff --git a/src/js/msp/MSPHelper.js b/src/js/msp/MSPHelper.js index 0d442aa42d8..8c8181d1ba2 100644 --- a/src/js/msp/MSPHelper.js +++ b/src/js/msp/MSPHelper.js @@ -14,7 +14,7 @@ import huffmanDecodeBuf from "../huffman"; import { defaultHuffmanTree, defaultHuffmanLenIndex } from "../default_huffman_tree"; import { updateTabList } from "../utils/updateTabList"; import { showErrorDialog } from "../utils/showErrorDialog"; -import GUI, { TABS } from "../gui"; +import GUI from "../gui"; import { OSD } from "../tabs/osd"; // Used for LED_STRIP diff --git a/src/js/tabs/configuration.js b/src/js/tabs/configuration.js deleted file mode 100644 index 3f938f04d52..00000000000 --- a/src/js/tabs/configuration.js +++ /dev/null @@ -1,710 +0,0 @@ -import semver from "semver"; -import { i18n } from "../localization"; -import GUI, { TABS } from "../gui"; -import { tracking } from "../Analytics"; -import { mspHelper } from "../msp/MSPHelper"; -import FC from "../fc"; -import MSP from "../msp"; -import MSPCodes from "../msp/MSPCodes"; -import { API_VERSION_1_45, API_VERSION_1_46, API_VERSION_1_47 } from "../data_storage"; -import { updateTabList } from "../utils/updateTabList"; -import $ from "jquery"; -import { have_sensor } from "../sensor_helpers"; -import { sensorTypes } from "../sensor_types"; -import { gui_log } from "../gui_log"; - -const configuration = { - analyticsChanges: {}, -}; - -const SENSOR_ALIGNMENTS = [ - "CW 0°", - "CW 90°", - "CW 180°", - "CW 270°", - "CW 0° flip", - "CW 90° flip", - "CW 180° flip", - "CW 270° flip", - "Custom", -]; - -const MAX_GYROS = 8; // Maximum number of gyros supported - -configuration.initialize = function (callback) { - const self = this; - - if (GUI.active_tab != "configuration") { - GUI.active_tab = "configuration"; - GUI.configuration_loaded = true; - } - - function load_serial_config() { - mspHelper.loadSerialConfig(load_config); - } - - function load_config() { - Promise.resolve(true) - .then(() => MSP.promise(MSPCodes.MSP_FEATURE_CONFIG)) - .then(() => MSP.promise(MSPCodes.MSP_BEEPER_CONFIG)) - .then(() => MSP.promise(MSPCodes.MSP_BOARD_ALIGNMENT_CONFIG)) - .then(() => MSP.promise(MSPCodes.MSP_ACC_TRIM)) - .then(() => MSP.promise(MSPCodes.MSP_ARMING_CONFIG)) - .then(() => MSP.promise(MSPCodes.MSP_RC_DEADBAND)) - .then(() => MSP.promise(MSPCodes.MSP_SENSOR_CONFIG)) - .then(() => MSP.promise(MSPCodes.MSP_SENSOR_ALIGNMENT)) - .then(() => - semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_45) - ? MSP.promise(MSPCodes.MSP_NAME) - : Promise.resolve(true), - ) - .then(() => - semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45) - ? MSP.promise(MSPCodes.MSP2_GET_TEXT, mspHelper.crunch(MSPCodes.MSP2_GET_TEXT, MSPCodes.CRAFT_NAME)) - : Promise.resolve(true), - ) - .then(() => MSP.promise(MSPCodes.MSP_RX_CONFIG)) - .then(() => - semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45) - ? MSP.promise(MSPCodes.MSP2_GET_TEXT, mspHelper.crunch(MSPCodes.MSP2_GET_TEXT, MSPCodes.PILOT_NAME)) - : Promise.resolve(true), - ) - .then(() => MSP.promise(MSPCodes.MSP_ADVANCED_CONFIG)) - .then(() => - semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_46) && FC.checkBuildOption("USE_MAG") - ? MSP.promise(MSPCodes.MSP_COMPASS_CONFIG) - : Promise.resolve(true), - ) - .then(() => - semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_47) - ? MSP.promise(MSPCodes.MSP2_GYRO_SENSOR) - : Promise.resolve(true), - ) - .then(() => load_html()); - } - - function load_html() { - $("#content").load("./tabs/configuration.html", process_html); - } - - load_serial_config(); - - function process_html() { - self.analyticsChanges = {}; - - const features_e = $(".tab-configuration .features"); - - FC.FEATURE_CONFIG.features.generateElements(features_e); - - // Dshot Beeper - const dshotBeeper_e = $(".tab-configuration .dshotbeeper"); - const dshotBeeperBeaconTone = $("select.dshotBeeperBeaconTone"); - const dshotBeaconCondition_e = $("tbody.dshotBeaconConditions"); - const dshotBeaconSwitch_e = $("tr.dshotBeaconSwitch"); - - for (let i = 1; i <= 5; i++) { - dshotBeeperBeaconTone.append(``); - } - dshotBeeper_e.show(); - - dshotBeeperBeaconTone.change(function () { - FC.BEEPER_CONFIG.dshotBeaconTone = dshotBeeperBeaconTone.val(); - }); - - dshotBeeperBeaconTone.val(FC.BEEPER_CONFIG.dshotBeaconTone); - - const template = $(".beepers .beeper-template"); - dshotBeaconSwitch_e.hide(); - FC.BEEPER_CONFIG.dshotBeaconConditions.generateElements(template, dshotBeaconCondition_e); - - $("input.condition", dshotBeaconCondition_e).change(function () { - const element = $(this); - FC.BEEPER_CONFIG.dshotBeaconConditions.updateData(element); - }); - - // DShot Beeper toggle all buttons - $(".dshot-beeper-enable-all").click(function () { - $("input.condition", dshotBeaconCondition_e).each(function () { - $(this).prop("checked", true).trigger("change"); - }); - }); - - $(".dshot-beeper-disable-all").click(function () { - $("input.condition", dshotBeaconCondition_e).each(function () { - $(this).prop("checked", false).trigger("change"); - }); - }); - - // Analog Beeper - const destination = $(".beepers .beeper-configuration"); - const beeper_e = $(".tab-configuration .beepers"); - - FC.BEEPER_CONFIG.beepers.generateElements(template, destination); - - // translate to user-selected language - i18n.localizePage(); - - // Gyro and PID update - const gyroTextElement = $("input.gyroFrequency"); - const gyroSelectElement = $("select.gyroSyncDenom"); - const pidSelectElement = $("select.pidProcessDenom"); - - function addDenomOption(element, denom, baseFreq) { - let denomDescription; - if (baseFreq === 0) { - denomDescription = i18n.getMessage("configurationSpeedPidNoGyro", { value: denom }); - } else { - denomDescription = i18n.getMessage("configurationKHzUnitLabel", { - value: (baseFreq / denom).toFixed(2), - }); - } - element.append(``); - } - - const updateGyroDenomReadOnly = function (gyroFrequency) { - gyroSelectElement.hide(); - - let gyroContent; - if (gyroFrequency === 0) { - gyroContent = i18n.getMessage("configurationSpeedGyroNoGyro"); - } else { - gyroContent = i18n.getMessage("configurationKHzUnitLabel", { - value: (gyroFrequency / 1000).toFixed(2), - }); - } - gyroTextElement.val(gyroContent); - }; - - $("div.gyroUse32kHz").hide(); - - updateGyroDenomReadOnly(FC.CONFIG.sampleRateHz); - - gyroSelectElement.val(FC.PID_ADVANCED_CONFIG.gyro_sync_denom); - - $(".systemconfigNote").html(i18n.getMessage("configurationLoopTimeHelp")); - - gyroSelectElement - .change(function () { - const originalPidDenom = parseInt(pidSelectElement.val()); - const pidBaseFreq = FC.CONFIG.sampleRateHz / 1000; - const MAX_DENOM = 8; - - pidSelectElement.empty(); - - for (let denom = 1; denom <= MAX_DENOM; denom++) { - addDenomOption(pidSelectElement, denom, pidBaseFreq); - } - - pidSelectElement.val(originalPidDenom); - }) - .change(); - - pidSelectElement.val(FC.PID_ADVANCED_CONFIG.pid_process_denom); - - $('input[id="accHardwareSwitch"]').prop("checked", FC.SENSOR_CONFIG.acc_hardware !== 1); - $('input[id="baroHardwareSwitch"]').prop("checked", FC.SENSOR_CONFIG.baro_hardware !== 1); - $('input[id="magHardwareSwitch"]').prop("checked", FC.SENSOR_CONFIG.mag_hardware !== 1); - - // Only show these sections for supported FW - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45)) { - $('input[name="craftName"]').val(FC.CONFIG.craftName); - $('input[name="pilotName"]').val(FC.CONFIG.pilotName); - } else { - $('input[name="craftName"]').val(FC.CONFIG.name); - $(".pilotName").hide(); - } - - $('input[name="fpvCamAngleDegrees"]').val(FC.RX_CONFIG.fpvCamAngleDegrees); - - // fill board alignment - $('input[name="board_align_roll"]').val(FC.BOARD_ALIGNMENT_CONFIG.roll); - $('input[name="board_align_pitch"]').val(FC.BOARD_ALIGNMENT_CONFIG.pitch); - $('input[name="board_align_yaw"]').val(FC.BOARD_ALIGNMENT_CONFIG.yaw); - - // fill accel trims - $('input[name="roll"]').val(FC.CONFIG.accelerometerTrims[1]); - $('input[name="pitch"]').val(FC.CONFIG.accelerometerTrims[0]); - - $('input[id="configurationSmallAngle"]').val(FC.ARMING_CONFIG.small_angle); - - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_46)) { - $('input[id="configurationGyroCalOnFirstArm"]').prop( - "checked", - FC.ARMING_CONFIG.gyro_cal_on_first_arm === 1, - ); - - if (FC.FEATURE_CONFIG.features.isEnabled("MOTOR_STOP")) { - $('input[id="configurationAutoDisarmDelay"]').val(FC.ARMING_CONFIG.auto_disarm_delay); - } else { - $('input[id="configurationAutoDisarmDelay"]').parent().parent().hide(); - } - } else { - $('input[id="configurationGyroCalOnFirstArm"]').parent().parent().hide(); - $('input[id="configurationAutoDisarmDelay"]').parent().parent().hide(); - } - - // Multi gyro handling for newer firmware - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_47)) { - // Hide deprecated gyro_box - $(".tab-configuration .gyro_box").parent().hide(); - - // Define gyro detection flags - const GYRO_DETECTION_FLAGS = { DETECTED_DUAL_GYROS: 1 << 7 }; - let gyroCount = 0; - // Determine how many gyros are detected (bit count) - for (let i = 0; i < MAX_GYROS; i++) { - GYRO_DETECTION_FLAGS[`DETECTED_GYRO_${i + 1}`] = 1 << i; - if ((FC.SENSOR_ALIGNMENT.gyro_detection_flags & (1 << i)) !== 0) { - gyroCount++; - } - } - - // Initialize gyro_enable_mask if needed - if (FC.SENSOR_ALIGNMENT.gyro_enable_mask === undefined) { - FC.SENSOR_ALIGNMENT.gyro_enable_mask = (1 << MAX_GYROS) - 1; // All enabled by default - } else if (FC.SENSOR_ALIGNMENT.gyro_enable_mask === 0) { - // Safety check: if somehow all gyros are disabled, enable the first one - FC.SENSOR_ALIGNMENT.gyro_enable_mask = 1; - } - - if (gyroCount > 1) { - // Track which gyros are detected - const detected_gyros = []; - - function createGyroBox(gyroIndex, container) { - // Create a new gyro alignment div - const gyroBox = $(`
`); - const gyroRow = $(`
`); - const gyroDetected = sensorTypes().gyro.elements[FC.GYRO_SENSOR.gyro_hardware[gyroIndex]]; - - const enableCheck = $(`
- -
`); - - gyroRow.append(enableCheck); - gyroBox.append(gyroRow); - - // Add the box to the container - container.append(gyroBox); - - // Initialize the enable checkbox - enableCheck - .find("input") - .prop("checked", (FC.SENSOR_ALIGNMENT.gyro_enable_mask & (1 << gyroIndex)) !== 0); - - // Add handler for enable/disable checkbox - enableCheck.find("input").on("change", function () { - const checked = $(this).is(":checked"); - - if (checked) { - // Enabling a gyro is always fine - FC.SENSOR_ALIGNMENT.gyro_enable_mask |= 1 << gyroIndex; - } else { - // Safety check: prevent disabling all gyros - const newMask = FC.SENSOR_ALIGNMENT.gyro_enable_mask & ~(1 << gyroIndex); - if (newMask === 0) { - // Prevent the action - keep the checkbox checked - $(this).prop("checked", true); - - // Show an error message to the user - gui_log(i18n.getMessage("configurationGyroRequiredWarning")); - return; - } - - // It's safe to disable this gyro - FC.SENSOR_ALIGNMENT.gyro_enable_mask = newMask; - } - - self.analyticsChanges[`Gyro${gyroIndex + 1}Enable`] = checked; - }); - } - - // For each possible gyro - for (let i = 0; i < MAX_GYROS; i++) { - detected_gyros[i] = - (FC.SENSOR_ALIGNMENT.gyro_detection_flags & GYRO_DETECTION_FLAGS[`DETECTED_GYRO_${i + 1}`]) != - 0; - - // If gyro is detected, create UI for it - if (detected_gyros[i]) { - createGyroBox(i, $(".tab-configuration .gyro_enable_configuration")); - } - } - - // Only show not found message if no gyros are detected - $(".gyro_notfound").toggle(!detected_gyros.some((detected) => detected)); - } else { - // Hide the gyro container if not needed - $(".tab-configuration .gyro_enable_box").parent().hide(); - } - } else { - // Hide the gyro enable box introduced in 1.47 - $(".tab-configuration .gyro_enable_box").parent().hide(); - - // Original code for older firmware versions remains unchanged - const orientation_gyro_to_use_e = $("select.gyro_to_use"); - const orientation_gyro_1_align_e = $("select.gyro_1_align"); - const orientation_gyro_2_align_e = $("select.gyro_2_align"); - - const GYRO_DETECTION_FLAGS = { - DETECTED_GYRO_1: 1 << 0, - DETECTED_GYRO_2: 1 << 1, - DETECTED_DUAL_GYROS: 1 << 7, - }; - - const detected_gyro_1 = - (FC.SENSOR_ALIGNMENT.gyro_detection_flags & GYRO_DETECTION_FLAGS.DETECTED_GYRO_1) != 0; - const detected_gyro_2 = - (FC.SENSOR_ALIGNMENT.gyro_detection_flags & GYRO_DETECTION_FLAGS.DETECTED_GYRO_2) != 0; - const detected_dual_gyros = - (FC.SENSOR_ALIGNMENT.gyro_detection_flags & GYRO_DETECTION_FLAGS.DETECTED_DUAL_GYROS) != 0; - - if (detected_gyro_1) { - orientation_gyro_to_use_e.append( - ``, - ); - } - if (detected_gyro_2) { - orientation_gyro_to_use_e.append( - ``, - ); - } - if (detected_dual_gyros) { - orientation_gyro_to_use_e.append( - ``, - ); - } - - for (let i = 0; i < SENSOR_ALIGNMENTS.length; i++) { - orientation_gyro_1_align_e.append(``); - orientation_gyro_2_align_e.append(``); - } - - orientation_gyro_to_use_e.val(FC.SENSOR_ALIGNMENT.gyro_to_use); - orientation_gyro_1_align_e.val(FC.SENSOR_ALIGNMENT.gyro_1_align); - orientation_gyro_2_align_e.val(FC.SENSOR_ALIGNMENT.gyro_2_align); - - $(".gyro_alignment_inputs_first").toggle(detected_gyro_1); - $(".gyro_alignment_inputs_second").toggle(detected_gyro_2); - $(".gyro_alignment_inputs_selection").toggle(detected_gyro_1 || detected_gyro_2); - $(".gyro_alignment_inputs_notfound").toggle(!detected_gyro_1 && !detected_gyro_2); - - // Keep original handlers - orientation_gyro_1_align_e.on("change", function () { - const value = parseInt($(this).val()); - - if (value !== FC.SENSOR_ALIGNMENT.gyro_1_align) { - const newValue = $(this).find("option:selected").text(); - self.analyticsChanges["Gyro1Alignment"] = newValue; - } - FC.SENSOR_ALIGNMENT.gyro_1_align = value; - }); - - orientation_gyro_2_align_e.on("change", function () { - const value = parseInt($(this).val()); - - if (value !== FC.SENSOR_ALIGNMENT.gyro_2_align) { - const newValue = $(this).find("option:selected").text(); - self.analyticsChanges["Gyro2Alignment"] = newValue; - } - FC.SENSOR_ALIGNMENT.gyro_2_align = value; - }); - } - - // Magnetometer - const orientation_mag_e = $("select.mag_align"); - - const hasMag = - have_sensor(FC.CONFIG.activeSensors, "mag") && semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_46); - - if (hasMag) { - $('input[name="mag_declination"]').val(FC.COMPASS_CONFIG.mag_declination.toFixed(1)); - } else { - $("div.mag_declination").parent().parent().hide(); - } - - for (let i = 0; i < SENSOR_ALIGNMENTS.length; i++) { - orientation_mag_e.append(``); - } - - orientation_mag_e.val(FC.SENSOR_ALIGNMENT.align_mag); - - function toggleMagCustomAlignmentInputs() { - // Toggle custom alignment visibility based on current value - const index = parseInt(orientation_mag_e.val()) - 1; - const isCustom = SENSOR_ALIGNMENTS[index] === "Custom"; - $(".mag_align_inputs").toggle(isCustom); - } - - orientation_mag_e.change(function () { - let value = parseInt($(this).val()); - - let newValue = undefined; - if (value !== FC.SENSOR_ALIGNMENT.align_mag) { - newValue = $(this).find("option:selected").text(); - } - self.analyticsChanges["MagAlignment"] = newValue; - - FC.SENSOR_ALIGNMENT.align_mag = value; - - toggleMagCustomAlignmentInputs(); - }); - - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_47)) { - $('input[name="mag_align_roll"]').val(FC.SENSOR_ALIGNMENT.mag_align_roll); - $('input[name="mag_align_pitch"]').val(FC.SENSOR_ALIGNMENT.mag_align_pitch); - $('input[name="mag_align_yaw"]').val(FC.SENSOR_ALIGNMENT.mag_align_yaw); - - toggleMagCustomAlignmentInputs(); - } else { - $(".tab-configuration .gyro_align_box").hide(); - $(".tab-configuration .mag_align_box").hide(); - } - - // Range finder - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_47)) { - const rangeFinderType_e = $("select.rangefinderType"); - const sonarElements = sensorTypes().sonar.elements; - - for (let i = 0; i < sonarElements.length; i++) { - rangeFinderType_e.append(``); - } - - rangeFinderType_e.val(FC.SENSOR_CONFIG.sonar_hardware); - } else { - $(".tab-configuration .rangefinder").parent().hide(); - } - - // Optical flow sensor - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_47)) { - const opticalflowType_e = $("select.opticalflowType"); - const opticalflowElements = sensorTypes().opticalflow.elements; - - for (let i = 0; i < opticalflowElements.length; i++) { - opticalflowType_e.append(``); - } - - opticalflowType_e.val(FC.SENSOR_CONFIG.opticalflow_hardware); - } else { - $(".tab-configuration .opticalflow").parent().hide(); - } - - // UI hooks - - $("input.feature", features_e).change(function () { - const element = $(this); - - FC.FEATURE_CONFIG.features.updateData(element); - updateTabList(FC.FEATURE_CONFIG.features); - }); - - $('input[id="accHardwareSwitch"]') - .change(function () { - const checked = $(this).is(":checked"); - $(".accelNeeded").toggle(checked); - }) - .change(); - - $(features_e) - .filter("select") - .change(function () { - const element = $(this); - - FC.FEATURE_CONFIG.features.updateData(element); - updateTabList(FC.FEATURE_CONFIG.features); - }); - - $("input.condition", beeper_e).change(function () { - const element = $(this); - FC.BEEPER_CONFIG.beepers.updateData(element); - }); - - // Analog Beeper toggle all buttons - $(".beeper-enable-all").click(function () { - $("input.condition", beeper_e).each(function () { - $(this).prop("checked", true).trigger("change"); - }); - }); - - $(".beeper-disable-all").click(function () { - $("input.condition", beeper_e).each(function () { - $(this).prop("checked", false).trigger("change"); - }); - }); - - function save_config() { - // Define all configuration operations to execute - const saveOperations = [ - { code: MSPCodes.MSP_SET_FEATURE_CONFIG }, - { code: MSPCodes.MSP_SET_BEEPER_CONFIG }, - { code: MSPCodes.MSP_SET_BOARD_ALIGNMENT_CONFIG }, - { code: MSPCodes.MSP_SET_RC_DEADBAND }, - { code: MSPCodes.MSP_SET_SENSOR_ALIGNMENT }, - { code: MSPCodes.MSP_SET_ADVANCED_CONFIG }, - { code: MSPCodes.MSP_SET_ACC_TRIM }, - { code: MSPCodes.MSP_SET_ARMING_CONFIG }, - { code: MSPCodes.MSP_SET_SENSOR_CONFIG }, - { - condition: () => semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45), - code: MSPCodes.MSP2_SET_TEXT, - extraParams: MSPCodes.CRAFT_NAME, - fallback: { code: MSPCodes.MSP_SET_NAME }, - }, - { - condition: () => semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45), - code: MSPCodes.MSP2_SET_TEXT, - extraParams: MSPCodes.PILOT_NAME, - }, - { code: MSPCodes.MSP_SET_RX_CONFIG }, - { - condition: () => semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_46), - code: MSPCodes.MSP_SET_COMPASS_CONFIG, - }, - ]; - - // Start with a resolved promise - let saveChain = Promise.resolve(true); - - // Build the promise chain - saveOperations.forEach((operation) => { - saveChain = saveChain.then(() => { - // Skip if operation has a condition that returns false - if (operation.condition && !operation.condition()) { - if (operation.fallback) { - // Use fallback operation if provided - return MSP.promise( - operation.fallback.code, - operation.fallback.extraParams - ? mspHelper.crunch(operation.fallback.code, operation.fallback.extraParams) - : mspHelper.crunch(operation.fallback.code), - ); - } - return Promise.resolve(true); - } - - // Execute the operation - return MSP.promise( - operation.code, - operation.extraParams - ? mspHelper.crunch(operation.code, operation.extraParams) - : mspHelper.crunch(operation.code), - ); - }); - }); - - // Complete the chain with final write - return saveChain.then(() => mspHelper.writeConfiguration(true)); - } - - $("a.save").on("click", function () { - // gather data that doesn't have automatic change event bound - FC.BOARD_ALIGNMENT_CONFIG.roll = parseInt($('input[name="board_align_roll"]').val()); - FC.BOARD_ALIGNMENT_CONFIG.pitch = parseInt($('input[name="board_align_pitch"]').val()); - FC.BOARD_ALIGNMENT_CONFIG.yaw = parseInt($('input[name="board_align_yaw"]').val()); - - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_47)) { - FC.SENSOR_ALIGNMENT.gyro_enable_mask = 0; - - // Gather the enable/disable state for all gyros - for (let i = 0; i < MAX_GYROS; i++) { - // Check if the checkbox is checked for this gyro - if ($(`#gyro_${i + 1}_enable`).is(":checked")) { - // Set the bit in the mask - FC.SENSOR_ALIGNMENT.gyro_enable_mask |= 1 << i; - } - // If not checked, the bit stays 0 as we initialized the mask to 0 - } - - FC.SENSOR_ALIGNMENT.mag_align_roll = parseInt($('input[name="mag_align_roll"]').val()); - FC.SENSOR_ALIGNMENT.mag_align_pitch = parseInt($('input[name="mag_align_pitch"]').val()); - FC.SENSOR_ALIGNMENT.mag_align_yaw = parseInt($('input[name="mag_align_yaw"]').val()); - } else { - FC.SENSOR_ALIGNMENT.gyro_1_align = parseInt($("select.gyro_1_align").val()); - FC.SENSOR_ALIGNMENT.gyro_2_align = parseInt($("select.gyro_2_align").val()); - FC.SENSOR_ALIGNMENT.gyro_to_use = parseInt($("select.gyro_to_use").val()); - } - - FC.CONFIG.accelerometerTrims[1] = parseInt($('input[name="roll"]').val()); - FC.CONFIG.accelerometerTrims[0] = parseInt($('input[name="pitch"]').val()); - - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_46)) { - FC.ARMING_CONFIG.gyro_cal_on_first_arm = $('input[id="configurationGyroCalOnFirstArm"]').is(":checked") - ? 1 - : 0; - // only update auto_disarm_delay if MOTOR_STOP is enabled - if (FC.FEATURE_CONFIG.features.isEnabled("MOTOR_STOP")) { - FC.ARMING_CONFIG.auto_disarm_delay = parseInt($('input[id="configurationAutoDisarmDelay"]').val()); - } - } - - // declination added first in #3676 - if (hasMag) { - FC.COMPASS_CONFIG.mag_declination = $('input[name="mag_declination"]').val(); - } - - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_47)) { - FC.SENSOR_CONFIG.sonar_hardware = $("select.rangefinderType").val(); - FC.SENSOR_CONFIG.opticalflow_hardware = $("select.opticalflowType").val(); - } - - FC.ARMING_CONFIG.small_angle = parseInt($('input[id="configurationSmallAngle"]').val()); - - FC.PID_ADVANCED_CONFIG.gyro_sync_denom = parseInt(gyroSelectElement.val()); - - const value = parseInt(pidSelectElement.val()); - - if (value !== FC.PID_ADVANCED_CONFIG.pid_process_denom) { - const newFrequency = pidSelectElement.find("option:selected").text(); - self.analyticsChanges["PIDLoopSettings"] = `denominator: ${value} | frequency: ${newFrequency}`; - } else { - self.analyticsChanges["PIDLoopSettings"] = undefined; - } - - FC.PID_ADVANCED_CONFIG.pid_process_denom = value; - - FC.RX_CONFIG.fpvCamAngleDegrees = parseInt($('input[name="fpvCamAngleDegrees"]').val()); - - tracking.sendSaveAndChangeEvents( - tracking.EVENT_CATEGORIES.FLIGHT_CONTROLLER, - self.analyticsChanges, - "configuration", - ); - self.analyticsChanges = {}; - - // fill some data - FC.SENSOR_CONFIG.acc_hardware = $('input[id="accHardwareSwitch"]').is(":checked") ? 0 : 1; - FC.SENSOR_CONFIG.baro_hardware = $('input[id="baroHardwareSwitch"]').is(":checked") ? 0 : 1; - FC.SENSOR_CONFIG.mag_hardware = $('input[id="magHardwareSwitch"]').is(":checked") ? 0 : 1; - - if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45)) { - FC.CONFIG.craftName = $('input[name="craftName"]').val().trim(); - FC.CONFIG.pilotName = $('input[name="pilotName"]').val().trim(); - } else { - FC.CONFIG.name = $('input[name="craftName"]').val().trim(); - } - - mspHelper.sendSerialConfig(save_config); - }); - - // status data pulled via separate timer with static speed - GUI.interval_add( - "status_pull", - function () { - MSP.send_message(MSPCodes.MSP_STATUS); - }, - 250, - true, - ); - GUI.content_ready(callback); - } -}; - -configuration.cleanup = function (callback) { - if (callback) callback(); -}; - -TABS.configuration = configuration; -export { configuration }; diff --git a/src/js/vue_components.js b/src/js/vue_components.js index 16def8d66c3..06b7037e21b 100644 --- a/src/js/vue_components.js +++ b/src/js/vue_components.js @@ -8,6 +8,7 @@ import HelpTab from "../components/tabs/HelpTab.vue"; import LandingTab from "../components/tabs/LandingTab.vue"; import OptionsTab from "../components/tabs/OptionsTab.vue"; import PortsTab from "../components/tabs/PortsTab.vue"; +import ConfigurationTab from "../components/tabs/ConfigurationTab.vue"; import ServosTab from "../components/tabs/ServosTab.vue"; // Registry of Vue tab components - used by main.js for dynamic mounting @@ -16,6 +17,7 @@ export const VueTabComponents = { landing: LandingTab, options: OptionsTab, ports: PortsTab, + configuration: ConfigurationTab, servos: ServosTab, }; @@ -33,6 +35,7 @@ export const BetaflightComponents = { app.component("LandingTab", LandingTab); app.component("OptionsTab", OptionsTab); app.component("PortsTab", PortsTab); + app.component("ConfigurationTab", ConfigurationTab); app.component("ServosTab", ServosTab); }, }; diff --git a/src/js/vue_tab_mounter.js b/src/js/vue_tab_mounter.js index 3a70eeadff8..0dd6b5e3222 100644 --- a/src/js/vue_tab_mounter.js +++ b/src/js/vue_tab_mounter.js @@ -63,6 +63,9 @@ export function mountVueTab(tabName, contentReadyCallback) { currentTabApp.provide("betaflightModel", globalThis.vm); } + // Set active tab for legacy compatibility + GUI.active_tab = tabName; + // Mount to content currentTabApp.mount(contentEl); diff --git a/src/tabs/configuration.html b/src/tabs/configuration.html deleted file mode 100644 index 2362cb8cddc..00000000000 --- a/src/tabs/configuration.html +++ /dev/null @@ -1,458 +0,0 @@ -
-
-
-
- -
-
-

-
- -
- -
- -
-
-
-
-
-
-
-

-
-
- - - -
-
- - -
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- - -
-
-
-
-
-
- -
-
-
- -
-
-
-
- -
-
-
-
-
-
- -
-
-
- - -
-
-
-
-
-
-
- -
-
-
- -
-
-
-
- -
- -
-
-
- - -
-
-
-
-
-
-
-

-
- - - - - - - - - - - -
-
-
-
- -
-
- -
- -
-
-
-
-
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
- - - -
-
-
-
-
-
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- - - -
-
-
-
-
-
- - - - -
-
-
- -
-
- - - -
-
-
-
-
-
-
- -
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
- -
-
-
- - - -
-
-
-
-
-
-
- - -
-
-
- - - -
-
-
-
-
-
-
- - -
-
-
- - - -
-
-
-
-
-
- -
-
- -
-
-
- -
-
-
-
-
-
-
- - - - - - - - - -
-
-
- -
- -
-
-
-
- -
- -
-
-
- - -
- - - - -
-
-
-
-
- -
-
-
-
-
-
-
- - -
- - - - - - - - - -
-
-
-
-
-
-
- - -
-
- -
-
- -
-