Compare commits

..

16 Commits

Author SHA1 Message Date
Savanni D'Gerinel 70c013218a Update pins for the realities of the board layout 2024-07-30 14:50:14 -04:00
Savanni D'Gerinel 37c7e04820 Turn on the built-in LED when software starts up 2024-07-20 11:21:16 -04:00
Savanni D'Gerinel 291663d4a3 Re-add the armv6 toolchain 2024-07-08 09:35:44 -04:00
Savanni D'Gerinel 2b0fc7639e Debounce buttons, fix colors, and add a new water pattern 2024-07-08 09:29:34 -04:00
Savanni D'Gerinel 80d8dedbaf Adjust colors and the blinker patterns 2024-07-08 09:29:34 -04:00
Savanni D'Gerinel d7a70119c8 Send out the full set of lights 2024-07-08 09:29:34 -04:00
Savanni D'Gerinel 54c4b99ab6 Improve the blinker animations and state transitions when switching blinkers 2024-07-08 09:29:34 -04:00
Savanni D'Gerinel ef5415303b Start monitoring events 2024-07-08 09:29:34 -04:00
Savanni D'Gerinel 8d183d6d8c Build some of the framework for the bike application
This now sends a set of lights to the dashboard from a pico. I had to
adjust some of the colors as they do not look nearly as good in lights
as they do in the screen. There is no real application loop yet, no the
ability to get feedback from external controls.
2024-07-08 09:29:32 -04:00
Savanni D'Gerinel 0b949111d2 Switch to a fixed point arithmatic library 2024-07-08 09:28:40 -04:00
Savanni D'Gerinel 6164cb3b39 Refactor the bike library until it compiles with no_std
Theoretically, this is the first step to getting to running on the pico
2024-07-08 09:28:40 -04:00
Savanni D'Gerinel 22f0f9061c Rotate the right side
The actual bike is going to be a long loop which folds from the end of
the left side to the back end of the right side. This requires that the
colors get moved around for proper mirroring.
2024-07-08 09:28:40 -04:00
Savanni D'Gerinel 0bb5e62f96 Set up a bunch of animations and some state transitions! 2024-07-08 09:28:40 -04:00
Savanni D'Gerinel 06aedc34bb Now I'm able to send messages from the UI to the core 2024-07-08 09:28:40 -04:00
Savanni D'Gerinel 84b077e20c Build the core of the application. 2024-07-08 09:28:40 -04:00
Savanni D'Gerinel fc2e88add2 Set up a GTK simulator for the bike lights engine 2024-07-08 09:28:38 -04:00
12 changed files with 1811 additions and 4 deletions

413
Cargo.lock generated
View File

@ -120,6 +120,12 @@ version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]] [[package]]
name = "async-channel" name = "async-channel"
version = "1.9.0" version = "1.9.0"
@ -330,6 +336,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "az"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
[[package]] [[package]]
name = "backtrace" name = "backtrace"
version = "0.3.69" version = "0.3.69"
@ -345,6 +357,15 @@ dependencies = [
"rustc-demangle", "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]] [[package]]
name = "base64" name = "base64"
version = "0.9.3" version = "0.9.3"
@ -367,6 +388,22 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" 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]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.3" version = "0.5.3"
@ -388,6 +425,12 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitfield"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -692,6 +735,38 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 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]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.11" version = "0.2.11"
@ -710,6 +785,15 @@ dependencies = [
"crc-catalog", "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]] [[package]]
name = "crc-catalog" name = "crc-catalog"
version = "2.4.0" version = "2.4.0"
@ -725,6 +809,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "critical-section"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.4" version = "0.8.4"
@ -826,6 +916,12 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "debug-helper"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e"
[[package]] [[package]]
name = "deflate" name = "deflate"
version = "0.8.6" version = "0.8.6"
@ -918,6 +1014,35 @@ dependencies = [
"serde 1.0.193", "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]] [[package]]
name = "emseries" name = "emseries"
version = "0.6.0" version = "0.6.0"
@ -1056,7 +1181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
dependencies = [ dependencies = [
"memoffset", "memoffset",
"rustc_version", "rustc_version 0.4.0",
] ]
[[package]] [[package]]
@ -1117,6 +1242,18 @@ dependencies = [
"tokio", "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]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.28" version = "1.0.28"
@ -1222,6 +1359,45 @@ dependencies = [
"percent-encoding 2.3.1", "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]] [[package]]
name = "ft-core" name = "ft-core"
version = "0.1.0" version = "0.1.0"
@ -1241,6 +1417,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "fugit"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
dependencies = [
"gcd",
]
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.29" version = "0.3.29"
@ -1369,6 +1554,12 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "gcd"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]] [[package]]
name = "gdk-pixbuf" name = "gdk-pixbuf"
version = "0.18.3" version = "0.18.3"
@ -2176,6 +2367,15 @@ dependencies = [
"windows-sys 0.48.0", "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]] [[package]]
name = "itertools" name = "itertools"
version = "0.12.0" version = "0.12.0"
@ -2303,6 +2503,20 @@ dependencies = [
"vcpkg", "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]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.3.8" version = "0.3.8"
@ -2531,6 +2745,21 @@ dependencies = [
"tempfile", "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]] [[package]]
name = "nix" name = "nix"
version = "0.27.1" version = "0.27.1"
@ -2646,6 +2875,26 @@ dependencies = [
"libc", "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]] [[package]]
name = "object" name = "object"
version = "0.32.1" version = "0.32.1"
@ -2768,6 +3017,12 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "panic-halt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
[[package]] [[package]]
name = "parking" name = "parking"
version = "2.2.0" version = "2.2.0"
@ -2942,6 +3197,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 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]] [[package]]
name = "piper" name = "piper"
version = "0.2.1" version = "0.2.1"
@ -3452,6 +3718,76 @@ dependencies = [
"thiserror", "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]] [[package]]
name = "rsa" name = "rsa"
version = "0.9.6" version = "0.9.6"
@ -3484,13 +3820,22 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 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]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [ dependencies = [
"semver", "semver 1.0.20",
] ]
[[package]] [[package]]
@ -3626,12 +3971,27 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6" 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]] [[package]]
name = "semver" name = "semver"
version = "1.0.20" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "serde"
version = "0.9.15" version = "0.9.15"
@ -3751,6 +4111,20 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simulator"
version = "0.1.0"
dependencies = [
"cairo-rs",
"fixed",
"gio",
"glib",
"gtk4",
"libadwaita",
"lights-core",
"pango",
]
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "0.2.3" version = "0.2.3"
@ -3835,7 +4209,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c"
dependencies = [ dependencies = [
"itertools", "itertools 0.12.0",
"nom", "nom",
"unicode_categories", "unicode_categories",
] ]
@ -4036,6 +4410,12 @@ dependencies = [
"urlencoding", "urlencoding",
] ]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]] [[package]]
name = "stringprep" name = "stringprep"
version = "0.1.4" version = "0.1.4"
@ -4626,6 +5006,12 @@ version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "usb-device"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508"
[[package]] [[package]]
name = "utf-8" name = "utf-8"
version = "0.7.6" version = "0.7.6"
@ -4673,6 +5059,12 @@ version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b" checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b"
[[package]]
name = "vcell"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
@ -4709,6 +5101,21 @@ dependencies = [
"warp", "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]] [[package]]
name = "wait-timeout" name = "wait-timeout"
version = "0.2.0" version = "0.2.0"

View File

@ -2,6 +2,9 @@
resolver = "2" resolver = "2"
members = [ members = [
"authdb", "authdb",
"bike-lights/bike",
"bike-lights/core",
"bike-lights/simulator",
"changeset", "changeset",
"config", "config",
"config-derive", "config-derive",

View File

@ -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"

View File

@ -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" }

View File

@ -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());
}
}

View File

@ -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" }

481
bike-lights/core/src/lib.rs Normal file
View File

@ -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);
}
}

View File

@ -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,
];

View File

@ -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];

View File

@ -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 = "*" }

View File

@ -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);
}

View File

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "1.77.0" channel = "1.77.0"
targets = [ "wasm32-unknown-unknown" ] targets = [ "wasm32-unknown-unknown", "thumbv6m-none-eabi" ]