From 25609ea02552d0885db8fee2f3dbe52b76fba7e1 Mon Sep 17 00:00:00 2001 From: Reed Krantz Date: Thu, 4 Jun 2026 20:20:48 -0500 Subject: [PATCH] feat(rust-esp32-c6): make the rust-esp32-c6 template --- flake.nix | 1 + rust-esp32-c6/default.nix | 44 ++++++++++++ rust-esp32-c6/template/.cargo/config.toml | 17 +++++ rust-esp32-c6/template/.clippy.toml | 1 + rust-esp32-c6/template/.envrc | 1 + .../.gitea/workflows/flake-update.yaml | 30 ++++++++ rust-esp32-c6/template/.gitignore | 23 ++++++ rust-esp32-c6/template/Cargo.toml | 48 +++++++++++++ rust-esp32-c6/template/build.rs | 71 +++++++++++++++++++ rust-esp32-c6/template/flake.nix | 42 +++++++++++ rust-esp32-c6/template/rust-toolchain.toml | 4 ++ rust-esp32-c6/template/src/bin/main.rs | 54 ++++++++++++++ rust-esp32-c6/template/src/lib.rs | 1 + 13 files changed, 337 insertions(+) create mode 100644 rust-esp32-c6/default.nix create mode 100644 rust-esp32-c6/template/.cargo/config.toml create mode 100644 rust-esp32-c6/template/.clippy.toml create mode 100644 rust-esp32-c6/template/.envrc create mode 100644 rust-esp32-c6/template/.gitea/workflows/flake-update.yaml create mode 100644 rust-esp32-c6/template/.gitignore create mode 100644 rust-esp32-c6/template/Cargo.toml create mode 100644 rust-esp32-c6/template/build.rs create mode 100644 rust-esp32-c6/template/flake.nix create mode 100644 rust-esp32-c6/template/rust-toolchain.toml create mode 100644 rust-esp32-c6/template/src/bin/main.rs create mode 100644 rust-esp32-c6/template/src/lib.rs diff --git a/flake.nix b/flake.nix index 81c97a3..f196736 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,7 @@ imports = [ ./cad ./rust + ./rust-esp32-c6 flake-parts.flakeModules.flakeModules # define the flake.templates option diff --git a/rust-esp32-c6/default.nix b/rust-esp32-c6/default.nix new file mode 100644 index 0000000..7761906 --- /dev/null +++ b/rust-esp32-c6/default.nix @@ -0,0 +1,44 @@ +{ + flake-parts-lib, + inputs, + self, + ... +}: { + flake = { + templates = { + rust-esp32-c6 = { + path = ./template; + description = "ESP32-C6 RISC-V rust project with a blink led example"; + }; + }; + + flakeModules.rust-esp32-c6 = { + imports = [ + inputs.treefmt-nix.flakeModule + self.outputs.flakeModules.rust + ]; + + options.perSystem = flake-parts-lib.mkPerSystemOption ({ + lib, + config, + pkgs, + self', + ... + }: let + cfg = config.krantz.rust.esp32-c6; + + craneLib = config.krantz.rust.craneLib; + in { + options.krantz.rust.esp32-c6 = { + enable = lib.mkEnableOption "additional tooling for the esp32 RISC-V"; + }; + + config = lib.mkIf cfg.enable { + krantz.rust.devDeps = lib.mkOptionDefault (with pkgs; [ + espflash + ]); + }; + }); + }; + }; +} diff --git a/rust-esp32-c6/template/.cargo/config.toml b/rust-esp32-c6/template/.cargo/config.toml new file mode 100644 index 0000000..a367c5a --- /dev/null +++ b/rust-esp32-c6/template/.cargo/config.toml @@ -0,0 +1,17 @@ +[target.riscv32imac-unknown-none-elf] +runner = "espflash flash --monitor --chip esp32c6 --log-format defmt" + +[env] +DEFMT_LOG="info" + +[build] +rustflags = [ + # Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.) + # NOTE: May negatively impact performance of produced code + "-C", "force-frame-pointers", +] + +target = "riscv32imac-unknown-none-elf" + +[unstable] +build-std = ["alloc", "core"] diff --git a/rust-esp32-c6/template/.clippy.toml b/rust-esp32-c6/template/.clippy.toml new file mode 100644 index 0000000..76f6c1d --- /dev/null +++ b/rust-esp32-c6/template/.clippy.toml @@ -0,0 +1 @@ +stack-size-threshold = 1024 diff --git a/rust-esp32-c6/template/.envrc b/rust-esp32-c6/template/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/rust-esp32-c6/template/.envrc @@ -0,0 +1 @@ +use flake diff --git a/rust-esp32-c6/template/.gitea/workflows/flake-update.yaml b/rust-esp32-c6/template/.gitea/workflows/flake-update.yaml new file mode 100644 index 0000000..e109afb --- /dev/null +++ b/rust-esp32-c6/template/.gitea/workflows/flake-update.yaml @@ -0,0 +1,30 @@ +name: "Flake.lock: update Nix dependencies for development environment only" + +on: + workflow_dispatch: # allows manual triggering + schedule: + - cron: '00 00 02 1/2 *' # runs the first of day the month every 2 months at 7:00pm local time (midnight UTC) + +jobs: + nix-flake-update: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: "install nix" + uses: cachix/install-nix-action@v31 + + - name: "setup git user" + uses: fregante/setup-git-user@v2 + + - name: "update flake.lock" + run: | + nix flake update --commit-lock-file + + - name: "check flake for errors" + run: | + nix flake check + + - name: "push update" + run: | + git push diff --git a/rust-esp32-c6/template/.gitignore b/rust-esp32-c6/template/.gitignore new file mode 100644 index 0000000..e21e98c --- /dev/null +++ b/rust-esp32-c6/template/.gitignore @@ -0,0 +1,23 @@ +# Generated by nix +.direnv/ +result + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Editor configuration +.vscode/ +.zed/ +.helix/ +.nvim.lua + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Ignore .DS_Store file in mac +**/.DS_Store diff --git a/rust-esp32-c6/template/Cargo.toml b/rust-esp32-c6/template/Cargo.toml new file mode 100644 index 0000000..93cb716 --- /dev/null +++ b/rust-esp32-c6/template/Cargo.toml @@ -0,0 +1,48 @@ +[package] +edition = "2024" +name = "rust-esp32-c6" +rust-version = "1.88" +version = "0.1.0" + +[[bin]] +name = "rust-esp32-c6" +path = "./src/bin/main.rs" + +[dependencies] +esp-hal = { version = "~1.1.0", features = ["defmt", "esp32c6", "unstable"] } + +esp-rtos = { version = "0.3.0", features = [ + "defmt", + "embassy", + "esp-alloc", + "esp32c6", +] } + +defmt = "1.0.1" +esp-bootloader-esp-idf = { version = "0.5.0", features = ["defmt", "esp32c6"] } + +embassy-executor = { version = "0.10.0", features = ["defmt"] } +embassy-time = { version = "0.5.0", features = ["defmt"] } +esp-alloc = { version = "0.10.0", features = ["defmt"] } +esp-backtrace = { version = "0.19.0", features = [ + "defmt", + "esp32c6", + "panic-handler", +] } +esp-println = { version = "0.17.0", features = ["defmt-espflash", "esp32c6"] } + +critical-section = "1.2.0" +static_cell = "2.1.1" + + +# For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html +[profile.dev] +# The default debug profile is too slow and too big for resource-constrained devices. +# Always build with some optimizations enabled. +opt-level = "s" + +[profile.release] +codegen-units = 1 # LLVM can perform better optimizations using a single thread +debug = 2 # prefer slower builds but better debugging experience +lto = 'fat' +opt-level = 's' diff --git a/rust-esp32-c6/template/build.rs b/rust-esp32-c6/template/build.rs new file mode 100644 index 0000000..7ebdf02 --- /dev/null +++ b/rust-esp32-c6/template/build.rs @@ -0,0 +1,71 @@ +fn main() { + linker_be_nice(); + println!("cargo:rustc-link-arg=-Tdefmt.x"); + // make sure linkall.x is the last linker script (otherwise might cause problems with flip-link) + println!("cargo:rustc-link-arg=-Tlinkall.x"); +} + +fn linker_be_nice() { + let args: Vec = std::env::args().collect(); + if args.len() > 1 { + let kind = &args[1]; + let what = &args[2]; + + match kind.as_str() { + "undefined-symbol" => match what.as_str() { + what if what.starts_with("_defmt_") => { + eprintln!(); + eprintln!( + "💡 `defmt` not found - make sure `defmt.x` is added as a linker script and you have included `use defmt_rtt as _;`" + ); + eprintln!(); + } + "_stack_start" => { + eprintln!(); + eprintln!("💡 Is the linker script `linkall.x` missing?"); + eprintln!(); + } + what if what.starts_with("esp_rtos_") => { + eprintln!(); + eprintln!( + "💡 `esp-radio` has no scheduler enabled. Make sure you have initialized `esp-rtos` or provided an external scheduler." + ); + eprintln!(); + } + "embedded_test_linker_file_not_added_to_rustflags" => { + eprintln!(); + eprintln!( + "💡 `embedded-test` not found - make sure `embedded-test.x` is added as a linker script for tests" + ); + eprintln!(); + } + "free" + | "malloc" + | "calloc" + | "get_free_internal_heap_size" + | "malloc_internal" + | "realloc_internal" + | "calloc_internal" + | "free_internal" => { + eprintln!(); + eprintln!( + "💡 Did you forget the `esp-alloc` dependency or didn't enable the `compat` feature on it?" + ); + eprintln!(); + } + _ => (), + }, + // we don't have anything helpful for "missing-lib" yet + _ => { + std::process::exit(1); + } + } + + std::process::exit(0); + } + + println!( + "cargo:rustc-link-arg=--error-handling-script={}", + std::env::current_exe().unwrap().display() + ); +} diff --git a/rust-esp32-c6/template/flake.nix b/rust-esp32-c6/template/flake.nix new file mode 100644 index 0000000..3aebc55 --- /dev/null +++ b/rust-esp32-c6/template/flake.nix @@ -0,0 +1,42 @@ +{ + description = "esp32 tool"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + + flake-parts.url = "github:hercules-ci/flake-parts"; + + project-templates = { + url = "git+file:///home/reed/code/project-templates"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-parts.follows = "flake-parts"; + }; + }; + + outputs = { + flake-parts, + project-templates, + ... + } @ inputs: + flake-parts.lib.mkFlake {inherit inputs;} { + imports = [ + project-templates.flakeModules.rust-esp32-c6 + ]; + + systems = ["aarch64-linux" "x86_64-linux" "aarch64-darwin"]; + + perSystem = {pkgs, ...}: { + krantz.rust = { + esp32-c6 = { + enable = true; + }; + + enable = true; + src = ./.; + + # runtimeDeps = with pkgs; []; + # buildDeps = with pkgs; []; + }; + }; + }; +} diff --git a/rust-esp32-c6/template/rust-toolchain.toml b/rust-esp32-c6/template/rust-toolchain.toml new file mode 100644 index 0000000..fc10d6a --- /dev/null +++ b/rust-esp32-c6/template/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "stable" +components = ["rust-src"] +targets = ["riscv32imac-unknown-none-elf"] diff --git a/rust-esp32-c6/template/src/bin/main.rs b/rust-esp32-c6/template/src/bin/main.rs new file mode 100644 index 0000000..2518662 --- /dev/null +++ b/rust-esp32-c6/template/src/bin/main.rs @@ -0,0 +1,54 @@ +#![no_std] +#![no_main] +#![deny( + clippy::mem_forget, + reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ + holding buffers for the duration of a data transfer." +)] +#![deny(clippy::large_stack_frames)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_time::{Duration, Timer}; +use esp_backtrace as _; +use esp_hal::clock::CpuClock; +use esp_hal::timer::timg::TimerGroup; +use esp_println as _; + +extern crate alloc; + +// This creates a default app-descriptor required by the esp-idf bootloader. +// For more information see: +esp_bootloader_esp_idf::esp_app_desc!(); + +#[allow( + clippy::large_stack_frames, + reason = "it's not unusual to allocate larger buffers etc. in main" +)] +#[esp_rtos::main] +async fn main(spawner: Spawner) -> ! { + // generator version: 1.3.0 + // generator parameters: --chip esp32c6 -o alloc -o unstable-hal -o esp-backtrace -o embassy -o defmt + + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); + + esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 65536); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + let sw_interrupt = + esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); + + info!("Embassy initialized!"); + + // TODO: Spawn some tasks + let _ = spawner; + + loop { + info!("Hello world!"); + Timer::after(Duration::from_secs(1)).await; + } + + // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.1.0/examples +} diff --git a/rust-esp32-c6/template/src/lib.rs b/rust-esp32-c6/template/src/lib.rs new file mode 100644 index 0000000..0c9ac1a --- /dev/null +++ b/rust-esp32-c6/template/src/lib.rs @@ -0,0 +1 @@ +#![no_std]