├── .gitignore ├── .gitmodules ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── esp32cam ├── .cargo │ └── config.toml ├── .gitignore ├── Cargo.toml ├── build.rs ├── components │ └── bindings.h ├── components_esp32.lock ├── rust-toolchain.toml ├── sdkconfig.defaults └── src │ ├── espcam.rs │ ├── lib.rs │ ├── main.rs │ └── wifi_handler.rs ├── wifi_tank ├── .cargo │ └── config.toml ├── .gitignore ├── .vscode │ └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── build.rs ├── rust-toolchain.toml └── src │ ├── main.rs │ └── motors.rs ├── wifi_tank_controller_client ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs └── wifitank.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | /.vscode 7 | /.embuild 8 | /target 9 | /Cargo.lock 10 | 11 | 12 | # These are backup files generated by rustfmt 13 | **/*.rs.bk 14 | 15 | # MSVC Windows builds of rustc generate these, which store debugging information 16 | *.pdb 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "esp32cam/components/esp32-camera"] 2 | path = esp32cam/components/esp32-camera 3 | url = git@github.com:espressif/esp32-camera.git 4 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021-2024 esp-rs 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2021-2024 esp-rs 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wifi Tank project 2 | 3 | This project is an ESP32 Wifi-controlled tank/rover with a camera. 4 | 5 | ![Frontal photo of built rover](./wifitank.jpg) 6 | 7 | ## Software 8 | 9 | The project consists of three crates for the rover control board, a client on the host machine, and the camera board. All should be able to connect to the same Wifi network. 10 | 11 | To build them install Rust and `espup` and run: 12 | 13 | ```bash 14 | $ source ~/export-esp.sh 15 | $ cargo run --release 16 | ``` 17 | 18 | ### wifi_tank 19 | Crate link: [wifi_tank](./wifi_tank/) 20 | 21 | This crate contains the ESP32 code for the control board on the rover itself. 22 | This should be flashed onto that board. 23 | 24 | Your Wifi credentials should be added to `.cargo/config.toml` before building, 25 | and use 2.5GHz Wifi and WPA2 personal authentication. 26 | 27 | The rover will connect to the Wifi with the hostname `wifitank` and 28 | listen for a UDP connection from the client on port 8080. 29 | 30 | ### wifi_tank_controller_client 31 | Crate link: [wifi_tank_controller_client](./wifi_tank_controller_client/) 32 | 33 | This client runs on the host machine connected to the same Wifi, and 34 | communicates to the control board and camera board on the rover via 35 | their hostnames (`wifitank` and `espressif` respectively). 36 | 37 | It expects a USB gamepad for input and uses the Directional Pad for 38 | controls. 39 | 40 | ### esp32cam 41 | Crate link: [esp32cam](./esp32cam/) 42 | 43 | This crate contains the ESP32 code for the ESP32-CAM module and should 44 | be flashed onto the camera board (see below for flashing details). The 45 | code serves a stream on HTTP at [http://espressif/camera](http://espressif/camera) 46 | once connected to the Wifi. 47 | 48 | This code is a modified version of [@Kezii's esp32cam_rs](https://github.com/Kezii/esp32cam_rs) crate. 49 | 50 | Initialise the `esp32-camera` git submodule with: 51 | 52 | ```bash 53 | $ git submodule update --init 54 | ``` 55 | 56 | The Wifi credentials should be added in `.cargo/config.toml` as for the the `wifi_tank` crate. 57 | 58 | ## Hardware 59 | 60 | - Control board: Any ESP32 development board - although one with 5V 61 | output is preferable. A Lolin D32 Pro board was used in development 62 | (but this does not have 5V out). Note this code is written for ESP32 Qtensa 63 | boards but it should be simple to adapt for the ESP32-C series RISC-V based ones too. 64 | - ESP32-CAM module. 65 | - Any USB-UART adapter to flash the ESP32-CAM module. 66 | - Any power bank to power the control board (via micro-USB). 67 | - A 4-battery AA battery holder for powering the motors (I used 68 | rechargeable batteries). 69 | - 4x L298N motor drivers (or any that will work with 3.3V control inputs) 70 | - 4x DC Gearbox motors (I used the "TT Motor" ones) 71 | - A robot car chassis (I used the Elegoo one, but you could also cut 72 | this from card). 73 | 74 | ### Flashing ESP32-CAM module 75 | 76 | In order to flash the ESP32-CAM module you must connect the `IO0` pin to 77 | the adjacent `GND` pin. 78 | 79 | Then connect the UART adapter with the UART `TX` pin connecting to the `U0R` 80 | pin on the ESP32-CAM, and the UART `RX` pin connecting to the `U0T` pin. 81 | Use the 5V output on the UART adapter (if possible) to the 5V input on the ESP32-CAM, 82 | and connect the ground from the UART adapter to the adjacent ground pin 83 | next to the 5V input (do not use the `GND/R` reset pin as this keeps the 84 | device resetting). 85 | 86 | Once connected you must reset the ESP32-CAM with the reset button on the 87 | board immediately before flashing, you then have ~2 seconds to start the 88 | flashing process with espflash. 89 | 90 | You can then connect it without the `IO0` jumper to test it (and see the serial output with `picom`). 91 | 92 | ### GPIO Pin setup 93 | 94 | #### Control board (Lolin D32 Pro) 95 | 96 | See [LastMinuteEngineers' Interface L298N DC Motor Driver Module with Arduino article](https://lastminuteengineers.com/l298n-dc-stepper-driver-arduino-tutorial/) for help with the Motor Driver pins. 97 | 98 | Connect the ground pin of the control board to the shared ground connections of the motor drivers. 99 | 100 | Left motor: 101 | 102 | - Pin 13 to EN1 103 | - Pin 12 to EN2 104 | - Pin 26 to EN3 105 | - Pin 25 to EN4 106 | 107 | 108 | Right motor: 109 | 110 | - Pin 21 to EN1 111 | - Pin 19 to EN2 112 | - Pin 22 to EN3 113 | - Pin 23 to EN4 114 | 115 | #### ESP32-CAM 116 | 117 | Here the GPIO pins all dealt with internally and the board communciates over Wifi. 118 | 119 | Connect the 5V input to a 5V source (e.g. from the UART converter or a 5V output from the control board if possible). 120 | Connect the grounds with the 5V source. 121 | 122 | Note you can use the 5V output from the L298N motor drivers, however at higher input voltages this is not guaranteed to be stable. 123 | 124 | Note technically the ESP32-CAM also supports 3.3V input, but this is unreliable and often hits brownout issues when actually working. 125 | 126 | Note this code requires SPI RAM to be enabled - `CONFIG_ESP32_SPIRAM_SUPPORT=y` in sdkconfig.defaults 127 | 128 | ## Possible improvements 129 | 130 | - Support ESP BLE Provisioning for setting the Wifi credentials via BLE for both 131 | boards. 132 | - Create a nostd version of the esp32cam crate. 133 | - Motor speed control (use ENA and ENB pins with PWM). 134 | - Test external antenna for ESP32-CAM 135 | 136 | ## Useful Resources 137 | 138 | - [@flyaruu's esp32-nostd crate](https://github.com/flyaruu/esp32-nostd) - the best reference for nostd esp_wifi usage. 139 | - [Floodplain's Rust on ESP32 no_std YouTube series](https://www.youtube.com/watch?v=o8yNNVFzNnM&list=PL0U7YUX2VnBFbwTi96wUB1nZzPVN3HzgS) 140 | - [Embassy on ESP blog series](https://dev.to/theembeddedrustacean/embassy-on-esp-gpio-5594) 141 | - [ESP32 Embedded Rust at the HAL blog series](https://blog.theembeddedrustacean.com/esp32-embedded-rust-at-the-hal-gpio-interrupts) 142 | - [LastMinuteEngineers' in-depth L298N motor driver tutorial](https://lastminuteengineers.com/l298n-dc-stepper-driver-arduino-tutorial/) 143 | - [Rust Networking with the Raspberry Pi Pico](https://murraytodd.medium.com/rust-networking-with-the-raspberry-pi-pico-w-002384a5954b) 144 | - [Rust Client/Server Comms on the Raspberry Pi Pico](https://murraytodd.medium.com/client-server-comms-on-the-raspberry-pi-pico-w-b0767ecfb4dc) 145 | - [IoT with Rust on ESP](https://dev.to/theembeddedrustacean/iot-with-rust-on-esp-connecting-wifi-4be6) 146 | - [Connect ESP32 to Wifi with Rust](https://medium.com/@rajeshpachaikani/connect-esp32-to-wifi-with-rust-7d12532f539b) 147 | - [LastMinuteEngineers' Getting Started with ESP32 CAM](https://lastminuteengineers.com/getting-started-with-esp32-cam/) 148 | -------------------------------------------------------------------------------- /esp32cam/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "xtensa-esp32-espidf" 3 | 4 | [target.xtensa-esp32-espidf] 5 | linker = "ldproxy" 6 | # runner = "espflash flash --monitor" # Select this runner for espflash v3.x.x 7 | runner = "espflash flash --monitor" # Select this runner for espflash v2.x.x 8 | rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110 9 | 10 | [unstable] 11 | build-std = ["std", "panic_abort"] 12 | 13 | [env] 14 | MCU="esp32" 15 | WIFI_SSID = "YOUR_SSID" 16 | WIFI_PASSWORD = "YOUR_PASSWORD" 17 | # Note: this variable is not used by the pio builder (`cargo build --features pio`) 18 | ESP_IDF_VERSION = "v5.2.2" 19 | 20 | # Workaround for https://github.com/esp-rs/esp-idf-template/issues/174 21 | CRATE_CC_NO_DEFAULTS = "1" 22 | -------------------------------------------------------------------------------- /esp32cam/.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /.embuild 3 | /target 4 | /Cargo.lock 5 | -------------------------------------------------------------------------------- /esp32cam/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "esp32cam" 3 | version = "0.1.0" 4 | authors = ["James McMurray "] 5 | edition = "2021" 6 | resolver = "2" 7 | rust-version = "1.77" 8 | 9 | [[bin]] 10 | name = "esp32cam" 11 | harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors 12 | 13 | [profile.release] 14 | opt-level = "s" 15 | lto = true 16 | 17 | [profile.dev] 18 | debug = true # Symbols are nice and they don't increase the size on Flash 19 | opt-level = "z" 20 | 21 | [features] 22 | default = ["std", "embassy", "esp-idf-svc/native"] 23 | 24 | pio = ["esp-idf-svc/pio"] 25 | std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"] 26 | alloc = ["esp-idf-svc/alloc"] 27 | nightly = ["esp-idf-svc/nightly"] 28 | experimental = ["esp-idf-svc/experimental"] 29 | embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"] 30 | 31 | [dependencies] 32 | log = { version = "0.4", default-features = false } 33 | esp-idf-svc = { version = "0.49", default-features = false } 34 | 35 | embedded-svc = "0.28" 36 | anyhow = "1" 37 | 38 | bstr = { version = "1.8.0", default-features = false } 39 | tokio = { version = "*", features = ["rt", "time", "sync","macros"] } 40 | lazy_static = "1.4.0" 41 | uuid = { version = "1.2.2", default-features = false, features = ["macro-diagnostics"] } 42 | rgb565 = "0.1.3" 43 | image = { version = "0.25", default-features = false, features = ["png"] } 44 | serde = { version = "1", features = ["derive"]} 45 | serde_json = { version = "1"} 46 | 47 | esp-idf-hal = "0.44" 48 | esp-idf-sys = "0.35" 49 | thiserror = "1" 50 | 51 | [build-dependencies] 52 | embuild = "0.32.0" 53 | 54 | [[package.metadata.esp-idf-sys.extra_components]] 55 | component_dirs = "components/esp32-camera" 56 | bindings_header = "components/bindings.h" 57 | bindings_module = "camera" 58 | 59 | 60 | -------------------------------------------------------------------------------- /esp32cam/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | embuild::espidf::sysenv::output(); 3 | } 4 | -------------------------------------------------------------------------------- /esp32cam/components/bindings.h: -------------------------------------------------------------------------------- 1 | #include "esp_camera.h" 2 | -------------------------------------------------------------------------------- /esp32cam/components_esp32.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | idf: 3 | source: 4 | type: idf 5 | version: 5.2.2 6 | manifest_hash: 06a76c1bdea0ee9953dbe66dc1eb5ea4b157dbc5999962e4b65153dbefdb7f21 7 | target: esp32 8 | version: 2.0.0 9 | -------------------------------------------------------------------------------- /esp32cam/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | -------------------------------------------------------------------------------- /esp32cam/sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # Rust often needs a bit of an extra main task stack size compared to C (the default is 3K) 2 | CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000 3 | CONFIG_ESP32_SPIRAM_SUPPORT=y 4 | 5 | # Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default). 6 | # This allows to use 1 ms granularity for thread sleeps (10 ms by default). 7 | #CONFIG_FREERTOS_HZ=1000 8 | 9 | # Workaround for https://github.com/espressif/esp-idf/issues/7631 10 | #CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n 11 | #CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n 12 | -------------------------------------------------------------------------------- /esp32cam/src/espcam.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use esp_idf_hal::gpio::*; 4 | use esp_idf_hal::peripheral::Peripheral; 5 | use esp_idf_sys::*; 6 | 7 | pub struct FrameBuffer<'a> { 8 | fb: *mut camera::camera_fb_t, 9 | _p: PhantomData<&'a camera::camera_fb_t>, 10 | } 11 | 12 | impl<'a> FrameBuffer<'a> { 13 | pub fn data(&self) -> &'a [u8] { 14 | unsafe { std::slice::from_raw_parts((*self.fb).buf, (*self.fb).len) } 15 | } 16 | 17 | pub fn width(&self) -> usize { 18 | unsafe { (*self.fb).width } 19 | } 20 | 21 | pub fn height(&self) -> usize { 22 | unsafe { (*self.fb).height } 23 | } 24 | 25 | pub fn format(&self) -> camera::pixformat_t { 26 | unsafe { (*self.fb).format } 27 | } 28 | 29 | pub fn timestamp(&self) -> camera::timeval { 30 | unsafe { (*self.fb).timestamp } 31 | } 32 | 33 | pub fn fb_return(&self) { 34 | unsafe { camera::esp_camera_fb_return(self.fb) } 35 | } 36 | } 37 | 38 | impl Drop for FrameBuffer<'_> { 39 | fn drop(&mut self) { 40 | self.fb_return(); 41 | } 42 | } 43 | 44 | pub struct CameraSensor<'a> { 45 | sensor: *mut camera::sensor_t, 46 | _p: PhantomData<&'a camera::sensor_t>, 47 | } 48 | 49 | impl<'a> CameraSensor<'a> { 50 | pub fn init_status(&self) -> Result<(), EspError> { 51 | esp!(unsafe { (*self.sensor).init_status.unwrap()(self.sensor) }) 52 | } 53 | pub fn reset(&self) -> Result<(), EspError> { 54 | esp!(unsafe { (*self.sensor).reset.unwrap()(self.sensor) }) 55 | } 56 | pub fn set_pixformat(&self, format: camera::pixformat_t) -> Result<(), EspError> { 57 | esp!(unsafe { (*self.sensor).set_pixformat.unwrap()(self.sensor, format) }) 58 | } 59 | pub fn set_framesize(&self, framesize: camera::framesize_t) -> Result<(), EspError> { 60 | esp!(unsafe { (*self.sensor).set_framesize.unwrap()(self.sensor, framesize) }) 61 | } 62 | pub fn set_contrast(&self, level: i32) -> Result<(), EspError> { 63 | esp!(unsafe { (*self.sensor).set_contrast.unwrap()(self.sensor, level) }) 64 | } 65 | pub fn set_brightness(&self, level: i32) -> Result<(), EspError> { 66 | esp!(unsafe { (*self.sensor).set_brightness.unwrap()(self.sensor, level) }) 67 | } 68 | pub fn set_saturation(&self, level: i32) -> Result<(), EspError> { 69 | esp!(unsafe { (*self.sensor).set_saturation.unwrap()(self.sensor, level) }) 70 | } 71 | pub fn set_sharpness(&self, level: i32) -> Result<(), EspError> { 72 | esp!(unsafe { (*self.sensor).set_sharpness.unwrap()(self.sensor, level) }) 73 | } 74 | pub fn set_denoise(&self, level: i32) -> Result<(), EspError> { 75 | esp!(unsafe { (*self.sensor).set_denoise.unwrap()(self.sensor, level) }) 76 | } 77 | pub fn set_gainceiling(&self, gainceiling: camera::gainceiling_t) -> Result<(), EspError> { 78 | esp!(unsafe { (*self.sensor).set_gainceiling.unwrap()(self.sensor, gainceiling) }) 79 | } 80 | pub fn set_quality(&self, quality: i32) -> Result<(), EspError> { 81 | esp!(unsafe { (*self.sensor).set_quality.unwrap()(self.sensor, quality) }) 82 | } 83 | pub fn set_colorbar(&self, enable: bool) -> Result<(), EspError> { 84 | esp!(unsafe { 85 | (*self.sensor).set_colorbar.unwrap()(self.sensor, if enable { 1 } else { 0 }) 86 | }) 87 | } 88 | pub fn set_whitebal(&self, enable: bool) -> Result<(), EspError> { 89 | esp!(unsafe { 90 | (*self.sensor).set_whitebal.unwrap()(self.sensor, if enable { 1 } else { 0 }) 91 | }) 92 | } 93 | pub fn set_gain_ctrl(&self, enable: bool) -> Result<(), EspError> { 94 | esp!(unsafe { 95 | (*self.sensor).set_gain_ctrl.unwrap()(self.sensor, if enable { 1 } else { 0 }) 96 | }) 97 | } 98 | pub fn set_exposure_ctrl(&self, enable: bool) -> Result<(), EspError> { 99 | esp!(unsafe { 100 | (*self.sensor).set_exposure_ctrl.unwrap()(self.sensor, if enable { 1 } else { 0 }) 101 | }) 102 | } 103 | pub fn set_hmirror(&self, enable: bool) -> Result<(), EspError> { 104 | esp!(unsafe { 105 | (*self.sensor).set_hmirror.unwrap()(self.sensor, if enable { 1 } else { 0 }) 106 | }) 107 | } 108 | pub fn set_vflip(&self, enable: bool) -> Result<(), EspError> { 109 | esp!(unsafe { (*self.sensor).set_vflip.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) 110 | } 111 | pub fn set_aec2(&self, enable: bool) -> Result<(), EspError> { 112 | esp!(unsafe { (*self.sensor).set_aec2.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) 113 | } 114 | pub fn set_awb_gain(&self, enable: bool) -> Result<(), EspError> { 115 | esp!(unsafe { 116 | (*self.sensor).set_awb_gain.unwrap()(self.sensor, if enable { 1 } else { 0 }) 117 | }) 118 | } 119 | pub fn set_agc_gain(&self, gain: i32) -> Result<(), EspError> { 120 | esp!(unsafe { (*self.sensor).set_agc_gain.unwrap()(self.sensor, gain) }) 121 | } 122 | pub fn set_aec_value(&self, gain: i32) -> Result<(), EspError> { 123 | esp!(unsafe { (*self.sensor).set_aec_value.unwrap()(self.sensor, gain) }) 124 | } 125 | pub fn set_special_effect(&self, effect: i32) -> Result<(), EspError> { 126 | esp!(unsafe { (*self.sensor).set_special_effect.unwrap()(self.sensor, effect) }) 127 | } 128 | pub fn set_wb_mode(&self, mode: i32) -> Result<(), EspError> { 129 | esp!(unsafe { (*self.sensor).set_wb_mode.unwrap()(self.sensor, mode) }) 130 | } 131 | pub fn set_ae_level(&self, level: i32) -> Result<(), EspError> { 132 | esp!(unsafe { (*self.sensor).set_ae_level.unwrap()(self.sensor, level) }) 133 | } 134 | pub fn set_dcw(&self, enable: bool) -> Result<(), EspError> { 135 | esp!(unsafe { (*self.sensor).set_dcw.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) 136 | } 137 | pub fn set_bpc(&self, enable: bool) -> Result<(), EspError> { 138 | esp!(unsafe { (*self.sensor).set_bpc.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) 139 | } 140 | pub fn set_wpc(&self, enable: bool) -> Result<(), EspError> { 141 | esp!(unsafe { (*self.sensor).set_wpc.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) 142 | } 143 | pub fn set_raw_gma(&self, enable: bool) -> Result<(), EspError> { 144 | esp!(unsafe { 145 | (*self.sensor).set_raw_gma.unwrap()(self.sensor, if enable { 1 } else { 0 }) 146 | }) 147 | } 148 | pub fn set_lenc(&self, enable: bool) -> Result<(), EspError> { 149 | esp!(unsafe { (*self.sensor).set_lenc.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) 150 | } 151 | pub fn get_reg(&self, reg: i32, mask: i32) -> Result<(), EspError> { 152 | esp!(unsafe { (*self.sensor).get_reg.unwrap()(self.sensor, reg, mask) }) 153 | } 154 | pub fn set_reg(&self, reg: i32, mask: i32, value: i32) -> Result<(), EspError> { 155 | esp!(unsafe { (*self.sensor).set_reg.unwrap()(self.sensor, reg, mask, value) }) 156 | } 157 | #[allow(clippy::too_many_arguments)] 158 | pub fn set_res_raw( 159 | &self, 160 | start_x: i32, 161 | start_y: i32, 162 | end_x: i32, 163 | end_y: i32, 164 | offset_x: i32, 165 | offset_y: i32, 166 | total_x: i32, 167 | total_y: i32, 168 | output_x: i32, 169 | output_y: i32, 170 | scale: bool, 171 | binning: bool, 172 | ) -> Result<(), EspError> { 173 | esp!(unsafe { 174 | (*self.sensor).set_res_raw.unwrap()( 175 | self.sensor, 176 | start_x, 177 | start_y, 178 | end_x, 179 | end_y, 180 | offset_x, 181 | offset_y, 182 | total_x, 183 | total_y, 184 | output_x, 185 | output_y, 186 | scale, 187 | binning, 188 | ) 189 | }) 190 | } 191 | #[allow(clippy::too_many_arguments)] 192 | pub fn set_pll( 193 | &self, 194 | bypass: i32, 195 | mul: i32, 196 | sys: i32, 197 | root: i32, 198 | pre: i32, 199 | seld5: i32, 200 | pclken: i32, 201 | pclk: i32, 202 | ) -> Result<(), EspError> { 203 | esp!(unsafe { 204 | (*self.sensor).set_pll.unwrap()( 205 | self.sensor, 206 | bypass, 207 | mul, 208 | sys, 209 | root, 210 | pre, 211 | seld5, 212 | pclken, 213 | pclk, 214 | ) 215 | }) 216 | } 217 | pub fn set_xclk(&self, timer: i32, xclk: i32) -> Result<(), EspError> { 218 | esp!(unsafe { (*self.sensor).set_xclk.unwrap()(self.sensor, timer, xclk) }) 219 | } 220 | } 221 | 222 | pub struct Camera<'a> { 223 | _p: PhantomData<&'a ()>, 224 | } 225 | 226 | impl<'a> Camera<'a> { 227 | #[allow(clippy::too_many_arguments)] 228 | pub fn new( 229 | pin_pwdn: impl Peripheral

+ 'a, 230 | pin_xclk: impl Peripheral

+ 'a, 231 | pin_d0: impl Peripheral

+ 'a, 232 | pin_d1: impl Peripheral

+ 'a, 233 | pin_d2: impl Peripheral

+ 'a, 234 | pin_d3: impl Peripheral

+ 'a, 235 | pin_d4: impl Peripheral

+ 'a, 236 | pin_d5: impl Peripheral

+ 'a, 237 | pin_d6: impl Peripheral

+ 'a, 238 | pin_d7: impl Peripheral

+ 'a, 239 | pin_vsync: impl Peripheral

+ 'a, 240 | pin_href: impl Peripheral

+ 'a, 241 | pin_pclk: impl Peripheral

+ 'a, 242 | pin_sda: impl Peripheral

+ 'a, 243 | pin_scl: impl Peripheral

+ 'a, 244 | pixel_format: camera::pixformat_t, 245 | frame_size: camera::framesize_t, 246 | ) -> Result { 247 | esp_idf_hal::into_ref!( 248 | pin_pwdn, pin_xclk, pin_d0, pin_d1, pin_d2, pin_d3, pin_d4, pin_d5, pin_d6, pin_d7, 249 | pin_vsync, pin_href, pin_pclk, pin_sda, pin_scl 250 | ); 251 | let config = camera::camera_config_t { 252 | pin_pwdn: pin_pwdn.pin(), 253 | pin_xclk: pin_xclk.pin(), 254 | pin_reset: 0xff, 255 | 256 | pin_d0: pin_d0.pin(), 257 | pin_d1: pin_d1.pin(), 258 | pin_d2: pin_d2.pin(), 259 | pin_d3: pin_d3.pin(), 260 | pin_d4: pin_d4.pin(), 261 | pin_d5: pin_d5.pin(), 262 | pin_d6: pin_d6.pin(), 263 | pin_d7: pin_d7.pin(), 264 | pin_vsync: pin_vsync.pin(), 265 | pin_href: pin_href.pin(), 266 | pin_pclk: pin_pclk.pin(), 267 | 268 | xclk_freq_hz: 20000000, 269 | ledc_timer: esp_idf_sys::ledc_timer_t_LEDC_TIMER_0, 270 | ledc_channel: esp_idf_sys::ledc_channel_t_LEDC_CHANNEL_0, 271 | 272 | pixel_format, 273 | frame_size, 274 | 275 | jpeg_quality: 32, // Originally 12 276 | fb_count: 1, 277 | grab_mode: camera::camera_grab_mode_t_CAMERA_GRAB_WHEN_EMPTY, 278 | 279 | fb_location: camera::camera_fb_location_t_CAMERA_FB_IN_PSRAM, 280 | 281 | __bindgen_anon_1: camera::camera_config_t__bindgen_ty_1 { 282 | pin_sccb_sda: pin_sda.pin(), 283 | }, 284 | __bindgen_anon_2: camera::camera_config_t__bindgen_ty_2 { 285 | pin_sccb_scl: pin_scl.pin(), 286 | }, 287 | 288 | ..Default::default() 289 | }; 290 | 291 | esp_idf_sys::esp!(unsafe { camera::esp_camera_init(&config) })?; 292 | Ok(Self { _p: PhantomData }) 293 | } 294 | 295 | pub fn get_framebuffer(&self) -> Option { 296 | let fb = unsafe { camera::esp_camera_fb_get() }; 297 | if fb.is_null() { 298 | //unsafe { camera::esp_camera_fb_return(fb); } 299 | None 300 | } else { 301 | Some(FrameBuffer { 302 | fb, 303 | _p: PhantomData, 304 | }) 305 | } 306 | } 307 | 308 | pub fn sensor(&self) -> CameraSensor<'a> { 309 | CameraSensor { 310 | sensor: unsafe { camera::esp_camera_sensor_get() }, 311 | _p: PhantomData, 312 | } 313 | } 314 | } 315 | 316 | impl<'a> Drop for Camera<'a> { 317 | fn drop(&mut self) { 318 | esp!(unsafe { camera::esp_camera_deinit() }).expect("error during esp_camera_deinit") 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /esp32cam/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod espcam; 2 | pub mod wifi_handler; 3 | -------------------------------------------------------------------------------- /esp32cam/src/main.rs: -------------------------------------------------------------------------------- 1 | pub mod espcam; 2 | pub mod wifi_handler; 3 | 4 | use std::sync::Arc; 5 | 6 | use esp_idf_svc::hal::peripherals::Peripherals; 7 | use espcam::Camera; 8 | 9 | use anyhow::{bail, Result}; 10 | 11 | use esp_idf_hal::io::Write; 12 | use esp_idf_svc::{ 13 | eventloop::EspSystemEventLoop, 14 | http::{server::EspHttpServer, Method}, 15 | }; 16 | use wifi_handler::my_wifi; 17 | 18 | fn main() -> Result<()> { 19 | esp_idf_svc::sys::link_patches(); 20 | esp_idf_svc::log::EspLogger::initialize_default(); 21 | 22 | let sysloop = EspSystemEventLoop::take()?; 23 | 24 | let peripherals = Peripherals::take().unwrap(); 25 | let wifi_ssid = env!("WIFI_SSID"); 26 | let wifi_password = env!("WIFI_PASSWORD"); 27 | 28 | let _wifi = match my_wifi(wifi_ssid, wifi_password, peripherals.modem, sysloop) { 29 | Ok(inner) => inner, 30 | Err(err) => { 31 | bail!("Could not connect to Wi-Fi network: {:?}", err) 32 | } 33 | }; 34 | 35 | let camera = Camera::new( 36 | peripherals.pins.gpio32, 37 | peripherals.pins.gpio0, 38 | peripherals.pins.gpio5, 39 | peripherals.pins.gpio18, 40 | peripherals.pins.gpio19, 41 | peripherals.pins.gpio21, 42 | peripherals.pins.gpio36, 43 | peripherals.pins.gpio39, 44 | peripherals.pins.gpio34, 45 | peripherals.pins.gpio35, 46 | peripherals.pins.gpio25, 47 | peripherals.pins.gpio23, 48 | peripherals.pins.gpio22, 49 | peripherals.pins.gpio26, 50 | peripherals.pins.gpio27, 51 | esp_idf_sys::camera::pixformat_t_PIXFORMAT_JPEG, 52 | // Set quality here 53 | esp_idf_sys::camera::framesize_t_FRAMESIZE_SVGA, 54 | ) 55 | .unwrap(); 56 | // camera.sensor().set_vflip(true)?; // Flip if needed 57 | 58 | let cam_arc = Arc::new(camera); 59 | let cam_arc_clone = cam_arc.clone(); 60 | 61 | let mut server = EspHttpServer::new(&esp_idf_svc::http::server::Configuration::default())?; 62 | 63 | server.fn_handler( 64 | "/camera", 65 | Method::Get, 66 | move |request| -> Result<(), anyhow::Error> { 67 | let part_boundary = "123456789000000000000987654321"; 68 | let frame_boundary = format!("\r\n--{}\r\n", part_boundary); 69 | 70 | let content_type = format!("multipart/x-mixed-replace;boundary={}", part_boundary); 71 | let headers = [("Content-Type", content_type.as_str())]; 72 | let mut response = request.into_response(200, Some("OK"), &headers).unwrap(); 73 | loop { 74 | if let Some(fb) = cam_arc_clone.get_framebuffer() { 75 | let data = fb.data(); 76 | let frame_part = format!( 77 | "Content-Type: image/jpeg\r\nContent-Length: {}\r\n\r\n", 78 | data.len() 79 | ); 80 | response.write_all(frame_part.as_bytes())?; 81 | response.write_all(data)?; 82 | response.write_all(frame_boundary.as_bytes())?; 83 | response.flush()?; 84 | } 85 | } 86 | 87 | Ok(()) 88 | }, 89 | )?; 90 | 91 | server.fn_handler("/", Method::Get, |request| -> Result<(), anyhow::Error> { 92 | let data = "esp32cam\"Failed"; 93 | 94 | 95 | let headers = [ 96 | ("Content-Type", "text/html"), 97 | ("Content-Length", &data.len().to_string()), 98 | ]; 99 | let mut response = request.into_response(200, Some("OK"), &headers)?; 100 | response.write_all(data.as_bytes())?; 101 | Ok(()) 102 | })?; 103 | 104 | loop { 105 | std::thread::sleep(std::time::Duration::from_millis(1000)); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /esp32cam/src/wifi_handler.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Result}; 2 | use esp_idf_svc::{ 3 | eventloop::EspSystemEventLoop, 4 | hal::peripheral, 5 | wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi}, 6 | }; 7 | use log::info; 8 | 9 | pub fn my_wifi( 10 | ssid: &str, 11 | pass: &str, 12 | modem: impl peripheral::Peripheral

+ 'static, 13 | sysloop: EspSystemEventLoop, 14 | ) -> Result>> { 15 | let mut auth_method = AuthMethod::WPA2Personal; 16 | if ssid.is_empty() { 17 | bail!("Missing WiFi name") 18 | } 19 | if pass.is_empty() { 20 | auth_method = AuthMethod::None; 21 | info!("Wifi password is empty"); 22 | } 23 | let mut esp_wifi = EspWifi::new(modem, sysloop.clone(), None)?; 24 | 25 | let mut wifi = BlockingWifi::wrap(&mut esp_wifi, sysloop)?; 26 | 27 | wifi.set_configuration(&Configuration::Client(ClientConfiguration::default()))?; 28 | 29 | info!("Starting wifi..."); 30 | 31 | wifi.start()?; 32 | 33 | info!("Scanning..."); 34 | 35 | let ap_infos = wifi.scan()?; 36 | 37 | let ours = ap_infos.into_iter().find(|a| a.ssid == ssid); 38 | 39 | let channel = if let Some(ours) = ours { 40 | info!( 41 | "Found configured access point {} on channel {}", 42 | ssid, ours.channel 43 | ); 44 | Some(ours.channel) 45 | } else { 46 | info!( 47 | "Configured access point {} not found during scanning, will go with unknown channel", 48 | ssid 49 | ); 50 | None 51 | }; 52 | 53 | wifi.set_configuration(&Configuration::Client(ClientConfiguration { 54 | ssid: ssid 55 | .try_into() 56 | .expect("SSID could not be converted to heapless String"), 57 | password: pass 58 | .try_into() 59 | .expect("Password could not be converted to heapless String"), 60 | channel, 61 | auth_method, 62 | ..Default::default() 63 | }))?; 64 | 65 | info!("Connecting wifi..."); 66 | 67 | wifi.connect()?; 68 | 69 | info!("Waiting for DHCP lease..."); 70 | 71 | wifi.wait_netif_up()?; 72 | 73 | let ip_info = wifi.wifi().sta_netif().get_ip_info()?; 74 | 75 | info!("Wifi DHCP info: {:?}", ip_info); 76 | info!( 77 | "Wifi DHCP hostname: {:?}", 78 | wifi.wifi().sta_netif().get_hostname()? 79 | ); 80 | 81 | Ok(Box::new(esp_wifi)) 82 | } 83 | -------------------------------------------------------------------------------- /wifi_tank/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.xtensa-esp32-none-elf] 2 | runner = "espflash flash --monitor" 3 | 4 | 5 | [env] 6 | ESP_LOG = "info" 7 | WIFI_SSID = "YOUR_SSID" 8 | WIFI_PASSWORD = "YOUR_PASSWORD" 9 | 10 | [build] 11 | rustflags = ["-C", "link-arg=-nostartfiles", "-C", "link-arg=-Trom_functions.x",] 12 | 13 | target = "xtensa-esp32-none-elf" 14 | 15 | [unstable] 16 | build-std = ["core", "alloc"] 17 | -------------------------------------------------------------------------------- /wifi_tank/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | -------------------------------------------------------------------------------- /wifi_tank/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.check.allTargets": false, 3 | } 4 | -------------------------------------------------------------------------------- /wifi_tank/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.91" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" 10 | 11 | [[package]] 12 | name = "as-slice" 13 | version = "0.1.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" 16 | dependencies = [ 17 | "generic-array 0.12.4", 18 | "generic-array 0.13.3", 19 | "generic-array 0.14.7", 20 | "stable_deref_trait", 21 | ] 22 | 23 | [[package]] 24 | name = "as-slice" 25 | version = "0.2.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" 28 | dependencies = [ 29 | "stable_deref_trait", 30 | ] 31 | 32 | [[package]] 33 | name = "atomic-polyfill" 34 | version = "1.0.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" 37 | dependencies = [ 38 | "critical-section", 39 | ] 40 | 41 | [[package]] 42 | name = "atomic-pool" 43 | version = "1.0.1" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "58c5fc22e05ec2884db458bf307dc7b278c9428888d2b6e6fad9c0ae7804f5f6" 46 | dependencies = [ 47 | "as-slice 0.1.5", 48 | "as-slice 0.2.1", 49 | "atomic-polyfill", 50 | "stable_deref_trait", 51 | ] 52 | 53 | [[package]] 54 | name = "autocfg" 55 | version = "1.4.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 58 | 59 | [[package]] 60 | name = "bare-metal" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" 64 | 65 | [[package]] 66 | name = "basic-toml" 67 | version = "0.1.9" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" 70 | dependencies = [ 71 | "serde", 72 | ] 73 | 74 | [[package]] 75 | name = "bitfield" 76 | version = "0.16.1" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "d5acf59e2452f0c4b968b15ce4b9468f57b45f7733b919d68b19fcc39264bfb8" 79 | 80 | [[package]] 81 | name = "bitflags" 82 | version = "1.3.2" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 85 | 86 | [[package]] 87 | name = "bitflags" 88 | version = "2.6.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 91 | 92 | [[package]] 93 | name = "bt-hci" 94 | version = "0.1.1" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "57a6508c63d7d137e8188833d9ed3ca97e40d676cf5217874c8c1c24851b012d" 97 | dependencies = [ 98 | "embassy-sync 0.6.0", 99 | "embedded-io", 100 | "embedded-io-async", 101 | "futures-intrusive", 102 | "heapless", 103 | ] 104 | 105 | [[package]] 106 | name = "bytemuck" 107 | version = "1.19.0" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" 110 | 111 | [[package]] 112 | name = "byteorder" 113 | version = "1.5.0" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 116 | 117 | [[package]] 118 | name = "cfg-if" 119 | version = "1.0.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 122 | 123 | [[package]] 124 | name = "chrono" 125 | version = "0.4.38" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 128 | dependencies = [ 129 | "num-traits", 130 | ] 131 | 132 | [[package]] 133 | name = "critical-section" 134 | version = "1.2.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 137 | 138 | [[package]] 139 | name = "darling" 140 | version = "0.20.10" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" 143 | dependencies = [ 144 | "darling_core", 145 | "darling_macro", 146 | ] 147 | 148 | [[package]] 149 | name = "darling_core" 150 | version = "0.20.10" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" 153 | dependencies = [ 154 | "fnv", 155 | "ident_case", 156 | "proc-macro2", 157 | "quote", 158 | "strsim", 159 | "syn", 160 | ] 161 | 162 | [[package]] 163 | name = "darling_macro" 164 | version = "0.20.10" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" 167 | dependencies = [ 168 | "darling_core", 169 | "quote", 170 | "syn", 171 | ] 172 | 173 | [[package]] 174 | name = "delegate" 175 | version = "0.12.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "4e018fccbeeb50ff26562ece792ed06659b9c2dae79ece77c4456bb10d9bf79b" 178 | dependencies = [ 179 | "proc-macro2", 180 | "quote", 181 | "syn", 182 | ] 183 | 184 | [[package]] 185 | name = "document-features" 186 | version = "0.2.10" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" 189 | dependencies = [ 190 | "litrs", 191 | ] 192 | 193 | [[package]] 194 | name = "embassy-executor" 195 | version = "0.6.1" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "0fd0a2386252214d31d22400730e28e9c6bc62b346df62802e30a0bb3677e43b" 198 | dependencies = [ 199 | "critical-section", 200 | "document-features", 201 | "embassy-executor-macros", 202 | "embassy-time-driver", 203 | "embassy-time-queue-driver", 204 | ] 205 | 206 | [[package]] 207 | name = "embassy-executor-macros" 208 | version = "0.6.1" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "c853e6bcad2d1c0811f4de404cef87363a1fa2535430cf76824c163cf75689ba" 211 | dependencies = [ 212 | "darling", 213 | "proc-macro2", 214 | "quote", 215 | "syn", 216 | ] 217 | 218 | [[package]] 219 | name = "embassy-futures" 220 | version = "0.1.1" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" 223 | 224 | [[package]] 225 | name = "embassy-net" 226 | version = "0.4.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "55cf91dd36dfd623de32242af711fd294d41159f02130052fc93c5c5ba93febe" 229 | dependencies = [ 230 | "as-slice 0.2.1", 231 | "atomic-pool", 232 | "document-features", 233 | "embassy-net-driver", 234 | "embassy-sync 0.5.0", 235 | "embassy-time", 236 | "embedded-io-async", 237 | "embedded-nal-async", 238 | "futures", 239 | "generic-array 0.14.7", 240 | "heapless", 241 | "managed", 242 | "smoltcp", 243 | "stable_deref_trait", 244 | ] 245 | 246 | [[package]] 247 | name = "embassy-net-driver" 248 | version = "0.2.0" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" 251 | 252 | [[package]] 253 | name = "embassy-sync" 254 | version = "0.5.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "dd938f25c0798db4280fcd8026bf4c2f48789aebf8f77b6e5cf8a7693ba114ec" 257 | dependencies = [ 258 | "cfg-if", 259 | "critical-section", 260 | "embedded-io-async", 261 | "futures-util", 262 | "heapless", 263 | ] 264 | 265 | [[package]] 266 | name = "embassy-sync" 267 | version = "0.6.0" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "b3e0c49ff02ebe324faf3a8653ba91582e2d0a7fdef5bc88f449d5aa1bfcc05c" 270 | dependencies = [ 271 | "cfg-if", 272 | "critical-section", 273 | "embedded-io-async", 274 | "futures-util", 275 | "heapless", 276 | ] 277 | 278 | [[package]] 279 | name = "embassy-time" 280 | version = "0.3.2" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "158080d48f824fad101d7b2fae2d83ac39e3f7a6fa01811034f7ab8ffc6e7309" 283 | dependencies = [ 284 | "cfg-if", 285 | "critical-section", 286 | "document-features", 287 | "embassy-time-driver", 288 | "embassy-time-queue-driver", 289 | "embedded-hal 0.2.7", 290 | "embedded-hal 1.0.0", 291 | "embedded-hal-async", 292 | "futures-util", 293 | "heapless", 294 | ] 295 | 296 | [[package]] 297 | name = "embassy-time-driver" 298 | version = "0.1.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "6e0c214077aaa9206958b16411c157961fb7990d4ea628120a78d1a5a28aed24" 301 | dependencies = [ 302 | "document-features", 303 | ] 304 | 305 | [[package]] 306 | name = "embassy-time-queue-driver" 307 | version = "0.1.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "f1177859559ebf42cd24ae7ba8fe6ee707489b01d0bf471f8827b7b12dcb0bc0" 310 | 311 | [[package]] 312 | name = "embedded-can" 313 | version = "0.4.1" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" 316 | dependencies = [ 317 | "nb 1.1.0", 318 | ] 319 | 320 | [[package]] 321 | name = "embedded-hal" 322 | version = "0.2.7" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 325 | dependencies = [ 326 | "nb 0.1.3", 327 | "void", 328 | ] 329 | 330 | [[package]] 331 | name = "embedded-hal" 332 | version = "1.0.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 335 | 336 | [[package]] 337 | name = "embedded-hal-async" 338 | version = "1.0.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" 341 | dependencies = [ 342 | "embedded-hal 1.0.0", 343 | ] 344 | 345 | [[package]] 346 | name = "embedded-hal-nb" 347 | version = "1.0.0" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" 350 | dependencies = [ 351 | "embedded-hal 1.0.0", 352 | "nb 1.1.0", 353 | ] 354 | 355 | [[package]] 356 | name = "embedded-io" 357 | version = "0.6.1" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 360 | 361 | [[package]] 362 | name = "embedded-io-async" 363 | version = "0.6.1" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" 366 | dependencies = [ 367 | "embedded-io", 368 | ] 369 | 370 | [[package]] 371 | name = "embedded-nal" 372 | version = "0.8.0" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "b8a943fad5ed3d3f8a00f1e80f6bba371f1e7f0df28ec38477535eb318dc19cc" 375 | dependencies = [ 376 | "nb 1.1.0", 377 | "no-std-net", 378 | ] 379 | 380 | [[package]] 381 | name = "embedded-nal-async" 382 | version = "0.7.1" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "72229137a4fc12d239b0b7f50f04b30790678da6d782a0f3f1909bf57ec4b759" 385 | dependencies = [ 386 | "embedded-io-async", 387 | "embedded-nal", 388 | "no-std-net", 389 | ] 390 | 391 | [[package]] 392 | name = "enum-as-inner" 393 | version = "0.6.1" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" 396 | dependencies = [ 397 | "heck", 398 | "proc-macro2", 399 | "quote", 400 | "syn", 401 | ] 402 | 403 | [[package]] 404 | name = "enumset" 405 | version = "1.1.5" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" 408 | dependencies = [ 409 | "enumset_derive", 410 | ] 411 | 412 | [[package]] 413 | name = "enumset_derive" 414 | version = "0.10.0" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" 417 | dependencies = [ 418 | "darling", 419 | "proc-macro2", 420 | "quote", 421 | "syn", 422 | ] 423 | 424 | [[package]] 425 | name = "equivalent" 426 | version = "1.0.1" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 429 | 430 | [[package]] 431 | name = "esp-alloc" 432 | version = "0.5.0" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "5162b6e9cd99cc974ec2742da3cf8433e7332853ae10fec922d174ff20fa4c5f" 435 | dependencies = [ 436 | "critical-section", 437 | "enumset", 438 | "linked_list_allocator", 439 | ] 440 | 441 | [[package]] 442 | name = "esp-backtrace" 443 | version = "0.14.2" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "cb7660d85e3e7b0e113aaeeffb1a155e64a09a5035d4104031875acdba4cb68e" 446 | dependencies = [ 447 | "esp-build", 448 | "esp-println", 449 | "semihosting", 450 | ] 451 | 452 | [[package]] 453 | name = "esp-build" 454 | version = "0.1.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "b94a4b8d74e7cc7baabcca5b2277b41877e039ad9cd49959d48ef94dac7eab4b" 457 | dependencies = [ 458 | "quote", 459 | "syn", 460 | "termcolor", 461 | ] 462 | 463 | [[package]] 464 | name = "esp-config" 465 | version = "0.1.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "7eeb7365af80ce4f8a1a2536676daf4e3d1487ec7a5b2eda0e8c458b9ba8b266" 468 | dependencies = [ 469 | "document-features", 470 | ] 471 | 472 | [[package]] 473 | name = "esp-hal" 474 | version = "0.21.1" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "dae8535b3c6762c286dba6a206ab83637fb347ed26e99d99011726d9ee2f4082" 477 | dependencies = [ 478 | "basic-toml", 479 | "bitfield", 480 | "bitflags 2.6.0", 481 | "bytemuck", 482 | "cfg-if", 483 | "chrono", 484 | "critical-section", 485 | "delegate", 486 | "document-features", 487 | "embassy-futures", 488 | "embassy-sync 0.6.0", 489 | "embedded-can", 490 | "embedded-hal 0.2.7", 491 | "embedded-hal 1.0.0", 492 | "embedded-hal-async", 493 | "embedded-hal-nb", 494 | "embedded-io", 495 | "embedded-io-async", 496 | "enumset", 497 | "esp-build", 498 | "esp-config", 499 | "esp-hal-procmacros", 500 | "esp-metadata", 501 | "esp-riscv-rt", 502 | "esp32", 503 | "fugit", 504 | "log", 505 | "nb 1.1.0", 506 | "paste", 507 | "portable-atomic", 508 | "rand_core", 509 | "serde", 510 | "strum", 511 | "ufmt-write", 512 | "void", 513 | "xtensa-lx", 514 | "xtensa-lx-rt", 515 | ] 516 | 517 | [[package]] 518 | name = "esp-hal-embassy" 519 | version = "0.4.0" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "1d17e1b37de4605fdcadbb2a9701065e7d6ed376b473bf8eeac1d879a53b3470" 522 | dependencies = [ 523 | "critical-section", 524 | "document-features", 525 | "embassy-executor", 526 | "embassy-time-driver", 527 | "esp-build", 528 | "esp-hal", 529 | "esp-hal-procmacros", 530 | "esp-metadata", 531 | "log", 532 | "portable-atomic", 533 | "static_cell", 534 | ] 535 | 536 | [[package]] 537 | name = "esp-hal-procmacros" 538 | version = "0.14.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "34022a362a27f781b83852afd0c856a43e2ee5b4334cdc292bd80c9be482a1f5" 541 | dependencies = [ 542 | "darling", 543 | "document-features", 544 | "litrs", 545 | "proc-macro-crate", 546 | "proc-macro-error2", 547 | "proc-macro2", 548 | "quote", 549 | "syn", 550 | ] 551 | 552 | [[package]] 553 | name = "esp-metadata" 554 | version = "0.4.0" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "f9972bbb21dcafe430b87f92efc7a788978a2d17cf8f572d104beeb48602482a" 557 | dependencies = [ 558 | "anyhow", 559 | "basic-toml", 560 | "serde", 561 | "strum", 562 | ] 563 | 564 | [[package]] 565 | name = "esp-println" 566 | version = "0.12.0" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "ee38e87bc7e303c299047c0e9bcd0f8ccca7c7e70d1fd78bbb565db14f33beb6" 569 | dependencies = [ 570 | "critical-section", 571 | "esp-build", 572 | "log", 573 | "portable-atomic", 574 | ] 575 | 576 | [[package]] 577 | name = "esp-riscv-rt" 578 | version = "0.9.0" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "bfc32298ed7c263b06c8b031704d8517cc62c819f2a9d5c261d0cb119634d6e9" 581 | dependencies = [ 582 | "document-features", 583 | "riscv", 584 | "riscv-rt-macros", 585 | ] 586 | 587 | [[package]] 588 | name = "esp-wifi" 589 | version = "0.10.1" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "44431e7946c560e7632f3d0241763681da1376b357f2cba1362be168bc38d162" 592 | dependencies = [ 593 | "bt-hci", 594 | "cfg-if", 595 | "critical-section", 596 | "document-features", 597 | "embassy-net-driver", 598 | "embassy-sync 0.6.0", 599 | "embedded-io", 600 | "embedded-io-async", 601 | "enumset", 602 | "esp-alloc", 603 | "esp-build", 604 | "esp-config", 605 | "esp-hal", 606 | "esp-metadata", 607 | "esp-wifi-sys", 608 | "fugit", 609 | "heapless", 610 | "libm", 611 | "log", 612 | "no-std-net", 613 | "num-derive", 614 | "num-traits", 615 | "portable-atomic", 616 | "portable_atomic_enum", 617 | "smoltcp", 618 | "xtensa-lx-rt", 619 | ] 620 | 621 | [[package]] 622 | name = "esp-wifi-sys" 623 | version = "0.6.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "f339ddb1664812e79992a8231a3f8603456c80b769066313150831f2b0bf66e7" 626 | dependencies = [ 627 | "anyhow", 628 | "log", 629 | ] 630 | 631 | [[package]] 632 | name = "esp32" 633 | version = "0.33.0" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "85287b57fae3e318b62fd860787b1ac85a5e7bf91ad43eb66837c5e567218009" 636 | dependencies = [ 637 | "critical-section", 638 | "vcell", 639 | "xtensa-lx", 640 | ] 641 | 642 | [[package]] 643 | name = "fnv" 644 | version = "1.0.7" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 647 | 648 | [[package]] 649 | name = "fugit" 650 | version = "0.3.7" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" 653 | dependencies = [ 654 | "gcd", 655 | ] 656 | 657 | [[package]] 658 | name = "futures" 659 | version = "0.3.31" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 662 | dependencies = [ 663 | "futures-channel", 664 | "futures-core", 665 | "futures-io", 666 | "futures-sink", 667 | "futures-task", 668 | "futures-util", 669 | ] 670 | 671 | [[package]] 672 | name = "futures-channel" 673 | version = "0.3.31" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 676 | dependencies = [ 677 | "futures-core", 678 | "futures-sink", 679 | ] 680 | 681 | [[package]] 682 | name = "futures-core" 683 | version = "0.3.31" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 686 | 687 | [[package]] 688 | name = "futures-intrusive" 689 | version = "0.5.0" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" 692 | dependencies = [ 693 | "futures-core", 694 | "lock_api", 695 | ] 696 | 697 | [[package]] 698 | name = "futures-io" 699 | version = "0.3.31" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 702 | 703 | [[package]] 704 | name = "futures-macro" 705 | version = "0.3.31" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 708 | dependencies = [ 709 | "proc-macro2", 710 | "quote", 711 | "syn", 712 | ] 713 | 714 | [[package]] 715 | name = "futures-sink" 716 | version = "0.3.31" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 719 | 720 | [[package]] 721 | name = "futures-task" 722 | version = "0.3.31" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 725 | 726 | [[package]] 727 | name = "futures-util" 728 | version = "0.3.31" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 731 | dependencies = [ 732 | "futures-core", 733 | "futures-macro", 734 | "futures-sink", 735 | "futures-task", 736 | "pin-project-lite", 737 | "pin-utils", 738 | ] 739 | 740 | [[package]] 741 | name = "gcd" 742 | version = "2.3.0" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" 745 | 746 | [[package]] 747 | name = "generic-array" 748 | version = "0.12.4" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" 751 | dependencies = [ 752 | "typenum", 753 | ] 754 | 755 | [[package]] 756 | name = "generic-array" 757 | version = "0.13.3" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" 760 | dependencies = [ 761 | "typenum", 762 | ] 763 | 764 | [[package]] 765 | name = "generic-array" 766 | version = "0.14.7" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 769 | dependencies = [ 770 | "typenum", 771 | "version_check", 772 | ] 773 | 774 | [[package]] 775 | name = "hash32" 776 | version = "0.3.1" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 779 | dependencies = [ 780 | "byteorder", 781 | ] 782 | 783 | [[package]] 784 | name = "hashbrown" 785 | version = "0.15.0" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" 788 | 789 | [[package]] 790 | name = "heapless" 791 | version = "0.8.0" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 794 | dependencies = [ 795 | "hash32", 796 | "portable-atomic", 797 | "stable_deref_trait", 798 | ] 799 | 800 | [[package]] 801 | name = "heck" 802 | version = "0.5.0" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 805 | 806 | [[package]] 807 | name = "ident_case" 808 | version = "1.0.1" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 811 | 812 | [[package]] 813 | name = "indexmap" 814 | version = "2.6.0" 815 | source = "registry+https://github.com/rust-lang/crates.io-index" 816 | checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 817 | dependencies = [ 818 | "equivalent", 819 | "hashbrown", 820 | ] 821 | 822 | [[package]] 823 | name = "libm" 824 | version = "0.2.10" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "a00419de735aac21d53b0de5ce2c03bd3627277cf471300f27ebc89f7d828047" 827 | 828 | [[package]] 829 | name = "linked_list_allocator" 830 | version = "0.10.5" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" 833 | 834 | [[package]] 835 | name = "litrs" 836 | version = "0.4.1" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" 839 | dependencies = [ 840 | "proc-macro2", 841 | ] 842 | 843 | [[package]] 844 | name = "lock_api" 845 | version = "0.4.12" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 848 | dependencies = [ 849 | "autocfg", 850 | "scopeguard", 851 | ] 852 | 853 | [[package]] 854 | name = "log" 855 | version = "0.4.22" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 858 | 859 | [[package]] 860 | name = "managed" 861 | version = "0.8.0" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" 864 | 865 | [[package]] 866 | name = "memchr" 867 | version = "2.7.4" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 870 | 871 | [[package]] 872 | name = "minijinja" 873 | version = "2.4.0" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "c9ca8daf4b0b4029777f1bc6e1aedd1aec7b74c276a43bc6f620a8e1a1c0a90e" 876 | dependencies = [ 877 | "serde", 878 | ] 879 | 880 | [[package]] 881 | name = "mutex-trait" 882 | version = "0.2.0" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" 885 | 886 | [[package]] 887 | name = "nb" 888 | version = "0.1.3" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 891 | dependencies = [ 892 | "nb 1.1.0", 893 | ] 894 | 895 | [[package]] 896 | name = "nb" 897 | version = "1.1.0" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 900 | 901 | [[package]] 902 | name = "no-std-net" 903 | version = "0.6.0" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" 906 | 907 | [[package]] 908 | name = "num-derive" 909 | version = "0.4.2" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" 912 | dependencies = [ 913 | "proc-macro2", 914 | "quote", 915 | "syn", 916 | ] 917 | 918 | [[package]] 919 | name = "num-traits" 920 | version = "0.2.19" 921 | source = "registry+https://github.com/rust-lang/crates.io-index" 922 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 923 | dependencies = [ 924 | "autocfg", 925 | ] 926 | 927 | [[package]] 928 | name = "paste" 929 | version = "1.0.15" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 932 | 933 | [[package]] 934 | name = "pin-project-lite" 935 | version = "0.2.15" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" 938 | 939 | [[package]] 940 | name = "pin-utils" 941 | version = "0.1.0" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 944 | 945 | [[package]] 946 | name = "portable-atomic" 947 | version = "1.9.0" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" 950 | 951 | [[package]] 952 | name = "portable_atomic_enum" 953 | version = "0.3.1" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "30d48f60c43e0120bb2bb48589a16d4bed2f4b911be41e299f2d0fc0e0e20885" 956 | dependencies = [ 957 | "portable-atomic", 958 | "portable_atomic_enum_macros", 959 | ] 960 | 961 | [[package]] 962 | name = "portable_atomic_enum_macros" 963 | version = "0.2.1" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "a33fa6ec7f2047f572d49317cca19c87195de99c6e5b6ee492da701cfe02b053" 966 | dependencies = [ 967 | "proc-macro2", 968 | "quote", 969 | "syn", 970 | ] 971 | 972 | [[package]] 973 | name = "proc-macro-crate" 974 | version = "3.2.0" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" 977 | dependencies = [ 978 | "toml_edit", 979 | ] 980 | 981 | [[package]] 982 | name = "proc-macro-error-attr2" 983 | version = "2.0.0" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" 986 | dependencies = [ 987 | "proc-macro2", 988 | "quote", 989 | ] 990 | 991 | [[package]] 992 | name = "proc-macro-error2" 993 | version = "2.0.1" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" 996 | dependencies = [ 997 | "proc-macro-error-attr2", 998 | "proc-macro2", 999 | "quote", 1000 | "syn", 1001 | ] 1002 | 1003 | [[package]] 1004 | name = "proc-macro2" 1005 | version = "1.0.89" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" 1008 | dependencies = [ 1009 | "unicode-ident", 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "quote" 1014 | version = "1.0.37" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1017 | dependencies = [ 1018 | "proc-macro2", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "r0" 1023 | version = "1.0.0" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" 1026 | 1027 | [[package]] 1028 | name = "rand_core" 1029 | version = "0.6.4" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1032 | 1033 | [[package]] 1034 | name = "riscv" 1035 | version = "0.11.1" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9" 1038 | dependencies = [ 1039 | "critical-section", 1040 | "embedded-hal 1.0.0", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "riscv-rt-macros" 1045 | version = "0.2.2" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "30f19a85fe107b65031e0ba8ec60c34c2494069fe910d6c297f5e7cb5a6f76d0" 1048 | dependencies = [ 1049 | "proc-macro2", 1050 | "quote", 1051 | "syn", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "rustversion" 1056 | version = "1.0.18" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" 1059 | 1060 | [[package]] 1061 | name = "scopeguard" 1062 | version = "1.2.0" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1065 | 1066 | [[package]] 1067 | name = "semihosting" 1068 | version = "0.1.16" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "a5c5996e5d1dec34b0dff3285e27124e70964504e3fd361bce330dc476cebafd" 1071 | 1072 | [[package]] 1073 | name = "serde" 1074 | version = "1.0.214" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" 1077 | dependencies = [ 1078 | "serde_derive", 1079 | ] 1080 | 1081 | [[package]] 1082 | name = "serde_derive" 1083 | version = "1.0.214" 1084 | source = "registry+https://github.com/rust-lang/crates.io-index" 1085 | checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" 1086 | dependencies = [ 1087 | "proc-macro2", 1088 | "quote", 1089 | "syn", 1090 | ] 1091 | 1092 | [[package]] 1093 | name = "serde_spanned" 1094 | version = "0.6.8" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 1097 | dependencies = [ 1098 | "serde", 1099 | ] 1100 | 1101 | [[package]] 1102 | name = "smoltcp" 1103 | version = "0.11.0" 1104 | source = "registry+https://github.com/rust-lang/crates.io-index" 1105 | checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97" 1106 | dependencies = [ 1107 | "bitflags 1.3.2", 1108 | "byteorder", 1109 | "cfg-if", 1110 | "heapless", 1111 | "managed", 1112 | ] 1113 | 1114 | [[package]] 1115 | name = "spin" 1116 | version = "0.9.8" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1119 | dependencies = [ 1120 | "lock_api", 1121 | ] 1122 | 1123 | [[package]] 1124 | name = "stable_deref_trait" 1125 | version = "1.2.0" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1128 | 1129 | [[package]] 1130 | name = "static_cell" 1131 | version = "2.1.0" 1132 | source = "registry+https://github.com/rust-lang/crates.io-index" 1133 | checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e" 1134 | dependencies = [ 1135 | "portable-atomic", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "strsim" 1140 | version = "0.11.1" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1143 | 1144 | [[package]] 1145 | name = "strum" 1146 | version = "0.26.3" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 1149 | dependencies = [ 1150 | "strum_macros", 1151 | ] 1152 | 1153 | [[package]] 1154 | name = "strum_macros" 1155 | version = "0.26.4" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 1158 | dependencies = [ 1159 | "heck", 1160 | "proc-macro2", 1161 | "quote", 1162 | "rustversion", 1163 | "syn", 1164 | ] 1165 | 1166 | [[package]] 1167 | name = "syn" 1168 | version = "2.0.85" 1169 | source = "registry+https://github.com/rust-lang/crates.io-index" 1170 | checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" 1171 | dependencies = [ 1172 | "proc-macro2", 1173 | "quote", 1174 | "unicode-ident", 1175 | ] 1176 | 1177 | [[package]] 1178 | name = "termcolor" 1179 | version = "1.4.1" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 1182 | dependencies = [ 1183 | "winapi-util", 1184 | ] 1185 | 1186 | [[package]] 1187 | name = "toml" 1188 | version = "0.8.19" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" 1191 | dependencies = [ 1192 | "serde", 1193 | "serde_spanned", 1194 | "toml_datetime", 1195 | "toml_edit", 1196 | ] 1197 | 1198 | [[package]] 1199 | name = "toml_datetime" 1200 | version = "0.6.8" 1201 | source = "registry+https://github.com/rust-lang/crates.io-index" 1202 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 1203 | dependencies = [ 1204 | "serde", 1205 | ] 1206 | 1207 | [[package]] 1208 | name = "toml_edit" 1209 | version = "0.22.22" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" 1212 | dependencies = [ 1213 | "indexmap", 1214 | "serde", 1215 | "serde_spanned", 1216 | "toml_datetime", 1217 | "winnow", 1218 | ] 1219 | 1220 | [[package]] 1221 | name = "typenum" 1222 | version = "1.17.0" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1225 | 1226 | [[package]] 1227 | name = "ufmt-write" 1228 | version = "0.1.0" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" 1231 | 1232 | [[package]] 1233 | name = "unicode-ident" 1234 | version = "1.0.13" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 1237 | 1238 | [[package]] 1239 | name = "vcell" 1240 | version = "0.1.3" 1241 | source = "registry+https://github.com/rust-lang/crates.io-index" 1242 | checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" 1243 | 1244 | [[package]] 1245 | name = "version_check" 1246 | version = "0.9.5" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1249 | 1250 | [[package]] 1251 | name = "void" 1252 | version = "1.0.2" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1255 | 1256 | [[package]] 1257 | name = "wifi_tank" 1258 | version = "0.1.0" 1259 | dependencies = [ 1260 | "embassy-executor", 1261 | "embassy-net", 1262 | "embassy-time", 1263 | "esp-alloc", 1264 | "esp-backtrace", 1265 | "esp-hal", 1266 | "esp-hal-embassy", 1267 | "esp-println", 1268 | "esp-wifi", 1269 | "heapless", 1270 | "log", 1271 | "static_cell", 1272 | ] 1273 | 1274 | [[package]] 1275 | name = "winapi-util" 1276 | version = "0.1.9" 1277 | source = "registry+https://github.com/rust-lang/crates.io-index" 1278 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1279 | dependencies = [ 1280 | "windows-sys", 1281 | ] 1282 | 1283 | [[package]] 1284 | name = "windows-sys" 1285 | version = "0.59.0" 1286 | source = "registry+https://github.com/rust-lang/crates.io-index" 1287 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1288 | dependencies = [ 1289 | "windows-targets", 1290 | ] 1291 | 1292 | [[package]] 1293 | name = "windows-targets" 1294 | version = "0.52.6" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1297 | dependencies = [ 1298 | "windows_aarch64_gnullvm", 1299 | "windows_aarch64_msvc", 1300 | "windows_i686_gnu", 1301 | "windows_i686_gnullvm", 1302 | "windows_i686_msvc", 1303 | "windows_x86_64_gnu", 1304 | "windows_x86_64_gnullvm", 1305 | "windows_x86_64_msvc", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "windows_aarch64_gnullvm" 1310 | version = "0.52.6" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1313 | 1314 | [[package]] 1315 | name = "windows_aarch64_msvc" 1316 | version = "0.52.6" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1319 | 1320 | [[package]] 1321 | name = "windows_i686_gnu" 1322 | version = "0.52.6" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1325 | 1326 | [[package]] 1327 | name = "windows_i686_gnullvm" 1328 | version = "0.52.6" 1329 | source = "registry+https://github.com/rust-lang/crates.io-index" 1330 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1331 | 1332 | [[package]] 1333 | name = "windows_i686_msvc" 1334 | version = "0.52.6" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1337 | 1338 | [[package]] 1339 | name = "windows_x86_64_gnu" 1340 | version = "0.52.6" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1343 | 1344 | [[package]] 1345 | name = "windows_x86_64_gnullvm" 1346 | version = "0.52.6" 1347 | source = "registry+https://github.com/rust-lang/crates.io-index" 1348 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1349 | 1350 | [[package]] 1351 | name = "windows_x86_64_msvc" 1352 | version = "0.52.6" 1353 | source = "registry+https://github.com/rust-lang/crates.io-index" 1354 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1355 | 1356 | [[package]] 1357 | name = "winnow" 1358 | version = "0.6.20" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" 1361 | dependencies = [ 1362 | "memchr", 1363 | ] 1364 | 1365 | [[package]] 1366 | name = "xtensa-lx" 1367 | version = "0.9.0" 1368 | source = "registry+https://github.com/rust-lang/crates.io-index" 1369 | checksum = "e758f94e1a1f71758f94052a2766dcb12604998eb372b8b2e30576e3ab1ba1e6" 1370 | dependencies = [ 1371 | "bare-metal", 1372 | "mutex-trait", 1373 | "spin", 1374 | ] 1375 | 1376 | [[package]] 1377 | name = "xtensa-lx-rt" 1378 | version = "0.17.1" 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" 1380 | checksum = "2ceb69c1487b78d83531c5d94fb81d0dceef1ccb0affba29f29420b1f72d3ddb" 1381 | dependencies = [ 1382 | "anyhow", 1383 | "bare-metal", 1384 | "document-features", 1385 | "enum-as-inner", 1386 | "minijinja", 1387 | "r0", 1388 | "serde", 1389 | "strum", 1390 | "toml", 1391 | "xtensa-lx", 1392 | "xtensa-lx-rt-proc-macros", 1393 | ] 1394 | 1395 | [[package]] 1396 | name = "xtensa-lx-rt-proc-macros" 1397 | version = "0.2.2" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "11277b1e4cbb7ffe44678c668518b249c843c81df249b8f096701757bc50d7ee" 1400 | dependencies = [ 1401 | "darling", 1402 | "proc-macro2", 1403 | "quote", 1404 | "syn", 1405 | ] 1406 | -------------------------------------------------------------------------------- /wifi_tank/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wifi_tank" 3 | version = "0.1.0" 4 | authors = ["James McMurray "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [dependencies] 9 | embassy-executor = "0.6.1" 10 | embassy-net = {version = "0.4.0", features = ["udp", "tcp", "proto-ipv4", "dns", "dhcpv4-hostname", "medium-ip"]} 11 | embassy-time = "0.3.2" 12 | esp-alloc = "0.5.0" 13 | esp-backtrace = { version = "0.14.2", features = [ 14 | "esp32", 15 | "exception-handler", 16 | "panic-handler", 17 | "println", 18 | ] } 19 | esp-hal = { version = "0.21.0", features = ["esp32"] } 20 | esp-hal-embassy = { version = "0.4.0", features = [ 21 | "esp32", 22 | "log", 23 | "integrated-timers", 24 | ] } 25 | esp-println = { version = "0.12.0", features = ["esp32", "log"] } 26 | esp-wifi = { version = "0.10.1", features = [ 27 | "esp32", 28 | "wifi", 29 | "wifi-default", 30 | "async", 31 | "embassy-net", 32 | "log", 33 | "esp-alloc", 34 | ] } 35 | heapless = "0.8.0" 36 | log = { version = "0.4.22", features = [] } # "release_max_level_off" 37 | static_cell = "2.1.0" 38 | 39 | [profile.dev] 40 | # Rust debug is too slow. 41 | # For debug builds always builds with some optimization 42 | opt-level = "s" 43 | 44 | [profile.release] 45 | codegen-units = 1 # LLVM can perform better optimizations using a single thread 46 | debug = 2 47 | debug-assertions = false 48 | incremental = false 49 | lto = 'fat' 50 | opt-level = 's' 51 | overflow-checks = false 52 | -------------------------------------------------------------------------------- /wifi_tank/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021-2024 esp-rs 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /wifi_tank/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2021-2024 esp-rs 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /wifi_tank/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rustc-link-arg-bins=-Tlinkall.x"); 3 | } 4 | -------------------------------------------------------------------------------- /wifi_tank/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "esp" 3 | -------------------------------------------------------------------------------- /wifi_tank/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | mod motors; 5 | 6 | use core::{mem::MaybeUninit, str::FromStr}; 7 | use embassy_executor::Spawner; 8 | use embassy_net::udp::{PacketMetadata, UdpSocket}; 9 | use embassy_net::{Config as NetConfig, DhcpConfig, Stack, StackResources}; 10 | use esp_backtrace as _; 11 | use esp_hal::{ 12 | delay::Delay, 13 | gpio::{self}, 14 | prelude::*, 15 | rng::Rng, 16 | timer::timg::TimerGroup, 17 | }; 18 | use esp_wifi::wifi::{self, AuthMethod, WifiDevice, WifiStaDevice}; 19 | use static_cell::StaticCell; 20 | 21 | use esp_alloc as _; 22 | 23 | type WifiDriver = WifiDevice<'static, WifiStaDevice>; 24 | const CLIENT_NAME: &str = "wifitank"; 25 | 26 | fn init_heap() { 27 | const HEAP_SIZE: usize = 128 * 1024; // 128KB RAM ought to be enough for anybody 28 | static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit(); 29 | 30 | unsafe { 31 | esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( 32 | HEAP.as_mut_ptr() as *mut u8, 33 | HEAP_SIZE, 34 | esp_alloc::MemoryCapability::Internal.into(), 35 | )); 36 | } 37 | } 38 | 39 | #[embassy_executor::task] 40 | async fn net_task(stack: &'static Stack) -> ! { 41 | stack.run().await 42 | } 43 | 44 | #[esp_hal_embassy::main] 45 | async fn main(spawner: Spawner) { 46 | let peripherals = esp_hal::init(esp_hal::Config::default()); 47 | let io = esp_hal::gpio::Io::new(peripherals.GPIO, peripherals.IO_MUX); 48 | 49 | let seed = 0x0123_4567_89ab_cdef; 50 | init_heap(); 51 | let timg0 = TimerGroup::new(peripherals.TIMG0); 52 | esp_hal_embassy::init(timg0.timer0); 53 | 54 | // Motors 55 | let left_v_a_pin = gpio::Output::new(io.pins.gpio13, gpio::Level::High); 56 | let left_v_b_pin = gpio::Output::new(io.pins.gpio12, gpio::Level::High); 57 | let left_g_a_pin = gpio::Output::new(io.pins.gpio26, gpio::Level::High); 58 | let left_g_b_pin = gpio::Output::new(io.pins.gpio25, gpio::Level::High); 59 | 60 | let right_v_a_pin = gpio::Output::new(io.pins.gpio21, gpio::Level::High); 61 | let right_v_b_pin = gpio::Output::new(io.pins.gpio19, gpio::Level::High); 62 | let right_g_a_pin = gpio::Output::new(io.pins.gpio22, gpio::Level::High); 63 | let right_g_b_pin = gpio::Output::new(io.pins.gpio23, gpio::Level::High); 64 | 65 | let left_motor = 66 | motors::MotorDriver::new(left_v_a_pin, left_v_b_pin, left_g_a_pin, left_g_b_pin); 67 | let right_motor = 68 | motors::MotorDriver::new(right_v_a_pin, right_v_b_pin, right_g_a_pin, right_g_b_pin); 69 | let mut motors = motors::Motors::new(left_motor, right_motor); 70 | 71 | esp_println::logger::init_logger_from_env(); 72 | log::info!("Loading"); 73 | let rng = Rng::new(peripherals.RNG); 74 | let delay = Delay::new(); 75 | 76 | let wifi_ssid = heapless::String::<32>::from_str(env!("WIFI_SSID")).unwrap(); 77 | let wifi_password = heapless::String::<64>::from_str(env!("WIFI_PASSWORD")).unwrap(); 78 | 79 | let timg1 = TimerGroup::new(peripherals.TIMG1); 80 | log::info!("Pre-wifi init"); 81 | let wifi_init = esp_wifi::init( 82 | esp_wifi::EspWifiInitFor::Wifi, 83 | timg1.timer0, 84 | rng, 85 | peripherals.RADIO_CLK, 86 | ) 87 | .unwrap(); 88 | 89 | // TODO: Move this to async task so we can reconnect automatically 90 | log::info!("Pre-wifi config: {}", &wifi_ssid); 91 | let wifi_config = esp_wifi::wifi::ClientConfiguration { 92 | ssid: wifi_ssid, 93 | bssid: None, 94 | auth_method: AuthMethod::WPA2Personal, // WPA2Personal - AP is technically WPA3 too, but seems board doesn't support this 95 | password: wifi_password, 96 | channel: None, 97 | }; 98 | 99 | log::info!("Post-wifi config"); 100 | 101 | log::info!("Pre-wifi creation"); 102 | let (wifi_device, mut wifi_controller): (WifiDevice, _) = 103 | wifi::new_with_config(&wifi_init, peripherals.WIFI, wifi_config).unwrap(); 104 | log::info!("Pre-wifi start"); 105 | wifi_controller.start().await.unwrap(); 106 | delay.delay(1000.millis()); 107 | log::info!("Pre-wifi connect"); 108 | for i in 0..20 { 109 | if wifi_controller.connect().await.is_ok() { 110 | break; 111 | } 112 | log::info!("Failed to connect, retrying - try {}...", i); 113 | delay.delay(1000.millis()); 114 | } 115 | 116 | // embassy-net setup 117 | let mut dhcp_config = DhcpConfig::default(); 118 | dhcp_config.hostname = Some(heapless::String::from_str(CLIENT_NAME).unwrap()); 119 | let net_config = NetConfig::dhcpv4(dhcp_config); 120 | 121 | log::info!("Pre-stack assignment"); 122 | static STACK: StaticCell> = StaticCell::new(); 123 | static RESOURCES: StaticCell> = StaticCell::new(); // Increase this if you start getting socket ring errors. 124 | let stack = &*STACK.init(Stack::new( 125 | wifi_device, 126 | net_config, 127 | RESOURCES.init(StackResources::<4>::new()), 128 | seed, 129 | )); 130 | let mac_addr = stack.hardware_address(); 131 | log::info!("Hardware configured. MAC Address is {}", mac_addr); 132 | 133 | spawner.spawn(net_task(stack)).unwrap(); 134 | 135 | stack.wait_config_up().await; 136 | 137 | match stack.config_v4() { 138 | Some(a) => log::info!("IP Address appears to be: {}", a.address), 139 | None => core::panic!("DHCP completed but no IP address was assigned!"), 140 | } 141 | 142 | let mut udp_rx_meta = [PacketMetadata::EMPTY; 16]; 143 | let mut udp_rx_buffer = [0; 1024]; 144 | let mut udp_tx_meta = [PacketMetadata::EMPTY; 16]; 145 | let mut udp_tx_buffer = [0; 1024]; 146 | let mut msg_buffer = [0; 128]; 147 | 148 | let mut udp_socket = UdpSocket::new( 149 | stack, 150 | &mut udp_rx_meta, 151 | &mut udp_rx_buffer, 152 | &mut udp_tx_meta, 153 | &mut udp_tx_buffer, 154 | ); 155 | 156 | udp_socket.bind(8080).unwrap(); 157 | 158 | loop { 159 | let (rx_size, from_addr) = udp_socket.recv_from(&mut msg_buffer).await.unwrap(); 160 | if rx_size == 0 { 161 | log::info!("Received empty message from {}", from_addr); 162 | continue; 163 | } 164 | let response = msg_buffer[rx_size - 1] as char; 165 | match response { 166 | 'F' => motors.forward(), 167 | 'B' => motors.backward(), 168 | 'L' => motors.left(), 169 | 'R' => motors.right(), 170 | 'N' => motors.stop(), 171 | 'Q' => { 172 | // TODO: Deep sleep here? 173 | motors.stop(); 174 | break; 175 | } 176 | _ => log::info!("Unknown command {}", response), 177 | } 178 | // TODO: Add timeout to stop motors if nothing received 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /wifi_tank/src/motors.rs: -------------------------------------------------------------------------------- 1 | use esp_hal::gpio::Output; 2 | 3 | enum MotorDriverState { 4 | Stopped, 5 | Forward, 6 | Reverse, 7 | } 8 | 9 | pub struct MotorDriver<'a> { 10 | v_a_pin: Output<'a, esp_hal::gpio::AnyPin>, 11 | v_b_pin: Output<'a, esp_hal::gpio::AnyPin>, 12 | g_a_pin: Output<'a, esp_hal::gpio::AnyPin>, 13 | g_b_pin: Output<'a, esp_hal::gpio::AnyPin>, 14 | state: MotorDriverState, 15 | } 16 | 17 | impl<'a> MotorDriver<'a> { 18 | pub fn new( 19 | v_a_pin: Output<'a, esp_hal::gpio::AnyPin>, 20 | v_b_pin: Output<'a, esp_hal::gpio::AnyPin>, 21 | g_a_pin: Output<'a, esp_hal::gpio::AnyPin>, 22 | g_b_pin: Output<'a, esp_hal::gpio::AnyPin>, 23 | ) -> Self { 24 | let mut motor_driver = Self { 25 | v_a_pin, 26 | v_b_pin, 27 | g_a_pin, 28 | g_b_pin, 29 | state: MotorDriverState::Stopped, 30 | }; 31 | motor_driver.stop(); 32 | motor_driver 33 | } 34 | 35 | pub fn forward(&mut self) { 36 | self.v_a_pin.set_high(); 37 | self.g_a_pin.set_low(); 38 | self.v_b_pin.set_high(); 39 | self.g_b_pin.set_low(); 40 | self.state = MotorDriverState::Forward; 41 | } 42 | 43 | pub fn backward(&mut self) { 44 | self.v_a_pin.set_low(); 45 | self.g_a_pin.set_high(); 46 | self.v_b_pin.set_low(); 47 | self.g_b_pin.set_high(); 48 | self.state = MotorDriverState::Reverse; 49 | } 50 | 51 | pub fn stop(&mut self) { 52 | self.v_a_pin.set_low(); 53 | self.g_a_pin.set_low(); 54 | self.v_b_pin.set_low(); 55 | self.g_b_pin.set_low(); 56 | self.state = MotorDriverState::Stopped; 57 | } 58 | } 59 | 60 | #[derive(Debug, PartialEq)] 61 | enum MotorsState { 62 | Stopped, 63 | Forward, 64 | Reverse, 65 | Left, 66 | Right, 67 | } 68 | 69 | pub struct Motors<'a> { 70 | left_motor: MotorDriver<'a>, 71 | right_motor: MotorDriver<'a>, 72 | state: MotorsState, 73 | } 74 | 75 | impl<'a> Motors<'a> { 76 | pub fn new(left_motor: MotorDriver<'a>, right_motor: MotorDriver<'a>) -> Self { 77 | let mut motors = Self { 78 | left_motor, 79 | right_motor, 80 | state: MotorsState::Stopped, 81 | }; 82 | motors.stop(); 83 | motors 84 | } 85 | 86 | pub fn forward(&mut self) { 87 | if self.state == MotorsState::Forward { 88 | return; 89 | } 90 | self.state = MotorsState::Forward; 91 | self.left_motor.forward(); 92 | self.right_motor.forward(); 93 | } 94 | 95 | pub fn backward(&mut self) { 96 | if self.state == MotorsState::Reverse { 97 | return; 98 | } 99 | self.state = MotorsState::Reverse; 100 | self.left_motor.backward(); 101 | self.right_motor.backward(); 102 | } 103 | 104 | pub fn stop(&mut self) { 105 | if self.state == MotorsState::Stopped { 106 | return; 107 | } 108 | self.state = MotorsState::Stopped; 109 | self.left_motor.stop(); 110 | self.right_motor.stop(); 111 | } 112 | 113 | pub fn left(&mut self) { 114 | if self.state == MotorsState::Left { 115 | return; 116 | } 117 | self.state = MotorsState::Left; 118 | self.left_motor.backward(); 119 | self.right_motor.forward(); 120 | } 121 | 122 | pub fn right(&mut self) { 123 | if self.state == MotorsState::Right { 124 | return; 125 | } 126 | self.state = MotorsState::Right; 127 | self.left_motor.forward(); 128 | self.right_motor.backward(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /wifi_tank_controller_client/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /wifi_tank_controller_client/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.4.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 25 | 26 | [[package]] 27 | name = "backtrace" 28 | version = "0.3.74" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 31 | dependencies = [ 32 | "addr2line", 33 | "cfg-if", 34 | "libc", 35 | "miniz_oxide", 36 | "object", 37 | "rustc-demangle", 38 | "windows-targets", 39 | ] 40 | 41 | [[package]] 42 | name = "bitflags" 43 | version = "2.6.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 46 | 47 | [[package]] 48 | name = "bumpalo" 49 | version = "3.16.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 52 | 53 | [[package]] 54 | name = "bytes" 55 | version = "1.8.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" 58 | 59 | [[package]] 60 | name = "cfg-if" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 64 | 65 | [[package]] 66 | name = "cfg_aliases" 67 | version = "0.2.1" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 70 | 71 | [[package]] 72 | name = "core-foundation" 73 | version = "0.10.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" 76 | dependencies = [ 77 | "core-foundation-sys", 78 | "libc", 79 | ] 80 | 81 | [[package]] 82 | name = "core-foundation-sys" 83 | version = "0.8.7" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 86 | 87 | [[package]] 88 | name = "fnv" 89 | version = "1.0.7" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 92 | 93 | [[package]] 94 | name = "gilrs" 95 | version = "0.11.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "bbb2c998745a3c1ac90f64f4f7b3a54219fd3612d7705e7798212935641ed18f" 98 | dependencies = [ 99 | "fnv", 100 | "gilrs-core", 101 | "log", 102 | "uuid", 103 | "vec_map", 104 | ] 105 | 106 | [[package]] 107 | name = "gilrs-core" 108 | version = "0.6.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "495af945e45efd6386227613cd9fb7bd7c43d3c095040e30c5304c489e6abed5" 111 | dependencies = [ 112 | "core-foundation", 113 | "inotify", 114 | "io-kit-sys", 115 | "js-sys", 116 | "libc", 117 | "libudev-sys", 118 | "log", 119 | "nix", 120 | "uuid", 121 | "vec_map", 122 | "wasm-bindgen", 123 | "web-sys", 124 | "windows", 125 | ] 126 | 127 | [[package]] 128 | name = "gimli" 129 | version = "0.31.1" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 132 | 133 | [[package]] 134 | name = "hermit-abi" 135 | version = "0.3.9" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 138 | 139 | [[package]] 140 | name = "inotify" 141 | version = "0.11.0" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" 144 | dependencies = [ 145 | "bitflags", 146 | "inotify-sys", 147 | "libc", 148 | ] 149 | 150 | [[package]] 151 | name = "inotify-sys" 152 | version = "0.1.5" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" 155 | dependencies = [ 156 | "libc", 157 | ] 158 | 159 | [[package]] 160 | name = "io-kit-sys" 161 | version = "0.4.1" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" 164 | dependencies = [ 165 | "core-foundation-sys", 166 | "mach2", 167 | ] 168 | 169 | [[package]] 170 | name = "js-sys" 171 | version = "0.3.72" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" 174 | dependencies = [ 175 | "wasm-bindgen", 176 | ] 177 | 178 | [[package]] 179 | name = "libc" 180 | version = "0.2.161" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" 183 | 184 | [[package]] 185 | name = "libudev-sys" 186 | version = "0.1.4" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" 189 | dependencies = [ 190 | "libc", 191 | "pkg-config", 192 | ] 193 | 194 | [[package]] 195 | name = "lock_api" 196 | version = "0.4.12" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 199 | dependencies = [ 200 | "autocfg", 201 | "scopeguard", 202 | ] 203 | 204 | [[package]] 205 | name = "log" 206 | version = "0.4.22" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 209 | 210 | [[package]] 211 | name = "mach2" 212 | version = "0.4.2" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" 215 | dependencies = [ 216 | "libc", 217 | ] 218 | 219 | [[package]] 220 | name = "memchr" 221 | version = "2.7.4" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 224 | 225 | [[package]] 226 | name = "miniz_oxide" 227 | version = "0.8.0" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 230 | dependencies = [ 231 | "adler2", 232 | ] 233 | 234 | [[package]] 235 | name = "mio" 236 | version = "1.0.2" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 239 | dependencies = [ 240 | "hermit-abi", 241 | "libc", 242 | "wasi", 243 | "windows-sys", 244 | ] 245 | 246 | [[package]] 247 | name = "nix" 248 | version = "0.29.0" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" 251 | dependencies = [ 252 | "bitflags", 253 | "cfg-if", 254 | "cfg_aliases", 255 | "libc", 256 | ] 257 | 258 | [[package]] 259 | name = "object" 260 | version = "0.36.5" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" 263 | dependencies = [ 264 | "memchr", 265 | ] 266 | 267 | [[package]] 268 | name = "once_cell" 269 | version = "1.20.2" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 272 | 273 | [[package]] 274 | name = "parking_lot" 275 | version = "0.12.3" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 278 | dependencies = [ 279 | "lock_api", 280 | "parking_lot_core", 281 | ] 282 | 283 | [[package]] 284 | name = "parking_lot_core" 285 | version = "0.9.10" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 288 | dependencies = [ 289 | "cfg-if", 290 | "libc", 291 | "redox_syscall", 292 | "smallvec", 293 | "windows-targets", 294 | ] 295 | 296 | [[package]] 297 | name = "pin-project-lite" 298 | version = "0.2.15" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" 301 | 302 | [[package]] 303 | name = "pkg-config" 304 | version = "0.3.31" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 307 | 308 | [[package]] 309 | name = "proc-macro2" 310 | version = "1.0.89" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" 313 | dependencies = [ 314 | "unicode-ident", 315 | ] 316 | 317 | [[package]] 318 | name = "quote" 319 | version = "1.0.37" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 322 | dependencies = [ 323 | "proc-macro2", 324 | ] 325 | 326 | [[package]] 327 | name = "redox_syscall" 328 | version = "0.5.7" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 331 | dependencies = [ 332 | "bitflags", 333 | ] 334 | 335 | [[package]] 336 | name = "rustc-demangle" 337 | version = "0.1.24" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 340 | 341 | [[package]] 342 | name = "scopeguard" 343 | version = "1.2.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 346 | 347 | [[package]] 348 | name = "signal-hook-registry" 349 | version = "1.4.2" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 352 | dependencies = [ 353 | "libc", 354 | ] 355 | 356 | [[package]] 357 | name = "smallvec" 358 | version = "1.13.2" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 361 | 362 | [[package]] 363 | name = "socket2" 364 | version = "0.5.7" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 367 | dependencies = [ 368 | "libc", 369 | "windows-sys", 370 | ] 371 | 372 | [[package]] 373 | name = "syn" 374 | version = "2.0.86" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" 377 | dependencies = [ 378 | "proc-macro2", 379 | "quote", 380 | "unicode-ident", 381 | ] 382 | 383 | [[package]] 384 | name = "tokio" 385 | version = "1.41.0" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" 388 | dependencies = [ 389 | "backtrace", 390 | "bytes", 391 | "libc", 392 | "mio", 393 | "parking_lot", 394 | "pin-project-lite", 395 | "signal-hook-registry", 396 | "socket2", 397 | "tokio-macros", 398 | "windows-sys", 399 | ] 400 | 401 | [[package]] 402 | name = "tokio-macros" 403 | version = "2.4.0" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 406 | dependencies = [ 407 | "proc-macro2", 408 | "quote", 409 | "syn", 410 | ] 411 | 412 | [[package]] 413 | name = "unicode-ident" 414 | version = "1.0.13" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 417 | 418 | [[package]] 419 | name = "uuid" 420 | version = "1.11.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" 423 | 424 | [[package]] 425 | name = "vec_map" 426 | version = "0.8.2" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 429 | 430 | [[package]] 431 | name = "wasi" 432 | version = "0.11.0+wasi-snapshot-preview1" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 435 | 436 | [[package]] 437 | name = "wasm-bindgen" 438 | version = "0.2.95" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" 441 | dependencies = [ 442 | "cfg-if", 443 | "once_cell", 444 | "wasm-bindgen-macro", 445 | ] 446 | 447 | [[package]] 448 | name = "wasm-bindgen-backend" 449 | version = "0.2.95" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" 452 | dependencies = [ 453 | "bumpalo", 454 | "log", 455 | "once_cell", 456 | "proc-macro2", 457 | "quote", 458 | "syn", 459 | "wasm-bindgen-shared", 460 | ] 461 | 462 | [[package]] 463 | name = "wasm-bindgen-macro" 464 | version = "0.2.95" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" 467 | dependencies = [ 468 | "quote", 469 | "wasm-bindgen-macro-support", 470 | ] 471 | 472 | [[package]] 473 | name = "wasm-bindgen-macro-support" 474 | version = "0.2.95" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" 477 | dependencies = [ 478 | "proc-macro2", 479 | "quote", 480 | "syn", 481 | "wasm-bindgen-backend", 482 | "wasm-bindgen-shared", 483 | ] 484 | 485 | [[package]] 486 | name = "wasm-bindgen-shared" 487 | version = "0.2.95" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" 490 | 491 | [[package]] 492 | name = "web-sys" 493 | version = "0.3.72" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" 496 | dependencies = [ 497 | "js-sys", 498 | "wasm-bindgen", 499 | ] 500 | 501 | [[package]] 502 | name = "wifi_tank_controller_client" 503 | version = "0.1.0" 504 | dependencies = [ 505 | "gilrs", 506 | "tokio", 507 | ] 508 | 509 | [[package]] 510 | name = "windows" 511 | version = "0.58.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" 514 | dependencies = [ 515 | "windows-core", 516 | "windows-targets", 517 | ] 518 | 519 | [[package]] 520 | name = "windows-core" 521 | version = "0.58.0" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" 524 | dependencies = [ 525 | "windows-implement", 526 | "windows-interface", 527 | "windows-result", 528 | "windows-strings", 529 | "windows-targets", 530 | ] 531 | 532 | [[package]] 533 | name = "windows-implement" 534 | version = "0.58.0" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" 537 | dependencies = [ 538 | "proc-macro2", 539 | "quote", 540 | "syn", 541 | ] 542 | 543 | [[package]] 544 | name = "windows-interface" 545 | version = "0.58.0" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" 548 | dependencies = [ 549 | "proc-macro2", 550 | "quote", 551 | "syn", 552 | ] 553 | 554 | [[package]] 555 | name = "windows-result" 556 | version = "0.2.0" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 559 | dependencies = [ 560 | "windows-targets", 561 | ] 562 | 563 | [[package]] 564 | name = "windows-strings" 565 | version = "0.1.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 568 | dependencies = [ 569 | "windows-result", 570 | "windows-targets", 571 | ] 572 | 573 | [[package]] 574 | name = "windows-sys" 575 | version = "0.52.0" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 578 | dependencies = [ 579 | "windows-targets", 580 | ] 581 | 582 | [[package]] 583 | name = "windows-targets" 584 | version = "0.52.6" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 587 | dependencies = [ 588 | "windows_aarch64_gnullvm", 589 | "windows_aarch64_msvc", 590 | "windows_i686_gnu", 591 | "windows_i686_gnullvm", 592 | "windows_i686_msvc", 593 | "windows_x86_64_gnu", 594 | "windows_x86_64_gnullvm", 595 | "windows_x86_64_msvc", 596 | ] 597 | 598 | [[package]] 599 | name = "windows_aarch64_gnullvm" 600 | version = "0.52.6" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 603 | 604 | [[package]] 605 | name = "windows_aarch64_msvc" 606 | version = "0.52.6" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 609 | 610 | [[package]] 611 | name = "windows_i686_gnu" 612 | version = "0.52.6" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 615 | 616 | [[package]] 617 | name = "windows_i686_gnullvm" 618 | version = "0.52.6" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 621 | 622 | [[package]] 623 | name = "windows_i686_msvc" 624 | version = "0.52.6" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 627 | 628 | [[package]] 629 | name = "windows_x86_64_gnu" 630 | version = "0.52.6" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 633 | 634 | [[package]] 635 | name = "windows_x86_64_gnullvm" 636 | version = "0.52.6" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 639 | 640 | [[package]] 641 | name = "windows_x86_64_msvc" 642 | version = "0.52.6" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 645 | -------------------------------------------------------------------------------- /wifi_tank_controller_client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wifi_tank_controller_client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | gilrs = "0.11.0" 8 | tokio = {version = "1.41.0", features = ["full"]} 9 | -------------------------------------------------------------------------------- /wifi_tank_controller_client/src/main.rs: -------------------------------------------------------------------------------- 1 | use core::str; 2 | use std::env; 3 | use std::error::Error; 4 | use tokio::net::UdpSocket; 5 | 6 | const TARGET_HOSTNAME: &str = "wifitank:8080"; 7 | const CAMERA_HOSTNAME: &str = "espressif:80"; 8 | 9 | #[tokio::main] 10 | async fn main() -> Result<(), Box> { 11 | use gilrs::{Button, Event, Gilrs}; 12 | 13 | let mut gilrs = Gilrs::new().unwrap(); 14 | 15 | // Iterate over all connected gamepads 16 | for (_id, gamepad) in gilrs.gamepads() { 17 | println!("{} is {:?}", gamepad.name(), gamepad.power_info()); 18 | } 19 | 20 | let mut active_gamepad = None; 21 | 22 | println!("Push button on gamepad!"); 23 | while active_gamepad.is_none() { 24 | // Examine new events 25 | while let Some(Event { 26 | id, event, time, .. 27 | }) = gilrs.next_event() 28 | { 29 | println!("{:?} New event from {}: {:?}", time, id, event); 30 | active_gamepad = Some(id); 31 | } 32 | } 33 | 34 | let addr = env::args() 35 | .nth(1) 36 | .unwrap_or_else(|| "0.0.0.0:8080".to_string()); 37 | 38 | println!("Waiting for connection to rover: {}", TARGET_HOSTNAME); 39 | 40 | let socket = UdpSocket::bind(&addr).await?; 41 | let peer = loop { 42 | if let Ok(p) = tokio::net::lookup_host(TARGET_HOSTNAME).await { 43 | if let Some(pi) = p.into_iter().next() { 44 | break pi; 45 | } 46 | } 47 | tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; 48 | }; 49 | println!("Connected to rover: {}", peer); 50 | 51 | // TODO: Move to other thread? 52 | println!("Waiting for connection to camera: {}", TARGET_HOSTNAME); 53 | let camera_peer = loop { 54 | if let Ok(p) = tokio::net::lookup_host(CAMERA_HOSTNAME).await { 55 | if let Some(pi) = p.into_iter().next() { 56 | break pi; 57 | } 58 | } 59 | tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; 60 | }; 61 | println!("Connected to camera: {}", camera_peer); 62 | let cam_url = format!("http://{}", CAMERA_HOSTNAME.split(':').next().unwrap()); 63 | std::process::Command::new("firefox") 64 | .arg("-kiosk") 65 | .arg(cam_url.as_str()) 66 | .spawn() 67 | .ok(); 68 | 69 | loop { 70 | while let Some(Event { 71 | id, event, time, .. 72 | }) = gilrs.next_event() 73 | // TODO: Make this real async? 74 | { 75 | println!("{:?} New event from {}: {:?}", time, id, event); 76 | } 77 | 78 | { 79 | let gamepad = gilrs.gamepad(active_gamepad.expect("Gamepad not found!")); 80 | if gamepad.is_pressed(Button::DPadUp) { 81 | socket.send_to(b"F", &peer).await?; 82 | } else if gamepad.is_pressed(Button::DPadDown) { 83 | socket.send_to(b"B", &peer).await?; 84 | } else if gamepad.is_pressed(Button::DPadLeft) { 85 | socket.send_to(b"L", &peer).await?; 86 | } else if gamepad.is_pressed(Button::DPadRight) { 87 | socket.send_to(b"R", &peer).await?; 88 | } else if gamepad.is_pressed(Button::Select) { 89 | socket.send_to(b"Q", &peer).await?; 90 | break; 91 | } else { 92 | socket.send_to(b"N", &peer).await?; 93 | } 94 | } 95 | 96 | tokio::time::sleep(tokio::time::Duration::from_millis(50)).await; 97 | } 98 | 99 | Ok(()) 100 | } 101 | -------------------------------------------------------------------------------- /wifitank.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamesmcm/esp32_wifi_tank/e7a8519bb122454f373fbb59e645710b1e3ca66c/wifitank.jpg --------------------------------------------------------------------------------