From 70641102930c067dc361026c99c626c85025196f Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Thu, 26 Mar 2026 15:51:40 -0600 Subject: [PATCH] feat: restore Heltec V3, add firmware output, support all common boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts the removal of heltec_v3 — it's a major board. Xtensa builds use the esp toolchain (+esp via espup); check-all gracefully skips boards whose toolchain isn't installed. Build artifacts now land in firmware/ with readable names like lora-dongle-rak_4631-release.elf instead of being buried in target/. DESIGN.md updated with "support all common boards" principle (#10) and corrected toolchain policy (#11). --- Cargo.lock | 729 ++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 12 + DESIGN.md | 15 +- Justfile | 70 +++- src/board/heltec_v3.rs | 48 +++ src/main.rs | 2 + 6 files changed, 854 insertions(+), 22 deletions(-) create mode 100644 src/board/heltec_v3.rs diff --git a/Cargo.lock b/Cargo.lock index 0114f563..49c64828 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + [[package]] name = "autocfg" version = "1.5.0" @@ -35,6 +41,21 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + [[package]] name = "bitfield" version = "0.13.2" @@ -47,6 +68,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" +[[package]] +name = "bitfield" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0" + [[package]] name = "bitflags" version = "1.3.2" @@ -77,6 +104,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "num-traits", +] + [[package]] name = "cobs" version = "0.3.0" @@ -92,7 +128,7 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" dependencies = [ - "bare-metal", + "bare-metal 0.2.5", "bitfield 0.13.2", "embedded-hal 0.2.7", "volatile-register", @@ -136,8 +172,18 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] @@ -154,13 +200,37 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", "quote", "syn 2.0.117", ] @@ -206,13 +276,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "delegate" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "document-features" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ - "litrs", + "litrs 1.0.0", ] [[package]] @@ -251,7 +332,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", "syn 2.0.117", @@ -333,6 +414,19 @@ dependencies = [ "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", +] + [[package]] name = "embassy-sync" version = "0.6.2" @@ -421,6 +515,26 @@ dependencies = [ "embedded-io-async", ] +[[package]] +name = "embassy-usb-synopsys-otg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d46be92e72bcf39e623ff74d739a8ab29b02f4909a9b05986ca81c2157ac254a" +dependencies = [ + "critical-section", + "embassy-sync 0.5.0", + "embassy-usb-driver", +] + +[[package]] +name = "embedded-can" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" +dependencies = [ + "nb 1.1.0", +] + [[package]] name = "embedded-hal" version = "0.2.7" @@ -446,6 +560,16 @@ dependencies = [ "embedded-hal 1.0.0", ] +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + [[package]] name = "embedded-io" version = "0.6.1" @@ -482,6 +606,221 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "enumset" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "esp-backtrace" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7660d85e3e7b0e113aaeeffb1a155e64a09a5035d4104031875acdba4cb68e" +dependencies = [ + "defmt 0.3.100", + "esp-build", + "esp-println", + "semihosting", +] + +[[package]] +name = "esp-build" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94a4b8d74e7cc7baabcca5b2277b41877e039ad9cd49959d48ef94dac7eab4b" +dependencies = [ + "quote", + "syn 2.0.117", + "termcolor", +] + +[[package]] +name = "esp-config" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7584e4cd1dac06201fd92fff1c84b396be5458ac4d93e9457e7a89b1b42c60e" +dependencies = [ + "document-features", +] + +[[package]] +name = "esp-hal" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5605e1518d63f7bf9fbd9885e61d2896060d2e4f28954736bdd74da911b676" +dependencies = [ + "basic-toml", + "bitfield 0.17.0", + "bitflags 2.11.0", + "bytemuck", + "cfg-if", + "chrono", + "critical-section", + "delegate", + "document-features", + "embassy-embedded-hal", + "embassy-futures", + "embassy-sync 0.6.2", + "embassy-usb-driver", + "embassy-usb-synopsys-otg", + "embedded-can", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "embedded-io-async", + "enumset", + "esp-build", + "esp-config", + "esp-hal-procmacros", + "esp-metadata", + "esp-riscv-rt", + "esp-synopsys-usb-otg", + "esp32s3", + "fugit", + "nb 1.1.0", + "paste", + "portable-atomic", + "rand_core", + "serde", + "strum", + "ufmt-write", + "usb-device", + "void", + "xtensa-lx", + "xtensa-lx-rt", +] + +[[package]] +name = "esp-hal-embassy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d0f2537ea2ff9bea26a1c8bfe43ad580d4c89febf27189653a9cf95f1f7961" +dependencies = [ + "critical-section", + "document-features", + "embassy-executor", + "embassy-time-driver", + "esp-build", + "esp-config", + "esp-hal", + "esp-hal-procmacros", + "esp-metadata", + "portable-atomic", + "static_cell", +] + +[[package]] +name = "esp-hal-procmacros" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69a9a8706b7d1182b56335d196e70eeb04e2b70f4b8db96432898bd3c2bdb91e" +dependencies = [ + "darling 0.20.11", + "document-features", + "litrs 0.4.2", + "object", + "proc-macro-crate", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "esp-metadata" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9972bbb21dcafe430b87f92efc7a788978a2d17cf8f572d104beeb48602482a" +dependencies = [ + "anyhow", + "basic-toml", + "serde", + "strum", +] + +[[package]] +name = "esp-println" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee38e87bc7e303c299047c0e9bcd0f8ccca7c7e70d1fd78bbb565db14f33beb6" +dependencies = [ + "critical-section", + "defmt 0.3.100", + "esp-build", + "log", + "portable-atomic", +] + +[[package]] +name = "esp-riscv-rt" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94aca65db6157aa5f42d9df6595b21462f28207ca4230b799aa3620352ef6a72" +dependencies = [ + "document-features", + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "esp-synopsys-usb-otg" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8938451cb19032f13365328ea66ab38c8d16deecdf322067442297110eb74468" +dependencies = [ + "critical-section", + "embedded-hal 0.2.7", + "ral-registers", + "usb-device", + "vcell", +] + +[[package]] +name = "esp32s3" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe69dca8db0ed37d222555f12dfaf99232592e84f54e56dd81e450a4769718bf" +dependencies = [ + "critical-section", + "vcell", +] + [[package]] name = "fixed" version = "1.31.0" @@ -500,6 +839,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fugit" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6" +dependencies = [ + "gcd", +] + [[package]] name = "futures-core" version = "0.3.32" @@ -529,6 +877,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + [[package]] name = "half" version = "2.7.1" @@ -558,6 +912,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "heapless" version = "0.8.0" @@ -569,18 +929,52 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "litrs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +dependencies = [ + "proc-macro2", +] + [[package]] name = "litrs" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -601,6 +995,10 @@ dependencies = [ "embassy-sync 0.6.2", "embassy-time", "embassy-usb", + "esp-backtrace", + "esp-hal", + "esp-hal-embassy", + "esp-println", "heapless", "lora-phy", "minijinja", @@ -630,6 +1028,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + [[package]] name = "memo-map" version = "0.3.3" @@ -646,6 +1050,12 @@ dependencies = [ "serde", ] +[[package]] +name = "mutex-trait" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" + [[package]] name = "nb" version = "0.1.3" @@ -802,6 +1212,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -824,6 +1243,12 @@ dependencies = [ "defmt 0.3.100", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -846,6 +1271,15 @@ dependencies = [ "serde", ] +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.8+spec-1.1.0", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -886,12 +1320,65 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r0" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" + +[[package]] +name = "ral-registers" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46b71a9d9206e8b46714c74255adcaea8b11e0350c1d8456165073c3f75fc81a" + [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "riscv" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", + "paste", + "riscv-macros", + "riscv-pac", +] + +[[package]] +name = "riscv-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "riscv-pac" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" + +[[package]] +name = "riscv-rt-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "rustc_version" version = "0.2.3" @@ -901,6 +1388,24 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semihosting" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e4abf97879f4e80db69a9fba7bd64998e9bdad25f58ef045a778e191172fd4" + [[package]] name = "semver" version = "0.9.0" @@ -946,6 +1451,24 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "ssmarshal" version = "1.0.0" @@ -977,6 +1500,28 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.117", +] + [[package]] name = "syn" version = "1.0.109" @@ -999,6 +1544,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "2.0.18" @@ -1019,12 +1573,89 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + +[[package]] +name = "toml_edit" +version = "0.25.8+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" +dependencies = [ + "indexmap", + "toml_datetime 1.1.0+spec-1.1.0", + "toml_parser", + "winnow 1.0.0", +] + +[[package]] +name = "toml_parser" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +dependencies = [ + "winnow 1.0.0", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "typenum" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "ufmt-write" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1069,7 +1700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb573c76e7884035ac5e1ab4a81234c187a82b6100140af0ab45757650ccda38" dependencies = [ "byteorder", - "hashbrown", + "hashbrown 0.13.2", "log", "proc-macro2", "quote", @@ -1105,6 +1736,90 @@ dependencies = [ "vcell", ] +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +dependencies = [ + "memchr", +] + +[[package]] +name = "xtensa-lx" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e758f94e1a1f71758f94052a2766dcb12604998eb372b8b2e30576e3ab1ba1e6" +dependencies = [ + "bare-metal 1.0.0", + "mutex-trait", + "spin", +] + +[[package]] +name = "xtensa-lx-rt" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0307d03dadbf95633942e13901984f2059df4c963367348168cbd21c962669" +dependencies = [ + "anyhow", + "bare-metal 1.0.0", + "document-features", + "enum-as-inner", + "minijinja", + "r0", + "serde", + "strum", + "toml", + "xtensa-lx", + "xtensa-lx-rt-proc-macros", +] + +[[package]] +name = "xtensa-lx-rt-proc-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11277b1e4cbb7ffe44678c668518b249c843c81df249b8f096701757bc50d7ee" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "zerocopy" version = "0.8.47" diff --git a/Cargo.toml b/Cargo.toml index c7dcfc20..ede29fab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,12 @@ postcard = { version = "1.0", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } static_cell = "2.1" +# ESP32-S3 (Heltec V3) — builds with nightly + -Zbuild-std=core +esp-backtrace = { version = "0.14", optional = true, features = ["esp32s3", "panic-handler", "defmt"] } +esp-hal = { version = "0.22", optional = true, features = ["esp32s3"] } +esp-hal-embassy = { version = "0.5", optional = true, features = ["esp32s3"] } +esp-println = { version = "0.12", optional = true, features = ["esp32s3", "defmt-espflash"] } + # nRF52840 (RAK 4631) cortex-m-rt = { version = "0.7", optional = true } embassy-nrf = { version = "0.2", optional = true, features = ["nrf52840", "gpiote", "time-driver-rtc1", "defmt"] } @@ -29,6 +35,12 @@ minijinja = "2.0" owo-colors = "4.0" [features] +heltec_v3 = [ + "dep:esp-backtrace", + "dep:esp-hal", + "dep:esp-hal-embassy", + "dep:esp-println", +] rak_4631 = [ "dep:cortex-m-rt", "dep:embassy-nrf", diff --git a/DESIGN.md b/DESIGN.md index 86ef8771..bd2c2ef0 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -47,10 +47,14 @@ radio — it just gives you clean access to it. 9. **Easy board support.** Adding a new board means dropping a `.rs` file and adding a Cargo feature. Nothing else changes. -10. **Standard toolchain only.** No custom Rust forks, no `espup`, no special SDKs. - If a board can't build with stock `rustup`, it doesn't ship. This rules out - Xtensa-based ESP32 chips (S2, S3) until upstream Rust fully supports them. - RISC-V ESP32 variants (C3, C6, H2) are fine. +10. **Support all common LoRa boards.** This project supports every common LoRa + board, and even some uncommon ones. The architecture is designed to accommodate + them cleanly. If the code isn't structured for a new board, fix the code. + +11. **Standard toolchain.** No custom Rust forks or special SDKs. Use stock `rustup` + channels (stable, nightly). Xtensa targets use nightly + `-Zbuild-std`; Cortex-M + and RISC-V targets use stable. Boards that need the `esp` toolchain (via `espup`) + are supported but gracefully skipped when the toolchain isn't installed. ## Architecture @@ -96,4 +100,5 @@ keep, but never prematurely. | Boot behavior | Idle until host commands | Host-driven principle | | Persistence | None | Always start fresh, true dumb pipe | | Board codegen | Jinja2 via build.rs | Auto-discovers boards from filesystem | -| Rust toolchain | Standard `rustup` only | No forks, no espup, no special SDKs | +| Rust toolchain | Stock `rustup` (stable/nightly) | No forks; `espup` for Xtensa boards only | +| Firmware output | `firmware/lora-dongle-{board}-{profile}.elf` | Readable names, not buried in `target/` | diff --git a/Justfile b/Justfile index c9e5e482..e5b58670 100644 --- a/Justfile +++ b/Justfile @@ -1,32 +1,82 @@ set shell := ["bash", "-c"] -rak_4631 := "rak_4631 thumbv7em-none-eabihf nRF52840_xxAA" -# Check all boards compile cleanly +# Board definitions: feature target chip [extra-cargo-args] +# Boards requiring the esp toolchain are marked — check-all skips them gracefully +# if the toolchain isn't installed. +heltec_v3 := "heltec_v3 xtensa-esp32s3-none-elf esp32s3" +rak_4631 := "rak_4631 thumbv7em-none-eabihf nRF52840_xxAA" + +firmware_dir := "firmware" + +# All known boards +boards := "heltec_v3 rak_4631" + +# Check all boards that can build with the available toolchain check-all: - just check rak_4631 + @for board in {{boards}}; do \ + if just _can_build $board 2>/dev/null; then \ + echo "── checking $board ──"; \ + just check $board; \ + else \ + echo "── skipping $board (toolchain not available) ──"; \ + fi; \ + done # Check a single board compiles check board: - @read -r feat target chip <<< "$(just _info {{board}})"; \ - cargo check --target $target --features $feat + @just _cargo {{board}} check # Run clippy on a single board clippy board: @read -r feat target chip <<< "$(just _info {{board}})"; \ - cargo clippy --target $target --features $feat -- -D warnings + tc=""; \ + case "$target" in xtensa-*) tc="+esp" ;; esac; \ + cargo $tc clippy --target $target --features $feat -- -D warnings + +# Build release firmware and copy to firmware/ with a readable name +build board profile="release": + @just _cargo {{board}} "build --{{profile}}" + @just _copy_firmware {{board}} {{profile}} # Build and flash a board via probe-rs flash board: + @just build {{board}} release @read -r feat target chip <<< "$(just _info {{board}})"; \ - cargo build --release --target $target --features $feat; \ - probe-rs run --chip $chip --target $target --features $feat + probe-rs run --chip $chip "{{firmware_dir}}/lora-dongle-{{board}}-release.elf" # Show binary size for a release build size board: + @just _cargo {{board}} "size --release" + +# ── Private helpers ─────────────────────────────────────────────────── + +# Run a cargo command for a board, using +esp toolchain for Xtensa targets +[private] +_cargo board cmd: @read -r feat target chip <<< "$(just _info {{board}})"; \ - cargo size --release --target $target --features $feat + tc=""; \ + case "$target" in xtensa-*) tc="+esp" ;; esac; \ + cargo $tc {{cmd}} --target $target --features $feat + +# Check if a board's toolchain is available +[private] +_can_build board: + @read -r feat target chip <<< "$(just _info {{board}})"; \ + case "$target" in \ + xtensa-*) rustup toolchain list | grep -q "^esp" ;; \ + *) rustup target list --installed | grep -q "^$target$" || rustup target add "$target" >/dev/null 2>&1 ;; \ + esac + +[private] +_copy_firmware board profile: + @mkdir -p {{firmware_dir}} + @read -r feat target chip <<< "$(just _info {{board}})"; \ + src="target/$target/{{profile}}/lora-dongle"; \ + dst="{{firmware_dir}}/lora-dongle-{{board}}-{{profile}}"; \ + if [ -f "$src" ]; then cp "$src" "$dst.elf"; echo "→ $dst.elf"; fi [private] _info name: - @if [ "{{name}}" == "rak_4631" ]; then echo "{{rak_4631}}"; \ + @if [ "{{name}}" == "heltec_v3" ]; then echo "{{heltec_v3}}"; \ + elif [ "{{name}}" == "rak_4631" ]; then echo "{{rak_4631}}"; \ else echo "Unknown board: {{name}}" >&2; exit 1; fi diff --git a/src/board/heltec_v3.rs b/src/board/heltec_v3.rs new file mode 100644 index 00000000..d1c6604c --- /dev/null +++ b/src/board/heltec_v3.rs @@ -0,0 +1,48 @@ +use embassy_time::Delay; + +use lora_phy::iv::GenericSx126xInterfaceVariant; +use lora_phy::sx126x::{Sx1262, Sx126x}; + +// ── Concrete peripheral types ──────────────────────────────────────── + +// TODO: replace these placeholders with real esp-hal peripheral types +// SPI2 for SX1262 (SCK=9, MOSI=10, MISO=11, NSS=8) +// GPIO for radio control (RST=12, DIO1=14, BUSY=13) +// Vext pin (GPIO21) LOW to power OLED +// I2C0 for SSD1306 (SDA=17, SCL=18) +// USB OTG for CDC-ACM + +type Iv = GenericSx126xInterfaceVariant<(), ()>; +type RadioSpiDevice = (); +pub type RadioDriver = Sx126x; + +// ── Peripheral bundles ─────────────────────────────────────────────── + +pub struct RadioParts { + pub driver: RadioDriver, + pub delay: Delay, +} + +pub struct UsbParts { + pub driver: (), +} + +pub struct DisplayParts { + pub i2c: (), +} + +// ── Board init ─────────────────────────────────────────────────────── + +pub struct Board; + +impl Board { + pub fn init() -> Self { + // TODO: call esp_hal::init(esp_hal::Config::default()) + Self + } + + pub fn into_parts(self) -> (RadioParts, UsbParts, Option) { + // TODO: wire up real peripherals once building with nightly + -Zbuild-std + todo!("Heltec V3 peripheral init") + } +} diff --git a/src/main.rs b/src/main.rs index d15362c8..69debf91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,8 @@ use crate::channel::{CommandChannel, ResponseChannel, StatusWatch}; cfg_if::cfg_if! { if #[cfg(feature = "rak_4631")] { use panic_probe as _; + } else if #[cfg(feature = "heltec_v3")] { + use esp_backtrace as _; } }