#27605 gated the dshot/pwm_out start on `param compare -s PASSTHRU_EN 0`.
PASSTHRU_EN is registered only when the serialpassthrough driver is
compiled in, which is not the case on most boards (e.g. fmu-v5x). There
param_find() fails, `param compare` returns "no match", the else branch
runs, and the FMU outputs never start: the PWM_AUX group disappears from
the Actuators screen and motors wired to those pins won't arm. SITL uses a
separate init.d-posix rcS and is unaffected, so CI did not catch it.
Use the start-unless-explicitly-enabled idiom already used elsewhere in
rcS (`param greater -s`): only skip the outputs when PASSTHRU_EN is
explicitly > 0. A missing or zero parameter starts the outputs as before.
* docs(hardware): rewrite board support guide around real process
The board support guide described a theoretical category-first process that
no longer matches how board support actually happens. Manufacturers were
left unsure of the concrete steps, leading to repeated email requests for
guidance.
Restructure the guide around the actual GitHub-driven flow: build your own
firmware target, reserve a board ID via a PX4-Bootloader PR, provide a
unique USB VID/PID, demonstrate flight with logs, and open the PR. Keep the
support categories as a reference section that explains support
responsibility rather than process.
Spell out that QGroundControl uses the USB VID/PID to identify hardware, and
warn that reusing another vendor's pair causes misidentification. Promote
the ArduPilot board-ID alignment note and point it at the current hwdef
location. Demote VER/REV ID to a clearly-deprecated edge case: explain the
resistor/EEPROM mechanism and its original cross-vendor baseboard/FMU intent,
note the ecosystem has deviated from it, and that Dronecode no longer offers
standards validation. Update the Discord link to chat.dronecode.org.
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
* docs(hardware): align porting guide with board support guide
The porting guide's 'Officially Supported Hardware' section restated the
board support process and contradicted the rewritten board support guide.
It also told manufacturers to ship hardware to the dev team for port
validation, a service Dronecode no longer offers.
Replace that section with a short pointer to the board support guide, which
is now the single source of truth for the support process, and keep only the
high-level support benefits and maintenance expectations. The technical
porting content (architecture, file layout, host OS config, RC UART wiring)
is unchanged.
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
* docs(hardware): make board support guide discoverable for manufacturers
The board support guide was only reachable by navigating into the hardware
integration section. A manufacturer browsing the user-facing autopilot pages,
the exact audience, had no path to it, and one of those pages still stated
the obsolete 4-month compatibility commitment.
Add 'how to get your board supported' pointers from the manufacturer-supported
and experimental autopilot pages, the flight controller index, and the
development landing page. Remove the stale 4-month rule from the
manufacturer-supported page. Strengthen the guide's opening paragraph so the
phrases manufacturers actually search appear up front.
Word the links to avoid conflating the guide (how to get a board supported)
with the 'Manufacturer Supported' category (one of several support levels).
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
* docs(docs): Minor tweaks
---------
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
Remove broken anchor link to modules_driver.md#rc-input which does not
exist. Use inline code formatting instead.
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
Add hardware documentation for the VOLOLAND NarinFC-H7 flight controller
including pinouts, serial port mapping, PWM output groups, and analog
inputs. Fix version badge to v1.17 and clarify RC_IN is wired directly
to FMU via RC_INPUT driver.
Based on work by @vololand and @hamishwillee from PR #26542.
Signed-off-by: Ramon Roche <mrpollo@gmail.com>
* fix(fw_latlon_control): do not reset alt err time constant on every param update
https://github.com/PX4/PX4-Autopilot/pull/23519 introduced this reset
using setForcedValue in the constructor of FixedwingPositionControl
https://github.com/PX4/PX4-Autopilot/pull/24056 accidentally moved it
from the constructor to the parameters_update
This means that now the time constant is reset to the tighter
low-altitude value (intended for use below FW_T_THR_LOW_HGT by
https://github.com/PX4/PX4-Autopilot/pull/23519) even though we could be
arbitrarily high.
Fix: Move both slew rate (given by a static constexpr) and initial reset
to constructor.
* style(fw_latlon_control): remove superficial newline
* fix(fw_mode_manager): honor DO_CHANGE_SPEED in manual airspeed modes
Re-anchor the throttle-stick airspeed interpolation on the last
MAV_CMD_DO_CHANGE_SPEED value (falling back to FW_AIRSPD_TRIM if none).
Previously, _commanded_manual_airspeed_setpoint was stored but never
read when FW_POS_STK_CONF bit 1 was set (the default), so
DO_CHANGE_SPEED had no effect in Position/Altitude.
With this change:
- Stick centered = commanded cruise speed (= FW_AIRSPD_TRIM if no command).
- Stick deflection deviates from cruise between FW_AIRSPD_MIN/MAX.
- Disconnecting the controller collapses the interpolation to the cruise
(Sticks zeroes the throttle channel on manual_control_signal_lost).
- No setpoint jump when a controller is plugged in with stick centered.
- No behavior change for users who never send DO_CHANGE_SPEED.
* fix(fw_mode_manager): clear DO_CHANGE_SPEED override on mode change
Reset _commanded_manual_airspeed_setpoint to NaN when entering a
manual airspeed mode from outside the manual airspeed pair, so a
prior MAV_CMD_DO_CHANGE_SPEED does not silently anchor the manual
airspeed setpoint after the pilot has left and re-entered manual
airspeed control. Toggling between Position and Altitude preserves
the commanded value since both modes share the same airspeed mechanic.
Also fold in a make-format whitespace fix in get_manual_airspeed_setpoint.
* docs(flight_modes_fw): document DO_CHANGE_SPEED in manual airspeed modes
Describe how MAV_CMD_DO_CHANGE_SPEED re-anchors the throttle-stick cruise
airspeed in Position and Altitude mode, matching the new behavior in the
fw_mode_manager. Covers the centered-stick anchor, deflection scaling
between FW_AIRSPD_MIN/MAX, the shared setpoint across both modes, and the
reset to trim when entering from any other mode.
Signed-off-by: Gregor Hohl <gregor.hohl@auterion.com>
* Subedit
* fix(fw_mode_manager): reset DO_CHANGE_SPEED override on any mode change
Reset _commanded_manual_airspeed_setpoint to NaN whenever the position
control mode changes, instead of only when entering manual airspeed
control from outside the Position/Altitude pair. This makes the manual
airspeed setpoint revert to FW_AIRSPD_TRIM on every mode change,
including toggling between Position and Altitude, for predictable
behavior.
Signed-off-by: Gregor Hohl <gregor.hohl@auterion.com>
* docs(flight_modes_fw): note manual airspeed resets on mode change
Clarify that the commanded airspeed defaults to FW_AIRSPD_TRIM when no
airspeed has been commanded, and document that the commanded airspeed
resets on every flight mode change.
Signed-off-by: Gregor Hohl <gregor.hohl@auterion.com>
* docs(flight_modes_fw): clarify DO_CHANGE_SPEED param1/param2 handling
Signed-off-by: Gregor Hohl <gregor.hohl@auterion.com>
---------
Signed-off-by: Gregor Hohl <gregor.hohl@auterion.com>
Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
Do not accept SET_ATTITUDE_TARGET as an offboard signal when it only
contains attitude or body rates without thrust.
Attitude and body-rate offboard control need a thrust source. If
THROTTLE_IGNORE is set and no thrust_body setpoint is provided, do not
refresh offboard_control_mode or publish the setpoint.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
* fix(geo): correct arc endpoint projection in get_distance_to_arc
The out-of-sector branch placed the arc start/end points with two errors:
- The cosine scaling was inverted and evaluated in degrees: cos() multiplied
the latitude term and was missing from the longitude term. One degree of
longitude is 111111 m * cos(latitude), so the east displacement must be
divided by cos(lat) in radians, while latitude needs no cosine.
- The displacement was added to the vehicle position instead of the arc
center, so the computed endpoints, and the returned crosstrack
distance/bearing, were meaningless.
Anchor the endpoints to the center and apply the projection correctly, and
add a unit test for the out-of-sector case.
Fixes#27557
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* fix(geo): correct arc sector membership test
The in-sector check in get_distance_to_arc() compared the vehicle->center
bearing against a sector defined by center->endpoint bearings, so it was
off by pi: a point lying exactly on the arc was classified out-of-sector
and reported kilometers of crosstrack error instead of ~zero. The
negative-sweep branch also computed the sector bounds with the wrong sign
(arc_start_bearing - arc_sweep), reflecting the wedge to the wrong side.
Replace the bound bookkeeping with a single sweep-direction-aware test on
the center->vehicle bearing, which handles either sweep sign and any zero
crossing. Add in-sector and negative-sweep unit tests.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* test(geo): avoid underscores in get_distance_to_arc test names
Rename the new tests to underscore-free lowerCamelCase (matching the
existing reprojectProject / projectReproject tests) to satisfy clang-tidy
google-readability-avoid-underscore-in-googletest-name.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* fix(geo): suppress false-positive clang-tidy swapped-argument warning
readability-suspicious-call-argument flags the center->vehicle bearing
call because the argument names cross-match the parameter names, but the
reversed order is intentional (it is the reciprocal of bearing_now). With
WarningsAsErrors this failed CI; annotate the line with NOLINTNEXTLINE.
---------
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Rename ADC_AIRSPEED_IN_CHANNEL to ADC_AIRSPEED_VOLTAGE_CHANNEL so the sensors module actually picks up the analog airspeed input. Drop the redundant CBRK_IO_SAFETY default. Fix linker script comments inherited from the Durandal template (wrong MCU variant, nonexistent boot switch). Note that M10 (TIM4_CH4) has no capture DMA, so no BDShot.
* fix(commander): save parameters synchronously after parameter reset
ParamResetAll and ParamResetAllConfig relied on the deferred autosave (300 ms delay, 2 s rate limit) to persist the reset, so a reboot or power cycle right after the command ack could leave the old parameters in storage. Save synchronously like ParamResetSensorFactory already does.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* fix(rcS): only run caldata BSON size repair on boards with caldata
The bsondump docsize block from #23088 repairs a zeroed BSON document size field in /fs/mtd_caldata, but it ran unconditionally with a comment claiming it checks /fs/mtd_params, producing failed-bsondump boot noise on every board without a caldata partition. Move it inside the MTD_CALDATA gate next to the param load it exists to protect.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
---------
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* fix(bootloader): scrub uncorrectable flash ECC errors on STM32H7 before boot
A torn write to a flash row (power lost mid program/erase) can leave it
with inconsistent ECC. On H7 a CPU read of such a row raises an
uncorrectable double-bit ECC error -> bus fault, so the application hard
faults on every boot before printing anything, and only a mass erase
recovers it: reflashing does not, because neither the app nor the
bootloader image touches the parameter sector.
Scan the application and parameter flash with DMA right after clock_init
(a DMA read latches FLASH_SR DBECCERR instead of bus-faulting the CPU)
and erase any sector that holds an uncorrectable error. A corrupt
parameter sector then re-seeds to defaults on the next app boot; corrupt
app flash fails the image check and stays in the bootloader for reflash.
Mirrors ArduPilot's bootloader ECC scrub. H7-gated; the scan runs before
the interface DMA is brought up, so DMA1 stream 0 is uncontended.
* test(tools): add STM32H7 flash ECC fault injection tool
A bench helper that deterministically plants an uncorrectable (double-bit)
flash ECC error on an STM32H7, reproducing the torn-write parameter-store
brick. Used to validate the bootloader ECC scrub and the parameter re-seed
recovery on hardware: plant the fault, then confirm the board boots clean
instead of hard-faulting every boot.
brick_ecc.c is a freestanding stub that double-programs one flash word with
two different single-bit clears (the recipe that yields an inconsistent ECC);
brick_ecc.sh builds it, loads it into AXI SRAM over ST-Link, runs it, and
resets the board. README documents usage and how to retarget another board.
* fix(bootloader): require DMA transfer-complete for a clean ECC scan chunk
The stream self-disables on both transfer-complete and transfer-error.
A non-ECC bus error (unreachable scan buffer, rejected FIFO config)
previously read as a clean chunk, silently turning the scan into a
no-op. Require TCIF so an unread chunk takes the skip-the-scan
fallback instead of passing.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
---------
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
PR #18421 added the initial mag calibration save and was deliberately merged with SENS_MAG_AUTOCAL=0 until parameter system concerns were addressed. PR #19818 (CAN GPS nodes) flipped the global default to 1 as an undiscussed side change, shipped in v1.14.0. Since then every uncalibrated mag marks itself calibrated (CAL_MAGn_ID set) shortly after the disarmed bias estimator stabilizes, so ground stations stop prompting for compass calibration on new setups, and the first flight happens on a hard-iron-only estimate with an assumed mount rotation.
Restore the disabled default and enable it where it is genuinely needed: CAN nodes, which have no GCS calibration flow and no EKF for in-flight refinement. MBE_ENABLE is untouched, the bias estimator still runs and corrects everywhere.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
#27473 removed the +/-50 byte threshold so small deltas were no longer
suppressed, but that also made zero-impact PRs get a comment. Restore
suppression only for exactly 0-byte deltas on all targets. An existing
comment is still always updated so it never goes stale.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
* feat: implemented serial passthrough
* fix: used make format
* fix: changed function order to match other drivers
* fix: moved passthrough from systemcmds to drivers
* fix: renamed BITBANG_TIMER to UART_BITBANG_TIMER
* fix: used make format
* feat: added PASSTHRU_EN guard to start of dshot&pwm_out
* fix: made changing ESC channels more stable
* fix: adjusted naming of guards
* fix: changed include guard of bitbang
* fix: removed unused variable SER_PASS_BAUD
* fix: adjusted comments
* fix: adjusted print_usage() to match other drivers
* fix: remove bitbang_write_byte from public API and some buffer guard
* fix: added Serialpassthrough&Bitbang to exclude list of allyesconfig.py
* fix: added missing flag to print_usage()
Flashing PX4 over another firmware (e.g. ArduPilot on an ARK FPV) or
first-booting a board with blank parameter storage played the error tune
on every boot: the param layer treated a blank store as corruption, and
the rcS recovery persisted with a file cp that is a no-op on
FLASH_BASED_PARAMS boards, so the store never became valid.
- param_import()/param_load() return 1 ("not yet stored") for a blank
source - empty file, zeroed FRAM, or erased flash - on both the file
and flash backends; a store that holds data but no valid entry (torn
write, bit-rot, foreign data) is still reported as corrupt (-EILSEQ).
The file backend detects a zero-length file via fstat (NuttX FAT
cannot read() a 0-byte file: no cluster chain returns an error, not
EOF) and otherwise inspects the leading BSON document length; the
flash backend scans the whole store
- new 'param load-or-init <backup>' command: loads from the default
storage; a blank store is seeded from the SD backup (or firmware
defaults) and persisted so the next boot loads normally. A corrupt
store, a backup that exists but cannot be opened or imported, or a
result that cannot be persisted returns failure. The three-way
loaded/blank/corrupt logic lives in C because nsh $? is a binary
success flag. A failed backup seed does not reset to defaults, which
preserves parameters loaded earlier in boot (e.g. factory calibration
from /fs/mtd_caldata)
- rcS uses 'param load-or-init' instead of 'param import', and the
corrupt-store recovery persists via 'param save' (works on
flash-backed boards) only when the backup actually imported, so a
corrupt backup keeps alerting the operator on the next boot
- commander ParamLoadDefault treats blank storage (1) as success and
warns the GCS that parameters were reset to defaults, instead of
reporting "Error loading settings"
- parameter_flashfs_init() in flashfs.c no longer reports a successful
erase byte count as a failure (parity with flashfs32.c)
Tested with new blankImport and corruptImport cases in 'tests
parameters' (empty/zeroed/erased sources import as "not yet stored",
garbage fails to import); the CustomDefaults test now restores the
firmware default so the suite is repeatable within one boot.
The STM32H7 flash-erase DCACHE invalidate fix this depends on
(PX4/NuttX#381) is already in main's NuttX submodule.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
tcdrain()/tcflush() in pyserial's POSIX backend raise termios.error,
which derives from Exception rather than OSError, so it slipped past the
`except (OSError, serial.SerialException)` guards in flush() and
reset_buffers(). When the USB CDC node tore down during a
reboot-to-bootloader re-enumeration (reliably reproduced flashing PX4
onto a board running ArduPilot) the raw error escaped every
ConnectionError retry loop and crashed the uploader. Catch termios.error
explicitly so it surfaces as ConnectionError and the existing
identify/reboot retry loops recover.
reboot() also sent REBOOT once and silently swallowed a missing ack,
reporting success even when the board never left the bootloader. Confirm
the reboot via the bootloader's INSYNC+OK ack (sent before it jumps, per
bl.c PROTO_BOOT) or by watching the serial device re-enumerate, and
resend REBOOT if neither is observed.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
PR #27237 added an unconditional verify_signature() step between verify
and reboot, sending the VERIFY_SIG (0x39) opcode on every upload. For
unsigned firmware this injects an unknown opcode into the bootloader
command stream immediately before BOOT; bootloaders that don't silently
ignore it are left out of phase for the BOOT handshake, so the board
intermittently stays in the bootloader after an otherwise-successful
flash (app LEDs never come on).
Gate the probe on firmware.image_signed so unsigned uploads go straight
from verify to reboot, as before #27237. Signature verification still
runs for signed images.
The Micro-XRCE-DDS-Client-v3 submodule was added without a corresponding
prune entry, so 'make format'/'make check_format' would reformat its
vendored sources. Add it to the exclusion list alongside the existing
Micro-XRCE-DDS-Client entry.
* feat(baro): refactor barometer drivers to use PX4Barometer class
* fix(baro): incoperated feedback
* feat(baro): initialize PX4Barometer with device ID in barometer drivers
* fix(baro): set default temperature to 15°C in PX4Barometer constructor
* fix(baro): clarify default temperature comment in PX4Barometer constructor
---------
Co-authored-by: Claudio Chies <chiesc@chies.com>