Skip to content

motor_srxl2: link an ESC powered together with the board - #12085

Merged
sensei-hacker merged 4 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:fix/srxl2-link-at-power-up
Oct 11, 2026
Merged

sensei-hacker merged 4 commits into
iNavFlight:maintenance-10.xfrom
MrScothh:fix/srxl2-link-at-power-up

Conversation

@MrScothh

@MrScothh MrScothh commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

An Avian powered from the same battery as the flight controller never linked: arming stayed blocked with HWFAIL until the battery was unplugged and plugged in again. That is the power-up case @UltraFly reported in #11184.

This builds on #12084 (ready delay), now merged.

Why

Measured on a Lucid H7 Wing with an Avian 70A, both on one bench supply, times from the board's reset:

What When
The Avian's handshakes (unit ID 0, as the specification says: 50 ms apart for its first 200 ms) 42.5, 92.6, 142.6, 192.7, 242.7 ms
The SRXL2 port opens 325 ms, after the USB init's two waits (200 ms disconnect pulse, 100 ms on H7)
The driver first runs, from its task about 5.8 s

An Avian left unanswered never speaks again. After its handshakes I sent it 17 kinds of frame: handshakes to 0x40 with baud 0 and 1, from source IDs 0x31, 0x21, 0x10 and 0x30, to 0x00, broadcasts, control data with telemetry requests with and without a handshake before them, all of it again at 400000 baud, and a long low. Not one byte came back.

Answering it early is not enough either. It ignores any answer while it is still sending its handshakes, with the answer 0.17, 1, 3, 5 or 10 ms after each one, and it only links once they are over, if control frames are still coming then:

Early answer Result
One answer, then nothing until the task its remaining handshakes, then silence: never links
An answer to every handshake, frames between no reply before the task; links there
One answer, then control frames first telemetry reply at 349 ms

A linked Avian that loses its frames announces itself again, and answers the task: over a board restart of about 6 s, and through a firmware flash with the battery connected.

The change

  • fc_init.c: timerInit() and serialInit() move ahead of the USB init. Nothing between the two positions uses them or is affected by them; that is the only change for other motor protocols.
  • A board set to SRXL2 then opens its ports and runs the driver, at the task's 5 ms tick, until every ESC has answered a telemetry request. It stops waiting for an ESC that has not spoken 200 ms after reset (or 100 ms after it started listening, if later), and leaves at 700 ms whatever happens.
  • Init's fixed waits (the 500 ms before hardware init and the 5 x 100 ms LED blink) keep the ESCs answered instead of sleeping, so an ESC powered a little after the board links too. If one starts linking during a wait, that wait lasts until it replies, up to 700 ms more; an ESC heard before the wait does not stretch it.
  • The rest of init still leaves the ESC without frames for about 4 s. The Avian announces itself through them and the task links it again, but one still starting begins its startup anew (measured below). The ready delay of srxl2: wait for the Avian's startup tones before allowing arming #12084 restarts at that announcement, and also when the driver itself has not fed the ESC for 150 ms, so it does not depend on an announcement surviving in the UART's buffer through those 4 s. With one battery powering both, arming is allowed 10 s after the board has started, about 16 s after the battery is connected on a Lucid H7.
  • The arming check asks for a recent telemetry reply rather than any frame: an ESC that has lost its frames still sends handshakes, and those used to count.
  • srxl2MotorInitialize() does nothing the second time, so the existing call sites keep the link.
  • The keepalive answer is not sent in the first 500 ms of a link: answered again during its handshakes, the Avian did not link when they ended.
  • Targets built with BRUSHED_MOTORS start as before: they force brushed motors, so SRXL2 never drives them.
  • The docs and three comments said an ESC is only found by its announcement and never after a restart. That second part was wrong, and so was what I wrote in Add support for SRXL2 as ESC protocol #11184 about it.

Related

Cost

Boot, on SRXL2 boards only: the first wait ends when the ESC replies, 349 ms after reset on the bench and never later than 700 ms; 110 ms after a restart with a linked ESC; about 185 ms when no ESC is powered. The later waits keep their length, unless an ESC starts linking during one: then that wait lasts up to 700 ms more.

Against #12084 (ready delay):

Target Flash RAM ITCM
MATEKF405SE +792 B (78.93%) -8 B
MATEKF722SE +8 B (98.43%) 0 0
KAKUTEF7 +8 B 0 0 (99.07%)
KAKUTEF7HDV +16 B 0 0 (99.27%)
TBS_LUCID_H7_WING +712 B +32 B +48 B
BLUEBERRYF435WING +888 B 0 (90.95%)

Tested

  • On the Lucid H7 Wing with an Avian 70A: both powered together (with the USB unplugged, the board read afterwards), a restart and a firmware flash with the ESC powered and linked, and SRXL2 set with the ESC unpowered. Builds: F405, F722, F745 (KAKUTEF7, KAKUTEF7HDV), H743, AT32F435, SITL, and ALIENFLIGHTF4 for the brushed case.

  • The later waits with an ESP32 that plays the Avian's power-up as measured above, powered at steps after a restart:

    ESC powered after the restart Linked
    0, 100, 150 ms yes
    200, 250 ms no: the USB init's waits, where nothing answers
    300, 600, 800, 950 ms, 1.1, 1.2, 1.3 s yes (all no before this revision)
    1.5, 2 s no: the rest of init blocks until the task
    after init yes
  • The Avian driving its motor (no propeller, 6S battery) through the 4 s without frames, 1.4 to 5.8 s after the ESC's power-up, as the start-up leaves it. A bench build logged the ESC's telemetry, which reports the throttle it takes and its rpm. For the first three rows it silenced the driver, which sent nothing and dropped what arrived; for the last one it stopped the driver's task, as init does:

    Throttle raised at Motor
    6.86 s, when the old check allowed arming never, over 12 s: the ESC reported 12% and stood still
    9.5 s turned at 12.68 s
    12.3 s turned at once
    when arming became allowed with srxl2: wait for the Avian's startup tones before allowing arming #12084, 15.80 s turned at once, 16.03 s
  • By @UltraFly on a NEXUS XR (F722) with an Avian 100A, together with srxl2: wait for the Avian's startup tones before allowing arming #12084: the board powered by the ESC's BEC, both starting together. The ESC links with its telemetry and the motor responds.

Not tested, testing wanted

  • The Avian with its motor powered at the very moment the board resets. The bench build above stood in for it: the ESC was powered while the board ran, and the driver was stopped as through the start-up.
  • A board restarted within about 2 s of the ESC's power-up, while the ESC is still starting: over MSP, that left the ESC deaf once in three tries, until its battery was plugged in again.
  • F405 and AT32 boards with a Smart ESC. The code is the same, but their boot timing is not, and nothing here was run on them.
  • An ESC that asks for 400000 baud. The Avian asks for 115200 only, so the baud change the early link can now reach was not exercised.
  • Twin setups, and Smart ESCs other than the Avian 70A and 100A.

An Avian powered with the board announces itself 43 to about 345 ms after
the board's reset, links only once those handshakes are over and only if
control frames are still coming, and never links if left unanswered. The
ports opened at 325 ms, after the USB wait, and the driver first ran from
the task at about 6 s, so a battery connected to board and ESC together
left the motor dead until it was unplugged and plugged in again.

Timer and serial init move ahead of the USB wait, and a board set to SRXL2
opens its ports there and runs the driver until every ESC has replied to a
telemetry request (349 ms on the bench), gives up on one that has not
spoken by 200 ms, and stops at 700 ms whatever happens. Init's fixed waits
keep the ESCs answered instead of sleeping, so one powered a little after
the board links too. Other motor protocols only see the two calls move.
The keepalive answer waits 500 ms into a link: answered again during its
handshakes, the Avian did not link when they ended.

The rest of init leaves the ESC without frames for about 4 s. The Avian
announces itself through them and the task links it again; one still
starting begins its startup anew, so the arming check counts its
readiness again from when the frames return. The check also asks for a
recent telemetry reply rather than any frame, since an ESC that has lost
its frames still sends handshakes.

The docs and two comments said a board restarting under a powered ESC
never gets it back: a linked Avian that loses its frames announces itself
again, and the task links it.
@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

Link SRXL2 ESCs during flight-controller power-up

🐞 Bug fix 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Start SRXL2 linking before USB initialization can miss an ESC’s power-up handshakes.
• Keep ESCs serviced during startup waits and require recent telemetry before arming.
• Restart the readiness delay after lost frames or renewed ESC announcements.
Diagram

graph TD
    Init["FC startup"] --> Ports["Early serial ports"] --> Link["Boot link loop"] --> Waits["Serviced waits"] --> Task["Motor task"] --> Gate{"Reply and ready?"}
    Link -->|"control frames"| Esc["SRXL2 ESC"]
    Esc -->|"telemetry reply"| Link
    Waits -->|"control frames"| Esc
    Task -->|"control frames"| Esc
    Esc -->|"telemetry reply"| Task
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Nonblocking boot-time SRXL2 service
  • ➕ Could preserve the duration of unrelated initialization work instead of adding a bounded link wait.
  • ➖ Would need an early scheduler or other service mechanism before normal tasks run.
  • ➖ Makes continuous frames and link completion across blocking startup waits harder to guarantee.

Recommendation: The bounded early link loop and servicing of existing waits are the more targeted approach: they cover the ESC’s short handshake window without restructuring startup. Reviewers should focus on timing boundaries, multiple ESCs, and whether every affected wait can safely process the driver.

Files changed (5) +182 / -53

Enhancement (1) +13 / -0
motor_srxl2.hExpose boot-time SRXL2 service functions +13/-0

Expose boot-time SRXL2 service functions

• Declares the early link wait and timed service functions for flight-controller initialization, with their waiting behavior documented.

src/main/io/motor_srxl2.h

Bug fix (2) +124 / -25
fc_init.cStart and service SRXL2 before normal motor initialization +29/-7

Start and service SRXL2 before normal motor initialization

• Moves timer and serial setup ahead of USB startup and, on eligible SRXL2 boards, opens and services ESC ports immediately. Replaces the fixed hardware-startup and LED waits with SRXL2-serviced waits while retaining ordinary delays for other configurations.

src/main/fc/fc_init.c

motor_srxl2.cAdd bounded boot linking and telemetry-based readiness +95/-18

Add bounded boot linking and telemetry-based readiness

• Adds early link and timed-service loops, makes initialization safe to call again, and keeps control frames flowing through startup waits. Arming connectivity now requires a recent telemetry reply and a 10-second readiness delay that restarts after renewed announcements or prolonged frame starvation.

src/main/io/motor_srxl2.c

Documentation (2) +45 / -28
Spektrum Smart ESC.mdCorrect Smart ESC power-up and reboot guidance +38/-21

Correct Smart ESC power-up and reboot guidance

• Documents the short handshake window, early linking, remaining startup gaps, and recovery after a board restart. Explains why arming remains blocked until telemetry and the readiness delay establish that the motor can respond.

docs/Spektrum Smart ESC.md

fc_core.cClarify the SRXL2 arming safeguard +7/-7

Clarify the SRXL2 arming safeguard

• Revises the arming-check comment to describe unanswered power-up handshakes and the longer delay before the ESC obeys throttle. The arming-check expression itself is unchanged.

src/main/fc/fc_core.c

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

qodo-free-for-open-source-projects Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Informational

1. Arming blocks 10 s after a brief pause ✓ Resolved
Description
The RUNNING state resets runningSinceMs whenever more than SRXL2_STARVED_MS (150 ms) has passed
since the last control frame, whatever caused the gap. Any runtime stall of the scheduler longer
than 150 ms, such as a settings save or an accelerometer calibration, sets
ARMING_DISABLED_HARDWARE_FAILURE and the OSD hardware warning again for 10 s, even when the gap is
shorter than the ~185 ms after which the ESC actually drops the link.
Code

src/main/io/motor_srxl2.c[R927-929]

+            if (now - e->lastControlMs >= SRXL2_STARVED_MS) {
+                e->runningSinceMs = now;
+            }
Evidence
The new code resets runningSinceMs after any gap of at least 150 ms. srxl2MotorIsConnected()
then returns false for SRXL2_READY_DELAY_MS (10 s), and updateArmingStatus() sets the
hardware-failure arming flag. The define's own comment says the ESC re-announces only about 185 ms
after its last frame, so a gap between 150 and 185 ms restarts the delay without any real link loss.

src/main/io/motor_srxl2.c[144-146]
src/main/io/motor_srxl2.c[1061-1072]
src/main/fc/fc_core.c[321-333]

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 SRXL2_STARVED_MS check (150 ms) restarts the 10 s ready delay on any gap in the driver's control frames. Gaps shorter than the ESC's own ~185 ms drop-out time still trigger it, so arming is blocked again with no real link loss.
## Fix Focus Areas
- src/main/io/motor_srxl2.c[144-146]
- src/main/io/motor_srxl2.c[926-932]
## Recommended Fix
Set SRXL2_STARVED_MS above the ~185 ms re-announce time, for example 250 ms, so only a gap the ESC actually drops the link on restarts the delay. Alternatively, apply the reset only while no telemetry reply has arrived since frames resumed.

ⓘ 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 tweak Display settings with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/main/io/motor_srxl2.c
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RAM / Flash usage vs. base branch — commit 2d54758

No size baseline is available yet for this PR's base commit (no per-commit baseline has been published for it). This comment will show deltas once one exists — rebasing the PR refreshes its base commit.

Target Flash Δ RAM Δ
MATEKF405 724015 B (no baseline) 136796 B (no baseline)
MATEKF722 480763 B (no baseline) 112124 B (no baseline)
MATEKF765 756159 B (no baseline) 153948 B (no baseline)
MATEKH743 800207 B (no baseline) 158472 B (no baseline)

See RAM/flash optimization guide for techniques to reduce usage.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Test firmware build ready — commit 81bde78

Download firmware for PR #12085

251 targets built. Find your board's .hex file by name on that page (e.g. MATEKF405SE.hex). Files are individually downloadable — no GitHub login required.

Development build for testing only. Use Full Chip Erase when flashing.

@UltraFly

UltraFly commented Oct 1, 2026

Copy link
Copy Markdown

I tested on avian 100A esc with nexus XR with both this fix and 12084 and I am correctly initializing esc with telemetry when powering up both together (power via esc bec). I confirm motor responds

@MrScothh

MrScothh commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

@UltraFly thanks, that is the case my bench could only stand in for: the board powered by the ESC's own BEC, both starting together, and here on a real NEXUS XR with an Avian other than my 70A. I've added it to the description.

# Conflicts:
#	docs/Spektrum Smart ESC.md
#	src/main/io/motor_srxl2.c
…tarts

A 150 ms gap in the board's frames restarted the 10 s arming wait whatever
the ESC's state, so a pause of the board's own, long after the ESC had
started, blocked arming again for 10 s with the OSD's hardware warning. Only
an Avian still starting when its frames stop starts over; one already started
takes the throttle back at once. The state is judged when the frames stopped,
so a gap that begins during the startup restarts the wait however long it is.
@MrScothh

MrScothh commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Tested on a second airframe: an E-flite T-28 with its own TBS Lucid H7 Wing and its Avian Smart ESC on UART8, 4S LiPo, propeller off.

  • Board and ESC powered together from the battery (USB out, battery in, USB back about 15 s later to read the state over MSP): maintenance-10.x (10.0.0-rc2) never linked, 2 of 2; with this PR it linked, 1 of 1.
  • Board rebooted over MSP 1 s after the ESC gained power, with USB keeping the board up: linked 3 of 3, each time 17.3 s after the battery went in.

@sensei-hacker

Copy link
Copy Markdown
Member

Thanks for this. Merging. Just an FYI heads up since you're doing a lot of SRXL2 work --
I plan for INAV 11 to split code into modular separate files, so that srxl2 code will live only in specific srxl2 files (to the extent that's possible). That is to then later allow on-fly linking, so that each protocol only takes up flash and RAM space for people actually using that protocol.

Control and telemetry protocols like srxl2 may be the first group that I do that with. So the goal will be all the HoTT stuff lives only in HoTT-specific files, all the CRSF stuff in CRSF-specific files, etc. It may be SRXL2 will be one of the first ones I work on.

As you do any other work on control and telemetry protocols, it may be helpful to keep that in mind and start moving that direction.

@sensei-hacker
sensei-hacker merged commit 1e765e4 into iNavFlight:maintenance-10.x Oct 11, 2026
25 checks passed
MrScothh added a commit to MrScothh/inav that referenced this pull request Oct 11, 2026
iNavFlight#12085 moved timerInit() and serialInit() ahead of the USB wait; serialPadsInit() goes right before serialInit() there, so the UARTs are on their pads before any port opens, the SRXL2 one included.
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.

3 participants