Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 22 additions & 46 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,36 @@
# SPDX-License-Identifier: GPL-3.0-or-later

[alias]
# All ESP32 targets build the binary in the ssh-stamp-esp32 crate.
# Board features select a specific PCB and imply the IC feature.
# `--no-default-features` prevents the default board from clashing.
# Every build goes through the xtask runner: `cargo xtask <command>`.
# Board/chip/toolchain knowledge lives in xtask/targets.toml and the
# `[build]` section of each ssh-stamp-*-boards/boards/*.toml, instead of one
# alias per board*feature combination (see issue #116).
#
# Boards with a BSP entry use a board-<name> feature:
build-esp32c5 = "build --release --target riscv32imac-unknown-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32c5-devkitc"
build-esp32c61 = "build --release --target riscv32imac-unknown-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32c61-devkitc"
build-esp32c6 = "build --release --target riscv32imac-unknown-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32c6-devkitc"
build-esp32c6-generic = "build --release --target riscv32imac-unknown-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32c6-generic"
build-esp32s2 = "build --profile esp32s2 --target xtensa-esp32s2-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32-s2-saola -Z build-std=core,alloc"
build-esp32s3-touch-lcd-43 = "build --release --target xtensa-esp32s3-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-waveshare-esp32-s3-touch-lcd-43,can -Z build-std=core,alloc"
# Bench variant: CAN in no-acknowledge mode (exactly one frame per write with no ACKing node attached)
build-esp32s3-touch-lcd-43-noack = "build --release --target xtensa-esp32s3-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-waveshare-esp32-s3-touch-lcd-43,can-no-ack -Z build-std=core,alloc"

run-esp32c5 = "run --release --target riscv32imac-unknown-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32c5-devkitc"
run-esp32c61 = "run --release --target riscv32imac-unknown-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32c61-devkitc"
run-esp32c6 = "run --release --target riscv32imac-unknown-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32c6-devkitc"
run-esp32c6-generic = "run --release --target riscv32imac-unknown-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32c6-generic"
run-esp32s2 = "run --profile esp32s2 --target xtensa-esp32s2-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-esp32-s2-saola"
run-esp32s3-touch-lcd-43 = "run --release --target xtensa-esp32s3-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-waveshare-esp32-s3-touch-lcd-43,can -Z build-std=core,alloc"
run-esp32s3-touch-lcd-43-noack = "run --release --target xtensa-esp32s3-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features --features board-waveshare-esp32-s3-touch-lcd-43,can-no-ack -Z build-std=core,alloc"

# IC-only targets (no BSP entry yet) build the library only — the binary hits
# the `compile_error!("No board feature selected.")` guard until a board module
# is added to ssh-stamp-esp32-boards. Use `--lib` so the alias succeeds.
build-esp32 = "build --release --target xtensa-esp32-none-elf -p ssh-stamp-esp32 --lib --no-default-features --features esp32 -Z build-std=core,alloc"
build-esp32c2 = "build --release --target riscv32imc-unknown-none-elf -p ssh-stamp-esp32 --lib --no-default-features --features esp32c2"
build-esp32c3 = "build --release --target riscv32imc-unknown-none-elf -p ssh-stamp-esp32 --lib --no-default-features --features esp32c3"
build-esp32s3 = "build --release --target xtensa-esp32s3-none-elf -p ssh-stamp-esp32 --lib --no-default-features --features esp32s3 -Z build-std=core,alloc"

# Test alias
test-ota = "test --package ota --target x86_64-unknown-linux-gnu"

# ota packer aliases
build-packer = "build --package ota --bin packer --target x86_64-unknown-linux-gnu"
packer = "run --package ota --bin packer --target x86_64-unknown-linux-gnu"

# doc aliases (cannot use "doc" — shadows cargo's built-in command)
build-doc = "doc --target riscv32imac-unknown-none-elf --no-deps --lib -p ssh-stamp -p ssh-stamp-hal -p ssh-stamp-esp32 -p ssh-stamp-esp32-boards -p ota --no-default-features --features ssh-stamp-esp32/board-esp32c6-devkitc"
# cargo xtask list
# cargo xtask build esp32c6-devkitc
# cargo xtask run waveshare-esp32-s3-touch-lcd-43 --features can-no-ack
# cargo xtask ci
xtask = "run --package xtask --"

# The OTA image packer is a host tool, not a build target.
packer = "run --package ota --bin packer"

# NOTE: there is deliberately no `[build] target` or `[unstable] build-std`
# here. Both used to be global, which forced *every* cargo invocation —
# including host tools like xtask and the packer — to cross-compile to
# riscv32imac. xtask now passes `--target` and `-Zbuild-std` per chip, so
# host tooling builds for the host on any OS without a hardcoded triple.
# Editors wanting cross-compilation diagnostics should set the target in
# their own rust-analyzer config, e.g.:
# "rust-analyzer.cargo.target": "riscv32imac-unknown-none-elf"

[target.xtensa-esp32-none-elf] # ESP32
runner = "espflash flash --baud=921600 --monitor --chip esp32"
rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32"']
[target.riscv32imc-unknown-none-elf] # ESP32-C2 / ESP32-C3
runner = "espflash flash --baud=921600 --monitor"
rustflags = ["-C", "force-frame-pointers"]
[target.riscv32imac-unknown-none-elf] # ESP32C6
[target.riscv32imac-unknown-none-elf] # ESP32-C5 / C6 / C61
runner = "espflash flash --baud=921600 --partition-table ssh-stamp-esp32/partitions.csv --monitor"
rustflags = ["-C", "force-frame-pointers"]

Expand All @@ -70,10 +53,3 @@ ESP_LOG = "info"
#ESP_HAL_CONFIG_STACK_GUARD_OFFSET=4096
#ESP_HAL_CONFIG_STACK_GUARD_VALUE=3740121773
#ESP_HAL_CONFIG_IMPL_CRITICAL_SECTION=true


[build]
target = "riscv32imac-unknown-none-elf"

[unstable]
build-std = ["core", "alloc"]
71 changes: 43 additions & 28 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,53 +16,68 @@ on:
- main

jobs:
espressif-targets:
name: Espressif target ${{ matrix.device.soc }}
targets:
name: Build ${{ matrix.target.name }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
# Just the target name plus which toolchain has to be installed before
# xtask can run. Everything else (triple, profile, features, build-std)
# comes from xtask/targets.toml — see `cargo xtask list`.
matrix:
device: [
# RISC-V devices:
{ soc: "esp32c2", board: "esp32c2", target: "riscv32imc-unknown-none-elf", toolchain: "stable", buildstd: "", binlib: "--lib" },
{ soc: "esp32c3", board: "esp32c3", target: "riscv32imc-unknown-none-elf", toolchain: "stable", buildstd: "", binlib: "--lib" },
{ soc: "esp32c5", board: "board-esp32c5-devkitc", target: "riscv32imac-unknown-none-elf", toolchain: "stable", buildstd: "", binlib: "--bin ssh-stamp-esp32" },
{ soc: "esp32c6", board: "board-esp32c6-devkitc", target: "riscv32imac-unknown-none-elf", toolchain: "stable", buildstd: "", binlib: "--bin ssh-stamp-esp32" },
{ soc: "esp32c61", board: "board-esp32c61-devkitc", target: "riscv32imac-unknown-none-elf", toolchain: "stable", buildstd: "", binlib: "--bin ssh-stamp-esp32" },
{ soc: "esp32c6", board: "board-esp32c6-generic", target: "riscv32imac-unknown-none-elf", toolchain: "stable", buildstd: "", binlib: "--bin ssh-stamp-esp32" },
# Xtensa devices:
{ soc: "esp32", board: "esp32", target: "xtensa-esp32-none-elf", toolchain: "esp", buildstd: "-Z build-std=core,alloc", binlib: "--lib" },
{ soc: "esp32s2", board: "board-esp32-s2-saola", target: "xtensa-esp32s2-none-elf", toolchain: "esp", buildstd: "-Z build-std=core,alloc", binlib: "--bin ssh-stamp-esp32" },
{ soc: "esp32s3", board: "esp32s3", target: "xtensa-esp32s3-none-elf", toolchain: "esp", buildstd: "-Z build-std=core,alloc", binlib: "--lib" },
{ soc: "esp32s3", board: "board-waveshare-esp32-s3-touch-lcd-43,can", target: "xtensa-esp32s3-none-elf", toolchain: "esp", buildstd: "-Z build-std=core,alloc", binlib: "--bin ssh-stamp-esp32" },
target: [
# Chips without a BSP entry: library-only builds.
{ name: "esp32c2", xtensa: false },
{ name: "esp32c3", xtensa: false },
{ name: "esp32", xtensa: true },
{ name: "esp32s3", xtensa: true },
# Boards: full firmware binaries.
{ name: "esp32c5-devkitc", xtensa: false },
{ name: "esp32c6-devkitc", xtensa: false },
{ name: "esp32c61-devkitc", xtensa: false },
{ name: "esp32c6-generic", xtensa: false },
{ name: "esp32-s2-saola", xtensa: true },
{ name: "waveshare-esp32-s3-touch-lcd-43", xtensa: true },
]
steps:
- name: Cache
uses: mozilla-actions/sccache-action@v0.0.10
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Rust toolchain for RISC-V
if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.device.soc) }}
if: ${{ !matrix.target.xtensa }}
uses: dtolnay/rust-toolchain@v1
with:
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf
toolchain: stable
components: rust-src, clippy, rustfmt
- name: Setup Rust toolchain for Xtensa
if: ${{ contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.device.soc) }}
if: ${{ matrix.target.xtensa }}
uses: esp-rs/xtensa-toolchain@v1.7.0
with:
ldproxy: false
version: 1.96.0

- name: Build project
run: cargo +${{ matrix.device.toolchain }} build --release --target ${{ matrix.device.target }} -p ssh-stamp-esp32 ${{ matrix.device.binlib }} --no-default-features --features ${{ matrix.device.board }} ${{ matrix.device.buildstd }}
- name: Build
run: cargo xtask build ${{ matrix.target.name }}

lint:
name: Lints and format
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Stable Rust Toolchain
uses: dtolnay/rust-toolchain@v1
with:
target: riscv32imac-unknown-none-elf
toolchain: stable
components: rust-src, clippy, rustfmt
- name: Clippy
run: cargo xtask clippy
- name: Format
run: cargo xtask fmt --check

- name: Check lints and format
if: ${{ matrix.device.board == 'board-esp32c6-devkitc' }}
run: |
cargo +${{ matrix.device.toolchain }} clippy --release --features board-esp32c6-devkitc --target riscv32imac-unknown-none-elf -p ssh-stamp-esp32 --bin ssh-stamp-esp32 --no-default-features -- -D warnings -A clippy::default_trait_access
cargo +${{ matrix.device.toolchain }} fmt -- --check
packer:
name: OTA Packer
runs-on: ubuntu-latest
Expand All @@ -74,10 +89,10 @@ jobs:
- name: Setup Stable Rust Toolchain
uses: dtolnay/rust-toolchain@v1
with:
target: riscv32imac-unknown-none-elf
toolchain: stable
- name: Build utility
run: cargo build-packer
run: cargo build --package ota --bin packer

docs:
name: Documentation
runs-on: ubuntu-latest
Expand All @@ -90,6 +105,6 @@ jobs:
target: riscv32imac-unknown-none-elf
toolchain: stable
- name: Build all docs
run: cargo build-doc
run: cargo xtask doc
- name: Check doc warnings
run: cargo build-doc 2>&1 | grep -iE "broken|warning.*link" || true
run: cargo xtask doc 2>&1 | grep -iE "broken|warning.*link" || true
3 changes: 1 addition & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ jobs:
- name: Setup Stable Rust Toolchain
uses: dtolnay/rust-toolchain@v1
with:
target: riscv32imac-unknown-none-elf
toolchain: stable
- name: Package test
run: cargo test-ota
run: cargo xtask test
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ license = "GPL-3.0-or-later"
[lib]

[workspace]
members = ["ssh-stamp-hal", "ssh-stamp-esp32", "ssh-stamp-esp32-boards", "ota"]
members = [
"ssh-stamp-hal",
"ssh-stamp-esp32",
"ssh-stamp-esp32-boards",
"ota",
"xtask",
]

[workspace.lints.clippy]
mem_forget = "warn"
Expand Down
92 changes: 68 additions & 24 deletions docs/BUILDING.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,89 @@
# Building

Everything is driven by `cargo xtask`, which knows every board, chip,
toolchain and target triple the project supports. Start with:

```
cargo xtask list # what can I build?
cargo xtask # usage
```

Tooling is controlled by `rust-toolchain.toml`. On a fresh host you'll typically need the Rust source component and a flasher (we use `espflash` below as an example):

```
rustup toolchain install stable --component rust-src
cargo install espflash --locked
```

Build/flash for your board using the short command pattern (replace `<target>` with the concrete chip you have):
Xtensa targets (ESP32/ESP32-S2/S3) do require `espup` in addition to `rustup`:

| Machine target | Rust toolchain target |
| --- | --- |
| `esp32` | `xtensa-esp32-none-elf` |
| `esp32c2` | `riscv32imc-unknown-none-elf` |
| `esp32c3` | `riscv32imc-unknown-none-elf` |
| `esp32c5` | `riscv32imac-unknown-none-elf` |
| `esp32c6` | `riscv32imac-unknown-none-elf` |
| `esp32c61` | `riscv32imac-unknown-none-elf` |
| `esp32s2` | `xtensa-esp32s2-none-elf` |
| `esp32s3` | `xtensa-esp32s3-none-elf` |
```
cargo install espup
espup install
source $HOME/export-esp.sh
```

## Building

Pass either a **board** (builds the firmware binary) or a bare **chip**
(builds the library only, for chips with no board definition yet):

```
rustup target add <rust-toolchain-target>
cargo build-<machine-target> # e.g. cargo build-esp32c6, cargo build-esp32c3, cargo build-esp32
cargo run-<machine-target> # convenience helper (if supported) that builds + flashes
cargo xtask build esp32c6-devkitc # a board
cargo xtask build esp32c3 # a chip, library only
cargo xtask build waveshare-esp32-s3-touch-lcd-43 # Xtensa, picks the esp toolchain itself
```

Xtensa targets (ESP32/ESP32-S2/S3) do require `espup` in addition to the `rustup` command above:
Optional features and profiles are flags rather than separate commands, and
anything after `--` goes straight to cargo:

```
cargo install espup
espup install
source $HOME/export-esp.sh
cargo xtask build esp32c6-devkitc --features sftp-ota
cargo xtask build waveshare-esp32-s3-touch-lcd-43 --features can-no-ack
cargo xtask build esp32c6-devkitc --profile dev -- --timings
```

# Flashing
You do not need to remember which targets need `+esp`, `-Zbuild-std` or a
special profile: that lives in [`xtask/targets.toml`](../xtask/targets.toml)
and in the `[build]` section of each board definition under
`ssh-stamp-esp32-boards/boards/`.

## Flashing

`run` builds, flashes and opens the serial monitor (via the `runner`
configured per target triple in `.cargo/config.toml`):

```
cargo xtask run esp32c6-devkitc
```

Flash the firmware and open the serial console (example):
## Everything CI checks

```
# build & flash (example for esp32c6)
cargo build-esp32c6 --release
cargo run-esp32c6
```
cargo xtask clippy # lints one representative board
cargo xtask fmt # --check to verify instead of rewrite
cargo xtask doc
cargo xtask test # host-side crates
cargo xtask ci # all of the above, for every board and chip
```

## Adding a board

1. Drop a `boards/<name>.toml` into the relevant BSP crate, with the pin map
and a `[build]` section naming its chip:
```toml
[build]
chip = "esp32c6"
# features = ["can"] # optional: features this board always needs
```
2. Add the matching `board-<name>` feature in that platform's `Cargo.toml`.

That is it — `cargo xtask list` picks it up by scanning the boards
directory, so no alias, matrix entry or xtask code has to change.

## Adding a chip or a new vendor

Add a `[chips.<name>]` entry to `xtask/targets.toml` with its target triple
(plus `toolchain`, `build-std` or `profile` if it needs them). A whole new
manufacturer is a `[platforms.<vendor>]` entry pointing at that vendor's
crate and boards directory; nothing in the xtask code is Espressif-specific.
4 changes: 3 additions & 1 deletion docs/USING.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ repository hard-codes UART pin numbers.
To see the available boards and their pin assignments, run:

```
cargo build-doc
cargo xtask doc
```

(`cargo xtask list` gives the same board list as a quick terminal summary.)

Then open `target/riscv32imac-unknown-none-elf/doc/ssh_stamp_esp32_boards/index.html`,
which contains the auto-generated per-board pin assignment table.
4 changes: 2 additions & 2 deletions ota/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Following you can find the steps to perform an OTA. Some steps are optionals.
#### 1. Build and extract app bin from elf file

```
cargo build-esp32c6
cargo xtask build esp32c6-devkitc
espflash save-image --chip=esp32c6 target/riscv32imac-unknown-none-elf/release/ssh-stamp ssh-stamp.bin
```

Expand All @@ -46,7 +46,7 @@ Optionally erase the flash for a fresh test
# optional
espflash erase-flash

cargo run-esp32c6 --features sftp-ota
cargo xtask run esp32c6-devkitc --features sftp-ota
```

At the end of the bootloader log look for the app offset (At this point Factory)
Expand Down
6 changes: 5 additions & 1 deletion ssh-stamp-esp32-boards/boards/esp32-s2-saola.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ url = "https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s2/esp32-

[pins]
uart_rx = 10
uart_tx = 11
uart_tx = 11

# Consumed by `cargo xtask`; the BSP build script ignores this section.
[build]
chip = "esp32s2"
Loading
Loading