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