Skip to content

Ports: an output pad for a UART's TX or RX - #2833

Open
MrScothh wants to merge 4 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:feature/serial-tx-pad
Open

MrScothh wants to merge 4 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:feature/serial-tx-pad

Conversation

@MrScothh

@MrScothh MrScothh commented Oct 10, 2026 •

Copy link
Copy Markdown

Configurator side of iNavFlight/inav#12143, which lets a UART's TX or RX move to a motor or servo pad. It replaces #2814, the ESC connector option of iNavFlight/inav#12087.

The Ports tab gets a Pins column at the end of the table, only on boards whose firmware lists at least one pad. Firmware without the feature answers MSP2_INAV_SERIAL_PADS as unsupported, and the column stays hidden. Each UART that can move shows a TX and/or RX choice: "Own pin" or the S pads it can reach.

  • TX and RX sit side by side when the table still fits on one line, and one above the other when it would wrap. This is measured, not tied to a window width, so a longer translation moves the threshold.
  • A pad that carries a motor or servo of the mixer gets a red note such as "servo 1: no arming", because the firmware refuses to arm with it. A pad used by the LED strip says "replaces LED strip".
  • A pad chosen for one pin is greyed out in the others.
  • Save sends MSP2_INAV_SET_SERIAL_PAD for each changed choice before the port configuration. If the firmware, or the parse-failure guard, refuses one, the choices already sent are written back to what the tab loaded, and the save stops with a message in the log instead of rebooting as if it had been stored. The new write is paired with its read for the parse-failure guard (js/msp.js).

1920x1080 at 100 %, TX and RX side by side:

Ports tab, 1920x1080

1366x768, one above the other:

Ports tab, 1366x768

The screenshots are from the SITL, which has no output pads, with MSP2_INAV_SERIAL_PADS answered by the list a TBS Lucid H7 Wing sent on my bench. The firmware side was tested on that board (see iNavFlight/inav#12143).

yarn test: all pass except unused MZTC writes remain unused and conservatively blocked, which fails on Windows on maintenance-10.x too: it excludes msp/MSPCodes.js with a forward slash, and readdirSync returns msp\MSPCodes.js there.

Test-merged with the open PRs that touch the same files: it conflicts with #2814, which this replaces, and in js/msp/MSPHelper.js with #2744 and #2832, where both sides add a case at the end of the same switch.

Firmware with USE_SERIAL_PADS lists, for each UART, the output pads its TX
or RX can move to (MSP2_INAV_SERIAL_PADS). The Ports tab shows them in a
Pins column at the end of the table: TX and RX side by side where the table
still fits on one line, one above the other where it would wrap. Firmware
without the feature answers unsupported and the column stays hidden.

A pad that carries a motor or servo of the mixer gets a red note, because
the board will not arm with it; a pad used by the LED strip says it replaces
it. Saving sends MSP2_INAV_SET_SERIAL_PAD for each changed choice before the
port configuration, and the write is paired with its read for the
parse-failure guard.
@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

Configure UART TX/RX on output pads from the Ports tab

✨ Enhancement 🕐 20-40 Minutes

Grey Divider

AI Description

• Add a Pins column for firmware that offers UART-compatible output pads.
• Show pad conflicts and adapt TX/RX choices to available table space.
• Save changed pad choices before port settings, stopping if a choice is refused.
Diagram

graph TD
  FW["Firmware pad list"] --> State["FC pad state"] --> UI["Pins selectors"] --> Writes["Pad writes"] --> Check{"Accepted?"} -->|Yes| Config["Port save"] --> Reboot["Reboot"]
  Check -->|No| Log["Log refusal"]
Loading
High-Level Assessment

Keep the dedicated pad read/write commands and Ports-tab controls. The existing serial-port configuration does not carry pad assignments, and hiding the controls when firmware reports no pads preserves compatibility without introducing a broader resource-allocation interface.

Files changed (8) +275 / -4

Enhancement (7) +242 / -4
fc.jsStore available UART pad assignments +7/-0

Store available UART pad assignments

• Initializes SERIAL_PADS as empty flight-controller state. An empty list also lets the Ports tab hide the feature on unsupported firmware.

js/fc.js

msp.jsPair pad writes with their source read +1/-0

Pair pad writes with their source read

• Maps the serial-pad write command to its read command so an unreadable pad response blocks dependent writes through the existing parse-failure guard.

js/msp.js

MSPCodes.jsRegister serial-pad MSP commands +2/-0

Register serial-pad MSP commands

• Adds the firmware command identifiers for reading available serial pads and setting a UART pin's pad.

js/msp/MSPCodes.js

MSPHelper.jsDecode firmware serial-pad options +20/-0

Decode firmware serial-pad options

• Populates FC.SERIAL_PADS from six-byte option records, including current selection and pad usage. Clears the list when the read command is unsupported and recognizes write responses.

js/msp/MSPHelper.js

ports.cssStyle conditional, responsive pin controls +64/-1

Style conditional, responsive pin controls

• Hides the column when no pads are available and styles side-by-side or stacked TX/RX choices. Aligns controls and highlights pad-usage notes.

src/css/tabs/ports.css

ports.htmlAdd Pins cells to the Ports table +2/-0

Add Pins cells to the Ports table

• Adds a localized Pins header and a cell in each port-row template for the dynamically generated selectors.

tabs/ports.html

ports.jsLoad, present, and save UART pad choices +146/-3

Load, present, and save UART pad choices

• Reads pad options before rendering, builds TX/RX selectors, disables pads selected elsewhere, and displays usage warnings. Measures table width to arrange selectors, then sends changed pad choices sequentially before saving ports; a refused response stops that save chain.

tabs/ports.js

Other (1) +33 / -0
messages.jsonAdd Pins labels and safety messages +33/-0

Add Pins labels and safety messages

• Adds English text for the column, TX/RX choices, motor and servo arming warnings, LED-strip replacement, help, and refused saves.

locale/en/messages.json

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

qodo-free-for-open-source-projects Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Moving a pad between pins can fail 🐞 Bug ≡ Correctness
Description
savePins() sends changes in table order instead of releasing currently assigned pads before
assigning them elsewhere. When a user moves a pad from RX to TX in the same row, TX is written first
while RX still owns the pad on the controller, so the controller can refuse a choice that the
updated form permits.
Code

tabs/ports.js[R440-446]

+            (function sendNext() {
+                const change = changes.shift();
+                if (!change) {
+                    next();
+                    return;
+                }
+                MSP.send_message(MSPCodes.MSP2_INAV_SET_SERIAL_PAD, change, false, function (result) {
Evidence
The form allows a pad once another selector changes away from it, but the save queue preserves DOM
order. TX is constructed before RX, so moving an RX pad to TX sends the assignment before the
release.

tabs/ports.js[206-224]
tabs/ports.js[270-279]
tabs/ports.js[432-452]

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

## Issue description
A pad reassignment can be sent before the write that frees that pad, causing an otherwise valid choice to be refused.
## Fix Focus Areas
- tabs/ports.js[432-453]
## Recommended Fix
Order changed selections so writes selecting Own pin release their current pads before writes assigning pads. Preserve refusal handling and stop before saving serial ports if either phase fails.

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



Remediation recommended

2. A blocked pin write leaves Save stalled ✓ Resolved
Description
savePins() checks for refusal only inside the MSP.send_message() callback and ignores its
immediate return value. If the parse-failure guard refuses the pin write, send_message() returns
false without calling back, so neither the refusal message nor the remaining save steps run.
Code

tabs/ports.js[R446-449]

+                MSP.send_message(MSPCodes.MSP2_INAV_SET_SERIAL_PAD, change, false, function (result) {
+                    // the FC answers a pad it cannot use with an error, which still completes the request
+                    if (result === false || MSP.unsupported) {
+                        GUI.log(i18n.getMessage('portsPinRefused'));
Evidence
The new write is paired with the pad read for the parse-failure guard. That guard can return before
queuing a request or invoking its callback, while this save chain advances only from the callback.

js/msp.js[27-40]
js/msp.js[153-183]
js/msp.js[405-410]
tabs/ports.js[440-453]

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 MSP write guard can refuse a pin write synchronously without invoking its callback, leaving the save chain stalled.
## Fix Focus Areas
- tabs/ports.js[440-453]
## Recommended Fix
Check the return value of `MSP.send_message()` as well as its response callback. Report an immediate refusal and terminate the save explicitly without attempting EEPROM write or reboot.

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


3. A refused choice leaves earlier pins changed ✓ Resolved
Description
savePins() sends each changed choice immediately but returns on a later refusal without reversing
the choices already accepted. If a save contains multiple pin changes and one is refused, earlier
changes remain applied on the controller while the EEPROM write and reboot are skipped and the log
says nothing was saved.
Code

tabs/ports.js[R448-452]

+                    if (result === false || MSP.unsupported) {
+                        GUI.log(i18n.getMessage('portsPinRefused'));
+                        return;
+                    }
+                    sendNext();
Evidence
The queue removes and sends changes one at a time; the refusal branch stops it without rollback.
EEPROM persistence is reached only through the success continuation, and the refusal message
unconditionally says nothing was saved.

tabs/ports.js[432-458]
locale/en/messages.json[5297-5300]

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

## Issue description
A failure after one or more accepted pin writes leaves a partially applied runtime configuration even though persistence is skipped.
## Fix Focus Areas
- tabs/ports.js[432-453]
- locale/en/messages.json[5297-5300]
## Recommended Fix
Track accepted writes and handle later failures explicitly, either by restoring their previous selections or by reporting that earlier changes may already be active. Do not claim that nothing was changed unless the save path guarantees it.

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


Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread tabs/ports.js Outdated
Comment thread tabs/ports.js Outdated
Comment thread tabs/ports.js Outdated
The pad list is read in a named step instead of a sixth nested callback,
and arrangePins(), which uses nothing of the tab's scope, moves to the
module.
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Configurator test build ready — commit 00fdbc9

Download build artifacts for PR #2833

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.

A pin choice refused after others went through left those in the FC's RAM,
where a save from another tab would have kept them. Every changed pin is
now written back to the value the tab loaded, and the log says whether that
worked. A write the parse-failure guard refuses ends the save the same way,
as send_message() never calls back for it.
It uses nothing of the tab's scope; SonarCloud's note on aa47e89.
@sonarqubecloud

Copy link
Copy Markdown

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