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:
1 parent
fb1b8af2d1
commit
f9a61a086f
211 files changed
+45657
-12740
No files matched your search
+153
-55
@@ -12,22 +12,72 @@ F722 platform / RTIC application ──► data channel ──► MAVLink teleme
|
||||
├── physical sensor drivers
|
||||
└── startup motor-pin inhibition
|
||||
|
||||
portable libraries
|
||||
math ─► navigation ─► control ─► safety
|
||||
Portable flight libraries (not connected to physical actuators)
|
||||
FlightEndpoint ──► Navigation / control / phases / safety
|
||||
│
|
||||
└── caller-owned synchronous output sink
|
||||
```
|
||||
|
||||
Each F722 board contract pairs a board-owned `UserConfig` schema and explicit
|
||||
`FACTORY_USER_CONFIG` with a board-owned `ImmutableConfig`. The immutable root
|
||||
is one literal typestate manifest and the sole source of output inhibition,
|
||||
physical routes, driver setup, sensor mount, bootstrap line rates, status LED,
|
||||
image role, and service frequency. The user root contains only settings
|
||||
consumed by that compiled driver graph.
|
||||
Each F722 board combines `F722UserConfig` with an immutable hardware manifest.
|
||||
The manifest selects the IMU route, driver settings, mount, serial inventory,
|
||||
status LED, optional I²C sensor, motor pins and service frequency. User settings
|
||||
select UART roles, baud and MAVLink settings. Motor pins are inhibited at startup.
|
||||
|
||||
The data channel owns rate selection, mailbox consumption, message encoding,
|
||||
and bounded TX classification. Board code supplies physical routes, target
|
||||
schema, and factory values; it does not receive generic output authority.
|
||||
`F722ImmutableConfig<R>` retains the IMU route type through initialization and
|
||||
interrupt acknowledgement. The sealed `Mpu6000Spi1` and `Icm42688Spi3` routes
|
||||
select IMU wiring and backend independently of serial inventory and LED wiring.
|
||||
|
||||
Configuration and runtime observability are separate planes:
|
||||
Each board declares one `PortCatalog<N>::DESCRIPTORS` array. Both
|
||||
`Ports<Catalog, N>` and `SerialInventory::new(&Catalog::DESCRIPTORS)` use it for
|
||||
stable UART/field identities, supported roles, baud allowlists and resource
|
||||
claims. Validation produces a boot-only `PortPlan<N>`. The same const `N` sizes
|
||||
I/O and protocol storage: five UARTs on each current board. Absent UARTs
|
||||
have no slots. A declared UART set to Disabled retains its slot but initializes
|
||||
no peripheral or pins. USB is fixed normal MAVLink outside that inventory,
|
||||
separate from ROM DFU.
|
||||
|
||||
Each MAVLink `Link` has its own scheduler, encoder and receive framing, with a
|
||||
separate transport queue. A shared collector snapshots sensor mailboxes for all
|
||||
links. See [MAVLink](mavlink.md) for scheduling and transport behavior.
|
||||
|
||||
`kaptan-flight-runtime::FlightEndpoint` runs Quad-X control and
|
||||
`kaptan-navigation::Navigation` without allocation. Callers supply
|
||||
observations, independently captured receipt/processing time, clock ticks and a
|
||||
synchronous output sink. Transport and wire encoding stay outside the core.
|
||||
The board images do not yet construct a flight endpoint.
|
||||
See [Safety boundary](safety-boundary.md#shared-runtime-commit-contract) for
|
||||
epochs, request sequencing and the output commit contract.
|
||||
|
||||
`FlightEndpoint::supervise` checks actual time, cached health and phase/stream
|
||||
deadlines without advancing navigation, integrating control or consuming a
|
||||
control release. It can inhibit and publish outcomes, but cannot renew enabled
|
||||
output. Callers must service the endpoint with actual time even when input
|
||||
stops. The core owns control release and deadline decisions.
|
||||
|
||||
Within `FlightEndpoint`, fusion, current prediction and initialization completion
|
||||
run only when the 100-Hz control service is due. Early `tick` calls supervise
|
||||
cached evidence without advancing navigation. Ingress admission and its
|
||||
diagnostics remain immediate, but receiving or queuing fresh observations alone
|
||||
does not renew capabilities. The actual control gap is checked before a due
|
||||
navigation update can refresh evidence.
|
||||
|
||||
`Navigation` initializes from a stationary window of IMU, fixed-reference local
|
||||
position/velocity, magnetic, pressure and range observations. It derives attitude
|
||||
and a fixed launch origin from sensors, with explicit calibration/reference
|
||||
identities. Its shared 16-state error covariance covers position, velocity,
|
||||
attitude error, gyro bias, accelerometer bias and barometric datum. Bounded
|
||||
delayed fusion and current prediction preserve observation timing and capability
|
||||
evidence.
|
||||
|
||||
The navigation `sensor-snapshots` feature enables driver snapshot conversion.
|
||||
The same flight estimator is available on host and embedded targets.
|
||||
|
||||
Navigation owns the 25-m/s² free-flight acceleration bound checked by
|
||||
complete-profile validation. See
|
||||
[Configuration](configuration.md#flight-configuration) for required installation
|
||||
data and the supported vehicle model.
|
||||
|
||||
Configuration and diagnostics use separate paths:
|
||||
|
||||
```text
|
||||
FILE_CONTROL / FILE_DATA
|
||||
@@ -42,50 +92,97 @@ kaptan-file-mavlink ──► virtual FileProvider
|
||||
kaptan-log / kaptan-err-reportable
|
||||
│
|
||||
▼
|
||||
kaptan-diagnostics-mavlink ──► platform-owned encoder / DMA TX
|
||||
kaptan-diagnostics-mavlink ──► per-link encoder / bounded TX
|
||||
```
|
||||
|
||||
The file dialect is intentionally Kaptan-native rather than standard MAVLink
|
||||
FTP, `PARAM_*`, or a QGC adapter. Its two messages know only bounded paths,
|
||||
bytes, positions, checksums, transfer ownership, and retries. The configuration
|
||||
provider maps `/config/user.kcfg` to a canonical self-describing artifact.
|
||||
KCFG carries generated metadata and a complete committed value set; the target
|
||||
streams it without retaining a whole-file buffer. Saving imports and validates
|
||||
one complete replacement candidate. The core distinguishes only immutable
|
||||
boot-active values from the latest committed values. A save cannot mutate
|
||||
controller or estimator state in flight.
|
||||
The Kaptan-native file dialect owns bounded transfers and retries across links.
|
||||
Its provider maps saved, active and factory-reset endpoints to streaming KCFG v3
|
||||
and the generic configuration lifecycle.
|
||||
|
||||
`ConfigManager` owns boot-active values, known committed values or unknown durable
|
||||
state, and the factory candidate. F722 supplies its UART-disabled setup fallback
|
||||
through `recover_with`. Readable invalid settings remain inspectable for USB
|
||||
repair. Saving or factory reset never changes active consumers until reboot.
|
||||
|
||||
Target-owned stores use private persistence formats. F722 alternates compact
|
||||
`KCSP` snapshots between two erase sectors, retaining only the old and
|
||||
replacement records across the commit boundary. That format is not exposed as the public KCFG file.
|
||||
Motor allocation/mixer topology is immutable compiled vehicle capability data,
|
||||
never a scalar field or persistence member. F722 recovers operational settings
|
||||
before constructing consumers. Its local USB CDC-ACM and directly wired UART
|
||||
accept only exact-target, CRC-valid Kaptan file requests while physical outputs
|
||||
remain inhibited. CRC is integrity-only; a shared or remote GCS still needs
|
||||
production authentication, source binding, and persistent anti-replay state.
|
||||
Disarmed activation and flight-consumer reconstruction remain separate reviewed
|
||||
work.
|
||||
`KCSP` snapshots between two erase sectors, retaining only the old and replacement
|
||||
records across the commit boundary. That format is not exposed as KCFG. Motor
|
||||
allocation/mixer topology remains compiled vehicle capability data.
|
||||
|
||||
The MAVLink dialect root directly includes one-message XML leaves under
|
||||
`publish/` and `transfer/`. The `exchange/` contract remains empty until a
|
||||
current consumer justifies a message. There are no category index XML files or
|
||||
separate enum catalogs. The build watches the definitions tree and generates
|
||||
one `generated::kaptan` module; leaves are never separately generated Rust
|
||||
modules. `kaptan-log` itself has
|
||||
no MAVLink, platform, time, allocation, or filesystem dependency; its sole
|
||||
configuration coupling is compile-time enum metadata for `LevelFilter`.
|
||||
`kaptan-err-reportable` independently owns explicitly assigned reportable-error
|
||||
identity and a bounded nonblocking ingress; it has no logging, MAVLink,
|
||||
platform, filesystem, or clock dependency. Event creation is independent from
|
||||
sink filtering, allowing a future blackbox and MAVLink to select different
|
||||
policies. The platform drains reportable errors before its bounded critical and
|
||||
best-effort log queues and removes a pending record only after TX admission.
|
||||
`LOG` carries one conventional level plus one bounded text field; `ERROR`
|
||||
carries an explicitly assigned 16-bit ID and the bounded `file:line` of the
|
||||
reporting point. Its message kind already identifies a reportable firmware
|
||||
error. Kaptan-native heartbeat and sensor publications remain separate from
|
||||
diagnostics.
|
||||
## I/O ownership
|
||||
|
||||
Sensor handoff uses owned `LatestMailbox<T>` storage split into exactly one
|
||||
non-cloneable producer and one non-cloneable consumer. Publishing replaces an
|
||||
unread value, and taking removes it at most once. Each operation copies or takes
|
||||
an `Option<T>` inside the platform critical section. No callback, allocation,
|
||||
retry or wait occurs inside that section. Storage stays borrowed while endpoints
|
||||
are in use, and moving, dropping or forgetting an endpoint holds no lock. The
|
||||
backend must exclude every participating context, using Cortex-M single-core exclusion on
|
||||
F722 and the `critical-section/std` backend for host threads.
|
||||
|
||||
UART TX and RX are interrupt-driven, without UART DMA. Each I/O owner retains
|
||||
the peripheral, TX queue and a 512-byte RX buffer with a typed `Timestamp` per
|
||||
byte captured at IRQ receipt. The worker takes at most 16 bytes per UART per
|
||||
turn under a short lock. Protocol parsing, scheduling and persistence run
|
||||
outside UART locks. Overflow or a hardware RX fault discards the damaged backlog
|
||||
and resets the decoder before later bytes. These bounds do not establish a
|
||||
measured latency guarantee.
|
||||
|
||||
`kaptan-tx::TransmitQueue` owns bounded admission and one logically active frame.
|
||||
Pending admission cannot replace that active frame. The queue itself is movable
|
||||
and does not promise DMA address stability. UART byte writes and USB endpoint
|
||||
writes borrow or copy its bytes synchronously. MAVLink owns encoding, while
|
||||
platform I/O owners handle transmission.
|
||||
|
||||
USB uses the repository-patched Synopsys driver. Runtime register waits allow
|
||||
higher-priority interrupts and stop after 5 ms or 1,080,000 polls per wait. These
|
||||
are fault limits, not measured execution times. A fault holds USB in peripheral
|
||||
reset until restart and discards its queued data. Brief critical sections protect
|
||||
USB ownership and endpoint buffers. Boot initialization still runs under RTIC's
|
||||
startup interrupt mask.
|
||||
|
||||
SPI1 and SPI3 DMA each use a once-claimed static pair of 32-byte TX/RX buffers.
|
||||
`start_transfer` copies in, so hardware retains no caller slice. Completion
|
||||
copies out only after checking stream disable, SPI quiescence, transfer counts
|
||||
and faults. Failure to establish shutdown or drain RX quarantines the storage
|
||||
and prevents reuse. Moving, dropping or forgetting the owner never releases
|
||||
that claim. The F722 implementation relies on DMA-accessible SRAM, disabled
|
||||
D-cache and device barriers. Physical SPI shutdown, timing and fault behavior
|
||||
still require hardware validation.
|
||||
|
||||
## Monotonic time
|
||||
|
||||
TIM2 is the canonical F722 clock. The configured 108-MHz timer input and PSC=107
|
||||
produce a free-running 32-bit counter at 1 MHz with ARR=`u32::MAX`. All readers
|
||||
and the CC1 service IRQ extend one shared `u64` microsecond epoch inside the same
|
||||
critical section. A consumer may remain silent across wraps if other readers
|
||||
or IRQ service keep extending that epoch. Combined observations must be strictly
|
||||
less than 2^32 microseconds apart, about 71.58 minutes, including interrupt
|
||||
blackouts and debugger halts. NMI and fault handlers must not access this clock.
|
||||
|
||||
CC1 schedules the current 100-Hz service at epoch-aligned 10-ms deadlines.
|
||||
Delayed service skips missed periods in constant work and emits at most one
|
||||
notification per IRQ. Rearming uses two bounded attempts before pending a
|
||||
recovery invocation. Callbacks and notifications run after clock exclusion ends.
|
||||
TIM2 is not reset for each service tick, and no update IRQ or timer DMA is used.
|
||||
Its IRQ is enabled only at the successful end of initialization.
|
||||
|
||||
Ordinary WFI retains the timer clock. Deep sleep, STOP/Standby, clock-tree changes,
|
||||
counter reset/regression and debugger halts spanning a full wrap are unsupported
|
||||
and require platform reset. Uptime must leave one service period before `u64`
|
||||
microsecond overflow. DWT is used only for the CPU-running SRAM flash-poll timeout
|
||||
at 216 MHz, not sensor timestamps or service deadlines. These contracts do not
|
||||
establish measured oscillator accuracy, IRQ latency, jitter or worst-case timing.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
`kaptan-log` has no MAVLink, platform, time, allocation or filesystem dependency.
|
||||
Its configuration coupling is compile-time enum metadata for `LevelFilter`.
|
||||
`kaptan-err-reportable` independently owns explicit error identities and a bounded
|
||||
nonblocking ingress, without logging, transport or clock dependencies.
|
||||
Event creation is independent from sink filtering. The worker drains reportable
|
||||
errors before logs and offers each diagnostic to all MAVLink links. Heartbeat
|
||||
and sensor publications use separate scheduling.
|
||||
|
||||
See [Configuration](configuration.md) for KCFG and the file-transfer contract,
|
||||
[Logs](logs.md) for the receiver contract, and [Errors](errors.md)
|
||||
@@ -96,7 +193,8 @@ for derived-error and failure-reporting scope.
|
||||
- A peripheral owner is local to the platform runtime.
|
||||
- Portable crates use typed units and validated values rather than raw wire or
|
||||
timer representations.
|
||||
- A safety permit is epoch-bound and must be revalidated at the only nonzero
|
||||
output boundary.
|
||||
- A failed observation or numerical check does not partially commit estimator,
|
||||
controller, allocator, or safety state.
|
||||
- `SafetySupervisor::evaluate_and_commit` checks current evidence at the
|
||||
synchronous enabled-output boundary. No transferable permit escapes it.
|
||||
- Rejected numerical updates do not partially commit computational candidates.
|
||||
Fault latches, inhibition and ingress replay watermarks follow their explicit
|
||||
failure contracts rather than rolling back authority or replay history.
|
||||
@@ -1,19 +1,13 @@
|
||||
# GEPRC GEP-F722-HD V2
|
||||
|
||||
The GEPRC GEP-F722-HD V2 board declaration uses the ICM-42688-P on SPI3 and
|
||||
MAVLink telemetry on USB CDC-ACM and USART2. It declares no vertical sensor stream. The
|
||||
manufacturer's [GEP-F722-HD V2 manual](https://geprc.com/wp-content/uploads/2025/05/GEP-F722-HD-v2%E9%A3%9E%E6%8E%A7_%E4%BA%A7%E5%93%81%E8%AF%B4%E6%98%8E%E4%B9%A6-.pdf)
|
||||
labels the external UART2 pads `T2` and `R2`.
|
||||
The GEPRC GEP-F722-HD V2 uses the ICM-42688-P on SPI3.
|
||||
The manufacturer's [GEP-F722-HD V2 manual](https://geprc.com/wp-content/uploads/2025/05/GEP-F722-HD-v2%E9%A3%9E%E6%8E%A7_%E4%BA%A7%E5%93%81%E8%AF%B4%E6%98%8E%E4%B9%A6-.pdf)
|
||||
labels UART1 as `T1`/`R1` and UART2 as `T2`/`R2`.
|
||||
|
||||
Its board-local `UserConfig` factory selects endpoint `1/1`, IMU and
|
||||
heartbeat publication rates, and an enabled MAVLink diagnostic sink with an
|
||||
`Info` level filter. The web configurator may replace those
|
||||
target-advertised values in the journal. Range
|
||||
and optical-flow fields are absent because this image has no vertical-sensor
|
||||
route. Its `ImmutableConfig` keeps the ICM-42688-P route, yaw-180° mount,
|
||||
USART2 pins and baud, active-high PA13 status LED, and PB0/PB1/PB4/PB5 output
|
||||
inhibition outside the writable schema. These selections appear together in
|
||||
the board's immutable typestate literal.
|
||||
The board declares its yaw-180° IMU mount, UART catalog, active-high PA13 LED
|
||||
and no I²C range sensor.
|
||||
Before fallible initialization, the platform holds the four documented motor
|
||||
pins PB0/PB1/PB4/PB5 low.
|
||||
|
||||
## Build and package
|
||||
|
||||
@@ -53,9 +47,10 @@ make firmware/tools/configurator
|
||||
Open `http://127.0.0.1:4861` and connect `/dev/serial/by-id/<kaptan-device>` or
|
||||
the discovered `/dev/ttyACM*` path from the Connection section.
|
||||
|
||||
## UART fallback
|
||||
## UART connection
|
||||
|
||||
Connect a 3.3 V USB-UART adapter with crossed data lines and common ground:
|
||||
First assign UART2 to `KaptanMavlink` over USB, save and restart. Then connect
|
||||
a 3.3 V USB-UART adapter with crossed data lines and common ground:
|
||||
|
||||
| Adapter | Flight controller |
|
||||
| --- | --- |
|
||||
@@ -65,11 +60,7 @@ Connect a 3.3 V USB-UART adapter with crossed data lines and common ground:
|
||||
|
||||
Enter `/dev/serial/by-id/<adapter>` in the same configurator's Connection section.
|
||||
|
||||
Open `http://127.0.0.1:4861`, Save the discovered fields, and manually
|
||||
power-cycle the controller after Save completes. The flash journal is outside
|
||||
the application image, so the exact range-limited DFU command above preserves
|
||||
it. A mass erase or full-chip erase removes committed settings and restores
|
||||
board factory defaults at the next boot. The image remains telemetry-only and
|
||||
applies startup motor-pin inhibition. The unsigned local configuration path
|
||||
assumes one directly connected client; disconnect every other transmitter from
|
||||
`R2` while configuring over UART.
|
||||
Use UART2's boot-active baud. If its role was changed or recovery disabled it,
|
||||
connect over normal USB. Follow [save/reset/reload](../configuration.md#web-configurator)
|
||||
before expecting new settings to apply. Disconnect other transmitters from `R2`
|
||||
while configuring. Physical motor control is not yet implemented.
|
||||
@@ -3,10 +3,20 @@
|
||||
Board pages describe supported hardware topology, build products, and safe
|
||||
verification. They do not declare a board flight-ready.
|
||||
|
||||
| Board | MCU family | Primary IMU | MAVLink links |
|
||||
| Board | IMU route | Serial inventory | Factory UART roles |
|
||||
| --- | --- | --- | --- |
|
||||
| [Kakute F7 Mini V3](kakute-f7-mini-v3.md) | STM32F722 | MPU-6000 | USB CDC-ACM, USART1 |
|
||||
| [GEPRC GEP-F722-HD V2](geprc-f722-v2.md) | STM32F722 | ICM-42688-P | USB CDC-ACM, USART2 |
|
||||
| [Kakute F7 Mini V3](kakute-f7-mini-v3.md) | `Mpu6000Spi1` | UART1, UART2, UART3, UART4, UART6 | All disabled |
|
||||
| [GEPRC GEP-F722-HD V2](geprc-f722-v2.md) | `Icm42688Spi3` | UART1, UART2, UART3, UART4, UART6 | All disabled |
|
||||
|
||||
USB CDC-ACM always uses Kaptan MAVLink and has no configurable role. ROM DFU is a separate flashing mode. UART roles and baud are
|
||||
restart-only user settings. Both factories use 115200 baud, endpoint `1/1`,
|
||||
20-Hz IMU, 2-Hz heartbeat, 20-Hz range/flow settings and the `Info` log filter.
|
||||
Range/flow publications require an enabled sensor source.
|
||||
|
||||
See [Configuration](../configuration.md) for role choices, supported baud values,
|
||||
save/reset/reload and invalid-storage USB repair. The range-limited DFU workflows
|
||||
on these pages preserve the journal outside the application image. Mass erase
|
||||
removes saved settings and selects factory values at the next boot.
|
||||
|
||||
All board images share the motor-pin inhibit boundary described in
|
||||
[Safety boundary](../safety-boundary.md).
|
||||
@@ -1,20 +1,14 @@
|
||||
# Kakute F7 Mini V3
|
||||
|
||||
The Holybro Kakute F7 Mini V3 board declaration uses the MPU-6000 on SPI1,
|
||||
MAVLink telemetry on USB CDC-ACM and USART1, and the MTF01 range/optical-flow
|
||||
route on UART4.
|
||||
The Holybro Kakute F7 Mini V3 uses the MPU-6000 on SPI1.
|
||||
The checked-in manufacturer datasheet is
|
||||
`firmware/boards/kakute-f7-mini-v3/datasheet.pdf`.
|
||||
|
||||
Its board-local `UserConfig` factory selects endpoint `1/1`, IMU, range,
|
||||
optical-flow and heartbeat publication rates, and an enabled MAVLink diagnostic
|
||||
sink with an `Info` level filter. The web configurator may replace those
|
||||
target-advertised values in the journal. Its `ImmutableConfig` keeps the
|
||||
MPU-6000 route, body-aligned
|
||||
mount and concrete driver setup, USART1 pins and baud, MTF01 route and baud,
|
||||
active-low PD2 status LED, and PB0/PB1/PB4/PB5 output inhibition outside the
|
||||
writable schema. These selections appear together in the board's immutable
|
||||
typestate literal.
|
||||
The board maps MPU axes `(X, Y, Z)` to `(-Y, X, Z)`, matching the manufacturer's
|
||||
`KAKUTEF7MINIV3` target, and supplies the driver settings. It declares the UART
|
||||
catalog, active-low PD2 LED and no I²C range sensor.
|
||||
Before fallible initialization, the platform holds all six documented
|
||||
motor pins PB0/PB1/PB4/PB5/PB6/PB7 low.
|
||||
|
||||
## Build and package
|
||||
|
||||
@@ -59,9 +53,10 @@ make firmware/tools/configurator
|
||||
Open `http://127.0.0.1:4861` and connect `/dev/serial/by-id/<kaptan-device>` or
|
||||
the discovered `/dev/ttyACM*` path from the Connection section.
|
||||
|
||||
## UART fallback
|
||||
## UART connection
|
||||
|
||||
Use a 3.3 V USB-to-UART adapter with crossed data lines and common ground:
|
||||
First assign UART1 to `KaptanMavlink` over USB, save and restart. Then connect
|
||||
a 3.3 V USB-UART adapter with crossed data lines and common ground:
|
||||
|
||||
| Adapter | Flight controller |
|
||||
| --- | --- |
|
||||
@@ -71,11 +66,7 @@ Use a 3.3 V USB-to-UART adapter with crossed data lines and common ground:
|
||||
|
||||
Enter `/dev/serial/by-id/<adapter>` in the same configurator's Connection section.
|
||||
|
||||
Open `http://127.0.0.1:4861`, edit the discovered fields, press Save, and
|
||||
manually power-cycle the controller after Save completes. The flash journal is
|
||||
outside the application image, so the exact range-limited DFU command above
|
||||
preserves it. A mass erase or full-chip erase removes committed settings and
|
||||
restores board factory defaults at the next boot. The image remains
|
||||
telemetry-only and does not enable motor control. The unsigned local
|
||||
configuration path assumes one directly connected client; disconnect every
|
||||
other transmitter from `R1` while configuring over UART.
|
||||
Use the UART's boot-active baud. If its role was changed or recovery disabled
|
||||
UARTs, connect over normal USB. Follow [save/reset/reload](../configuration.md#web-configurator)
|
||||
before expecting new settings to apply. Disconnect other transmitters from the
|
||||
selected RX pad while configuring. Physical motor control is not yet implemented.
|
||||
+221
-106
@@ -1,16 +1,56 @@
|
||||
# Configuration
|
||||
|
||||
Kaptan has one generic configuration model in `kaptan-config`. Target schemas
|
||||
declare typed fields and validation once; derive code generates the stable
|
||||
field registry and schema fingerprint. Boards supply the sole erased-flash
|
||||
factory values. The browser contains no board, driver, sensor, schema, or
|
||||
parameter catalog.
|
||||
`kaptan-config` owns typed fields, validation and the committed-versus-active
|
||||
lifecycle. The F722 platform supplies the shared user schema and factory values.
|
||||
Boards declare their UART catalog and immutable hardware context. The
|
||||
configurator renders target metadata without a board or parameter catalog.
|
||||
|
||||
## Current deployment matrix
|
||||
## Flight configuration
|
||||
|
||||
| Deployment | Configuration consumed today | Remote writes |
|
||||
`kaptan-flight-config::CandidateProfile` owns the flight policy, gains, thrust
|
||||
model, navigation noise densities and timing constraints. It is consumed by the
|
||||
portable runtime and its tests. The board firmware uses its own board schema
|
||||
and **KCFG v3** service.
|
||||
Field IDs **6, 7 and 25** remain reserved.
|
||||
The other field IDs retain their meanings and numbering.
|
||||
|
||||
Construct the candidate's raw sections and supply a `NavigationInstallation`
|
||||
through `VehicleCapabilities::quad_x`. It declares sources, calibration and
|
||||
reference identities, range mounting and quantization, contact geometry,
|
||||
magnetic field and local pressure/temperature. Complete validation produces
|
||||
the immutable `VehicleProfile`. The current runtime supports Quad-X allocation
|
||||
and four equal contact spheres on a body-frame rectangle.
|
||||
|
||||
The current controller descriptors include fields 41..44:
|
||||
|
||||
| ID | Key | Meaning |
|
||||
| --- | --- | --- |
|
||||
| STM32F722 image | Board factory settings, or the newest valid flash-journal snapshot. | `/config/user.kcfg` over local USB CDC-ACM or the directly wired UART while motor pins remain inhibited. |
|
||||
| 41 | `control.attitude_roll_pitch_gain_per_s` | Roll/pitch attitude gain in 1/s. |
|
||||
| 42 | `control.attitude_yaw_gain_per_s` | Yaw attitude gain in 1/s. |
|
||||
| 43 | `control.yaw_rate_pid.proportional` | Yaw rate proportional gain. |
|
||||
| 44 | `control.yaw_rate_pid.integral` | Yaw rate integral gain. |
|
||||
|
||||
Roll/pitch rate gains use the rate-PID fields. Derivative and contribution limits
|
||||
are shared.
|
||||
|
||||
Use the generated `kaptan-flight-config::SCHEMA_ID` from the source/build being
|
||||
used. Schema identity describes the field contract; it does not qualify a
|
||||
physical vehicle or identify calibrated values.
|
||||
|
||||
The test fixture has a 10-ms control period, 20-ms control deadline, 250-ms
|
||||
maximum command-admission TTL, 300-ms offboard stream timeout and one-second
|
||||
fallback hold. Its configured output lifetime ceiling is 50 ms, further limited
|
||||
by the 20-ms control deadline and live evidence. The navigation time-error
|
||||
ceiling is 2000 us. These are library contracts, not measured F722 timing
|
||||
guarantees or a qualified aircraft profile.
|
||||
|
||||
Navigation owns `MAXIMUM_FREE_ACCELERATION_MPS2 = 25`. Complete-profile
|
||||
validation checks maximum collective thrust `T_max <= m * (25 - g)` using `f64`
|
||||
arithmetic, and requires thrust above weight. The bound applies to free flight,
|
||||
not contact impulses. Mixer and installation remain compiled capabilities.
|
||||
|
||||
The 1.2-kg/18-N fixture and its installation values exist only in test support.
|
||||
They do not calibrate physical sensors or identify a propulsion system.
|
||||
|
||||
## Web configurator
|
||||
|
||||
@@ -27,155 +67,230 @@ as `/dev/serial/by-id/<kaptan-device>` or `/dev/ttyACM0`. For UART, connect a
|
||||
3.3 V USB-UART adapter to the board-specific RX/TX pads and enter its device
|
||||
path instead.
|
||||
|
||||
The web tool defaults to MAVLink source system `255` and baud `115200`.
|
||||
`--source-system` and `--baud` remain optional command-line overrides. Every
|
||||
simultaneously connected GCS must use a different nonzero system ID so transfer
|
||||
ownership and retries cannot alias another client.
|
||||
The tool defaults to MAVLink source system `255` and baud `115200`.
|
||||
`--source-system` and `--baud` are command-line overrides. Match the adapter baud
|
||||
to the UART's boot-active value. For example, from `firmware/`, launch
|
||||
`cargo run -p kaptan-configurator --locked --offline -- --baud 230400`.
|
||||
Peers sharing a link need distinct nonzero source identities, but those
|
||||
identities do not authenticate them.
|
||||
|
||||
Open `http://127.0.0.1:4861`. On discovery, the configurator downloads the
|
||||
complete `/config/user.kcfg` artifact, validates it, and renders the metadata
|
||||
and values contained in that file. Save produces one complete edited KCFG
|
||||
artifact and uploads it. The target validates the complete candidate before
|
||||
atomically persisting it. The boot-active runtime does not change; manually
|
||||
power-cycle the controller after a successful save when the UI reports that a
|
||||
restart is required.
|
||||
1. Connect and wait for discovery to read both saved and boot-active artifacts.
|
||||
Each field shows **Boot-active**, **Saved** and **Draft** values.
|
||||
2. Edit the draft and press **Save**. The target checks the downloaded baseline,
|
||||
validates the complete replacement and persists it. After success, the tool
|
||||
reloads both artifacts to establish the next save's baseline.
|
||||
3. To replace saved settings with the target's factory values, press
|
||||
**Reset saved configuration**. This is a persistence operation, not a reboot.
|
||||
It also works when saved state is unknown, provided active metadata was read.
|
||||
4. Save and factory reset leave active roles, baud, identity, rates and logging
|
||||
unchanged. When restart is required, manually reset or power-cycle the board,
|
||||
then reconnect and check the reloaded values. A UART may now use another baud
|
||||
or no longer carry MAVLink. Normal USB remains the configuration route.
|
||||
|
||||
Saving erases one 128-KiB STM32F7 journal sector synchronously. Expect roughly
|
||||
one second without telemetry or sensor servicing while the target flash
|
||||
controller is busy; the host gives the final close a separate five-second
|
||||
timeout. Keep the vehicle unpowered except for setup power and do not disconnect
|
||||
it during Save.
|
||||
A stale baseline returns `REQUEST_CONFLICT` without writing flash. The tool
|
||||
discards that draft. Disconnect and reconnect to reload before editing again.
|
||||
If saved state is unknown, ordinary saves are blocked. Reconnect to inspect it,
|
||||
then use factory reset or reboot recovery as described below.
|
||||
|
||||
The artifact carries generated field IDs, native scalar kinds, display keys,
|
||||
units, bounds, enum choices, factory values, committed values, schema identity,
|
||||
and the derived restart-required state. A new board or field therefore needs no
|
||||
configurator release. `u8`, `u16`, and enum-backed values retain their native
|
||||
width rather than being widened at the configuration boundary.
|
||||
A changed save or explicit factory reset synchronously erases a 128-KiB journal
|
||||
sector. Flash activity can interrupt telemetry and sensor servicing. The host
|
||||
allows five seconds for the final close, which is not a measured erase-time
|
||||
guarantee. Keep setup power connected until the operation completes. An unchanged
|
||||
ordinary replacement skips flash writes.
|
||||
|
||||
The Incoming MAVLink table groups live, CRC-valid Kaptan packets by source and
|
||||
message ID and shows receive frequency and total count. Selecting a message
|
||||
watches its latest decoded value. The separate Runtime logs view retains
|
||||
individual `LOG` and `ERROR` events in receive order, with local receive time,
|
||||
level, and readable text.
|
||||
See [MAVLink](mavlink.md) and [Logs](logs.md) for the packet and runtime-log views.
|
||||
|
||||
## Board ownership
|
||||
|
||||
Each F722 board declares one `UserConfig` root for the settings consumed by its
|
||||
compiled driver graph. The explicit `FACTORY_USER_CONFIG` literal is the sole
|
||||
erased-flash value. There is no `Default` implementation and no platform-wide
|
||||
union of board schemas.
|
||||
Each F722 board selects `F722UserConfig<Catalog, N>`. The shared factory values
|
||||
leave every configurable UART disabled. USB always carries Kaptan MAVLink and
|
||||
has no configurable role. Board catalogs determine which UART fields exist.
|
||||
|
||||
Both current boards expose MAVLink identity, IMU and heartbeat publication
|
||||
rates, and the MAVLink log level filter. Kakute also exposes range and
|
||||
optical-flow rates because its immutable hardware manifest contains an MTF01.
|
||||
Those fields are absent from GEPRC rather than present-but-disabled.
|
||||
Both current boards expose MAVLink identity, publication rates, log filtering,
|
||||
and `ports` fields from `kaptan-ports`. Range and optical-flow rates are present
|
||||
on both boards because either can assign an inventoried UART to MTF01. Their
|
||||
streams are enabled only when the selected sensor service supplies them.
|
||||
|
||||
The adjacent `IMMUTABLE_CONFIG` literal remains the sole source of facts that
|
||||
cannot safely be edited as scalar configuration:
|
||||
The adjacent `IMMUTABLE_CONFIG` manifest holds:
|
||||
|
||||
- output-inhibit and status-LED routes;
|
||||
- IMU model, bus, interrupt route, mount, and driver setup;
|
||||
- MAVLink UART and optional vertical-sensor routes and bootstrap baud rates;
|
||||
- the fixed F722 USB CDC-ACM transport;
|
||||
- service-timer frequency; and
|
||||
- compiled image role.
|
||||
- the IMU route, buses and interrupt wiring
|
||||
- sensor mount and driver setup
|
||||
- the available serial inventory, status LED and optional I²C range route
|
||||
- service-timer frequency.
|
||||
|
||||
The F722 platform consumes stable semantic field IDs through the generic
|
||||
`Config::field_value` boundary. It does not know a board section, Rust field,
|
||||
or aggregate shape. Whole-candidate validation checks both generated local
|
||||
rules and relationships such as publication budget and immutable hardware
|
||||
capabilities.
|
||||
Motor pins are inhibited at startup. The board images have no physical actuator
|
||||
driver.
|
||||
|
||||
UART roles and baud are writable settings applied only at boot. Each inventoried
|
||||
UART supports `Disabled = 0`, `KaptanMavlink = 1` (`Role::Mavlink`), and
|
||||
`Mtf01 = 2`. Disabled ports do not initialize their peripheral or pins.
|
||||
One board-owned `PortCatalog<N>::DESCRIPTORS` array supplies both
|
||||
`Ports<Catalog, N>` metadata and `SerialInventory`. The same const `N` sizes the
|
||||
boot plan, I/O slots and protocol slots. An absent UART has no slot or field.
|
||||
A declared but disabled UART retains its bounded slot storage.
|
||||
|
||||
| Keys | Role / baud field IDs | MCU TX / RX | Board inventory |
|
||||
| --- | --- | --- | --- |
|
||||
| `ports.uart1.role`, `ports.uart1.baud` | 201 / 202 | PA9 / PA10 | Both |
|
||||
| `ports.uart2.role`, `ports.uart2.baud` | 203 / 204 | PA2 / PA3 | Both |
|
||||
| `ports.uart3.role`, `ports.uart3.baud` | 205 / 206 | PB10 / PB11 | Both |
|
||||
| `ports.uart4.role`, `ports.uart4.baud` | 207 / 208 | PA0 / PA1 | Both |
|
||||
| `ports.uart6.role`, `ports.uart6.baud` | 211 / 212 | PC6 / PC7 | Both |
|
||||
|
||||
Roles are native `u8` values and baud is `u32`. The F722 allowlist is exactly
|
||||
**115200, 230400, 460800**. Intermediate values are rejected even though the
|
||||
displayed bounds span that range. The current MTF01 driver requires **115200**.
|
||||
At most one UART may use MTF01, and it cannot coexist with the immutable I²C
|
||||
range source. These restrictions come from current platform capabilities.
|
||||
|
||||
Whole-candidate validation checks resource conflicts and the enabled publication
|
||||
schedule against the 100-Hz service budget. Each MAVLink UART must also carry
|
||||
the scheduled framing-inclusive bytes at 8N1. This is an admission check, not a
|
||||
delivery guarantee for unscheduled file or diagnostic traffic. USB has no role
|
||||
or baud field. See [Boards](boards/index.md) for factory assignments.
|
||||
|
||||
## Recovery and activation
|
||||
|
||||
`ConfigManager::recover_with` owns recovery state. F722 supplies an explicit
|
||||
setup policy that validates factory settings with all UART roles disabled when
|
||||
stored settings cannot be activated. Normal USB MAVLink is initialized
|
||||
independently of the UART plan. It is not ROM DFU, and its availability still
|
||||
depends on successful clocks, USB hardware and application initialization.
|
||||
|
||||
- Empty storage selects the board factory configuration.
|
||||
- Readable but invalid committed settings remain available for inspection and
|
||||
repair through USB. The boot-active export shows the UART-disabled fallback.
|
||||
- Unreadable storage or a storage error leaves committed state **unknown**.
|
||||
Active values and factory metadata remain readable. Ordinary writes are
|
||||
blocked until reboot recovery establishes a baseline or factory reset succeeds.
|
||||
|
||||
Fallback selection never rewrites storage. A persistence error also makes the
|
||||
committed view unknown because the attempted write may already be durable.
|
||||
Reboot may therefore activate that attempted value. Explicit factory reset
|
||||
always writes the validated factory, even if values appear unchanged. A failed
|
||||
reset leaves durability unknown. Neither operation changes the immutable
|
||||
boot-active view or its boot diagnostic.
|
||||
|
||||
## Configuration endpoints
|
||||
|
||||
| Path | Access | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `/config/user.kcfg` | Read/write | Read the known committed values. Write a complete replacement guarded by its expected base. |
|
||||
| `/config/active.kcfg` | Read-only | Inspect the actual boot selection, including recovery fallback. |
|
||||
| `/config/reset.kcfg` | Write-only | Accept only exact factory requested values. Ignore the expected base and force persistence. |
|
||||
|
||||
Reset still requires a complete schema-matching KCFG artifact. The configurator
|
||||
builds it from active metadata. There is no file command to reboot or activate
|
||||
settings live.
|
||||
|
||||
## KCFG artifact
|
||||
|
||||
KCFG is the public, canonical import/export representation. It is independent
|
||||
of MAVLink and of the private storage format. A file contains:
|
||||
|
||||
- `KCFG` magic and one format version;
|
||||
- generated 16-byte schema identity and exact field count;
|
||||
- one canonical self-describing record for every field; and
|
||||
- `KCFG` magic, format version **3** and a restart flag
|
||||
- generated 16-byte schema identity and exact field count
|
||||
- one record per field with ID, native kind, requested/current value, factory
|
||||
value and expected-base value at native width, plus field flags, bounds,
|
||||
display key, unit and enum choices
|
||||
- a big-endian CRC-32C trailer covering the preceding artifact bytes.
|
||||
|
||||
The trailer uses network byte order deliberately. The generic file dialect
|
||||
also calculates CRC-32C over every complete file byte; network-order storage
|
||||
keeps that independent transfer checksum content-specific instead of producing
|
||||
the fixed residue obtained by appending a reflected CRC in little-endian order.
|
||||
Exports put the selected view's value in both current and expected-base slots.
|
||||
Edits change only requested values. On ordinary save, the manager compares every
|
||||
expected-base scalar's native kind and bits with current committed values before
|
||||
whole-candidate validation, no-op detection or persistence. This is a full-value
|
||||
compare-and-swap (CAS). Native-kind-and-bit equality also governs no-op and
|
||||
restart comparisons. Positive and negative floating-point zero differ, while
|
||||
identical NaN payload bits compare equal when the field's rules allow NaN.
|
||||
It is not a CRC, generation counter or history check. A change away from and
|
||||
back to the same values still matches.
|
||||
|
||||
Field flag bits 0 and 1 indicate minimum and maximum bounds. Bit 2 is `finite`
|
||||
and bit 3 is `nonzero`. Other bits are reserved zero. These constraints are
|
||||
independent: `finite` rejects NaN and infinities, while `nonzero` rejects numeric
|
||||
zero, including both floating-point signed zeros. `nonzero` alone does not imply
|
||||
finite values. Any declared floating-point range rejects NaN as unordered even
|
||||
without `finite`. Infinities are subject to the declared bounds and `finite`
|
||||
rule. These flags are descriptor metadata and contribute to schema identity.
|
||||
|
||||
The configurator exchanges floating-point values with the browser as strings.
|
||||
Finite decimal strings preserve signed zero. Nonfinite values use exact IEEE
|
||||
bit strings, `bits:0x` followed by 8 hexadecimal digits for `f32` or 16 for `f64`,
|
||||
preserving NaN sign and payload. This representation does not bypass validation.
|
||||
|
||||
CRC-32C detects accidental corruption. The big-endian trailer avoids the fixed
|
||||
residue produced by a little-endian reflected CRC under the outer transfer CRC,
|
||||
but neither checksum proves unique content or authenticates a peer.
|
||||
|
||||
An imported file must match the target-generated schema, descriptor metadata,
|
||||
factory values, field order, native scalar kinds, and exact length. Unknown,
|
||||
duplicate, missing, malformed, noncanonical, out-of-range, and relationally
|
||||
invalid values are rejected. The target stages only the typed candidate and a
|
||||
small parser state; it never buffers the complete public file. Exports are also
|
||||
generated by range directly from compiled metadata and current committed
|
||||
values. One small export plan containing the exact length and checksums is
|
||||
prepared when a read opens and reused for every requested range.
|
||||
invalid requested values are rejected. The target stages typed requested and
|
||||
expected-base candidates with a small parser state, not a whole-file buffer.
|
||||
Exports are generated by range from metadata and the selected view, with one
|
||||
length/checksum plan prepared at open.
|
||||
|
||||
Schema identity is required because a complete file can otherwise be validly
|
||||
encoded while belonging to another firmware schema. It is generated from the
|
||||
schema and is not a hand-maintained board ID.
|
||||
The board-agnostic `decode_kcfg` API instead borrows a complete input artifact
|
||||
and requires caller-owned 8192-byte field-ID scratch. Hosted callers can allocate
|
||||
that bitmap directly on the heap. Decoding visits fields once, with work
|
||||
O(input bytes + 8192), and the returned view borrows only the input, so scratch
|
||||
is immediately reusable. This dynamic decoder is distinct from the bounded
|
||||
typed streaming importer used by firmware.
|
||||
|
||||
The artifact header's restart bit describes the committed values in an export.
|
||||
It is not an instruction accepted from an upload: after persistence the target
|
||||
derives restart state by comparing committed values with immutable boot-active
|
||||
values.
|
||||
Schema identity detects schema mismatch. It is not a device identity,
|
||||
authentication token or proof of all hardware compatibility. Complete-candidate
|
||||
validation against the immutable context remains authoritative.
|
||||
|
||||
The restart bit reports manager state, including on the active export. It is not
|
||||
an upload instruction. The target derives it from committed versus boot-active
|
||||
values, or sets it when durable state is unknown.
|
||||
|
||||
## Generic file dialect
|
||||
|
||||
Kaptan uses two custom messages rather than standard MAVLink FTP or a family of
|
||||
configuration-specific requests:
|
||||
|
||||
```text
|
||||
FILE_CONTROL
|
||||
FILE_DATA
|
||||
```
|
||||
Kaptan uses custom `FILE_CONTROL` and `FILE_DATA` messages rather than standard
|
||||
MAVLink FTP or configuration-specific messages.
|
||||
|
||||
`FILE_CONTROL` opens, closes, aborts, and acknowledges a transfer. It
|
||||
carries a client-selected nonzero `u16` transfer ID, exact target, position,
|
||||
complete file size, CRC-32C, operation/status, and at most 64 bytes of path or
|
||||
result detail. A source does not reuse an ID during the ten-second replay lease.
|
||||
A new ID from the same source may supersede an open only before the first byte
|
||||
has advanced; a progressed abandoned transfer remains pinned until lease expiry.
|
||||
result detail. A peer does not reuse an ID during the ten-second replay lease.
|
||||
A new ID from the same source and ingress may supersede an open only before the
|
||||
first byte advances. A progressed abandoned transfer stays pinned until expiry.
|
||||
`FILE_DATA` uses the full MAVLink payload budget for a
|
||||
246-byte chunk. The logical final-chunk length follows from the opened file
|
||||
size, so no redundant length field is sent with every data packet. An upload
|
||||
request carries status `UNSPECIFIED`; a read-data response carries status `OK`.
|
||||
All failures remain typed `FILE_CONTROL` responses.
|
||||
|
||||
The service owns one bounded stop-and-wait transfer. Chunks are sequential;
|
||||
immediate retries are replay-safe, conflicting retries are rejected, and stale
|
||||
transfer IDs cannot mutate a later transfer. A receive fault or bounded lease
|
||||
expiry aborts staged provider state. The generic layer knows only paths, bytes,
|
||||
positions, checksums, ownership, and retry state. It does not parse KCFG or
|
||||
contain configuration policy.
|
||||
One stop-and-wait transfer is shared across all links. Ownership includes the
|
||||
claimed source system/component and the platform-supplied ingress identity.
|
||||
Chunks are sequential. Immediate retries compare exact retained bytes, not just
|
||||
checksums. Conflicting retries are rejected. Accepted owner activity renews the
|
||||
ten-second monotonic lease, while rejected traffic does not. Expiry aborts staged
|
||||
provider state. A receive discontinuity resets framing without itself releasing
|
||||
the file lease. Responses use the requesting link.
|
||||
|
||||
The current provider exposes exactly `/config/user.kcfg`. A successful write
|
||||
close has generic status `OK` and content beginning with the authoritative
|
||||
seven-byte KCFG result prefix: restart flag, primary field, related field, and
|
||||
validation-rule code. Its exact layout is `flags:u8 | primary_field:u16 LE |
|
||||
related_field:u16 LE | validation_rule:u16 LE`; flag bit 0, when set, means a
|
||||
For either writable KCFG endpoint, a successful close has generic status `OK`
|
||||
and an authoritative seven-byte KCFG result prefix:
|
||||
`flags:u8 | primary_field:u16 LE |
|
||||
related_field:u16 LE | validation_rule:u16 LE`. Flag bit 0, when set, means a
|
||||
restart is required, and every other flag bit is reserved zero. The field
|
||||
sentinel is `0xffff`; rule zero means no rule.
|
||||
sentinel is `0xffff`. Rule zero means no rule.
|
||||
KCFG `CONTENT_REJECTED` responses use the same prefix and may append bounded
|
||||
UTF-8 detail. The configurator rejects a successful close with a missing,
|
||||
malformed, or failure-bearing prefix. The file layer passes these bytes through
|
||||
without interpreting them.
|
||||
|
||||
Local USB CDC-ACM and the directly wired UART are the F722 transports. CRC
|
||||
validates framing integrity, not peer authenticity. Use one directly connected
|
||||
client and disconnect other transmitters from the board RX line while using the
|
||||
UART. A shared or remote GCS still requires signing, source/key binding,
|
||||
persistent anti-replay state, and provisioning before this inbound service can
|
||||
be exposed safely.
|
||||
See [MAVLink](mavlink.md) for the local-link trust boundary.
|
||||
|
||||
## Private persistence
|
||||
|
||||
F722 uses private persistence separate from the public KCFG artifact.
|
||||
The F722 private storage format is independent of the public KCFG artifact.
|
||||
|
||||
### F722 flash journal
|
||||
|
||||
F722 stores a compact private `KCSP` scalar snapshot inside a two-slot journal.
|
||||
Each entry stores its stable field ID, scalar kind, and value at that kind's
|
||||
native width; an `u8` is one byte rather than a widened integer slot. Each
|
||||
F722 stores a `KCSP` scalar snapshot inside a two-slot journal.
|
||||
Each entry stores its stable field ID, scalar kind and value at native width. Each
|
||||
journal record includes payload length, CRC-32C, commit marker, and a private
|
||||
monotonic sequence. Saving erases the inactive slot, programs and verifies the
|
||||
complete replacement, and writes the commit marker last. The previous valid
|
||||
|
||||
+13
-2
@@ -78,14 +78,25 @@ different reportable error is already pending.
|
||||
`PersistConfigurationError` ID that distinguishes journal geometry, model,
|
||||
sequence, flash-controller, bounds, unlock, and verification
|
||||
failures. Replaying the cached close result does not repeat the report or
|
||||
flash operation.
|
||||
flash operation. Any persistence error makes committed state unknown and
|
||||
blocks ordinary saves until reboot recovery or a successful explicit factory
|
||||
reset. Boot-active values remain unchanged.
|
||||
- A stale KCFG expected base returns `REQUEST_CONFLICT` before persistence.
|
||||
It is a reload-required editing conflict, not a reportable firmware error.
|
||||
Invalid boot configuration uses the UART-disabled USB setup path and retains
|
||||
readable saved values for repair. See [Configuration](configuration.md).
|
||||
- SPI completion errors never copy partial data into the caller's receive
|
||||
buffer. If bounded shutdown cannot establish quiescence, the SPI route
|
||||
quarantines its static DMA storage and refuses further transfers. This is
|
||||
distinct from transient `Busy`. Physical SPI shutdown and fault behavior
|
||||
remain hardware-validation work.
|
||||
- Reported `ReportableError` values reach the F722 MAVLink worker as a
|
||||
`ERROR`, subject to bounded transport admission. The payload is
|
||||
`{ error_id, raised_at[96] }`. `raised_at` is a zero-padded UTF-8
|
||||
`file:line` prefix for the reporting point.
|
||||
Dynamic human-readable context belongs in `LOG`, while
|
||||
structured protocol failures remain in their owning response message.
|
||||
- Control, navigation, and safety error types are derive-ready but
|
||||
- Control, navigation and safety error types are derive-ready but
|
||||
their live owners are not yet attached to a MAVLink worker.
|
||||
|
||||
F722 uses `panic-halt`, without live panic reporting or retained-record replay.
|
||||
|
||||
@@ -30,8 +30,8 @@ target-specific commands in [Development and verification](development.md).
|
||||
|
||||
| Path | Responsibility |
|
||||
| --- | --- |
|
||||
| `firmware/boards/` | Board-owned user schemas, factory values, immutable hardware declarations, and image composition roots. |
|
||||
| `firmware/platforms/stm32f7/` | Reviewed F722 RTIC platform, peripheral ownership, and shared linker layout. |
|
||||
| `firmware/boards/` | Hardware declarations, UART catalogs and image roots. |
|
||||
| `firmware/platforms/stm32f7/` | F722 RTIC platform, user schema, factory values, peripheral ownership and linker layout. |
|
||||
| `firmware/libs/` | Typed math, estimation, control, safety, protocol, and driver crates. |
|
||||
| `firmware/tools/configurator/` | Board-agnostic loopback web UI and serial bridge. |
|
||||
| `docs/` | This Markdown documentation site. |
|
||||
|
||||
+3
-3
@@ -4,9 +4,9 @@ Kaptan is an embedded Rust flight-controller project. This manual is the
|
||||
single source of project documentation: board support, safety boundaries,
|
||||
development workflow, and design references live here as Markdown.
|
||||
|
||||
> **Status:** the checked-in firmware is telemetry-only. It does not expose
|
||||
> physical motor authority, and this site does not make a physical-flight
|
||||
> release claim.
|
||||
> **Status:** The board images do not yet run the flight runtime or implement
|
||||
> motor drivers. Motor pins are inhibited at startup. Physical flight has not
|
||||
> been qualified.
|
||||
|
||||
## Index
|
||||
|
||||
|
||||
+11
-9
@@ -60,11 +60,12 @@ Rust error variant. Renaming or reordering a variant does not change it.
|
||||
diagnostics. Protocol failures remain in their owning response; for example, a
|
||||
rejected KCFG upload returns a `FILE_CONTROL` failure and readable
|
||||
bounded detail.
|
||||
Delivery is bounded and lossy under link pressure; heartbeat and safety
|
||||
Delivery is bounded and lossy under link pressure. Heartbeat and sensor
|
||||
telemetry use separate paths. Logs have no publication rate setting and are
|
||||
not periodic streams. A retained event leaves its MAVLink sink only once the
|
||||
lower transport admits it; temporary queue pressure only defers that same
|
||||
event.
|
||||
not periodic streams. The worker offers each retained event to USB and every
|
||||
MAVLink UART, then removes it once at least one transport admits it. Admission
|
||||
does not prove receipt or delivery to every link. If none admits it, the event
|
||||
remains pending. Opening USB does not take diagnostics away from UART.
|
||||
|
||||
## Filtering
|
||||
|
||||
@@ -73,8 +74,11 @@ Trace (1), Debug (2), Info (3), Warn (4), and Error (5). `Info`, for example,
|
||||
accepts Info, Warn, and Error log events. `Off` suppresses `LOG` on MAVLink
|
||||
without suppressing reportable `ERROR` events or logs sent to another
|
||||
sink. There is no separate enable flag and no MAVLink-specific set of extra
|
||||
runtime log levels. F722 uses the validated filter recovered at boot, falling
|
||||
back to its board factory when the journal is empty or invalid.
|
||||
runtime log levels. F722 uses the validated boot-active filter. Empty storage
|
||||
uses factory values. Invalid storage selects the setup fallback, which also
|
||||
uses the factory filter but disables UART roles. Saved changes and factory
|
||||
reset do not alter the active filter until reboot. See
|
||||
[configuration recovery](configuration.md#recovery-and-activation).
|
||||
|
||||
Events retain the compact typed `Level` and sink configuration retains the
|
||||
compact typed `LevelFilter`. Schema metadata supplies the filter choices to the
|
||||
@@ -82,6 +86,4 @@ configurator.
|
||||
Only the MAVLink adapter converts the five event levels to the typed Kaptan
|
||||
wire enum.
|
||||
|
||||
The log ingress is transport-neutral and unfiltered. F722 images accept the
|
||||
unsigned Kaptan file service on local USB CDC-ACM and their directly wired UART; that physical-link assumption does not extend to a shared
|
||||
or remote MAVLink network.
|
||||
The log ingress is transport-neutral and unfiltered.
|
||||
+55
-36
@@ -9,48 +9,67 @@ dialect. Inbound processing is narrowly scoped:
|
||||
- Signed frames are discarded because no signature verifier or persistent
|
||||
replay state exists.
|
||||
- CRC-valid unsigned MAVLink 2 frames are retained only when their message ID
|
||||
and extra CRC belong to the Kaptan dialect. Exact-target Kaptan file requests
|
||||
are the only inbound records with a current firmware consumer.
|
||||
and extra CRC belong to the Kaptan dialect. The board firmware consumes
|
||||
exact-target Kaptan file requests.
|
||||
|
||||
Shared or remote inbound authority is deliberately unsupported. Enabling it
|
||||
requires verified MAVLink 2 signing, configured source/target binding,
|
||||
persistent anti-replay state, and a provisioning/key-rotation policy as one
|
||||
reviewed feature.
|
||||
|
||||
Each F722 image exposes MAVLink on its board-selected UART and as a USB CDC-ACM
|
||||
serial device. Linux normally names the application USB link `/dev/ttyACM*`;
|
||||
this is distinct from the STM32 ROM DFU device used while the Boot pin is
|
||||
asserted. UART and USB have independent receive framers. A host that opens the
|
||||
CDC port and asserts DTR becomes the publication and diagnostic link; UART is
|
||||
used while USB is closed. A file response is always returned on the link that
|
||||
carried its request, so connecting USB cannot redirect a UART transaction.
|
||||
Each F722 board image initializes USB CDC-ACM as a MAVLink link independently
|
||||
of UART roles. Linux normally names it `/dev/ttyACM*`. It is separate from the
|
||||
STM32 ROM DFU device used for flashing. Every UART assigned `KaptanMavlink` at
|
||||
boot has its own link. USB needs an enumerated device and asserted DTR to carry
|
||||
traffic. Opening or closing it does not redirect UART publications.
|
||||
|
||||
Each link has independent scheduling, encoding, framing and bounded TX state.
|
||||
Current links share the same boot-active endpoint identity and publication
|
||||
rates, with one shared sensor snapshot per service turn. There are no per-link
|
||||
rate settings. A stalled link does not consume another link's publication state,
|
||||
though all links share the protocol worker and flash-operation stalls. File
|
||||
responses always return on the requesting link.
|
||||
|
||||
The current descriptor uses the pid.codes shared development identity
|
||||
`1209:0001` plus the STM32 factory device ID as its serial number. Obtain and
|
||||
substitute a project-specific PID before distributing hardware as a product.
|
||||
|
||||
These local links are operational file transports, not authenticated
|
||||
The normal telemetry links are operational file transports, not authenticated
|
||||
remote-control channels. Any transmitter physically connected to either link
|
||||
can issue file requests, so use a direct USB cable or USB-UART adapter and
|
||||
disconnect other transmitters while configuring. The platform retains its
|
||||
startup-configured low motor-output state throughout this workflow; no marker
|
||||
type is treated as GPIO ownership or command authority. A networked GCS must
|
||||
add verified signing, source binding, and durable replay protection before it
|
||||
can receive authority.
|
||||
type is treated as GPIO ownership or command authority.
|
||||
|
||||
The custom Kaptan file service admits one exact source principal for a bounded
|
||||
stop-and-wait transfer. A receive fault or lease expiry aborts its staged
|
||||
provider state. Completed control replies have a bounded replay path so a lost
|
||||
close response does not repeat a flash operation. This serialization does not
|
||||
authenticate its peer.
|
||||
One file service serializes transfers across USB and all MAVLink UARTs. Its
|
||||
owner is the claimed source system/component plus ingress identity, so the same
|
||||
wire identity on another port cannot take over a transfer. Completed replies
|
||||
have an exact bounded replay path so a lost close response does not repeat a
|
||||
flash operation. Neither source identity, schema identity nor CRC authenticates
|
||||
a peer. [Configuration](configuration.md#generic-file-dialect) defines the lease
|
||||
and retry contract.
|
||||
|
||||
Telemetry transmission uses bounded frame classes. A critical frame may
|
||||
replace pending best-effort traffic, but never a UART-DMA-owned or active USB
|
||||
frame. UART completion retries preserve ordering, while USB advances a retained
|
||||
frame across bounded CDC endpoint writes. DTR close, suspend, and
|
||||
deconfiguration pause USB transmission and return publications to UART. Only a
|
||||
bus reset discards USB transmission state. Closing DTR also discards partial
|
||||
input. File-transfer and response-replay state expire under the protocol lease,
|
||||
not on transport disconnect.
|
||||
Telemetry transmission uses bounded frame classes. A critical frame may replace
|
||||
pending best-effort traffic, but never an active frame. UART TX is IRQ-owned:
|
||||
each interrupt writes at most one byte and retires at most one completed frame.
|
||||
USB advances a retained frame through bounded CDC endpoint writes. DTR close,
|
||||
suspend and deconfiguration pause USB TX. Only a bus reset discards its TX state.
|
||||
Closing DTR discards partial input. UART receive faults reset framing. File and
|
||||
replay state remain governed by the protocol lease rather than disconnects.
|
||||
|
||||
USB receive backpressure masks only `GINTMSK.RXFLVLM` while application packet
|
||||
bytes remain unconsumed. The final byte cursor and gate update share the USB
|
||||
resource lock. The OTG FS NVIC vector stays enabled, as do IN/TX completion,
|
||||
reset and suspend interrupt sources. This does not make control reception
|
||||
independent: EP0 shares the RX FIFO and cannot bypass a blocked bulk packet at
|
||||
its head. Once buffered bytes are consumed, RXFLVLM is restored. An incomplete
|
||||
MAVLink frame alone does not keep RX masked.
|
||||
|
||||
UART role and baud changes take effect after reboot. See
|
||||
[Configuration](configuration.md) for allowed values and USB repair, and
|
||||
[Architecture](architecture.md#io-ownership) for RX buffering and ownership.
|
||||
Normal USB configuration recovery does not guarantee availability after an
|
||||
arbitrary clock, USB hardware or initialization failure.
|
||||
|
||||
The local web configurator exposes a bounded live view of CRC-valid inbound
|
||||
frames. It groups packets by source and message ID and measures receive
|
||||
@@ -71,35 +90,35 @@ one-message leaves grouped by interaction contract:
|
||||
| Directory | Contract |
|
||||
| --- | --- |
|
||||
| `publish/` | Zero-reply records for any interested receiver. |
|
||||
| `exchange/` | Exact-endpoint requests with one terminal response; currently empty. |
|
||||
| `transfer/` | Exact-endpoint, bounded multi-message sessions. |
|
||||
|
||||
The root includes every message leaf directly; there are no redundant category
|
||||
index files or separate enum catalogs. Placement is a wire-contract
|
||||
classification, while bindgen still generates one flat `generated::kaptan`
|
||||
module. Bare message names are scoped by that sole dialect and must not be
|
||||
merged with another MAVLink dialect. `exchange/` gains its first XML only when
|
||||
an exchange has a current consumer.
|
||||
merged with another MAVLink dialect.
|
||||
`LOG` is an outbound human-readable four-field record. `ERROR`
|
||||
is the separate bounded channel for reportable firmware errors, carrying a
|
||||
stable explicit ID plus the `file:line` of its reporting point, and needs no
|
||||
redundant level field. `FILE_CONTROL` and `FILE_DATA` implement
|
||||
Kaptan's generic virtual-file transport without standard MAVLink FTP
|
||||
compatibility. The current configurator reads and replaces
|
||||
`/config/user.kcfg`. See
|
||||
compatibility. The configurator reads saved and boot-active KCFG values and can
|
||||
save replacements or reset saved settings to factory values. See
|
||||
[Configuration](configuration.md), [Logs](logs.md), and [Errors](errors.md) for
|
||||
the exact contracts.
|
||||
|
||||
The current scheduled publications are deliberately native rather than
|
||||
The board firmware's scheduled publications are native rather than
|
||||
projections into a broader dialect:
|
||||
|
||||
| Publication | Current payload |
|
||||
| --- | --- |
|
||||
| `HEARTBEAT` (51027) | The single current initialized/output-inhibited state. |
|
||||
| `IMU` (51028) | Monotonic microseconds, body acceleration in mg, angular rate in mrad/s, and temperature in cdegC. |
|
||||
| `IMU` (51028) | `u64` monotonic microseconds, `i16[3]` body acceleration in mg, `i32[3]` angular rate in mrad/s, and `i16` temperature in cdegC. |
|
||||
| `RANGE` (51029) | Monotonic microseconds and downward distance in mm. |
|
||||
| `OPTICAL_FLOW` (51030) | Monotonic microseconds, integer ground-relative velocity in mm/s, distance in mm, and quality. |
|
||||
|
||||
These records preserve the firmware's bounded native values. They do not
|
||||
carry unused sensor IDs, repeated board topology, float projections, narrowed
|
||||
timestamps, or clamped measurements.
|
||||
|
||||
The IMU payload is 28 bytes with MAVLink extra CRC **22**. Wire field ordering
|
||||
places time at byte 0, angular rate at byte 8, acceleration at byte 20 and
|
||||
temperature at byte 26. The signed 32-bit gyro units preserve the full decoded
|
||||
MPU6000 ±2000°/s range through mounting, mailbox handoff and encoding.
|
||||
+74
-20
@@ -1,34 +1,88 @@
|
||||
# Safety boundary
|
||||
|
||||
Every checked-in F722 image drives PB0, PB1, PB4, and PB5 low before normal
|
||||
board initialization. This startup output inhibition configures each pin as a
|
||||
low, push-pull GPIO output; it runs before clock-tree setup, board routing,
|
||||
UART setup, or RTIC tasks. There is no marker or token standing in for the
|
||||
register writes.
|
||||
F722 startup drives PB0, PB1, PB4 and PB5 low, plus PB6 and PB7 on Kakute.
|
||||
It configures them as push-pull GPIO outputs before clock-tree setup, board
|
||||
routing, UART setup or RTIC tasks.
|
||||
|
||||
This is a firmware startup property, not a hardware latch. Reset temporarily
|
||||
returns GPIO configuration to the MCU reset state, and the code cannot prove
|
||||
external routing, pull resistors, ESC polarity, or ESC power behavior. Board
|
||||
support must document those electrical assumptions; continuous inhibition
|
||||
external routing, pull resistors, ESC polarity or ESC power behavior. Board
|
||||
support must document those electrical assumptions. Continuous inhibition
|
||||
through reset requires board hardware that provides it.
|
||||
|
||||
There is no checked-in platform actuator route:
|
||||
|
||||
- no DShot or PWM peripheral implementation is linked;
|
||||
- no TIM3/TIM5 motor task or DMA output path exists;
|
||||
- only CRC-valid Kaptan-dialect MAVLink input reaches the bounded virtual-file
|
||||
service, which has no actuator path;
|
||||
- no arm command is defined or admitted.
|
||||
|
||||
The framing and file-transfer crates do not grant hardware access or actuator
|
||||
authority.
|
||||
The board images have no DShot, PWM or other actuator driver. The portable
|
||||
flight runtime is not yet connected to physical motors. File requests grant
|
||||
no actuator access.
|
||||
|
||||
Run the structural guard after any platform or board change:
|
||||
|
||||
```sh
|
||||
make firmware -- test -p platform-stm32f7 \
|
||||
cd firmware
|
||||
cargo test -p platform-stm32f7 \
|
||||
--test platform_safety_boundary --locked --offline
|
||||
```
|
||||
|
||||
This boundary is deliberately stronger than an application-level disarm flag:
|
||||
it removes the physical actuator implementation from the platform image.
|
||||
## Shared runtime commit contract
|
||||
|
||||
`FlightEndpoint` uses `SafetySupervisor::evaluate_and_commit` to
|
||||
evaluate current evidence and invoke an enabled-output callback at most once
|
||||
while exclusively borrowing that supervisor. The callback synchronously retains
|
||||
a core-owned `OutputRecord` in caller-owned storage. This establishes local
|
||||
publication only. The receiving consumer must check the record's binding,
|
||||
sequence and absolute validity at application time. A queued record or successful
|
||||
USB write is not a reusable permission or proof of remote application.
|
||||
|
||||
Runtime ingress carries a producer session and runtime generation, with separate
|
||||
command-owner, arm, offboard and origin bindings. Commands and setpoints have
|
||||
independent strictly increasing sequences. Retransmission retains its original
|
||||
identity and bytes. Replays and wrong bindings cannot renew authority, and checked
|
||||
authority sequences cannot wrap. A local `begin_epoch` is allowed only while
|
||||
disarmed after successful inhibited publication. It resets navigation and request
|
||||
watermarks while preserving the producer's output sequence and clock history.
|
||||
These typed runtime identities do not authenticate a peer.
|
||||
|
||||
Observation admission receives independently captured processing time in that
|
||||
epoch. The measurement-time upper bound must not follow receipt time, receipt
|
||||
must not follow processing time, and conservative measurement age must satisfy
|
||||
the stream's freshness bound.
|
||||
Receipt alone does not establish accepted or current evidence. Explicit clock
|
||||
ticks advance navigation and supervision even when input stops. The current
|
||||
control period is 10 ms on an absolute due grid with a 20-ms control deadline.
|
||||
`supervise` checks time, cached health and deadlines without integrating control
|
||||
or renewing enabled output. A deadline miss is a fault, not permission to replay
|
||||
an old allocation indefinitely.
|
||||
|
||||
Returned enabled-output errors latch a fault and trigger an inhibition attempt.
|
||||
The sink must remain callable for that attempt. At terminal landing, the endpoint
|
||||
clears arm authority and attempts inhibition before publishing terminal command
|
||||
outcomes. Successful stop completion requires accepted inhibition; a rejected
|
||||
inhibition leaves the command failed. Callers must reconcile committed outcomes after
|
||||
both successful and failed core calls. This ordering does not prove physical
|
||||
touchdown or motor stop.
|
||||
|
||||
Output callbacks must not panic. Cleanup guarantees apply to
|
||||
returned errors, not unwinding. A host that catches an unwind must discard the
|
||||
affected endpoint and output owner rather than resume them or use an epoch reset
|
||||
as recovery. Platform panic containment owns the physical
|
||||
response. Abort or halt alone does not establish zero output.
|
||||
|
||||
## Core failure policy
|
||||
|
||||
`TakeoffHoverLand` starts only from `ArmedIdle` on a fresh command. Settled
|
||||
takeoff starts a 30-second hold, followed by landing under the existing deadline
|
||||
and landing reserve. It completes after accepted output inhibition. A superseding
|
||||
command cancels the mission. The board integration prerequisites above still apply.
|
||||
|
||||
With required navigation and output health present, offboard stream expiry
|
||||
after 300 ms latches fallback, holds for the configured duration (at most one
|
||||
second) from expiry, then lands.
|
||||
Late packets do not resume the expired stream. Loss of required IMU/range or
|
||||
other navigation capability, numerical failure, control-deadline failure or
|
||||
output-publication failure can terminate and inhibit instead. Hold-then-land is
|
||||
not promised without its required evidence.
|
||||
|
||||
The core's configured output lifetime is at most 50 ms, further shortened by
|
||||
capability evidence and the current 20-ms control deadline. A future physical
|
||||
composition must enforce output validity at its actuator boundary and establish
|
||||
its own hardware timing and failure behavior. The 1.2-kg/18-N test fixture is
|
||||
uncalibrated. Component verification does not qualify a vehicle.
|
||||
Reference in new issue
Block a user