diff --git a/Cargo.lock b/Cargo.lock index 78dab3f..e9a06bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,15 +17,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -41,30 +32,6 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "async-compression" version = "0.4.29" @@ -78,7 +45,7 @@ dependencies = [ "futures-core", "memchr", "pin-project-lite", - "tokio 1.47.1", + "tokio", ] [[package]] @@ -87,26 +54,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.5.0", -] - [[package]] name = "autocfg" version = "1.5.0" @@ -126,12 +73,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cfg-if 1.0.3", + "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -197,23 +144,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "either", - "iovec", -] - [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.2.34" @@ -223,12 +165,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.3" @@ -236,42 +172,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] -name = "chrono" -version = "0.4.41" +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags 1.3.2", -] +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "compression-codecs" @@ -295,9 +199,9 @@ checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -315,72 +219,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "cfg-if 1.0.3", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" -dependencies = [ - "autocfg 1.5.0", - "cfg-if 0.1.10", - "crossbeam-utils", - "lazy_static", - "maybe-uninit", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -dependencies = [ - "cfg-if 0.1.10", - "crossbeam-utils", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -dependencies = [ - "autocfg 1.5.0", - "cfg-if 0.1.10", - "lazy_static", -] - -[[package]] -name = "d" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d098a39024957345cff847d6196b8252500ed26251934f0b26f58e76f1d041" -dependencies = [ - "clap", - "failure", - "futures", - "futures-fs", - "hyper 0.12.36", - "log 0.4.27", - "mime_guess", - "percent-encoding 1.0.1", - "pretty_env_logger", + "cfg-if", ] [[package]] @@ -391,15 +230,9 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - [[package]] name = "embedded-graphics" version = "0.8.1" @@ -424,25 +257,19 @@ dependencies = [ ] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "embedded-hal" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if 1.0.3", -] +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" [[package]] -name = "env_logger" -version = "0.6.2" +name = "embedded-hal-nb" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" dependencies = [ - "atty", - "humantime", - "log 0.4.27", - "regex", - "termcolor", + "embedded-hal", + "nb", ] [[package]] @@ -451,44 +278,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure 0.12.6", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - [[package]] name = "fdeflate" version = "0.3.7" @@ -523,58 +312,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ - "percent-encoding 2.3.2", + "percent-encoding", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags 1.3.2", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - [[package]] name = "futures-channel" version = "0.3.31" @@ -591,27 +337,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -dependencies = [ - "futures", - "num_cpus", -] - -[[package]] -name = "futures-fs" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9f2aeb603383051bab2898cb253a0efed9b590582d0b7baaa0b25de2a536d5" -dependencies = [ - "bytes 0.4.12", - "futures", - "futures-cpupool", -] - [[package]] name = "futures-io" version = "0.3.31" @@ -652,21 +377,11 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if 1.0.3", + "cfg-if", + "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if 1.0.3", - "libc", - "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasi", + "wasm-bindgen", ] [[package]] @@ -676,21 +391,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] -name = "h2" -version = "0.1.26" +name = "gpio-cdev" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" +checksum = "09831ec59b80be69e75d29cf36e16afbbe5fd1af9c1bf4689ad91c77db5aa6a6" dependencies = [ - "byteorder", - "bytes 0.4.12", - "fnv", - "futures", - "http 0.1.21", - "indexmap 1.9.3", - "log 0.4.27", - "slab", - "string", - "tokio-io", + "bitflags 2.9.3", + "libc", + "nix 0.27.1", +] + +[[package]] +name = "gpiocdev" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ae5319702c7dc34334b9f5e2b73d4ffedb0544675b5931af61b3b13a13f928e" +dependencies = [ + "gpiocdev-uapi", + "thiserror", +] + +[[package]] +name = "gpiocdev-embedded-hal" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af983262d31a1acaec17c949a0bb2a7df7fbdd7ab89c9a6bf6f98a38b25bd690" +dependencies = [ + "embedded-hal", + "gpiocdev", + "thiserror", +] + +[[package]] +name = "gpiocdev-uapi" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb160ed63d4235176759e49b7e26aa063603ec4547e458ab1ea98483040028c" +dependencies = [ + "bitflags 2.9.3", + "ioctl-sys", + "libc", + "thiserror", ] [[package]] @@ -700,77 +441,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", - "bytes 1.10.1", + "bytes", "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.11.0", + "http", + "indexmap", "slab", - "tokio 1.47.1", + "tokio", "tokio-util", "tracing", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "http" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" -dependencies = [ - "bytes 0.4.12", - "fnv", - "itoa 0.4.8", -] - [[package]] name = "http" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ - "bytes 1.10.1", + "bytes", "fnv", - "itoa 1.0.15", -] - -[[package]] -name = "http-body" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" -dependencies = [ - "bytes 0.4.12", - "futures", - "http 0.1.21", - "tokio-buf", + "itoa", ] [[package]] @@ -779,8 +476,8 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ - "bytes 1.10.1", - "http 1.3.1", + "bytes", + "http", ] [[package]] @@ -789,10 +486,10 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ - "bytes 1.10.1", + "bytes", "futures-core", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] @@ -802,45 +499,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "hyper" -version = "0.12.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c843caf6296fc1f93444735205af9ed4e109a539005abb2564ae1d6fad34c52" -dependencies = [ - "bytes 0.4.12", - "futures", - "futures-cpupool", - "h2 0.1.26", - "http 0.1.21", - "http-body 0.1.0", - "httparse", - "iovec", - "itoa 0.4.8", - "log 0.4.27", - "net2", - "rustc_version", - "time", - "tokio 0.1.22", - "tokio-buf", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-threadpool", - "tokio-timer", - "want 0.2.0", -] - [[package]] name = "hyper" version = "1.7.0" @@ -848,19 +506,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ "atomic-waker", - "bytes 1.10.1", + "bytes", "futures-channel", "futures-core", - "h2 0.4.12", - "http 1.3.1", - "http-body 1.0.1", + "h2", + "http", + "http-body", "httparse", - "itoa 1.0.15", + "itoa", "pin-project-lite", "pin-utils", - "smallvec 1.15.1", - "tokio 1.47.1", - "want 0.3.1", + "smallvec", + "tokio", + "want", ] [[package]] @@ -869,32 +527,17 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", - "hyper 1.7.0", + "http", + "hyper", "hyper-util", "rustls", + "rustls-native-certs", "rustls-pki-types", - "tokio 1.47.1", + "tokio", "tokio-rustls", "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes 1.10.1", - "http-body-util", - "hyper 1.7.0", - "hyper-util", - "native-tls", - "tokio 1.47.1", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.16" @@ -902,47 +545,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64", - "bytes 1.10.1", + "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "hyper 1.7.0", + "http", + "http-body", + "hyper", "ipnet", "libc", - "percent-encoding 2.3.2", + "percent-encoding", "pin-project-lite", - "socket2", - "system-configuration", - "tokio 1.47.1", + "socket2 0.6.0", + "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] -name = "iana-time-zone" -version = "0.1.63" +name = "i2cdev" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "a01f563561b53e2def844167c92984da13779a53ca409e948a03400633cb8228" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log 0.4.27", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", + "bitflags 2.9.3", + "byteorder", + "libc", + "nix 0.26.4", ] [[package]] @@ -982,7 +611,7 @@ dependencies = [ "icu_normalizer_data", "icu_properties", "icu_provider", - "smallvec 1.15.1", + "smallvec", "zerovec", ] @@ -1038,7 +667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", - "smallvec 1.15.1", + "smallvec", "utf8_iter", ] @@ -1066,16 +695,6 @@ dependencies = [ "zune-jpeg", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg 1.5.0", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.11.0" @@ -1083,7 +702,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", - "hashbrown 0.15.5", + "hashbrown", +] + +[[package]] +name = "io-kit-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" +dependencies = [ + "core-foundation-sys", + "mach2", ] [[package]] @@ -1093,18 +722,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ "bitflags 2.9.3", - "cfg-if 1.0.3", + "cfg-if", "libc", ] [[package]] -name = "iovec" -version = "0.1.4" +name = "ioctl-sys" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] +checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c" [[package]] name = "ipnet" @@ -1122,12 +748,6 @@ dependencies = [ "serde", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.15" @@ -1144,34 +764,21 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "killpaper" version = "0.1.0" dependencies = [ - "d", "embedded-graphics", + "embedded-graphics-core", + "embedded-hal", + "gpiocdev-embedded-hal", "image", + "linux-embedded-hal", "reqwest", "serde", "serde_json", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" version = "0.2.175" @@ -1179,10 +786,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] -name = "linux-raw-sys" -version = "0.9.4" +name = "linux-embedded-hal" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "a8d566b726e1b6810f66c6f1c769889248439c27f06a59df2881a735454cfa02" +dependencies = [ + "cast", + "embedded-hal", + "embedded-hal-nb", + "gpio-cdev", + "i2cdev", + "nb", + "nix 0.27.1", + "serialport", + "spidev", + "sysfs_gpio", +] [[package]] name = "litemap" @@ -1190,24 +809,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -dependencies = [ - "log 0.4.27", -] - [[package]] name = "log" version = "0.4.27" @@ -1215,10 +816,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] -name = "maybe-uninit" -version = "2.0.0" +name = "mach2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] [[package]] name = "memchr" @@ -1228,11 +832,20 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" -version = "0.5.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.5.0", + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", ] [[package]] @@ -1241,33 +854,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" -[[package]] -name = "mime" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" -dependencies = [ - "log 0.3.9", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "1.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216929a5ee4dd316b1702eedf5e74548c123d370f47841ceaac38ca154690ca3" -dependencies = [ - "mime 0.2.6", - "phf", - "phf_codegen", - "unicase", -] - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1278,25 +864,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log 0.4.27", - "miow", - "net2", - "slab", - "winapi 0.2.8", -] - [[package]] name = "mio" version = "1.0.4" @@ -1304,48 +871,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "windows-sys 0.59.0", ] [[package]] -name = "miow" -version = "0.2.2" +name = "nb" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "nix" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", + "memoffset 0.6.5", ] [[package]] -name = "native-tls" -version = "0.2.14" +name = "nix" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ + "bitflags 1.3.2", + "cfg-if", "libc", - "log 0.4.27", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", + "memoffset 0.7.1", + "pin-utils", ] [[package]] -name = "net2" -version = "0.2.39" +name = "nix" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "cfg-if 0.1.10", + "bitflags 2.9.3", + "cfg-if", "libc", - "winapi 0.3.9", ] [[package]] @@ -1354,17 +924,7 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg 1.5.0", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi 0.5.2", - "libc", + "autocfg", ] [[package]] @@ -1382,127 +942,18 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "openssl" -version = "0.10.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" -dependencies = [ - "bitflags 2.9.3", - "cfg-if 1.0.3", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-sys" -version = "0.9.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "parking_lot" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -dependencies = [ - "lock_api", - "parking_lot_core", - "rustc_version", -] - -[[package]] -name = "parking_lot_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" -dependencies = [ - "cfg-if 0.1.10", - "cloudabi", - "libc", - "redox_syscall", - "rustc_version", - "smallvec 0.6.14", - "winapi 0.3.9", -] - -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "phf" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_shared" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -dependencies = [ - "siphasher", - "unicase", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1515,12 +966,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - [[package]] name = "png" version = "0.17.16" @@ -1544,14 +989,12 @@ dependencies = [ ] [[package]] -name = "pretty_env_logger" -version = "0.3.1" +name = "ppv-lite86" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "717ee476b1690853d222af4634056d830b5197ffd747726a9a1eee6da9f49074" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "chrono", - "env_logger", - "log 0.4.27", + "zerocopy", ] [[package]] @@ -1564,10 +1007,56 @@ dependencies = [ ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "quinn" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.5.10", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" +dependencies = [ + "bytes", + "getrandom", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.5.10", + "tracing", + "windows-sys 0.52.0", +] [[package]] name = "quote" @@ -1578,162 +1067,36 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "rand" -version = "0.6.5" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "autocfg 0.1.8", "libc", "rand_chacha", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi 0.3.9", + "rand_core", ] [[package]] name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", -] - -[[package]] -name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "rand_core 0.4.2", + "ppv-lite86", + "rand_core", ] [[package]] name = "rand_core" -version = "0.4.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "rand_core 0.3.1", + "getrandom", ] -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi 0.3.9", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi 0.3.9", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "regex" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" - [[package]] name = "reqwest" version = "0.12.23" @@ -1742,32 +1105,31 @@ checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "async-compression", "base64", - "bytes 1.10.1", - "encoding_rs", + "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.4.12", - "http 1.3.1", - "http-body 1.0.1", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.7.0", + "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", - "log 0.4.27", - "mime 0.3.17", - "native-tls", - "percent-encoding 2.3.2", + "log", + "percent-encoding", "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "tokio 1.47.1", - "tokio-native-tls", + "tokio", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -1785,8 +1147,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if 1.0.3", - "getrandom 0.2.16", + "cfg-if", + "getrandom", "libc", "untrusted", "windows-sys 0.52.0", @@ -1799,26 +1161,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] -name = "rustc_version" -version = "0.2.3" +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" -dependencies = [ - "bitflags 2.9.3", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.60.2", -] +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustls" @@ -1827,18 +1173,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ + "web-time", "zeroize", ] @@ -1882,9 +1242,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.11.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" dependencies = [ "bitflags 2.9.3", "core-foundation", @@ -1903,21 +1263,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "1.0.219" @@ -1935,7 +1280,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] @@ -1944,7 +1289,7 @@ version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ - "itoa 1.0.15", + "itoa", "memchr", "ryu", "serde", @@ -1957,11 +1302,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.15", + "itoa", "ryu", "serde", ] +[[package]] +name = "serialport" +version = "4.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b" +dependencies = [ + "bitflags 2.9.3", + "cfg-if", + "core-foundation", + "core-foundation-sys", + "io-kit-sys", + "mach2", + "nix 0.26.4", + "scopeguard", + "unescaper", + "winapi", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1974,33 +1337,28 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" - [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.0" @@ -2011,44 +1369,29 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "spidev" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2800c129338c77998c1dcb6d4200447e018beb7d29959eb104cba2d45f83c1a" +dependencies = [ + "bitflags 2.9.3", + "libc", + "nix 0.26.4", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "string" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" -dependencies = [ - "bytes 0.4.12", -] - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.106" @@ -2069,18 +1412,6 @@ dependencies = [ "futures-core", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "synstructure" version = "0.13.2" @@ -2089,70 +1420,36 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] -name = "system-configuration" -version = "0.6.1" +name = "sysfs_gpio" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "e8808c55bc926565c62ef7838bcaa8add51585236803e2bdfa1472e3a3ab5e17" dependencies = [ - "bitflags 2.9.3", - "core-foundation", - "system-configuration-sys", + "nix 0.23.2", ] [[package]] -name = "system-configuration-sys" -version = "0.6.0" +name = "thiserror" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "core-foundation-sys", - "libc", + "thiserror-impl", ] [[package]] -name = "tempfile" -version = "3.21.0" +name = "thiserror-impl" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ - "fastrand", - "getrandom 0.3.3", - "once_cell", - "rustix", - "windows-sys 0.60.2", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2166,23 +1463,20 @@ dependencies = [ ] [[package]] -name = "tokio" -version = "0.1.22" +name = "tinyvec" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ - "bytes 0.4.12", - "futures", - "mio 0.6.23", - "num_cpus", - "tokio-current-thread", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-threadpool", - "tokio-timer", + "tinyvec_macros", ] +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.47.1" @@ -2190,87 +1484,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", - "bytes 1.10.1", + "bytes", "io-uring", "libc", - "mio 1.0.4", + "mio", "pin-project-lite", "slab", - "socket2", + "socket2 0.6.0", "windows-sys 0.59.0", ] -[[package]] -name = "tokio-buf" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" -dependencies = [ - "bytes 0.4.12", - "either", - "futures", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" -dependencies = [ - "futures", - "tokio-executor", -] - -[[package]] -name = "tokio-executor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" -dependencies = [ - "crossbeam-utils", - "futures", -] - -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -dependencies = [ - "bytes 0.4.12", - "futures", - "log 0.4.27", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio 1.47.1", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -dependencies = [ - "crossbeam-utils", - "futures", - "lazy_static", - "log 0.4.27", - "mio 0.6.23", - "num_cpus", - "parking_lot", - "slab", - "tokio-executor", - "tokio-io", - "tokio-sync", -] - [[package]] name = "tokio-rustls" version = "0.26.2" @@ -2278,60 +1501,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls", - "tokio 1.47.1", -] - -[[package]] -name = "tokio-sync" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" -dependencies = [ - "fnv", - "futures", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" -dependencies = [ - "bytes 0.4.12", - "futures", - "iovec", - "mio 0.6.23", - "tokio-io", - "tokio-reactor", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" -dependencies = [ - "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils", - "futures", - "lazy_static", - "log 0.4.27", - "num_cpus", - "slab", - "tokio-executor", -] - -[[package]] -name = "tokio-timer" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" -dependencies = [ - "crossbeam-utils", - "futures", - "slab", - "tokio-executor", + "tokio", ] [[package]] @@ -2340,11 +1510,11 @@ version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ - "bytes 1.10.1", + "bytes", "futures-core", "futures-sink", "pin-project-lite", - "tokio 1.47.1", + "tokio", ] [[package]] @@ -2357,7 +1527,7 @@ dependencies = [ "futures-util", "pin-project-lite", "sync_wrapper", - "tokio 1.47.1", + "tokio", "tower-layer", "tower-service", ] @@ -2369,10 +1539,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "bitflags 2.9.3", - "bytes 1.10.1", + "bytes", "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "iri-string", "pin-project-lite", "tower", @@ -2418,12 +1588,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "unicase" -version = "1.4.2" +name = "unescaper" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26" dependencies = [ - "version_check", + "thiserror", ] [[package]] @@ -2432,18 +1602,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "unicode-width" -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 = "untrusted" version = "0.9.0" @@ -2458,7 +1616,7 @@ checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", - "percent-encoding 2.3.2", + "percent-encoding", "serde", ] @@ -2468,35 +1626,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - -[[package]] -name = "want" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" -dependencies = [ - "futures", - "log 0.4.27", - "try-lock", -] - [[package]] name = "want" version = "0.3.1" @@ -2506,34 +1635,19 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.3+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if 1.0.3", + "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -2546,10 +1660,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", - "log 0.4.27", + "log", "proc-macro2", "quote", - "syn 2.0.106", + "syn", "wasm-bindgen-shared", ] @@ -2559,7 +1673,7 @@ version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ - "cfg-if 1.0.3", + "cfg-if", "js-sys", "once_cell", "wasm-bindgen", @@ -2584,7 +1698,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2609,10 +1723,14 @@ dependencies = [ ] [[package]] -name = "winapi" -version = "0.2.8" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "winapi" @@ -2624,110 +1742,25 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" -dependencies = [ - "windows-sys 0.60.2", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-registry" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2736,16 +1769,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.3", + "windows-targets", ] [[package]] @@ -2754,31 +1778,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2787,118 +1794,54 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "wit-bindgen" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" - [[package]] name = "writeable" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "yoke" version = "0.8.0" @@ -2919,8 +1862,28 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", - "synstructure 0.13.2", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2940,8 +1903,8 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", - "synstructure 0.13.2", + "syn", + "synstructure", ] [[package]] @@ -2980,7 +1943,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3ad685e..15c05da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,22 @@ name = "killpaper" version = "0.1.0" edition = "2024" +#rust-version = "1.64" [dependencies] -d = "0.0.1" -embedded-graphics = "0.8.1" +embedded-graphics-core = { version = "0.4", optional = true } +embedded-graphics = "0.8" +embedded-hal = "1.0.0" +gpiocdev-embedded-hal = "0.1.2" image = { version = "0.25.6", default-features = false, features = ["jpeg", "png"] } -reqwest = { version = "0.12", features = ["json", "blocking", "gzip", "brotli"] } +linux-embedded-hal = "0.4.0" +reqwest = { version = "0.12", default-features = false, features = ["http2","rustls-tls-native-roots","json", "blocking", "gzip", "brotli"] } serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.143" + + +[features] +# Remove the linux-dev feature to build the tests on non unix systems +default = ["graphics"] +graphics = [] + diff --git a/src/color.rs b/src/color.rs new file mode 100644 index 0000000..1b1e21a --- /dev/null +++ b/src/color.rs @@ -0,0 +1,561 @@ +//! B/W Color for EPDs +//! +//! EPD representation of multicolor with separate buffers +//! for each bit makes it hard to properly represent colors here + +#[cfg(feature = "graphics")] +use embedded_graphics::pixelcolor::BinaryColor; +#[cfg(feature = "graphics")] +use embedded_graphics::pixelcolor::PixelColor; + +/// When trying to parse u8 to one of the color types +#[derive(Debug, PartialEq, Eq)] +pub struct OutOfColorRangeParseError(u8); +impl core::fmt::Display for OutOfColorRangeParseError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Outside of possible Color Range: {}", self.0) + } +} + +impl OutOfColorRangeParseError { + fn _new(size: u8) -> OutOfColorRangeParseError { + OutOfColorRangeParseError(size) + } +} + +/// Only for the Black/White-Displays +// TODO : 'color' is not a good name for black and white, rename it to BiColor/BWColor ? +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum Color { + /// Black color + Black, + /// White color + #[default] + White, +} + +/// Only for the Black/White/Color-Displays +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum TriColor { + /// Black color + Black, + /// White color + #[default] + White, + /// Chromatic color + Chromatic, +} + +/// For the 7 Color Displays +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum OctColor { + /// Black Color + Black = 0x00, + /// White Color + #[default] + White = 0x01, + /// Green Color + Green = 0x02, + /// Blue Color + Blue = 0x03, + /// Red Color + Red = 0x04, + /// Yellow Color + Yellow = 0x05, + /// Orange Color + Orange = 0x06, + /// HiZ / Clean Color + HiZ = 0x07, +} + +/// Color trait for use in `Display`s +pub trait ColorType { + /// Number of bit used to represent this color type in a single buffer. + /// To get the real number of bits per pixel you should multiply this by `BUFFER_COUNT` + const BITS_PER_PIXEL_PER_BUFFER: usize; + + /// Number of buffer used to represent this color type + /// splitted buffer like tricolo is 2, otherwise this should be 1. + const BUFFER_COUNT: usize; + + /// Return the data used to set a pixel color + /// + /// * bwrbit is used to tell the value of the unused bit when a chromatic + /// color is set (TriColor only as for now) + /// * pos is the pixel position in the line, used to know which pixels must be set + /// + /// Return values are : + /// * .0 is the mask used to exclude this pixel from the byte (eg: 0x7F in BiColor) + /// * .1 are the bits used to set the color in the byte (eg: 0x80 in BiColor) + /// this is u16 because we set 2 bytes in case of split buffer + fn bitmask(&self, bwrbit: bool, pos: u32) -> (u8, u16); +} + +impl ColorType for Color { + const BITS_PER_PIXEL_PER_BUFFER: usize = 1; + const BUFFER_COUNT: usize = 1; + fn bitmask(&self, _bwrbit: bool, pos: u32) -> (u8, u16) { + let bit = 0x80 >> (pos % 8); + match self { + Color::Black => (!bit, 0u16), + Color::White => (!bit, bit as u16), + } + } +} + +impl ColorType for TriColor { + const BITS_PER_PIXEL_PER_BUFFER: usize = 1; + const BUFFER_COUNT: usize = 2; + fn bitmask(&self, bwrbit: bool, pos: u32) -> (u8, u16) { + let bit = 0x80 >> (pos % 8); + match self { + TriColor::Black => (!bit, 0u16), + TriColor::White => (!bit, bit as u16), + TriColor::Chromatic => ( + !bit, + if bwrbit { + (bit as u16) << 8 + } else { + (bit as u16) << 8 | bit as u16 + }, + ), + } + } +} + +impl ColorType for OctColor { + const BITS_PER_PIXEL_PER_BUFFER: usize = 4; + const BUFFER_COUNT: usize = 1; + fn bitmask(&self, _bwrbit: bool, pos: u32) -> (u8, u16) { + let mask = !(0xF0 >> ((pos % 2) * 4)); + let bits = self.get_nibble() as u16; + (mask, if pos % 2 == 1 { bits } else { bits << 4 }) + } +} + +#[cfg(feature = "graphics")] +impl From for OctColor { + fn from(b: BinaryColor) -> OctColor { + match b { + BinaryColor::On => OctColor::Black, + BinaryColor::Off => OctColor::White, + } + } +} + +#[cfg(feature = "graphics")] +impl From for embedded_graphics::pixelcolor::Rgb888 { + fn from(b: OctColor) -> Self { + let (r, g, b) = b.rgb(); + Self::new(r, g, b) + } +} + +#[cfg(feature = "graphics")] +impl From for OctColor { + fn from(p: embedded_graphics::pixelcolor::Rgb888) -> OctColor { + use embedded_graphics::prelude::RgbColor; + let colors = [ + OctColor::Black, + OctColor::White, + OctColor::Green, + OctColor::Blue, + OctColor::Red, + OctColor::Yellow, + OctColor::Orange, + OctColor::HiZ, + ]; + // if the user has already mapped to the right color space, it will just be in the list + if let Some(found) = colors.iter().find(|c| c.rgb() == (p.r(), p.g(), p.b())) { + return *found; + } + + // This is not ideal but just pick the nearest color + *colors + .iter() + .map(|c| (c, c.rgb())) + .map(|(c, (r, g, b))| { + let dist = (i32::from(r) - i32::from(p.r())).pow(2) + + (i32::from(g) - i32::from(p.g())).pow(2) + + (i32::from(b) - i32::from(p.b())).pow(2); + (c, dist) + }) + .min_by_key(|(_c, dist)| *dist) + .map(|(c, _)| c) + .unwrap_or(&OctColor::White) + } +} + +#[cfg(feature = "graphics")] +impl From for OctColor { + fn from(b: embedded_graphics::pixelcolor::raw::RawU4) -> Self { + use embedded_graphics::prelude::RawData; + OctColor::from_nibble(b.into_inner()).unwrap() + } +} + +#[cfg(feature = "graphics")] +impl PixelColor for OctColor { + type Raw = embedded_graphics::pixelcolor::raw::RawU4; +} + +impl OctColor { + /// Gets the Nibble representation of the Color as needed by the display + pub fn get_nibble(self) -> u8 { + self as u8 + } + /// Converts two colors into a single byte for the Display + pub fn colors_byte(a: OctColor, b: OctColor) -> u8 { + a.get_nibble() << 4 | b.get_nibble() + } + + ///Take the nibble (lower 4 bits) and convert to an OctColor if possible + pub fn from_nibble(nibble: u8) -> Result { + match nibble & 0xf { + 0x00 => Ok(OctColor::Black), + 0x01 => Ok(OctColor::White), + 0x02 => Ok(OctColor::Green), + 0x03 => Ok(OctColor::Blue), + 0x04 => Ok(OctColor::Red), + 0x05 => Ok(OctColor::Yellow), + 0x06 => Ok(OctColor::Orange), + 0x07 => Ok(OctColor::HiZ), + e => Err(OutOfColorRangeParseError(e)), + } + } + ///Split the nibbles of a single byte and convert both to an OctColor if possible + pub fn split_byte(byte: u8) -> Result<(OctColor, OctColor), OutOfColorRangeParseError> { + let low = OctColor::from_nibble(byte & 0xf)?; + let high = OctColor::from_nibble((byte >> 4) & 0xf)?; + Ok((high, low)) + } + /// Converts to limited range of RGB values. + pub fn rgb(self) -> (u8, u8, u8) { + match self { + OctColor::White => (0xff, 0xff, 0xff), + OctColor::Black => (0x00, 0x00, 0x00), + OctColor::Green => (0x00, 0xff, 0x00), + OctColor::Blue => (0x00, 0x00, 0xff), + OctColor::Red => (0xff, 0x00, 0x00), + OctColor::Yellow => (0xff, 0xff, 0x00), + OctColor::Orange => (0xff, 0x80, 0x00), + OctColor::HiZ => (0x80, 0x80, 0x80), /* looks greyish */ + } + } +} +//TODO: Rename get_bit_value to bit() and get_byte_value to byte() ? + +impl Color { + /// Get the color encoding of the color for one bit + pub fn get_bit_value(self) -> u8 { + match self { + Color::White => 1u8, + Color::Black => 0u8, + } + } + + /// Gets a full byte of black or white pixels + pub fn get_byte_value(self) -> u8 { + match self { + Color::White => 0xff, + Color::Black => 0x00, + } + } + + /// Parses from u8 to Color + fn from_u8(val: u8) -> Self { + match val { + 0 => Color::Black, + 1 => Color::White, + e => panic!( + "DisplayColor only parses 0 and 1 (Black and White) and not `{}`", + e + ), + } + } + + /// Returns the inverse of the given color. + /// + /// Black returns White and White returns Black + pub fn inverse(self) -> Color { + match self { + Color::White => Color::Black, + Color::Black => Color::White, + } + } +} + +impl From for Color { + fn from(value: u8) -> Self { + Color::from_u8(value) + } +} + +#[cfg(feature = "graphics")] +impl From for Color { + fn from(b: embedded_graphics::pixelcolor::raw::RawU1) -> Self { + use embedded_graphics::prelude::RawData; + if b.into_inner() == 0 { + Color::White + } else { + Color::Black + } + } +} + +#[cfg(feature = "graphics")] +impl From for embedded_graphics::pixelcolor::raw::RawU1 { + fn from(color: Color) -> Self { + Self::new(color.get_bit_value()) + } +} + +#[cfg(feature = "graphics")] +impl PixelColor for Color { + type Raw = embedded_graphics::pixelcolor::raw::RawU1; +} + +#[cfg(feature = "graphics")] +impl From for Color { + fn from(b: BinaryColor) -> Color { + match b { + BinaryColor::On => Color::Black, + BinaryColor::Off => Color::White, + } + } +} + +#[cfg(feature = "graphics")] +impl From for Color { + fn from(rgb: embedded_graphics::pixelcolor::Rgb888) -> Self { + use embedded_graphics::pixelcolor::RgbColor; + if rgb == RgbColor::BLACK { + Color::Black + } else if rgb == RgbColor::WHITE { + Color::White + } else { + // choose closest color + if (rgb.r() as u16 + rgb.g() as u16 + rgb.b() as u16) > 255 * 3 / 2 { + Color::White + } else { + Color::Black + } + } + } +} + +#[cfg(feature = "graphics")] +impl From for embedded_graphics::pixelcolor::Rgb888 { + fn from(color: Color) -> Self { + use embedded_graphics::pixelcolor::RgbColor; + match color { + Color::Black => Self::BLACK, + Color::White => Self::WHITE, + } + } +} + +#[cfg(feature = "graphics")] +impl From for Color { + fn from(rgb: embedded_graphics::pixelcolor::Rgb565) -> Self { + use embedded_graphics::pixelcolor::RgbColor; + if rgb == RgbColor::BLACK { + Color::Black + } else if rgb == RgbColor::WHITE { + Color::White + } else { + // choose closest color + if (rgb.r() as u16 + rgb.g() as u16 + rgb.b() as u16) > 255 * 3 / 2 { + Color::White + } else { + Color::Black + } + } + } +} + +#[cfg(feature = "graphics")] +impl From for embedded_graphics::pixelcolor::Rgb565 { + fn from(color: Color) -> Self { + use embedded_graphics::pixelcolor::RgbColor; + match color { + Color::Black => Self::BLACK, + Color::White => Self::WHITE, + } + } +} + +#[cfg(feature = "graphics")] +impl From for Color { + fn from(rgb: embedded_graphics::pixelcolor::Rgb555) -> Self { + use embedded_graphics::pixelcolor::RgbColor; + if rgb == RgbColor::BLACK { + Color::Black + } else if rgb == RgbColor::WHITE { + Color::White + } else { + // choose closest color + if (rgb.r() as u16 + rgb.g() as u16 + rgb.b() as u16) > 255 * 3 / 2 { + Color::White + } else { + Color::Black + } + } + } +} + +#[cfg(feature = "graphics")] +impl From for embedded_graphics::pixelcolor::Rgb555 { + fn from(color: Color) -> Self { + use embedded_graphics::pixelcolor::RgbColor; + match color { + Color::Black => Self::BLACK, + Color::White => Self::WHITE, + } + } +} + +impl TriColor { + /// Get the color encoding of the color for one bit + pub fn get_bit_value(self) -> u8 { + match self { + TriColor::White => 1u8, + TriColor::Black | TriColor::Chromatic => 0u8, + } + } + + /// Gets a full byte of black or white pixels + pub fn get_byte_value(self) -> u8 { + match self { + TriColor::White => 0xff, + TriColor::Black | TriColor::Chromatic => 0x00, + } + } +} + +#[cfg(feature = "graphics")] +impl From for TriColor { + fn from(b: embedded_graphics::pixelcolor::raw::RawU2) -> Self { + use embedded_graphics::prelude::RawData; + if b.into_inner() == 0b00 { + TriColor::White + } else if b.into_inner() == 0b01 { + TriColor::Black + } else { + TriColor::Chromatic + } + } +} + +#[cfg(feature = "graphics")] +impl PixelColor for TriColor { + type Raw = embedded_graphics::pixelcolor::raw::RawU2; +} + +#[cfg(feature = "graphics")] +impl From for TriColor { + fn from(b: BinaryColor) -> TriColor { + match b { + BinaryColor::On => TriColor::Black, + BinaryColor::Off => TriColor::White, + } + } +} +#[cfg(feature = "graphics")] +impl From for TriColor { + fn from(rgb: embedded_graphics::pixelcolor::Rgb888) -> Self { + use embedded_graphics::pixelcolor::RgbColor; + if rgb == RgbColor::BLACK { + TriColor::Black + } else if rgb == RgbColor::WHITE { + TriColor::White + } else { + // there is no good approximation here since we don't know which color is 'chromatic' + TriColor::Chromatic + } + } +} +#[cfg(feature = "graphics")] +impl From for embedded_graphics::pixelcolor::Rgb888 { + fn from(tri_color: TriColor) -> Self { + use embedded_graphics::pixelcolor::RgbColor; + match tri_color { + TriColor::Black => embedded_graphics::pixelcolor::Rgb888::BLACK, + TriColor::White => embedded_graphics::pixelcolor::Rgb888::WHITE, + // assume chromatic is red + TriColor::Chromatic => embedded_graphics::pixelcolor::Rgb888::new(255, 0, 0), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_u8() { + assert_eq!(Color::Black, Color::from(0u8)); + assert_eq!(Color::White, Color::from(1u8)); + } + + // test all values aside from 0 and 1 which all should panic + #[test] + fn from_u8_panic() { + for val in 2..=u8::MAX { + extern crate std; + let result = std::panic::catch_unwind(|| Color::from(val)); + assert!(result.is_err()); + } + } + + #[test] + fn u8_conversion_black() { + assert_eq!(Color::from(Color::Black.get_bit_value()), Color::Black); + assert_eq!(Color::from(0u8).get_bit_value(), 0u8); + } + + #[test] + fn u8_conversion_white() { + assert_eq!(Color::from(Color::White.get_bit_value()), Color::White); + assert_eq!(Color::from(1u8).get_bit_value(), 1u8); + } + + #[test] + fn test_oct() { + let left = OctColor::Red; + let right = OctColor::Green; + assert_eq!( + OctColor::split_byte(OctColor::colors_byte(left, right)), + Ok((left, right)) + ); + } + + #[test] + fn test_tricolor_bitmask() { + assert_eq!( + TriColor::Black.bitmask(false, 0), + (0b01111111, u16::from_le_bytes([0b00000000, 0b00000000])) + ); + assert_eq!( + TriColor::White.bitmask(false, 0), + (0b01111111, u16::from_le_bytes([0b10000000, 0b00000000])) + ); + assert_eq!( + TriColor::Chromatic.bitmask(false, 0), + (0b01111111, u16::from_le_bytes([0b10000000, 0b10000000])) + ); + + assert_eq!( + TriColor::Black.bitmask(true, 0), + (0b01111111, u16::from_le_bytes([0b00000000, 0b00000000])) + ); + assert_eq!( + TriColor::White.bitmask(true, 0), + (0b01111111, u16::from_le_bytes([0b10000000, 0b00000000])) + ); + assert_eq!( + TriColor::Chromatic.bitmask(true, 0), + (0b01111111, u16::from_le_bytes([0b00000000, 0b10000000])) + ); + } +} diff --git a/src/display.rs b/src/display.rs index 9e0fd54..5046562 100644 --- a/src/display.rs +++ b/src/display.rs @@ -2,8 +2,7 @@ use embedded_graphics::{ framebuffer::Framebuffer, mono_font::{MonoTextStyle, ascii::FONT_8X13}, pixelcolor::{ - Gray8, - raw::{LittleEndian, RawU8}, + raw::{BigEndian}, }, prelude::*, primitives::{PrimitiveStyle, Rectangle, StyledDrawable}, @@ -13,8 +12,8 @@ use embedded_graphics::{ use image::RgbImage; use image::{GrayImage}; - - +use embedded_graphics::pixelcolor::BinaryColor; + use embedded_graphics::pixelcolor::raw::RawU1; use crate::killinfo::KillInfo; const WIDTH: usize = 800; @@ -23,7 +22,7 @@ const HEIGHT: usize = 480; pub struct Display { width: usize, height: usize, - buffer: Framebuffer, + buffer: Framebuffer, } impl Display { @@ -32,44 +31,54 @@ impl Display { width : WIDTH, height : HEIGHT, buffer: - Framebuffer::::new(), + Framebuffer::::new(), } } + pub fn clear(&mut self, on: bool) { + let color = if on { + BinaryColor::On + } else { + BinaryColor::Off + }; + + self.buffer.clear(color).ok(); +} pub fn draw_rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: u8) { let rect = Rectangle::new(Point::new(x, y), Size::new(w, h)); - let style = PrimitiveStyle::with_fill(Gray8::new(color)); + let bw = if color > 127 { BinaryColor::On } else { BinaryColor::Off }; + let style = PrimitiveStyle::with_fill(bw); rect.draw_styled(&style, &mut self.buffer).ok(); } pub fn draw_text(&mut self, x: i32, y: i32, text: &str, color: u8) { - let style = MonoTextStyle::new(&FONT_8X13, Gray8::new(color)); + let bw = if color > 127 { BinaryColor::On } else { BinaryColor::Off }; + let style = MonoTextStyle::new(&FONT_8X13, bw); Text::new(text, Point::new(x, y), style) .draw(&mut self.buffer) .ok(); } - pub fn draw_kill_info(&mut self, kill: &KillInfo, x: i32, y: i32) { let mut current_y = y; // Draw Alliance logo (64x64) if present if let Some(alliance) = &kill.victim.alliance { - let gray_vec = rgb_to_gray_vec(&alliance.logo); - let logo_raw = ImageRaw::::new(&gray_vec, alliance.logo.width() as u32); + let bw_vec = rgb_to_bw_dithered(&alliance.logo); + let logo_raw = ImageRaw::::new(&bw_vec, alliance.logo.width() as u32); Image::new(&logo_raw, Point::new(x, y)).draw(&mut self.buffer).ok(); } // Draw Ship icon (64x64) if present if let Some(ship) = &kill.victim.ship { - let gray_vec = rgb_to_gray_vec(&ship.icon); - let logo_raw = ImageRaw::::new(&gray_vec, ship.icon.width() as u32); + let bw_vec = rgb_to_bw_dithered(&ship.icon); + let logo_raw = ImageRaw::::new(&bw_vec, ship.icon.width() as u32); Image::new(&logo_raw, Point::new(x + 64 + 4, current_y)).draw(&mut self.buffer).ok(); } let text_x = x + 64 + 64 + 8; - let style = MonoTextStyle::new(&FONT_8X13, Gray8::new(255)); + let style = MonoTextStyle::new(&FONT_8X13, BinaryColor::Off ); current_y += 8; // Character name + Alliance short @@ -79,7 +88,16 @@ impl Display { .as_ref() .map(|a| a.short.as_str()) .unwrap_or(""); - let char_line = format!("{} [{}]", kill.victim.character.name, alliance_short); + + + let char_name = kill + .victim + .character + .as_ref() + .map(|a| a.name.as_str()) + .unwrap_or(""); + + let char_line = format!("{} [{}]", char_name, alliance_short); Text::new(&char_line, Point::new(text_x, current_y), style).draw(&mut self.buffer).ok(); current_y += 16; @@ -94,23 +112,130 @@ impl Display { } - pub fn flush(&self) { + pub fn draw_loss_info(&mut self, kill: &KillInfo, x: i32, y: i32) { + let mut current_y = y; + + // Draw Alliance logo (64x64) if present + if let Some(character) = &kill.victim.character { + let bw_vec = rgb_to_bw_dithered(&character.portrait); + let logo_raw = ImageRaw::::new(&bw_vec, character.portrait.width() as u32); + Image::new(&logo_raw, Point::new(x, y)).draw(&mut self.buffer).ok(); + } + + // Draw Ship icon (64x64) if present + if let Some(ship) = &kill.victim.ship { + + let bw_vec = rgb_to_bw_dithered(&ship.icon); + let logo_raw = ImageRaw::::new(&bw_vec, ship.icon.width() as u32); + Image::new(&logo_raw, Point::new(x + 64 + 4, current_y)).draw(&mut self.buffer).ok(); + } + + let text_x = x + 64 + 64 + 8; + let style = MonoTextStyle::new(&FONT_8X13, BinaryColor::Off ); + current_y += 8; + + // Character name + Alliance short + let alliance_short = kill + .victim + .alliance + .as_ref() + .map(|a| a.short.as_str()) + .unwrap_or(""); + + + let char_name = kill + .victim + .character + .as_ref() + .map(|a| a.name.as_str()) + .unwrap_or(""); + + let char_line = format!("{} [{}]", char_name, alliance_short); + Text::new(&char_line, Point::new(text_x, current_y), style).draw(&mut self.buffer).ok(); + current_y += 16; + + // Ship name + total value + let ship_name = kill.victim.ship.as_ref().map(|s| s.name.as_str()).unwrap_or("Unknown"); + let value_line = format!("{} - {:.2}M ISK", ship_name, kill.total_value / 1_000_000f64); + Text::new(&value_line, Point::new(text_x, current_y), style).draw(&mut self.buffer).ok(); + current_y += 16; + + // System name + Text::new(&kill.system_name, Point::new(text_x, current_y), style).draw(&mut self.buffer).ok(); + } + + + pub fn flush(&self) { let buf = self.buffer.data(); - let img = GrayImage::from_raw(self.width as u32, self.height as u32, buf.iter().map(|p| *p).collect()).unwrap(); + let img_data: Vec = buf + .iter() + .flat_map(|byte| (0..8).map(move |i| if (byte >> (7-i)) & 1 == 1 { 255 } else { 0 })) + .take(self.width * self.height) + .collect(); + + let img = GrayImage::from_raw(self.width as u32, self.height as u32, img_data).unwrap(); img.save("output.png").unwrap(); println!("Saved framebuffer to {:?}", "output.png"); } } +fn rgb_to_bw_dithered(rgb: &RgbImage) -> Vec { + let w = rgb.width() as usize; + let h = rgb.height() as usize; - -fn rgb_to_gray_vec(rgb: &RgbImage) -> Vec { - rgb.pixels() + // Convert to grayscale f32 buffer + let mut gray: Vec = rgb + .pixels() .map(|p| { let [r, g, b] = p.0; - (0.299 * r as f32 + 0.587 * g as f32 + 0.114 * b as f32) as u8 + 0.299 * r as f32 + 0.587 * g as f32 + 0.114 * b as f32 }) - .collect() + .collect(); + + let mut bits = Vec::new(); + let mut byte = 0u8; + let mut bit_idx = 7; + + for y in 0..h { + for x in 0..w { + let idx = y * w + x; + let old = gray[idx]; + let new = if old >= 128.0 { 255.0 } else { 0.0 }; + let error = old - new; + + // Quantize to B/W + let bw = new > 127.0; + if bw { + byte |= 1 << bit_idx; + } + bit_idx -= 1; + if bit_idx == -1 { + bits.push(byte); + byte = 0; + bit_idx = 7; + } + + // Distribute error (Floyd–Steinberg) + if x + 1 < w { + gray[idx + 1] += error * 7.0 / 16.0; + } + if y + 1 < h { + if x > 0 { + gray[idx + w - 1] += error * 3.0 / 16.0; + } + gray[idx + w] += error * 5.0 / 16.0; + if x + 1 < w { + gray[idx + w + 1] += error * 1.0 / 16.0; + } + } + } + } + + if bit_idx > 0 { + bits.push(byte); + } + + bits } \ No newline at end of file diff --git a/src/epaper.rs b/src/epaper.rs new file mode 100644 index 0000000..442044b --- /dev/null +++ b/src/epaper.rs @@ -0,0 +1,274 @@ +use embedded_graphics::{ + image::{Image, ImageRaw}, mono_font::{MonoTextStyleBuilder},prelude::*, text::{Baseline, Text, TextStyleBuilder} +}; + +use crate::epd7in5_v2::Epd7in5; +use crate::epd7in5_v2::Display7in5; +use crate::graphics::Display; +use linux_embedded_hal::{ + spidev::{self, SpidevOptions}, Delay, SPIError, SpidevDevice +}; + +use crate::traits::{*}; +use crate::color::Color; +use embedded_hal::digital::OutputPin; +use embedded_hal::digital::PinState; + + +use crate::killinfo::KillInfo; +use image::RgbImage; + + +pub struct EPaper { + spi : SpidevDevice, + epd: Epd7in5, + display: Display<800, 480, false, 48000 , Color>, + delay :Delay, +} + + + +impl EPaper { + pub fn new() -> Result { + + let mut spi = SpidevDevice::open("/dev/spidev0.0").expect("spidev directory"); + let options = SpidevOptions::new() + .bits_per_word(8) + .max_speed_hz(10_000_000) + .mode(spidev::SpiModeFlags::SPI_MODE_0) + .build(); + spi.configure(&options).expect("spi configuration"); + + // setup display pins + let mut cs = gpiocdev_embedded_hal::OutputPin::new("/dev/gpiochip0", 26, PinState::High).unwrap(); + cs.set_high().unwrap(); + let busy = gpiocdev_embedded_hal::InputPin::new("/dev/gpiochip0", 24).unwrap(); + let dc = gpiocdev_embedded_hal::OutputPin::new("/dev/gpiochip0", 25, PinState::High).unwrap(); + let rst = gpiocdev_embedded_hal::OutputPin::new("/dev/gpiochip0", 17, PinState::High).unwrap(); + + let mut delay = Delay {}; + println!("START"); + let epd7in5 = Epd7in5::new(&mut spi, busy, dc, rst, &mut delay, None).expect("epd new"); + let display = Display7in5::default(); + println!("Device successfully initialized!"); + + return Ok(EPaper { spi, epd: epd7in5, display, delay : Delay {}}); + + } + + pub fn flush(&mut self) -> Result<(), SPIError> { + self.epd.update_and_display_frame(&mut self.spi, self.display.buffer(), &mut self.delay)?; + Ok(()) + } + + + pub fn sleep(&mut self) -> Result<(), SPIError> { + self.epd.sleep(&mut self.spi, &mut self.delay)?; + Ok(()) + + } + + pub fn clear(&mut self, on: bool) { + let color = if on { + Color::Black + } else { + Color::White + }; + + self.display.clear(color).ok(); +} + + pub fn draw_text(&mut self, x: i32, y: i32, text: &str) { + let style = MonoTextStyleBuilder::new() + .font(&embedded_graphics::mono_font::ascii::FONT_8X13) + .text_color(Color::White) + .background_color(Color::Black) + .build(); + + let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build(); + + let _ = Text::with_text_style(text, Point::new(x, y), style, text_style).draw(&mut self.display); + + } + + + + pub fn draw_kill_info(&mut self, kill: &KillInfo, x: i32, y: i32) { + let mut current_y = y; + + // Draw Alliance logo (64x64) if present + if let Some(alliance) = &kill.victim.alliance { + let bw_vec = rgb_to_bw_dithered(&alliance.logo); + let logo_raw = ImageRaw::::new(bw_vec.as_slice(), alliance.logo.width() as u32); + Image::new(&logo_raw, Point::new(x, y)).draw(&mut self.display).ok(); + } + + // Draw Ship icon (64x64) if present + if let Some(ship) = &kill.victim.ship { + + let bw_vec = rgb_to_bw_dithered(&ship.icon); + let width = ship.icon.width() as u32; + let raw_image: ImageRaw<'_, Color> = ImageRaw::::new(&bw_vec, width); + Image::new(&raw_image, Point::new(x + 64 + 4, current_y)).draw(&mut self.display).ok(); + } + + let text_x = x + 64 + 64 + 8; + let style = MonoTextStyleBuilder::new() + .font(&embedded_graphics::mono_font::ascii::FONT_8X13) + .text_color(Color::White) + .background_color(Color::Black) + .build(); + + current_y += 8; + + // Character name + Alliance short + let alliance_short = kill + .victim + .alliance + .as_ref() + .map(|a| a.short.as_str()) + .unwrap_or(""); + + + let char_name = kill + .victim + .character + .as_ref() + .map(|a| a.name.as_str()) + .unwrap_or(""); + + let char_line = format!("{} [{}]", char_name, alliance_short); + Text::new(&char_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok(); + current_y += 16; + + // Ship name + total value + let ship_name = kill.victim.ship.as_ref().map(|s| s.name.as_str()).unwrap_or("Unknown"); + let value_line = format!("{} - {:.2}M ISK", ship_name, kill.total_value / 1_000_000f64); + Text::new(&value_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok(); + current_y += 16; + + // System name + Text::new(&kill.system_name, Point::new(text_x, current_y), style).draw(&mut self.display).ok(); + } + + + + pub fn draw_loss_info(&mut self, kill: &KillInfo, x: i32, y: i32) { + let mut current_y = y; + + // Draw Alliance logo (64x64) if present + if let Some(character) = &kill.victim.character { + let bw_vec = rgb_to_bw_dithered(&character.portrait); + let logo_raw = ImageRaw::::new(&bw_vec, character.portrait.width() as u32); + Image::new(&logo_raw, Point::new(x, y)).draw(&mut self.display).ok(); + } + + // Draw Ship icon (64x64) if present + if let Some(ship) = &kill.victim.ship { + + let bw_vec = rgb_to_bw_dithered(&ship.icon); + let logo_raw = ImageRaw::::new(&bw_vec, ship.icon.width() as u32); + Image::new(&logo_raw, Point::new(x + 64 + 4, current_y)).draw(&mut self.display).ok(); + } + + let text_x = x + 64 + 64 + 8; + let style = MonoTextStyleBuilder::new() + .font(&embedded_graphics::mono_font::ascii::FONT_8X13) + .text_color(Color::Black) + .background_color(Color::White) + .build(); + current_y += 8; + + // Character name + Alliance short + let alliance_short = kill + .victim + .alliance + .as_ref() + .map(|a| a.short.as_str()) + .unwrap_or(""); + + + let char_name = kill + .victim + .character + .as_ref() + .map(|a| a.name.as_str()) + .unwrap_or(""); + + let char_line = format!("{} [{}]", char_name, alliance_short); + Text::new(&char_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok(); + current_y += 16; + + // Ship name + total value + let ship_name = kill.victim.ship.as_ref().map(|s| s.name.as_str()).unwrap_or("Unknown"); + let value_line = format!("{} - {:.2}M ISK", ship_name, kill.total_value / 1_000_000f64); + Text::new(&value_line, Point::new(text_x, current_y), style).draw(&mut self.display).ok(); + current_y += 16; + + // System name + Text::new(&kill.system_name, Point::new(text_x, current_y), style).draw(&mut self.display).ok(); + } + +} + + + + +fn rgb_to_bw_dithered(rgb: &RgbImage) -> Vec { + let w = rgb.width() as usize; + let h = rgb.height() as usize; + + // Convert to grayscale f32 buffer + let mut gray: Vec = rgb + .pixels() + .map(|p| { + let [r, g, b] = p.0; + 0.299 * r as f32 + 0.587 * g as f32 + 0.114 * b as f32 + }) + .collect(); + + let mut bits = Vec::new(); + let mut byte = 0u8; + let mut bit_idx = 7; + + for y in 0..h { + for x in 0..w { + let idx = y * w + x; + let old = gray[idx]; + let new = if old >= 128.0 { 255.0 } else { 0.0 }; + let error = old - new; + + // Quantize to B/W + let bw = new > 127.0; + if bw { + byte |= 1 << bit_idx; + } + bit_idx -= 1; + if bit_idx == -1 { + bits.push(byte); + byte = 0; + bit_idx = 7; + } + + // Distribute error (Floyd–Steinberg) + if x + 1 < w { + gray[idx + 1] += error * 7.0 / 16.0; + } + if y + 1 < h { + if x > 0 { + gray[idx + w - 1] += error * 3.0 / 16.0; + } + gray[idx + w] += error * 5.0 / 16.0; + if x + 1 < w { + gray[idx + w + 1] += error * 1.0 / 16.0; + } + } + } + } + + if bit_idx > 0 { + bits.push(byte); + } + + bits +} \ No newline at end of file diff --git a/src/epd7in5_v2/command.rs b/src/epd7in5_v2/command.rs new file mode 100644 index 0000000..95292d3 --- /dev/null +++ b/src/epd7in5_v2/command.rs @@ -0,0 +1,153 @@ +//! SPI Commands for the Waveshare 7.5" E-Ink Display + +use crate::traits; + +/// Epd7in5 commands +/// +/// Should rarely (never?) be needed directly. +/// +/// For more infos about the addresses and what they are doing look into the PDFs. +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub(crate) enum Command { + /// Set Resolution, LUT selection, BWR pixels, gate scan direction, source shift + /// direction, booster switch, soft reset. + PanelSetting = 0x00, + + /// Selecting internal and external power + PowerSetting = 0x01, + + /// After the Power Off command, the driver will power off following the Power Off + /// Sequence; BUSY signal will become "0". This command will turn off charge pump, + /// T-con, source driver, gate driver, VCOM, and temperature sensor, but register + /// data will be kept until VDD becomes OFF. Source Driver output and Vcom will remain + /// as previous condition, which may have 2 conditions: 0V or floating. + PowerOff = 0x02, + + /// Setting Power OFF sequence + PowerOffSequenceSetting = 0x03, + + /// Turning On the Power + /// + /// After the Power ON command, the driver will power on following the Power ON + /// sequence. Once complete, the BUSY signal will become "1". + PowerOn = 0x04, + + /// Starting data transmission + BoosterSoftStart = 0x06, + + /// This command makes the chip enter the deep-sleep mode to save power. + /// + /// The deep sleep mode would return to stand-by by hardware reset. + /// + /// The only one parameter is a check code, the command would be excuted if check code = 0xA5. + DeepSleep = 0x07, + + /// This command starts transmitting data and write them into SRAM. To complete data + /// transmission, command DSP (Data Stop) must be issued. Then the chip will start to + /// send data/VCOM for panel. + /// + /// BLACK/WHITE or OLD_DATA + DataStartTransmission1 = 0x10, + + /// To stop data transmission, this command must be issued to check the `data_flag`. + /// + /// After this command, BUSY signal will become "0" until the display update is + /// finished. + DataStop = 0x11, + + /// After this command is issued, driver will refresh display (data/VCOM) according to + /// SRAM data and LUT. + /// + /// After Display Refresh command, BUSY signal will become "0" until the display + /// update is finished. + DisplayRefresh = 0x12, + + /// RED or NEW_DATA + DataStartTransmission2 = 0x13, + + /// Dual SPI - what for? + DualSpi = 0x15, + + /// This command builds the VCOM Look-Up Table (LUTC). + LutForVcom = 0x20, + /// This command builds the Black Look-Up Table (LUTB). + LutBlack = 0x21, + /// This command builds the White Look-Up Table (LUTW). + LutWhite = 0x22, + /// This command builds the Gray1 Look-Up Table (LUTG1). + LutGray1 = 0x23, + /// This command builds the Gray2 Look-Up Table (LUTG2). + LutGray2 = 0x24, + /// This command builds the Red0 Look-Up Table (LUTR0). + LutRed0 = 0x25, + /// This command builds the Red1 Look-Up Table (LUTR1). + LutRed1 = 0x26, + /// This command builds the Red2 Look-Up Table (LUTR2). + LutRed2 = 0x27, + /// This command builds the Red3 Look-Up Table (LUTR3). + LutRed3 = 0x28, + /// This command builds the XON Look-Up Table (LUTXON). + LutXon = 0x29, + + /// The command controls the PLL clock frequency. + PllControl = 0x30, + + /// This command reads the temperature sensed by the temperature sensor. + TemperatureSensor = 0x40, + /// This command selects the Internal or External temperature sensor. + TemperatureCalibration = 0x41, + /// This command could write data to the external temperature sensor. + TemperatureSensorWrite = 0x42, + /// This command could read data from the external temperature sensor. + TemperatureSensorRead = 0x43, + + /// This command indicates the interval of Vcom and data output. When setting the + /// vertical back porch, the total blanking will be kept (20 Hsync). + VcomAndDataIntervalSetting = 0x50, + /// This command indicates the input power condition. Host can read this flag to learn + /// the battery condition. + LowPowerDetection = 0x51, + + /// This command defines non-overlap period of Gate and Source. + TconSetting = 0x60, + /// This command defines alternative resolution and this setting is of higher priority + /// than the RES\[1:0\] in R00H (PSR). + TconResolution = 0x61, + /// This command defines MCU host direct access external memory mode. + SpiFlashControl = 0x65, + + /// The LUT_REV / Chip Revision is read from OTP address = 25001 and 25000. + Revision = 0x70, + /// This command reads the IC status. + GetStatus = 0x71, + + /// This command implements related VCOM sensing setting. + AutoMeasurementVcom = 0x80, + /// This command gets the VCOM value. + ReadVcomValue = 0x81, + /// This command sets `VCOM_DC` value. + VcmDcSetting = 0x82, + // /// This is in all the Waveshare controllers for Epd7in5, but it's not documented + // /// anywhere in the datasheet `¯\_(ツ)_/¯` + // FlashMode = 0xE5, +} + +impl traits::Command for Command { + /// Returns the address of the command + fn address(self) -> u8 { + self as u8 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::Command as CommandTrait; + + #[test] + fn command_addr() { + assert_eq!(Command::PanelSetting.address(), 0x00); + assert_eq!(Command::DisplayRefresh.address(), 0x12); + } +} diff --git a/src/epd7in5_v2/mod.rs b/src/epd7in5_v2/mod.rs new file mode 100644 index 0000000..8d0d45f --- /dev/null +++ b/src/epd7in5_v2/mod.rs @@ -0,0 +1,260 @@ +//! A simple Driver for the Waveshare 7.5" E-Ink Display (V2) via SPI +//! +//! # References +//! +//! - [Datasheet](https://www.waveshare.com/wiki/7.5inch_e-Paper_HAT) +//! - [Waveshare C driver](https://github.com/waveshare/e-Paper/blob/702def0/RaspberryPi%26JetsonNano/c/lib/e-Paper/EPD_7in5_V2.c) +//! - [Waveshare Python driver](https://github.com/waveshare/e-Paper/blob/702def0/RaspberryPi%26JetsonNano/python/lib/waveshare_epd/epd7in5_V2.py) +//! +//! Important note for V2: +//! Revision V2 has been released on 2019.11, the resolution is upgraded to 800×480, from 640×384 of V1. +//! The hardware and interface of V2 are compatible with V1, however, the related software should be updated. + +use embedded_hal::{ + delay::DelayNs, + digital::{InputPin, OutputPin}, + spi::SpiDevice, +}; + +use crate::color::Color; +use crate::interface::DisplayInterface; +use crate::traits::{InternalWiAdditions, RefreshLut, WaveshareDisplay}; + +pub(crate) mod command; +use self::command::Command; +use crate::buffer_len; + +/// Full size buffer for use with the 7in5 v2 EPD +#[cfg(feature = "graphics")] +pub type Display7in5 = crate::graphics::Display< + WIDTH, + HEIGHT, + false, + { buffer_len(WIDTH as usize, HEIGHT as usize) }, + Color, +>; + +/// Width of the display +pub const WIDTH: u32 = 800; +/// Height of the display +pub const HEIGHT: u32 = 480; +/// Default Background Color +pub const DEFAULT_BACKGROUND_COLOR: Color = Color::White; +const IS_BUSY_LOW: bool = true; +const SINGLE_BYTE_WRITE: bool = false; + +/// Epd7in5 (V2) driver +/// +pub struct Epd7in5 { + /// Connection Interface + interface: DisplayInterface, + /// Background Color + color: Color, +} + +impl InternalWiAdditions + for Epd7in5 +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + // Reset the device + self.interface.reset(delay, 10_000, 2_000); + + // V2 procedure as described here: + // https://github.com/waveshare/e-Paper/blob/master/RaspberryPi%26JetsonNano/python/lib/waveshare_epd/epd7in5bc_V2.py + // and as per specs: + // https://www.waveshare.com/w/upload/6/60/7.5inch_e-Paper_V2_Specification.pdf + + self.cmd_with_data(spi, Command::PowerSetting, &[0x07, 0x07, 0x3f, 0x3f])?; + self.cmd_with_data(spi, Command::BoosterSoftStart, &[0x17, 0x17, 0x28, 0x17])?; + self.command(spi, Command::PowerOn)?; + delay.delay_ms(100); + self.wait_until_idle(spi, delay)?; + self.cmd_with_data(spi, Command::PanelSetting, &[0x1F])?; + self.cmd_with_data(spi, Command::TconResolution, &[0x03, 0x20, 0x01, 0xE0])?; + self.cmd_with_data(spi, Command::DualSpi, &[0x00])?; + self.cmd_with_data(spi, Command::VcomAndDataIntervalSetting, &[0x10, 0x07])?; + self.cmd_with_data(spi, Command::TconSetting, &[0x22])?; + Ok(()) + } +} + +impl WaveshareDisplay + for Epd7in5 +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + type DisplayColor = Color; + fn new( + spi: &mut SPI, + busy: BUSY, + dc: DC, + rst: RST, + delay: &mut DELAY, + delay_us: Option, + ) -> Result { + let interface = DisplayInterface::new(busy, dc, rst, delay_us); + let color = DEFAULT_BACKGROUND_COLOR; + + let mut epd = Epd7in5 { interface, color }; + + epd.init(spi, delay)?; + + Ok(epd) + } + + fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.init(spi, delay) + } + + fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.command(spi, Command::PowerOff)?; + self.wait_until_idle(spi, delay)?; + self.cmd_with_data(spi, Command::DeepSleep, &[0xA5])?; + Ok(()) + } + + fn update_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.cmd_with_data(spi, Command::DataStartTransmission2, buffer)?; + Ok(()) + } + + fn update_partial_frame( + &mut self, + _spi: &mut SPI, + _delay: &mut DELAY, + _buffer: &[u8], + _x: u32, + _y: u32, + _width: u32, + _height: u32, + ) -> Result<(), SPI::Error> { + unimplemented!(); + } + + fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.command(spi, Command::DisplayRefresh)?; + Ok(()) + } + + fn update_and_display_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error> { + self.update_frame(spi, buffer, delay)?; + self.command(spi, Command::DisplayRefresh)?; + Ok(()) + } + + fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.wait_until_idle(spi, delay)?; + self.send_resolution(spi)?; + + self.command(spi, Command::DataStartTransmission1)?; + self.interface.data_x_times(spi, 0x00, WIDTH / 8 * HEIGHT)?; + + self.command(spi, Command::DataStartTransmission2)?; + self.interface.data_x_times(spi, 0x00, WIDTH / 8 * HEIGHT)?; + + self.command(spi, Command::DisplayRefresh)?; + Ok(()) + } + + fn set_background_color(&mut self, color: Color) { + self.color = color; + } + + fn background_color(&self) -> &Color { + &self.color + } + + fn width(&self) -> u32 { + WIDTH + } + + fn height(&self) -> u32 { + HEIGHT + } + + fn set_lut( + &mut self, + _spi: &mut SPI, + _delay: &mut DELAY, + _refresh_rate: Option, + ) -> Result<(), SPI::Error> { + unimplemented!(); + } + + fn wait_until_idle(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error> { + self.interface + .wait_until_idle_with_cmd(spi, delay, IS_BUSY_LOW, Command::GetStatus) + } +} + +impl Epd7in5 +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + fn command(&mut self, spi: &mut SPI, command: Command) -> Result<(), SPI::Error> { + self.interface.cmd(spi, command) + } + + fn send_data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> { + self.interface.data(spi, data) + } + + fn cmd_with_data( + &mut self, + spi: &mut SPI, + command: Command, + data: &[u8], + ) -> Result<(), SPI::Error> { + self.interface.cmd_with_data(spi, command, data) + } + + fn send_resolution(&mut self, spi: &mut SPI) -> Result<(), SPI::Error> { + let w = self.width(); + let h = self.height(); + + self.command(spi, Command::TconResolution)?; + self.send_data(spi, &[(w >> 8) as u8])?; + self.send_data(spi, &[w as u8])?; + self.send_data(spi, &[(h >> 8) as u8])?; + self.send_data(spi, &[h as u8]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn epd_size() { + assert_eq!(WIDTH, 800); + assert_eq!(HEIGHT, 480); + assert_eq!(DEFAULT_BACKGROUND_COLOR, Color::White); + } +} diff --git a/src/graphics.rs b/src/graphics.rs new file mode 100644 index 0000000..cb3c196 --- /dev/null +++ b/src/graphics.rs @@ -0,0 +1,487 @@ +//! Graphics Support for EPDs + +use crate::color::{ColorType, TriColor}; +use core::marker::PhantomData; +use embedded_graphics::prelude::*; + +/// Display rotation, only 90° increments supported +#[derive(Clone, Copy, Default)] +pub enum DisplayRotation { + /// No rotation + #[default] + Rotate0, + /// Rotate by 90 degrees clockwise + Rotate90, + /// Rotate by 180 degrees clockwise + Rotate180, + /// Rotate 270 degrees clockwise + Rotate270, +} + +/// count the number of bytes per line knowing that it may contains padding bits +const fn line_bytes(width: u32, bits_per_pixel: usize) -> usize { + // round to upper 8 bit count + (width as usize * bits_per_pixel + 7) / 8 +} + +/// Display buffer used for drawing with embedded graphics +/// This can be rendered on EPD using ... +/// +/// - WIDTH: width in pixel when display is not rotated +/// - HEIGHT: height in pixel when display is not rotated +/// - BWRBIT: mandatory value of the B/W when chromatic bit is set, can be any value for non +/// tricolor epd +/// - COLOR: color type used by the target display +/// - BYTECOUNT: This is redundant with previous data and should be removed when const generic +/// expressions are stabilized +/// +/// More on BWRBIT: +/// +/// Different chromatic displays differently treat the bits in chromatic color planes. +/// Some of them ([crate::epd2in13bc]) will render a color pixel if bit is set for that pixel, +/// which is a `BWRBIT = true` mode. +/// +/// Other displays, like [crate::epd5in83b_v2] in opposite, will draw color pixel if bit is +/// cleared for that pixel, which is a `BWRBIT = false` mode. +/// +/// BWRBIT=true: chromatic doesn't override white, white bit cleared for black, white bit set for white, both bits set for chromatic +/// BWRBIT=false: chromatic does override white, both bits cleared for black, white bit set for white, red bit set for black +pub struct Display< + const WIDTH: u32, + const HEIGHT: u32, + const BWRBIT: bool, + const BYTECOUNT: usize, + COLOR: ColorType + PixelColor, +> { + buffer: [u8; BYTECOUNT], + rotation: DisplayRotation, + _color: PhantomData, +} + +impl< + const WIDTH: u32, + const HEIGHT: u32, + const BWRBIT: bool, + const BYTECOUNT: usize, + COLOR: ColorType + PixelColor, + > Default for Display +{ + /// Initialize display with the color '0', which may not be the same on all device. + /// Many devices have a bit parameter polarity that should be changed if this is not the right + /// one. + /// However, every device driver should implement a DEFAULT_COLOR constant to indicate which + /// color this represents (TODO) + /// + /// If you want a specific default color, you can still call clear() to set one. + // inline is necessary here to allow heap allocation via Box on stack limited programs + #[inline(always)] + fn default() -> Self { + Self { + // default color must be 0 for every bit in a pixel to make this work everywere + buffer: [0u8; BYTECOUNT], + rotation: DisplayRotation::default(), + _color: PhantomData, + } + } +} + +/// For use with embedded_grahics +impl< + const WIDTH: u32, + const HEIGHT: u32, + const BWRBIT: bool, + const BYTECOUNT: usize, + COLOR: ColorType + PixelColor, + > DrawTarget for Display +{ + type Color = COLOR; + type Error = core::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for pixel in pixels { + self.set_pixel(pixel); + } + Ok(()) + } +} + +/// For use with embedded_grahics +impl< + const WIDTH: u32, + const HEIGHT: u32, + const BWRBIT: bool, + const BYTECOUNT: usize, + COLOR: ColorType + PixelColor, + > OriginDimensions for Display +{ + fn size(&self) -> Size { + match self.rotation { + DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => Size::new(WIDTH, HEIGHT), + DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => Size::new(HEIGHT, WIDTH), + } + } +} + +impl< + const WIDTH: u32, + const HEIGHT: u32, + const BWRBIT: bool, + const BYTECOUNT: usize, + COLOR: ColorType + PixelColor, + > Display +{ + /// get internal buffer to use it (to draw in epd) + pub fn buffer(&self) -> &[u8] { + &self.buffer + } + + /// Set the display rotation. + /// + /// This only concerns future drawing made to it. Anything aready drawn + /// stays as it is in the buffer. + pub fn set_rotation(&mut self, rotation: DisplayRotation) { + self.rotation = rotation; + } + + /// Get current rotation + pub fn rotation(&self) -> DisplayRotation { + self.rotation + } + + /// Set a specific pixel color on this display + pub fn set_pixel(&mut self, pixel: Pixel) { + set_pixel( + &mut self.buffer, + WIDTH, + HEIGHT, + self.rotation, + BWRBIT, + pixel, + ); + } +} + +/// Some Tricolor specifics +impl + Display +{ + /// get black/white internal buffer to use it (to draw in epd) + pub fn bw_buffer(&self) -> &[u8] { + &self.buffer[..self.buffer.len() / 2] + } + + /// get chromatic internal buffer to use it (to draw in epd) + pub fn chromatic_buffer(&self) -> &[u8] { + &self.buffer[self.buffer.len() / 2..] + } +} + +/// Same as `Display`, except that its characteristics are defined at runtime. +/// See display for documentation as everything is the same except that default +/// is replaced by a `new` method. +pub struct VarDisplay<'a, COLOR: ColorType + PixelColor> { + width: u32, + height: u32, + bwrbit: bool, + buffer: &'a mut [u8], + rotation: DisplayRotation, + _color: PhantomData, +} + +/// For use with embedded_grahics +impl DrawTarget for VarDisplay<'_, COLOR> { + type Color = COLOR; + type Error = core::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for pixel in pixels { + self.set_pixel(pixel); + } + Ok(()) + } +} + +/// For use with embedded_grahics +impl OriginDimensions for VarDisplay<'_, COLOR> { + fn size(&self) -> Size { + match self.rotation { + DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => { + Size::new(self.width, self.height) + } + DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => { + Size::new(self.height, self.width) + } + } + } +} + +/// Error found during usage of VarDisplay +#[derive(Debug)] +pub enum VarDisplayError { + /// The provided buffer was too small + BufferTooSmall, +} + +impl<'a, COLOR: ColorType + PixelColor> VarDisplay<'a, COLOR> { + /// You must allocate the buffer by yourself, it must be large enough to contain all pixels. + /// + /// Parameters are documented in `Display` as they are the same as the const generics there. + /// bwrbit should be false for non tricolor displays + pub fn new( + width: u32, + height: u32, + buffer: &'a mut [u8], + bwrbit: bool, + ) -> Result { + let myself = Self { + width, + height, + bwrbit, + buffer, + rotation: DisplayRotation::default(), + _color: PhantomData, + }; + // enfore some constraints dynamicly + if myself.buffer_size() > myself.buffer.len() { + return Err(VarDisplayError::BufferTooSmall); + } + Ok(myself) + } + + /// get the number of used bytes in the buffer + fn buffer_size(&self) -> usize { + self.height as usize + * line_bytes( + self.width, + COLOR::BITS_PER_PIXEL_PER_BUFFER * COLOR::BUFFER_COUNT, + ) + } + + /// get internal buffer to use it (to draw in epd) + pub fn buffer(&self) -> &[u8] { + &self.buffer[..self.buffer_size()] + } + + /// Set the display rotation. + /// + /// This only concerns future drawing made to it. Anything aready drawn + /// stays as it is in the buffer. + pub fn set_rotation(&mut self, rotation: DisplayRotation) { + self.rotation = rotation; + } + + /// Get current rotation + pub fn rotation(&self) -> DisplayRotation { + self.rotation + } + + /// Set a specific pixel color on this display + pub fn set_pixel(&mut self, pixel: Pixel) { + let size = self.buffer_size(); + set_pixel( + &mut self.buffer[..size], + self.width, + self.height, + self.rotation, + self.bwrbit, + pixel, + ); + } +} + +/// Some Tricolor specifics +impl VarDisplay<'_, TriColor> { + /// get black/white internal buffer to use it (to draw in epd) + pub fn bw_buffer(&self) -> &[u8] { + &self.buffer[..self.buffer_size() / 2] + } + + /// get chromatic internal buffer to use it (to draw in epd) + pub fn chromatic_buffer(&self) -> &[u8] { + &self.buffer[self.buffer_size() / 2..self.buffer_size()] + } +} + +// This is a function to share code between `Display` and `VarDisplay` +// It sets a specific pixel in a buffer to a given color. +// The big number of parameters is due to the fact that it is an internal function to both +// strctures. +fn set_pixel( + buffer: &mut [u8], + width: u32, + height: u32, + rotation: DisplayRotation, + bwrbit: bool, + pixel: Pixel, +) { + let Pixel(point, color) = pixel; + + // final coordinates + let (x, y) = match rotation { + // as i32 = never use more than 2 billion pixel per line or per column + DisplayRotation::Rotate0 => (point.x, point.y), + DisplayRotation::Rotate90 => (width as i32 - 1 - point.y, point.x), + DisplayRotation::Rotate180 => (width as i32 - 1 - point.x, height as i32 - 1 - point.y), + DisplayRotation::Rotate270 => (point.y, height as i32 - 1 - point.x), + }; + + // Out of range check + if (x < 0) || (x >= width as i32) || (y < 0) || (y >= height as i32) { + // don't do anything in case of out of range + return; + } + + let index = x as usize * COLOR::BITS_PER_PIXEL_PER_BUFFER / 8 + + y as usize * line_bytes(width, COLOR::BITS_PER_PIXEL_PER_BUFFER); + let (mask, bits) = color.bitmask(bwrbit, x as u32); + + if COLOR::BUFFER_COUNT == 2 { + // split buffer is for tricolor displays that use 2 buffer for 2 bits per pixel + buffer[index] = buffer[index] & mask | (bits & 0xFF) as u8; + let index = index + buffer.len() / 2; + buffer[index] = buffer[index] & mask | (bits >> 8) as u8; + } else { + buffer[index] = buffer[index] & mask | bits as u8; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::color::*; + use embedded_graphics::{ + primitives::{Line, PrimitiveStyle}, + }; + + // test buffer length + #[test] + fn graphics_size() { + // example definition taken from epd1in54 + let display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + assert_eq!(display.buffer().len(), 5000); + } + + // test default background color on all bytes + #[test] + fn graphics_default() { + let display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + for &byte in display.buffer() { + assert_eq!(byte, 0); + } + } + + #[test] + fn graphics_rotation_0() { + let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + let _ = Line::new(Point::new(0, 0), Point::new(7, 0)) + .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + assert_eq!(buffer[0], Color::Black.get_byte_value()); + + for &byte in buffer.iter().skip(1) { + assert_eq!(byte, 0); + } + } + + #[test] + fn graphics_rotation_90() { + let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + display.set_rotation(DisplayRotation::Rotate90); + let _ = Line::new(Point::new(0, 192), Point::new(0, 199)) + .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + assert_eq!(buffer[0], Color::Black.get_byte_value()); + + for &byte in buffer.iter().skip(1) { + assert_eq!(byte, 0); + } + } + + #[test] + fn graphics_rotation_180() { + let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + display.set_rotation(DisplayRotation::Rotate180); + let _ = Line::new(Point::new(192, 199), Point::new(199, 199)) + .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + extern crate std; + std::println!("{:?}", buffer); + + assert_eq!(buffer[0], Color::Black.get_byte_value()); + + for &byte in buffer.iter().skip(1) { + assert_eq!(byte, 0); + } + } + + #[test] + fn graphics_rotation_270() { + let mut display = Display::<200, 200, false, { 200 * 200 / 8 }, Color>::default(); + display.set_rotation(DisplayRotation::Rotate270); + let _ = Line::new(Point::new(199, 0), Point::new(199, 7)) + .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) + .draw(&mut display); + + let buffer = display.buffer(); + + extern crate std; + std::println!("{:?}", buffer); + + assert_eq!(buffer[0], Color::Black.get_byte_value()); + + for &byte in buffer.iter().skip(1) { + assert_eq!(byte, 0); + } + } + + #[test] + fn graphics_set_pixel_tricolor_false() { + let mut display = Display::<4, 4, false, { 4 * 4 * 2 / 8 }, TriColor>::default(); + display.set_pixel(Pixel(Point::new(0, 0), TriColor::White)); + display.set_pixel(Pixel(Point::new(1, 0), TriColor::Chromatic)); + display.set_pixel(Pixel(Point::new(2, 0), TriColor::Black)); + + let bw_buffer = display.bw_buffer(); + let chromatic_buffer = display.chromatic_buffer(); + + extern crate std; + std::println!("{:?}", bw_buffer); + std::println!("{:?}", chromatic_buffer); + + assert_eq!(bw_buffer, [192, 0]); + assert_eq!(chromatic_buffer, [64, 0]); + } + + #[test] + fn graphics_set_pixel_tricolor_true() { + let mut display = Display::<4, 4, true, { 4 * 4 * 2 / 8 }, TriColor>::default(); + display.set_pixel(Pixel(Point::new(0, 0), TriColor::White)); + display.set_pixel(Pixel(Point::new(1, 0), TriColor::Chromatic)); + display.set_pixel(Pixel(Point::new(2, 0), TriColor::Black)); + + let bw_buffer = display.bw_buffer(); + let chromatic_buffer = display.chromatic_buffer(); + + extern crate std; + std::println!("{:?}", bw_buffer); + std::println!("{:?}", chromatic_buffer); + + assert_eq!(bw_buffer, [128, 0]); + assert_eq!(chromatic_buffer, [64, 0]); + } +} diff --git a/src/interface.rs b/src/interface.rs new file mode 100644 index 0000000..200be11 --- /dev/null +++ b/src/interface.rs @@ -0,0 +1,208 @@ +use crate::traits::Command; +use core::marker::PhantomData; +use embedded_hal::{delay::*, digital::*, spi::SpiDevice}; + +/// The Connection Interface of all (?) Waveshare EPD-Devices +/// +/// SINGLE_BYTE_WRITE defines if a data block is written bytewise +/// or blockwise to the spi device +pub(crate) struct DisplayInterface { + /// SPI + _spi: PhantomData, + /// DELAY + _delay: PhantomData, + /// Low for busy, Wait until display is ready! + busy: BUSY, + /// Data/Command Control Pin (High for data, Low for command) + dc: DC, + /// Pin for Resetting + rst: RST, + /// number of ms the idle loop should sleep on + delay_us: u32, +} + +impl + DisplayInterface +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + /// Creates a new `DisplayInterface` struct + /// + /// If no delay is given, a default delay of 10ms is used. + pub fn new(busy: BUSY, dc: DC, rst: RST, delay_us: Option) -> Self { + // default delay of 10ms + let delay_us = delay_us.unwrap_or(10_000); + DisplayInterface { + _spi: PhantomData, + _delay: PhantomData, + busy, + dc, + rst, + delay_us, + } + } + + /// Basic function for sending [Commands](Command). + /// + /// Enables direct interaction with the device with the help of [data()](DisplayInterface::data()) + pub(crate) fn cmd(&mut self, spi: &mut SPI, command: T) -> Result<(), SPI::Error> { + // low for commands + let _ = self.dc.set_low(); + + // Transfer the command over spi + self.write(spi, &[command.address()]) + } + + /// Basic function for sending an array of u8-values of data over spi + /// + /// Enables direct interaction with the device with the help of [command()](Epd4in2::command()) + pub(crate) fn data(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> { + // high for data + let _ = self.dc.set_high(); + + if SINGLE_BYTE_WRITE { + for val in data.iter().copied() { + // Transfer data one u8 at a time over spi + self.write(spi, &[val])?; + } + } else { + self.write(spi, data)?; + } + + Ok(()) + } + + /// Basic function for sending [Commands](Command) and the data belonging to it. + /// + /// TODO: directly use ::write? cs wouldn't needed to be changed twice than + pub(crate) fn cmd_with_data( + &mut self, + spi: &mut SPI, + command: T, + data: &[u8], + ) -> Result<(), SPI::Error> { + self.cmd(spi, command)?; + self.data(spi, data) + } + + /// Basic function for sending the same byte of data (one u8) multiple times over spi + /// + /// Enables direct interaction with the device with the help of [command()](ConnectionInterface::command()) + pub(crate) fn data_x_times( + &mut self, + spi: &mut SPI, + val: u8, + repetitions: u32, + ) -> Result<(), SPI::Error> { + // high for data + let _ = self.dc.set_high(); + // Transfer data (u8) over spi + for _ in 0..repetitions { + self.write(spi, &[val])?; + } + Ok(()) + } + + // spi write helper/abstraction function + fn write(&mut self, spi: &mut SPI, data: &[u8]) -> Result<(), SPI::Error> { + // transfer spi data + // Be careful!! Linux has a default limit of 4096 bytes per spi transfer + // see https://raspberrypi.stackexchange.com/questions/65595/spi-transfer-fails-with-buffer-size-greater-than-4096 + if cfg!(target_os = "linux") { + for data_chunk in data.chunks(4096) { + spi.write(data_chunk)?; + } + Ok(()) + } else { + spi.write(data) + } + } + + /// Waits until device isn't busy anymore (busy == HIGH) + /// + /// This is normally handled by the more complicated commands themselves, + /// but in the case you send data and commands directly you might need to check + /// if the device is still busy + /// + /// is_busy_low + /// + /// - TRUE for epd4in2, epd2in13, epd2in7, epd5in83, epd7in5 + /// - FALSE for epd2in9, epd1in54 (for all Display Type A ones?) + /// + /// Most likely there was a mistake with the 2in9 busy connection + pub(crate) fn _wait_until_idle(&mut self, delay: &mut DELAY, is_busy_low: bool) { + while self.is_busy(is_busy_low) { + // This has been removed and added many time : + // - it is faster to not have it + // - it is complicated to pass the delay everywhere all the time + // - busy waiting can consume more power that delaying + // - delay waiting enables task switching on realtime OS + // -> keep it and leave the decision to the user + if self.delay_us > 0 { + delay.delay_us(self.delay_us); + } + } + } + + /// Same as `wait_until_idle` for device needing a command to probe Busy pin + pub(crate) fn wait_until_idle_with_cmd( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + is_busy_low: bool, + status_command: T, + ) -> Result<(), SPI::Error> { + self.cmd(spi, status_command)?; + if self.delay_us > 0 { + delay.delay_us(self.delay_us); + } + while self.is_busy(is_busy_low) { + self.cmd(spi, status_command)?; + if self.delay_us > 0 { + delay.delay_us(self.delay_us); + } + } + Ok(()) + } + + /// Checks if device is still busy + /// + /// This is normally handled by the more complicated commands themselves, + /// but in the case you send data and commands directly you might need to check + /// if the device is still busy + /// + /// is_busy_low + /// + /// - TRUE for epd4in2, epd2in13, epd2in7, epd5in83, epd7in5 + /// - FALSE for epd2in9, epd1in54 (for all Display Type A ones?) + /// + /// Most likely there was a mistake with the 2in9 busy connection + /// //TODO: use the #cfg feature to make this compile the right way for the certain types + pub(crate) fn is_busy(&mut self, is_busy_low: bool) -> bool { + (is_busy_low && self.busy.is_low().unwrap_or(false)) + || (!is_busy_low && self.busy.is_high().unwrap_or(false)) + } + + /// Resets the device. + /// + /// Often used to awake the module from deep sleep. See [Epd4in2::sleep()](Epd4in2::sleep()) + /// + /// The timing of keeping the reset pin low seems to be important and different per device. + /// Most displays seem to require keeping it low for 10ms, but the 7in5_v2 only seems to reset + /// properly with 2ms + pub(crate) fn reset(&mut self, delay: &mut DELAY, initial_delay: u32, duration: u32) { + let _ = self.rst.set_high(); + delay.delay_us(initial_delay); + + let _ = self.rst.set_low(); + delay.delay_us(duration); + let _ = self.rst.set_high(); + //TODO: the upstream libraries always sleep for 200ms here + // 10ms works fine with just for the 7in5_v2 but this needs to be validated for other devices + delay.delay_us(200_000); + } +} diff --git a/src/killinfo.rs b/src/killinfo.rs index ef083f8..87314ba 100644 --- a/src/killinfo.rs +++ b/src/killinfo.rs @@ -7,7 +7,7 @@ use crate::services::esi_static::{EsiClient, EsiError}; use crate::services::zkill::{ZkillClient, ZkillError}; pub struct Individual { - pub character: Character, + pub character: Option, pub alliance: Option, pub corporation: Corporation, pub ship: Option, @@ -111,7 +111,13 @@ impl Individual { alli_id: u32, ship_id: u32, ) -> Result { - let character = client.get_character(char_id)?; + + let character = if char_id != 0 { + client.get_character(char_id).ok() + } else { + None + }; + let corporation = client.get_corporation(corp_id)?; // Only try fetching alliance if alli_id != 0, else None diff --git a/src/lib.rs b/src/lib.rs index 01f303b..9e08701 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,119 @@ +//! A simple Driver for the [Waveshare](https://github.com/waveshare/e-Paper) E-Ink Displays via SPI +//! +//! - Built using [`embedded-hal`] traits. +//! - Graphics support is added through [`embedded-graphics`] +//! +//! [`embedded-graphics`]: https://docs.rs/embedded-graphics/ +//! [`embedded-hal`]: https://docs.rs/embedded-hal +//! + +//! +//! # Example +//! +//!```rust, no_run +//!# use embedded_hal_mock::eh1::*; +//!# fn main() -> Result<(), embedded_hal::spi::ErrorKind> { +//!use embedded_graphics::{ +//! pixelcolor::BinaryColor::On as Black, prelude::*, primitives::{Line, PrimitiveStyle}, +//!}; +//!use epd_waveshare::{epd1in54::*, prelude::*}; +//!# +//!# let expectations = []; +//!# let mut spi = spi::Mock::new(&expectations); +//!# let expectations = []; +//!# let cs_pin = digital::Mock::new(&expectations); +//!# let busy_in = digital::Mock::new(&expectations); +//!# let dc = digital::Mock::new(&expectations); +//!# let rst = digital::Mock::new(&expectations); +//!# let mut delay = delay::NoopDelay::new(); +//! +//!// Setup EPD +//!let mut epd = Epd1in54::new(&mut spi, busy_in, dc, rst, &mut delay, None)?; +//! +//!// Use display graphics from embedded-graphics +//!let mut display = Display1in54::default(); +//! +//!// Use embedded graphics for drawing a line +//! +//!let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +//! .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) +//! .draw(&mut display); +//! +//! // Display updated frame +//!epd.update_frame(&mut spi, &display.buffer(), &mut delay)?; +//!epd.display_frame(&mut spi, &mut delay)?; +//! +//!// Set the EPD to sleep +//!epd.sleep(&mut spi, &mut delay)?; +//!# Ok(()) +//!# } +//!``` +//! +//! # Other information and requirements +//! +//! - Buffersize: Wherever a buffer is used it always needs to be of the size: `width / 8 * length`, +//! where width and length being either the full e-ink size or the partial update window size +//! +//! ### SPI +//! +//! MISO is not connected/available. SPI_MODE_0 is used (CPHL = 0, CPOL = 0) with 8 bits per word, MSB first. +//! +//! Maximum speed tested by myself was 8Mhz but more should be possible (Ben Krasnow used 18Mhz with his implemenation) +//! + +#[cfg(feature = "graphics")] +pub mod graphics; + +pub mod traits; + +pub mod color; + +pub mod rect; + +/// Interface for the physical connection between display and the controlling device +pub mod interface; + +pub mod epd7in5_v2; + + pub mod killinfo; pub mod model; pub mod services; + + +pub mod type_a; + +/// Includes everything important besides the chosen Display +pub mod prelude { + pub use crate::color::{Color, OctColor, TriColor}; + pub use crate::traits::{ + QuickRefresh, RefreshLut, WaveshareDisplay, WaveshareThreeColorDisplay, + }; + + pub use crate::SPI_MODE; + + #[cfg(feature = "graphics")] + pub use crate::graphics::{Display, DisplayRotation}; +} + +/// Computes the needed buffer length. Takes care of rounding up in case width +/// is not divisible by 8. +/// +/// unused +/// bits width +/// <----><------------------------> +/// \[XXXXX210\]\[76543210\]...\[76543210\] ^ +/// \[XXXXX210\]\[76543210\]...\[76543210\] | height +/// \[XXXXX210\]\[76543210\]...\[76543210\] v +pub const fn buffer_len(width: usize, height: usize) -> usize { + (width + 7) / 8 * height +} + +use embedded_hal::spi::{Mode, Phase, Polarity}; + +/// SPI mode - +/// For more infos see [Requirements: SPI](index.html#spi) +pub const SPI_MODE: Mode = Mode { + phase: Phase::CaptureOnFirstTransition, + polarity: Polarity::IdleLow, +}; diff --git a/src/main.rs b/src/main.rs index 6ad138f..6f99a42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,73 +1,37 @@ -use embedded_graphics::{ - mono_font::{MonoTextStyle, ascii::FONT_6X10}, - pixelcolor::Gray8, -}; -use image::{GrayImage, Pixel, RgbImage}; +pub mod graphics; + +pub mod traits; + +pub mod color; + +pub mod rect; + +/// Interface for the physical connection between display and the controlling device +pub mod interface; + +pub mod epd7in5_v2; pub mod display; pub mod killinfo; pub mod model; pub mod services; +pub mod epaper; + +pub mod type_a; use crate::killinfo::KillInfo; use crate::services::esi_static::EsiClient; use crate::services::zkill::ZkillClient; -fn rgb_to_gray8(img: &RgbImage) -> GrayImage { - let mut gray = GrayImage::new(img.width(), img.height()); - for (x, y, pixel) in img.enumerate_pixels() { - let luma = pixel.to_luma()[0]; // simple conversion - gray.put_pixel(x, y, image::Luma([luma])); - } - gray + +pub const fn buffer_len(width: usize, height: usize) -> usize { + (width + 7) / 8 * height } -// Example function to render kill info on a display buffer -fn render_killmails(killmails: &[KillInfo], buffer: &mut GrayImage, x_off: i64) { - let mut y_offset = 0; - for k in killmails { - // Draw victim portrait - if let Some(alli) = &k.victim.alliance { - let logo = &alli.logo; - let gray_logo = rgb_to_gray8(logo); - // copy portrait into buffer at (0, y_offset) - image::imageops::overlay(buffer, &gray_logo, x_off + 0, y_offset as i64); - } - - if let Some(ship) = &k.victim.ship { - let icon = &ship.icon; - let gray_icon = rgb_to_gray8(icon); - // copy portrait into buffer at (0, y_offset) - image::imageops::overlay(buffer, &gray_icon, x_off + 64, y_offset as i64); - } - - // Draw victim name - //let text_style = MonoTextStyle::new(&FONT_6X10, Gray8::new(255)); - // You can use embedded_graphics Text or a real framebuffer draw function - // Example with embedded_graphics (requires DrawTarget) - // Text::new(&k.victim.character.name, Point::new(50, y_offset as i32), text_style) - // .draw(display)?; - - // Draw system name and value - println!( - "{} {:.2}M ISK in {}", - k.victim.character.name, - k.total_value / 1_000_000.0, - k.system_name - ); - - // Advance y offset - y_offset += 60; // adjust spacing - if y_offset >= buffer.height() as usize { - break; - } - } -} fn main() { - // Simulated 800x480 grayscale display buffer - let mut buffer = GrayImage::new(800, 480); + let esi = EsiClient::new(); let zkill = ZkillClient::new(); @@ -76,6 +40,12 @@ fn main() { let my_corp_id = 98685373; let mut display = display::Display::new(); + let mut epaper = epaper::EPaper::new().expect("DisplayError"); + + display.clear(true); + epaper.clear(true); + epaper.flush().expect("flush error"); + let response = zkill .get_corporation_kills(my_corp_id, past_seconds) @@ -95,6 +65,7 @@ fn main() { let y : i32 = ii * 60; display.draw_kill_info(k, 0, y); + epaper.draw_kill_info(k, 0, y); } let response = zkill @@ -112,11 +83,12 @@ fn main() { let ii = i as i32; let y : i32 = ii * 60; - display.draw_kill_info(k, 400, y); + display.draw_loss_info(k, 400, y); + epaper.draw_loss_info(k, 400, y); } - display.flush();; + display.flush(); + epaper.flush().expect("flush error"); + - // Save buffer for debugging - buffer.save("killmails_display.png").unwrap(); } diff --git a/src/rect.rs b/src/rect.rs new file mode 100644 index 0000000..4fa2d02 --- /dev/null +++ b/src/rect.rs @@ -0,0 +1,87 @@ +//! Rectangle operations for bigger displays with multiple _windows_ +use core::cmp; + +/// A rectangle +#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)] +pub struct Rect { + /// Origin X + pub x: u32, + /// Origin Y + pub y: u32, + /// Width + pub w: u32, + /// Height + pub h: u32, +} + +impl Rect { + /// Construct a new rectangle + pub const fn new(x: u32, y: u32, w: u32, h: u32) -> Rect { + Rect { x, y, w, h } + } + /// Compute intersection with another rectangle + pub fn intersect(&self, other: Rect) -> Rect { + let x = cmp::max(self.x, other.x); + let y = cmp::max(self.y, other.y); + let w = cmp::min(self.x + self.w, other.x + other.w).saturating_sub(x); + let h = cmp::min(self.y + self.h, other.y + other.h).saturating_sub(y); + Rect { x, y, w, h } + } + /// Move rectangle by (-dx,-dy) + pub fn sub_offset(&self, dx: u32, dy: u32) -> Rect { + Rect { + x: self.x - dx, + y: self.y - dy, + w: self.w, + h: self.h, + } + } + /// Test whether the rectangle is empty. + pub fn is_empty(&self) -> bool { + self.w == 0 || self.h == 0 + } +} + +#[test] +fn test_intersect() { + let r1 = Rect::new(0, 0, 10, 10); + let r2 = Rect::new(6, 3, 10, 10); + let r3 = r1.intersect(r2); + assert!(matches!( + r3, + Rect { + x: 6, + y: 3, + w: 4, + h: 7 + } + )); + + let r1 = Rect::new(0, 0, 10, 10); + let r2 = Rect::new(10, 11, 10, 10); + let r3 = r1.intersect(r2); + assert!(matches!( + r3, + Rect { + x: _, + y: _, + w: 0, + h: 0 + } + )); +} + +#[test] +fn sub_offset() { + let r1 = Rect::new(10, 10, 10, 10); + let r2 = r1.sub_offset(10, 5); + assert!(matches!( + r2, + Rect { + x: 0, + y: 5, + w: 10, + h: 10 + } + )); +} diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..abd2b3f --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,378 @@ +use core::marker::Sized; +use embedded_hal::{delay::*, digital::*, spi::SpiDevice}; + +/// All commands need to have this trait which gives the address of the command +/// which needs to be send via SPI with activated CommandsPin (Data/Command Pin in CommandMode) +pub(crate) trait Command: Copy { + fn address(self) -> u8; +} + +/// Seperates the different LUT for the Display Refresh process +#[derive(Debug, Clone, PartialEq, Eq, Copy, Default)] +pub enum RefreshLut { + /// The "normal" full Lookuptable for the Refresh-Sequence + #[default] + Full, + /// The quick LUT where not the full refresh sequence is followed. + /// This might lead to some + Quick, +} + +pub(crate) trait InternalWiAdditions +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + /// This initialises the EPD and powers it up + /// + /// This function is already called from + /// - [new()](WaveshareDisplay::new()) + /// - [`wake_up`] + /// + /// + /// This function calls [reset](WaveshareDisplay::reset), + /// so you don't need to call reset your self when trying to wake your device up + /// after setting it to sleep. + fn init(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error>; +} + +/// Functions to interact with three color panels +pub trait WaveshareThreeColorDisplay: + WaveshareDisplay +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + /// Transmit data to the SRAM of the EPD + /// + /// Updates both the black and the secondary color layers + fn update_color_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + black: &[u8], + chromatic: &[u8], + ) -> Result<(), SPI::Error>; + + /// Update only the black/white data of the display. + /// + /// This must be finished by calling `update_chromatic_frame`. + fn update_achromatic_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + black: &[u8], + ) -> Result<(), SPI::Error>; + + /// Update only the chromatic data of the display. + /// + /// This should be preceded by a call to `update_achromatic_frame`. + /// This data takes precedence over the black/white data. + fn update_chromatic_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + chromatic: &[u8], + ) -> Result<(), SPI::Error>; +} + +/// All the functions to interact with the EPDs +/// +/// This trait includes all public functions to use the EPDs +/// +/// # Example +/// +///```rust, no_run +///# use embedded_hal_mock::eh1::*; +///# fn main() -> Result<(), embedded_hal::spi::ErrorKind> { +///use embedded_graphics::{ +/// pixelcolor::BinaryColor::On as Black, prelude::*, primitives::{Line, PrimitiveStyle}, +///}; +///use epd_waveshare::{epd4in2::*, prelude::*}; +///# +///# let expectations = []; +///# let mut spi = spi::Mock::new(&expectations); +///# let expectations = []; +///# let cs_pin = digital::Mock::new(&expectations); +///# let busy_in = digital::Mock::new(&expectations); +///# let dc = digital::Mock::new(&expectations); +///# let rst = digital::Mock::new(&expectations); +///# let mut delay = delay::NoopDelay::new(); +/// +///// Setup EPD +///let mut epd = Epd4in2::new(&mut spi, busy_in, dc, rst, &mut delay, None)?; +/// +///// Use display graphics from embedded-graphics +///let mut display = Display4in2::default(); +/// +///// Use embedded graphics for drawing a line +/// +///let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +/// .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) +/// .draw(&mut display); +/// +/// // Display updated frame +///epd.update_frame(&mut spi, &display.buffer(), &mut delay)?; +///epd.display_frame(&mut spi, &mut delay)?; +/// +///// Set the EPD to sleep +///epd.sleep(&mut spi, &mut delay)?; +///# Ok(()) +///# } +///``` +/// +/// # Heap allocation +/// +/// For systems where stack space is limited but heap space is available +/// (i.e. ESP32 platforms with PSRAM) it's possible to allocate the display buffer +/// in heap; the Displayxxxx:default() is implemented as always inline, so you can +/// use Box::new to request heap space for the display buffer. +/// +///```rust, no_run +///# use epd_waveshare::epd4in2::Display4in2; +///# use epd_waveshare::prelude::*; +///# use embedded_graphics_core::prelude::*; +///# use embedded_graphics::primitives::*; +///let mut display = Box::new(Display4in2::default()); +///let _ = Line::new(Point::new(0, 120), Point::new(0, 295)) +/// .into_styled(PrimitiveStyle::with_stroke(Color::Black, 1)) +/// .draw(&mut *display); +///``` +pub trait WaveshareDisplay +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + /// The Color Type used by the Display + type DisplayColor; + /// Creates a new driver from a SPI peripheral, CS Pin, Busy InputPin, DC + /// + /// `delay_us` is the number of us the idle loop should sleep on. + /// Setting it to 0 implies busy waiting. + /// Setting it to None means a default value is used. + /// + /// This already initialises the device. + fn new( + spi: &mut SPI, + busy: BUSY, + dc: DC, + rst: RST, + delay: &mut DELAY, + delay_us: Option, + ) -> Result + where + Self: Sized; + + /// Let the device enter deep-sleep mode to save power. + /// + /// The deep sleep mode returns to standby with a hardware reset. + fn sleep(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error>; + + /// Wakes the device up from sleep + /// + /// Also reintialises the device if necessary. + fn wake_up(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error>; + + /// Sets the backgroundcolor for various commands like [clear_frame](WaveshareDisplay::clear_frame) + fn set_background_color(&mut self, color: Self::DisplayColor); + + /// Get current background color + fn background_color(&self) -> &Self::DisplayColor; + + /// Get the width of the display + fn width(&self) -> u32; + + /// Get the height of the display + fn height(&self) -> u32; + + /// Transmit a full frame to the SRAM of the EPD + fn update_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error>; + + /// Transmits partial data to the SRAM of the EPD + /// + /// (x,y) is the top left corner + /// + /// BUFFER needs to be of size: width / 8 * height ! + #[allow(clippy::too_many_arguments)] + fn update_partial_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + buffer: &[u8], + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error>; + + /// Displays the frame data from SRAM + /// + /// This function waits until the device isn`t busy anymore + fn display_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error>; + + /// Provide a combined update&display and save some time (skipping a busy check in between) + fn update_and_display_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error>; + + /// Clears the frame buffer on the EPD with the declared background color + /// + /// The background color can be changed with [`WaveshareDisplay::set_background_color`] + fn clear_frame(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error>; + + /// Trait for using various Waveforms from different LUTs + /// E.g. for partial refreshes + /// + /// A full refresh is needed after a certain amount of quick refreshes! + /// + /// WARNING: Quick Refresh might lead to ghosting-effects/problems with your display. Especially for the 4.2in Display! + /// + /// If None is used the old value will be loaded on the LUTs once more + fn set_lut( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + refresh_rate: Option, + ) -> Result<(), SPI::Error>; + + /// Wait until the display has stopped processing data + /// + /// You can call this to make sure a frame is displayed before goin further + fn wait_until_idle(&mut self, spi: &mut SPI, delay: &mut DELAY) -> Result<(), SPI::Error>; +} + +/// Allows quick refresh support for displays that support it; lets you send both +/// old and new frame data to support this. +/// +/// When using the quick refresh look-up table, the display must receive separate display +/// buffer data marked as old, and new. This is used to determine which pixels need to change, +/// and how they will change. This isn't required when using full refreshes. +/// +/// (todo: Example ommitted due to CI failures.) +/// Example: +///```rust, no_run +///# use embedded_hal_mock::eh1::*; +///# fn main() -> Result<(), embedded_hal::spi::ErrorKind> { +///# use embedded_graphics::{ +///# pixelcolor::BinaryColor::On as Black, prelude::*, primitives::{Line, PrimitiveStyle}, +///# }; +///# use epd_waveshare::{epd4in2::*, prelude::*}; +///# use epd_waveshare::graphics::VarDisplay; +///# +///# let expectations = []; +///# let mut spi = spi::Mock::new(&expectations); +///# let expectations = []; +///# let cs_pin = digital::Mock::new(&expectations); +///# let busy_in = digital::Mock::new(&expectations); +///# let dc = digital::Mock::new(&expectations); +///# let rst = digital::Mock::new(&expectations); +///# let mut delay = delay::NoopDelay::new(); +///# +///# // Setup EPD +///# let mut epd = Epd4in2::new(&mut spi, busy_in, dc, rst, &mut delay, None)?; +///let (x, y, frame_width, frame_height) = (20, 40, 80,80); +/// +///let mut buffer = [DEFAULT_BACKGROUND_COLOR.get_byte_value(); 80 / 8 * 80]; +///let mut display = VarDisplay::new(frame_width, frame_height, &mut buffer,false).unwrap(); +/// +///epd.update_partial_old_frame(&mut spi, &mut delay, display.buffer(), x, y, frame_width, frame_height) +/// .ok(); +/// +///display.clear(Color::White).ok(); +///// Execute drawing commands here. +/// +///epd.update_partial_new_frame(&mut spi, &mut delay, display.buffer(), x, y, frame_width, frame_height) +/// .ok(); +///# Ok(()) +///# } +///``` +pub trait QuickRefresh +where + SPI: SpiDevice, + BUSY: InputPin, + DC: OutputPin, + RST: OutputPin, + DELAY: DelayNs, +{ + /// Updates the old frame. + fn update_old_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error>; + + /// Updates the new frame. + fn update_new_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error>; + + /// Displays the new frame + fn display_new_frame(&mut self, spi: &mut SPI, _delay: &mut DELAY) -> Result<(), SPI::Error>; + + /// Updates and displays the new frame. + fn update_and_display_new_frame( + &mut self, + spi: &mut SPI, + buffer: &[u8], + delay: &mut DELAY, + ) -> Result<(), SPI::Error>; + + /// Updates the old frame for a portion of the display. + #[allow(clippy::too_many_arguments)] + fn update_partial_old_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + buffer: &[u8], + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error>; + + /// Updates the new frame for a portion of the display. + #[allow(clippy::too_many_arguments)] + fn update_partial_new_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + buffer: &[u8], + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error>; + + /// Clears the partial frame buffer on the EPD with the declared background color + /// The background color can be changed with [`WaveshareDisplay::set_background_color`] + fn clear_partial_frame( + &mut self, + spi: &mut SPI, + delay: &mut DELAY, + x: u32, + y: u32, + width: u32, + height: u32, + ) -> Result<(), SPI::Error>; +} diff --git a/src/type_a/command.rs b/src/type_a/command.rs new file mode 100644 index 0000000..170156e --- /dev/null +++ b/src/type_a/command.rs @@ -0,0 +1,103 @@ +//! SPI Commands for the Waveshare 2.9" and 1.54" E-Ink Display + +use crate::traits; + +/// Epd1in54 and EPD2IN9 commands +/// +/// Should rarely (never?) be needed directly. +/// +/// For more infos about the addresses and what they are doing look into the pdfs +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub(crate) enum Command { + /// Driver Output control + /// 3 Databytes: + /// A[7:0] + /// 0.. A[8] + /// 0.. B[2:0] + /// Default: Set A[8:0] = 0x127 and B[2:0] = 0x0 + DriverOutputControl = 0x01, + GateDrivingVoltage = 0x03, + SourceDrivingVoltage = 0x04, + /// Booster Soft start control + /// 3 Databytes: + /// 1.. A[6:0] + /// 1.. B[6:0] + /// 1.. C[6:0] + /// Default: A[7:0] = 0xCF, B[7:0] = 0xCE, C[7:0] = 0x8D + BoosterSoftStartControl = 0x0C, + GateScanStartPosition = 0x0F, + //TODO: useful? + // GateScanStartPosition = 0x0F, + /// Deep Sleep Mode Control + /// 1 Databyte: + /// 0.. A[0] + /// Values: + /// A[0] = 0: Normal Mode (POR) + /// A[0] = 1: Enter Deep Sleep Mode + DeepSleepMode = 0x10, + // /// Data Entry mode setting + DataEntryModeSetting = 0x11, + + SwReset = 0x12, + + TemperatureSensorSelection = 0x18, + + TemperatureSensorControl = 0x1A, + + MasterActivation = 0x20, + + DisplayUpdateControl1 = 0x21, + + DisplayUpdateControl2 = 0x22, + + WriteRam = 0x24, + + WriteRam2 = 0x26, + + WriteVcomRegister = 0x2C, + + WriteLutRegister = 0x32, + + WriteOtpSelection = 0x37, + + SetDummyLinePeriod = 0x3A, + + SetGateLineWidth = 0x3B, + + BorderWaveformControl = 0x3C, + + WriteLutRegisterEnd = 0x3f, + + SetRamXAddressStartEndPosition = 0x44, + + SetRamYAddressStartEndPosition = 0x45, + + SetRamXAddressCounter = 0x4E, + + SetRamYAddressCounter = 0x4F, + + Nop = 0xFF, +} + +impl traits::Command for Command { + /// Returns the address of the command + fn address(self) -> u8 { + self as u8 + } +} + +#[cfg(test)] +mod tests { + use super::Command; + use crate::traits::Command as CommandTrait; + + #[test] + fn command_addr() { + assert_eq!(Command::DriverOutputControl.address(), 0x01); + + assert_eq!(Command::SetRamXAddressCounter.address(), 0x4E); + + assert_eq!(Command::Nop.address(), 0xFF); + } +} diff --git a/src/type_a/constants.rs b/src/type_a/constants.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/type_a/mod.rs b/src/type_a/mod.rs new file mode 100644 index 0000000..84c4f1c --- /dev/null +++ b/src/type_a/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod command; +pub(crate) mod constants;