├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .gitignore ├── .gitpod.Dockerfile ├── .gitpod.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── Cargo.toml ├── LICENSE ├── README.md ├── desktop ├── Cargo.toml └── src │ └── main.rs ├── engine ├── Cargo.toml ├── assets │ └── img │ │ └── espressif.bmp └── src │ ├── assets.rs │ ├── engine.rs │ ├── lib.rs │ └── spritebuf.rs ├── esp-wrover-kit ├── .cargo │ └── config.toml ├── Cargo.toml ├── rust-toolchain.toml └── src │ └── main.rs ├── esp32-c3-devkit-rust ├── .cargo │ └── config.toml ├── Cargo.toml ├── rust-toolchain.toml └── src │ └── main.rs ├── esp32-c6-devkit ├── .cargo │ └── config.toml ├── Cargo.toml ├── rust-toolchain.toml └── src │ └── main.rs ├── esp32-s2-kaluga ├── .cargo │ └── config.toml ├── Cargo.toml ├── rust-toolchain.toml └── src │ └── main.rs ├── esp32-s2-usb-otg ├── .cargo │ └── config.toml ├── Cargo.toml ├── rust-toolchain.toml └── src │ └── main.rs ├── esp32-s3-box ├── .cargo │ └── config.toml ├── Cargo.toml ├── rust-toolchain.toml └── src │ └── main.rs ├── esp32-s3-usb-otg ├── .cargo │ └── config.toml ├── Cargo.toml ├── rust-toolchain.toml └── src │ └── main.rs ├── m5stack-core2 ├── .cargo │ └── config.toml ├── Cargo.toml ├── rust-toolchain.toml └── src │ └── main.rs ├── m5stack-fire ├── .cargo │ └── config.toml ├── Cargo.toml ├── diagram.json ├── run-wokwi.sh ├── rust-toolchain.toml ├── src │ └── main.rs └── wokwi.toml └── wasm ├── Cargo.toml ├── README.md ├── icons ├── manifest-icon-192.maskable.png └── manifest-icon-512.maskable.png ├── index.html ├── index.js ├── manifest.json ├── package.json ├── service-worker.js ├── src └── lib.rs └── webpack.config.js /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VARIANT=bullseye 2 | FROM debian:${VARIANT} 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ENV LC_ALL=C.UTF-8 5 | ENV LANG=C.UTF-8 6 | 7 | # Arguments 8 | ARG CONTAINER_USER=esp 9 | ARG CONTAINER_GROUP=esp 10 | ARG TOOLCHAIN_VERSION=1.65.0.1 11 | ARG ESP_BOARD=esp32 12 | ARG INSTALL_RUST_TOOLCHAIN=install-rust-toolchain.sh 13 | 14 | # Install dependencies 15 | RUN apt-get update \ 16 | && apt-get install -y git curl gcc clang ninja-build libudev-dev unzip xz-utils\ 17 | python3 python3-pip python3-venv libusb-1.0-0 libssl-dev pkg-config libtinfo5 libpython2.7 \ 18 | && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts 19 | 20 | # Set users 21 | RUN adduser --disabled-password --gecos "" ${CONTAINER_USER} 22 | USER ${CONTAINER_USER} 23 | WORKDIR /home/${CONTAINER_USER} 24 | 25 | # Install Rust toolchain, extra crates and esp-idf 26 | ENV PATH=${PATH}:/home/${CONTAINER_USER}/.cargo/bin:/home/${CONTAINER_USER}/opt/bin 27 | 28 | ADD --chown=${CONTAINER_USER}:${CONTAINER_GROUP} \ 29 | https://github.com/esp-rs/rust-build/releases/download/v${TOOLCHAIN_VERSION}/${INSTALL_RUST_TOOLCHAIN} \ 30 | /home/${CONTAINER_USER}/${INSTALL_RUST_TOOLCHAIN} 31 | 32 | RUN chmod a+x ${INSTALL_RUST_TOOLCHAIN} \ 33 | && ./${INSTALL_RUST_TOOLCHAIN} \ 34 | --extra-crates "ldproxy cargo-espflash wokwi-server web-flash" \ 35 | --export-file /home/${CONTAINER_USER}/export-esp.sh \ 36 | --build-target "${ESP_BOARD}" \ 37 | && rustup component add clippy rustfmt 38 | 39 | # Activate ESP environment 40 | RUN echo "source /home/${CONTAINER_USER}/export-esp.sh" >> ~/.bashrc 41 | 42 | CMD [ "/bin/bash" ] 43 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{crate_name}}", 3 | // Select between image and build propieties to pull or build the image. 4 | // "image": "docker.io/espressif/idf-rust:esp32_1.65.0.1", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | "CONTAINER_USER": "esp", 9 | "CONTAINER_GROUP": "esp", 10 | "ESP_BOARD": "esp32" 11 | } 12 | }, 13 | "settings": { 14 | "editor.formatOnPaste": true, 15 | "editor.formatOnSave": true, 16 | "editor.formatOnSaveMode": "file", 17 | "editor.formatOnType": true, 18 | "lldb.executable": "/usr/bin/lldb", 19 | "files.watcherExclude": { 20 | "**/target/**": true 21 | }, 22 | "rust-analyzer.checkOnSave.command": "clippy", 23 | "rust-analyzer.checkOnSave.allTargets": false, 24 | "[rust]": { 25 | "editor.defaultFormatter": "rust-lang.rust-analyzer" 26 | } 27 | }, 28 | "extensions": [ 29 | "rust-lang.rust-analyzer", 30 | "tamasfe.even-better-toml", 31 | "serayuzgur.crates", 32 | "mutantdino.resourcemonitor", 33 | "yzhang.markdown-all-in-one", 34 | "webfreak.debug", 35 | "actboy168.tasks" 36 | ], 37 | "forwardPorts": [ 38 | 9012, 39 | 9333, 40 | 8000 41 | ], 42 | "workspaceMount": "source=${localWorkspaceFolder},target=/home/esp/spooky,type=bind,consistency=cached", 43 | "workspaceFolder": "/home/esp/spooky" 44 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | target/ 4 | 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | .DS_Store 14 | 15 | node_modules 16 | dist/ 17 | /pkg/ 18 | wasm-pack.log 19 | 20 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | # Note: gitpod/workspace-base image references older version of CMake, it's necessary to install newer one 2 | FROM gitpod/workspace-full 3 | ENV LC_ALL=C.UTF-8 4 | ENV LANG=C.UTF-8 5 | 6 | # ARGS 7 | ARG CONTAINER_USER=gitpod 8 | ARG CONTAINER_GROUP=gitpod 9 | ARG ESP_BOARD="esp32,esp32s2,esp32s3,esp32c3" 10 | ARG INSTALL_RUST_TOOLCHAIN=espup 11 | 12 | # Install dependencies for building wokwi-server 13 | RUN sudo install-packages libudev-dev 14 | 15 | # Install build/runtime dependencies for application 16 | RUN sudo install-packages libsdl2-dev 17 | 18 | # RUN sudo install-packages git curl gcc ninja-build libudev-dev \ 19 | # libusb-1.0-0 libssl-dev pkg-config libtinfo5 clang \ 20 | # libsdl2-dev npm 21 | 22 | # Set User 23 | USER ${CONTAINER_USER} 24 | WORKDIR /home/${CONTAINER_USER} 25 | 26 | # Install Rust toolchain, extra crates and esp-idf 27 | ENV PATH=${PATH}:/home/${CONTAINER_USER}/.cargo/bin:/home/${CONTAINER_USER}/opt/bin 28 | ADD --chown=${CONTAINER_USER}:${CONTAINER_GROUP} \ 29 | https://github.com/esp-rs/espup/releases/latest/download/espup-x86_64-unknown-linux-gnu \ 30 | /home/${CONTAINER_USER}/${INSTALL_RUST_TOOLCHAIN} 31 | RUN chmod a+x ${INSTALL_RUST_TOOLCHAIN} \ 32 | && ./${INSTALL_RUST_TOOLCHAIN} install \ 33 | --extra-crates "cargo-espflash,wokwi-server" \ 34 | --export-file /home/${CONTAINER_USER}/export-esp.sh \ 35 | --targets "${ESP_BOARD}" 36 | # Disabled: 37 | # && rustup component add clippy rustfmt 38 | # Not released to crates.io: 39 | # cargo install web-flash 40 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | tasks: 4 | - name: Setup environment variables for Rust and ESP-IDF 5 | command: | 6 | source /home/gitpod/export-esp.sh 7 | vscode: 8 | extensions: 9 | - matklad.rust-analyzer 10 | - tamasfe.even-better-toml 11 | - anwar.resourcemonitor 12 | - yzhang.markdown-all-in-one 13 | - webfreak.debug 14 | - actboy168.tasks 15 | - serayuzgur.crates 16 | ports: 17 | - port: 9012 18 | visibility: public 19 | - port: 9333 20 | visibility: public 21 | - port: 8000 22 | visibility: public 23 | onOpen: open-browser -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "gdb", 6 | "request": "attach", 7 | "name": "Wokwi Debug", 8 | "executable":"${workspaceFolder}/target/target.xtensa-esp32-none-elf/debug/spooky", 9 | "target": "localhost:9333", 10 | "remote": true, 11 | "gdbpath":"/home/${input:user}/.espressif/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtensa-esp32-elf/bin/xtensa-esp32-elf-gdb", 12 | "cwd": "${workspaceRoot}", 13 | "stopAtConnect": true, 14 | "valuesFormatting": "parseText" 15 | } 16 | ], 17 | "inputs": [ 18 | { 19 | "type": "pickString", 20 | "id": "user", 21 | "description": "Select the user: esp for VsCode and Codespaces and gitpod for Gitpod:", 22 | "options": [ 23 | "esp", 24 | "gitpod" 25 | ], 26 | "default": "esp" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build", 6 | "type": "shell", 7 | "command": "scripts/build.sh ${input:buildMode}", 8 | "options": { 9 | "cwd": "${workspaceFolder}" 10 | }, 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | } 15 | }, 16 | { 17 | "label": "Build & Flash", 18 | "type": "shell", 19 | "command": "scripts/flash.sh ${input:buildMode}", 20 | "options": { 21 | "cwd": "${workspaceFolder}" 22 | }, 23 | "group": { 24 | "kind": "test", 25 | "isDefault": true 26 | } 27 | }, 28 | { 29 | "label": "Build & Run Wokwi", 30 | "type": "shell", 31 | "command": "scripts/run-wokwi.sh ${input:buildMode}", 32 | "options": { 33 | "cwd": "${workspaceFolder}" 34 | }, 35 | "group": { 36 | "kind": "test", 37 | "isDefault": false 38 | } 39 | }, 40 | ], 41 | "inputs": [ 42 | { 43 | "type": "pickString", 44 | "id": "buildMode", 45 | "description": "Select the build mode:", 46 | "options": [ 47 | "release", 48 | "debug" 49 | ], 50 | "default": "release" 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | # Set explicit resolver to "2" to avoid problems with std/no_std projects. 4 | resolver = "2" 5 | 6 | members = [ 7 | "desktop", 8 | "esp-wrover-kit", 9 | "esp32-c6-devkit", 10 | "esp32-c3-devkit-rust", 11 | "esp32-s2-kaluga", 12 | "esp32-s2-usb-otg", 13 | "esp32-s3-usb-otg", 14 | "esp32-s3-box", 15 | "m5stack-core2", 16 | "m5stack-fire", 17 | "engine", 18 | "wasm" 19 | ] 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Juraj Michálek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32 Rust Multi Target Template 2 | 3 | Template project for creating interactive apps with Rust Bare Metal (no_std) on ESP32. 4 | The template contains also WASM and Desktop version which could help with faster implementation of concepts. 5 | Each target HW is in separate directory. Shared code is stored in `engine`. 6 | 7 | ## Version of espflash 8 | 9 | Use `espflash 2.x` which supports also targets like ESP32-C6. 10 | 11 | Installation: 12 | 13 | ``` 14 | cargo install espflash --git https://github.com/esp-rs/espflash.git 15 | ``` 16 | 17 | ## Generate new project 18 | 19 | ``` 20 | cargo generate https://github.com/georgik/esp32-rust-multi-target-template.git 21 | cd project-name 22 | ``` 23 | 24 | ## Build and flash 25 | 26 | ### Build WASM version 27 | 28 | ``` 29 | cd wasm 30 | npm install 31 | npm run serve 32 | ``` 33 | 34 | Open in web browser: https://localhost:8443. 35 | 36 | Note: https is required for access to accelerometer data - https://w3c.github.io/deviceorientation/#security-and-privacy . It's possible to run the app without accelerometer on http. 37 | 38 | ### Build for ESP32-S3-BOX with ILI9486 39 | 40 | Control: IMU 41 | - tilt the board to move the character 42 | - move quickly up to perform the first action 43 | - move quickly down to perform second action 44 | 45 | ``` 46 | cd esp32-s3-box 47 | cargo espflash flash --release --monitor 48 | ``` 49 | 50 | ### Build for ESP32-C6-DevKitM-1 with ILI9341 51 | 52 | Control: Not implemented 53 | 54 | ``` 55 | cd esp32-c6-devkit 56 | cargo espflash flash --release --monitor 57 | ``` 58 | 59 | ### Build for ESP32-C3-DeviKit-RUST with ILI9341 60 | 61 | Control: IMU 62 | - tilt board to move character 63 | 64 | ``` 65 | cd esp32-c3-devkit-rust 66 | cargo espflash flash --release --monitor 67 | ``` 68 | 69 | #### Features 70 | 71 | - Embedded Graphics 72 | - Framebuffer 73 | - Random maze generator 74 | - IMU Accelerometer control 75 | 76 | ### Build for dekstop 77 | 78 | Control: keyboard 79 | - press arrows or W,A,S,D to move the character 80 | - press Enter to perform action 81 | 82 | - macOS prerequisites: 83 | ``` 84 | brew install SDL2 85 | export LIBRARY_PATH="$LIBRARY_PATH:$(brew --prefix)/lib" 86 | ``` 87 | 88 | - OpenSUSE Linux prerequisites: 89 | ``` 90 | sudo zypper install SDL2-devel 91 | ``` 92 | 93 | - run: 94 | ``` 95 | cd desktop 96 | cargo run 97 | ``` 98 | 99 | ### Build for ESP32-S3-USB-OTG with ST7789 100 | 101 | Control: buttons 102 | - press button to move the character 103 | - press up & down to perform the first action 104 | - press ok & menu to perform the secomnd action 105 | 106 | ``` 107 | cd esp32-s3-usb-otg 108 | cargo espflash flash --release --monitor 109 | ``` 110 | 111 | ### Build for ESP32-S2-USB-OTG with ST7789 112 | 113 | Control: buttons 114 | - press button to move the character 115 | - press up & down to perform the first action 116 | - press ok & menu to perform the second action 117 | 118 | ``` 119 | cd esp32-s2-usb-otg 120 | cargo espflash flash --release --monitor 121 | ``` 122 | 123 | ### Build for M5STACK-FIRE with ESP32 and ILI9341 124 | 125 | HW: https://docs.makerfactory.io/m5stack/core/fire/ 126 | 127 | Control: MPU-9250, buttons 128 | - tilt the board to move the character 129 | - move quickly up or press button C to perform the first action 130 | - move quickly down or press button B to perform the second action 131 | 132 | ``` 133 | cd m5stack-fire 134 | cargo espflash flash --release --monitor 135 | ``` 136 | 137 | #### Build M5STACK-FIRE using GitPod.io and run with Wokwi 138 | 139 | - Open in [GitPod.io](https://gitpod.io/github.com/georgik/esp32-spooky-maze-game) 140 | 141 | ``` 142 | cd m5stack-fire 143 | ./run-wokwi.sh 144 | ``` 145 | 146 | - Wokwi project: https://wokwi.com/projects/350825213595746900 147 | 148 | #### Build M5STACK-FIRE using Codespaces and run with Wokwi 149 | 150 | - Navigate to [GitHub repository](https://github.com/georgik/esp32-spooky-maze-game) 151 | - Click Open, select Codespaces tab, click Create Codespace 152 | 153 | ``` 154 | cd m5stack-fire 155 | ./run-wokwi.sh 156 | ``` 157 | 158 | #### Build M5STACK-FIRE and run Wokwi in local VS Code 159 | 160 | Preview: install VS Code Wokwi plugin (private beta available on request) 161 | 162 | ``` 163 | cd m5stack-fire 164 | cargo build --release --no-default-features --features "wokwi" 165 | ``` 166 | 167 | Press F1, select Wokwi: Start simulation 168 | 169 | ### Build for M5STACK-CORE2 with ESP32 and ILI9342C 170 | 171 | HW: https://shop.m5stack.com/products/m5stack-core2-esp32-iot-development-kit 172 | 173 | Control: MPU6886 174 | 175 | ``` 176 | cd m5stack-core2 177 | cargo espflash flash --release --monitor 178 | ``` 179 | 180 | ### Build for ESP32-S2-Kaluga v1.3 181 | 182 | HW: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-wrover-kit.html 183 | 184 | Control: buttons (partialy implemented based on of https://github.com/espressif/esp-bsp/blob/master/esp32_s2_kaluga_kit/esp32_s2_kaluga_kit.c#L59) 185 | - more details https://github.com/espressif/esp-bsp/blob/master/esp32_s2_kaluga_kit/include/bsp/esp32_s2_kaluga_kit.h#L299 186 | - K3-K6 to move the character 187 | - (not supported) press K5 button to perform the first action 188 | - (not supported) press K6 button to perform the second action 189 | 190 | ``` 191 | cd esp32-s2-kaluga 192 | cargo espflash flash --release --monitor 193 | ``` 194 | 195 | Note for older version 1.2 - GPIO6 is used to control backlight. 196 | 197 | ### Build for ESP Wrover Kit 198 | 199 | HW: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-wrover-kit.html 200 | 201 | Control: limited, only one button available 202 | - it's not possible to move the character 203 | - press button Boot to to perform the first action 204 | 205 | ``` 206 | cd esp-wrover-kit 207 | cargo espflash flash --release --monitor 208 | ``` 209 | 210 | ### Build for ESP32-S2 with ILI9341 211 | 212 | See tag v0.1.0. 213 | 214 | ``` 215 | cargo espflash flash --release --target xtensa-esp32s2-none-elf --features esp32s2_ili9341 --monitor 216 | ``` 217 | 218 | ## Development 219 | 220 | Following steps are useful for IDE integration, so that IDE can recognize which is your current target and fature set. 221 | 222 | Check `target` configurad in the file `.cargo/config.toml`. 223 | It should be one of following values: 224 | ``` 225 | target = "xtensa-esp32-none-elf" 226 | target = "xtensa-esp32s2-none-elf" 227 | target = "xtensa-esp32s3-none-elf" 228 | target = "riscv32imac-unknown-none-elf" 229 | ``` 230 | 231 | If no value is selected, make sure to specify target on command line. 232 | 233 | Check default `features` in `Cargo.toml`. Make sure that default set contains your board and display combinations. 234 | 235 | If no value is selected, make sure to specify features on command line. 236 | 237 | -------------------------------------------------------------------------------- /desktop/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-desktop" 3 | version = "0.1.0" 4 | authors = ["Juraj Michalek "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | 9 | [dependencies] 10 | embedded-graphics = "0.7" 11 | embedded-graphics-framebuf = "0.2.0" 12 | getrandom = { version = "0.2.8", features = ["js"] } 13 | rand_chacha = { version = "0.3.1", default-features = false } 14 | tinybmp = "0.4.0" 15 | heapless = { version = "0.7.14", default-features = false } 16 | {{crate_name}}-engine = { path = "../engine" } 17 | embedded-graphics-simulator = "0.4.0" 18 | -------------------------------------------------------------------------------- /desktop/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | // #![no_std] 3 | // #![no_main] 4 | 5 | use embedded_graphics::{ 6 | pixelcolor::Rgb565, 7 | prelude::*, 8 | }; 9 | use embedded_graphics_simulator::{ 10 | sdl2::Keycode, SimulatorDisplay, SimulatorEvent, Window, OutputSettingsBuilder, 11 | }; 12 | use embedded_graphics_framebuf::{FrameBuf}; 13 | 14 | use std::time::Duration; 15 | 16 | use {{crate_name}}_engine::{ spritebuf::SpriteBuf, engine::Engine }; 17 | 18 | pub struct Universe { 19 | engine: Engine, 20 | } 21 | 22 | fn get_seed_buffer() -> Option<[u8; 32]> { 23 | let mut seed_buffer = [0u8; 32]; 24 | getrandom::getrandom(&mut seed_buffer).unwrap(); 25 | Some(seed_buffer) 26 | } 27 | 28 | impl > Universe { 29 | 30 | pub fn new(engine:Engine) -> Universe { 31 | Universe { 32 | engine 33 | } 34 | } 35 | 36 | pub fn move_up(&mut self) { 37 | self.engine.move_up(); 38 | } 39 | 40 | pub fn move_down(&mut self) { 41 | self.engine.move_down(); 42 | } 43 | 44 | pub fn move_left(&mut self) { 45 | self.engine.move_left(); 46 | } 47 | 48 | pub fn move_right(&mut self) { 49 | self.engine.move_right(); 50 | } 51 | 52 | pub fn initialize(&mut self) { 53 | self.engine.initialize(); 54 | } 55 | 56 | pub fn render_frame(&mut self) -> &D { 57 | self.engine.tick(); 58 | self.engine.draw() 59 | // display.flush().unwrap(); 60 | 61 | } 62 | } 63 | 64 | 65 | 66 | fn main() -> Result<(), core::convert::Infallible> { 67 | let output_settings = OutputSettingsBuilder::new().scale(2).build(); 68 | let mut window = Window::new("{{project-name}}", &output_settings); 69 | 70 | let mut data = [Rgb565::BLACK ; 320*240]; 71 | let fbuf = FrameBuf::new(&mut data, 320, 240); 72 | let spritebuf = SpriteBuf::new(fbuf); 73 | 74 | let engine = Engine::new(spritebuf, get_seed_buffer()); 75 | 76 | let mut universe = Universe::new(engine); 77 | universe.initialize(); 78 | let mut display = SimulatorDisplay::new(Size::new(320, 200)); 79 | 80 | 81 | display.draw_iter(universe.render_frame().into_iter()).unwrap(); 82 | window.update(&display); 83 | 84 | 'running: loop { 85 | 86 | for event in window.events() { 87 | match event { 88 | SimulatorEvent::Quit => break 'running, 89 | SimulatorEvent::KeyDown { keycode, .. } => { 90 | match keycode { 91 | Keycode::Left | Keycode::A => universe.move_left(), 92 | Keycode::Right | Keycode::D => universe.move_right(), 93 | Keycode::Up | Keycode::W => universe.move_up(), 94 | Keycode::Down | Keycode::S => universe.move_down(), 95 | // Keycode::Return => ..., 96 | // Keycode::Space => ...., 97 | _ => {}, 98 | }; 99 | } 100 | // SimulatorEvent::MouseButtonUp { point, .. } => { 101 | // move_circle(&mut display, position, point)?; 102 | // position = point; 103 | // } 104 | _ => {} 105 | } 106 | } 107 | display.draw_iter(universe.render_frame().into_iter()).unwrap(); 108 | window.update(&display); 109 | std::thread::sleep(Duration::from_millis(50)); 110 | } 111 | 112 | Ok(()) 113 | } 114 | 115 | -------------------------------------------------------------------------------- /engine/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-engine" 3 | version = "0.2.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | embedded-graphics = "0.7" 8 | embedded-graphics-framebuf = "0.2.0" 9 | tinybmp = "0.4.0" 10 | rand = { version = "0.8.5", default-features = false } 11 | rand_chacha = { version = "0.3.1", default-features = false } 12 | heapless = { version = "0.7.14", default-features = false } 13 | 14 | [features] 15 | std = [] 16 | wasm = [ "std" ] 17 | -------------------------------------------------------------------------------- /engine/assets/img/espressif.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgik/esp32-rust-multi-target-template/717eb0d5ba39a4769bad0befa8e7667e0106ddbd/engine/assets/img/espressif.bmp -------------------------------------------------------------------------------- /engine/src/assets.rs: -------------------------------------------------------------------------------- 1 | 2 | use embedded_graphics::{ 3 | pixelcolor::Rgb565, 4 | }; 5 | use tinybmp::Bmp; 6 | 7 | pub struct Assets<'a> { 8 | pub logo: Option>, 9 | } 10 | 11 | impl Assets<'static> { 12 | pub fn new() -> Assets<'static> { 13 | Assets { 14 | logo: None, 15 | } 16 | } 17 | 18 | pub fn load(&mut self) { 19 | self.logo = Some(Bmp::::from_slice(include_bytes!("../assets/img/espressif.bmp")).unwrap()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /engine/src/engine.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | use embedded_graphics::{ 4 | prelude::{Point, RgbColor}, 5 | mono_font::{ 6 | ascii::{FONT_8X13}, 7 | MonoTextStyle, 8 | }, 9 | text::Text, 10 | pixelcolor::Rgb565, 11 | Drawable, 12 | image::Image, 13 | }; 14 | 15 | use crate::{assets::Assets}; 16 | use heapless::String; 17 | use tinybmp::Bmp; 18 | 19 | pub struct Engine { 20 | pub start_time: u64, 21 | pub avatar_x: i32, 22 | pub avatar_y: i32, 23 | display: D, 24 | assets: Option>, 25 | step_size_x: u32, 26 | step_size_y: u32, 27 | animation_step: u32, 28 | } 29 | 30 | 31 | impl > Engine { 32 | pub fn new(display:D, seed: Option<[u8; 32]>) -> Engine { 33 | Engine { 34 | start_time: 0, 35 | avatar_x: 9*16, 36 | avatar_y: 7*16, 37 | display, 38 | assets: None, 39 | step_size_x: 16, 40 | step_size_y: 16, 41 | // #[cfg(any(feature = "imu_controls"))] 42 | animation_step: 0, 43 | } 44 | } 45 | 46 | fn check_object_collisions(&mut self) { 47 | } 48 | 49 | fn is_walkable(&self, x: i32, y: i32) -> bool { 50 | // Boundaries of the scene 51 | !( (x < 0) || (y < 0) || (x > 280) || (y > 200) ) 52 | } 53 | 54 | pub fn move_right(&mut self) { 55 | let new_avatar_x = self.avatar_x + self.step_size_x as i32; 56 | if self.is_walkable(new_avatar_x, self.avatar_y) { 57 | self.avatar_x = new_avatar_x; 58 | self.check_object_collisions(); 59 | } 60 | } 61 | 62 | pub fn move_left(&mut self) { 63 | let new_avatar_x = self.avatar_x - self.step_size_x as i32; 64 | if self.is_walkable(new_avatar_x, self.avatar_y) { 65 | self.avatar_x = new_avatar_x; 66 | self.check_object_collisions(); 67 | } 68 | } 69 | 70 | pub fn move_up(&mut self) { 71 | let new_avatar_y = self.avatar_y - self.step_size_y as i32; 72 | if self.is_walkable(self.avatar_x, new_avatar_y) { 73 | self.avatar_y = new_avatar_y; 74 | self.check_object_collisions(); 75 | } 76 | } 77 | 78 | pub fn move_down(&mut self) { 79 | let new_avatar_y = self.avatar_y + self.step_size_y as i32; 80 | if self.is_walkable(self.avatar_x, new_avatar_y) { 81 | self.avatar_y = new_avatar_y; 82 | self.check_object_collisions(); 83 | } 84 | } 85 | 86 | pub fn draw_background(&mut self, _camera_x: i32, _camera_y: i32) { 87 | self.display.clear(RgbColor::BLACK); 88 | } 89 | 90 | 91 | pub fn tick(&mut self) { 92 | // self.animation_step += 1; 93 | // if self.animation_step > 1 { 94 | // self.animation_step = 0; 95 | // } 96 | 97 | // self.maze.move_npcs(); 98 | // self.check_npc_collision(); 99 | } 100 | 101 | pub fn initialize(&mut self) { 102 | let mut assets = Assets::new(); 103 | assets.load(); 104 | self.assets = Some(assets); 105 | 106 | self.draw_background(0, 0); 107 | 108 | } 109 | 110 | fn draw_status_number(&mut self, value: u32, x: i32, y: i32) { 111 | let value_message: String<5> = String::from(value); 112 | Text::new(&value_message, Point::new(x, y), MonoTextStyle::new(&FONT_8X13, Rgb565::WHITE)) 113 | .draw(&mut self.display); 114 | } 115 | 116 | pub fn draw(&mut self) -> &mut D { 117 | self.draw_background(0, 0); 118 | 119 | match self.assets { 120 | Some(ref mut assets) => { 121 | 122 | let logo_bmp:Bmp = assets.logo.unwrap(); 123 | let position = Point::new(self.avatar_x, self.avatar_y); 124 | let tile = Image::new(&logo_bmp, position); 125 | tile.draw(&mut self.display); 126 | 127 | // display.flush().unwrap(); 128 | }, 129 | None => { 130 | panic!("Assets not loaded"); 131 | } 132 | }; 133 | 134 | &mut self.display 135 | } 136 | 137 | 138 | } 139 | 140 | 141 | -------------------------------------------------------------------------------- /engine/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | pub mod assets; 3 | pub mod engine; 4 | pub mod spritebuf; 5 | -------------------------------------------------------------------------------- /engine/src/spritebuf.rs: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/bernii/embedded-graphics-framebuf 2 | 3 | use embedded_graphics::{ 4 | prelude::{RgbColor}, 5 | prelude::{Point, DrawTarget, Size}, 6 | geometry::OriginDimensions, 7 | Pixel, 8 | }; 9 | use embedded_graphics::{pixelcolor::Rgb565}; 10 | use embedded_graphics_framebuf::{FrameBuf, backends::FrameBufferBackend, PixelIterator}; 11 | 12 | pub struct SpriteBuf> { 13 | pub fbuf: FrameBuf, 14 | } 15 | 16 | impl> OriginDimensions for SpriteBuf { 17 | fn size(&self) -> Size { 18 | self.fbuf.size() 19 | } 20 | } 21 | 22 | impl> SpriteBuf { 23 | pub fn new(fbuf:FrameBuf) -> Self { 24 | Self { 25 | fbuf, 26 | } 27 | } 28 | 29 | /// Get the framebuffers width. 30 | pub fn width(&self) -> usize { 31 | self.fbuf.width() 32 | } 33 | 34 | /// Get the framebuffers height. 35 | pub fn height(&self) -> usize { 36 | self.fbuf.height() 37 | } 38 | 39 | /// Set a pixel's color. 40 | pub fn set_color_at(&mut self, p: Point, color: Rgb565) { 41 | self.fbuf.set_color_at(p, color) 42 | } 43 | 44 | /// Get a pixel's color. 45 | pub fn get_color_at(&self, p: Point) -> Rgb565 { 46 | self.fbuf.get_color_at(p) 47 | } 48 | } 49 | 50 | impl<'a, B: FrameBufferBackend> IntoIterator for &'a SpriteBuf { 51 | type Item = Pixel; 52 | type IntoIter = PixelIterator<'a, Rgb565, B>; 53 | 54 | fn into_iter(self) -> Self::IntoIter { 55 | self.fbuf.into_iter() 56 | } 57 | } 58 | 59 | impl> DrawTarget for SpriteBuf { 60 | type Color = Rgb565; 61 | type Error = core::convert::Infallible; 62 | 63 | fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> 64 | where 65 | I: IntoIterator>, 66 | { 67 | for Pixel(coord, color) in pixels.into_iter() { 68 | if color.g() == 0 && color.b() == 31 && color.r() == 31 { 69 | continue; 70 | } 71 | if coord.x >= 0 72 | && coord.x < self.width() as i32 73 | && coord.y >= 0 74 | && coord.y < self.height() as i32 75 | { 76 | self.fbuf.set_color_at(coord, color); 77 | } 78 | } 79 | Ok(()) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /esp-wrover-kit/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_arch = "xtensa")'] 2 | runner = "espflash flash --monitor" 3 | rustflags = [ 4 | # Tell the `core` library that we have atomics, even though it's not 5 | # specified in the target definition 6 | "--cfg", 'target_has_atomic="8"', 7 | "--cfg", 'target_has_atomic="16"', 8 | "--cfg", 'target_has_atomic="32"', 9 | "--cfg", 'target_has_atomic="ptr"', 10 | 11 | "-C", "link-arg=-nostartfiles", 12 | # Enable the atomic codegen option for Xtensa 13 | "-C", "target-feature=+s32c1i", 14 | "-C", "link-arg=-Wl,-Tlinkall.x", 15 | "-C", "force-frame-pointers" 16 | ] 17 | 18 | [target.riscv32imac-unknown-none-elf] 19 | rustflags = [ 20 | "-C", "link-arg=-Tlinkall.x", 21 | ] 22 | 23 | [build] 24 | # Uncomment the target if you'd like to use automatic code hinting in your IDE 25 | target = "xtensa-esp32-none-elf" 26 | # target = "xtensa-esp32s2-none-elf" 27 | # target = "xtensa-esp32s3-none-elf" 28 | # target = "riscv32imac-unknown-none-elf" 29 | 30 | [unstable] 31 | build-std = [ "core", "alloc" ] 32 | -------------------------------------------------------------------------------- /esp-wrover-kit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-wrover-kit" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [target.xtensa-esp32-none-elf.dependencies] 9 | xtensa-atomic-emulation-trap = "0.4.0" 10 | esp32-hal = "0.10.0" 11 | esp-backtrace = { version = "0.6.0", features = [ 12 | "esp32", 13 | "panic-handler", 14 | "print-uart", 15 | ] } 16 | xtensa-lx-rt = { version = "0.15.0", features = ["esp32"], optional = true } 17 | 18 | [dependencies] 19 | embedded-graphics = "0.7" 20 | embedded-hal = "0.2" 21 | display-interface = "0.4" 22 | display-interface-spi = "0.4" 23 | mipidsi = { git = "https://github.com/rfuest/mipidsi.git", branch = "display-driver-hal" } 24 | panic-halt = "0.2" 25 | rand = { version = "0.8.5", default-features = false } 26 | rand_chacha = { version = "0.3.1", default-features = false } 27 | shared-bus = { version = "0.2.4" } 28 | {{crate_name}}-engine = { path = "../engine", default-features = false, features = [] } 29 | heapless = { version = "0.7.14", default-features = false } 30 | embedded-graphics-framebuf = "0.2.0" 31 | 32 | [features] 33 | default = [ "esp_wrover_kit" ] 34 | 35 | system_timer = [] 36 | 37 | button_controls = [] 38 | imu_controls = [] 39 | 40 | esp32 = [] 41 | esp32s2 = ["system_timer"] 42 | esp32s3 = [] 43 | esp32c3 = ["system_timer"] 44 | 45 | # Enable this feature in case you have an ESP32 Wrover Kit with ILI9341 46 | esp_wrover_kit = [ "xtensa-lx-rt", "esp32", "esp32-hal/eh1" ] 47 | -------------------------------------------------------------------------------- /esp-wrover-kit/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | components = ["rustfmt", "rustc-dev"] 4 | targets = ["xtensa-esp32-none-elf"] 5 | # targets = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf","xtensa-esp32s3-none-elf"] 6 | -------------------------------------------------------------------------------- /esp-wrover-kit/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(default_alloc_error_handler)] 4 | 5 | // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-wrover-kit.html 6 | 7 | use display_interface_spi::SPIInterfaceNoCS; 8 | use embedded_graphics::{ 9 | mono_font::{ascii::FONT_8X13, MonoTextStyle}, 10 | prelude::{DrawTarget, Point, RgbColor}, 11 | text::Text, 12 | Drawable, 13 | }; 14 | 15 | #[cfg(feature = "esp32")] 16 | use esp32_hal as hal; 17 | #[cfg(feature = "esp32c3")] 18 | use esp32c3_hal as hal; 19 | #[cfg(feature = "esp32s2")] 20 | use esp32s2_hal as hal; 21 | #[cfg(feature = "esp32s3")] 22 | use esp32s3_hal as hal; 23 | 24 | use hal::{ 25 | clock::{ClockControl, CpuClock}, 26 | peripherals::Peripherals, 27 | prelude::*, 28 | spi, 29 | timer::TimerGroup, 30 | Delay, Rng, Rtc, IO, 31 | }; 32 | 33 | // use panic_halt as _; 34 | use esp_backtrace as _; 35 | 36 | use mipidsi::hal::{ Orientation, Rotation }; 37 | 38 | #[cfg(feature = "xtensa-lx-rt")] 39 | use xtensa_lx_rt::entry; 40 | 41 | use embedded_graphics::pixelcolor::Rgb565; 42 | 43 | use {{crate_name}}_engine::{engine::Engine, spritebuf::SpriteBuf}; 44 | 45 | #[cfg(any(feature = "imu_controls"))] 46 | use shared_bus::BusManagerSimple; 47 | 48 | use embedded_graphics_framebuf::FrameBuf; 49 | use embedded_hal::digital::v2::OutputPin; 50 | 51 | pub struct Universe { 52 | pub engine: Engine, 53 | } 54 | 55 | impl> Universe { 56 | pub fn new(seed: Option<[u8; 32]>, engine: Engine) -> Universe { 57 | Universe { engine } 58 | } 59 | 60 | pub fn initialize(&mut self) { 61 | self.engine.initialize(); 62 | } 63 | 64 | pub fn move_up(&mut self) { 65 | self.engine.move_up(); 66 | } 67 | 68 | pub fn move_down(&mut self) { 69 | self.engine.move_down(); 70 | } 71 | 72 | pub fn move_left(&mut self) { 73 | self.engine.move_left(); 74 | } 75 | 76 | pub fn move_right(&mut self) { 77 | self.engine.move_right(); 78 | } 79 | 80 | pub fn render_frame(&mut self) -> &D { 81 | self.engine.tick(); 82 | self.engine.draw() 83 | } 84 | } 85 | 86 | #[entry] 87 | fn main() -> ! { 88 | let peripherals = Peripherals::take(); 89 | 90 | #[cfg(any(feature = "esp32"))] 91 | let mut system = peripherals.DPORT.split(); 92 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 93 | let mut system = peripherals.SYSTEM.split(); 94 | let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze(); 95 | 96 | // Disable the RTC and TIMG watchdog timers 97 | let mut rtc = Rtc::new(peripherals.RTC_CNTL); 98 | let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); 99 | let mut wdt0 = timer_group0.wdt; 100 | let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); 101 | let mut wdt1 = timer_group1.wdt; 102 | 103 | #[cfg(feature = "esp32c3")] 104 | rtc.swd.disable(); 105 | #[cfg(feature = "xtensa-lx-rt")] 106 | rtc.rwdt.disable(); 107 | 108 | wdt0.disable(); 109 | wdt1.disable(); 110 | 111 | let mut delay = Delay::new(&clocks); 112 | 113 | let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); 114 | 115 | let mut backlight = io.pins.gpio5.into_push_pull_output(); 116 | 117 | let spi = spi::Spi::new( 118 | peripherals.SPI3, // Real HW working with SPI2, but Wokwi seems to work only with SPI3 119 | io.pins.gpio19, // SCLK 120 | io.pins.gpio23, // MOSI 121 | io.pins.gpio25, // MISO 122 | io.pins.gpio22, // CS 123 | 60u32.MHz(), 124 | spi::SpiMode::Mode0, 125 | &mut system.peripheral_clock_control, 126 | &clocks, 127 | ); 128 | 129 | backlight.set_low().unwrap(); 130 | 131 | let reset = io.pins.gpio18.into_push_pull_output(); 132 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio21.into_push_pull_output()); 133 | 134 | let mut display = mipidsi::Builder::ili9341_rgb565(di) 135 | .with_display_size(320, 240) 136 | .with_orientation(Orientation::new().rotate(Rotation::Deg90).flip_vertical()) 137 | .init(&mut delay, Some(reset)) 138 | .unwrap(); 139 | 140 | Text::new( 141 | "Initializing...", 142 | Point::new(80, 110), 143 | MonoTextStyle::new(&FONT_8X13, RgbColor::WHITE), 144 | ) 145 | .draw(&mut display) 146 | .unwrap(); 147 | 148 | let button_boot = io.pins.gpio2.into_pull_up_input(); 149 | 150 | let mut rng = Rng::new(peripherals.RNG); 151 | let mut seed_buffer = [0u8; 32]; 152 | rng.read(&mut seed_buffer).unwrap(); 153 | let mut data = [Rgb565::BLACK; 320 * 240]; 154 | let fbuf = FrameBuf::new(&mut data, 320, 240); 155 | let spritebuf = SpriteBuf::new(fbuf); 156 | let engine = Engine::new(spritebuf, Some(seed_buffer)); 157 | let mut universe = Universe::new(Some(seed_buffer), engine); 158 | universe.initialize(); 159 | 160 | loop { 161 | // if button_boot.is_low().unwrap() { 162 | // } 163 | 164 | display 165 | .draw_iter(universe.render_frame().into_iter()) 166 | .unwrap(); 167 | // delay.delay_ms(300u32); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /esp32-c3-devkit-rust/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imac-unknown-none-elf] 2 | runner = "espflash flash --monitor" 3 | rustflags = [ 4 | "-C", "link-arg=-Tlinkall.x", 5 | ] 6 | 7 | [build] 8 | target = "riscv32imac-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = [ "core", "alloc" ] 12 | -------------------------------------------------------------------------------- /esp32-c3-devkit-rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}_esp32c3" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [target.riscv32imac-unknown-none-elf.dependencies] 9 | esp32c3-hal = "0.7.0" 10 | esp-backtrace = { version = "0.6.0", features = [ 11 | "esp32c3", 12 | "panic-handler", 13 | "print-uart", 14 | ] } 15 | esp-println = { version = "0.4.0", features = [ "esp32c3" ] } 16 | 17 | [dependencies] 18 | esp-alloc = "0.2.0" 19 | embedded-graphics = "0.7" 20 | embedded-hal = "0.2" 21 | display-interface = "0.4" 22 | display-interface-spi = "0.4" 23 | icm42670 = { git = "https://github.com/jessebraham/icm42670/" } 24 | mipidsi = { git = "https://github.com/almindor/mipidsi.git" } 25 | panic-halt = "0.2" 26 | rand = { version = "0.8.5", default-features = false } 27 | rand_chacha = { version = "0.3.1", default-features = false } 28 | petgraph = { git = "https://github.com/zendurix/petgraph.git", branch = "better_no_std", default-features = false, features = [ 29 | "graphmap", 30 | ] } 31 | shared-bus = { version = "0.2.4" } 32 | {{crate_name}}-engine = { path = "../engine", default-features = false, features = [] } 33 | heapless = { version = "0.7.14", default-features = false } 34 | embedded-graphics-framebuf = "0.2.0" 35 | 36 | [features] 37 | default = [ "esp32c3_ili9341" ] 38 | 39 | system_timer = [] 40 | 41 | button_controls = [] 42 | imu_controls = [] 43 | 44 | esp32 = [] 45 | esp32s2 = ["system_timer"] 46 | esp32s3 = [] 47 | esp32c3 = ["system_timer"] 48 | 49 | esp32c3_ili9341 = [ "esp32c3", "esp32c3-hal/eh1", "imu_controls" ] 50 | -------------------------------------------------------------------------------- /esp32-c3-devkit-rust/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | components = ["rustfmt", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /esp32-c3-devkit-rust/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(default_alloc_error_handler)] 4 | 5 | #[global_allocator] 6 | static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); 7 | 8 | use display_interface_spi::SPIInterfaceNoCS; 9 | use embedded_graphics::{ 10 | mono_font::{ascii::FONT_8X13, MonoTextStyle}, 11 | prelude::{DrawTarget, Point, RgbColor}, 12 | text::Text, 13 | Drawable, 14 | }; 15 | 16 | use esp_println::println; 17 | 18 | #[cfg(feature = "esp32")] 19 | use esp32_hal as hal; 20 | #[cfg(feature = "esp32c3")] 21 | use esp32c3_hal as hal; 22 | #[cfg(feature = "esp32s2")] 23 | use esp32s2_hal as hal; 24 | #[cfg(feature = "esp32s3")] 25 | use esp32s3_hal as hal; 26 | 27 | use hal::{ 28 | clock::{ClockControl, CpuClock}, 29 | // gdma::Gdma, 30 | i2c, 31 | peripherals::Peripherals, 32 | prelude::*, 33 | spi, 34 | timer::TimerGroup, 35 | Delay, 36 | Rng, 37 | Rtc, 38 | IO, 39 | }; 40 | 41 | // systimer was introduced in ESP32-S2, it's not available for ESP32 42 | #[cfg(feature = "system_timer")] 43 | use hal::systimer::SystemTimer; 44 | 45 | use mipidsi::{ Orientation }; 46 | 47 | // use panic_halt as _; 48 | use esp_backtrace as _; 49 | 50 | #[cfg(feature = "riscv-rt")] 51 | use riscv_rt::entry; 52 | #[cfg(feature = "xtensa-lx-rt")] 53 | use xtensa_lx_rt::entry; 54 | 55 | use embedded_graphics::pixelcolor::Rgb565; 56 | // use esp32s2_hal::Rng; 57 | 58 | use {{crate_name}}_engine::{engine::Engine, spritebuf::SpriteBuf}; 59 | 60 | #[cfg(any(feature = "imu_controls"))] 61 | use icm42670::{accelerometer::Accelerometer, Address, Icm42670}; 62 | #[cfg(any(feature = "imu_controls"))] 63 | use shared_bus::BusManagerSimple; 64 | 65 | use embedded_graphics_framebuf::FrameBuf; 66 | use embedded_hal::digital::v2::OutputPin; 67 | 68 | pub struct Universe { 69 | pub engine: Engine, 70 | // #[cfg(any(feature = "imu_controls"))] 71 | icm: I, 72 | // icm: Option>>>> 73 | // delay: Some(Delay), 74 | } 75 | 76 | impl> 77 | Universe 78 | { 79 | pub fn new(icm: I, seed: Option<[u8; 32]>, engine: Engine) -> Universe { 80 | Universe { 81 | engine, 82 | // #[cfg(any(feature = "imu_controls"))] 83 | icm, 84 | // delay: None, 85 | } 86 | } 87 | 88 | pub fn initialize(&mut self) { 89 | self.engine.initialize(); 90 | } 91 | 92 | pub fn render_frame(&mut self) -> &D { 93 | #[cfg(any(feature = "imu_controls"))] 94 | { 95 | let accel_threshold = 0.20; 96 | let accel_norm = self.icm.accel_norm().unwrap(); 97 | 98 | if accel_norm.y > accel_threshold { 99 | self.engine.move_left(); 100 | } 101 | 102 | if accel_norm.y < -accel_threshold { 103 | self.engine.move_right(); 104 | } 105 | 106 | if accel_norm.x > accel_threshold { 107 | self.engine.move_down(); 108 | } 109 | 110 | if accel_norm.x < -accel_threshold { 111 | self.engine.move_up(); 112 | } 113 | 114 | // if accel_norm.z < -1.2 { 115 | // } else if accel_norm.z > 1.5 { 116 | // } 117 | } 118 | 119 | self.engine.tick(); 120 | self.engine.draw() 121 | } 122 | } 123 | 124 | #[entry] 125 | fn main() -> ! { 126 | let peripherals = Peripherals::take(); 127 | 128 | #[cfg(any(feature = "esp32"))] 129 | let mut system = peripherals.DPORT.split(); 130 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 131 | let mut system = peripherals.SYSTEM.split(); 132 | let mut clocks = ClockControl::configure(system.clock_control, CpuClock::Clock160MHz).freeze(); 133 | 134 | // Disable the RTC and TIMG watchdog timers 135 | let mut rtc = Rtc::new(peripherals.RTC_CNTL); 136 | let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); 137 | let mut wdt0 = timer_group0.wdt; 138 | let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); 139 | let mut wdt1 = timer_group1.wdt; 140 | 141 | #[cfg(feature = "esp32c3")] 142 | rtc.swd.disable(); 143 | #[cfg(feature = "xtensa-lx-rt")] 144 | rtc.rwdt.disable(); 145 | 146 | wdt0.disable(); 147 | wdt1.disable(); 148 | 149 | let mut delay = Delay::new(&clocks); 150 | // self.delay = Some(delay); 151 | 152 | println!("About to initialize the SPI LED driver"); 153 | let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); 154 | // let mut backlight = io.pins.gpio0.into_push_pull_output(); 155 | 156 | 157 | #[cfg(feature = "esp32")] 158 | backlight.set_low().unwrap(); 159 | // #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 160 | // backlight.set_high().unwrap(); 161 | 162 | #[cfg(feature = "esp32c3")] 163 | let spi = spi::Spi::new( 164 | peripherals.SPI2, 165 | io.pins.gpio6, 166 | io.pins.gpio7, 167 | io.pins.gpio0, 168 | io.pins.gpio20, 169 | 60u32.MHz(), 170 | spi::SpiMode::Mode0, 171 | &mut system.peripheral_clock_control, 172 | &mut clocks, 173 | ); 174 | 175 | let reset = io.pins.gpio3.into_push_pull_output(); 176 | 177 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio21.into_push_pull_output()); 178 | 179 | #[cfg(any( 180 | feature = "esp32s2_ili9341", 181 | feature = "esp32_wrover_kit", 182 | feature = "esp32c3_ili9341" 183 | ))] 184 | let mut delay = Delay::new(&clocks); 185 | 186 | // #[cfg(any(feature = "esp32s3_box"))] 187 | let mut display = mipidsi::Builder::ili9341_rgb565(di) 188 | .with_display_size(240 as u16, 320 as u16) 189 | // .with_framebuffer_size(240 as u16, 320 as u16) 190 | .with_orientation(mipidsi::Orientation::Landscape(true)) 191 | .with_color_order(mipidsi::ColorOrder::Rgb) 192 | .init(&mut delay, Some(reset)) 193 | .unwrap(); 194 | 195 | // let mut display = mipidsi::Builder::ili9341_rgb565(di) 196 | // .with_display_size(240, 240) 197 | // // .with_orientation(mipidsi::Orientation::PortraitInverted(false)) 198 | // // .with_color_order(mipidsi::ColorOrder::Rgb) 199 | // .init(&mut delay, Some(reset)) 200 | // .unwrap(); 201 | println!("Initialzied"); 202 | // let mut display = mipidsi::Display::ili9342c_rgb565(di, core::prelude::v1::Some(reset), display_options); 203 | // #[cfg(any( 204 | // feature = "esp32s2_ili9341", 205 | // feature = "esp32_wrover_kit", 206 | // feature = "esp32c3_ili9341" 207 | // ))] 208 | // let mut display = Ili9341::new( 209 | // di, 210 | // reset, 211 | // &mut delay, 212 | // Orientation::Portrait, 213 | // DisplaySize240x320, 214 | // ) 215 | // .unwrap(); 216 | 217 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 218 | display 219 | .init( 220 | &mut delay, 221 | DisplayOptions { 222 | ..DisplayOptions::default() 223 | }, 224 | ) 225 | .unwrap(); 226 | 227 | // display.clear(RgbColor::WHITE).unwrap(); 228 | 229 | Text::new( 230 | "Initializing...", 231 | Point::new(80, 110), 232 | MonoTextStyle::new(&FONT_8X13, RgbColor::BLACK), 233 | ) 234 | .draw(&mut display) 235 | .unwrap(); 236 | 237 | #[cfg(any(feature = "imu_controls"))] 238 | println!("Initializing IMU"); 239 | #[cfg(any(feature = "imu_controls"))] 240 | let sda = io.pins.gpio10; 241 | #[cfg(any(feature = "imu_controls"))] 242 | let scl = io.pins.gpio8; 243 | 244 | #[cfg(any(feature = "imu_controls"))] 245 | let i2c = i2c::I2C::new( 246 | peripherals.I2C0, 247 | sda, 248 | scl, 249 | 100u32.kHz(), 250 | &mut system.peripheral_clock_control, 251 | &clocks, 252 | ); 253 | 254 | #[cfg(any(feature = "imu_controls"))] 255 | let bus = BusManagerSimple::new(i2c); 256 | #[cfg(any(feature = "imu_controls"))] 257 | let icm = Icm42670::new(bus.acquire_i2c(), Address::Primary).unwrap(); 258 | 259 | let mut rng = Rng::new(peripherals.RNG); 260 | let mut seed_buffer = [0u8; 32]; 261 | rng.read(&mut seed_buffer).unwrap(); 262 | let mut data = [Rgb565::BLACK; 320 * 240]; 263 | let fbuf = FrameBuf::new(&mut data, 320, 240); 264 | let spritebuf = SpriteBuf::new(fbuf); 265 | let engine = Engine::new(spritebuf, Some(seed_buffer)); 266 | 267 | let mut universe = Universe::new(icm, Some(seed_buffer), engine); 268 | universe.initialize(); 269 | 270 | // #[cfg(any(feature = "imu_controls"))] 271 | // let accel_threshold = 0.20; 272 | 273 | loop { 274 | display 275 | .draw_iter(universe.render_frame().into_iter()) 276 | .unwrap(); 277 | // delay.delay_ms(300u32); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /esp32-c6-devkit/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.riscv32imac-unknown-none-elf] 2 | runner = "espflash flash --monitor" 3 | rustflags = [ 4 | "-C", "link-arg=-Tlinkall.x", 5 | ] 6 | 7 | [build] 8 | target = "riscv32imac-unknown-none-elf" 9 | 10 | [unstable] 11 | build-std = [ "core", "alloc" ] 12 | -------------------------------------------------------------------------------- /esp32-c6-devkit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}_esp32c6" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [target.riscv32imac-unknown-none-elf.dependencies] 9 | esp32c6-hal = { version = "*", git = "https://github.com/jessebraham/esp-hal.git", branch = "feature/esp32c6" } 10 | esp-backtrace = { version = "0.6.0", features = [ 11 | "esp32c3", 12 | "panic-handler", 13 | "print-uart", 14 | ] } 15 | esp-println = { version = "0.4.0", features = [ "esp32c3" ] } 16 | 17 | [dependencies] 18 | esp-alloc = "0.2.0" 19 | embedded-graphics = "0.7" 20 | embedded-hal = "0.2" 21 | display-interface = "0.4" 22 | display-interface-spi = "0.4" 23 | icm42670 = { git = "https://github.com/jessebraham/icm42670/" } 24 | mipidsi = { git = "https://github.com/almindor/mipidsi.git" } 25 | panic-halt = "0.2" 26 | rand = { version = "0.8.5", default-features = false } 27 | rand_chacha = { version = "0.3.1", default-features = false } 28 | shared-bus = { version = "0.2.4" } 29 | {{crate_name}}-engine = { path = "../engine", default-features = false, features = [] } 30 | heapless = { version = "0.7.14", default-features = false } 31 | embedded-graphics-framebuf = "0.2.0" 32 | 33 | [features] 34 | default = [ "esp32c6_ili9341" ] 35 | 36 | system_timer = [] 37 | 38 | button_controls = [] 39 | imu_controls = [] 40 | 41 | esp32 = [] 42 | esp32s2 = ["system_timer"] 43 | esp32s3 = [] 44 | esp32c6 = ["system_timer"] 45 | 46 | esp32c6_ili9341 = [ "esp32c6", "esp32c6-hal/eh1" ] 47 | -------------------------------------------------------------------------------- /esp32-c6-devkit/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | components = ["rustfmt", "rustc-dev"] 4 | -------------------------------------------------------------------------------- /esp32-c6-devkit/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(default_alloc_error_handler)] 4 | 5 | #[global_allocator] 6 | static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); 7 | 8 | use display_interface_spi::SPIInterfaceNoCS; 9 | use embedded_graphics::{ 10 | mono_font::{ascii::FONT_8X13, MonoTextStyle}, 11 | prelude::{DrawTarget, Point, RgbColor}, 12 | text::Text, 13 | Drawable, 14 | }; 15 | 16 | use esp_println::println; 17 | 18 | use esp32c6_hal as hal; 19 | 20 | use hal::{ 21 | clock::{ClockControl, CpuClock}, 22 | // gdma::Gdma, 23 | i2c, 24 | peripherals::Peripherals, 25 | prelude::*, 26 | spi, 27 | timer::TimerGroup, 28 | Delay, 29 | // Rng, 30 | Rtc, 31 | IO, 32 | }; 33 | 34 | // systimer was introduced in ESP32-S2, it's not available for ESP32 35 | #[cfg(feature = "system_timer")] 36 | use hal::systimer::SystemTimer; 37 | 38 | use mipidsi::{ Orientation }; 39 | 40 | // use panic_halt as _; 41 | use esp_backtrace as _; 42 | 43 | use embedded_graphics::pixelcolor::Rgb565; 44 | 45 | use {{crate_name}}_engine::{engine::Engine, spritebuf::SpriteBuf}; 46 | 47 | #[cfg(any(feature = "imu_controls"))] 48 | use icm42670::{accelerometer::Accelerometer, Address, Icm42670}; 49 | #[cfg(any(feature = "imu_controls"))] 50 | use shared_bus::BusManagerSimple; 51 | 52 | use embedded_graphics_framebuf::FrameBuf; 53 | use embedded_hal::digital::v2::OutputPin; 54 | 55 | pub struct Universe { 56 | pub engine: Engine, 57 | } 58 | 59 | impl> 60 | Universe 61 | { 62 | pub fn new(seed: Option<[u8; 32]>, engine: Engine) -> Universe { 63 | Universe { 64 | engine, 65 | } 66 | } 67 | 68 | pub fn initialize(&mut self) { 69 | self.engine.initialize(); 70 | } 71 | 72 | pub fn render_frame(&mut self) -> &D { 73 | #[cfg(any(feature = "imu_controls"))] 74 | { 75 | let accel_threshold = 0.20; 76 | let accel_norm = self.icm.accel_norm().unwrap(); 77 | 78 | if accel_norm.y > accel_threshold { 79 | self.engine.move_left(); 80 | } 81 | 82 | if accel_norm.y < -accel_threshold { 83 | self.engine.move_right(); 84 | } 85 | 86 | if accel_norm.x > accel_threshold { 87 | self.engine.move_down(); 88 | } 89 | 90 | if accel_norm.x < -accel_threshold { 91 | self.engine.move_up(); 92 | } 93 | 94 | // if accel_norm.z < -1.2 { 95 | // } else if accel_norm.z > 1.5 { 96 | // } 97 | } 98 | 99 | self.engine.tick(); 100 | self.engine.draw() 101 | } 102 | } 103 | 104 | #[entry] 105 | fn main() -> ! { 106 | let peripherals = Peripherals::take(); 107 | 108 | let mut system = peripherals.PCR.split(); 109 | let mut clocks = ClockControl::configure(system.clock_control, CpuClock::Clock160MHz).freeze(); 110 | 111 | // Disable the RTC and TIMG watchdog timers 112 | let mut rtc = Rtc::new(peripherals.LP_CLKRST); 113 | let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); 114 | let mut wdt0 = timer_group0.wdt; 115 | let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); 116 | let mut wdt1 = timer_group1.wdt; 117 | 118 | rtc.swd.disable(); 119 | rtc.rwdt.disable(); 120 | 121 | wdt0.disable(); 122 | wdt1.disable(); 123 | 124 | let mut delay = Delay::new(&clocks); 125 | // self.delay = Some(delay); 126 | 127 | println!("About to initialize the SPI LED driver"); 128 | let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); 129 | // let mut backlight = io.pins.gpio0.into_push_pull_output(); 130 | 131 | let spi = spi::Spi::new( 132 | peripherals.SPI2, 133 | io.pins.gpio6, // SCLK 134 | io.pins.gpio7, // MOSO 135 | io.pins.gpio0, // MISO 136 | io.pins.gpio20, // CS 137 | 60u32.MHz(), 138 | spi::SpiMode::Mode0, 139 | &mut system.peripheral_clock_control, 140 | &mut clocks, 141 | ); 142 | 143 | let reset = io.pins.gpio3.into_push_pull_output(); 144 | 145 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio21.into_push_pull_output()); 146 | 147 | let mut delay = Delay::new(&clocks); 148 | 149 | let mut display = mipidsi::Builder::ili9341_rgb565(di) 150 | .with_display_size(240 as u16, 320 as u16) 151 | // .with_framebuffer_size(240 as u16, 320 as u16) 152 | .with_orientation(mipidsi::Orientation::Landscape(true)) 153 | .with_color_order(mipidsi::ColorOrder::Rgb) 154 | .init(&mut delay, Some(reset)) 155 | .unwrap(); 156 | 157 | // let mut display = mipidsi::Builder::ili9341_rgb565(di) 158 | // .with_display_size(240, 240) 159 | // // .with_orientation(mipidsi::Orientation::PortraitInverted(false)) 160 | // // .with_color_order(mipidsi::ColorOrder::Rgb) 161 | // .init(&mut delay, Some(reset)) 162 | // .unwrap(); 163 | println!("Initialzied"); 164 | // let mut display = mipidsi::Display::ili9342c_rgb565(di, core::prelude::v1::Some(reset), display_options); 165 | // #[cfg(any( 166 | // feature = "esp32s2_ili9341", 167 | // feature = "esp32_wrover_kit", 168 | // feature = "esp32c3_ili9341" 169 | // ))] 170 | // let mut display = Ili9341::new( 171 | // di, 172 | // reset, 173 | // &mut delay, 174 | // Orientation::Portrait, 175 | // DisplaySize240x320, 176 | // ) 177 | // .unwrap(); 178 | 179 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 180 | display 181 | .init( 182 | &mut delay, 183 | DisplayOptions { 184 | ..DisplayOptions::default() 185 | }, 186 | ) 187 | .unwrap(); 188 | 189 | // display.clear(RgbColor::WHITE).unwrap(); 190 | 191 | Text::new( 192 | "Initializing...", 193 | Point::new(80, 110), 194 | MonoTextStyle::new(&FONT_8X13, RgbColor::BLACK), 195 | ) 196 | .draw(&mut display) 197 | .unwrap(); 198 | 199 | #[cfg(any(feature = "imu_controls"))] 200 | println!("Initializing IMU"); 201 | #[cfg(any(feature = "imu_controls"))] 202 | let sda = io.pins.gpio10; 203 | #[cfg(any(feature = "imu_controls"))] 204 | let scl = io.pins.gpio8; 205 | 206 | #[cfg(any(feature = "imu_controls"))] 207 | let i2c = i2c::I2C::new( 208 | peripherals.I2C0, 209 | sda, 210 | scl, 211 | 100u32.kHz(), 212 | &mut system.peripheral_clock_control, 213 | &clocks, 214 | ); 215 | 216 | #[cfg(any(feature = "imu_controls"))] 217 | let bus = BusManagerSimple::new(i2c); 218 | #[cfg(any(feature = "imu_controls"))] 219 | let icm = Icm42670::new(bus.acquire_i2c(), Address::Primary).unwrap(); 220 | 221 | // let mut rng = Rng::new(peripherals.RNG); 222 | let mut seed_buffer = [0u8; 32]; 223 | // rng.read(&mut seed_buffer).unwrap(); 224 | let mut data = [Rgb565::BLACK; 320 * 240]; 225 | let fbuf = FrameBuf::new(&mut data, 320, 240); 226 | let spritebuf = SpriteBuf::new(fbuf); 227 | let engine = Engine::new(spritebuf, Some(seed_buffer)); 228 | 229 | let mut universe = Universe::new(Some(seed_buffer), engine); 230 | universe.initialize(); 231 | 232 | 233 | loop { 234 | display 235 | .draw_iter(universe.render_frame().into_iter()) 236 | .unwrap(); 237 | // delay.delay_ms(300u32); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /esp32-s2-kaluga/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_arch = "xtensa")'] 2 | runner = "espflash flash --monitor" 3 | rustflags = [ 4 | # Tell the `core` library that we have atomics, even though it's not 5 | # specified in the target definition 6 | "--cfg", 'target_has_atomic="8"', 7 | "--cfg", 'target_has_atomic="16"', 8 | "--cfg", 'target_has_atomic="32"', 9 | "--cfg", 'target_has_atomic="ptr"', 10 | 11 | "-C", "link-arg=-nostartfiles", 12 | # Enable the atomic codegen option for Xtensa 13 | "-C", "target-feature=+s32c1i", 14 | "-C", "link-arg=-Wl,-Tlinkall.x", 15 | "-C", "force-frame-pointers" 16 | ] 17 | 18 | [build] 19 | # Uncomment the target if you'd like to use automatic code hinting in your IDE 20 | # target = "xtensa-esp32-none-elf" 21 | target = "xtensa-esp32s2-none-elf" 22 | #target = "xtensa-esp32s3-none-elf" 23 | # target = "riscv32imac-unknown-none-elf" 24 | 25 | [unstable] 26 | build-std = [ "core", "alloc" ] 27 | -------------------------------------------------------------------------------- /esp32-s2-kaluga/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-s2-kaluga" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [target.xtensa-esp32s2-none-elf.dependencies] 9 | xtensa-atomic-emulation-trap = "0.4.0" 10 | esp32s2-hal = "0.7.0" 11 | esp-backtrace = { version = "0.6.0", features = [ 12 | "esp32s2", 13 | "panic-handler", 14 | "print-uart", 15 | ] } 16 | xtensa-lx-rt = { version = "0.15.0", features = ["esp32s2"], optional = true } 17 | esp-println = { version = "0.4.0", features = ["esp32s2"] } 18 | 19 | [dependencies] 20 | esp-alloc = "0.2.0" 21 | embedded-graphics = "0.7" 22 | embedded-hal = "0.2" 23 | display-interface = "0.4" 24 | display-interface-spi = "0.4" 25 | mipidsi = { git = "https://github.com/rfuest/mipidsi.git", branch = "display-driver-hal" } 26 | panic-halt = "0.2" 27 | rand = { version = "0.8.5", default-features = false } 28 | rand_chacha = { version = "0.3.1", default-features = false } 29 | shared-bus = { version = "0.2.4" } 30 | {{crate_name}}-engine = { path = "../engine", default-features = false, features = [] } 31 | heapless = { version = "0.7.14", default-features = false } 32 | embedded-graphics-framebuf = "0.2.0" 33 | 34 | [features] 35 | default = ["rt", "esp32s2_kaluga" ] 36 | rt = ["xtensa-lx-rt"] 37 | 38 | system_timer = [] 39 | 40 | button_controls = [] 41 | imu_controls = [] 42 | 43 | static_maze = [] 44 | 45 | esp32 = [] 46 | esp32s2 = ["system_timer"] 47 | esp32s3 = [] 48 | esp32c3 = ["system_timer"] 49 | 50 | esp32s2_kaluga = ["xtensa-lx-rt", "esp32s2" ] 51 | -------------------------------------------------------------------------------- /esp32-s2-kaluga/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | components = ["rustfmt", "rustc-dev"] 4 | #targets = ["xtensa-esp32s3-none-elf"] 5 | targets = ["xtensa-esp32s2-none-elf"] 6 | # targets = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf","xtensa-esp32s3-none-elf"] 7 | -------------------------------------------------------------------------------- /esp32-s2-kaluga/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(default_alloc_error_handler)] 4 | 5 | // Main baord: https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-esp32-s2-kaluga-1-kit.html 6 | // Buttons - Lyra extension board: https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-esp-lyrat-8311a_v1.3.html 7 | 8 | use display_interface_spi::SPIInterfaceNoCS; 9 | use embedded_graphics::{ 10 | prelude::{Point, DrawTarget, RgbColor}, 11 | mono_font::{ 12 | ascii::{FONT_8X13}, 13 | MonoTextStyle, 14 | }, 15 | text::Text, 16 | Drawable, 17 | }; 18 | 19 | use mipidsi::hal::{ Orientation, Rotation }; 20 | use mipidsi::ColorOrder; 21 | 22 | use esp_println::println; 23 | 24 | #[cfg(feature="esp32")] 25 | use esp32_hal as hal; 26 | #[cfg(feature="esp32s2")] 27 | use esp32s2_hal as hal; 28 | #[cfg(feature="esp32s3")] 29 | use esp32s3_hal as hal; 30 | #[cfg(feature="esp32c3")] 31 | use esp32c3_hal as hal; 32 | 33 | use hal::{ 34 | clock::{ ClockControl, CpuClock }, 35 | // gdma::Gdma, 36 | i2c, 37 | peripherals::Peripherals, 38 | prelude::*, 39 | spi, 40 | timer::TimerGroup, 41 | Rng, 42 | Rtc, 43 | IO, 44 | Delay, 45 | adc::{AdcConfig, Attenuation, ADC, ADC1}, 46 | }; 47 | 48 | // systimer was introduced in ESP32-S2, it's not available for ESP32 49 | #[cfg(feature="system_timer")] 50 | use hal::systimer::{SystemTimer}; 51 | 52 | // use panic_halt as _; 53 | use esp_backtrace as _; 54 | 55 | #[cfg(feature="xtensa-lx-rt")] 56 | use xtensa_lx_rt::entry; 57 | #[cfg(feature="riscv-rt")] 58 | use riscv_rt::entry; 59 | 60 | use embedded_graphics::{pixelcolor::Rgb565}; 61 | // use esp32s2_hal::Rng; 62 | 63 | #[cfg(any(feature = "esp32s2_ili9341", feature = "esp32_wrover_kit", feature = "esp32c3_ili9341"))] 64 | use ili9341::{DisplaySize240x320, Ili9341, Orientation}; 65 | 66 | use {{crate_name}}_engine::{spritebuf::SpriteBuf, engine::Engine}; 67 | 68 | use embedded_hal::digital::v2::OutputPin; 69 | use embedded_graphics_framebuf::{FrameBuf}; 70 | 71 | pub struct Universe { 72 | pub engine: Engine, 73 | } 74 | 75 | 76 | impl > Universe { 77 | pub fn new(seed: Option<[u8; 32]>, engine:Engine) -> Universe { 78 | Universe { 79 | engine, 80 | } 81 | } 82 | 83 | pub fn initialize(&mut self) { 84 | self.engine.initialize(); 85 | } 86 | 87 | pub fn move_up(&mut self) { 88 | self.engine.move_up(); 89 | } 90 | 91 | pub fn move_down(&mut self) { 92 | self.engine.move_down(); 93 | } 94 | 95 | pub fn move_left(&mut self) { 96 | self.engine.move_left(); 97 | } 98 | 99 | pub fn move_right(&mut self) { 100 | self.engine.move_right(); 101 | } 102 | 103 | pub fn render_frame(&mut self) -> &D { 104 | 105 | self.engine.tick(); 106 | self.engine.draw() 107 | 108 | } 109 | 110 | } 111 | 112 | 113 | #[entry] 114 | fn main() -> ! { 115 | let peripherals = Peripherals::take(); 116 | 117 | #[cfg(any(feature = "esp32"))] 118 | let mut system = peripherals.DPORT.split(); 119 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 120 | let mut system = peripherals.SYSTEM.split(); 121 | let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze(); 122 | 123 | // Disable the RTC and TIMG watchdog timers 124 | let mut rtc = Rtc::new(peripherals.RTC_CNTL); 125 | let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); 126 | let mut wdt0 = timer_group0.wdt; 127 | let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); 128 | let mut wdt1 = timer_group1.wdt; 129 | 130 | #[cfg(feature="esp32c3")] 131 | rtc.swd.disable(); 132 | #[cfg(feature="xtensa-lx-rt")] 133 | rtc.rwdt.disable(); 134 | 135 | wdt0.disable(); 136 | wdt1.disable(); 137 | 138 | let mut delay = Delay::new(&clocks); 139 | // self.delay = Some(delay); 140 | 141 | println!("About to initialize the SPI LED driver"); 142 | let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); 143 | 144 | // Create ADC instances 145 | let analog = peripherals.SENS.split(); 146 | 147 | let mut adc1_config = AdcConfig::new(); 148 | 149 | let mut button_ladder_pin = 150 | adc1_config.enable_pin(io.pins.gpio6.into_analog(), Attenuation::Attenuation11dB); 151 | 152 | let mut adc1 = ADC::::adc(analog.adc1, adc1_config).unwrap(); 153 | 154 | // Backlight is on GPIO6 in version 1.2, version 1.3 has display always on 155 | // let mut backlight = io.pins.gpio6.into_push_pull_output(); 156 | // backlight.set_high().unwrap(); 157 | 158 | let spi = spi::Spi::new( 159 | peripherals.SPI2, 160 | io.pins.gpio15, // SCLK 161 | io.pins.gpio9, // MOSI 162 | io.pins.gpio8, // MISO 163 | io.pins.gpio11, // CS 164 | 60u32.MHz(), 165 | spi::SpiMode::Mode0, 166 | &mut system.peripheral_clock_control, 167 | &clocks); 168 | 169 | let reset = io.pins.gpio16.into_push_pull_output(); 170 | 171 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio13.into_push_pull_output()); 172 | 173 | #[cfg(any(feature = "esp32s2_ili9341", feature = "esp32_wrover_kit", feature = "esp32c3_ili9341"))] 174 | let mut delay = Delay::new(&clocks); 175 | 176 | let mut display = mipidsi::Builder::ili9341_rgb565(di) 177 | .with_display_size(320, 240) 178 | .with_color_order(ColorOrder::Bgr) 179 | .with_orientation(Orientation::new().rotate(Rotation::Deg90)) 180 | .init(&mut delay, Some(reset)) 181 | .unwrap(); 182 | 183 | Text::new( 184 | "Initializing...", 185 | Point::new(80, 110), 186 | MonoTextStyle::new(&FONT_8X13, RgbColor::BLACK), 187 | ) 188 | .draw(&mut display) 189 | .unwrap(); 190 | 191 | 192 | let mut rng = Rng::new(peripherals.RNG); 193 | let mut seed_buffer = [0u8;32]; 194 | rng.read(&mut seed_buffer).unwrap(); 195 | let mut data = [Rgb565::BLACK ; 320*240]; 196 | let fbuf = FrameBuf::new(&mut data, 320, 240); 197 | let spritebuf = SpriteBuf::new(fbuf); 198 | let engine = Engine::new(spritebuf, Some(seed_buffer)); 199 | 200 | let mut universe = Universe::new(Some(seed_buffer), engine); 201 | universe.initialize(); 202 | 203 | loop { 204 | 205 | let button_value: u16 = nb::block!(adc1.read(&mut button_ladder_pin)).unwrap(); 206 | // Based on https://github.com/espressif/esp-bsp/blob/master/esp32_s2_kaluga_kit/include/bsp/esp32_s2_kaluga_kit.h#L299 207 | if button_value > 4000 && button_value < 5000 { 208 | universe.engine.move_right(); 209 | } else if button_value >= 5000 && button_value < 6000 { 210 | universe.engine.move_left(); 211 | } else if button_value >= 6000 && button_value < 7000 { 212 | universe.engine.move_down(); 213 | } else if button_value >= 7000 && button_value < 8180 { 214 | universe.engine.move_up(); 215 | } 216 | 217 | display.draw_iter(universe.render_frame().into_iter()).unwrap(); 218 | // delay.delay_ms(300u32); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /esp32-s2-usb-otg/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_arch = "xtensa")'] 2 | runner = "espflash flash --monitor" 3 | rustflags = [ 4 | # Tell the `core` library that we have atomics, even though it's not 5 | # specified in the target definition 6 | "--cfg", 'target_has_atomic="8"', 7 | "--cfg", 'target_has_atomic="16"', 8 | "--cfg", 'target_has_atomic="32"', 9 | "--cfg", 'target_has_atomic="ptr"', 10 | 11 | "-C", "link-arg=-nostartfiles", 12 | # Enable the atomic codegen option for Xtensa 13 | "-C", "target-feature=+s32c1i", 14 | "-C", "link-arg=-Wl,-Tlinkall.x", 15 | "-C", "force-frame-pointers" 16 | ] 17 | 18 | [build] 19 | # Uncomment the target if you'd like to use automatic code hinting in your IDE 20 | # target = "xtensa-esp32-none-elf" 21 | target = "xtensa-esp32s2-none-elf" 22 | #target = "xtensa-esp32s3-none-elf" 23 | # target = "riscv32imac-unknown-none-elf" 24 | 25 | [unstable] 26 | build-std = [ "core", "alloc" ] 27 | -------------------------------------------------------------------------------- /esp32-s2-usb-otg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-s2-otg" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [target.xtensa-esp32s2-none-elf.dependencies] 9 | xtensa-atomic-emulation-trap = "0.4.0" 10 | esp32s2-hal = "0.7.0" 11 | esp-backtrace = { version = "0.6.0", features = [ 12 | "esp32s2", 13 | "panic-handler", 14 | "print-uart", 15 | ] } 16 | xtensa-lx-rt = { version = "0.15.0", features = ["esp32s2"], optional = true } 17 | esp-println = { version = "0.4.0", features = ["esp32s2"] } 18 | 19 | [dependencies] 20 | esp-alloc = "0.2.0" 21 | embedded-graphics = "0.7" 22 | embedded-hal = "0.2" 23 | display-interface = "0.4" 24 | display-interface-spi = "0.4" 25 | mipidsi = "0.6.0" 26 | panic-halt = "0.2" 27 | rand = { version = "0.8.5", default-features = false } 28 | rand_chacha = { version = "0.3.1", default-features = false } 29 | shared-bus = { version = "0.2.4" } 30 | {{crate_name}}-engine = { path = "../engine", default-features = false, features = [ ] } 31 | heapless = { version = "0.7.14", default-features = false } 32 | embedded-graphics-framebuf = "0.2.0" 33 | 34 | [features] 35 | default = ["rt", "esp32s2_usb_otg" ] 36 | rt = ["xtensa-lx-rt"] 37 | 38 | system_timer = [] 39 | 40 | button_controls = [] 41 | imu_controls = [] 42 | 43 | static_maze = [] 44 | 45 | esp32 = [] 46 | esp32s2 = ["system_timer"] 47 | esp32s3 = [] 48 | esp32c3 = ["system_timer"] 49 | 50 | # Enable this feature in case you have an ESP32-S3-USB-OTG board with ST7789 51 | esp32s2_usb_otg = ["xtensa-lx-rt", "esp32s2", "button_controls"] 52 | 53 | -------------------------------------------------------------------------------- /esp32-s2-usb-otg/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | components = ["rustfmt", "rustc-dev"] 4 | #targets = ["xtensa-esp32s3-none-elf"] 5 | targets = ["xtensa-esp32s2-none-elf"] 6 | # targets = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf","xtensa-esp32s3-none-elf"] 7 | -------------------------------------------------------------------------------- /esp32-s2-usb-otg/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(default_alloc_error_handler)] 4 | 5 | use display_interface_spi::SPIInterfaceNoCS; 6 | use embedded_graphics::{ 7 | prelude::{Point, DrawTarget, RgbColor}, 8 | mono_font::{ 9 | ascii::{FONT_8X13}, 10 | MonoTextStyle, 11 | }, 12 | text::Text, 13 | Drawable, 14 | }; 15 | 16 | use esp_println::println; 17 | 18 | #[cfg(feature="esp32")] 19 | use esp32_hal as hal; 20 | #[cfg(feature="esp32s2")] 21 | use esp32s2_hal as hal; 22 | #[cfg(feature="esp32s3")] 23 | use esp32s3_hal as hal; 24 | #[cfg(feature="esp32c3")] 25 | use esp32c3_hal as hal; 26 | 27 | use hal::{ 28 | clock::{ ClockControl, CpuClock }, 29 | // gdma::Gdma, 30 | i2c, 31 | peripherals::Peripherals, 32 | prelude::*, 33 | spi, 34 | timer::TimerGroup, 35 | Rng, 36 | Rtc, 37 | IO, 38 | Delay, 39 | }; 40 | 41 | // systimer was introduced in ESP32-S2, it's not available for ESP32 42 | #[cfg(feature="system_timer")] 43 | use hal::systimer::{SystemTimer}; 44 | 45 | // use panic_halt as _; 46 | use esp_backtrace as _; 47 | 48 | #[cfg(feature="xtensa-lx-rt")] 49 | use xtensa_lx_rt::entry; 50 | #[cfg(feature="riscv-rt")] 51 | use riscv_rt::entry; 52 | 53 | use embedded_graphics::{pixelcolor::Rgb565}; 54 | // use esp32s2_hal::Rng; 55 | 56 | #[cfg(any(feature = "esp32s2_ili9341", feature = "esp32_wrover_kit", feature = "esp32c3_ili9341"))] 57 | use ili9341::{DisplaySize240x320, Ili9341, Orientation}; 58 | 59 | use {{crate_name}}_engine::{spritebuf::SpriteBuf, engine::Engine}; 60 | 61 | use embedded_hal::digital::v2::OutputPin; 62 | use embedded_graphics_framebuf::{FrameBuf}; 63 | 64 | pub struct Universe { 65 | pub engine: Engine, 66 | } 67 | 68 | 69 | impl > Universe { 70 | pub fn new(seed: Option<[u8; 32]>, engine:Engine) -> Universe { 71 | Universe { 72 | engine, 73 | } 74 | } 75 | 76 | pub fn initialize(&mut self) { 77 | self.engine.initialize(); 78 | } 79 | 80 | pub fn move_up(&mut self) { 81 | self.engine.move_up(); 82 | } 83 | 84 | pub fn move_down(&mut self) { 85 | self.engine.move_down(); 86 | } 87 | 88 | pub fn move_left(&mut self) { 89 | self.engine.move_left(); 90 | } 91 | 92 | pub fn move_right(&mut self) { 93 | self.engine.move_right(); 94 | } 95 | 96 | pub fn render_frame(&mut self) -> &D { 97 | 98 | self.engine.tick(); 99 | self.engine.draw() 100 | 101 | } 102 | 103 | } 104 | 105 | 106 | #[entry] 107 | fn main() -> ! { 108 | let peripherals = Peripherals::take(); 109 | 110 | #[cfg(any(feature = "esp32"))] 111 | let mut system = peripherals.DPORT.split(); 112 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 113 | let mut system = peripherals.SYSTEM.split(); 114 | let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze(); 115 | 116 | // Disable the RTC and TIMG watchdog timers 117 | let mut rtc = Rtc::new(peripherals.RTC_CNTL); 118 | let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); 119 | let mut wdt0 = timer_group0.wdt; 120 | let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); 121 | let mut wdt1 = timer_group1.wdt; 122 | 123 | #[cfg(feature="esp32c3")] 124 | rtc.swd.disable(); 125 | #[cfg(feature="xtensa-lx-rt")] 126 | rtc.rwdt.disable(); 127 | 128 | wdt0.disable(); 129 | wdt1.disable(); 130 | 131 | let mut delay = Delay::new(&clocks); 132 | // self.delay = Some(delay); 133 | 134 | println!("About to initialize the SPI LED driver"); 135 | let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); 136 | 137 | // https://espressif-docs.readthedocs-hosted.com/projects/espressif-esp-dev-kits/en/latest/esp32s3/esp32-s3-usb-otg/user_guide.html 138 | // let button_up = button::Button::new(); 139 | 140 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 141 | let button_ok_pin = io.pins.gpio0.into_pull_up_input(); 142 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 143 | let button_menu_pin = io.pins.gpio14.into_pull_up_input(); 144 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 145 | let button_up_pin = io.pins.gpio10.into_pull_up_input(); 146 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 147 | let button_down_pin = io.pins.gpio11.into_pull_up_input(); 148 | 149 | #[cfg(feature = "esp32")] 150 | let mut backlight = io.pins.gpio5.into_push_pull_output(); 151 | #[cfg(any(feature = "esp32s2", feature = "esp32s3_usb_otg"))] 152 | let mut backlight = io.pins.gpio9.into_push_pull_output(); 153 | #[cfg(feature = "esp32c3")] 154 | let mut backlight = io.pins.gpio0.into_push_pull_output(); 155 | 156 | #[cfg(feature = "esp32")] 157 | let spi = spi::Spi::new( 158 | peripherals.SPI2, 159 | io.pins.gpio19, 160 | io.pins.gpio23, 161 | io.pins.gpio25, 162 | io.pins.gpio22, 163 | 100u32.MHz(), 164 | spi::SpiMode::Mode0, 165 | &mut system.peripheral_clock_control, 166 | &mut clocks); 167 | 168 | #[cfg(any(feature = "esp32s2", feature = "esp32s3_usb_otg"))] 169 | let spi = spi::Spi::new( 170 | peripherals.SPI3, 171 | io.pins.gpio6, 172 | io.pins.gpio7, 173 | io.pins.gpio12, 174 | io.pins.gpio5, 175 | 60u32.MHz(), 176 | spi::SpiMode::Mode0, 177 | &mut system.peripheral_clock_control, 178 | &clocks); 179 | 180 | #[cfg(feature = "esp32")] 181 | backlight.set_low().unwrap(); 182 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 183 | backlight.set_high().unwrap(); 184 | 185 | 186 | #[cfg(feature = "esp32c3")] 187 | let spi = spi::Spi::new( 188 | peripherals.SPI2, 189 | io.pins.gpio6, 190 | io.pins.gpio7, 191 | io.pins.gpio12, 192 | io.pins.gpio20, 193 | 100u32.MHz(), 194 | spi::SpiMode::Mode0, 195 | &mut system.peripheral_clock_control, 196 | &mut clocks); 197 | 198 | #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3_usb_otg"))] 199 | let reset = io.pins.gpio18.into_push_pull_output(); 200 | #[cfg(any(feature = "esp32s3_box"))] 201 | let reset = io.pins.gpio48.into_push_pull_output(); 202 | #[cfg(any(feature = "esp32c3"))] 203 | let reset = io.pins.gpio9.into_push_pull_output(); 204 | 205 | #[cfg(any(feature = "esp32", feature = "esp32c3"))] 206 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio21.into_push_pull_output()); 207 | #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] 208 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio4.into_push_pull_output()); 209 | 210 | #[cfg(any(feature = "esp32s2_ili9341", feature = "esp32_wrover_kit", feature = "esp32c3_ili9341"))] 211 | let mut delay = Delay::new(&clocks); 212 | 213 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 214 | let mut display = mipidsi::Builder::st7789(di) 215 | .with_display_size(240, 240) 216 | .with_orientation(mipidsi::Orientation::PortraitInverted(false)) 217 | .init(&mut delay, Some(reset)).unwrap(); 218 | 219 | Text::new( 220 | "Initializing...", 221 | Point::new(80, 110), 222 | MonoTextStyle::new(&FONT_8X13, RgbColor::BLACK), 223 | ) 224 | .draw(&mut display) 225 | .unwrap(); 226 | 227 | 228 | let mut rng = Rng::new(peripherals.RNG); 229 | let mut seed_buffer = [0u8;32]; 230 | rng.read(&mut seed_buffer).unwrap(); 231 | let mut data = [Rgb565::BLACK ; 240*240]; 232 | let fbuf = FrameBuf::new(&mut data, 240, 240); 233 | let spritebuf = SpriteBuf::new(fbuf); 234 | let engine = Engine::new(spritebuf, Some(seed_buffer)); 235 | 236 | let mut universe = Universe::new(Some(seed_buffer), engine); 237 | universe.initialize(); 238 | 239 | loop { 240 | let button_down = button_down_pin.is_low().unwrap(); 241 | let button_up = button_up_pin.is_low().unwrap(); 242 | let button_ok = button_ok_pin.is_low().unwrap(); 243 | let button_menu = button_menu_pin.is_low().unwrap(); 244 | 245 | if button_down { 246 | universe.engine.move_down(); 247 | } else if button_up { 248 | universe.move_up(); 249 | } else if button_menu { 250 | universe.move_left(); 251 | } if button_ok { 252 | universe.move_right(); 253 | } 254 | 255 | display.draw_iter(universe.render_frame().into_iter()).unwrap(); 256 | // delay.delay_ms(300u32); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /esp32-s3-box/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_arch = "xtensa")'] 2 | runner = "espflash flash --monitor" 3 | rustflags = [ 4 | # Tell the `core` library that we have atomics, even though it's not 5 | # specified in the target definition 6 | "--cfg", 'target_has_atomic="8"', 7 | "--cfg", 'target_has_atomic="16"', 8 | "--cfg", 'target_has_atomic="32"', 9 | "--cfg", 'target_has_atomic="ptr"', 10 | 11 | "-C", "link-arg=-nostartfiles", 12 | # Enable the atomic codegen option for Xtensa 13 | "-C", "target-feature=+s32c1i", 14 | "-C", "link-arg=-Wl,-Tlinkall.x", 15 | "-C", "force-frame-pointers" 16 | ] 17 | 18 | [target.riscv32imac-unknown-none-elf] 19 | rustflags = [ 20 | "-C", "link-arg=-Tlinkall.x", 21 | ] 22 | 23 | [build] 24 | # Uncomment the target if you'd like to use automatic code hinting in your IDE 25 | # target = "xtensa-esp32-none-elf" 26 | # target = "xtensa-esp32s2-none-elf" 27 | target = "xtensa-esp32s3-none-elf" 28 | # target = "riscv32imac-unknown-none-elf" 29 | 30 | [unstable] 31 | build-std = [ "core", "alloc" ] 32 | -------------------------------------------------------------------------------- /esp32-s3-box/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-s3-box" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [target.xtensa-esp32s3-none-elf.dependencies] 9 | xtensa-atomic-emulation-trap = "0.4.0" 10 | esp32s3-hal = "0.7.0" 11 | esp-backtrace = { version = "0.6.0", features = [ 12 | "esp32s3", 13 | "panic-handler", 14 | "print-uart", 15 | ] } 16 | xtensa-lx-rt = { version = "0.15.0", features = ["esp32s3"], optional = true } 17 | esp-println = { version = "0.4.0", features = ["esp32s3"] } 18 | 19 | [dependencies] 20 | esp-alloc = "0.2.0" 21 | embedded-graphics = "0.7" 22 | embedded-hal = "0.2" 23 | display-interface = "0.4" 24 | display-interface-spi = "0.4" 25 | icm42670 = { git = "https://github.com/jessebraham/icm42670/" } 26 | mipidsi = "0.6.0" 27 | panic-halt = "0.2" 28 | rand = { version = "0.8.5", default-features = false } 29 | rand_chacha = { version = "0.3.1", default-features = false } 30 | shared-bus = { version = "0.2.4" } 31 | {{crate_name}}-engine = { path = "../engine" } 32 | heapless = { version = "0.7.14", default-features = false } 33 | embedded-graphics-framebuf = "0.2.0" 34 | 35 | [features] 36 | default = ["esp32s3_box"] 37 | 38 | system_timer = [] 39 | 40 | button_controls = [] 41 | imu_controls = [] 42 | 43 | esp32 = [] 44 | esp32s2 = ["system_timer"] 45 | esp32s3 = [] 46 | esp32c3 = ["system_timer"] 47 | 48 | # Enable this feature in case you have an ESP32-S3-BOX board with ILI9342C 49 | esp32s3_box = ["xtensa-lx-rt", "esp32s3", "esp32s3-hal/eh1", "imu_controls"] 50 | -------------------------------------------------------------------------------- /esp32-s3-box/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | components = ["rustfmt", "rustc-dev"] 4 | targets = ["xtensa-esp32s3-none-elf"] 5 | # targets = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf","xtensa-esp32s3-none-elf"] 6 | -------------------------------------------------------------------------------- /esp32-s3-box/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(default_alloc_error_handler)] 4 | 5 | #[global_allocator] 6 | static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); 7 | 8 | use display_interface_spi::SPIInterfaceNoCS; 9 | use embedded_graphics::{ 10 | mono_font::{ascii::FONT_8X13, MonoTextStyle}, 11 | prelude::{DrawTarget, Point, RgbColor}, 12 | text::Text, 13 | Drawable, 14 | }; 15 | 16 | use esp_println::println; 17 | 18 | #[cfg(feature = "esp32")] 19 | use esp32_hal as hal; 20 | #[cfg(feature = "esp32c3")] 21 | use esp32c3_hal as hal; 22 | #[cfg(feature = "esp32s2")] 23 | use esp32s2_hal as hal; 24 | #[cfg(feature = "esp32s3")] 25 | use esp32s3_hal as hal; 26 | 27 | use hal::{ 28 | clock::{ClockControl, CpuClock}, 29 | // gdma::Gdma, 30 | i2c, 31 | peripherals::Peripherals, 32 | prelude::*, 33 | spi, 34 | timer::TimerGroup, 35 | Delay, 36 | Rng, 37 | Rtc, 38 | IO, 39 | }; 40 | 41 | // systimer was introduced in ESP32-S2, it's not available for ESP32 42 | #[cfg(feature = "system_timer")] 43 | use hal::systimer::SystemTimer; 44 | 45 | // use panic_halt as _; 46 | use esp_backtrace as _; 47 | 48 | #[cfg(feature = "riscv-rt")] 49 | use riscv_rt::entry; 50 | #[cfg(feature = "xtensa-lx-rt")] 51 | use xtensa_lx_rt::entry; 52 | 53 | use embedded_graphics::pixelcolor::Rgb565; 54 | // use esp32s2_hal::Rng; 55 | 56 | #[cfg(any( 57 | feature = "esp32s2_ili9341", 58 | feature = "esp32_wrover_kit", 59 | feature = "esp32c3_ili9341" 60 | ))] 61 | use ili9341::{DisplaySize240x320, Ili9341, Orientation}; 62 | 63 | use {{crate_name}}_engine::{engine::Engine, spritebuf::SpriteBuf}; 64 | 65 | #[cfg(any(feature = "imu_controls"))] 66 | use icm42670::{accelerometer::Accelerometer, Address, Icm42670}; 67 | #[cfg(any(feature = "imu_controls"))] 68 | use shared_bus::BusManagerSimple; 69 | 70 | use embedded_graphics_framebuf::FrameBuf; 71 | use embedded_hal::digital::v2::OutputPin; 72 | 73 | pub struct Universe { 74 | pub engine: Engine, 75 | icm: I, 76 | } 77 | 78 | impl> 79 | Universe 80 | { 81 | pub fn new(icm: I, seed: Option<[u8; 32]>, engine: Engine) -> Universe { 82 | Universe { 83 | engine, 84 | // #[cfg(any(feature = "imu_controls"))] 85 | icm, 86 | // delay: None, 87 | } 88 | } 89 | 90 | pub fn initialize(&mut self) { 91 | self.engine.initialize(); 92 | } 93 | 94 | pub fn render_frame(&mut self) -> &D { 95 | #[cfg(any(feature = "imu_controls"))] 96 | { 97 | let accel_threshold = 0.20; 98 | let accel_norm = self.icm.accel_norm().unwrap(); 99 | 100 | if accel_norm.y > accel_threshold { 101 | self.engine.move_left(); 102 | } 103 | 104 | if accel_norm.y < -accel_threshold { 105 | self.engine.move_right(); 106 | } 107 | 108 | if accel_norm.x > accel_threshold { 109 | self.engine.move_down(); 110 | } 111 | 112 | if accel_norm.x < -accel_threshold { 113 | self.engine.move_up(); 114 | } 115 | 116 | // if accel_norm.z < -1.2 { 117 | // } else if accel_norm.z > 1.5 { 118 | // } 119 | } 120 | 121 | self.engine.tick(); 122 | self.engine.draw() 123 | } 124 | } 125 | 126 | #[entry] 127 | fn main() -> ! { 128 | const HEAP_SIZE: usize = 65535 * 4; 129 | static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; 130 | unsafe { ALLOCATOR.init(HEAP.as_mut_ptr(), HEAP_SIZE) } 131 | 132 | let peripherals = Peripherals::take(); 133 | 134 | #[cfg(any(feature = "esp32"))] 135 | let mut system = peripherals.DPORT.split(); 136 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 137 | let mut system = peripherals.SYSTEM.split(); 138 | let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze(); 139 | 140 | // Disable the RTC and TIMG watchdog timers 141 | let mut rtc = Rtc::new(peripherals.RTC_CNTL); 142 | let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); 143 | let mut wdt0 = timer_group0.wdt; 144 | let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); 145 | let mut wdt1 = timer_group1.wdt; 146 | 147 | #[cfg(feature = "esp32c3")] 148 | rtc.swd.disable(); 149 | #[cfg(feature = "xtensa-lx-rt")] 150 | rtc.rwdt.disable(); 151 | 152 | wdt0.disable(); 153 | wdt1.disable(); 154 | 155 | let mut delay = Delay::new(&clocks); 156 | // self.delay = Some(delay); 157 | 158 | println!("About to initialize the SPI LED driver"); 159 | let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); 160 | 161 | // https://espressif-docs.readthedocs-hosted.com/projects/espressif-esp-dev-kits/en/latest/esp32s3/esp32-s3-usb-otg/user_guide.html 162 | // let button_up = button::Button::new(); 163 | 164 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 165 | let button_ok_pin = io.pins.gpio0.into_pull_up_input(); 166 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 167 | let button_menu_pin = io.pins.gpio14.into_pull_up_input(); 168 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 169 | let button_up_pin = io.pins.gpio10.into_pull_up_input(); 170 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 171 | let button_down_pin = io.pins.gpio11.into_pull_up_input(); 172 | 173 | #[cfg(feature = "esp32")] 174 | let mut backlight = io.pins.gpio5.into_push_pull_output(); 175 | #[cfg(any(feature = "esp32s2", feature = "esp32s3_usb_otg"))] 176 | let mut backlight = io.pins.gpio9.into_push_pull_output(); 177 | #[cfg(feature = "esp32c3")] 178 | let mut backlight = io.pins.gpio0.into_push_pull_output(); 179 | 180 | #[cfg(feature = "esp32")] 181 | let spi = spi::Spi::new( 182 | peripherals.SPI2, 183 | io.pins.gpio19, 184 | io.pins.gpio23, 185 | io.pins.gpio25, 186 | io.pins.gpio22, 187 | 100u32.MHz(), 188 | spi::SpiMode::Mode0, 189 | &mut system.peripheral_clock_control, 190 | &mut clocks, 191 | ); 192 | 193 | #[cfg(any(feature = "esp32s2", feature = "esp32s3_usb_otg"))] 194 | let spi = spi::Spi::new( 195 | peripherals.SPI3, 196 | io.pins.gpio6, 197 | io.pins.gpio7, 198 | io.pins.gpio12, 199 | io.pins.gpio5, 200 | 100u32.MHz(), 201 | spi::SpiMode::Mode0, 202 | &mut system.peripheral_clock_control, 203 | &mut clocks, 204 | ); 205 | 206 | #[cfg(any(feature = "esp32s3_box"))] 207 | let sclk = io.pins.gpio7; 208 | #[cfg(any(feature = "esp32s3_box"))] 209 | let mosi = io.pins.gpio6; 210 | 211 | // let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); 212 | // let dma_channel = dma.channel0; 213 | 214 | // let mut descriptors = [0u32; 8 * 3]; 215 | // let mut rx_descriptors = [0u32; 8 * 3]; 216 | 217 | #[cfg(any(feature = "esp32s3_box"))] 218 | let spi = spi::Spi::new_no_cs_no_miso( 219 | peripherals.SPI2, 220 | sclk, 221 | mosi, 222 | 60u32.MHz(), 223 | spi::SpiMode::Mode0, 224 | &mut system.peripheral_clock_control, 225 | &clocks, 226 | ); 227 | // .with_dma(dma_channel.configure( 228 | // false, 229 | // &mut descriptors, 230 | // &mut rx_descriptors, 231 | // DmaPriority::Priority0, 232 | // )); 233 | 234 | #[cfg(any(feature = "esp32s3_box"))] 235 | let mut backlight = io.pins.gpio45.into_push_pull_output(); 236 | 237 | #[cfg(feature = "esp32")] 238 | backlight.set_low().unwrap(); 239 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 240 | backlight.set_high().unwrap(); 241 | 242 | #[cfg(feature = "esp32c3")] 243 | let spi = spi::Spi::new( 244 | peripherals.SPI2, 245 | io.pins.gpio6, 246 | io.pins.gpio7, 247 | io.pins.gpio12, 248 | io.pins.gpio20, 249 | 100u32.MHz(), 250 | spi::SpiMode::Mode0, 251 | &mut system.peripheral_clock_control, 252 | &mut clocks, 253 | ); 254 | 255 | #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3_usb_otg"))] 256 | let reset = io.pins.gpio18.into_push_pull_output(); 257 | #[cfg(any(feature = "esp32s3_box"))] 258 | let reset = io.pins.gpio48.into_push_pull_output(); 259 | #[cfg(any(feature = "esp32c3"))] 260 | let reset = io.pins.gpio9.into_push_pull_output(); 261 | 262 | #[cfg(any(feature = "esp32", feature = "esp32c3"))] 263 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio21.into_push_pull_output()); 264 | #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] 265 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio4.into_push_pull_output()); 266 | 267 | #[cfg(any( 268 | feature = "esp32s2_ili9341", 269 | feature = "esp32_wrover_kit", 270 | feature = "esp32c3_ili9341" 271 | ))] 272 | let mut delay = Delay::new(&clocks); 273 | 274 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 275 | let mut display = mipidsi::Display::st7789(di, reset); 276 | 277 | //https://github.com/espressif/esp-box/blob/master/components/bsp/src/boards/esp32_s3_box.c 278 | 279 | #[cfg(any(feature = "esp32s3_box"))] 280 | let mut display = mipidsi::Builder::ili9342c_rgb565(di) 281 | .with_display_size(320, 240) 282 | .with_orientation(mipidsi::Orientation::PortraitInverted(false)) 283 | .with_color_order(mipidsi::ColorOrder::Bgr) 284 | .init(&mut delay, Some(reset)) 285 | .unwrap(); 286 | // let mut display = mipidsi::Display::ili9342c_rgb565(di, core::prelude::v1::Some(reset), display_options); 287 | #[cfg(any( 288 | feature = "esp32s2_ili9341", 289 | feature = "esp32_wrover_kit", 290 | feature = "esp32c3_ili9341" 291 | ))] 292 | let mut display = Ili9341::new( 293 | di, 294 | reset, 295 | &mut delay, 296 | Orientation::Portrait, 297 | DisplaySize240x320, 298 | ) 299 | .unwrap(); 300 | 301 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 302 | display 303 | .init( 304 | &mut delay, 305 | DisplayOptions { 306 | ..DisplayOptions::default() 307 | }, 308 | ) 309 | .unwrap(); 310 | 311 | // display.clear(RgbColor::WHITE).unwrap(); 312 | 313 | Text::new( 314 | "Initializing...", 315 | Point::new(80, 110), 316 | MonoTextStyle::new(&FONT_8X13, RgbColor::BLACK), 317 | ) 318 | .draw(&mut display) 319 | .unwrap(); 320 | 321 | #[cfg(any(feature = "imu_controls"))] 322 | println!("Initializing IMU"); 323 | #[cfg(any(feature = "imu_controls"))] 324 | let sda = io.pins.gpio8; 325 | #[cfg(any(feature = "imu_controls"))] 326 | let scl = io.pins.gpio18; 327 | 328 | #[cfg(any(feature = "imu_controls"))] 329 | let i2c = i2c::I2C::new( 330 | peripherals.I2C0, 331 | sda, 332 | scl, 333 | 100u32.kHz(), 334 | &mut system.peripheral_clock_control, 335 | &clocks, 336 | ); 337 | 338 | #[cfg(any(feature = "imu_controls"))] 339 | let bus = BusManagerSimple::new(i2c); 340 | #[cfg(any(feature = "imu_controls"))] 341 | let icm = Icm42670::new(bus.acquire_i2c(), Address::Primary).unwrap(); 342 | 343 | let mut rng = Rng::new(peripherals.RNG); 344 | let mut seed_buffer = [0u8; 32]; 345 | rng.read(&mut seed_buffer).unwrap(); 346 | let mut data = [Rgb565::BLACK; 320 * 240]; 347 | let fbuf = FrameBuf::new(&mut data, 320, 240); 348 | let spritebuf = SpriteBuf::new(fbuf); 349 | let engine = Engine::new(spritebuf, Some(seed_buffer)); 350 | 351 | let mut universe = Universe::new(icm, Some(seed_buffer), engine); 352 | universe.initialize(); 353 | 354 | // #[cfg(any(feature = "imu_controls"))] 355 | // let accel_threshold = 0.20; 356 | 357 | loop { 358 | display 359 | .draw_iter(universe.render_frame().into_iter()) 360 | .unwrap(); 361 | // delay.delay_ms(300u32); 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /esp32-s3-usb-otg/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_arch = "xtensa")'] 2 | runner = "espflash flash --monitor" 3 | rustflags = [ 4 | # Tell the `core` library that we have atomics, even though it's not 5 | # specified in the target definition 6 | "--cfg", 'target_has_atomic="8"', 7 | "--cfg", 'target_has_atomic="16"', 8 | "--cfg", 'target_has_atomic="32"', 9 | "--cfg", 'target_has_atomic="ptr"', 10 | 11 | "-C", "link-arg=-nostartfiles", 12 | # Enable the atomic codegen option for Xtensa 13 | "-C", "target-feature=+s32c1i", 14 | "-C", "link-arg=-Wl,-Tlinkall.x", 15 | "-C", "force-frame-pointers" 16 | ] 17 | 18 | [build] 19 | # Uncomment the target if you'd like to use automatic code hinting in your IDE 20 | # target = "xtensa-esp32-none-elf" 21 | # target = "xtensa-esp32s2-none-elf" 22 | target = "xtensa-esp32s3-none-elf" 23 | # target = "riscv32imac-unknown-none-elf" 24 | 25 | [unstable] 26 | build-std = [ "core", "alloc" ] 27 | -------------------------------------------------------------------------------- /esp32-s3-usb-otg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-s3-otg" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [target.xtensa-esp32s3-none-elf.dependencies] 9 | xtensa-atomic-emulation-trap = "0.4.0" 10 | esp32s3-hal = "0.7.0" 11 | esp-backtrace = { version = "0.6.0", features = [ 12 | "esp32s3", 13 | "panic-handler", 14 | "print-uart", 15 | ] } 16 | xtensa-lx-rt = { version = "0.15.0", features = ["esp32s3"], optional = true } 17 | esp-println = { version = "0.4.0", features = [ "esp32s3" ] } 18 | 19 | 20 | [dependencies] 21 | esp-alloc = "0.2.0" 22 | embedded-graphics = "0.7" 23 | embedded-hal = "0.2" 24 | display-interface = "0.4" 25 | display-interface-spi = "0.4" 26 | mipidsi = "0.6.0" 27 | panic-halt = "0.2" 28 | rand = { version = "0.8.5", default-features = false } 29 | rand_chacha = { version = "0.3.1", default-features = false } 30 | shared-bus = { version = "0.2.4" } 31 | {{crate_name}}-engine = { path = "../engine" } 32 | heapless = { version = "0.7.14", default-features = false } 33 | embedded-graphics-framebuf = "0.2.0" 34 | 35 | [features] 36 | default = ["rt", "esp32s3_usb_otg"] 37 | rt = ["xtensa-lx-rt"] 38 | 39 | system_timer = [] 40 | 41 | button_controls = [] 42 | imu_controls = [] 43 | 44 | esp32 = [] 45 | esp32s2 = [ "system_timer" ] 46 | esp32s3 = [ ] 47 | esp32c3 = [ "system_timer" ] 48 | 49 | # Enable this feature in case you have an ESP32-S3-USB-OTG board with ST7789 50 | esp32s3_usb_otg = [ "xtensa-lx-rt", "esp32s3", "esp32s3-hal/eh1", "button_controls" ] 51 | -------------------------------------------------------------------------------- /esp32-s3-usb-otg/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | components = ["rustfmt", "rustc-dev"] 4 | targets = ["xtensa-esp32s3-none-elf"] 5 | # targets = ["xtensa-esp32s2-none-elf"] 6 | # targets = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf","xtensa-esp32s3-none-elf"] 7 | -------------------------------------------------------------------------------- /esp32-s3-usb-otg/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(default_alloc_error_handler)] 4 | 5 | #[global_allocator] 6 | static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); 7 | 8 | use display_interface_spi::SPIInterfaceNoCS; 9 | use embedded_graphics::{ 10 | prelude::{Point, DrawTarget, RgbColor}, 11 | mono_font::{ 12 | ascii::{FONT_8X13}, 13 | MonoTextStyle, 14 | }, 15 | text::Text, 16 | Drawable, 17 | }; 18 | 19 | use esp_println::println; 20 | 21 | #[cfg(feature="esp32")] 22 | use esp32_hal as hal; 23 | #[cfg(feature="esp32s2")] 24 | use esp32s2_hal as hal; 25 | #[cfg(feature="esp32s3")] 26 | use esp32s3_hal as hal; 27 | #[cfg(feature="esp32c3")] 28 | use esp32c3_hal as hal; 29 | 30 | use hal::{ 31 | clock::{ ClockControl, CpuClock }, 32 | // gdma::Gdma, 33 | i2c, 34 | peripherals::Peripherals, 35 | prelude::*, 36 | spi, 37 | timer::TimerGroup, 38 | Rng, 39 | Rtc, 40 | IO, 41 | Delay, 42 | }; 43 | 44 | // systimer was introduced in ESP32-S2, it's not available for ESP32 45 | #[cfg(feature="system_timer")] 46 | use hal::systimer::{SystemTimer}; 47 | 48 | // use panic_halt as _; 49 | use esp_backtrace as _; 50 | 51 | #[cfg(feature="xtensa-lx-rt")] 52 | use xtensa_lx_rt::entry; 53 | #[cfg(feature="riscv-rt")] 54 | use riscv_rt::entry; 55 | 56 | use embedded_graphics::{pixelcolor::Rgb565}; 57 | // use esp32s2_hal::Rng; 58 | 59 | #[cfg(any(feature = "esp32s2_ili9341", feature = "esp32_wrover_kit", feature = "esp32c3_ili9341"))] 60 | use ili9341::{DisplaySize240x320, Ili9341, Orientation}; 61 | 62 | use {{crate_name}}_engine::{spritebuf::SpriteBuf, engine::Engine}; 63 | 64 | use embedded_hal::digital::v2::OutputPin; 65 | use embedded_graphics_framebuf::{FrameBuf}; 66 | 67 | pub struct Universe { 68 | pub engine: Engine, 69 | } 70 | 71 | 72 | impl > Universe { 73 | pub fn new(seed: Option<[u8; 32]>, engine:Engine) -> Universe { 74 | Universe { 75 | engine, 76 | } 77 | } 78 | 79 | pub fn initialize(&mut self) { 80 | self.engine.initialize(); 81 | } 82 | 83 | pub fn move_up(&mut self) { 84 | self.engine.move_up(); 85 | } 86 | 87 | pub fn move_down(&mut self) { 88 | self.engine.move_down(); 89 | } 90 | 91 | pub fn move_left(&mut self) { 92 | self.engine.move_left(); 93 | } 94 | 95 | pub fn move_right(&mut self) { 96 | self.engine.move_right(); 97 | } 98 | 99 | pub fn render_frame(&mut self) -> &D { 100 | 101 | self.engine.tick(); 102 | self.engine.draw() 103 | 104 | } 105 | 106 | } 107 | 108 | 109 | #[entry] 110 | fn main() -> ! { 111 | const HEAP_SIZE: usize = 65535*4; 112 | static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; 113 | unsafe { ALLOCATOR.init(HEAP.as_mut_ptr(), HEAP_SIZE) } 114 | 115 | let peripherals = Peripherals::take(); 116 | 117 | #[cfg(any(feature = "esp32"))] 118 | let mut system = peripherals.DPORT.split(); 119 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 120 | let mut system = peripherals.SYSTEM.split(); 121 | let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze(); 122 | 123 | // Disable the RTC and TIMG watchdog timers 124 | let mut rtc = Rtc::new(peripherals.RTC_CNTL); 125 | let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); 126 | let mut wdt0 = timer_group0.wdt; 127 | let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); 128 | let mut wdt1 = timer_group1.wdt; 129 | 130 | #[cfg(feature="esp32c3")] 131 | rtc.swd.disable(); 132 | #[cfg(feature="xtensa-lx-rt")] 133 | rtc.rwdt.disable(); 134 | 135 | wdt0.disable(); 136 | wdt1.disable(); 137 | 138 | let mut delay = Delay::new(&clocks); 139 | // self.delay = Some(delay); 140 | 141 | println!("About to initialize the SPI LED driver"); 142 | let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); 143 | 144 | // https://espressif-docs.readthedocs-hosted.com/projects/espressif-esp-dev-kits/en/latest/esp32s3/esp32-s3-usb-otg/user_guide.html 145 | // let button_up = button::Button::new(); 146 | 147 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 148 | let button_ok_pin = io.pins.gpio0.into_pull_up_input(); 149 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 150 | let button_menu_pin = io.pins.gpio14.into_pull_up_input(); 151 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 152 | let button_up_pin = io.pins.gpio10.into_pull_up_input(); 153 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 154 | let button_down_pin = io.pins.gpio11.into_pull_up_input(); 155 | 156 | #[cfg(any(feature = "esp32s2", feature = "esp32s3_usb_otg"))] 157 | let mut backlight = io.pins.gpio9.into_push_pull_output(); 158 | 159 | 160 | #[cfg(any(feature = "esp32s3_box"))] 161 | let spi = spi::Spi::new_no_cs_no_miso( 162 | peripherals.SPI2, 163 | sclk, 164 | mosi, 165 | 60u32.MHz(), 166 | spi::SpiMode::Mode0, 167 | &mut system.peripheral_clock_control, 168 | &clocks, 169 | ); 170 | 171 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 172 | backlight.set_high().unwrap(); 173 | 174 | 175 | #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3_usb_otg"))] 176 | let reset = io.pins.gpio18.into_push_pull_output(); 177 | #[cfg(any(feature = "esp32s3_box"))] 178 | let reset = io.pins.gpio48.into_push_pull_output(); 179 | #[cfg(any(feature = "esp32c3"))] 180 | let reset = io.pins.gpio9.into_push_pull_output(); 181 | 182 | #[cfg(any(feature = "esp32", feature = "esp32c3"))] 183 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio21.into_push_pull_output()); 184 | #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] 185 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio4.into_push_pull_output()); 186 | 187 | #[cfg(any(feature = "esp32s2_ili9341", feature = "esp32_wrover_kit", feature = "esp32c3_ili9341"))] 188 | let mut delay = Delay::new(&clocks); 189 | 190 | #[cfg(any(feature = "esp32s2_usb_otg", feature = "esp32s3_usb_otg"))] 191 | let mut display = mipidsi::Builder::st7789(di) 192 | .with_display_size(240, 240) 193 | .with_orientation(mipidsi::Orientation::PortraitInverted(false)) 194 | .init(&mut delay, Some(reset)).unwrap(); 195 | 196 | Text::new( 197 | "Initializing...", 198 | Point::new(80, 110), 199 | MonoTextStyle::new(&FONT_8X13, RgbColor::BLACK), 200 | ) 201 | .draw(&mut display) 202 | .unwrap(); 203 | 204 | 205 | let mut rng = Rng::new(peripherals.RNG); 206 | let mut seed_buffer = [0u8;32]; 207 | rng.read(&mut seed_buffer).unwrap(); 208 | let mut data = [Rgb565::BLACK ; 240*240]; 209 | let fbuf = FrameBuf::new(&mut data, 240, 240); 210 | let spritebuf = SpriteBuf::new(fbuf); 211 | let engine = Engine::new(spritebuf, Some(seed_buffer)); 212 | 213 | let mut universe = Universe::new(Some(seed_buffer), engine); 214 | universe.initialize(); 215 | 216 | loop { 217 | let button_down = button_down_pin.is_low().unwrap(); 218 | let button_up = button_up_pin.is_low().unwrap(); 219 | let button_ok = button_ok_pin.is_low().unwrap(); 220 | let button_menu = button_menu_pin.is_low().unwrap(); 221 | 222 | if button_down { 223 | universe.engine.move_down(); 224 | } else if button_up { 225 | universe.move_up(); 226 | } else if button_menu { 227 | universe.move_left(); 228 | } if button_ok { 229 | universe.move_right(); 230 | } 231 | 232 | display.draw_iter(universe.render_frame().into_iter()).unwrap(); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /m5stack-core2/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_arch = "xtensa")'] 2 | runner = "espflash flash --monitor" 3 | rustflags = [ 4 | # Tell the `core` library that we have atomics, even though it's not 5 | # specified in the target definition 6 | "--cfg", 'target_has_atomic="8"', 7 | "--cfg", 'target_has_atomic="16"', 8 | "--cfg", 'target_has_atomic="32"', 9 | "--cfg", 'target_has_atomic="ptr"', 10 | 11 | "-C", "link-arg=-nostartfiles", 12 | # Enable the atomic codegen option for Xtensa 13 | "-C", "target-feature=+s32c1i", 14 | "-C", "link-arg=-Wl,-Tlinkall.x", 15 | "-C", "force-frame-pointers" 16 | ] 17 | 18 | [target.riscv32imac-unknown-none-elf] 19 | rustflags = [ 20 | "-C", "link-arg=-Tlinkall.x", 21 | ] 22 | 23 | [build] 24 | # Uncomment the target if you'd like to use automatic code hinting in your IDE 25 | target = "xtensa-esp32-none-elf" 26 | # target = "xtensa-esp32s2-none-elf" 27 | # target = "xtensa-esp32s3-none-elf" 28 | # target = "riscv32imac-unknown-none-elf" 29 | 30 | [unstable] 31 | build-std = [ "core", "alloc" ] 32 | -------------------------------------------------------------------------------- /m5stack-core2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-m5stack-core2" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [target.xtensa-esp32-none-elf.dependencies] 9 | xtensa-atomic-emulation-trap = "0.4.0" 10 | esp32-hal = "0.10.0" 11 | esp-backtrace = { version = "0.6.0", features = [ 12 | "esp32", 13 | "panic-handler", 14 | "print-uart", 15 | ] } 16 | xtensa-lx-rt = { version = "0.15.0", features = ["esp32"], optional = true } 17 | 18 | [dependencies] 19 | embedded-graphics = "0.7" 20 | embedded-hal = "0.2" 21 | display-interface = "0.4" 22 | display-interface-spi = "0.4" 23 | mpu9250 = { version = "0.24.1", default-features = false, features = [ 24 | "i2c", 25 | ], optional = true } 26 | mpu6050 = { version = "0.1.6", optional = true } 27 | mpu6886 = { version = "0.1.0", optional = true } 28 | mipidsi = { git = "https://github.com/rfuest/mipidsi.git", branch = "display-driver-hal" } 29 | panic-halt = "0.2" 30 | rand = { version = "0.8.5", default-features = false } 31 | rand_chacha = { version = "0.3.1", default-features = false } 32 | shared-bus = { version = "0.2.4" } 33 | {{crate_name}}-engine = { path = "../engine", default-features = false, features = [ ] } 34 | heapless = { version = "0.7.14", default-features = false } 35 | embedded-graphics-framebuf = "0.2.0" 36 | 37 | [features] 38 | default = ["m5stack_core2"] 39 | 40 | system_timer = [] 41 | 42 | button_controls = [] 43 | imu_controls = [] 44 | 45 | esp32 = [] 46 | esp32s2 = ["system_timer"] 47 | esp32s3 = [] 48 | esp32c3 = ["system_timer"] 49 | 50 | mpu6050 = ["imu_controls", "dep:mpu6050"] 51 | mpu6886 = ["imu_controls", "dep:mpu6886"] 52 | mpu9250 = ["imu_controls", "dep:mpu9250"] 53 | 54 | wokwi = [ "xtensa-lx-rt", "esp32", "esp32-hal/eh1", "mpu6050" ] 55 | 56 | m5stack_core2 = ["xtensa-lx-rt", "esp32", "esp32-hal/eh1", "mpu6886" ] 57 | -------------------------------------------------------------------------------- /m5stack-core2/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | components = ["rustfmt", "rustc-dev"] 4 | targets = ["xtensa-esp32-none-elf"] 5 | # targets = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf","xtensa-esp32s3-none-elf"] 6 | -------------------------------------------------------------------------------- /m5stack-core2/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(default_alloc_error_handler)] 4 | 5 | // https://docs.makerfactory.io/m5stack/core/fire/ 6 | 7 | use display_interface_spi::SPIInterfaceNoCS; 8 | use embedded_graphics::{ 9 | mono_font::{ascii::FONT_8X13, MonoTextStyle}, 10 | prelude::{DrawTarget, Point, RgbColor}, 11 | text::Text, 12 | Drawable, 13 | }; 14 | 15 | #[cfg(feature = "esp32")] 16 | use esp32_hal as hal; 17 | #[cfg(feature = "esp32c3")] 18 | use esp32c3_hal as hal; 19 | #[cfg(feature = "esp32s2")] 20 | use esp32s2_hal as hal; 21 | #[cfg(feature = "esp32s3")] 22 | use esp32s3_hal as hal; 23 | 24 | use hal::{ 25 | clock::{ClockControl, CpuClock}, 26 | i2c, 27 | peripherals::Peripherals, 28 | prelude::*, 29 | spi, 30 | timer::TimerGroup, 31 | Delay, Rng, Rtc, IO, 32 | }; 33 | 34 | // use panic_halt as _; 35 | use esp_backtrace as _; 36 | 37 | #[cfg(feature = "wokwi")] 38 | use mipidsi::hal::{ Orientation, Rotation }; 39 | 40 | #[cfg(feature = "mpu9250")] 41 | use mpu9250::{ImuMeasurements, Mpu9250}; 42 | 43 | #[cfg(feature = "mpu6050")] 44 | use mpu6050::Mpu6050; 45 | 46 | #[cfg(feature = "mpu6886")] 47 | use mpu6886::Mpu6886; 48 | 49 | #[cfg(feature = "xtensa-lx-rt")] 50 | use xtensa_lx_rt::entry; 51 | 52 | use embedded_graphics::pixelcolor::Rgb565; 53 | 54 | use {{crate_name}}_engine::{engine::Engine, spritebuf::SpriteBuf}; 55 | 56 | #[cfg(any(feature = "imu_controls"))] 57 | use shared_bus::BusManagerSimple; 58 | 59 | use embedded_graphics_framebuf::FrameBuf; 60 | use embedded_hal::digital::v2::OutputPin; 61 | 62 | pub struct Universe { 63 | pub engine: Engine, 64 | } 65 | 66 | impl> Universe { 67 | pub fn new(seed: Option<[u8; 32]>, engine: Engine) -> Universe { 68 | Universe { engine } 69 | } 70 | 71 | pub fn initialize(&mut self) { 72 | self.engine.initialize(); 73 | } 74 | 75 | pub fn move_up(&mut self) { 76 | self.engine.move_up(); 77 | } 78 | 79 | pub fn move_down(&mut self) { 80 | self.engine.move_down(); 81 | } 82 | 83 | pub fn move_left(&mut self) { 84 | self.engine.move_left(); 85 | } 86 | 87 | pub fn move_right(&mut self) { 88 | self.engine.move_right(); 89 | } 90 | 91 | pub fn render_frame(&mut self) -> &D { 92 | self.engine.tick(); 93 | self.engine.draw() 94 | } 95 | } 96 | 97 | #[entry] 98 | fn main() -> ! { 99 | let peripherals = Peripherals::take(); 100 | 101 | #[cfg(any(feature = "esp32"))] 102 | let mut system = peripherals.DPORT.split(); 103 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 104 | let mut system = peripherals.SYSTEM.split(); 105 | let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze(); 106 | 107 | // Disable the RTC and TIMG watchdog timers 108 | let mut rtc = Rtc::new(peripherals.RTC_CNTL); 109 | let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); 110 | let mut wdt0 = timer_group0.wdt; 111 | let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); 112 | let mut wdt1 = timer_group1.wdt; 113 | 114 | #[cfg(feature = "esp32c3")] 115 | rtc.swd.disable(); 116 | #[cfg(feature = "xtensa-lx-rt")] 117 | rtc.rwdt.disable(); 118 | 119 | wdt0.disable(); 120 | wdt1.disable(); 121 | 122 | let mut delay = Delay::new(&clocks); 123 | 124 | let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); 125 | 126 | #[cfg(feature = "esp32")] 127 | let mut backlight = io.pins.gpio3.into_push_pull_output(); 128 | 129 | #[cfg(feature = "esp32")] 130 | let spi = spi::Spi::new( 131 | peripherals.SPI3, // Real HW working with SPI2, but Wokwi seems to work only with SPI3 132 | io.pins.gpio18, // SCLK 133 | io.pins.gpio23, // MOSI 134 | io.pins.gpio38, // MISO 135 | io.pins.gpio5, // CS 136 | 60u32.MHz(), 137 | spi::SpiMode::Mode0, 138 | &mut system.peripheral_clock_control, 139 | &clocks, 140 | ); 141 | 142 | backlight.set_high().unwrap(); 143 | 144 | let reset = io.pins.gpio4.into_push_pull_output(); 145 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio15.into_push_pull_output()); 146 | 147 | #[cfg(feature = "m5stack_core2")] 148 | let mut display = mipidsi::Builder::ili9342c_rgb565(di) 149 | .with_display_size(320, 240) 150 | .init(&mut delay, Some(reset)) 151 | .unwrap(); 152 | 153 | #[cfg(feature = "wokwi")] 154 | let mut display = mipidsi::Builder::ili9341_rgb565(di) 155 | .with_display_size(320, 240) 156 | .with_orientation(Orientation::new().rotate(Rotation::Deg90).flip_vertical()) 157 | .init(&mut delay, Some(reset)) 158 | .unwrap(); 159 | 160 | Text::new( 161 | "Initializing...", 162 | Point::new(80, 110), 163 | MonoTextStyle::new(&FONT_8X13, RgbColor::WHITE), 164 | ) 165 | .draw(&mut display) 166 | .unwrap(); 167 | 168 | // let button_a = io.pins.gpio39.into_pull_up_input(); 169 | #[cfg(feature = "wokwi")] 170 | let button_b = io.pins.gpio34.into_pull_up_input(); 171 | #[cfg(feature = "wokwi")] 172 | let button_c = io.pins.gpio35.into_pull_up_input(); 173 | // #[cfg(feature = "m5stack_core2")] 174 | // let button_b = io.pins.gpio38.into_pull_up_input(); 175 | // #[cfg(feature = "m5stack_core2")] 176 | // let button_c = io.pins.gpio37.into_pull_up_input(); 177 | 178 | #[cfg(any(feature = "imu_controls"))] 179 | let sda = io.pins.gpio21; 180 | #[cfg(any(feature = "imu_controls"))] 181 | let scl = io.pins.gpio22; 182 | 183 | #[cfg(any(feature = "imu_controls"))] 184 | let i2c = i2c::I2C::new( 185 | peripherals.I2C0, 186 | sda, 187 | scl, 188 | 100u32.kHz(), 189 | &mut system.peripheral_clock_control, 190 | &clocks, 191 | ); 192 | 193 | #[cfg(any(feature = "imu_controls"))] 194 | let bus = BusManagerSimple::new(i2c); 195 | 196 | #[cfg(any(feature = "mpu9250"))] 197 | let mut icm = Mpu9250::imu_default(bus.acquire_i2c(), &mut delay).unwrap(); 198 | 199 | #[cfg(any(feature = "mpu6050"))] 200 | let mut icm = Mpu6050::new(bus.acquire_i2c()); 201 | #[cfg(any(feature = "mpu6050"))] 202 | icm.init(&mut delay).unwrap(); 203 | 204 | #[cfg(any(feature = "mpu6886"))] 205 | let mut icm = Mpu6886::new(bus.acquire_i2c()); 206 | 207 | let mut rng = Rng::new(peripherals.RNG); 208 | let mut seed_buffer = [0u8; 32]; 209 | rng.read(&mut seed_buffer).unwrap(); 210 | let mut data = [Rgb565::BLACK; 320 * 240]; 211 | let fbuf = FrameBuf::new(&mut data, 320, 240); 212 | let spritebuf = SpriteBuf::new(fbuf); 213 | let engine = Engine::new(spritebuf, Some(seed_buffer)); 214 | let mut universe = Universe::new(Some(seed_buffer), engine); 215 | universe.initialize(); 216 | 217 | loop { 218 | #[cfg(feature = "m5stack_core2")] 219 | { 220 | // if button_c.is_low().unwrap() { 221 | // } 222 | // 223 | // if button_b.is_low().unwrap() { 224 | // } 225 | 226 | } 227 | 228 | #[cfg(feature = "wokwi")] 229 | { 230 | if button_c.is_high().unwrap() { 231 | } 232 | 233 | if button_b.is_high().unwrap() { 234 | } 235 | 236 | } 237 | 238 | #[cfg(feature = "mpu9250")] 239 | { 240 | let accel_threshold = 1.00; 241 | let measurement: ImuMeasurements<[f32; 3]> = icm.all().unwrap(); 242 | 243 | if measurement.accel[0] > accel_threshold { 244 | universe.move_left(); 245 | } 246 | 247 | if measurement.accel[0] < -accel_threshold { 248 | universe.move_right(); 249 | } 250 | 251 | if measurement.accel[1] > accel_threshold { 252 | universe.move_down(); 253 | } 254 | 255 | if measurement.accel[1] < -accel_threshold { 256 | universe.move_up(); 257 | } 258 | 259 | // if measurement.accel[2] < -10.2 { 260 | // } else if measurement.accel[2] > 20.5 { 261 | // } 262 | } 263 | 264 | #[cfg(any(feature = "mpu6050", feature = "mpu6886"))] 265 | { 266 | #[cfg(feature = "mpu6050")] 267 | let accel_threshold = 1.00; 268 | 269 | #[cfg(feature = "mpu6886")] 270 | let accel_threshold = 0.30; 271 | 272 | let measurement = icm.get_acc().unwrap(); 273 | // let measurement: ImuMeasurements<[f32; 3]> = icm.all().unwrap(); 274 | 275 | if measurement.x > accel_threshold { 276 | universe.move_left(); 277 | } 278 | 279 | if measurement.x < -accel_threshold { 280 | universe.move_right(); 281 | } 282 | 283 | if measurement.y > accel_threshold { 284 | universe.move_down(); 285 | } 286 | 287 | if measurement.y < -accel_threshold { 288 | universe.move_up(); 289 | } 290 | 291 | // if measurement.z < -10.2 { 292 | // } else if measurement.z > 20.5 { 293 | // } 294 | } 295 | display 296 | .draw_iter(universe.render_frame().into_iter()) 297 | .unwrap(); 298 | // delay.delay_ms(300u32); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /m5stack-fire/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_arch = "xtensa")'] 2 | runner = "espflash flash --monitor" 3 | rustflags = [ 4 | # Tell the `core` library that we have atomics, even though it's not 5 | # specified in the target definition 6 | "--cfg", 'target_has_atomic="8"', 7 | "--cfg", 'target_has_atomic="16"', 8 | "--cfg", 'target_has_atomic="32"', 9 | "--cfg", 'target_has_atomic="ptr"', 10 | 11 | "-C", "link-arg=-nostartfiles", 12 | # Enable the atomic codegen option for Xtensa 13 | "-C", "target-feature=+s32c1i", 14 | "-C", "link-arg=-Wl,-Tlinkall.x", 15 | "-C", "force-frame-pointers" 16 | ] 17 | 18 | [target.riscv32imac-unknown-none-elf] 19 | rustflags = [ 20 | "-C", "link-arg=-Tlinkall.x", 21 | ] 22 | 23 | [build] 24 | # Uncomment the target if you'd like to use automatic code hinting in your IDE 25 | target = "xtensa-esp32-none-elf" 26 | # target = "xtensa-esp32s2-none-elf" 27 | # target = "xtensa-esp32s3-none-elf" 28 | # target = "riscv32imac-unknown-none-elf" 29 | 30 | [unstable] 31 | build-std = [ "core", "alloc" ] 32 | -------------------------------------------------------------------------------- /m5stack-fire/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-m5stack-fire" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [target.xtensa-esp32-none-elf.dependencies] 9 | xtensa-atomic-emulation-trap = "0.4.0" 10 | esp32-hal = "0.10.0" 11 | esp-backtrace = { version = "0.6.0", features = [ 12 | "esp32", 13 | "panic-handler", 14 | "print-uart", 15 | ] } 16 | xtensa-lx-rt = { version = "0.15.0", features = ["esp32"], optional = true } 17 | 18 | [dependencies] 19 | embedded-graphics = "0.7" 20 | embedded-hal = "0.2" 21 | display-interface = "0.4" 22 | display-interface-spi = "0.4" 23 | mpu9250 = { version = "0.24.1", default-features = false, features = [ 24 | "i2c", 25 | ], optional = true } 26 | mpu6050 = { version = "0.1.6", optional = true } 27 | mipidsi = { git = "https://github.com/rfuest/mipidsi.git", branch = "display-driver-hal" } 28 | panic-halt = "0.2" 29 | rand = { version = "0.8.5", default-features = false } 30 | rand_chacha = { version = "0.3.1", default-features = false } 31 | shared-bus = { version = "0.2.4" } 32 | {{crate_name}}-engine = { path = "../engine", default-features = false, features = [ ] } 33 | heapless = { version = "0.7.14", default-features = false } 34 | embedded-graphics-framebuf = "0.2.0" 35 | 36 | [features] 37 | default = ["m5stack_core2"] 38 | 39 | system_timer = [] 40 | 41 | button_controls = [] 42 | imu_controls = [] 43 | 44 | esp32 = [] 45 | esp32s2 = ["system_timer"] 46 | esp32s3 = [] 47 | esp32c3 = ["system_timer"] 48 | 49 | mpu6050 = ["imu_controls", "dep:mpu6050"] 50 | mpu9250 = ["imu_controls", "dep:mpu9250"] 51 | 52 | wokwi = [ "xtensa-lx-rt", "esp32", "esp32-hal/eh1", "mpu6050" ] 53 | 54 | m5stack_core2 = ["xtensa-lx-rt", "esp32", "esp32-hal/eh1", "mpu9250" ] 55 | -------------------------------------------------------------------------------- /m5stack-fire/diagram.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "author": "Kirill Mikhailov [playfulFence]", 4 | "editor": "wokwi", 5 | "parts": [ 6 | { 7 | "type": "wokwi-esp32-devkit-v1", 8 | "id": "esp", 9 | "top": -494.32, 10 | "left": -455.03, 11 | "attrs": { "builder": "rust-std-esp32" } 12 | }, 13 | { 14 | "type": "wokwi-ili9341", 15 | "id": "lcd1", 16 | "top": -546.22, 17 | "left": -134.92, 18 | "rotate": 90, 19 | "attrs": {} 20 | }, 21 | { "type": "wokwi-mpu6050", "id": "imu1", "top": -235.29, "left": -316.38, "attrs": {} }, 22 | { 23 | "type": "wokwi-pushbutton", 24 | "id": "btn1", 25 | "top": -234.07, 26 | "left": -490.67, 27 | "attrs": { "color": "green" } 28 | }, 29 | { 30 | "type": "wokwi-pushbutton", 31 | "id": "btn2", 32 | "top": -234.73, 33 | "left": -402, 34 | "attrs": { "color": "red" } 35 | } 36 | ], 37 | "connections": [ 38 | [ "esp:TX0", "$serialMonitor:RX", "", [] ], 39 | [ "esp:RX0", "$serialMonitor:TX", "", [] ], 40 | [ "esp:3V3", "lcd1:VCC", "green", [] ], 41 | [ "esp:GND.1", "lcd1:GND", "black", [] ], 42 | [ "esp:D18", "lcd1:SCK", "blue", [] ], 43 | [ "esp:D23", "lcd1:MOSI", "orange", [] ], 44 | [ "esp:D14", "lcd1:CS", "red", [] ], 45 | [ "esp:D27", "lcd1:D/C", "magenta", [] ], 46 | [ "esp:D33", "lcd1:RST", "yellow", [] ], 47 | [ "lcd1:LED", "esp:3V3", "white", [] ], 48 | [ "imu1:VCC", "esp:3V3", "red", [ "v0" ] ], 49 | [ "imu1:GND", "esp:GND.2", "black", [ "v0" ] ], 50 | [ "imu1:SDA", "esp:D21", "green", [ "v-144.89", "h227.76" ] ], 51 | [ "imu1:SCL", "esp:D22", "green", [ "v0" ] ], 52 | [ "btn1:1.l", "esp:VIN", "green", [ "h0" ] ], 53 | [ "btn1:2.r", "esp:D34", "green", [ "v0" ] ], 54 | [ "btn2:1.l", "esp:VIN", "green", [ "h0" ] ], 55 | [ "btn2:2.r", "esp:D35", "green", [ "v0" ] ] 56 | ], 57 | "serialMonitor": { "display": "terminal" }, 58 | "dependencies": {} 59 | } 60 | -------------------------------------------------------------------------------- /m5stack-fire/run-wokwi.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [ "${USER}" == "gitpod" ]; then 6 | export CURRENT_PROJECT=/workspace/esp32-spooky-maze-game 7 | elif [ "${CODESPACE_NAME}" != "" ]; then 8 | export CURRENT_PROJECT=/workspaces/esp32-spooky-maze-game 9 | else 10 | export CURRENT_PROJECT=~/workspace 11 | fi 12 | 13 | BUILD_MODE="" 14 | case "$1" in 15 | ""|"release") 16 | cargo build --release --no-default-features --features "wokwi" 17 | BUILD_MODE="release" 18 | ;; 19 | "debug") 20 | cargo build --no-default-features --features "wokwi" 21 | BUILD_MODE="debug" 22 | ;; 23 | *) 24 | echo "Wrong argument. Only \"debug\"/\"release\" arguments are supported" 25 | exit 1;; 26 | esac 27 | 28 | 29 | if [ "${USER}" == "gitpod" ];then 30 | gp_url=$(gp url 9012) 31 | echo "gp_url=${gp_url}" 32 | export WOKWI_HOST=${gp_url:8} 33 | elif [ "${CODESPACE_NAME}" != "" ];then 34 | export WOKWI_HOST=${CODESPACE_NAME}-9012.preview.app.github.dev 35 | fi 36 | 37 | export ESP_BOARD="esp32" 38 | export ESP_ELF="spooky_m5" 39 | export ESP_ARCH="xtensa-esp32-none-elf" 40 | export WOKWI_PROJECT_ID="350825213595746900" 41 | # if [ "${ESP_BOARD}" == "esp32c3" ]; then 42 | # export ESP_ARCH="riscv32imc-esp-espidf" 43 | # export WOKWI_PROJECT_ID="330910629554553426" 44 | # elif [ "${ESP_BOARD}" == "esp32s2" ]; then 45 | # export WOKWI_PROJECT_ID="330831847505265234" 46 | # export ESP_ARCH="xtensa-esp32s2-espidf" 47 | # else 48 | # export WOKWI_PROJECT_ID="331440829570744915" 49 | # export ESP_ARCH="xtensa-esp32-espidf" 50 | # fi 51 | 52 | echo "WOKWI_HOST=${WOKWI_HOST}" 53 | echo "Running: wokwi-server --chip ${ESP_BOARD} --id ${WOKWI_PROJECT_ID} ${CURRENT_PROJECT}/target/${ESP_ARCH}/${BUILD_MODE}/${ESP_ELF}" 54 | wokwi-server --chip ${ESP_BOARD} --id ${WOKWI_PROJECT_ID} "${CURRENT_PROJECT}/target/${ESP_ARCH}/${BUILD_MODE}/${ESP_ELF}" 55 | -------------------------------------------------------------------------------- /m5stack-fire/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | components = ["rustfmt", "rustc-dev"] 4 | targets = ["xtensa-esp32-none-elf"] 5 | # targets = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf","xtensa-esp32s3-none-elf"] 6 | -------------------------------------------------------------------------------- /m5stack-fire/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | #![feature(default_alloc_error_handler)] 4 | 5 | // https://docs.makerfactory.io/m5stack/core/fire/ 6 | 7 | use display_interface_spi::SPIInterfaceNoCS; 8 | use embedded_graphics::{ 9 | mono_font::{ascii::FONT_8X13, MonoTextStyle}, 10 | prelude::{DrawTarget, Point, RgbColor}, 11 | text::Text, 12 | Drawable, 13 | }; 14 | 15 | #[cfg(feature = "esp32")] 16 | use esp32_hal as hal; 17 | #[cfg(feature = "esp32c3")] 18 | use esp32c3_hal as hal; 19 | #[cfg(feature = "esp32s2")] 20 | use esp32s2_hal as hal; 21 | #[cfg(feature = "esp32s3")] 22 | use esp32s3_hal as hal; 23 | 24 | use hal::{ 25 | clock::{ClockControl, CpuClock}, 26 | i2c, 27 | peripherals::Peripherals, 28 | prelude::*, 29 | spi, 30 | timer::TimerGroup, 31 | Delay, Rng, Rtc, IO, 32 | }; 33 | 34 | // use panic_halt as _; 35 | use esp_backtrace as _; 36 | 37 | #[cfg(feature = "wokwi")] 38 | use mipidsi::hal::{ Orientation, Rotation }; 39 | 40 | #[cfg(feature = "mpu9250")] 41 | use mpu9250::{ImuMeasurements, Mpu9250}; 42 | 43 | #[cfg(feature = "mpu6050")] 44 | use mpu6050::Mpu6050; 45 | 46 | #[cfg(feature = "xtensa-lx-rt")] 47 | use xtensa_lx_rt::entry; 48 | 49 | use embedded_graphics::pixelcolor::Rgb565; 50 | 51 | use {{crate_name}}_engine::{engine::Engine, spritebuf::SpriteBuf}; 52 | 53 | #[cfg(any(feature = "imu_controls"))] 54 | use shared_bus::BusManagerSimple; 55 | 56 | use embedded_graphics_framebuf::FrameBuf; 57 | use embedded_hal::digital::v2::OutputPin; 58 | 59 | pub struct Universe { 60 | pub engine: Engine, 61 | } 62 | 63 | impl> Universe { 64 | pub fn new(seed: Option<[u8; 32]>, engine: Engine) -> Universe { 65 | Universe { engine } 66 | } 67 | 68 | pub fn initialize(&mut self) { 69 | self.engine.initialize(); 70 | } 71 | 72 | pub fn move_up(&mut self) { 73 | self.engine.move_up(); 74 | } 75 | 76 | pub fn move_down(&mut self) { 77 | self.engine.move_down(); 78 | } 79 | 80 | pub fn move_left(&mut self) { 81 | self.engine.move_left(); 82 | } 83 | 84 | pub fn move_right(&mut self) { 85 | self.engine.move_right(); 86 | } 87 | 88 | pub fn render_frame(&mut self) -> &D { 89 | self.engine.tick(); 90 | self.engine.draw() 91 | } 92 | } 93 | 94 | #[entry] 95 | fn main() -> ! { 96 | let peripherals = Peripherals::take(); 97 | 98 | #[cfg(any(feature = "esp32"))] 99 | let mut system = peripherals.DPORT.split(); 100 | #[cfg(any(feature = "esp32s2", feature = "esp32s3", feature = "esp32c3"))] 101 | let mut system = peripherals.SYSTEM.split(); 102 | let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze(); 103 | 104 | // Disable the RTC and TIMG watchdog timers 105 | let mut rtc = Rtc::new(peripherals.RTC_CNTL); 106 | let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); 107 | let mut wdt0 = timer_group0.wdt; 108 | let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); 109 | let mut wdt1 = timer_group1.wdt; 110 | 111 | #[cfg(feature = "esp32c3")] 112 | rtc.swd.disable(); 113 | #[cfg(feature = "xtensa-lx-rt")] 114 | rtc.rwdt.disable(); 115 | 116 | wdt0.disable(); 117 | wdt1.disable(); 118 | 119 | let mut delay = Delay::new(&clocks); 120 | 121 | let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); 122 | 123 | #[cfg(feature = "esp32")] 124 | let mut backlight = io.pins.gpio32.into_push_pull_output(); 125 | 126 | #[cfg(feature = "esp32")] 127 | let spi = spi::Spi::new( 128 | peripherals.SPI3, // Real HW working with SPI2, but Wokwi seems to work only with SPI3 129 | io.pins.gpio18, // SCLK 130 | io.pins.gpio23, // MOSI 131 | io.pins.gpio19, // MISO 132 | io.pins.gpio14, // CS 133 | 60u32.MHz(), 134 | spi::SpiMode::Mode0, 135 | &mut system.peripheral_clock_control, 136 | &clocks, 137 | ); 138 | 139 | backlight.set_high().unwrap(); 140 | 141 | let reset = io.pins.gpio33.into_push_pull_output(); 142 | let di = SPIInterfaceNoCS::new(spi, io.pins.gpio27.into_push_pull_output()); 143 | 144 | #[cfg(feature = "m5stack_core2")] 145 | let mut display = mipidsi::Builder::ili9341_rgb565(di) 146 | .with_display_size(320, 240) 147 | .init(&mut delay, Some(reset)) 148 | .unwrap(); 149 | 150 | #[cfg(feature = "wokwi")] 151 | let mut display = mipidsi::Builder::ili9341_rgb565(di) 152 | .with_display_size(320, 240) 153 | .with_orientation(Orientation::new().rotate(Rotation::Deg90).flip_vertical()) 154 | .init(&mut delay, Some(reset)) 155 | .unwrap(); 156 | 157 | Text::new( 158 | "Initializing...", 159 | Point::new(80, 110), 160 | MonoTextStyle::new(&FONT_8X13, RgbColor::WHITE), 161 | ) 162 | .draw(&mut display) 163 | .unwrap(); 164 | 165 | // let button_a = io.pins.gpio39.into_pull_up_input(); 166 | #[cfg(feature = "wokwi")] 167 | let button_b = io.pins.gpio34.into_pull_up_input(); 168 | #[cfg(feature = "wokwi")] 169 | let button_c = io.pins.gpio35.into_pull_up_input(); 170 | #[cfg(feature = "m5stack_core2")] 171 | let button_b = io.pins.gpio38.into_pull_up_input(); 172 | #[cfg(feature = "m5stack_core2")] 173 | let button_c = io.pins.gpio37.into_pull_up_input(); 174 | 175 | #[cfg(any(feature = "imu_controls"))] 176 | let sda = io.pins.gpio21; 177 | #[cfg(any(feature = "imu_controls"))] 178 | let scl = io.pins.gpio22; 179 | 180 | #[cfg(any(feature = "imu_controls"))] 181 | let i2c = i2c::I2C::new( 182 | peripherals.I2C0, 183 | sda, 184 | scl, 185 | 100u32.kHz(), 186 | &mut system.peripheral_clock_control, 187 | &clocks, 188 | ); 189 | 190 | #[cfg(any(feature = "imu_controls"))] 191 | let bus = BusManagerSimple::new(i2c); 192 | 193 | #[cfg(any(feature = "mpu9250"))] 194 | let mut icm = Mpu9250::imu_default(bus.acquire_i2c(), &mut delay).unwrap(); 195 | 196 | #[cfg(any(feature = "mpu6050"))] 197 | let mut icm = Mpu6050::new(bus.acquire_i2c()); 198 | #[cfg(any(feature = "mpu6050"))] 199 | icm.init(&mut delay).unwrap(); 200 | 201 | let mut rng = Rng::new(peripherals.RNG); 202 | let mut seed_buffer = [0u8; 32]; 203 | rng.read(&mut seed_buffer).unwrap(); 204 | let mut data = [Rgb565::BLACK; 320 * 240]; 205 | let fbuf = FrameBuf::new(&mut data, 320, 240); 206 | let spritebuf = SpriteBuf::new(fbuf); 207 | let engine = Engine::new(spritebuf, Some(seed_buffer)); 208 | let mut universe = Universe::new(Some(seed_buffer), engine); 209 | universe.initialize(); 210 | 211 | loop { 212 | #[cfg(feature = "m5stack_core2")] 213 | { 214 | if button_c.is_low().unwrap() { 215 | } 216 | 217 | if button_b.is_low().unwrap() { 218 | } 219 | 220 | } 221 | 222 | #[cfg(feature = "wokwi")] 223 | { 224 | if button_c.is_high().unwrap() { 225 | } 226 | 227 | if button_b.is_high().unwrap() { 228 | } 229 | 230 | } 231 | 232 | #[cfg(feature = "mpu9250")] 233 | { 234 | let accel_threshold = 1.00; 235 | let measurement: ImuMeasurements<[f32; 3]> = icm.all().unwrap(); 236 | 237 | if measurement.accel[0] > accel_threshold { 238 | universe.move_left(); 239 | } 240 | 241 | if measurement.accel[0] < -accel_threshold { 242 | universe.move_right(); 243 | } 244 | 245 | if measurement.accel[1] > accel_threshold { 246 | universe.move_down(); 247 | } 248 | 249 | if measurement.accel[1] < -accel_threshold { 250 | universe.move_up(); 251 | } 252 | 253 | // if measurement.accel[2] < -10.2 { 254 | // } else if measurement.accel[2] > 20.5 { 255 | // } 256 | } 257 | 258 | #[cfg(feature = "mpu6050")] 259 | { 260 | let accel_threshold = 1.00; 261 | let measurement = icm.get_acc().unwrap(); 262 | // let measurement: ImuMeasurements<[f32; 3]> = icm.all().unwrap(); 263 | 264 | if measurement.x > accel_threshold { 265 | universe.move_left(); 266 | } 267 | 268 | if measurement.x < -accel_threshold { 269 | universe.move_right(); 270 | } 271 | 272 | if measurement.y > accel_threshold { 273 | universe.move_down(); 274 | } 275 | 276 | if measurement.y < -accel_threshold { 277 | universe.move_up(); 278 | } 279 | 280 | // if measurement.z < -10.2 { 281 | // } else if measurement.z > 20.5 { 282 | // } 283 | } 284 | display 285 | .draw_iter(universe.render_frame().into_iter()) 286 | .unwrap(); 287 | // delay.delay_ms(300u32); 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /m5stack-fire/wokwi.toml: -------------------------------------------------------------------------------- 1 | 2 | 3 | [wokwi] 4 | version = 1 5 | elf = "../target/xtensa-esp32-none-elf/release/{{crate_name}}-m5" 6 | firmware = "../target/xtensa-esp32-none-elf/release/{{crate_name}}-m5" 7 | -------------------------------------------------------------------------------- /wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{crate_name}}-wasm" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | embedded-graphics = "0.7" 13 | wasm-bindgen = "0.2.83" 14 | web-sys = { version = "0.3.60", features = ['Window', 'Performance', 'PerformanceTiming']} 15 | embedded-graphics-framebuf = "0.2.0" 16 | embedded-graphics-web-simulator = { git = "https://github.com/georgik/embedded-graphics-web-simulator.git", branch = "feature/sprite" } 17 | getrandom = { version = "0.2.8", features = ["js"] } 18 | rand_chacha = { version = "0.3.1", default-features = false } 19 | {{crate_name}}-engine = { path = "../engine", default-features = false, features = [ "wasm" ]} 20 | -------------------------------------------------------------------------------- /wasm/README.md: -------------------------------------------------------------------------------- 1 | This README contains HTML5 specific instructions. Please, refer to parent README for full explanation. 2 | 3 | 4 | ## Commands 5 | 6 | ``` 7 | npm run serve 8 | ``` 9 | 10 | ### Generate assets 11 | 12 | ``` 13 | npx pwa-asset-generator img icons 14 | ``` -------------------------------------------------------------------------------- /wasm/icons/manifest-icon-192.maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgik/esp32-rust-multi-target-template/717eb0d5ba39a4769bad0befa8e7667e0106ddbd/wasm/icons/manifest-icon-192.maskable.png -------------------------------------------------------------------------------- /wasm/icons/manifest-icon-512.maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/georgik/esp32-rust-multi-target-template/717eb0d5ba39a4769bad0befa8e7667e0106ddbd/wasm/icons/manifest-icon-512.maskable.png -------------------------------------------------------------------------------- /wasm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 11 | 13 | 14 | 15 | 16 | 18 |

19 | {{project-name}} 20 |

21 |
22 | 23 | 24 | 25 | 26 |
27 |
28 | 29 | 30 | 31 | 42 | 43 | -------------------------------------------------------------------------------- /wasm/index.js: -------------------------------------------------------------------------------- 1 | var keyUp; 2 | 3 | const motionDirection = { 4 | none: 0, 5 | up: 1, 6 | down: 2, 7 | left: 3, 8 | right: 4 9 | }; 10 | 11 | const tiltThreshold = 2; 12 | 13 | const rust = import('./pkg') 14 | .then(m => { 15 | let universe = m.Universe.new(); 16 | var motionRequestHorizontal = motionDirection.none; 17 | var motionRequestVertical = motionDirection.none; 18 | 19 | document.getElementById('html-console').innerHTML = "Initialize"; 20 | 21 | document.getElementById('button-up').addEventListener('click', () => { 22 | universe.move_up(); 23 | }); 24 | document.getElementById('button-down').addEventListener('click', () => { 25 | universe.move_down(); 26 | }); 27 | document.getElementById('button-left').addEventListener('click', () => { 28 | universe.move_left(); 29 | }); 30 | document.getElementById('button-right').addEventListener('click', () => { 31 | universe.move_right(); 32 | }); 33 | 34 | window.addEventListener("devicemotion", (event) => { 35 | 36 | if (event.accelerationIncludingGravity.y > tiltThreshold) { 37 | motionRequestVertical = motionDirection.down; 38 | } else if (event.accelerationIncludingGravity.y < -tiltThreshold) { 39 | motionRequestVertical = motionDirection.up; 40 | } 41 | 42 | if (event.accelerationIncludingGravity.x < -tiltThreshold) { 43 | motionRequestHorizontal = motionDirection.right; 44 | } else if (event.accelerationIncludingGravity.x > tiltThreshold) { 45 | motionRequestHorizontal = motionDirection.left; 46 | } 47 | }, true); 48 | 49 | document.addEventListener('keydown', (event) => { 50 | if ((event.key === "Up") || (event.key == "ArrowUp")) { 51 | universe.move_up(); 52 | } else if ((event.key === "Down") || (event.key === "ArrowDown")) { 53 | universe.move_down(); 54 | } else if ((event.key === "Left") || (event.key === "ArrowLeft")) { 55 | universe.move_left(); 56 | } else if ((event.key === "Right") || (event.key === "ArrowRight")) { 57 | universe.move_right(); 58 | } 59 | }); 60 | 61 | try { 62 | universe.initialize(); 63 | document.getElementById('html-console').innerHTML = "Game is running"; 64 | } catch (err) { 65 | document.getElementById('html-console').innerHTML = err.message; 66 | } 67 | 68 | var oldTimestamp = 0; 69 | function renderFrame(timestamp) { 70 | if (timestamp - oldTimestamp > 200) { 71 | 72 | if (motionRequestVertical == motionDirection.up) { // up 73 | universe.move_up(); 74 | } else if (motionRequestVertical == motionDirection.down) { // down 75 | universe.move_down(); 76 | } 77 | 78 | if (motionRequestHorizontal == motionDirection.left) { // left 79 | universe.move_left(); 80 | } else if (motionRequestHorizontal == motionDirection.right) { // right 81 | universe.move_right(); 82 | } 83 | motionRequestVertical = motionDirection.none; 84 | motionRequestHorizontal = motionDirection.none; 85 | 86 | universe.render_frame(); 87 | oldTimestamp = timestamp; 88 | } 89 | requestAnimationFrame(renderFrame); 90 | } 91 | requestAnimationFrame(renderFrame); 92 | }) 93 | .catch(console.error); 94 | 95 | -------------------------------------------------------------------------------- /wasm/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"{{project-name}}", 3 | "short_name":"{{crate_name}}", 4 | "start_url":"https://georgik.rocks/wp-content/html5/spooky/", 5 | "display":"standalone", 6 | "background_color":"#5900b3", 7 | "theme_color":"#000000", 8 | "scope": ".", 9 | "description":"This is PWA version of Rust application for ESP32.", 10 | "icons":[ 11 | { 12 | "src": "icons/manifest-icon-192.maskable.png", 13 | "sizes": "192x192", 14 | "type": "image/png", 15 | "purpose": "any" 16 | }, 17 | { 18 | "src": "icons/manifest-icon-192.maskable.png", 19 | "sizes": "192x192", 20 | "type": "image/png", 21 | "purpose": "maskable" 22 | }, 23 | { 24 | "src": "icons/manifest-icon-512.maskable.png", 25 | "sizes": "512x512", 26 | "type": "image/png", 27 | "purpose": "any" 28 | }, 29 | { 30 | "src": "icons/manifest-icon-512.maskable.png", 31 | "sizes": "512x512", 32 | "type": "image/png", 33 | "purpose": "maskable" 34 | } 35 | ] 36 | 37 | } 38 | -------------------------------------------------------------------------------- /wasm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "webpack", 4 | "serve": "webpack serve --host 0.0.0.0 --port 8443 --https", 5 | "pwa": "webpack serve --host 0.0.0.0 --port 8080" 6 | }, 7 | "devDependencies": { 8 | "@wasm-tool/wasm-pack-plugin": "1.5.0", 9 | "html-webpack-plugin": "^5.3.2", 10 | "text-encoding": "^0.7.0", 11 | "webpack": "^5.49.0", 12 | "webpack-cli": "^4.10.0", 13 | "webpack-dev-server": "^3.11.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /wasm/service-worker.js: -------------------------------------------------------------------------------- 1 | importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.3.1/workbox-sw.js'); 2 | 3 | workbox.setConfig({ 4 | // set the local path is not using Google CDN 5 | //modulePathPrefix: '/directory/to/workbox/' 6 | 7 | // By default, workbox-sw will use the debug build for sites on localhost, 8 | // but for any other origin it’ll use the production build. Force by setting to true. 9 | // debug: true 10 | }); 11 | // Force verbose logging even for the production 12 | // workbox.core.setLogLevel(workbox.core.LOG_LEVELS.debug) 13 | 14 | // Cache settings 15 | workbox.core.setCacheNameDetails({ 16 | // set the default cache name prefix. each domain should be unique to stop clashes 17 | // this is used for runtime and precaching only 18 | prefix: 'spooky-cache' 19 | }); 20 | 21 | // Using cache first strategy since the JS files are fingerprinted and this 22 | // filename will change once a new version is created 23 | workbox.routing.registerRoute( 24 | // match only with assets on the assets domain 25 | new RegExp('https://georgik.rocks/.*\.js$'), 26 | workbox.strategies.cacheFirst({ 27 | cacheName: 'spooky-js-cache', 28 | plugins: [ 29 | new workbox.expiration.Plugin({ 30 | // 28 days cache before expiration 31 | maxAgeSeconds: 24 * 60 * 60 * 28, 32 | // Opt-in to automatic cleanup whenever a quota errors occurs anywhere in Workbox 33 | purgeOnQuotaError: true // Opt-in to automatic cleanup. 34 | }) 35 | ] 36 | }) 37 | ); 38 | 39 | // Using cache first strategy since the CSS files are fingerprinted and this 40 | // filename will change once a new version is created 41 | workbox.routing.registerRoute( 42 | // match only with assets on the assets domain 43 | new RegExp('https://georgik.rocks/.*\.css$'), 44 | workbox.strategies.cacheFirst({ 45 | cacheName: 'spooky-css-cache', 46 | plugins: [ 47 | new workbox.expiration.Plugin({ 48 | // 28 days cache before expiration 49 | maxAgeSeconds: 24 * 60 * 60 * 28, 50 | // Opt-in to automatic cleanup whenever a quota errors occurs anywhere in Workbox 51 | purgeOnQuotaError: true // Opt-in to automatic cleanup. 52 | }) 53 | ] 54 | }) 55 | ); 56 | 57 | // Using cache first strategy since the WOFF2 files are fingerprinted and this 58 | // filename will change once a new version is created 59 | workbox.routing.registerRoute( 60 | // match only with assets on the assets domain 61 | new RegExp('https://georgik.rocks/.*\.woff2$'), 62 | workbox.strategies.cacheFirst({ 63 | cacheName: 'spooky-font-cache', 64 | plugins: [ 65 | new workbox.expiration.Plugin({ 66 | // 28 days cache before expiration 67 | maxAgeSeconds: 24 * 60 * 60 * 28, 68 | // Opt-in to automatic cleanup whenever a quota errors occurs anywhere in Workbox 69 | purgeOnQuotaError: true // Opt-in to automatic cleanup. 70 | }) 71 | ] 72 | }) 73 | ); 74 | 75 | // If any precache rules are defined in the Workbox setup this is where they will be injected 76 | workbox.precaching.precacheAndRoute([]); 77 | 78 | // skip the 'worker in waiting' phase and activate immediately 79 | workbox.skipWaiting(); 80 | // claim any clients that match the worker scope immediately. requests on these pages will 81 | // now go via the service worker. 82 | workbox.clientsClaim(); 83 | 84 | 85 | -------------------------------------------------------------------------------- /wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | // #![no_std] 2 | // #![no_main] 3 | 4 | use embedded_graphics::{ 5 | pixelcolor::Rgb565, 6 | }; 7 | use embedded_graphics_web_simulator::{ 8 | display::{WebSimulatorDisplay}, output_settings::OutputSettingsBuilder, 9 | }; 10 | 11 | use wasm_bindgen::prelude::*; 12 | use web_sys::{console}; 13 | 14 | use {{crate_name}}_engine::{ engine::Engine }; 15 | 16 | #[wasm_bindgen] 17 | pub struct Universe { 18 | engine: Engine>, 19 | } 20 | 21 | fn get_seed_buffer() -> Option<[u8; 32]> { 22 | let mut seed_buffer = [0u8; 32]; 23 | getrandom::getrandom(&mut seed_buffer).unwrap(); 24 | Some(seed_buffer) 25 | } 26 | 27 | 28 | #[wasm_bindgen] 29 | impl Universe { 30 | 31 | pub fn new() -> Universe { 32 | Universe { 33 | engine: { 34 | let document = web_sys::window().unwrap().document().unwrap(); 35 | let output_settings = OutputSettingsBuilder::new() 36 | .scale(2) 37 | // .pixel_spacing(1) 38 | .build(); 39 | let display = WebSimulatorDisplay::new( 40 | (320, 240), 41 | &output_settings, 42 | document.get_element_by_id("graphics").as_ref(), 43 | ); 44 | // let mut data = [Rgb565::BLACK ; 320*240]; 45 | // let fbuf = FrameBuf::new(&mut data, 320, 240); 46 | // let spritebuf = SpriteBuf::new(fbuf); 47 | Engine::new(display, get_seed_buffer()) 48 | } 49 | } 50 | } 51 | 52 | pub fn move_up(&mut self) { 53 | self.engine.move_up(); 54 | } 55 | 56 | pub fn move_down(&mut self) { 57 | self.engine.move_down(); 58 | } 59 | 60 | pub fn move_left(&mut self) { 61 | self.engine.move_left(); 62 | } 63 | 64 | pub fn move_right(&mut self) { 65 | self.engine.move_right(); 66 | } 67 | 68 | pub fn initialize(&mut self) { 69 | self.engine.initialize(); 70 | } 71 | 72 | pub fn render_frame(&mut self) { 73 | 74 | console::log_1(&"tick".into()); 75 | self.engine.tick(); 76 | let display:&mut WebSimulatorDisplay = self.engine.draw(); 77 | display.flush().unwrap(); 78 | 79 | } 80 | } -------------------------------------------------------------------------------- /wasm/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const webpack = require('webpack'); 4 | const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin"); 5 | 6 | module.exports = { 7 | entry: './index.js', 8 | output: { 9 | path: path.resolve(__dirname, 'dist'), 10 | filename: 'index.js', 11 | }, 12 | plugins: [ 13 | new HtmlWebpackPlugin({ 14 | template: 'index.html' 15 | }), 16 | new WasmPackPlugin({ 17 | crateDirectory: path.resolve(__dirname, ".") 18 | }), 19 | // Have this example work in Edge which doesn't ship `TextEncoder` or 20 | // `TextDecoder` at this time. 21 | new webpack.ProvidePlugin({ 22 | TextDecoder: ['text-encoding', 'TextDecoder'], 23 | TextEncoder: ['text-encoding', 'TextEncoder'] 24 | }) 25 | ], 26 | mode: 'development', 27 | experiments: { 28 | asyncWebAssembly: true 29 | } 30 | }; 31 | --------------------------------------------------------------------------------