1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-14 20:10:13 +03:00

Merge pull request #2296 from limonspb/esc_dshot_reverse

This commit is contained in:
Michael Keller 2021-01-01 15:31:06 +01:00 committed by GitHub
commit d5d0146af7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1255 additions and 103 deletions

View file

@ -2577,7 +2577,7 @@
"message": "<strong>Safety notice</strong><br /><strong class=\"message-negative\">Remove all propellers to prevent injury!</strong><br />The motors will <strong>spin up!</strong>" "message": "<strong>Safety notice</strong><br /><strong class=\"message-negative\">Remove all propellers to prevent injury!</strong><br />The motors will <strong>spin up!</strong>"
}, },
"motorsRemapDialogExplanations": { "motorsRemapDialogExplanations": {
"message": "<strong>Information notice</strong><br />Motors will spin up one by one and you will be able to select which motor is spinning. The battery should be plugged in, correct ESC protocol should be selected. This utility can only re-arrange currently active motors. More complex re-mapping requires the CLI Resource command. Refer to this <a href=\"https://github.com/betaflight/betaflight/wiki/Betaflight-resource-remapping\" target=\"_blank\" rel=\"noopener noreferrer\">Wiki page</a>." "message": "<strong>Information notice</strong><br />Motors will spin up one by one and you will be able to select which motor is spinning. The battery should be plugged in, correct ESC protocol should be selected. This utility can only re-arrange currently active motors. More complex re-mapping requires the CLI Resource command. Refer to this <a href=\"https://github.com/betaflight/betaflight/wiki/Betaflight-resource-remapping\" target=\"_blank\" rel=\"noopener noreferrer\">Wiki page</a>."
}, },
"motorsRemapDialogSave": { "motorsRemapDialogSave": {
"message": "Save" "message": "Save"
@ -2586,6 +2586,82 @@
"message": "Start over" "message": "Start over"
}, },
"escDshotDirectionDialog-Title": {
"message": "Motor Direction - <strong class=\"message-negative-italic\">Warning: Ensure props are removed!</strong>"
},
"escDshotDirectionDialog-SelectMotor": {
"message": "Select one or all motors"
},
"escDshotDirectionDialog-SelectMotorSafety": {
"message": "Motors will spin when selected!"
},
"escDshotDirectionDialog-RiskNotice": {
"message": "<strong>Safety notice</strong><br /><strong class=\"message-negative-italic\">Remove all propellers to prevent injury!</strong><br />The motors will <strong>spin up</strong> immediately when selected!"
},
"escDshotDirectionDialog-UnderstandRisks": {
"message": "<strong>I understand the risks</strong>,<br />all propellers are removed."
},
"escDshotDirectionDialog-InformationNotice": {
"message": "<strong>Information notice</strong><br />To change the motor directions, the battery must be plugged in and the correct ESC protocol must be set up in the $t(tabConfiguration.message) tab. Note that not all Dshot ESCs will work with this dialog. Check your ESC firmware."
},
"escDshotDirectionDialog-NormalInformationNotice": {
"message": "Set motor spin direction by selecting and spinning each motor individually."
},
"escDshotDirectionDialog-WizardInformationNotice": {
"message": "Resets all motor spin directions, then allows the user to choose which to reverse."
},
"escDshotDirectionDialog-Open": {
"message": "Motor direction"
},
"escDshotDirectionDialog-CommandNormal": {
"message": "Normal"
},
"escDshotDirectionDialog-CommandReverse": {
"message": "Reverse"
},
"escDshotDirectionDialog-CommandSpin": {
"message": "Test motor"
},
"escDshotDirectionDialog-ReleaseButtonToStop": {
"message": "Release button to stop"
},
"escDshotDirectionDialog-ReleaseToStop": {
"message": "Release to stop"
},
"escDshotDirectionDialog-Start": {
"message": "Individually"
},
"escDshotDirectionDialog-StartWizard": {
"message": "Wizard"
},
"escDshotDirectionDialog-SetDirectionHint": {
"message": "Change direction of selected motor(s)"
},
"escDshotDirectionDialog-SetDirectionHintSafety": {
"message": "Motors will spin when setting the direction!"
},
"escDshotDirectionDialog-WrongProtocolText": {
"message": "Feature works with DSHOT ESCs only.<br />Verify that your ESC (electric speed controller) supports DSHOT protocol and change it on $t(tabConfiguration.message) tab."
},
"escDshotDirectionDialog-WrongMixerText": {
"message": "Number of motors is 0.<br />Verify the current Mixer on $t(tabConfiguration.message) tab or setup a custom one through CLI. Refer to this <a href=\"https://github.com/betaflight/betaflight/blob/master/docs/Mixer.md\" target=\"_blank\" rel=\"noopener noreferrer\">Wiki page</a>."
},
"escDshotDirectionDialog-WrongFirmwareText": {
"message": "Update the firmware.<br /> Make sure you are using the latest firmware: Betaflight 4.3 or newer."
},
"escDshotDirectionDialog-WizardActionHint": {
"message": "Click on motor numbers individually to change spin direction"
},
"escDshotDirectionDialog-WizardActionHintSecondLine": {
"message": "Verify all motors are spinning correctly"
},
"escDshotDirectionDialog-SpinWizard": {
"message": "Start / spin motors"
},
"escDshotDirectionDialog-StopWizard": {
"message": "Stop motors"
},
"sensorsInfo": { "sensorsInfo": {
"message": "Keep in mind that using fast update periods and rendering multiple graphs at the same time is resource heavy and will burn your battery quicker if you use a laptop.<br />We recommend to only render graphs for sensors you are interested in while using reasonable update periods." "message": "Keep in mind that using fast update periods and rendering multiple graphs at the same time is resource heavy and will burn your battery quicker if you use a laptop.<br />We recommend to only render graphs for sensors you are interested in while using reasonable update periods."
}, },

View file

@ -0,0 +1,66 @@
<div class="escDshotDirection-Component">
<h3 i18n="escDshotDirectionDialog-Title" class="escDshotDirection-ComponentHeader"></h3>
<div class="componentContent" id="escDshotDirectionDialog-ConfigErrors">
<div class="escDshotDirectionErrorTextBlock" id="escDshotDirectionDialog-WrongProtocol" i18n="escDshotDirectionDialog-WrongProtocolText"></div>
<div class="escDshotDirectionErrorTextBlock" id="escDshotDirectionDialog-WrongMixer" i18n="escDshotDirectionDialog-WrongMixerText"></div>
<div class="escDshotDirectionErrorTextBlock" id="escDshotDirectionDialog-WrongFirmware" i18n="escDshotDirectionDialog-WrongFirmwareText"></div>
</div>
<div class="componentContent" id="escDshotDirectionDialog-MainContent">
<div id="escDshotDirectionDialog-MixerPreview" class="grey">
<img id="escDshotDirectionDialog-MixerPreviewImg" alt="" src="./resources/motor_order/custom.svg" />
</div>
<div id="escDshotDirectionDialog-NormalDialog" class="display-contents">
<h4 id="escDshotDirectionDialog-ActionHint" i18n="escDshotDirectionDialog-SelectMotor"></h4>
<h4 id="escDshotDirectionDialog-ActionHintSafety" i18n="escDshotDirectionDialog-SelectMotorSafety"></h4>
<div id="escDshotDirectionDialog-SelectMotorButtonsWrapper">
</div>
<div id = "escDshotDirectionDialog-SecondActionBlock" class="display-contents">
<h4 id="escDshotDirectionDialog-SecondHint" i18n="escDshotDirectionDialog-SetDirectionHint"></h4>
<h4 id="escDshotDirectionDialog-SecondHintSafety" i18n="escDshotDirectionDialog-SetDirectionHintSafety"></h4>
<div id="escDshotDirectionDialog-CommandsWrapper">
<a href="#" id="escDshotDirectionDialog-RotationNormal" class="regular-button" i18n="escDshotDirectionDialog-CommandNormal"></a>
<a href="#" id="escDshotDirectionDialog-RotationReverse" class="regular-button" i18n="escDshotDirectionDialog-CommandReverse"></a>
</div>
</div>
</div>
<div id="escDshotDirectionDialog-WizardDialog" class="display-contents">
<a href="#" id="escDshotDirectionDialog-SpinWizard" class="regular-button" i18n="escDshotDirectionDialog-SpinWizard"></a>
<div id="escDshotDirectionDialog-SpinningWizard" class="display-contents">
<h4 id="escDshotDirectionDialog-WizardActionHint" i18n="escDshotDirectionDialog-WizardActionHint"></h4>
<h4 id="escDshotDirectionDialog-WizardActionHintSecondLine" i18n="escDshotDirectionDialog-WizardActionHintSecondLine"></h4>
<div id="escDshotDirectionDialog-WizardMotorButtons">
</div>
<a href="#" id="escDshotDirectionDialog-StopWizard" class="regular-button" i18n="escDshotDirectionDialog-StopWizard"></a>
</div>
</div>
</div>
<div class="componentContent" id="escDshotDirectionDialog-Warning">
<div>
<p class="escDshotDirectionDialog-RiskNoticeText" i18n="escDshotDirectionDialog-RiskNotice"></p>
<div class="escDshotDirectionToggleParentContainer">
<div class="escDshotDirectionToggleNarrow">
<input id="escDshotDirectionDialog-safetyCheckbox" type="checkbox" class="toggle"/>
</div>
<div class="escDshotDirectionDialog-ToggleWide">
<span class="motorsEnableTestMode escDshotDirectionDialog-RiskNoticeText" i18n="escDshotDirectionDialog-UnderstandRisks"></span>
</div>
</div>
<div class="escDshotDirectionDialog-InformationNotice" i18n="escDshotDirectionDialog-InformationNotice"></div>
<div id="escDshotDirectionDialog-StartWizardBlock" class="escDshotDirectionDialog-StartBlock">
<div class="escDshotDirectionDialog-Buttons">
<a href="#" id="escDshotDirectionDialog-StartWizard" class="regular-button escDshotDirectionDialog-StartButton" i18n="escDshotDirectionDialog-StartWizard"></a>
</div>
<div class="escDshotDirectionDialog-Description" i18n="escDshotDirectionDialog-WizardInformationNotice"></div>
</div>
<div id="escDshotDirectionDialog-StartNormalBlock" class="escDshotDirectionDialog-StartBlock">
<div class="escDshotDirectionDialog-Buttons">
<a href="#" id="escDshotDirectionDialog-Start" class="regular-button escDshotDirectionDialog-StartButton" i18n="escDshotDirectionDialog-Start"></a>
</div>
<div class="escDshotDirectionDialog-Description" i18n="escDshotDirectionDialog-NormalInformationNotice"></div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,67 @@
'use strict';
class EscDshotCommandQueue
{
constructor (intervalMs)
{
this._intervalId = null;
this._interval = intervalMs;
this._queue = [];
this._purging = false;
}
pushCommand(command, buffer)
{
this._queue.push([command, buffer]);
}
pushPause(milliseconds)
{
const counter = Math.ceil(milliseconds / this._interval);
for (let i = 0; i < counter; i++) {
this.pushCommand(null, null);
}
}
start()
{
if (null === this._intervalId) {
this._intervalId = setInterval(
() => { this._checkQueue(); },
this._interval);
}
}
stop()
{
if(null !== this._intervalId) {
clearInterval(this._intervalId);
this._intervalId = null;
}
}
stopWhenEmpty()
{
this._purging = true;
}
clear()
{
this._queue = [];
}
_checkQueue()
{
if (0 !== this._queue.length) {
const command = this._queue.shift();
if (null !== command[0]) {
MSP.send_message(command[0], command[1]);
}
} else if (this._purging) {
this._purging = false;
this.stop();
}
}
}

View file

@ -0,0 +1,383 @@
'use strict';
class EscDshotDirectionComponent
{
constructor(contentDiv, onLoadedCallback, motorConfig)
{
this._buttonTimeoutMs = 400;
const motorDriverQueueIntervalMs = 100;
const motorDriverStopMotorsPauseMs = 400;
this._motorDriver = new EscDshotDirectionMotorDriver(motorConfig,
motorDriverQueueIntervalMs, motorDriverStopMotorsPauseMs);
this._escProtocolIsDshot = motorConfig.escProtocolIsDshot;
this._numberOfMotors = motorConfig.numberOfMotors;
this._contentDiv = contentDiv;
this._onLoadedCallback = onLoadedCallback;
this._currentSpinningMotor = -1;
this._selectedMotor = -1;
this._motorIsSpinning = false;
this._allMotorsAreSpinning = false;
this._spinDirectionToggleIsActive = true;
this._activationButtonTimeoutId = null;
this._contentDiv.load("./components/EscDshotDirection/Body.html", () =>
{
this._initializeDialog();
});
}
static get PUSHED_BUTTON_CLASS() { return "pushed"; }
static get HIGHLIGHTED_BUTTON_CLASS() { return "highlighted"; }
static get RED_TEXT_CLASS() { return "red-text"; }
_readDom()
{
this._domAgreeSafetyCheckBox = $("#escDshotDirectionDialog-safetyCheckbox");
this._domStartButton = $("#escDshotDirectionDialog-Start");
this._domStartWizardButton = $("#escDshotDirectionDialog-StartWizard");
this._domMainContentBlock = $("#escDshotDirectionDialog-MainContent");
this._domWarningContentBlock = $("#escDshotDirectionDialog-Warning");
this._domMixerImg = $("#escDshotDirectionDialog-MixerPreviewImg");
this._domMotorButtonsBlock = $("#escDshotDirectionDialog-SelectMotorButtonsWrapper");
this._domSpinDirectionWrapper = $("#escDshotDirectionDialog-CommandsWrapper");
this._domActionHint = $("#escDshotDirectionDialog-ActionHint");
this._domSpinNormalButton = $("#escDshotDirectionDialog-RotationNormal");
this._domSpinReverseButton = $("#escDshotDirectionDialog-RotationReverse");
this._domSecondHint = $("#escDshotDirectionDialog-SecondHint");
this._domSecondActionDiv = $("#escDshotDirectionDialog-SecondActionBlock");
this._domConfigErrors = $("#escDshotDirectionDialog-ConfigErrors");
this._domWrongProtocolMessage = $("#escDshotDirectionDialog-WrongProtocol");
this._domWrongMixerMessage = $("#escDshotDirectionDialog-WrongMixer");
this._domWrongFirmwareMessage = $("#escDshotDirectionDialog-WrongFirmware");
this._domWizardBlock = $("#escDshotDirectionDialog-WizardDialog");
this._domNormalDialogBlock = $("#escDshotDirectionDialog-NormalDialog");
this._domSpinningWizard = $("#escDshotDirectionDialog-SpinningWizard");
this._domSpinWizardButton = $("#escDshotDirectionDialog-SpinWizard");
this._domStopWizardButton = $("#escDshotDirectionDialog-StopWizard");
this._domWizardMotorButtonsBlock = $("#escDshotDirectionDialog-WizardMotorButtons");
this._domStartWizardBlock = $("#escDshotDirectionDialog-StartWizardBlock");
this._domStartNormalBlock = $("#escDshotDirectionDialog-StartNormalBlock");
this._topHintText = i18n.getMessage("escDshotDirectionDialog-SelectMotor");
this._releaseToStopText = i18n.getMessage("escDshotDirectionDialog-ReleaseToStop");
this._releaseButtonToStopText = i18n.getMessage("escDshotDirectionDialog-ReleaseButtonToStop");
this._normalText = i18n.getMessage("escDshotDirectionDialog-CommandNormal");
this._reverseText = i18n.getMessage("escDshotDirectionDialog-CommandReverse");
this._secondHintText = i18n.getMessage("escDshotDirectionDialog-SetDirectionHint");
}
_initializeDialog()
{
this._readDom();
this._createMotorButtons();
this._createWizardMotorButtons();
this._domSecondActionDiv.toggle(false);
i18n.localizePage();
this._resetGui();
this._domAgreeSafetyCheckBox.on("change", () => {
const enabled = this._domAgreeSafetyCheckBox.is(':checked');
this._domStartNormalBlock.toggle(enabled);
this._domStartWizardBlock.toggle(enabled);
});
this._domStartButton.on("click", () => {
this._onStartButtonClicked();
});
this._domStartWizardButton.on("click", () => {
this._onStartWizardButtonClicked();
});
this._domSpinWizardButton.on("click", () => {
this._onSpinWizardButtonClicked();
});
this._domStopWizardButton.on("click", () => {
this._onStopWizardButtonClicked();
});
const imgSrc = CommonUtils.GetMixerImageSrc(FC.MIXER_CONFIG.mixer, FC.MIXER_CONFIG.reverseMotorDir, FC.CONFIG.apiVersion);
this._domMixerImg.attr('src', imgSrc);
this._onLoadedCallback();
}
_activateNormalReverseButtons(timeoutMs)
{
this._activationButtonTimeoutId = setTimeout(() => {
this._subscribeDirectionSpinButton(this._domSpinNormalButton,
DshotCommand.dshotCommands_e.DSHOT_CMD_SPIN_DIRECTION_1, this._normalText);
this._subscribeDirectionSpinButton(this._domSpinReverseButton,
DshotCommand.dshotCommands_e.DSHOT_CMD_SPIN_DIRECTION_2, this._reverseText);
}, timeoutMs);
}
_deactivateNormalReverseButtons()
{
if (null !== this._activationButtonTimeoutId)
{
clearTimeout(this._activationButtonTimeoutId);
}
this._domSpinNormalButton.off();
this._domSpinReverseButton.off();
}
_subscribeDirectionSpinButton(button, direction, buttonText)
{
button.on("mousedown touchstart", () => {
this._sendCurrentEscSpinDirection(direction);
this._motorIsSpinning = true;
button.text(this._releaseToStopText);
button.addClass(EscDshotDirectionComponent.HIGHLIGHTED_BUTTON_CLASS);
this._motorDriver.spinMotor(this._selectedMotor);
this._domSecondHint.html(this._releaseButtonToStopText);
this._domSecondHint.addClass(EscDshotDirectionComponent.RED_TEXT_CLASS);
});
button.on("mouseup mouseout touchend", () => {
if (this._motorIsSpinning) {
button.text(buttonText);
this._motorIsSpinning = false;
button.removeClass(EscDshotDirectionComponent.HIGHLIGHTED_BUTTON_CLASS);
this._motorDriver.stopAllMotors();
this._domSecondHint.text(this._secondHintText);
this._domSecondHint.removeClass(EscDshotDirectionComponent.RED_TEXT_CLASS);
this._deactivateNormalReverseButtons();
this._activateNormalReverseButtons(this._buttonTimeoutMs);
}
});
}
_sendCurrentEscSpinDirection(direction)
{
this._motorDriver.setEscSpinDirection(this._selectedMotor, direction);
}
_createMotorButtons()
{
this._motorButtons = {};
for (let i = 0; i < this._numberOfMotors; i++) {
this._addMotorButton(i + 1, i);
}
this._addMotorButton("All", DshotCommand.ALL_MOTORS);
}
_addMotorButton(buttonText, motorIndex)
{
const button = $(`<a href="#" class="regular-button ${EscDshotDirectionComponent.PUSHED_BUTTON_CLASS}"></a>`).text(buttonText);
this._domMotorButtonsBlock.append(button);
this._motorButtons[motorIndex] = button;
button.on("mousedown touchstart", () => {
this._domSecondActionDiv.toggle(true);
this._motorIsSpinning = true;
this._domActionHint.html(this._releaseButtonToStopText);
this._domActionHint.addClass(EscDshotDirectionComponent.RED_TEXT_CLASS);
this._changeSelectedMotor(motorIndex);
button.addClass(EscDshotDirectionComponent.HIGHLIGHTED_BUTTON_CLASS);
this._motorDriver.spinMotor(this._selectedMotor);
});
button.on("mouseup mouseout touchend", () => {
if (this._motorIsSpinning) {
this._domActionHint.html(this._topHintText);
this._domActionHint.removeClass(EscDshotDirectionComponent.RED_TEXT_CLASS);
this._motorIsSpinning = false;
button.removeClass(EscDshotDirectionComponent.HIGHLIGHTED_BUTTON_CLASS);
this._motorDriver.stopAllMotors();
this._deactivateNormalReverseButtons();
this._activateNormalReverseButtons(this._buttonTimeoutMs);
}
});
}
_createWizardMotorButtons()
{
this._wizardMotorButtons = {};
for (let i = 0; i < this._numberOfMotors; i++) {
this._addWizardMotorButton(i + 1, i);
}
}
_activateWizardMotorButtons(timeoutMs)
{
this._activationButtonTimeoutId = setTimeout(() => {
for (let i = 0; i < this._numberOfMotors; i++) {
this._activateWizardMotorButton(i);
}
}, timeoutMs);
}
_deactivateWizardMotorButtons()
{
if (null !== this._activationButtonTimeoutId)
{
clearTimeout(this._activationButtonTimeoutId);
}
for (let i = 0; i < this._numberOfMotors; i++) {
const button = this._wizardMotorButtons[i];
button.off();
}
}
_addWizardMotorButton(buttonText, motorIndex)
{
const button = $(`<a href="#" class="regular-button"></a>`).text(buttonText);
this._domWizardMotorButtonsBlock.append(button);
this._wizardMotorButtons[motorIndex] = button;
}
_activateWizardMotorButton(motorIndex)
{
const button = this._wizardMotorButtons[motorIndex];
button.on("click", () => {
this._wizardMotorButtonClick(button, motorIndex);
});
}
_wizardMotorButtonClick(button, motorIndex)
{
this._deactivateWizardMotorButtons();
const currentlyDown = button.hasClass(EscDshotDirectionComponent.PUSHED_BUTTON_CLASS);
if (currentlyDown) {
button.removeClass(EscDshotDirectionComponent.PUSHED_BUTTON_CLASS);
this._motorDriver.setEscSpinDirection(motorIndex, DshotCommand.dshotCommands_e.DSHOT_CMD_SPIN_DIRECTION_1);
} else {
this._motorDriver.setEscSpinDirection(motorIndex, DshotCommand.dshotCommands_e.DSHOT_CMD_SPIN_DIRECTION_2);
button.addClass(EscDshotDirectionComponent.PUSHED_BUTTON_CLASS);
}
this._activateWizardMotorButtons(this._buttonTimeoutMs);
}
_changeSelectedMotor(newIndex)
{
if (this._selectedMotor >= 0) {
this._motorButtons[this._selectedMotor].addClass(EscDshotDirectionComponent.PUSHED_BUTTON_CLASS);
}
this._selectedMotor = newIndex;
if (this._selectedMotor > -1) {
this._motorButtons[this._selectedMotor].removeClass(EscDshotDirectionComponent.PUSHED_BUTTON_CLASS);
}
}
close()
{
this._motorDriver.stopAllMotorsNow();
this._motorDriver.deactivate();
this._resetGui();
}
_resetGui()
{
this._toggleMainContent(false);
this._domStartNormalBlock.hide();
this._domStartWizardBlock.hide();
this._domAgreeSafetyCheckBox.prop('checked', false);
this._domAgreeSafetyCheckBox.trigger('change');
this._domSecondActionDiv.toggle(false);
this._changeSelectedMotor(-1);
this._checkForConfigurationErrors();
}
_checkForConfigurationErrors()
{
let anyError = false;
this._domWrongProtocolMessage.hide();
this._domWrongMixerMessage.hide();
this._domWrongFirmwareMessage.hide();
if (!this._escProtocolIsDshot) {
anyError = true;
this._domWrongProtocolMessage.show();
}
if (this._numberOfMotors <= 0) {
anyError = true;
this._domWrongMixerMessage.show();
}
if (!semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_44)) {
// if BF4.2 or older - show the error message
anyError = true;
this._domWrongFirmwareMessage.show();
}
if (anyError) {
this._domMainContentBlock.hide();
this._domWarningContentBlock.hide();
this._domStartNormalBlock.hide();
this._domStartWizardBlock.hide();
this._domConfigErrors.show();
} else {
this._domConfigErrors.hide();
}
}
_onStartButtonClicked()
{
this._toggleMainContent(true);
this._domWizardBlock.toggle(false);
this._domNormalDialogBlock.toggle(true);
this._motorDriver.activate();
}
_onStartWizardButtonClicked()
{
this._domSpinningWizard.toggle(false);
this._domSpinWizardButton.toggle(true);
this._toggleMainContent(true);
this._domWizardBlock.toggle(true);
this._domNormalDialogBlock.toggle(false);
this._motorDriver.activate();
}
_onSpinWizardButtonClicked()
{
for (let i = 0; i < this._numberOfMotors; i++) {
this._wizardMotorButtons[i].removeClass(EscDshotDirectionComponent.PUSHED_BUTTON_CLASS);
}
this._motorDriver.setEscSpinDirection(DshotCommand.ALL_MOTORS, DshotCommand.dshotCommands_e.DSHOT_CMD_SPIN_DIRECTION_1);
this._domSpinWizardButton.toggle(false);
this._domSpinningWizard.toggle(true);
this._motorDriver.spinAllMotors();
this._activateWizardMotorButtons(0);
}
_onStopWizardButtonClicked()
{
this._domSpinWizardButton.toggle(true);
this._domSpinningWizard.toggle(false);
this._motorDriver.stopAllMotorsNow();
this._deactivateWizardMotorButtons();
}
_toggleMainContent(value)
{
this._domWarningContentBlock.toggle(!value);
this._domMainContentBlock.toggle(value);
this._domConfigErrors.toggle(false);
}
}

View file

@ -0,0 +1,155 @@
'use strict';
class EscDshotDirectionMotorDriver
{
constructor(motorConfig, motorDriverQueueIntervalMs, motorDriverStopMotorsPauseMs)
{
this._numberOfMotors = motorConfig.numberOfMotors;
this._motorStopValue = motorConfig.motorStopValue;
this._motorSpinValue = motorConfig.motorSpinValue;
this._motorDriverStopMotorsPauseMs = motorDriverStopMotorsPauseMs;
this._state = [];
for (let i = 0; i < this._numberOfMotors; i++)
{
this._state.push(this._motorStopValue);
}
this._stateStack = [];
this._EscDshotCommandQueue = new EscDshotCommandQueue(motorDriverQueueIntervalMs);
}
activate()
{
this._EscDshotCommandQueue.start();
}
deactivate()
{
this._EscDshotCommandQueue.stopWhenEmpty();
}
stopMotor(motorIndex)
{
this._spinMotor(motorIndex, this._motorStopValue);
}
spinMotor(motorIndex)
{
this._spinMotor(motorIndex, this._motorSpinValue);
}
spinAllMotors()
{
this._spinAllMotors(this._motorSpinValue);
}
stopAllMotors()
{
this._spinAllMotors(this._motorStopValue);
}
stopAllMotorsNow()
{
this._EscDshotCommandQueue.clear();
this._spinAllMotors(this._motorStopValue);
}
setEscSpinDirection(motorIndex, direction)
{
let needStopMotor = false;
if (DshotCommand.ALL_MOTORS === motorIndex) {
needStopMotor = this._isAnythingSpinning();
} else {
needStopMotor = this._isMotorSpinning(motorIndex);
}
if (needStopMotor) {
this._pushState();
this._spinMotor(motorIndex, this._motorStopValue);
this._EscDshotCommandQueue.pushPause(this._motorDriverStopMotorsPauseMs);
this._sendEscSpinDirection(motorIndex, direction);
this._popState();
this._sendState();
} else {
this._sendEscSpinDirection(motorIndex, direction);
}
}
_pushState()
{
const state = [...this._state];
this._stateStack.push(state);
}
_popState()
{
const state = this._stateStack.pop();
this._state = [...state];
}
_isAnythingSpinning()
{
let result = false;
for (let i = 0; i < this._numberOfMotors; i++) {
if (this._motorStopValue !== this._state[i]) {
result = true;
break;
}
}
return result;
}
_isMotorSpinning(motorIndex)
{
return (this._motorStopValue !== this._state[motorIndex]);
}
_sendEscSpinDirection(motorIndex, direction)
{
const buffer = [];
buffer.push8(DshotCommand.dshotCommandType_e.DSHOT_CMD_TYPE_BLOCKING);
buffer.push8(motorIndex);
buffer.push8(2); // two commands
buffer.push8(direction);
buffer.push8(DshotCommand.dshotCommands_e.DSHOT_CMD_SAVE_SETTINGS);
this._EscDshotCommandQueue.pushCommand(MSPCodes.MSP2_SEND_DSHOT_COMMAND, buffer);
}
_spinMotor(motorIndex, value)
{
if (DshotCommand.ALL_MOTORS === motorIndex) {
this._spinAllMotors(value);
} else {
this._state[motorIndex] = value;
this._sendState();
}
}
_spinAllMotors(value)
{
for (let i = 0; i < this._numberOfMotors; i++) {
this._state[i] = value;
}
this._sendState();
}
_sendState()
{
const buffer = [];
for (let i = 0; i < this._numberOfMotors; i++) {
buffer.push16(this._state[i]);
}
this._EscDshotCommandQueue.pushCommand(MSPCodes.MSP_SET_MOTOR, buffer);
}
}

View file

@ -0,0 +1,177 @@
.escDshotDirection-Component {
display: flex;
height: 100%;
flex-flow: column;
}
.escDshotDirection-ComponentHeader {
padding-bottom: 12px;
}
#escDshotDirectionDialog-MainContent {
display: flex;
height: 100%;
flex-flow: column;
}
#escDshotDirectionDialog-Warning {
display: flex;
height: 100%;
flex-flow: column;
border-top: 1px solid var(--superSubtleAccent);
padding-top: 16px;
}
.escDshotDirectionToggleParentContainer {
display: flex;
margin-bottom: 1.5em;
margin-top: 1.5em;
}
.escDshotDirectionToggleNarrow {
margin-right: 12px;
display: flex;
align-items: center;
}
.escDshotDirectionDialog-ToggleWide {
flex: 1;
}
.escDshotDirectionDialog-RiskNoticeText {
font-size: 1.2em;
}
.escDshotDirectionDialogInformationNotice {
font-size: 1.0em;
margin-bottom: 1.5em;
margin-top: 1.5em;
}
#escDshotDirectionDialog-MixerPreview {
width: 100%;
padding-top: 8px;
padding-bottom: 9px;
margin-bottom: 8px;
}
#escDshotDirectionDialog-MixerPreviewImg {
display: block;
width: 160px;
height: 160px;
margin-left: auto;
margin-right: auto;
margin-top: auto;
margin-bottom: auto;
}
#escDshotDirectionDialog-MainContent h4 {
margin-left: auto;
margin-right: auto;
font-weight: 500;
}
#escDshotDirectionDialog-MainContent .red-text {
color: #EE0000;
}
#escDshotDirectionDialog-ActionHint, #escDshotDirectionDialog-SecondHint, #escDshotDirectionDialog-WizardActionHint {
margin-top: 10px;
}
#escDshotDirectionDialog-ActionHintSafety, #escDshotDirectionDialog-SecondHintSafety {
margin-top: 0px;
}
#escDshotDirectionDialog-SelectMotorButtonsWrapper, #escDshotDirectionDialog-WizardMotorButtons {
margin-left: auto;
margin-right: auto;
}
#escDshotDirectionDialog-SelectMotorButtonsWrapper .regular-button, #escDshotDirectionDialog-WizardMotorButtons .regular-button {
font-size: 15px;
line-height: 34px;
padding: 0px;
margin-left: 4px;
margin-right: 4px;
border-radius: 17px;
width: 34px;
height: 34px;
text-align: center;
}
#escDshotDirectionDialog-NormalDialog .regular-button.pushed:hover {
background-color: #993333;
}
#escDshotDirectionDialog-NormalDialog .regular-button:hover {
background-color: #993333;
}
#escDshotDirectionDialog-MainContent .regular-button.highlighted {
background-color: #EE2222;
}
#escDshotDirectionDialog-CommandsWrapper {
margin-left: auto;
margin-right: auto;
}
#escDshotDirectionDialog-CommandSpin {
margin-left: auto;
margin-right: auto;
width: 224px;
display: block;
text-align: center;
}
#escDshotDirectionDialog-CommandsWrapper .regular-button {
width: 130px;
text-align: center;
margin-left: 5px;
margin-right: 5px;
}
.escDshotDirectionErrorTextBlock {
margin-top: 12px;
font-weight: 500;
}
.display-contents {
display: contents;
}
#escDshotDirectionDialog-SpinWizard, #escDshotDirectionDialog-StopWizard {
margin-left: auto;
margin-right: auto;
width: 160px;
text-align: center;
}
.escDshotDirectionDialog-InformationNotice {
margin-top: 18px;
padding-top: 16px;
padding-bottom: 16px;
border-top: 1px solid var(--superSubtleAccent);
border-bottom: 1px solid var(--superSubtleAccent);
}
.escDshotDirectionDialog-StartButton {
width: 80px;
text-align: center;
margin-left: 0px;
margin-right: 16px;
margin-top: 0px;
margin-bottom: 0px;
}
.escDshotDirectionDialog-Buttons {
float: left;
margin: 0px;
}
.escDshotDirectionDialog-StartBlock {
display: flex;
margin-top: 16px;
}

View file

@ -25,7 +25,7 @@ class MotorOutputReorderComponent
_readDom() _readDom()
{ {
this._domAgreeSafetyCheckBox = $('#motorsEnableTestMode-dialogMotorOutputReorder'); this._domAgreeSafetyCheckBox = $('#motorsEnableTestMode-dialogMotorOutputReorder');
this._domAgreeButton = $('#dialogMotorOutputReorderAgreeButton'); this._domStartButton = $('#dialogMotorOutputReorderAgreeButton');
this._domStartOverButton = $('#motorsRemapDialogStartOver'); this._domStartOverButton = $('#motorsRemapDialogStartOver');
this._domSaveButton = $('#motorsRemapDialogSave'); this._domSaveButton = $('#motorsRemapDialogSave');
this._domMainContentBlock = $('#dialogMotorOutputReorderMainContent'); this._domMainContentBlock = $('#dialogMotorOutputReorderMainContent');
@ -44,12 +44,12 @@ class MotorOutputReorderComponent
this._domAgreeSafetyCheckBox.change(() => this._domAgreeSafetyCheckBox.change(() =>
{ {
const enabled = this._domAgreeSafetyCheckBox.is(':checked'); const enabled = this._domAgreeSafetyCheckBox.is(':checked');
this._domAgreeButton.toggle(enabled); this._domStartButton.toggle(enabled);
}); });
this._domAgreeButton.click(() => this._domStartButton.click(() =>
{ {
this._onAgreeButtonClicked(); this._onStartButtonClicked();
}); });
this._domStartOverButton.click(() => this._domStartOverButton.click(() =>
{ {
@ -75,7 +75,7 @@ class MotorOutputReorderComponent
{ {
this._domMainContentBlock.hide(); this._domMainContentBlock.hide();
this._domWarningContentBlock.show(); this._domWarningContentBlock.show();
this._domAgreeButton.hide(); this._domStartButton.hide();
this._domAgreeSafetyCheckBox.prop('checked', false); this._domAgreeSafetyCheckBox.prop('checked', false);
this._domAgreeSafetyCheckBox.change(); this._domAgreeSafetyCheckBox.change();
@ -138,7 +138,7 @@ class MotorOutputReorderComponent
} }
} }
_onAgreeButtonClicked() _onStartButtonClicked()
{ {
this._domActionHintBlock.text(i18n.getMessage("motorOutputReorderDialogSelectSpinningMotor")); this._domActionHintBlock.text(i18n.getMessage("motorOutputReorderDialogSelectSpinningMotor"));
this._domWarningContentBlock.hide(); this._domWarningContentBlock.hide();

View file

@ -5,8 +5,6 @@
} }
.motorOutputReorderComponentHeader { .motorOutputReorderComponentHeader {
border-bottom: 1px solid var(--accent);
margin-bottom: 10px;
padding-bottom: 12px; padding-bottom: 12px;
} }

View file

@ -14,6 +14,10 @@
--gimbalBackground: var(--subtleAccent); --gimbalBackground: var(--subtleAccent);
--gimbalCrosshair: var(--mutedText); --gimbalCrosshair: var(--mutedText);
--switcherysecond: #858585; --switcherysecond: #858585;
--pushedButton-background: #616161;
--pushedButton-fontColor: #ffffff;
--hoverButton-background: #ffcc3e;
--superSubtleAccent: #595959;
} }
.background_paper { .background_paper {
@ -24,6 +28,11 @@ body {
color: white; color: white;
} }
::backdrop {
background-image: none;
background-color: rgba(0, 0, 0, 0.5);
}
#options-window { #options-window {
background-color: #393b3a; background-color: #393b3a;
} }
@ -219,7 +228,7 @@ button {
} }
.tab-auxiliary .buttons a:hover { .tab-auxiliary .buttons a:hover {
background-color: #393b3a; background-color: var(--hoverButton-background);
} }
@ -357,7 +366,7 @@ button {
} }
.tab-gps #loadmap .controls a:hover { .tab-gps #loadmap .controls a:hover {
background-color: #393b3a; background-color: var(--hoverButton-background);
} }
.tab-gps #loadmap .controls a:active { .tab-gps #loadmap .controls a:active {

View file

@ -16,6 +16,10 @@
--gimbalBackground: #eee; --gimbalBackground: #eee;
--gimbalCrosshair: var(--subtleAccent); --gimbalCrosshair: var(--subtleAccent);
--switcherysecond: #c4c4c4; --switcherysecond: #c4c4c4;
--pushedButton-background: #c4c4c4;
--pushedButton-fontColor: #000000;
--hoverButton-background: #ffcc3e;
--superSubtleAccent: #CCCCCC;
} }
* { * {
@ -40,6 +44,11 @@ body {
overflow: hidden; overflow: hidden;
} }
::backdrop {
background-image: none;
background-color: rgba(1, 1, 1, 0.5);
}
a { a {
text-decoration: none; text-decoration: none;
color: var(--linkText); color: var(--linkText);
@ -140,7 +149,11 @@ input[type="number"]::-webkit-inner-spin-button {
font-weight: bold !important; font-weight: bold !important;
} }
.message-negative-italic {
color: var(--error) !important;
font-weight: bold !important;
font-style: italic;
}
/** Main wrapper **/ /** Main wrapper **/
#main-wrapper { #main-wrapper {
@ -1478,7 +1491,7 @@ dialog .dialog_toolbar .btn a {
} }
dialog .dialog_toolbar .btn a:hover { dialog .dialog_toolbar .btn a:hover {
background-color: #ffcc3e; background-color: var(--hoverButton-background);
transition: all ease 0.2s; transition: all ease 0.2s;
} }
@ -1574,7 +1587,7 @@ dialog .dialog_toolbar .btn a.disabled {
} }
.content_toolbar .btn a:hover { .content_toolbar .btn a:hover {
background-color: #ffcc3e; background-color: var(--hoverButton-background);
transition: all ease 0.2s; transition: all ease 0.2s;
} }
@ -1862,7 +1875,7 @@ dialog .dialog_toolbar .btn a.disabled {
} }
.fixed_band .save_btn a:hover { .fixed_band .save_btn a:hover {
background-color: #ffcc3f; background-color: var(--hoverButton-background);
transition: all ease 0.2s; transition: all ease 0.2s;
} }
@ -1898,7 +1911,7 @@ dialog .dialog_toolbar .btn a.disabled {
} }
.default_btn a:hover { .default_btn a:hover {
background-color: #ffcc3f; background-color: var(--hoverButton-background);
color: #000; color: #000;
text-shadow: 0 1px rgba(255, 255, 255, 0.25); text-shadow: 0 1px rgba(255, 255, 255, 0.25);
transition: all ease 0.2s; transition: all ease 0.2s;
@ -1907,7 +1920,7 @@ dialog .dialog_toolbar .btn a.disabled {
} }
.default_btn a:active { .default_btn a:active {
background-color: #ffcc3f; background-color: var(--hoverButton-background);
transition: all ease 0.0s; transition: all ease 0.0s;
box-shadow: inset 0 1px 5px rgba(0, 0, 0, 0.35); box-shadow: inset 0 1px 5px rgba(0, 0, 0, 0.35);
} }
@ -1917,6 +1930,7 @@ dialog .dialog_toolbar .btn a.disabled {
} }
.regular-button { .regular-button {
-webkit-user-drag: none;
margin-top: 8px; margin-top: 8px;
margin-bottom: 8px; margin-bottom: 8px;
margin-right: 10px; margin-right: 10px;
@ -1934,6 +1948,16 @@ dialog .dialog_toolbar .btn a.disabled {
line-height: 28px; line-height: 28px;
} }
.regular-button:hover {
background-color: var(--hoverButton-background);
}
.regular-button.pushed {
background-color: var(--pushedButton-background);
color: var(--pushedButton-fontColor);
border-radius: 3px;
}
.small { .small {
width: auto; width: auto;
position: relative; position: relative;

View file

@ -25,7 +25,7 @@
.tab-motors #dialogMotorOutputReorder { .tab-motors #dialogMotorOutputReorder {
width: 400px; width: 400px;
height:440px height: 440px
;} ;}
.tab-motors #dialogMotorOutputReorderContentWrapper { .tab-motors #dialogMotorOutputReorderContentWrapper {
@ -39,6 +39,31 @@
flex-grow: 1; flex-grow: 1;
} }
.tab-motors #escDshotDirectionDialog-closebtn {
margin-right: 0px;
margin-bottom: 0px;
position: absolute;
right: 0px;
bottom: 0px;
}
.tab-motors #escDshotDirectionDialog {
width: 400px;
height: 440px;
}
.tab-motors #escDshotDirectionDialog-ContentWrapper {
display: flex;
flex-flow: column;
width: 100%;
height: 100%;
position: relative;
}
.tab-motors #escDshotDirectionDialog-Content {
flex-grow: 1;
}
.tab-motors .mixerPreview img { .tab-motors .mixerPreview img {
width: 120px; width: 120px;
height: 120px; height: 120px;
@ -457,4 +482,16 @@
margin: 0; margin: 0;
height: auto; height: auto;
} }
.tab-motors #escDshotDirectionDialog- {
position: fixed;
width: calc(100% - 2em);
bottom: 0;
top: 56px;
border-radius: unset;
border: none;
overflow: auto;
margin: 0;
height: auto;
}
} }

View file

@ -181,4 +181,5 @@ const MSPCodes = {
MSP2_BETAFLIGHT_BIND: 0x3000, MSP2_BETAFLIGHT_BIND: 0x3000,
MSP2_MOTOR_OUTPUT_REORDERING: 0x3001, MSP2_MOTOR_OUTPUT_REORDERING: 0x3001,
MSP2_SET_MOTOR_OUTPUT_REORDERING: 0x3002, MSP2_SET_MOTOR_OUTPUT_REORDERING: 0x3002,
MSP2_SEND_DSHOT_COMMAND: 0x3003,
}; };

View file

@ -57,26 +57,6 @@ function MspHelper() {
self.mspMultipleCache = []; self.mspMultipleCache = [];
} }
MspHelper.prototype.reorderPwmProtocols = function (protocol) {
let result = protocol;
if (semver.lt(FC.CONFIG.apiVersion, "1.26.0")) {
switch (protocol) {
case 5:
result = 7;
break;
case 7:
result = 5;
break;
default:
break;
}
}
return result;
}
MspHelper.prototype.process_data = function(dataHandler) { MspHelper.prototype.process_data = function(dataHandler) {
const self = this; const self = this;
const data = dataHandler.dataView; // DataView (allowing us to view arrayBuffer as struct/union) const data = dataHandler.dataView; // DataView (allowing us to view arrayBuffer as struct/union)
@ -1039,7 +1019,7 @@ MspHelper.prototype.process_data = function(dataHandler) {
FC.PID_ADVANCED_CONFIG.gyro_sync_denom = data.readU8(); FC.PID_ADVANCED_CONFIG.gyro_sync_denom = data.readU8();
FC.PID_ADVANCED_CONFIG.pid_process_denom = data.readU8(); FC.PID_ADVANCED_CONFIG.pid_process_denom = data.readU8();
FC.PID_ADVANCED_CONFIG.use_unsyncedPwm = data.readU8(); FC.PID_ADVANCED_CONFIG.use_unsyncedPwm = data.readU8();
FC.PID_ADVANCED_CONFIG.fast_pwm_protocol = self.reorderPwmProtocols(data.readU8()); FC.PID_ADVANCED_CONFIG.fast_pwm_protocol = EscProtocols.ReorderPwmProtocols(FC.CONFIG.apiVersion, data.readU8());
FC.PID_ADVANCED_CONFIG.motor_pwm_rate = data.readU16(); FC.PID_ADVANCED_CONFIG.motor_pwm_rate = data.readU16();
if (semver.gte(FC.CONFIG.apiVersion, "1.24.0")) { if (semver.gte(FC.CONFIG.apiVersion, "1.24.0")) {
FC.PID_ADVANCED_CONFIG.digitalIdlePercent = data.readU16() / 100; FC.PID_ADVANCED_CONFIG.digitalIdlePercent = data.readU16() / 100;
@ -1569,6 +1549,9 @@ MspHelper.prototype.process_data = function(dataHandler) {
case MSPCodes.MSP2_SET_MOTOR_OUTPUT_REORDERING: case MSPCodes.MSP2_SET_MOTOR_OUTPUT_REORDERING:
console.log('Motor output reordering set'); console.log('Motor output reordering set');
break; break;
case MSPCodes.MSP2_SEND_DSHOT_COMMAND:
console.log('DSHOT command sent');
break;
case MSPCodes.MSP_MULTIPLE_MSP: case MSPCodes.MSP_MULTIPLE_MSP:
@ -1981,7 +1964,7 @@ MspHelper.prototype.crunch = function(code) {
buffer.push8(FC.PID_ADVANCED_CONFIG.gyro_sync_denom) buffer.push8(FC.PID_ADVANCED_CONFIG.gyro_sync_denom)
.push8(FC.PID_ADVANCED_CONFIG.pid_process_denom) .push8(FC.PID_ADVANCED_CONFIG.pid_process_denom)
.push8(FC.PID_ADVANCED_CONFIG.use_unsyncedPwm) .push8(FC.PID_ADVANCED_CONFIG.use_unsyncedPwm)
.push8(self.reorderPwmProtocols(FC.PID_ADVANCED_CONFIG.fast_pwm_protocol)) .push8(EscProtocols.ReorderPwmProtocols(FC.CONFIG.apiVersion, FC.PID_ADVANCED_CONFIG.fast_pwm_protocol))
.push16(FC.PID_ADVANCED_CONFIG.motor_pwm_rate); .push16(FC.PID_ADVANCED_CONFIG.motor_pwm_rate);
if (semver.gte(FC.CONFIG.apiVersion, "1.24.0")) { if (semver.gte(FC.CONFIG.apiVersion, "1.24.0")) {
buffer.push16(FC.PID_ADVANCED_CONFIG.digitalIdlePercent * 100); buffer.push16(FC.PID_ADVANCED_CONFIG.digitalIdlePercent * 100);
@ -2278,6 +2261,10 @@ MspHelper.prototype.crunch = function(code) {
break; break;
case MSPCodes.MSP2_SEND_DSHOT_COMMAND:
buffer.push8(1);
break;
default: default:
return false; return false;
} }

View file

@ -213,14 +213,8 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
} }
function refreshMixerPreview() { function refreshMixerPreview() {
const mixer = FC.MIXER_CONFIG.mixer const imgSrc = CommonUtils.GetMixerImageSrc(FC.MIXER_CONFIG.mixer, FC.MIXER_CONFIG.reverseMotorDir, FC.CONFIG.apiVersion);
let reverse = ""; $('.mixerPreview img').attr('src', imgSrc);
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_36)) {
reverse = FC.MIXER_CONFIG.reverseMotorDir ? "_reversed" : "";
}
$('.mixerPreview img').attr('src', './resources/motor_order/' + mixerList[mixer - 1].image + reverse + '.svg');
} }
const reverseMotorSwitch_e = $('#reverseMotorSwitch'); const reverseMotorSwitch_e = $('#reverseMotorSwitch');
@ -462,34 +456,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
} }
// ESC protocols // ESC protocols
const escProtocols = [ const escProtocols = EscProtocols.GetAvailableProtocols(FC.CONFIG.apiVersion);
'PWM',
'ONESHOT125',
'ONESHOT42',
'MULTISHOT',
];
if (semver.gte(FC.CONFIG.apiVersion, "1.20.0")) {
escProtocols.push('BRUSHED');
}
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_31)) {
escProtocols.push('DSHOT150');
escProtocols.push('DSHOT300');
escProtocols.push('DSHOT600');
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_42)) {
escProtocols.push('DSHOT1200');
}
}
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_36)) {
escProtocols.push('PROSHOT1000');
}
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_43)) {
escProtocols.push('DISABLED');
}
const esc_protocol_e = $('select.escprotocol'); const esc_protocol_e = $('select.escprotocol');

View file

@ -31,7 +31,8 @@ TABS.motors = {
// These are translated into proper Dshot values on the flight controller // These are translated into proper Dshot values on the flight controller
DSHOT_DISARMED_VALUE: 1000, DSHOT_DISARMED_VALUE: 1000,
DSHOT_MAX_VALUE: 2000, DSHOT_MAX_VALUE: 2000,
DSHOT_3D_NEUTRAL: 1500 DSHOT_3D_NEUTRAL: 1500,
numberOfValidOutputs: -1,
}; };
TABS.motors.initialize = function (callback) { TABS.motors.initialize = function (callback) {
@ -105,7 +106,7 @@ TABS.motors.initialize = function (callback) {
} }
function initDataArray(length) { function initDataArray(length) {
const data = new Array(length); const data = Array.from({length: length});
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
data[i] = []; data[i] = [];
data[i].min = -1; data[i].min = -1;
@ -126,8 +127,8 @@ TABS.motors.initialize = function (callback) {
} }
} }
while (data[0].length > 300) { while (data[0].length > 300) {
for (let i = 0; i < data.length; i++) { for (const item of data) {
data[i].shift(); item.shift();
} }
} }
return sampleNumber + 1; return sampleNumber + 1;
@ -220,13 +221,8 @@ TABS.motors.initialize = function (callback) {
} }
function update_model(mixer) { function update_model(mixer) {
let reverse = ""; const imgSrc = CommonUtils.GetMixerImageSrc(mixer, FC.MIXER_CONFIG.reverseMotorDir, FC.CONFIG.apiVersion);
$('.mixerPreview img').attr('src', imgSrc);
if (semver.gte(FC.CONFIG.apiVersion, API_VERSION_1_36)) {
reverse = FC.MIXER_CONFIG.reverseMotorDir ? "_reversed" : "";
}
$('.mixerPreview img').attr('src', './resources/motor_order/' + mixerList[mixer - 1].image + reverse + '.svg');
const motorOutputReorderConfig = new MotorOutputReorderConfig(100); const motorOutputReorderConfig = new MotorOutputReorderConfig(100);
const domMotorOutputReorderDialogOpen = $('#motorOutputReorderDialogOpen'); const domMotorOutputReorderDialogOpen = $('#motorOutputReorderDialogOpen');
@ -234,6 +230,8 @@ TABS.motors.initialize = function (callback) {
const isMotorReorderingAvailable = (mixerList[mixer - 1].name in motorOutputReorderConfig) const isMotorReorderingAvailable = (mixerList[mixer - 1].name in motorOutputReorderConfig)
&& (FC.MOTOR_OUTPUT_ORDER) && (FC.MOTOR_OUTPUT_ORDER.length > 0); && (FC.MOTOR_OUTPUT_ORDER) && (FC.MOTOR_OUTPUT_ORDER.length > 0);
domMotorOutputReorderDialogOpen.toggle(isMotorReorderingAvailable); domMotorOutputReorderDialogOpen.toggle(isMotorReorderingAvailable);
self.escProtocolIsDshot = EscProtocols.IsProtocolDshot(FC.CONFIG.apiVersion, FC.PID_ADVANCED_CONFIG.fast_pwm_protocol);
} }
function process_html() { function process_html() {
@ -244,12 +242,6 @@ TABS.motors.initialize = function (callback) {
self.feature3DEnabled = FC.FEATURE_CONFIG.features.isEnabled('3D'); self.feature3DEnabled = FC.FEATURE_CONFIG.features.isEnabled('3D');
if (FC.PID_ADVANCED_CONFIG.fast_pwm_protocol >= TABS.configuration.DSHOT_PROTOCOL_MIN_VALUE) {
self.escProtocolIsDshot = true;
} else {
self.escProtocolIsDshot = false;
}
$('#motorsEnableTestMode').prop('checked', false); $('#motorsEnableTestMode').prop('checked', false);
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_42) || !(FC.MOTOR_CONFIG.use_dshot_telemetry || FC.MOTOR_CONFIG.use_esc_sensor)) { if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_42) || !(FC.MOTOR_CONFIG.use_dshot_telemetry || FC.MOTOR_CONFIG.use_esc_sensor)) {
@ -416,7 +408,7 @@ TABS.motors.initialize = function (callback) {
function computeAndUpdate(sensor_data, data, max_read) { function computeAndUpdate(sensor_data, data, max_read) {
let sum = 0.0; let sum = 0.0;
for (let j = 0, jlength = data.length; j < jlength; j++) { for (let j = 0, jlength = data.length; j < jlength; j++) {
for (let k = 0, klength = data[j].length; k < klength; k++){ for (let k = 0, klength = data[j].length; k < klength; k++) {
sum += data[j][k][1]*data[j][k][1]; sum += data[j][k][1]*data[j][k][1];
} }
} }
@ -455,6 +447,7 @@ TABS.motors.initialize = function (callback) {
motor_mah_drawing_e.text(i18n.getMessage('motorsADrawingValue', [FC.ANALOG.amperage.toFixed(2)])); motor_mah_drawing_e.text(i18n.getMessage('motorsADrawingValue', [FC.ANALOG.amperage.toFixed(2)]));
motor_mah_drawn_e.text(i18n.getMessage('motorsmAhDrawnValue', [FC.ANALOG.mAhdrawn])); motor_mah_drawn_e.text(i18n.getMessage('motorsmAhDrawnValue', [FC.ANALOG.mAhdrawn]));
} }
GUI.interval_add('motors_power_data_pull_slow', power_data_pull, 250, true); // 4 fps GUI.interval_add('motors_power_data_pull_slow', power_data_pull, 250, true); // 4 fps
$('a.reset_max').click(function () { $('a.reset_max').click(function () {
@ -463,7 +456,7 @@ TABS.motors.initialize = function (callback) {
accelOffsetEstablished = false; accelOffsetEstablished = false;
}); });
const numberOfValidOutputs = (FC.MOTOR_DATA.indexOf(0) > -1) ? FC.MOTOR_DATA.indexOf(0) : 8; self.numberOfValidOutputs = (FC.MOTOR_DATA.indexOf(0) > -1) ? FC.MOTOR_DATA.indexOf(0) : 8;
let rangeMin; let rangeMin;
let rangeMax; let rangeMax;
let neutral3d; let neutral3d;
@ -526,7 +519,7 @@ TABS.motors.initialize = function (callback) {
function setSlidersEnabled(isEnabled) { function setSlidersEnabled(isEnabled) {
if (isEnabled && !self.armed) { if (isEnabled && !self.armed) {
$('div.sliders input').slice(0, numberOfValidOutputs).prop('disabled', false); $('div.sliders input').slice(0, self.numberOfValidOutputs).prop('disabled', false);
// unlock master slider // unlock master slider
$('div.sliders input:last').prop('disabled', false); $('div.sliders input:last').prop('disabled', false);
@ -583,14 +576,14 @@ TABS.motors.initialize = function (callback) {
const val = $(this).val(); const val = $(this).val();
$('div.sliders input:not(:disabled, :last)').val(val); $('div.sliders input:not(:disabled, :last)').val(val);
$('div.values li:not(:last)').slice(0, numberOfValidOutputs).text(val); $('div.values li:not(:last)').slice(0, self.numberOfValidOutputs).text(val);
$('div.sliders input:not(:last):first').trigger('input'); $('div.sliders input:not(:last):first').trigger('input');
}); });
// check if motors are already spinning // check if motors are already spinning
let motorsRunning = false; let motorsRunning = false;
for (let i = 0; i < numberOfValidOutputs; i++) { for (let i = 0; i < self.numberOfValidOutputs; i++) {
if (!self.feature3DEnabled) { if (!self.feature3DEnabled) {
if (FC.MOTOR_DATA[i] > rangeMin) { if (FC.MOTOR_DATA[i] > rangeMin) {
motorsRunning = true; motorsRunning = true;
@ -747,7 +740,11 @@ TABS.motors.initialize = function (callback) {
zeroThrottleValue = neutral3d; zeroThrottleValue = neutral3d;
} }
setup_motor_output_reordering_dialog(content_ready, zeroThrottleValue); setup_motor_output_reordering_dialog(SetupEscDshotDirectionDialogCallback, zeroThrottleValue);
function SetupEscDshotDirectionDialogCallback() {
SetupdescDshotDirectionDialog(content_ready, zeroThrottleValue);
}
function content_ready() { function content_ready() {
GUI.content_ready(callback); GUI.content_ready(callback);
@ -764,9 +761,9 @@ TABS.motors.initialize = function (callback) {
callbackFunction, mixerList[FC.MIXER_CONFIG.mixer - 1].name, callbackFunction, mixerList[FC.MIXER_CONFIG.mixer - 1].name,
zeroThrottleValue, zeroThrottleValue + 200); zeroThrottleValue, zeroThrottleValue + 200);
$('#dialogMotorOutputReorder-closebtn').click(closeDialog); $('#dialogMotorOutputReorder-closebtn').click(closeDialogMotorOutputReorder);
function closeDialog() function closeDialogMotorOutputReorder()
{ {
domDialogMotorOutputReorder[0].close(); domDialogMotorOutputReorder[0].close();
motorOutputReorderComponent.close(); motorOutputReorderComponent.close();
@ -776,7 +773,7 @@ TABS.motors.initialize = function (callback) {
function onDocumentKeyPress(event) function onDocumentKeyPress(event)
{ {
if (27 === event.which) { if (27 === event.which) {
closeDialog(); closeDialogMotorOutputReorder();
} }
} }
@ -786,6 +783,47 @@ TABS.motors.initialize = function (callback) {
domDialogMotorOutputReorder[0].showModal(); domDialogMotorOutputReorder[0].showModal();
}); });
} }
function SetupdescDshotDirectionDialog(callbackFunction, zeroThrottleValue)
{
const domEscDshotDirectionDialog = $('#escDshotDirectionDialog');
const idleThrottleValue = zeroThrottleValue + FC.PID_ADVANCED_CONFIG.digitalIdlePercent * 1000 / 100;
const motorConfig = {
numberOfMotors: self.numberOfValidOutputs,
motorStopValue: zeroThrottleValue,
motorSpinValue: idleThrottleValue,
escProtocolIsDshot: self.escProtocolIsDshot,
};
const escDshotDirectionComponent = new EscDshotDirectionComponent(
$('#escDshotDirectionDialog-Content'), callbackFunction, motorConfig);
$('#escDshotDirectionDialog-closebtn').on("click", closeEscDshotDirectionDialog);
function closeEscDshotDirectionDialog()
{
domEscDshotDirectionDialog[0].close();
escDshotDirectionComponent.close();
$(document).off("keydown", onDocumentKeyPress);
}
function onDocumentKeyPress(event)
{
if (27 === event.which) {
closeEscDshotDirectionDialog();
}
}
$('#escDshotDirectionDialog-Open').click(function()
{
$(document).on("keydown", onDocumentKeyPress);
domEscDshotDirectionDialog[0].showModal();
});
callbackFunction();
}
}; };
TABS.motors.cleanup = function (callback) { TABS.motors.cleanup = function (callback) {

View file

@ -0,0 +1,15 @@
'use strict';
class CommonUtils
{
static GetMixerImageSrc(mixerIndex, reverseMotorDir, apiVersion)
{
let reverse = "";
if (semver.gte(apiVersion, API_VERSION_1_36)) {
reverse = reverseMotorDir ? "_reversed" : "";
}
return `./resources/motor_order/${mixerList[mixerIndex - 1].image}${reverse}.svg`;
}
}

View file

@ -0,0 +1,40 @@
'use strict';
class DshotCommand
{
static get ALL_MOTORS() { return 255; }
}
DshotCommand.dshotCommands_e = {
DSHOT_CMD_MOTOR_STOP: 0,
DSHOT_CMD_BEACON1: 1,
DSHOT_CMD_BEACON2: 2,
DSHOT_CMD_BEACON3: 3,
DSHOT_CMD_BEACON4: 4,
DSHOT_CMD_BEACON5: 5,
DSHOT_CMD_ESC_INFO: 6, // V2 includes settings
DSHOT_CMD_SPIN_DIRECTION_1: 7,
DSHOT_CMD_SPIN_DIRECTION_2: 8,
DSHOT_CMD_3D_MODE_OFF: 9,
DSHOT_CMD_3D_MODE_ON: 10,
DSHOT_CMD_SETTINGS_REQUEST: 11, // Currently not implemented
DSHOT_CMD_SAVE_SETTINGS: 12,
DSHOT_CMD_SPIN_DIRECTION_NORMAL: 20,
DSHOT_CMD_SPIN_DIRECTION_REVERSED: 21,
DSHOT_CMD_LED0_ON: 22, // BLHeli32 only
DSHOT_CMD_LED1_ON: 23, // BLHeli32 only
DSHOT_CMD_LED2_ON: 24, // BLHeli32 only
DSHOT_CMD_LED3_ON: 25, // BLHeli32 only
DSHOT_CMD_LED0_OFF: 26, // BLHeli32 only
DSHOT_CMD_LED1_OFF: 27, // BLHeli32 only
DSHOT_CMD_LED2_OFF: 28, // BLHeli32 only
DSHOT_CMD_LED3_OFF: 29, // BLHeli32 only
DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF: 30, // KISS audio Stream mode on/Off
DSHOT_CMD_SILENT_MODE_ON_OFF: 31, // KISS silent Mode on/Off
DSHOT_CMD_MAX: 47,
};
DshotCommand.dshotCommandType_e = {
DSHOT_CMD_TYPE_INLINE: 0, // dshot commands sent inline with motor signal (motors must be enabled)
DSHOT_CMD_TYPE_BLOCKING: 1, // dshot commands sent in blocking method (motors must be disabled)
};

View file

@ -0,0 +1,95 @@
'use strict';
class EscProtocols
{
static get PROTOCOL_PWM() { return "PWM"; }
static get PROTOCOL_ONESHOT125() { return "ONESHOT125"; }
static get PROTOCOL_ONESHOT42() { return "ONESHOT42"; }
static get PROTOCOL_MULTISHOT() { return "MULTISHOT"; }
static get PROTOCOL_BRUSHED() { return "BRUSHED"; }
static get PROTOCOL_DSHOT150() { return "DSHOT150"; }
static get PROTOCOL_DSHOT300() { return "DSHOT300"; }
static get PROTOCOL_DSHOT600() { return "DSHOT600"; }
static get PROTOCOL_DSHOT1200() { return "DSHOT1200"; }
static get PROTOCOL_PROSHOT1000() { return "PROSHOT1000"; }
static get PROTOCOL_DISABLED() { return "DISABLED"; }
static get DSHOT_PROTOCOLS_SET()
{
return [
EscProtocols.PROTOCOL_DSHOT150,
EscProtocols.PROTOCOL_DSHOT300,
EscProtocols.PROTOCOL_DSHOT600,
EscProtocols.PROTOCOL_DSHOT1200,
EscProtocols.PROTOCOL_PROSHOT1000,
];
}
static GetProtocolName(apiVersion, protocolIndex)
{
const escProtocols = EscProtocols.GetAvailableProtocols(apiVersion);
return escProtocols[protocolIndex];
}
static IsProtocolDshot(apiVersion, protocolIndex)
{
const protocolName = EscProtocols.GetProtocolName(apiVersion, protocolIndex);
return EscProtocols.DSHOT_PROTOCOLS_SET.includes(protocolName);
}
static GetAvailableProtocols(apiVersion)
{
const escProtocols = [
EscProtocols.PROTOCOL_PWM,
EscProtocols.PROTOCOL_ONESHOT125,
EscProtocols.PROTOCOL_ONESHOT42,
EscProtocols.PROTOCOL_MULTISHOT,
];
if (semver.gte(apiVersion, "1.20.0")) {
escProtocols.push(EscProtocols.PROTOCOL_BRUSHED);
}
if (semver.gte(apiVersion, API_VERSION_1_31)) {
escProtocols.push(EscProtocols.PROTOCOL_DSHOT150);
escProtocols.push(EscProtocols.PROTOCOL_DSHOT300);
escProtocols.push(EscProtocols.PROTOCOL_DSHOT600);
if (semver.lt(apiVersion, API_VERSION_1_42)) {
escProtocols.push(EscProtocols.PROTOCOL_DSHOT1200);
}
}
if (semver.gte(apiVersion, API_VERSION_1_36)) {
escProtocols.push(EscProtocols.PROTOCOL_PROSHOT1000);
}
if (semver.gte(apiVersion, API_VERSION_1_43)) {
escProtocols.push(EscProtocols.PROTOCOL_DISABLED);
}
return escProtocols;
}
static ReorderPwmProtocols(apiVersion, protocolIndex)
{
let result = protocolIndex;
if (semver.lt(apiVersion, "1.26.0")) {
switch (protocolIndex) {
case 5:
result = 7;
break;
case 7:
result = 5;
break;
default:
break;
}
}
return result;
}
}

View file

@ -42,6 +42,7 @@
<link type="text/css" rel="stylesheet" href="./components/MotorOutputReordering/Styles.css" media="all"/> <link type="text/css" rel="stylesheet" href="./components/MotorOutputReordering/Styles.css" media="all"/>
<link type="text/css" rel="stylesheet" href="./css/select2_custom.css" media="all"/> <link type="text/css" rel="stylesheet" href="./css/select2_custom.css" media="all"/>
<link type="text/css" rel="stylesheet" href="./node_modules/select2/dist/css/select2.min.css" media="all"/> <link type="text/css" rel="stylesheet" href="./node_modules/select2/dist/css/select2.min.css" media="all"/>
<link type="text/css" rel="stylesheet" href="./components/EscDshotDirection/Styles.css" media="all"/>
<link type="text/css" rel="stylesheet" href="./css/dark-theme.css" media="all" disabled/> <link type="text/css" rel="stylesheet" href="./css/dark-theme.css" media="all" disabled/>
@ -72,6 +73,7 @@
<script type="text/javascript" src="./js/utils/common.js"></script> <script type="text/javascript" src="./js/utils/common.js"></script>
<script type="text/javascript" src="./js/utils/css.js"></script> <script type="text/javascript" src="./js/utils/css.js"></script>
<script type="text/javascript" src="./js/utils/window_watchers.js"></script> <script type="text/javascript" src="./js/utils/window_watchers.js"></script>
<script type="text/javascript" src="./js/utils/CommonUtils.js"></script>
<script type="text/javascript" src="./js/injected_methods.js"></script> <script type="text/javascript" src="./js/injected_methods.js"></script>
<script type="text/javascript" src="./js/ConfigStorage.js"></script> <script type="text/javascript" src="./js/ConfigStorage.js"></script>
<script type="text/javascript" src="./js/data_storage.js"></script> <script type="text/javascript" src="./js/data_storage.js"></script>
@ -142,6 +144,11 @@
<script type="text/javascript" src="./components/MotorOutputReordering/MotorOutputReorderingCanvas.js"></script> <script type="text/javascript" src="./components/MotorOutputReordering/MotorOutputReorderingCanvas.js"></script>
<script type="text/javascript" src="./components/MotorOutputReordering/MotorOutputReorderingConfig.js"></script> <script type="text/javascript" src="./components/MotorOutputReordering/MotorOutputReorderingConfig.js"></script>
<script type="text/javascript" src="./node_modules/select2/dist/js/select2.min.js"></script> <script type="text/javascript" src="./node_modules/select2/dist/js/select2.min.js"></script>
<script type="text/javascript" src="./js/utils/EscProtocols.js"></script>
<script type="text/javascript" src="./js/utils/DshotCommand.js"></script>
<script type="text/javascript" src="./components/EscDshotDirection/EscDshotDirectionComponent.js"></script>
<script type="text/javascript" src="./components/EscDshotDirection/EscDshotDirectionMotorDriver.js"></script>
<script type="text/javascript" src="./components/EscDshotDirection/EscDshotCommandQueue.js"></script>
<title></title> <title></title>
</head> </head>

View file

@ -101,6 +101,7 @@
</div> </div>
<div class="power_info"> <div class="power_info">
<a href="#" id="motorOutputReorderDialogOpen" class="regular-button" i18n="motorOutputReorderDialogOpen"></a> <a href="#" id="motorOutputReorderDialogOpen" class="regular-button" i18n="motorOutputReorderDialogOpen"></a>
<a href="#" id="escDshotDirectionDialog-Open" class="regular-button" i18n="escDshotDirectionDialog-Open"></a>
<span i18n="motorsVoltage" class="power_text"></span><span class="motors-bat-voltage power_value"></span> <span i18n="motorsVoltage" class="power_text"></span><span class="motors-bat-voltage power_value"></span>
<span i18n="motorsADrawing" class="power_text"></span><span class="motors-bat-mah-drawing power_value"></span> <span i18n="motorsADrawing" class="power_text"></span><span class="motors-bat-mah-drawing power_value"></span>
<span i18n="motorsmAhDrawn" class="power_text"></span><span class="motors-bat-mah-drawn power_value"></span> <span i18n="motorsmAhDrawn" class="power_text"></span><span class="motors-bat-mah-drawn power_value"></span>
@ -200,4 +201,13 @@
</div> </div>
</div> </div>
</dialog> </dialog>
</div>
<dialog id="escDshotDirectionDialog">
<div id="escDshotDirectionDialog-ContentWrapper">
<div id="escDshotDirectionDialog-Content">
</div>
<a href="#" id="escDshotDirectionDialog-closebtn" class="regular-button right" i18n="close"></a>
</div>
</dialog>
</div>