manifest: add label_pretty and firmware_category fields

Add human-readable labels and firmware classification to the build
manifest so ground stations like QGC can display friendly names and
filter builds by category.

New fields:
- label_pretty: human-readable variant name (e.g. "Multicopter")
- firmware_category: auto-detected classification
  - "vehicle" for multicopter, fixedwing, vtol, rover, uuv, spacecraft
  - "peripheral" for CAN sensor nodes (GPS, flow, mag, etc.)
  - "bootloader" for bootloader/canbootloader
  - "dev" for everything else (default, zenoh, mavlink-dev, etc.)

Peripheral detection uses ROMFSROOT="cannode" which is shared by all
~18 CAN sensor boards across ARK, Holybro, CUAV, Freefly, Matek, NXP.

A build-time warning fires when an unrecognized label falls through to
"dev", so new vehicle types are not silently hidden from end users.

Boards can override via CONFIG_BOARD_FIRMWARE_CATEGORY in .px4board.
CONFIG_BOARD_LABEL_PRETTY set on all px4/fmu-v6x variants as Phase 1.

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
This commit is contained in:
Ramon Roche 2026-02-10 15:52:55 -08:00
parent 68c391328a
commit 63f059fd47
No known key found for this signature in database
GPG Key ID: 275988FAE5821713
19 changed files with 231 additions and 0 deletions

24
Kconfig
View File

@ -54,6 +54,30 @@ menu "Toolchain"
string "Architecture"
default ""
config BOARD_LABEL_PRETTY
string "Human-readable label for this build variant"
default ""
help
A short display name for this build variant (e.g. "Multicopter",
"Rover"). Used by ground stations like QGC to show a user-friendly
name instead of raw target strings. Must be set in every .px4board
variant file because non-base variants inherit from default.
config BOARD_FIRMWARE_CATEGORY
string "Firmware category override (vehicle, peripheral, dev, bootloader)"
default ""
help
Override the auto-detected firmware category. Normally inferred
from the build label and board config: vehicle types (multicopter,
fixedwing, etc.) map to "vehicle", bootloader/canbootloader map to
"bootloader", CAN peripheral boards (ROMFSROOT=cannode) map to
"peripheral", and everything else maps to "dev". Set this only
when the auto-detection is wrong for a particular board variant.
IMPORTANT: If you add a new vehicle type with a label not yet in
_VEHICLE_LABELS (in gen_board_manifest_from_defconfig.py), either
add it there or set this to "vehicle" — otherwise the build will
be hidden from end-users in QGC.
config BOARD_LTO
bool "(EXPERIMENTAL) Link Time Optimization (LTO)"
default n

View File

@ -2,6 +2,13 @@
import argparse, json, os, re, sys
from typing import Dict
_VEHICLE_LABELS = frozenset({
"multicopter", "fixedwing", "vtol", "rover", "uuv", "spacecraft",
})
_BOOTLOADER_LABELS = frozenset({
"bootloader", "canbootloader",
})
def parse_defconfig(path: str) -> Dict[str, str]:
d: Dict[str, str] = {}
if not path or not os.path.exists(path):
@ -36,6 +43,38 @@ def detect_chip(defcfg: Dict[str,str]) -> str:
s = defcfg.get("CONFIG_ARCH_CHIP", "")
return s.lower().replace("_", "") if s else ""
def detect_firmware_category(target: str, defcfg: Dict[str, str]) -> str:
"""Infer firmware_category from the build label and defconfig.
Detection order:
1. bootloader / canbootloader labels "bootloader"
2. Known vehicle labels "vehicle"
3. ROMFSROOT == "cannode" (CAN peripheral boards) "peripheral"
4. Everything else "dev"
If you are adding a NEW vehicle type (e.g. "balloon"), you must EITHER:
1. Add the label to _VEHICLE_LABELS in this file, OR
2. Set CONFIG_BOARD_FIRMWARE_CATEGORY="vehicle" in the .px4board file
Otherwise the build will be classified as "dev" and hidden from
end-users in ground stations like QGroundControl.
"""
label = target.rsplit("_", 1)[-1].lower() if target else ""
if label in _BOOTLOADER_LABELS:
return "bootloader"
if label in _VEHICLE_LABELS:
return "vehicle"
# CAN peripheral boards (sensors, GPS, flow, etc.) use cannode ROMFS
romfsroot = defcfg.get("CONFIG_BOARD_ROMFSROOT", "")
if romfsroot == "cannode":
return "peripheral"
if label not in ("default", ""):
print(f"WARNING: label '{label}' (target '{target}') is not a known "
f"vehicle type — defaulting firmware_category to 'dev'. "
f"If this is a vehicle type, add it to _VEHICLE_LABELS in "
f"{__file__} or set CONFIG_BOARD_FIRMWARE_CATEGORY in the "
f".px4board file.", file=sys.stderr)
return "dev"
def pick(preferred: str, fallback_key: str, defcfg: Dict[str, str]) -> str:
return preferred if preferred else defcfg.get(fallback_key, "")
@ -51,6 +90,8 @@ def main():
ap.add_argument("--chip", default="")
ap.add_argument("--vid", default="")
ap.add_argument("--pid", default="")
ap.add_argument("--label-pretty", default="")
ap.add_argument("--firmware-category", default="")
ap.add_argument("--out", help="Write to file instead of stdout")
args = ap.parse_args()
@ -64,10 +105,16 @@ def main():
chip = args.chip or detect_chip(defcfg)
vid = norm_hex(pick(args.vid, "CONFIG_CDCACM_VENDORID", defcfg))
pid = norm_hex(pick(args.pid, "CONFIG_CDCACM_PRODUCTID", defcfg))
label_pretty = pick(args.label_pretty, "CONFIG_BOARD_LABEL_PRETTY", defcfg)
firmware_cat = pick(args.firmware_category,"CONFIG_BOARD_FIRMWARE_CATEGORY",defcfg)
if not firmware_cat:
firmware_cat = detect_firmware_category(target, defcfg)
manifest = {
"name": name,
"target": target,
"label_pretty": label_pretty,
"firmware_category": firmware_cat,
"manufacturer": manufacturer,
"hardware": {
"architecture": arch,

View File

@ -1,3 +1,4 @@
CONFIG_BOARD_TOOLCHAIN="arm-none-eabi"
CONFIG_BOARD_ARCHITECTURE="cortex-m7"
CONFIG_BOARD_LABEL_PRETTY="Bootloader"
CONFIG_BOARD_ROMFSROOT=""

View File

@ -1,5 +1,6 @@
CONFIG_BOARD_TOOLCHAIN="arm-none-eabi"
CONFIG_BOARD_ARCHITECTURE="cortex-m7"
CONFIG_BOARD_LABEL_PRETTY="Default"
CONFIG_BOARD_ETHERNET=y
CONFIG_BOARD_SERIAL_GPS1="/dev/ttyS0"
CONFIG_BOARD_SERIAL_GPS2="/dev/ttyS7"

View File

@ -1 +1,2 @@
CONFIG_BOARD_LABEL_PRETTY="Flash Analysis"
CONFIG_BOARD_LINKER_PREFIX="flash-analysis"

View File

@ -1,2 +1,3 @@
CONFIG_BOARD_LABEL_PRETTY="MAVLink Dev"
CONFIG_MAVLINK_DIALECT="development"
CONFIG_MODULES_UXRCE_DDS_CLIENT=n

View File

@ -1,3 +1,4 @@
CONFIG_BOARD_LABEL_PRETTY="Multicopter"
CONFIG_COMMON_DIFFERENTIAL_PRESSURE=n
CONFIG_MODE_NAVIGATOR_VTOL_TAKEOFF=n
CONFIG_MODULES_AIRSPEED_SELECTOR=n

View File

@ -1,5 +1,6 @@
CONFIG_BOARD_TOOLCHAIN="arm-none-eabi"
CONFIG_BOARD_ARCHITECTURE="cortex-m7"
CONFIG_BOARD_LABEL_PRETTY="Performance Test"
CONFIG_BOARD_ETHERNET=y
CONFIG_BOARD_SERIAL_GPS1="/dev/ttyS0"
CONFIG_BOARD_SERIAL_GPS2="/dev/ttyS7"

View File

@ -1,3 +1,4 @@
CONFIG_BOARD_LABEL_PRETTY="Rover"
CONFIG_MODULES_AIRSPEED_SELECTOR=n
CONFIG_MODULES_FLIGHT_MODE_MANAGER=n
CONFIG_MODULES_FW_ATT_CONTROL=n

View File

@ -1,3 +1,4 @@
CONFIG_BOARD_LABEL_PRETTY="Spacecraft"
CONFIG_BOARD_PWM_FREQ=100000
CONFIG_MODULES_AIRSPEED_SELECTOR=n
CONFIG_MODULES_FLIGHT_MODE_MANAGER=n

View File

@ -1,3 +1,4 @@
CONFIG_BOARD_LABEL_PRETTY="UUV"
CONFIG_MODULES_AIRSPEED_SELECTOR=n
CONFIG_MODULES_FLIGHT_MODE_MANAGER=n
CONFIG_MODULES_FW_ATT_CONTROL=n

View File

@ -1,3 +1,4 @@
CONFIG_BOARD_LABEL_PRETTY="Zenoh"
# CONFIG_BOARD_UAVCAN_TIMER_OVERRIDE is not set
CONFIG_DRIVERS_UAVCAN=n
CONFIG_MODULES_UXRCE_DDS_CLIENT=n

View File

@ -481,6 +481,7 @@
- [PX4 Board Configuration (kconfig)](hardware/porting_guide_config.md)
- [NuttX Board Porting Guide](hardware/porting_guide_nuttx.md)
- [Board Firmware Packaging (.deb)](hardware/board_packaging.md)
- [Firmware Manifest & Metadata](hardware/firmware_manifest.md)
- [Serial Port Mapping](hardware/serial_port_mapping.md)
- [Airframes](dev_airframes/index.md)
- [Adding a New Airframe](dev_airframes/adding_a_new_frame.md)

View File

@ -478,6 +478,7 @@
- [Flight Controller Porting Guide](/hardware/porting_guide.md)
- [PX4 Board Configuration (kconfig)](/hardware/porting_guide_config.md)
- [NuttX Board Porting Guide](/hardware/porting_guide_nuttx.md)
- [Firmware Manifest & Metadata](/hardware/firmware_manifest.md)
- [Serial Port Mapping](/hardware/serial_port_mapping.md)
- [Airframes](/dev_airframes/index.md)
- [Adding a New Airframe](/dev_airframes/adding_a_new_frame.md)

View File

@ -78,6 +78,15 @@ If _QGroundControl_ installs the FMUv2 target (see console during installation),
You can update it by following the instructions in [Bootloader update > FMUv2 Bootloader Update](../advanced_config/bootloader_update.md#fmuv2-bootloader-update).
## Firmware Variants
PX4 boards may provide multiple firmware variants for different vehicle types (multicopter, fixed-wing, rover, etc.).
QGroundControl shows vehicle firmware by default.
CAN peripheral firmware (for sensor nodes like GPS, optical flow, magnetometer, etc.) is shown in a separate section.
Developer builds (such as `default` or `zenoh`) are available in advanced mode.
For technical details on how firmware variants are classified and discovered, see [Firmware Manifest & Metadata](../hardware/firmware_manifest.md).
## Further Information
- [QGroundControl User Guide > Firmware](https://docs.qgroundcontrol.com/master/en/qgc-user-guide/setup_view/firmware.html).

View File

@ -113,6 +113,9 @@ Contact PX4 board maintainers at [boards@px4.io](mailto:boards@px4.io) and reque
2. The assignment of REV and VER ID resistor values.
3. If the board supports USB: Either request the assignment of a USB VID and PID or provide the USB VID and PID.
4. Set `CONFIG_BOARD_LABEL_PRETTY` in all `.px4board` variant files so that ground stations can display a human-readable name for each build variant.
See [Firmware Manifest & Metadata](firmware_manifest.md) for details on firmware categories and how QGC discovers firmware.
Integrate the board according to the board porting release process described in the [porting guide](../hardware/porting_guide.md)
:::warning

View File

@ -0,0 +1,110 @@
# Firmware Manifest & Metadata
Each PX4 NuttX build produces a **manifest** — a JSON metadata object that describes the firmware target, board hardware, and build variant.
This manifest is embedded in the `.px4` firmware file and aggregated into a unified release manifest consumed by ground stations like QGroundControl for firmware discovery and selection.
## Manifest Schema
```json
{
"name": "px4_fmu-v6x",
"target": "px4_fmu-v6x_multicopter",
"label_pretty": "Multicopter",
"firmware_category": "vehicle",
"manufacturer": "Holybro",
"hardware": {
"architecture": "cortex-m7",
"vendor_id": "0x1234",
"product_id": "0x5678",
"chip": "stm32h753ii",
"productstr": "Pixhawk 6X"
}
}
```
### Field Descriptions
| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Board name without variant label (e.g. `px4_fmu-v6x`) |
| `target` | string | Full build target including variant (e.g. `px4_fmu-v6x_multicopter`) |
| `label_pretty` | string | Human-readable variant name shown in ground stations (e.g. "Multicopter") |
| `firmware_category` | string | Build classification: `vehicle`, `peripheral`, `dev`, or `bootloader` |
| `manufacturer` | string | Board manufacturer name |
| `hardware` | object | Hardware details (architecture, USB IDs, chip, product string) |
## Firmware Categories
The `firmware_category` field classifies each build for ground station filtering:
| Value | Description | Examples | Ground Station Behavior |
|-------|-------------|----------|------------------------|
| `vehicle` | Production firmware for a vehicle type | multicopter, fixedwing, vtol, rover, uuv, spacecraft | Shown to users (primary) |
| `peripheral` | Firmware for CAN sensor nodes and peripherals | ark/can-gps, holybro/can-gps-v1, ark/can-flow, ark/mag | Shown in a dedicated peripheral/sensor section |
| `dev` | Developer/engineering builds | default, zenoh, mavlink-dev, flash-analysis, performance-test | Hidden by default, advanced mode only |
| `bootloader` | Bootloader binaries | bootloader, canbootloader | Never shown to end users |
### Auto-Detection
The firmware category is automatically inferred from the build label (the last segment of the target string after the final `_`):
- Labels `multicopter`, `fixedwing`, `vtol`, `rover`, `uuv`, `spacecraft``vehicle`
- Labels `bootloader`, `canbootloader``bootloader`
- Boards with `CONFIG_BOARD_ROMFSROOT="cannode"` (CAN sensor peripherals) → `peripheral`
- Everything else (`default`, `zenoh`, `mavlink-dev`, etc.) → `dev`
The known vehicle labels are maintained in `_VEHICLE_LABELS` in `Tools/manifest/gen_board_manifest_from_defconfig.py`.
Peripheral detection uses the `cannode` ROMFS root, which is shared by all CAN sensor boards (ARK, Holybro, CUAV, Freefly, Matek, NXP, etc.).
A build-time warning is emitted to stderr when an unrecognized label (other than `default`) falls through to `dev`, so that new vehicle types are not silently hidden from end users.
### Adding a New Vehicle Type
If you are adding a new vehicle type (e.g. `balloon`), you must do **one** of the following — otherwise the build is silently classified as `dev` and hidden from end users in QGroundControl:
1. **Add the label to `_VEHICLE_LABELS`** in `Tools/manifest/gen_board_manifest_from_defconfig.py` — this is the preferred approach when the vehicle type applies across multiple boards.
2. **Set `CONFIG_BOARD_FIRMWARE_CATEGORY="vehicle"`** in the `.px4board` file — use this for one-off overrides or when a board variant doesn't follow the standard naming.
### Overriding Auto-Detection
If a board variant needs a non-standard classification for any reason, set `CONFIG_BOARD_FIRMWARE_CATEGORY` in the `.px4board` file:
```
CONFIG_BOARD_FIRMWARE_CATEGORY="vehicle"
```
This override takes precedence over auto-detection.
## Pretty Labels (`label_pretty`)
The `label_pretty` field provides a human-readable name for each build variant.
Ground stations display this instead of raw target strings like `px4_fmu-v6x_default`.
### Setting `label_pretty`
Set `CONFIG_BOARD_LABEL_PRETTY` in each `.px4board` file:
```
CONFIG_BOARD_LABEL_PRETTY="Multicopter"
```
### Kconfig Inheritance Caveat
Non-base variants (e.g. `multicopter.px4board`, `rover.px4board`) are merged on top of `default.px4board`.
If `default.px4board` sets `CONFIG_BOARD_LABEL_PRETTY="Default"` and a variant file does not override it, the variant inherits the "Default" label — which is incorrect.
**Every variant file must set its own `CONFIG_BOARD_LABEL_PRETTY`.**
See [PX4 Board Configuration > Kconfig Label Inheritance](porting_guide_config.md#px4-kconfig-label-inheritance) for more details on how variant configs inherit from defaults.
## Build Pipeline
The manifest flows through three stages:
1. **`gen_board_manifest_from_defconfig.py`** — Generates per-build `manifest.json` from CMake variables and defconfig values, including auto-detection of `firmware_category`
2. **`px_mkfw.py`** — Bundles the manifest into the `.px4` firmware file
3. **`update_firmware_manifest.py`** — Aggregates individual `.px4` manifests into a unified release manifest for ground station consumption
## Backward Compatibility
- Old `.px4` files without `label_pretty` / `firmware_category`: ground stations fall back to raw label and filename matching (existing behavior)
- These are additive fields — no `format_version` bump is needed

View File

@ -45,6 +45,30 @@ When changing the `cyphal.px4board` it only stores the delta of the Kconfig keys
When modifying a Kconfig key in `default.px4board` it will be modified in all derivative configurations of the same board that had the same config as well.
:::
## Board Metadata Fields
PX4 board files can include metadata used by ground stations for firmware discovery and display.
### `CONFIG_BOARD_LABEL_PRETTY`
A human-readable name for the build variant (e.g. `"Multicopter"`, `"Rover"`).
Ground stations like QGroundControl display this instead of raw target strings.
```
CONFIG_BOARD_LABEL_PRETTY="Multicopter"
```
::: warning
Due to [Kconfig label inheritance](#px4-kconfig-label-inheritance), every variant file must set its own `CONFIG_BOARD_LABEL_PRETTY`.
If omitted, a variant inherits the value from `default.px4board`, which is typically wrong.
:::
### `CONFIG_BOARD_FIRMWARE_CATEGORY`
Optional override for the auto-detected firmware category (`vehicle`, `peripheral`, `dev`, or `bootloader`).
Normally this is inferred from the build label and does not need to be set.
See [Firmware Manifest & Metadata](firmware_manifest.md) for details on the auto-detection rules and when to use this override.
## PX4 Menuconfig Setup
The [menuconfig](https://pypi.org/project/kconfiglib/#menuconfig-interfaces) tool is used to modify the PX4 board configuration, adding/removing modules, drivers, and other features.

View File

@ -439,6 +439,8 @@ if (TARGET parameters_xml AND TARGET airframes_xml)
--chip "${CONFIG_ARCH_CHIP}"
--vid "${CONFIG_CDCACM_VENDORID}"
--pid "${CONFIG_CDCACM_PRODUCTID}"
--label-pretty "${CONFIG_BOARD_LABEL_PRETTY}"
--firmware-category "${CONFIG_BOARD_FIRMWARE_CATEGORY}"
--out ${MANIFEST_JSON}
DEPENDS
${PX4_SOURCE_DIR}/Tools/manifest/gen_board_manifest_from_defconfig.py