diff --git a/.github/workflows/build-esp32-c6-rust.yaml b/.github/workflows/build-esp32-c6-rust.yaml new file mode 100644 index 0000000..9d04fd2 --- /dev/null +++ b/.github/workflows/build-esp32-c6-rust.yaml @@ -0,0 +1,48 @@ +name: Continuous Integration for ESP32-C6 + +on: + push: + paths: + - "esp32-c6-devkit/**" + - "spooky-core/**" + - "spooky-embedded/**" + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + rust-checks: + name: Rust Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + project: [ "esp32-c6-devkit" ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Rust (Xtensa) + uses: esp-rs/xtensa-toolchain@v1.5 + with: + default: true + buildtargets: esp32c6 + ldproxy: false + + - name: Enable caching + uses: Swatinem/rust-cache@v2 + + - name: Run checks sequentially within each job + run: | + cd ${{ matrix.project }} + # Format check + cargo fmt --all -- --check --color always + + # Clippy linting (with release mode if desired) + cargo clippy --release --all-features --workspace -- -D warnings + + # Build (Release) + cargo build --release diff --git a/.github/workflows/build-esp32-rust.yaml b/.github/workflows/build-esp32-rust.yaml deleted file mode 100644 index f23b3c6..0000000 --- a/.github/workflows/build-esp32-rust.yaml +++ /dev/null @@ -1,161 +0,0 @@ -name: Continuous Integration - -on: - push: - paths: - - "esp32-s3-box/**" - - "esp32-s3-box-3/**" - - "m5stack-cores3/**" - - "spooky-core/**" - - "spooky-embedded/**" - workflow_dispatch: - -env: - CARGO_TERM_COLOR: always - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - -jobs: - rust-checks: - name: Rust Checks - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - project: [ "esp32-s3-box", "esp32-s3-box-3", "m5stack-cores3" ] - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Rust (Xtensa) - uses: esp-rs/xtensa-toolchain@v1.5 - with: - default: true - buildtargets: esp32s3 - ldproxy: false - - - name: Enable caching - uses: Swatinem/rust-cache@v2 - - - name: Run checks sequentially within each job - run: | - cd ${{ matrix.project }} - # Format check - cargo fmt --all -- --check --color always - - # Clippy linting (with release mode if desired) - cargo clippy --release --all-features --workspace -- -D warnings - - # Build (Release) - cargo build --release - - - -#name: Build ESP32 Rust Binaries in Docker image espressif/idf-rust:all_... and upload to Releases -# -#on: -# workflow_dispatch: -# inputs: -# release_tag: -# description: "Upload to specific release" -# required: true -# default: 'v0.6.1' -# skip_projects: -# description: "Skip projects during build (e.g. esp32-c3-devkit-rust)" -# required: false -# default: '' -# -#jobs: -# get_release: -# name: Get release -# runs-on: ubuntu-latest -# outputs: -# upload_url: ${{ steps.get_upload_url.outputs.url }} -# steps: -# - uses: octokit/request-action@v2.x -# id: get_release -# with: -# route: GET /repos/{owner}/{repo}/releases/tags/${{ github.event.inputs.release_tag }} -# owner: georgik -# repo: esp32-spooky-maze-game -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# - name: get upload url -# id: get_upload_url -# run: | -# url=$(echo "$response" | jq -r '.upload_url' | sed 's/{?name,label}//') -# echo "::set-output name=url::$url" -# env: -# response: ${{ steps.get_release.outputs.data }} -# -# build: -# runs-on: ubuntu-20.04 -# container: -# image: espressif/idf-rust:all_1.73.0.0 -# options: --user esp --workdir /home/esp -# needs: get_release -# steps: -# - name: Clone repository with specific branch -# run: | -# export HOME=/home/esp -# cd /home/esp -# curl -L https://wokwi.com/ci/install.sh | sh -# pwd -# git clone --depth 1 --branch ${{ github.ref_name }} https://github.com/georgik/esp32-spooky-maze-game.git project -# - name: Build and upload binaries -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# WOKWI_CLI_TOKEN: ${{ secrets.WOKWI_CLI_TOKEN }} -# run: | -# # Workaround GitHub issue with setting HOME in container https://github.com/actions/runner/issues/863 -# export HOME=/home/esp -# cd /home/esp -# . /home/esp/.bashrc -# . /home/esp/export-esp.sh -# # Upload loop for each binary -# cd project -# for FIRMWARE_DIR in `ls -d esp* m5stack*`; do -# -# # If FIRMWARE_DIR is a substring in SKIP_PROJECTS, skip it -# if echo "${{ github.event.inputs.skip_projects }}" | grep -q "${FIRMWARE_DIR}"; then -# echo "Skipping $FIRMWARE_DIR" -# continue -# fi -# -# cd $FIRMWARE_DIR -# echo "Building $FIRMWARE_DIR" -# VERSION=$(grep '^version =' Cargo.toml | cut -d '"' -f2) -# CHIP=$(grep 'hal = { package =' Cargo.toml | cut -d '"' -f2 | cut -d '-' -f1) -# -# cargo espflash save-image --chip ${CHIP} --release --merge --skip-padding spooky-maze-${FIRMWARE_DIR}.bin -# -# # If skip-wokwi-test.toml exists, skip Wokwi test -# #if [ -f "skip-wokwi-test.toml" ]; then -# # echo "Skipping Wokwi test for $FIRMWARE_DIR" -# #else -# # /home/esp/bin/wokwi-cli --timeout 5000 --timeout-exit-code 0 --screenshot-part "lcd1" --screenshot-time 5000 --screenshot-file "screenshot.png" "." -# #fi -# -# asset_path="/home/esp/project/${FIRMWARE_DIR}/spooky-maze-${FIRMWARE_DIR}.bin" -# asset_name="spooky-maze-${FIRMWARE_DIR}-${{ github.event.inputs.release_tag }}.bin" -# curl \ -# --request POST \ -# --header "authorization: Bearer $GITHUB_TOKEN" \ -# --header "Content-Type: application/octet-stream" \ -# --data-binary "@$asset_path" \ -# --url "${{ needs.get_release.outputs.upload_url }}?name=${asset_name}" -# -# # If skip-wokwi-test.toml exists, skip Wokwi test -# #if [ ! -f "skip-wokwi-test.toml" ]; then -# # asset_path="/home/esp/project/${FIRMWARE_DIR}/screenshot.png" -# # asset_name="spooky-maze-${FIRMWARE_DIR}-${{ github.event.inputs.release_tag }}.png" -# # curl \ -# # --request POST \ -# # --header "authorization: Bearer $GITHUB_TOKEN" \ -# # --header "Content-Type: application/octet-stream" \ -# # --data-binary "@$asset_path" \ -# # --url "${{ needs.get_release.outputs.upload_url }}?name=${asset_name}" -# #fi -# -# cd - -# done diff --git a/.github/workflows/build-esp32-s3-rust.yaml b/.github/workflows/build-esp32-s3-rust.yaml new file mode 100644 index 0000000..c6ab2c7 --- /dev/null +++ b/.github/workflows/build-esp32-s3-rust.yaml @@ -0,0 +1,50 @@ +name: Continuous Integration for ESP32-S3 + +on: + push: + paths: + - "esp32-s3-box/**" + - "esp32-s3-box-3/**" + - "m5stack-cores3/**" + - "spooky-core/**" + - "spooky-embedded/**" + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + rust-checks: + name: Rust Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + project: [ "esp32-s3-box", "esp32-s3-box-3", "m5stack-cores3" ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Rust (Xtensa) + uses: esp-rs/xtensa-toolchain@v1.5 + with: + default: true + buildtargets: esp32s3 + ldproxy: false + + - name: Enable caching + uses: Swatinem/rust-cache@v2 + + - name: Run checks sequentially within each job + run: | + cd ${{ matrix.project }} + # Format check + cargo fmt --all -- --check --color always + + # Clippy linting (with release mode if desired) + cargo clippy --release --all-features --workspace -- -D warnings + + # Build (Release) + cargo build --release diff --git a/esp32-c6-devkit/.cargo/config.toml b/esp32-c6-devkit/.cargo/config.toml index 20b4800..fc74d88 100644 --- a/esp32-c6-devkit/.cargo/config.toml +++ b/esp32-c6-devkit/.cargo/config.toml @@ -1,18 +1,17 @@ [target.riscv32imac-unknown-none-elf] runner = "espflash flash --monitor" + +[env] +ESP_LOG="INFO" + +[build] rustflags = [ - "-C", "link-arg=-Tlinkall.x", # Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.) # NOTE: May negatively impact performance of produced code "-C", "force-frame-pointers", ] -[env] -# Use clean build after changing ESP_LOGLEVEL -ESP_LOGLEVEL="DEBUG" - -[build] target = "riscv32imac-unknown-none-elf" [unstable] -build-std = [ "core", "alloc" ] +build-std = ["core"] diff --git a/esp32-c6-devkit/Cargo.toml b/esp32-c6-devkit/Cargo.toml index 15b73d0..828a442 100644 --- a/esp32-c6-devkit/Cargo.toml +++ b/esp32-c6-devkit/Cargo.toml @@ -1,30 +1,44 @@ [package] name = "spooky-esp32-c6" -version = "0.10.0" +version = "0.11.0" authors = ["Juraj Michálek "] edition = "2021" license = "MIT" -[target.riscv32imac-unknown-none-elf.dependencies] -hal = { package = "esp32c6-hal", version = "0.7.0" } -esp-backtrace = { version = "0.9.0", features = ["esp32c6", "panic-handler", "exception-handler", "print-uart"] } -esp-println = { version = "0.7.0", features = [ "esp32c6", "log" ] } - [dependencies] -esp-alloc = "0.3.0" -embedded-graphics = "0.8.0" -embedded-hal = "0.2" -display-interface = "0.4" -display-interface-spi = "0.4" -log = { version = "0.4.18" } -mipidsi = "0.7.1" -panic-halt = "0.2" -rand = { version = "0.8.5", default-features = false } -rand_chacha = { version = "0.3.1", default-features = false } -petgraph = { git = "https://github.com/zendurix/petgraph.git", branch = "better_no_std", default-features = false, features = [ - "graphmap", +esp-backtrace = { version = "0.14.2", features = [ + "esp32c6", + "exception-handler", + "panic-handler", + "println", +]} + +esp-hal = { version = "0.22.0", features = [ + "esp32c6", ] } -shared-bus = { version = "0.3.0" } -spooky-core = { path = "../spooky-core", default-features = false, features = [ "static_maze"]} -spooky-embedded = { path = "../spooky-embedded", default-features = false, features = [ "esp32c6", "static_maze", "resolution_320x240" ] } -spi-dma-displayinterface = { path = "../spi-dma-displayinterface", features = [ "esp32c6" ] } +esp-println = { version = "0.12.0", features = ["esp32c6", "log"] } +log = { version = "0.4.21" } +critical-section = "1.2.0" + +esp-alloc = "0.5.0" +embedded-graphics = "0.8.0" +embedded-hal = "1.0.0" +mipidsi = "0.8.0" +spooky-core = { path = "../spooky-core", default-features = false, features = [ "static_maze"] } +spooky-embedded = { path = "../spooky-embedded", default-features = false, features = [ "static_maze", "resolution_320x240" ] } +esp-display-interface-spi-dma = "0.2.0" +esp-bsp = { path = "../../esp-bsp-rs", features = [ "esp32c6devkitc1" ] } + +[profile.dev] +# Rust debug is too slow. +# For debug builds always builds with some optimization +opt-level = "s" + +[profile.release] +codegen-units = 1 # LLVM can perform better optimizations using a single thread +debug = 2 +debug-assertions = false +incremental = false +lto = 'fat' +opt-level = 's' +overflow-checks = false diff --git a/esp32-c6-devkit/build.rs b/esp32-c6-devkit/build.rs new file mode 100644 index 0000000..d177129 --- /dev/null +++ b/esp32-c6-devkit/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=-Tlinkall.x"); +} \ No newline at end of file diff --git a/esp32-c6-devkit/rust-toolchain.toml b/esp32-c6-devkit/rust-toolchain.toml index f524680..be506bb 100644 --- a/esp32-c6-devkit/rust-toolchain.toml +++ b/esp32-c6-devkit/rust-toolchain.toml @@ -1,5 +1,4 @@ [toolchain] -channel = "nightly" -components = ["rustfmt", "rustc-dev"] -# targets = ["xtensa-esp32s3-none-elf"] -# targets = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf","xtensa-esp32s3-none-elf"] +channel = "stable" +components = ["rust-src"] +targets = ["riscv32imac-unknown-none-elf"] \ No newline at end of file diff --git a/esp32-c6-devkit/src/main.rs b/esp32-c6-devkit/src/main.rs index 0ca946c..0965d23 100644 --- a/esp32-c6-devkit/src/main.rs +++ b/esp32-c6-devkit/src/main.rs @@ -1,10 +1,14 @@ #![no_std] #![no_main] -#[global_allocator] -static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); +use esp_display_interface_spi_dma::display_interface_spi_dma; +use esp_bsp::prelude::*; +extern crate alloc; -use spi_dma_displayinterface::spi_dma_displayinterface; +#[allow(unused_imports)] +use esp_backtrace as _; + +use esp_hal::rng::Rng; use embedded_graphics::{ mono_font::{ascii::FONT_8X13, MonoTextStyle}, @@ -12,123 +16,82 @@ use embedded_graphics::{ text::Text, Drawable, }; - +use embedded_hal::delay::DelayNs; use log::info; -use hal::{ - clock::{ClockControl, CpuClock}, +use esp_hal::{ + delay::Delay, + dma::Dma, dma::DmaPriority, - gdma::Gdma, - peripherals::Peripherals, + gpio::{Level, Output}, + i2c::master::I2c, prelude::*, - spi::{ - master::{prelude::*, Spi}, - SpiMode, - }, - Delay, - Rng, - IO, - adc::{AdcConfig, Attenuation, ADC, ADC1}, + spi::master::Spi, }; use spooky_embedded::{ app::app_loop, - embedded_display::{LCD_H_RES, LCD_V_RES, LCD_MEMORY_SIZE}, - controllers::{ - composites::ladder_composite::LadderCompositeController, - ladder::LadderMovementController - }, + embedded_display::LCD_MEMORY_SIZE, }; -use esp_backtrace as _; - #[entry] fn main() -> ! { - let peripherals = Peripherals::take(); - - let system = peripherals.SYSTEM.split(); - - // With DMA we have sufficient throughput, so we can clock down the CPU to 80MHz - let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock80MHz).freeze(); - - esp_println::logger::init_logger_from_env(); - - info!("About to initialize the SPI LED driver"); - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); - - let lcd_sclk = io.pins.gpio6; - let lcd_mosi = io.pins.gpio7; - let lcd_cs = io.pins.gpio20; - let lcd_miso = io.pins.gpio0; // random unused pin - let lcd_dc = io.pins.gpio21.into_push_pull_output(); - let _lcd_backlight = io.pins.gpio4.into_push_pull_output(); - let lcd_reset = io.pins.gpio3.into_push_pull_output(); - - let adc_pin = io.pins.gpio2.into_analog(); - - let dma = Gdma::new(peripherals.DMA); - let dma_channel = dma.channel0; - - let mut descriptors = [0u32; 8 * 3]; - let mut rx_descriptors = [0u32; 8 * 3]; - - let spi = Spi::new( - peripherals.SPI2, - 40u32.MHz(), - SpiMode::Mode0, - &clocks - ).with_pins( - Some(lcd_sclk), - Some(lcd_mosi), - Some(lcd_miso), - Some(lcd_cs), - ).with_dma(dma_channel.configure( - false, - &mut descriptors, - &mut rx_descriptors, - DmaPriority::Priority0, - )); - - let di = spi_dma_displayinterface::new_no_cs(LCD_MEMORY_SIZE, spi, lcd_dc); - - let mut delay = Delay::new(&clocks); - - let mut display = match mipidsi::Builder::ili9341_rgb565(di) - .with_display_size(LCD_H_RES, LCD_V_RES) - .with_orientation(mipidsi::Orientation::Landscape(true)) - .with_color_order(mipidsi::ColorOrder::Rgb) - .init(&mut delay, Some(lcd_reset)) { - Ok(disp) => { disp }, - Err(_) => { panic!() }, - }; - - info!("Display initialized"); + // Initialize peripherals + let peripherals = esp_hal::init(esp_hal::Config::default()); + esp_alloc::heap_allocator!(280 * 1024); + // esp_alloc::psram_allocator!(peripherals.PSRAM, esp_hal::psram); + + let mut delay = Delay::new(); + + info!("Initializing SPI LCD driver for ESP32S3Box"); + + // Use the `lcd_i2c_init` macro to initialize I2C for accelerometer + // let i2c = i2c_init!(peripherals); + + // Use the `lcd_spi` macro to initialize the SPI interface + let spi = lcd_spi!(peripherals); + info!("SPI ready"); + + // Use the `lcd_display_interface` macro to create the display interface + let di = lcd_display_interface!(peripherals, spi); + + // ESP32-S3-BOX display initialization workaround: Wait for the display to power up. + delay.delay_ns(500_000u32); + + let mut display = lcd_display!(peripherals, di) + .init(&mut delay) + .unwrap(); + + // Use the `lcd_backlight_init` macro to turn on the backlight + lcd_backlight_init!(peripherals); + + info!("Initializing..."); + + // Render an "Initializing..." message on the display Text::new( "Initializing...", Point::new(80, 110), - MonoTextStyle::new(&FONT_8X13, RgbColor::BLACK), + MonoTextStyle::new(&FONT_8X13, RgbColor::WHITE), ) - .draw(&mut display) - .unwrap(); + .draw(&mut display) + .unwrap(); - let mut rng = Rng::new(peripherals.RNG); - let mut seed_buffer = [1u8; 32]; - rng.read(&mut seed_buffer).unwrap(); - info!("Initializing the ADC"); - let mut adc1_config = AdcConfig::new(); - let adc_pin = adc1_config.enable_pin(adc_pin, Attenuation::Attenuation11dB); + // Initialize the random number generator + let mut rng = Rng::new(peripherals.RNG); + let mut seed_buffer = [0u8; 32]; + rng.read(&mut seed_buffer); - let analog = peripherals.APB_SARADC.split(); - let adc1 = ADC::::adc(analog.adc1, adc1_config).unwrap(); + // Initialize the movement controllers + // let accel_movement_controller = AccelMovementController::new(icm, 0.2); + let demo_movement_controller = + spooky_core::demo_movement_controller::DemoMovementController::new(seed_buffer); + // let movement_controller = + // AccelCompositeController::new(demo_movement_controller, accel_movement_controller); info!("Entering main loop"); - let ladder_movement_controller = LadderMovementController::new(adc1, adc_pin); - let demo_movement_controller = spooky_core::demo_movement_controller::DemoMovementController::new(seed_buffer); - let movement_controller = LadderCompositeController::new(demo_movement_controller, ladder_movement_controller); - - app_loop(&mut display, seed_buffer, movement_controller); - loop {} + // Enter the application loop + app_loop(&mut display, seed_buffer, demo_movement_controller); }