Compare commits
5 Commits
60b394f24d
...
a5cbb1e759
| Author | SHA1 | Date | |
|---|---|---|---|
| a5cbb1e759 | |||
| efe2539ceb | |||
| ff60803f88 | |||
| 6b54fb570a | |||
| 86ccf24333 |
383
Cargo.lock
generated
@@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.95"
|
||||
@@ -14,12 +20,6 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.9"
|
||||
@@ -43,9 +43,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
@@ -59,6 +59,12 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -80,6 +86,15 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d8a42181e0652c2997ae4d217f25b63c5337a52fd2279736e97b832fa0a3cff"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
@@ -123,15 +138,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||
checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
|
||||
|
||||
[[package]]
|
||||
name = "delegate"
|
||||
version = "0.13.1"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc2323e10c92e1cf4d86e11538512e6dc03ceb586842970b6332af3d4046a046"
|
||||
checksum = "297806318ef30ad066b15792a8372858020ae3ca2e414ee6c2133b1eb9e9e945"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -149,9 +164,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "embassy-embedded-hal"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5794414bc20e0d750f145bc0e82366b19dd078e9e075e8331fb8dd069a1cb6a2"
|
||||
checksum = "41fea5ef5bed4d3468dfd44f5c9fa4cda8f54c86d4fb4ae683eacf9d39e2ea12"
|
||||
dependencies = [
|
||||
"embassy-futures",
|
||||
"embassy-sync",
|
||||
@@ -166,9 +181,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "embassy-executor"
|
||||
version = "0.6.3"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f64f84599b0f4296b92a4b6ac2109bc02340094bda47b9766c5f9ec6a318ebf8"
|
||||
checksum = "90327bcc66333a507f89ecc4e2d911b265c45f5c9bc241f98eee076752d35ac6"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"document-features",
|
||||
@@ -195,9 +210,9 @@ checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067"
|
||||
|
||||
[[package]]
|
||||
name = "embassy-net"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f9f2979069031c153e41075a43074c36a64492e598780b27944a605f829d23"
|
||||
checksum = "ed041cc19a603d657124fddefdcbe5ef8bd60e77d972793ebb57de93394f5949"
|
||||
dependencies = [
|
||||
"document-features",
|
||||
"embassy-net-driver",
|
||||
@@ -207,7 +222,7 @@ dependencies = [
|
||||
"embedded-nal-async",
|
||||
"heapless",
|
||||
"managed",
|
||||
"smoltcp 0.12.0",
|
||||
"smoltcp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -218,9 +233,9 @@ checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d"
|
||||
|
||||
[[package]]
|
||||
name = "embassy-sync"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3899a6e39fa3f54bf8aaf00979f9f9c0145a522f7244810533abbb748be6ce82"
|
||||
checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"critical-section",
|
||||
@@ -232,36 +247,39 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "embassy-time"
|
||||
version = "0.3.2"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "158080d48f824fad101d7b2fae2d83ac39e3f7a6fa01811034f7ab8ffc6e7309"
|
||||
checksum = "f820157f198ada183ad62e0a66f554c610cdcd1a9f27d4b316358103ced7a1f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"critical-section",
|
||||
"document-features",
|
||||
"embassy-time-driver",
|
||||
"embassy-time-queue-driver",
|
||||
"embassy-time-queue-utils",
|
||||
"embedded-hal 0.2.7",
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"futures-util",
|
||||
"heapless",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-time-driver"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e0c214077aaa9206958b16411c157961fb7990d4ea628120a78d1a5a28aed24"
|
||||
checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba"
|
||||
dependencies = [
|
||||
"document-features",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-time-queue-driver"
|
||||
name = "embassy-time-queue-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1177859559ebf42cd24ae7ba8fe6ee707489b01d0bf471f8827b7b12dcb0bc0"
|
||||
checksum = "dc55c748d16908a65b166d09ce976575fb8852cf60ccd06174092b41064d8f83"
|
||||
dependencies = [
|
||||
"embassy-executor",
|
||||
"heapless",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-can"
|
||||
@@ -397,20 +415,22 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "esp-alloc"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5162b6e9cd99cc974ec2742da3cf8433e7332853ae10fec922d174ff20fa4c5f"
|
||||
checksum = "408c0d4c7f51efb256af18641047b0d83b58a485be9edf5a559da796abef0b63"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"critical-section",
|
||||
"document-features",
|
||||
"enumset",
|
||||
"linked_list_allocator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esp-backtrace"
|
||||
version = "0.14.2"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7660d85e3e7b0e113aaeeffb1a155e64a09a5035d4104031875acdba4cb68e"
|
||||
checksum = "3c83ca63fd02ca40644ae91ae63362e3a6e7f53458f6c1356decf892343d2418"
|
||||
dependencies = [
|
||||
"esp-build",
|
||||
"esp-println",
|
||||
@@ -418,9 +438,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "esp-build"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b94a4b8d74e7cc7baabcca5b2277b41877e039ad9cd49959d48ef94dac7eab4b"
|
||||
checksum = "8aa1c8f9954c9506699cf1ca10a2adcc226ff10b6ae3cb9e875cf2c6a0b9a372"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -429,22 +449,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "esp-config"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7584e4cd1dac06201fd92fff1c84b396be5458ac4d93e9457e7a89b1b42c60e"
|
||||
checksum = "bd428a3b4b2975772f24eabea123d45cf6a5e28020396ed5dcb331ee3a904046"
|
||||
dependencies = [
|
||||
"document-features",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esp-hal"
|
||||
version = "0.22.0"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a5605e1518d63f7bf9fbd9885e61d2896060d2e4f28954736bdd74da911b676"
|
||||
checksum = "a573175c540fd1d21a9cf07b0dee286b5a8f4cfde4b35da0f4f4657de7942c45"
|
||||
dependencies = [
|
||||
"basic-toml",
|
||||
"bitfield",
|
||||
"bitflags 2.6.0",
|
||||
"bitflags 2.8.0",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
@@ -455,7 +475,6 @@ dependencies = [
|
||||
"embassy-futures",
|
||||
"embassy-sync",
|
||||
"embedded-can",
|
||||
"embedded-hal 0.2.7",
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-async",
|
||||
"embedded-hal-nb",
|
||||
@@ -469,29 +488,34 @@ dependencies = [
|
||||
"esp-riscv-rt",
|
||||
"esp32c3",
|
||||
"fugit",
|
||||
"instability",
|
||||
"log",
|
||||
"nb 1.1.0",
|
||||
"paste",
|
||||
"portable-atomic",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"riscv",
|
||||
"serde",
|
||||
"strum",
|
||||
"ufmt-write",
|
||||
"void",
|
||||
"xtensa-lx",
|
||||
"xtensa-lx-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esp-hal-embassy"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7d0f2537ea2ff9bea26a1c8bfe43ad580d4c89febf27189653a9cf95f1f7961"
|
||||
checksum = "4cea15ef146c7689fede0c3a7d765e07b1eb22ef462f1203a137dacc615b031a"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"document-features",
|
||||
"embassy-executor",
|
||||
"embassy-sync",
|
||||
"embassy-time",
|
||||
"embassy-time-driver",
|
||||
"embassy-time-queue-utils",
|
||||
"esp-build",
|
||||
"esp-config",
|
||||
"esp-hal",
|
||||
@@ -503,9 +527,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "esp-hal-procmacros"
|
||||
version = "0.15.0"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69a9a8706b7d1182b56335d196e70eeb04e2b70f4b8db96432898bd3c2bdb91e"
|
||||
checksum = "e4a3297005c2b31cd00e2ba50037edc9bddf99da3afe1c97a2d1b0165a312eab"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"document-features",
|
||||
@@ -519,9 +543,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "esp-metadata"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9972bbb21dcafe430b87f92efc7a788978a2d17cf8f572d104beeb48602482a"
|
||||
checksum = "fb15c17e50f4cccb0d88305c19eae2d5533d750f0a05b6a05f1c99864974758e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"basic-toml",
|
||||
@@ -531,9 +555,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "esp-println"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee38e87bc7e303c299047c0e9bcd0f8ccca7c7e70d1fd78bbb565db14f33beb6"
|
||||
checksum = "645e54eb592ca0a3d60213b1695e2a5fc0b51ca6d693c08d6983857224a629ed"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"esp-build",
|
||||
@@ -554,9 +578,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "esp-wifi"
|
||||
version = "0.11.0"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d36018303cd60a30184c6f605a921038c08ff34afc870e22070ea08a6280a2"
|
||||
checksum = "321b112db6629693fae49644b1ba28e8b917b58829533e9cdd0400183af1877d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"critical-section",
|
||||
@@ -580,15 +604,15 @@ dependencies = [
|
||||
"num-traits",
|
||||
"portable-atomic",
|
||||
"portable_atomic_enum",
|
||||
"rand_core",
|
||||
"smoltcp 0.11.0",
|
||||
"rand_core 0.6.4",
|
||||
"smoltcp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esp-wifi-sys"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7730d6093853119088bbb793e3383801965ebe8fcf91e0817781d94628aaf718"
|
||||
checksum = "c6b5438361891c431970194a733415006fb3d00b6eb70b3dcb66fd58f04d9b39"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"log",
|
||||
@@ -596,14 +620,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "esp32c3"
|
||||
version = "0.26.0"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d1750382dc49fa333ee6b1ba96f8c540038b9666f5a233dc1221c98e3236118"
|
||||
checksum = "61655d48e45039dfac5ae769581fb50ea7f61dea3227b4b744a1a900d03fbbd4"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@@ -695,15 +729,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.0"
|
||||
name = "include_file_compress"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
checksum = "fd6823f4ab0232f685fd0320c7210f39a5264bae4b223e45c1910e04d795df75"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"flate2",
|
||||
"quote",
|
||||
"syn",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "instability"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"indoc",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
@@ -739,9 +805,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
version = "0.4.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||
|
||||
[[package]]
|
||||
name = "managed"
|
||||
@@ -755,6 +821,7 @@ version = "0.26.0"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"maud_macros",
|
||||
"picoserve",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -777,18 +844,21 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minijinja"
|
||||
version = "2.5.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c37e1b517d1dcd0e51dc36c4567b9d5a29262b3ec8da6cb5d35e27a8fb529b5"
|
||||
checksum = "cff7b8df5e85e30b87c2b0b3f58ba3a87b68e133738bf512a7713769326dbca9"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mutex-trait"
|
||||
version = "0.2.0"
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a"
|
||||
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
@@ -805,6 +875,15 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.4.2"
|
||||
@@ -833,9 +912,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "picoserve"
|
||||
version = "0.13.3"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4086ade38e021293486610bdfd6a520d24e942264c535b297366e234f2feb9be"
|
||||
checksum = "6d2c9a99cfe7a070728554f1d42f62067937ce30f9b057a6b507e0cc14fe96e9"
|
||||
dependencies = [
|
||||
"const-sha1",
|
||||
"data-encoding",
|
||||
@@ -845,16 +924,30 @@ dependencies = [
|
||||
"futures-util",
|
||||
"heapless",
|
||||
"lhash",
|
||||
"log",
|
||||
"picoserve_derive",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde-json-core",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "picoserve_derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62ba0d83906d0357fedd23de7c5e3a5235342c248cc1d954d43d5e7b455c375c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@@ -945,9 +1038,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -973,6 +1066,15 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "riscv"
|
||||
version = "0.12.1"
|
||||
@@ -1022,9 +1124,9 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -1066,19 +1168,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"heapless",
|
||||
"managed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.12.0"
|
||||
@@ -1092,6 +1181,16 @@ dependencies = [
|
||||
"managed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sntpc"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78f778a0f82b3cf5d75f858eceee38e84d5292f1d03415e88cc4ec45ca6ba8a2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"embassy-net",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@@ -1137,9 +1236,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.93"
|
||||
version = "2.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
|
||||
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1156,10 +1255,50 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@@ -1178,9 +1317,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.22"
|
||||
version = "0.22.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@@ -1197,9 +1336,9 @@ checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
@@ -1220,9 +1359,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "wallas-embassy"
|
||||
name = "wallas-esp32c3"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"critical-section",
|
||||
"embassy-executor",
|
||||
"embassy-net",
|
||||
@@ -1237,11 +1377,15 @@ dependencies = [
|
||||
"esp-println",
|
||||
"esp-wifi",
|
||||
"heapless",
|
||||
"include_file_compress",
|
||||
"log",
|
||||
"maud",
|
||||
"nom",
|
||||
"picoserve",
|
||||
"rand_core",
|
||||
"smoltcp 0.11.0",
|
||||
"rand_core 0.9.0",
|
||||
"serde",
|
||||
"smoltcp",
|
||||
"sntpc",
|
||||
"static_cell",
|
||||
]
|
||||
|
||||
@@ -1329,31 +1473,30 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.20"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||
checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xtensa-lx"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e758f94e1a1f71758f94052a2766dcb12604998eb372b8b2e30576e3ab1ba1e6"
|
||||
checksum = "51cbb46c78cfd284c9378070ab90bae9d14d38b3766cb853a97c0a137f736d5b"
|
||||
dependencies = [
|
||||
"bare-metal",
|
||||
"mutex-trait",
|
||||
"critical-section",
|
||||
"document-features",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xtensa-lx-rt"
|
||||
version = "0.17.2"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c0307d03dadbf95633942e13901984f2059df4c963367348168cbd21c962669"
|
||||
checksum = "689c2ef159d9cd4fc9503603e9999968a84a30db9bde0f0f880d0cceea0190a9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bare-metal",
|
||||
"document-features",
|
||||
"enum-as-inner",
|
||||
"minijinja",
|
||||
@@ -1376,3 +1519,23 @@ dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
38
Cargo.toml
@@ -4,25 +4,25 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
esp-backtrace = { version = "0.14.2", features = [
|
||||
esp-backtrace = { version = "0.15.0", features = [
|
||||
"esp32c3",
|
||||
"exception-handler",
|
||||
"panic-handler",
|
||||
"println",
|
||||
]}
|
||||
|
||||
esp-hal = { version = "0.22.0", features = [
|
||||
esp-hal = { version = "0.23.1", features = [
|
||||
"esp32c3",
|
||||
] }
|
||||
esp-println = { version = "0.12.0", features = ["esp32c3", "log"] }
|
||||
esp-println = { version = "0.13.0", features = ["esp32c3", "log"] }
|
||||
log = { version = "0.4.21" }
|
||||
esp-alloc = { version = "0.5.0" }
|
||||
esp-alloc = { version = "0.6.0" }
|
||||
embedded-io = "0.6.1"
|
||||
|
||||
embedded-io-async = "0.6.1"
|
||||
embassy-net = { version = "0.5.0", features = [ "tcp", "udp", "dhcpv4", "medium-ethernet"] }
|
||||
embassy-net = { version = "0.6.0", features = [ "tcp", "udp", "dhcpv4", "dns", "medium-ethernet"] }
|
||||
|
||||
esp-wifi = { version = "0.11.0", default-features=false, features = [
|
||||
esp-wifi = { version = "0.12.0", default-features=false, features = [
|
||||
"esp32c3",
|
||||
"utils",
|
||||
"wifi",
|
||||
@@ -30,12 +30,12 @@ esp-wifi = { version = "0.11.0", default-features=false, features = [
|
||||
"log",
|
||||
] }
|
||||
embassy-sync = "0.6.1"
|
||||
rand_core = "0.6.4"
|
||||
rand_core = "0.9.0"
|
||||
nom = { version = "8", default-features = false, features = [ "alloc" ] }
|
||||
heapless = { version = "0.8.0", default-features = false }
|
||||
smoltcp = { version = "0.11.0", default-features = false, features = [
|
||||
smoltcp = { version = "0.12.0", default-features = false, features = [
|
||||
"medium-ethernet",
|
||||
"proto-dhcpv4",
|
||||
"proto-igmp",
|
||||
"proto-ipv4",
|
||||
"socket-dhcpv4",
|
||||
"socket-icmp",
|
||||
@@ -43,17 +43,23 @@ smoltcp = { version = "0.11.0", default-features = false, features = [
|
||||
"socket-tcp",
|
||||
"socket-udp",
|
||||
] }
|
||||
embassy-executor = { version = "0.6.0", features = [
|
||||
"task-arena-size-40960"
|
||||
embassy-executor = { version = "0.7.0", features = [
|
||||
"task-arena-size-163840"
|
||||
] }
|
||||
embassy-time = { version = "0.3.1", features = ["generic-queue-8"] }
|
||||
esp-hal-embassy = { version = "0.5.0", features = ["esp32c3"] }
|
||||
embassy-time = { version = "0.4.0", features = ["generic-queue-8"] }
|
||||
esp-hal-embassy = { version = "0.6.0", features = ["esp32c3"] }
|
||||
static_cell = { version = "2.1.0", features = ["nightly"] }
|
||||
critical-section = "1.2.0"
|
||||
maud = { path = "/home/jda/src/rust/maud/target/package/maud-0.26.0", features = ["alloc"] }
|
||||
picoserve = { version = "0.13.3", default-features = false, features = [
|
||||
"embassy"
|
||||
maud = { path = "/home/jda/src/rust/maud/target/package/maud-0.26.0", features = ["alloc", "picoserve"] }
|
||||
picoserve = { version = "0.14.1", default-features = false, features = [
|
||||
"alloc",
|
||||
"embassy",
|
||||
"log",
|
||||
] }
|
||||
sntpc = { version = "0.5.1", default-features = false, features = [ "embassy-socket" ] }
|
||||
chrono = { version = "0.4.39", default-features = false, features = [ "alloc" ] }
|
||||
serde = { version = "1.0.217", default-features = false }
|
||||
include_file_compress = "0.1.3"
|
||||
|
||||
[profile.dev]
|
||||
# Rust debug is too slow.
|
||||
|
||||
1
build.rs
@@ -1,3 +1,4 @@
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-arg-bins=-Tlinkall.x");
|
||||
println!("cargo:rustc-env=CARGO_PKG_VERSION_SAFE={}", env!("CARGO_PKG_VERSION").replace(".", "_"));
|
||||
}
|
||||
|
||||
BIN
resources/7193_wallas-uus-logo-1.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
resources/espressif-systems-logo-png_seeklogo-407805.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
resources/risc-v-core.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
@@ -1,5 +1,6 @@
|
||||
echo "Execute as '. ./set-env.sh'"
|
||||
read -p "wifi ssid " WIFI_SSID
|
||||
read -p "wifi password " WIFI_PASSWORD
|
||||
read -p "esp loglevel (trace, debug, info, warn, error) " ESP_LOGLEVEL
|
||||
export WIFI_SSID WIFI_PASSWORD ESP_LOGLEVEL
|
||||
read -s -p "wifi password " WIFI_PASSWORD
|
||||
echo
|
||||
read -p "esp loglevel (trace, debug, info, warn, error) " ESP_LOG
|
||||
export WIFI_SSID WIFI_PASSWORD ESP_LOG
|
||||
|
||||
159
src/database.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
|
||||
use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex};
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_time::Instant;
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::RefCell;
|
||||
use crate::serial;
|
||||
|
||||
pub struct DataPoint {
|
||||
pub instant: Instant,
|
||||
pub t1: i8,
|
||||
pub t2: i8,
|
||||
pub target: i8,
|
||||
pub current: i8,
|
||||
}
|
||||
|
||||
impl Copy for DataPoint { }
|
||||
|
||||
impl Clone for DataPoint {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl DataPoint {
|
||||
pub const fn default() -> Self {
|
||||
DataPoint { instant: Instant::from_ticks(0), t1: 0, t2: 0, target: 0, current: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
struct RingBuffer<T: Copy, const CAP: usize> {
|
||||
buf: [T; CAP],
|
||||
index: usize,
|
||||
has_wrapped: bool,
|
||||
}
|
||||
|
||||
impl<T: Copy, const CAP: usize> RingBuffer<T, CAP> {
|
||||
pub const fn new(def: T) -> Self {
|
||||
Self {
|
||||
buf: [def; CAP], index: 0, has_wrapped: false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
if self.has_wrapped {
|
||||
CAP
|
||||
} else { self.index }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest pushed item (youngest item)
|
||||
*/
|
||||
pub fn get_latest(&self) -> Option<T> {
|
||||
if self.index == 0 && self.has_wrapped {
|
||||
Some(self.buf[CAP-1])
|
||||
} else if self.index == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.buf[self.index-1])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get oldest item in the ringbuffer
|
||||
pub fn get_first(&self) -> Option<T> {
|
||||
if self.has_wrapped {
|
||||
Some(self.buf[self.index])
|
||||
} else if self.index > 0 {
|
||||
Some(self.buf[0])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get all items in the buffer, oldest at index 0,
|
||||
* youngest at index CAP.
|
||||
*/
|
||||
pub fn get_all(&self) -> Vec<T> {
|
||||
let size = self.size();
|
||||
let mut res = Vec::<T>::with_capacity(size);
|
||||
let mut i = if self.has_wrapped { self.index } else { 0 };
|
||||
|
||||
for _ in 0..size {
|
||||
res.push(self.buf[i]);
|
||||
i = i + 1;
|
||||
if i >= CAP {
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an item to the buffer
|
||||
*/
|
||||
pub fn push(&mut self, val: T) {
|
||||
self.buf[self.index] = val;
|
||||
self.index = self.index + 1;
|
||||
if self.index >= CAP {
|
||||
self.has_wrapped = true;
|
||||
self.index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MutexRingBuffer<M: RawMutex, T: Copy, const CAP: usize> {
|
||||
inner: Mutex<M, RefCell<RingBuffer<T, CAP>>>,
|
||||
}
|
||||
|
||||
impl<M: RawMutex, T: Copy, const CAP: usize> MutexRingBuffer<M, T, CAP> {
|
||||
pub const fn new(def: T) -> Self {
|
||||
Self {
|
||||
inner: Mutex::new(RefCell::new(RingBuffer::new(def))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&self, val: T) {
|
||||
self.inner.lock(|rc| {
|
||||
let mut rb = rc.borrow_mut();
|
||||
rb.push(val);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_all(&self) -> Vec<T> {
|
||||
self.inner.lock(|rc| {
|
||||
let rb = rc.borrow();
|
||||
rb.get_all()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_latest(&self) -> Option<T> {
|
||||
self.inner.lock(|rc| {
|
||||
let rb = rc.borrow();
|
||||
rb.get_latest()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub static DATAPOINT_BUFFER: MutexRingBuffer<CriticalSectionRawMutex, DataPoint, 200> = MutexRingBuffer::new(DataPoint::default());
|
||||
|
||||
pub fn database_spawn(spawner: embassy_executor::Spawner) {
|
||||
let subscriber = serial::DOMAIN_MESSAGE_CHANNEL.subscriber().unwrap();
|
||||
spawner.spawn(data_subscriber(subscriber)).ok();
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn data_subscriber (mut subscriber: serial::DomainMessageSubscriber<'static>) {
|
||||
loop {
|
||||
let data = subscriber.next_message_pure().await;
|
||||
if let serial::DomainMessage::WallasData(i, t1, t2, target, current) = data {
|
||||
DATAPOINT_BUFFER.push(DataPoint{instant: i, t1: t1, t2: t2, target: target, current: current});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
243
src/httpd.rs
@@ -1,9 +1,23 @@
|
||||
use picoserve::{make_static, routing::get, AppBuilder, AppRouter, Router};
|
||||
use picoserve::routing::PathRouter;
|
||||
use embassy_time::Duration;
|
||||
use picoserve::routing::{get, get_service, parse_path_segment};
|
||||
use picoserve::response::{IntoResponse, File, Json};
|
||||
use picoserve::response::status::StatusCode;
|
||||
use embassy_time::{Duration, Instant, with_timeout};
|
||||
use embassy_net::Stack;
|
||||
use maud::{DOCTYPE, html, Markup};
|
||||
use alloc::vec::Vec;
|
||||
use crate::sntp_client::get_instant;
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde::ser::SerializeSeq;
|
||||
use include_file_compress::include_file_compress_deflate;
|
||||
use crate::serial;
|
||||
use crate::database::DATAPOINT_BUFFER;
|
||||
|
||||
use static_cell::StaticCell;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
|
||||
|
||||
const STYLES_CSS_FILENAME: &str = concat!("/styles-", env!("CARGO_PKG_VERSION_SAFE"), ".css");
|
||||
const APP_JS_FILENAME: &str = concat!("/app-", env!("CARGO_PKG_VERSION_SAFE"), ".js");
|
||||
|
||||
static PICO_CONFIG : picoserve::Config<Duration> = picoserve::Config::new(
|
||||
picoserve::Timeouts {
|
||||
@@ -12,32 +26,52 @@ static PICO_CONFIG : picoserve::Config<Duration> = picoserve::Config::new(
|
||||
write: Some(Duration::from_secs(1)),
|
||||
}).keep_connection_alive();
|
||||
|
||||
/**
|
||||
struct AppProps;
|
||||
|
||||
impl AppBuilder for AppProps {
|
||||
type PathRouter = impl picoserve::routing::PathRouter;
|
||||
|
||||
fn build_app(self) -> picoserve::Router<Self::PathRouter> {
|
||||
picoserve::Router::new().route("/", get(|| async move { "Hello World" }))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn httpd_spawn(spawner: embassy_executor::Spawner, size: usize, stack: Stack<'static>) -> Result<(), super::Error> {
|
||||
for i in 0..size {
|
||||
for i in 0..size {
|
||||
spawner.must_spawn(web_task(i, stack));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
static DEFLATE_CACHEABLE_CONTENT_HEADERS: &'static [(&'static str, &'static str); 2] = &[("Content-Encoding", "deflate"), ("Cache-Control", "public, s-maxage=28800, max-age=28800")];
|
||||
static CACHEABLE_IMAGE_HEADERS: &'static [(&'static str, &'static str); 1] = &[DEFLATE_CACHEABLE_CONTENT_HEADERS[1]];
|
||||
static PNG_CONTENT_TYPE: &'static str = "image/png";
|
||||
static CSS_CONTENT_TYPE: &'static str = "text/css";
|
||||
static JS_CONTENT_TYPE: &'static str = "text/javascript";
|
||||
|
||||
#[embassy_executor::task(pool_size = super::MAX_CONCURRENT_SOCKETS)]
|
||||
async fn web_task(
|
||||
id: usize,
|
||||
stack: Stack<'static>,
|
||||
) -> ! {
|
||||
let api_router = picoserve::Router::new()
|
||||
.route("/latest", get(|| async { latest() }))
|
||||
.route("/allreadings", get(|| async { allreadings() }))
|
||||
.route("/metrics", get(|| async { metrics () }))
|
||||
.route("/cmd/start", get(|| { cmd_start() }))
|
||||
.route("/cmd/stop", get(|| { cmd_stop() }))
|
||||
.route("/cmd/ventilate", get(|| { cmd_ventilate() }))
|
||||
.route(("/cmd/temperature", parse_path_segment::<i8>()), get(|t| { cmd_temperature(t) }));
|
||||
|
||||
let image_router = picoserve::Router::new()
|
||||
.route("/favicon.png", get_service(File::with_content_type_and_headers(&PNG_CONTENT_TYPE,
|
||||
include_bytes!("static/favicon.png"), CACHEABLE_IMAGE_HEADERS)))
|
||||
.route("/risc-v-logo.png", get_service(File::with_content_type_and_headers(&PNG_CONTENT_TYPE,
|
||||
include_bytes!("static/risc-v-logo.png"), CACHEABLE_IMAGE_HEADERS)))
|
||||
.route("/espressif-logo.png", get_service(File::with_content_type_and_headers(&PNG_CONTENT_TYPE,
|
||||
include_bytes!("static/espressif-logo.png"), CACHEABLE_IMAGE_HEADERS)))
|
||||
.route("/wallas-logo.png", get_service(File::with_content_type_and_headers(&PNG_CONTENT_TYPE,
|
||||
include_bytes!("static/wallas-logo.png"), CACHEABLE_IMAGE_HEADERS)));
|
||||
let app =
|
||||
picoserve::Router::new().route("/", get(|| async { "Hello World" })).route("/test", get(|| async { "Test" }));
|
||||
picoserve::Router::new()
|
||||
.nest("/api/v1", api_router)
|
||||
.route("/", get(|| async { index() }))
|
||||
.route(STYLES_CSS_FILENAME, get_service(File::with_content_type_and_headers(&CSS_CONTENT_TYPE,
|
||||
include_file_compress_deflate!("src/static/styles.css", 5), DEFLATE_CACHEABLE_CONTENT_HEADERS)))
|
||||
.route(APP_JS_FILENAME, get_service(File::with_content_type_and_headers(&JS_CONTENT_TYPE,
|
||||
include_file_compress_deflate!("src/static/app.js", 5), DEFLATE_CACHEABLE_CONTENT_HEADERS)))
|
||||
.nest("/images", image_router);
|
||||
|
||||
let port = 80;
|
||||
let mut tcp_rx_buffer = [0; 1024];
|
||||
@@ -56,3 +90,178 @@ async fn web_task(
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn page(heading: &str, content: Markup) -> Markup {
|
||||
html! {
|
||||
(DOCTYPE)
|
||||
html {
|
||||
head {
|
||||
link rel="stylesheet" type="text/css" href=(STYLES_CSS_FILENAME);
|
||||
link rel="icon" type="image/png" href="/images/facicon.png"
|
||||
script src=(APP_JS_FILENAME) {};
|
||||
title { (heading) }
|
||||
}
|
||||
body {
|
||||
h1 { (heading) }
|
||||
(content)
|
||||
div #footer {
|
||||
a href="https://riscv.org/" {
|
||||
img src="/images/risc-v-logo.png";
|
||||
};
|
||||
a href="https://wallas.fi/" {
|
||||
img src="/images/wallas-logo.png";
|
||||
};
|
||||
a href="https://www.espressif.com/" {
|
||||
img src="/images/espressif-logo.png";
|
||||
};
|
||||
br;
|
||||
"RISC-V is a registered trademark of RISC-V International · Wallas is a registered trademark of Wallas-Marin Oy · Espressif is a registered trademark of Espressif Systems"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
struct TemperatureReading {
|
||||
#[serde(serialize_with = "instant_to_string")]
|
||||
time: Instant,
|
||||
target: i8,
|
||||
temperature: i8,
|
||||
}
|
||||
|
||||
fn instant_to_string<S>(val: &Instant, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer
|
||||
{
|
||||
s.serialize_str(get_instant(val).as_str())
|
||||
}
|
||||
|
||||
fn option_instant_to_string<S>(oval: &Option<Instant>, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer
|
||||
{
|
||||
match oval {
|
||||
Some(val) => s.serialize_str(get_instant(val).as_str()),
|
||||
None => s.serialize_str("none"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(transparent)]
|
||||
struct AllTemperatureReadings {
|
||||
#[serde(serialize_with = "vec_temperature_reading")]
|
||||
v: Vec<TemperatureReading>,
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement serialization as alloc::vec::Vec does not implement 'Serialize'
|
||||
*/
|
||||
fn vec_temperature_reading<S>(v: &Vec<TemperatureReading>, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer
|
||||
{
|
||||
let mut seq = s.serialize_seq(Some(v.len())).unwrap();
|
||||
for e in v {
|
||||
seq.serialize_element(e)?
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
fn allreadings() -> impl IntoResponse {
|
||||
Json(AllTemperatureReadings {
|
||||
v: DATAPOINT_BUFFER.get_all().iter()
|
||||
.map(|d| TemperatureReading { time: d.instant, target: d.target, temperature: d.current })
|
||||
.collect::<Vec<TemperatureReading>>() })
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct LatestResponse {
|
||||
#[serde(skip_serializing_if = "Option::is_none", serialize_with = "option_instant_to_string")]
|
||||
time: Option<Instant>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
target: Option<i8>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
temperature: Option<i8>,
|
||||
}
|
||||
|
||||
impl LatestResponse {
|
||||
pub fn none () -> Self {
|
||||
LatestResponse { time: None, target: None, temperature: None }
|
||||
}
|
||||
|
||||
pub fn some (time: Instant, target: i8, temperature: i8) -> Self {
|
||||
LatestResponse { time: Some(time), target: Some(target), temperature: Some(temperature) }
|
||||
}
|
||||
}
|
||||
|
||||
fn metrics() -> impl IntoResponse {
|
||||
match DATAPOINT_BUFFER.get_latest() {
|
||||
None => "".into(),
|
||||
Some(datapoint) => {
|
||||
alloc::format!(include_str!("static/prometheus-template.txt"), target = datapoint.target, temperature = datapoint.current, command = datapoint.t1, state = datapoint.t2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn latest() -> impl IntoResponse {
|
||||
match DATAPOINT_BUFFER.get_latest() {
|
||||
None => Json(LatestResponse::none()),
|
||||
Some(datapoint) => Json(LatestResponse::some(datapoint.instant, datapoint.target, datapoint.current))
|
||||
}
|
||||
}
|
||||
|
||||
fn index() -> impl IntoResponse {
|
||||
page("Wallas 22GB Wifi Extension", html! {
|
||||
div .opaque { p .intro { "Beta version of ESP32C3 based Wifi extension to the Wallas 361062 Control Panel for the DT/GB Heaters" } }
|
||||
div .opaque { p #graph { "Waiting for data ..." } }
|
||||
div .opaque { p #latest { "Waiting for latest reading ..." } }
|
||||
button #"button-start" .button { "Start heater" }
|
||||
button #"button-stop" .button { "Stop heater" }
|
||||
button #"button-ventilate" .button { "Start ventilator" }
|
||||
button #"button-target" .button { "Set target temperature" }
|
||||
})
|
||||
}
|
||||
|
||||
async fn wait_for_ok () -> () {
|
||||
let mut subscriber = serial::DOMAIN_MESSAGE_CHANNEL.subscriber().unwrap();
|
||||
loop {
|
||||
let data = subscriber.next_message_pure().await;
|
||||
if let serial::DomainMessage::AtOk = data {
|
||||
return ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CMDLOCK: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(());
|
||||
|
||||
async fn cmd(c: serial::DomainCommand) -> impl IntoResponse {
|
||||
match CMDLOCK.try_lock() {
|
||||
Err(_) => (StatusCode::LOCKED, ""),
|
||||
_ => {
|
||||
serial::DOMAIN_COMMAND_CHANNEL.publisher().unwrap().publish_immediate(c);
|
||||
match with_timeout(Duration::from_secs(5), wait_for_ok()).await {
|
||||
Ok(()) => (StatusCode::NO_CONTENT, ""),
|
||||
Err(_) => (StatusCode::GATEWAY_TIMEOUT, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn cmd_start() -> impl IntoResponse {
|
||||
cmd(serial::DomainCommand::Start).await
|
||||
}
|
||||
|
||||
async fn cmd_stop() -> impl IntoResponse {
|
||||
cmd(serial::DomainCommand::Stop).await
|
||||
}
|
||||
|
||||
async fn cmd_ventilate() -> impl IntoResponse {
|
||||
cmd(serial::DomainCommand::Ventilate).await
|
||||
}
|
||||
|
||||
async fn cmd_temperature(t: i8) -> impl IntoResponse {
|
||||
cmd(serial::DomainCommand::Temperature(t)).await
|
||||
}
|
||||
|
||||
54
src/main.rs
@@ -2,23 +2,21 @@
|
||||
#![no_main]
|
||||
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::prelude::*;
|
||||
use log::{info, error};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use heapless::String;
|
||||
use esp_hal::rng::Rng;
|
||||
use esp_hal::timer::systimer::SystemTimer;
|
||||
use esp_hal::timer::timg::TimerGroup;
|
||||
use esp_hal::clock::CpuClock;
|
||||
|
||||
/**
|
||||
* Embassy includes
|
||||
*/
|
||||
use esp_hal_embassy::init as initialize_embassy;
|
||||
use esp_hal::timer::systimer::Target;
|
||||
//use esp_hal_embassy::init as initialize_embassy;
|
||||
use esp_hal_embassy::main;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use esp_hal::timer::timg::TimerGroup;
|
||||
|
||||
/**
|
||||
* Rng functionality
|
||||
@@ -31,7 +29,7 @@ use self::random::RngWrapper;
|
||||
*/
|
||||
mod wifi;
|
||||
use self::wifi::connect as connect_to_wifi;
|
||||
use self::wifi::Error as WifiError;
|
||||
// use self::wifi::Error as WifiError;
|
||||
|
||||
/**
|
||||
* httpd
|
||||
@@ -45,6 +43,24 @@ use httpd::httpd_spawn;
|
||||
mod serial;
|
||||
use serial::serial_spawn;
|
||||
|
||||
/**
|
||||
* sntp client
|
||||
*/
|
||||
mod sntp_client;
|
||||
use sntp_client::sntp_client_spawn;
|
||||
pub use sntp_client::get_now;
|
||||
|
||||
/**
|
||||
* database
|
||||
*/
|
||||
mod database;
|
||||
use database::database_spawn;
|
||||
|
||||
|
||||
/// CARGO_PKG_VERSION
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const VERSION_SAFE: &str = env!("CARGO_PKG_VERSION_SAFE");
|
||||
|
||||
/// SSID for WiFi network
|
||||
const WIFI_SSID: &str = env!("WIFI_SSID");
|
||||
|
||||
@@ -54,12 +70,17 @@ const WIFI_PASSWORD: &str = env!("WIFI_PASSWORD");
|
||||
/// Size of heap for dynamically-allocated memory
|
||||
const HEAP_MEMORY_SIZE: usize = 72 * 1024;
|
||||
|
||||
const MAX_CONCURRENT_SOCKETS: usize = 5;
|
||||
const HTTPD_SOCKETS: usize = 8;
|
||||
const DHCP_SOCKETS: usize = 1;
|
||||
const SNTP_SOCKETS: usize = 1;
|
||||
const DNS_SOCKETS: usize = 1;
|
||||
const MAX_CONCURRENT_SOCKETS: usize = HTTPD_SOCKETS + DHCP_SOCKETS + DNS_SOCKETS + SNTP_SOCKETS;
|
||||
|
||||
|
||||
#[main]
|
||||
async fn main(spawner: Spawner) {
|
||||
|
||||
info!("booting firmware");
|
||||
info!("booting firmware version {}", VERSION);
|
||||
|
||||
if let Err(error) = main_fallible(spawner).await {
|
||||
error!("Error while running firmware: {error:?}");
|
||||
@@ -81,8 +102,9 @@ async fn main_fallible(
|
||||
|
||||
esp_println::logger::init_logger_from_env();
|
||||
|
||||
let systimer = SystemTimer::new(peripherals.SYSTIMER).split::<Target>();
|
||||
initialize_embassy(systimer.alarm0);
|
||||
// let systimer = SystemTimer::new(peripherals.SYSTIMER).split::<Target>();
|
||||
let tmg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
// initialize_embassy(tmg0.timer0);
|
||||
|
||||
let rng = Rng::new(peripherals.RNG);
|
||||
|
||||
@@ -90,11 +112,15 @@ async fn main_fallible(
|
||||
let password =
|
||||
String::<64>::try_from(WIFI_PASSWORD).map_err(|()| Error::ParseCredentials)?;
|
||||
|
||||
let stack = connect_to_wifi(spawner, TimerGroup::new(peripherals.TIMG0), rng, peripherals.WIFI, peripherals.RADIO_CLK, (ssid, password)).await.unwrap();
|
||||
let stack = connect_to_wifi(spawner, tmg0, rng, peripherals.WIFI, peripherals.RADIO_CLK, (ssid, password)).await.unwrap();
|
||||
|
||||
httpd_spawn(spawner, MAX_CONCURRENT_SOCKETS-1, stack);
|
||||
let _ = httpd_spawn(spawner, HTTPD_SOCKETS, stack);
|
||||
|
||||
serial_spawn(spawner, peripherals.UART0.into(), peripherals.GPIO20.into(), peripherals.GPIO21.into());
|
||||
let _ = sntp_client_spawn(spawner, stack);
|
||||
|
||||
serial_spawn(spawner, peripherals.UART0.into());
|
||||
|
||||
let _ = database_spawn(spawner);
|
||||
|
||||
info!("firmware done booting");
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//! Random numbers generator
|
||||
|
||||
use rand_core::CryptoRng;
|
||||
use rand_core::Error;
|
||||
use rand_core::RngCore;
|
||||
|
||||
use esp_hal::rng::Rng;
|
||||
@@ -39,10 +38,12 @@ impl RngCore for RngWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||
self.fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl CryptoRng for RngWrapper {}
|
||||
|
||||
215
src/serial.rs
@@ -1,36 +1,223 @@
|
||||
use esp_hal::{
|
||||
// clock::ClockControl,
|
||||
peripherals::{Peripherals},
|
||||
prelude::*,
|
||||
uart::{AtCmdConfig, AnyUart, Uart, UartRx, UartTx, Config},
|
||||
gpio::AnyPin,
|
||||
// peripherals::{Peripherals},
|
||||
uart::{AnyUart, Uart, UartRx, UartTx, Config},
|
||||
Async,
|
||||
};
|
||||
use log::{info, error};
|
||||
use log::{info, error, debug};
|
||||
|
||||
const BUFFER_SIZE: usize = 64;
|
||||
// Channel stuff
|
||||
// use embassy_sync::channel::Channel;
|
||||
use embassy_sync::pubsub::{PubSubChannel, Subscriber};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_time::{Instant, Timer};
|
||||
use heapless::Vec;
|
||||
use nom::Parser;
|
||||
|
||||
pub fn serial_spawn(spawner: embassy_executor::Spawner, peri_uart: AnyUart, rx_pin: AnyPin, tx_pin: AnyPin) {
|
||||
const BUFFER_SIZE: usize = 256;
|
||||
type BaseMessage = heapless::String<BUFFER_SIZE>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DomainMessage {
|
||||
AtOk,
|
||||
WallasData(Instant, i8, i8, i8, i8),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DomainCommand {
|
||||
Start,
|
||||
Stop,
|
||||
Ventilate,
|
||||
Temperature(i8),
|
||||
}
|
||||
|
||||
type DomainMessageChannel = PubSubChannel<CriticalSectionRawMutex, DomainMessage, 3, 2, 1>;
|
||||
pub type DomainMessageSubscriber<'a> = Subscriber<'a, CriticalSectionRawMutex, DomainMessage, 3, 2, 1>;
|
||||
|
||||
pub static DOMAIN_MESSAGE_CHANNEL: DomainMessageChannel = DomainMessageChannel::new();
|
||||
|
||||
type DomainCommandChannel = PubSubChannel<CriticalSectionRawMutex, DomainCommand, 3, 1, 1>;
|
||||
|
||||
pub static DOMAIN_COMMAND_CHANNEL: DomainCommandChannel = DomainCommandChannel::new();
|
||||
|
||||
pub fn serial_spawn(spawner: embassy_executor::Spawner, peri_uart: AnyUart) {
|
||||
// Initialize and configure UART0
|
||||
let config = Config::default().rx_fifo_full_threshold(BUFFER_SIZE as u16);
|
||||
let my_uart = Uart::new_with_config(peri_uart, config, rx_pin, tx_pin).unwrap().into_async();
|
||||
let config = Config::default().with_rx_fifo_full_threshold(BUFFER_SIZE as u16);
|
||||
// removed rx_pin, tx_pin
|
||||
let my_uart = Uart::new(peri_uart, config).unwrap().into_async();
|
||||
// Split UART0 to create seperate Tx and Rx handles
|
||||
let (rx, _tx) = my_uart.split();
|
||||
let (rx, tx) = my_uart.split();
|
||||
|
||||
spawner.spawn(reader(rx)).ok();
|
||||
//spawner.spawn(writer(tx)).ok();
|
||||
spawner.spawn(writer(tx)).ok();
|
||||
spawner.spawn(fakedata()).ok();
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn fakedata() {
|
||||
let domain_publisher = DOMAIN_MESSAGE_CHANNEL.publisher().unwrap();
|
||||
let mut target: i8 = 22;
|
||||
let mut current: i8 = 10;
|
||||
let mut direction: i8 = 1;
|
||||
loop {
|
||||
Timer::after_secs(15).await;
|
||||
domain_publisher.publish_immediate(DomainMessage::WallasData(Instant::now(), 0, 0, target, current));
|
||||
current = current + direction;
|
||||
if current >= 22 || current <= 5 {
|
||||
direction = direction * -1;
|
||||
if target == 22 {
|
||||
target = 5;
|
||||
} else {
|
||||
target = 22;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn at_ok_parser (i: &str) -> Option<()> {
|
||||
match (
|
||||
nom::bytes::complete::tag("AT+OK"),
|
||||
nom::character::complete::crlf::<&str, nom::error::Error<&str>>
|
||||
).parse(i) {
|
||||
Ok((_residual, (_, _))) => {
|
||||
Some(())
|
||||
}
|
||||
Err(_e) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn at_wallas_parser (i: &str) -> Option<(i8, i8, i8, i8)> {
|
||||
match (
|
||||
nom::bytes::complete::tag("AT+WALLAS="),
|
||||
nom::character::complete::i8,
|
||||
nom::character::complete::char(','),
|
||||
nom::character::complete::i8,
|
||||
nom::character::complete::char(','),
|
||||
nom::character::complete::i8,
|
||||
nom::character::complete::char(','),
|
||||
nom::character::complete::i8,
|
||||
nom::character::complete::crlf::<&str, nom::error::Error<&str>>
|
||||
).parse(i) {
|
||||
Ok((_residual, (_, t1, _, t2, _, t3, _, t4, _))) => {
|
||||
Some((t1, t2, t3, t4))
|
||||
}
|
||||
Err(_e) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serial_receive(msg: &str) {
|
||||
let domain_publisher = DOMAIN_MESSAGE_CHANNEL.publisher().unwrap();
|
||||
loop {
|
||||
if let Some(()) = at_ok_parser(&msg) {
|
||||
domain_publisher.publish_immediate(DomainMessage::AtOk);
|
||||
} else if let Some((t1, t2, t3, t4)) = at_wallas_parser(&msg) {
|
||||
domain_publisher.publish_immediate(DomainMessage::WallasData(Instant::now(), t1, t2, t3, t4));
|
||||
} else {
|
||||
error!("msg received but unmatched '{}'", &msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn writer(mut tx: UartTx<'static, Async>) {
|
||||
let mut wbuf: [u8; 32] = [0u8; 32];
|
||||
let mut domain_subscriber = DOMAIN_COMMAND_CHANNEL.subscriber().unwrap();
|
||||
loop {
|
||||
let cmd = domain_subscriber.next_message_pure().await;
|
||||
info!("Writing message {:?} to serial port", &cmd);
|
||||
let _ = embedded_io_async::Write::write(&mut tx,
|
||||
match &cmd {
|
||||
DomainCommand::Start => b"START\r\n",
|
||||
DomainCommand::Stop => b"STOP\r\n",
|
||||
DomainCommand::Ventilate => b"VENT\r\n",
|
||||
DomainCommand::Temperature(temp) => {
|
||||
let msg = alloc::format!("TEMP={}\r\n", temp);
|
||||
wbuf[0..msg.len()].copy_from_slice(msg.as_bytes());
|
||||
&wbuf[0..msg.len()]
|
||||
}
|
||||
}
|
||||
).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn reader(mut rx: UartRx<'static, Async>) {
|
||||
let mut rbuf: [u8; BUFFER_SIZE] = [0u8; BUFFER_SIZE];
|
||||
loop {
|
||||
let r = embedded_io_async::Read::read(&mut rx, &mut rbuf[0..]).await;
|
||||
match r {
|
||||
Ok(len) => {
|
||||
let mut offset: usize = 0;
|
||||
let mut eaten: usize = 0;
|
||||
let mut msg : Option<BaseMessage> = None;
|
||||
loop {
|
||||
if let Some(base_msg) = msg {
|
||||
//BASE_CHANNEL.send(base_msg).await;
|
||||
serial_receive(&base_msg);
|
||||
msg = None;
|
||||
}
|
||||
if eaten != 0 {
|
||||
for n in 0..offset {
|
||||
rbuf[n] = rbuf[n+eaten];
|
||||
}
|
||||
eaten = 0;
|
||||
}
|
||||
let r = embedded_io_async::Read::read(&mut rx, &mut rbuf[offset..]).await;
|
||||
match r {
|
||||
Ok(len) => {
|
||||
let new_offset = len + offset;
|
||||
// send_line will send two numbers: new offset (after eaten bytes have been
|
||||
// cleared) - and 'eaten' how many bytes have been consumed
|
||||
let send_line = |start: usize, end: usize| -> (usize, usize, Option<BaseMessage>) {
|
||||
// send rbuf[start, end] somewhere
|
||||
let msg : BaseMessage = BaseMessage::from_utf8(Vec::from_slice(&rbuf[start..end]).unwrap()).unwrap();
|
||||
if end == new_offset {
|
||||
(0, 0, Some(msg))
|
||||
} else {
|
||||
let residual = new_offset - end;
|
||||
/*
|
||||
for n in 0..residual
|
||||
rbuf[n] = rbuf[end+n];
|
||||
} */
|
||||
(residual, end, Some(msg))
|
||||
}
|
||||
};
|
||||
for i in offset..new_offset {
|
||||
if rbuf[i] == 0x0d {
|
||||
let next = i+1;
|
||||
if next < new_offset && rbuf[next] == 0x0a {
|
||||
(offset, eaten, msg) = send_line(0, offset+next);
|
||||
continue;
|
||||
} else {
|
||||
(offset, eaten, msg) = send_line(0, offset+i);
|
||||
continue;
|
||||
}
|
||||
} else if rbuf[i] == 0x0a {
|
||||
let next = i+1;
|
||||
if next < new_offset && rbuf[next] == 0x0a {
|
||||
(offset, eaten, msg) = send_line(0, offset+next);
|
||||
continue;
|
||||
} else {
|
||||
(offset, eaten, msg) = send_line(0, offset+1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if buffer is full... then consider this a line, even withour cr/lf
|
||||
if new_offset == BUFFER_SIZE {
|
||||
debug!("serial receive BUFFER_SIZE characters, but no cr/lf so far");
|
||||
send_line(0, new_offset);
|
||||
offset = 0;
|
||||
continue;
|
||||
}
|
||||
offset = offset + len;
|
||||
info!("Read: {len}, data: {:?}", &rbuf[..len]);
|
||||
}
|
||||
Err(e) => error!("RX Error: {:?}", e),
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
103
src/sntp_client.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
|
||||
use core::net::{IpAddr, SocketAddr};
|
||||
use embassy_net::Stack;
|
||||
use embassy_net::dns::DnsQueryType;
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use embassy_net::udp::{PacketMetadata, UdpSocket};
|
||||
use embassy_sync::watch::Watch;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use alloc::string::String;
|
||||
|
||||
use log::error;
|
||||
use sntpc::{get_time, NtpContext, NtpTimestampGenerator};
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Timestamp {
|
||||
tstamp: Instant,
|
||||
}
|
||||
|
||||
static UTC_DATETIME: Watch<CriticalSectionRawMutex, Duration, 2> = Watch::new();
|
||||
|
||||
pub fn get_now() -> String {
|
||||
get_instant(&Instant::now())
|
||||
}
|
||||
|
||||
pub fn get_instant(instant: &Instant) -> String {
|
||||
let offset = UTC_DATETIME.try_get();
|
||||
match offset {
|
||||
None => {
|
||||
alloc::format!("LAUNCH+{}s", instant.as_secs())
|
||||
},
|
||||
Some(duration) => {
|
||||
let time = *instant + duration;
|
||||
let micros: i64 = time.as_micros() as i64;
|
||||
let dt = DateTime::<Utc>::from_timestamp_micros(micros);
|
||||
match dt {
|
||||
Some(val) => alloc::format!("{}", val.format("%Y-%m-%d %H:%M:%S")),
|
||||
None => alloc::format!("LAUNCH+{}s", instant.as_secs())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl NtpTimestampGenerator for Timestamp {
|
||||
fn init(&mut self) {
|
||||
self.tstamp = Instant::now();
|
||||
}
|
||||
|
||||
fn timestamp_sec(&self) -> u64 {
|
||||
self.tstamp.as_secs()
|
||||
}
|
||||
|
||||
fn timestamp_subsec_micros(&self) -> u32 {
|
||||
(self.tstamp.as_micros() % 1_000_000) as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sntp_client_spawn(spawner: embassy_executor::Spawner, stack: Stack<'static>) {
|
||||
|
||||
spawner.must_spawn(sntp_client(stack));
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn sntp_client(stack: Stack<'static>) {
|
||||
|
||||
// Create UDP socket
|
||||
let mut rx_meta = [PacketMetadata::EMPTY; 16];
|
||||
let mut rx_buffer = [0; 4096];
|
||||
let mut tx_meta = [PacketMetadata::EMPTY; 16];
|
||||
let mut tx_buffer = [0; 4096];
|
||||
|
||||
let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);
|
||||
socket.bind(123).unwrap();
|
||||
|
||||
let context = NtpContext::new(Timestamp{tstamp: Instant::now()});
|
||||
let server_list : alloc::vec::Vec<&str> = alloc::vec!["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org"];
|
||||
let mut current_server = 2;
|
||||
|
||||
let sender = UTC_DATETIME.sender();
|
||||
|
||||
loop {
|
||||
let dns_res = stack.dns_query (server_list[current_server], DnsQueryType::A).await.expect("Failed to resolve NTP server");
|
||||
let addr: IpAddr = dns_res[0].into();
|
||||
let result =
|
||||
get_time(SocketAddr::from((addr, 123)), &socket, context).await;
|
||||
|
||||
match result {
|
||||
Ok(time) => {
|
||||
let offset = Duration::from_micros(time.offset as u64);
|
||||
sender.send(offset);
|
||||
Timer::after(Duration::from_secs(7*24*3600)).await; // redo SNTP check in a week
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error getting time: {:?}", e);
|
||||
current_server = (current_server + 1) % 4;
|
||||
Timer::after(Duration::from_secs(30)).await;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
187
src/static/app.js
Normal file
@@ -0,0 +1,187 @@
|
||||
/* app.js */
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => main(), false);
|
||||
|
||||
async function main () {
|
||||
|
||||
var pgraph = document.querySelector("#graph");
|
||||
if (pgraph) LoadGraph(pgraph);
|
||||
|
||||
var platest = document.querySelector("#latest");
|
||||
if (platest) LoadLatest(platest);
|
||||
|
||||
var bstart = document.querySelector("#button-start");
|
||||
if (bstart) bstart.addEventListener("click",
|
||||
() => wallas_command("start", "Wallas Heater did not acknowledge the start command", "Wallas heater started"));
|
||||
|
||||
var bstop = document.querySelector("#button-stop");
|
||||
if (bstop) bstop.addEventListener("click",
|
||||
() => wallas_command("stop", "Wallas Heater did not acknowledge the stop command", "Wallas heater stopped"));
|
||||
|
||||
var bventilate = document.querySelector("#button-ventilate");
|
||||
if (bventilate) bventilate.addEventListener("click",
|
||||
() => wallas_command("ventilate", "Wallas Heater did not acknowledge the ventilate command", "Wallas heater is ventilating"));
|
||||
|
||||
var btarget = document.querySelector("#button-target");
|
||||
if (btarget) {
|
||||
btarget.addEventListener("click", set_temperature_dialog);
|
||||
var target_label = document.querySelector("label#target-label");
|
||||
document.querySelector("input#target").addEventListener("input", (i) => { target_label.innerHTML = `Target temperature is ${i.target.value}°C`; });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function LoadLatest(platest) {
|
||||
await sleep(500);
|
||||
while (true) {
|
||||
fetch("/api/v1/latest").then((response) => response.json()).then((json) => {
|
||||
if (json.time) {
|
||||
time = json.time;
|
||||
temperature = json.temperature;
|
||||
target = json.target;
|
||||
platest.innerHTML = `Temperature was ${temperature}°C at ${time} UTC, target temperature was ${target}°C`;
|
||||
}
|
||||
})
|
||||
await sleep(10000);
|
||||
}
|
||||
};
|
||||
|
||||
const svg_xmlns = "http://www.w3.org/2000/svg";
|
||||
function appendSVGElement(parent, name, attributes, text = null) {
|
||||
var elem = document.createElementNS(svg_xmlns, name);
|
||||
for (var aname in attributes) {
|
||||
elem.setAttributeNS(null, aname, attributes[aname]);
|
||||
}
|
||||
if (text != null) {
|
||||
textnode = document.createTextNode(text);
|
||||
elem.appendChild(textnode);
|
||||
}
|
||||
return parent.appendChild(elem);
|
||||
}
|
||||
|
||||
function appendElement(parent, name, attributes, text = null) {
|
||||
var elem = document.createElement(name);
|
||||
for (var aname in attributes) {
|
||||
elem.setAttribute(aname, attributes[aname]);
|
||||
}
|
||||
if (text != null) {
|
||||
textnode = document.createTextNode(text);
|
||||
elem.appendChild(textnode);
|
||||
}
|
||||
return parent.appendChild(elem);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function LoadGraph(pgraph) {
|
||||
await sleep(1000);
|
||||
while (true) {
|
||||
fetch("/api/v1/allreadings").then((response) => response.json()).then((json) => {
|
||||
if (json instanceof Array && json.length > 0) {
|
||||
var width = pgraph.offsetWidth;
|
||||
var height = pgraph.offsetHeight;
|
||||
var temp_y = y => (25 - y) * height/35;
|
||||
var temp_i = i => (99 - i) * (width - 48)/100;
|
||||
var graph = document.createDocumentFragment();
|
||||
var svg = appendSVGElement(graph, "svg", {"width": "100%", "height": "100%", "viewBox": `0 0 ${width} ${height}`});
|
||||
var target_points = [];
|
||||
var temperature_points = [];
|
||||
for (var i = 0; i < json.length && i < 99; i++) {
|
||||
var r = json[json.length - i - 1];
|
||||
var x_coord = temp_i(i);
|
||||
var temperature_y = temp_y(r.temperature);
|
||||
temperature_points.push(`${x_coord},${temperature_y}`);
|
||||
var target_y = temp_y(r.target);
|
||||
target_points.push(`${x_coord},${target_y}`);
|
||||
}
|
||||
temperature_points_str = temperature_points.join(" ");
|
||||
target_points_str = target_points.join(" ");
|
||||
|
||||
// Horizontal lines
|
||||
// zero line
|
||||
appendSVGElement(svg, "polyline", {"class": "grid grid-zero", "points": ""+temp_i(100)+","+temp_y(0)+" "+temp_i(0)+","+temp_y(0)});
|
||||
// ten line
|
||||
appendSVGElement(svg, "polyline", {"class": "grid", "points": ""+temp_i(100)+","+temp_y(10)+" "+temp_i(0)+","+temp_y(10)});
|
||||
// twenty line
|
||||
appendSVGElement(svg, "polyline", {"class": "grid", "points": ""+temp_i(100)+","+temp_y(20)+" "+temp_i(0)+","+temp_y(20)});
|
||||
|
||||
// Vertical lines
|
||||
appendSVGElement(svg, "polyline", {"class": "grid", "points": ""+temp_i(10)+","+temp_y(24)+" "+temp_i(10)+","+temp_y(-9)});
|
||||
appendSVGElement(svg, "polyline", {"class": "grid", "points": ""+temp_i(30)+","+temp_y(24)+" "+temp_i(30)+","+temp_y(-9)});
|
||||
appendSVGElement(svg, "polyline", {"class": "grid", "points": ""+temp_i(50)+","+temp_y(24)+" "+temp_i(50)+","+temp_y(-9)});
|
||||
appendSVGElement(svg, "polyline", {"class": "grid", "points": ""+temp_i(70)+","+temp_y(24)+" "+temp_i(70)+","+temp_y(-9)});
|
||||
appendSVGElement(svg, "polyline", {"class": "grid", "points": ""+temp_i(90)+","+temp_y(24)+" "+temp_i(90)+","+temp_y(-9)});
|
||||
// text
|
||||
appendSVGElement(svg, "text", {"x": temp_i(0), "y": temp_y(0), "dx": 6, "dy": 5, "fill": "#000000"}, "0°C");
|
||||
appendSVGElement(svg, "text", {"x": temp_i(0), "y": temp_y(10), "dx": 6, "dy": 5, "fill": "#000000"}, "10°C");
|
||||
appendSVGElement(svg, "text", {"x": temp_i(0), "y": temp_y(20), "dx": 6, "dy": 5, "fill": "#000000"}, "20°C");
|
||||
|
||||
|
||||
target_polyline = appendSVGElement(svg, "polyline", {"class": "data target", "points": target_points_str});
|
||||
temperature_polyline = appendSVGElement(svg, "polyline", {"class": "data temperature", "points": temperature_points_str});
|
||||
//temperature_polyline.onmouseover = () => { console.log("yup"); };
|
||||
pgraph.replaceChildren(graph);
|
||||
}
|
||||
})
|
||||
await sleep(10000);
|
||||
}
|
||||
}
|
||||
|
||||
async function set_temperature_dialog() {
|
||||
var target = null;
|
||||
await fetch("/api/v1/latest").then((response) => response.json()).then((json) => {
|
||||
console.log(json);
|
||||
if (json.target) {
|
||||
if (json.target < 5) {
|
||||
target = 5;
|
||||
} else if (json.target > 25) {
|
||||
target = 25;
|
||||
} else {
|
||||
target = json.target;
|
||||
}
|
||||
}
|
||||
});
|
||||
var dialog = document.querySelector("dialog#set-target");
|
||||
dialog.showModal();
|
||||
var target_label = document.querySelector("label#target-label");
|
||||
var target_input = document.querySelector("input#target");
|
||||
console.log(target);
|
||||
target_input.value = target;
|
||||
target_label.innerHTML = `Target temperature is ${target}°C`;
|
||||
target_input.addEventListener("input", (i) => { target_label.innerHTML = `Target temperature is ${i.target.value}°C`; });
|
||||
document.querySelector("span.cancel-x").addEventListener("click", () => { dialog.close(); });
|
||||
document.querySelector("button#set-target").addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
dialog.close();
|
||||
var temperature = target_input.value;
|
||||
wallas_command(`temperature/${temperature}`, "Wallas Heater did not accept the command", "Temperature target set to ${temperature}°C");
|
||||
});
|
||||
}
|
||||
|
||||
async function wallas_command(cmd, no_atok, success) {
|
||||
var dialog = document.querySelector("dialog#message");
|
||||
dialog.addEventListener('cancel', (event) => { event.preventDefault(); });
|
||||
dialog.innerHTML = "Sending command....";
|
||||
dialog.showModal();
|
||||
fetch(`/api/v1/cmd/${cmd}`).then((response) => {
|
||||
if (response.status == 423) {
|
||||
dialog.innerHTML = "Busy executing other command, try again later";
|
||||
} else if (response.status == 504) {
|
||||
dialog.innerHTML = no_atok;
|
||||
} else if (response.status == 204) {
|
||||
dialog.innerHTML = success;
|
||||
} else {
|
||||
dialog.innerHTML = "Trouble communicating with Wallas Heater, try again later";
|
||||
}
|
||||
setTimeout(() => {
|
||||
dialog.close();
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
|
||||
BIN
src/static/espressif-logo.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src/static/favicon.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
9
src/static/prometheus-template.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# TYPE wallas_heater_target gauge
|
||||
wallas_heater_target {target}
|
||||
# TYPE wallas_heater_temperature gauge
|
||||
wallas_heater_temperature {temperature}
|
||||
# TYPE wallas_heater_command gauge
|
||||
wallas_heater_command {command}
|
||||
# TYPE wallas_heater_state gauge
|
||||
wallas_heter_state {state}
|
||||
|
||||
BIN
src/static/risc-v-logo.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
115
src/static/styles.css
Normal file
@@ -0,0 +1,115 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
font-family: serif;
|
||||
text-align: center;
|
||||
color: #321C0B;
|
||||
background-color: #D5E4F6;
|
||||
height: 100%;
|
||||
}
|
||||
h1 {
|
||||
color: #B15C1B;
|
||||
}
|
||||
h4 {
|
||||
color: #2969B2;
|
||||
}
|
||||
p.intro {
|
||||
border: 1px solid #B15C1B;
|
||||
padding: 0.2em;
|
||||
margin: 0.3em 10vw 0.3em 10vw;
|
||||
}
|
||||
div.opaque {
|
||||
background: rgba(213, 228, 246, 0.8);
|
||||
}
|
||||
p#graph {
|
||||
border: 1px solid #B15C1B;
|
||||
padding: 0.2em;
|
||||
margin: 0.3em 15vw 0.3em 15vw;
|
||||
height: 30vw;
|
||||
line-height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
p#latest {
|
||||
border: 1px solid #B15C1B;
|
||||
padding: 0.2em;
|
||||
margin: 0.3em 10vw 0.3em 10vw;
|
||||
opacity: 1;
|
||||
}
|
||||
span.cancel-x {
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
top: 0.5em;
|
||||
right: 0.5em;
|
||||
cursor: default;
|
||||
color: #9f0000;
|
||||
}
|
||||
span.cancel-x:hover {
|
||||
color: #ff0000;
|
||||
}
|
||||
div#footer {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
font-size: 70%;
|
||||
width: 100%;
|
||||
padding-bottom: 0.3em;
|
||||
z-index: -1;
|
||||
}
|
||||
dialog#set-target {
|
||||
padding: 1em;
|
||||
width: 25em;
|
||||
}
|
||||
dialog#set-target form {
|
||||
margin-top: 2em;
|
||||
}
|
||||
dialog#set-target button {
|
||||
margin-top: 2em;
|
||||
}
|
||||
dialog.dialog-center {
|
||||
position: fixed;
|
||||
background: rgb(213, 228, 246);
|
||||
z-index: 3;
|
||||
width: 25em;
|
||||
border: 2px solid #ef7606;
|
||||
}
|
||||
div#footer > a > img {
|
||||
padding-left: 3vw;
|
||||
padding-right: 3vw;
|
||||
}
|
||||
button {
|
||||
background-color: rgb(160, 171, 185);
|
||||
border: 2px solid rgb(160, 171, 185);
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
button:hover {
|
||||
border: 2px solid black;
|
||||
background-color: #ef7606;
|
||||
}
|
||||
polyline.data {
|
||||
stroke-width: 2px;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
fill: none;
|
||||
}
|
||||
polyline.temperature {
|
||||
stroke: #0079d9;
|
||||
}
|
||||
polyline.target {
|
||||
stroke: #B15C1B;
|
||||
stroke-dasharray: 0.5,1;
|
||||
}
|
||||
polyline.grid {
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
stroke: rgb(160, 171, 185);
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 5,7;
|
||||
}
|
||||
polyline.grid-zero {
|
||||
stroke: rgb(107, 114, 123);
|
||||
stroke-dasharray: 5,3;
|
||||
}
|
||||
BIN
src/static/wallas-logo.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
@@ -45,7 +45,7 @@ use esp_hal::peripherals::TIMG0;
|
||||
use esp_hal::peripherals::WIFI;
|
||||
use esp_hal::rng::Rng;
|
||||
use esp_hal::timer::timg::TimerGroup;
|
||||
use esp_hal::Blocking;
|
||||
// use esp_hal::Blocking;
|
||||
|
||||
use heapless::String;
|
||||
|
||||
@@ -70,7 +70,7 @@ pub static STOP_WIFI_SIGNAL: Signal<CriticalSectionRawMutex, ()> = Signal::new()
|
||||
/// Connect to WiFi
|
||||
pub async fn connect(
|
||||
spawner: Spawner,
|
||||
timg0: TimerGroup<'static, TIMG0, Blocking>,
|
||||
timg0: TimerGroup<TIMG0>,
|
||||
rng: Rng,
|
||||
wifi: WIFI,
|
||||
radio_clock_control: RADIO_CLK,
|
||||
|
||||