Import independent seat firmware source without build artifacts

This commit is contained in:
2026-10-26 11:19:21 +08:00
commit 83d003055a
1205 changed files with 1012409 additions and 0 deletions
@@ -0,0 +1,59 @@
---
name: n7-seat-self-compose
description: Implement and compile arbitrary Chinese or English N7 four-motor seat-action firmware requests for Classic CAN OTA with full source access.
---
# N7 Seat Self Compose
Implement the user's request directly in the complete writable firmware task copy.
Choose the design and edit any canonical source, driver, generator, build file, or
other project file needed. For the supplied action runtime, edit the authoritative
`firmware/application/ota_self_compose_plan.h`; never edit or depend on a prior
`build/gcc/application_source` cache. The build helper generates a clean compiler
source tree from the canonical firmware plus `third_party` before every build. The
Base APIs are optional accelerators, not a required design.
Do not create `plan.json` or a capabilities manifest. There is no second reviewer,
server-side compiler, source audit, repair limit, or task timeout. The server publishes
the exact firmware you compile.
Read [references/base-action-api.md](references/base-action-api.md) when the request
uses the supplied four-axis action runtime.
## Hardware facts that must remain compatible
- Motor 0, 前后: fixed range 480 Hall across 215.00 mm. Nearest to far moves backward.
- Motor 1, 高度/坐盆: fixed range 373 Hall across 55.00 mm. Nearest to far moves upward.
- Motor 2, 靠背: fixed range 917 Hall across 68.00 degrees. Nearest to far reclines downward.
- Motor 3, 腿托: fixed range 647 Hall across 72.00 degrees. Nearest to far lifts upward.
- Motor 3 always drives at 60% PWM. Other motors default to 80% and may use the
requested low 60%, medium 80%, high 95%, or custom duty.
- Board-persisted Hall positions are the position source for every action. Persist
the updated current positions. Never replace the four fixed maximum Hall values.
- Ordinary timed motion remains inside 5%..95% and reverses at either boundary while
its requested time continues. Cycle motion reverses at those boundaries. Absolute,
relative-distance, and relative-angle targets clamp to 5%..95% and stop at target.
- After each real start or direction reversal, ignore stall detection for 2 seconds.
Then a continuous 1-second window with 0, 1, or 2 Hall rising edges is a stall;
3 or more edges is movement.
Keep Classic CAN at 500 kbit/s and retain the App control/telemetry ABI: standard IDs
`0x6A0`/`0x6A2`, `SEAT_INFO` opcode `0x60`, selectors 0 through 14, all four live
position/range/flags responses, `TEST_START`, `TEST_STOP`, `SEAT_STOP`, status queries,
and Enter Bootloader. Once START is accepted, the board completes the action itself.
CAN silence, query failure, RX overflow, or Bus-Off must not end motion; explicit STOP,
real driver faults, and Hall stall remain effective.
Apply the same board-owned lifetime to the dedicated calibration/homing Base firmware:
after `SEAT_CALIBRATE` or `SEAT_HOME` is accepted, it must complete without an App lease
or heartbeat. CAN remains available only for position/status queries, explicit STOP and
Enter Bootloader; silence or CAN faults never abort calibration/homing.
## Compile your exact source
Run:
`sh .agents/skills/n7-seat-self-compose/scripts/build_firmware.sh`
Inspect the real GCC output, edit whatever is necessary, and rerun the helper as many
times as needed until it prints `N7_CODEX_FIRMWARE_BUILD_OK`. Finish only after that
same helper has produced `codex-output/self-compose.raw.bin`. No server-side rebuild or
source audit follows it.
@@ -0,0 +1,4 @@
interface:
display_name: "N7 Seat Self Compose"
short_description: "Build N7 CAN OTA firmware while preserving live seat telemetry"
default_prompt: "Use $n7-seat-self-compose to implement and compile this seat-motion request while preserving the immutable board CAN position telemetry contract."
@@ -0,0 +1,100 @@
# N7 board-side action API
The macros below construct C steps in the authoritative
`firmware/application/ota_self_compose_plan.h`. The build helper copies that file
into a clean generated compiler tree on every build; do not edit or reuse
`build/gcc/application_source`. The runtime reads the board's restored/live Hall
position at each step. `mm_x100` and `degree_x100` are fixed point with two decimals.
## Direction and range
| Axis | Motor | Fixed Hall | Physical range | Toward far | Toward near |
|---|---:|---:|---:|---|---|
| 前后 | 0 | 480 | 215.00 mm | `OTA_SEAT_SLIDE_BACKWARD` | `OTA_SEAT_SLIDE_FORWARD` |
| 高度/坐盆 | 1 | 373 | 55.00 mm | `OTA_SEAT_HEIGHT_UP` | `OTA_SEAT_HEIGHT_DOWN` |
| 靠背 | 2 | 917 | 68.00° | `OTA_SEAT_BACKREST_RECLINE_DOWN` | `OTA_SEAT_BACKREST_RAISE` |
| 腿托 | 3 | 647 | 72.00° | `OTA_SEAT_LEGREST_RAISE` | `OTA_SEAT_LEGREST_LOWER` |
Nearest is position zero. Normal DIY Hall bounds are 24..456, 19..354, 46..871,
and 33..614 for motors 0..3. Do not seek the physical 0%/100% endpoint from an
ordinary DIY image. If board position is invalid, do not blind-move the mechanism.
## Speed and stall
- `OTA_SEAT_SPEED_LOW`: 60%
- `OTA_SEAT_SPEED_MEDIUM`: 80%
- `OTA_SEAT_SPEED_HIGH`: 95%
- `OTA_SEAT_SPEED_DEFAULT`: 80%
- `OTA_SEAT_SPEED_CUSTOM(n)`: custom 1%..95%
Motor 3 ignores the duty argument and always uses 60%. After each actual PWM start
or reversal, its Hall stall window starts only after 2 seconds. In the following
continuous 1000 ms window, at most 2 rising edges is stall; at least 3 is movement.
## Actions
Timed motion:
```c
OTA_SEAT_SLIDE_FOR(direction, durationMs, duty, pauseMs)
OTA_SEAT_HEIGHT_FOR(direction, durationMs, duty, pauseMs)
OTA_SEAT_BACKREST_FOR(direction, durationMs, duty, pauseMs)
OTA_SEAT_LEGREST_FOR(direction, durationMs, duty, pauseMs)
```
If a timed action reaches 5% or 95%, it reverses and continues the remaining time.
Absolute board position:
```c
OTA_SEAT_SLIDE_TO(positionMmX100, duty, pauseMs)
OTA_SEAT_HEIGHT_TO(positionMmX100, duty, pauseMs)
OTA_SEAT_BACKREST_TO(angleDegX100, duty, pauseMs)
OTA_SEAT_LEGREST_TO(angleDegX100, duty, pauseMs)
```
Relative distance/angle:
```c
OTA_SEAT_SLIDE_BY(direction, distanceMmX100, duty, pauseMs)
OTA_SEAT_HEIGHT_BY(direction, distanceMmX100, duty, pauseMs)
OTA_SEAT_BACKREST_BY(direction, angleDegX100, duty, pauseMs)
OTA_SEAT_LEGREST_BY(direction, angleDegX100, duty, pauseMs)
```
Absolute and relative targets clamp to the axis's 5%..95% interval and stop there.
Cycles:
```c
OTA_SEAT_SLIDE_CYCLE(firstDirection, cycles, duty, endpointPauseMs)
OTA_SEAT_HEIGHT_CYCLE(firstDirection, cycles, duty, endpointPauseMs)
OTA_SEAT_BACKREST_CYCLE(firstDirection, cycles, duty, endpointPauseMs)
OTA_SEAT_LEGREST_CYCLE(firstDirection, cycles, duty, endpointPauseMs)
```
One cycle is two boundary legs. `cycles == 0` continues until explicit STOP or a
user-requested program duration. The board owns execution after one `TEST_START`.
## Required CAN position contract
Retain the Base `SEAT_INFO` implementation at 500 kbit/s using request ID `0x6A0`
and response ID `0x6A2`, DLC 8. Selectors 0..14 retain their existing job, four live
position/range, four flags, four physical range, persistent state, and runtime-kind
meanings. Continue servicing these queries and explicit STOP during every action,
but never use communication activity as an action lease.
## Minimal C plan
```c
#define OTA_SELF_COMPOSE_STEP_COUNT (2u)
#define OTA_SELF_COMPOSE_PROGRAM_REPEAT (1u)
#define OTA_SELF_COMPOSE_MAX_RUNTIME_MS (0ul)
static const ota_self_compose_step_t otaSelfComposePlan[] =
{
OTA_SEAT_HEIGHT_TO(3000ul, OTA_SEAT_SPEED_MEDIUM, 200ul),
OTA_SEAT_BACKREST_BY(OTA_SEAT_BACKREST_RECLINE_DOWN, 3000ul,
OTA_SEAT_SPEED_CUSTOM(75u), 0ul),
};
```
@@ -0,0 +1,63 @@
#!/bin/sh
set -eu
# This is the only firmware build entry point used by the composing Codex.
# Codex may modify the complete writable task source; the service supplies
# separate incremental build/output directories. Every compiler control is one.
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
workspace=$(CDPATH= cd -- "$script_dir/../../../.." && pwd)
build_dir=${N7_CODEX_FIRMWARE_BUILD_DIR:?N7_CODEX_FIRMWARE_BUILD_DIR is required}
output_dir=${N7_CODEX_FIRMWARE_OUTPUT_DIR:?N7_CODEX_FIRMWARE_OUTPUT_DIR is required}
task_build_dir="$build_dir/task-workspace"
generated_source="$build_dir/generated-application-source"
build_cpu=${N7_BUILD_CPU:-0}
report_failure() {
result=$?
trap - EXIT
if [ "$result" -ne 0 ]; then
echo "N7_CODEX_FIRMWARE_BUILD_FAILED exit=$result" >&2
fi
exit "$result"
}
trap report_failure EXIT
export N7_BUILD_JOBS=1
export CMAKE_BUILD_PARALLEL_LEVEL=1
export MAKEFLAGS=-j1
export NINJAFLAGS=-j1
export OMP_NUM_THREADS=1
export OPENBLAS_NUM_THREADS=1
export MKL_NUM_THREADS=1
export NUMEXPR_NUM_THREADS=1
export RAYON_NUM_THREADS=1
export GOMAXPROCS=1
mkdir -p "$build_dir" "$output_dir"
rm -rf "$generated_source"
python3 "$workspace/tools/prepare_gcc_application_source.py" \
--source-root "$workspace/third_party/ZL-test-scripts" \
--project-root "$workspace" \
--output-root "$generated_source"
test -s "$generated_source/src/main_cm0plus.c"
test -s "$generated_source/src/ota_self_compose_plan.h"
echo "N7_CODEX_FIRMWARE_BUILD_START cpu=1"
taskset -c "$build_cpu" cmake \
-S "$workspace/firmware/gcc" -B "$task_build_dir" -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_TOOLCHAIN_FILE="$workspace/firmware/gcc/arm-none-eabi-toolchain.cmake" \
-DOTA_PROJECT_ROOT="$workspace" \
-DOTA_SDK_ROOT="$workspace/third_party/ZL-test-scripts/T2G_Sample_Driver_Library_7.9.0" \
-DOTA_SOURCE_ROOT="$workspace/third_party/ZL-test-scripts" \
-DOTA_GENERATED_SOURCE_ROOT="$generated_source" \
-DOTA_OUTPUT_DIR="$output_dir"
taskset -c "$build_cpu" cmake --build "$task_build_dir" \
--target app_self_compose --parallel 1
firmware="$output_dir/self-compose.raw.bin"
test -s "$firmware"
size=$(wc -c <"$firmware" | tr -d ' ')
sha256=$(sha256sum "$firmware" | awk '{print $1}')
echo "N7_CODEX_FIRMWARE_BUILD_OK cpu=1 bytes=$size sha256=$sha256"
trap - EXIT