feat(firmware): validate configuration and migrate sensor-driven flight APIs

Validate configuration through its owning schemas and keep board
declarations separate from shared F722 resource ownership. Bound USB
transitions, isolate transport failures and retain restart-only activation.

Migrate navigation and flight consumers with the sensor and schema
contracts. Require explicit installation data and synchronous output
authority; board motor outputs remain inhibited.

Validation: assembled-prefix host tests, strict Clippy, platform/USB tests
and both ARM release builds. Final-source coverage and scoped Miri/Kani
evidence are retained separately from hardware qualification.
This commit is contained in:
Onur Özkan committed 2026-11-07 18:12:11 +03:00
1 parent fb1b8af2d1
commit f9a61a086f
211 files changed
+45657 -12740

No files matched your search

+1 -3
View File
@@ -10,10 +10,8 @@ build = "build.rs"
workspace = true
[dependencies]
kaptan-config = { path = "../../libs/kaptan-config" }
platform-stm32f7 = { path = "../../platforms/stm32f7" }
# RTIC's attribute expansion emits absolute `rtic` support paths in the final
# board crate; the task graph and all RTIC source remain platform-owned.
# RTIC's macro emits absolute `rtic` paths in the board crate.
rtic = { version = "2.3", default-features = false, features = [
"thumbv7-backend",
] }
+24 -68
View File
@@ -1,79 +1,35 @@
//! Declarative support for the GEPRC TAKER F722 BLS 60A V2 stack.
//! Declarative support for the GEPRC GEP-F722-HD V2.
#![forbid(unsafe_code)]
use platform_stm32f7::kaptan_ports::{PortCatalog, PortDescriptor};
use platform_stm32f7::{
F7Board, F722Hardware, F722ImmutableConfig as ImmutableConfig, Icm42688, ImuMount, LevelFilter,
Mavlink, Pa13ActiveHigh, Pb0Pb1Pb4Pb5Low, Pc3, VerticalSensor,
F7Board, F722Hardware, F722ImmutableConfig, F722UserConfig, Icm42688Spi3, ImuMount, MotorPins,
SerialInventory, StatusLed, UART1, UART2, UART3, UART4, UART6, VerticalSensor,
};
/// User-writable MAVLink endpoint policy.
#[derive(Clone, kaptan_config::Config)]
struct MavlinkConfig {
#[config(id = 101, key = "mavlink.system_id", unit = "id", range(min = 1))]
system_id: u8,
#[config(id = 102, key = "mavlink.component_id", unit = "id", range(min = 1))]
component_id: u8,
#[config(
enum,
id = 105,
key = "mavlink.logging.level_filter",
unit = "log_level"
)]
log_level_filter: LevelFilter,
}
/// User-writable telemetry publication policy.
#[derive(Clone, kaptan_config::Config)]
struct TelemetryConfig {
#[config(
id = 103,
key = "telemetry.imu_hz",
unit = "Hz",
range(min = 1, max = 100)
)]
imu_hz: u8,
#[config(
id = 104,
key = "telemetry.heartbeat_hz",
unit = "Hz",
range(min = 1, max = 10)
)]
heartbeat_hz: u8,
}
/// Complete user-writable configuration advertised by this board.
#[derive(Clone, kaptan_config::Config)]
pub struct UserConfig {
mavlink: MavlinkConfig,
telemetry: TelemetryConfig,
}
/// ICM-42688-P-based GEP-F722-HD V2 paired with the 60 A stack.
pub struct GeprcF722V2;
impl F7Board for GeprcF722V2 {
type UserConfig = UserConfig;
type ImmutableConfig = ImmutableConfig;
impl PortCatalog<5> for GeprcF722V2 {
const DESCRIPTORS: [PortDescriptor; 5] = [UART1, UART2, UART3, UART4, UART6];
}
const FACTORY_USER_CONFIG: UserConfig = UserConfig {
mavlink: MavlinkConfig {
system_id: 1,
component_id: 1,
log_level_filter: LevelFilter::Info,
},
telemetry: TelemetryConfig {
imu_hz: 20,
heartbeat_hz: 2,
},
};
const IMMUTABLE_CONFIG: ImmutableConfig = ImmutableConfig {
impl F7Board for GeprcF722V2 {
type UserConfig = F722UserConfig<Self, 5>;
type ImmutableConfig = F722ImmutableConfig<Icm42688Spi3>;
const FACTORY_USER_CONFIG: Self::UserConfig = Self::UserConfig::FACTORY;
const IMMUTABLE_CONFIG: Self::ImmutableConfig = F722ImmutableConfig {
name: "GEPRC TAKER F722 V2 (ICM-42688-P)",
service_hz: 100,
hardware: F722Hardware::new()
.output_inhibit(Pb0Pb1Pb4Pb5Low)
.imu(Icm42688::on_spi3().with_drdy(Pc3).mounted(ImuMount::Yaw180))
.mavlink(Mavlink::on_usart2().baud(115_200))
.vertical_sensor(VerticalSensor::none())
.status_led(Pa13ActiveHigh)
.telemetry_only(),
hardware: F722Hardware::new(
Icm42688Spi3 {
mount: ImuMount::Yaw180,
},
SerialInventory::new(&Self::DESCRIPTORS),
StatusLed::ActiveHighPa13,
VerticalSensor::None,
MotorPins::Four,
),
};
}
+1 -3
View File
@@ -9,10 +9,8 @@ version.workspace = true
workspace = true
[dependencies]
kaptan-config = { path = "../../libs/kaptan-config" }
platform-stm32f7 = { path = "../../platforms/stm32f7" }
# RTIC's attribute expansion emits absolute `rtic` support paths in the final
# board crate; the task graph and all RTIC source remain platform-owned.
# RTIC's macro emits absolute `rtic` paths in the board crate.
rtic = { version = "2.3", default-features = false, features = [
"thumbv7-backend",
] }
+30 -98
View File
@@ -1,110 +1,42 @@
//! Declarative Kakute F7 Mini V3 board description.
//!
//! This module deliberately contains no MCU registers, RTIC concepts, unsafe
//! code, interrupt names, DMA handling, or task scheduling. Those concerns
//! belong to the STM32F7 platform crate.
#![forbid(unsafe_code)]
use platform_stm32f7::kaptan_ports::{PortCatalog, PortDescriptor};
use platform_stm32f7::{
AccelerometerRange, DigitalLowPassFilter, F7Board, F722Hardware,
F722ImmutableConfig as ImmutableConfig, GyroscopeRange, ImuMount, LevelFilter, Mavlink,
Mpu6000, Mpu6000Config, Pa4, Pb0Pb1Pb4Pb5Low, Pd2ActiveLow, VerticalSensor,
AccelerometerRange, DigitalLowPassFilter, F7Board, F722Hardware, F722ImmutableConfig,
F722UserConfig, GyroscopeRange, ImuMount, MotorPins, Mpu6000Config, Mpu6000Spi1,
SerialInventory, StatusLed, UART1, UART2, UART3, UART4, UART6, VerticalSensor,
};
/// User-writable MAVLink endpoint policy.
#[derive(Clone, kaptan_config::Config)]
struct MavlinkConfig {
#[config(id = 101, key = "mavlink.system_id", unit = "id", range(min = 1))]
system_id: u8,
#[config(id = 102, key = "mavlink.component_id", unit = "id", range(min = 1))]
component_id: u8,
#[config(
enum,
id = 105,
key = "mavlink.logging.level_filter",
unit = "log_level"
)]
log_level_filter: LevelFilter,
}
/// User-writable telemetry publication policy.
#[derive(Clone, kaptan_config::Config)]
struct TelemetryConfig {
#[config(
id = 103,
key = "telemetry.imu_hz",
unit = "Hz",
range(min = 1, max = 100)
)]
imu_hz: u8,
#[config(
id = 104,
key = "telemetry.heartbeat_hz",
unit = "Hz",
range(min = 1, max = 10)
)]
heartbeat_hz: u8,
#[config(
id = 106,
key = "telemetry.range_hz",
unit = "Hz",
range(min = 1, max = 100)
)]
range_hz: u8,
#[config(
id = 107,
key = "telemetry.optical_flow_hz",
unit = "Hz",
range(min = 1, max = 100)
)]
optical_flow_hz: u8,
}
/// Complete user-writable configuration advertised by this board.
#[derive(Clone, kaptan_config::Config)]
pub struct UserConfig {
mavlink: MavlinkConfig,
telemetry: TelemetryConfig,
}
/// Holybro Kakute F7 Mini V3.
pub struct KakuteF7MiniV3;
impl F7Board for KakuteF7MiniV3 {
type UserConfig = UserConfig;
type ImmutableConfig = ImmutableConfig;
impl PortCatalog<5> for KakuteF7MiniV3 {
const DESCRIPTORS: [PortDescriptor; 5] = [UART1, UART2, UART3, UART4, UART6];
}
const FACTORY_USER_CONFIG: UserConfig = UserConfig {
mavlink: MavlinkConfig {
system_id: 1,
component_id: 1,
log_level_filter: LevelFilter::Info,
},
telemetry: TelemetryConfig {
imu_hz: 20,
heartbeat_hz: 2,
range_hz: 20,
optical_flow_hz: 20,
},
};
const IMMUTABLE_CONFIG: ImmutableConfig = ImmutableConfig {
impl F7Board for KakuteF7MiniV3 {
type UserConfig = F722UserConfig<Self, 5>;
type ImmutableConfig = F722ImmutableConfig<Mpu6000Spi1>;
const FACTORY_USER_CONFIG: Self::UserConfig = Self::UserConfig::FACTORY;
const IMMUTABLE_CONFIG: Self::ImmutableConfig = F722ImmutableConfig {
name: "Kakute F7 Mini V3",
service_hz: 100,
hardware: F722Hardware::new()
.output_inhibit(Pb0Pb1Pb4Pb5Low)
.imu(
Mpu6000::on_spi1()
.with_drdy(Pa4)
.mounted(ImuMount::BodyAligned)
.configured(Mpu6000Config {
accelerometer: AccelerometerRange::PlusMinus4G,
gyroscope: GyroscopeRange::PlusMinus500DegreesPerSecond,
digital_low_pass_filter: DigitalLowPassFilter::Bandwidth42Hz,
sample_rate_divider: 9,
}),
)
.mavlink(Mavlink::on_usart1().baud(115_200))
.vertical_sensor(VerticalSensor::mtf01_on_uart4().baud(115_200))
.status_led(Pd2ActiveLow)
.telemetry_only(),
hardware: F722Hardware::new(
Mpu6000Spi1 {
mount: ImuMount::Yaw270,
config: Mpu6000Config {
accelerometer: AccelerometerRange::PlusMinus4G,
gyroscope: GyroscopeRange::PlusMinus500DegreesPerSecond,
digital_low_pass_filter: DigitalLowPassFilter::Bandwidth42Hz,
sample_rate_divider: 9,
},
},
SerialInventory::new(&Self::DESCRIPTORS),
StatusLed::ActiveLowPd2,
VerticalSensor::None,
MotorPins::Six,
),
};
}