diff --git a/vinscant/.cargo/config.toml b/vinscant/.cargo/config.toml new file mode 100644 index 0000000..e227712 --- /dev/null +++ b/vinscant/.cargo/config.toml @@ -0,0 +1,25 @@ +[build] +target = "xtensa-esp32s2-espidf" +#target = "xtensa-esp32-espidf" + +[target.xtensa-esp32s2-espidf] +linker = "ldproxy" +runner = "espflash flash --monitor -T partitions.csv -B 921600" # Select this runner for espflash v3.x.x +rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110 + +[target.xtensa-esp32-espidf] +linker = "ldproxy" +runner = "espflash flash --monitor -B 921600" # Select this runner for espflash v3.x.x +rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110 + +[unstable] +build-std = ["std", "panic_abort"] + +[env] +MCU="esp32s2" +#MCU="esp32" +# Note: this variable is not used by the pio builder (`cargo build --features pio`) +ESP_IDF_VERSION = "v5.2.2" + +# Workaround for https://github.com/esp-rs/esp-idf-template/issues/174 +CRATE_CC_NO_DEFAULTS = "1" diff --git a/vinscant/.github/workflows/rust_ci.yml b/vinscant/.github/workflows/rust_ci.yml new file mode 100644 index 0000000..9c9fb34 --- /dev/null +++ b/vinscant/.github/workflows/rust_ci.yml @@ -0,0 +1,40 @@ +name: Continuous Integration + +on: + push: + paths-ignore: + - "**/README.md" + pull_request: + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + rust-checks: + name: Rust Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + action: + - command: build + args: --release + - command: fmt + args: --all -- --check --color always + - command: clippy + args: --all-targets --all-features --workspace -- -D warnings + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Rust + uses: esp-rs/xtensa-toolchain@v1.5 + with: + default: true + buildtargets: esp32s2 + ldproxy: true + - name: Enable caching + uses: Swatinem/rust-cache@v2 + - name: Run command + run: cargo ${{ matrix.action.command }} ${{ matrix.action.args }} diff --git a/vinscant/.gitignore b/vinscant/.gitignore index 3e652d3..470dacd 100644 --- a/vinscant/.gitignore +++ b/vinscant/.gitignore @@ -1,6 +1,5 @@ -key.txt -mfrc522.py -webrepl_cli.py - # ESP-IDF default build directory name -build +/.vscode +/.embuild +/target +cfg.toml diff --git a/vinscant/Cargo.lock b/vinscant/Cargo.lock new file mode 100644 index 0000000..211acf8 --- /dev/null +++ b/vinscant/Cargo.lock @@ -0,0 +1,2606 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "377e4c0ba83e4431b10df45c1d4666f178ea9c552cac93e60c3a88bf32785923" +dependencies = [ + "as-slice", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags 2.9.1", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.105", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bstr" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "build-time" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1219c19fc29b7bfd74b7968b420aff5bc951cf517800176e795d6b2300dd382" +dependencies = [ + "chrono", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + +[[package]] +name = "bytemuck" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "camino" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d07aa9a93b00c76f71bc35d598bed923f6d4f3a9ca5c24b7737ae1a292841c0" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "cargo_toml" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" +dependencies = [ + "serde", + "toml 0.7.8", +] + +[[package]] +name = "cc" +version = "1.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" +dependencies = [ + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "cvt" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ae9bf77fbf2d39ef573205d554d87e86c12f1994e9ea335b0651b9b278bcf1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.105", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "defmt" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" +dependencies = [ + "defmt 1.0.1", +] + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.14", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "embassy-executor" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90327bcc66333a507f89ecc4e2d911b265c45f5c9bc241f98eee076752d35ac6" +dependencies = [ + "critical-section", + "document-features", + "embassy-executor-macros", +] + +[[package]] +name = "embassy-executor-macros" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "embassy-futures" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" + +[[package]] +name = "embassy-sync" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-sink", + "futures-util", + "heapless", +] + +[[package]] +name = "embassy-time-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba" +dependencies = [ + "document-features", +] + +[[package]] +name = "embassy-time-queue-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc55c748d16908a65b166d09ce976575fb8852cf60ccd06174092b41064d8f83" +dependencies = [ + "embassy-executor", + "heapless", +] + +[[package]] +name = "embedded-can" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io", +] + +[[package]] +name = "embedded-svc" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7770e30ab55cfbf954c00019522490d6ce26a3334bede05a732ba61010e98e0" +dependencies = [ + "defmt 0.3.100", + "embedded-io", + "embedded-io-async", + "enumset", + "heapless", + "log", + "num_enum", + "serde", + "strum 0.25.0", + "strum_macros 0.25.3", +] + +[[package]] +name = "embuild" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e3e470e31fd4cae065d37f7cad56d42861ba1f9a35aa277694dee3d6b357c4" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "filetime", + "log", + "shlex", + "thiserror 1.0.69", +] + +[[package]] +name = "embuild" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e188ad2bbe82afa841ea4a29880651e53ab86815db036b2cb9f8de3ac32dad75" +dependencies = [ + "anyhow", + "bindgen", + "bitflags 1.3.2", + "cargo_toml", + "cmake", + "filetime", + "globwalk", + "home", + "log", + "regex", + "remove_dir_all", + "serde", + "serde_json", + "shlex", + "strum 0.24.1", + "tempfile", + "thiserror 1.0.69", + "toml 0.7.8", + "ureq", + "which", +] + +[[package]] +name = "enumset" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ee17054f550fd7400e1906e2f9356c7672643ed34008a9e8abe147ccd2d821" +dependencies = [ + "enumset_derive", + "serde", +] + +[[package]] +name = "enumset_derive" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d07902c93376f1e96c34abc4d507c0911df3816cef50b01f5a2ff3ad8c370d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "esp-idf-hal" +version = "0.45.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775ce25171dc4f615146a4a27ed3a64c6fd99ced77d7112062f2b19bf933f5db" +dependencies = [ + "atomic-waker", + "critical-section", + "embassy-sync", + "embedded-can", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "embedded-io-async", + "embuild 0.33.1", + "enumset", + "esp-idf-sys", + "heapless", + "log", + "nb 1.1.0", + "num_enum", +] + +[[package]] +name = "esp-idf-svc" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc07aaba257d28d54a96af005ca67d0b38876d8837f5d54a3e0547e100b219c" +dependencies = [ + "embassy-futures", + "embassy-time-driver", + "embassy-time-queue-utils", + "embedded-hal-async", + "embedded-svc", + "embuild 0.33.1", + "enumset", + "esp-idf-hal", + "futures-io", + "heapless", + "log", + "num_enum", + "uncased", +] + +[[package]] +name = "esp-idf-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb77a3d02b579a60a811ed9be22b78c5e794bc492d833ee7fc44d3a0155885e1" +dependencies = [ + "anyhow", + "build-time", + "cargo_metadata", + "cmake", + "const_format", + "embuild 0.33.1", + "envy", + "libc", + "regex", + "serde", + "strum 0.24.1", + "which", +] + +[[package]] +name = "fast-srgb8" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_at" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14af6c9694ea25db25baa2a1788703b9e7c6648dcaeeebeb98f7561b5384c036" +dependencies = [ + "aligned", + "cfg-if", + "cvt", + "libc", + "nix", + "windows-sys 0.52.0", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "globset" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "serde", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + +[[package]] +name = "libredox" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +dependencies = [ + "bitflags 2.9.1", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "litrs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "mfrc522" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "543f2c392f438e427aa9009338710434884b0dc95d0c39110f456846be0aa352" +dependencies = [ + "embedded-hal 1.0.0", + "heapless", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "normpath" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "palette" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +dependencies = [ + "approx", + "fast-srgb8", + "palette_derive", + "phf", +] + +[[package]] +name = "palette_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn 2.0.105", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit 0.22.27", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "proc-macro2" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "remove_dir_all" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a694f9e0eb3104451127f6cc1e5de55f59d3b1fc8c5ddfaeb6f1e716479ceb4a" +dependencies = [ + "cfg-if", + "cvt", + "fs_at", + "libc", + "normpath", + "windows-sys 0.59.0", +] + +[[package]] +name = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.60.2", +] + +[[package]] +name = "rustls" +version = "0.23.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "serde_json" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smart-leds-trait" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edeb89c73244414bb0568611690dd095b2358b3fda5bae65ad784806cca00157" +dependencies = [ + "rgb", +] + +[[package]] +name = "smart_led_effects" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622dac88eaf804fdcef1a09c1bca92c5cf98d473b15a908aac358453fd0e5048" +dependencies = [ + "palette", + "rand 0.8.5", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros 0.24.3", +] + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.3", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.105", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix 1.0.8", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" +dependencies = [ + "thiserror-impl 2.0.14", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml-cfg" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c587298ddd135c156e92e8c3eae69614d6eecea8e2d8a09daab011e5e6a21d" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "serde", + "syn 2.0.105", + "toml 0.8.23", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow 0.7.12", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vinscant" +version = "0.1.0" +dependencies = [ + "anyhow", + "embedded-svc", + "embuild 0.33.1", + "esp-idf-svc", + "hex", + "log", + "mfrc522", + "palette", + "rand 0.9.2", + "rgb", + "smart_led_effects", + "toml-cfg", + "ws2812-esp32-rmt-driver", + "ws2812-spi", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.105", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "ws2812-esp32-rmt-driver" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c578740a03455e0a88e01c7233384c4e1c1fa9e181898547110b4911e98b69" +dependencies = [ + "embuild 0.32.0", + "esp-idf-hal", + "esp-idf-sys", + "heapless", + "paste", + "smart-leds-trait", +] + +[[package]] +name = "ws2812-spi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2fd98e2b649252eced2ec3aa8d5048e7d2ac294276b0567939bbf47741f9934" +dependencies = [ + "embedded-hal 1.0.0", + "smart-leds-trait", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.105", +] diff --git a/vinscant/Cargo.toml b/vinscant/Cargo.toml new file mode 100644 index 0000000..6546aac --- /dev/null +++ b/vinscant/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "vinscant" +version = "0.1.0" +authors = ["axel"] +edition = "2021" +resolver = "2" +rust-version = "1.77" + +[lib] +name = "lib" +path = "src/lib.rs" + +[[bin]] +name = "vinscant" +harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors + +[profile.release] +opt-level = "s" + +[profile.dev] +debug = true # Symbols are nice and they don't increase the size on Flash +opt-level = "z" + +[features] +default = ["std", "embassy", "esp-idf-svc/native", "esp32s2"] +esp32 = [] +esp32s2 = [] + +pio = ["esp-idf-svc/pio"] +std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"] +alloc = ["esp-idf-svc/alloc"] +nightly = ["esp-idf-svc/nightly"] +experimental = ["esp-idf-svc/experimental"] +embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"] + +[dependencies] +log = { version = "0.4", default-features = false } +esp-idf-svc = { version = "0.51", default-features = false } +toml-cfg = "0.2.0" +anyhow = "1.0.86" +embedded-svc = "0.28.0" +mfrc522 = "0.8.0" +hex = "0.4.3" +ws2812-esp32-rmt-driver = { version = "0.12.0", features = ["smart-leds-trait"] } +smart_led_effects = "0.1.7" +palette = "0.7.6" +rgb = "0.8.50" +rand = "0.9.2" +ws2812-spi = "0.5.1" + +[build-dependencies] +embuild = "0.33.1" +toml-cfg = "0.2.0" diff --git a/vinscant/README.md b/vinscant/README.md index 9a8bcb9..eea1da3 100644 --- a/vinscant/README.md +++ b/vinscant/README.md @@ -2,35 +2,35 @@ ono # Hardware: -- ESP32-S2 +- [ESP32-S2](https://web.archive.org/web/20241012003116/https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-saola-1-v1.2.html) - RFID-RC522 Connect RFID-RC522 Rfid reader on these pins: - -``` -SDA/CS: 34 -MOSI: 35 -SCK: 34 -MISO: 36 -RST: 16 -``` +| | esp32s2 | esp32 | +|--------|---------|-------| +| SDA/CS | 33 | 13 | +| MOSI | 35 | 4 | +| SCK | 34 | 0 | +| MISO | 36 | 27 | +| RST | / | / | # Setup: - If you're on windows and the board is not detected: install the ESP32-S2 toolchain from https://dl.espressif.com/dl/esp-idf/ - Or just install the usb chip driver for your board (eg. for CP2102N: https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers?tab=downloads) -- Install micropython - - Get binary for correct chip. Current: https://micropython.org/download/ESP32_GENERIC_S2/ - - install using commands on that website + - Or switch to Linux (recommended option) +- Install [esp-rs](https://docs.espressif.com/projects/rust/book/installation/riscv-and-xtensa.html) +```sh +cargo install espup +espup install +``` - connect to serial -- connect to wifi and setup webrepl (see https://docs.micropython.org/en/latest/esp8266/tutorial/repl.html) -- get `webrepl_cli.py` from https://github.com/micropython/webrepl -- copy all the files in `mpfiles` and `key.txt` (with the correct key set on vingo) to the microcontroller using `upload_file.sh` +- `cargo run` - beep boop # Future additions -- Beeps -- Boops -- Status light based on server response -- Switch to esp-rs +- [x] Beeps +- [x] Boops +- [x] Status light based on server response +- [x] Switch to esp-rs diff --git a/vinscant/build.rs b/vinscant/build.rs new file mode 100644 index 0000000..d2567a9 --- /dev/null +++ b/vinscant/build.rs @@ -0,0 +1,16 @@ +#[toml_cfg::toml_config] +pub struct Config { + #[default("")] + wifi_ssid: &'static str, + #[default("")] + wifi_psk: &'static str, +} + +fn main() { + eprintln!("{}", embuild::espidf::sysenv::idf_path().unwrap()); + // Check if the `cfg.toml` file exists and has been filled out. + if !std::path::Path::new("cfg.toml").exists() { + panic!("You need to create a `cfg.toml` file with your Wi-Fi credentials! Use `cfg.toml.example` as a template."); + } + embuild::espidf::sysenv::output(); +} diff --git a/vinscant/cfg.toml.example b/vinscant/cfg.toml.example new file mode 100644 index 0000000..856a398 --- /dev/null +++ b/vinscant/cfg.toml.example @@ -0,0 +1,5 @@ +[vinscant] +wifi_ssid = "Your SSID here" +wifi_psk = "Your password here" +auth_key = "Your key here" +# use with CONFIG var in code diff --git a/vinscant/mpfiles/boot.py b/vinscant/mpfiles/boot.py deleted file mode 100644 index 9ad8275..0000000 --- a/vinscant/mpfiles/boot.py +++ /dev/null @@ -1,29 +0,0 @@ -import gc -import machine -import network -import time -import webrepl - -network.hostname("vinscant") -wlan = network.WLAN(network.STA_IF) -wlan.active(True) -print("Connecting to WiFi...") -wlan.connect('Zeus WPI', 'zeusisdemax') -while not wlan.isconnected(): - pass -print("Connected to WiFi with ifconfig:", wlan.ifconfig()) - -print("Starting webrepl...") -webrepl.start() -print("Webrepl started") - -print("Heap bytes used before GC:", gc.mem_alloc()) -gc.collect() -print("Heap bytes used after GC:", gc.mem_alloc()) - -print("Boot done") - -print("Starting watchdog in 1s, interupt now with Ctrl+C") -time.sleep(1) -watchdog = machine.WDT(timeout=10 * 1000) -print("Watchdog started") diff --git a/vinscant/mpfiles/lib/__init__.py b/vinscant/mpfiles/lib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/vinscant/mpfiles/lib/mfrc522.py b/vinscant/mpfiles/lib/mfrc522.py deleted file mode 100644 index a7e05e7..0000000 --- a/vinscant/mpfiles/lib/mfrc522.py +++ /dev/null @@ -1,451 +0,0 @@ -# From https://github.com/danjperron/micropython-mfrc522/ - -from machine import Pin, SPI -from os import uname - -class MFRC522: - DEBUG = False - OK = 0 - NOTAGERR = 1 - ERR = 2 - - NTAG_213 = 213 - NTAG_215 = 215 - NTAG_216 = 216 - NTAG_NONE = 0 - - REQIDL = 0x26 - REQALL = 0x52 - AUTHENT1A = 0x60 - AUTHENT1B = 0x61 - - PICC_ANTICOLL1 = 0x93 - PICC_ANTICOLL2 = 0x95 - PICC_ANTICOLL3 = 0x97 - - def __init__(self, sck, mosi, miso, rst, cs,baudrate=1000000,spi_id=0): - self.sck = Pin(sck, Pin.OUT) - self.mosi = Pin(mosi, Pin.OUT) - self.miso = Pin(miso) - self.rst = Pin(rst, Pin.OUT) - self.cs = Pin(cs, Pin.OUT) - - self.rst.value(0) - self.cs.value(1) - self.NTAG = 0 - self.NTAG_MaxPage = 0 - board = uname()[0] - - if board == 'WiPy' or board == 'LoPy' or board == 'FiPy': - self.spi = SPI(0) - self.spi.init(SPI.MASTER, baudrate=1000000, pins=(self.sck, self.mosi, self.miso)) - elif (board == 'esp8266') or (board == 'esp32'): - self.spi = SPI(baudrate=100000, polarity=0, phase=0, sck=self.sck, mosi=self.mosi, miso=self.miso) - self.spi.init() - elif board == 'rp2': - self.spi = SPI(spi_id,baudrate=baudrate,sck=self.sck, mosi= self.mosi, miso= self.miso) - else: - raise RuntimeError("Unsupported platform") - - self.rst.value(1) - self.init() - - def _wreg(self, reg, val): - self.cs.value(0) - self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e))) - self.spi.write(b'%c' % int(0xff & val)) - self.cs.value(1) - - def _rreg(self, reg): - self.cs.value(0) - self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80))) - val = self.spi.read(1) - self.cs.value(1) - - return val[0] - - def _sflags(self, reg, mask): - self._wreg(reg, self._rreg(reg) | mask) - - def _cflags(self, reg, mask): - self._wreg(reg, self._rreg(reg) & (~mask)) - - def _tocard(self, cmd, send): - recv = [] - bits = irq_en = wait_irq = n = 0 - stat = self.ERR - - if cmd == 0x0E: - irq_en = 0x12 - wait_irq = 0x10 - elif cmd == 0x0C: - irq_en = 0x77 - wait_irq = 0x30 - - self._wreg(0x02, irq_en | 0x80) - self._cflags(0x04, 0x80) - self._sflags(0x0A, 0x80) - self._wreg(0x01, 0x00) - - for c in send: - self._wreg(0x09, c) - self._wreg(0x01, cmd) - - if cmd == 0x0C: - self._sflags(0x0D, 0x80) - - i = 2000 - while True: - n = self._rreg(0x04) - i -= 1 - if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)): - break - - self._cflags(0x0D, 0x80) - - if i: - if (self._rreg(0x06) & 0x1B) == 0x00: - stat = self.OK - - if n & irq_en & 0x01: - stat = self.NOTAGERR - elif cmd == 0x0C: - n = self._rreg(0x0A) - lbits = self._rreg(0x0C) & 0x07 - if lbits != 0: - bits = (n - 1) * 8 + lbits - else: - bits = n * 8 - - if n == 0: - n = 1 - elif n > 16: - n = 16 - - for _ in range(n): - recv.append(self._rreg(0x09)) - else: - stat = self.ERR - - return stat, recv, bits - - def _crc(self, data): - self._cflags(0x05, 0x04) - self._sflags(0x0A, 0x80) - - for c in data: - self._wreg(0x09, c) - - self._wreg(0x01, 0x03) - - i = 0xFF - while True: - n = self._rreg(0x05) - i -= 1 - if not ((i != 0) and not (n & 0x04)): - break - - return [self._rreg(0x22), self._rreg(0x21)] - - def init(self): - self.reset() - self._wreg(0x2A, 0x8D) - self._wreg(0x2B, 0x3E) - self._wreg(0x2D, 30) - self._wreg(0x2C, 0) - self._wreg(0x15, 0x40) - self._wreg(0x11, 0x3D) - self.antenna_on() - - def reset(self): - self._wreg(0x01, 0x0F) - - def antenna_on(self, on=True): - if on and ~(self._rreg(0x14) & 0x03): - self._sflags(0x14, 0x03) - else: - self._cflags(0x14, 0x03) - - def request(self, mode): - self._wreg(0x0D, 0x07) - (stat, recv, bits) = self._tocard(0x0C, [mode]) - - if (stat != self.OK) | (bits != 0x10): - stat = self.ERR - - return stat, bits - - def anticoll(self,anticolN): - ser_chk = 0 - ser = [anticolN, 0x20] - - self._wreg(0x0D, 0x00) - (stat, recv, bits) = self._tocard(0x0C, ser) - - if stat == self.OK: - if len(recv) == 5: - for i in range(4): - ser_chk = ser_chk ^ recv[i] - if ser_chk != recv[4]: - stat = self.ERR - else: - stat = self.ERR - - return stat, recv - - def PcdSelect(self, serNum,anticolN): - backData = [] - buf = [] - buf.append(anticolN) - buf.append(0x70) - #i = 0 - ###xorsum=0; - for i in serNum: - buf.append(i) - #while i<5: - # buf.append(serNum[i]) - # i = i + 1 - pOut = self._crc(buf) - buf.append(pOut[0]) - buf.append(pOut[1]) - (status, backData, backLen) = self._tocard( 0x0C, buf) - if (status == self.OK) and (backLen == 0x18): - return 1 - else: - return 0 - - - def SelectTag(self, uid): - byte5 = 0 - - #(status,puid)= self.anticoll(self.PICC_ANTICOLL1) - #print("uid",uid,"puid",puid) - for i in uid: - byte5 = byte5 ^ i - puid = uid + [byte5] - - if self.PcdSelect(puid,self.PICC_ANTICOLL1) == 0: - return (self.ERR,[]) - return (self.OK , uid) - - def tohexstring(self,v): - s="[" - for i in v: - if i != v[0]: - s = s+ ", " - s=s+ "0x{:02X}".format(i) - s= s+ "]" - return s - - def SelectTagSN(self): - valid_uid=[] - (status,uid)= self.anticoll(self.PICC_ANTICOLL1) - #print("Select Tag 1:",self.tohexstring(uid)) - if status != self.OK: - return (self.ERR,[]) - - if self.DEBUG: print("anticol(1) {}".format(uid)) - if self.PcdSelect(uid,self.PICC_ANTICOLL1) == 0: - return (self.ERR,[]) - if self.DEBUG: print("pcdSelect(1) {}".format(uid)) - - #check if first byte is 0x88 - if uid[0] == 0x88 : - #ok we have another type of card - valid_uid.extend(uid[1:4]) - (status,uid)=self.anticoll(self.PICC_ANTICOLL2) - #print("Select Tag 2:",self.tohexstring(uid)) - if status != self.OK: - return (self.ERR,[]) - if self.DEBUG: print("Anticol(2) {}".format(uid)) - rtn = self.PcdSelect(uid,self.PICC_ANTICOLL2) - if self.DEBUG: print("pcdSelect(2) return={} uid={}".format(rtn,uid)) - if rtn == 0: - return (self.ERR,[]) - if self.DEBUG: print("PcdSelect2() {}".format(uid)) - #now check again if uid[0] is 0x88 - if uid[0] == 0x88 : - valid_uid.extend(uid[1:4]) - (status , uid) = self.anticoll(self.PICC_ANTICOLL3) - #print("Select Tag 3:",self.tohexstring(uid)) - if status != self.OK: - return (self.ERR,[]) - if self.DEBUG: print("Anticol(3) {}".format(uid)) - if self.PcdSelect(uid,self.PICC_ANTICOLL3) == 0: - return (self.ERR,[]) - if self.DEBUG: print("PcdSelect(3) {}".format(uid)) - valid_uid.extend(uid[0:5]) - # if we are here than the uid is ok - # let's remove the last BYTE whic is the XOR sum - - return (self.OK , valid_uid[:len(valid_uid)-1]) - #return (self.OK , valid_uid) - - def auth(self, mode, addr, sect, ser): - return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0] - - def authKeys(self,uid,addr,keyA=None, keyB=None): - status = self.ERR - if keyA is not None: - status = self.auth(self.AUTHENT1A, addr, keyA, uid) - elif keyB is not None: - status = self.auth(self.AUTHENT1B, addr, keyB, uid) - return status - - def stop_crypto1(self): - self._cflags(0x08, 0x08) - - def read(self, addr): - data = [0x30, addr] - data += self._crc(data) - (stat, recv, _) = self._tocard(0x0C, data) - return stat, recv - - def write(self, addr, data): - buf = [0xA0, addr] - buf += self._crc(buf) - (stat, recv, bits) = self._tocard(0x0C, buf) - - if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A): - stat = self.ERR - else: - buf = [] - for i in range(16): - buf.append(data[i]) - buf += self._crc(buf) - (stat, recv, bits) = self._tocard(0x0C, buf) - if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A): - stat = self.ERR - return stat - - - def writeSectorBlock(self,uid, sector, block, data, keyA=None, keyB = None): - absoluteBlock = sector * 4 + (block % 4) - if absoluteBlock > 63 : - return self.ERR - if len(data) != 16: - return self.ERR - if self.authKeys(uid,absoluteBlock,keyA,keyB) != self.ERR : - return self.write(absoluteBlock, data) - return self.ERR - - def readSectorBlock(self,uid ,sector, block, keyA=None, keyB = None): - absoluteBlock = sector * 4 + (block % 4) - if absoluteBlock > 63 : - return self.ERR, None - if self.authKeys(uid,absoluteBlock,keyA,keyB) != self.ERR : - return self.read(absoluteBlock) - return self.ERR, None - - def MFRC522_DumpClassic1K(self,uid, Start=0, End=64, keyA=None, keyB=None): - for absoluteBlock in range(Start,End): - status = self.authKeys(uid,absoluteBlock,keyA,keyB) - # Check if authenticated - print("{:02d} S{:02d} B{:1d}: ".format(absoluteBlock, absoluteBlock//4 , absoluteBlock % 4),end="") - if status == self.OK: - status, block = self.read(absoluteBlock) - if status == self.ERR: - break - else: - for value in block: - print("{:02X} ".format(value),end="") - print(" ",end="") - for value in block: - if (value > 0x20) and (value < 0x7f): - print(chr(value),end="") - else: - print('.',end="") - print("") - else: - break - if status == self.ERR: - print("Authentication error") - return self.ERR - return self.OK - - def MFRC522_Dump_NTAG(self,Start=0, End=135): - for absoluteBlock in range(Start,End,4): - MaxIndex = 4 * 135 - status = self.OK - print("Page {:02d}: ".format(absoluteBlock),end="") - if status == self.OK: - status, block = self.read(absoluteBlock) - if status == self.ERR: - break - else: - Index = absoluteBlock*4 - for i in range(len(block)): - if Index < MaxIndex : - print("{:02X} ".format(block[i]),end="") - else: - print(" ",end="") - if (i%4)==3: - print(" ",end="") - Index+=1 - print(" ",end="") - Index = absoluteBlock*4 - for value in block: - if Index < MaxIndex: - if (value > 0x20) and (value < 0x7f): - print(chr(value),end="") - else: - print('.',end="") - Index+=1 - print("") - else: - break - if status == self.ERR: - print("Authentication error") - return self.ERR - return self.OK - - def writeNTAGPage(self,page,data): - if page>self.NTAG_MaxPage: - return self.ERR - if page < 4: - return self.ERR - if len(data) != 4: - return self.ERR - - return self.write(page,data+[0]*12) - - def getNTAGVersion(self): - buf = [0x60] - buf += self._crc(buf) - stat, recv,_ = self._tocard(0x0C, buf) - return stat, recv - - #Version NTAG213 = [0x0 ,0x4, 0x4, 0x2, 0x1, 0x0,0x0f, 0x3] - #Version NTAG215 = [0x0 ,0x4, 0x4, 0x2, 0x1, 0x0,0x11, 0x3] - #Version NTAG216 = [0x0 ,0x4, 0x4, 0x2, 0x1, 0x0,0x13, 0x3] - - def IsNTAG(self): - self.NTAG = self.NTAG_NONE - self.NTAG_MaxPage=0 - (stat , rcv) = self.getNTAGVersion() - if stat == self.OK: - if len(rcv) < 8: - return False #do we have at least 8 bytes - if rcv[0] != 0: - return False #check header - if rcv[1] != 4: - return False #check Vendor ID - if rcv[2] != 4: - return False #check product type - if rcv[3] != 2: - return False #check subtype - if rcv[7] != 3: - return False #check protocol - if rcv[6] == 0xf: - self.NTAG= self.NTAG_213 - self.NTAG_MaxPage = 44 - return True - if rcv[6] == 0x11: - self.NTAG= self.NTAG_215 - self.NTAG_MaxPage = 134 - return True - if rcv[7] == 0x13: - self.NTAG= self.NTAG_216 - self.NTAG_MaxPage = 230 - return True - return False diff --git a/vinscant/mpfiles/lib/music.py b/vinscant/mpfiles/lib/music.py deleted file mode 100644 index 67a8586..0000000 --- a/vinscant/mpfiles/lib/music.py +++ /dev/null @@ -1,32 +0,0 @@ -import io -from machine import Pin, PWM, Timer -class MusicPlayer: - def __init__(self, melody: io.IOBase, pin: Pin) -> None: - self.melody = melody - self.pin = pin - self.pwm: PWM = PWM(pin, freq=1, duty_u16=0) - self.timer = Timer(0) - - @staticmethod - def midi_to_freq(note: int): - return 440 * 2**((float(note) - 69) / 12) / 2 - - def start(self): - self.timer.init(mode=Timer.PERIODIC, freq=1, callback=self.playNote) - - def playNote(self, ignored: Timer): - note = self.melody.read(1) - if len(note) == 0: - self.close() - return - note = note[0] - self.pwm.freq(int(MusicPlayer.midi_to_freq(note))) - if note == 0: - self.pwm.duty_u16(0) - else: - self.pwm.duty_u16(32767) - - def close(self): - self.timer.deinit() - self.pwm.deinit() - self.melody.close() diff --git a/vinscant/mpfiles/lib/term_color.py b/vinscant/mpfiles/lib/term_color.py deleted file mode 100644 index 5d52edd..0000000 --- a/vinscant/mpfiles/lib/term_color.py +++ /dev/null @@ -1,9 +0,0 @@ -# Ansi color codes -RESET = "\x1b[0m" -BOLD = "\x1b[1m" -UNDERLINE = "\x1b[4m" -BLACK = "\x1b[30m" -RED = "\x1b[31m" -GREEN = "\x1b[32m" -YELLOW = "\x1b[33m" -BLUE = "\x1b[34m" diff --git a/vinscant/mpfiles/lib/umidiparser.py b/vinscant/mpfiles/lib/umidiparser.py deleted file mode 100644 index 1a02bc8..0000000 --- a/vinscant/mpfiles/lib/umidiparser.py +++ /dev/null @@ -1,1380 +0,0 @@ -""" -NAME - umidiparser -ff -AUTHOR - Hermann Paul von Borries - -LICENSE - MIT, Copyright (c) Hermann Paul von Borries - -INSTALLATION - Copy umidiparser.py to your device. - -DESCRIPTION - This module allows to parse midi files. - This module does not contain a sound synthesizer, only the capabilities to - read and interpret a midi file. - Example: - - import umidiparser - Import utime - for event in umidiplay.MidiFile("example.mid"): - utime.sleep_us( event.delta_us ) - if event.status == umidiplay.NOTE_ON: - ... start the note event.note on event.channel with event.velocity - elif event.status == umidiplay.NOTE_OFF : - ... stop the note event.note stop to sound ... - elif event.status == umidiplay.PROGRAM_CHANGE: - ... change midi program to event.program on event.channel .... - else: - print("other event", event ) - -""" - -import time -import sys - -# Change log: v1.2 -# Added CircuitPython compatibility -# Removed use of os.path.abspath, does not exist in micropython/circuitpython -# Renamed time functions "ticks_now_us", "ticks_diff_us" to "time_now_us", "time_diff_us" -# Changed decode from meta event data for compatibility with all Python versions, now only -# ascii is decoded. -# New method MidiTrack.play, removed track_number parameter. -# Corrected possible error if playing open file again -# Added event.is_channel() to test for channel events -# Allow MidiFile.play() used in async for (with asyncio.sleep instead of sleep). Requires asyncio -# Play funcion computes event.timestamp_us for each event -# Change log: v1.3 -# For CircuitPython, it's import asyncio. Also time_now_us now returns an integer. - -# Compatibility wrapper for python/micropython/circuitpython functions -_implementation = sys.implementation.name -if _implementation == "micropython": - from micropython import const - import uasyncio as asyncio - time_sleep_us = lambda usec: time.sleep_us( usec ) - time_now_us = lambda: time.ticks_us() - time_diff_us = lambda x, y: time.ticks_diff( x, y ) - asyncio_sleep_ms = lambda x: asyncio.sleep_ms( x ) -elif _implementation == "circuitpython": - from micropython import const - try: - import asyncio - except: - pass - time_sleep_us = lambda usec: time.sleep( usec/1_000_000 ) - time_now_us = lambda: (time.monotonic_ns()+500)//1_000 - time_diff_us = lambda x, y: x - y - asyncio_sleep_ms = lambda x: asyncio.sleep_ms( x ) -else: - # Must be CPython - import asyncio - const = lambda x: x - time_sleep_us = lambda usec: time.sleep( usec/1_000_000 ) - time_now_us = lambda : int(time.time()*1_000_000) - time_diff_us = lambda x, y: x - y - asyncio_sleep_ms = lambda x: asyncio.sleep( x/1000 ) - - # Make the @micropython.native decorator do nothing - def micropython( function ): - return function - micropython.native = lambda function : function - -# Only utf-8 encoding available in Micropython 1.19.1 or CircuitPython 7.3.3 -# Should probably be .decode("iso8859-1", errors="backslashreplace") -decode_ascii = lambda x : "".join( chr(z) for z in x ) - - -# Constants for the MIDI channel events, first nibble -NOTE_OFF = const(0x80) -NOTE_ON = const(0x90) -POLYTOUCH = const(0xa0) -CONTROL_CHANGE = const(0xb0) -PROGRAM_CHANGE = const(0xc0) -AFTERTOUCH = const(0xd0) -PITCHWHEEL = const(0xe0) -_FIRST_CHANNEL_EVENT = const(0x80) -_LAST_CHANNEL_EVENT = const(0xef) -# Most midi channel events have 2 bytes of data, except this range that has 1 byte events -# which consists of two event types: -_FIRST_1BYTE_EVENT = const(0xc0) -_LAST_1BYTE_EVENT = const(0xdf) - -# Meta messages -_META_PREFIX = const(0xff) -# Meta messages, second byte -SEQUENCE_NUMBER = const(0x00) -TEXT = const(0x01) -COPYRIGHT = const(0x02) -TRACK_NAME = const(0x03) -INSTRUMENT_NAME = const(0x04) -LYRICS = const(0x05) -MARKER = const(0x06) -CUE_MARKER = const(0x07) -PROGRAM_NAME = const(0x08) -DEVICE_NAME = const(0x09) -CHANNEL_PREFIX = const(0x20) -MIDI_PORT = const(0x21) -END_OF_TRACK = const(0x2f) -SET_TEMPO = const(0x51) -SMPTE_OFFSET = const(0x54) -TIME_SIGNATURE = const(0x58) -KEY_SIGNATURE = const(0x59) -SEQUENCER_SPECIFIC = const(0x7f) -_FIRST_META_EVENT = const(0x00) -_LAST_META_EVENT = const(0x7f) - -# Sysex/escape events -SYSEX = const(0xf0) -ESCAPE = const(0xf7) - -# MidiParser reuses a buffer for meta and sysex events -# This is the starting size for this buffer. -# If there are larger messages in a file, this buffer will increase automatically -# to accomodate the larger data -_INITIAL_EVENT_BUFFER_SIZE = const(20) - - -# Parse midi variable length number format, -# used for time deltas and meta message lengths -@micropython.native -def _midi_number_to_int( midi_data ): - # Converts a midi variable length number to integer. In midi files, - # variable length numbers are used for delta times (time difference between - #one midi event and the next) and for variable length fields in meta messages. - data_byte = next( midi_data ) - if data_byte <= 0x7f: - # This "if" is really only for an improvement in speed, because - # most variable length numbers tend to be 1 byte long, so this - # is the most probable execution path. - return data_byte - - # The value spans more than one byte, parse until - # a byte with most significant bit "on" is found, and gather - # 7 bits of data for each byte found, according to Midi standard. - value = data_byte & 0x7f - while data_byte >= 0x80: - data_byte = next( midi_data ) - value = (value<<7) | (data_byte & 0x7f ) - return value - - - -def _process_events( event_iterator, - miditicks_per_quarter, - reuse_event_object ): - # This function iterates through the provided event iterator, - # getting one MidiEvent at a time, and processes MIDI meta set tempo - # events to convert the time delta in MIDI ticks to time delta in microseconds, - # rounded to the next microsecond. - # The function also ensures that there will be a MIDI meta end of track event - # at the end (as should be according to MIDI standard). - # If the reuse_event_object parameter is set to False, a independent deep copy - # of each event is returned. If the reuse_event_object is True, the same - # object is returned over and over, to reduce CPU usage and RAM heap allocation. - - # Start with default "microseconds per quarter" according to midi standard - tempo = 500_000 - - for event in event_iterator: - - if not reuse_event_object: - event = event.copy() - - # According to MIDI spec - # time (in ms) = number_of_ticks * tempo / divisor * 1000 - # where tempo is expressed in microseconds per quarter note and - # the divisor is expressed in MIDI ticks per quarter note - # Do the math in microseconds. - # Do not use floating point, in some microcontrollers - # floating point is slow or lacks precision. - event.delta_us = ( event.delta_miditicks * tempo \ - + (miditicks_per_quarter//2) \ - ) // miditicks_per_quarter - - - # Process tempo meta event, get tempo to be used for - # event.delta_us calculation for next events. - status = event.status - if status == SET_TEMPO: - tempo = event.tempo - - # If end_of_track is seen, stop processing events - elif status == END_OF_TRACK: - yield event - # Ignore events after end of track - break - - yield event - - else: - # Loop ended without end_of_track meta event, yield one - # last event of type "end of track" to make caller happy. - yield MidiEvent()._set_end_of_track() - - - - - -class MidiParser: - # This class instantiates a MidiParser, the class constructor - # accepts a iterable with MIDI events in MIDI file format, i.e. - # it accepts a iterable for the content of a MIDI file track. - def __init__( self, midi_data ): - # Initialize a parser on the midi_data iterable. The parsing - # is then done with the parse_events method. - - # Allocate data buffers for the sake of CPU and RAM efficiency, - # to avoid allocating new objects for each event parsed. - - # Save midi data iterator - self._midi_data = midi_data - - # The first event cannot be a "running status event" - self._running_status = None - - # This buffer is for meta and sysex events. - # This buffer can potentially grow - # because it will eventually contain the largest midi meta, sysex, - # or escape event in the file. - self._buffer = bytearray(_INITIAL_EVENT_BUFFER_SIZE ) - - # The most frequently used buffers are 1 and 2 bytes long - # For CPU/RAM efficiency, preallocate these buffers. - # These two buffers are used only for midi channel events - self._buffer1 = memoryview(bytearray(1)) - self._buffer2 = memoryview(bytearray(2)) - - - def parse_events( self ): - # This generator will parse the midi_data iterable - # and yield MidiEvent objects until end of data (i.e. this - # function is in itself a generator for events) - - # For CPU and RAM efficiency, the midi event is returned - # in the same object, that is, the MidiEvent returned is allocated - # once, and set before yielding to the new values. It is responsibility - # of the caller to copy the event if needed. The data buffer in the - # event is also reused from one call to the next (see __init__). - - # Exceptions: - # ValueError if the midi event subtype is in the range 0x7f to 0xff. - # RuntimeError if there is a running status event without a previous - # midi channel event. - # RuntimeError if a system common or real time event is detected (event - # status byte 0xf1-0xf6 and 0xf7-0xfe. These type of events are not allowed - # in MIDI files. - - event = MidiEvent() - midi_data = self._midi_data - try: - while True: - # Parse a delta time - delta = _midi_number_to_int( midi_data ) - - # Parse a message - event_status, data = self._parse_message( ) - - # Set the event with new data - event._set( event_status, data, delta ) - - yield event - - except StopIteration: - # No more input data, "next" in called function got end of data, - # stop this generator - return - - - @micropython.native - def _parse_non_channel_events( self, event_status ): - # Parses meta, sysex and escape events - - # Precondition: the event status byte has already been read. - # Postcondition: the event data has been parsed and the next - # byte in midi_data is a new MIDI event. - - midi_data = self._midi_data - - if event_status == _META_PREFIX: - # Midi messages event status has format - # 0xff nn (nn from 0x00-0x7f) - # discard 0xff, keep nn as event status - event_status = next( midi_data ) - - # The second event status byte might not be in range - # defined by standard - if not _FIRST_META_EVENT \ - <= event_status \ - <= _LAST_META_EVENT: - raise ValueError(\ - f"Meta midi second event status byte (0x{event_status:x}) " - "not in range 0x00-0x7f") - - # All non-channel events have a variable length field - data_length = _midi_number_to_int( midi_data ) - - # Data might be longer than available buffer - if data_length >= len(self._buffer): - # Increase buffer size to fit the data. - self._buffer = bytearray( data_length ) - - # Use a memoryview for efficiency, this avoids copying the buffer - # and allows to return a slice of the current size efficiently. - data = memoryview(self._buffer)[0:data_length] - - # Now copy the data from the input midi_data to the buffer - for idx in range(data_length): - data[idx] = next( midi_data ) - - return event_status, data - - @micropython.native - def _parse_channel_event( self, event_status, data_byte ): - # Parse midi channel events, i.e. events with event status - # byte 0x80 to 0xef. Procesess both regular channel events and - # running status events. - # Precondition: the event status byte has already been read. - # Postcondition: the event data has been parsed and the next - # byte in midi_data is a new MIDI event. - - # Check if this event is 1 or 2 bytes long - if _FIRST_1BYTE_EVENT <= event_status \ - <= _LAST_1BYTE_EVENT: - # This is a one-byte midi channel event, - # such as program change, use 1 byte preallocated buffer - data = self._buffer1 - data[0] = data_byte - - else: - # This is a two-byte midi channel event, such as note on, - # note off, use preallocated buffer - data = self._buffer2 - data[0] = data_byte - data[1]= next( self._midi_data ) - - return event_status, data - - @micropython.native - def _parse_message( self ): - # Parse a MIDI message in a MIDI file. Uses _parse_channel_event - # or _parse_non_channel_events depending on the event status byte. - # Precondition: the next byte in the midi_data is the - # event status byte. - midi_data = self._midi_data - - # Preconditions: the next byte in midi_data should now be - # the starting byte of a midi event, i.e a - # midi event status byte. Delta time has already been parsed - - # Get event_status byte - event_status = next( midi_data ) - if event_status < 0x80: - # This is a running event. It has no event status byte, - # only data, the event status byte is the one from the last midi channel - # event seen in the midi_data. - - # A running event at the beginning of a track is an error - if self._running_status is None: - raise RuntimeError("Midi running status without previous channel event") - - # Reuse the event_status as first data byte and parse the event - return self._parse_channel_event( - self._running_status, - event_status ) - - if _FIRST_CHANNEL_EVENT \ - <= event_status \ - <= _LAST_CHANNEL_EVENT: - # Not a running event, this is a midi channel event - # (status 0x80-0xef) followed by 1 or 2 bytes of data - - # Remember event status in case next event is a - # running status event - self._running_status = event_status - - return self._parse_channel_event( - event_status, - next( midi_data ) ) - - if event_status in (_META_PREFIX, SYSEX, ESCAPE ): - return self._parse_non_channel_events( event_status ) - - # Neither midi channel event, nor meta, nor sysex/escape. - # Real time and system common events are not expected in MIDI files. - raise RuntimeError("Real time/system common event" - f" status 0x{event_status:x}" - " not supported in midi files") - - -class MidiEvent: - """ - Represents a parsed midi event. - - """ - @micropython.native - def __init__( self ): - """ - Initializes MidiEvent, all instances are assigned None as value, - this is. This method is used internally by MidiParser. - - Usually you will not need to create an instance of MidiEvent, - MidiEvent objects are returned by iterating over the MidiFile - object. - - """ - # MidiEvent private instance variables: - # self._event_status_byte - # The original event status byte, as detected in the midi file. - # The difference with self.status is that the _event_status_byte - # still has the channel number in the lower half in the case of - # a midi channel event. - # - # self._status - # Same as self._event_status_byte but with the lower nibble - # cleared for midi channel events. self.status is a read only - # property for self._status. - # - # self._data - # The raw data of the event. self.data is the read only - # property for self._data. - - self._event_status_byte = None - self._data = None - - self._status = None - self.delta_miditicks = None - self.delta_us = None - self.timestamp_us = None - - @micropython.native - def _set( self, event_status, data, delta_miditicks ): - # Set event information to the event status byte, data and - # delta time (in miditicks) specified in the parameters, replacing all - # previous information in the event (if there was any). - - # event_status: contains the event status byte as found in the file. - # In case of midi channel events, lower nibble has the channel number. - # For meta messages, the first byte of the meta status is 0xff, - # so the event_status contains the second byte or "meta event type" - # with values from 0 to 0x7f (_FIRST_META_EVENT to _LAST_META_EVENT). - - # data: a buffer with the raw data of the event. - - # delta_miditicks: the delta time (time difference with previous event) - # in midi ticks (or pulses). - - # Store event status byte and compute event.status - self._event_status_byte = event_status - if _FIRST_CHANNEL_EVENT <= event_status <= _LAST_CHANNEL_EVENT: - self._status = event_status & 0xf0 - else: - self._status = event_status - - self._data = data - self.delta_miditicks = delta_miditicks - self.delta_us = None - - return self - - - def _set_end_of_track( self ): - self._set( END_OF_TRACK, b'', 0 ) - self.delta_us = 0 - return self - - @micropython.native - def _check_property_available( self, *argv ): - # This method is used to check availabilty of a property. - # Check if self._status is in list of possible status values - # and raises AttributeError if not. - if self._status not in argv: - raise AttributeError( - f"Midi event 0x{self._status:02x}" - " does not support attribute") - - - def _get_event_name( self ): - # This metod is used by __str___. - # Computes the event name as a string. To keep memory - # requirements at a minimum, instead of having a dictionary of - # names, this method uses the global variables of thid - # module as dictionary. - - # Make a dictionary out of the global names of this module - # Exclude private names starting with _ and - # exclude names that don't translate to an integer - event_names_dict = { globals()[varname] : varname.lower() \ - for varname in globals() \ - if isinstance(globals()[varname], int) \ - and varname[0:1] != "_" } - - try: - name = event_names_dict[self._status] - except KeyError: - # Show meaningful information for custom event numbers - if _FIRST_META_EVENT <= self._status <= _LAST_META_EVENT: - name = f"meta_0x{self._status:02x}" - else: - name = f"midi_0x{self._status:02x}" - return name - - def _get_property_dict( self ): - # This is used by __str__ - # Get values for allvalid @properties for - # this event, except the "data" property - - property_dict = {} - for prop in dir(MidiEvent): - if prop[0:1] != "_": - try: - value = getattr( self, prop ) - # Filter methods from the list - if isinstance(value,(int,str)): - property_dict[prop] = getattr(self, prop ) - except AttributeError: - pass - return property_dict - - def __str__( self ): - """ - Standard method to translate the event information to a string, - """ - description = self._get_event_name() - # Add event time attributes - description += " delta[miditicks]=" + str( self.delta_miditicks ) - # Add data, show only a couple of bytes of data - description += " data=" + str( bytes( self._data[0:5] ) ) - if len(self._data) > 5: - # Show only first 5 bytes in the data field. - description = description[0:-1] + "...'" - - # Show time in microseconds only if already computed - if self.delta_us is not None: - description += " delta[usec]=" + str(self.delta_us) - - # Get all @property names and their values - for prop, value in self._get_property_dict().items(): - description += " " + prop + "=" + str(value) - return description - - @property - @micropython.native - def status( self ): - """ - Returns the event status. For midi channel events, such as note on, note off, - program change, the lower nibble (lower 4 bits) are cleared (set to zero). - For a meta event, this is the meta type, for example 0x2f for "end of track". - This is the event type: note on, note off, meta text, etc. - """ - return self._status - - @property - @micropython.native - def channel( self ): - """ - Returns the channel number for the event, 0-15. - - channel property available for: NOTE_OFF NOTE_ON - POLYTOUCH CONTROL_CHANGE PROGRAM_CHANGE AFTERTOUCH - CHANNEL_PREFIX - """ - if _FIRST_CHANNEL_EVENT<= self._status <= \ - _LAST_CHANNEL_EVENT: - # For midi event status byte 0x80 to 0xef, - # the channel is part of the event status byte - return self._event_status_byte & 0x0f - if self._status == CHANNEL_PREFIX: - return self._data[0] - raise AttributeError - - - @property - @micropython.native - def note( self ): - """ - Returns the note number for the event, usually 0-127. - - note property available for: NOTE_OFF NOTE_ON POLYTOUCH - """ - self._check_property_available( NOTE_ON, - NOTE_OFF, - POLYTOUCH ) - return self._data[0] - - @property - @micropython.native - def velocity( self ): - """ - Returns the velocity fot the event, usually 0-127. - - velocity property available for: NOTE_OFF NOTE_ON - """ - - self._check_property_available( NOTE_ON, NOTE_OFF ) - return self._data[1] - - @property - def value( self ): - """ - Returns the the value in the event. - - value property available for: AFTERTOUCH, CONTROL_CHANGE, POLYTOUCH - """ - if self._status == AFTERTOUCH: - return self._data[0] - if self._status in ( CONTROL_CHANGE, POLYTOUCH): - return self._data[1] - raise AttributeError - - @property - def pitch( self ): - """ - Returns the pitch for a PITCHWHEEL midi channel event. - - -8192 is the lowest value possible, 0 (zero) means "no pitch bend" - and 8191 is the highest possible value. - """ - self._check_property_available( PITCHWHEEL ) - # lsb (0 - 127) and msb (0 - 127) together form a 14-bit number, - # allowing fine adjustment to pitch. - # Using hex, 00 40 is the central (no bend) setting. - # 00 00 gives the maximum downwards bend, and 7F 7F the - # maximum upwards bend. - # Return 0 for no bend/central bend, -8192 to -1 for downward - # bend and -1 to 8191 for upward bend - return (((self._data[1]&0x7f)-0x40)<<7)| (self._data[0]&0x7f) - - @property - def program( self ): - """ - Returns the program number 0-127 for a PROGRAM_CHANGE event. - """ - self._check_property_available( PROGRAM_CHANGE ) - return self._data[0] - - @property - def control( self ): - """ - Returns the value for the controller 0-127 for a CONTROL_CHANGE event. - """ - self._check_property_available( CONTROL_CHANGE ) - return self._data[0] - - @property - def number( self ): - """ - Returns number of a SEQUENCE_NUMBER meta event. - Values range from 0 to 2**24. - """ - self._check_property_available( SEQUENCE_NUMBER ) - # Meta event sequence number has a 2 byte big endian number - return int.from_bytes(self._data[0:2], "big" ) - - @property - def text( self ): - """ - Returns the text for a meta events. - - text property is available for: TEXT COPYRIGHT LYRICS MARKER CUE_MARKER - - Both event.text and event.name decode the data. Non ASCII - characters are shown for example as \xa5 - - """ - self._check_property_available( TEXT, - COPYRIGHT, - LYRICS, - MARKER, - CUE_MARKER ) - - return decode_ascii( self.data ) - - @property - def name( self ): - """ - Returns the text for a meta events. - - name property available for: TRACK_NAME INSTRUMENT_NAME PROGRAM_NAME DEVICE_NAME - - See text property for description of text conversion. - - The raw data can be retrieved using the data property. - """ - self._check_property_available( TRACK_NAME, - INSTRUMENT_NAME, - PROGRAM_NAME, - DEVICE_NAME ) - return decode_ascii( self.data ) - - @property - def port( self ): - """ - Returns the port number 0-256 for a meta MIDI_PORT message - """ - self._check_property_available( MIDI_PORT ) - # Meta port event - return self._data[0] - - @property - def tempo( self ): - """ - Returns the tempo (0 to 2**32 microseconds per quarter beat) - for a SET_TEMPO meta event. - This module interprets the tempo event before returning it, so - the following events returned will have their delta_us property - calculated with the new tempo value. - - """ - self._check_property_available( SET_TEMPO ) - # Meta tempo event, 4 bytes big endian with tempo - # in microseconds per quarter note or beat - return int.from_bytes( self._data[0:3], "big") - - @property - def key( self ): - """ - Returns the key, as str, for a KEY_SIGNATURE meta event. - - For mayor keys: - C, D, E, F, G, A, B, C#, F#, Cb, Db, Eb, Gb, Ab - - For minor keys: - Cm, Dm, Em, Fm, Gm, Am, Bm, C#m, F#m, Cbm, Dbm, Ebm, Gbm, Abm - - If the midi message contains a value out of range, a ValueError - is raised. The raw data can be read with the data property. - """ - self._check_property_available( KEY_SIGNATURE ) - # Translate data of key meta messages to scale name - # 2 data bytes: sharps/flats and mayor/minor - # sharps/flats: 0=no flats/sharps, 1 to 7 number of sharps, -1 to -7 number of flats - # mayor/minor: 0=mayor, 1=minor - sharps_flats = self._data[0] - minor_mayor = self._data[1] - - if sharps_flats > 128: - sharps_flats -= 256 - if sharps_flats not in range(-7,8) \ - or minor_mayor not in [0,1]: - raise ValueError( - "Midi file format error, key signature meta unrecogized data" ) - if minor_mayor == 0: - scale_names = ("Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", - "C", - "G", "D", "A", "E", "B", "F#", "C#" ) - else: - scale_names = ( "Abm", "Ebm", "Bbm", "Fm", "Cm", "Gm", "Dm", - "Am", - "Em", "Bm", "F#m", "C#m", "G#m", "D#m", "A#m" ) - return scale_names[sharps_flats+7] - - # Time signature meta message - @property - def numerator( self ): - """ - Returns the numerator for the TIME_SIGNATURE meta messages, 0-255. - """ - self._check_property_available( TIME_SIGNATURE ) - return self._data[0] - - @property - def denominator( self ): - """ - Returns the denominator for the TIME_SIGNATURE meta messages, 0-255. - """ - self._check_property_available( TIME_SIGNATURE ) - return 2**self._data[1] - - @property - def clocks_per_click( self ): - """ - Returns the clocks_per_click for the TIME_SIGNATURE meta messages, 0-255. - """ - self._check_property_available( TIME_SIGNATURE ) - return self._data[2] - - @property - def notated_32nd_notes_per_beat( self ): - """ - Returns the notated_32nd_notes_per_beat for the TIME_SIGNATURE meta messages, - 0-255. - """ - self._check_property_available( TIME_SIGNATURE ) - return self._data[3] - - @property - def frame_rate( self ): - """ - Returns the frame for the SMPTE_OFFSET meta messages, - which can be 24, 25, 29.97 or 30. - - An invalid value in the MIDI file will raise a IndexError - """ - self._check_property_available( SMPTE_OFFSET ) - return [24,25,29.97,30][(self._data[0] >> 5)] - - @property - def hours( self ): - """ - Returns the hour for the SMPTE_OFFSET meta message, - usually from 0 to 23. - """ - self._check_property_available( SMPTE_OFFSET ) - return self._data[0] & 0x1f - - @property - def minutes( self ): - """ - Returns the minutes for the SMPTE_OFFSET meta message, - usually from 0 to 59. - """ - self._check_property_available( SMPTE_OFFSET ) - return self._data[1] - - @property - def seconds( self ): - """ - Returns the seconds for the SMPTE_OFFSET meta message, - usually from 0 to 59. - """ - self._check_property_available( SMPTE_OFFSET ) - return self._data[2] - - @property - def frames( self ): - """ - Returns the frames for the SMPTE_OFFSET meta message, - usually from 0 to 255. - """ - self._check_property_available( SMPTE_OFFSET ) - return self._data[3] - - @property - def sub_frames( self ): - """ - Returns the sub frames for the SMPTE_OFFSET meta message, - usually from 0 to 59. - """ - self._check_property_available( SMPTE_OFFSET ) - return self._data[4] - - @property - def data( self ): - """ - Returns the raw data for the underlying message, with no transofrmations, - as a memoryview, without the event status byte or meta prefix. - """ - - return self._data - - @micropython.native - def copy( self ): - """ - Returns a deep copy (a complete independent copy) of the event. - """ - - my_copy = MidiEvent() - my_copy._event_status_byte = self._event_status_byte - my_copy._status = self._status - my_copy._data = bytearray( self._data ) - my_copy.delta_miditicks = self.delta_miditicks - my_copy.delta_us = self.delta_us - my_copy.timestamp_us = self.timestamp_us - return my_copy - - def is_meta( self ): - """ - Returns True if this is a Meta event, such as - lyrics, set tempo or key signature. - Returns False if this is a MIDI channel event, - or a Sysex or Escape event. - """ - return _FIRST_META_EVENT <= self._status <= _LAST_META_EVENT - - def is_channel( self ): - """ - Returns True if this event is a channel event - """ - return _FIRST_CHANNEL_EVENT <= self._status <= _LAST_CHANNEL_EVENT - - def to_midi( self ): - """ - Returns the event as bytes, in a format that allows sending the - data to a MIDI controller. - - to_midi will raise AttributeError if the event is for MIDI meta messages, these - occur in MIDI files and are not normally sent to MIDI controllers. - - This function has not been tested with a real MIDI device. - """ - if self.is_meta(): - raise AttributeError - return self._event_status_byte.to_bytes( 1, "big") + self._data - - - -class MidiTrack: - """ - This object contains the track of a midi file. It is - created internally by the MidiFile function for each track - chunk found in the midi file. - - MidiTrack objects are accessible via the MidiFile.tracks list - - """ - def __init__( self, - file_object, - filename, - reuse_event_object, - buffer_size, - miditicks_per_quarter ): - """ - The MidiTrack cosntructor is called internally by MidiFile, - you don't need to create a MidiTrack. - """ - - # Parameters are: - # file_object: This is the currently opened midi file, positioned at the start - # of this track's data, - # just before the 4 bytes with the track or chunk length. - # filename: the file name of file_object. - # file_object.name not available on CircuitPython - self._reuse_event_object = reuse_event_object - self._miditicks_per_quarter = miditicks_per_quarter - self._buffer_size = buffer_size - - # MTrk header in file has just been processed, get chunk length - self._track_length = int.from_bytes( file_object.read(4), "big" ) - - if buffer_size <= 0: - # Read the entire track data to RAM - self._track_data = file_object.read( self._track_length ) - else: - # Store filename and start position to open file when interation over track data starts - self._filename = filename - self._start_position = file_object.tell() - - # Skip rest of track chunk, fast forward to beginning of next track - file_object.seek( self._track_length, 1 ) - - self._track_parser = None - self.event = None - self.current_miditicks = None - - def _buffered_data_generator( self ): - # Generator to return byte by byte from a buffered track - # (a track entirely in memory, buffer_size=0) - # This way seems to be rather fast: - return ( data_byte for data_byte in self._track_data ) - - - def _file_data_generator( self ): - # Generator to return byte by byte of a track with - # buffer_size>0. Reads portions of n bytes and then returns - # byte by byte. - - # Allocate buffer to be reused for each read - buffer = bytearray( self._buffer_size ) - - # Open file again to read the track - with open( self._filename, "rb") as file: - file.seek( self._start_position ) - unread_bytes = self._track_length - while True: - # Read a buffer of data and yield byte by byte to caller - bytes_read = file.readinto( buffer ) - yield from memoryview(buffer)[0:bytes_read] - # Check if end of track reached - unread_bytes -= bytes_read - if unread_bytes <= 0 or bytes_read == 0: - return - - - def _get_midi_data( self ): - # Choose method to return data - if self._buffer_size <= 0: - return self._buffered_data_generator - return self._file_data_generator - - def __iter__( self ): - """ - Iterating through a track will yield all events of that track - of MIDI file. For example, to parse the first track in a midi file: - - for event in MidiFile("example.mid").track[0]: - .... process event ... - - event.delta_miditicks will have the time difference with the previous - event, in MIDI ticks (pulses). - - event.delta_us is calculated as the time difference with the previous event - in microseconds. For this calculation, the set tempo events and - the MIDI ticks per quarter note (also called "pulses per beat") - of the MIDI file header are taken into consideration. - - The last event will always be a END_OF_TRACK event, if missing in the file. - - """ - # Get the parser to return event by event, process set tempo meta events, - # calculate delta_us and ensure END_OF_TRACK present at the end. - # This is used to parse a single track, for multitrack processing _track_parse_start - # method is used - return _process_events( - MidiParser( iter(self._get_midi_data()()) ).parse_events(), - self._miditicks_per_quarter, - self._reuse_event_object ) - - # _track_parse_start and _track_parse_next are an iterator used - # to merge tracks. Instead of just iterationg, they also keep track of the - # sum of midi ticks in thr track. They allow comparing tracks to know which - # has the next event. - def _track_parse_start( self ): - # This is an internal method called by MidiFile for multitrack processing. - self._track_parser = MidiParser( iter(self._get_midi_data()()) ).parse_events() - - # Get first event to get things going... - self.event = next( self._track_parser ) - self.current_miditicks = self.event.delta_miditicks - - return self - - - @micropython.native - def _track_parse_next( self ): - # Used internally by MidiFile object. - # After doing a _track_parse_start, this will return the next event in track. - self.event = next( self._track_parser ) - self.current_miditicks += self.event.delta_miditicks - return self.event - - - @micropython.native - def __lt__( self, compare_to ): - """ - Used internally by the min function to compare the current time in miditicks - of the different tracks, the goal is to find the next midi event - of all tracks (the one with the smallest time since the beginning of the track) - """ - # Valid after _track_parse_start, in conjunction with _track_parse_next. - return self.current_miditicks < compare_to.current_miditicks - - def _get_current_miditicks(self): - return self.current_miditicks - - def play( self ): - """ - Plays the track. Intended for use with format 2 MIDI files. - Sleeps between events, yielding the events on time. - See also MidiFile.play. - - """ - return MidiPlay( self ) - -class MidiFile: - """ - Parses a MIDI file. - """ - def __init__( self, - filename, - buffer_size=100, - reuse_event_object=False ): - """ - filename - The name of a MIDI file, usually a .mid or .rtx MIDI file. - - buffer_size=100 - The buffer size that will be allocated for each track, 0=read - complete track to memory. - - reuse_event_object=False - True will reuse the event object during parsing, using less RAM. - - Returns an iterator over the events in the MIDI file. - """ - - # Store parameters - self._reuse_event_object = reuse_event_object - self._buffer_size = buffer_size - - # Process file - with open( filename, "rb" ) as file: - - # First chunk must be MThd midi header, process header and validate - self._format_type, \ - number_of_chunks, \ - self._miditicks_per_quarter = self._get_header( file ) - - self._filename = filename - - # Get all track objects of the file. - # Disregard the number of chunks, read the real number of tracks present. - self.tracks = [] - for _ in range(number_of_chunks): - track_id = file.read(4).decode( "latin-1" ) - # Only process MTrk chunks - if track_id == "MTrk": - self.tracks.append( MidiTrack( file, - filename, - reuse_event_object, - buffer_size, - self._miditicks_per_quarter) ) - else: - # Skip non-track chunk, - # use MidiTrack but ignore result - MidiTrack( file, - filename, - reuse_event_object, - 10, - self._miditicks_per_quarter ) - - - def _get_header( self, file ): - # Decodes the MIDI file header, returns the - # values of the header: - # format type (0-2), number of data chunks, MIDI ticks per quarter note - - track_id = file.read(4).decode( "latin-1" ) - if track_id != "MThd": - # It is said that Mac midi files may have 128 extra - # bytes prepended (i.e. a Mac BInary Header) - # Just in case, skip bytes until 128 bytes have been ignored, - # then read track_id again - # I have not been able to verify this. - file.read(128-4) - track_id = file.read(4).decode( "latin-1" ) - if track_id != "MThd": - raise ValueError("Midi file does not start with MThd header") - header_len = int.from_bytes( file.read(4), "big" ) - - if header_len < 6: - raise ValueError( - f"Midi file header MThd length ({header_len}) is smaller than 6 bytes" - ) - # IF header is larger than 6 bytes, the extra bytes are ignored. - - header_data = file.read( header_len ) - - # Format type 0: single MTrk chunk - # Format type 1: two or more MTrk chunks to be merged - # Format type 2: multiple MTrk chunks, each to be used separately - format_type = int.from_bytes( header_data[0:2], "big" ) - - # Get number of data chunks (track chunks) in the file - number_of_chunks = int.from_bytes( header_data[2:4], "big" ) - - # Get pulses per beat - miditicks_per_quarter = int.from_bytes( header_data[4:6], "big" ) - - if miditicks_per_quarter > 32767: - raise ValueError("Midi SMPTE time codes not supported") - - return format_type, number_of_chunks, miditicks_per_quarter - - @property - def format_type( self ): - """ - Returns the MIDI format type as stored in the header of the MIDI file: - """ - return self._format_type - - @property - def miditicks_per_quarter( self ): - """ - Return the midi ticks per quarter note (also called pulses per beat) - parameter in the MIDI header of the file. - """ - return self._miditicks_per_quarter - - @property - def filename( self ): - """ - Return the file name of the MIDI file, with absolute path. - """ - return self._filename - - @property - def buffer_size( self ): - """ - Return the buffer_size value. 0=tracks are buffered entirely in RAM. - A number, for example buffer_size=100 means a buffer of 100 bytes is - allocated per track to read the track data. - - This allows to read large MIDI files efficiently on microcontrollers with small RAM. - """ - return self._buffer_size - - @property - def reuse_event_object( self ): - """ - Return the value of reuse_event_object. - True: when iterating through a track or midi file, the same event object - is returned over and over (this is an optimization recommended for Micropython) - - False: when iterating through a track or midi file, a different event - object is returned each time (this is the typical Python behavior). - """ - return self._reuse_event_object - - def _track_merger( self ): - # Merges all tracks of a multitrack format 1 file - - # Iterate through each track, set up one iterator for each track - # For this code to work, the track interator will always yield - # a END_OF_TRACK event at the end of the track. - play_tracks = [ track._track_parse_start() for track in self.tracks ] - - # Current miditicks keeps the time, in MIDI ticks, since start of track - # of the last event returned - current_miditicks = 0 - - while True: - # From all tracks, select the track with the next event, this is - # the one with the lowest "current MIDI ticks time". - next_track = min( play_tracks ) # Uses the __lt__ function of track - - # Get the current event of the selected track - event = next_track.event - - # Adjust event miditicks to time difference with last event overall, - # replacing delta time with last event in the event's track - track_miditicks = next_track._get_current_miditicks() - event.delta_miditicks = track_miditicks - current_miditicks - - # If end_of_track is seen, don't continue to process this track - if event.status == END_OF_TRACK: - # Delete the track from the list of tracks being processed - track_index = play_tracks.index(next_track) - del play_tracks[track_index] - - # If all tracks have ended, stop processing file - if len(play_tracks) == 0: - # Yield only the last end_of_track found - yield event - # And stop iteration - return - - # Don't yield end of track events (except for the last track) - continue - - yield event - - # Update current time, this is now the time of the event - # just returned - current_miditicks = track_miditicks - - # Get the next event of the selected track. - # This has to be done after the yield, because this might - # overwrite the yielded message if reuse_event_object=True. - next_track._track_parse_next() - - - def __iter__( self ): - """ - To get all the events of a format type 0 or format type 1 MIDI file, - iterate through the MidiFile object, for example: - for event in MidiFile("example.mid"): - print(event) - - """ - # Check if there are tracks, there should be.... - if len(self.tracks) == 0: - # No tracks in file, simulate an empty track - # This will yield a single END_OF_TRACK event. - return _process_events( iter([]), - self._miditicks_per_quarter, - self._reuse_event_object ) - - # Iterate over track instead of file for format 2 files - if self._format_type == 2 and len(self.tracks) > 1: - raise RuntimeError( - "It's not possible to merge tracks of a MIDI format type 2 file") - - # If there is only one track present, - # iterate through the track only, no track merge is needed. - # This reduces CPU usage, in some cases by up to 15%. - if len(self.tracks) == 1: - return iter(self.tracks[0]) - - # For multitrack file type 1 files, tracks must be merged. - # Type 0 files with many tracks (not standard) are merged too. - return _process_events( self._track_merger(), - self._miditicks_per_quarter, - self._reuse_event_object ) - - def length_us( self ): - """ - Returns the length of the MidiFile in microseconds. - """ - # Returns the duration of playback time of the midi file microseconds - - # Start playing time at 0, in case there are no events - playback_time_us = 0 - - # Iterate through all events - # The complete file must be processed to compute length - # Open another instance of the file, so that the current process is not disturbed - for event in MidiFile( self.filename ): - playback_time_us += event.delta_us - - # Return the last time seen, or 0 if there were no events - return playback_time_us - - def play( self ): - """ - Iterate through the events of a MIDI file or a track, - sleep until the event has to take place, and - yield the event. Playing time is measured always from the start - of file, correcting a possible accumulation of timing errors. - """ - return MidiPlay( self ) - - -class MidiPlay: - """ - Internal class used to play a MIDI file waiting after each event for the next one. - Use: MidiPlay( instance_of_MidiFile ) or MidiPlay( instance_of_MidiTrack ) - Uses the __iter__/__next__ functions of MidiFile and MidiTrack to iterathe over the events. - """ - def __init__( self, midi_event_source ): - self.midi_event_source = midi_event_source - - - def get_event_generator( self ): - # Generator to iterate over the events and calculate the wait time - # for each event. Wait time is corrected by adjusting with real time compared - # to time since start of file. - playing_started_at = time_now_us() - midi_time = 0 - for event in self.midi_event_source: - midi_time += event.delta_us - now = time_now_us() - playing_time = time_diff_us( now, playing_started_at ) - event.timestamp_us = midi_time - yield (event, midi_time - playing_time) - - def __iter__( self ): - self.iterator = iter( self.get_event_generator() ) - return self - - def __next__( self ): - event, wait_time = next( self.iterator ) - if wait_time > 0: - time_sleep_us( wait_time ) - return event - - def __aiter__( self ): - return self.__iter__() - - async def __anext__( self ): - # asyncio version of __next__ - try: - event, wait_time = next( self. iterator ) - except StopIteration: - raise StopAsyncIteration - # If wait time <= 0, execute asyncio.sleep anyhow to yield control to other tasks - await asyncio_sleep_ms( max(wait_time//1_000,0) ) - return event - diff --git a/vinscant/mpfiles/main.py b/vinscant/mpfiles/main.py deleted file mode 100644 index 8d15c6e..0000000 --- a/vinscant/mpfiles/main.py +++ /dev/null @@ -1,208 +0,0 @@ -from machine import bitstream, Pin, PWM, WDT, Timer -from neopixel import NeoPixel -import esp32 -import gc -import time -import urequests as req -from umqtt.robust import MQTTClient - -from lib.mfrc522 import MFRC522 -import lib.term_color as tc - -def get_key(): - with open("res/key.txt", "r") as f: - return f.read().strip() - -def uid_to_string(uid): - ret = "" - for i in uid: - ret += "%02X" % i - return ret - -class Led: - def __init__(self, pin: Pin): - self.neopixel = NeoPixel(pin, 1) - - def setColor(self, r, g, b): - #print("color change begin") - self.neopixel[0] = (r, g, b) - # rmt causes hang - # bitbanging causes green to always be 255 - # study the following file for more info - # https://github.com/micropython/micropython/blob/master/extmod/machine_bitstream.c - esp32.RMT.bitstream_channel(None) # disable RMT, use bitbanging - self.neopixel.write() - - def turnOff(self): - self.setColor(0, 0, 0) - -class Buzzer: - def __init__(self, pin: Pin): - self.pin = pin - self.pwm: PWM = None - - def start(self, frequency=500): - self.pwm = PWM(self.pin, freq=frequency) - - def stop(self): - if self.pwm: - self.pwm.deinit() - -class StatusNotifier: - colors = ((255, 0, 0), (255, 127, 0), (0, 255, 0)) - - def __init__(self, buzzer: Buzzer, led: Led): - self.buzzer = buzzer - self.led = led - self.state = 0 - - def processing(self): - self.led.setColor(*StatusNotifier.colors[1]) - - def idle(self): - self.buzzer.stop() - self.led.setColor(*StatusNotifier.colors[self.state]) - self.state = (self.state + 1) % 3 - - def gotoSleep(self, timer: Timer = None): - print("(StatusNotifier will sleep and feed watchdog for some time)") - watchdog.feed() - if timer: - timer.deinit() - else: - time.sleep(.5) - watchdog.feed() - self.buzzer.stop() - time.sleep(1.5) - watchdog.feed() - self.idle() - - def good(self, name=None): - self.led.setColor(*StatusNotifier.colors[2]) - self.buzzer.start(500) - Timer(0).init(period=500, mode=Timer.ONE_SHOT, callback=self.gotoSleep) - mqtt = Mqtt() - mqtt.blink() - mqtt.close() - if name: - leddy.setText(f"Welkom {name}!") - - def error(self): - self.led.setColor(*StatusNotifier.colors[0]) - self.buzzer.start(250) - self.gotoSleep() - -class Leddy: - def __init__(self, address="http://leddy") -> None: - self.address = address - - def _post(self, command: str): - try: - req.post(self.address, data=command, timeout=2).close() - except Exception: - print("vinscant: leddy doesn't work :\x28") # indentation does weird - - def setText(self, text: str): - watchdog.feed() - self._post(f"ScrollingText {text}") - watchdog.feed() - time.sleep(3) - watchdog.feed() - self._post(f"Option autoResetMs {5 * 1000}") - watchdog.feed() - -class Mqtt: - def __init__(self, name="vinscant", host="koin", port="1883") -> None: - self.client = MQTTClient(name, host, port) - self.client.connect() - - def blink(self): - topic = 'zigbee2mqtt/bulb_0_0/set' - payload = '{"effect": "okay"}' - self.client.publish(bytes(topic, 'utf-8'), bytes(payload, 'utf-8')) - - def close(self): - self.client.disconnect() - -print("Starting Vinscant...") - -print("- Loading config...") -led_pin = Pin(18, Pin.OUT) -buzzer_pin = Pin(37, Pin.OUT) -scanner_pin_nrst = Pin(16, Pin.OUT) -scanner_pin_cs = Pin(33, Pin.OUT) -scanner_pin_sck = Pin(34, Pin.OUT) -scanner_pin_mosi = Pin(35, Pin.OUT) -scanner_pin_miso = Pin(36, Pin.OUT) -key: str = get_key() - -print("- Creating class instances...") -led = Led(pin=led_pin) -buzzer = Buzzer(pin=buzzer_pin) -notifier = StatusNotifier(buzzer=buzzer, led=led) -leddy = Leddy() -scanner = MFRC522(rst=scanner_pin_nrst, cs=scanner_pin_cs, sck=scanner_pin_sck, mosi=scanner_pin_mosi, miso=scanner_pin_miso) -last_uid = '' -last_time = 0 - -print("- Idling StatusNotifier...") -notifier.idle() - -print("- Running main loop...") - -try: - while True: - watchdog.feed() - - print("Start of loop") - - heap_used_before = gc.mem_alloc() - gc.collect() - heap_used_after = gc.mem_alloc() - print(f"GC collected {heap_used_before - heap_used_after} bytes") - - watchdog.feed() - - print(f"{tc.BOLD}Place card before reader to read from address 0x08{tc.RESET}") - while scanner.request(scanner.REQIDL)[0] != scanner.OK: - watchdog.feed() - notifier.idle() - time.sleep(0.5) # Give webrepl some time to breathe - watchdog.feed() - - watchdog.feed() - - status, uid = scanner.SelectTagSN() - if status != scanner.OK: - print(f"{tc.YELLOW}Error during SelectTagSN{tc.RESET}") - notifier.error() - continue - - watchdog.feed() - - notifier.processing() - uid = uid_to_string(uid) - print(f"Detected card: {uid}") - - current_time = time.time() - if uid == last_uid and current_time - last_time <= 15: - print(f"{tc.YELLOW}Card already seen{tc.RESET}") - notifier.error() - continue - last_uid = uid - last_time = current_time - - res = req.post("https://zess.zeus.gent/api/scans", data=f"{uid};{key}") - watchdog.feed() - if 200 <= res.status_code < 300: - name = res.text - print(f"vingo: {res.status_code} {tc.GREEN}{name}{tc.RESET}") - notifier.good(name) - else: - print(f"vingo: {res.status_code} {tc.YELLOW}{res.text}{tc.RESET}") - notifier.error() - res.close() # does not support with statements - -except KeyboardInterrupt: - print(f"{tc.RED}KeyboardInterrupt{tc.RESET}") - watchdog = WDT(timeout=60 * 60 * 1000) diff --git a/vinscant/partitions.csv b/vinscant/partitions.csv new file mode 100644 index 0000000..173bd45 --- /dev/null +++ b/vinscant/partitions.csv @@ -0,0 +1,5 @@ +# ESP-IDF Partition Table +# Name,Type,SubType,Offset,Size,Flags +nvs,data,nvs,0x9000,0x6000, +phy_init,data,phy,0xf000,0x1000, +factory,app,factory,0x10000,0x300000, diff --git a/vinscant/rust-toolchain.toml b/vinscant/rust-toolchain.toml new file mode 100644 index 0000000..a2f5ab5 --- /dev/null +++ b/vinscant/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "esp" diff --git a/vinscant/sdkconfig.defaults b/vinscant/sdkconfig.defaults new file mode 100644 index 0000000..0e2f263 --- /dev/null +++ b/vinscant/sdkconfig.defaults @@ -0,0 +1,14 @@ +# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K) +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000 + +# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default). +# This allows to use 1 ms granuality for thread sleeps (10 ms by default). +#CONFIG_FREERTOS_HZ=1000 + +# Workaround for https://github.com/espressif/esp-idf/issues/7631 +#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n +#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="../../../../../../partitions.csv" +CONFIG_ESP_INT_WDT_TIMEOUT_MS=0 diff --git a/vinscant/src/buzzer.rs b/vinscant/src/buzzer.rs new file mode 100644 index 0000000..e4e474a --- /dev/null +++ b/vinscant/src/buzzer.rs @@ -0,0 +1,40 @@ +use esp_idf_svc::hal::{ + gpio::OutputPin, + ledc::{ + config, LedcDriver, LedcTimerDriver, CHANNEL0, + TIMER0, + }, + peripheral::Peripheral, + units::Hertz, +}; + +pub struct Buzzer<'a> { + timer_driver: LedcTimerDriver<'a, TIMER0>, + channel: LedcDriver<'a>, +} +impl<'a> Buzzer<'a> { + pub fn new( + timer: TIMER0, + channel: CHANNEL0, + pin: impl Peripheral
+ 'a,
+ ) -> Self {
+ let timer_driver =
+ LedcTimerDriver::new(timer, &config::TimerConfig::new().frequency(100.into())).unwrap();
+ let channel = LedcDriver::new(channel, &timer_driver, pin).unwrap();
+ let mut buzzer = Buzzer {
+ timer_driver,
+ channel,
+ };
+ buzzer.off();
+ buzzer
+ }
+
+ pub fn off(&mut self) {
+ let _ = self.channel.set_duty(0);
+ }
+
+ pub fn on(&mut self, frequency: Hertz) {
+ let _ = self.channel.set_duty(64);
+ let _ = self.timer_driver.set_frequency(frequency);
+ }
+}
diff --git a/vinscant/src/card_request.rs b/vinscant/src/card_request.rs
new file mode 100644
index 0000000..b68be00
--- /dev/null
+++ b/vinscant/src/card_request.rs
@@ -0,0 +1,46 @@
+use embedded_svc::http::client::Client;
+use esp_idf_svc::{
+ http::client::{Configuration, EspHttpConnection},
+ io::EspIOError,
+ sys::{esp_crt_bundle_attach, EspError},
+};
+use mfrc522::Uid;
+
+pub enum CardError {
+ ServerError,
+ ConnectionError(EspError),
+ NotFoundError,
+}
+
+impl From + 'static,
+ sysloop: EspSystemEventLoop,
+) -> Result