init
This commit is contained in:
commit
4e48060bfa
18 changed files with 2273 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Cargo.toml
|
||||||
|
target
|
||||||
|
.env
|
||||||
|
result
|
478
Cargo.lock
generated
Normal file
478
Cargo.lock
generated
Normal file
|
@ -0,0 +1,478 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-polyfill"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bare-metal"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bare-metal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bxcan"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40ac3d0c0a542d0ab5521211f873f62706a7136df415676f676d347e5a41dd80"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"nb 1.1.0",
|
||||||
|
"vcell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "canome"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bxcan",
|
||||||
|
"critical-section",
|
||||||
|
"defmt",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"heapless",
|
||||||
|
"nb 1.1.0",
|
||||||
|
"rtic-monotonics",
|
||||||
|
"stm32f1xx-hal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
|
||||||
|
dependencies = [
|
||||||
|
"bare-metal 0.2.5",
|
||||||
|
"bitfield",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"volatile-register",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-rt"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2722f5b7d6ea8583cffa4d247044e280ccbb9fe501bed56552e2ba48b02d5f3d"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m-rt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-rt-macros"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "critical-section"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3939552907426de152b3c2c6f51ed53f98f448babd26f28694c95f5906194595"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"defmt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-macros"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18bdc7a7b92ac413e19e95240e75d3a73a8d8e78aa24a594c22cbb4d44b4bbda"
|
||||||
|
dependencies = [
|
||||||
|
"defmt-parser",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.60",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-parser"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-dma"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446"
|
||||||
|
dependencies = [
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||||
|
dependencies = [
|
||||||
|
"nb 0.1.3",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fugit"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
|
||||||
|
dependencies = [
|
||||||
|
"gcd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fugit-timer"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9607bfc4c388f9d629704f56ede4a007546cad417b3bcd6fc7c87dc7edce04a"
|
||||||
|
dependencies = [
|
||||||
|
"fugit",
|
||||||
|
"nb 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gcd"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
|
dependencies = [
|
||||||
|
"hash32",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||||
|
dependencies = [
|
||||||
|
"nb 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-common"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0786b50b81ef9d2a944a000f60405bb28bf30cd45da2d182f3fe636b2321f35c"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-monotonics"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"cfg-if",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"fugit",
|
||||||
|
"rtic-time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-time"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"futures-util",
|
||||||
|
"rtic-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stm32f1"
|
||||||
|
version = "0.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2dc80735831c28fe85384e1e28428fb6d201f67c696e369a239ed9c5eba369d"
|
||||||
|
dependencies = [
|
||||||
|
"bare-metal 1.0.0",
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"vcell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stm32f1xx-hal"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30845662b9ce46a2ec04da97666a2b32458bee5032bb0452d0caf1536a96a542"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"bxcan",
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"embedded-dma",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"fugit",
|
||||||
|
"fugit-timer",
|
||||||
|
"nb 1.1.0",
|
||||||
|
"stm32f1",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.59"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.59"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.60",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcell"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "void"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "volatile-register"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
|
||||||
|
dependencies = [
|
||||||
|
"vcell",
|
||||||
|
]
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = [
|
||||||
|
"canome"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
157
LICENSE.md
Normal file
157
LICENSE.md
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
# GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc.
|
||||||
|
<https://fsf.org/>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates the
|
||||||
|
terms and conditions of version 3 of the GNU General Public License,
|
||||||
|
supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
## 0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the
|
||||||
|
GNU General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License, other
|
||||||
|
than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
## 1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
## 2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
- a) under this License, provided that you make a good faith effort
|
||||||
|
to ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
- b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
## 3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from a
|
||||||
|
header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
- a) Give prominent notice with each copy of the object code that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
- b) Accompany the object code with a copy of the GNU GPL and this
|
||||||
|
license document.
|
||||||
|
|
||||||
|
## 4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that, taken
|
||||||
|
together, effectively do not restrict modification of the portions of
|
||||||
|
the Library contained in the Combined Work and reverse engineering for
|
||||||
|
debugging such modifications, if you also do each of the following:
|
||||||
|
|
||||||
|
- a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
- b) Accompany the Combined Work with a copy of the GNU GPL and this
|
||||||
|
license document.
|
||||||
|
- c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
- d) Do one of the following:
|
||||||
|
- 0) Convey the Minimal Corresponding Source under the terms of
|
||||||
|
this License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
- 1) Use a suitable shared library mechanism for linking with
|
||||||
|
the Library. A suitable mechanism is one that (a) uses at run
|
||||||
|
time a copy of the Library already present on the user's
|
||||||
|
computer system, and (b) will operate properly with a modified
|
||||||
|
version of the Library that is interface-compatible with the
|
||||||
|
Linked Version.
|
||||||
|
- e) Provide Installation Information, but only if you would
|
||||||
|
otherwise be required to provide such information under section 6
|
||||||
|
of the GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the Application
|
||||||
|
with a modified version of the Linked Version. (If you use option
|
||||||
|
4d0, the Installation Information must accompany the Minimal
|
||||||
|
Corresponding Source and Corresponding Application Code. If you
|
||||||
|
use option 4d1, you must provide the Installation Information in
|
||||||
|
the manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.)
|
||||||
|
|
||||||
|
## 5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the Library
|
||||||
|
side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
- a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities, conveyed under the terms of this License.
|
||||||
|
- b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
## 6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
as you received it specifies that a certain numbered version of the
|
||||||
|
GNU Lesser General Public License "or any later version" applies to
|
||||||
|
it, you have the option of following the terms and conditions either
|
||||||
|
of that published version or of any later version published by the
|
||||||
|
Free Software Foundation. If the Library as you received it does not
|
||||||
|
specify a version number of the GNU Lesser General Public License, you
|
||||||
|
may choose any version of the GNU Lesser General Public License ever
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
33
README.md
Normal file
33
README.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
CANome is a library that assists you in writing your own CAN based, fully distributed, smart home system.
|
||||||
|
|
||||||
|
A bus system provides many benefits over other system. Some of them are listed below:
|
||||||
|
- **Energy Efficiency:** Each device only needs to implement the fairly lightweight CAN stack. The bus cable can also transport the required power for the nodes, reducing the numbers of AC-DC-adapters.
|
||||||
|
- **Reliability:** A distributed system can handle the loss of nodes without impacting the complete system. Additionally, a wired channel is typically more reliable than a wireless one.
|
||||||
|
- **Security:** Embedded security is hard, using a bus doesn't make your application more secure, but you have a greatly reduced attack surface: An attacker needs physical access to your bus to even start to communicate with a node.
|
||||||
|
|
||||||
|
Many other smart home projects use a meta-configuration-language to configure the firmware on each device.
|
||||||
|
A distributed system requires more logic on the nodes than a centralized one.
|
||||||
|
A meta-configuration-language will always have missing features that are a core component of a regular programming language, like rust.
|
||||||
|
|
||||||
|
This project helps you in creating a distributed smart home system in rust by providing a library with the building blocks.
|
||||||
|
You express the entire node logic as regular embedded rust code using this library to avoid writing extensive repeating code.
|
||||||
|
|
||||||
|
# Protocol
|
||||||
|
This project uses a very simple protocol that is based on CAN.
|
||||||
|
Each node can expose states.
|
||||||
|
A state is a Rust type that can be encoded/decoded from a CAN frame.
|
||||||
|
The format of a frame is implicitly transmitted by the CAN message identifier.
|
||||||
|
When a state changes, the node broadcasts the new state on the bus.
|
||||||
|
|
||||||
|
Other nodes can "subscribe" to a CAN message identifier.
|
||||||
|
They always store a copy of the state that is automatically updated when a new message with that CAN message identifier arrives.
|
||||||
|
Additionally, a node can use a CAN remote frame to fetch the state from the source node.
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
I'm happy about any contribution in any form.
|
||||||
|
Feel free to submit feature requests and bug reports using a GitHub Issue.
|
||||||
|
PR's are also appreciated.
|
||||||
|
|
||||||
|
# License
|
||||||
|
The Sourcecode in this Library is licensed under [LGPLv3](https://www.gnu.org/licenses/lgpl-3.0.en.html).
|
540
canome/Cargo.lock
generated
Normal file
540
canome/Cargo.lock
generated
Normal file
|
@ -0,0 +1,540 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-polyfill"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bare-metal"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bare-metal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bxcan"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40ac3d0c0a542d0ab5521211f873f62706a7136df415676f676d347e5a41dd80"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"nb 1.1.0",
|
||||||
|
"vcell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
|
||||||
|
dependencies = [
|
||||||
|
"bare-metal 0.2.5",
|
||||||
|
"bitfield",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"volatile-register",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-rt"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m-rt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m-rt-macros"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "critical-section"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3939552907426de152b3c2c6f51ed53f98f448babd26f28694c95f5906194595"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"defmt-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-macros"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18bdc7a7b92ac413e19e95240e75d3a73a8d8e78aa24a594c22cbb4d44b4bbda"
|
||||||
|
dependencies = [
|
||||||
|
"defmt-parser",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.52",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "defmt-parser"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-dma"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446"
|
||||||
|
dependencies = [
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||||
|
dependencies = [
|
||||||
|
"nb 0.1.3",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fugit"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
|
||||||
|
dependencies = [
|
||||||
|
"gcd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fugit-timer"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9607bfc4c388f9d629704f56ede4a007546cad417b3bcd6fc7c87dc7edce04a"
|
||||||
|
dependencies = [
|
||||||
|
"fugit",
|
||||||
|
"nb 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gcd"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
|
dependencies = [
|
||||||
|
"hash32",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||||
|
dependencies = [
|
||||||
|
"nb 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pfzetto-smarthome"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bxcan",
|
||||||
|
"critical-section",
|
||||||
|
"defmt",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"heapless",
|
||||||
|
"hex",
|
||||||
|
"rtic",
|
||||||
|
"rtic-monotonics",
|
||||||
|
"stm32f1xx-hal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c443db16326376bdd64377da268f6616d5f804aba8ce799bac7d1f7f244e9d51"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"bare-metal 1.0.0",
|
||||||
|
"critical-section",
|
||||||
|
"rtic-core",
|
||||||
|
"rtic-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-common"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0786b50b81ef9d2a944a000f60405bb28bf30cd45da2d182f3fe636b2321f35c"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-core"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-macros"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54053598ea24b1b74937724e366558412a1777eb2680b91ef646db540982789a"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.52",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-monotonics"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "058c2397dbd5bb4c5650a0e368c3920953e458805ff5097a0511b8147b3619d7"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"cfg-if",
|
||||||
|
"cortex-m",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"fugit",
|
||||||
|
"rtic-time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rtic-time"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75b232e7aebc045cfea81cdd164bc2727a10aca9a4568d406d0a5661cdfd0f19"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"futures-util",
|
||||||
|
"rtic-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stm32f1"
|
||||||
|
version = "0.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2dc80735831c28fe85384e1e28428fb6d201f67c696e369a239ed9c5eba369d"
|
||||||
|
dependencies = [
|
||||||
|
"bare-metal 1.0.0",
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"vcell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stm32f1xx-hal"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30845662b9ce46a2ec04da97666a2b32458bee5032bb0452d0caf1536a96a542"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"bxcan",
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"embedded-dma",
|
||||||
|
"embedded-hal 0.2.7",
|
||||||
|
"fugit",
|
||||||
|
"fugit-timer",
|
||||||
|
"nb 1.1.0",
|
||||||
|
"stm32f1",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.52"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.52",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcell"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "void"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "volatile-register"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
|
||||||
|
dependencies = [
|
||||||
|
"vcell",
|
||||||
|
]
|
26
canome/Cargo.toml
Normal file
26
canome/Cargo.toml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[package]
|
||||||
|
name = "canome"
|
||||||
|
description = "a library that helps building a CAN-based smart home system"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = [ "Paul Zinselmeyer <info@pfzetto.de>" ]
|
||||||
|
license = "LGPL-3.0-or-later"
|
||||||
|
edition = "2021"
|
||||||
|
repository = "https://github.com/pfzetto/canome"
|
||||||
|
keywords = [ "CAN", "embedded", "smarthome" ]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
defmt = "0.3"
|
||||||
|
|
||||||
|
heapless = { version = "0.8.0", default-features = false }
|
||||||
|
critical-section = "1.0"
|
||||||
|
|
||||||
|
embedded-hal = { version = "0.2.7", optional = true }
|
||||||
|
stm32f1xx-hal = { version = "0.10.0", optional = true }
|
||||||
|
rtic-monotonics = { version = "1.0.0", optional = true }
|
||||||
|
bxcan = { version = "0.7.0", optional = true }
|
||||||
|
nb = "1.1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = [ "can", "stm32" ]
|
||||||
|
can = [ "dep:bxcan" ]
|
||||||
|
stm32 = [ "dep:embedded-hal", "dep:stm32f1xx-hal", "dep:rtic-monotonics", "dep:bxcan" ]
|
293
canome/src/bus.rs
Normal file
293
canome/src/bus.rs
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
use core::{
|
||||||
|
cell::RefCell,
|
||||||
|
fmt::Debug,
|
||||||
|
future::{poll_fn, Future},
|
||||||
|
marker::PhantomData,
|
||||||
|
task::{Poll, Waker},
|
||||||
|
};
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use heapless::{LinearMap, Vec};
|
||||||
|
|
||||||
|
use crate::{BusBackend, Channel, Decode, Encode};
|
||||||
|
|
||||||
|
pub struct Bus<B: BusBackend, const CHANNELS: usize, const WAKER_CAP: usize> {
|
||||||
|
rx_queues: critical_section::Mutex<RefCell<LinearMap<B::ID, BusInner<B, WAKER_CAP>, CHANNELS>>>,
|
||||||
|
backend: B,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BusError<B: BusBackend> {
|
||||||
|
Backend(B::Error),
|
||||||
|
ChannelCapExceeded,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct BusInner<B: BusBackend, const WAKER_CAP: usize> {
|
||||||
|
state: B::Data,
|
||||||
|
wakers: Vec<Waker, WAKER_CAP>,
|
||||||
|
sender: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BusChannelTransceiver<'a, B: BusBackend, C: Channel<B>> {
|
||||||
|
bus: &'a dyn BusBehaviour<B>,
|
||||||
|
_channel: PhantomData<C>,
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BusChannelReceiver<'a, B: BusBackend, C: Channel<B>> {
|
||||||
|
bus: &'a dyn BusBehaviour<B>,
|
||||||
|
_channel: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BusBehaviour<B: BusBackend> {
|
||||||
|
fn get(&self, id: B::ID) -> Option<B::Data>;
|
||||||
|
fn register_waker(&self, id: B::ID, waker: &Waker);
|
||||||
|
fn publish(&self, id: B::ID, value: B::Data) -> Result<(), BusError<B>>;
|
||||||
|
fn publish_local(&self, id: &B::ID, value: B::Data);
|
||||||
|
fn request(&self, id: B::ID) -> Result<(), BusError<B>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BusBackend, T: BusBehaviour<B>> BusBehaviour<B> for &T {
|
||||||
|
fn get(&self, id: <B as BusBackend>::ID) -> Option<<B as BusBackend>::Data> {
|
||||||
|
self.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_waker(&self, id: <B as BusBackend>::ID, waker: &Waker) {
|
||||||
|
self.register_waker(id, waker)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn publish(
|
||||||
|
&self,
|
||||||
|
id: <B as BusBackend>::ID,
|
||||||
|
value: <B as BusBackend>::Data,
|
||||||
|
) -> Result<(), BusError<B>> {
|
||||||
|
self.publish(id, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn publish_local(&self, id: &<B as BusBackend>::ID, value: <B as BusBackend>::Data) {
|
||||||
|
self.publish_local(id, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request(&self, id: <B as BusBackend>::ID) -> Result<(), BusError<B>> {
|
||||||
|
self.request(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BusRx<C: Channel<B>, B: BusBackend> {
|
||||||
|
fn get(&self) -> C::State;
|
||||||
|
fn request(&self) -> Result<(), BusError<B>>;
|
||||||
|
fn next(&self) -> impl Future<Output = C::State>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BusTx<C: Channel<B>, B: BusBackend> {
|
||||||
|
fn publish(&self, value: C::State) -> Result<(), BusError<B>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BusBackend, const CHANNELS: usize, const WAKER_CAP: usize> BusBehaviour<B>
|
||||||
|
for Bus<B, CHANNELS, WAKER_CAP>
|
||||||
|
where
|
||||||
|
B::ID: Eq,
|
||||||
|
{
|
||||||
|
fn get(&self, id: B::ID) -> Option<B::Data> {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
self.rx_queues
|
||||||
|
.borrow(cs)
|
||||||
|
.borrow_mut()
|
||||||
|
.get_mut(&id)
|
||||||
|
.map(|x| x.state.clone())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_waker(&self, id: B::ID, waker: &Waker) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
if let Some(inner) = self.rx_queues.borrow(cs).borrow_mut().get_mut(&id) {
|
||||||
|
if inner.wakers.iter().any(|ew| waker.will_wake(ew)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if inner.wakers.is_full() {
|
||||||
|
inner.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
if inner.wakers.push(waker.clone()).is_err() {
|
||||||
|
panic!("tried to push a waker to a zero length waker list");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn publish(&self, id: B::ID, value: B::Data) -> Result<(), BusError<B>> {
|
||||||
|
self.publish_local(&id, value.clone());
|
||||||
|
|
||||||
|
self.backend
|
||||||
|
.publish(id, Some(value))
|
||||||
|
.map_err(BusError::Backend)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn publish_local(&self, id: &B::ID, value: B::Data) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
if let Some(inner) = self.rx_queues.borrow(cs).borrow_mut().get_mut(id) {
|
||||||
|
inner.state = value.clone();
|
||||||
|
inner.wake();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request(&self, id: B::ID) -> Result<(), BusError<B>> {
|
||||||
|
self.backend.publish(id, None).map_err(BusError::Backend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BusBackend, const WAKER_CAP: usize> BusInner<B, WAKER_CAP> {
|
||||||
|
fn wake(&mut self) {
|
||||||
|
let mut wakers = Vec::new();
|
||||||
|
core::mem::swap(&mut wakers, &mut self.wakers);
|
||||||
|
wakers.into_iter().for_each(|w| w.wake());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BusBackend, const SUBS: usize, const WAKER_CAP: usize> Bus<B, SUBS, WAKER_CAP>
|
||||||
|
where
|
||||||
|
B::ID: Eq,
|
||||||
|
{
|
||||||
|
pub const fn new(backend: B) -> Self {
|
||||||
|
Self {
|
||||||
|
rx_queues: Mutex::new(RefCell::new(LinearMap::new())),
|
||||||
|
backend,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transceiver<C: Channel<B>>(&self) -> Result<BusChannelTransceiver<'_, B, C>, BusError<B>>
|
||||||
|
where
|
||||||
|
C::State: Default + Encode<B::Data>,
|
||||||
|
{
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut rx_queues = self.rx_queues.borrow(cs).borrow_mut();
|
||||||
|
if let Some(existing) = rx_queues.get_mut(&C::ID) {
|
||||||
|
existing.sender = true;
|
||||||
|
Ok(BusChannelTransceiver {
|
||||||
|
bus: self,
|
||||||
|
_channel: PhantomData,
|
||||||
|
})
|
||||||
|
} else if rx_queues.len() < SUBS {
|
||||||
|
self.request(C::ID).ok();
|
||||||
|
let _ = rx_queues.insert(
|
||||||
|
C::ID,
|
||||||
|
BusInner {
|
||||||
|
state: C::State::default().encode(),
|
||||||
|
wakers: Vec::new(),
|
||||||
|
sender: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Ok(BusChannelTransceiver {
|
||||||
|
bus: self,
|
||||||
|
_channel: PhantomData,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(BusError::ChannelCapExceeded)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receiver<C: Channel<B>>(&self) -> Result<BusChannelReceiver<'_, B, C>, BusError<B>>
|
||||||
|
where
|
||||||
|
C::State: Default + Encode<B::Data>,
|
||||||
|
{
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut rx_queues = self.rx_queues.borrow(cs).borrow_mut();
|
||||||
|
if rx_queues.contains_key(&C::ID) {
|
||||||
|
Ok(BusChannelReceiver {
|
||||||
|
bus: self,
|
||||||
|
_channel: PhantomData,
|
||||||
|
})
|
||||||
|
} else if rx_queues.len() < SUBS {
|
||||||
|
self.request(C::ID).ok();
|
||||||
|
let _ = rx_queues.insert(
|
||||||
|
C::ID,
|
||||||
|
BusInner {
|
||||||
|
state: C::State::default().encode(),
|
||||||
|
wakers: Vec::new(),
|
||||||
|
sender: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Ok(BusChannelReceiver {
|
||||||
|
bus: self,
|
||||||
|
_channel: PhantomData,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(BusError::ChannelCapExceeded)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backend(&self) -> &B {
|
||||||
|
&self.backend
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, B: BusBackend, C: Channel<B>> BusTx<C, B> for BusChannelTransceiver<'a, B, C>
|
||||||
|
where
|
||||||
|
C::State: Encode<B::Data>,
|
||||||
|
{
|
||||||
|
fn publish(&self, value: C::State) -> Result<(), BusError<B>> {
|
||||||
|
self.bus.publish(C::ID, value.encode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, B: BusBackend, C> BusRx<C, B> for BusChannelTransceiver<'a, B, C>
|
||||||
|
where
|
||||||
|
C::State: Decode<B::Data> + PartialEq + Default,
|
||||||
|
C: Channel<B>,
|
||||||
|
{
|
||||||
|
fn get(&self) -> C::State {
|
||||||
|
self.bus
|
||||||
|
.get(C::ID)
|
||||||
|
.and_then(|x| C::State::decode(x).ok())
|
||||||
|
.expect("valid group")
|
||||||
|
}
|
||||||
|
fn request(&self) -> Result<(), BusError<B>> {
|
||||||
|
self.bus.request(C::ID)
|
||||||
|
}
|
||||||
|
async fn next(&self) -> C::State {
|
||||||
|
let old_state: C::State = self.get();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
self.bus.register_waker(C::ID, cx.waker());
|
||||||
|
|
||||||
|
let state: C::State = self.get();
|
||||||
|
match state == old_state {
|
||||||
|
false => Poll::Ready(state),
|
||||||
|
true => Poll::Pending,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, B: BusBackend, C> BusRx<C, B> for BusChannelReceiver<'a, B, C>
|
||||||
|
where
|
||||||
|
C::State: Decode<B::Data> + PartialEq + Default,
|
||||||
|
C: Channel<B>,
|
||||||
|
{
|
||||||
|
fn get(&self) -> C::State {
|
||||||
|
self.bus
|
||||||
|
.get(C::ID)
|
||||||
|
.and_then(|x| C::State::decode(x).ok())
|
||||||
|
.expect("valid group")
|
||||||
|
}
|
||||||
|
fn request(&self) -> Result<(), BusError<B>> {
|
||||||
|
self.bus.request(C::ID)
|
||||||
|
}
|
||||||
|
async fn next(&self) -> C::State {
|
||||||
|
let old_state: C::State = self.get();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
self.bus.register_waker(C::ID, cx.waker());
|
||||||
|
|
||||||
|
let state: C::State = self.get();
|
||||||
|
match state == old_state {
|
||||||
|
false => Poll::Ready(state),
|
||||||
|
true => Poll::Pending,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
177
canome/src/can.rs
Normal file
177
canome/src/can.rs
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
use core::{fmt::Debug, marker::ConstParamTy};
|
||||||
|
|
||||||
|
use crate::{bus::BusBehaviour, BusBackend, BusDataFormat};
|
||||||
|
use bxcan::{ExtendedId, Id, Instance, Rx0, StandardId, Tx};
|
||||||
|
use defmt::Format;
|
||||||
|
use heapless::mpmc::MpMcQueue;
|
||||||
|
|
||||||
|
pub struct BxCanBus<const TX_CAP: usize> {
|
||||||
|
tx_queue: MpMcQueue<(<Self as BusBackend>::ID, Option<<Self as BusBackend>::Data>), TX_CAP>,
|
||||||
|
on_publish: &'static (dyn Fn(&Self) + Send + Sync),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const TX_CAP: usize> Debug for BxCanBus<TX_CAP> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("BxCanBus").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, ConstParamTy, Debug, Clone, Copy)]
|
||||||
|
pub enum BxCanId {
|
||||||
|
Standard(u16),
|
||||||
|
Extended(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BxCanBusError {
|
||||||
|
/// An object couldn't be added to the TX queue of [`BxCanBus`] because it is full.
|
||||||
|
TxQueueFull,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const TX_CAP: usize> BusBackend for BxCanBus<TX_CAP> {
|
||||||
|
type ID = BxCanId;
|
||||||
|
|
||||||
|
type Data = CanDataFormat;
|
||||||
|
|
||||||
|
type Error = BxCanBusError;
|
||||||
|
|
||||||
|
fn publish(&self, id: Self::ID, data: Option<Self::Data>) -> Result<(), Self::Error> {
|
||||||
|
self.tx_queue
|
||||||
|
.enqueue((id, data))
|
||||||
|
.map_err(|_| Self::Error::TxQueueFull)?;
|
||||||
|
|
||||||
|
(self.on_publish)(self);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const TX_CAP: usize> Default for BxCanBus<TX_CAP> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
tx_queue: MpMcQueue::new(),
|
||||||
|
on_publish: &Self::empty_hook,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const TX_CAP: usize> BxCanBus<TX_CAP> {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
tx_queue: MpMcQueue::new(),
|
||||||
|
on_publish: &Self::empty_hook,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn empty_hook(&self) {}
|
||||||
|
|
||||||
|
pub const fn new_with_hook(on_publish: &'static (dyn Fn(&Self) + Send + Sync)) -> Self {
|
||||||
|
Self {
|
||||||
|
tx_queue: MpMcQueue::new(),
|
||||||
|
on_publish,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_tx_interrupt<I: Instance>(
|
||||||
|
&self,
|
||||||
|
can_tx: &mut Tx<I>,
|
||||||
|
) -> Result<(), BxCanBusError> {
|
||||||
|
let rx = &self.tx_queue;
|
||||||
|
|
||||||
|
while let Some((channel_id, data)) = rx.dequeue() {
|
||||||
|
let frame = encode_frame(channel_id, data);
|
||||||
|
match can_tx.transmit(&frame) {
|
||||||
|
Ok(status) => match status.dequeued_frame().map(decode_frame) {
|
||||||
|
None => {}
|
||||||
|
Some((channel_id, data)) => rx
|
||||||
|
.enqueue((channel_id, data))
|
||||||
|
.map_err(|_| BxCanBusError::TxQueueFull)?,
|
||||||
|
},
|
||||||
|
Err(nb::Error::WouldBlock) => {
|
||||||
|
rx.enqueue((channel_id, data))
|
||||||
|
.map_err(|_| BxCanBusError::TxQueueFull)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(_) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn handle_rx_interrupt<I: Instance>(
|
||||||
|
&self,
|
||||||
|
can_rx: &mut Rx0<I>,
|
||||||
|
bus: impl BusBehaviour<Self>,
|
||||||
|
) -> Result<(), BxCanBusError> {
|
||||||
|
loop {
|
||||||
|
match can_rx.receive() {
|
||||||
|
Ok(frame) => {
|
||||||
|
let (channel_id, data) = decode_frame(&frame);
|
||||||
|
if let Some(data) = data {
|
||||||
|
bus.publish_local(&channel_id, data);
|
||||||
|
} else if let Some(state) = bus.get(channel_id) {
|
||||||
|
self.tx_queue
|
||||||
|
.enqueue((channel_id, Some(state)))
|
||||||
|
.map_err(|_| BxCanBusError::TxQueueFull)?;
|
||||||
|
|
||||||
|
(self.on_publish)(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(nb::Error::WouldBlock) => break,
|
||||||
|
Err(nb::Error::Other(_)) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Format, Debug)]
|
||||||
|
pub struct CanDataFormat {
|
||||||
|
length: u8,
|
||||||
|
data: [u8; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CanDataFormat {
|
||||||
|
pub fn new(data: &[u8]) -> Self {
|
||||||
|
assert!(data.len() <= 8);
|
||||||
|
|
||||||
|
let mut padded = [0_u8; 8];
|
||||||
|
padded[0..data.len()].copy_from_slice(data);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
length: data.len() as u8,
|
||||||
|
data: padded,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> &[u8] {
|
||||||
|
&self.data[..self.length as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BusDataFormat for CanDataFormat {}
|
||||||
|
|
||||||
|
fn decode_frame(frame: &bxcan::Frame) -> (BxCanId, Option<CanDataFormat>) {
|
||||||
|
let id = match frame.id() {
|
||||||
|
Id::Standard(id) => BxCanId::Standard(id.as_raw()),
|
||||||
|
Id::Extended(id) => BxCanId::Extended(id.as_raw()),
|
||||||
|
};
|
||||||
|
if let Some(data) = frame.data() {
|
||||||
|
(id, Some(CanDataFormat::new(data)))
|
||||||
|
} else {
|
||||||
|
(id, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_frame(id: BxCanId, data: Option<CanDataFormat>) -> bxcan::Frame {
|
||||||
|
let id = match id {
|
||||||
|
BxCanId::Standard(id) => Id::Standard(StandardId::new(id).unwrap()),
|
||||||
|
BxCanId::Extended(id) => Id::Extended(ExtendedId::new(id).unwrap()),
|
||||||
|
};
|
||||||
|
if let Some(data) = data {
|
||||||
|
bxcan::Frame::new_data(id, bxcan::Data::new(data.data()).expect("valid can data"))
|
||||||
|
} else {
|
||||||
|
bxcan::Frame::new_remote(id, 8)
|
||||||
|
}
|
||||||
|
}
|
52
canome/src/contact.rs
Normal file
52
canome/src/contact.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use defmt::Format;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Format, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ContactState {
|
||||||
|
#[default]
|
||||||
|
Open,
|
||||||
|
Closed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContactState {
|
||||||
|
pub fn is_open(&self) -> bool {
|
||||||
|
matches!(self, Self::Open)
|
||||||
|
}
|
||||||
|
pub fn is_closed(&self) -> bool {
|
||||||
|
matches!(self, Self::Closed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "can")]
|
||||||
|
mod can {
|
||||||
|
use crate::{can::CanDataFormat, Decode, Encode};
|
||||||
|
|
||||||
|
use super::ContactState;
|
||||||
|
|
||||||
|
impl Encode<CanDataFormat> for ContactState {
|
||||||
|
fn encode(self) -> CanDataFormat {
|
||||||
|
let data = match self {
|
||||||
|
Self::Open => 0,
|
||||||
|
Self::Closed => 1,
|
||||||
|
};
|
||||||
|
CanDataFormat::new(&[data])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode<CanDataFormat> for ContactState {
|
||||||
|
fn decode(value: CanDataFormat) -> Result<Self, CanDataFormat>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let data = value.data();
|
||||||
|
if data.len() != 1 {
|
||||||
|
return Err(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match data[0] & 1 {
|
||||||
|
0 => Self::Open,
|
||||||
|
1 => Self::Closed,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
canome/src/cover.rs
Normal file
51
canome/src/cover.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use defmt::Format;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Format, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct CoverState {
|
||||||
|
pub tilt: u16,
|
||||||
|
pub position: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CoverState {
|
||||||
|
pub fn is_open(&self) -> bool {
|
||||||
|
self.position > 0
|
||||||
|
}
|
||||||
|
pub fn is_closed(&self) -> bool {
|
||||||
|
self.position == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "can")]
|
||||||
|
mod can {
|
||||||
|
use crate::{can::CanDataFormat, Decode, Encode};
|
||||||
|
|
||||||
|
use super::CoverState;
|
||||||
|
|
||||||
|
impl Encode<CanDataFormat> for CoverState {
|
||||||
|
fn encode(self) -> CanDataFormat {
|
||||||
|
let hue = self.tilt.to_be_bytes();
|
||||||
|
let saturation = self.position.to_be_bytes();
|
||||||
|
CanDataFormat::new(&[hue[0], hue[1], saturation[0], saturation[1]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode<CanDataFormat> for CoverState {
|
||||||
|
fn decode(value: CanDataFormat) -> Result<Self, CanDataFormat>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let data = value.data();
|
||||||
|
if data.len() != 4 {
|
||||||
|
return Err(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hue = u16::from_be_bytes([data[0], data[1]]);
|
||||||
|
let saturation = u16::from_be_bytes([data[2], data[3]]);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
tilt: hue,
|
||||||
|
position: saturation,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
canome/src/lib.rs
Normal file
36
canome/src/lib.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#![no_std]
|
||||||
|
#![feature(adt_const_params)]
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
pub mod bus;
|
||||||
|
#[cfg(feature = "can")]
|
||||||
|
pub mod can;
|
||||||
|
pub mod contact;
|
||||||
|
pub mod cover;
|
||||||
|
pub mod light;
|
||||||
|
pub mod state;
|
||||||
|
#[cfg(feature = "stm32")]
|
||||||
|
pub mod stm32;
|
||||||
|
pub mod time;
|
||||||
|
|
||||||
|
pub trait Channel<B: BusBackend> {
|
||||||
|
const ID: B::ID;
|
||||||
|
type State: Encode<B::Data> + Decode<B::Data>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Encode<F: BusDataFormat> {
|
||||||
|
fn encode(self) -> F;
|
||||||
|
}
|
||||||
|
pub trait Decode<F: BusDataFormat> {
|
||||||
|
fn decode(value: F) -> Result<Self, F>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BusDataFormat: Debug + Clone {}
|
||||||
|
pub trait BusBackend {
|
||||||
|
type ID: Debug + Clone;
|
||||||
|
type Data: BusDataFormat;
|
||||||
|
type Error;
|
||||||
|
fn publish(&self, id: Self::ID, data: Option<Self::Data>) -> Result<(), Self::Error>;
|
||||||
|
}
|
53
canome/src/light.rs
Normal file
53
canome/src/light.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use defmt::Format;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Format, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct LightState {
|
||||||
|
pub hue: u16,
|
||||||
|
pub saturation: u16,
|
||||||
|
pub brightness: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "can")]
|
||||||
|
mod can {
|
||||||
|
use crate::{can::CanDataFormat, Decode, Encode};
|
||||||
|
|
||||||
|
use super::LightState;
|
||||||
|
|
||||||
|
impl Encode<CanDataFormat> for LightState {
|
||||||
|
fn encode(self) -> CanDataFormat {
|
||||||
|
let hue = self.hue.to_be_bytes();
|
||||||
|
let saturation = self.saturation.to_be_bytes();
|
||||||
|
let brightness = self.brightness.to_be_bytes();
|
||||||
|
CanDataFormat::new(&[
|
||||||
|
hue[0],
|
||||||
|
hue[1],
|
||||||
|
saturation[0],
|
||||||
|
saturation[1],
|
||||||
|
brightness[0],
|
||||||
|
brightness[1],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode<CanDataFormat> for LightState {
|
||||||
|
fn decode(value: CanDataFormat) -> Result<Self, CanDataFormat>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let data = value.data();
|
||||||
|
if data.len() != 6 {
|
||||||
|
return Err(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hue = u16::from_be_bytes([data[0], data[1]]);
|
||||||
|
let saturation = u16::from_be_bytes([data[2], data[3]]);
|
||||||
|
let brightness = u16::from_be_bytes([data[4], data[5]]);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
hue,
|
||||||
|
saturation,
|
||||||
|
brightness,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
canome/src/state.rs
Normal file
41
canome/src/state.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Default)]
|
||||||
|
pub enum ThreeWaySwitchState {
|
||||||
|
#[default]
|
||||||
|
Off,
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "can")]
|
||||||
|
mod can {
|
||||||
|
use crate::{can::CanDataFormat, Decode, Encode};
|
||||||
|
|
||||||
|
use super::ThreeWaySwitchState;
|
||||||
|
|
||||||
|
impl Encode<CanDataFormat> for ThreeWaySwitchState {
|
||||||
|
fn encode(self) -> CanDataFormat {
|
||||||
|
let desc = match self {
|
||||||
|
Self::Off => 0,
|
||||||
|
Self::A => 1,
|
||||||
|
Self::B => 2,
|
||||||
|
};
|
||||||
|
CanDataFormat::new(&[desc])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Decode<CanDataFormat> for ThreeWaySwitchState {
|
||||||
|
fn decode(value: CanDataFormat) -> Result<Self, CanDataFormat>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
if value.data().len() != 1 {
|
||||||
|
return Err(value);
|
||||||
|
}
|
||||||
|
match value.data()[0] {
|
||||||
|
0 => Ok(Self::Off),
|
||||||
|
1 => Ok(Self::A),
|
||||||
|
2 => Ok(Self::B),
|
||||||
|
_ => Err(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
77
canome/src/stm32.rs
Normal file
77
canome/src/stm32.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use crate::{bus::BusRx, state::ThreeWaySwitchState, BusBackend, Channel};
|
||||||
|
use rtic_monotonics::{
|
||||||
|
systick::{
|
||||||
|
fugit::{Duration, Instant},
|
||||||
|
Systick,
|
||||||
|
},
|
||||||
|
Monotonic,
|
||||||
|
};
|
||||||
|
use stm32f1xx_hal::gpio::{ExtiPin, Input, Pin, PullDown};
|
||||||
|
|
||||||
|
pub mod buttonmatrix;
|
||||||
|
pub mod motorfader;
|
||||||
|
|
||||||
|
pub struct ThreeWaySwitch<const AP: char, const AN: u8, const BP: char, const BN: u8> {
|
||||||
|
a: Pin<AP, AN, Input<PullDown>>,
|
||||||
|
b: Pin<BP, BN, Input<PullDown>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const AP: char, const AN: u8, const BP: char, const BN: u8> ThreeWaySwitch<AP, AN, BP, BN> {
|
||||||
|
pub const fn new(a: Pin<AP, AN, Input<PullDown>>, b: Pin<BP, BN, Input<PullDown>>) -> Self {
|
||||||
|
Self { a, b }
|
||||||
|
}
|
||||||
|
pub fn state(&self) -> ThreeWaySwitchState {
|
||||||
|
match (self.a.is_low(), self.b.is_low()) {
|
||||||
|
(true, false) => ThreeWaySwitchState::A,
|
||||||
|
(false, true) => ThreeWaySwitchState::B,
|
||||||
|
_ => ThreeWaySwitchState::Off,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn a(&mut self) -> &mut Pin<AP, AN, Input<PullDown>> {
|
||||||
|
&mut self.a
|
||||||
|
}
|
||||||
|
pub fn b(&mut self) -> &mut Pin<BP, BN, Input<PullDown>> {
|
||||||
|
&mut self.b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChannelDerivative<'a, C: Channel<B>, R: BusRx<C, B>, B: BusBackend> {
|
||||||
|
channel: &'a R,
|
||||||
|
last: C::State,
|
||||||
|
updated: Instant<u32, 1, 1000>,
|
||||||
|
}
|
||||||
|
impl<'a, C, R, B> ChannelDerivative<'a, C, R, B>
|
||||||
|
where
|
||||||
|
C: Channel<B>,
|
||||||
|
C::State: Default + PartialEq + Clone,
|
||||||
|
R: BusRx<C, B>,
|
||||||
|
B: BusBackend,
|
||||||
|
{
|
||||||
|
pub fn new(channel: &'a R) -> Self {
|
||||||
|
Self {
|
||||||
|
channel,
|
||||||
|
last: C::State::default(),
|
||||||
|
updated: Systick::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn update(&mut self, new: C::State) -> Option<(C::State, Duration<u32, 1, 1000>)> {
|
||||||
|
if new != self.last {
|
||||||
|
self.last = new.clone();
|
||||||
|
let now = Systick::now();
|
||||||
|
let last_state_for = now - self.updated;
|
||||||
|
self.updated = now;
|
||||||
|
Some((new, last_state_for))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn next(&mut self) -> (C::State, Duration<u32, 1, 1000>) {
|
||||||
|
loop {
|
||||||
|
if let Some(value) = self.update(self.channel.next().await) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
78
canome/src/stm32/buttonmatrix.rs
Normal file
78
canome/src/stm32/buttonmatrix.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use defmt::Format;
|
||||||
|
use rtic_monotonics::{systick::Systick, Monotonic};
|
||||||
|
use stm32f1xx_hal::gpio::{ErasedPin, Input, Output, PullDown};
|
||||||
|
|
||||||
|
pub struct ButtonMatrix<const ROWS: usize, const COLS: usize> {
|
||||||
|
rows: [ErasedPin<Output>; ROWS],
|
||||||
|
cols: [ErasedPin<Input<PullDown>>; COLS],
|
||||||
|
pub state: [[ButtonState; ROWS]; COLS],
|
||||||
|
}
|
||||||
|
impl<const ROWS: usize, const COLS: usize> ButtonMatrix<ROWS, COLS> {
|
||||||
|
pub fn new(
|
||||||
|
mut rows: [ErasedPin<Output>; ROWS],
|
||||||
|
cols: [ErasedPin<Input<PullDown>>; COLS],
|
||||||
|
) -> Self {
|
||||||
|
for row in rows.iter_mut() {
|
||||||
|
row.set_low();
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
rows,
|
||||||
|
cols,
|
||||||
|
state: [[ButtonState::Released; ROWS]; COLS],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn scan(&mut self) -> [[Option<ButtonEvent>; ROWS]; COLS] {
|
||||||
|
const CLICK_TIME: u16 = 200;
|
||||||
|
let mut events = [[None; ROWS]; COLS];
|
||||||
|
for row in self.rows.iter_mut() {
|
||||||
|
row.set_low();
|
||||||
|
}
|
||||||
|
|
||||||
|
let millis_since_epoch: u16 = Systick::now().duration_since_epoch().to_millis() as u16;
|
||||||
|
|
||||||
|
for (ri, row) in self.rows.iter_mut().enumerate() {
|
||||||
|
row.set_high();
|
||||||
|
for (ci, col) in self.cols.iter_mut().enumerate() {
|
||||||
|
let cell = &mut self.state[ci][ri];
|
||||||
|
let pressed = col.is_high();
|
||||||
|
match (&cell, pressed) {
|
||||||
|
(ButtonState::Released, true) => {
|
||||||
|
*cell = ButtonState::Ambigous(millis_since_epoch);
|
||||||
|
}
|
||||||
|
(ButtonState::Ambigous(pressed_at), true)
|
||||||
|
if millis_since_epoch.wrapping_sub(*pressed_at) > CLICK_TIME =>
|
||||||
|
{
|
||||||
|
events[ci][ri] = Some(ButtonEvent::Pressed);
|
||||||
|
*cell = ButtonState::Pressed;
|
||||||
|
}
|
||||||
|
(ButtonState::Ambigous(pressed_at), false)
|
||||||
|
if millis_since_epoch.wrapping_sub(*pressed_at) <= CLICK_TIME =>
|
||||||
|
{
|
||||||
|
events[ci][ri] = Some(ButtonEvent::Clicked);
|
||||||
|
*cell = ButtonState::Released;
|
||||||
|
}
|
||||||
|
(ButtonState::Pressed, false) => {
|
||||||
|
events[ci][ri] = Some(ButtonEvent::Released);
|
||||||
|
*cell = ButtonState::Released;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row.set_low();
|
||||||
|
}
|
||||||
|
|
||||||
|
events
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone, Format)]
|
||||||
|
pub enum ButtonEvent {
|
||||||
|
Clicked,
|
||||||
|
Pressed,
|
||||||
|
Released,
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone, Format, PartialEq, Eq)]
|
||||||
|
pub enum ButtonState {
|
||||||
|
Ambigous(u16),
|
||||||
|
Pressed,
|
||||||
|
Released,
|
||||||
|
}
|
130
canome/src/stm32/motorfader.rs
Normal file
130
canome/src/stm32/motorfader.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
|
use embedded_hal::{
|
||||||
|
adc::{Channel, OneShot},
|
||||||
|
digital::v2::{InputPin, OutputPin},
|
||||||
|
PwmPin,
|
||||||
|
};
|
||||||
|
use rtic_monotonics::systick::{fugit::ExtU32, Systick};
|
||||||
|
use stm32f1xx_hal::gpio::{Dynamic, Pin, HL};
|
||||||
|
|
||||||
|
pub struct Fader<PA, PB, PP, PADC, const CP: char, const CN: u8>
|
||||||
|
where
|
||||||
|
PA: PwmPin<Duty = u16>,
|
||||||
|
PB: PwmPin<Duty = u16>,
|
||||||
|
PP: Channel<PADC>,
|
||||||
|
Pin<CP, CN, Dynamic>: HL,
|
||||||
|
{
|
||||||
|
pub cap_avg: u16,
|
||||||
|
pub target: u16,
|
||||||
|
pub touched: bool,
|
||||||
|
pub position: u16,
|
||||||
|
pin_a: PA,
|
||||||
|
pin_b: PB,
|
||||||
|
pin_pot: PP,
|
||||||
|
adc_pot: PhantomData<PADC>,
|
||||||
|
cap_pin: Pin<CP, CN, Dynamic>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PA, PB, PP, PADC, const CP: char, const CN: u8> Fader<PA, PB, PP, PADC, CP, CN>
|
||||||
|
where
|
||||||
|
PA: PwmPin<Duty = u16>,
|
||||||
|
PB: PwmPin<Duty = u16>,
|
||||||
|
PP: Channel<PADC>,
|
||||||
|
Pin<CP, CN, Dynamic>: HL,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
target: u16,
|
||||||
|
pin_a: PA,
|
||||||
|
pin_b: PB,
|
||||||
|
pin_pot: PP,
|
||||||
|
cap_pin: Pin<CP, CN, Dynamic>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
cap_avg: 0,
|
||||||
|
target,
|
||||||
|
touched: false,
|
||||||
|
position: 0,
|
||||||
|
pin_a,
|
||||||
|
pin_b,
|
||||||
|
pin_pot,
|
||||||
|
adc_pot: PhantomData,
|
||||||
|
cap_pin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn drive<O>(&mut self, pot_adc: &mut O, crl: &mut <Pin<CP, CN, Dynamic> as HL>::Cr)
|
||||||
|
where
|
||||||
|
O: OneShot<PADC, u16, PP>,
|
||||||
|
<O as OneShot<PADC, u16, PP>>::Error: Debug,
|
||||||
|
{
|
||||||
|
//TODO remove unwrap
|
||||||
|
self.position = pot_adc.read(&mut self.pin_pot).unwrap() * 16;
|
||||||
|
|
||||||
|
(self.touched, self.cap_avg) = sense_touch(&mut self.cap_pin, crl, self.cap_avg).await;
|
||||||
|
|
||||||
|
let diff: i32 = (self.position as i32) - (self.target as i32);
|
||||||
|
|
||||||
|
const START: u32 = 850;
|
||||||
|
|
||||||
|
//TODO rework fader movement
|
||||||
|
let p = |max: u16| {
|
||||||
|
((START + ((max as u32) - START) * diff.unsigned_abs() * 2 / (u16::MAX as u32)) as u16)
|
||||||
|
.clamp(0, max)
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.touched {
|
||||||
|
self.pin_a.set_duty(0);
|
||||||
|
self.pin_b.set_duty(0);
|
||||||
|
} else if diff < 0 {
|
||||||
|
self.pin_a.set_duty(0);
|
||||||
|
self.pin_b.set_duty(p(self.pin_b.get_max_duty()));
|
||||||
|
} else {
|
||||||
|
self.pin_a.set_duty(p(self.pin_a.get_max_duty()));
|
||||||
|
self.pin_b.set_duty(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO rework sensing
|
||||||
|
pub async fn sense_touch<const P: char, const N: u8>(
|
||||||
|
pin: &mut Pin<P, N, Dynamic>,
|
||||||
|
crl: &mut <Pin<P, N, Dynamic> as HL>::Cr,
|
||||||
|
last_avg: u16,
|
||||||
|
) -> (bool, u16)
|
||||||
|
where
|
||||||
|
Pin<P, N, Dynamic>: HL,
|
||||||
|
{
|
||||||
|
const DEV: u16 = 5;
|
||||||
|
const SAMPLES: usize = 4;
|
||||||
|
let mut samples = [0_u16; SAMPLES];
|
||||||
|
for sample in samples.iter_mut() {
|
||||||
|
pin.make_push_pull_output(crl);
|
||||||
|
pin.set_high();
|
||||||
|
Systick::delay(5.micros()).await;
|
||||||
|
pin.set_low();
|
||||||
|
Systick::delay(5.micros()).await;
|
||||||
|
|
||||||
|
pin.make_floating_input(crl);
|
||||||
|
while pin.is_low().unwrap() && (*sample <= last_avg + DEV || last_avg == 0) {
|
||||||
|
*sample += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = samples.iter().sum::<u16>() / SAMPLES as u16;
|
||||||
|
|
||||||
|
if last_avg == 0 {
|
||||||
|
(false, total)
|
||||||
|
} else if total > last_avg && diff(total, last_avg) > DEV {
|
||||||
|
(true, last_avg)
|
||||||
|
} else {
|
||||||
|
(false, total)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff(a: u16, b: u16) -> u16 {
|
||||||
|
if a > b {
|
||||||
|
a - b
|
||||||
|
} else {
|
||||||
|
b - a
|
||||||
|
}
|
||||||
|
}
|
40
canome/src/time.rs
Normal file
40
canome/src/time.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use defmt::Format;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Format, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Timestamp {
|
||||||
|
millis_since_epoch: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "can")]
|
||||||
|
mod can {
|
||||||
|
use crate::{can::CanDataFormat, Decode, Encode};
|
||||||
|
|
||||||
|
use super::Timestamp;
|
||||||
|
|
||||||
|
impl Encode<CanDataFormat> for Timestamp {
|
||||||
|
fn encode(self) -> CanDataFormat {
|
||||||
|
let timestamp = self.millis_since_epoch.to_be_bytes();
|
||||||
|
CanDataFormat::new(×tamp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode<CanDataFormat> for Timestamp {
|
||||||
|
fn decode(value: CanDataFormat) -> Result<Self, CanDataFormat>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let data = value.data();
|
||||||
|
if data.len() != 8 {
|
||||||
|
return Err(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut timestamp = [0; 8];
|
||||||
|
timestamp.copy_from_slice(data);
|
||||||
|
let timestamp = i64::from_be_bytes(timestamp);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
millis_since_epoch: timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue