diff --git a/esp32/.cargo/config.toml b/.cargo/config.toml similarity index 72% rename from esp32/.cargo/config.toml rename to .cargo/config.toml index 7e7fcc9..03bf83c 100644 --- a/esp32/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,14 +1,15 @@ +[build] +target = "xtensa-esp32-none-elf" [target.xtensa-esp32-none-elf] runner = "espflash flash --monitor" rustflags = [ - "-C", "link-arg=-Wl,-Tlinkall.x", - "-C", "link-arg=-nostartfiles", + "-C", + "link-arg=-Wl,-Tlinkall.x", + "-C", + "link-arg=-nostartfiles", ] -[build] -target = "xtensa-esp32-none-elf" - [unstable] build-std = ["core", "alloc", "compiler_builtins"] diff --git a/.gitignore b/.gitignore index bdce26b..28de11a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ export node_modules +target diff --git a/esp32/Cargo.lock b/Cargo.lock similarity index 71% rename from esp32/Cargo.lock rename to Cargo.lock index e5e4d15..2a8116d 100644 --- a/esp32/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,18 @@ dependencies = [ "ufmt 0.1.2", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -22,6 +34,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + [[package]] name = "allocator-api2" version = "0.3.1" @@ -34,6 +55,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "as5600" version = "0.8.0" @@ -45,12 +75,27 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ascii-canvas" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" +dependencies = [ + "term", +] + [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "az" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7" + [[package]] name = "bare-metal" version = "0.2.5" @@ -66,12 +111,33 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitfield" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + [[package]] name = "bitfield" version = "0.19.4" @@ -113,6 +179,30 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bt-hci" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "211713d2e9fb4793ce4360a712c0764264aff6be48932ccf02ca2a331c0436a9" +dependencies = [ + "btuuid", + "defmt 1.1.0", + "embassy-sync 0.8.0", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", + "futures-intrusive", + "heapless 0.9.3", +] + +[[package]] +name = "btuuid" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f48f1e9b0aad0a4f05d17bdeae0fa20ff798e272a03a6940ca27ad9c5a6ae7" +dependencies = [ + "defmt 0.3.100", +] + [[package]] name = "bytemuck" version = "1.25.0" @@ -127,9 +217,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.61" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "shlex", @@ -141,6 +231,27 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "defmt 1.1.0", + "num-traits", + "pure-rust-locales", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "const-default" version = "1.0.0" @@ -169,6 +280,26 @@ dependencies = [ "volatile-register", ] +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -178,12 +309,27 @@ dependencies = [ "libc", ] +[[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 = "critical-section" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.7" @@ -194,6 +340,40 @@ dependencies = [ "typenum", ] +[[package]] +name = "cyw43" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033e856bf6f38c96fee3e3f398cceff0ca2c2fae5db255bcd23495216c7047da" +dependencies = [ + "aligned", + "bt-hci", + "cortex-m", + "cortex-m-rt", + "defmt 1.1.0", + "embassy-futures", + "embassy-net-driver-channel 0.4.0", + "embassy-sync 0.8.0", + "embassy-time", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-io-async 0.7.0", + "futures", + "heapless 0.9.3", +] + +[[package]] +name = "cyw43-pio" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "768b74f36be804f7b1020938e711d91c0ab5a847cf620d3756245ae7e641e1e2" +dependencies = [ + "cyw43", + "defmt 1.1.0", + "embassy-rp", + "fixed", +] + [[package]] name = "darling" version = "0.20.11" @@ -297,20 +477,26 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + [[package]] name = "defmt" version = "0.3.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" dependencies = [ - "defmt 1.0.1", + "defmt 1.1.0", ] [[package]] name = "defmt" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" dependencies = [ "bitflags 1.3.2", "defmt-macros", @@ -318,9 +504,9 @@ dependencies = [ [[package]] name = "defmt-macros" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" dependencies = [ "defmt-parser", "proc-macro-error2", @@ -340,12 +526,12 @@ dependencies = [ [[package]] name = "defmt-rtt" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +checksum = "c0f73a4a4a91609e977ae3b7bd831ffa292edfd42ad140a3244a61d805b0e05e" dependencies = [ "critical-section", - "defmt 1.0.1", + "defmt 1.1.0", ] [[package]] @@ -359,6 +545,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "device-state" +version = "0.1.0" +dependencies = [ + "owned_str", + "serde", + "serde_json", + "ufmt 0.2.0", +] + [[package]] name = "digest" version = "0.10.7" @@ -398,6 +594,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "embassy-embedded-hal" version = "0.6.0" @@ -405,7 +607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0641612053b2f34fc250bb63f6630ae75de46e02ade7f457268447081d709ce" dependencies = [ "embassy-futures", - "embassy-hal-internal", + "embassy-hal-internal 0.4.0", "embassy-sync 0.8.0", "embedded-hal 0.2.7", "embedded-hal 1.0.0", @@ -424,7 +626,7 @@ dependencies = [ "cordyceps", "cortex-m", "critical-section", - "defmt 1.0.1", + "defmt 1.1.0", "document-features", "embassy-executor-macros", "embassy-executor-timer-queue", @@ -463,20 +665,32 @@ dependencies = [ "num-traits", ] +[[package]] +name = "embassy-hal-internal" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568659fc53866d3d85c60fa33723fb751aa69e71507634fc2c19e7649432fb75" +dependencies = [ + "cortex-m", + "critical-section", + "defmt 1.1.0", + "num-traits", +] + [[package]] name = "embassy-net" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "347bc855bdbdf50ed9c5a1d80e8204badb0ba149b8732dde38e1e9708ed9d313" dependencies = [ - "defmt 1.0.1", + "defmt 1.1.0", "document-features", "embassy-net-driver", "embassy-sync 0.8.0", "embassy-time", "embedded-io-async 0.7.0", "embedded-nal-async", - "heapless 0.9.2", + "heapless 0.9.3", "managed", "smoltcp", ] @@ -490,6 +704,68 @@ dependencies = [ "defmt 0.3.100", ] +[[package]] +name = "embassy-net-driver-channel" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b2739fbcf6cd206ae08779c7d709087b16577d255f2ea4a45bc4bbbf305b3f" +dependencies = [ + "embassy-futures", + "embassy-net-driver", + "embassy-sync 0.7.2", +] + +[[package]] +name = "embassy-net-driver-channel" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07d2eb9f05a6fc876500949856ea1be40773d866d8cb99384f72d0ae4568c16" +dependencies = [ + "embassy-futures", + "embassy-net-driver", + "embassy-sync 0.8.0", +] + +[[package]] +name = "embassy-rp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d98f472894c1ecffac5596c657af5305c57282d29e8746d7fec033951931bc8" +dependencies = [ + "cfg-if", + "chrono", + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt 1.1.0", + "document-features", + "embassy-embedded-hal", + "embassy-futures", + "embassy-hal-internal 0.5.0", + "embassy-sync 0.8.0", + "embassy-time", + "embassy-time-driver", + "embassy-time-queue-utils", + "embassy-usb-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", + "embedded-storage", + "embedded-storage-async", + "fixed", + "nb 1.1.0", + "pio", + "rand_core 0.6.4", + "rand_core 0.9.5", + "rp-pac", + "rp2040-boot2", + "sha2-const-stable", + "smart-leds", +] + [[package]] name = "embassy-sync" version = "0.6.2" @@ -529,7 +805,7 @@ dependencies = [ "embedded-io-async 0.7.0", "futures-core", "futures-sink", - "heapless 0.9.2", + "heapless 0.9.3", ] [[package]] @@ -540,7 +816,7 @@ checksum = "592b0c143ec626e821d4d90da51a2bd91d559d6c442b7c74a47d368c9e23d97a" dependencies = [ "cfg-if", "critical-section", - "defmt 1.0.1", + "defmt 1.1.0", "document-features", "embassy-time-driver", "embedded-hal 0.2.7", @@ -565,7 +841,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168297bf80aaf114b3c9ad589bf38b01b3009b9af7f97cd18086c5bbf96f5693" dependencies = [ "embassy-executor-timer-queue", - "heapless 0.9.2", + "heapless 0.9.3", +] + +[[package]] +name = "embassy-usb" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4462e48b19a4f401a11901bdd981aab80c6a826608016a0bdc73cbbab31954" +dependencies = [ + "embassy-futures", + "embassy-net-driver-channel 0.3.2", + "embassy-sync 0.7.2", + "embassy-usb-driver", + "embedded-io-async 0.6.1", + "heapless 0.8.0", + "ssmarshal", + "usbd-hid", +] + +[[package]] +name = "embassy-usb-driver" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a189cbf1d8ab3c591f88632f85c1df892b6ab04116037cbee39dc37cbdcd79f" +dependencies = [ + "defmt 1.1.0", + "embedded-io-async 0.7.0", +] + +[[package]] +name = "embassy-usb-logger" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deed6d36715838d6adbbff13b215b03a9deeaa66a64d5fccd6353708ccfb8b8f" +dependencies = [ + "embassy-futures", + "embassy-sync 0.7.2", + "embassy-usb", + "log", ] [[package]] @@ -613,6 +927,16 @@ dependencies = [ "nb 1.1.0", ] +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + [[package]] name = "embedded-io" version = "0.6.1" @@ -624,6 +948,9 @@ name = "embedded-io" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7" +dependencies = [ + "defmt 1.1.0", +] [[package]] name = "embedded-io-async" @@ -640,6 +967,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2564b9f813c544241430e147d8bc454815ef9ac998878d30cc3055449f7fd4c0" dependencies = [ + "defmt 1.1.0", "embedded-io 0.7.1", ] @@ -677,6 +1005,21 @@ dependencies = [ "embedded-storage", ] +[[package]] +name = "ena" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" +dependencies = [ + "log", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "enumset" version = "1.1.12" @@ -731,7 +1074,7 @@ dependencies = [ "esp-config", "esp-metadata-generated", "esp-println", - "heapless 0.9.2", + "heapless 0.9.3", "riscv", "semihosting", "xtensa-lx", @@ -899,7 +1242,7 @@ dependencies = [ "esp-sync", "esp-wifi-sys-esp32", "esp32 0.40.2", - "heapless 0.9.2", + "heapless 0.9.3", "instability", "num-derive", "num-traits", @@ -998,8 +1341,9 @@ dependencies = [ "arrayvec", "as5600", "critical-section", - "defmt 1.0.1", + "defmt 1.1.0", "defmt-rtt", + "device-state", "embassy-executor", "embassy-futures", "embassy-net", @@ -1016,13 +1360,9 @@ dependencies = [ "esp-radio", "esp-rtos", "log", - "owned_str", "panic-probe", "portable-atomic", - "serde", - "serde_json", "static_cell", - "ufmt 0.2.0", ] [[package]] @@ -1094,6 +1434,24 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixed" +version = "1.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9af2cbf772fa6d1c11358f92ef554cb6b386201210bcf0e91fb7fba8a907fb40" +dependencies = [ + "az", + "bytemuck", + "half", + "typenum", +] + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "fnv" version = "1.0.7" @@ -1109,12 +1467,63 @@ dependencies = [ "gcd", ] +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "futures-sink" version = "0.3.32" @@ -1134,6 +1543,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", + "futures-macro", + "futures-sink", "futures-task", "pin-project-lite", ] @@ -1169,6 +1580,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + [[package]] name = "hash32" version = "0.3.1" @@ -1180,9 +1602,18 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heapless" @@ -1196,11 +1627,11 @@ dependencies = [ [[package]] name = "heapless" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" dependencies = [ - "defmt 1.0.1", + "defmt 1.1.0", "hash32", "stable_deref_trait", ] @@ -1224,7 +1655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.17.1", ] [[package]] @@ -1249,6 +1680,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -1279,6 +1719,47 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lalrpop" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "sha3", + "string_cache", + "term", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" +dependencies = [ + "regex-automata", + "rustversion", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1303,6 +1784,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -1358,6 +1848,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1387,6 +1883,27 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -1406,7 +1923,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" dependencies = [ "cortex-m", - "defmt 1.0.1", + "defmt 1.1.0", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", ] [[package]] @@ -1415,12 +1955,117 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico" +version = "0.1.0" +dependencies = [ + "ag-lcd", + "arrayvec", + "as5600", + "cortex-m", + "cortex-m-rt", + "critical-section", + "cyw43", + "cyw43-pio", + "defmt 1.1.0", + "defmt-rtt", + "embassy-executor", + "embassy-futures", + "embassy-net", + "embassy-rp", + "embassy-sync 0.8.0", + "embassy-time", + "embassy-usb-logger", + "embedded-hal-compat", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", + "log", + "owned_str", + "panic-probe", + "portable-atomic", + "static_cell", + "ufmt 0.2.0", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pio" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ba4153cee9585abc451271aa437d9e8defdea8b468d48ba6b8f098cbe03d7f" +dependencies = [ + "pio-core", + "pio-proc", +] + +[[package]] +name = "pio-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61d90fddc3d67f21bbf93683bc461b05d6a29c708caf3ffb79947d7ff7095406" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "pio-parser" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825266c1eaddf54f636d06eefa4bf3c99d774c14ec46a4a6c6e5128a0f10d205" +dependencies = [ + "lalrpop", + "lalrpop-util", + "pio-core", +] + +[[package]] +name = "pio-proc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed4a76571f5fe51af43cc80ac870fe0c79cc0cdd686b9002a6c4c84bfdd0176b" +dependencies = [ + "codespan-reporting", + "lalrpop-util", + "pio-core", + "pio-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -1460,6 +2105,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -1506,6 +2157,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pure-rust-locales" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d" +dependencies = [ + "defmt 1.1.0", +] + [[package]] name = "quote" version = "1.0.45" @@ -1533,6 +2193,27 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + [[package]] name = "regex-automata" version = "0.4.14" @@ -1550,6 +2231,12 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + [[package]] name = "riscv" version = "0.15.0" @@ -1622,6 +2309,25 @@ dependencies = [ "svgbobdoc", ] +[[package]] +name = "rp-pac" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af65855c40b2c35079514c5489abffc0429347fef25d8467ff98ad84b4322d3" +dependencies = [ + "cortex-m", + "cortex-m-rt", +] + +[[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 = "rustc_version" version = "0.2.3" @@ -1643,12 +2349,27 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "semihosting" version = "0.1.25" @@ -1748,6 +2469,22 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1773,12 +2510,36 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smart-leds" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66df34e571fa9993fa6f99131a374d58ca3d694b75f9baac93458fe0d6057bf0" +dependencies = [ + "smart-leds-trait", +] + +[[package]] +name = "smart-leds-trait" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f4441a131924d58da6b83a7ad765c460e64630cce504376c3a87a2558c487f" +dependencies = [ + "rgb", +] + [[package]] name = "smoltcp" version = "0.13.1" @@ -1789,7 +2550,7 @@ dependencies = [ "byteorder", "cfg-if", "defmt 0.3.100", - "heapless 0.9.2", + "heapless 0.9.3", "managed", ] @@ -1808,6 +2569,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f368519fc6c85fc1afdb769fb5a51123f6158013e143656e25a3485a0d401c" +[[package]] +name = "ssmarshal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e6ad23b128192ed337dfa4f1b8099ced0c2bf30d61e551b65fda5916dbb850" +dependencies = [ + "encode_unicode", + "serde", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -1823,6 +2594,18 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + [[package]] name = "strsim" version = "0.11.1" @@ -1885,6 +2668,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "term" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" +dependencies = [ + "windows-sys", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -2082,12 +2874,65 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "unsafe-libyaml" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless 0.8.0", + "portable-atomic", +] + +[[package]] +name = "usbd-hid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f291ab53d428685cc780f08a2eb9d5d6ff58622db2b36e239a4f715f1e184c" +dependencies = [ + "serde", + "ssmarshal", + "usb-device", + "usbd-hid-macros", +] + +[[package]] +name = "usbd-hid-descriptors" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee54712c5d778d2fb2da43b1ce5a7b5060886ef7b09891baeb4bf36910a3ed" +dependencies = [ + "bitfield 0.14.0", +] + +[[package]] +name = "usbd-hid-macros" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb573c76e7884035ac5e1ab4a81234c187a82b6100140af0ab45757650ccda38" +dependencies = [ + "byteorder", + "hashbrown 0.13.2", + "log", + "proc-macro2", + "quote", + "serde", + "syn 1.0.109", + "usbd-hid-descriptors", +] + [[package]] name = "valuable" version = "0.1.1" @@ -2121,6 +2966,16 @@ dependencies = [ "vcell", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "winapi-util" version = "0.1.11" @@ -2194,6 +3049,26 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b44aa51 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[workspace] +members = [ + "device-state", + "esp32", + "pico", +] +resolver = "2" + +[profile.release] +debug = true +debug-assertions = true +lto = "fat" +codegen-units = 1 diff --git a/device-state/Cargo.toml b/device-state/Cargo.toml new file mode 100644 index 0000000..ba0508e --- /dev/null +++ b/device-state/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "device-state" +version = "0.1.0" +edition = "2024" + +[dependencies] +owned_str = "0.1.2" +serde = { version = "1.0.228", default-features = false, features = ["derive", "alloc"] } +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +ufmt = "0.2.0" diff --git a/device-state/src/lib.rs b/device-state/src/lib.rs new file mode 100644 index 0000000..e162dde --- /dev/null +++ b/device-state/src/lib.rs @@ -0,0 +1,360 @@ +#![no_std] + +extern crate alloc; + +use alloc::string::String; +use core::str::FromStr; + +use owned_str::OwnedStr; +use serde::{Deserialize, Serialize}; +use ufmt::uwrite; + +const ARROW_RIGHT: char = char::from_u32(0b0111_1110).unwrap(); +const ARROW_LEFT: char = char::from_u32(0b0111_1111).unwrap(); +const DOT: char = char::from_u32(0b1010_0101).unwrap(); + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ViewState { + Loading, + Question, + Results, +} + +#[derive(Deserialize, Clone, Copy)] +pub enum QuestionType { + Choice, + Numeric { min: i32, max: i32 }, +} + +#[derive(Clone)] +pub struct QuestionData { + pub text: OwnedStr<256>, + pub q_type: QuestionType, + pub points: i32, + pub index: usize, +} + +#[derive(Deserialize)] +pub struct QuestionDataNet<'a> { + pub text: &'a str, + pub q_type: QuestionType, + pub points: i32, + pub index: usize, +} + +#[derive(Deserialize)] +pub enum ProxyOutput<'a> { + Question(QuestionDataNet<'a>), + Results, + Error(&'a str), +} + +impl<'a> From> for QuestionData { + fn from(value: QuestionDataNet<'a>) -> Self { + QuestionData { + text: OwnedStr::from_str(value.text).unwrap(), + q_type: value.q_type, + points: value.points, + index: value.index, + } + } +} + +#[derive(Clone, Copy)] +pub struct WheelData { + pub value: i32, + pub min: i32, + pub max: i32, + pub accumulated: i32, +} + +impl WheelData { + pub const fn empty() -> Self { + Self { + value: 0, + min: 0, + max: 0, + accumulated: 0, + } + } +} + +pub struct DeviceState { + view: ViewState, + question: Option, + wheel: WheelData, + last_index: usize, + title_offset: usize, +} + +impl DeviceState { + pub const fn new() -> Self { + Self { + view: ViewState::Loading, + question: None, + wheel: WheelData::empty(), + last_index: 0, + title_offset: 0, + } + } + + pub fn reset(&mut self) { + self.question = None; + self.wheel = WheelData::empty(); + self.last_index = 0; + self.title_offset = 0; + } + + pub fn view_state(&self) -> ViewState { + self.view + } + + pub fn wheel(&self) -> WheelData { + self.wheel + } + + pub fn wheel_mut(&mut self) -> &mut WheelData { + &mut self.wheel + } + + pub fn apply_proxy_output(&mut self, data: ProxyOutput<'_>) { + match data { + ProxyOutput::Question(data) => { + let data: QuestionData = data.into(); + let mut future_wheel = WheelData::empty(); + if let QuestionType::Numeric { min, max } = data.q_type { + future_wheel.max = max; + future_wheel.min = min; + future_wheel.value = (min + max) / 2; + } + self.question = Some(data); + self.view = ViewState::Question; + self.wheel = future_wheel; + } + ProxyOutput::Results => { + self.view = ViewState::Results; + } + ProxyOutput::Error(_) => {} + } + } + + pub fn question(&self) -> Option<&QuestionData> { + self.question.as_ref() + } + + pub fn tick(&mut self) { + if self.view != ViewState::Question { + return; + } + let Some(question) = self.question.as_ref() else { + return; + }; + self.title_offset = self.title_offset.wrapping_add(1); + if question.index != self.last_index { + self.last_index = question.index; + self.title_offset = 0; + } + } + + pub fn render_lines(&mut self) -> Option<(OwnedStr<16>, OwnedStr<16>)> { + if self.view != ViewState::Question { + return None; + } + + let question = self.question.as_ref()?; + let title_line = if question.text.len() > 16 { + self.title_offset %= question.text.len() - 16; + OwnedStr::from_str(&question.text[self.title_offset..self.title_offset + 16]).unwrap() + } else { + OwnedStr::from_str(&question.text).unwrap() + }; + + let number_str: OwnedStr<16> = match question.q_type { + QuestionType::Choice => { + let mut writer = OwnedStrWriter::new(); + writer.push(DOT).unwrap(); + writer.push(' ').unwrap(); + uwrite!(writer, "{}", question.points).unwrap(); + writer.into() + } + QuestionType::Numeric { min, max } => { + let mut writer = OwnedStrWriter::new(); + if self.wheel.value > min { + writer.push(ARROW_LEFT).unwrap(); + writer.push(' ').unwrap(); + } + uwrite!(writer, "{}", self.wheel.value).unwrap(); + if self.wheel.value < max { + writer.push(ARROW_RIGHT).unwrap(); + writer.push(' ').unwrap(); + } + + writer.into() + } + }; + let second_line = center_str::<16>(&number_str, 16).unwrap(); + Some((title_line, second_line)) + } + + pub fn response_value(&self, button: u8) -> i32 { + match self.question.as_ref() { + Some(q) => match q.q_type { + QuestionType::Numeric { .. } => self.wheel.value, + QuestionType::Choice => button as i32, + }, + _ => button as i32, + } + } +} + +#[derive(Serialize)] +pub enum WriteType<'a> { + QuizResponse(i32), + DeviceId(&'a str), +} + +pub fn parse_proxy_output<'a>(input: &'a str) -> Result, serde_json::Error> { + serde_json::from_str::>(input) +} + +pub fn serialize_write(data: &WriteType<'_>) -> Result { + serde_json::to_string(data) +} + +pub const WHEEL_TICKS: i32 = 4096; + +pub fn wheel_delta(old_angle: i32, current_angle: i32, inverted: bool) -> i32 { + let mut diff = current_angle - old_angle; + if diff.abs() > WHEEL_TICKS / 2 { + diff = if diff > 0 { + diff - WHEEL_TICKS + } else { + diff + WHEEL_TICKS + }; + } + + if inverted { + -diff + } else { + diff + } +} + +pub fn apply_wheel_delta( + value: &mut i32, + min: i32, + max: i32, + accumulated: &mut i32, + diff: i32, + precision: i32, +) { + if max == min || precision <= 0 { + return; + } + + *accumulated += diff; + let step_count = *accumulated / precision; + if step_count == 0 { + return; + } + + *accumulated -= step_count * precision; + *value += step_count; + if *value < min { + *value = min; + } else if *value > max { + *value = max; + } + if *value == min || *value == max { + *accumulated = 0; + } +} + +pub struct OwnedStrWriter(OwnedStr); + +impl OwnedStrWriter { + pub fn new() -> Self { + Self(OwnedStr::new()) + } + + pub fn push(&mut self, c: char) -> Result<(), owned_str::Error> { + self.0.try_push(c).map(|_| ()) + } +} + +impl From> for OwnedStrWriter { + fn from(owned_str: OwnedStr) -> Self { + Self(owned_str) + } +} + +impl From> for OwnedStr { + fn from(writer: OwnedStrWriter) -> Self { + writer.0 + } +} + +impl ufmt::uWrite for OwnedStrWriter { + type Error = owned_str::Error; + fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { + self.0.try_push_str(s).map(|_| ()) + } + fn write_char(&mut self, c: char) -> Result<(), Self::Error> { + self.0.try_push(c).map(|_| ()) + } +} + +pub fn center_str(text: &str, width: usize) -> Result, owned_str::Error> { + let mut res = OwnedStr::new(); + let len = text.len(); + let padding = (width.saturating_sub(len) + 1) / 2; + for _ in 0..padding { + res.try_push(' ')?; + } + res.try_push_str(text)?; + Ok(res) +} + +#[cfg(test)] +mod tests { + use super::{apply_wheel_delta, wheel_delta}; + + #[test] + fn wraps_forward_across_zero() { + assert_eq!(wheel_delta(4090, 5, false), 11); + } + + #[test] + fn wraps_backward_across_zero() { + assert_eq!(wheel_delta(5, 4090, false), -11); + } + + #[test] + fn inverts_direction() { + assert_eq!(wheel_delta(10, 20, true), -10); + } + + #[test] + fn accumulates_before_applying_selection() { + let mut value = 5; + let mut accumulated = 0; + + apply_wheel_delta(&mut value, 0, 10, &mut accumulated, 10, 32); + assert_eq!(value, 5); + assert_eq!(accumulated, 10); + + apply_wheel_delta(&mut value, 0, 10, &mut accumulated, 22, 32); + assert_eq!(value, 6); + assert_eq!(accumulated, 0); + } + + #[test] + fn clamps_and_resets_at_bounds() { + let mut value = 10; + let mut accumulated = 0; + + apply_wheel_delta(&mut value, 0, 10, &mut accumulated, 64, 32); + assert_eq!(value, 10); + assert_eq!(accumulated, 0); + } +} diff --git a/esp32/Cargo.toml b/esp32/Cargo.toml index 0b5ac38..1906585 100644 --- a/esp32/Cargo.toml +++ b/esp32/Cargo.toml @@ -3,17 +3,11 @@ name = "esp32" version = "0.1.0" edition = "2024" -[profile.release] -debug = true -debug-assertions = true -lto = "fat" -codegen-units = 1 - - [dependencies] +[dependencies.device-state] +path = "../device-state" + [target.'cfg(target_arch = "xtensa")'.dependencies] -serde = { version = "1.0.228", default-features = false, features = ["derive", "alloc"] } -serde_json = { version = "1.0", default-features = false, features = ["alloc"] } arrayvec = { version = "0.7.6", default-features = false } ag-lcd = { version = "0.3", features = ["ufmt"] } as5600 = "0.8.0" @@ -36,19 +30,15 @@ embassy-time = { version = "0.5.1", features = [ esp-backtrace = { version = "0.19.0", features = ["esp32", "println"] } static_cell = "2.1.1" log = "0.4" -ufmt = "0.2.0" -owned_str = "0.1.2" [target.'cfg(target_arch = "arm")'.dependencies] embedded-hal-compat = "0.13.0" -ufmt = "0.2.0" log = "0.4" ag-lcd={ version = "0.3", features = ["ufmt"]} as5600 = "0.8.0" embassy-futures = "0.1.2" embedded-io-async = "0.6.1" embedded-io = "0.7.1" -owned_str = "0.1.2" embassy-sync = "0.8.0" embassy-net = { version = "0.9.1",features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"]} embassy-executor = { version = "0.10.0", features = [ diff --git a/esp32/src/input.rs b/esp32/src/input.rs index 8be575d..1cee3e5 100644 --- a/esp32/src/input.rs +++ b/esp32/src/input.rs @@ -7,8 +7,8 @@ use esp_hal::peripherals::{GPIO21, GPIO22, I2C0}; use esp_hal::time::Rate; use esp_println::println; -use crate::WHEEL_VALUE; -use esp32::{apply_wheel_delta, wheel_delta}; +use crate::STATE; +use device_state::{apply_wheel_delta, wheel_delta}; pub static INPUT: Channel = Channel::new(); pub static ANGLE: Mutex = Mutex::new(0); @@ -39,7 +39,8 @@ pub async fn rotation_read_task(config: RotationConfig) { *locked = angle; drop(locked); let diff = wheel_delta(old, angle as i32, crate::WHEEL_INVERTED); - let mut wheel = WHEEL_VALUE.lock().await; + let mut state = STATE.lock().await; + let wheel = state.wheel(); if wheel.max != wheel.min { let min = wheel.min; let max = wheel.max; @@ -47,6 +48,7 @@ pub async fn rotation_read_task(config: RotationConfig) { let mut accumulated = wheel.accumulated; let precision = crate::WHEEL_PRECISION; apply_wheel_delta(&mut value, min, max, &mut accumulated, diff, precision); + let wheel = state.wheel_mut(); wheel.value = value; wheel.accumulated = accumulated; } diff --git a/esp32/src/lib.rs b/esp32/src/lib.rs index 0c83ddc..0c9ac1a 100644 --- a/esp32/src/lib.rs +++ b/esp32/src/lib.rs @@ -1,5 +1 @@ #![no_std] - -mod wheel; - -pub use wheel::{apply_wheel_delta, wheel_delta}; diff --git a/esp32/src/main.rs b/esp32/src/main.rs index 9d057c1..5528b71 100644 --- a/esp32/src/main.rs +++ b/esp32/src/main.rs @@ -3,16 +3,13 @@ extern crate alloc; -use core::str::FromStr; - use embassy_executor::Spawner; use embassy_futures::select::{Either, select}; -use embassy_net::tcp::{State, TcpReader, TcpWriter}; +use embassy_net::tcp::{TcpReader, TcpWriter}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; use embassy_time::Timer; -use embedded_io::Write; use esp_backtrace as _; use esp_hal::{ gpio::{Input, InputConfig as GpioInputConfig, Pin, Pull}, @@ -20,14 +17,11 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::println; -use owned_str::OwnedStr; -use serde::{Deserialize, Serialize}; -use ufmt::uwrite; +use device_state::{DeviceState, ViewState}; mod buffer; mod input; mod net; -mod owned_str_writer; mod screen; pub use input::ANGLE; @@ -42,12 +36,6 @@ const WHEEL_PRECISION: i32 = 32; const WHEEL_INVERTED: bool = false; const DEVICE_ID: &str = "esp32-1"; -#[derive(Deserialize)] -enum QuestionType { - Choice, - Numeric { min: i32, max: i32 }, -} - #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum TcpDisconnect { ReadError, @@ -56,72 +44,11 @@ pub enum TcpDisconnect { Cancelled, } -struct QuestionData { - text: OwnedStr<256>, - q_type: QuestionType, - points: i32, - index: usize, -} - -#[derive(Deserialize)] -struct QuestionDataNet<'a> { - text: &'a str, - q_type: QuestionType, - points: i32, - index: usize, -} - -#[derive(Deserialize)] -enum ProxyOutput<'a> { - Question(QuestionDataNet<'a>), - Results, - Error(&'a str), -} - -impl<'a> From> for QuestionData { - fn from(value: QuestionDataNet<'a>) -> Self { - QuestionData { - text: OwnedStr::from_str(value.text).unwrap(), - q_type: value.q_type, - points: value.points, - index: value.index, - } - } -} - -#[derive(Clone, Copy)] -struct WheelData { - value: i32, - min: i32, - max: i32, - accumulated: i32, -} - -#[derive(Clone, Copy)] -enum MainState { - Loading, - Question, - Results, -} - -static MAIN_STATE: Mutex = Mutex::new(MainState::Loading); -static QUESTION: Mutex> = Mutex::new(None); -static QUESTION_UPDATE: Signal = Signal::new(); -static WHEEL_VALUE: Mutex = Mutex::new(WheelData { - value: 0, - min: 0, - max: 0, - accumulated: 0, -}); +pub static STATE: Mutex = Mutex::new(DeviceState::new()); pub async fn reset_state() { - *QUESTION.lock().await = None; - *WHEEL_VALUE.lock().await = WheelData { - value: 0, - min: 0, - max: 0, - accumulated: 0, - }; + let mut state = STATE.lock().await; + state.reset(); } #[panic_handler] @@ -155,59 +82,23 @@ pub async fn tcp_read_loop( let Ok(str) = core::str::from_utf8(&buf[..len]) else { continue; }; - let mut question_data = None; - let mut future_wheel = WheelData { - value: 0, - min: 0, - max: 0, - accumulated: 0, - }; if let Some(last) = str.lines().last() { - let Ok(data) = serde_json::from_str::(last) else { + let Ok(data) = device_state::parse_proxy_output(last) else { continue; }; - match data { - ProxyOutput::Question(data) => { - let data: QuestionData = data.into(); - match data.q_type { - QuestionType::Numeric { min, max } => { - future_wheel.max = max; - future_wheel.min = min; - future_wheel.value = (min + max) / 2; - } - _ => {} - }; - question_data = Some(data); - } - ProxyOutput::Results => { - *MAIN_STATE.lock().await = MainState::Results; - } - ProxyOutput::Error(e) => {} - } - } - - if let Some(question_data) = question_data { - *QUESTION.lock().await = Some(question_data); - *MAIN_STATE.lock().await = MainState::Question; - *WHEEL_VALUE.lock().await = future_wheel; - QUESTION_UPDATE.signal(()); + let mut state = STATE.lock().await; + state.apply_proxy_output(data); } } } -#[derive(Serialize)] -enum WriteType<'a> { - QuizResponse(i32), - DeviceId(&'a str), -} - pub async fn tcp_write_loop( mut write: TcpWriter<'_>, cancel: &Signal, ) -> Result<(), TcpDisconnect> { if write .write( - serde_json::to_string(&WriteType::DeviceId(DEVICE_ID)) + device_state::serialize_write(&device_state::WriteType::DeviceId(DEVICE_ID)) .unwrap() .as_bytes(), ) @@ -225,19 +116,9 @@ pub async fn tcp_write_loop( Either::Second(()) => return Err(TcpDisconnect::Cancelled), }; println!("button={}", data); - let value = { - let question = QUESTION.lock().await; - let wheel = *WHEEL_VALUE.lock().await; - match question.as_ref() { - Some(q) => match q.q_type { - QuestionType::Numeric { .. } => wheel.value, - QuestionType::Choice => data as _, - }, - _ => data as _, - } - }; - let data = WriteType::QuizResponse(value); - let buffer = serde_json::to_string(&data).unwrap(); + let value = STATE.lock().await.response_value(data); + let data = device_state::WriteType::QuizResponse(value); + let buffer = device_state::serialize_write(&data).unwrap(); println!("write: {}", &buffer); if write.write(buffer.as_bytes()).await.is_err() { cancel.signal(()); @@ -246,72 +127,31 @@ pub async fn tcp_write_loop( } } -const ARROW_RIGHT: char = char::from_u32(0b0111_1110).unwrap(); -const ARROW_LEFT: char = char::from_u32(0b0111_1111).unwrap(); -const DOT: char = char::from_u32(0b1010_0101).unwrap(); - #[embassy_executor::task] pub async fn main_loop() { - let mut last_index = 0; - let mut title_offset = 0; println!("Main loop started"); loop { embassy_time::Timer::after_millis(50).await; - let state = *MAIN_STATE.lock().await; + let mut state = STATE.lock().await; + state.tick(); + let lines = state.render_lines(); + let view = state.view_state(); + drop(state); - match state { - MainState::Loading => { - continue; - } - MainState::Question => {} - MainState::Results => { + match view { + ViewState::Loading => continue, + ViewState::Results => { overwrite_lcd("Results", "").await; continue; } + ViewState::Question => {} } - let wheel = *WHEEL_VALUE.lock().await; - let question = QUESTION.lock().await; - let Some(question) = question.as_ref() else { + let Some((title_line, second_line)) = lines else { continue; }; - title_offset += 1; - if question.index != last_index { - last_index = question.index; - title_offset = 0; - } - let title_line = if question.text.len() > 16 { - title_offset %= question.text.len() - 16; - &question.text[title_offset..title_offset + 16] - } else { - &question.text - }; - let number_str: OwnedStr<16> = match question.q_type { - QuestionType::Choice => { - let mut writer = owned_str_writer::OwnedStrWriter::new(); - writer.push(DOT).unwrap(); - writer.push(' ').unwrap(); - uwrite!(writer, "{}", question.points).unwrap(); - writer.into() - } - QuestionType::Numeric { min, max } => { - let mut writer = owned_str_writer::OwnedStrWriter::new(); - if wheel.value > min { - writer.push(ARROW_LEFT).unwrap(); - writer.push(' ').unwrap(); - } - uwrite!(writer, "{}", wheel.value).unwrap(); - if wheel.value < max { - writer.push(ARROW_RIGHT).unwrap(); - writer.push(' ').unwrap(); - } - - writer.into() - } - }; - let second_line = owned_str_writer::center_str::<16>(&number_str, 16).unwrap(); println!("lcd: {} {}", title_line, second_line); - screen::overwrite_lcd(title_line, &second_line).await; + screen::overwrite_lcd(&title_line, &second_line).await; } } diff --git a/esp32/src/net.rs b/esp32/src/net.rs index 39da53b..f647807 100644 --- a/esp32/src/net.rs +++ b/esp32/src/net.rs @@ -14,7 +14,7 @@ use esp_radio::wifi::sta::StationConfig; use esp_radio::wifi::{Config, ControllerConfig, scan::ScanConfig}; use crate::screen::overwrite_lcd; -use crate::{TcpDisconnect, buffer::wait_for_config, tcp_read_loop, tcp_write_loop}; +use crate::{buffer::wait_for_config, tcp_read_loop, tcp_write_loop}; use crate::{WIFI_NETWORK, WIFI_PASSWORD}; pub struct NetworkConfig<'a> { diff --git a/esp32/src/owned_str_writer.rs b/esp32/src/owned_str_writer.rs deleted file mode 100644 index c740050..0000000 --- a/esp32/src/owned_str_writer.rs +++ /dev/null @@ -1,50 +0,0 @@ -use owned_str::{Error, OwnedStr}; -use ufmt::uWrite; - -pub struct OwnedStrWriter(OwnedStr); - -impl OwnedStrWriter { - pub fn new() -> Self { - Self(OwnedStr::new()) - } - - pub fn push(&mut self, c: char) -> Result<(), Error> { - self.0.try_push(c).map(|_| ()) - } -} - -impl From> for OwnedStrWriter { - fn from(owned_str: OwnedStr) -> Self { - Self(owned_str) - } -} - -impl From> for OwnedStr { - fn from(writer: OwnedStrWriter) -> Self { - writer.0 - } -} - -impl uWrite for OwnedStrWriter { - type Error = owned_str::Error; - fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { - self.0.try_push_str(s).map(|_| ()) - } - fn write_char(&mut self, c: char) -> Result<(), Self::Error> { - self.0.try_push(c).map(|_| ()) - } -} - -pub fn center_str( - str: &str, - width: usize, -) -> Result, owned_str::Error> { - let mut res = OwnedStr::new(); - let len = str.len(); - let padding = (width.saturating_sub(len) + 1) / 2; - for _ in 0..padding { - res.try_push(' ')?; - } - res.try_push_str(str)?; - Ok(res) -} diff --git a/esp32/src/wheel.rs b/esp32/src/wheel.rs deleted file mode 100644 index bc4ac2d..0000000 --- a/esp32/src/wheel.rs +++ /dev/null @@ -1,48 +0,0 @@ -pub const WHEEL_TICKS: i32 = 4096; - -pub fn wheel_delta(old_angle: i32, current_angle: i32, inverted: bool) -> i32 { - let mut diff = current_angle - old_angle; - if diff.abs() > WHEEL_TICKS / 2 { - diff = if diff > 0 { - diff - WHEEL_TICKS - } else { - diff + WHEEL_TICKS - }; - } - - if inverted { - -diff - } else { - diff - } -} - -pub fn apply_wheel_delta( - value: &mut i32, - min: i32, - max: i32, - accumulated: &mut i32, - diff: i32, - precision: i32, -) { - if max == min || precision <= 0 { - return; - } - - *accumulated += diff; - let step_count = *accumulated / precision; - if step_count == 0 { - return; - } - - *accumulated -= step_count * precision; - *value += step_count; - if *value < min { - *value = min; - } else if *value > max { - *value = max; - } - if *value == min || *value == max { - *accumulated = 0; - } -} diff --git a/esp32/test.sh b/esp32/test.sh deleted file mode 100644 index 8b66c77..0000000 --- a/esp32/test.sh +++ /dev/null @@ -1 +0,0 @@ -rustc --test wheel-tests.rs -o wheel-tests && ./wheel-tests diff --git a/esp32/wheel-tests.rs b/esp32/wheel-tests.rs deleted file mode 100644 index 3f416c2..0000000 --- a/esp32/wheel-tests.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![allow(dead_code)] - -mod wheel { - include!("src/wheel.rs"); -} - -use wheel::{apply_wheel_delta, wheel_delta}; - -#[test] -fn wraps_forward_across_zero() { - assert_eq!(wheel_delta(4090, 5, false), 11); -} - -#[test] -fn wraps_backward_across_zero() { - assert_eq!(wheel_delta(5, 4090, false), -11); -} - -#[test] -fn inverts_direction() { - assert_eq!(wheel_delta(10, 20, true), -10); -} - -#[test] -fn accumulates_before_applying_selection() { - let mut value = 5; - let mut accumulated = 0; - - apply_wheel_delta(&mut value, 0, 10, &mut accumulated, 10, 32); - assert_eq!(value, 5); - assert_eq!(accumulated, 10); - - apply_wheel_delta(&mut value, 0, 10, &mut accumulated, 22, 32); - assert_eq!(value, 6); - assert_eq!(accumulated, 0); -} - -#[test] -fn clamps_and_resets_at_bounds() { - let mut value = 10; - let mut accumulated = 0; - - apply_wheel_delta(&mut value, 0, 10, &mut accumulated, 64, 32); - assert_eq!(value, 10); - assert_eq!(accumulated, 0); -} diff --git a/esp32/flake.lock b/flake.lock similarity index 100% rename from esp32/flake.lock rename to flake.lock diff --git a/esp32/flake.nix b/flake.nix similarity index 100% rename from esp32/flake.nix rename to flake.nix