An applicaiton and simulator for a bike lighting system #245
|
@ -120,6 +120,12 @@ version = "1.0.75"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.9.0"
|
||||
|
@ -330,6 +336,12 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
|
@ -345,6 +357,15 @@ dependencies = [
|
|||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.9.3"
|
||||
|
@ -367,6 +388,22 @@ version = "1.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bike"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"embedded-alloc",
|
||||
"embedded-hal",
|
||||
"fixed",
|
||||
"fugit",
|
||||
"lights-core",
|
||||
"panic-halt",
|
||||
"rp-pico",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
|
@ -388,6 +425,12 @@ version = "0.10.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitfield"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -692,6 +735,38 @@ version = "0.8.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
|
||||
dependencies = [
|
||||
"bare-metal",
|
||||
"bitfield",
|
||||
"embedded-hal",
|
||||
"volatile-register",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1"
|
||||
dependencies = [
|
||||
"cortex-m-rt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt-macros"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.11"
|
||||
|
@ -710,6 +785,15 @@ dependencies = [
|
|||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-any"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73"
|
||||
dependencies = [
|
||||
"debug-helper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.4.0"
|
||||
|
@ -725,6 +809,12 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.4"
|
||||
|
@ -826,6 +916,12 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||
|
||||
[[package]]
|
||||
name = "debug-helper"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e"
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "0.8.6"
|
||||
|
@ -918,6 +1014,35 @@ dependencies = [
|
|||
"serde 1.0.193",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-alloc"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddae17915accbac2cfbc64ea0ae6e3b330e6ea124ba108dada63646fd3c6f815"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"linked_list_allocator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-dma"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[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 = "emseries"
|
||||
version = "0.6.0"
|
||||
|
@ -1056,7 +1181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
|
||||
dependencies = [
|
||||
"memoffset",
|
||||
"rustc_version",
|
||||
"rustc_version 0.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1117,6 +1242,18 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02c69ce7e7c0f17aa18fdd9d0de39727adb9c6281f2ad12f57cbe54ae6e76e7d"
|
||||
dependencies = [
|
||||
"az",
|
||||
"bytemuck",
|
||||
"half",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
|
@ -1222,6 +1359,45 @@ dependencies = [
|
|||
"percent-encoding 2.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frunk"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11a351b59e12f97b4176ee78497dff72e4276fb1ceb13e19056aca7fa0206287"
|
||||
dependencies = [
|
||||
"frunk_core",
|
||||
"frunk_derives",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frunk_core"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af2469fab0bd07e64ccf0ad57a1438f63160c69b2e57f04a439653d68eb558d6"
|
||||
|
||||
[[package]]
|
||||
name = "frunk_derives"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e"
|
||||
dependencies = [
|
||||
"frunk_proc_macro_helpers",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frunk_proc_macro_helpers"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35b54add839292b743aeda6ebedbd8b11e93404f902c56223e51b9ec18a13d2c"
|
||||
dependencies = [
|
||||
"frunk_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ft-core"
|
||||
version = "0.1.0"
|
||||
|
@ -1241,6 +1417,15 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "fugit"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
|
||||
dependencies = [
|
||||
"gcd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.29"
|
||||
|
@ -1369,6 +1554,12 @@ dependencies = [
|
|||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcd"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
|
||||
|
||||
[[package]]
|
||||
name = "gdk-pixbuf"
|
||||
version = "0.18.3"
|
||||
|
@ -2176,6 +2367,15 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.0"
|
||||
|
@ -2303,6 +2503,20 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lights-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"fixed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked_list_allocator"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.8"
|
||||
|
@ -2531,6 +2745,21 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[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.27.1"
|
||||
|
@ -2646,6 +2875,26 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.1"
|
||||
|
@ -2768,6 +3017,12 @@ dependencies = [
|
|||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "panic-halt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.0"
|
||||
|
@ -2942,6 +3197,17 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pio"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"num_enum",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "piper"
|
||||
version = "0.2.1"
|
||||
|
@ -3452,6 +3718,76 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rp-pico"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6341771e6f8e5d130b2b3cbc23435b7847761adf198af09f4b2a60407d43bd56"
|
||||
dependencies = [
|
||||
"cortex-m-rt",
|
||||
"fugit",
|
||||
"rp2040-boot2",
|
||||
"rp2040-hal",
|
||||
"usb-device",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rp2040-boot2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21"
|
||||
dependencies = [
|
||||
"crc-any",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rp2040-hal"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ff2b9ae7e6dd6720fd9f64250c9087260e50fe98b6b032ccca65be3581167ca"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"critical-section",
|
||||
"embedded-dma",
|
||||
"embedded-hal",
|
||||
"frunk",
|
||||
"fugit",
|
||||
"itertools 0.10.5",
|
||||
"nb 1.1.0",
|
||||
"paste",
|
||||
"pio",
|
||||
"rand_core 0.6.4",
|
||||
"rp2040-hal-macros",
|
||||
"rp2040-pac",
|
||||
"usb-device",
|
||||
"vcell",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rp2040-hal-macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e"
|
||||
dependencies = [
|
||||
"cortex-m-rt",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rp2040-pac"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12d9d8375815f543f54835d01160d4e47f9e2cae75f17ff8f1ec19ce1da96e4c"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"critical-section",
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.6"
|
||||
|
@ -3484,13 +3820,22 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"semver 1.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3626,12 +3971,27 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "0.9.15"
|
||||
|
@ -3751,6 +4111,20 @@ version = "0.3.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "simulator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cairo-rs",
|
||||
"fixed",
|
||||
"gio",
|
||||
"glib",
|
||||
"gtk4",
|
||||
"libadwaita",
|
||||
"lights-core",
|
||||
"pango",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.2.3"
|
||||
|
@ -3835,7 +4209,7 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"itertools 0.12.0",
|
||||
"nom",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
@ -4036,6 +4410,12 @@ dependencies = [
|
|||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.4"
|
||||
|
@ -4626,6 +5006,12 @@ version = "2.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "usb-device"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508"
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
|
@ -4673,6 +5059,12 @@ version = "1.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b"
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
@ -4709,6 +5101,21 @@ dependencies = [
|
|||
"warp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "volatile-register"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
|
||||
dependencies = [
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
resolver = "2"
|
||||
members = [
|
||||
"authdb",
|
||||
"bike-lights/bike",
|
||||
"bike-lights/core",
|
||||
"bike-lights/simulator",
|
||||
"changeset",
|
||||
"config",
|
||||
"config-derive",
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
[build]
|
||||
target = "thumbv6m-none-eabi"
|
||||
|
||||
[target.thumbv6m-none-eabi]
|
||||
rustflags = [
|
||||
"-C", "link-arg=--nmagic",
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
"-C", "inline-threshold=5",
|
||||
"-C", "no-vectorize-loops",
|
||||
]
|
||||
|
||||
runner = "elf2uf2-rs -d"
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "bike"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
az = { version = "1" }
|
||||
cortex-m-rt = { version = "0.7.3" }
|
||||
cortex-m = { version = "0.7.7" }
|
||||
embedded-alloc = { version = "0.5.1" }
|
||||
embedded-hal = { version = "0.2.7" }
|
||||
fixed = { version = "1" }
|
||||
fugit = { version = "0.3.7" }
|
||||
lights-core = { path = "../core" }
|
||||
panic-halt = { version = "0.2.0" }
|
||||
rp-pico = { version = "0.8.0" }
|
|
@ -0,0 +1,222 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use az::*;
|
||||
use core::cell::RefCell;
|
||||
use cortex_m::delay::Delay;
|
||||
use embedded_alloc::Heap;
|
||||
use embedded_hal::{blocking::spi::Write, digital::v2::InputPin, digital::v2::OutputPin};
|
||||
use fixed::types::I16F16;
|
||||
use fugit::RateExtU32;
|
||||
use lights_core::{App, BodyPattern, DashboardPattern, Event, Instant, FPS, UI};
|
||||
use panic_halt as _;
|
||||
use rp_pico::{
|
||||
entry,
|
||||
hal::{
|
||||
clocks::init_clocks_and_plls,
|
||||
gpio::{FunctionSio, Pin, PinId, PullUp, SioInput},
|
||||
pac::{CorePeripherals, Peripherals},
|
||||
spi::{Enabled, Spi, SpiDevice, ValidSpiPinout},
|
||||
watchdog::Watchdog,
|
||||
Clock, Sio,
|
||||
},
|
||||
Pins,
|
||||
};
|
||||
|
||||
#[global_allocator]
|
||||
static HEAP: Heap = Heap::empty();
|
||||
|
||||
const LIGHT_SCALE: I16F16 = I16F16::lit("256.0");
|
||||
const DASHBOARD_BRIGHTESS: u8 = 1;
|
||||
const BODY_BRIGHTNESS: u8 = 8;
|
||||
|
||||
struct DebouncedButton<P: PinId> {
|
||||
debounce: Instant,
|
||||
pin: Pin<P, FunctionSio<SioInput>, PullUp>,
|
||||
}
|
||||
|
||||
impl<P: PinId> DebouncedButton<P> {
|
||||
fn new(pin: Pin<P, FunctionSio<SioInput>, PullUp>) -> Self {
|
||||
Self {
|
||||
debounce: Instant((0 as u32).into()),
|
||||
pin,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_low(&self, time: Instant) -> bool {
|
||||
if time <= self.debounce {
|
||||
return false;
|
||||
}
|
||||
self.pin.is_low().unwrap_or(false)
|
||||
}
|
||||
|
||||
fn set_debounce(&mut self, time: Instant) {
|
||||
self.debounce = time + Instant((250 as u32).into());
|
||||
}
|
||||
}
|
||||
|
||||
struct BikeUI<
|
||||
D: SpiDevice,
|
||||
P: ValidSpiPinout<D>,
|
||||
LeftId: PinId,
|
||||
RightId: PinId,
|
||||
PreviousId: PinId,
|
||||
NextId: PinId,
|
||||
> {
|
||||
spi: RefCell<Spi<Enabled, D, P, 8>>,
|
||||
left_blinker_button: DebouncedButton<LeftId>,
|
||||
right_blinker_button: DebouncedButton<RightId>,
|
||||
previous_animation_button: DebouncedButton<PreviousId>,
|
||||
next_animation_button: DebouncedButton<NextId>,
|
||||
}
|
||||
|
||||
impl<
|
||||
D: SpiDevice,
|
||||
P: ValidSpiPinout<D>,
|
||||
LeftId: PinId,
|
||||
RightId: PinId,
|
||||
PreviousId: PinId,
|
||||
NextId: PinId,
|
||||
> BikeUI<D, P, LeftId, RightId, PreviousId, NextId>
|
||||
{
|
||||
fn new(
|
||||
spi: Spi<Enabled, D, P, 8>,
|
||||
left_blinker_button: Pin<LeftId, FunctionSio<SioInput>, PullUp>,
|
||||
right_blinker_button: Pin<RightId, FunctionSio<SioInput>, PullUp>,
|
||||
previous_animation_button: Pin<PreviousId, FunctionSio<SioInput>, PullUp>,
|
||||
next_animation_button: Pin<NextId, FunctionSio<SioInput>, PullUp>,
|
||||
) -> Self {
|
||||
Self {
|
||||
spi: RefCell::new(spi),
|
||||
left_blinker_button: DebouncedButton::new(left_blinker_button),
|
||||
right_blinker_button: DebouncedButton::new(right_blinker_button),
|
||||
previous_animation_button: DebouncedButton::new(previous_animation_button),
|
||||
next_animation_button: DebouncedButton::new(next_animation_button),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
D: SpiDevice,
|
||||
P: ValidSpiPinout<D>,
|
||||
LeftId: PinId,
|
||||
RightId: PinId,
|
||||
PreviousId: PinId,
|
||||
NextId: PinId,
|
||||
> UI for BikeUI<D, P, LeftId, RightId, PreviousId, NextId>
|
||||
{
|
||||
fn check_event(&mut self, current_time: Instant) -> Option<Event> {
|
||||
if self.left_blinker_button.is_low(current_time) {
|
||||
self.left_blinker_button.set_debounce(current_time);
|
||||
Some(Event::LeftBlinker)
|
||||
} else if self.right_blinker_button.is_low(current_time) {
|
||||
self.right_blinker_button.set_debounce(current_time);
|
||||
Some(Event::RightBlinker)
|
||||
} else if self.previous_animation_button.is_low(current_time) {
|
||||
self.previous_animation_button.set_debounce(current_time);
|
||||
Some(Event::PreviousPattern)
|
||||
} else if self.next_animation_button.is_low(current_time) {
|
||||
self.next_animation_button.set_debounce(current_time);
|
||||
Some(Event::NextPattern)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern) {
|
||||
let mut lights: [u8; 260] = [0; 260];
|
||||
lights[256] = 0xff;
|
||||
lights[257] = 0xff;
|
||||
lights[258] = 0xff;
|
||||
lights[259] = 0xff;
|
||||
for (idx, rgb) in dashboard_lights.iter().enumerate() {
|
||||
lights[(idx + 1) * 4 + 0] = 0xe0 + DASHBOARD_BRIGHTESS;
|
||||
lights[(idx + 1) * 4 + 1] = (I16F16::from(rgb.r) * LIGHT_SCALE).saturating_as();
|
||||
lights[(idx + 1) * 4 + 2] = (I16F16::from(rgb.b) * LIGHT_SCALE).saturating_as();
|
||||
lights[(idx + 1) * 4 + 3] = (I16F16::from(rgb.g) * LIGHT_SCALE).saturating_as();
|
||||
}
|
||||
for (idx, rgb) in body_lights.iter().enumerate() {
|
||||
lights[(idx + 4) * 4 + 0] = 0xe0 + BODY_BRIGHTNESS;
|
||||
lights[(idx + 4) * 4 + 1] = (I16F16::from(rgb.b) * LIGHT_SCALE).saturating_as();
|
||||
lights[(idx + 4) * 4 + 2] = (I16F16::from(rgb.g) * LIGHT_SCALE).saturating_as();
|
||||
lights[(idx + 4) * 4 + 3] = (I16F16::from(rgb.r) * LIGHT_SCALE).saturating_as();
|
||||
}
|
||||
let mut spi = self.spi.borrow_mut();
|
||||
spi.write(lights.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
{
|
||||
use core::mem::MaybeUninit;
|
||||
const HEAP_SIZE: usize = 8096;
|
||||
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
|
||||
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
|
||||
}
|
||||
|
||||
let mut pac = Peripherals::take().unwrap();
|
||||
let core = CorePeripherals::take().unwrap();
|
||||
let sio = Sio::new(pac.SIO);
|
||||
let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
||||
|
||||
let pins = Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
|
||||
let clocks = init_clocks_and_plls(
|
||||
12_000_000u32,
|
||||
pac.XOSC,
|
||||
pac.CLOCKS,
|
||||
pac.PLL_SYS,
|
||||
pac.PLL_USB,
|
||||
&mut pac.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
let mut delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
|
||||
let mut spi_clk = pins.gpio10.into_function();
|
||||
let mut spi_sdo = pins.gpio11.into_function();
|
||||
let spi = Spi::<_, _, _, 8>::new(pac.SPI1, (spi_sdo, spi_clk));
|
||||
let mut spi = spi.init(
|
||||
&mut pac.RESETS,
|
||||
clocks.peripheral_clock.freq(),
|
||||
1_u32.MHz(),
|
||||
embedded_hal::spi::MODE_1,
|
||||
);
|
||||
|
||||
let left_blinker_button = pins.gpio17.into_pull_up_input();
|
||||
let right_blinker_button = pins.gpio16.into_pull_up_input();
|
||||
let previous_animation_button = pins.gpio27.into_pull_up_input();
|
||||
let next_animation_button = pins.gpio26.into_pull_up_input();
|
||||
|
||||
let ui = BikeUI::new(
|
||||
spi,
|
||||
left_blinker_button,
|
||||
right_blinker_button,
|
||||
previous_animation_button,
|
||||
next_animation_button,
|
||||
);
|
||||
|
||||
let mut app = App::new(Box::new(ui));
|
||||
|
||||
let mut led_pin = pins.led.into_push_pull_output();
|
||||
led_pin.set_high();
|
||||
|
||||
let mut time = Instant::default();
|
||||
let delay_ms = 1000 / (FPS as u32);
|
||||
loop {
|
||||
app.tick(time);
|
||||
|
||||
delay.delay_ms(delay_ms);
|
||||
time = time + Instant(delay_ms.into());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "lights-core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
az = { version = "1" }
|
||||
fixed = { version = "1" }
|
|
@ -0,0 +1,481 @@
|
|||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
use alloc::boxed::Box;
|
||||
use az::*;
|
||||
use core::{
|
||||
clone::Clone,
|
||||
cmp::PartialEq,
|
||||
default::Default,
|
||||
ops::{Add, Sub},
|
||||
option::Option,
|
||||
};
|
||||
use fixed::types::{I48F16, I8F8, U128F0, U16F0};
|
||||
|
||||
mod patterns;
|
||||
pub use patterns::*;
|
||||
|
||||
mod types;
|
||||
pub use types::{BodyPattern, DashboardPattern, RGB};
|
||||
|
||||
fn calculate_frames(starting_time: U128F0, now: U128F0) -> U16F0 {
|
||||
let frames_128 = (now - starting_time) / U128F0::from(FPS);
|
||||
(frames_128 % U128F0::from(U16F0::MAX)).cast()
|
||||
}
|
||||
|
||||
fn calculate_slope(start: I8F8, end: I8F8, frames: U16F0) -> I8F8 {
|
||||
let slope_i16f16 = (I48F16::from(end) - I48F16::from(start)) / I48F16::from(frames);
|
||||
slope_i16f16.saturating_as()
|
||||
}
|
||||
|
||||
fn linear_ease(value: I8F8, frames: U16F0, slope: I8F8) -> I8F8 {
|
||||
let value_i16f16 = I48F16::from(value) + I48F16::from(frames) * I48F16::from(slope);
|
||||
value_i16f16.saturating_as()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub struct Instant(pub U128F0);
|
||||
|
||||
impl Default for Instant {
|
||||
fn default() -> Self {
|
||||
Self(U128F0::from(0 as u8))
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Instant {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, r: Self) -> Self::Output {
|
||||
Self(self.0 + r.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Instant {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, r: Self) -> Self::Output {
|
||||
Self(self.0 - r.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub const FPS: u8 = 30;
|
||||
|
||||
pub trait UI {
|
||||
fn check_event(&mut self, current_time: Instant) -> Option<Event>;
|
||||
fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern);
|
||||
}
|
||||
|
||||
pub trait Animation {
|
||||
fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern);
|
||||
}
|
||||
|
||||
/*
|
||||
pub struct DefaultAnimation {}
|
||||
|
||||
impl Animation for DefaultAnimation {
|
||||
fn tick(&mut self, _: Instant) -> (DashboardPattern, BodyPattern) {
|
||||
(WATER_DASHBOARD, WATER_BODY)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub struct Fade {
|
||||
starting_dashboard: DashboardPattern,
|
||||
starting_lights: BodyPattern,
|
||||
|
||||
start_time: Instant,
|
||||
dashboard_slope: [RGB<I8F8>; 3],
|
||||
body_slope: [RGB<I8F8>; 60],
|
||||
frames: U16F0,
|
||||
}
|
||||
|
||||
impl Fade {
|
||||
fn new(
|
||||
dashboard: DashboardPattern,
|
||||
lights: BodyPattern,
|
||||
ending_dashboard: DashboardPattern,
|
||||
ending_lights: BodyPattern,
|
||||
frames: U16F0,
|
||||
time: Instant,
|
||||
) -> Self {
|
||||
let mut dashboard_slope = [Default::default(); 3];
|
||||
let mut body_slope = [Default::default(); 60];
|
||||
for i in 0..3 {
|
||||
let slope = RGB {
|
||||
r: calculate_slope(dashboard[i].r, ending_dashboard[i].r, frames),
|
||||
g: calculate_slope(dashboard[i].g, ending_dashboard[i].g, frames),
|
||||
b: calculate_slope(dashboard[i].b, ending_dashboard[i].b, frames),
|
||||
};
|
||||
dashboard_slope[i] = slope;
|
||||
}
|
||||
|
||||
for i in 0..60 {
|
||||
let slope = RGB {
|
||||
r: calculate_slope(lights[i].r, ending_lights[i].r, frames),
|
||||
g: calculate_slope(lights[i].g, ending_lights[i].g, frames),
|
||||
b: calculate_slope(lights[i].b, ending_lights[i].b, frames),
|
||||
};
|
||||
body_slope[i] = slope;
|
||||
}
|
||||
|
||||
Self {
|
||||
starting_dashboard: dashboard,
|
||||
starting_lights: lights,
|
||||
start_time: time,
|
||||
dashboard_slope,
|
||||
body_slope,
|
||||
frames,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animation for Fade {
|
||||
fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern) {
|
||||
let mut frames = calculate_frames(self.start_time.0, time.0);
|
||||
if frames > self.frames {
|
||||
frames = self.frames
|
||||
}
|
||||
let mut dashboard_pattern: DashboardPattern = OFF_DASHBOARD;
|
||||
let mut body_pattern: BodyPattern = OFF_BODY;
|
||||
|
||||
for i in 0..3 {
|
||||
dashboard_pattern[i].r = linear_ease(
|
||||
self.starting_dashboard[i].r,
|
||||
frames,
|
||||
self.dashboard_slope[i].r,
|
||||
);
|
||||
dashboard_pattern[i].g = linear_ease(
|
||||
self.starting_dashboard[i].g,
|
||||
frames,
|
||||
self.dashboard_slope[i].g,
|
||||
);
|
||||
dashboard_pattern[i].b = linear_ease(
|
||||
self.starting_dashboard[i].b,
|
||||
frames,
|
||||
self.dashboard_slope[i].b,
|
||||
);
|
||||
}
|
||||
|
||||
for i in 0..60 {
|
||||
body_pattern[i].r =
|
||||
linear_ease(self.starting_lights[i].r, frames, self.body_slope[i].r);
|
||||
body_pattern[i].g =
|
||||
linear_ease(self.starting_lights[i].g, frames, self.body_slope[i].g);
|
||||
body_pattern[i].b =
|
||||
linear_ease(self.starting_lights[i].b, frames, self.body_slope[i].b);
|
||||
}
|
||||
|
||||
(dashboard_pattern, body_pattern)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FadeDirection {
|
||||
Transition,
|
||||
FadeIn,
|
||||
FadeOut,
|
||||
}
|
||||
|
||||
pub enum BlinkerDirection {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
pub struct Blinker {
|
||||
transition: Fade,
|
||||
fade_in: Fade,
|
||||
fade_out: Fade,
|
||||
direction: FadeDirection,
|
||||
|
||||
start_time: Instant,
|
||||
frames: U16F0,
|
||||
}
|
||||
|
||||
impl Blinker {
|
||||
fn new(
|
||||
starting_dashboard: DashboardPattern,
|
||||
starting_body: BodyPattern,
|
||||
direction: BlinkerDirection,
|
||||
time: Instant,
|
||||
) -> Self {
|
||||
let mut ending_dashboard = OFF_DASHBOARD.clone();
|
||||
|
||||
match direction {
|
||||
BlinkerDirection::Left => {
|
||||
ending_dashboard[0].r = LEFT_BLINKER_DASHBOARD[0].r;
|
||||
ending_dashboard[0].g = LEFT_BLINKER_DASHBOARD[0].g;
|
||||
ending_dashboard[0].b = LEFT_BLINKER_DASHBOARD[0].b;
|
||||
}
|
||||
BlinkerDirection::Right => {
|
||||
ending_dashboard[2].r = RIGHT_BLINKER_DASHBOARD[2].r;
|
||||
ending_dashboard[2].g = RIGHT_BLINKER_DASHBOARD[2].g;
|
||||
ending_dashboard[2].b = RIGHT_BLINKER_DASHBOARD[2].b;
|
||||
}
|
||||
}
|
||||
|
||||
let mut ending_body = OFF_BODY.clone();
|
||||
match direction {
|
||||
BlinkerDirection::Left => {
|
||||
for i in 0..30 {
|
||||
ending_body[i].r = LEFT_BLINKER_BODY[i].r;
|
||||
ending_body[i].g = LEFT_BLINKER_BODY[i].g;
|
||||
ending_body[i].b = LEFT_BLINKER_BODY[i].b;
|
||||
}
|
||||
}
|
||||
BlinkerDirection::Right => {
|
||||
for i in 30..60 {
|
||||
ending_body[i].r = RIGHT_BLINKER_BODY[i].r;
|
||||
ending_body[i].g = RIGHT_BLINKER_BODY[i].g;
|
||||
ending_body[i].b = RIGHT_BLINKER_BODY[i].b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Blinker {
|
||||
transition: Fade::new(
|
||||
starting_dashboard.clone(),
|
||||
starting_body.clone(),
|
||||
ending_dashboard.clone(),
|
||||
ending_body.clone(),
|
||||
BLINKER_FRAMES,
|
||||
time,
|
||||
),
|
||||
fade_in: Fade::new(
|
||||
OFF_DASHBOARD.clone(),
|
||||
OFF_BODY.clone(),
|
||||
ending_dashboard.clone(),
|
||||
ending_body.clone(),
|
||||
BLINKER_FRAMES,
|
||||
time,
|
||||
),
|
||||
fade_out: Fade::new(
|
||||
ending_dashboard.clone(),
|
||||
ending_body.clone(),
|
||||
OFF_DASHBOARD.clone(),
|
||||
OFF_BODY.clone(),
|
||||
BLINKER_FRAMES,
|
||||
time,
|
||||
),
|
||||
direction: FadeDirection::Transition,
|
||||
start_time: time,
|
||||
frames: BLINKER_FRAMES,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animation for Blinker {
|
||||
fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern) {
|
||||
let frames = calculate_frames(self.start_time.0, time.0);
|
||||
if frames > self.frames {
|
||||
match self.direction {
|
||||
FadeDirection::Transition => {
|
||||
self.direction = FadeDirection::FadeOut;
|
||||
self.fade_out.start_time = time;
|
||||
}
|
||||
FadeDirection::FadeIn => {
|
||||
self.direction = FadeDirection::FadeOut;
|
||||
self.fade_out.start_time = time;
|
||||
}
|
||||
FadeDirection::FadeOut => {
|
||||
self.direction = FadeDirection::FadeIn;
|
||||
self.fade_in.start_time = time;
|
||||
}
|
||||
}
|
||||
self.start_time = time;
|
||||
}
|
||||
|
||||
match self.direction {
|
||||
FadeDirection::Transition => self.transition.tick(time),
|
||||
FadeDirection::FadeIn => self.fade_in.tick(time),
|
||||
FadeDirection::FadeOut => self.fade_out.tick(time),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
Brake,
|
||||
BrakeRelease,
|
||||
LeftBlinker,
|
||||
NextPattern,
|
||||
PreviousPattern,
|
||||
RightBlinker,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Pattern {
|
||||
Water,
|
||||
GayPride,
|
||||
TransPride,
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
fn previous(&self) -> Pattern {
|
||||
match self {
|
||||
Pattern::Water => Pattern::TransPride,
|
||||
Pattern::GayPride => Pattern::Water,
|
||||
Pattern::TransPride => Pattern::GayPride,
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&self) -> Pattern {
|
||||
match self {
|
||||
Pattern::Water => Pattern::GayPride,
|
||||
Pattern::GayPride => Pattern::TransPride,
|
||||
Pattern::TransPride => Pattern::Water,
|
||||
}
|
||||
}
|
||||
|
||||
fn dashboard(&self) -> DashboardPattern {
|
||||
match self {
|
||||
Pattern::Water => WATER_DASHBOARD,
|
||||
Pattern::GayPride => PRIDE_DASHBOARD,
|
||||
Pattern::TransPride => TRANS_PRIDE_DASHBOARD,
|
||||
}
|
||||
}
|
||||
|
||||
fn body(&self) -> BodyPattern {
|
||||
match self {
|
||||
Pattern::Water => OFF_BODY,
|
||||
Pattern::GayPride => PRIDE_BODY,
|
||||
Pattern::TransPride => TRANS_PRIDE_BODY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum State {
|
||||
Pattern(Pattern),
|
||||
Brake,
|
||||
LeftBlinker,
|
||||
RightBlinker,
|
||||
BrakeLeftBlinker,
|
||||
BrakeRightBlinker,
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
ui: Box<dyn UI>,
|
||||
state: State,
|
||||
home_pattern: Pattern,
|
||||
current_animation: Box<dyn Animation>,
|
||||
dashboard_lights: DashboardPattern,
|
||||
lights: BodyPattern,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(ui: Box<dyn UI>) -> Self {
|
||||
let pattern = Pattern::Water;
|
||||
Self {
|
||||
ui,
|
||||
state: State::Pattern(pattern),
|
||||
home_pattern: pattern,
|
||||
current_animation: Box::new(Fade::new(
|
||||
OFF_DASHBOARD,
|
||||
OFF_BODY,
|
||||
pattern.dashboard(),
|
||||
pattern.body(),
|
||||
DEFAULT_FRAMES,
|
||||
Instant((0 as u32).into()),
|
||||
)),
|
||||
dashboard_lights: OFF_DASHBOARD,
|
||||
lights: OFF_BODY,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_animation(&mut self, time: Instant) {
|
||||
match self.state {
|
||||
State::Pattern(ref pattern) => {
|
||||
self.current_animation = Box::new(Fade::new(
|
||||
self.dashboard_lights.clone(),
|
||||
self.lights.clone(),
|
||||
pattern.dashboard(),
|
||||
pattern.body(),
|
||||
DEFAULT_FRAMES,
|
||||
time,
|
||||
))
|
||||
}
|
||||
State::Brake => {
|
||||
self.current_animation = Box::new(Fade::new(
|
||||
self.dashboard_lights.clone(),
|
||||
self.lights.clone(),
|
||||
BRAKES_DASHBOARD,
|
||||
BRAKES_BODY,
|
||||
BRAKES_FRAMES,
|
||||
time,
|
||||
));
|
||||
}
|
||||
State::LeftBlinker => {
|
||||
self.current_animation = Box::new(Blinker::new(
|
||||
self.dashboard_lights.clone(),
|
||||
self.lights.clone(),
|
||||
BlinkerDirection::Left,
|
||||
time,
|
||||
));
|
||||
}
|
||||
State::RightBlinker => {
|
||||
self.current_animation = Box::new(Blinker::new(
|
||||
self.dashboard_lights.clone(),
|
||||
self.lights.clone(),
|
||||
BlinkerDirection::Right,
|
||||
time,
|
||||
));
|
||||
}
|
||||
State::BrakeLeftBlinker => (),
|
||||
State::BrakeRightBlinker => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_state(&mut self, event: Event) {
|
||||
match event {
|
||||
Event::Brake => {
|
||||
if self.state == State::Brake {
|
||||
self.state = State::Pattern(self.home_pattern);
|
||||
} else {
|
||||
self.state = State::Brake;
|
||||
}
|
||||
}
|
||||
Event::BrakeRelease => self.state = State::Pattern(self.home_pattern),
|
||||
Event::LeftBlinker => match self.state {
|
||||
State::Brake => self.state = State::BrakeLeftBlinker,
|
||||
State::BrakeLeftBlinker => self.state = State::Brake,
|
||||
State::LeftBlinker => self.state = State::Pattern(self.home_pattern),
|
||||
_ => self.state = State::LeftBlinker,
|
||||
},
|
||||
Event::NextPattern => match self.state {
|
||||
State::Pattern(ref pattern) => {
|
||||
self.home_pattern = pattern.next();
|
||||
self.state = State::Pattern(self.home_pattern);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::PreviousPattern => match self.state {
|
||||
State::Pattern(ref pattern) => {
|
||||
self.home_pattern = pattern.previous();
|
||||
self.state = State::Pattern(self.home_pattern);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RightBlinker => match self.state {
|
||||
State::Brake => self.state = State::BrakeRightBlinker,
|
||||
State::BrakeRightBlinker => self.state = State::Brake,
|
||||
State::RightBlinker => self.state = State::Pattern(self.home_pattern),
|
||||
_ => self.state = State::RightBlinker,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, time: Instant) {
|
||||
match self.ui.check_event(time) {
|
||||
Some(event) => {
|
||||
self.update_state(event);
|
||||
self.update_animation(time);
|
||||
}
|
||||
None => {}
|
||||
};
|
||||
|
||||
let (dashboard, lights) = self.current_animation.tick(time);
|
||||
self.dashboard_lights = dashboard.clone();
|
||||
self.lights = lights.clone();
|
||||
self.ui.update_lights(dashboard, lights);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
use crate::{BodyPattern, DashboardPattern, RGB};
|
||||
use fixed::types::{I8F8, U16F0};
|
||||
|
||||
pub const RGB_OFF: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0"),
|
||||
g: I8F8::lit("0"),
|
||||
b: I8F8::lit("0"),
|
||||
};
|
||||
|
||||
pub const RGB_WHITE: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("1"),
|
||||
g: I8F8::lit("1"),
|
||||
b: I8F8::lit("1"),
|
||||
};
|
||||
|
||||
pub const BRAKES_RED: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("1"),
|
||||
g: I8F8::lit("0"),
|
||||
b: I8F8::lit("0"),
|
||||
};
|
||||
|
||||
pub const BLINKER_AMBER: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("1"),
|
||||
g: I8F8::lit("0.15"),
|
||||
b: I8F8::lit("0"),
|
||||
};
|
||||
|
||||
pub const PRIDE_RED: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0.95"),
|
||||
g: I8F8::lit("0.00"),
|
||||
b: I8F8::lit("0.00"),
|
||||
};
|
||||
|
||||
pub const PRIDE_ORANGE: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("1.0"),
|
||||
g: I8F8::lit("0.25"),
|
||||
b: I8F8::lit("0"),
|
||||
};
|
||||
|
||||
pub const PRIDE_YELLOW: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("1.0"),
|
||||
g: I8F8::lit("0.85"),
|
||||
b: I8F8::lit("0"),
|
||||
};
|
||||
|
||||
pub const PRIDE_GREEN: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0"),
|
||||
g: I8F8::lit("0.95"),
|
||||
b: I8F8::lit("0.05"),
|
||||
};
|
||||
|
||||
pub const PRIDE_INDIGO: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0.04"),
|
||||
g: I8F8::lit("0.15"),
|
||||
b: I8F8::lit("0.55"),
|
||||
};
|
||||
|
||||
pub const PRIDE_VIOLET: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0.75"),
|
||||
g: I8F8::lit("0.0"),
|
||||
b: I8F8::lit("0.80"),
|
||||
};
|
||||
|
||||
pub const TRANS_BLUE: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0.06"),
|
||||
g: I8F8::lit("0.41"),
|
||||
b: I8F8::lit("0.98"),
|
||||
};
|
||||
|
||||
pub const TRANS_PINK: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0.96"),
|
||||
g: I8F8::lit("0.16"),
|
||||
b: I8F8::lit("0.32"),
|
||||
};
|
||||
|
||||
pub const WATER_1: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0.0"),
|
||||
g: I8F8::lit("0.0"),
|
||||
b: I8F8::lit("0.75"),
|
||||
};
|
||||
|
||||
pub const WATER_2: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0.8"),
|
||||
g: I8F8::lit("0.8"),
|
||||
b: I8F8::lit("0.8"),
|
||||
};
|
||||
|
||||
pub const WATER_3: RGB<I8F8> = RGB {
|
||||
r: I8F8::lit("0.00"),
|
||||
g: I8F8::lit("0.75"),
|
||||
b: I8F8::lit("0.75"),
|
||||
};
|
||||
|
||||
pub const OFF_DASHBOARD: DashboardPattern = [RGB_OFF; 3];
|
||||
pub const OFF_BODY: BodyPattern = [RGB_OFF; 60];
|
||||
|
||||
pub const DEFAULT_FRAMES: U16F0 = U16F0::lit("30");
|
||||
|
||||
pub const WATER_DASHBOARD: DashboardPattern = [WATER_1, WATER_2, WATER_3];
|
||||
|
||||
pub const WATER_BODY: BodyPattern = [RGB_OFF; 60];
|
||||
|
||||
pub const PRIDE_DASHBOARD: DashboardPattern = [PRIDE_RED, PRIDE_GREEN, PRIDE_INDIGO];
|
||||
|
||||
pub const PRIDE_BODY: BodyPattern = [
|
||||
// Left Side
|
||||
// Red
|
||||
PRIDE_RED,
|
||||
PRIDE_RED,
|
||||
PRIDE_RED,
|
||||
PRIDE_RED,
|
||||
PRIDE_RED,
|
||||
// Orange
|
||||
PRIDE_ORANGE,
|
||||
PRIDE_ORANGE,
|
||||
PRIDE_ORANGE,
|
||||
PRIDE_ORANGE,
|
||||
PRIDE_ORANGE,
|
||||
// Yellow
|
||||
PRIDE_YELLOW,
|
||||
PRIDE_YELLOW,
|
||||
PRIDE_YELLOW,
|
||||
PRIDE_YELLOW,
|
||||
PRIDE_YELLOW,
|
||||
// Green
|
||||
PRIDE_GREEN,
|
||||
PRIDE_GREEN,
|
||||
PRIDE_GREEN,
|
||||
PRIDE_GREEN,
|
||||
PRIDE_GREEN,
|
||||
// Indigo
|
||||
PRIDE_INDIGO,
|
||||
PRIDE_INDIGO,
|
||||
PRIDE_INDIGO,
|
||||
PRIDE_INDIGO,
|
||||
PRIDE_INDIGO,
|
||||
// Violet
|
||||
PRIDE_VIOLET,
|
||||
PRIDE_VIOLET,
|
||||
PRIDE_VIOLET,
|
||||
PRIDE_VIOLET,
|
||||
PRIDE_VIOLET,
|
||||
// Right Side
|
||||
// Violet
|
||||
PRIDE_VIOLET,
|
||||
PRIDE_VIOLET,
|
||||
PRIDE_VIOLET,
|
||||
PRIDE_VIOLET,
|
||||
PRIDE_VIOLET,
|
||||
// Indigo
|
||||
PRIDE_INDIGO,
|
||||
PRIDE_INDIGO,
|
||||
PRIDE_INDIGO,
|
||||
PRIDE_INDIGO,
|
||||
PRIDE_INDIGO,
|
||||
// Green
|
||||
PRIDE_GREEN,
|
||||
PRIDE_GREEN,
|
||||
PRIDE_GREEN,
|
||||
PRIDE_GREEN,
|
||||
PRIDE_GREEN,
|
||||
// Yellow
|
||||
PRIDE_YELLOW,
|
||||
PRIDE_YELLOW,
|
||||
PRIDE_YELLOW,
|
||||
PRIDE_YELLOW,
|
||||
PRIDE_YELLOW,
|
||||
// Orange
|
||||
PRIDE_ORANGE,
|
||||
PRIDE_ORANGE,
|
||||
PRIDE_ORANGE,
|
||||
PRIDE_ORANGE,
|
||||
PRIDE_ORANGE,
|
||||
// Red
|
||||
PRIDE_RED,
|
||||
PRIDE_RED,
|
||||
PRIDE_RED,
|
||||
PRIDE_RED,
|
||||
PRIDE_RED,
|
||||
];
|
||||
|
||||
pub const TRANS_PRIDE_DASHBOARD: DashboardPattern = [TRANS_BLUE, RGB_WHITE, TRANS_PINK];
|
||||
|
||||
pub const TRANS_PRIDE_BODY: BodyPattern = [
|
||||
// Left Side
|
||||
TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_PINK, TRANS_PINK,
|
||||
TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, RGB_WHITE, RGB_WHITE, RGB_WHITE, RGB_WHITE,
|
||||
RGB_WHITE, RGB_WHITE, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK,
|
||||
TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE,
|
||||
// Right side
|
||||
TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_PINK, TRANS_PINK,
|
||||
TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, RGB_WHITE, RGB_WHITE, RGB_WHITE, RGB_WHITE,
|
||||
RGB_WHITE, RGB_WHITE, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK,
|
||||
TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE,
|
||||
];
|
||||
|
||||
pub const BRAKES_FRAMES: U16F0 = U16F0::lit("15");
|
||||
|
||||
pub const BRAKES_DASHBOARD: DashboardPattern = [BRAKES_RED; 3];
|
||||
|
||||
pub const BRAKES_BODY: BodyPattern = [BRAKES_RED; 60];
|
||||
|
||||
pub const BLINKER_FRAMES: U16F0 = U16F0::lit("10");
|
||||
|
||||
pub const LEFT_BLINKER_DASHBOARD: DashboardPattern = [BLINKER_AMBER, RGB_OFF, RGB_OFF];
|
||||
|
||||
pub const LEFT_BLINKER_BODY: BodyPattern = [
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
];
|
||||
|
||||
pub const RIGHT_BLINKER_DASHBOARD: DashboardPattern = [RGB_OFF, RGB_OFF, BLINKER_AMBER];
|
||||
|
||||
pub const RIGHT_BLINKER_BODY: BodyPattern = [
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
RGB_OFF,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
BLINKER_AMBER,
|
||||
];
|
|
@ -0,0 +1,17 @@
|
|||
use core::default::Default;
|
||||
use fixed::types::I8F8;
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct RGB<T> {
|
||||
pub r: T,
|
||||
pub g: T,
|
||||
pub b: T,
|
||||
}
|
||||
|
||||
const DASHBOARD_LIGHT_COUNT: usize = 3;
|
||||
|
||||
pub type DashboardPattern = [RGB<I8F8>; DASHBOARD_LIGHT_COUNT];
|
||||
|
||||
const BODY_LIGHT_COUNT: usize = 60;
|
||||
|
||||
pub type BodyPattern = [RGB<I8F8>; BODY_LIGHT_COUNT];
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "simulator"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
|
||||
cairo-rs = { version = "0.18" }
|
||||
fixed = { version = "1" }
|
||||
gio = { version = "0.18" }
|
||||
glib = { version = "0.18" }
|
||||
gtk = { version = "0.7", package = "gtk4", features = [ "v4_8" ] }
|
||||
lights-core = { path = "../core" }
|
||||
pango = { version = "*" }
|
|
@ -0,0 +1,288 @@
|
|||
use adw::prelude::*;
|
||||
use fixed::types::{I8F8, U128F0};
|
||||
use glib::{Object, Sender};
|
||||
use gtk::subclass::prelude::*;
|
||||
use lights_core::{
|
||||
App, BodyPattern, DashboardPattern, Event, Instant, FPS, OFF_BODY, OFF_DASHBOARD, RGB, UI,
|
||||
};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
env,
|
||||
rc::Rc,
|
||||
sync::mpsc::{Receiver, TryRecvError},
|
||||
};
|
||||
|
||||
const WIDTH: i32 = 640;
|
||||
const HEIGHT: i32 = 480;
|
||||
|
||||
pub struct Update {
|
||||
dashboard: DashboardPattern,
|
||||
lights: BodyPattern,
|
||||
}
|
||||
|
||||
pub struct DashboardLightsPrivate {
|
||||
lights: Rc<RefCell<DashboardPattern>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for DashboardLightsPrivate {
|
||||
const NAME: &'static str = "DashboardLights";
|
||||
type Type = DashboardLights;
|
||||
type ParentType = gtk::DrawingArea;
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
lights: Rc::new(RefCell::new(OFF_DASHBOARD)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for DashboardLightsPrivate {}
|
||||
impl WidgetImpl for DashboardLightsPrivate {}
|
||||
impl DrawingAreaImpl for DashboardLightsPrivate {}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct DashboardLights(ObjectSubclass<DashboardLightsPrivate>) @extends gtk::DrawingArea, gtk::Widget;
|
||||
}
|
||||
|
||||
impl DashboardLights {
|
||||
pub fn new() -> Self {
|
||||
let s: Self = Object::builder().build();
|
||||
|
||||
s.set_width_request(WIDTH);
|
||||
s.set_height_request(100);
|
||||
|
||||
s.set_draw_func({
|
||||
let s = s.clone();
|
||||
move |_, context, width, _| {
|
||||
let start = width as f64 / 2. - 150.;
|
||||
let lights = s.imp().lights.borrow();
|
||||
for i in 0..3 {
|
||||
context.set_source_rgb(
|
||||
lights[i].r.into(),
|
||||
lights[i].g.into(),
|
||||
lights[i].b.into(),
|
||||
);
|
||||
context.rectangle(start + 100. * i as f64, 10., 80., 80.);
|
||||
let _ = context.fill();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
pub fn set_lights(&self, lights: DashboardPattern) {
|
||||
*self.imp().lights.borrow_mut() = lights;
|
||||
self.queue_draw();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BikeLightsPrivate {
|
||||
lights: Rc<RefCell<BodyPattern>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for BikeLightsPrivate {
|
||||
const NAME: &'static str = "BikeLights";
|
||||
type Type = BikeLights;
|
||||
type ParentType = gtk::DrawingArea;
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
lights: Rc::new(RefCell::new(OFF_BODY)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for BikeLightsPrivate {}
|
||||
impl WidgetImpl for BikeLightsPrivate {}
|
||||
impl DrawingAreaImpl for BikeLightsPrivate {}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct BikeLights(ObjectSubclass<BikeLightsPrivate>) @extends gtk::DrawingArea, gtk::Widget;
|
||||
}
|
||||
|
||||
impl BikeLights {
|
||||
pub fn new() -> Self {
|
||||
let s: Self = Object::builder().build();
|
||||
|
||||
s.set_width_request(WIDTH);
|
||||
s.set_height_request(640);
|
||||
|
||||
let center = WIDTH as f64 / 2.;
|
||||
|
||||
s.set_draw_func({
|
||||
let s = s.clone();
|
||||
move |_, context, _, _| {
|
||||
let lights = s.imp().lights.borrow();
|
||||
for i in 0..30 {
|
||||
context.set_source_rgb(
|
||||
lights[i].r.into(),
|
||||
lights[i].g.into(),
|
||||
lights[i].b.into(),
|
||||
);
|
||||
context.rectangle(center - 45., 5. + 20. * i as f64, 15., 15.);
|
||||
let _ = context.fill();
|
||||
}
|
||||
for i in 0..30 {
|
||||
context.set_source_rgb(
|
||||
lights[i + 30].r.into(),
|
||||
lights[i + 30].g.into(),
|
||||
lights[i + 30].b.into(),
|
||||
);
|
||||
context.rectangle(center + 15., 5. + 20. * (30. - (i + 1) as f64), 15., 15.);
|
||||
let _ = context.fill();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
pub fn set_lights(&self, lights: [RGB<I8F8>; 60]) {
|
||||
*self.imp().lights.borrow_mut() = lights;
|
||||
self.queue_draw();
|
||||
}
|
||||
}
|
||||
|
||||
struct GTKUI {
|
||||
tx: Sender<Update>,
|
||||
rx: Receiver<Event>,
|
||||
}
|
||||
|
||||
impl UI for GTKUI {
|
||||
fn check_event(&mut self, _: Instant) -> Option<Event> {
|
||||
match self.rx.try_recv() {
|
||||
Ok(event) => Some(event),
|
||||
Err(TryRecvError::Empty) => None,
|
||||
Err(TryRecvError::Disconnected) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_lights(&self, dashboard_lights: DashboardPattern, lights: BodyPattern) {
|
||||
self.tx
|
||||
.send(Update {
|
||||
dashboard: dashboard_lights,
|
||||
lights,
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let adw_app = adw::Application::builder()
|
||||
.application_id("com.luminescent-dreams.bike-light-simulator")
|
||||
.build();
|
||||
|
||||
adw_app.connect_activate(move |adw_app| {
|
||||
let (update_tx, update_rx) =
|
||||
gtk::glib::MainContext::channel::<Update>(gtk::glib::Priority::DEFAULT);
|
||||
let (event_tx, event_rx) = std::sync::mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut bike_app = App::new(Box::new(GTKUI {
|
||||
tx: update_tx,
|
||||
rx: event_rx,
|
||||
}));
|
||||
loop {
|
||||
bike_app.tick(Instant(U128F0::from(
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis(),
|
||||
)));
|
||||
std::thread::sleep(std::time::Duration::from_millis(1000 / (FPS as u64)));
|
||||
}
|
||||
});
|
||||
|
||||
let window = adw::ApplicationWindow::builder()
|
||||
.application(adw_app)
|
||||
.default_width(WIDTH)
|
||||
.default_height(HEIGHT)
|
||||
.build();
|
||||
let layout = gtk::Box::builder()
|
||||
.orientation(gtk::Orientation::Vertical)
|
||||
.build();
|
||||
let controls = gtk::Box::builder()
|
||||
.orientation(gtk::Orientation::Horizontal)
|
||||
.build();
|
||||
|
||||
let dashboard_lights = DashboardLights::new();
|
||||
let bike_lights = BikeLights::new();
|
||||
|
||||
let left_button = gtk::Button::builder().label("L").build();
|
||||
let brake_button = gtk::Button::builder().label("Brakes").build();
|
||||
let right_button = gtk::Button::builder().label("R").build();
|
||||
|
||||
left_button.connect_clicked({
|
||||
let event_tx = event_tx.clone();
|
||||
move |_| {
|
||||
let _ = event_tx.send(Event::LeftBlinker);
|
||||
}
|
||||
});
|
||||
|
||||
brake_button.connect_clicked({
|
||||
let event_tx = event_tx.clone();
|
||||
move |_| {
|
||||
let _ = event_tx.send(Event::Brake);
|
||||
}
|
||||
});
|
||||
|
||||
right_button.connect_clicked({
|
||||
let event_tx = event_tx.clone();
|
||||
move |_| {
|
||||
let _ = event_tx.send(Event::RightBlinker);
|
||||
}
|
||||
});
|
||||
|
||||
controls.append(&left_button);
|
||||
controls.append(&brake_button);
|
||||
controls.append(&right_button);
|
||||
layout.append(&controls);
|
||||
|
||||
let pattern_controls = gtk::Box::builder()
|
||||
.orientation(gtk::Orientation::Horizontal)
|
||||
.build();
|
||||
|
||||
let previous_pattern = gtk::Button::builder().label("Previous").build();
|
||||
let next_pattern = gtk::Button::builder().label("Next").build();
|
||||
|
||||
previous_pattern.connect_clicked({
|
||||
let event_tx = event_tx.clone();
|
||||
move |_| {
|
||||
let _ = event_tx.send(Event::PreviousPattern);
|
||||
}
|
||||
});
|
||||
|
||||
next_pattern.connect_clicked({
|
||||
let event_tx = event_tx.clone();
|
||||
move |_| {
|
||||
let _ = event_tx.send(Event::NextPattern);
|
||||
}
|
||||
});
|
||||
|
||||
pattern_controls.append(&previous_pattern);
|
||||
pattern_controls.append(&next_pattern);
|
||||
layout.append(&pattern_controls);
|
||||
|
||||
layout.append(&dashboard_lights);
|
||||
layout.append(&bike_lights);
|
||||
|
||||
update_rx.attach(None, {
|
||||
let dashboard_lights = dashboard_lights.clone();
|
||||
let bike_lights = bike_lights.clone();
|
||||
move |Update { dashboard, lights }| {
|
||||
dashboard_lights.set_lights(dashboard);
|
||||
bike_lights.set_lights(lights);
|
||||
glib::ControlFlow::Continue
|
||||
}
|
||||
});
|
||||
|
||||
window.set_content(Some(&layout));
|
||||
window.present();
|
||||
});
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
ApplicationExtManual::run_with_args(&adw_app, &args);
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "1.77.0"
|
||||
targets = [ "wasm32-unknown-unknown" ]
|
||||
targets = [ "wasm32-unknown-unknown", "thumbv6m-none-eabi" ]
|
||||
|
|
Loading…
Reference in New Issue