From 061abd74b9779d1e7606dce489445abf9b4123e8 Mon Sep 17 00:00:00 2001 From: Yandrik Date: Mon, 25 Nov 2024 21:53:08 +0100 Subject: [PATCH] feat: bachelor test impl --- .idea/kolibri-cyd-tester-app-embassy.iml | 4 + Cargo.lock | 228 +++--- app/Cargo.toml | 15 +- app/rustfmt.toml | 19 + .../{exapp-k-timer.rs => exapp-lvgl-timer.rs} | 29 +- app/src/bin/exapp-slint-timer.rs | 516 ++++++++++++++ app/src/bin/light-control.rs | 453 ++++++++++++ app/src/bin/microwave-ui.rs | 669 ++++++++++++++++++ app/src/bin/timer.rs | 586 +++++++++++++++ lvgl-based/.cargo/config.toml | 4 +- lvgl-based/rustfmt.toml | 19 + lvgl-based/src/bin/light-control.rs | 63 +- lvgl-based/src/bin/microwave-ui.rs | 82 ++- lvgl-based/src/bin/timer.rs | 80 ++- rustfmt.toml | 3 + slint-based/.cargo/config.toml | 2 +- slint-based/Cargo.lock | 4 +- slint-based/Cargo.toml | 24 +- slint-based/build.rs | 4 + slint-based/rustfmt.toml | 1 + slint-based/src/bin/light-control.rs | 189 +++-- slint-based/src/bin/microwave-ui.rs | 255 ++++--- slint-based/src/bin/timer.rs | 255 ++++--- slint/ui.slint | 0 24 files changed, 3079 insertions(+), 425 deletions(-) create mode 100644 app/rustfmt.toml rename app/src/bin/{exapp-k-timer.rs => exapp-lvgl-timer.rs} (95%) create mode 100644 app/src/bin/exapp-slint-timer.rs create mode 100644 app/src/bin/light-control.rs create mode 100644 app/src/bin/microwave-ui.rs create mode 100644 app/src/bin/timer.rs create mode 100644 lvgl-based/rustfmt.toml create mode 100644 slint/ui.slint diff --git a/.idea/kolibri-cyd-tester-app-embassy.iml b/.idea/kolibri-cyd-tester-app-embassy.iml index 72348da..10dde7d 100644 --- a/.idea/kolibri-cyd-tester-app-embassy.iml +++ b/.idea/kolibri-cyd-tester-app-embassy.iml @@ -5,8 +5,12 @@ + + + + diff --git a/Cargo.lock b/Cargo.lock index da17574..fe85d14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -19,43 +19,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "anyhow" -version = "1.0.88" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "atomic" @@ -77,9 +77,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "az" @@ -128,9 +128,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" [[package]] name = "byteorder" @@ -146,9 +146,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.17" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -168,33 +168,33 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "critical-section" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "darling" @@ -217,7 +217,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -228,7 +228,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -239,7 +239,7 @@ checksum = "4e018fccbeeb50ff26562ece792ed06659b9c2dae79ece77c4456bb10d9bf79b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "embassy-executor" -version = "0.6.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ed0e24bdd4a5f4ff1b72ee4f264b1d23e179ea71a77d984b5fd24877a2bbe1" +checksum = "f64f84599b0f4296b92a4b6ac2109bc02340094bda47b9766c5f9ec6a318ebf8" dependencies = [ "critical-section", "document-features", @@ -300,14 +300,14 @@ dependencies = [ [[package]] name = "embassy-executor-macros" -version = "0.5.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d4c0c34b32c2c653c9eecce1cefaf8539dd9a54e61deb5499254f01e2fcac2" +checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -497,9 +497,9 @@ dependencies = [ [[package]] name = "embedded-sdmmc" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "150f320125310e179b9e73b081173b349e63c5c7d4ca44db4e5b9121b10387ec" +checksum = "eb637331040ec9b35f6a8151904d1aca914cc349d14c91b9a3e92ba789b22f3f" dependencies = [ "byteorder", "embedded-hal 1.0.0", @@ -531,7 +531,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -552,7 +552,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -579,7 +579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94a4b8d74e7cc7baabcca5b2277b41877e039ad9cd49959d48ef94dac7eab4b" dependencies = [ "quote", - "syn 2.0.77", + "syn 2.0.89", "termcolor", ] @@ -659,7 +659,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -690,9 +690,9 @@ dependencies = [ [[package]] name = "esp-riscv-rt" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc32298ed7c263b06c8b031704d8517cc62c819f2a9d5c261d0cb119634d6e9" +checksum = "94aca65db6157aa5f42d9df6595b21462f28207ca4230b799aa3620352ef6a72" dependencies = [ "document-features", "riscv", @@ -725,6 +725,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "format_no_std" version = "1.2.0" @@ -742,21 +748,21 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-task", @@ -790,9 +796,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heapless" @@ -834,9 +840,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -880,10 +886,11 @@ dependencies = [ [[package]] name = "kolibri-embedded-gui" version = "0.0.0-alpha.1" -source = "git+https://github.com/Yandrik/kolibri.git?branch=optimizations#f3e92212ac29431561748e6c37427651b3151bc4" +source = "git+https://github.com/Yandrik/kolibri.git?branch=optimizations#ab04bf432d09eace365029e6420bf6e59ac8fc2f" dependencies = [ "embedded-graphics", "embedded-iconoir", + "foldhash", "heapless 0.7.17", ] @@ -932,9 +939,9 @@ checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" [[package]] name = "minijinja" -version = "2.2.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7d3e3a3eece1fa4618237ad41e1de855ced47eab705cec1c9a920e1d1c5aad" +checksum = "2c37e1b517d1dcd0e51dc36c4567b9d5a29262b3ec8da6cb5d35e27a8fb529b5" dependencies = [ "serde", ] @@ -990,9 +997,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -1002,9 +1009,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "portable-atomic" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "proc-macro-crate" @@ -1041,9 +1048,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1080,23 +1087,43 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "riscv" -version = "0.11.1" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" +checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7" dependencies = [ "critical-section", "embedded-hal 1.0.0", + "paste", + "riscv-macros", + "riscv-pac", ] [[package]] -name = "riscv-rt-macros" -version = "0.2.1" +name = "riscv-macros" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d100d466dbb76681ef6a9386f3da9abc570d57394e86da0ba5af8c4408486d" +checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.89", +] + +[[package]] +name = "riscv-pac" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" + +[[package]] +name = "riscv-rt-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", ] [[package]] @@ -1110,9 +1137,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "scopeguard" @@ -1122,9 +1149,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semihosting" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11f0f8bcc4088e72905fcd826b820e72de6113218c22f1734cd94a3a5cf9172" +checksum = "a5c5996e5d1dec34b0dff3285e27124e70964504e3fd361bce330dc476cebafd" [[package]] name = "semver" @@ -1134,29 +1161,29 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -1216,7 +1243,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.89", ] [[package]] @@ -1232,9 +1259,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -1273,9 +1300,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -1313,9 +1340,9 @@ checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "utf8parse" @@ -1347,16 +1374,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", + "windows-sys", ] [[package]] @@ -1434,9 +1452,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -1464,9 +1482,9 @@ dependencies = [ [[package]] name = "xtensa-lx-rt" -version = "0.17.1" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ceb69c1487b78d83531c5d94fb81d0dceef1ccb0affba29f29420b1f72d3ddb" +checksum = "5c0307d03dadbf95633942e13901984f2059df4c963367348168cbd21c962669" dependencies = [ "anyhow", "bare-metal", @@ -1490,5 +1508,5 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.89", ] diff --git a/app/Cargo.toml b/app/Cargo.toml index 04d6e90..c30ce84 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "kolibri-cyd-tester-app-embassy" -version = "0.1.0" -edition = "2021" +name = "kolibri-cyd-tester-app-embassy" +version = "0.1.0" +edition = "2021" rust-version = "1.76.0" [[bin]] @@ -19,13 +19,16 @@ path = "src/bin/xpt.rs" name = "calibrate" path = "src/bin/calibrate.rs" +[[bin]] +name = "light-control" + [lib] [dependencies] -bit_field = "0.10.2" -display-interface = "0.5.0" +bit_field = "0.10.2" +display-interface = "0.5.0" display-interface-spi = "0.5.0" -embassy-embedded-hal = "0.2.0" +embassy-embedded-hal = "0.2.0" # defmt = "0.3.8" # defmt-rtt = "0.4.1" embassy-executor = { version = "0.6.0", features = ["log"] } diff --git a/app/rustfmt.toml b/app/rustfmt.toml new file mode 100644 index 0000000..7dcd950 --- /dev/null +++ b/app/rustfmt.toml @@ -0,0 +1,19 @@ +# Edition +edition = "2021" + +# Comments +format_code_in_doc_comments = true +normalize_comments = true +wrap_comments = true + +# Imports +group_imports = "StdExternalCrate" +imports_granularity = "Crate" +imports_layout = "HorizontalVertical" + +# Miscellaneous +enum_discrim_align_threshold = 25 +hex_literal_case = "Upper" + +# Length +max_width=60 diff --git a/app/src/bin/exapp-k-timer.rs b/app/src/bin/exapp-lvgl-timer.rs similarity index 95% rename from app/src/bin/exapp-k-timer.rs rename to app/src/bin/exapp-lvgl-timer.rs index d261464..f360396 100644 --- a/app/src/bin/exapp-k-timer.rs +++ b/app/src/bin/exapp-lvgl-timer.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] +extern crate alloc; + use core::{cell::RefCell, cmp::min, fmt}; use display_interface_spi::SPIInterface; @@ -42,6 +44,7 @@ use kolibri_embedded_gui::{ style::medsize_rgb565_style, ui::{Interaction, Ui}, }; +use lvgl::{self, Display, DrawBuffer}; use mipidsi::{ models::ILI9486Rgb565, options::{ColorInversion, ColorOrder, Orientation, Rotation}, @@ -50,6 +53,19 @@ use mipidsi::{ use static_cell::StaticCell; use xpt2046::Xpt2046; +// fn init_heap() { +// const HEAP_SIZE: usize = 32 * 1024; +// static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit(); +// +// unsafe { +// esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( +// HEAP.as_mut_ptr() as *mut u8, +// HEAP_SIZE, +// esp_alloc::MemoryCapability::Internal.into(), +// )); +// } +// } + #[embassy_executor::task] async fn touch_task( touch_irq: GpioPin<36>, @@ -235,10 +251,10 @@ async fn main(spawner: Spawner) { let mut display = ProfilerDisplay::new(display); - { - let mut ui = Ui::new_fullscreen(&mut display, medsize_rgb565_style()); - ui.clear_background().ok(); - } + const HOR_RES: u32 = 320; + const VER_RES: u32 = 240; + lvgl::init(); + {} backlight.set_high(); // init touchscreen pins @@ -287,6 +303,7 @@ async fn main(spawner: Spawner) { // Periodically feed the RWDT watchdog timer when our tasks are not running: let mut sm = SmartstateProvider::<20>::new(); + loop { // SMART REDRAWING ENABLE / DISABLE // sm.force_redraw_all(); @@ -383,7 +400,7 @@ async fn main(spawner: Spawner) { .add_horizontal(IconButton::new(size32px::actions::AddCircle).smartstate(sm.next())) .clicked() { - if !(appdata.timer_running() || appdata.timer_paused()) { + if !appdata.timer_running() { appdata.add_secs(10); } } @@ -392,7 +409,7 @@ async fn main(spawner: Spawner) { .add(IconButton::new(size32px::actions::MinusCircle).smartstate(sm.next())) .clicked() { - if !(appdata.timer_running() || appdata.timer_paused()) { + if !appdata.timer_running() { appdata.sub_secs(10); } } diff --git a/app/src/bin/exapp-slint-timer.rs b/app/src/bin/exapp-slint-timer.rs new file mode 100644 index 0000000..a1e4757 --- /dev/null +++ b/app/src/bin/exapp-slint-timer.rs @@ -0,0 +1,516 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use core::{cell::RefCell, cmp::min, fmt}; + +use display_interface_spi::SPIInterface; +use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; +use embassy_executor::Spawner; +use embassy_sync::{ + blocking_mutex::{raw::NoopRawMutex, NoopMutex}, + signal::Signal, +}; +use embassy_time::{Duration, Instant, Timer}; +use embedded_graphics::{ + mono_font::ascii, + pixelcolor::Rgb565, + prelude::{DrawTarget, Point, RgbColor, Size, WebColors}, +}; +use embedded_graphics_profiler_display::ProfilerDisplay; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + gpio::{GpioPin, Input, Io, Level, Output, Pull, NO_PIN}, + peripherals::{Peripherals, SPI2, SPI3}, + prelude::*, + rtc_cntl::Rtc, + spi::{master::Spi, FullDuplexMode, SpiMode}, + system::SystemControl, + timer::timg::TimerGroup, +}; +use esp_println::println; +use kolibri_cyd_tester_app_embassy::Debouncer; +use kolibri_embedded_gui::{ + button::Button, + checkbox::Checkbox, + icon::IconWidget, + iconbutton::IconButton, + icons::{size12px, size24px, size32px, size48px, size96px}, + label::Label, + smartstate::SmartstateProvider, + spacer::Spacer, + style::medsize_rgb565_style, + ui::{Interaction, Ui}, +}; +use mipidsi::{ + models::ILI9486Rgb565, + options::{ColorInversion, ColorOrder, Orientation, Rotation}, + Builder, +}; +use slint::platform::software_renderer::MinimalSoftwareWindow; +use static_cell::StaticCell; +use xpt2046::Xpt2046; + +slint::include_modules!(); + +use esp_alloc as _; + +fn init_heap() { + const HEAP_SIZE: usize = 32 * 1024; + static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit(); + + unsafe { + esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( + HEAP.as_mut_ptr() as *mut u8, + HEAP_SIZE, + esp_alloc::MemoryCapability::Internal.into(), + )); + } +} + +#[embassy_executor::task] +async fn touch_task( + touch_irq: GpioPin<36>, + spi: &'static mut NoopMutex>>, + touch_cs: GpioPin<33>, + touch_signal: &'static Signal>, +) -> ! { + let mut touch_driver = Xpt2046::new( + SpiDevice::new(spi, Output::new(touch_cs, Level::Low)), + Input::new(touch_irq, Pull::Up), + xpt2046::Orientation::LandscapeFlipped, + ); + touch_driver.set_num_samples(16); + touch_driver.init(&mut embassy_time::Delay).unwrap(); + + println!("touch task"); + + loop { + touch_driver.run().expect("Running Touch driver failed"); + if touch_driver.is_touched() { + let point = touch_driver.get_touch_point(); + touch_signal.signal(Some(Point::new(point.x + 25, 240 - point.y))); + } else { + touch_signal.signal(None); + } + Timer::after(Duration::from_millis(1)).await; // 100 a second + + // Your touch handling logic here + } +} + +struct AppData { + timer_start: Instant, + timer_set_duration: Duration, + timer_remaining_duration: Duration, + timer_running: bool, + timer_paused: bool, +} + +impl AppData { + fn new() -> Self { + Self { + timer_start: Instant::now(), + timer_set_duration: Duration::from_secs(10), + timer_remaining_duration: Duration::from_secs(10), + timer_running: false, + timer_paused: false, + } + } + + fn set_timer_duration(&mut self, duration: Duration) { + self.timer_set_duration = duration; + } + + fn add_secs(&mut self, secs: u64) { + self.set_timer_duration( + self.timer_set_duration + .checked_add(Duration::from_secs(secs)) + .unwrap_or(Duration::from_secs(5999)) + .min(Duration::from_secs(5999)), + ); + } + + fn sub_secs(&mut self, secs: u64) { + self.set_timer_duration( + self.timer_set_duration + .checked_sub(Duration::from_secs(secs)) + .unwrap_or(Duration::from_secs(10)) + .max(Duration::from_secs(10)), + ); + } + + fn start_timer(&mut self) { + if !self.timer_paused { + self.timer_remaining_duration = self.timer_set_duration; + } + + self.timer_start = Instant::now(); + self.timer_running = true; + self.timer_paused = false; + } + + fn pause_timer(&mut self) { + self.timer_paused = true; + self.timer_remaining_duration = self + .timer_remaining_duration + .checked_sub(self.timer_start.elapsed()) + .unwrap_or(Duration::from_secs(0)); + } + + fn reset_timer(&mut self) { + self.timer_start = Instant::now(); + self.timer_paused = false; + self.timer_running = false; + self.timer_remaining_duration = self.timer_set_duration; + } + + fn remaining(&self) -> Duration { + if self.timer_stopped() { + self.timer_set_duration + } else if self.timer_paused() { + self.timer_remaining_duration + } else { + self.timer_remaining_duration + .checked_sub(self.timer_start.elapsed()) + .unwrap_or(Duration::from_secs(0)) + } + } + + fn timer_stopped(&self) -> bool { + !self.timer_running + } + + fn timer_paused(&self) -> bool { + self.timer_running && self.timer_paused + } + + fn timer_running(&self) -> bool { + self.timer_running && !self.timer_paused + } + + fn timer_finished(&self) -> bool { + self.timer_running && self.remaining() == Duration::from_secs(0) + } +} + +struct MyPlatform { + window: Rc, +} + +impl Platform for MyPlatform { + fn create_window_adapter(&self) -> Result, slint::PlatformError> { + // Since on MCUs, there can be only one window, just return a clone of self.window. + // We'll also use the same window in the event loop. + Ok(self.window.clone()) + } + fn duration_since_start(&self) -> core::time::Duration { + core::time::Duration::from_micros(self.timer.get_time()) + } + // optional: You can put the event loop there, or in the main function, see later + fn run_event_loop(&self) -> Result<(), slint::PlatformError> { + todo!(); + } +} + +#[main] +async fn main(spawner: Spawner) { + + init_heap(); + + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Enable the RWDT watchdog timer: + let mut rtc = Rtc::new(peripherals.LPWR); + rtc.rwdt.set_timeout(2.secs()); + rtc.rwdt.enable(); + println!("RWDT watchdog enabled!"); + + // Initialize the SYSTIMER peripheral, and then Embassy: + let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + esp_hal_embassy::init(&clocks, timg0.timer0); + println!("Embassy initialized!"); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + // let mut led = Output::new(io.pins.gpio5, Level::High); + // Initialize SPI + let sclk = io.pins.gpio14; + let miso = io.pins.gpio12; + let mosi = io.pins.gpio13; + let cs = io.pins.gpio15; + let dc = io.pins.gpio2; + let mut backlight = Output::new(io.pins.gpio21, Level::Low); + + // Note: RST is not initialized as it's set to -1 in the instructions + + // up to 80MHz is possible (even tho the display driver isn't supposed to be + // that fast) Dataseheet sais 10MHz, so we're gonna go with that + let mut spi = Spi::new(peripherals.SPI2, 10.MHz(), SpiMode::Mode0, &mut clocks).with_pins( + Some(sclk), + Some(mosi), + Some(miso), + NO_PIN, + ); + + static DISP_SPI_BUS: StaticCell>>> = + StaticCell::new(); + let spi_bus = NoopMutex::new(RefCell::new(spi)); + let spi_bus = DISP_SPI_BUS.init(spi_bus); + + let di = SPIInterface::new( + SpiDevice::new(spi_bus, Output::new(cs, Level::Low)), + Output::new(dc, Level::Low), + ); + let display = Builder::new(ILI9486Rgb565, di) + .orientation(Orientation { + rotation: Rotation::Deg90, + mirrored: true, + }) + .color_order(ColorOrder::Bgr) + .invert_colors(ColorInversion::Inverted) + .init(&mut embassy_time::Delay) + .unwrap(); + + let mut display = ProfilerDisplay::new(display); + + let window = MinimalSoftwareWindow::new(Default::default()); + slint::platform::set_platform(Box::new(MyPlatform { + window: window.clone(), + //... + })) + .unwrap(); + + let ui = + + + backlight.set_high(); + + // init touchscreen pins + let touch_irq = io.pins.gpio36; + let touch_mosi = io.pins.gpio32; + let touch_miso = io.pins.gpio39; + let touch_clk = io.pins.gpio25; + let touch_cs = io.pins.gpio33; + + // 2MHz is the MAX! DO NOT DECREASE! This is really important. + let mut touch_spi = Spi::new(peripherals.SPI3, 2.MHz(), SpiMode::Mode0, &mut clocks).with_pins( + Some(touch_clk), + Some(touch_mosi), + Some(touch_miso), + NO_PIN, + ); + static TOUCH_SPI_BUS: StaticCell>>> = + StaticCell::new(); + let touch_spi_bus = NoopMutex::new(RefCell::new(touch_spi)); + let touch_spi_bus = TOUCH_SPI_BUS.init(touch_spi_bus); + + let touch_signal = Signal::new(); + static TOUCH_SIGNAL: StaticCell>> = StaticCell::new(); + let touch_signal = &*TOUCH_SIGNAL.init(touch_signal); + + spawner + .spawn(touch_task(touch_irq, touch_spi_bus, touch_cs, touch_signal)) + .unwrap(); + + // TODO: Spawn some tasks + let _ = spawner; + + // variables + let mut appdata = AppData::new(); + let (mut prev_mins, mut prev_secs, mut prev_millis) = (0, 0, 0); + let mut finished = false; + + // touchpoints + + let mut last_touch = None; + + static BUF_CELL: StaticCell<[Rgb565; 100 * 100]> = StaticCell::new(); + let buf = BUF_CELL.init([Rgb565::BLACK; 100 * 100]); + + let mut textbuf = [0u8; 64]; + + // Periodically feed the RWDT watchdog timer when our tasks are not running: + let mut sm = SmartstateProvider::<20>::new(); + loop { + // SMART REDRAWING ENABLE / DISABLE + // sm.force_redraw_all(); + + let start_time = embassy_time::Instant::now(); + sm.restart_counter(); + let mut ui = Ui::new_fullscreen(&mut display, medsize_rgb565_style()); + if let Some(touch) = touch_signal.try_take() { + let interact = match (touch, last_touch) { + (Some(point), Some(_)) => Interaction::Drag(point), + (Some(point), None) => Interaction::Click(point), + (None, Some(point)) => Interaction::Release(point), + (None, None) => Interaction::None, + }; + ui.interact(interact); + // println!("{:?}, {:?}, {:?}", last_touch, touch, interact); + + last_touch = touch; + } + + // BUFFER ENABLE/DISABLE + ui.set_buffer(buf); + + let start_draw_time = embassy_time::Instant::now(); + ui.sub_ui(|ui| { + ui.style_mut().default_font = ascii::FONT_9X18_BOLD; + ui.add(Label::new("Kolibri Timer App").smartstate(sm.next())); + Ok(()) + }) + .ok(); + + let remaining = appdata.remaining(); + + ui.add(Spacer::new(Size::new(0, 60))); + ui.add_horizontal(Spacer::new(Size::new(80, 0))); + ui.sub_ui(|ui| { + ui.style_mut().default_font = ascii::FONT_10X20; + if remaining.as_secs() / 60 != prev_mins { + sm.peek().force_redraw(); + prev_mins = remaining.as_secs() / 60; + } + ui.add_horizontal( + Label::new( + &format_no_std::show( + &mut textbuf, + format_args!("{:02}", remaining.as_secs() / 60), + ) + .unwrap(), + ) + .smartstate(sm.next()), + ); + ui.add_horizontal(Label::new(":").smartstate(sm.next())); + + if remaining.as_secs() % 60 != prev_secs { + sm.peek().force_redraw(); + prev_secs = remaining.as_secs() % 60; + } + ui.add_horizontal( + Label::new( + &format_no_std::show( + &mut textbuf, + format_args!("{:02}", remaining.as_secs() % 60), + ) + .unwrap(), + ) + .smartstate(sm.next()), + ); + ui.add_horizontal(Label::new(":").smartstate(sm.next())); + + if remaining.as_millis() % 1000 != prev_millis { + sm.peek().force_redraw(); + prev_millis = remaining.as_millis() % 1000; + } + ui.add( + Label::new( + &format_no_std::show( + &mut textbuf, + format_args!("{:03}", remaining.as_millis() % 1000), + ) + .unwrap(), + ) + .smartstate(sm.next()), + ); + Ok(()) + }) + .ok(); + + ui.add_horizontal(Spacer::new(Size::new(65, 0))); + ui.sub_ui(|ui| { + if appdata.timer_running() { + ui.style_mut().icon_color = Rgb565::CSS_LIGHT_GRAY; + } + if ui + .add_horizontal(IconButton::new(size32px::actions::AddCircle).smartstate(sm.next())) + .clicked() + { + if !appdata.timer_running() { + appdata.add_secs(10); + } + } + ui.add_horizontal(Label::new("+/- 10s").smartstate(sm.next())); + if ui + .add(IconButton::new(size32px::actions::MinusCircle).smartstate(sm.next())) + .clicked() + { + if !appdata.timer_running() { + appdata.sub_secs(10); + } + } + Ok(()) + }) + .ok(); + + ui.add_horizontal(Spacer::new(Size::new(80, 0))); + if ui + .add_horizontal(IconButton::new(size48px::actions::Undo).smartstate(sm.next())) + .clicked() + { + appdata.reset_timer(); + sm.force_redraw_all(); + } + + if !finished && appdata.timer_finished() { + sm.peek().force_redraw(); + } + + if appdata.timer_finished() { + if ui + .add_horizontal( + IconButton::new(size48px::actions::RemoveSquare).smartstate(sm.next()), + ) + .clicked() + { + appdata.reset_timer(); + sm.force_redraw_all(); + } + } else if appdata.timer_running() { + if ui + .add_horizontal(IconButton::new(size48px::music::Pause).smartstate(sm.next())) + .clicked() + { + appdata.pause_timer(); + sm.force_redraw_all(); + } + } else { + if ui + .add_horizontal(IconButton::new(size48px::music::Play).smartstate(sm.next())) + .clicked() + { + appdata.start_timer(); + sm.force_redraw_all(); + } + } + + finished = appdata.timer_finished(); + + let end_time = embassy_time::Instant::now(); + let draw_time = display.get_time(); + let prep_time = start_draw_time - start_time; + let proc_time = end_time - start_draw_time; + let proc_time = proc_time - min(draw_time, proc_time); + rtc.rwdt.feed(); + + if draw_time.as_micros() > 0 { + println!( + "draw time: {}.{:03}ms | prep time: {}.{:03}ms | proc time: {}.{:03}ms | total time: {}.{:03}ms", + draw_time.as_millis(), + draw_time.as_micros() % 100, + prep_time.as_millis(), + prep_time.as_micros() % 100, + proc_time.as_millis(), + proc_time.as_micros() % 100, + (draw_time + prep_time + proc_time).as_millis(), + (draw_time + prep_time + proc_time).as_micros() % 100, ); + } + display.reset_time(); + Timer::after(Duration::from_millis(17)).await; // 60 a second + } +} diff --git a/app/src/bin/light-control.rs b/app/src/bin/light-control.rs new file mode 100644 index 0000000..87b4861 --- /dev/null +++ b/app/src/bin/light-control.rs @@ -0,0 +1,453 @@ +#![no_std] +#![no_main] + +use core::{cell::RefCell, cmp::min, str::FromStr}; + +use display_interface_spi::SPIInterface; +use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; +use embassy_executor::Spawner; +use embassy_sync::{ + blocking_mutex::{raw::NoopRawMutex, NoopMutex}, + signal::Signal, +}; +use embassy_time::{Duration, Timer}; +use embedded_graphics::{ + mono_font::ascii, + pixelcolor::Rgb565, + prelude::{Point, RgbColor, Size, WebColors}, +}; +use embedded_graphics_profiler_display::ProfilerDisplay; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + gpio::{ + GpioPin, + Input, + Io, + Level, + Output, + Pull, + NO_PIN, + }, + peripherals::{Peripherals, SPI2, SPI3}, + prelude::*, + rtc_cntl::Rtc, + spi::{master::Spi, FullDuplexMode, SpiMode}, + system::SystemControl, + timer::timg::TimerGroup, +}; +use esp_println::println; +use kolibri_embedded_gui::{ + iconbutton::IconButton, + icons::size32px, + label::{HashLabel, Hasher, Label}, + slider::Slider, + smartstate::SmartstateProvider, + spacer::Spacer, + style::medsize_rgb565_style, + toggle_switch::ToggleSwitch, + ui::{Interaction, Ui}, +}; +use mipidsi::{ + models::ILI9341Rgb565, + options::{ColorOrder, Orientation, Rotation}, + Builder, +}; +use static_cell::StaticCell; +use xpt2046::Xpt2046; + +#[embassy_executor::task] +async fn touch_task( + touch_irq: GpioPin<36>, + spi: &'static mut NoopMutex< + RefCell>, + >, + touch_cs: GpioPin<33>, + touch_signal: &'static Signal< + NoopRawMutex, + Option, + >, +) -> ! { + let mut touch_driver = Xpt2046::new( + SpiDevice::new( + spi, + Output::new(touch_cs, Level::Low), + ), + Input::new(touch_irq, Pull::Up), + xpt2046::Orientation::LandscapeFlipped, + ); + touch_driver.set_num_samples(16); + touch_driver.init(&mut embassy_time::Delay).unwrap(); + + println!("touch task"); + + loop { + touch_driver + .run() + .expect("Running Touch driver failed"); + if touch_driver.is_touched() { + let point = touch_driver.get_touch_point(); + touch_signal.signal(Some(Point::new( + point.x + 25, + 240 - point.y, + ))); + } else { + touch_signal.signal(None); + } + Timer::after(Duration::from_millis(1)).await; // 100 + // a second + + // Your touch handling logic here + } +} + +fn lerp_fixed(start: u8, end: u8, t: u8, max_t: u8) -> u8 { + let (start, end, t, max_t) = + (start as u16, end as u16, t as u16, max_t as u16); + let t = t.min(max_t); + let result = start + + ((end - start.min(end)) * t + (max_t / 2)) + / max_t; + result as u8 +} + +#[derive(Debug, Clone)] +struct Lamp { + pub name: heapless::String<64>, + pub on: bool, + pub brightness: i16, +} + +impl Lamp { + pub fn new(name: &str) -> Self { + Self { + name: heapless::String::from_str(name).unwrap(), + on: false, + brightness: 255, + } + } +} + +enum Page { + Home, + LampCtrl(usize), +} +struct AppData { + lamps: heapless::Vec, +} + +impl AppData { + fn new() -> Self { + Self { + lamps: heapless::Vec::new(), + } + } + + fn add_lamp(&mut self, name: &str) { + self.lamps.push(Lamp::new(name)).unwrap(); + } +} + +#[main] +async fn main(spawner: Spawner) { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let mut clocks = + ClockControl::boot_defaults(system.clock_control) + .freeze(); + + // Enable the RWDT watchdog timer: + let mut rtc = Rtc::new(peripherals.LPWR); + rtc.rwdt.set_timeout(2.secs()); + rtc.rwdt.enable(); + println!("RWDT watchdog enabled!"); + + // Initialize the SYSTIMER peripheral, and then Embassy: + let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + esp_hal_embassy::init(&clocks, timg0.timer0); + println!("Embassy initialized!"); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + // let mut led = Output::new(io.pins.gpio5, + // Level::High); Initialize SPI + let sclk = io.pins.gpio14; + let miso = io.pins.gpio12; + let mosi = io.pins.gpio13; + let cs = io.pins.gpio15; + let dc = io.pins.gpio2; + let mut backlight = + Output::new(io.pins.gpio21, Level::Low); + + // Note: RST is not initialized as it's set to -1 in the + // instructions + + // up to 80MHz is possible (even tho the display driver + // isn't supposed to be that fast) Dataseheet sais + // 10MHz, so we're gonna go with that + let mut spi = Spi::new( + peripherals.SPI2, + 10.MHz(), + SpiMode::Mode0, + &mut clocks, + ) + .with_pins( + Some(sclk), + Some(mosi), + Some(miso), + NO_PIN, + ); + + static DISP_SPI_BUS: StaticCell< + NoopMutex>>, + > = StaticCell::new(); + let spi_bus = NoopMutex::new(RefCell::new(spi)); + let spi_bus = DISP_SPI_BUS.init(spi_bus); + + let di = SPIInterface::new( + SpiDevice::new( + spi_bus, + Output::new(cs, Level::Low), + ), + Output::new(dc, Level::Low), + ); + let display = Builder::new(ILI9341Rgb565, di) + .orientation(Orientation { + rotation: Rotation::Deg90, + mirrored: true, + }) + .color_order(ColorOrder::Bgr) + // .invert_colors(ColorInversion::Inverted) + .init(&mut embassy_time::Delay) + .unwrap(); + + let mut display = ProfilerDisplay::new(display); + let style = medsize_rgb565_style(); + + { + let mut ui = + Ui::new_fullscreen(&mut display, style); + ui.clear_background().ok(); + } + backlight.set_high(); + + // init touchscreen pins + let touch_irq = io.pins.gpio36; + let touch_mosi = io.pins.gpio32; + let touch_miso = io.pins.gpio39; + let touch_clk = io.pins.gpio25; + let touch_cs = io.pins.gpio33; + + // 2MHz is the MAX! DO NOT DECREASE! This is really + // important. + let mut touch_spi = Spi::new( + peripherals.SPI3, + 2.MHz(), + SpiMode::Mode0, + &mut clocks, + ) + .with_pins( + Some(touch_clk), + Some(touch_mosi), + Some(touch_miso), + NO_PIN, + ); + static TOUCH_SPI_BUS: StaticCell< + NoopMutex>>, + > = StaticCell::new(); + let touch_spi_bus = + NoopMutex::new(RefCell::new(touch_spi)); + let touch_spi_bus = TOUCH_SPI_BUS.init(touch_spi_bus); + + let touch_signal = Signal::new(); + static TOUCH_SIGNAL: StaticCell< + Signal>, + > = StaticCell::new(); + let touch_signal = &*TOUCH_SIGNAL.init(touch_signal); + + spawner + .spawn(touch_task( + touch_irq, + touch_spi_bus, + touch_cs, + touch_signal, + )) + .unwrap(); + + // TODO: Spawn some tasks + let _ = spawner; + + // variables + let mut appdata = AppData::new(); + + appdata.add_lamp("Front Door"); + appdata.add_lamp("Living Room"); + // appdata.lamps[1].on = true; + appdata.add_lamp("Bedroom"); + // appdata.lamps[2].on = true; + appdata.add_lamp("Bathroom"); + appdata.add_lamp("Porch"); + + let mut cur_page = Page::Home; + + // touchpoints + + let mut last_touch = None; + + static BUF_CELL: StaticCell<[Rgb565; 200 * 100]> = + StaticCell::new(); + let buf = BUF_CELL.init([Rgb565::BLACK; 200 * 100]); + + let mut textbuf = [0u8; 64]; + let hasher = Hasher::new(); + + // Periodically feed the RWDT watchdog timer when our + // tasks are not running: + let mut sm = SmartstateProvider::<20>::new(); + loop { + // SMART REDRAWING ENABLE / DISABLE + // sm.force_redraw_all(); + + let start_time = embassy_time::Instant::now(); + sm.restart_counter(); + let mut ui = + Ui::new_fullscreen(&mut display, style); + if let Some(touch) = touch_signal.try_take() { + let interact = match (touch, last_touch) { + (Some(point), Some(_)) => { + Interaction::Drag(point) + } + (Some(point), None) => { + Interaction::Click(point) + } + (None, Some(point)) => { + Interaction::Release(point) + } + (None, None) => Interaction::None, + }; + ui.interact(interact); + // println!("{:?}, {:?}, {:?}", last_touch, + // touch, interact); + + last_touch = touch; + } + + // BUFFER ENABLE/DISABLE + ui.set_buffer(buf); + + let start_draw_time = embassy_time::Instant::now(); + if let Page::Home = cur_page { + ui.add_centered( + HashLabel::new( + "Light Control App (Kolibri)", + sm.next(), + &hasher, + ) + // .smartstate(sm.next()) + .with_font(ascii::FONT_9X18_BOLD), + ); + } + + match cur_page { + Page::Home => { + for (i, lamp) in + appdata.lamps.iter().enumerate() + { + let mut break_loop = false; + ui.sub_ui(|ui| { + ui.style_mut().icon_color = if lamp.on { + Rgb565::CSS_GOLD + } else { + Rgb565::WHITE + }; + if ui + .add_horizontal( + IconButton::new(size32px::home::LightBulb) + .label(lamp.name.as_str()) + .smartstate(sm.next()), + ) + .clicked() + { + cur_page = Page::LampCtrl(i); + ui.clear_background().ok(); + sm.force_redraw_all(); + break_loop = true; + } + Ok(()) + }) + .ok(); + if break_loop { + break; + } + if i % 3 == 2 { + ui.new_row(); + } + } + } + Page::LampCtrl(lamp) => { + let lamp = &mut appdata.lamps[lamp]; + if ui + .add_horizontal( + IconButton::new(size32px::navigation::NavArrowLeft).smartstate(sm.next()), + ) + .clicked() + { + cur_page = Page::Home; + ui.clear_background().ok(); + sm.force_redraw_all(); + continue; + } + ui.add_horizontal(Spacer::new(Size::new( + 30, 0, + ))); + ui.add( + Label::new(lamp.name.as_str()) + .smartstate(sm.next()) + .with_font(ascii::FONT_9X18_BOLD), + ); + ui.add(Spacer::new(Size::new(0, 20))); + ui.add_centered( + Slider::new( + &mut lamp.brightness, + 0..=255, + ) + .width(300) + .label("Brightness") + .smartstate(sm.next()), + ); + ui.add(Spacer::new(Size::new(0, 10))); + ui.add_centered( + ToggleSwitch::new(&mut lamp.on) + .smartstate(sm.next()), + ); + ui.add_centered( + Label::new("Turn on/off") + .smartstate(sm.next()), + ); + } + } + + let end_time = embassy_time::Instant::now(); + let draw_time = display.get_time(); + let prep_time = start_draw_time - start_time; + let proc_time = end_time - start_draw_time; + let proc_time = + proc_time - min(draw_time, proc_time); + rtc.rwdt.feed(); + + if draw_time.as_micros() > 0 { + println!( + "draw time: {}.{:03}ms | prep time: {}.{:03}ms | proc time: {}.{:03}ms | total time: {}.{:03}ms", + draw_time.as_millis(), + draw_time.as_micros() % 100, + prep_time.as_millis(), + prep_time.as_micros() % 100, + proc_time.as_millis(), + proc_time.as_micros() % 100, + (draw_time + prep_time + proc_time).as_millis(), + (draw_time + prep_time + proc_time).as_micros() % 100, ); + } + display.reset_time(); + Timer::after(Duration::from_millis(17)).await; // 60 + // a second + } +} diff --git a/app/src/bin/microwave-ui.rs b/app/src/bin/microwave-ui.rs new file mode 100644 index 0000000..c07b242 --- /dev/null +++ b/app/src/bin/microwave-ui.rs @@ -0,0 +1,669 @@ +#![no_std] +#![no_main] + +use core::{cell::RefCell, cmp::min}; + +use display_interface_spi::SPIInterface; +use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; +use embassy_executor::Spawner; +use embassy_sync::{ + blocking_mutex::{raw::NoopRawMutex, NoopMutex}, + signal::Signal, +}; +use embassy_time::{Duration, Instant, Timer}; +use embedded_graphics::{ + mono_font::ascii, + pixelcolor::Rgb565, + prelude::{Point, RgbColor, Size, WebColors}, +}; +use embedded_graphics_profiler_display::ProfilerDisplay; +use esp_hal::{ + clock::ClockControl, + gpio::{ + GpioPin, + Input, + Io, + Level, + Output, + Pull, + NO_PIN, + }, + peripherals::{Peripherals, SPI2, SPI3}, + prelude::*, + rtc_cntl::Rtc, + spi::{master::Spi, FullDuplexMode, SpiMode}, + system::SystemControl, + timer::timg::TimerGroup, +}; +use esp_println::println; +use kolibri_embedded_gui::{ + iconbutton::IconButton, + icons::{size32px, size48px}, + label::{HashLabel, Hasher, Label}, + smartstate::SmartstateProvider, + spacer::Spacer, + style::medsize_rgb565_style, + ui::{Interaction, Ui}, +}; +use mipidsi::{ + models::ILI9341Rgb565, + options::{ColorOrder, Orientation, Rotation}, + Builder, +}; +use static_cell::StaticCell; +use xpt2046::Xpt2046; + +#[embassy_executor::task] +async fn touch_task( + touch_irq: GpioPin<36>, + spi: &'static mut NoopMutex< + RefCell>, + >, + touch_cs: GpioPin<33>, + touch_signal: &'static Signal< + NoopRawMutex, + Option, + >, +) -> ! { + let mut touch_driver = Xpt2046::new( + SpiDevice::new( + spi, + Output::new(touch_cs, Level::Low), + ), + Input::new(touch_irq, Pull::Up), + xpt2046::Orientation::LandscapeFlipped, + ); + touch_driver.set_num_samples(16); + touch_driver.init(&mut embassy_time::Delay).unwrap(); + + println!("touch task"); + + loop { + touch_driver + .run() + .expect("Running Touch driver failed"); + if touch_driver.is_touched() { + let point = touch_driver.get_touch_point(); + touch_signal.signal(Some(Point::new( + point.x + 25, + 240 - point.y, + ))); + } else { + touch_signal.signal(None); + } + Timer::after(Duration::from_millis(1)).await; // 100 + // a second + + // Your touch handling logic here + } +} + +struct AppData { + timer_start: Instant, + timer_set_duration: Duration, + timer_remaining_duration: Duration, + timer_running: bool, + timer_paused: bool, + wattage_level: u8, +} + +impl AppData { + fn new() -> Self { + Self { + timer_start: Instant::now(), + timer_set_duration: Duration::from_secs(10), + timer_remaining_duration: Duration::from_secs( + 10, + ), + timer_running: false, + timer_paused: false, + wattage_level: 5, + } + } + + fn set_timer_duration(&mut self, duration: Duration) { + self.timer_set_duration = duration; + } + + fn add_secs(&mut self, secs: u64) { + self.set_timer_duration( + self.timer_set_duration + .checked_add(Duration::from_secs(secs)) + .unwrap_or(Duration::from_secs(5999)) + .min(Duration::from_secs(5999)), + ); + } + + fn sub_secs(&mut self, secs: u64) { + self.set_timer_duration( + self.timer_set_duration + .checked_sub(Duration::from_secs(secs)) + .unwrap_or(Duration::from_secs(10)) + .max(Duration::from_secs(10)), + ); + } + + fn start_timer(&mut self) { + if !self.timer_paused { + self.timer_remaining_duration = + self.timer_set_duration; + } + + self.timer_start = Instant::now(); + self.timer_running = true; + self.timer_paused = false; + } + + fn pause_timer(&mut self) { + self.timer_paused = true; + self.timer_remaining_duration = self + .timer_remaining_duration + .checked_sub(self.timer_start.elapsed()) + .unwrap_or(Duration::from_secs(0)); + } + + fn reset_timer(&mut self) { + self.timer_start = Instant::now(); + self.timer_paused = false; + self.timer_running = false; + self.timer_remaining_duration = + self.timer_set_duration; + } + + fn remaining(&self) -> Duration { + if self.timer_stopped() { + self.timer_set_duration + } else if self.timer_paused() { + self.timer_remaining_duration + } else { + self.timer_remaining_duration + .checked_sub(self.timer_start.elapsed()) + .unwrap_or(Duration::from_secs(0)) + } + } + + fn timer_stopped(&self) -> bool { + !self.timer_running + } + + fn timer_paused(&self) -> bool { + self.timer_running && self.timer_paused + } + + fn timer_running(&self) -> bool { + self.timer_running && !self.timer_paused + } + + fn timer_finished(&self) -> bool { + self.timer_running + && self.remaining() == Duration::from_secs(0) + } + + fn set_wattage_level(&mut self, level: u8) { + assert!( + level < 6, + "wattage level cannot be over 6" + ); + self.wattage_level = level; + } + + fn get_wattage_level_str(&self) -> &'static str { + match self.wattage_level { + 0 => "180W", + 1 => "220W", + 2 => "360W", + 3 => "480W", + 4 => "620W", + 5 => "800W", + _ => unreachable!(), + } + } +} + +#[main] +async fn main(spawner: Spawner) { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let mut clocks = + ClockControl::boot_defaults(system.clock_control) + .freeze(); + + // Enable the RWDT watchdog timer: + let mut rtc = Rtc::new(peripherals.LPWR); + rtc.rwdt.set_timeout(2.secs()); + rtc.rwdt.enable(); + println!("RWDT watchdog enabled!"); + + // Initialize the SYSTIMER peripheral, and then Embassy: + let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + esp_hal_embassy::init(&clocks, timg0.timer0); + println!("Embassy initialized!"); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + // let mut led = Output::new(io.pins.gpio5, + // Level::High); Initialize SPI + let sclk = io.pins.gpio14; + let miso = io.pins.gpio12; + let mosi = io.pins.gpio13; + let cs = io.pins.gpio15; + let dc = io.pins.gpio2; + let mut backlight = + Output::new(io.pins.gpio21, Level::Low); + + // Note: RST is not initialized as it's set to -1 in the + // instructions + + // up to 80MHz is possible (even tho the display driver + // isn't supposed to be that fast) Dataseheet sais + // 10MHz, so we're gonna go with that + let mut spi = Spi::new( + peripherals.SPI2, + 10.MHz(), + SpiMode::Mode0, + &mut clocks, + ) + .with_pins( + Some(sclk), + Some(mosi), + Some(miso), + NO_PIN, + ); + + static DISP_SPI_BUS: StaticCell< + NoopMutex>>, + > = StaticCell::new(); + let spi_bus = NoopMutex::new(RefCell::new(spi)); + let spi_bus = DISP_SPI_BUS.init(spi_bus); + + let di = SPIInterface::new( + SpiDevice::new( + spi_bus, + Output::new(cs, Level::Low), + ), + Output::new(dc, Level::Low), + ); + + let display = Builder::new(ILI9341Rgb565, di) + .orientation(Orientation { + rotation: Rotation::Deg90, + mirrored: true, + }) + .color_order(ColorOrder::Bgr) + // .invert_colors(ColorInversion::Inverted) + .init(&mut embassy_time::Delay) + .unwrap(); + + let mut display = ProfilerDisplay::new(display); + + { + let mut ui = Ui::new_fullscreen( + &mut display, + medsize_rgb565_style(), + ); + ui.clear_background().ok(); + } + backlight.set_high(); + + // init touchscreen pins + let touch_irq = io.pins.gpio36; + let touch_mosi = io.pins.gpio32; + let touch_miso = io.pins.gpio39; + let touch_clk = io.pins.gpio25; + let touch_cs = io.pins.gpio33; + + // 2MHz is the MAX! DO NOT DECREASE! This is really + // important. + let mut touch_spi = Spi::new( + peripherals.SPI3, + 2.MHz(), + SpiMode::Mode0, + &mut clocks, + ) + .with_pins( + Some(touch_clk), + Some(touch_mosi), + Some(touch_miso), + NO_PIN, + ); + static TOUCH_SPI_BUS: StaticCell< + NoopMutex>>, + > = StaticCell::new(); + let touch_spi_bus = + NoopMutex::new(RefCell::new(touch_spi)); + let touch_spi_bus = TOUCH_SPI_BUS.init(touch_spi_bus); + + let touch_signal = Signal::new(); + static TOUCH_SIGNAL: StaticCell< + Signal>, + > = StaticCell::new(); + let touch_signal = &*TOUCH_SIGNAL.init(touch_signal); + + spawner + .spawn(touch_task( + touch_irq, + touch_spi_bus, + touch_cs, + touch_signal, + )) + .unwrap(); + + // TODO: Spawn some tasks + let _ = spawner; + + // variables + let mut appdata = AppData::new(); + let (mut prev_mins, mut prev_secs, mut prev_millis) = + (0, 0, 0); + let mut finished = false; + + // touchpoints + + let mut last_touch = None; + + static BUF_CELL: StaticCell<[Rgb565; 100 * 100]> = + StaticCell::new(); + let buf = BUF_CELL.init([Rgb565::BLACK; 100 * 100]); + + let mut textbuf = [0u8; 64]; + + // Periodically feed the RWDT watchdog timer when our + // tasks are not running: + let mut sm = SmartstateProvider::<20>::new(); + let hasher = Hasher::new(); + loop { + // SMART REDRAWING ENABLE / DISABLE + // sm.force_redraw_all(); + + let start_time = embassy_time::Instant::now(); + sm.restart_counter(); + let mut ui = Ui::new_fullscreen( + &mut display, + medsize_rgb565_style(), + ); + if let Some(touch) = touch_signal.try_take() { + let interact = match (touch, last_touch) { + (Some(point), Some(_)) => { + Interaction::Drag(point) + } + (Some(point), None) => { + Interaction::Click(point) + } + (None, Some(point)) => { + Interaction::Release(point) + } + (None, None) => Interaction::None, + }; + ui.interact(interact); + // println!("{:?}, {:?}, {:?}", last_touch, + // touch, interact); + + last_touch = touch; + } + + // BUFFER ENABLE/DISABLE + ui.set_buffer(buf); + + let start_draw_time = embassy_time::Instant::now(); + ui.sub_ui(|ui| { + ui.style_mut().default_font = + ascii::FONT_9X18_BOLD; + ui.add( + Label::new("Kolibri Microwave UI") + .smartstate(sm.next()), + ); + Ok(()) + }) + .ok(); + + let remaining = appdata.remaining(); + + ui.right_panel_ui(200, false, |ui| { + ui.add(Spacer::new(Size::new(0, 30))); + ui.add_horizontal(Spacer::new(Size::new( + 15, 0, + ))); + ui.sub_ui(|ui| { + ui.style_mut().default_font = + ascii::FONT_10X20; + // if remaining.as_secs() / 60 != prev_mins + // { sm.peek(). + // force_redraw(); + // prev_mins = remaining.as_secs() / 60; + // } + ui.add_horizontal(HashLabel::new( + &format_no_std::show( + &mut textbuf, + format_args!( + "{:02}", + remaining.as_secs() / 60 + ), + ) + .unwrap(), + sm.next(), + &hasher, + )); + ui.add_horizontal( + Label::new(":").smartstate(sm.next()), + ); + + ui.add_horizontal(HashLabel::new( + &format_no_std::show( + &mut textbuf, + format_args!( + "{:02}", + remaining.as_secs() % 60 + ), + ) + .unwrap(), + sm.next(), + &hasher, + )); + ui.add_horizontal( + Label::new(":").smartstate(sm.next()), + ); + + ui.add(HashLabel::new( + &format_no_std::show( + &mut textbuf, + format_args!( + "{:03}", + remaining.as_millis() % 1000 + ), + ) + .unwrap(), + sm.next(), + &hasher, + )); + Ok(()) + }) + .ok(); + + // ui.add_horizontal(Spacer::new(Size::new(65, + // 0))); + ui.sub_ui(|ui| { + if appdata.timer_running() { + ui.style_mut().icon_color = + Rgb565::CSS_LIGHT_GRAY; + } + if ui + .add_horizontal( + IconButton::new( + size32px::actions::AddCircle, + ) + .smartstate(sm.next()), + ) + .clicked() + { + if !(appdata.timer_running() + || appdata.timer_paused()) + { + appdata.add_secs(10); + } + } + ui.add_horizontal( + Label::new("+/- 10s") + .smartstate(sm.next()), + ); + if ui + .add( + IconButton::new( + size32px::actions::MinusCircle, + ) + .smartstate(sm.next()), + ) + .clicked() + { + if !(appdata.timer_running() + || appdata.timer_paused()) + { + appdata.sub_secs(10); + } + } + Ok(()) + }) + .ok(); + + ui.add_horizontal(Spacer::new(Size::new( + 15, 0, + ))); + if ui + .add_horizontal( + IconButton::new( + size48px::actions::Undo, + ) + .smartstate(sm.next()), + ) + .clicked() + { + appdata.reset_timer(); + sm.force_redraw_all(); + } + + if !finished && appdata.timer_finished() { + sm.peek().force_redraw(); + } + + if appdata.timer_finished() { + if ui + .add_horizontal( + IconButton::new( + size48px::actions::RemoveSquare, + ) + .smartstate(sm.next()), + ) + .clicked() + { + appdata.reset_timer(); + sm.force_redraw_all(); + } + } else if appdata.timer_running() { + if ui + .add_horizontal( + IconButton::new( + size48px::music::Pause, + ) + .smartstate(sm.next()), + ) + .clicked() + { + appdata.pause_timer(); + sm.force_redraw_all(); + } + } else { + if ui + .add_horizontal( + IconButton::new( + size48px::music::Play, + ) + .smartstate(sm.next()), + ) + .clicked() + { + appdata.start_timer(); + sm.force_redraw_all(); + } + } + Ok(()) + }) + .ok(); + ui.add(Spacer::new(Size::new(0, 20))); + ui.expand_row_height(65); + ui.right_panel_ui(80, false, |ui| { + if appdata.timer_running() { + ui.style_mut().icon_color = + Rgb565::CSS_LIGHT_GRAY; + } + if ui + .add( + IconButton::new( + size32px::actions::AddCircle, + ) + .smartstate(sm.next()), + ) + .clicked() + { + if !appdata.timer_running() { + appdata.set_wattage_level( + (appdata.wattage_level + 1).min(5), + ); + } + } + ui.expand_row_height(40); + // ui.add_horizontal(Spacer::new(Size::new(0, + // 40))); + ui.add( + HashLabel::new( + appdata.get_wattage_level_str(), + sm.next(), + &hasher, + ) + .with_font(ascii::FONT_10X20), + ); + if ui + .add( + IconButton::new( + size32px::actions::MinusCircle, + ) + .smartstate(sm.next()), + ) + .clicked() + { + if !appdata.timer_running() { + appdata.set_wattage_level( + appdata + .wattage_level + .saturating_sub(1), + ); + } + } + Ok(()) + }) + .ok(); + + finished = appdata.timer_finished(); + + let end_time = embassy_time::Instant::now(); + let draw_time = display.get_time(); + let prep_time = start_draw_time - start_time; + let proc_time = end_time - start_draw_time; + let proc_time = + proc_time - min(draw_time, proc_time); + rtc.rwdt.feed(); + + if draw_time.as_micros() > 0 { + println!( + "draw time: {}.{:03}ms | prep time: {}.{:03}ms | proc time: {}.{:03}ms | total time: {}.{:03}ms", + draw_time.as_millis(), + draw_time.as_micros() % 100, + prep_time.as_millis(), + prep_time.as_micros() % 100, + proc_time.as_millis(), + proc_time.as_micros() % 100, + (draw_time + prep_time + proc_time).as_millis(), + (draw_time + prep_time + proc_time).as_micros() % 100, ); + } + display.reset_time(); + Timer::after(Duration::from_millis(17)).await; // 60 + // a second + } +} diff --git a/app/src/bin/timer.rs b/app/src/bin/timer.rs new file mode 100644 index 0000000..58c2f6b --- /dev/null +++ b/app/src/bin/timer.rs @@ -0,0 +1,586 @@ +#![no_std] +#![no_main] + +use core::{cell::RefCell, cmp::min}; + +use display_interface_spi::SPIInterface; +use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; +use embassy_executor::Spawner; +use embassy_sync::{ + blocking_mutex::{raw::NoopRawMutex, NoopMutex}, + signal::Signal, +}; +use embassy_time::{Duration, Instant, Timer}; +use embedded_graphics::{ + mono_font::ascii, + pixelcolor::Rgb565, + prelude::{Point, RgbColor, Size, WebColors}, +}; +use embedded_graphics_profiler_display::ProfilerDisplay; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + gpio::{ + GpioPin, + Input, + Io, + Level, + Output, + Pull, + NO_PIN, + }, + peripherals::{Peripherals, SPI2, SPI3}, + prelude::*, + rtc_cntl::Rtc, + spi::{master::Spi, FullDuplexMode, SpiMode}, + system::SystemControl, + timer::timg::TimerGroup, +}; +use esp_println::println; +use kolibri_embedded_gui::{ + iconbutton::IconButton, + icons::{size32px, size48px}, + label::Label, + smartstate::SmartstateProvider, + spacer::Spacer, + style::medsize_rgb565_style, + ui::{Interaction, Ui}, +}; +use mipidsi::{ + models::ILI9341Rgb565, + options::{ColorOrder, Orientation, Rotation}, + Builder, +}; +use static_cell::StaticCell; +use xpt2046::Xpt2046; + +#[embassy_executor::task] +async fn touch_task( + touch_irq: GpioPin<36>, + spi: &'static mut NoopMutex< + RefCell>, + >, + touch_cs: GpioPin<33>, + touch_signal: &'static Signal< + NoopRawMutex, + Option, + >, +) -> ! { + let mut touch_driver = Xpt2046::new( + SpiDevice::new( + spi, + Output::new(touch_cs, Level::Low), + ), + Input::new(touch_irq, Pull::Up), + xpt2046::Orientation::LandscapeFlipped, + ); + touch_driver.set_num_samples(16); + touch_driver.init(&mut embassy_time::Delay).unwrap(); + + println!("touch task"); + + loop { + touch_driver + .run() + .expect("Running Touch driver failed"); + if touch_driver.is_touched() { + let point = touch_driver.get_touch_point(); + touch_signal.signal(Some(Point::new( + point.x + 25, + 240 - point.y, + ))); + } else { + touch_signal.signal(None); + } + Timer::after(Duration::from_millis(1)).await; // 100 + // a second + + // Your touch handling logic here + } +} + +struct AppData { + timer_start: Instant, + timer_set_duration: Duration, + timer_remaining_duration: Duration, + timer_running: bool, + timer_paused: bool, +} + +impl AppData { + fn new() -> Self { + Self { + timer_start: Instant::now(), + timer_set_duration: Duration::from_secs(10), + timer_remaining_duration: Duration::from_secs( + 10, + ), + timer_running: false, + timer_paused: false, + } + } + + fn set_timer_duration(&mut self, duration: Duration) { + self.timer_set_duration = duration; + } + + fn add_secs(&mut self, secs: u64) { + self.set_timer_duration( + self.timer_set_duration + .checked_add(Duration::from_secs(secs)) + .unwrap_or(Duration::from_secs(5999)) + .min(Duration::from_secs(5999)), + ); + } + + fn sub_secs(&mut self, secs: u64) { + self.set_timer_duration( + self.timer_set_duration + .checked_sub(Duration::from_secs(secs)) + .unwrap_or(Duration::from_secs(10)) + .max(Duration::from_secs(10)), + ); + } + + fn start_timer(&mut self) { + if !self.timer_paused { + self.timer_remaining_duration = + self.timer_set_duration; + } + + self.timer_start = Instant::now(); + self.timer_running = true; + self.timer_paused = false; + } + + fn pause_timer(&mut self) { + self.timer_paused = true; + self.timer_remaining_duration = self + .timer_remaining_duration + .checked_sub(self.timer_start.elapsed()) + .unwrap_or(Duration::from_secs(0)); + } + + fn reset_timer(&mut self) { + self.timer_start = Instant::now(); + self.timer_paused = false; + self.timer_running = false; + self.timer_remaining_duration = + self.timer_set_duration; + } + + fn remaining(&self) -> Duration { + if self.timer_stopped() { + self.timer_set_duration + } else if self.timer_paused() { + self.timer_remaining_duration + } else { + self.timer_remaining_duration + .checked_sub(self.timer_start.elapsed()) + .unwrap_or(Duration::from_secs(0)) + } + } + + fn timer_stopped(&self) -> bool { + !self.timer_running + } + + fn timer_paused(&self) -> bool { + self.timer_running && self.timer_paused + } + + fn timer_running(&self) -> bool { + self.timer_running && !self.timer_paused + } + + fn timer_finished(&self) -> bool { + self.timer_running + && self.remaining() == Duration::from_secs(0) + } +} + +#[main] +async fn main(spawner: Spawner) { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let mut clocks = + ClockControl::boot_defaults(system.clock_control) + .freeze(); + + // Enable the RWDT watchdog timer: + let mut rtc = Rtc::new(peripherals.LPWR); + rtc.rwdt.set_timeout(2.secs()); + rtc.rwdt.enable(); + println!("RWDT watchdog enabled!"); + + // Initialize the SYSTIMER peripheral, and then Embassy: + let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks); + esp_hal_embassy::init(&clocks, timg0.timer0); + println!("Embassy initialized!"); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + // let mut led = Output::new(io.pins.gpio5, + // Level::High); Initialize SPI + let sclk = io.pins.gpio14; + let miso = io.pins.gpio12; + let mosi = io.pins.gpio13; + let cs = io.pins.gpio15; + let dc = io.pins.gpio2; + let mut backlight = + Output::new(io.pins.gpio21, Level::Low); + + // Note: RST is not initialized as it's set to -1 in the + // instructions + + // up to 80MHz is possible (even tho the display driver + // isn't supposed to be that fast) Dataseheet sais + // 10MHz, so we're gonna go with that + let mut spi = Spi::new( + peripherals.SPI2, + 10.MHz(), + SpiMode::Mode0, + &mut clocks, + ) + .with_pins( + Some(sclk), + Some(mosi), + Some(miso), + NO_PIN, + ); + + static DISP_SPI_BUS: StaticCell< + NoopMutex>>, + > = StaticCell::new(); + let spi_bus = NoopMutex::new(RefCell::new(spi)); + let spi_bus = DISP_SPI_BUS.init(spi_bus); + + let di = SPIInterface::new( + SpiDevice::new( + spi_bus, + Output::new(cs, Level::Low), + ), + Output::new(dc, Level::Low), + ); + let display = Builder::new(ILI9341Rgb565, di) + .orientation(Orientation { + rotation: Rotation::Deg90, + mirrored: true, + }) + .color_order(ColorOrder::Bgr) + // .invert_colors(ColorInversion::Inverted) + .init(&mut embassy_time::Delay) + .unwrap(); + + let mut display = ProfilerDisplay::new(display); + + { + let mut ui = Ui::new_fullscreen( + &mut display, + medsize_rgb565_style(), + ); + ui.clear_background().ok(); + } + backlight.set_high(); + + // init touchscreen pins + let touch_irq = io.pins.gpio36; + let touch_mosi = io.pins.gpio32; + let touch_miso = io.pins.gpio39; + let touch_clk = io.pins.gpio25; + let touch_cs = io.pins.gpio33; + + // 2MHz is the MAX! DO NOT DECREASE! This is really + // important. + let mut touch_spi = Spi::new( + peripherals.SPI3, + 2.MHz(), + SpiMode::Mode0, + &mut clocks, + ) + .with_pins( + Some(touch_clk), + Some(touch_mosi), + Some(touch_miso), + NO_PIN, + ); + static TOUCH_SPI_BUS: StaticCell< + NoopMutex>>, + > = StaticCell::new(); + let touch_spi_bus = + NoopMutex::new(RefCell::new(touch_spi)); + let touch_spi_bus = TOUCH_SPI_BUS.init(touch_spi_bus); + + let touch_signal = Signal::new(); + static TOUCH_SIGNAL: StaticCell< + Signal>, + > = StaticCell::new(); + let touch_signal = &*TOUCH_SIGNAL.init(touch_signal); + + spawner + .spawn(touch_task( + touch_irq, + touch_spi_bus, + touch_cs, + touch_signal, + )) + .unwrap(); + + // TODO: Spawn some tasks + let _ = spawner; + + // variables + let mut appdata = AppData::new(); + let (mut prev_mins, mut prev_secs, mut prev_millis) = + (0, 0, 0); + let mut finished = false; + + // touchpoints + + let mut last_touch = None; + + static BUF_CELL: StaticCell<[Rgb565; 100 * 100]> = + StaticCell::new(); + let buf = BUF_CELL.init([Rgb565::BLACK; 100 * 100]); + + let mut textbuf = [0u8; 64]; + + // Periodically feed the RWDT watchdog timer when our + // tasks are not running: + let mut sm = SmartstateProvider::<20>::new(); + loop { + // SMART REDRAWING ENABLE / DISABLE + // sm.force_redraw_all(); + + let start_time = embassy_time::Instant::now(); + sm.restart_counter(); + let mut ui = Ui::new_fullscreen( + &mut display, + medsize_rgb565_style(), + ); + if let Some(touch) = touch_signal.try_take() { + let interact = match (touch, last_touch) { + (Some(point), Some(_)) => { + Interaction::Drag(point) + } + (Some(point), None) => { + Interaction::Click(point) + } + (None, Some(point)) => { + Interaction::Release(point) + } + (None, None) => Interaction::None, + }; + ui.interact(interact); + // println!("{:?}, {:?}, {:?}", last_touch, + // touch, interact); + + last_touch = touch; + } + + // BUFFER ENABLE/DISABLE + ui.set_buffer(buf); + + let start_draw_time = embassy_time::Instant::now(); + ui.sub_ui(|ui| { + ui.style_mut().default_font = + ascii::FONT_9X18_BOLD; + ui.add( + Label::new("Kolibri Timer App") + .smartstate(sm.next()), + ); + Ok(()) + }) + .ok(); + + let remaining = appdata.remaining(); + + ui.add(Spacer::new(Size::new(0, 60))); + ui.add_horizontal(Spacer::new(Size::new(80, 0))); + ui.sub_ui(|ui| { + ui.style_mut().default_font = ascii::FONT_10X20; + if remaining.as_secs() / 60 != prev_mins { + sm.peek().force_redraw(); + prev_mins = remaining.as_secs() / 60; + } + ui.add_horizontal( + Label::new( + &format_no_std::show( + &mut textbuf, + format_args!( + "{:02}", + remaining.as_secs() / 60 + ), + ) + .unwrap(), + ) + .smartstate(sm.next()), + ); + ui.add_horizontal( + Label::new(":").smartstate(sm.next()), + ); + + if remaining.as_secs() % 60 != prev_secs { + sm.peek().force_redraw(); + prev_secs = remaining.as_secs() % 60; + } + ui.add_horizontal( + Label::new( + &format_no_std::show( + &mut textbuf, + format_args!( + "{:02}", + remaining.as_secs() % 60 + ), + ) + .unwrap(), + ) + .smartstate(sm.next()), + ); + ui.add_horizontal( + Label::new(":").smartstate(sm.next()), + ); + + if remaining.as_millis() % 1000 != prev_millis { + sm.peek().force_redraw(); + prev_millis = remaining.as_millis() % 1000; + } + ui.add( + Label::new( + &format_no_std::show( + &mut textbuf, + format_args!( + "{:03}", + remaining.as_millis() % 1000 + ), + ) + .unwrap(), + ) + .smartstate(sm.next()), + ); + Ok(()) + }) + .ok(); + + ui.add_horizontal(Spacer::new(Size::new(65, 0))); + ui.sub_ui(|ui| { + if appdata.timer_running() { + ui.style_mut().icon_color = + Rgb565::CSS_LIGHT_GRAY; + } + if ui + .add_horizontal( + IconButton::new( + size32px::actions::AddCircle, + ) + .smartstate(sm.next()), + ) + .clicked() + { + if !(appdata.timer_running() + || appdata.timer_paused()) + { + appdata.add_secs(10); + } + } + ui.add_horizontal( + Label::new("+/- 10s").smartstate(sm.next()), + ); + if ui + .add( + IconButton::new( + size32px::actions::MinusCircle, + ) + .smartstate(sm.next()), + ) + .clicked() + { + if !(appdata.timer_running() + || appdata.timer_paused()) + { + appdata.sub_secs(10); + } + } + Ok(()) + }) + .ok(); + + ui.add_horizontal(Spacer::new(Size::new(80, 0))); + if ui + .add_horizontal( + IconButton::new(size48px::actions::Undo) + .smartstate(sm.next()), + ) + .clicked() + { + appdata.reset_timer(); + sm.force_redraw_all(); + } + + if !finished && appdata.timer_finished() { + sm.peek().force_redraw(); + } + + if appdata.timer_finished() { + if ui + .add_horizontal( + IconButton::new( + size48px::actions::RemoveSquare, + ) + .smartstate(sm.next()), + ) + .clicked() + { + appdata.reset_timer(); + sm.force_redraw_all(); + } + } else if appdata.timer_running() { + if ui + .add_horizontal( + IconButton::new(size48px::music::Pause) + .smartstate(sm.next()), + ) + .clicked() + { + appdata.pause_timer(); + sm.force_redraw_all(); + } + } else { + if ui + .add_horizontal( + IconButton::new(size48px::music::Play) + .smartstate(sm.next()), + ) + .clicked() + { + appdata.start_timer(); + sm.force_redraw_all(); + } + } + + finished = appdata.timer_finished(); + + let end_time = embassy_time::Instant::now(); + let draw_time = display.get_time(); + let prep_time = start_draw_time - start_time; + let proc_time = end_time - start_draw_time; + let proc_time = + proc_time - min(draw_time, proc_time); + rtc.rwdt.feed(); + + if draw_time.as_micros() > 0 { + println!( + "draw time: {}.{:03}ms | prep time: {}.{:03}ms | proc time: {}.{:03}ms | total time: {}.{:03}ms", + draw_time.as_millis(), + draw_time.as_micros() % 100, + prep_time.as_millis(), + prep_time.as_micros() % 100, + proc_time.as_millis(), + proc_time.as_micros() % 100, + (draw_time + prep_time + proc_time).as_millis(), + (draw_time + prep_time + proc_time).as_micros() % 100, ); + } + display.reset_time(); + Timer::after(Duration::from_millis(17)).await; // 60 + // a second + } +} diff --git a/lvgl-based/.cargo/config.toml b/lvgl-based/.cargo/config.toml index 1d6343d..3141385 100644 --- a/lvgl-based/.cargo/config.toml +++ b/lvgl-based/.cargo/config.toml @@ -4,14 +4,14 @@ target = "xtensa-esp32-espidf" [target.xtensa-esp32-espidf] linker = "ldproxy" # runner = "espflash --monitor" # Select this runner for espflash v1.x.x -runner = "espflash flash --monitor --baud 921600" # Select this runner for espflash v2.x.x +runner = "espflash flash --monitor" # --baud 921600" # Select this runner for espflash v2.x.x rustflags = [ # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110 "--cfg", "espidf_time64", # Added the following 2 entries so lvgl will build without getting string.h file not found "--sysroot", - "/home/ed/.rustup/toolchains/esp/xtensa-esp32s3-elf/esp-13.2.0_20230928/xtensa-esp-elf/xtensa-esp-elf/include", + "/home/yannik/.rustup/toolchains/esp/xtensa-esp32s3-elf/esp-13.2.0_20230928/xtensa-esp-elf/xtensa-esp-elf/include", ] [unstable] diff --git a/lvgl-based/rustfmt.toml b/lvgl-based/rustfmt.toml new file mode 100644 index 0000000..7dcd950 --- /dev/null +++ b/lvgl-based/rustfmt.toml @@ -0,0 +1,19 @@ +# Edition +edition = "2021" + +# Comments +format_code_in_doc_comments = true +normalize_comments = true +wrap_comments = true + +# Imports +group_imports = "StdExternalCrate" +imports_granularity = "Crate" +imports_layout = "HorizontalVertical" + +# Miscellaneous +enum_discrim_align_threshold = 25 +hex_literal_case = "Upper" + +# Length +max_width=60 diff --git a/lvgl-based/src/bin/light-control.rs b/lvgl-based/src/bin/light-control.rs index b470881..f7e6c66 100644 --- a/lvgl-based/src/bin/light-control.rs +++ b/lvgl-based/src/bin/light-control.rs @@ -9,14 +9,22 @@ use std::{ use cstr_core::CString; use display_interface_spi::SPIInterface; -use embedded_graphics_core::{draw_target::DrawTarget, prelude::Point}; +use embedded_graphics_core::{ + draw_target::DrawTarget, + prelude::Point, +}; use embedded_graphics_profiler_display::ProfilerDisplay; use esp_idf_hal::spi::SpiSingleDeviceDriver; use esp_idf_hal::{ delay::{self, Delay}, gpio::*, peripherals::Peripherals, - spi::{config::DriverConfig, Dma, SpiConfig, SpiDeviceDriver}, + spi::{ + config::DriverConfig, + Dma, + SpiConfig, + SpiDeviceDriver, + }, units::FromValueType, // for converting 26MHz to value }; use lvgl::{ @@ -42,16 +50,24 @@ use lvgl::{ Widget, }; use mipidsi::{ - models::ILI9486Rgb565, - options::{ColorInversion, ColorOrder, Orientation, Rotation}, + models::ILI9341Rgb565, + options::{ + ColorInversion, + ColorOrder, + Orientation, + Rotation, + }, Builder, }; use xpt2046::Xpt2046; fn lerp_fixed(start: u8, end: u8, t: u8, max_t: u8) -> u8 { - let (start, end, t, max_t) = (start as u16, end as u16, t as u16, max_t as u16); + let (start, end, t, max_t) = + (start as u16, end as u16, t as u16, max_t as u16); let t = t.min(max_t); - let result = start + ((end - start.min(end)) * t + (max_t / 2)) / max_t; + let result = start + + ((end - start.min(end)) * t + (max_t / 2)) + / max_t; result as u8 } @@ -65,7 +81,9 @@ struct Lamp { impl Lamp { pub fn new(name: &str) -> Self { Self { - name: heapless::String::from(heapless::String::from_str(name).unwrap()), + name: heapless::String::from( + heapless::String::from_str(name).unwrap(), + ), on: false, brightness: 255, } @@ -97,8 +115,8 @@ fn main() -> Result<(), LvError> { const VER_RES: u32 = 240; const LINES: u32 = 20; - // It is necessary to call this function once. Otherwise some patches to the - // runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 + // It is necessary to call this function once. Otherwise + // some patches to the runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 esp_idf_svc::sys::link_patches(); // Initialize lvgl @@ -127,7 +145,9 @@ fn main() -> Result<(), LvError> { Some(miso), // sdi Some(cs), // cs &DriverConfig::new().dma(Dma::Channel1(4096)), - &SpiConfig::new().write_only(true).baudrate(10.MHz().into()), + &SpiConfig::new() + .write_only(true) + .baudrate(10.MHz().into()), ) .unwrap(); @@ -140,8 +160,9 @@ fn main() -> Result<(), LvError> { bklt.set_high().unwrap(); // Configuration for M5Stack Core Development Kit V1.0 - // Puts display in landscape mode with the three buttons at the bottom of screen - // let mut m5stack_display = Builder::ili9342c_rgb565(di) + // Puts display in landscape mode with the three buttons + // at the bottom of screen let mut m5stack_display = + // Builder::ili9342c_rgb565(di) // .with_display_size(320, 240) // .with_color_order(ColorOrder::Bgr) // .with_orientation(Orientation::Portrait(false)) @@ -154,23 +175,25 @@ fn main() -> Result<(), LvError> { // .with_orientation(Orientation::Portrait(false)) // .with_color_order(ColorOrder::Bgr) // .with_invert_colors(true) - // .init(&mut Delay::new_default(), None::>) + // .init(&mut Delay::new_default(), + // None::>) // .unwrap(); - let raw_display = Builder::new(ILI9486Rgb565, di) + let raw_display = Builder::new(ILI9341Rgb565, di) .orientation(Orientation { rotation: Rotation::Deg90, mirrored: true, }) .color_order(ColorOrder::Bgr) - .invert_colors(ColorInversion::Inverted) + // .invert_colors(ColorInversion::Inverted) .init(&mut Delay::new_default()) .unwrap(); let mut raw_display = ProfilerDisplay::new(raw_display); - // Stack size value - 20,000 for 10 lines, 40,000 for 20 lines - // let (touch_send, touch_recv) = channel(); + // Stack size value - 20,000 for 10 lines, 40,000 for + // 20 lines let (touch_send, touch_recv) = + // channel(); let touch_irq = pins.gpio36; let touch_mosi = pins.gpio32; let touch_miso = pins.gpio39; @@ -185,7 +208,9 @@ fn main() -> Result<(), LvError> { Some(touch_miso), Some(touch_cs), &DriverConfig::new(), - &SpiConfig::new().write_only(true).baudrate(2.MHz().into()), + &SpiConfig::new() + .write_only(true) + .baudrate(2.MHz().into()), ) .unwrap(), PinDriver::input(touch_irq).unwrap(), @@ -317,7 +342,7 @@ fn main() -> Result<(), LvError> { cont.on_event(|_btn, event| { if let Event::Pressed = event { - println!("lamp {:?}", lamp.name); + // println!("lamp {:?}", lamp.name); // page = Page::LampCtrl(lamp); brightness_slider.set_value(lerp_fixed(0, 100, lamp.brightness, 255).into(), AnimationState::OFF); diff --git a/lvgl-based/src/bin/microwave-ui.rs b/lvgl-based/src/bin/microwave-ui.rs index aac343f..64c4376 100644 --- a/lvgl-based/src/bin/microwave-ui.rs +++ b/lvgl-based/src/bin/microwave-ui.rs @@ -9,14 +9,22 @@ use std::{ use cstr_core::CString; use display_interface_spi::SPIInterface; -use embedded_graphics_core::{draw_target::DrawTarget, prelude::Point}; +use embedded_graphics_core::{ + draw_target::DrawTarget, + prelude::Point, +}; use embedded_graphics_profiler_display::ProfilerDisplay; use esp_idf_hal::spi::SpiSingleDeviceDriver; use esp_idf_hal::{ delay::{self, Delay}, gpio::*, peripherals::Peripherals, - spi::{config::DriverConfig, Dma, SpiConfig, SpiDeviceDriver}, + spi::{ + config::DriverConfig, + Dma, + SpiConfig, + SpiDeviceDriver, + }, units::FromValueType, // for converting 26MHz to value }; use lvgl::{ @@ -42,25 +50,26 @@ use lvgl::{ Widget, }; use mipidsi::{ - models::ILI9486Rgb565, - options::{ColorInversion, ColorOrder, Orientation, Rotation}, + models::ILI9341Rgb565, + options::{ + ColorInversion, + ColorOrder, + Orientation, + Rotation, + }, Builder, }; use xpt2046::Xpt2046; fn lerp_fixed(start: u8, end: u8, t: u8, max_t: u8) -> u8 { - let (start, end, t, max_t) = (start as u16, end as u16, t as u16, max_t as u16); + let (start, end, t, max_t) = + (start as u16, end as u16, t as u16, max_t as u16); let t = t.min(max_t); - let result = start + ((end - start.min(end)) * t + (max_t / 2)) / max_t; + let result = start + + ((end - start.min(end)) * t + (max_t / 2)) + / max_t; result as u8 } - -#[derive(Debug, Clone)] -struct Lamp { - pub name: heapless::String<64>, - pub on: bool, - pub brightness: u8, -} struct AppData { timer_start: Instant, timer_set_duration: Duration, @@ -75,7 +84,9 @@ impl AppData { Self { timer_start: Instant::now(), timer_set_duration: Duration::from_secs(10), - timer_remaining_duration: Duration::from_secs(10), + timer_remaining_duration: Duration::from_secs( + 10, + ), timer_running: false, timer_paused: false, wattage_level: 5, @@ -106,7 +117,8 @@ impl AppData { fn start_timer(&mut self) { if !self.timer_paused { - self.timer_remaining_duration = self.timer_set_duration; + self.timer_remaining_duration = + self.timer_set_duration; } self.timer_start = Instant::now(); @@ -126,7 +138,8 @@ impl AppData { self.timer_start = Instant::now(); self.timer_paused = false; self.timer_running = false; - self.timer_remaining_duration = self.timer_set_duration; + self.timer_remaining_duration = + self.timer_set_duration; } fn remaining(&self) -> Duration { @@ -154,11 +167,15 @@ impl AppData { } fn timer_finished(&self) -> bool { - self.timer_running && self.remaining() == Duration::from_secs(0) + self.timer_running + && self.remaining() == Duration::from_secs(0) } fn set_wattage_level(&mut self, level: u8) { - assert!(level < 6, "wattage level cannot be over 6"); + assert!( + level < 6, + "wattage level cannot be over 6" + ); self.wattage_level = level; } @@ -180,8 +197,8 @@ fn main() -> Result<(), LvError> { const VER_RES: u32 = 240; const LINES: u32 = 20; - // It is necessary to call this function once. Otherwise some patches to the - // runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 + // It is necessary to call this function once. Otherwise + // some patches to the runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 esp_idf_svc::sys::link_patches(); // Initialize lvgl @@ -210,7 +227,9 @@ fn main() -> Result<(), LvError> { Some(miso), // sdi Some(cs), // cs &DriverConfig::new().dma(Dma::Channel1(4096)), - &SpiConfig::new().write_only(true).baudrate(10.MHz().into()), + &SpiConfig::new() + .write_only(true) + .baudrate(10.MHz().into()), ) .unwrap(); @@ -223,8 +242,9 @@ fn main() -> Result<(), LvError> { bklt.set_high().unwrap(); // Configuration for M5Stack Core Development Kit V1.0 - // Puts display in landscape mode with the three buttons at the bottom of screen - // let mut m5stack_display = Builder::ili9342c_rgb565(di) + // Puts display in landscape mode with the three buttons + // at the bottom of screen let mut m5stack_display = + // Builder::ili9342c_rgb565(di) // .with_display_size(320, 240) // .with_color_order(ColorOrder::Bgr) // .with_orientation(Orientation::Portrait(false)) @@ -237,23 +257,25 @@ fn main() -> Result<(), LvError> { // .with_orientation(Orientation::Portrait(false)) // .with_color_order(ColorOrder::Bgr) // .with_invert_colors(true) - // .init(&mut Delay::new_default(), None::>) + // .init(&mut Delay::new_default(), + // None::>) // .unwrap(); - let raw_display = Builder::new(ILI9486Rgb565, di) + let raw_display = Builder::new(ILI9341Rgb565, di) .orientation(Orientation { rotation: Rotation::Deg90, mirrored: true, }) .color_order(ColorOrder::Bgr) - .invert_colors(ColorInversion::Inverted) + // .invert_colors(ColorInversion::Inverted) .init(&mut Delay::new_default()) .unwrap(); let mut raw_display = ProfilerDisplay::new(raw_display); - // Stack size value - 20,000 for 10 lines, 40,000 for 20 lines - // let (touch_send, touch_recv) = channel(); + // Stack size value - 20,000 for 10 lines, 40,000 for + // 20 lines let (touch_send, touch_recv) = + // channel(); let touch_irq = pins.gpio36; let touch_mosi = pins.gpio32; let touch_miso = pins.gpio39; @@ -268,7 +290,9 @@ fn main() -> Result<(), LvError> { Some(touch_miso), Some(touch_cs), &DriverConfig::new(), - &SpiConfig::new().write_only(true).baudrate(2.MHz().into()), + &SpiConfig::new() + .write_only(true) + .baudrate(2.MHz().into()), ) .unwrap(), PinDriver::input(touch_irq).unwrap(), diff --git a/lvgl-based/src/bin/timer.rs b/lvgl-based/src/bin/timer.rs index 5e89642..5038251 100644 --- a/lvgl-based/src/bin/timer.rs +++ b/lvgl-based/src/bin/timer.rs @@ -8,14 +8,22 @@ use std::{ use cstr_core::CString; use display_interface_spi::SPIInterface; -use embedded_graphics_core::{draw_target::DrawTarget, prelude::Point}; +use embedded_graphics_core::{ + draw_target::DrawTarget, + prelude::Point, +}; use embedded_graphics_profiler_display::ProfilerDisplay; use esp_idf_hal::spi::SpiSingleDeviceDriver; use esp_idf_hal::{ delay::{self, Delay}, gpio::*, peripherals::Peripherals, - spi::{config::DriverConfig, Dma, SpiConfig, SpiDeviceDriver}, + spi::{ + config::DriverConfig, + Dma, + SpiConfig, + SpiDeviceDriver, + }, units::FromValueType, // for converting 26MHz to value }; use lvgl::{ @@ -38,8 +46,13 @@ use lvgl::{ Widget, }; use mipidsi::{ - models::ILI9486Rgb565, - options::{ColorInversion, ColorOrder, Orientation, Rotation}, + models::ILI9341Rgb565, + options::{ + ColorInversion, + ColorOrder, + Orientation, + Rotation, + }, Builder, }; use xpt2046::Xpt2046; @@ -57,7 +70,9 @@ impl AppData { Self { timer_start: Instant::now(), timer_set_duration: Duration::from_secs(10), - timer_remaining_duration: Duration::from_secs(10), + timer_remaining_duration: Duration::from_secs( + 10, + ), timer_running: false, timer_paused: false, } @@ -87,7 +102,8 @@ impl AppData { fn start_timer(&mut self) { if !self.timer_paused { - self.timer_remaining_duration = self.timer_set_duration; + self.timer_remaining_duration = + self.timer_set_duration; } self.timer_start = Instant::now(); @@ -107,7 +123,8 @@ impl AppData { self.timer_start = Instant::now(); self.timer_paused = false; self.timer_running = false; - self.timer_remaining_duration = self.timer_set_duration; + self.timer_remaining_duration = + self.timer_set_duration; } fn remaining(&self) -> Duration { @@ -135,7 +152,8 @@ impl AppData { } fn timer_finished(&self) -> bool { - self.timer_running && self.remaining() == Duration::from_secs(0) + self.timer_running + && self.remaining() == Duration::from_secs(0) } } @@ -144,8 +162,8 @@ fn main() -> Result<(), LvError> { const VER_RES: u32 = 240; const LINES: u32 = 20; - // It is necessary to call this function once. Otherwise some patches to the - // runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 + // It is necessary to call this function once. Otherwise + // some patches to the runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 esp_idf_svc::sys::link_patches(); // Initialize lvgl @@ -174,7 +192,9 @@ fn main() -> Result<(), LvError> { Some(miso), // sdi Some(cs), // cs &DriverConfig::new().dma(Dma::Channel1(4096)), - &SpiConfig::new().write_only(true).baudrate(10.MHz().into()), + &SpiConfig::new() + .write_only(true) + .baudrate(10.MHz().into()), ) .unwrap(); @@ -187,8 +207,9 @@ fn main() -> Result<(), LvError> { bklt.set_high().unwrap(); // Configuration for M5Stack Core Development Kit V1.0 - // Puts display in landscape mode with the three buttons at the bottom of screen - // let mut m5stack_display = Builder::ili9342c_rgb565(di) + // Puts display in landscape mode with the three buttons + // at the bottom of screen let mut m5stack_display = + // Builder::ili9342c_rgb565(di) // .with_display_size(320, 240) // .with_color_order(ColorOrder::Bgr) // .with_orientation(Orientation::Portrait(false)) @@ -201,23 +222,25 @@ fn main() -> Result<(), LvError> { // .with_orientation(Orientation::Portrait(false)) // .with_color_order(ColorOrder::Bgr) // .with_invert_colors(true) - // .init(&mut Delay::new_default(), None::>) + // .init(&mut Delay::new_default(), + // None::>) // .unwrap(); - let raw_display = Builder::new(ILI9486Rgb565, di) + let raw_display = Builder::new(ILI9341Rgb565, di) .orientation(Orientation { rotation: Rotation::Deg90, mirrored: true, }) .color_order(ColorOrder::Bgr) - .invert_colors(ColorInversion::Inverted) + // .invert_colors(ColorInversion::Inverted) .init(&mut Delay::new_default()) .unwrap(); let mut raw_display = ProfilerDisplay::new(raw_display); - // Stack size value - 20,000 for 10 lines, 40,000 for 20 lines - // let (touch_send, touch_recv) = channel(); + // Stack size value - 20,000 for 10 lines, 40,000 for + // 20 lines let (touch_send, touch_recv) = + // channel(); let touch_irq = pins.gpio36; let touch_mosi = pins.gpio32; let touch_miso = pins.gpio39; @@ -232,7 +255,9 @@ fn main() -> Result<(), LvError> { Some(touch_miso), Some(touch_cs), &DriverConfig::new(), - &SpiConfig::new().write_only(true).baudrate(2.MHz().into()), + &SpiConfig::new() + .write_only(true) + .baudrate(2.MHz().into()), ) .unwrap(), PinDriver::input(touch_irq).unwrap(), @@ -401,15 +426,18 @@ fn main() -> Result<(), LvError> { let mut was_finished = false; let mut last_rem_time: Duration = appdata.remaining() + Duration::from_millis(10); + let mut last_time = Instant::now(); loop { let start_time = Instant::now(); let rem_time = appdata.remaining(); - let val = CString::new(format!("{:02}:{:02}:{:03}", - rem_time.as_secs() / 60, - rem_time.as_secs() % 60, - rem_time.as_millis() % 1000)).unwrap(); + if rem_time != last_rem_time { + let val = CString::new(format!("{:02}:{:02}:{:03}", + rem_time.as_secs() / 60, + rem_time.as_secs() % 60, + rem_time.as_millis() % 1000)).unwrap(); - time.set_text(&val).unwrap(); + time.set_text(&val).unwrap(); + } last_rem_time = rem_time; @@ -426,7 +454,9 @@ fn main() -> Result<(), LvError> { // seconds // delay::FreeRtos::delay_ms(1); - lvgl::tick_inc(Instant::now().duration_since(start_time)); + let now_time = Instant::now(); + lvgl::tick_inc(now_time.duration_since(last_time)); + last_time = now_time; let end_time = Instant::now(); diff --git a/rustfmt.toml b/rustfmt.toml index 090d2e1..7dcd950 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -14,3 +14,6 @@ imports_layout = "HorizontalVertical" # Miscellaneous enum_discrim_align_threshold = 25 hex_literal_case = "Upper" + +# Length +max_width=60 diff --git a/slint-based/.cargo/config.toml b/slint-based/.cargo/config.toml index 6027dce..f6f41e1 100644 --- a/slint-based/.cargo/config.toml +++ b/slint-based/.cargo/config.toml @@ -10,7 +10,7 @@ # # runner = "espflash --monitor" # Select this runner for espflash v1.x.x -runner = "espflash flash --monitor --baud 921600" # Select this runner for espflash v2.x.x +# runner = "espflash flash --monitor --baud 921600" # Select this runner for espflash v2.x.x # Target specific options [target.thumbv6m-none-eabi] diff --git a/slint-based/Cargo.lock b/slint-based/Cargo.lock index 1732f3e..b215835 100644 --- a/slint-based/Cargo.lock +++ b/slint-based/Cargo.lock @@ -2340,9 +2340,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" [[package]] name = "jni" diff --git a/slint-based/Cargo.toml b/slint-based/Cargo.toml index 23cc17e..4aba21f 100644 --- a/slint-based/Cargo.toml +++ b/slint-based/Cargo.toml @@ -12,7 +12,15 @@ build = "build.rs" # [[bin]] # name = "main" +[features] +starter = [] +timer = [] +light-control = [] +microwave-ui = [] +simulator = ["slint/backend-winit"] + [[bin]] + name = "starter" [[bin]] @@ -60,19 +68,3 @@ embedded-hal-bus = "0.2.0" slint-build = { version = "1.8" } -[features] -simulator = ["slint/backend-winit"] -# pico = [ -# "slint/unsafe-single-threaded", -# "slint/libm", -# "cortex-m", -# "cortex-m-rt", -# "embedded-alloc", -# "embedded-hal", -# "fugit", -# "display-interface-spi", -# "embedded-graphics-core", -# "shared-bus", -# "panic-halt" -# ] - diff --git a/slint-based/build.rs b/slint-based/build.rs index 1f0828e..9d2fe0a 100644 --- a/slint-based/build.rs +++ b/slint-based/build.rs @@ -1,22 +1,26 @@ fn main() { + #[cfg(feature = "starter")] slint_build::compile_with_config( "ui/app-window.slint", slint_build::CompilerConfiguration::new() .embed_resources(slint_build::EmbedResourcesKind::EmbedForSoftwareRenderer), ) .unwrap(); + #[cfg(feature = "timer")] slint_build::compile_with_config( "ui/timer-app.slint", slint_build::CompilerConfiguration::new() .embed_resources(slint_build::EmbedResourcesKind::EmbedForSoftwareRenderer), ) .unwrap(); + #[cfg(feature = "light-control")] slint_build::compile_with_config( "ui/lights-app.slint", slint_build::CompilerConfiguration::new() .embed_resources(slint_build::EmbedResourcesKind::EmbedForSoftwareRenderer), ) .unwrap(); + #[cfg(feature = "microwave-ui")] slint_build::compile_with_config( "ui/microwave-ui.slint", slint_build::CompilerConfiguration::new() diff --git a/slint-based/rustfmt.toml b/slint-based/rustfmt.toml index 2a35f02..c685184 100644 --- a/slint-based/rustfmt.toml +++ b/slint-based/rustfmt.toml @@ -1 +1,2 @@ use_small_heuristics = "Max" +max_width = 60 diff --git a/slint-based/src/bin/light-control.rs b/slint-based/src/bin/light-control.rs index 8e2ded3..e71772e 100644 --- a/slint-based/src/bin/light-control.rs +++ b/slint-based/src/bin/light-control.rs @@ -26,7 +26,9 @@ use esp_hal::interrupt::Priority; use esp_hal::{self, prelude::*}; use esp_hal::{ clock::ClockControl, - gpio::{GpioPin, Input, Io, Level, Output, Pull, NO_PIN}, + gpio::{ + GpioPin, Input, Io, Level, Output, Pull, NO_PIN, + }, peripherals::{Peripherals, SPI2, SPI3}, prelude::*, rtc_cntl::Rtc, @@ -38,18 +40,22 @@ use esp_hal_embassy::InterruptExecutor; use esp_println::println; use mipidsi::{ models::ILI9486Rgb565, - options::{ColorInversion, ColorOrder, Orientation, Rotation}, + options::{ + ColorInversion, ColorOrder, Orientation, Rotation, + }, Builder, Display, }; use slint::platform::software_renderer::MinimalSoftwareWindow; -use slint::platform::{Platform, PointerEventButton, WindowEvent}; +use slint::platform::{ + Platform, PointerEventButton, WindowEvent, +}; use slint::private_unstable_api::re_exports::LogicalPoint; use slint::LogicalPosition; use static_cell::StaticCell; use xpt2046::Xpt2046; use esp_alloc as _; - +use mipidsi::models::ILI9341Rgb565; // slint::slint!{ export MyUI := Window {} } /* slint::include_modules!(); @@ -59,14 +65,18 @@ slint::include_modules!(); fn init_heap() { const HEAP_SIZE: usize = 64 * 1024; - static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit(); + static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = + MaybeUninit::uninit(); unsafe { - esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( - HEAP.as_mut_ptr() as *mut u8, - HEAP_SIZE, - esp_alloc::MemoryCapability::Internal.into(), - )); + esp_alloc::HEAP.add_region( + esp_alloc::HeapRegion::new( + HEAP.as_mut_ptr() as *mut u8, + HEAP_SIZE, + esp_alloc::MemoryCapability::Internal + .into(), + ), + ); } } @@ -78,20 +88,31 @@ async fn touch_task( Output<'static, GpioPin<33>>, &'static mut Delay, >, - touch_signal: &'static Signal>, + touch_signal: &'static Signal< + NoopRawMutex, + Option, + >, ) -> ! { - let mut touch_driver = - Xpt2046::new(spi, Input::new(touch_irq, Pull::Up), xpt2046::Orientation::LandscapeFlipped); + let mut touch_driver = Xpt2046::new( + spi, + Input::new(touch_irq, Pull::Up), + xpt2046::Orientation::LandscapeFlipped, + ); touch_driver.set_num_samples(16); touch_driver.init(&mut embassy_time::Delay).unwrap(); esp_println::println!("touch task"); loop { - touch_driver.run().expect("Running Touch driver failed"); + touch_driver + .run() + .expect("Running Touch driver failed"); if touch_driver.is_touched() { let point = touch_driver.get_touch_point(); - touch_signal.signal(Some(Point::new(point.x + 25, 240 - point.y))); + touch_signal.signal(Some(Point::new( + point.x + 25, + 240 - point.y, + ))); } else { touch_signal.signal(None); } @@ -112,16 +133,23 @@ struct CYDPlatform { impl Platform for CYDPlatform { fn create_window_adapter( &self, - ) -> Result, slint::PlatformError> { + ) -> Result< + Rc, + slint::PlatformError, + > { Ok(self.window.clone()) } fn duration_since_start(&self) -> core::time::Duration { - embassy_time::Instant::from_millis(0).elapsed().into() + embassy_time::Instant::from_millis(0) + .elapsed() + .into() } //noinspection DuplicatedCode - fn run_event_loop(&self) -> Result<(), slint::PlatformError> { + fn run_event_loop( + &self, + ) -> Result<(), slint::PlatformError> { todo!(); } } @@ -131,13 +159,17 @@ async fn main(spawner: Spawner) { init_heap(); let peripherals = Peripherals::take(); let system = SystemControl::new(peripherals.SYSTEM); - let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clocks = + ClockControl::boot_defaults(system.clock_control) + .freeze(); let mut rtc = Rtc::new(peripherals.LPWR); rtc.rwdt.disable(); - let mut timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer_group0 = + TimerGroup::new(peripherals.TIMG0, &clocks); timer_group0.wdt.disable(); - let mut timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut timer_group1 = + TimerGroup::new(peripherals.TIMG1, &clocks); timer_group1.wdt.disable(); esp_hal_embassy::init(&clocks, timer_group0.timer0); @@ -151,21 +183,34 @@ async fn main(spawner: Spawner) { let touch_cs = io.pins.gpio33; // 2MHz is the MAX! DO NOT DECREASE! This is really important. - let mut touch_spi = Spi::new(peripherals.SPI3, 2.MHz(), SpiMode::Mode0, &mut clocks).with_pins( + let mut touch_spi = Spi::new( + peripherals.SPI3, + 2.MHz(), + SpiMode::Mode0, + &mut clocks, + ) + .with_pins( Some(touch_clk), Some(touch_mosi), Some(touch_miso), NO_PIN, ); - static TOUCH_DELAY_STATICCELL: StaticCell = StaticCell::new(); + static TOUCH_DELAY_STATICCELL: StaticCell = + StaticCell::new(); let mut delay = TOUCH_DELAY_STATICCELL.init(Delay); - let touch_spi = - ExclusiveDevice::new(touch_spi, Output::new(touch_cs, Level::Low), delay).unwrap(); + let touch_spi = ExclusiveDevice::new( + touch_spi, + Output::new(touch_cs, Level::Low), + delay, + ) + .unwrap(); let touch_signal = Signal::new(); - static TOUCH_SIGNAL: StaticCell>> = StaticCell::new(); + static TOUCH_SIGNAL: StaticCell< + Signal>, + > = StaticCell::new(); let touch_signal = &*TOUCH_SIGNAL.init(touch_signal); // let sw_int = system.software_interrupt_control.software_interrupt2; @@ -174,7 +219,13 @@ async fn main(spawner: Spawner) { // let executor = InterruptExecutor::<2>::new(sw_int); // let executor = EXECUTOR.init(executor); - spawner.spawn(touch_task(touch_irq, touch_spi, touch_signal)).unwrap(); + spawner + .spawn(touch_task( + touch_irq, + touch_spi, + touch_signal, + )) + .unwrap(); // executor.start(Priority::Priority1); @@ -184,9 +235,16 @@ async fn main(spawner: Spawner) { let mosi = io.pins.gpio13; let cs = io.pins.gpio15; let dc = io.pins.gpio2; - let mut backlight = Output::new(io.pins.gpio21, Level::Low); + let mut backlight = + Output::new(io.pins.gpio21, Level::Low); - let mut spi = Spi::new(peripherals.SPI2, 10u32.MHz(), SpiMode::Mode0, &clocks).with_pins( + let mut spi = Spi::new( + peripherals.SPI2, + 10u32.MHz(), + SpiMode::Mode0, + &clocks, + ) + .with_pins( Some(sclk), Some(mosi), Some(miso), @@ -197,16 +255,26 @@ async fn main(spawner: Spawner) { // let spi_bus = NoopMutex::new(RefCell::new(spi)); // let spi_bus = DISP_SPI_BUS.init(spi_bus); - static spi_delay_staticcell: StaticCell = StaticCell::new(); + static spi_delay_staticcell: StaticCell = + StaticCell::new(); let mut delay = spi_delay_staticcell.init(Delay); - let spi = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), delay).unwrap(); + let spi = ExclusiveDevice::new( + spi, + Output::new(cs, Level::Low), + delay, + ) + .unwrap(); - let di = SPIInterface::new(spi, Output::new(dc, Level::Low)); + let di = + SPIInterface::new(spi, Output::new(dc, Level::Low)); - let display = Builder::new(ILI9486Rgb565, di) - .orientation(Orientation { rotation: Rotation::Deg90, mirrored: true }) + let display = Builder::new(ILI9341Rgb565, di) + .orientation(Orientation { + rotation: Rotation::Deg90, + mirrored: true, + }) .color_order(ColorOrder::Bgr) - .invert_colors(ColorInversion::Inverted) + // .invert_colors(ColorInversion::Inverted) .init(&mut embassy_time::Delay) .unwrap(); @@ -215,10 +283,14 @@ async fn main(spawner: Spawner) { backlight.set_high(); let size = display.bounding_box().size; - let size = slint::PhysicalSize::new(size.width as u32, size.height as u32); + // let size = slint::PhysicalSize::new(size.width as u32, size.height as u32); - let window = MinimalSoftwareWindow::new(Default::default()); - slint::platform::set_platform(Box::new(CYDPlatform { window: window.clone() })).unwrap(); + let window = + MinimalSoftwareWindow::new(Default::default()); + slint::platform::set_platform(Box::new(CYDPlatform { + window: window.clone(), + })) + .unwrap(); let ui = create_slint_app(); @@ -238,16 +310,28 @@ async fn main(spawner: Spawner) { let button = PointerEventButton::Left; let interact = match (touch, last_touch) { (Some(point), Some(_)) => { - Some(WindowEvent::PointerMoved { position: point_to_logical_pos(point) }) + Some(WindowEvent::PointerMoved { + position: point_to_logical_pos( + point, + ), + }) + } + (Some(point), None) => { + Some(WindowEvent::PointerPressed { + position: point_to_logical_pos( + point, + ), + button, + }) + } + (None, Some(point)) => { + Some(WindowEvent::PointerReleased { + position: point_to_logical_pos( + point, + ), + button, + }) } - (Some(point), None) => Some(WindowEvent::PointerPressed { - position: point_to_logical_pos(point), - button, - }), - (None, Some(point)) => Some(WindowEvent::PointerReleased { - position: point_to_logical_pos(point), - button, - }), (None, None) => None, }; if let Some(event) = interact { @@ -266,8 +350,6 @@ async fn main(spawner: Spawner) { renderer.render_by_line(&mut buffer_provider); }); - let button = PointerEventButton::Left; - if window.has_active_animations() { continue; } @@ -278,7 +360,8 @@ async fn main(spawner: Spawner) { let draw_time = display.get_time(); let prep_time = start_draw_time - start_time; let proc_time = end_time - start_draw_time; - let proc_time = proc_time - min(draw_time, proc_time); + let proc_time = + proc_time - min(draw_time, proc_time); rtc.rwdt.feed(); if draw_time.as_micros() > 0 { @@ -313,9 +396,11 @@ impl< E: core::fmt::Debug, DT: DrawTarget, // RST: OutputPin, - > slint::platform::software_renderer::LineBufferProvider for &mut DrawBuffer<'_, DT> + > slint::platform::software_renderer::LineBufferProvider + for &mut DrawBuffer<'_, DT> { - type TargetPixel = slint::platform::software_renderer::Rgb565Pixel; + type TargetPixel = + slint::platform::software_renderer::Rgb565Pixel; fn process_line( &mut self, diff --git a/slint-based/src/bin/microwave-ui.rs b/slint-based/src/bin/microwave-ui.rs index c750ff2..c7248b0 100644 --- a/slint-based/src/bin/microwave-ui.rs +++ b/slint-based/src/bin/microwave-ui.rs @@ -4,17 +4,12 @@ extern crate alloc; use alloc::boxed::Box; use alloc::rc::Rc; -use alloc::sync::Arc; use core::mem::MaybeUninit; -use core::{cell::RefCell, cmp::min, fmt}; +use core::{cell::RefCell, cmp::min}; use display_interface_spi::SPIInterface; -use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::{ - blocking_mutex::{raw::NoopRawMutex, NoopMutex}, - signal::Signal, -}; +use embassy_sync::signal::Signal; use embassy_time::Delay; use embassy_time::{Duration, Instant, Timer}; use embedded_graphics_core::pixelcolor::Rgb565; @@ -23,35 +18,33 @@ use embedded_graphics_core::primitives::Rectangle; use embedded_graphics_profiler_display::ProfilerDisplay; use embedded_hal::digital::OutputPin; use embedded_hal_bus::spi::ExclusiveDevice; -use esp_backtrace as _; -use esp_hal::interrupt::Priority; use esp_hal::{self, prelude::*}; use esp_hal::{ clock::ClockControl, - gpio::{GpioPin, Input, Io, Level, Output, Pull, NO_PIN}, - peripherals::{Peripherals, SPI2, SPI3}, + gpio::{ + GpioPin, Input, Io, Level, Output, Pull, NO_PIN, + }, + peripherals::{Peripherals, SPI3}, prelude::*, rtc_cntl::Rtc, spi::{master::Spi, FullDuplexMode, SpiMode}, system::SystemControl, timer::timg::TimerGroup, }; -use esp_hal_embassy::InterruptExecutor; use esp_println::println; use mipidsi::{ - models::ILI9486Rgb565, - options::{ColorInversion, ColorOrder, Orientation, Rotation}, - Builder, Display, + options::{ColorOrder, Orientation, Rotation}, + Builder, }; use slint::platform::software_renderer::MinimalSoftwareWindow; -use slint::platform::{Platform, PointerEventButton, WindowEvent}; -use slint::private_unstable_api::re_exports::LogicalPoint; +use slint::platform::{ + Platform, PointerEventButton, WindowEvent, +}; use slint::{format, LogicalPosition}; use static_cell::StaticCell; use xpt2046::Xpt2046; -use esp_alloc as _; - +use mipidsi::models::ILI9341Rgb565; // slint::slint!{ export MyUI := Window {} } /* slint::include_modules!(); @@ -61,14 +54,18 @@ slint::include_modules!(); fn init_heap() { const HEAP_SIZE: usize = 64 * 1024; - static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit(); + static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = + MaybeUninit::uninit(); unsafe { - esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( - HEAP.as_mut_ptr() as *mut u8, - HEAP_SIZE, - esp_alloc::MemoryCapability::Internal.into(), - )); + esp_alloc::HEAP.add_region( + esp_alloc::HeapRegion::new( + HEAP.as_mut_ptr() as *mut u8, + HEAP_SIZE, + esp_alloc::MemoryCapability::Internal + .into(), + ), + ); } } @@ -80,20 +77,31 @@ async fn touch_task( Output<'static, GpioPin<33>>, &'static mut Delay, >, - touch_signal: &'static Signal>, + touch_signal: &'static Signal< + CriticalSectionRawMutex, + Option, + >, ) -> ! { - let mut touch_driver = - Xpt2046::new(spi, Input::new(touch_irq, Pull::Up), xpt2046::Orientation::LandscapeFlipped); + let mut touch_driver = Xpt2046::new( + spi, + Input::new(touch_irq, Pull::Up), + xpt2046::Orientation::LandscapeFlipped, + ); touch_driver.set_num_samples(1); touch_driver.init(&mut embassy_time::Delay).unwrap(); esp_println::println!("touch task"); loop { - touch_driver.run().expect("Running Touch driver failed"); + touch_driver + .run() + .expect("Running Touch driver failed"); if touch_driver.is_touched() { let point = touch_driver.get_touch_point(); - touch_signal.signal(Some(Point::new(point.x + 25, 240 - point.y))); + touch_signal.signal(Some(Point::new( + point.x + 25, + 240 - point.y, + ))); } else { touch_signal.signal(None); } @@ -114,16 +122,23 @@ struct CYDPlatform { impl Platform for CYDPlatform { fn create_window_adapter( &self, - ) -> Result, slint::PlatformError> { + ) -> Result< + Rc, + slint::PlatformError, + > { Ok(self.window.clone()) } fn duration_since_start(&self) -> core::time::Duration { - embassy_time::Instant::from_millis(0).elapsed().into() + embassy_time::Instant::from_millis(0) + .elapsed() + .into() } //noinspection DuplicatedCode - fn run_event_loop(&self) -> Result<(), slint::PlatformError> { + fn run_event_loop( + &self, + ) -> Result<(), slint::PlatformError> { todo!(); } } @@ -141,7 +156,9 @@ impl AppData { Self { timer_start: Instant::now(), timer_set_duration: Duration::from_secs(10), - timer_remaining_duration: Duration::from_secs(10), + timer_remaining_duration: Duration::from_secs( + 10, + ), timer_running: false, timer_paused: false, } @@ -171,7 +188,8 @@ impl AppData { fn start_timer(&mut self) { if !self.timer_paused { - self.timer_remaining_duration = self.timer_set_duration; + self.timer_remaining_duration = + self.timer_set_duration; } self.timer_start = Instant::now(); @@ -191,7 +209,8 @@ impl AppData { self.timer_start = Instant::now(); self.timer_paused = false; self.timer_running = false; - self.timer_remaining_duration = self.timer_set_duration; + self.timer_remaining_duration = + self.timer_set_duration; } fn remaining(&self) -> Duration { @@ -219,7 +238,8 @@ impl AppData { } fn timer_finished(&self) -> bool { - self.timer_running && self.remaining() == Duration::from_secs(0) + self.timer_running + && self.remaining() == Duration::from_secs(0) } } @@ -228,13 +248,17 @@ async fn main(spawner: Spawner) { init_heap(); let peripherals = Peripherals::take(); let system = SystemControl::new(peripherals.SYSTEM); - let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clocks = + ClockControl::boot_defaults(system.clock_control) + .freeze(); let mut rtc = Rtc::new(peripherals.LPWR); rtc.rwdt.disable(); - let mut timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer_group0 = + TimerGroup::new(peripherals.TIMG0, &clocks); timer_group0.wdt.disable(); - let mut timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut timer_group1 = + TimerGroup::new(peripherals.TIMG1, &clocks); timer_group1.wdt.disable(); esp_hal_embassy::init(&clocks, timer_group0.timer0); @@ -248,22 +272,34 @@ async fn main(spawner: Spawner) { let touch_cs = io.pins.gpio33; // 2MHz is the MAX! DO NOT DECREASE! This is really important. - let mut touch_spi = Spi::new(peripherals.SPI3, 2.MHz(), SpiMode::Mode0, &mut clocks).with_pins( + let mut touch_spi = Spi::new( + peripherals.SPI3, + 2.MHz(), + SpiMode::Mode0, + &mut clocks, + ) + .with_pins( Some(touch_clk), Some(touch_mosi), Some(touch_miso), NO_PIN, ); - static TOUCH_DELAY_STATICCELL: StaticCell = StaticCell::new(); + static TOUCH_DELAY_STATICCELL: StaticCell = + StaticCell::new(); let mut delay = TOUCH_DELAY_STATICCELL.init(Delay); - let touch_spi = - ExclusiveDevice::new(touch_spi, Output::new(touch_cs, Level::Low), delay).unwrap(); + let touch_spi = ExclusiveDevice::new( + touch_spi, + Output::new(touch_cs, Level::Low), + delay, + ) + .unwrap(); let touch_signal = Signal::new(); - static TOUCH_SIGNAL: StaticCell>> = - StaticCell::new(); + static TOUCH_SIGNAL: StaticCell< + Signal>, + > = StaticCell::new(); let touch_signal = &*TOUCH_SIGNAL.init(touch_signal); // let sw_int = system.software_interrupt_control.software_interrupt2; @@ -279,7 +315,13 @@ async fn main(spawner: Spawner) { // executor // .start(Priority::Priority2) - spawner.spawn(touch_task(touch_irq, touch_spi, touch_signal)).unwrap(); + spawner + .spawn(touch_task( + touch_irq, + touch_spi, + touch_signal, + )) + .unwrap(); // Display setup let sclk = io.pins.gpio14; @@ -287,9 +329,16 @@ async fn main(spawner: Spawner) { let mosi = io.pins.gpio13; let cs = io.pins.gpio15; let dc = io.pins.gpio2; - let mut backlight = Output::new(io.pins.gpio21, Level::Low); + let mut backlight = + Output::new(io.pins.gpio21, Level::Low); - let mut spi = Spi::new(peripherals.SPI2, 10u32.MHz(), SpiMode::Mode0, &clocks).with_pins( + let mut spi = Spi::new( + peripherals.SPI2, + 10u32.MHz(), + SpiMode::Mode0, + &clocks, + ) + .with_pins( Some(sclk), Some(mosi), Some(miso), @@ -300,16 +349,26 @@ async fn main(spawner: Spawner) { // let spi_bus = NoopMutex::new(RefCell::new(spi)); // let spi_bus = DISP_SPI_BUS.init(spi_bus); - static SPI_DELAY_STATICCELL: StaticCell = StaticCell::new(); + static SPI_DELAY_STATICCELL: StaticCell = + StaticCell::new(); let mut delay = SPI_DELAY_STATICCELL.init(Delay); - let spi = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), delay).unwrap(); + let spi = ExclusiveDevice::new( + spi, + Output::new(cs, Level::Low), + delay, + ) + .unwrap(); - let di = SPIInterface::new(spi, Output::new(dc, Level::Low)); + let di = + SPIInterface::new(spi, Output::new(dc, Level::Low)); - let display = Builder::new(ILI9486Rgb565, di) - .orientation(Orientation { rotation: Rotation::Deg90, mirrored: true }) + let display = Builder::new(ILI9341Rgb565, di) + .orientation(Orientation { + rotation: Rotation::Deg90, + mirrored: true, + }) .color_order(ColorOrder::Bgr) - .invert_colors(ColorInversion::Inverted) + // .invert_colors(ColorInversion::Inverted) .init(&mut embassy_time::Delay) .unwrap(); @@ -318,10 +377,14 @@ async fn main(spawner: Spawner) { backlight.set_high(); let size = display.bounding_box().size; - let size = slint::PhysicalSize::new(size.width as u32, size.height as u32); + // let size = slint::PhysicalSize::new(size.width as u32, size.height as u32); - let window = MinimalSoftwareWindow::new(Default::default()); - slint::platform::set_platform(Box::new(CYDPlatform { window: window.clone() })).unwrap(); + let window = + MinimalSoftwareWindow::new(Default::default()); + slint::platform::set_platform(Box::new(CYDPlatform { + window: window.clone(), + })) + .unwrap(); let ui = create_slint_app(); @@ -337,15 +400,25 @@ async fn main(spawner: Spawner) { let mut appdata = Rc::new(RefCell::new(AppData::new())); let mut cl_appdata = appdata.clone(); - ui.on_add_10s(move || cl_appdata.borrow_mut().add_secs(10)); + ui.on_add_10s(move || { + cl_appdata.borrow_mut().add_secs(10) + }); let mut cl_appdata = appdata.clone(); - ui.on_sub_10s(move || cl_appdata.borrow_mut().sub_secs(10)); + ui.on_sub_10s(move || { + cl_appdata.borrow_mut().sub_secs(10) + }); let mut cl_appdata = appdata.clone(); - ui.on_start_timer(move || cl_appdata.borrow_mut().start_timer()); + ui.on_start_timer(move || { + cl_appdata.borrow_mut().start_timer() + }); let mut cl_appdata = appdata.clone(); - ui.on_stop_timer(move || cl_appdata.borrow_mut().pause_timer()); + ui.on_stop_timer(move || { + cl_appdata.borrow_mut().pause_timer() + }); let mut cl_appdata = appdata.clone(); - ui.on_reset_timer(move || cl_appdata.borrow_mut().reset_timer()); + ui.on_reset_timer(move || { + cl_appdata.borrow_mut().reset_timer() + }); loop { let start_time = Instant::now(); @@ -354,16 +427,28 @@ async fn main(spawner: Spawner) { let button = PointerEventButton::Left; let interact = match (touch, last_touch) { (Some(point), Some(_)) => { - Some(WindowEvent::PointerMoved { position: point_to_logical_pos(point) }) + Some(WindowEvent::PointerMoved { + position: point_to_logical_pos( + point, + ), + }) + } + (Some(point), None) => { + Some(WindowEvent::PointerPressed { + position: point_to_logical_pos( + point, + ), + button, + }) + } + (None, Some(point)) => { + Some(WindowEvent::PointerReleased { + position: point_to_logical_pos( + point, + ), + button, + }) } - (Some(point), None) => Some(WindowEvent::PointerPressed { - position: point_to_logical_pos(point), - button, - }), - (None, Some(point)) => Some(WindowEvent::PointerReleased { - position: point_to_logical_pos(point), - button, - }), (None, None) => None, }; if let Some(event) = interact { @@ -377,11 +462,18 @@ async fn main(spawner: Spawner) { { let appdata = appdata.borrow(); let remaining = appdata.remaining(); - ui.set_show_reset_timer(appdata.timer_finished()); - ui.set_show_start_timer( - !appdata.timer_running() || appdata.timer_paused() && !appdata.timer_finished(), + ui.set_show_reset_timer( + appdata.timer_finished(), + ); + ui.set_show_start_timer( + !appdata.timer_running() + || appdata.timer_paused() + && !appdata.timer_finished(), + ); + ui.set_show_stop_timer( + appdata.timer_running() + && !appdata.timer_finished(), ); - ui.set_show_stop_timer(appdata.timer_running() && !appdata.timer_finished()); ui.set_timer_text(format!( "{:02}:{:02}:{:03}", @@ -400,8 +492,6 @@ async fn main(spawner: Spawner) { renderer.render_by_line(&mut buffer_provider); }); - let button = PointerEventButton::Left; - if window.has_active_animations() { continue; } @@ -412,7 +502,8 @@ async fn main(spawner: Spawner) { let draw_time = display.get_time(); let prep_time = start_draw_time - start_time; let proc_time = end_time - start_draw_time; - let proc_time = proc_time - min(draw_time, proc_time); + let proc_time = + proc_time - min(draw_time, proc_time); rtc.rwdt.feed(); if draw_time.as_micros() > 0 { @@ -456,9 +547,11 @@ impl< E: core::fmt::Debug, DT: DrawTarget, // RST: OutputPin, - > slint::platform::software_renderer::LineBufferProvider for &mut DrawBuffer<'_, DT> + > slint::platform::software_renderer::LineBufferProvider + for &mut DrawBuffer<'_, DT> { - type TargetPixel = slint::platform::software_renderer::Rgb565Pixel; + type TargetPixel = + slint::platform::software_renderer::Rgb565Pixel; fn process_line( &mut self, diff --git a/slint-based/src/bin/timer.rs b/slint-based/src/bin/timer.rs index 89d49d4..9eed632 100644 --- a/slint-based/src/bin/timer.rs +++ b/slint-based/src/bin/timer.rs @@ -4,17 +4,12 @@ extern crate alloc; use alloc::boxed::Box; use alloc::rc::Rc; -use alloc::sync::Arc; use core::mem::MaybeUninit; -use core::{cell::RefCell, cmp::min, fmt}; +use core::{cell::RefCell, cmp::min}; use display_interface_spi::SPIInterface; -use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::{ - blocking_mutex::{raw::NoopRawMutex, NoopMutex}, - signal::Signal, -}; +use embassy_sync::signal::Signal; use embassy_time::Delay; use embassy_time::{Duration, Instant, Timer}; use embedded_graphics_core::pixelcolor::Rgb565; @@ -23,35 +18,33 @@ use embedded_graphics_core::primitives::Rectangle; use embedded_graphics_profiler_display::ProfilerDisplay; use embedded_hal::digital::OutputPin; use embedded_hal_bus::spi::ExclusiveDevice; -use esp_backtrace as _; -use esp_hal::interrupt::Priority; use esp_hal::{self, prelude::*}; use esp_hal::{ clock::ClockControl, - gpio::{GpioPin, Input, Io, Level, Output, Pull, NO_PIN}, - peripherals::{Peripherals, SPI2, SPI3}, + gpio::{ + GpioPin, Input, Io, Level, Output, Pull, NO_PIN, + }, + peripherals::{Peripherals, SPI3}, prelude::*, rtc_cntl::Rtc, spi::{master::Spi, FullDuplexMode, SpiMode}, system::SystemControl, timer::timg::TimerGroup, }; -use esp_hal_embassy::InterruptExecutor; use esp_println::println; use mipidsi::{ - models::ILI9486Rgb565, - options::{ColorInversion, ColorOrder, Orientation, Rotation}, - Builder, Display, + options::{ColorOrder, Orientation, Rotation}, + Builder, }; use slint::platform::software_renderer::MinimalSoftwareWindow; -use slint::platform::{Platform, PointerEventButton, WindowEvent}; -use slint::private_unstable_api::re_exports::LogicalPoint; +use slint::platform::{ + Platform, PointerEventButton, WindowEvent, +}; use slint::{format, LogicalPosition}; use static_cell::StaticCell; use xpt2046::Xpt2046; -use esp_alloc as _; - +use mipidsi::models::ILI9341Rgb565; // slint::slint!{ export MyUI := Window {} } /* slint::include_modules!(); @@ -61,14 +54,18 @@ slint::include_modules!(); fn init_heap() { const HEAP_SIZE: usize = 32 * 1024; - static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit(); + static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = + MaybeUninit::uninit(); unsafe { - esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( - HEAP.as_mut_ptr() as *mut u8, - HEAP_SIZE, - esp_alloc::MemoryCapability::Internal.into(), - )); + esp_alloc::HEAP.add_region( + esp_alloc::HeapRegion::new( + HEAP.as_mut_ptr() as *mut u8, + HEAP_SIZE, + esp_alloc::MemoryCapability::Internal + .into(), + ), + ); } } @@ -80,20 +77,31 @@ async fn touch_task( Output<'static, GpioPin<33>>, &'static mut Delay, >, - touch_signal: &'static Signal>, + touch_signal: &'static Signal< + CriticalSectionRawMutex, + Option, + >, ) -> ! { - let mut touch_driver = - Xpt2046::new(spi, Input::new(touch_irq, Pull::Up), xpt2046::Orientation::LandscapeFlipped); + let mut touch_driver = Xpt2046::new( + spi, + Input::new(touch_irq, Pull::Up), + xpt2046::Orientation::LandscapeFlipped, + ); touch_driver.set_num_samples(1); touch_driver.init(&mut embassy_time::Delay).unwrap(); esp_println::println!("touch task"); loop { - touch_driver.run().expect("Running Touch driver failed"); + touch_driver + .run() + .expect("Running Touch driver failed"); if touch_driver.is_touched() { let point = touch_driver.get_touch_point(); - touch_signal.signal(Some(Point::new(point.x + 25, 240 - point.y))); + touch_signal.signal(Some(Point::new( + point.x + 25, + 240 - point.y, + ))); } else { touch_signal.signal(None); } @@ -114,16 +122,23 @@ struct CYDPlatform { impl Platform for CYDPlatform { fn create_window_adapter( &self, - ) -> Result, slint::PlatformError> { + ) -> Result< + Rc, + slint::PlatformError, + > { Ok(self.window.clone()) } fn duration_since_start(&self) -> core::time::Duration { - embassy_time::Instant::from_millis(0).elapsed().into() + embassy_time::Instant::from_millis(0) + .elapsed() + .into() } //noinspection DuplicatedCode - fn run_event_loop(&self) -> Result<(), slint::PlatformError> { + fn run_event_loop( + &self, + ) -> Result<(), slint::PlatformError> { todo!(); } } @@ -141,7 +156,9 @@ impl AppData { Self { timer_start: Instant::now(), timer_set_duration: Duration::from_secs(10), - timer_remaining_duration: Duration::from_secs(10), + timer_remaining_duration: Duration::from_secs( + 10, + ), timer_running: false, timer_paused: false, } @@ -171,7 +188,8 @@ impl AppData { fn start_timer(&mut self) { if !self.timer_paused { - self.timer_remaining_duration = self.timer_set_duration; + self.timer_remaining_duration = + self.timer_set_duration; } self.timer_start = Instant::now(); @@ -191,7 +209,8 @@ impl AppData { self.timer_start = Instant::now(); self.timer_paused = false; self.timer_running = false; - self.timer_remaining_duration = self.timer_set_duration; + self.timer_remaining_duration = + self.timer_set_duration; } fn remaining(&self) -> Duration { @@ -219,7 +238,8 @@ impl AppData { } fn timer_finished(&self) -> bool { - self.timer_running && self.remaining() == Duration::from_secs(0) + self.timer_running + && self.remaining() == Duration::from_secs(0) } } @@ -228,13 +248,17 @@ async fn main(spawner: Spawner) { init_heap(); let peripherals = Peripherals::take(); let system = SystemControl::new(peripherals.SYSTEM); - let mut clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clocks = + ClockControl::boot_defaults(system.clock_control) + .freeze(); let mut rtc = Rtc::new(peripherals.LPWR); rtc.rwdt.disable(); - let mut timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut timer_group0 = + TimerGroup::new(peripherals.TIMG0, &clocks); timer_group0.wdt.disable(); - let mut timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut timer_group1 = + TimerGroup::new(peripherals.TIMG1, &clocks); timer_group1.wdt.disable(); esp_hal_embassy::init(&clocks, timer_group0.timer0); @@ -248,22 +272,34 @@ async fn main(spawner: Spawner) { let touch_cs = io.pins.gpio33; // 2MHz is the MAX! DO NOT DECREASE! This is really important. - let mut touch_spi = Spi::new(peripherals.SPI3, 2.MHz(), SpiMode::Mode0, &mut clocks).with_pins( + let mut touch_spi = Spi::new( + peripherals.SPI3, + 2.MHz(), + SpiMode::Mode0, + &mut clocks, + ) + .with_pins( Some(touch_clk), Some(touch_mosi), Some(touch_miso), NO_PIN, ); - static TOUCH_DELAY_STATICCELL: StaticCell = StaticCell::new(); + static TOUCH_DELAY_STATICCELL: StaticCell = + StaticCell::new(); let mut delay = TOUCH_DELAY_STATICCELL.init(Delay); - let touch_spi = - ExclusiveDevice::new(touch_spi, Output::new(touch_cs, Level::Low), delay).unwrap(); + let touch_spi = ExclusiveDevice::new( + touch_spi, + Output::new(touch_cs, Level::Low), + delay, + ) + .unwrap(); let touch_signal = Signal::new(); - static TOUCH_SIGNAL: StaticCell>> = - StaticCell::new(); + static TOUCH_SIGNAL: StaticCell< + Signal>, + > = StaticCell::new(); let touch_signal = &*TOUCH_SIGNAL.init(touch_signal); // let sw_int = system.software_interrupt_control.software_interrupt2; @@ -279,7 +315,13 @@ async fn main(spawner: Spawner) { // executor // .start(Priority::Priority2) - spawner.spawn(touch_task(touch_irq, touch_spi, touch_signal)).unwrap(); + spawner + .spawn(touch_task( + touch_irq, + touch_spi, + touch_signal, + )) + .unwrap(); // Display setup let sclk = io.pins.gpio14; @@ -287,9 +329,16 @@ async fn main(spawner: Spawner) { let mosi = io.pins.gpio13; let cs = io.pins.gpio15; let dc = io.pins.gpio2; - let mut backlight = Output::new(io.pins.gpio21, Level::Low); + let mut backlight = + Output::new(io.pins.gpio21, Level::Low); - let mut spi = Spi::new(peripherals.SPI2, 10u32.MHz(), SpiMode::Mode0, &clocks).with_pins( + let mut spi = Spi::new( + peripherals.SPI2, + 10u32.MHz(), + SpiMode::Mode0, + &clocks, + ) + .with_pins( Some(sclk), Some(mosi), Some(miso), @@ -300,16 +349,26 @@ async fn main(spawner: Spawner) { // let spi_bus = NoopMutex::new(RefCell::new(spi)); // let spi_bus = DISP_SPI_BUS.init(spi_bus); - static SPI_DELAY_STATICCELL: StaticCell = StaticCell::new(); + static SPI_DELAY_STATICCELL: StaticCell = + StaticCell::new(); let mut delay = SPI_DELAY_STATICCELL.init(Delay); - let spi = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), delay).unwrap(); + let spi = ExclusiveDevice::new( + spi, + Output::new(cs, Level::Low), + delay, + ) + .unwrap(); - let di = SPIInterface::new(spi, Output::new(dc, Level::Low)); + let di = + SPIInterface::new(spi, Output::new(dc, Level::Low)); - let display = Builder::new(ILI9486Rgb565, di) - .orientation(Orientation { rotation: Rotation::Deg90, mirrored: true }) + let display = Builder::new(ILI9341Rgb565, di) + .orientation(Orientation { + rotation: Rotation::Deg90, + mirrored: true, + }) .color_order(ColorOrder::Bgr) - .invert_colors(ColorInversion::Inverted) + // .invert_colors(ColorInversion::Inverted) .init(&mut embassy_time::Delay) .unwrap(); @@ -318,10 +377,14 @@ async fn main(spawner: Spawner) { backlight.set_high(); let size = display.bounding_box().size; - let size = slint::PhysicalSize::new(size.width as u32, size.height as u32); + // let size = slint::PhysicalSize::new(size.width as u32, size.height as u32); - let window = MinimalSoftwareWindow::new(Default::default()); - slint::platform::set_platform(Box::new(CYDPlatform { window: window.clone() })).unwrap(); + let window = + MinimalSoftwareWindow::new(Default::default()); + slint::platform::set_platform(Box::new(CYDPlatform { + window: window.clone(), + })) + .unwrap(); let ui = create_slint_app(); @@ -337,15 +400,25 @@ async fn main(spawner: Spawner) { let mut appdata = Rc::new(RefCell::new(AppData::new())); let mut cl_appdata = appdata.clone(); - ui.on_add_10s(move || cl_appdata.borrow_mut().add_secs(10)); + ui.on_add_10s(move || { + cl_appdata.borrow_mut().add_secs(10) + }); let mut cl_appdata = appdata.clone(); - ui.on_sub_10s(move || cl_appdata.borrow_mut().sub_secs(10)); + ui.on_sub_10s(move || { + cl_appdata.borrow_mut().sub_secs(10) + }); let mut cl_appdata = appdata.clone(); - ui.on_start_timer(move || cl_appdata.borrow_mut().start_timer()); + ui.on_start_timer(move || { + cl_appdata.borrow_mut().start_timer() + }); let mut cl_appdata = appdata.clone(); - ui.on_stop_timer(move || cl_appdata.borrow_mut().pause_timer()); + ui.on_stop_timer(move || { + cl_appdata.borrow_mut().pause_timer() + }); let mut cl_appdata = appdata.clone(); - ui.on_reset_timer(move || cl_appdata.borrow_mut().reset_timer()); + ui.on_reset_timer(move || { + cl_appdata.borrow_mut().reset_timer() + }); loop { let start_time = Instant::now(); @@ -354,16 +427,28 @@ async fn main(spawner: Spawner) { let button = PointerEventButton::Left; let interact = match (touch, last_touch) { (Some(point), Some(_)) => { - Some(WindowEvent::PointerMoved { position: point_to_logical_pos(point) }) + Some(WindowEvent::PointerMoved { + position: point_to_logical_pos( + point, + ), + }) + } + (Some(point), None) => { + Some(WindowEvent::PointerPressed { + position: point_to_logical_pos( + point, + ), + button, + }) + } + (None, Some(point)) => { + Some(WindowEvent::PointerReleased { + position: point_to_logical_pos( + point, + ), + button, + }) } - (Some(point), None) => Some(WindowEvent::PointerPressed { - position: point_to_logical_pos(point), - button, - }), - (None, Some(point)) => Some(WindowEvent::PointerReleased { - position: point_to_logical_pos(point), - button, - }), (None, None) => None, }; if let Some(event) = interact { @@ -377,11 +462,18 @@ async fn main(spawner: Spawner) { { let appdata = appdata.borrow(); let remaining = appdata.remaining(); - ui.set_show_reset_timer(appdata.timer_finished()); - ui.set_show_start_timer( - !appdata.timer_running() || appdata.timer_paused() && !appdata.timer_finished(), + ui.set_show_reset_timer( + appdata.timer_finished(), + ); + ui.set_show_start_timer( + !appdata.timer_running() + || appdata.timer_paused() + && !appdata.timer_finished(), + ); + ui.set_show_stop_timer( + appdata.timer_running() + && !appdata.timer_finished(), ); - ui.set_show_stop_timer(appdata.timer_running() && !appdata.timer_finished()); ui.set_timer_text(format!( "{:02}:{:02}:{:03}", @@ -400,8 +492,6 @@ async fn main(spawner: Spawner) { renderer.render_by_line(&mut buffer_provider); }); - let button = PointerEventButton::Left; - if window.has_active_animations() { continue; } @@ -412,7 +502,8 @@ async fn main(spawner: Spawner) { let draw_time = display.get_time(); let prep_time = start_draw_time - start_time; let proc_time = end_time - start_draw_time; - let proc_time = proc_time - min(draw_time, proc_time); + let proc_time = + proc_time - min(draw_time, proc_time); rtc.rwdt.feed(); if draw_time.as_micros() > 0 { @@ -456,9 +547,11 @@ impl< E: core::fmt::Debug, DT: DrawTarget, // RST: OutputPin, - > slint::platform::software_renderer::LineBufferProvider for &mut DrawBuffer<'_, DT> + > slint::platform::software_renderer::LineBufferProvider + for &mut DrawBuffer<'_, DT> { - type TargetPixel = slint::platform::software_renderer::Rgb565Pixel; + type TargetPixel = + slint::platform::software_renderer::Rgb565Pixel; fn process_line( &mut self, diff --git a/slint/ui.slint b/slint/ui.slint new file mode 100644 index 0000000..e69de29