import semver from 'semver'; import { i18n } from '../localization'; const configuration = { // intended }; configuration.initialize = function (callback) { 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_GPS_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(() => load_html()); } function load_html() { $('#content').load("./tabs/configuration.html", process_html); } load_serial_config(); function process_html() { 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); }); // 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(); const alignments = [ 'CW 0°', 'CW 90°', 'CW 180°', 'CW 270°', 'CW 0° flip', 'CW 90° flip', 'CW 180° flip', 'CW 270° flip', ]; if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_42)) { alignments.push('Custom'); } const gyro_align_content_e = $('.tab-configuration .gyro_align_content'); const legacy_gyro_alignment_e = $('.tab-configuration .legacy_gyro_alignment'); const legacy_accel_alignment_e = $('.tab-configuration .legacy_accel_alignment'); const orientation_gyro_e = $('select.gyroalign'); const orientation_acc_e = $('select.accalign'); const orientation_mag_e = $('select.magalign'); 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'); gyro_align_content_e.hide(); // default value for (let i = 0; i < alignments.length; i++) { orientation_gyro_e.append(``); orientation_acc_e.append(``); orientation_mag_e.append(``); } orientation_gyro_e.val(FC.SENSOR_ALIGNMENT.align_gyro); orientation_acc_e.val(FC.SENSOR_ALIGNMENT.align_acc); orientation_mag_e.val(FC.SENSOR_ALIGNMENT.align_mag); orientation_gyro_e.change(function () { FC.SENSOR_ALIGNMENT.align_gyro = parseInt($(this).val()); }); orientation_acc_e.change(function () { FC.SENSOR_ALIGNMENT.align_acc = parseInt($(this).val()); }); orientation_mag_e.change(function () { FC.SENSOR_ALIGNMENT.align_mag = parseInt($(this).val()); }); // Multi gyro config gyro_align_content_e.show(); legacy_gyro_alignment_e.hide(); legacy_accel_alignment_e.hide(); 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 < 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); orientation_gyro_1_align_e.change(function () { FC.SENSOR_ALIGNMENT.gyro_1_align = parseInt($(this).val()); }); orientation_gyro_2_align_e.change(function () { FC.SENSOR_ALIGNMENT.gyro_2_align = parseInt($(this).val()); }); // 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 updateGyroDenom = function (gyroBaseFreq) { gyroTextElement.hide(); const originalGyroDenom = gyroSelectElement.val(); gyroSelectElement.empty(); const MAX_DENOM = 8; for (let denom = 1; denom <= MAX_DENOM; denom++) { addDenomOption(gyroSelectElement, denom, gyroBaseFreq); } gyroSelectElement.val(originalGyroDenom); gyroSelectElement.change(); }; 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(); if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_43)) { updateGyroDenomReadOnly(FC.CONFIG.sampleRateHz); } else { updateGyroDenom(8); } gyroSelectElement.val(FC.PID_ADVANCED_CONFIG.gyro_sync_denom); $('.systemconfigNote').html(i18n.getMessage('configurationLoopTimeHelp')); gyroSelectElement.change(function () { const originalPidDenom = parseInt(pidSelectElement.val()); let pidBaseFreq; if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_43)) { pidBaseFreq = FC.CONFIG.sampleRateHz / 1000; } else { pidBaseFreq = 8; pidBaseFreq /= parseInt($(this).val()); } pidSelectElement.empty(); const MAX_DENOM = 8; 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); $('input[name="fpvCamAngleDegrees"]').attr("max", 90); // generate GPS const gpsProtocols = [ 'NMEA', 'UBLOX', 'MSP', ]; const gpsBaudRates = [ '115200', '57600', '38400', '19200', '9600', ]; const gpsSbas = [ i18n.getMessage('gpsSbasAutoDetect'), i18n.getMessage('gpsSbasEuropeanEGNOS'), i18n.getMessage('gpsSbasNorthAmericanWAAS'), i18n.getMessage('gpsSbasJapaneseMSAS'), i18n.getMessage('gpsSbasIndianGAGAN'), ]; if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_43)) { gpsSbas.push(i18n.getMessage('gpsSbasNone')); } const gpsProtocolElement = $('select.gps_protocol'); const gpsAutoBaudElement = $('input[name="gps_auto_baud"]'); const gpsAutoBaudGroup = $('.gps_auto_baud'); const gpsAutoConfigElement = $('input[name="gps_auto_config"]'); const gpsAutoConfigGroup = $('.gps_auto_config'); const gpsUbloxGalileoElement = $('input[name="gps_ublox_galileo"]'); const gpsUbloxGalileoGroup = $('.gps_ublox_galileo'); const gpsUbloxSbasElement = $('select.gps_ubx_sbas'); const gpsUbloxSbasGroup = $('.gps_ubx_sbas'); const gpsHomeOnceElement = $('input[name="gps_home_once"]'); const gpsBaudrateElement = $('select.gps_baudrate'); for (let protocolIndex = 0; protocolIndex < gpsProtocols.length; protocolIndex++) { gpsProtocolElement.append(``); } gpsProtocolElement.change(function () { FC.GPS_CONFIG.provider = parseInt($(this).val()); // Call this to enable or disable auto config elements depending on the protocol gpsAutoConfigElement.change(); }).val(FC.GPS_CONFIG.provider).change(); gpsAutoBaudElement.prop('checked', FC.GPS_CONFIG.auto_baud === 1); gpsAutoConfigElement.change(function () { const checked = $(this).is(":checked"); const ubloxSelected = FC.GPS_CONFIG.provider === gpsProtocols.indexOf('UBLOX'); const enableGalileoVisible = checked && ubloxSelected && semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_43); gpsUbloxGalileoGroup.toggle(enableGalileoVisible); const enableSbasVisible = checked && ubloxSelected; gpsUbloxSbasGroup.toggle(enableSbasVisible); }).prop('checked', FC.GPS_CONFIG.auto_config === 1).change(); gpsAutoBaudGroup.show(); gpsAutoConfigGroup.show(); gpsUbloxGalileoElement.change(function() { FC.GPS_CONFIG.ublox_use_galileo = $(this).is(':checked') ? 1 : 0; }).prop('checked', FC.GPS_CONFIG.ublox_use_galileo > 0).change(); for (let sbasIndex = 0; sbasIndex < gpsSbas.length; sbasIndex++) { gpsUbloxSbasElement.append(``); } gpsUbloxSbasElement.change(function () { FC.GPS_CONFIG.ublox_sbas = parseInt($(this).val()); }).val(FC.GPS_CONFIG.ublox_sbas); $('.gps_home_once').toggle(semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_43)); gpsHomeOnceElement.change(function() { FC.GPS_CONFIG.home_point_once = $(this).is(':checked') ? 1 : 0; }).prop('checked', FC.GPS_CONFIG.home_point_once > 0).change(); for (let baudRateIndex = 0; baudRateIndex < gpsBaudRates.length; baudRateIndex++) { gpsBaudrateElement.append(``); } gpsBaudrateElement.prop("disabled", true); gpsBaudrateElement.parent().hide(); // 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]); $('._smallAngle').show(); $('input[id="configurationSmallAngle"]').val(FC.ARMING_CONFIG.small_angle); // UI hooks function checkUpdateGpsControls() { if (FC.FEATURE_CONFIG.features.isEnabled('GPS')) { $('.gpsSettings').show(); } else { $('.gpsSettings').hide(); } } $('input.feature', features_e).change(function () { const element = $(this); FC.FEATURE_CONFIG.features.updateData(element); updateTabList(FC.FEATURE_CONFIG.features); if (element.attr('name') === 'GPS') { checkUpdateGpsControls(); } }); $('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); }); checkUpdateGpsControls(); $('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()); FC.CONFIG.accelerometerTrims[1] = parseInt($('input[name="roll"]').val()); FC.CONFIG.accelerometerTrims[0] = parseInt($('input[name="pitch"]').val()); // small angle configuration FC.ARMING_CONFIG.small_angle = parseInt($('input[id="configurationSmallAngle"]').val()); FC.SENSOR_ALIGNMENT.gyro_to_use = parseInt(orientation_gyro_to_use_e.val()); FC.PID_ADVANCED_CONFIG.gyro_sync_denom = parseInt(gyroSelectElement.val()); const value = parseInt(pidSelectElement.val()); FC.PID_ADVANCED_CONFIG.pid_process_denom = value; FC.RX_CONFIG.fpvCamAngleDegrees = parseInt($('input[name="fpvCamAngleDegrees"]').val()); // fill some data FC.GPS_CONFIG.auto_baud = $('input[name="gps_auto_baud"]').is(':checked') ? 1 : 0; FC.GPS_CONFIG.auto_config = $('input[name="gps_auto_config"]').is(':checked') ? 1 : 0; 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(); } function save_serial_config() { mspHelper.sendSerialConfig(save_config); } function save_config() { Promise .resolve(true) .then(() => MSP.promise(MSPCodes.MSP_SET_FEATURE_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_FEATURE_CONFIG))) .then(() => MSP.promise(MSPCodes.MSP_SET_BEEPER_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_BEEPER_CONFIG))) .then(() => MSP.promise(MSPCodes.MSP_SET_BOARD_ALIGNMENT_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_BOARD_ALIGNMENT_CONFIG))) .then(() => MSP.promise(MSPCodes.MSP_SET_GPS_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_GPS_CONFIG))) .then(() => MSP.promise(MSPCodes.MSP_SET_RC_DEADBAND, mspHelper.crunch(MSPCodes.MSP_SET_RC_DEADBAND))) .then(() => MSP.promise(MSPCodes.MSP_SET_SENSOR_ALIGNMENT, mspHelper.crunch(MSPCodes.MSP_SET_SENSOR_ALIGNMENT))) .then(() => MSP.promise(MSPCodes.MSP_SET_ADVANCED_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_ADVANCED_CONFIG))) .then(() => MSP.promise(MSPCodes.MSP_SET_ACC_TRIM, mspHelper.crunch(MSPCodes.MSP_SET_ACC_TRIM))) .then(() => MSP.promise(MSPCodes.MSP_SET_ARMING_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_ARMING_CONFIG))) .then(() => MSP.promise(MSPCodes.MSP_SET_SENSOR_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_SENSOR_CONFIG))) .then(() => semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45) ? MSP.promise(MSPCodes.MSP2_SET_TEXT, mspHelper.crunch(MSPCodes.MSP2_SET_TEXT, MSPCodes.CRAFT_NAME)) : MSP.promise(MSPCodes.MSP_SET_NAME, mspHelper.crunch(MSPCodes.MSP_SET_NAME))) .then(() => semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_45) ? MSP.promise(MSPCodes.MSP2_SET_TEXT, mspHelper.crunch(MSPCodes.MSP2_SET_TEXT, MSPCodes.PILOT_NAME)) : Promise.resolve(true)) .then(() => MSP.promise(MSPCodes.MSP_SET_RX_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_RX_CONFIG))) .then(() => MSP.promise(MSPCodes.MSP_EEPROM_WRITE)) .then(() => reboot()); } function reboot() { GUI.log(i18n.getMessage('configurationEepromSaved')); GUI.tab_switch_cleanup(function() { MSP.send_message(MSPCodes.MSP_SET_REBOOT, false, false, reinitializeConnection); }); } save_serial_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(); }; window.TABS.configuration = configuration; export { configuration };