Skip to content
Merged
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
123 changes: 116 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-gnu
components: clippy, rustfmt

- name: Cache cargo registry
uses: Swatinem/rust-cache@v2
Expand All @@ -33,10 +34,75 @@ jobs:
- name: Run tests
run: cargo test -p longfred-proto --target x86_64-unknown-linux-gnu

build:
name: Build firmware
- name: rustfmt check
run: cargo fmt --all -- --check

- name: clippy (proto, host)
run: |
# Workspace lints apply `clippy::all = deny` and `pedantic = warn`.
# `missing_docs` is warn-by-policy (CODING-GUIDELINES §12.2); pre-existing
# gaps are tracked separately and must not block this PR.
cargo clippy -p longfred-proto --target x86_64-unknown-linux-gnu -- \
-A rustdoc::missing_docs -A missing_docs

clippy:
name: Clippy firmware (${{ matrix.variant }})
needs: test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- variant: longfred-standard
features: variant-longfred-standard
- variant: longfred-mini
features: variant-longfred-mini
- variant: markwtech
features: variant-markwtech
- variant: heiko-wifred
features: variant-heiko-wifred
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: riscv32imac-unknown-none-elf
components: clippy
rust-src: true

- name: Cache cargo registry
uses: Swatinem/rust-cache@v2
with:
workspaces: .
key: clippy-${{ matrix.variant }}

- name: Clippy
run: |
# Workspace lints apply `clippy::all = deny` and `pedantic = warn`.
# `missing_docs` is warn-by-policy (CODING-GUIDELINES §12.2); pre-existing
# gaps are tracked separately and must not block this PR.
cargo clippy -p longfred-firmware --no-default-features \
--features "${{ matrix.features }}" -- \
-A rustdoc::missing_docs -A missing_docs

build:
name: Build firmware (${{ matrix.variant }})
needs: clippy
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- variant: longfred-standard
features: variant-longfred-standard
- variant: longfred-mini
features: variant-longfred-mini
- variant: markwtech
features: variant-markwtech
- variant: heiko-wifred
features: variant-heiko-wifred
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -51,6 +117,7 @@ jobs:
uses: Swatinem/rust-cache@v2
with:
workspaces: .
key: ${{ matrix.variant }}

- name: Install espflash
uses: taiki-e/install-action@v2
Expand All @@ -60,22 +127,64 @@ jobs:
- name: Build firmware
run: |
set -euo pipefail
cargo build -p longfred-firmware --release --bin longfred
VARIANT="${{ matrix.variant }}"
FEATURES="${{ matrix.features }}"
cargo build -p longfred-firmware --release --bin longfred \
--no-default-features --features "${FEATURES}"
mkdir -p dist

ELF="target/riscv32imac-unknown-none-elf/release/longfred"
cp "$ELF" dist/longfred-esp32c6.elf
espflash save-image --chip esp32c6 --merge "$ELF" dist/longfred-esp32c6.bin
cp "$ELF" "dist/longfred-${VARIANT}-esp32c6.elf"
espflash save-image --chip esp32c6 --merge "$ELF" \
"dist/longfred-${VARIANT}-esp32c6.bin"

(
cd dist
sha256sum longfred-esp32c6.elf longfred-esp32c6.bin > SHA256SUMS
sha256sum "longfred-${VARIANT}-esp32c6.elf" "longfred-${VARIANT}-esp32c6.bin" > SHA256SUMS
)

- name: Upload firmware
uses: actions/upload-artifact@v4
with:
name: firmware
name: firmware-${{ matrix.variant }}
path: dist/
if-no-files-found: error
retention-days: 90

size-check:
name: Check ESP32-C6 size
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install espflash
uses: taiki-e/install-action@v2
with:
tool: espflash@4.5.0

- name: Download firmware artifacts
uses: actions/download-artifact@v4
with:
pattern: firmware-*
path: artifact-download

- name: Stage ELFs into dist/
run: |
set -euo pipefail
mkdir -p dist
for variant in longfred-standard longfred-mini markwtech heiko-wifred; do
src="artifact-download/firmware-${variant}/longfred-${variant}-esp32c6.elf"
dst="dist/longfred-${variant}-esp32c6.elf"
if [[ ! -f "$src" ]]; then
echo "error: missing artifact ELF: $src" >&2
ls -laR artifact-download >&2 || true
exit 1
fi
cp -f "$src" "$dst"
echo "staged $dst"
done

- name: Check flash/RAM budget
run: ./scripts/check-esp32c6-size.sh --check-only
13 changes: 8 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,17 @@ jobs:
echo "Timed out after 15 minutes waiting for CI workflow on commit ${SHA}"
exit 1

- name: Download CI firmware
- name: Download CI firmware artifacts
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
gh run download "${{ steps.ci.outputs.run_id }}" \
--name firmware \
--dir release-assets
mkdir -p release-assets
for variant in longfred-standard longfred-mini markwtech heiko-wifred; do
gh run download "${{ steps.ci.outputs.run_id }}" \
--name "firmware-${variant}" \
--dir "release-assets/${variant}"
done

- name: Create release if missing
env:
Expand All @@ -84,7 +87,7 @@ jobs:
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
mapfile -d '' FILES < <(find release-assets -type f -print0)
mapfile -d '' FILES < <(find release-assets -type f \( -name '*.elf' -o -name '*.bin' -o -name 'SHA256SUMS' \) -print0)
if [ "${#FILES[@]}" -eq 0 ]; then
echo "No release assets found"
exit 1
Expand Down
12 changes: 12 additions & 0 deletions Cargo.lock

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

18 changes: 18 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ edition = "2024"
rust-version = "1.95"
license = "Apache-2.0"

[workspace.lints.rust]
# esp-hal peripheral ownership transfer requires `unsafe` blocks; the policy
# is `warn` so HAL integration compiles while new `unsafe` outside HAL modules
# stays visible in review. New `unsafe` must include a `SAFETY:` justification.
unsafe_code = "warn"
missing_docs = "warn"
unused_must_use = "deny"

[workspace.lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
dbg_macro = "deny"
expect_used = "deny"
panic = "deny"
todo = "deny"
unimplemented = "deny"
unwrap_used = "deny"

[profile.dev]
opt-level = "s"

Expand Down
86 changes: 76 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,25 +1,91 @@
# LongFred — common Cargo tasks (run from this directory).
#
# make build # ESP32-C6 firmware (riscv32imac)
# make build-wokwi # same build + stage ELF for Wokwi simulator
# make test # host tests for longfred-proto
# make build # default variant (longfred-standard)
# make build VARIANT=longfred-mini # single hardware variant
# make build-all # all variants (debug)
# make build-all-release # all variants (release)
# make size # flash/RAM report for all variants
# make build-wokwi # same build + stage ELF for Wokwi
# make test # host tests for longfred-proto

.PHONY: all build build-wokwi test help
CARGO ?= cargo
PACKAGE := longfred-firmware
BIN := longfred

# Hardware variants (Cargo feature = variant-<name>).
VARIANTS := longfred-standard longfred-mini markwtech heiko-wifred
VARIANT ?= longfred-standard

ifeq ($(filter $(VARIANT),$(VARIANTS)),)
$(error unknown VARIANT='$(VARIANT)'; choose one of: $(VARIANTS))
endif

FEATURES := --no-default-features --features variant-$(VARIANT)
# Isolate Cargo artifacts per variant so feature switches cannot reuse a stale ELF.
TARGET_DIR := target/$(VARIANT)

.PHONY: all build build-release build-all build-all-release \
build-longfred-standard build-longfred-mini build-markwtech build-heiko-wifred \
build-wokwi size check-size check-size-only test help

all: build test

help:
@echo "Targets:"
@echo " build - cargo build -p longfred-firmware"
@echo " build-wokwi - build + copy ELF to wokwi/longfred"
@echo " test - cargo test -p longfred-proto (host)"
@echo " all - build + test (default)"
@echo " build [VARIANT=...] - cargo build -p $(PACKAGE) (debug)"
@echo " build-release [VARIANT] - release build for one variant"
@echo " build-all - debug build for every variant"
@echo " build-all-release - release build for every variant"
@echo " build-<variant> - shorthand debug builds:"
@echo " $(VARIANTS)"
@echo " size / check-size - release-build all variants + ESP32-C6 flash/RAM report"
@echo " check-size-only - check existing dist/*.elf (no cargo; used by CI)"
@echo " build-wokwi - build + copy ELF to wokwi/longfred"
@echo " test - cargo test -p longfred-proto (host)"
@echo " all - build + test (default)"
@echo ""
@echo "VARIANT (default: $(VARIANT)): $(VARIANTS)"

build:
cargo build -p longfred-firmware
$(CARGO) build -p $(PACKAGE) --target-dir $(TARGET_DIR) $(FEATURES)

build-release:
$(CARGO) build -p $(PACKAGE) --release --bin $(BIN) --target-dir $(TARGET_DIR) $(FEATURES)

build-all:
@for v in $(VARIANTS); do \
echo "==> build VARIANT=$$v"; \
$(MAKE) --no-print-directory build VARIANT=$$v || exit 1; \
done

build-all-release:
@for v in $(VARIANTS); do \
echo "==> build-release VARIANT=$$v"; \
$(MAKE) --no-print-directory build-release VARIANT=$$v || exit 1; \
done

build-longfred-standard:
@$(MAKE) --no-print-directory build VARIANT=longfred-standard

build-longfred-mini:
@$(MAKE) --no-print-directory build VARIANT=longfred-mini

build-markwtech:
@$(MAKE) --no-print-directory build VARIANT=markwtech

build-heiko-wifred:
@$(MAKE) --no-print-directory build VARIANT=heiko-wifred

build-wokwi:
./scripts/wokwi-prep.sh

# Release-build every variant and verify it fits ESP32-C6 flash partition + on-chip RAM.
size check-size:
./scripts/check-esp32c6-size.sh

# Verify prebuilt dist/longfred-<variant>-esp32c6.elf files (CI after artifact download).
check-size-only:
./scripts/check-esp32c6-size.sh --check-only

test:
cargo test -p longfred-proto --target x86_64-unknown-linux-gnu
$(CARGO) test -p longfred-proto --target x86_64-unknown-linux-gnu
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
# longfred
A BigFred wireless physical client
# LongFred

Wireless physical throttle client for BigFred (WiThrottle / Z21).

## Hardware variants

Build-time Cargo features (mutually exclusive):

| Feature | Description |
|---------|-------------|
| `variant-longfred-standard` (default) | OLED 128×64, MCP23017×2, 5-way + F-keys + encoder |
| `variant-longfred-mini` | Same as standard, OLED 128×32 |
| `variant-markwtech` | Keypad + 2.42" OLED, WiTcontroller-style |
| `variant-heiko-wifred` | Headless wiFred-style (LEDs + pot), Wi‑Fi config only |

Docs: [docs/hardware/](docs/hardware/), provisioning: [docs/provisioning.md](docs/provisioning.md).

```bash
cargo build -p longfred-firmware --release --bin longfred
cargo build -p longfred-firmware --release --bin longfred \
--no-default-features --features variant-longfred-mini
```

## Host tests

```bash
cargo test -p longfred-proto --target x86_64-unknown-linux-gnu
```
Loading
Loading