Commit Graph
100 Commits
Author SHA1 Message Date
Jacob Dahl 7c8d6daca9 docs(agents): move the shared driver code rule to drivers.instructions.md (#28853)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-gazebo-dev) (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 hex (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v2 (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
Sync ROS 2 messages to px4_msgs / sync_to_px4_msgs (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
It only matters when working under src/drivers/, which the scoped file already covers, and AGENTS.md is loaded for every task. The section was also not wrapped like the rest of AGENTS.md.

Assisted-by: Claude:claude-opus-5-5
2026-09-25 23:41:55 -06:00
Jacob Dahl 2d277ec00b docs(agents): say where code shared between drivers goes (#28852)
Agents put helpers used by several drivers of one type next to those drivers in src/drivers/<type>/, where the decoders they sit beside already live in src/lib/.

Assisted-by: Claude:claude-opus-5-5
2026-09-25 23:37:02 -06:00
Jacob Dahl bd7d86e141 fix(uavcan): keep GNSS jamming and spoofing with RelPosHeading (#28847)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-gazebo-dev) (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 hex (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v2 (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
Sync ROS 2 messages to px4_msgs / sync_to_px4_msgs (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
The Fix2 ecef_position_velocity block also carries the node's noise, jamming and spoofing state, but it was skipped whenever a RelPosHeading was pending, so jamming and spoofing read unknown for as long as a dual-antenna heading was valid.

Assisted-by: Claude:claude-opus-5-5
2026-09-25 16:41:30 -06:00
Jacob Dahl 4e91ae1c42 ci(flash_analysis): rename size table columns to Δ and Total (#28844)
Shorter headers keep the five-column table narrow, and "used" read as the amount used by the change rather than the target's total.

Assisted-by: Claude:claude-opus-5-5

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-25 15:59:06 -06:00
Jacob Dahl 4dbd2e069a fix(simulation): cast gz airspeed temperature to double (#28843)
The macOS build has been failing since #28842: clang reports the
implicit float to double conversion under -Wdouble-promotion, which the
build treats as an error. GCC does not warn for argument conversions,
so the Linux builds passed.

Assisted-by: Claude:claude-opus-5-5
2026-09-25 12:05:22 -06:00
Jacob Dahl 253479bbeb feat(ci): report flash and RAM use against capacity, analyze fmu-v2 (#28838)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-gazebo-dev) (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 hex (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v2 (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
* feat(ci): report flash and RAM use against capacity, analyze fmu-v2

The size comment showed only the delta, which does not say how close a target is to its limit. Capacity comes from the board's own linker script, because the flash-analysis build links against an oversized flash region so that a change which overflows still gets a report.

fmu-v2 adds an F4 next to the F7 and H7 targets, and with 1 MB of flash it is the FMU that overflows first.

Assisted-by: Claude:claude-opus-5-5

* fix(ci): shorten the size comment's usage columns

Five columns with capacities spelled out made the table too wide to read at a glance.

Assisted-by: Claude:claude-opus-5-5
2026-09-24 17:54:56 -06:00
Jacob Dahl 816a234175 docs(dronecan): drop EKF2_RNG_A_HMAX from rangefinder setup (#28836)
EKF2_RNG_A_HMAX is the height below which conditional range aid uses the rangefinder as the height source, not the maximum range the EKF uses; that comes from the sensor's reported max distance (UAVCAN_RNG_MAX). The DIST and smartmicro pages set it to the sensor range (30, 50, 175 m), above the parameter's 10 m max, so QGroundControl only accepts it with a force-save. The right value depends on the vehicle's baro ground effect rather than the sensor, so the default applies.

Assisted-by: Claude:claude-opus-5-5
2026-09-24 11:17:24 -06:00
Jacob Dahl a7646e2d6d fix(drivers/imu/st/lsm6dsv): reset before telling the 16X from the 32X (#28823)
CTRL8 bit2 identifies the 32X only at its reset value, and probe() read it before any reset. After a warm restart from firmware that cleared the bit (drivers without 32X support write the 16X CTRL8 encoding), a 32X was detected as a 16X and its accel ran at ±32 g while scaled as ±16 g.

Assisted-by: Claude:claude-opus-5-5

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-23 15:43:39 -06:00
Jacob Dahl bb4e77b00c fix(boards/ark_fpv): drive SPIX_SYNC only on the IIM-42653 revision (#28825)
SPIX_SYNC is the 32 kHz CLKIN on IMU pin 9. On FMUM 1 that pin is the
LSM6DSV32X INT2 push-pull output, so the MCU drove against it: the IMU
die ran about 10 degC hotter and accel read about 0.19 g off on one
axis.

Assisted-by: Claude:claude-opus-5-5
2026-09-23 15:13:04 -06:00
Jacob Dahl d551d33b61 docs(agents): keep agents out of the Crowdin-owned translation trees (#28807)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-gazebo-dev) (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 hex (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
* docs(instructions): keep agents out of the Crowdin-owned translation trees

docs/ko, docs/zh and docs/uk regenerate from docs/en, but nothing in the scoped guidance said so, and repo-wide sweeps edit them along with the English source.

Assisted-by: Claude:claude-opus-5-5

* docs(agents): state the translation rule in AGENTS.md

Agents load AGENTS.md directly; the .github/instructions file only reached them through a pointer to go read it.

Assisted-by: Claude:claude-opus-5-5
2026-09-22 18:24:11 -06:00
Jacob Dahl 2ec03ff1c3 fix(ci): skip the flash comment for sub-30 B deltas and agent-doc changes (#28806)
The commit hash compiled into px4_firmware_version_binary() changes its code size, so a PR with no code change reported -8 B on v6x and got a comment. Changes that only touch agent instructions also no longer build the targets.

Assisted-by: Claude:claude-opus-5-5

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-22 17:52:55 -06:00
Jacob Dahl 9e9871d403 docs(agents): user-set worktree location, italic Assisted-by in PR bodies (#28805)
* docs(agents): let a user's own instructions set the worktree location

A fixed ../PX4-Autopilot-worktrees/ path overrode contributors who keep worktrees elsewhere. Their own agent instructions now win, with the sibling directory as the default.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* docs(agents): italicize the PR body's Assisted-by line

Set apart in italics, the disclosure reads as a footnote rather than part of the Solution section above it.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-22 17:33:12 -06:00
Jacob Dahl 11ad677f8e fix(commander): clarify sensor startup diagnostics (#28620)
Use the arming-check events to explain initialization waits without misleading legacy preflight messages.

Assisted-by: Codex

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-22 17:00:32 -06:00
Jacob Dahl 3d154f5d45 docs(maintainers): retire inactive owners and set Architecture (#28570)
Daniel Agar, Paul Riseborough, and David Sidrane no longer work on the project. Architecture has been vacant in practice.

Assisted-by: Grok:grok-4.6

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-22 16:58:24 -06:00
Jacob Dahl 1d761cdf2e fix(ci): report flash and static RAM usage separately (#28789)
* fix(ci): report flash and static RAM usage separately

Bloaty's VM total includes zero-initialized RAM and understates the combined cost of data and code copied from flash into RAM. Report the flash load image and static RAM allocations independently so the PR headline reflects each resource's actual change.

Assisted-by: Codex

* docs(ci): trim memory analysis comment

Assisted-by: Codex

* fix(ci): diff PR flash usage against the merge commit's base

The base branch tip is fetched when the job runs, so a PR merged while its job was queued was compared against itself and reported a near-zero change, and commits landed in the meantime showed up as reverted.

* fix(ci): measure flash image from section headers

ld maps the ELF header into the first LOAD segment when its max page size exceeds the flash origin's alignment (binutils 2.34 defaults to 64 KiB), so segment-based accounting rejected valid firmware as outside the flash region.

* feat(ci): flag notable flash and RAM changes

Growth over 100 B and 1000 B gets yellow and red markers, savings over 100 B green, so reviewers can spot size regressions without reading the numbers.

* refactor(ci): derive flash and RAM totals without per-target constants

The matrix duplicated each board's flash-analysis linker region, which would drift silently. Section headers alone distinguish code executing in place (VMA == LMA) from data copied to or reserved in RAM, and the image span matches objcopy -O binary.

* fix(ci): limit the bloaty breakdown to VM sizes

Debug, symbol and string table rows only change the file size and made up most of the comment.

* fix(ci): post flash analysis only when size changes

A push that puts flash and static RAM back to zero was still rewriting the sticky comment, so a PR with no size change carried a zero report. Remove that comment instead. Fork tokens cannot delete it, so those PRs hand a delete artifact to the poster.

Assisted-by: Grok:grok-4.7
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(ci): retain zero-delta flash analysis comments

Existing reports can be updated when size deltas return to zero, avoiding a separate privileged deletion path.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Assisted-by: Codex:gpt-6

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-22 16:56:58 -06:00
Jacob Dahl 85459fae96 fix(lockstep): stop SIH from passing the barrier on banked releases (#28802) 2026-09-23 09:35:18 +12:00
Jacob Dahl eff5e1b350 chore(agents): move Claude instructions and skills to AGENTS.md and .agents (#28803)
* chore(agents): move Claude instructions and skills to AGENTS.md and .agents

Claude Code now reads AGENTS.md and .agents/skills, so one client-agnostic copy serves every assistant and the Codex adapters that pointed back into .claude go away. The build skill splits by host: Linux builds natively, macOS needs the container.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* chore(agents): expose .agents to Claude Code via imports and symlinks

Claude Code reads AGENTS.md only when no CLAUDE.md or CLAUDE.local.md exists and never reads .agents/, so the skills and instructions were invisible to it. Worktrees move outside the repository, where no tool's recursive search reaches them.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* chore(agents): refine build, commit, pr and review-pr skills

Linux builds run in whatever checkout the agent is in. PR bodies now carry the Assisted-by disclosure and an optional Testing section for testing worth reporting. Commits preserve history for reviewers since PRs are squash-merged. Reviews can check the area's history for intentional or in-flight work.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* feat(agents): push and refresh the PR description from the commit skill

A commit on a pushed branch otherwise leaves the PR stale until someone remembers to push and re-edit it.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(agents): drop trailing blank line in .gitignore

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-22 14:26:39 -06:00
Jacob Dahl da1c2a25cb fix(flashparams): append unsaved deltas and compact at boot (#28532)
* fix(flashparams): append unsaved deltas and compact at boot

Same-bank program of a full BSON snapshot stalls instruction fetch for tens of ms, which drops a high-rate IMU FIFO and DShot while disarmed. The 1 s sector erase belongs at boot, not on a wrap mid-session.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(flashparams): serialize compact and encode before erase

param_reset_all() queued an autosave that could program flash during the boot compact erase. Reset without autosave under file_mutex, build the snapshot in RAM before erasing, and compact only when a burst of deltas would not fit so COM_FLIGHT_UUID is not a 1 s erase every flight.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(flashparams): append in log order and encode resets as null

Walk order was not write order on multi-sector maps, and a torn H7 header skipped the rest of the sector so a successful retry vanished. Resets encoded as the current default also came back as overrides after a firmware default change.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(flashparams): version the log token and keep default-equal overrides

Records carry a new `plog` token. Firmware before the log format wrote one
`parm` snapshot and cannot replay deltas; it ignores `plog` records and
treats a store without `parm` as empty (STM32H7 erases it at init), so a
downgrade boots on defaults instead of loading a stale snapshot and then
failing every save into the deltas behind it. A `parm` record is imported
alone as the newest state and the store is rewritten as a `plog` snapshot.

The boot snapshot and import no longer drop values equal to the current
default. They run before the airframe applies its set-default values, so
the comparison discarded user overrides that happened to match the firmware
default. Deltas still tombstone a value matching the default in force, as
the file backend does at save time.

Boot compaction stages the snapshot before deciding, and a failure before
the erase keeps the log and returns success: RAM holds what the log said,
so the store is not corrupt. Compaction is skipped when the compacted
layout would lack the headroom too, so a large snapshot does not erase on
every boot. Appends are row aligned on H7, header fields are read only
once the header lies inside the sector, and the append scan stops at a
blank header like the replay walker.

* fix(param): clear only the unsaved bits that were pending when the save began

A param_set that lands while the export runs is not in what gets written.
Clearing the whole bitset afterwards lost its bit. Full-snapshot saves
picked the value up on the next save; flash deltas never wrote it.

* fix(flashfs): keep every record inside the range the walkers read

The walkers stop at a sector's last word and reject a record that ends past it, but the writer accepted a record that ends anywhere inside the sector. A record landing in the last four bytes was reported written and never replayed, and a snapshot of exactly max_payload bytes was unreadable, so import returned -EILSEQ. The F7 writer also padded the entry to a word inside a heap block allocated without room for the padding.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(param): keep the unsaved bit of a param set during a save

Clearing the bits that were pending when the save began still dropped a param_set that landed after the exporter had read that param's value. Move the pending bits to a saving set before the export so a set during the save marks the param again, and put them back if the save fails.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(flashparams): cover file imports, tombstones and stale records

A delta carries only params with the unsaved bit, and three paths left RAM ahead of the log with no bit set. A file import (param import, param load, the rcS backup recovery) set values as saved, so the param save that followed wrote nothing and the values reverted on reboot; on flash boards an import now leaves its changes unsaved, as the file backend leaves them until its next full export. A BSON null reached param_modify_on_import with the previous node's value still in the union, so a translation reading it without a type check could fire on a tombstone; decode tombstones first. A record that no longer names a param, or names one of another type, was replayed through a translation on every boot and kept until a compaction happened to run; treat it as a reason to compact at boot. A log whose replay failed inside a CRC-valid record, or that has no snapshot, gets a snapshot on the next save instead of a delta nothing would reach.

The buffer encoder wrote 0 as the document length, which the file importer takes as a blank store; record the real length so a buffered document is a valid BSON file.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-22 12:27:49 -06:00
Jacob Dahl f90b40d61e fix(sensors): do not register a callback for a disabled barometer (#28788)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, amd64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (humble, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-dev) (push) Canceled after 0s
ROS Development Container / Build ROS Development Image (jazzy, arm64, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (humble, ros2-gazebo-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-dev) (push) Canceled after 0s
ROS Development Container / Publish Multi-Architecture ROS Development Image (jazzy, ros2-gazebo-dev) (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 hex (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
* fix(sensors): do not register a callback for a disabled barometer

Since disabled barometers are fed to the voter at priority 0 they also
register a sensor_baro callback while nothing is selected. Callbacks are
only cleared on a selection change, and with every barometer disabled
the selection stays at -1, so a lone disabled barometer woke Run() and
UpdateStatus() at its sample rate indefinitely instead of on the 50 ms
schedule. The scheduled cycle already keeps the disabled validator fed,
and a re-enabled sensor registers again when it is selected.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* test(sensors): cover the disabled-barometer callback gate

Same harness as the vehicle_magnetometer functional test: a lone
barometer disabled from boot must leave no sensor_baro callback
registered, and must register one once it is re-enabled and selected.
Fails without the gate in Run().

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-21 22:57:37 -06:00
Jacob DahlandBalduin 0e9d8f6bea refactor(platform): share I2C/SPI driver startup failure handling (#28786)
Each default driver instantiation emits the same allocation and initialization failure handling. Share that code outside the template while keeping init() resolved on the concrete driver. Allocation failures are now logged under SPI_I2C.

Extracted from Balduin (mbjd) in PX4/PX4-Autopilot#27816, commit 8a2391730f.

Assisted-by: Codex

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-09-21 22:50:23 -06:00
Jacob DahlandBalduin b9d630aa47 refactor(modes): share navigation state names across translation units (#28787)
The header currently gives each consumer its own copy of the navigation state name table. Use C++17 inline linkage so the linker can retain one copy.

Extracted from Balduin (mbjd) in PX4/PX4-Autopilot#27816, commit a0a0ebf69e.

Assisted-by: Codex

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-09-21 22:50:05 -06:00
Jacob Dahl e034dd91c5 fix(metadata): validate component metadata against current schemas (#28785)
The schema path still says component_information/, so --skip-if-no-schema makes the check a no-op. QGC only accepts parameter metadata version 1, while the schema floor rose for optional fields. Override that floor and keep emitting version 1.

Assisted-by: Grok:4.7

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-22 14:43:05 +12:00
Jacob DahlandBalduin c42695cfb0 refactor(mavlink): use tables for default stream configuration (#28631)
Repeated stream configuration calls duplicate argument setup in flash.
Store the defaults as constant data while preserving their order and
single-stream lookup behavior.

Extracted from PX4/PX4-Autopilot#27816.


Assisted-by: Codex

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-09-08 21:20:19 -06:00
Jacob Dahl b8ba996bcf fix(mavlink): make packed parameter transfers reliable (#28630)
* fix(mavlink): make packed parameter transfers reliable

Recover pipelined uploads after packet loss without hiding failed parameter writes. Preserve correct defaults while values change during download, and avoid invalid float-to-integer conversions.

Assisted-by: Codex:GPT-6

* refactor(mavlink): allocate upload reorder blocks with new

malloc plus placement new was unlike the rest of the FTP server, which
uses nothrow new with a null check. Document why the reorder heap is
bounded only by the file length: mavftp retries a lost chunk after
sending every remaining one, the same bound ArduPilot's buffer has.
2026-09-08 21:20:10 -06:00
Jacob DahlandBalduin 6236d4a855 build(uxrce_dds_client): inherit firmware optimization flags (#28629)
The module's custom -O2 override defeats the default -Os on NuttX.
Use the selected build configuration to recover flash space.

Extracted from Balduin's optimization in PX4/PX4-Autopilot#27816.

Assisted-by: Codex

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-09-08 21:20:00 -06:00
Jacob Dahl 0658d87271 feat(uavcan): correct the bus clock rate from time sync and share the clock across drivers (#28572)
* feat(uavcan): correct the bus clock rate from time sync and share the clock across drivers

Stepping only the offset once a second leaves the bus clock a full second
of crystal drift behind between syncs, 8 us on an ARK X20 against an
ARKV6S where the sync measurement itself scatters by 1.5 us. Modelling
UTC as HRT plus an offset and a rate, and integrating half the rate error
each sync, brings the residual down to that noise. The same clock served
the stm32, stm32h7 and kinetis drivers as three copies; it now lives once.
`uavcannode status` reports the adjustment count, last step and rate.

* fix(uavcannode): guard HRT sync diagnostics by driver

SocketCAN uses a separate clock without the HRT sync status API, so the unconditional diagnostic broke NXP node builds.

Assisted-by: Codex
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-08 20:43:37 -06:00
Jacob Dahl ebc4d50a9e fix(mavlink): identify files in FTP open failure logs (#28627)
Failed FTP opens need the requested path and access mode to identify the file behind the error.

Assisted-by: Codex

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-08 20:25:55 -06:00
Jacob Dahl 7cfa8d1d40 docs(gps): UART2 cannot update u-blox firmware (#28593)
u-blox does not support firmware upgrade on UART2. GPS_UBX_MODE 7 is a diagnostic port only.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-08 20:17:13 -06:00
Jacob Dahl dcfce4343a feat(mavlink): serve and accept packed parameters as @PARAM/param.pck (#28435)
* feat(mavlink): serve packed parameters as @PARAM/param.pck

QGC already downloads ArduPilot parameters as a packed FTP file, which
is much more reliable on lossy SiK links than the PARAM_VALUE firehose.
Serve the same format from PX4 so a hash miss can use that path.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(mavlink): serialize the full message when resending a cached FTP reply

_last_reply caches only the header, PayloadHeader and four data bytes,
but the resend path handed that 19-byte array to the serializer as a
complete mavlink_file_transfer_protocol_t, which reads 254 bytes and
transmits whatever follows the cache. The decoded request is dead once
the resend decision is made, so expand the cache into it instead of
adding another message to the receiver thread's stack. Clear the unused
payload before caching so a resent NAK matches the original.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* test(mavsdk_tests): download parameters via MAVLink FTP

Fetch @PARAM/param.pck through MAVSDK's burst download on SIH and check
every entry against the PARAM_VALUE stream, so the packed format and the
FTP server's session, size and EOF handling stay covered by the SITL
tests that QGC and MAVSDK both rely on.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(mavlink): cache FTP Open ACKs whose size LSB is 5

_reply skipped the compact cache whenever data[0] was kErrNoSessionsAvailable. On a NAK that is the error code; on an Open ACK it is the file size LSB. A lost Open ACK for param.pck of that length was retried as a second Open and failed.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* feat(tools): download packed parameters over MAVFTP

Host-side check of @PARAM/param.pck against the PARAM_VALUE stream.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(mavlink): pad so a defaulted value does not straddle an FTP block

pack() kept the last 4 bytes of an entry in one chunk. With withdefaults those bytes are the default, so the value can still split. QGC always requests defaults; a hole-fill then retries one chunk with a live param_get().

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(mavlink): snapshot param.pck at open so a param_set cannot corrupt it

Pack the used set once into a heap buffer. Reads memcpy that snapshot, so a value change during the download cannot shift later entries or splice two generations across a retried FTP block.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(mavlink): freeze param.pck membership instead of snapshotting the file

Keep which parameters appear and whether each includes a default in bitsets (~1 KB). Pack values live. Layout cannot shift mid-download; a retried block cannot splice a number.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* style(mavlink): format MavlinkFtpParamTest.cpp

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* refactor(mavlink): keep FTP work buffers as members

@PARAM/param.pck is a connect-time path, not the rare path the lazy new[] was for. send() could free the buffers after 2 s of no request while a burst was still running. Fold them into the instance (495 B) and always idle-close the session at 30 s.

A new Open takes over the single session so a lost Terminate on a slow link does not NAK until that close.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(tools): make mavftp_param.py reliable on a radio link

pymavlink applied FTP timings after ResetSessions, wrote param.pck?withdefaults=1 into cwd, and bumped the session id on Open retry (PX4 only ACKs session 0). Set radio timings at construction, pin session 0, retry a missed Open, and treat a complete decode as success.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* feat(mavlink): accept packed parameter FTP uploads

CreateFile/Write/Terminate of @PARAM/param.pck applies the ArduPilot
packed stream so a GCS can bulk-load without a PARAM_SET round trip per
value. Unknown and read-only names are skipped; Terminate NAKs a
truncated file or the with-defaults magic.

* test(mavsdk_tests): upload MPC_XY_P via packed param FTP

Pin the SIH upload case to a known float instead of whatever
get_all_params() returns first.

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-08 20:16:14 -06:00
Jacob Dahl 69e8bf05e3 feat(ark_x20-gps): capture the ZED-X20P timepulse for Fix2 timestamps (#28626)
The node's Fix2 publisher only fills the message timestamp while pps_capture is running, since that is what puts the send time and the receiver's gnss_timestamp on the same clock. Without it the flight controller's GNSS bridge cannot recover the navigation epoch and falls back to receive time. TIMEPULSE is on PB2, which has no timer alternate function on the F412, so the capture uses EXTI like can-rtk-gps.
2026-09-08 20:14:27 -06:00
Jacob Dahl b8d5d2bc58 fix(optical_flow): normalize PixArt SQUAL onto the 0-255 quality contract (#28625)
sensor_optical_flow.quality is defined as 0 = worst, 255 = best, and EKF2 scales flow noise linearly with it, but the PAA3905 and PAW3902 drivers passed the raw SQUAL register through. Raw SQUAL is mode dependent (the datasheet noise floor is 0x19/0x46/0x55 for bright, low light and super low light), and the DroneCAN flow message carries no mode, so a consumer could not tell a solid bright-mode reading from one count above the super-low-light floor. Each mode's floor now maps to 0 and raw 255 stays 255; the false-motion discard, which also needs the shutter condition, shares the same constants.
2026-09-08 20:14:18 -06:00
Jacob Dahl 4af7b4e77e fix(uavcan): publish UNKNOWN node timestamps until time-synced and map them through the bus time base on the FC (#28176)
* fix(uavcannode): publish UNKNOWN until time-synced and fix RawIMU integration_interval units

The RawIMU and RangeSensor publishers computed the bus timestamp as
getUtcTime() - (hrt_now - sample_hrt). getUtcTime() is 0 until the time-sync
master disciplines the node's clock, so the unsigned subtraction underflowed and
the truncated uint56 field went out as ~7.2e16 us. Publish UNKNOWN until the
clock is disciplined, from one shared helper.

RawIMU.integration_interval is specified in seconds but was assigned
vehicle_imu.delta_angle_dt, which is in microseconds.

* fix(uavcan): map node timestamps through the bus time base in the sensor bridges

The accel, gyro and rangefinder bridges took msg.timestamp.usec verbatim as
timestamp_sample whenever it was non-zero, which assumes the bus shared time
base is HRT. It is not: the FC seeds its bus clock from HRT only after its own
init has run, so the base leads HRT by that duration, and the FC may also be a
slave to a lower node ID master. An undisciplined node, or one stamping a
foreign epoch, put the sample time arbitrarily far from HRT.

The CAN ISR stamps every received transfer in the same bus base the node used,
so the sample age is the difference between the two. Bound that age to a
plausible transport window and subtract it from the receive time, falling back
to the receive time otherwise, in one shared helper.

* fix(uavcan): stamp RelPosHeading and BatteryInfoAux in the bus base, map RelPosHeading on the FC

Both node publishers put node-local time into a bus-base field, and the
FC took the RelPosHeading stamp verbatim as HRT. Same defect as RawIMU
and RangeSensorMeasurement, same two helpers.

* fix(uavcan): read the bus clock and HRT together when mapping node timestamps

The ISR receive stamp is taken at the first frame of a transfer while HRT was
read in the callback, so the transfer and scheduling latency stayed in
timestamp_sample as jitter. Sampling both clocks at the same instant cancels
the bus-to-HRT offset exactly. The node-side helper takes the node for the same
reason.

The FC bus base leads HRT by the free-running timer phase at seeding, not by
the init duration; correct the comment.

* fix(uavcan): send true point samples in RawIMU and consume the integral on the FC

RawIMU.*_latest carried the window mean, delta / integration_interval,
under a field the DSDL defines as the latest point sample. The mean lags
the message timestamp by half the interval, 2.5 ms at IMU_INTEG_RATE 200,
and the FC published it as an instantaneous rate at the window end.

The node now fills *_latest from the sensor_gyro / sensor_accel instance
behind vehicle_imu, the same batch-end sample every PX4 IMU driver
produces, and keeps the integrals and timestamp as they were.

The FC bridges now consume the integral when one is present: divided by
its interval it is float32 rather than float16, anti-aliased against the
node's full-rate stream, and successive means spaced one interval apart
integrate back to the node's exact delta angle in VehicleIMU. It is
stamped at the window centroid. The point sample is used only when the
node reports no integral, as the DSDL specifies.
2026-09-08 20:14:07 -06:00
Jacob Dahl b8023ac8f9 feat(invensense): board-selectable anti-alias bandwidth for CAN flow nodes (#28624)
* feat(invensense): board-selectable anti-alias bandwidth, 250 Hz on ARK flow nodes

The ICM-42688-P / IIM-4265x drivers hard-code the chip defaults (AAF 585 Hz, UI filter 1st order at ODR/2), which suit a flight controller decimating 8 kHz to a 1-2 kHz rate loop. A CAN flow node integrates the same stream to 1 kHz for the flow gyro compensation and 200 Hz for RawIMU, so everything between 500 Hz and the AAF knee aliases into what it sends. A flight on an ARK Flow MR showed the node pitch gyro at 1 rad/s RMS against 0.2 on the FC with an accel vibration metric 5x higher.

`-B <hz>` selects the closest anti-alias filter row from the datasheet table and a 3rd-order UI filter at the closest ODR/N; ARK Flow and Flow MR start their IMU with `-B 250`. EKF2_OF_DELAY drops from 20 ms to 7 ms, the transport delay measured for a DroneCAN flow frame after its integration window closes.

* fix(invensense): use size_t for the UI divisor loop index

clang-tidy bugprone-too-small-loop-variable: the index was uint8_t against a sizeof bound.

* refactor(invensense): replace the anti-alias table with three fixed presets

A 63-row table plus nearest-match search is more than the option needs. `-B` now takes exactly 126, 258 or 394 Hz, each row carrying its UI filter setting, and rejects anything else. ARK Flow and Flow MR use 258. The icm42688p line on ARK Flow had been missed.
2026-09-08 20:13:50 -06:00
Jacob Dahl 3f6fe0e40c refactor(uavcan): base the libuavcan clock on HRT instead of a dedicated timer (#28569)
* refactor(uavcan): base the libuavcan clock on HRT instead of a dedicated timer

The stm32, stm32h7 and kinetis drivers each ran a 16-bit hardware timer
with a software overflow extension, a second copy of what HRT provides,
and slewed UTC by patching that accumulator from the timer interrupt.
Every CAN board paid a general-purpose timer for it, and the bus time
base led HRT by the timer phase at seeding even when the FC is the sync
master.

Monotonic time is now hrt_absolute_time() and UTC is HRT plus an offset
that time sync moves. Each adjustment is applied directly; the rate PID
and its lock accessors had no callers, and at the sub-100 us errors sync
produces after convergence a 1 Hz offset correction bounds the error to
crystal drift over one second. The timer number plumbing and the
BOARD_UAVCAN_TIMER_OVERRIDE option go with it.

* refactor(uavcan): drop the vestigial clock::init and the timer enables it left behind

With the hardware timer gone, init() only set a flag, so the flag is set
where the singleton is constructed. The three boards that enabled their
override timer in defconfig alongside the override no longer need it.
2026-09-08 20:12:29 -06:00
Jacob Dahl bb75224d6d feat(afbrs50): static rate/DFM profiles, pipeline hardening, range offset calibration (#28454)
* feat(afbrs50): static rate/DFM profiles, pipeline hardening, range offset calibration

The distance-based short/long range switching only re-evaluates after a
valid measurement, so once the target leaves the range the driver latches
short-range mode and the sensor stays blind; frames that fail evaluation
were never published, leaving consumers on the stale last value. Flight
characterization of the LV85D and LX85D showed frame rate is the dominant
range knob and DFM 4X beats the mode-default 8X on validity, spread and
wrong-window returns at every rate, so the switching is replaced by
SENS_AFBR_RATE / SENS_AFBR_DFM / SENS_AFBR_PROF with per-module defaults,
SENS_AFBR_MODE gains Auto (module default) with fallback when the API
rejects a mode, and the rate is clamped to the API's 5 Hz frame-time floor
that previously put CONFIGURE in a silent retry loop.

Invalid or quality-gated (SENS_AFBR_QMIN) frames now publish
max_distance + 1 with quality 0 so uavcannode emits TOO_FAR, and the max
distance is bounded by the configured unambiguous range.

Pipeline fixes: measurementReadyCallback dereferenced g_dev after its null
check, stop() deleted the object while a callback could still arrive, the
DRDY interrupt stayed bound to the destroyed handle, S2PI_Abort left the
bus BUSY forever, S2PI_Init leaked on restart, setRateAndDfm spun
unbounded, and a lost completion callback or wedged device stalled the
state machine for good. Non-OK result codes are counted by name for
'afbrs50 status'.

'afbrs50 cal' runs the vendor absolute range offset calibration on a
low-priority task (the sequence busy-waits and would starve the IWDG
feeder on wq:uavcan) and persists the offsets to SENS_AFBR_OFS_LO/HI.

* fix(afbrs50): evaluate every completed frame and guard CONFIGURE against stale completions

Argus_EvaluateData is what releases the API's raw data buffer, and the
API refuses new measurements and rejects configuration writes once two
buffers are held. An error-status callback skipped it, so two bad frames
wedged the driver into the stall recovery with nothing published.

The abort completion of that recovery, arriving from the SPI thread,
could overwrite CONFIGURE with TRIGGER and skip the reconfigure.

Also note the removed parameters in the 1.18 release notes.

* refactor(afbrs50): run the driver in its own task and the SPI transfers on the bus work queue

The transfer work item ran on wq:SPI0 (which does not exist on STM32
targets) purely for its near-top priority: before API 1.6.6 a DRDY
firing within ~60 us of the last SPI clock was lost unless the transfer
callback had already run. Since 1.6.6 ADS_SPI_Callback re-checks the
IRQ pin and recovers a DRDY that arrives before the callback, so the
deadline is gone and the transfer item can live on the work queue of
the bus the sensor actually sits on, at its conventional priority. The
blocking exchange stays on a work queue because the API requests
transfers from hrt interrupt context.

The state machine cannot share that thread: the API's configuration
calls spin in ADS_AwaitIdle until the transfer they queued completes.
It previously borrowed hp_default, whose 2800 byte stack
Argus_EvaluateData overflows and whose priority puts the driver's
blocking configuration waits ahead of dshot and pwm_out. It now runs as
a SCHED_PRIORITY_SLOW_DRIVER task woken by the completion callback
through a semaphore, so the range offset calibration no longer needs
its own task either: the driver drops to SCHED_PRIORITY_DEFAULT for the
duration of the sequence, below the wq:uavcan IWDG feeder, and a stop
request aborts it.

* fix(afbrs50): publish only NO_OBJECT as too far, reinit on stuck CONFIGURE, protect calibration

Every evaluation failure and quality-gated frame went out as max_distance + 1, which collision prevention clamps to max_distance and enters as free space regardless of signal_quality, so a sensor fault on a horizontal mount cleared a real obstacle. Only the device's own STATUS_ARGUS_NO_OBJECT is published that way now; errors and gated frames are counted and left to the consumers' stream timeouts.

CONFIGURE drains a raw buffer an abort may leave behind, since the API rejects configuration writes until it is evaluated, and falls back to Argus_ReinitMode after ten consecutive failures because a sticky error status never returns to IDLE on its own.

The vendor calibration sequence blocks longer than ModuleBase's 5 s stop deadline while holding pointers into the task stack, so 'afbrs50 stop' is refused while it runs and a stop clears a not-yet-started request. The sequence also rewrites the per-pixel offset tables, which cannot be persisted; they are restored afterwards so the sensor runs in the state the stored global offsets re-create at boot. The destructor now also stops the API's periodic timer, the last path that could reach the completion callback after ModuleBase has deleted the instance.

* fix(afbrs50): publish invalid frames with signal quality 0 instead of dropping them

Dropping errored and quality-gated frames left a receiver unable to tell a sensor returning invalid readings from one that fell off the bus. Every frame is published again with signal_quality 0 marking the invalid ones. The distance sent with it is chosen for collision prevention, which ignores quality: NO_OBJECT stays beyond max_distance (free space, TOO_FAR on DroneCAN), errors carry min_distance (discarded, UNDEFINED on DroneCAN), and a gated frame keeps its measured distance with the quality floored to 0.
2026-09-08 20:12:16 -06:00
Jacob Dahl 2322c173aa fix(sensor_calibration): default external accel/gyro priority below internal (#28594)
External IMUs inherited the magnetometer's external-first default of 75,
so an uncalibrated CAN IMU (ARK Flow, X20) took over as primary gyro the
moment it appeared, ahead of the on-board FIFO IMUs, and a calibration
then saved that priority. A magnetometer away from the power electronics
is the better primary; an IMU behind a bus with its own clock, transport
latency and no heater is not. Default external accel and gyro to Low (25)
so they only win when explicitly prioritized.
2026-09-08 10:07:15 -06:00
Jacob Dahl 91bb35c8b3 fix(nuttx): bump to the stm32h7 MDIO poll fix (#28591)
Every PHY register access on STM32H7 spent 5 ms in up_mdelay(), so the
boot-time autonegotiation link wait ran netinit at 44% CPU for 65 s with
the net lock held whenever no ethernet cable was attached (PX4/NuttX#414,
apache/nuttx#20067). Also picks up the two upstream stm32_mdio fixes for
the lower-half cast and the Clause 22 write register field.
2026-09-07 09:44:09 -06:00
Jacob Dahl 6dc89338a8 fix(commander): build mag calibration only with CONFIG_SENSORS_VEHICLE_MAGNETOMETER (#28555)
Boards that compile the magnetometer pipeline out still linked 8.6 KB of
calibration that could never run. holybro_kakutef7 is one of them and sits
at 100 % of flash on main. The calibration commands answer UNSUPPORTED
there.
2026-09-04 21:26:50 -06:00
Jacob Dahl 0294b6200f fix(paa3905): rate-limit challenging surface warning (#28553)
The Motion ChallengingSurface bit stays asserted for the whole time over a bad surface, so PX4_WARN floods the console at the run-loop rate.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-04 17:21:36 -06:00
Jacob Dahl 8f9b91b2bd fix(boards): correct the F412 chip selection and app flash size on ARK cannodes (#28456)
* fix(can-flow-mr): use STM32F412VG and 960K of app flash

The part is STM32F412VGH6 (100-pin, 1MB), not the 48-pin 512KB CE.
App flash starts at 0x08010000 after the 64K bootloader+params window,
so the region is 960K, not 928K.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(boards): select STM32F412VG on the 100-pin 1MB ARK cannodes

Teseo, X20, F9P, and Septentrio/MOSAIC/G5 GPS modules use
STM32F412VGH6, same as Flow MR. They were still selecting the
48-pin 512KB CE and a 928K app region that double-counted params.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(ark/cannode): use STM32F412CG and 960K of app flash

The part is STM32F412CGU6, not the 512KB CE. App flash starts at
0x08010000 after the 64K bootloader+params window, so the region is
960K, not 928K.

* fix(ark/mag): use STM32F412VG and 960K of app flash

The part is STM32F412VGH6, not the 48-pin 512KB CE, and the app region
after the 64K bootloader+params reservation is 960K, not 928K.

* docs(boards): fix the F412 SRAM description in the cannode linker scripts

The F412 has one contiguous 256KB SRAM at 0x20000000. The block list
these scripts carried is F42x boilerplate and describes memory the part
does not have.

* fix(ark/dist): use STM32F412VG and 960K of app flash

The part is STM32F412VGH6, not the 48-pin 512KB CE. App flash starts at
0x08010000 after the 64K bootloader+params window, so the region is
960K, not 928K.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(boards): give F412 cannodes a 256K SRAM region (#28498)

NuttX now ends the F412 heap at 0x20040000. The linker scripts still
capped .data/.bss at 192K, leftover from the F42x split.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* docs(boards): fix the F412 SRAM description on the 512K ARK cannodes

The F412 has one contiguous 256KB SRAM at 0x20000000. The block list
these scripts carried is F42x boilerplate and describes memory the part
does not have.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(ark/cannode): drop the PH1 boot jumper defines

PH1 is OSC_OUT for the HSE crystal. The jumper option was already
undefined, so these defines could not be enabled without stopping
the clock.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(boards): correct ARK cannode I2C bitbang pins

The bitbang GPIO defines did not match the schematic nets, so a bus reset would drive the wrong pins.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-03 19:29:57 -06:00
Jacob Dahl 39eae27610 fix(uavcan): apply UAVCAN_BITRATE on FlexCAN again, size uavcan/TTY work queues for NuttX 12 (#28509)
* fix(uavcan): apply UAVCAN_BITRATE on FlexCAN again

58dfff0de7 turned the SIOCSCANBITRATE path off for i.MX RT, S32K and Kinetis,
which is every board in the tree that builds SocketCAN, so UAVCAN_BITRATE
stopped doing anything at all. It was written before db6f7ec304, and the crash
it works around is that commit's bug: the driver reports bit/s on NuttX 12 and
the caller still divided by 1000, so a 1 Mbit/s board asked its controller for
1000 bit/s on every boot.

Benched on an ARK FMU-v6XRT with the gate removed: 1 Mbit/s -> 500 kbit/s ->
1 Mbit/s round-trips with both DroneCAN nodes coming back OPERAT, buses
error-active, no fault. Requesting a rate the controller cannot reach is also
survivable now -- it is reported and the configured rate is kept, where before
a negative return from here made CanDriver::init() give up and DroneCAN never
started.

The %u for a uint32_t is fixed as well; nothing compiled this branch while the
gate was in place.

* fix(work_queue): size the uavcan and TTY work queues for NuttX 12

NuttX 12.12 costs a few hundred bytes of stack on the paths that go through
the file layer, and that is more than these two defaults left spare. Measured
against the same PX4 tree on 10.3.0:

  wq:uavcan  2860/3728 -> 3184/3736  (ARK FMU-v6X, two DroneCAN GNSS nodes)
  wq:ttyS4   1088/1704 -> 1432/1712  (ARK FMU-v6XRT, crsf_rc)

load_mon reports wq:ttyS4 low on stack with 280 bytes left, and wq:uavcan sits
at 85 % of its own. 4096 and 2048 restore the margin the 10.3.0 figures had.
2026-09-03 12:31:07 -07:00
Jacob Dahl d89835fb25 fix(dshot): emit the DShot rate that was asked for on STM32 (#28511)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (macos-15) (push) Canceled after 0s
MacOS build / build (macos-latest) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
* fix(dshot): emit the DShot rate that was asked for on STM32

io_timer_set_dshot_burst_mode() wrote the tick count into ARR, but an STM32 timer
period is ARR + 1 ticks, and the prescaler divided by that same tick count. Every
bit came out one tick long, so the output ran at 21/20 of the requested rate and
the 7/14 compare counts -- betaflight's, where the period really is 20 ticks --
landed on 21 and gave 33.3 %/66.7 % instead of the protocol's 37.5 %/75 %.

Measured on an ARK FMU-v6X (240 MHz timer, BDShot300) with a logic analyser:

  bit period  3.500 us (+5.00 %)  ->  3.333 us (-0.01 %)
  bit 0 high  33.2 %              ->  37.3 %
  bit 1 high  66.5 %              ->  74.9 %

Subtracting one from ARR alone is not enough. A tick count fixed at 20 (19, 18)
cannot divide every timer clock by every rate: 200 MHz at DShot600 would go from
+0.80 % to -4.00 % and 90 MHz from -2.00 % to -6.67 %. Choose the tick count and
the prescaler together instead, scoring the emitted rate first and the two high
times second, and derive the compare counts from the tick count that wins. That
is exact on 84, 90, 96, 108, 168, 180, 216 and 240 MHz at all three rates, and
within 0.25 % on 160 MHz and 0.80 % on 200 MHz, where no integer pair is exact.

The search only depends on the timer clock and the rate, so io_timer.c caches its
answer per timer and recomputes only if the rate changes, which is once. That
leaves the transmit path with no arithmetic at all where it previously ran a
modulo loop and two divisions on every burst and every capture.

The capture prescaler comes out of the same struct and no longer follows the
transmit tick count. It only sets the resolution of a free-running counter, so it
aims for a fixed 20 ticks per response bit, which is what keeps a one-to-three bit
run inside the interval window convert_edge_intervals_to_bitstream() accepts.

* fix(dshot): search up to 40 ticks per bit and keep the search in 32 bits

333 cycles per bit (200 MHz at DShot600, 100 MHz at DShot300) is 9 x 37, so a
ceiling of 32 ticks left those clocks at 0.79 % when 37 x 9 lands within 0.10 %.
The wider range also moves the clocks that were settling on a 39-40 % bit-0
duty onto 37.5 %.

The rounded prescaler keeps ticks * prescaler * rate within half a bit of the
timer clock, so the 64-bit compare bought nothing. The timing cache now starts
on a rate nobody asks for, so its zero-rate guard is reachable instead of
returning a zeroed struct.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-02 16:53:36 -06:00
Jacob Dahl 19ce32980e fix(ark/fmu-v6xrt): probe PAB EEPROM after I2C6 is up (#28513)
The 24LC64T on I2C3_BASE is wired to LPI2C6 (X1_8/10). Manifest MTD talks I2C, so it has to run after those pins are I2C and VDD_5V_PERIPH is on, or the chip does not attach and netman cannot save.
2026-09-02 16:21:31 -06:00
Jacob Dahl a90d30bfcf fix(v6xrt): expose SPI6 nRESET to the SCH16T driver (#28501)
The driver only hard-resets when SPI6_nRESET_EXTERNAL1 / SPI6_RESET are defined. v6xrt named the pin GPIO_SPI6_nRESET_EXTERNAL1, so SCH16T never left software reset.
2026-09-01 19:07:10 -06:00
Jacob Dahl 5208271705 fix(uavcan): make DroneCAN time sync work on SocketCAN (#28458)
The FC-side GlobalTimeSyncMaster needs the TX timestamp of its previous
sync frame, delivered by a loopback receive; without it every broadcast
carries previous_transmission_timestamp_usec 0 and slaves never adjust.
The SocketCAN platform had no loopback at all, never filled the UTC
timestamp of received frames, and clock::adjustUtc() was a stub, so the
UTC clock was wall time rather than the HRT base the sensor bridges
expect. Time sync was therefore a silent no-op on every i.MX RT, Kinetis
and S32K board.

Frames sent with CanIOFlagLoopback are now handed back from receive()
stamped at the moment they entered a hardware mailbox: NuttX has no TX
echo, so this stands in for the true transmission time with a bias of at
most one frame time when the sync frame wins arbitration. All received
frames get a UTC timestamp derived from SO_TIMESTAMP, and SystemClock is
one shared instance whose first adjustUtc() is absolute, so the existing
hrt_absolute_time() seed makes bus time equal HRT as on STM32.
2026-09-01 15:36:11 -06:00
Jacob Dahl 0bc69038da fix(uavcan): report FlexCAN bus state and apply UAVCAN_BITRATE on SocketCAN (#28457)
* fix(uavcan): report FlexCAN bus state and error counters on SocketCAN

The SocketCAN platform driver returned 0 from getErrorCount(),
getRxQueueOverflowCount() and isInBusOffState(), so a controller sitting
error-passive or bus-off was invisible from `uavcan status` and the
can_interface_status topic on every i.MX RT board. Read them through
NuttX's SIOCGCANERRORS instead, and print fault confinement, TEC/REC and
RX overruns per interface. Builds without the ioctl keep the old zeros.

* fix(uavcan): apply UAVCAN_BITRATE on SocketCAN

CanDriver::init() ignored its bitrate argument, so the controller stayed
at the Kconfig rate and a bus configured for anything but 1 Mbit/s never
came up. Program the nominal rate through SIOCSCANBITRATE when it differs
from what the driver reports, keeping the data-phase settings untouched.

The driver applies the timing at the next ifup, so the interface is taken
down around the request. Older PX4/NuttX restarted a running controller
from inside the ioctl instead, which on FlexCAN with ECC RAM
initialisation is a bus fault; against that NuttX the rate is left as
configured with a warning.

* fix(boards): switch SocketCAN defconfigs to NETDEV_CAN_IOCTL

PX4/NuttX#401 merges the per-command CAN ioctl options into a single
NETDEV_CAN_IOCTL, so the old BITRATE/FILTER/ERROR names no longer
exist there and would be silently dropped, disabling the whole CAN
ioctl block. Requires the NuttX gitlink to include PX4/NuttX#401;
until that bump the option is dropped against the old tree and the
bitrate/error ioctls are inert at runtime (the code builds either
way). mr-canhubk3 is included so its bitrate ioctl survives the bump.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* chore(nuttx): bump NuttX to the px4_firmware_nuttx-10.3.0+ tip

Picks up PX4/NuttX#401 so SIOCGCANERRORS, SIOCSCANBITRATE-requires-ifdown
and NETDEV_CAN_IOCTL are in the tree the SocketCAN changes need. Also
includes PX4/NuttX#399 (STM32F412VG/CG chip selections), which landed
between main's gitlink and #401.

Assisted-by: Grok:grok-4.6
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-01 15:25:46 -06:00
Jacob Dahl 9338031b11 fix(ci): attach FMU bootloader .bin to GitHub releases (#28495)
* fix(ci): attach FMU bootloader .bin to GitHub releases

Releases only shipped `_bootloader.px4`, the USB uploader envelope. SWD recovery needs the raw `.bin` at 0x08000000.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(ci): do not attach unused bootloader .px4

USB flashing would write it into the application slot. SWD uses the raw .bin.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(ci): omit bootloader SBOMs from GitHub releases

GitHub Releases glob artifacts/**/*.sbom.spdx.json. Bootloader metadata dirs are not a recovery artifact.

*_bootloader_* variants (e.g. bootloader_secureboot) stay omitted: those images are baked with in-tree test keys.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* docs(bootloader): point SWD recovery at the release .bin

Match the cannode pre-built blurb. The .px4 envelope is not the SWD image.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* docs(bootloader): scope release .bin to SWD programmers

gdb load needs ELF program headers. The release image is a raw .bin for ST-Link / CubeProgrammer / OpenOCD, and only in-tree *_bootloader targets are attached.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-01 14:41:26 -06:00
Jacob Dahl df05384209 chore(nuttx): bump NuttX to the px4_firmware_nuttx-10.3.0+ tip (#28497)
Picks up PX4/NuttX#402, where memmem() missed a needle that ends the
haystack, and a configurable STM32 Ethernet TX watchdog timeout whose
default is the 60 s the drivers hard-coded before.
2026-09-01 14:00:46 -06:00
Jacob Dahl 6e881aa826 feat(boards/ark_pi6x): add fixed-wing build variant (#28493)
The default label is multicopter-only and already occupies 96.8% of the
1792 KB application slot, so the fixed-wing stack cannot be added
alongside it. This variant swaps the two, mirroring ark_fpv_fw, and
lands at 95.3%.

PX4 only bundles the 2xxx/3xxx airframes when FW_RATE_CONTROL is
enabled, so the plane entries added to the board airframe whitelist are
inert for every other label.
2026-09-01 11:53:37 -06:00
Jacob Dahl b9c85cc2ea fix(heater): publish status once per period, drop copied battery fields (#28437)
Run() published heater_status at both GPIO edges, 200 Hz for a 100 Hz
controller, and the default log profile recorded it at full rate. At
100 % duty the off phase ran with a zero delay, so the element was
switched off and back on every period and heater_on read false for that
instant. heater_current and supply_voltage were battery_status.current_a
and voltage_v, already logged there, and nominal_multiplier read 0 unless
HEATERn_NOM_V compensation was active.

The off phase no longer publishes and is skipped at full duty; the status
goes out once per controller cycle with heater_on meaning the element is
driven this period. The two battery fields are removed (the voltage is
still read for the V_nom compensation), nominal_multiplier is 1 when no
scaling is applied, and heater_status is logged at 1 Hz by default.

Assisted-by: Claude:claude-fable-5

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-09-01 10:14:54 -06:00
Jacob Dahl 34f467b610 fix(uavcan): reply to file services on the request interface (#28455)
* fix(uavcan): reply to file services on the request interface

Firmware-update File.Read responses were queued on every CAN interface. Dual-CAN boards use independent buses, so the other interface's TX queue filled with a copy of the image.

Assisted-by: Grok:grok-4.6
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* perf(uavcan): set the response iface mask only for opted-in servers

The unconditional write restored AllIfacesMask on every response of every
ServiceServer instantiation, costing ~168 B of flash for a mask that was
already in place.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(uavcan): restore all-iface mask when request-iface replies are off

The per-response write was dropped to save flash, so disabling the option left the publisher on the last request's interface.

Assisted-by: Grok:grok-4.6
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-30 18:31:15 -06:00
Jacob Dahl cbc6a6f7ab fix(ark/fmu-v6xrt): size the IOB pool for CAN receive (#28462)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (macos-15) (push) Canceled after 0s
MacOS build / build (macos-latest) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
NuttX queues every received CAN frame in one I/O buffer from the pool
shared with the rest of the network stack. At 24 buffers the two FlexCAN
interfaces together have 7 ms of headroom at 3260 frames/s, and the
uavcan thread is preempted for longer than that: the socket layer
dropped 445 frames per 300 s on the bench, which shows up as transfer
errors and failed DroneCAN parameter reads. 192 buffers (40 KB of the
1.1 MB free) give 59 ms at that rate; IOB_NCHAINS follows by default.

Assisted-by: Claude:claude-fable-5

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-29 21:16:30 -06:00
Jacob Dahl 3f6b26f90b fix(ark/fmu-v6xrt): start the BMP390 as internal on Shared I2C2 (#28453)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (macos-15) (push) Canceled after 0s
MacOS build / build (macos-latest) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
I2C2 carries the onboard BMP390 and the PM2 connector. Labelling it External forced -X, so the baro was classified external.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-29 12:20:57 -06:00
Jacob Dahl 0a441d9443 fix(commander): capture home on the ground before motors spin (#27734)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (macos-15) (push) Canceled after 0s
MacOS build / build (macos-latest) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
Sync ROS 2 messages to px4_msgs / sync_to_px4_msgs (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
Move the arm-time home capture to before ARMING_STATE_ARMED so it runs before the ESCs are enabled and prop wash perturbs the baro, and gate it on landed && (!_mission_in_progress || seq_current == 0). The previous !_mission_in_progress gate skipped the capture entirely when arming straight into a mission, leaving home referenced to a stale (cold-baro, fewer-sats) on-ground fix. seq_current distinguishes a genuine mission start from a mid-mission re-arm (land/disarm/continue), which must not move home.

Part of #27730.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-28 23:12:28 -06:00
Jacob Dahl 3969a1b8ff fix(ci): publish flashable CAN node firmware (#28445)
* fix(ci): publish flashable CAN node firmware

Release packaging only collected .px4 files, which the DroneCAN bootloader cannot flash.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(ci): attach canbootloader .bin as the SWD image

The cannode bootloader is a raw .bin for st-flash, not a .px4 envelope.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(ci): drop cannode.zip from release assets

The target-named .uavcan.bin and _canbootloader.bin files are the flashable images; the zip only duplicated them.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-28 22:17:34 -06:00
Jacob Dahl c9f5442402 feat(sensors): classify sensors independently of bus topology (#28352)
* feat(sensors): decouple sensor internal/external classification from the bus

A sensor's internal/external classification was derived from its bus
(px4_i2c_bus_external / px4_spi_bus_external), but a bus is a wire: one bus
routinely serves both chips soldered on the FC and a pinned-out connector, and
any onboard sensor on such a shared bus was classified external. That hands an
onboard mag an operator-settable rotation instead of the board rotation, feeds
an onboard baro's self-heated die temperature into air density as if it were
ambient, and inverts the 75/50 default priority. FMU-v6C and AirBrainH743
worked around it with hand-rolled device_id whitelists behind
BOARD_OVERRIDE_I2C_DEVICE_EXTERNAL; FMU-v6XRT's second onboard baro was simply
misclassified.

Classify the device instead of the bus: drivers publish is_external in
sensor_accel/gyro/mag/baro, derived from the device id by default and
overridden by the new -O start flag ("onboard") for onboard sensors that share
a bus with an external connector. -I/-X stay pure bus probe filters.

The question "is this sensor external" had four answers. It now has one, with
a single override point:

  -O -> I2CSPIDriverConfig::external -> Device::set_external() and the
        PX4* wrappers -> is_external in the sensor topic

px4_i2c_device_external() was px4_i2c_bus_external() with a device id decode in
front, and calibration::DeviceExternal() forwarded to device_is_external();
both are gone. Device::external() stops being a bus query: it is non-virtual,
defaults to device_is_external(), and takes the declared value through
set_external(), so the five I2C/SPI overrides that used to answer it from the
bus are deleted and cannot diverge again. device::I2C and device::SPI set it
from the config, so every driver built on the bus framework follows -O without
doing anything. px4_i2c_bus_external() and px4_spi_bus_external() survive as
what they honestly are - bus predicates - reachable only through the fallback.

-O is opt-in per driver (BusCLIArguments::support_onboard), the same way -k is:
a flag that every driver advertised but only a handful honoured would be a
silent no-op on the rest. This also drops the special case for the mcp23009 and
mcp23017 GPIO expanders, which use -O for their output state and simply do not
opt in.

sensor_gyro_fifo carries is_external too. VehicleAngularVelocity prefers the
FIFO topic for any IMU that publishes one, so without it the rate controller
would take its rotation from the bus while VehicleIMU took it from the topic -
the same chip, two classifications.

The BOARD_OVERRIDE_I2C_DEVICE_EXTERNAL hook is removed along with both board
implementations, replaced by -O on the affected rc.board_sensors lines.
FMU-v5x, MR-CANHUBK3, KakuteF7, NXT-Dual and MicoAir H743-Lite each start an
onboard barometer on an external bus and get the flag as well.

Assisted-by: Claude:claude-fable-5, Claude:claude-opus-5[1m]
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* feat(sensors): treat shared buses as first-class topology

A boolean internal/external flag cannot describe a bus that carries both
hard-mounted chips and a connector. -O was an opt-in override for that
case, so most drivers silently ignored it and classification still
followed the bus.

Declare Internal / External / Shared on the bus, probe External and
Shared, and classify each sensor from -I/-s vs -X/-S.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(ci): drop stale ITCM symbols and clang-tidy errors

ITCM lists still named calibration::DeviceExternal and
px4_spi_bus_external after both were removed. The host test stubs
forwarded varargs in a way the analyzer rejected, and stripped I2C
headers kept extra trailing newlines.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(sensors): inline device_is_external into the header

Reaching it through drivers__device pulled the kernel-only library into
the userspace image of a protected build; giving it a library of its own
put an archive referencing px4_i2c_buses/px4_spi_buses after the board
library that defines them. Inlining sidesteps both.

* fix(sensors): compile tcbp001ta and probe canhubk3 GPS mag

tcbp001ta is not an I2CSPIDriver, so config.external does not exist; it
only ever starts on internal SPI. -X on canhubk3 I2C2 never ran because
that bus is Internal.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-28 21:14:13 -06:00
Jacob Dahl b4f9388cd9 fix(drivers/imu): clip FIFO samples against the declared range, not the int16 rail (#28310)
* fix(drivers/imu): clip FIFO samples against the declared range, not the int16 rail

updateFIFO() counted a sample as clipped only within 1 LSB of INT16_MIN
or INT16_MAX, while update() compares against _clip_limit (range/scale).
The two agree only for sensors whose sensitivity is exactly range/32768.
ST parts leave headroom in the word: the LSM6DSV80X high-g channel at
+/-80 g and 3.904 mg/LSB saturates at 20492 counts, its gyro at
+/-4000 dps and 140 mdps/LSB at 28571, the LSM9DS1 and ADIS16607 are
similar, and the BMI055's 12-bit accel word never reaches the rail at
all. None of them could report a clip on the FIFO path, so the EKF's
delta-velocity clipping handling never engaged on those sensors.

Compare against _clip_limit on the FIFO path as well. For rail-scaled
sensors the threshold moves from 32766 to 32735 counts (the existing
0.999 margin), which is what the non-FIFO path already used.

* docs(msg): SensorGyroFifo/SensorAccelFifo counts are raw, not SI

x/y/z are int16 counts; SI is count * scale. The comments called them
rad/s and m/s^2, which is what sensor_gyro/sensor_accel carry.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-28 21:13:44 -06:00
Jacob Dahl 243936abe2 fix(drivers/invensense): drop invalid gyro FIFO samples (#28308)
With FIFO_HOLD_LAST_DATA_EN clear the sensor inserts -32768 for accel and gyro samples that carry no data (power-on until the first ODR sample, a disabled sensor, mismatched ODRs) and limits valid samples to -32766..+32767. The accel path already drops these; the gyro path published them as negative full scale.
2026-08-28 21:13:24 -06:00
Jacob Dahl 55bb62c2c7 refactor(sensors)!: replace SENS_INT_BARO_EN with CAL_BAROn_PRIO (#28351)
* refactor(sensors)!: replace SENS_INT_BARO_EN with CAL_BAROn_PRIO

SENS_INT_BARO_EN gated driver startup, so disabling the internal barometer
also stopped it being logged, and every board had to reimplement the check in
its rc.board_sensors - most never did. CAL_BAROn_PRIO already selects
barometers per device_id, so use it instead: 0 now means the driver runs and
the data is logged, corrected and voter-tracked, but the sensor is never
selected, never used as a failover, and never blocks arming.

Disabling a barometer that way was previously only partly effective. The voter
could still pick a priority-0 sensor when it was the only one with data, fault
demotion could raise a disabled sensor's priority back to 1, and the clamp in
ParametersUpdate() could not reach 0 at all once a sensor had been demoted.

Excluding a sensor from selection must not stop it being evaluated:
DataValidator::confidence() is the only thing that maintains the error state
every sensor reports, so a priority-0 sensor is still scored, just never
chosen. Handing over from a sensor the operator disabled is likewise not a
failover, and is no longer counted as one.

The arming check follows the magnetometer's shape: the barometer actually in
use has to be healthy, as does any the estimator is fusing, but the spares do
not. Having no enabled barometer, or none the voter can select, each reports
its own failure.

A disabled barometer now also receives the relative and GNSS offsets, which is
what makes its logged data directly comparable to the primary.

Users who had SENS_INT_BARO_EN=0 lose the setting on upgrade: the calibration
slot depends on enumeration order and is not knowable at import time. The
internal barometer returns as a failover only, since external barometers
default to priority 75 against 50.

Assisted-by: Claude:claude-opus-5[1m]
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* test(sensors): run the data validator tests in CI

The DataValidator tests still included <validation/data_validator.h>, a path
that stopped existing when the library moved out of ecl, and their CMakeLists
was never added to any build - so nothing had compiled them, let alone run
them, for years.

Port them to gtest and register them with px4_add_unit_gtest so they run with
the rest of the unit tests. Coverage is unchanged apart from replacing rand()
with a fixed sequence, so a failure reproduces, and adding the priority-0
cases: a disabled sensor is never selected even when it is the only one with
data, it still reports its own error state, and handing over from one is not
a failover.

DataValidator is otherwise a leaf library, but print() reaches for the logging
macros and the high-resolution timer, which would drag uORB, the work queues
and the POSIX daemon into a host test. A small stub translation unit supplies
those three symbols instead.

Assisted-by: Claude:claude-opus-5[1m]
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-28 20:27:18 -06:00
Jacob Dahl 3826a2a706 fix(ark): pin the primary IMU on fmu-v6x and pi6x (#28444)
* fix(ark/fmu-v6x): pin the bus-1 IMU as the primary

The three IMUs run at equal voter priority, so the sensor voter selects
the primary by whichever validates first at boot and keeps it — a
non-deterministic race. Seed the intended IMU's calibration slot (per
hwtype: IIM-42652 on ARKV6X000, IIM-42653 on ARKV6X001, both on SPI1,
already the heater's regulated sensor) with a higher priority so
selection is deterministic; the voter still fails over to the other
IMUs if it degrades.

param set-default is shadowed by a stored calibration, so this takes
effect on a fresh flash calibrated afterwards; an already-calibrated
board keeps its stored priority until the params are reset.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(ark/pi6x): pin the SPI1 IMU as the primary

Same equal-priority voter race as fmu-v6x: two ICM-42688-P run at equal
priority, so the primary is decided by a boot race. Seed the SPI1 IMU's
calibration slot (already the heater's regulated sensor) with a higher
priority so selection is deterministic, with fallback to the SPI2 IMU on
degradation. Replaces the placeholder TODO on HEATER1_SENS_ID, whose id
is the running SPI1 device.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-28 19:10:19 -06:00
Jacob Dahl 7eede5e670 fix(drivers/invensense): always publish full-scale FIFO data (#28134)
Publish the 16 bit FIFO registers directly and set the scale once in
Configure(), removing the dual decode pass and the per-batch scale
switch. The 20 bit extension nibble is no longer used.

Also corrects the IIM42653 range, which reported +/-16 g and +/-2000 dps
while ACCEL_CONFIG0/GYRO_CONFIG0 program +/-32 g and +/-4000 dps.
2026-08-28 18:44:13 -06:00
Jacob DahlandBalduin c092f80f38 refactor(drv_hrt): out-line hrt_elapsed_time to save flash (#28443)
hrt_elapsed_time() was a static inline expanding to ~15 bytes at each
of its ~450 call sites (call to hrt_absolute_time plus 64-bit compare
and subtract). Move the definition into px4_platform so each call site
is a single branch. px4iofirmware does not link px4_platform, so it
compiles the new source directly.

Saves 3592 bytes of flash on px4_fmu-v6x_default.

Co-authored-by: Balduin <balduin@auterion.com>
2026-08-28 18:40:25 -06:00
Jacob DahlandBalduin 26c5ef2076 refactor(px4_work_queue): out-line ScheduledWorkItem ctor and ScheduleNow (#28440)
* refactor(px4_work_queue): out-line ScheduledWorkItem constructor to save flash

The header-inline constructor zero-initialises the hrt_call member at
every derived work-item constructor across ~150 classes. Move the
definition (verbatim) into ScheduledWorkItem.cpp next to the
destructor; the derived constructors already pay a call into the base
constructor chain, so this only removes the duplicated inline stores.

Saves 1376 B of .text on px4_fmu-v6x_default.

Signed-off-by: Balduin <balduin@auterion.com>

* refactor(px4_work_queue): out-line WorkItem::ScheduleNow to save flash

The body was inlined at ~240 call sites; a plain call is smaller at
every one of them.

Saves 672 B FLASH on px4_fmu-v6x_default.

Signed-off-by: Balduin <balduin@auterion.com>

---------

Signed-off-by: Balduin <balduin@auterion.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-08-28 18:31:57 -06:00
Jacob DahlandBalduin 28c5230c2b refactor(mavlink): de-templatize handle_message_command_both to save flash (#28442)
The two instantiations (mavlink_command_long_t / mavlink_command_int_t)
compiled to byte-identical 760 B functions: the body only reads command,
target_system/component and param1-4, which both decode handlers copy
1:1 into the vehicle_command_s they pass alongside. Take only the
normalized vehicle command instead.

Saves 760 B FLASH on px4_fmu-v6x_default.

Signed-off-by: Balduin <balduin@auterion.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-08-28 18:26:21 -06:00
Jacob DahlandBalduin 255d9c602b refactor(platform): out-line BusCLIArguments constructor to save flash (#28441)
The constructor is inlined into every I2C/SPI driver's command-line
entry point, and with it the ~15 default member initializers and the
32-byte _options zero-fill, costing ~50-70 B per driver. Move the
definition (verbatim) into i2c_spi_buses.cpp, which already holds the
rest of the CLI parsing code. Purely cold-path (driver start/CLI).

Saves 2456 B of .text on px4_fmu-v6x_default.

Signed-off-by: Balduin <balduin@auterion.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-08-28 18:26:09 -06:00
Jacob DahlandBalduin d3ca991b8e refactor(mavlink): make streams_list constexpr to save flash and RAM (#28439)
The StreamListItem constructor was not constexpr, so the ~100-entry
streams_list was built at boot by 2.3 KB of static-init code into
.bss. Constant-initialise it into .rodata instead.

Saves 1272 bytes of flash and 1088 bytes of RAM on px4_fmu-v6x_default.

Signed-off-by: Balduin <balduin@auterion.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-08-28 18:25:41 -06:00
Jacob Dahl 55aa0ea75e feat(dshot): EDT and BDShot HAL contract on i.MX RT (#28412)
* feat(dshot): EDT and BDShot HAL contract on i.MX RT

The FlexIO driver ignored edt_enable and only marked a channel ready after a CRC-good post-training frame, so one missing ESC blocked telemetry for every motor. Match the STM32 consumer contract: ready every cycle, consecutive CRC hysteresis, train on any valid GCR, and gate FlexIO output from up_dshot_arm.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(dshot): keep the trained BDShot offset across an i.MX RT dropout

Going offline restarted baud training from BDSHOT_TCMP_MIN_OFFSET. The
offset tracks the ESC oscillator, not the link, so a transient dropout
threw away a still-valid result and pinned the channel offline for the
whole re-sweep, starving the ESC RPM notch. Train once on first connect
and let the success hysteresis handle recovery.

Also close three smaller gaps: the sweep stopped one round early and
never evaluated BDSHOT_TCMP_MAX_OFFSET, up_bdshot_get_erpm carried a
bound check the next line subsumes, and re-arming left a stale channel
state that latched a garbage SHIFTBUFBIS read as a response.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(dshot): harvest i.MX RT BDShot RX before the next TX

dshot_motor_data_set flipped state to DSHOT_START and cleared SHIFTSTAT before the frame was consumed, so a delayed FlexIO IRQ transmitted irq_data instead of latching telemetry. Harvest under a critical section and skip the burst while the receive window is still open.

Zero driver state in up_dshot_init so a module restart cannot keep a stale online bit through retraining. After a second offline period, restart the TCMP sweep so a wrong baud can recover.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(dshot): guard the whole i.MX RT DShot cycle, not just the receive

The busy window started at the IRQ's receive timestamp, so a trigger landing during the frame itself reconfigured the shifter mid-transmit, and up_dshot_arm enabled the shifter interrupt with nothing queued. The first trigger after arming also counted a missing response, and the trained TCMP was updated outside the critical section the IRQ reads it in.

Stamp the cycle at transmit and size the window for frame, ESC turnaround and response. Latch, decode and reconfigure in one critical section, mask the IRQ by the enable registers instead of a channel mask, and leave the timer interrupt off while receiving. Warn for outputs the FlexIO cannot serve instead of dropping them silently.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(dshot): keep the i.MX RT DShot trigger out of a critical section

up_dshot_trigger runs at the output rate, so masking every interrupt on the MCU for the whole latch, decode and reconfigure pass was far too long. The IRQ only acts on a channel whose interrupt is enabled and the trigger only touches a channel whose cycle is over, so the two contexts never own the same channel at once and none of that needed a lock.

What does: the SHIFTBUF write and the interrupt enable must not be separated by preemption, since the IRQ has to queue the second word within 20 us at DShot1200, and SHIFTSIEN/TIMIEN have no set/clear aliases, so the thread's read-modify-write must be atomic against the IRQ's. That is a dozen register accesses.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(dshot): do not clear SSF after re-enabling the i.MX RT transmit shifter

In transmit mode SSF sets on enable and is the timer's active-low trigger. Clearing it before SHIFTBUF is written asserts the trigger, so the timer shifts an empty shifter for one compare and the real word lands late: 14 garbage sub-bits, a truncated frame, no ESC response. Only the SHIFTBUF write may clear it.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(dshot): resynchronise the i.MX RT BDShot receive timer on the channel pin

The receive timer was set to reset on its timer pin's rising edge, but the timer pin was left at FXIO_D0, so the baud counter never resynchronised; and with the timer output starting high the first shift came a full period after the start edge, so every sample sat on a bit boundary. Only a baud 2-3 % faster than the ESC's pulled the samples inside the bits, which is why training found a three-count window and why channels fell off it per run.

Point the timer pin at the channel pin so a baud-mode reset reloads the divider on every falling edge of the response, and start the output low so the shift lands mid-bit. On an ARK 4in1 at DShot300 every offset from -10 to +15 now decodes 198/200, all four channels train on the first sweep, and the CRC error rate matches the previous driver. The status output shows the training mask.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* perf(dshot): run the i.MX RT DShot cycle from ITCM

up_dshot_trigger executed from the NOR over XIP, so its ~1 µs critical section measured 5.6 µs worst case on instruction cache misses at 800 Hz. Map the per-cycle path — the trigger, its FlexIO callees, the HAL getters and the DShot module's Run/updateOutputs/telemetry — into ITCM on fmu-v6xrt, about 3 KB. decode_gcr_payload is inlined so the list needs no compiler-named partial section.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(dshot): keep the i.MX RT BDShot offset across a dropout

A missing response counted toward the re-sweep, so any dropout over 0.5 s — a wire, an ESC power cycle — cost a full seven-second sweep after the ESC came back although its oscillator had not changed. Only frames that arrive and fail to decode restart training now; a dropout just takes the channel offline and it is back 200 good frames after the ESC returns.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* perf(dshot): sweep the i.MX RT BDShot baud in 25-frame rounds

With the receive timer resynchronising, every offset is either clean or fails outright, so 200 frames per offset only stretched the sweep to eight seconds at 800 Hz once the whole window started passing. 25 frames with one allowed miss give the same mask in a second, well inside the five seconds DShot.cpp ignores telemetry after boot.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* docs(dshot): restore the i.MX RT FlexIO register comments

The rewrite dropped the comments naming what each shifter and timer register write configures. They are the only prose map of the FlexIO setup, so keep them wherever the code they describe survives.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-28 17:27:27 -06:00
Jacob Dahl 595395e1e4 fix(dshot): retry EDT enable until the ESC confirms it (#28438)
EDT enable was sent once, a second after bidirectional telemetry came online, and never checked. AM32 only executes commands once armed, which takes a second of zero throttle plus its arming tune, so that single request can be dropped. Bluejay clears EDT whenever the motor stops, so it was off after the first flight. Treat any EDT frame after a request as confirmation, retry once a second up to five times, and start over on reconnect and on disarm.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-28 17:26:16 -06:00
Jacob Dahl a3189c5379 fix(motion_planning): stop auto setpoint drifting past an unreached waypoint (#27733)
* fix(motion_planning): aim at the target once the trajectory passes it

The L1 look-ahead point was projected forward along the prev->target line even after the smoothed trajectory passed the target. When a multicopter overshoots a mission waypoint the navigator has not marked reached, the setpoint triplet stays fixed on that waypoint, so the look-ahead kept marching down the extended leg and the vehicle drifted away from it indefinitely (no failsafe) instead of braking onto it.

Once the trajectory is at or past the target along the leg, return the target as the crossing point so the smoother decelerates and holds on the waypoint. Normal cornering is unaffected: while approaching the target the look-ahead is unchanged, and the navigator advances the triplet before the trajectory passes the waypoint.

Part of #27730.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* test(motion_planning): cover drift past an unreached waypoint

Add a PositionSmoothing regression test that flies a fixed waypoint
triplet through the target without the navigator advancing, as happens
when a multicopter overshoots a waypoint it cannot accept. It asserts
the smoother brakes and turns back to the target instead of marching the
look-ahead point down the extended leg, which otherwise drives the
setpoint away from the waypoint unbounded with no failsafe.

While here, compute the prev->target vector and its length once in
_getL1Point instead of deriving the unit vector and the leg length
separately. Behavior-neutral.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-28 16:27:51 +02:00
Jacob Dahl c2cf9be6aa fix(uavcan): classify TX frames dropped while peeking the queue (#28397)
peek() discards entries whose deadline has passed before returning the head of
the queue, and counted them with a bare registerRejectedFrame(). That is the
fourth drop path; the reported split covers three. The frames landed in the
total and in neither bucket, so `uavcan status` printed lines like

	TX rejected:   64 frames (0 expired, 0 no memory)

which reads as "no drops worth caring about" when 64 frames of in-flight
transfers had just been discarded. Serving a DroneCAN node firmware update hits
this path on every interface.

The total stays a separate counter rather than the sum of the two buckets: it is
what makes an unclassified path visible at all, which is how this one was found.
2026-08-26 16:37:23 -06:00
Jacob Dahl 1888206a4e fix(uavcan): correct TX queue block accounting and report queue pressure (#28395)
* fix(uavcan): count a TX queue block only once the pool provides it

LimitedPoolAllocator::allocate() incremented used_blocks_ before asking the
underlying allocator, so an allocation the pool could not satisfy still spent
quota. Nothing ever hands that block back, because deallocate() only runs for a
pointer that was returned, so every queue that asked for memory while the pool
was empty stays permanently smaller than its configured limit.

The pool is shared between the RX side and one TX queue per interface, so it
does empty transiently under load -- a DroneCAN node firmware update is enough.
The damage accumulates and is only cleared by restarting the node.

* feat(uavcan): report TX queue depth and why frames were dropped

`uavcan status` gains, per interface:

	TX queue peak: 44/84 blocks
	TX rejected:   80 frames (78 expired, 2 no memory)

CanTxQueue already counted rejected frames but tx_queues_ is private and
CanIOManager exposed no accessor, so the count was unreachable. It also conflated
three causes: a frame handed over with its deadline already passed, a queued frame
evicted as expired to make room, and a frame dropped because the queue was at its
block limit. Only the last two are memory pressure, and they call for opposite
responses, so a single count sends you the wrong way.

The peak is what makes the count actionable. The limit is
pool_soft / (num_ifaces + 1) + 1, derived from the soft capacity in
allocator.hpp, so a peak sitting at the limit says raising that capacity will
help and a peak well under it says the frames are ageing out for some other
reason and a larger pool will not.

Measured on an FMU-v6XRT serving a DroneCAN node firmware update: one node holds
44 of 84 blocks and drops nothing, another pins the limit and drops hundreds. The
counts alone did not distinguish those.
2026-08-26 15:39:15 -06:00
Jacob Dahl cd512ae9d7 chore(nuttx): bump NuttX for the FlexCAN TX fixes (#28393)
The imxrt FlexCAN driver left the TX mailbox CS word uninitialised, so
classic frames went out with EDL set and appeared on the wire as CAN FD,
and its TX timeout handler aborted mailboxes that were still
transmitting. Either one takes the interface error passive and it never
recovers.

Picks up PX4/NuttX #395 and #396.
2026-08-26 12:29:00 -06:00
Jacob Dahl 3540e00466 chore(nuttx): bump NuttX for the USB resume interrupt fix (#28383)
Brings in PX4/NuttX#393 (backport of apache/nuttx#19936). The DWC2-derived
USB device drivers masked off the WKUP interrupt, so CLASS_RESUME was never
delivered. cdcacm_suspend() marks the serial device disconnected and the
matching resume never runs, leaving the CDC/ACM port returning -ENOTCONN for
the rest of the boot after the first host autosuspend.

Assisted-by: Claude:claude-opus-5[1m]

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-25 10:24:40 -06:00
Jacob Dahl 677d1fa500 chore(nuttx): bump NuttX for the imxrt FlexCAN TX ordering fix (#28381)
Brings in PX4/NuttX#394 (backport of apache/nuttx#19957). imxrt FlexCAN
handed out the lowest free TX mailbox, so a refilled low mailbox could
win arbitration over an older frame of the same CAN ID still queued in a
higher one. Multi-frame DroneCAN transfers arrived out of order and were
dropped by the receiver.

Assisted-by: Claude:claude-opus-5[1m]

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-25 10:21:36 -06:00
Jacob Dahl dc53eeb0cd chore(claude): build one target and shorten PR bodies in the pr skill (#28382)
Building both a board and SITL for every PR wastes minutes on changes that
cannot reach either target. Build the one target the diff can affect, or
none. Descriptions get read only if they are short, so cap them at a
sentence or two per section and drop the CI boilerplate.

Assisted-by: Claude:claude-opus-5[1m]

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-25 10:13:21 -06:00
Jacob Dahl e172022c6a fix(uavcan): stop losing and reordering SocketCAN TX frames (#28363)
* fix(uavcan): retry SocketCAN frames the driver could not take

CanIface::send() forwarded sendmsg()'s return straight to libuavcan. A frame
the CAN driver had no free hardware mailbox for never reached the bus and
NuttX does not buffer it, so returning negative there loses it -- and losing
one frame destroys the whole multi-frame transfer it belonged to.

libuavcan already distinguishes "queue full, retry" (0) from "error" (-1), so
report 0 and let it re-queue the frame. A non-blocking send the driver could
not take immediately surfaces as ETIMEDOUT from net_timedwait(), not the
ENOBUFS or EAGAIN one would expect, which is why matching only those left the
frame loss in place.

receive() had the mirror problem: uc_can_io maps any negative onto -ErrDriver,
so an empty read after a spurious POLLIN was reported as a driver failure.

Assisted-by: Claude:claude-opus-5[1m]
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* fix(uavcan): do not let a new frame overtake the TX queue

CanIOManager::send() asks the TX queue whether it holds something of equal or
higher priority and, if so, sends that first. But when sendFromTxQueue()
returns 0 -- it had nothing left to send, e.g. the head expired -- the code
fell through and sent the new frame directly, putting it on the wire ahead of
the equal-priority frames still queued behind it.

Every frame of a multi-frame transfer shares one CAN ID, so those are exactly
equal priority: the bypass reorders a transfer that the queue was holding in
the right order, and the receiver discards it on the toggle bit.

Only send directly when the queue does not already own the ordering.

Assisted-by: Claude:claude-opus-5[1m]
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-25 09:47:25 -06:00
Jacob Dahl af2e7b4311 chore(claude): harden the rebase-onto-main skill against losing work (#28368)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (macos-15) (push) Canceled after 0s
MacOS build / build (macos-latest) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
Sync ROS 2 messages to px4_msgs / sync_to_px4_msgs (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
The skill force-pushes over the only copy of the pre-rebase head without
ever checking that the rebase preserved it, and says nothing about the two
things that most often go wrong around it.

Adds a git range-diff gate before the push, the worktree collision that
makes git checkout fail mid-loop, and the --onto follow-up a stacked child
branch needs once its base moves.

Assisted-by: Claude:claude-opus-5

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-24 17:18:15 -06:00
Jacob Dahl 1427414a44 fix(nuttx): pin the submodule back to the px4_firmware_nuttx-10.3.0+ tip (#28367)
#28366 unintentionally carried a submodule bump to 39508f26a2, which
exists on PX4/NuttX only as refs/pull/394/head. main therefore depends on
an open pull request's head commit instead of a branch commit: it resolves
today only because GitHub serves PR refs, and breaks if #394 is closed or
its branch is force-pushed.

The two imxrt FlexCAN commits it pulled in are unreferenced by anything on
main and land properly when PX4/NuttX#394 merges.
2026-08-24 16:32:05 -06:00
Jacob Dahl 757cf137b1 fix(uavcan): name the DroneCAN node after the board (#28366)
Every FMU has advertised NodeInfo.name "org.pixhawk.pixhawk" since 2014, so an
ARK FMU-v6XRT, an ARK FMU-v6X and a Pixhawk 6X are indistinguishable on the bus
by anything except their unique id. The cannode side already derives its name
per board through board_get_uavcan_hw_name(); do the same on the FMU side from
px4_board_name(), which is the only board identity the module already has.
2026-08-24 16:08:17 -06:00
Jacob Dahl 02c82831e0 feat(gps): Galileo HAS mode, correction protocol reporting, GPSDrivers bump (#28335)
* feat(gps): Galileo HAS mode for the ZED-X20P and GPSDrivers bump

GPS_UBX_MODE 8 selects the new UBXMode::GalileoHAS. The receiver only
processes HAS while host corrections are off, which makes RTCM and SPARTN
from the autopilot dead input, so it is an explicit mode rather than a
default and the description says what it gives up.

The submodule bump also brings UBX-SEC-SIG jamming state for F9 HPG 1.51
and X20, where the MON-RF flag is deprecated and always 0, and stops the
F9P-L1L2 path from warning about a missing jamming monitor on 1.51.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* feat(sensor_gps): report the correction protocol, not just RTCM status

The u-blox driver now reads UBX-RXM-COR, which covers RTCM3, SPARTN and
Galileo HAS, so rtcm_msg_used and rtcm_crc_failed no longer describe
only RTCM. They become corrections_msg_used and corrections_crc_failed,
and corrections_protocol says which protocol the last message was: the
only receiver-side confirmation that a SPARTN stream or HAS is actually
being consumed, and the first time either field has moved on an X20.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-24 13:27:02 -06:00
Jacob Dahl 079363fc51 fix(gps): limit GPS_UBX_DYNMODEL to models u-blox defines (#28252)
The range allowed 1, which is reserved on every u-blox generation, and
9 (wrist-worn watch), which is not available on all products. Either one
is rejected by the receiver, and the dynamic model rides in a CFG-VALSET
whose NAK aborts configuration, so a parameter change alone was enough
to leave the vehicle with no GPS at all.

Cap the range at the airborne models and list the portable, pedestrian
and sea models that were already inside the range but undocumented.
2026-08-24 13:18:50 -06:00
Jacob Dahl 07463f4750 fix(i2c_launcher): default battery index from the -t argument (#28329)
The ctor tested uninitialized _batt_index instead of the parameter, so
`start -b 1` with no -t stored -1 and INA226 rejected it after the 1-3
index check. instances[] was indexed by 1-based bus number but sized as
the bus count, so bus == N wrote one past the end.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-24 12:56:21 -06:00
Jacob Dahl bd62df5e3a fix(drivers/bmp388): poll STATUS without consuming the data registers (#28342)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (macos-15) (push) Canceled after 0s
MacOS build / build (macos-latest) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
drdy_press/drdy_temp are cleared by reading a DATA register (DS table 29),
so the combined STATUS+DATA burst threw away any conversion that completed
mid-read: the flag was gone by the next poll, every retry saw it clear, and
the sample died at the max-conversion timeout. The first poll is scheduled
at the typical conversion time, and for the BMP388 that value (36.9 ms,
DS001 table 21) is 249 us below what DS 3.9.2 computes for this OSR, so it
lands inside the window far more often than on the BMP390 that was bench
tested. Read STATUS alone, then burst DATA_0..DATA_5 once both flags set.

Bound the CMD_RDY gate in RESET as well: a sensor that never reports ready
kept the state machine polling a shared bus at 500 Hz with no way out.

Report a persistently failing CONFIGURE. init() returns before the first
one runs, so a bad calibration CRC left the driver silently not publishing.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-22 10:13:44 -06:00
Jacob Dahl dcbb9eba61 fix(drivers/iis2mdc): read the temperature registers in their own transfer (#28336)
The sample path read 8 bytes from OUTX_L and took the last two as TEMP_OUT. A burst read on this part wraps around inside OUTX..OUTZ (with or without the sub-address auto-increment bit), so those bytes were OUTX again and the published temperature was the X field in disguise: ~25 °C on a calibrated mag, and unresponsive to the board warming from 40 to 55 °C. Reading TEMP_OUT_L/H separately tracks the neighbouring IMUs within ~2 °C.

Assisted-by: Claude:claude-fable-5

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-21 18:09:58 -06:00
Jacob Dahl c9de2151e9 fix(nuttx): provide floating and wide abs() overloads on NuttX targets (#28334)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (macos-15) (push) Canceled after 0s
MacOS build / build (macos-latest) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
NuttX's <cstdlib> only re-exports the C int abs(int) and its <cmath> adds
no abs overloads, so under -nostdinc++ every abs(float/double/int64)
call binds to int abs(int) and truncates, while the same source is
correct under libstdc++ on SITL. -Werror never sees it because the
implicit conversion is not warned on.

A build-time trap (deleted float/double/long/long long abs declarations)
across v5, v6x, v6xrt and v6x_uuv found truncating sites in
lat_lon_alt, ekf2 checkLatLonValidity, ilabs INS position validity,
local_position_estimator bias saturation, uuv_pos_control height
thresholds, hott longitude, the uxrce_dds_client clock-sync delta, and
the uavcan stm32/stm32h7/kinetis UTC lock thresholds.

Force-include a header declaring the overloads libstdc++ provides so
both qualified and unqualified calls resolve correctly everywhere, and
nothing can bind to int abs(int) again.
2026-08-21 15:14:43 -06:00
Jacob Dahl c3df7ba58a fix(drivers/bmp388): collect after forced conversion completes, rewrite driver (#28331)
* fix(drivers/bmp388): collect after forced conversion completes

ScheduleOnInterval counted measure() I2C against the conversion window, so
the first DRDY poll could beat the chip's max conversion time. Not-ready
was counted as a comms error and the next forced-mode write aborted the
sample. Wait from the convert command, retry DRDY until the datasheet max,
and timestamp the midpoint of that conversion.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

* refactor(drivers/bmp388): rewrite as an I2C state-machine driver

The driver carried the Bosch BMP3 API wholesale: #define register maps,
SET_BITS/GET_BITS macros, duplicate calibration structs, an IBMP388
interface over I2C and SPI, and a blocking init that slept in the work
queue. Replace it with the IMU-style layout: constexpr registers, one
class on device::I2C, a RESET/WAIT_FOR_RESET/CONFIGURE/MEASURE/COLLECT
loop, and persistent-failure reset.

No board starts bmp388 on SPI, and the SPI path never accounted for the
BMP3 dummy read byte, so it is dropped. Compensation stays the Bosch
integer implementation.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>

---------

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-21 14:06:29 -06:00
Jacob Dahl 133982a74f fix(drivers/bmp388): wait for cmd_rdy before soft reset (#28330)
STATUS resets to 0, so a single read after POR can miss cmd_rdy and
fail init. Poll until the decoder is ready (tstartup is 2 ms), then
issue 0xB6.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-21 12:06:10 -06:00
Jacob DahlandBalduin 122b2666ec refactor(parameters): merge Param<> constructor calls to save flash (#28320)
Every Param<> constructor inlines param_set_used(handle()) followed by
param_get(handle(), &_val), materialising the handle constant twice at
each of the ~2000 instantiation sites. Fold both into a single
out-of-line param_get_mark_used() so each constructor emits one call.

The failsafe_web build stubs the param API itself, so it gets a matching
stub. The smaller constructors let the compiler fully inline the
defaulted FlightTaskDescend constructor on the ITCM boards, so its entry
is dropped from their linker scripts.

Saves 7232 B of .text on px4_fmu-v6x_default.


Assisted-by: Claude:claude-fable-5

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-08-21 10:35:38 -06:00
Jacob DahlandBalduin 6c53460f2e refactor(platform): out-line ModuleParams setParent and destructor to save flash (#28321)
Both were defined inline in the header, so the List add/remove bodies
were duplicated into the constructor and destructor of every one of the
150+ ModuleParams-derived classes. Construction and destruction are cold
paths; a call is smaller at each site.

Two consumers reach ModuleParams without px4_platform on the link line
and now need the definitions explicitly: health_and_arming_checks (the
functional-ModeManagement test only pulls it in transitively through
modules__commander, after px4_platform) and the failsafe_web emscripten
build, which compiles module_params.cpp directly.

The smaller constructors let the compiler fully inline the defaulted
FlightTaskDescend constructor on the ITCM boards, so its entry is
dropped from their linker scripts.

Saves 3968 B of .text on px4_fmu-v6x_default.


Assisted-by: Claude:claude-fable-5

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Co-authored-by: Balduin <balduin@auterion.com>
2026-08-21 10:34:09 -06:00
Jacob Dahl a0071377f1 fix(drivers/icm45686): retry WHO_AM_I on probe instead of requiring three matches (#28319)
Build all targets / Scan for Board Targets (push) Canceled after 0s
Build all targets / Seed [${{ matrix.chip_family }}] (push) Canceled after 0s
Build all targets / Build [${{ matrix.runner }}][${{ matrix.group }}] (push) Canceled after 0s
Build all targets / Upload Artifacts (push) Canceled after 0s
Checks / Gate Checks [check_format] (push) Canceled after 0s
Checks / Gate Checks [check_newlines] (push) Canceled after 0s
Checks / Gate Checks [module_documentation] (push) Canceled after 0s
Checks / Gate Checks [shellcheck_all] (push) Canceled after 0s
Checks / Gate Checks [validate_module_configs] (push) Canceled after 0s
Checks / Unit Tests (push) Canceled after 0s
MacOS build / build (macos-15) (push) Canceled after 0s
MacOS build / build (macos-latest) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:22.04) (push) Canceled after 0s
Ubuntu environment build / Build and Test (ubuntu:24.04) (push) Canceled after 0s
Container build / Set Tags and Variables (push) Canceled after 0s
Container build / Build Container (amd64) (push) Canceled after 0s
Container build / Build Container (arm64) (push) Canceled after 0s
Container build / Deploy To Registry (push) Canceled after 0s
Docs - Orchestrator / T1: Detect Changes (push) Canceled after 0s
Docs - Orchestrator / T2: PR Metadata (push) Canceled after 0s
Docs - Orchestrator / T2: Metadata Sync (push) Canceled after 0s
Docs - Orchestrator / T2: Link Check (push) Canceled after 0s
Docs - Orchestrator / T3: Build Site (push) Canceled after 0s
Docs - Orchestrator / T4: Deploy (push) Canceled after 0s
Failsafe Simulator Build / build (failsafe_web) (push) Canceled after 0s
ITCM check / Checking nxp_mr-tropic (push) Canceled after 0s
ITCM check / Checking nxp_tropic-community (push) Canceled after 0s
ITCM check / Checking px4_fmu-v5x (push) Canceled after 0s
ITCM check / Checking px4_fmu-v6xrt (push) Canceled after 0s
ROS Integration Tests / build (push) Canceled after 0s
ROS Translation Node Tests / Build and test [humble] (push) Canceled after 0s
ROS Translation Node Tests / Build and test [jazzy] (push) Canceled after 0s
SITL Tests / Testing PX4 quadx (push) Canceled after 0s
SITL Tests / Testing PX4 standard_vtol (push) Canceled after 0s
SITL Tests / Testing PX4 xvert (push) Canceled after 0s
Docs - Crowdin - Upload Guide sources (en) / upload-to-crowdin (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v5x (push) Canceled after 0s
FLASH usage analysis / Analyzing px4_fmu-v6x (push) Canceled after 0s
Python CI Checks / build (push) Canceled after 0s
FLASH usage analysis / Publish Results (push) Canceled after 0s
Static Analysis / Clang-Tidy (push) Canceled after 0s
The loop advertised three attempts but returned on the first mismatch, so a single garbage SPI read after power-up failed the probe. Match the ICM42688P retry: succeed on the first good ID, fail after three.

Assisted-by: Grok:grok-4.6

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-20 20:23:38 -06:00
Jacob Dahl b7cc273db6 refactor(gps): shrink the GPS driver by ~4 KB of flash (#28309)
* refactor(gps): publish RTCM chunks without a per-topic template

publish_rtcm_chunks() was instantiated once for Publication and once for PublicationMulti, duplicating the chunking loop. Select the topic inside the loop instead.

px4_fmu-v6x_default: gps.cpp.obj 8744 -> 8590 B.

Assisted-by: Claude:claude-fable-5

* build(gps): bump GPSDrivers to 2b05a67

Pulls in the flash-diet refactor (#230) and the unsupported-config-key fixes (#225).

px4_fmu-v6x_default: -4,200 B from #230, +816 B from #225.
2026-08-20 20:16:33 -06:00
Jacob Dahl 13a0618c0b fix(nxp/adc): initialise each ADC once, not once globally (#28313)
board_determine_hw_info() inits LPADC2 and consumes the function-static
once flag, so board_adc's later LPADC1 init is a no-op. CFG stays at
reset (PWREN=0, no settling). Match the STM32 once-per-base pattern.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-20 20:14:18 -06:00
Jacob Dahl db3b630dde fix(manifest): return the matching PAB manifest entry (#28315)
board_query_manifest walked the list for mft[ndx].id == id then
returned &mft[id]. On a sparse list that is the wrong row, and for
PX4_MFT_T100_ETH (id 7) it is out of bounds.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-20 19:48:57 -06:00
Jacob Dahl 7c796818e7 fix(commander): report the worst IMU in the consistency check (#28318)
Inconsistency is deviation from the mean of all IMUs, so a single fault
also pushes the healthy ones off the mean. Reporting the first index
over threshold can name a good sensor. Report argmax instead.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-20 19:44:36 -06:00
Jacob Dahl 89e1b24cb2 fix(drivers/power_monitor): reject out-of-range INA226 cal and INA228 index (#28316)
CAL = 0.00512 / (current_lsb * R_shunt) is a 16-bit register. Legal
INA226_CURRENT × INA226_SHUNT combos overflow it, and the float-to-uint16
conversion is undefined. Fail start with the offending params rather than
program a wrapped value.

ina228 -t outside 1-3 was accepted; Battery() silently clamps to index 1.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-20 19:42:22 -06:00
Jacob Dahl 0c8f4527c0 fix(drivers/iis2mdc): print the I2C address the driver actually uses (#28317)
usage advertised 0x30; instantiate uses 0x1E (datasheet SAD = 0011110b).

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
2026-08-20 19:39:27 -06:00
Jacob DahlandHamish Willee 0055c88565 feat(gps): add u-blox u-center mode on UART2 (#28280)
* feat(gps): add u-blox u-center mode on UART2

GPS_UBX_BAUD2 alone never reached the receiver: outside the RTCM and GCS
modes the driver leaves UART2 untouched, so attaching a USB-serial adapter
to it produced no traffic. GPS_UBX_MODE 7 configures UART2 as a UBX
diagnostic port while the autopilot keeps its session on UART1.

* chore(gps): bump PX4-GPSDrivers submodule

Pulls in the u-center UART2 mode from PX4-GPSDrivers#228.

* docs(docs): prettier and cross link

* docs(docs): subedit

* chore(gps): bump PX4-GPSDrivers submodule

Picks up the info-level log for the u-center UART2 configuration, on top
of the CFG-UART1/2-ENABLED key ID fix now merged upstream.

* feat(gps): log what each UART carries after configuration

The line reported the link speed but not what the receiver was actually
configured to do with the port. Bumps PX4-GPSDrivers for the UART1 role
lookup and the matching per-port lines on UART2.

* feat(tools): add ubx_monitor.py, a live view of a u-blox serial link

Autobauds, then reports what the receiver is actually sending: link
utilisation, MON-VER identity, NAV-PVT fix state and per-message rates.
Written to check the u-center diagnostic port, useful for any GPS UART.

* chore(gps): point PX4-GPSDrivers at main

The u-center UART2 mode merged upstream (#228); track main instead of the
development branch.

---------

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
2026-08-19 16:05:24 -06:00
Jacob Dahl a3d5fb11bb fix(uavcan): warn while the dynamic node ID allocation table is full (#28243)
centralized::Storage never evicts, so a node ID spent on a board that is
gone stays spent. Once all of them are, every board the table has not
seen before is refused a node ID through a UAVCAN_TRACE that compiles to
nothing here - silent on the bus and on the console, so a full table
reads as a dead node.

Boards already in the table still get their cached ID and join fine, so
the bus looks healthy. A test station cycling boards through one flight
controller fills the table and stops allocating for good.

PX4_ERR every 5 s while full: the table does not empty itself, so this
is a fault someone has to clear, not a one-time notice.
2026-08-14 17:20:16 -06:00
Jacob Dahl 668c1f16b3 fix(dshot): resynchronise serial ESC telemetry to its request (#28222)
A serial telemetry response is matched to a motor by which request was
outstanding when it was decoded, not by anything in the frame itself.
Nothing discarded stale bytes between requests, so a response that arrived
after its own request had timed out stayed in the RX FIFO and was decoded
as the *next* motor's response — an intact frame with a good CRC, simply
attributed to the wrong ESC.

That offset is self-sustaining: from then on every request finds a
complete frame already buffered, decodes it immediately, and so never
times out again to fall back into step. All ESC readings stay shifted by
one motor until something else resets the stream.

It is easy to miss on a multirotor, where the motors report similar
voltage, current and temperature — but RPM is per-motor, so any test that
spins one motor at a time reads that motor's RPM as zero while its
voltage and temperature look perfectly healthy.

Flush the RX buffer when a request is started, so a response can only ever
be attributed to the motor it was asked of.

Also restore the pre-rework 30 ms response timeout. 5 ms is short enough
that a busy ESC regularly misses it, which is what produced the late
responses in the first place; giving up early costs a whole round-robin
pass, not a single frame.
2026-08-14 11:16:56 -06:00
Jacob Dahl e9c1c83e35 fix(dataman): increase task stack size (#28202)
* fix(dataman): increase task stack size

Enabling CONFIG_FS_LARGEFILE widened off_t and fsblkcnt_t to 64-bit
through the VFS, FAT and mmcsd layers that the dataman file backend
calls into, which pushed the task past the load_mon warning threshold:

  WARN  [load_mon] dataman low on stack! (252 bytes left)

The task only ever had 1420 bytes, since PX4_STACK_ADJUSTED is a no-op
on 32-bit NuttX targets. That left roughly 1168 bytes of peak usage
against a 300 byte warning threshold, so it was already running close
to the limit beforehand. Bump to 1800 to restore headroom.

* Update src/modules/dataman/dataman.cpp

* fix(dataman): use a single task stack size

The littlefs and non-littlefs sizes were within 200 bytes of each other,
which is not worth a conditional. Use 2000 everywhere so no target loses
headroom.
2026-08-10 12:31:46 -06:00