From ed701e858027714e0ad11cc511d0f81519c40542 Mon Sep 17 00:00:00 2001 From: baldeau Date: Fri, 27 Jun 2025 16:41:18 +0200 Subject: [PATCH] up --- .DS_Store | Bin 6148 -> 8196 bytes Cargo.lock | 1245 +++++++++++++---- Cargo.toml | 111 +- blinky/Cargo.toml | 33 + blinky/src/common.rs | 4 + blinky/src/main.rs | 32 + bluetooth_recorder/.DS_Store | Bin 0 -> 6148 bytes bluetooth_recorder/Cargo.toml | 43 + bluetooth_recorder/src/main.rs | 489 +++++++ build.rs | 22 +- build_and_flash.sh | 55 +- mac_clear_ble_cache.sh | 8 + memory-nrf52840.x | 20 + trouble-example-apps/Cargo.toml | 33 + trouble-example-apps/src/ble_advertise.rs | 62 + .../src/ble_advertise_multiple.rs | 77 + trouble-example-apps/src/ble_bas_central.rs | 86 ++ .../src/ble_bas_central_sec.rs | 98 ++ .../src/ble_bas_peripheral.rs | 195 +++ .../src/ble_bas_peripheral_sec.rs | 228 +++ trouble-example-apps/src/ble_beacon.rs | 109 ++ trouble-example-apps/src/ble_l2cap_central.rs | 74 + .../src/ble_l2cap_peripheral.rs | 88 ++ trouble-example-apps/src/ble_scanner.rs | 66 + trouble-example-apps/src/common.rs | 7 + trouble-example-apps/src/fmt.rs | 259 ++++ .../src/high_throughput_ble_l2cap_central.rs | 128 ++ .../high_throughput_ble_l2cap_peripheral.rs | 115 ++ trouble-example-apps/src/lib.rs | 18 + 29 files changed, 3329 insertions(+), 376 deletions(-) create mode 100644 blinky/Cargo.toml create mode 100644 blinky/src/common.rs create mode 100644 blinky/src/main.rs create mode 100644 bluetooth_recorder/.DS_Store create mode 100644 bluetooth_recorder/Cargo.toml create mode 100644 bluetooth_recorder/src/main.rs create mode 100755 mac_clear_ble_cache.sh create mode 100644 memory-nrf52840.x create mode 100644 trouble-example-apps/Cargo.toml create mode 100644 trouble-example-apps/src/ble_advertise.rs create mode 100644 trouble-example-apps/src/ble_advertise_multiple.rs create mode 100644 trouble-example-apps/src/ble_bas_central.rs create mode 100644 trouble-example-apps/src/ble_bas_central_sec.rs create mode 100644 trouble-example-apps/src/ble_bas_peripheral.rs create mode 100644 trouble-example-apps/src/ble_bas_peripheral_sec.rs create mode 100644 trouble-example-apps/src/ble_beacon.rs create mode 100644 trouble-example-apps/src/ble_l2cap_central.rs create mode 100644 trouble-example-apps/src/ble_l2cap_peripheral.rs create mode 100644 trouble-example-apps/src/ble_scanner.rs create mode 100644 trouble-example-apps/src/common.rs create mode 100644 trouble-example-apps/src/fmt.rs create mode 100644 trouble-example-apps/src/high_throughput_ble_l2cap_central.rs create mode 100644 trouble-example-apps/src/high_throughput_ble_l2cap_peripheral.rs create mode 100644 trouble-example-apps/src/lib.rs diff --git a/.DS_Store b/.DS_Store index 9ee7f3bbecd6baa8caae1b3f75910df46d0bb4d0..0d714fa07ba23e8588672d75d3374d8b421af14d 100644 GIT binary patch delta 642 zcmah{O-mb56g|UCo>LRXuOMS^o)FhNFq^11+=(FA^Ilro5W)9&+&k~yd(ORQ-cj~w!v$b$b!uyXa%~=E zr{nz(^ZiWPaL?ak6zIWsueG&s6xVB)1KVw zMGtUvZU=R=5pcKBKnXQ|`|_ccUY@-(9x`5KcBnH$xgt{0XKT^eb4c8Rj~5KI@RV@j zUpNG#t>BFGng3duXI+`FB_59iWnHUAsp~rxYtYXSxgifl>g5b8*r;x#hgA+7Vh7Lh z1h4Q6S!Q;}ISof%#Mb8?;2jFsL>}9;8KTr=*GkC+@kkhtsgxrf;ePUV*WOnHzp1p^ z`ceK!BRG}nZI|3_Kgj3*$ZP+KeH&D7er}HE)b7n`HvaMCzFaE0IC*4I%*jD4B@1FD zm(+3w=;(h^=fHJz*@};*7HO4!le;xKv~jGa844X;B(mnD8z+20y`*TyzB1)hvMApq OY}*(gfm3^_oU&k)a04kU|!?3Br2BJz_pg$*Wm3rjOGrB6;1F%{)vC}k*SNCGM=25Ko{ z$Ye-l$eDaXNPc5k2IFFO4h}(PpjAL1zzrl^LAGuz{LVa?U&a&U1_maGdq7TP*c{I@ GhZz9IdLYOE diff --git a/Cargo.lock b/Cargo.lock index 61c7c4d..5099ae2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,13 +3,14 @@ version = 4 [[package]] -name = "Inflector" -version = "0.11.4" +name = "aes" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "lazy_static", - "regex", + "cfg-if", + "cipher", + "cpufeatures", ] [[package]] @@ -21,7 +22,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -103,10 +104,30 @@ dependencies = [ ] [[package]] -name = "base64" -version = "0.13.1" +name = "base16ct" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.8.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.94", +] [[package]] name = "bitfield" @@ -132,6 +153,126 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +[[package]] +name = "blinky" +version = "0.1.0" +dependencies = [ + "atomic-pool", + "cortex-m", + "cortex-m-rt", + "defmt 1.0.1", + "defmt-rtt", + "embassy-embedded-hal", + "embassy-executor", + "embassy-futures", + "embassy-nrf", + "embassy-sync 0.7.0", + "embassy-time", + "embassy-usb", + "fixed", + "heapless", + "libm", + "lsm6ds3tr", + "panic-probe", + "static_cell", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "bluetooth_demo" +version = "0.1.0" +dependencies = [ + "atomic-pool", + "bt-hci", + "cortex-m", + "cortex-m-rt", + "defmt 1.0.1", + "defmt-rtt", + "embassy-embedded-hal", + "embassy-executor", + "embassy-futures", + "embassy-nrf", + "embassy-sync 0.7.0", + "embassy-time", + "fixed", + "futures", + "heapless", + "libm", + "lsm6ds3tr", + "nrf-mpsl", + "nrf-sdc", + "panic-probe", + "rand", + "rand_chacha", + "rand_core 0.6.4", + "static_cell", + "trouble-example-apps", + "trouble-host", +] + +[[package]] +name = "bluetooth_recorder" +version = "0.1.0" +dependencies = [ + "atomic-pool", + "bt-hci", + "cortex-m", + "cortex-m-rt", + "defmt 1.0.1", + "defmt-rtt", + "embassy-embedded-hal", + "embassy-executor", + "embassy-futures", + "embassy-nrf", + "embassy-sync 0.7.0", + "embassy-time", + "fixed", + "futures", + "heapless", + "libm", + "lsm6ds3tr", + "nrf-mpsl", + "nrf-sdc", + "panic-probe", + "rand", + "rand_chacha", + "rand_core 0.6.4", + "static_cell", + "trouble-example-apps", + "trouble-host", +] + +[[package]] +name = "bt-hci" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7f7c19df9648c1da4f5356c4256533e38bd65633b6a41654922475a1c6d777" +dependencies = [ + "defmt 1.0.1", + "embassy-sync 0.7.0", + "embassy-time", + "embedded-io", + "embedded-io-async", + "futures-intrusive", + "heapless", + "log", + "uuid", +] + +[[package]] +name = "bumpalo" +version = "3.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" + [[package]] name = "bytemuck" version = "1.21.0" @@ -145,10 +286,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "cast" -version = "0.3.0" +name = "cexpr" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] [[package]] name = "cfg-if" @@ -157,10 +301,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "const-default" -version = "1.0.0" +name = "cipher" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmac" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" +dependencies = [ + "cipher", + "dbl", + "digest", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "cortex-m" @@ -170,7 +355,6 @@ checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" dependencies = [ "bare-metal", "bitfield 0.13.2", - "critical-section", "embedded-hal 0.2.7", "volatile-register", ] @@ -195,6 +379,24 @@ dependencies = [ "syn 2.0.94", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "critical-once-cell" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3390de2417d532078f4f618c8e2abbc206d6cb11ef4a0a0b8c5ccae5b525c130" +dependencies = [ + "critical-section", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -208,13 +410,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] -name = "darling" -version = "0.13.4" +name = "crypto-bigint" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "typenum", ] [[package]] @@ -223,22 +437,8 @@ version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -251,37 +451,44 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", + "strsim", "syn 2.0.94", ] -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote", - "syn 1.0.109", -] - [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.10", + "darling_core", "quote", "syn 2.0.94", ] [[package]] -name = "defmt" -version = "0.3.10" +name = "dbl" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130" +checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "defmt" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" +dependencies = [ + "defmt 1.0.1", +] + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" dependencies = [ "bitflags 1.3.2", "defmt-macros", @@ -289,9 +496,9 @@ dependencies = [ [[package]] name = "defmt-macros" -version = "0.4.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" dependencies = [ "defmt-parser", "proc-macro-error2", @@ -302,21 +509,42 @@ dependencies = [ [[package]] name = "defmt-parser" -version = "0.4.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ "thiserror", ] [[package]] name = "defmt-rtt" -version = "0.4.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab697b3dbbc1750b7c8b821aa6f6e7f2480b47a99bc057a2ed7b170ebef0c51" +checksum = "b2cac3b8a5644a9e02b75085ebad3b6deafdbdbdec04bb25086523828aa4dfd1" dependencies = [ "critical-section", - "defmt", + "defmt 1.0.1", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", ] [[package]] @@ -328,15 +556,49 @@ dependencies = [ "litrs", ] +[[package]] +name = "doxygen-rs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "415b6ec780d34dcf624666747194393603d0373b7141eef01d12ee58881507d9" +dependencies = [ + "phf", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array 0.14.7", + "group", + "hkdf", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "embassy-embedded-hal" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fea5ef5bed4d3468dfd44f5c9fa4cda8f54c86d4fb4ae683eacf9d39e2ea12" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" dependencies = [ - "defmt", + "defmt 1.0.1", "embassy-futures", - "embassy-sync 0.6.2", + "embassy-hal-internal 0.2.0 (git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece)", + "embassy-sync 0.7.0", "embassy-time", "embedded-hal 0.2.7", "embedded-hal 1.0.0", @@ -349,12 +611,11 @@ dependencies = [ [[package]] name = "embassy-executor" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90327bcc66333a507f89ecc4e2d911b265c45f5c9bc241f98eee076752d35ac6" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" dependencies = [ "cortex-m", "critical-section", - "defmt", + "defmt 1.0.1", "document-features", "embassy-executor-macros", ] @@ -362,10 +623,9 @@ dependencies = [ [[package]] name = "embassy-executor-macros" version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" dependencies = [ - "darling 0.20.10", + "darling", "proc-macro2", "quote", "syn 2.0.94", @@ -374,18 +634,25 @@ dependencies = [ [[package]] name = "embassy-futures" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" [[package]] name = "embassy-hal-internal" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef3bac31ec146321248a169e9c7b5799f1e0b3829c7a9b324cb4600a7438f59" +dependencies = [ + "num-traits", +] + +[[package]] +name = "embassy-hal-internal" +version = "0.2.0" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" dependencies = [ "cortex-m", "critical-section", - "defmt", + "defmt 1.0.1", "num-traits", ] @@ -409,23 +676,22 @@ dependencies = [ [[package]] name = "embassy-nrf" version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7494efe1e0183568327c7e681d4b77ed5241921dcb5976426d9a65b65a0c81" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" dependencies = [ "bitflags 2.8.0", "cfg-if", "cortex-m", "cortex-m-rt", "critical-section", - "defmt", + "defmt 1.0.1", "document-features", "embassy-embedded-hal", - "embassy-hal-internal", - "embassy-sync 0.6.2", + "embassy-hal-internal 0.2.0 (git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece)", + "embassy-sync 0.7.0", "embassy-time", "embassy-time-driver", "embassy-time-queue-utils", - "embassy-usb-driver", + "embassy-usb-driver 0.1.0 (git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece)", "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", @@ -435,20 +701,8 @@ dependencies = [ "embedded-storage-async", "fixed", "nrf-pac", - "rand_core", -] - -[[package]] -name = "embassy-sync" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd938f25c0798db4280fcd8026bf4c2f48789aebf8f77b6e5cf8a7693ba114ec" -dependencies = [ - "cfg-if", - "critical-section", - "embedded-io-async", - "futures-util", - "heapless", + "rand_core 0.6.4", + "rand_core 0.9.3", ] [[package]] @@ -459,7 +713,20 @@ checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049" dependencies = [ "cfg-if", "critical-section", - "defmt", + "embedded-io-async", + "futures-sink", + "futures-util", + "heapless", +] + +[[package]] +name = "embassy-sync" +version = "0.7.0" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" +dependencies = [ + "cfg-if", + "critical-section", + "defmt 1.0.1", "embedded-io-async", "futures-sink", "futures-util", @@ -469,12 +736,11 @@ dependencies = [ [[package]] name = "embassy-time" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f820157f198ada183ad62e0a66f554c610cdcd1a9f27d4b316358103ced7a1f8" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" dependencies = [ "cfg-if", "critical-section", - "defmt", + "defmt 1.0.1", "document-features", "embassy-time-driver", "embedded-hal 0.2.7", @@ -486,8 +752,7 @@ dependencies = [ [[package]] name = "embassy-time-driver" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" dependencies = [ "document-features", ] @@ -495,8 +760,7 @@ dependencies = [ [[package]] name = "embassy-time-queue-utils" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc55c748d16908a65b166d09ce976575fb8852cf60ccd06174092b41064d8f83" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" dependencies = [ "embassy-executor", "heapless", @@ -508,11 +772,11 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d0b882133fa684b9d4652351cd7aac5afe8a2c2bf4a7da59f442ff61087cda2" dependencies = [ - "defmt", + "defmt 0.3.100", "embassy-futures", "embassy-net-driver-channel", "embassy-sync 0.6.2", - "embassy-usb-driver", + "embassy-usb-driver 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "heapless", "ssmarshal", "usbd-hid", @@ -524,28 +788,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970" dependencies = [ - "defmt", + "defmt 0.3.100", ] [[package]] -name = "embedded-alloc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd" +name = "embassy-usb-driver" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy.git?rev=f35aa4005a63e8d478b2b95aaa2bfb316b72dece#f35aa4005a63e8d478b2b95aaa2bfb316b72dece" dependencies = [ - "const-default", - "critical-section", - "linked_list_allocator", - "rlsf", -] - -[[package]] -name = "embedded-dma" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" -dependencies = [ - "stable_deref_trait", + "defmt 1.0.1", + "embedded-io-async", ] [[package]] @@ -563,6 +815,9 @@ name = "embedded-hal" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" +dependencies = [ + "defmt 0.3.100", +] [[package]] name = "embedded-hal-async" @@ -578,6 +833,9 @@ name = "embedded-io" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +dependencies = [ + "defmt 0.3.100", +] [[package]] name = "embedded-io-async" @@ -585,6 +843,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" dependencies = [ + "defmt 0.3.100", "embedded-io", ] @@ -609,6 +868,16 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fixed" version = "1.28.0" @@ -657,12 +926,33 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", +] + [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.94", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -682,6 +972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-macro", "futures-sink", "futures-task", "pin-project-lite", @@ -714,6 +1005,24 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", ] [[package]] @@ -754,6 +1063,24 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -761,28 +1088,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "lazy_static" -version = "1.5.0" +name = "inout" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] [[package]] name = "libc" -version = "0.2.169" +version = "0.2.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets", +] [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" - -[[package]] -name = "linked_list_allocator" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "litrs" @@ -790,6 +1143,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.25" @@ -799,9 +1162,9 @@ checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "lsm6ds3tr" version = "0.3.0" -source = "git+https://avoid.sh/PetActivityMonitor/lsm6ds3tr.git?rev=b368f6200c3ec9a84114e8ca137eb5fe0aa08e29#b368f6200c3ec9a84114e8ca137eb5fe0aa08e29" +source = "git+https://hax.avoid.sh/PetActivityMonitor/lsm6ds3tr.git?rev=b368f6200c3ec9a84114e8ca137eb5fe0aa08e29#b368f6200c3ec9a84114e8ca137eb5fe0aa08e29" dependencies = [ - "defmt", + "defmt 0.3.100", "embedded-hal 1.0.0", "heapless", ] @@ -814,36 +1177,35 @@ dependencies = [ "atomic-pool", "cortex-m", "cortex-m-rt", - "defmt", + "critical-once-cell", + "defmt 1.0.1", "defmt-rtt", "embassy-embedded-hal", "embassy-executor", "embassy-futures", "embassy-nrf", - "embassy-sync 0.5.0", + "embassy-sync 0.7.0", "embassy-time", "embassy-usb", - "embedded-alloc", - "embedded-hal 1.0.0", - "embedded-hal-async", "fixed", "heapless", "libm", "lsm6ds3tr", - "nrf-softdevice", - "nrf-softdevice-s140", - "nrf52840-hal", "panic-probe", "static_cell", - "usb-device 0.2.9", - "usbd-serial", ] [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nb" @@ -861,23 +1223,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" [[package]] -name = "nrf-hal-common" -version = "0.16.1" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea48ed7f0843fe6ec5aac0c42701e9194adfa1ee82ce6d028d64e5f68542be4" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nrf-mpsl" +version = "0.1.0" +source = "git+https://github.com/alexmoon/nrf-sdc.git?rev=7be9b853e15ca0404d65c623d1ec5795fd96c204#7be9b853e15ca0404d65c623d1ec5795fd96c204" dependencies = [ - "cast", - "cfg-if", "cortex-m", - "embedded-dma", - "embedded-hal 0.2.7", + "critical-section", + "defmt 1.0.1", + "embassy-nrf", + "embassy-sync 0.7.0", + "embedded-io", "embedded-storage", - "fixed", - "nb 1.1.0", - "nrf-usbd", - "nrf52840-pac", - "rand_core", - "void", + "embedded-storage-async", + "nrf-mpsl-sys", +] + +[[package]] +name = "nrf-mpsl-sys" +version = "0.1.1" +source = "git+https://github.com/alexmoon/nrf-sdc.git?rev=7be9b853e15ca0404d65c623d1ec5795fd96c204#7be9b853e15ca0404d65c623d1ec5795fd96c204" +dependencies = [ + "bindgen", + "doxygen-rs", ] [[package]] @@ -891,80 +1268,32 @@ dependencies = [ ] [[package]] -name = "nrf-softdevice" +name = "nrf-sdc" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225a21d963b2382617dbbff5953dc8c6c43fe56db5c5155f4d20a30c50c2766d" +source = "git+https://github.com/alexmoon/nrf-sdc.git?rev=7be9b853e15ca0404d65c623d1ec5795fd96c204#7be9b853e15ca0404d65c623d1ec5795fd96c204" dependencies = [ - "cortex-m", - "cortex-m-rt", + "bt-hci", "critical-section", - "defmt", - "embassy-futures", - "embassy-sync 0.5.0", - "embedded-storage", - "embedded-storage-async", - "fixed", - "futures", - "heapless", - "nrf-softdevice-macro", - "nrf-softdevice-s140", - "nrf52840-pac", - "num_enum", + "defmt 1.0.1", + "embassy-hal-internal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "embassy-nrf", + "embassy-sync 0.7.0", + "embedded-io", + "embedded-io-async", + "nrf-mpsl", + "nrf-sdc-sys", + "rand_core 0.9.3", ] [[package]] -name = "nrf-softdevice-macro" +name = "nrf-sdc-sys" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af0752f2f12e202fa29f5a8d2f6dcc8a421c7a7e368d9dab7feb6bfe24ff0e9" +source = "git+https://github.com/alexmoon/nrf-sdc.git?rev=7be9b853e15ca0404d65c623d1ec5795fd96c204#7be9b853e15ca0404d65c623d1ec5795fd96c204" dependencies = [ - "Inflector", - "darling 0.13.4", - "proc-macro2", - "quote", - "syn 1.0.109", - "uuid", -] - -[[package]] -name = "nrf-softdevice-s140" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea93a1e11efdafcf914ed6b81b5be0cd99ee8ff6c3060102265ea863a355393" - -[[package]] -name = "nrf-usbd" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b2907c0b3ec4d264981c1fc5a2321d51c463d5a63d386e573f00e84d5495e6" -dependencies = [ - "cortex-m", - "critical-section", - "usb-device 0.2.9", - "vcell", -] - -[[package]] -name = "nrf52840-hal" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709f85690f7f8d04612a18838785ee9176e1292ce3e53f0c68692b4cf5b4948e" -dependencies = [ - "embedded-hal 0.2.7", - "nrf-hal-common", - "nrf52840-pac", -] - -[[package]] -name = "nrf52840-pac" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30713f36f1be02e5bc9abefa30eae4a1f943d810f199d4923d3ad062d1be1b3d" -dependencies = [ - "cortex-m", - "cortex-m-rt", - "vcell", + "bindgen", + "doxygen-rs", + "nrf-mpsl-sys", + "winnow", ] [[package]] @@ -976,31 +1305,21 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_enum" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.94", -] - [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "elliptic-curve", + "primeorder", +] [[package]] name = "panic-probe" @@ -1009,7 +1328,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4047d9235d1423d66cc97da7d07eddb54d4f154d6c13805c6d0793956f4f25b0" dependencies = [ "cortex-m", - "defmt", + "defmt 0.3.100", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.94", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", ] [[package]] @@ -1030,6 +1391,34 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy 0.8.25", +] + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.94", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -1070,12 +1459,37 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" + [[package]] name = "regex" version = "1.11.1" @@ -1106,16 +1520,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "rlsf" -version = "0.2.1" +name = "rustc-hash" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf" -dependencies = [ - "cfg-if", - "const-default", - "libc", - "svgbobdoc", -] +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" @@ -1126,6 +1534,31 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "0.9.0" @@ -1161,6 +1594,18 @@ dependencies = [ "syn 2.0.94", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "ssmarshal" version = "1.0.0" @@ -1186,12 +1631,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -1199,17 +1638,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "svgbobdoc" -version = "0.3.0" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" -dependencies = [ - "base64", - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-width", -] +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -1237,33 +1669,24 @@ dependencies = [ name = "tflite_demo" version = "0.1.0" dependencies = [ - "assign-resources", "atomic-pool", "cortex-m", "cortex-m-rt", - "defmt", + "defmt 1.0.1", "defmt-rtt", "embassy-embedded-hal", "embassy-executor", "embassy-futures", "embassy-nrf", - "embassy-sync 0.5.0", + "embassy-sync 0.7.0", "embassy-time", "embassy-usb", - "embedded-alloc", - "embedded-hal 1.0.0", - "embedded-hal-async", "fixed", "heapless", "libm", "lsm6ds3tr", - "nrf-softdevice", - "nrf-softdevice-s140", - "nrf52840-hal", "panic-probe", "static_cell", - "usb-device 0.2.9", - "usbd-serial", ] [[package]] @@ -1286,6 +1709,64 @@ dependencies = [ "syn 2.0.94", ] +[[package]] +name = "trouble-example-apps" +version = "0.1.0" +dependencies = [ + "bt-hci", + "defmt 1.0.1", + "embassy-executor", + "embassy-futures", + "embassy-sync 0.7.0", + "embassy-time", + "embedded-hal 1.0.0", + "embedded-io", + "heapless", + "log", + "rand_core 0.6.4", + "static_cell", + "trouble-host", +] + +[[package]] +name = "trouble-host" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f759a3da7d8501bf4be7a11d0754f1da3d2bad226d64cdad9314cec00288fbc" +dependencies = [ + "aes", + "bt-hci", + "cmac", + "defmt 1.0.1", + "embassy-futures", + "embassy-sync 0.7.0", + "embassy-time", + "embedded-io", + "futures", + "heapless", + "log", + "p256", + "rand_chacha", + "rand_core 0.6.4", + "static_cell", + "trouble-host-macros", + "zerocopy 0.8.25", +] + +[[package]] +name = "trouble-host-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5c25c521a9a0cad8c62b86bd3ca9abd6c94842ecf62b64fc56f0d91bb1a426" +dependencies = [ + "convert_case", + "darling", + "proc-macro2", + "quote", + "syn 2.0.94", + "uuid", +] + [[package]] name = "typenum" version = "1.17.0" @@ -1299,16 +1780,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] -name = "unicode-width" -version = "0.1.14" +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "usb-device" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "usb-device" @@ -1328,7 +1803,7 @@ checksum = "e6f291ab53d428685cc780f08a2eb9d5d6ff58622db2b36e239a4f715f1e184c" dependencies = [ "serde", "ssmarshal", - "usb-device 0.3.2", + "usb-device", "usbd-hid-macros", ] @@ -1357,22 +1832,15 @@ dependencies = [ "usbd-hid-descriptors", ] -[[package]] -name = "usbd-serial" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db75519b86287f12dcf0d171c7cf4ecc839149fe9f3b720ac4cfce52959e1dfe" -dependencies = [ - "embedded-hal 0.2.7", - "nb 0.1.3", - "usb-device 0.2.9", -] - [[package]] name = "uuid" -version = "1.11.1" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "vcell" @@ -1401,13 +1869,153 @@ dependencies = [ "vcell", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.94", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.94", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.6.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" +dependencies = [ + "memchr", +] + [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive 0.8.25", ] [[package]] @@ -1420,3 +2028,20 @@ dependencies = [ "quote", "syn 2.0.94", ] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.94", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index f76b198..a21bb9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,30 @@ [workspace] -members = ["tflite_demo", "lsm6ds3tr_demo"] +members = [ + "tflite_demo", + "lsm6ds3tr_demo", + "bluetooth_demo", + "blinky", + "bluetooth_recorder", +] +resolver = "2" [workspace.features] -default = ["ble-l2cap", "ble-gatt-server", "ble-gatt-client", "ble-sec"] +nrf52840 = ["embassy-nrf/nrf52840", "nrf-sdc/nrf52840"] -ble-l2cap = ["nrf-softdevice/ble-l2cap"] -ble-gatt-server = ["nrf-softdevice/ble-gatt-server"] -ble-gatt-client = ["nrf-softdevice/ble-gatt-client"] -ble-sec = ["nrf-softdevice/ble-sec"] - -nrf52840 = [ - "embassy-nrf/nrf52840", - "nrf-softdevice/nrf52840", - "nrf-softdevice/s140", - "dep:nrf-softdevice-s140", +defmt = [ + "dep:defmt", + "trouble-host/defmt", + "bt-hci/defmt", + "embedded-io/defmt-03", + "embedded-hal/defmt-03", ] +security = ["trouble-host/security"] [workspace.dependencies] -assign-resources = "0.4.1" embassy-futures = { version = "0.1.0" } embassy-usb = { version = "0.3.0", features = ["defmt"] } embassy-executor = { version = "0.7.0", features = [ - "task-arena-size-32768", + # "task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", @@ -41,38 +44,62 @@ embassy-nrf = { version = "0.3.1", features = [ "reset-pin-as-gpio", ] } embassy-embedded-hal = "0.3.0" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } -cortex-m-rt = "0.7.0" -defmt = "0.3" -defmt-rtt = "0.4" +embassy-sync = { version = "0.7.0" } + +futures = { version = "0.3", default-features = false, features = [ + "async-await", +] } +nrf-sdc = { version = "0.1.0", default-features = false, features = [ + "defmt", + "peripheral", + "central", + "nrf52", +] } +nrf-mpsl = { version = "0.1.0", default-features = false, features = [ + "defmt", + "critical-section-impl", + "nrf52", +] } +bt-hci = { version = "0.3", default-features = false, features = ["defmt"] } +trouble-host = { version = "0.2.0", features = ["derive", "scan"] } +trouble-example-apps = { version = "0.1.0", path = "trouble-example-apps", features = [ + "defmt", +] } + +defmt = "1.0.1" +defmt-rtt = "1.0.0" + +# cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6" } +cortex-m-rt = "0.7.5" panic-probe = { version = "0.3", features = ["print-defmt"] } -nrf52840-hal = "0.16.0" -usb-device = "0.2.7" -usbd-serial = "0.1.0" -microflow = { path = "../microflow-rs" } -# nalgebra = { version = "0.33.2", default-features = false, features = [ -# "macros", -# ] } -libm = "0.2" +rand = { version = "0.8.5", default-features = false } +static_cell = "2.0.0" +rand_core = { version = "0.6" } +rand_chacha = { version = "0.3", default-features = false } + +libm = "0.2.15" panic-halt = "1.0.0" heapless = "0.8.0" -lsm6ds3tr = { git = "https://avoid.sh/PetActivityMonitor/lsm6ds3tr.git", rev = "b368f6200c3ec9a84114e8ca137eb5fe0aa08e29", features = [ +lsm6ds3tr = { git = "https://hax.avoid.sh/PetActivityMonitor/lsm6ds3tr.git", rev = "b368f6200c3ec9a84114e8ca137eb5fe0aa08e29", features = [ "defmt", ] } -embassy-sync = { version = "0.5.0" } -embedded-storage = "0.3.0" -embedded-hal = "1.0.0" -embedded-hal-async = { version = "1.0.0" } -embedded-alloc = "0.6.0" -nrf-softdevice = { version = "0.1.0", features = [ - "defmt", - "ble-peripheral", - "ble-central", - "critical-section-impl", - "nrf52840", - "s140", -] } -nrf-softdevice-s140 = { version = "0.1.1" } +assign-resources = "0.4.1" + fixed = "1.24.0" atomic-pool = "1.0.1" -static_cell = "2.0.0" +critical-once-cell = "0.2.0" + +microflow = { version = "0.1.3" } + +[patch.crates-io] +nrf-sdc = { git = "https://github.com/alexmoon/nrf-sdc.git", rev = "7be9b853e15ca0404d65c623d1ec5795fd96c204" } +nrf-mpsl = { git = "https://github.com/alexmoon/nrf-sdc.git", rev = "7be9b853e15ca0404d65c623d1ec5795fd96c204" } + +embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", rev = "f35aa4005a63e8d478b2b95aaa2bfb316b72dece" } +embassy-nrf = { git = "https://github.com/embassy-rs/embassy.git", rev = "f35aa4005a63e8d478b2b95aaa2bfb316b72dece" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy.git", rev = "f35aa4005a63e8d478b2b95aaa2bfb316b72dece" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy.git", rev = "f35aa4005a63e8d478b2b95aaa2bfb316b72dece" } +embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "f35aa4005a63e8d478b2b95aaa2bfb316b72dece" } +embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "f35aa4005a63e8d478b2b95aaa2bfb316b72dece" } +embassy-embedded-hal = { git = "https://github.com/embassy-rs/embassy.git", rev = "f35aa4005a63e8d478b2b95aaa2bfb316b72dece" } diff --git a/blinky/Cargo.toml b/blinky/Cargo.toml new file mode 100644 index 0000000..2c49cd7 --- /dev/null +++ b/blinky/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "blinky" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cortex-m.workspace = true +cortex-m-rt.workspace = true +# nrf52840-hal.workspace = true +# microflow.workspace = true +libm.workspace = true +# nalgebra.workspace = true +heapless.workspace = true +lsm6ds3tr.workspace = true +defmt.workspace = true +defmt-rtt.workspace = true +# embedded-alloc.workspace = true +# embedded-hal.workspace = true +# embedded-hal-async.workspace = true +embassy-nrf.workspace = true +embassy-time.workspace = true +embassy-executor.workspace = true +embassy-sync.workspace = true +embassy-embedded-hal.workspace = true +fixed.workspace = true +atomic-pool.workspace = true +static_cell.workspace = true +embassy-usb.workspace = true +embassy-futures.workspace = true +panic-probe.workspace = true +# assign-resources.workspace = true diff --git a/blinky/src/common.rs b/blinky/src/common.rs new file mode 100644 index 0000000..eb65974 --- /dev/null +++ b/blinky/src/common.rs @@ -0,0 +1,4 @@ +#![macro_use] + +use defmt_rtt as _; // global logger +use embassy_nrf as _; // time driver diff --git a/blinky/src/main.rs b/blinky/src/main.rs new file mode 100644 index 0000000..f221b5b --- /dev/null +++ b/blinky/src/main.rs @@ -0,0 +1,32 @@ +#![no_main] +#![no_std] + +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_time::{Duration, Timer}; + +mod common; + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let led = Output::new(p.P0_06, Level::Low, OutputDrive::Standard); + + unwrap!(spawner.spawn(blinker(led, Duration::from_millis(300)))); +} + +#[embassy_executor::task] +async fn blinker(mut led: Output<'static>, interval: Duration) { + loop { + led.set_high(); + Timer::after(interval).await; + led.set_low(); + Timer::after(interval).await; + } +} diff --git a/bluetooth_recorder/.DS_Store b/bluetooth_recorder/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9ad35ee736a1a202262b781aefb45145dd36a0bc GIT binary patch literal 6148 zcmeHKF-`+P3>=db5i}`jkoErT70E`Y-qYF-bEiAO-%F0yf{=ZPxsx?5&fJ u^Ilu%_w+AgZ7XLO9RfJ?kx7BSP~a2V?j>*l literal 0 HcmV?d00001 diff --git a/bluetooth_recorder/Cargo.toml b/bluetooth_recorder/Cargo.toml new file mode 100644 index 0000000..ae7ff5a --- /dev/null +++ b/bluetooth_recorder/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "bluetooth_recorder" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +nrf52840 = ["embassy-nrf/nrf52840", "nrf-sdc/nrf52840"] + +[dependencies] +embassy-futures.workspace = true +embassy-executor.workspace = true +embassy-time.workspace = true +embassy-nrf.workspace = true +embassy-embedded-hal.workspace = true +embassy-sync.workspace = true + +futures.workspace = true +nrf-sdc.workspace = true +nrf-mpsl.workspace = true +bt-hci.workspace = true +trouble-host.workspace = true +trouble-example-apps.workspace = true + +defmt.workspace = true +defmt-rtt.workspace = true + +cortex-m.workspace = true +cortex-m-rt.workspace = true +panic-probe.workspace = true +rand.workspace = true +static_cell.workspace = true +rand_core.workspace = true +rand_chacha.workspace = true + +libm.workspace = true +heapless.workspace = true +lsm6ds3tr.workspace = true + +fixed.workspace = true +atomic-pool.workspace = true +# critical-once-cell.workspace = true diff --git a/bluetooth_recorder/src/main.rs b/bluetooth_recorder/src/main.rs new file mode 100644 index 0000000..bab2460 --- /dev/null +++ b/bluetooth_recorder/src/main.rs @@ -0,0 +1,489 @@ +#![no_std] +#![no_main] + +use core::ops::Deref; + +use core::fmt::Write; +use defmt::{info, unwrap, warn}; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive}; +use embassy_nrf::mode::Async; +use embassy_nrf::peripherals::{self, RNG}; +use embassy_nrf::pwm::SequenceMode; +use embassy_nrf::{bind_interrupts, rng, twim, Peri, Peripherals}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::Mutex; +use heapless::String; +use lsm6ds3tr::interface::I2cInterface; +use lsm6ds3tr::{ + registers::{GyroSampleRate, GyroScale}, + AccelSampleRate, AccelScale, AccelSettings, GyroSettings, LsmSettings, LSM6DS3TR, +}; +use nrf_sdc::mpsl::MultiprotocolServiceLayer; +use nrf_sdc::{self as sdc, mpsl}; +use static_cell::{ConstStaticCell, StaticCell}; +use trouble_host::prelude::*; +use {defmt_rtt as _, panic_probe as _}; + +use embassy_futures::join::join; +use embassy_futures::select::select; +use embassy_time::Timer; + +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; + EGU0_SWI0 => nrf_sdc::mpsl::LowPrioInterruptHandler; + CLOCK_POWER => nrf_sdc::mpsl::ClockInterruptHandler; + RADIO => nrf_sdc::mpsl::HighPrioInterruptHandler; + TIMER0 => nrf_sdc::mpsl::HighPrioInterruptHandler; + RTC0 => nrf_sdc::mpsl::HighPrioInterruptHandler; +}); + +#[embassy_executor::task] +async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! { + mpsl.run().await +} + +/// How many outgoing L2CAP buffers per link +const L2CAP_TXQ: u8 = 3; + +/// How many incoming L2CAP buffers per link +const L2CAP_RXQ: u8 = 3; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; + +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att + +// GATT Server definition +#[gatt_server] +struct Server { + fake_service: FakeService, + led_service: LedService, + gyro_service: GyroService, +} + +/// fakr service +#[gatt_service(uuid = "18B10000-E8F2-537E-4F6C-D104768A1210")] +struct FakeService { + /// Fake Life + #[descriptor(uuid = descriptors::VALID_RANGE, read, value = [0, 100])] + #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "hello", read, value = "Fake Life")] + #[characteristic( + uuid = "407813df-5dd4-1f87-ec11-cdb001100000", + read, + notify, + value = 10 + )] + level: u8, + #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "boolean", read, value = "Bollean")] + #[characteristic(uuid = "408813df-5dd4-1f87-ec11-cdb001100000", write, read, notify)] + status: bool, +} + +#[gatt_service(uuid = "17B10000-E8F2-537E-4F6C-D104768A1214")] +struct LedService { + #[descriptor( + uuid = descriptors::MEASUREMENT_DESCRIPTION, + name = "led", + read, + value = "Led Toggle" + )] + #[characteristic( + uuid = "17B10000-E8F2-537E-4F6C-D104768A1215", + write, + read, + notify, + value = false + )] + on_off: bool, +} + +#[gatt_service(uuid = "19B10000-E8F2-537E-4F6C-D104768A1214")] +struct GyroService { + #[descriptor( + uuid = descriptors::MEASUREMENT_DESCRIPTION, + name = "gyro_toggle", + read, + value = "Gyro Toggle" + )] + #[characteristic( + uuid = "19B10000-E8F2-537E-4F6C-D104768A1215", + write, + read, + notify, + value = false + )] + on_off: bool, // This characteristic controls whether IMU data is sent + #[descriptor( + uuid = descriptors::MEASUREMENT_DESCRIPTION, + name = "imu_data", + read, + value = "Combined Gyro and Accel Data" + )] + #[characteristic(uuid = "19B10000-E8F2-537E-4F6C-D104768A1216", read, notify, value = [0;32])] + imu_data: [u8; 32], // Gyro (X, Y, Z), Accel (X, Y, Z) as 6 f32s = 24 bytes + Timestamp (u64) = 8 bytes = 32 bytes +} +fn build_sdc<'d, const N: usize>( + p: nrf_sdc::Peripherals<'d>, + rng: &'d mut rng::Rng, + mpsl: &'d MultiprotocolServiceLayer, + mem: &'d mut sdc::Mem, +) -> Result, nrf_sdc::Error> { + sdc::Builder::new()? + .support_adv()? + .support_peripheral()? + .peripheral_count(1)? + .buffer_cfg( + DefaultPacketPool::MTU as u16, + DefaultPacketPool::MTU as u16, + L2CAP_TXQ, + L2CAP_RXQ, + )? + .build(p, rng, mpsl, mem) +} + +bind_interrupts!(struct IrqsTest { + TWISPI0 => twim::InterruptHandler; +}); + +type Imu<'a> = LSM6DS3TR>>; +static IMU: StaticCell>> = StaticCell::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + + let mut pin = Output::new(p.P1_08, Level::High, OutputDrive::HighDrive); + pin.set_high(); + Timer::after_millis(100).await; + + let sda_pin = p.P0_07; + let scl_pin = p.P0_27; + + let mut imu_config = twim::Config::default(); + // This limits the ADXL355 ODR to 200Hz max. + imu_config.frequency = twim::Frequency::K400; + // Internal pullups for SCL and SDA must be enabled. + imu_config.scl_pullup = true; + imu_config.sda_pullup = true; + + static RAM_BUFFER: ConstStaticCell<[u8; 16]> = ConstStaticCell::new([0; 16]); + + let i2c = twim::Twim::new( + p.TWISPI0, + IrqsTest, + sda_pin, + scl_pin, + imu_config, + RAM_BUFFER.take(), + ); + let settings = LsmSettings::basic() + .with_gyro( + GyroSettings::new() + .with_sample_rate(GyroSampleRate::_104Hz) + .with_scale(GyroScale::_2000DPS), + ) + .with_accel( + AccelSettings::new() + .with_sample_rate(AccelSampleRate::_104Hz) + .with_scale(AccelScale::_4G), + ); + + let mut imu_driver = LSM6DS3TR::new(I2cInterface::new(i2c)).with_settings(settings); + imu_driver + .init() + .expect("LSM6DS3TR-C initialization failure!"); + + let imu = IMU.init(Mutex::new(imu_driver)); + + let mpsl_p = + mpsl::Peripherals::new(p.RTC0, p.TIMER0, p.TEMP, p.PPI_CH19, p.PPI_CH30, p.PPI_CH31); + let lfclk_cfg = mpsl::raw::mpsl_clock_lfclk_cfg_t { + source: mpsl::raw::MPSL_CLOCK_LF_SRC_RC as u8, + rc_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_CTIV as u8, + rc_temp_ctiv: mpsl::raw::MPSL_RECOMMENDED_RC_TEMP_CTIV as u8, + accuracy_ppm: mpsl::raw::MPSL_DEFAULT_CLOCK_ACCURACY_PPM as u16, + skip_wait_lfclk_started: mpsl::raw::MPSL_DEFAULT_SKIP_WAIT_LFCLK_STARTED != 0, + }; + static MPSL: StaticCell = StaticCell::new(); + let mpsl = MPSL.init(unwrap!(mpsl::MultiprotocolServiceLayer::new( + mpsl_p, Irqs, lfclk_cfg + ))); + spawner.must_spawn(mpsl_task(&*mpsl)); + + let sdc_p = sdc::Peripherals::new( + p.PPI_CH17, p.PPI_CH18, p.PPI_CH20, p.PPI_CH21, p.PPI_CH22, p.PPI_CH23, p.PPI_CH24, + p.PPI_CH25, p.PPI_CH26, p.PPI_CH27, p.PPI_CH28, p.PPI_CH29, + ); + + let mut rng = rng::Rng::new(p.RNG, Irqs); + + let mut sdc_mem = sdc::Mem::<4720>::new(); + let sdc = unwrap!(build_sdc(sdc_p, &mut rng, mpsl, &mut sdc_mem)); + + let led_pin = p.P0_06.into(); + + // peripheral::run(sdc).await; + run(sdc, led_pin, imu).await; +} + +/// Run the BLE stack. +pub async fn run( + controller: C, + led_pin: Peri<'static, AnyPin>, + imu: &'static Mutex>, +) where + C: Controller, +{ + let mut led = Output::new(led_pin, Level::Low, OutputDrive::Standard); + + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + // info!("Our address = {:?}", address); + + let mut resources: HostResources = + HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut peripheral, + runner, + .. + } = stack.build(); + + // info!("Starting advertising and GATT service"); + let server = Server::new_with_config(GapConfig::Peripheral(PeripheralConfig { + name: "TrouBLE", + appearance: &appearance::power_device::GENERIC_POWER_DEVICE, + })) + .unwrap(); + + let _ = join(ble_task(runner), async { + loop { + match advertise("Trouble Example", &mut peripheral, &server).await { + Ok(conn) => { + // set up tasks when the connection is established to a central, so they don't run when no one is connected. + let a = gatt_events_task(&server, &conn); + let b = custom_task(&server, &conn, &stack, &mut led, imu); + // run until any task ends (usually because the connection has been closed), + // then return to advertising state. + select(a, b).await; + // a.await; + + led.set_low(); + } + Err(e) => { + panic!("[adv] error: {:?}", e); + } + } + } + }) + .await; +} + +/// This is a background task that is required to run forever alongside any other BLE tasks. +/// +/// ## Alternative +/// +/// If you didn't require this to be generic for your application, you could statically spawn this with i.e. +/// +/// ```rust,ignore +/// +/// #[embassy_executor::task] +/// async fn ble_task(mut runner: Runner<'static, SoftdeviceController<'static>>) { +/// runner.run().await; +/// } +/// +/// spawner.must_spawn(ble_task(runner)); +/// ``` +async fn ble_task(mut runner: Runner<'_, C, P>) { + loop { + if let Err(e) = runner.run().await { + panic!("[ble_task] error: {:?}", e); + } + } +} + +/// Stream Events until the connection closes. +/// +/// This function will handle the GATT events and process them. +/// This is how we interact with read and write requests. +async fn gatt_events_task( + server: &Server<'_>, + conn: &GattConnection<'_, '_, P>, +) -> Result<(), Error> { + let fake_level = server.fake_service.level; + let led_level = server.led_service.handle; + let reason = loop { + match conn.next().await { + GattConnectionEvent::Disconnected { reason } => break reason, + GattConnectionEvent::Gatt { event } => { + match &event { + GattEvent::Read(event) => { + // if event.handle() == fake_level.handle { + // let value = server.get(&fake_level); + // // info!("[gatt] Read Event to Level Characteristic: {:?}", value); + // } + if event.handle() == led_level { + let value = server.get(&server.led_service.on_off); + // info!("[gatt] Read Event to Level Characteristic: {:?}", value); + } + } + GattEvent::Write(event) => { + // if event.handle() == fake_level.handle { + // // info!( + // // "[gatt] Write Event to Level Characteristic: {:?}", + // // event.data() + // // ); + // } + if event.handle() == led_level { + // info!( + // "[gatt] Write Event to Level Characteristic: {:?}", + // event.data() + // ); + } + } + _ => {} + }; + // This step is also performed at drop(), but writing it explicitly is necessary + // in order to ensure reply is sent. + match event.accept() { + Ok(reply) => reply.send().await, + Err(e) => panic!("[gatt] error sending response"), + }; + } + _ => {} // ignore other Gatt Connection Events + } + }; + // info!("[gatt] disconnected: {:?}", reason); + Ok(()) +} + +/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect. +async fn advertise<'values, 'server, C: Controller>( + name: &'values str, + peripheral: &mut Peripheral<'values, C, DefaultPacketPool>, + server: &'server Server<'values>, +) -> Result, BleHostError> { + let mut advertiser_data = [0; 31]; + let len = AdStructure::encode_slice( + &[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ServiceUuids16(&[[0x0f, 0x18]]), + AdStructure::CompleteLocalName(name.as_bytes()), + ], + &mut advertiser_data[..], + )?; + let advertiser = peripheral + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &advertiser_data[..len], + scan_data: &[], + }, + ) + .await?; + // info!("[adv] advertising"); + let conn = advertiser.accept().await?.with_attribute_server(server)?; + // info!("[adv] connection established"); + Ok(conn) +} + +/// Example task to use the BLE notifier interface. +/// This task will notify the connected central of a counter value every 2 seconds. +/// It will also read the RSSI value every 2 seconds. +/// and will stop when the connection is closed by the central or an error occurs. +async fn custom_task( + server: &Server<'_>, + conn: &GattConnection<'_, '_, P>, + stack: &Stack<'_, C, P>, + led: &mut Output<'static>, + imu: &'static Mutex>, +) { + let mut tick: u8 = 0; + let fake_level = server.fake_service.level; + let led_on_off = server.led_service.on_off; + let gyro_service_on_off = server.gyro_service.on_off; // Get gyro service toggle + let imu_data_char = &server.gyro_service.imu_data; // Combined IMU data characteristic + + loop { + // Only read and send IMU data if the gyro_service_on_off characteristic is true + if gyro_service_on_off.get(server).unwrap_or(false) { + let mut imu_guard = imu.lock().await; + + if let (Ok(xyz_a), Ok(xyz_g)) = (imu_guard.read_accel_raw(), imu_guard.read_gyro()) { + info!("Accel: x:{} y:{} z:{}", xyz_a.x, xyz_a.y, xyz_a.z); + info!("Gyro: x:{} y:{} z:{}", xyz_g.x, xyz_g.y, xyz_g.z); + + // Capture timestamp immediately after reading IMU data for better accuracy + let current_time_us = embassy_time::Instant::now().as_micros(); + + // Convert i32 gyro values to f32 and then to their byte representation + let gx_f32 = xyz_g.x as f32; + let gy_f32 = xyz_g.y as f32; + let gz_f32 = xyz_g.z as f32; + + // Convert i32 accel values to f32 and then to their byte representation + let ax_f32 = xyz_a.x as f32; + let ay_f32 = xyz_a.y as f32; + let az_f32 = xyz_a.z as f32; + + let gx_bytes = gx_f32.to_le_bytes(); + let gy_bytes = gy_f32.to_le_bytes(); + let gz_bytes = gz_f32.to_le_bytes(); + let ax_bytes = ax_f32.to_le_bytes(); + let ay_bytes = ay_f32.to_le_bytes(); + let az_bytes = az_f32.to_le_bytes(); + + // Combine gyro, accel, and timestamp byte arrays into a single [u8; 32] array + // Order: Gx, Gy, Gz, Ax, Ay, Az (each 4 bytes) then Timestamp (8 bytes) + let mut combined_imu_data = [0u8; 24]; + combined_imu_data[0..4].copy_from_slice(&gx_bytes); + combined_imu_data[4..8].copy_from_slice(&gy_bytes); + combined_imu_data[8..12].copy_from_slice(&gz_bytes); + combined_imu_data[12..16].copy_from_slice(&ax_bytes); + combined_imu_data[16..20].copy_from_slice(&ay_bytes); + combined_imu_data[20..24].copy_from_slice(&az_bytes); + let timestamp_bytes = current_time_us.to_le_bytes(); + + let mut final_imu_packet = [0u8; 32]; + final_imu_packet[0..24].copy_from_slice(&combined_imu_data); + final_imu_packet[24..32].copy_from_slice(×tamp_bytes); + + // Update combined IMU characteristic and notify + imu_data_char.set(server, &final_imu_packet); + if imu_data_char.notify(conn, &final_imu_packet).await.is_err() { + info!( + "[custom_task] error notifying combined IMU and timestamp characteristic" + ); + break; + }; + } else { + warn!("Could not read IMU data"); + } + } // End of gyro_service_on_off check + + tick = tick.wrapping_add(1); + // info!("[custom_task] notifying connection of tick {}", tick); + if fake_level.notify(conn, &tick).await.is_err() { + // info!("[custom_task] error notifying connection"); + break; + }; + // read RSSI (Received Signal Strength Indicator) of the connection. + if let Ok(rssi) = conn.raw().rssi(stack).await { + // info!("[custom_task] RSSI: {:?}", rssi); + } else { + // info!("[custom_task] error getting RSSI"); + break; + }; + if led_on_off.get(server).unwrap() == true { + led.set_high(); + } + // Timer::after_millis(100).await; + if led_on_off.get(server).unwrap() == true { + led.set_low(); + } + Timer::after_millis(100).await + } +} diff --git a/build.rs b/build.rs index d4dbebf..0ddb7b8 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,22 @@ +fn linker_data() -> %'static [u8] { + return include_bytes!("memory.x"); +} + fn main() { - // Rebuild if memory.x file was rebuild. - // Linker seems to pick it up automatically. - // (via the include in link.x which is added with a linker argument!) + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(linker_data()) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); } \ No newline at end of file diff --git a/build_and_flash.sh b/build_and_flash.sh index 0251973..ec25f79 100755 --- a/build_and_flash.sh +++ b/build_and_flash.sh @@ -1,28 +1,41 @@ #!/bin/bash set -e -COM_PORT=/dev/cu.usbmodem1101 +# Find the COM port dynamically +COM_PORT=$(ls /dev/cu.usbmodem* 2>/dev/null | head -n 1) -echo -e "bootloader" > $COM_PORT +# Check if a COM port was found +if [ -z "$COM_PORT" ]; then + echo "Error: No /dev/cu.usbmodem device found." + exit 1 +else + echo "Using COM Port: $COM_PORT" + # Your script logic goes here, using $COM_PORT + # For example: + # minicom -D $COM_PORT + # screen $COM_PORT 9600 + # echo -e "bootloader" > $COM_PORT -sleep 1s + # sleep 1s -while getopts 'abc:h' opt; do - case "$opt" in - ?|h) - echo "Usage: $(basename $0) crate_name" - exit 1 - ;; - esac -done -shift "$(($OPTIND -1))" -CRATE=$1 + while getopts 'abc:h' opt; do + case "$opt" in + ?|h) + echo "Usage: $(basename $0) crate_name" + exit 1 + ;; + esac + done + shift "$(($OPTIND -1))" + CRATE=$1 + + cargo build -p $CRATE --release + arm-none-eabi-objcopy -O ihex target/thumbv7em-none-eabihf/release/$CRATE target/$CRATE.hex + adafruit-nrfutil dfu genpkg --dev-type 0x0052 --sd-req 0x0123 --application target/$CRATE.hex target/$CRATE.zip + # Use our custom reboot system to boot the controller into serial-only DFU mode. + # echo -e "bootloader" > $COM_PORT + # Wait for the reboot. + # sleep 1s + adafruit-nrfutil --verbose dfu serial -pkg target/$CRATE.zip -p $COM_PORT -b 115200 --singlebank +fi -cargo build -p $CRATE --release -arm-none-eabi-objcopy -O ihex target/thumbv7em-none-eabihf/release/$CRATE target/$CRATE.hex -adafruit-nrfutil dfu genpkg --dev-type 0x0052 --sd-req 0x0123 --application target/$CRATE.hex target/$CRATE.zip -# Use our custom reboot system to boot the controller into serial-only DFU mode. -# echo -e "bootloader" > $COM_PORT -# Wait for the reboot. -# sleep 1s -adafruit-nrfutil --verbose dfu serial -pkg target/$CRATE.zip -p $COM_PORT -b 115200 --singlebank \ No newline at end of file diff --git a/mac_clear_ble_cache.sh b/mac_clear_ble_cache.sh new file mode 100755 index 0000000..c75b2ea --- /dev/null +++ b/mac_clear_ble_cache.sh @@ -0,0 +1,8 @@ +#!/bin/zsh + +# A script to clear the macOS Bluetooth cache to get the latest BLE service changes. + +sudo pkill bluetoothd +sudo rm /Library/Preferences/com.apple.Bluetooth.plist &> /dev/null +rm ~/Library/Preferences/ByHost/com.apple.Bluetooth.*.plist &> /dev/null +sudo pkill bluetoothd diff --git a/memory-nrf52840.x b/memory-nrf52840.x new file mode 100644 index 0000000..7328124 --- /dev/null +++ b/memory-nrf52840.x @@ -0,0 +1,20 @@ +MEMORY +{ + /* https://infocenter.nordicsemi.com/index.jsp?topic=%2Fsds_s140%2FSDS%2Fs1xx%2Fmem_usage%2Fmem_resource_reqs.html&cp=5_7_4_0_13_0_0 */ + + /* Need to leave space for the SoftDevice + These values are confirmed working for S140 7.3.0 + + They were extracted from the Arduino IDE plugin linked indirectly at https://wiki.seeedstudio.com/XIAO_BLE/ + */ + FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xED000 - 0x27000 + + /* SRAM required by Softdevice depend on + * - Attribute Table Size (Number of Services and Characteristics) + * - Vendor UUID count + * - Max ATT MTU + * - Concurrent connection peripheral + central + secure links + * - Event Len, HVN queue, Write CMD queue + */ + RAM (rwx) : ORIGIN = 0x20006000, LENGTH = 0x20040000 - 0x20006000 +} \ No newline at end of file diff --git a/trouble-example-apps/Cargo.toml b/trouble-example-apps/Cargo.toml new file mode 100644 index 0000000..c445e72 --- /dev/null +++ b/trouble-example-apps/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "trouble-example-apps" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[dependencies] +trouble-host = { version = "0.2.0", features = ["derive", "scan"] } +bt-hci = { version = "0.3.2" } +embassy-executor = { version = "0.7.0" } +embassy-futures = "0.1.1" +embassy-sync = { version = "0.7" } +embassy-time = "0.4" +embedded-hal = "1.0" +static_cell = "2" +embedded-io = "0.6" +heapless = "0.8" +rand_core = { version = "0.6", default-features = false } + +defmt = { version = "1.0.1", optional = true } +log = { version = "0.4", optional = true } + + +[features] +defmt = [ + "dep:defmt", + "trouble-host/defmt", + "bt-hci/defmt", + "embedded-io/defmt-03", + "embedded-hal/defmt-03", +] +log = ["dep:log", "trouble-host/log", "bt-hci/log"] +security = ["trouble-host/security"] diff --git a/trouble-example-apps/src/ble_advertise.rs b/trouble-example-apps/src/ble_advertise.rs new file mode 100644 index 0000000..e19142c --- /dev/null +++ b/trouble-example-apps/src/ble_advertise.rs @@ -0,0 +1,62 @@ +use bt_hci::cmd::le::*; +use bt_hci::controller::ControllerCmdSync; +use embassy_futures::join::join; +use embassy_time::{Duration, Timer}; +use trouble_host::prelude::*; + +pub async fn run(controller: C) +where + C: Controller + + for<'t> ControllerCmdSync> + + ControllerCmdSync + + ControllerCmdSync + + ControllerCmdSync + + ControllerCmdSync + + for<'t> ControllerCmdSync> + + for<'t> ControllerCmdSync>, +{ + let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut peripheral, + mut runner, + .. + } = stack.build(); + + let mut adv_data = [0; 31]; + let len = AdStructure::encode_slice( + &[ + AdStructure::CompleteLocalName(b"Trouble Advert"), + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + ], + &mut adv_data[..], + ) + .unwrap(); + + info!("Starting advertising"); + let _ = join(runner.run(), async { + loop { + let mut params = AdvertisementParameters::default(); + params.interval_min = Duration::from_millis(100); + params.interval_max = Duration::from_millis(100); + let _advertiser = peripheral + .advertise( + ¶ms, + Advertisement::NonconnectableScannableUndirected { + adv_data: &adv_data[..len], + scan_data: &[], + }, + ) + .await + .unwrap(); + loop { + info!("Still running"); + Timer::after(Duration::from_secs(60)).await; + } + } + }) + .await; +} diff --git a/trouble-example-apps/src/ble_advertise_multiple.rs b/trouble-example-apps/src/ble_advertise_multiple.rs new file mode 100644 index 0000000..2e72d99 --- /dev/null +++ b/trouble-example-apps/src/ble_advertise_multiple.rs @@ -0,0 +1,77 @@ +use bt_hci::cmd::le::*; +use bt_hci::controller::ControllerCmdSync; +use embassy_futures::join::join; +use embassy_time::{Duration, Timer}; +use trouble_host::prelude::*; + +pub async fn run(controller: C) +where + C: Controller + + for<'t> ControllerCmdSync> + + ControllerCmdSync + + ControllerCmdSync + + ControllerCmdSync + + ControllerCmdSync + + for<'t> ControllerCmdSync> + + for<'t> ControllerCmdSync>, +{ + let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut peripheral, + mut runner, + .. + } = stack.build(); + + let mut adv_data = [0; 31]; + let len = AdStructure::encode_slice( + &[ + AdStructure::CompleteLocalName(b"Trouble Multiadv"), + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + ], + &mut adv_data[..], + ) + .unwrap(); + + let mut params_1m = AdvertisementParameters::default(); + params_1m.primary_phy = PhyKind::Le1M; + params_1m.secondary_phy = PhyKind::Le1M; + params_1m.interval_min = Duration::from_millis(160); + params_1m.interval_max = Duration::from_millis(160); + + let mut params_coded = AdvertisementParameters::default(); + params_coded.primary_phy = PhyKind::LeCoded; + params_coded.secondary_phy = PhyKind::LeCoded; + params_coded.interval_min = Duration::from_millis(400); + params_coded.interval_max = Duration::from_millis(400); + let sets = [ + AdvertisementSet { + params: params_1m, + data: Advertisement::ExtNonconnectableScannableUndirected { + scan_data: &adv_data[..len], + }, + }, + AdvertisementSet { + params: params_coded, + data: Advertisement::ExtNonconnectableScannableUndirected { + scan_data: &adv_data[..len], + }, + }, + ]; + let mut handles = AdvertisementSet::handles(&sets); + + info!("Starting advertising"); + let _ = join(runner.run(), async { + loop { + let _advertiser = peripheral.advertise_ext(&sets, &mut handles).await.unwrap(); + loop { + info!("Still running"); + Timer::after(Duration::from_secs(60)).await; + } + } + }) + .await; +} diff --git a/trouble-example-apps/src/ble_bas_central.rs b/trouble-example-apps/src/ble_bas_central.rs new file mode 100644 index 0000000..1cdb11f --- /dev/null +++ b/trouble-example-apps/src/ble_bas_central.rs @@ -0,0 +1,86 @@ +use embassy_futures::join::join; +use embassy_time::{Duration, Timer}; +use trouble_host::prelude::*; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; + +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC + +pub async fn run(controller: C) +where + C: Controller, +{ + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut central, + mut runner, + .. + } = stack.build(); + + // NOTE: Modify this to match the address of the peripheral you want to connect to. + // Currently it matches the address used by the peripheral examples + let target: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + + let config = ConnectConfig { + connect_params: Default::default(), + scan_config: ScanConfig { + filter_accept_list: &[(target.kind, &target.addr)], + ..Default::default() + }, + }; + + info!("Scanning for peripheral..."); + let _ = join(runner.run(), async { + info!("Connecting"); + + let conn = central.connect(&config).await.unwrap(); + info!("Connected, creating gatt client"); + + let client = GattClient::::new(&stack, &conn) + .await + .unwrap(); + + let _ = join(client.task(), async { + info!("Looking for battery service"); + let services = client.services_by_uuid(&Uuid::new_short(0x180f)).await.unwrap(); + let service = services.first().unwrap().clone(); + + info!("Looking for value handle"); + let c: Characteristic = client + .characteristic_by_uuid(&service, &Uuid::new_short(0x2a19)) + .await + .unwrap(); + + info!("Subscribing notifications"); + let mut listener = client.subscribe(&c, false).await.unwrap(); + + let _ = join( + async { + loop { + let mut data = [0; 1]; + client.read_characteristic(&c, &mut data[..]).await.unwrap(); + info!("Read value: {}", data[0]); + Timer::after(Duration::from_secs(10)).await; + } + }, + async { + loop { + let data = listener.next().await; + info!("Got notification: {:?} (val: {})", data.as_ref(), data.as_ref()[0]); + } + }, + ) + .await; + }) + .await; + }) + .await; +} diff --git a/trouble-example-apps/src/ble_bas_central_sec.rs b/trouble-example-apps/src/ble_bas_central_sec.rs new file mode 100644 index 0000000..9cb9684 --- /dev/null +++ b/trouble-example-apps/src/ble_bas_central_sec.rs @@ -0,0 +1,98 @@ +use embassy_futures::join::join; +use embassy_time::{Duration, Timer}; +use rand_core::{CryptoRng, RngCore}; +use trouble_host::prelude::*; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; + +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC + +pub async fn run(controller: C, random_generator: &mut RNG) +where + C: Controller, + RNG: RngCore + CryptoRng, +{ + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources) + .set_random_address(address) + .set_random_generator_seed(random_generator); + + let Host { + mut central, + mut runner, + .. + } = stack.build(); + + // NOTE: Modify this to match the address of the peripheral you want to connect to. + // Currently it matches the address used by the peripheral examples + let target: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + + let config = ConnectConfig { + connect_params: Default::default(), + scan_config: ScanConfig { + filter_accept_list: &[(target.kind, &target.addr)], + ..Default::default() + }, + }; + + info!("Scanning for peripheral..."); + let _ = join(runner.run(), async { + info!("Connecting"); + + let conn = central.connect(&config).await.unwrap(); + info!("Connected, creating gatt client"); + + #[cfg(feature = "security")] + { + if let Err(_error) = central.pairing(&conn).await { + error!("Pairing failed"); + } + } + + let client = GattClient::::new(&stack, &conn) + .await + .unwrap(); + + let _ = join(client.task(), async { + info!("Looking for battery service"); + let services = client.services_by_uuid(&Uuid::new_short(0x180f)).await.unwrap(); + let service = services.first().unwrap().clone(); + + info!("Looking for value handle"); + let c: Characteristic = client + .characteristic_by_uuid(&service, &Uuid::new_short(0x2a19)) + .await + .unwrap(); + + info!("Subscribing notifications"); + let mut listener = client.subscribe(&c, false).await.unwrap(); + + let _ = join( + async { + loop { + let mut data = [0; 1]; + client.read_characteristic(&c, &mut data[..]).await.unwrap(); + info!("Read value: {}", data[0]); + Timer::after(Duration::from_secs(10)).await; + } + }, + async { + loop { + let data = listener.next().await; + info!("Got notification: {:?} (val: {})", data.as_ref(), data.as_ref()[0]); + } + }, + ) + .await; + }) + .await; + }) + .await; +} diff --git a/trouble-example-apps/src/ble_bas_peripheral.rs b/trouble-example-apps/src/ble_bas_peripheral.rs new file mode 100644 index 0000000..fcb8855 --- /dev/null +++ b/trouble-example-apps/src/ble_bas_peripheral.rs @@ -0,0 +1,195 @@ +use embassy_futures::join::join; +use embassy_futures::select::select; +use embassy_time::Timer; +use trouble_host::prelude::*; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; + +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att + +// GATT Server definition +#[gatt_server] +struct Server { + battery_service: BatteryService, +} + +/// Battery service +#[gatt_service(uuid = service::BATTERY)] +struct BatteryService { + /// Battery Level + #[descriptor(uuid = descriptors::VALID_RANGE, read, value = [0, 100])] + #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "hello", read, value = "Battery Level")] + #[characteristic(uuid = characteristic::BATTERY_LEVEL, read, notify, value = 10)] + level: u8, + #[characteristic(uuid = "408813df-5dd4-1f87-ec11-cdb001100000", write, read, notify)] + status: bool, +} + +/// Run the BLE stack. +pub async fn run(controller: C) +where + C: Controller, +{ + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut peripheral, runner, .. + } = stack.build(); + + info!("Starting advertising and GATT service"); + let server = Server::new_with_config(GapConfig::Peripheral(PeripheralConfig { + name: "TrouBLE", + appearance: &appearance::power_device::GENERIC_POWER_DEVICE, + })) + .unwrap(); + + let _ = join(ble_task(runner), async { + loop { + match advertise("Trouble Example", &mut peripheral, &server).await { + Ok(conn) => { + // set up tasks when the connection is established to a central, so they don't run when no one is connected. + let a = gatt_events_task(&server, &conn); + let b = custom_task(&server, &conn, &stack); + // run until any task ends (usually because the connection has been closed), + // then return to advertising state. + select(a, b).await; + } + Err(e) => { + #[cfg(feature = "defmt")] + let e = defmt::Debug2Format(&e); + panic!("[adv] error: {:?}", e); + } + } + } + }) + .await; +} + +/// This is a background task that is required to run forever alongside any other BLE tasks. +/// +/// ## Alternative +/// +/// If you didn't require this to be generic for your application, you could statically spawn this with i.e. +/// +/// ```rust,ignore +/// +/// #[embassy_executor::task] +/// async fn ble_task(mut runner: Runner<'static, SoftdeviceController<'static>>) { +/// runner.run().await; +/// } +/// +/// spawner.must_spawn(ble_task(runner)); +/// ``` +async fn ble_task(mut runner: Runner<'_, C, P>) { + loop { + if let Err(e) = runner.run().await { + #[cfg(feature = "defmt")] + let e = defmt::Debug2Format(&e); + panic!("[ble_task] error: {:?}", e); + } + } +} + +/// Stream Events until the connection closes. +/// +/// This function will handle the GATT events and process them. +/// This is how we interact with read and write requests. +async fn gatt_events_task(server: &Server<'_>, conn: &GattConnection<'_, '_, P>) -> Result<(), Error> { + let level = server.battery_service.level; + let reason = loop { + match conn.next().await { + GattConnectionEvent::Disconnected { reason } => break reason, + GattConnectionEvent::Gatt { event } => { + match &event { + GattEvent::Read(event) => { + if event.handle() == level.handle { + let value = server.get(&level); + info!("[gatt] Read Event to Level Characteristic: {:?}", value); + } + } + GattEvent::Write(event) => { + if event.handle() == level.handle { + info!("[gatt] Write Event to Level Characteristic: {:?}", event.data()); + } + } + _ => {} + }; + // This step is also performed at drop(), but writing it explicitly is necessary + // in order to ensure reply is sent. + match event.accept() { + Ok(reply) => reply.send().await, + Err(e) => warn!("[gatt] error sending response: {:?}", e), + }; + } + _ => {} // ignore other Gatt Connection Events + } + }; + info!("[gatt] disconnected: {:?}", reason); + Ok(()) +} + +/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect. +async fn advertise<'values, 'server, C: Controller>( + name: &'values str, + peripheral: &mut Peripheral<'values, C, DefaultPacketPool>, + server: &'server Server<'values>, +) -> Result, BleHostError> { + let mut advertiser_data = [0; 31]; + let len = AdStructure::encode_slice( + &[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ServiceUuids16(&[[0x0f, 0x18]]), + AdStructure::CompleteLocalName(name.as_bytes()), + ], + &mut advertiser_data[..], + )?; + let advertiser = peripheral + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &advertiser_data[..len], + scan_data: &[], + }, + ) + .await?; + info!("[adv] advertising"); + let conn = advertiser.accept().await?.with_attribute_server(server)?; + info!("[adv] connection established"); + Ok(conn) +} + +/// Example task to use the BLE notifier interface. +/// This task will notify the connected central of a counter value every 2 seconds. +/// It will also read the RSSI value every 2 seconds. +/// and will stop when the connection is closed by the central or an error occurs. +async fn custom_task( + server: &Server<'_>, + conn: &GattConnection<'_, '_, P>, + stack: &Stack<'_, C, P>, +) { + let mut tick: u8 = 0; + let level = server.battery_service.level; + loop { + tick = tick.wrapping_add(1); + info!("[custom_task] notifying connection of tick {}", tick); + if level.notify(conn, &tick).await.is_err() { + info!("[custom_task] error notifying connection"); + break; + }; + // read RSSI (Received Signal Strength Indicator) of the connection. + if let Ok(rssi) = conn.raw().rssi(stack).await { + info!("[custom_task] RSSI: {:?}", rssi); + } else { + info!("[custom_task] error getting RSSI"); + break; + }; + Timer::after_secs(2).await; + } +} diff --git a/trouble-example-apps/src/ble_bas_peripheral_sec.rs b/trouble-example-apps/src/ble_bas_peripheral_sec.rs new file mode 100644 index 0000000..9b4820e --- /dev/null +++ b/trouble-example-apps/src/ble_bas_peripheral_sec.rs @@ -0,0 +1,228 @@ +use embassy_futures::join::join; +use embassy_futures::select::select; +use embassy_time::Timer; +use rand_core::{CryptoRng, RngCore}; +use trouble_host::prelude::*; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; + +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att + +// GATT Server definition +#[gatt_server] +struct Server { + battery_service: BatteryService, +} + +/// Battery service +#[gatt_service(uuid = service::BATTERY)] +struct BatteryService { + /// Battery Level + #[descriptor(uuid = descriptors::VALID_RANGE, read, value = [0, 100])] + #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "hello", read, value = "Battery Level")] + #[characteristic(uuid = characteristic::BATTERY_LEVEL, read, notify, value = 10)] + level: u8, + #[characteristic(uuid = "408813df-5dd4-1f87-ec11-cdb001100000", write, read, notify)] + status: bool, +} + +/// Run the BLE stack. +pub async fn run(controller: C, random_generator: &mut RNG) +where + C: Controller, + RNG: RngCore + CryptoRng, +{ + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + info!("Our address = {}", address); + + let mut resources: HostResources = + HostResources::new(); + let stack = trouble_host::new(controller, &mut resources) + .set_random_address(address) + .set_random_generator_seed(random_generator); + let Host { + mut peripheral, + runner, + .. + } = stack.build(); + + info!("Starting advertising and GATT service"); + let server = Server::new_with_config(GapConfig::Peripheral(PeripheralConfig { + name: "TrouBLE", + appearance: &appearance::power_device::GENERIC_POWER_DEVICE, + })) + .unwrap(); + + let _ = join(ble_task(runner), async { + loop { + match advertise("Trouble Example", &mut peripheral, &server).await { + Ok(conn) => { + // set up tasks when the connection is established to a central, so they don't run when no one is connected. + let a = gatt_events_task(&server, &conn); + let b = custom_task(&server, &conn, &stack); + // run until any task ends (usually because the connection has been closed), + // then return to advertising state. + select(a, b).await; + } + Err(e) => { + #[cfg(feature = "defmt")] + let e = defmt::Debug2Format(&e); + panic!("[adv] error: {:?}", e); + } + } + } + }) + .await; +} + +/// This is a background task that is required to run forever alongside any other BLE tasks. +/// +/// ## Alternative +/// +/// If you didn't require this to be generic for your application, you could statically spawn this with i.e. +/// +/// ```rust,ignore +/// +/// #[embassy_executor::task] +/// async fn ble_task(mut runner: Runner<'static, SoftdeviceController<'static>>) { +/// runner.run().await; +/// } +/// +/// spawner.must_spawn(ble_task(runner)); +/// ``` +async fn ble_task(mut runner: Runner<'_, C, P>) { + loop { + if let Err(e) = runner.run().await { + #[cfg(feature = "defmt")] + let e = defmt::Debug2Format(&e); + panic!("[ble_task] error: {:?}", e); + } + } +} + +/// Stream Events until the connection closes. +/// +/// This function will handle the GATT events and process them. +/// This is how we interact with read and write requests. +async fn gatt_events_task( + server: &Server<'_>, + conn: &GattConnection<'_, '_, DefaultPacketPool>, +) -> Result<(), Error> { + let level = server.battery_service.level; + let reason = loop { + match conn.next().await { + GattConnectionEvent::Disconnected { reason } => break reason, + GattConnectionEvent::Gatt { event } => { + let result = match &event { + GattEvent::Read(event) => { + if event.handle() == level.handle { + let value = server.get(&level); + info!("[gatt] Read Event to Level Characteristic: {:?}", value); + } + #[cfg(feature = "security")] + if conn.raw().encrypted() { + None + } else { + Some(AttErrorCode::INSUFFICIENT_ENCRYPTION) + } + #[cfg(not(feature = "security"))] + None + } + GattEvent::Write(event) => { + if event.handle() == level.handle { + info!( + "[gatt] Write Event to Level Characteristic: {:?}", + event.data() + ); + } + #[cfg(feature = "security")] + if conn.raw().encrypted() { + None + } else { + Some(AttErrorCode::INSUFFICIENT_ENCRYPTION) + } + #[cfg(not(feature = "security"))] + None + } + _ => None, + }; + + let reply_result = if let Some(code) = result { + event.reject(code) + } else { + event.accept() + }; + match reply_result { + Ok(reply) => reply.send().await, + Err(e) => warn!("[gatt] error sending response: {:?}", e), + } + } + _ => {} // ignore other Gatt Connection Events + } + }; + info!("[gatt] disconnected: {:?}", reason); + Ok(()) +} + +/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect. +async fn advertise<'values, 'server, C: Controller>( + name: &'values str, + peripheral: &mut Peripheral<'values, C, DefaultPacketPool>, + server: &'server Server<'values>, +) -> Result, BleHostError> { + let mut advertiser_data = [0; 31]; + let len = AdStructure::encode_slice( + &[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ServiceUuids16(&[[0x0f, 0x18]]), + AdStructure::CompleteLocalName(name.as_bytes()), + ], + &mut advertiser_data[..], + )?; + let advertiser = peripheral + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &advertiser_data[..len], + scan_data: &[], + }, + ) + .await?; + info!("[adv] advertising"); + let conn = advertiser.accept().await?.with_attribute_server(server)?; + info!("[adv] connection established"); + Ok(conn) +} + +/// Example task to use the BLE notifier interface. +/// This task will notify the connected central of a counter value every 2 seconds. +/// It will also read the RSSI value every 2 seconds. +/// and will stop when the connection is closed by the central or an error occurs. +async fn custom_task( + server: &Server<'_>, + conn: &GattConnection<'_, '_, P>, + stack: &Stack<'_, C, P>, +) { + let mut tick: u8 = 0; + let level = server.battery_service.level; + loop { + tick = tick.wrapping_add(1); + info!("[custom_task] notifying connection of tick {}", tick); + if level.notify(conn, &tick).await.is_err() { + info!("[custom_task] error notifying connection"); + break; + }; + // read RSSI (Received Signal Strength Indicator) of the connection. + if let Ok(rssi) = conn.raw().rssi(stack).await { + info!("[custom_task] RSSI: {:?}", rssi); + } else { + info!("[custom_task] error getting RSSI"); + break; + }; + Timer::after_secs(2).await; + } +} diff --git a/trouble-example-apps/src/ble_beacon.rs b/trouble-example-apps/src/ble_beacon.rs new file mode 100644 index 0000000..b09945c --- /dev/null +++ b/trouble-example-apps/src/ble_beacon.rs @@ -0,0 +1,109 @@ +// BLE beacon example +// +// A beacon is a device that advertises packets that are constantly being +// updated to reflect the current state of the device, but usually does not +// accept any conections. This allows broadcasting device information. +// + +use bt_hci::cmd::le::*; +use bt_hci::controller::ControllerCmdSync; +use embassy_futures::join::join; +use embassy_time::{Duration, Instant, Timer}; +use trouble_host::prelude::*; + +// Use your company ID (register for free with Bluetooth SIG) +const COMPANY_ID: u16 = 0xFFFF; + +fn make_adv_payload(start: Instant, update_count: u32) -> [u8; 8] { + let mut data = [0u8; 8]; + let elapsed_ms = Instant::now().duration_since(start).as_millis() as u32; + data[0..4].copy_from_slice(&update_count.to_be_bytes()); + data[4..8].copy_from_slice(&elapsed_ms.to_be_bytes()); + data +} + +pub async fn run(controller: C) +where + C: Controller + + for<'t> ControllerCmdSync> + + ControllerCmdSync + + ControllerCmdSync + + ControllerCmdSync + + ControllerCmdSync + + for<'t> ControllerCmdSync> + + for<'t> ControllerCmdSync>, +{ + let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut peripheral, + mut runner, + .. + } = stack.build(); + + let mut adv_data = [0; 64]; + let mut update_count = 0u32; + let start = Instant::now(); + let len = AdStructure::encode_slice( + &[ + AdStructure::CompleteLocalName(b"Trouble Beacon"), + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ManufacturerSpecificData { + company_identifier: COMPANY_ID, + payload: &make_adv_payload(start, update_count), + }, + ], + &mut adv_data[..], + ) + .unwrap(); + + info!("Starting advertising"); + let _ = join(runner.run(), async { + loop { + let mut params = AdvertisementParameters::default(); + params.interval_min = Duration::from_millis(25); + params.interval_max = Duration::from_millis(150); + let _advertiser = peripheral + .advertise( + ¶ms, + Advertisement::NonconnectableNonscannableUndirected { + adv_data: &adv_data[..len], + }, + ) + .await + .unwrap(); + loop { + Timer::after(Duration::from_millis(10)).await; + update_count = update_count.wrapping_add(1); + + let len = AdStructure::encode_slice( + &[ + AdStructure::CompleteLocalName(b"Trouble Beacon"), + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ManufacturerSpecificData { + company_identifier: COMPANY_ID, + payload: &make_adv_payload(start, update_count), + }, + ], + &mut adv_data[..], + ) + .unwrap(); + + peripheral + .update_adv_data(Advertisement::NonconnectableNonscannableUndirected { + adv_data: &adv_data[..len], + }) + .await + .unwrap(); + + if update_count % 100 == 0 { + info!("Still running: Updated the beacon {} times", update_count); + } + } + } + }) + .await; +} diff --git a/trouble-example-apps/src/ble_l2cap_central.rs b/trouble-example-apps/src/ble_l2cap_central.rs new file mode 100644 index 0000000..21092bd --- /dev/null +++ b/trouble-example-apps/src/ble_l2cap_central.rs @@ -0,0 +1,74 @@ +use embassy_futures::join::join; +use embassy_time::{Duration, Timer}; +use trouble_host::prelude::*; + +use crate::common::PSM_L2CAP_EXAMPLES; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; + +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC + +pub async fn run(controller: C) +where + C: Controller, +{ + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut central, + mut runner, + .. + } = stack.build(); + + // NOTE: Modify this to match the address of the peripheral you want to connect to. + // Currently, it matches the address used by the peripheral examples + let target: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + + let config = ConnectConfig { + connect_params: Default::default(), + scan_config: ScanConfig { + filter_accept_list: &[(target.kind, &target.addr)], + ..Default::default() + }, + }; + + info!("Scanning for peripheral..."); + let _ = join(runner.run(), async { + loop { + let conn = central.connect(&config).await.unwrap(); + info!("Connected, creating l2cap channel"); + const PAYLOAD_LEN: usize = 27; + let config = L2capChannelConfig { + mtu: Some(PAYLOAD_LEN as u16), + ..Default::default() + }; + let mut ch1 = L2capChannel::create(&stack, &conn, PSM_L2CAP_EXAMPLES, &config) + .await + .unwrap(); + info!("New l2cap channel created, sending some data!"); + for i in 0..10 { + let tx = [i; PAYLOAD_LEN]; + ch1.send(&stack, &tx).await.unwrap(); + } + info!("Sent data, waiting for them to be sent back"); + let mut rx = [0; PAYLOAD_LEN]; + for i in 0..10 { + let len = ch1.receive(&stack, &mut rx).await.unwrap(); + assert_eq!(len, rx.len()); + assert_eq!(rx, [i; PAYLOAD_LEN]); + } + + info!("Received successfully!"); + + Timer::after(Duration::from_secs(60)).await; + } + }) + .await; +} diff --git a/trouble-example-apps/src/ble_l2cap_peripheral.rs b/trouble-example-apps/src/ble_l2cap_peripheral.rs new file mode 100644 index 0000000..6ff19d0 --- /dev/null +++ b/trouble-example-apps/src/ble_l2cap_peripheral.rs @@ -0,0 +1,88 @@ +use embassy_futures::join::join; +use embassy_time::{Duration, Timer}; +use trouble_host::prelude::*; + +use crate::common::PSM_L2CAP_EXAMPLES; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; + +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC + +pub async fn run(controller: C) +where + C: Controller, +{ + // Hardcoded peripheral address + let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut peripheral, + mut runner, + .. + } = stack.build(); + + let mut adv_data = [0; 31]; + let adv_data_len = AdStructure::encode_slice( + &[AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED)], + &mut adv_data[..], + ) + .unwrap(); + + let mut scan_data = [0; 31]; + let scan_data_len = + AdStructure::encode_slice(&[AdStructure::CompleteLocalName(b"Trouble")], &mut scan_data[..]).unwrap(); + + let _ = join(runner.run(), async { + loop { + info!("Advertising, waiting for connection..."); + let advertiser = peripheral + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &adv_data[..adv_data_len], + scan_data: &scan_data[..scan_data_len], + }, + ) + .await + .unwrap(); + let conn = advertiser.accept().await.unwrap(); + + info!("Connection established"); + + let config = L2capChannelConfig { + mtu: Some(PAYLOAD_LEN as u16), + ..Default::default() + }; + let mut ch1 = L2capChannel::accept(&stack, &conn, &[PSM_L2CAP_EXAMPLES], &config) + .await + .unwrap(); + + info!("L2CAP channel accepted"); + + // Size of payload we're expecting + const PAYLOAD_LEN: usize = 27; + let mut rx = [0; PAYLOAD_LEN]; + for i in 0..10 { + let len = ch1.receive(&stack, &mut rx).await.unwrap(); + assert_eq!(len, rx.len()); + assert_eq!(rx, [i; PAYLOAD_LEN]); + } + + info!("L2CAP data received, echoing"); + Timer::after(Duration::from_secs(1)).await; + for i in 0..10 { + let tx = [i; PAYLOAD_LEN]; + ch1.send(&stack, &tx).await.unwrap(); + } + info!("L2CAP data echoed"); + + Timer::after(Duration::from_secs(60)).await; + } + }) + .await; +} diff --git a/trouble-example-apps/src/ble_scanner.rs b/trouble-example-apps/src/ble_scanner.rs new file mode 100644 index 0000000..3c93c04 --- /dev/null +++ b/trouble-example-apps/src/ble_scanner.rs @@ -0,0 +1,66 @@ +use bt_hci::cmd::le::LeSetScanParams; +use bt_hci::controller::ControllerCmdSync; +use core::cell::RefCell; +use embassy_futures::join::join; +use embassy_time::{Duration, Timer}; +use heapless::Deque; +use trouble_host::prelude::*; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; +const L2CAP_CHANNELS_MAX: usize = 1; + +pub async fn run(controller: C) +where + C: Controller + ControllerCmdSync, +{ + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]); + + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + + let Host { + central, mut runner, .. + } = stack.build(); + + let printer = Printer { + seen: RefCell::new(Deque::new()), + }; + let mut scanner = Scanner::new(central); + let _ = join(runner.run_with_handler(&printer), async { + let mut config = ScanConfig::default(); + config.active = true; + config.phys = PhySet::M1; + config.interval = Duration::from_secs(1); + config.window = Duration::from_secs(1); + let mut _session = scanner.scan(&config).await.unwrap(); + // Scan forever + loop { + Timer::after(Duration::from_secs(1)).await; + } + }) + .await; +} + +struct Printer { + seen: RefCell>, +} + +impl EventHandler for Printer { + fn on_adv_reports(&self, mut it: LeAdvReportsIter<'_>) { + let mut seen = self.seen.borrow_mut(); + while let Some(Ok(report)) = it.next() { + if seen.iter().find(|b| b.raw() == report.addr.raw()).is_none() { + info!("discovered: {:?}", report.addr); + if seen.is_full() { + seen.pop_front(); + } + seen.push_back(report.addr).unwrap(); + } + } + } +} diff --git a/trouble-example-apps/src/common.rs b/trouble-example-apps/src/common.rs new file mode 100644 index 0000000..8f2d3e0 --- /dev/null +++ b/trouble-example-apps/src/common.rs @@ -0,0 +1,7 @@ +// PSM from the dynamic range (0x0080-0x00FF) according to the Bluetooth +// Specification for L2CAP channels using LE Credit Based Flow Control mode. +// used for the BLE L2CAP examples. +// +// https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-60/out/en/host/logical-link-control-and-adaptation-protocol-specification.html#UUID-1ffdf913-7b8a-c7ba-531e-2a9c6f6da8fb +// +pub(crate) const PSM_L2CAP_EXAMPLES: u16 = 0x0081; diff --git a/trouble-example-apps/src/fmt.rs b/trouble-example-apps/src/fmt.rs new file mode 100644 index 0000000..c46fff3 --- /dev/null +++ b/trouble-example-apps/src/fmt.rs @@ -0,0 +1,259 @@ +#![macro_use] +#![allow(unused_macros)] + +use core::fmt::{Debug, Display, LowerHex}; + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unreachable { + ($($x:tt)*) => { + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + #[allow(dead_code)] + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} + +#[allow(unused)] +pub(crate) struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/trouble-example-apps/src/high_throughput_ble_l2cap_central.rs b/trouble-example-apps/src/high_throughput_ble_l2cap_central.rs new file mode 100644 index 0000000..4632c0b --- /dev/null +++ b/trouble-example-apps/src/high_throughput_ble_l2cap_central.rs @@ -0,0 +1,128 @@ +use bt_hci::cmd::le::{LeReadLocalSupportedFeatures, LeSetDataLength, LeSetPhy}; +use bt_hci::controller::{ControllerCmdAsync, ControllerCmdSync}; +use embassy_futures::join::join; +use embassy_time::{Duration, Instant, Timer}; +use trouble_host::prelude::*; + +use crate::common::PSM_L2CAP_EXAMPLES; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; + +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC + +pub async fn run(controller: C) +where + C: Controller + + ControllerCmdSync + + ControllerCmdAsync + + ControllerCmdSync, + P: PacketPool, +{ + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1b, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut central, + mut runner, + .. + } = stack.build(); + + // NOTE: Modify this to match the address of the peripheral you want to connect to. + // Currently, it matches the address used by the peripheral examples + let target: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + + let config = ConnectConfig { + connect_params: ConnectParams { + min_connection_interval: Duration::from_millis(80), + max_connection_interval: Duration::from_millis(80), + ..Default::default() + }, + scan_config: ScanConfig { + filter_accept_list: &[(target.kind, &target.addr)], + ..Default::default() + }, + }; + + info!("Scanning for peripheral..."); + let _ = join(runner.run(), async { + loop { + // Check that the controller used supports the necessary features for high throughput. + let res = stack + .command(LeReadLocalSupportedFeatures::new()) + .await + .expect("LeReadLocalSupportedFeatures command failed"); + assert!(res.supports_le_data_packet_length_extension()); + assert!(res.supports_le_2m_phy()); + + let conn = central.connect(&config).await.expect("Connect failed"); + info!("Connected, creating l2cap channel"); + + // Once connected, request a change in the PDU data length. + stack + .command(LeSetDataLength::new(conn.handle(), 251, 2120)) + .await + .expect("LeSetDataLength command failed"); + + // and request changing the physical link to 2M PHY. + // *Note* Change to the PDU data length and PHY can also be initiated by the peripheral. + conn.set_phy(&stack, PhyKind::Le2M) + .await + .expect("set phy command failed"); + + const PAYLOAD_LEN: usize = 2510; + const L2CAP_MTU: usize = 251; + let l2cap_channel_config = L2capChannelConfig { + mtu: Some(PAYLOAD_LEN as u16 - 6), + mps: Some(L2CAP_MTU as u16 - 4), + // Ensure there will be enough credits to send data throughout the entire connection event. + flow_policy: CreditFlowPolicy::Every(50), + initial_credits: Some(200), + }; + + let mut ch1 = L2capChannel::create(&stack, &conn, PSM_L2CAP_EXAMPLES, &l2cap_channel_config) + .await + .expect("L2capChannel create failed"); + + // Wait for the ratios to switch to 2M PHY. + // If we do not wait, communication will still occur at 1M for the first 500 ms. + Timer::after(Duration::from_secs(1)).await; + + info!("New l2cap channel created, sending some data!"); + + const NUM_PAYLOADS: u8 = 40; + + let start = Instant::now(); + + for i in 0..NUM_PAYLOADS { + let tx = [i; PAYLOAD_LEN]; + ch1.send(&stack, &tx).await.expect("L2CAP send failed"); + } + + let duration = start.elapsed(); + + info!( + "Sent {} bytes at {} kbps, waiting for them to be sent back.", + (PAYLOAD_LEN as u64 * NUM_PAYLOADS as u64), + ((PAYLOAD_LEN as u64 * NUM_PAYLOADS as u64 * 8).div_ceil(duration.as_millis())) + ); + + let mut rx = [0; PAYLOAD_LEN]; + for i in 0..NUM_PAYLOADS { + let len = ch1.receive(&stack, &mut rx).await.expect("L2CAP receive failed"); + assert_eq!(len, rx.len()); + assert_eq!(rx, [i; PAYLOAD_LEN]); + } + + info!("Received successfully!"); + + Timer::after(Duration::from_secs(60)).await; + } + }) + .await; +} diff --git a/trouble-example-apps/src/high_throughput_ble_l2cap_peripheral.rs b/trouble-example-apps/src/high_throughput_ble_l2cap_peripheral.rs new file mode 100644 index 0000000..4318ac4 --- /dev/null +++ b/trouble-example-apps/src/high_throughput_ble_l2cap_peripheral.rs @@ -0,0 +1,115 @@ +use bt_hci::cmd::le::LeReadLocalSupportedFeatures; +use bt_hci::controller::ControllerCmdSync; +use embassy_futures::join::join; +use embassy_time::{Duration, Instant, Timer}; +use trouble_host::prelude::*; + +use crate::common::PSM_L2CAP_EXAMPLES; + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; + +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 3; // Signal + att + CoC + +pub async fn run(controller: C) +where + C: Controller + ControllerCmdSync, + P: PacketPool, +{ + // Hardcoded peripheral address + let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources = HostResources::new(); + let stack = trouble_host::new(controller, &mut resources).set_random_address(address); + let Host { + mut peripheral, + mut runner, + .. + } = stack.build(); + + let mut adv_data = [0; 31]; + let adv_data_len = AdStructure::encode_slice( + &[AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED)], + &mut adv_data[..], + ) + .unwrap(); + + let mut scan_data = [0; 31]; + let scan_data_len = + AdStructure::encode_slice(&[AdStructure::CompleteLocalName(b"TroubleHT")], &mut scan_data[..]).unwrap(); + + let _ = join(runner.run(), async { + loop { + // Check that the controller used supports the necessary features for high throughput. + let res = stack + .command(LeReadLocalSupportedFeatures::new()) + .await + .expect("LeReadLocalSupportedFeatures command failed"); + assert!(res.supports_le_data_packet_length_extension()); + assert!(res.supports_le_2m_phy()); + + info!("Advertising, waiting for connection..."); + let advertiser = peripheral + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &adv_data[..adv_data_len], + scan_data: &scan_data[..scan_data_len], + }, + ) + .await + .expect("Advertising failed"); + let conn = advertiser.accept().await.expect("Connection failed"); + + info!("Connection established"); + + const PAYLOAD_LEN: usize = 2510; + const L2CAP_MTU: usize = 251; + let l2cap_channel_config = L2capChannelConfig { + mtu: Some(PAYLOAD_LEN as u16 - 6), + mps: Some(L2CAP_MTU as u16 - 4), + // Ensure there will be enough credits to send data throughout the entire connection event. + flow_policy: CreditFlowPolicy::Every(50), + initial_credits: Some(200), + }; + + let mut ch1 = L2capChannel::accept(&stack, &conn, &[PSM_L2CAP_EXAMPLES], &l2cap_channel_config) + .await + .expect("L2capChannel create failed"); + + info!("L2CAP channel accepted"); + + // Size of payload we're expecting + const NUM_PAYLOADS: u8 = 40; + let mut rx = [0; PAYLOAD_LEN]; + for i in 0..NUM_PAYLOADS { + let len = ch1.receive(&stack, &mut rx).await.expect("L2CAP receive failed"); + assert_eq!(len, rx.len()); + assert_eq!(rx, [i; PAYLOAD_LEN]); + } + + info!("L2CAP data received, echoing"); + Timer::after(Duration::from_secs(1)).await; + + let start = Instant::now(); + + for i in 0..NUM_PAYLOADS { + let tx = [i; PAYLOAD_LEN]; + ch1.send(&stack, &tx).await.expect("L2CAP send failed"); + } + + let duration = start.elapsed(); + + info!( + "L2CAP data of {} bytes echoed at {} kbps.", + (PAYLOAD_LEN as u64 * NUM_PAYLOADS as u64), + ((PAYLOAD_LEN as u64 * NUM_PAYLOADS as u64 * 8).div_ceil(duration.as_millis())) + ); + + Timer::after(Duration::from_secs(60)).await; + } + }) + .await; +} diff --git a/trouble-example-apps/src/lib.rs b/trouble-example-apps/src/lib.rs new file mode 100644 index 0000000..a1f4ed7 --- /dev/null +++ b/trouble-example-apps/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] +#![allow(dead_code)] + +pub(crate) mod common; +pub(crate) mod fmt; + +pub mod ble_advertise; +pub mod ble_advertise_multiple; +pub mod ble_bas_central; +pub mod ble_bas_central_sec; +pub mod ble_bas_peripheral; +pub mod ble_bas_peripheral_sec; +pub mod ble_beacon; +pub mod ble_l2cap_central; +pub mod ble_l2cap_peripheral; +pub mod ble_scanner; +pub mod high_throughput_ble_l2cap_central; +pub mod high_throughput_ble_l2cap_peripheral;