Skip to content

Mixer wizard: check each motor's spin direction after locating it - #2798

Open
Raffi1202 wants to merge 2 commits into
iNavFlight:maintenance-11.xfrom
Raffi1202:motor-direction-wizard-step
Open

Raffi1202 wants to merge 2 commits into
iNavFlight:maintenance-11.xfrom
Raffi1202:motor-direction-wizard-step

Conversation

@Raffi1202

Copy link
Copy Markdown

Replaces #2794 after the review on iNavFlight/inav#12011: no separate dialog, no new MSP. Needs iNavFlight/inav#12022 (dshot_reversed_motors).

What it does

The Mixer tab motor wizard (#2580) gets one more phase after every motor has been located. Each output is spun for two seconds in turn, its position button turns orange, and the wizard asks whether the motor turns the way the arrow at that position shows.

  • Turns correctly moves on to the next motor.
  • Reverse flips that output's bit in dshot_reversed_motors via the settings MSP, waits 300 ms for the firmware to send the direction commands, and spins the motor again so the result is visible.
  • Spin again repeats the two-second spin. Skip direction check and Emergency Stop work as expected.

The phase only appears when motor_pwm_protocol is a DShot variant and the firmware answers for dshot_reversed_motors; on PWM ESCs or older firmware the wizard ends after the positions as before.

The setting lives in FC RAM until the mixer is saved, same as the motor mapping the wizard produces. The hint text says so.

Tested

  • node --check, locale JSON valid.
  • Not exercised against hardware or SITL: SITL has no DShot, so the phase does not show there. The position phase is unchanged. Needs a bench run with props off on a DShot quad.

DShot only. After the positions are known every output is spun for two
seconds and the user confirms the direction against the arrows or reverses
it, which flips that output's bit in dshot_reversed_motors. The firmware
re-sends the direction commands on its own; nothing is written to the ESC.
Hidden on PWM ESCs and on firmware without the setting.
@sonarqubecloud

Copy link
Copy Markdown

@Raffi1202
Raffi1202 marked this pull request as ready for review September 23, 2026 14:16
@qodo-code-review

Copy link
Copy Markdown
Contributor

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add DShot motor direction checks to the mixer wizard

✨ Enhancement 🕐 20-40 Minutes

Grey Divider

AI Description

• Adds DShot-only direction verification after the mixer wizard locates every motor.
• Reverses selected outputs through dshot_reversed_motors and re-spins them for confirmation.
• Preserves existing completion behavior for PWM ESCs and unsupported firmware.
Diagram

graph TD
  A["Locate motors"] --> B{"DShot supported?"}
  B -- No --> G["Complete wizard"]
  B -- Yes --> C["Spin motor"] --> D{"User choice"}
  D -- Correct --> E["Next motor"] --> H{"More motors?"}
  D -- Reverse --> F["Flip reverse bit"] --> C
  D -- Again --> C
  D -- Skip --> G
  H -- Yes --> C
  H -- No --> G
Loading
High-Level Assessment

The current approach is appropriate: it extends the existing wizard, uses the established settings MSP, and lets firmware issue the DShot direction commands. A separate dialog or dedicated MSP would add navigation and protocol complexity without improving the workflow.

Files changed (5) +172 / -5

Enhancement (3) +136 / -5
mixer.cssHighlight the motor under direction review +7/-0

Highlight the motor under direction review

• Adds an orange pulsing state to the mapped position button for the motor currently being checked.

src/css/tabs/mixer.css

mixer.htmlAdd the direction-check wizard phase +19/-0

Add the direction-check wizard phase

• Introduces a DShot direction confirmation section with repeat, accept, reverse, skip, and emergency-stop controls. User-facing buttons include fallback text.

tabs/mixer.html

mixer.jsImplement DShot motor direction verification +110/-5

Implement DShot motor direction verification

• Detects compatible DShot firmware, spins each mapped motor for confirmation, and toggles its 'dshot_reversed_motors' bit when requested. It also manages apply delays, repeated spins, progression, skipping, emergency stops, and modal cleanup.

tabs/mixer.js

Other (2) +36 / -0
messages.jsonAdd German direction-check translations +18/-0

Add German direction-check translations

• Adds German prompts, guidance, and action labels for confirming, repeating, reversing, or skipping motor direction checks.

locale/de/messages.json

messages.jsonAdd English direction-check copy +18/-0

Add English direction-check copy

• Defines the English direction question, persistence guidance, and action labels. Button text also serves as an inline fallback when localization is unavailable.

locale/en/messages.json

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. A stale reversal spins the wrong motor 🐞 Bug ☼ Reliability
Description
The Reverse handler captures directionMotor and schedules a later spin without verifying that the
wizard is still checking the same motor and run. If the user advances, clicks Reverse repeatedly, or
closes and quickly reopens before setSetting completes, the callback can interrupt the current
spin and energize an earlier output.
Code

tabs/mixer.js[R899-903]

+            mspHelper.setSetting('dshot_reversed_motors', wizardState.reversedMask, function() {
+                // Give the firmware time to notice the change and get the command frames out
+                wizardState.directionTimer = setTimeout(function() {
+                    wizardState.directionTimer = null;
+                    spinMotorForDirection(motorIndex);
Evidence
The callback uses the captured motorIndex, while the OK handler can increment directionMotor and
immediately start another output. spinMotorForDirection only checks whether the wizard is active,
and setSetting completes asynchronously, so it does not reject callbacks belonging to an earlier
motor or an earlier modal run.

tabs/mixer.js[849-858]
tabs/mixer.js[885-903]
tabs/mixer.js[1015-1017]
js/msp/MSPHelper.js[3802-3809]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Asynchronous reversal callbacks can outlive the motor or wizard run that created them and subsequently spin the wrong output.

## Fix Focus Areas
- tabs/mixer.js[895-905]
- tabs/mixer.js[1015-1017]

## Recommended Fix
Track a wizard-run generation and the expected direction motor when starting the setting operation. Before scheduling or starting the follow-up spin, verify that the wizard is active, the generation still matches, and `directionMotor` still equals the captured motor; also disable or serialize direction actions while a reversal is pending.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Failed reversals look successful 🐞 Bug ☼ Reliability
Description
The Reverse handler updates reversedMask and schedules the verification spin from setSetting's
callback even when the setting write fails. A transport or encoding failure therefore leaves the
controller unchanged while the wizard continues without an error and bases later full-mask writes on
a value that was never applied.
Code

tabs/mixer.js[R898-901]

+            wizardState.reversedMask ^= (1 << motorIndex);
+            mspHelper.setSetting('dshot_reversed_motors', wizardState.reversedMask, function() {
+                // Give the firmware time to notice the change and get the command frames out
+                wizardState.directionTimer = setTimeout(function() {
Evidence
setSetting invokes its callback from the success path, but its catch handler also invokes the same
callback after encoding or MSP failures. The new handler cannot distinguish those outcomes, and it
mutates the local mask before initiating the asynchronous write.

tabs/mixer.js[895-905]
js/msp/MSPHelper.js[3802-3809]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The direction wizard proceeds as though a reversal succeeded even when the setting write failed, leaving its local mask inconsistent with the flight controller.

## Fix Focus Areas
- tabs/mixer.js[895-905]
- js/msp/MSPHelper.js[3802-3809]

## Recommended Fix
Use a setting-write API that exposes success or failure, and only commit `reversedMask` and schedule the verification spin after confirmed success. On failure, restore the previous mask, keep the current motor selected, show an actionable error, and leave the motor stopped.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Slow reads skip direction checks 🐞 Bug ≡ Correctness
Description
The modal initializes directionAvailable to false and updates it only after two asynchronous
setting reads, while the position-completion path immediately branches on its current value. If all
positions are selected before those reads resolve, a supported DShot controller goes directly to
completion and the later result cannot enter the omitted phase.
Code

tabs/mixer.js[R999-1002]

+            Promise.all([
+                mspHelper.getSetting('motor_pwm_protocol'),
+                mspHelper.getSetting('dshot_reversed_motors'),
+            ]).then(function([protocol, reversed]) {
Evidence
The last-position handler only starts direction checking when directionAvailable is already true.
Modal opening resets that flag before launching Promise.all, and its completion handler merely
changes the flag rather than revisiting an already completed wizard.

tabs/mixer.js[808-815]
tabs/mixer.js[995-1011]
js/msp/MSPHelper.js[3706-3749]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Position identification can finish before asynchronous DShot capability detection, causing a supported controller to skip the direction phase.

## Fix Focus Areas
- tabs/mixer.js[808-815]
- tabs/mixer.js[995-1011]

## Recommended Fix
Store the capability lookup Promise for the current wizard run and await it before choosing between `startDirectionCheck` and `wizardComplete`. Disable Start until detection settles or show a pending state at position completion, while preserving the fallback to completion when either setting is unavailable.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 15 rules
Review mode: ⚖️ Balanced: This adds hardware-control wizard logic, firmware setting detection, asynchronous timing, and safety-stop behavior across UI and runtime paths, making a careful single-pass review warranted.

Grey Divider

Tip of the day
💡 Did you know, you can keep summaries lean with Finding overflow, which tucks the rest behind 'View more'

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread tabs/mixer.js
Comment on lines +899 to +903
mspHelper.setSetting('dshot_reversed_motors', wizardState.reversedMask, function() {
// Give the firmware time to notice the change and get the command frames out
wizardState.directionTimer = setTimeout(function() {
wizardState.directionTimer = null;
spinMotorForDirection(motorIndex);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. A stale reversal spins the wrong motor 🐞 Bug ☼ Reliability

The Reverse handler captures directionMotor and schedules a later spin without verifying that the
wizard is still checking the same motor and run. If the user advances, clicks Reverse repeatedly, or
closes and quickly reopens before setSetting completes, the callback can interrupt the current
spin and energize an earlier output.
Agent Prompt
## Issue description
Asynchronous reversal callbacks can outlive the motor or wizard run that created them and subsequently spin the wrong output.

## Fix Focus Areas
- tabs/mixer.js[895-905]
- tabs/mixer.js[1015-1017]

## Recommended Fix
Track a wizard-run generation and the expected direction motor when starting the setting operation. Before scheduling or starting the follow-up spin, verify that the wizard is active, the generation still matches, and `directionMotor` still equals the captured motor; also disable or serialize direction actions while a reversal is pending.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread tabs/mixer.js
Comment on lines +898 to +901
wizardState.reversedMask ^= (1 << motorIndex);
mspHelper.setSetting('dshot_reversed_motors', wizardState.reversedMask, function() {
// Give the firmware time to notice the change and get the command frames out
wizardState.directionTimer = setTimeout(function() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Failed reversals look successful 🐞 Bug ☼ Reliability

The Reverse handler updates reversedMask and schedules the verification spin from setSetting's
callback even when the setting write fails. A transport or encoding failure therefore leaves the
controller unchanged while the wizard continues without an error and bases later full-mask writes on
a value that was never applied.
Agent Prompt
## Issue description
The direction wizard proceeds as though a reversal succeeded even when the setting write failed, leaving its local mask inconsistent with the flight controller.

## Fix Focus Areas
- tabs/mixer.js[895-905]
- js/msp/MSPHelper.js[3802-3809]

## Recommended Fix
Use a setting-write API that exposes success or failure, and only commit `reversedMask` and schedule the verification spin after confirmed success. On failure, restore the previous mask, keep the current motor selected, show an actionable error, and leave the motor stopped.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread tabs/mixer.js
Comment on lines +999 to +1002
Promise.all([
mspHelper.getSetting('motor_pwm_protocol'),
mspHelper.getSetting('dshot_reversed_motors'),
]).then(function([protocol, reversed]) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Slow reads skip direction checks 🐞 Bug ≡ Correctness

The modal initializes directionAvailable to false and updates it only after two asynchronous
setting reads, while the position-completion path immediately branches on its current value. If all
positions are selected before those reads resolve, a supported DShot controller goes directly to
completion and the later result cannot enter the omitted phase.
Agent Prompt
## Issue description
Position identification can finish before asynchronous DShot capability detection, causing a supported controller to skip the direction phase.

## Fix Focus Areas
- tabs/mixer.js[808-815]
- tabs/mixer.js[995-1011]

## Recommended Fix
Store the capability lookup Promise for the current wizard run and await it before choosing between `startDirectionCheck` and `wizardComplete`. Disable Start until detection settles or show a pending state at position completion, while preserving the fallback to completion when either setting is unavailable.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@github-actions

Copy link
Copy Markdown

Configurator test build ready — commit 1b0a155

Download build artifacts for PR #2798

Available platforms (scroll to the Artifacts section at the bottom of the run page):

  • Windows x64 (ZIP, MSI) and x32 (ZIP, MSI)
  • macOS arm64 (ZIP, DMG) and x64 (ZIP, DMG)
  • Linux x64 (DEB, RPM, ZIP) and aarch64 (DEB, RPM, ZIP)

A GitHub login is required to download artifacts. Build is for testing only.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant