├── .dockerignore ├── .vscode ├── settings.json ├── launch.json └── tasks.json ├── rust-toolchain.toml ├── wokwi.toml ├── .github ├── dependabot.yml ├── workflows │ ├── issue_handler.yml │ ├── ci.yml │ └── ci_docker.yml ├── verify.sh └── rust_ci.yml ├── .gitignore ├── scripts ├── build.sh └── flash.sh ├── post-script.rhai ├── .cargo └── config.toml ├── diagram.json ├── src └── main.rs ├── LICENSE-MIT ├── .devcontainer ├── devcontainer.json └── Dockerfile ├── Cargo.toml ├── cargo-generate.toml ├── README.md ├── docs └── README.md ├── pre-script.rhai └── LICENSE-APACHE /.dockerignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "{{ toolchain }}" 3 | {%- if arch == "riscv" %} 4 | components = ["rust-src"] 5 | targets = ["{{ rust_target }}"] 6 | {% endif %} 7 | -------------------------------------------------------------------------------- /wokwi.toml: -------------------------------------------------------------------------------- 1 | [wokwi] 2 | version = 1 3 | gdbServerPort = 3333 4 | elf = "target/{{ rust_target }}/debug/{{ project-name }}" 5 | firmware = "target/{{ rust_target }}/debug/{{ project-name }}" 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | # Check for updates every Monday 6 | schedule: 7 | interval: "weekly" 8 | open-pull-requests-limit: 20 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | which idf.py >/dev/null || { 4 | source ~/export-esp.sh >/dev/null 2>&1 5 | } 6 | 7 | case "$1" in 8 | "" | "release") 9 | cargo build --release 10 | ;; 11 | "debug") 12 | cargo build 13 | ;; 14 | *) 15 | echo "Wrong argument. Only \"debug\"/\"release\" arguments are supported" 16 | exit 1 17 | ;; 18 | esac 19 | -------------------------------------------------------------------------------- /.github/workflows/issue_handler.yml: -------------------------------------------------------------------------------- 1 | name: Add new issues to project 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@v1.0.2 14 | with: 15 | project-url: https://github.com/orgs/esp-rs/projects/2 16 | github-token: ${{ secrets.PAT }} 17 | -------------------------------------------------------------------------------- /scripts/flash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | BUILD_MODE="" 6 | case "$1" in 7 | "" | "release") 8 | bash scripts/build.sh 9 | BUILD_MODE="release" 10 | ;; 11 | "debug") 12 | bash scripts/build.sh debug 13 | BUILD_MODE="debug" 14 | ;; 15 | *) 16 | echo "Wrong argument. Only \"debug\"/\"release\" arguments are supported" 17 | exit 1 18 | ;; 19 | esac 20 | 21 | web-flash --chip {{ mcu }} target/{{ rust_target }}/${BUILD_MODE}/{{ crate_name }} 22 | -------------------------------------------------------------------------------- /post-script.rhai: -------------------------------------------------------------------------------- 1 | if variable::get("wifi"){ 2 | print("\nFor more information and examples of esp-wifi showcasing Wifi,BLE and ESP-NOW, see https://github.com/esp-rs/esp-wifi/blob/main/esp-wifi/docs/examples.md\n"); 3 | } 4 | 5 | if variable::get("ci") { 6 | file::rename(".github/rust_ci.yml", ".github/workflows/rust_ci.yml"); 7 | } 8 | 9 | 10 | try { 11 | system::command("cargo", ["fmt"]); 12 | } catch { 13 | print("Failed to run rustfmt, please ensure rustfmt is installed and in your PATH variable."); 14 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Wokwi GDB", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/target/{{ rust_target }}/debug/{{ crate_name }}", 9 | "cwd": "${workspaceFolder}", 10 | "MIMode": "gdb", 11 | "miDebuggerPath": "${userHome}/.espressif/tools/{{ gcc_target }}/esp-2021r2-patch3-8.4.0/{{ gcc_target }}/bin/{{ gcc_target }}-gdb", 12 | "miDebuggerServerAddress": "localhost:3333" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.{{ rust_target }}] 2 | runner = "espflash flash --monitor" 3 | 4 | 5 | [env] 6 | ESP_LOGLEVEL="INFO" 7 | 8 | [build] 9 | rustflags = [ 10 | {%- if arch == "xtensa" %} 11 | "-C", "link-arg=-nostartfiles", 12 | {% else %} 13 | # Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.) 14 | # NOTE: May negatively impact performance of produced code 15 | "-C", "force-frame-pointers", 16 | {% endif -%} 17 | ] 18 | 19 | target = "{{ rust_target }}" 20 | 21 | [unstable] 22 | {% if alloc -%} 23 | build-std = ["alloc", "core"] 24 | {%- else -%} 25 | build-std = ["core"] 26 | {%- endif %} 27 | -------------------------------------------------------------------------------- /diagram.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "editor": "wokwi", 4 | "author": "{{authors}}", 5 | "parts": [ 6 | { 7 | "type": "{{ wokwi_board }}", 8 | "id": "esp", 9 | "top": 0.59, 10 | "left": 0.67, 11 | "attrs": { 12 | "flashSize": "16" 13 | } 14 | } 15 | ], 16 | "connections": [ 17 | [ 18 | "esp:TX", 19 | "$serialMonitor:RX", 20 | "", 21 | [] 22 | ], 23 | [ 24 | "esp:RX", 25 | "$serialMonitor:TX", 26 | "", 27 | [] 28 | ] 29 | ], 30 | "serialMonitor": { 31 | "display": "terminal", 32 | "convertEol": true 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use esp_backtrace as _; 5 | use esp_hal::{ 6 | clock::ClockControl, delay::Delay, peripherals::Peripherals, prelude::*, system::SystemControl, 7 | }; 8 | 9 | {% if alloc -%} 10 | {{ alloc_snippet }} 11 | {% endif -%} 12 | 13 | #[entry] 14 | fn main() -> ! { 15 | let peripherals = Peripherals::take(); 16 | let system = SystemControl::new(peripherals.SYSTEM); 17 | 18 | let clocks = ClockControl::max(system.clock_control).freeze(); 19 | let delay = Delay::new(&clocks); 20 | 21 | {%- if alloc %} 22 | init_heap(); 23 | {%- endif %} 24 | 25 | esp_println::logger::init_logger_from_env(); 26 | 27 | {% if wifi -%} 28 | {{ esp_wifi_snippet }} 29 | {% endif -%} 30 | 31 | loop { 32 | log::info!("Hello world!"); 33 | delay.delay(500.millis()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.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 | "inputs": [ 30 | { 31 | "type": "pickString", 32 | "id": "buildMode", 33 | "description": "Select the build mode:", 34 | "options": [ 35 | "release", 36 | "debug" 37 | ], 38 | "default": "release" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright [year] [fullname] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /.github/verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo "Verifying $1" 6 | 7 | # If you want to execute this script locally, you need to update the path to 8 | # esp-template direcotry and execute it from a folder outside of the esp-template 9 | template_path="esp-template-gh" 10 | 11 | # Function to perform build, Rustfmt check, and Clippy check 12 | perform_checks() { 13 | cd "$1" 14 | echo " - Build check" 15 | cargo build --release 16 | echo "- Rustfmt check" 17 | cargo fmt -- --check 18 | echo "- Clippy check" 19 | cargo clippy --no-deps -- -D warnings -A dead_code -A clippy::empty_loop 20 | cd .. 21 | } 22 | 23 | # Generate templates 24 | cargo generate \ 25 | -a \ 26 | --path $template_path --name=test-complex --silent --vcs=none \ 27 | -d advanced=true -d ci=false -d devcontainer=false -d wokwi=false \ 28 | -d alloc=true -d wifi=true -d mcu=$1 29 | 30 | cargo generate \ 31 | -a \ 32 | --path $template_path --name=test-simple --silent --vcs=none \ 33 | -d advanced=false -d mcu=$1 34 | 35 | # Perform checks 36 | perform_checks test-complex 37 | perform_checks test-simple 38 | 39 | # Clean up 40 | rm -rf test-complex test-simple 41 | -------------------------------------------------------------------------------- /.github/rust_ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "**/README.md" 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | {% raw -%} 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | {%- endraw %} 15 | 16 | jobs: 17 | rust-checks: 18 | name: Rust Checks 19 | runs-on: ubuntu-latest 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | action: 24 | - command: build 25 | args: --release 26 | - command: fmt 27 | args: --all -- --check --color always 28 | - command: clippy 29 | args: --all-features --workspace -- -D warnings 30 | steps: 31 | - name: Checkout repository 32 | uses: actions/checkout@v4 33 | - name: Setup Rust 34 | {%- if arch == "riscv" %} 35 | uses: dtolnay/rust-toolchain@v1 36 | with: 37 | target: {{ rust_target }} 38 | toolchain: nightly 39 | components: rust-src, rustfmt, clippy 40 | {%- else %} 41 | uses: esp-rs/xtensa-toolchain@v1.5 42 | with: 43 | default: true 44 | buildtargets: {{ mcu }} 45 | ldproxy: false 46 | {%- endif %} 47 | - name: Enable caching 48 | uses: Swatinem/rust-cache@v2 49 | - name: Run command 50 | {%- raw %} 51 | run: cargo ${{ matrix.action.command }} ${{ matrix.action.args }} 52 | {%- endraw %} 53 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{ crate_name }}", 3 | // Select between image and build properties to pull or build the image. 4 | // "image": "docker.io/espressif/idf-rust:{{ mcu }}_latest", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | "CONTAINER_USER": "esp", 9 | "CONTAINER_GROUP": "esp", 10 | "ESP_BOARD": "{{ mcu }}" 11 | } 12 | }, 13 | "customizations": { 14 | "vscode": { 15 | "settings": { 16 | "editor.formatOnPaste": true, 17 | "editor.formatOnSave": true, 18 | "editor.formatOnSaveMode": "file", 19 | "editor.formatOnType": true, 20 | "lldb.executable": "/usr/bin/lldb", 21 | "files.watcherExclude": { 22 | "**/target/**": true 23 | }, 24 | "rust-analyzer.checkOnSave.command": "clippy", 25 | "rust-analyzer.checkOnSave.allTargets": false, 26 | "[rust]": { 27 | "editor.defaultFormatter": "rust-lang.rust-analyzer" 28 | } 29 | }, 30 | "extensions": [ 31 | "rust-lang.rust-analyzer", 32 | "tamasfe.even-better-toml", 33 | "serayuzgur.crates", 34 | "mutantdino.resourcemonitor", 35 | "yzhang.markdown-all-in-one", 36 | "ms-vscode.cpptools", 37 | "actboy168.tasks", 38 | "Wokwi.wokwi-vscode" 39 | ] 40 | } 41 | }, 42 | "forwardPorts": [ 43 | 8000, 44 | 3333 45 | ], 46 | "workspaceMount": "source=${localWorkspaceFolder},target=/home/esp/{{ crate_name }},type=bind,consistency=cached", 47 | "workspaceFolder": "/home/esp/{{ crate_name }}" 48 | } 49 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{ project-name }}" 3 | version = "0.1.0" 4 | authors = ["{{ authors }}"] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [dependencies] 9 | esp-backtrace = { version = "0.13.0", features = [ 10 | "{{ mcu }}", 11 | "exception-handler", 12 | "panic-handler", 13 | "println", 14 | ] } 15 | esp-hal = { version = "0.19.0", features = [ "{{ mcu }}" ] } 16 | esp-println = { version = "0.10.0", features = ["{{ mcu }}", "log"] } 17 | log = { version = "0.4.21" } 18 | {% if alloc -%} 19 | esp-alloc = { version = "0.4.0" } 20 | {% endif -%} 21 | {% if wifi -%} 22 | embedded-svc = { version = "0.26.1", default-features = false, features = [] } 23 | embedded-io = "0.6.1" 24 | esp-wifi = { version = "0.7.0", features = [ 25 | "{{ mcu }}", 26 | "phy-enable-usb", 27 | "utils", 28 | "{{ esp_wifi_feature }}", 29 | ] } 30 | heapless = { version = "0.8.0", default-features = false } 31 | smoltcp = { version = "0.11.0", default-features = false, features = [ 32 | "medium-ethernet", 33 | "proto-dhcpv4", 34 | "proto-igmp", 35 | "proto-ipv4", 36 | "socket-dhcpv4", 37 | "socket-icmp", 38 | "socket-raw", 39 | "socket-tcp", 40 | "socket-udp", 41 | ] } 42 | {% endif -%} 43 | 44 | [profile.dev] 45 | # Rust debug is too slow. 46 | # For debug builds always builds with some optimization 47 | opt-level = "s" 48 | 49 | [profile.release] 50 | codegen-units = 1 # LLVM can perform better optimizations using a single thread 51 | debug = 2 52 | debug-assertions = false 53 | incremental = false 54 | lto = 'fat' 55 | opt-level = 's' 56 | overflow-checks = false 57 | -------------------------------------------------------------------------------- /cargo-generate.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | cargo_generate_version = ">=0.17.4" 3 | ignore = [".git", ".github/dependabot.yml", ".github/workflows/ci_docker.yml", ".github/workflows/ci.yml", ".github/workflows/issue_handler.yml", ".github/verify.sh", "README.md"] 4 | 5 | [hooks] 6 | pre = ["pre-script.rhai"] 7 | post = ["post-script.rhai"] 8 | 9 | [placeholders.mcu] 10 | type = "string" 11 | prompt = "Which MCU to target?" 12 | choices = ["esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32s2", "esp32s3"] 13 | default = "esp32" 14 | 15 | [placeholders.advanced] 16 | type = "bool" 17 | prompt = "Configure advanced template options?" 18 | default = false 19 | 20 | [conditional.'advanced'.placeholders.alloc] 21 | type = "bool" 22 | prompt = "Enable allocations via the esp-alloc crate?" 23 | default = false 24 | 25 | [conditional.'advanced'.placeholders.wifi] 26 | type = "bool" 27 | prompt = "Enable WiFi/Bluetooth/ESP-NOW via the esp-wifi crate?" 28 | default = false 29 | 30 | [conditional.'advanced'.placeholders.devcontainer] 31 | type = "bool" 32 | prompt = "Configure project to use Dev Containers (VS Code and GitHub Codespaces)?" 33 | default = false 34 | 35 | [conditional.'advanced'.placeholders.wokwi] 36 | type = "bool" 37 | prompt = "Configure project to support Wokwi simulation with Wokwi VS Code extension?" 38 | default = false 39 | 40 | [conditional.'advanced'.placeholders.ci] 41 | type = "bool" 42 | prompt = "Add CI files for GitHub Action?" 43 | default = false 44 | 45 | [conditional.'!devcontainer'] 46 | ignore = [ 47 | ".devcontainer/", 48 | ".vscode/tasks.json", 49 | "docs/", 50 | "scripts/", 51 | ".dockerignore", 52 | ] 53 | 54 | [conditional.'!ci'] 55 | ignore = [".github"] 56 | 57 | [conditional.'!wokwi'] 58 | ignore = [".vscode/launch.json", "diagram.json", "wokwi.toml"] 59 | 60 | [conditional.'!advanced'] 61 | ignore = [ 62 | ".github", 63 | ".vscode/launch.json", 64 | ".vscode/tasks.json", 65 | ".devcontainer/", 66 | "docs/", 67 | "scripts/", 68 | ".github", 69 | ".dockerignore", 70 | "diagram.json", 71 | "wokwi.toml", 72 | ] 73 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | paths-ignore: 5 | - "**/README.md" 6 | pull_request: 7 | paths-ignore: 8 | - "**/README.md" 9 | workflow_dispatch: 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | SSID: "" 15 | PASSWORD: "" 16 | 17 | # Cancel any currently running workflows from the same PR, branch, or 18 | # tag when a new workflow is triggered. 19 | # 20 | # https://stackoverflow.com/a/66336834 21 | concurrency: 22 | cancel-in-progress: true 23 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 24 | 25 | jobs: 26 | verify: 27 | runs-on: ubuntu-latest 28 | 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | chip: [ 33 | "esp32", 34 | "esp32c2", 35 | "esp32c3", 36 | "esp32c6", 37 | "esp32h2", 38 | "esp32s2", 39 | "esp32s3", 40 | ] 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | with: 45 | path: "esp-template-gh" 46 | 47 | # Rust toolchain for Xtensa: 48 | - if: matrix.chip == 'esp32' || matrix.chip == 'esp32s2' || matrix.chip == 'esp32s3' 49 | uses: esp-rs/xtensa-toolchain@v1.5 50 | with: 51 | default: true 52 | buildtargets: ${{ matrix.chip }} 53 | ldproxy: false 54 | 55 | # Rust toolchain for RISC-V: 56 | - if: matrix.chip != 'esp32' && matrix.chip != 'esp32s2' && matrix.chip != 'esp32s3' 57 | uses: dtolnay/rust-toolchain@nightly 58 | with: 59 | target: riscv32imac-unknown-none-elf 60 | components: clippy,rustfmt,rust-src 61 | 62 | - uses: Swatinem/rust-cache@v2 63 | 64 | - name: Install cargo-generate (binary) 65 | continue-on-error: true 66 | id: binary 67 | run: | 68 | VERSION="$(git ls-remote --refs --sort="version:refname" --tags "https://github.com/cargo-generate/cargo-generate" | cut -d/ -f3- | tail -n1)" 69 | sudo curl \ 70 | -L "https://github.com/cargo-generate/cargo-generate/releases/latest/download/cargo-generate-$VERSION-x86_64-unknown-linux-gnu.tar.gz" \ 71 | -o "/home/runner/.cargo/bin/cargo-generate.tar.gz" 72 | tar xf "/home/runner/.cargo/bin/cargo-generate.tar.gz" -C "/home/runner/.cargo/bin/" 73 | chmod u+x "/home/runner/.cargo/bin/cargo-generate" 74 | 75 | - name: Install cargo-generate (source) 76 | if: steps.binary.outcome != 'success' 77 | run: cargo install cargo-generate 78 | 79 | - name: generate and check 80 | run: bash esp-template-gh/.github/verify.sh ${{ matrix.chip }} 81 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Base image 2 | ARG VARIANT=bookworm-slim 3 | FROM debian:${VARIANT} 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | ENV LC_ALL=C.UTF-8 6 | ENV LANG=C.UTF-8 7 | 8 | # Arguments 9 | ARG CONTAINER_USER=esp 10 | ARG CONTAINER_GROUP=esp 11 | ARG ESP_BOARD=all 12 | ARG GITHUB_TOKEN 13 | 14 | # Install dependencies 15 | RUN apt-get update \ 16 | && apt-get install -y git curl llvm-dev libclang-dev clang unzip \ 17 | libusb-1.0-0 libssl-dev libudev-dev pkg-config \ 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 rustup 26 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \ 27 | --default-toolchain none -y --profile minimal 28 | 29 | # Update envs 30 | ENV PATH=${PATH}:/home/${CONTAINER_USER}/.cargo/bin 31 | 32 | # Install extra crates 33 | RUN ARCH=$($HOME/.cargo/bin/rustup show | grep "Default host" | sed -e 's/.* //') && \ 34 | curl -L "https://github.com/esp-rs/espup/releases/latest/download/espup-${ARCH}" -o "${HOME}/.cargo/bin/espup" && \ 35 | chmod u+x "${HOME}/.cargo/bin/espup" && \ 36 | curl -L "https://github.com/esp-rs/espflash/releases/latest/download/cargo-espflash-${ARCH}.zip" -o "${HOME}/.cargo/bin/cargo-espflash.zip" && \ 37 | unzip "${HOME}/.cargo/bin/cargo-espflash.zip" -d "${HOME}/.cargo/bin/" && \ 38 | rm "${HOME}/.cargo/bin/cargo-espflash.zip" && \ 39 | chmod u+x "${HOME}/.cargo/bin/cargo-espflash" && \ 40 | curl -L "https://github.com/esp-rs/espflash/releases/latest/download/espflash-${ARCH}.zip" -o "${HOME}/.cargo/bin/espflash.zip" && \ 41 | unzip "${HOME}/.cargo/bin/espflash.zip" -d "${HOME}/.cargo/bin/" && \ 42 | rm "${HOME}/.cargo/bin/espflash.zip" && \ 43 | chmod u+x "${HOME}/.cargo/bin/espflash" && \ 44 | curl -L "https://github.com/esp-rs/esp-web-flash-server/releases/latest/download/web-flash-${ARCH}.zip" -o "${HOME}/.cargo/bin/web-flash.zip" && \ 45 | unzip "${HOME}/.cargo/bin/web-flash.zip" -d "${HOME}/.cargo/bin/" && \ 46 | rm "${HOME}/.cargo/bin/web-flash.zip" && \ 47 | chmod u+x "${HOME}/.cargo/bin/web-flash" 48 | 49 | # Install Xtensa Rust 50 | RUN if [ -n "${GITHUB_TOKEN}" ]; then export GITHUB_TOKEN=${GITHUB_TOKEN}; fi \ 51 | && ${HOME}/.cargo/bin/espup install\ 52 | --targets "${ESP_BOARD}" \ 53 | --log-level debug \ 54 | --export-file /home/${CONTAINER_USER}/export-esp.sh 55 | 56 | # Set default toolchain 57 | RUN rustup default {{ toolchain }} 58 | 59 | # Activate ESP environment 60 | RUN echo "source /home/${CONTAINER_USER}/export-esp.sh" >> ~/.bashrc 61 | 62 | CMD [ "/bin/bash" ] 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp-template 2 | [![CI](https://github.com/esp-rs/esp-template/actions/workflows/ci.yml/badge.svg)](https://github.com/esp-rs/esp-template/actions/workflows/ci.yml) 3 | [![Container CI](https://github.com/esp-rs/esp-template/actions/workflows/ci_docker.yml/badge.svg)](https://github.com/esp-rs/esp-template/actions/workflows/ci_docker.yml) 4 | 5 | A minimalist template for use with [cargo-generate] to create `no_std` applications targeting Espressif's line of SoCs and modules. At present, this template supports the **ESP32**, **ESP32-C2**, **ESP32-C3**,**ESP32-C6**, **ESP32-H2**, **ESP32-S2**, and **ESP32-S3**; additional devices will be added as they become available. 6 | 7 | To generate a project using this template: 8 | 9 | ```bash 10 | cargo generate esp-rs/esp-template 11 | ``` 12 | 13 | After running the command, there will be a few prompts: 14 | - `Project Name`: Name of the crate. 15 | - `Which MCU to target?`: SoC model. 16 | - `Configure advanced template options?`: If `false`, skips the rest of the prompts and uses their default value. If `true`, you will be prompted with: 17 | - `Enable allocations via the esp-alloc crate?`: Adds [`esp-alloc`] dependency, and initializes the heap. 18 | - `Configure project to support Wokwi simulation with Wokwi VS Code extension?`: Adds support for Wokwi simulation using [VS Code Wokwi extension]. 19 | - `Setup logging using the log crate?`: Adds [`log`] dependency and initializes logging. 20 | - `Configure project to use Dev Containers (VS Code and GitHub Codespaces)?`: Adds support for: 21 | - [VS Code Dev Containers] 22 | - [GitHub Codespaces] 23 | 24 | Dev Containers also allow flashing from the container using [web flash] and have the [VS Code Wokwi extension] already installed. 25 | 26 | For a more detailed explanation of the template, see [Understanding esp-template] chapter of [The Rust on ESP Book]. If you're seeking a beginner's guide for utilizing `no_std` (bare metal), consult the [Embedded Rust (no_std) on Espressif][no_std-training] training. 27 | 28 | [cargo-generate]: https://github.com/cargo-generate/cargo-generate 29 | [`esp-alloc`]: https://github.com/esp-rs/esp-alloc 30 | [VS Code Dev Containers]: https://code.visualstudio.com/docs/remote/containers#_quick-start-open-an-existing-folder-in-a-container 31 | [GitHub Codespaces]: https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace 32 | [Wokwi simulator]: https://wokwi.com/ 33 | [VS Code Wokwi extension]: https://marketplace.visualstudio.com/items?itemName=wokwi.wokwi-vscode 34 | [web flash]: https://github.com/bjoernQ/esp-web-flash-server 35 | [Understanding esp-template]: https://esp-rs.github.io/book/writing-your-own-application/generate-project/esp-template.html 36 | [The Rust on ESP Book]: https://esp-rs.github.io/book/ 37 | [`log`]: https://docs.rs/log/latest/log/ 38 | [no_std-training]: https://esp-rs.github.io/no_std-training/ 39 | 40 | ## License 41 | 42 | Licensed under either of: 43 | 44 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 45 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 46 | 47 | at your option. 48 | 49 | ### Contribution 50 | 51 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in 52 | the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without 53 | any additional terms or conditions. 54 | -------------------------------------------------------------------------------- /.github/workflows/ci_docker.yml: -------------------------------------------------------------------------------- 1 | name: Container CI 2 | on: 3 | push: 4 | paths: 5 | - ".devcontainer/Dockerfile" 6 | pull_request: 7 | paths: 8 | - ".devcontainer/Dockerfile" 9 | schedule: 10 | - cron: "50 7 * * *" 11 | workflow_dispatch: 12 | 13 | env: 14 | CARGO_TERM_COLOR: always 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | # Cancel any currently running workflows from the same PR, branch, or 18 | # tag when a new workflow is triggered. 19 | # 20 | # https://stackoverflow.com/a/66336834 21 | concurrency: 22 | cancel-in-progress: true 23 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 24 | 25 | jobs: 26 | docker-checks: 27 | name: Build project using the generated Dockerfile 28 | runs-on: ubuntu-latest 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | board: ["esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32s2", "esp32s3"] 33 | steps: 34 | - uses: actions/checkout@v4 35 | with: 36 | path: /home/runner/work/esp-template/esp-template/github-esp-template 37 | - uses: Swatinem/rust-cache@v2 38 | - name: Setup | cargo-generate (binaries) 39 | id: binaries 40 | continue-on-error: true 41 | run: | 42 | sudo curl -L "https://github.com/cargo-generate/cargo-generate/releases/latest/download/cargo-generate-$(git ls-remote --refs --sort="version:refname" --tags "https://github.com/cargo-generate/cargo-generate" | cut -d/ -f3- | tail -n1)-x86_64-unknown-linux-gnu.tar.gz" -o "/home/runner/.cargo/bin/cargo-generate.tar.gz" 43 | tar xf "/home/runner/.cargo/bin/cargo-generate.tar.gz" -C /home/runner/.cargo/bin 44 | chmod u+x /home/runner/.cargo/bin/cargo-generate 45 | - name: Setup | cargo-generate (cargo) 46 | if: steps.binaries.outcome != 'success' 47 | run: cargo install cargo-generate 48 | - name: Generate 49 | if: matrix.board == 'esp32h2' 50 | run: cargo generate --path /home/runner/work/esp-template/esp-template/github-esp-template --allow-commands --name test-${{ matrix.board }} --vcs none --silent -d mcu=${{ matrix.board }} -d advanced=true -d devcontainer=true -d wokwi=false -d alloc=false -d ci=false 51 | - name: Generate 52 | if: matrix.board != 'esp32h2' 53 | run: cargo generate --path /home/runner/work/esp-template/esp-template/github-esp-template --allow-commands --name test-${{ matrix.board }} --vcs none --silent -d mcu=${{ matrix.board }} -d advanced=true -d wifi=false -d devcontainer=true -d wokwi=false -d alloc=false -d ci=false 54 | - name: Update ownership 55 | run: | 56 | sudo chown 1000:1000 -R test-${{ matrix.board }} 57 | - uses: docker/build-push-action@v6 58 | with: 59 | context: . 60 | tags: test-${{ matrix.board }}:latest 61 | build-args: | 62 | ESP_BOARD=${{ matrix.board }} 63 | GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} 64 | file: test-${{ matrix.board }}/.devcontainer/Dockerfile 65 | push: false 66 | - name: Run the build process with Docker 67 | uses: addnab/docker-run-action@v3 68 | with: 69 | image: test-${{ matrix.board }}:latest 70 | options: -u esp -v ${{ github.workspace }}/test-${{ matrix.board }}:/home/esp/test-${{ matrix.board }} 71 | run: | 72 | cd /home/esp/test-${{ matrix.board }} 73 | bash -c 'source /home/esp/export-esp.sh && cargo build' 74 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # {{ crate_name }} 2 | 3 | ## Dev Containers 4 | This repository offers Dev Containers supports for: 5 | - [VS Code Dev Containers](https://code.visualstudio.com/docs/remote/containers#_quick-start-open-an-existing-folder-in-a-container) 6 | - [GitHub Codespaces](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace) 7 | > **Note** 8 | > 9 | > In [order to use GitHub Codespaces](https://github.com/features/codespaces#faq) 10 | > the project needs to be published in a GitHub repository and the user needs 11 | > to be part of the Codespaces beta or have the project under an organization. 12 | 13 | If using VS Code or GitHub Codespaces, you can pull the image instead of building it 14 | from the Dockerfile by selecting the `image` property instead of `build` in 15 | `.devcontainer/devcontainer.json`. Further customization of the Dev Container can 16 | be achieved, see [`.devcontainer.json` reference](https://code.visualstudio.com/docs/remote/devcontainerjson-reference). 17 | 18 | When using Dev Containers, some tooling to facilitate building, flashing and 19 | simulating in Wokwi is also added. 20 | ### Build 21 | - Terminal approach: 22 | 23 | ``` 24 | scripts/build.sh [debug | release] 25 | ``` 26 | > If no argument is passed, `release` will be used as default 27 | 28 | 29 | - UI approach: 30 | 31 | The default build task is already set to build the project, and it can be used 32 | in VS Code and GH Codespaces: 33 | - From the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) (`Ctrl-Shift-P` or `Cmd-Shift-P`) run the `Tasks: Run Build Task` command. 34 | - `Terminal`-> `Run Build Task` in the menu. 35 | - With `Ctrl-Shift-B` or `Cmd-Shift-B`. 36 | - From the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) (`Ctrl-Shift-P` or `Cmd-Shift-P`) run the `Tasks: Run Task` command and 37 | select `Build`. 38 | - From UI: Press `Build` on the left side of the Status Bar. 39 | 40 | ### Flash 41 | 42 | > **Note** 43 | > 44 | > When using GitHub Codespaces, we need to make the ports 45 | > public, [see instructions](https://docs.github.com/en/codespaces/developing-in-codespaces/forwarding-ports-in-your-codespace#sharing-a-port). 46 | 47 | - Terminal approach: 48 | - Using `flash.sh` script: 49 | 50 | ``` 51 | scripts/flash.sh [debug | release] 52 | ``` 53 | > If no argument is passed, `release` will be used as default 54 | 55 | - UI approach: 56 | - From the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) (`Ctrl-Shift-P` or `Cmd-Shift-P`) run the `Tasks: Run Task` command and 57 | select `Build & Flash`. 58 | - From UI: Press `Build & Flash` on the left side of the Status Bar. 59 | - Any alternative flashing method from host machine. 60 | 61 | 62 | ### Wokwi Simulation 63 | 64 | #### VS Code Dev Containers and GitHub Codespaces 65 | 66 | The Dev Container includes the Wokwi Vs Code installed, hence you can simulate your built projects doing the following: 67 | 1. Press `F1` 68 | 2. Run `Wokwi: Start Simulator` 69 | 70 | > **Note** 71 | > 72 | > We assume that the project is built in `debug` mode, if you want to simulate projects in release, please update the `elf` and `firmware` proprieties in `wokwi.toml`. 73 | 74 | For more information and details on how to use the Wokwi extension, see [Getting Started] and [Debugging your code] Chapter of the Wokwi documentation. 75 | 76 | [Getting Started]: https://docs.wokwi.com/vscode/getting-started 77 | [Debugging your code]: https://docs.wokwi.com/vscode/debugging 78 | 79 | > **Warning** 80 | > 81 | > ESP32-C2 is not, yet, not supported in Wokwi. 82 | 83 | 84 | -------------------------------------------------------------------------------- /pre-script.rhai: -------------------------------------------------------------------------------- 1 | let metadata = #{ 2 | // Xtensa devices: 3 | esp32: #{ 4 | wokwi_board: "board-esp32-devkit-c-v4", 5 | esp_wifi_init: "Wifi", 6 | esp_wifi_feature: "wifi", 7 | esp_wifi_timer: "timer::timg::TimerGroup::new(peripherals.TIMG1, &clocks, None).timer0" 8 | }, 9 | esp32s2: #{ 10 | wokwi_board: "board-esp32-s2-devkitm-1", 11 | esp_wifi_init: "Wifi", 12 | esp_wifi_feature: "wifi", 13 | esp_wifi_timer: "timer::timg::TimerGroup::new(peripherals.TIMG1, &clocks, None).timer0" 14 | }, 15 | esp32s3: #{ 16 | wokwi_board: "board-esp32-s3-devkitc-1", 17 | esp_wifi_init: "Wifi", 18 | esp_wifi_feature: "wifi", 19 | esp_wifi_timer: "timer::timg::TimerGroup::new(peripherals.TIMG1, &clocks, None).timer0" 20 | }, 21 | 22 | // RISC-V devices: 23 | esp32c2: #{ 24 | extensions: "imc", 25 | wokwi_board: "", 26 | esp_wifi_init: "Wifi", 27 | esp_wifi_feature: "wifi", 28 | esp_wifi_timer: "timer::systimer::SystemTimer::new(peripherals.SYSTIMER).alarm0" 29 | }, 30 | esp32c3: #{ 31 | extensions: "imc", 32 | wokwi_board: "board-esp32-c3-devkitm-1", 33 | esp_wifi_init: "Wifi", 34 | esp_wifi_feature: "wifi", 35 | esp_wifi_timer: "timer::systimer::SystemTimer::new(peripherals.SYSTIMER).alarm0" 36 | }, 37 | esp32c6: #{ 38 | extensions: "imac", 39 | wokwi_board: "board-esp32-c6-devkitc-1", 40 | esp_wifi_init: "Wifi", 41 | esp_wifi_feature: "wifi", 42 | esp_wifi_timer: "timer::systimer::SystemTimer::new(peripherals.SYSTIMER).alarm0" 43 | }, 44 | esp32h2: #{ 45 | extensions: "imac", 46 | wokwi_board: "board-esp32-h2-devkitm-1", 47 | esp_wifi_init: "Ble", 48 | esp_wifi_feature: "ble", 49 | esp_wifi_timer: "timer::systimer::SystemTimer::new(peripherals.SYSTIMER).alarm0" 50 | }, 51 | }; 52 | 53 | let mcu = variable::get("mcu"); 54 | let meta = metadata.get(mcu); 55 | 56 | variable::set("esp_wifi_feature", meta.get("esp_wifi_feature")); 57 | variable::set("wokwi_board", meta.get("wokwi_board")); 58 | 59 | if mcu in ["esp32", "esp32s2", "esp32s3"] { 60 | // Xtensa devices: 61 | variable::set("arch", "xtensa"); 62 | variable::set("gcc_target", `xtensa-${mcu}-elf`); 63 | variable::set("rust_target", `xtensa-${mcu}-none-elf`); 64 | variable::set("toolchain", "esp"); 65 | } else { 66 | // RISC-V devices: 67 | let extensions = meta.get("extensions"); 68 | 69 | variable::set("arch", "riscv"); 70 | variable::set("gcc_target", "riscv32-esp-elf"); 71 | variable::set("rust_target", `riscv32${extensions}-unknown-none-elf`); 72 | variable::set("toolchain", "nightly"); 73 | } 74 | 75 | let advanced = variable::get("advanced"); 76 | if !advanced { 77 | variable::set("alloc", false); 78 | variable::set("ci", false); 79 | variable::set("devcontainer", false); 80 | variable::set("wokwi", false); 81 | variable::set("wifi", false); 82 | } 83 | 84 | // 85 | // Snippets - These should be short & self-contained, not depending on other snippets existing where possible. 86 | // 87 | 88 | // dependencies: none 89 | variable::set("alloc_snippet", 90 | ` 91 | extern crate alloc; 92 | use core::mem::MaybeUninit; 93 | 94 | #[global_allocator] 95 | static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); 96 | 97 | fn init_heap() { 98 | const HEAP_SIZE: usize = 32 * 1024; 99 | static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit(); 100 | 101 | unsafe { 102 | ALLOCATOR.init(HEAP.as_mut_ptr() as *mut u8, HEAP_SIZE); 103 | } 104 | } 105 | `); 106 | 107 | // depends on: `peripherals` being in scope 108 | variable::set("esp_wifi_snippet", 109 | ` 110 | let timer = esp_hal::timer::PeriodicTimer::new(esp_hal::${meta.esp_wifi_timer}.into()); 111 | let _init = esp_wifi::initialize( 112 | esp_wifi::EspWifiInitFor::${meta.esp_wifi_init}, 113 | timer, 114 | esp_hal::rng::Rng::new(peripherals.RNG), 115 | peripherals.RADIO_CLK, 116 | &clocks, 117 | ) 118 | .unwrap(); 119 | ` 120 | ); 121 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------