7e3c8b954f032435917277990d306990213a1e6a
BOM-DAT/diode-dat/diode-dat.md
| ... | ... | @@ -165,6 +165,9 @@ For **high-voltage AC** rectification (like 220V/110V mains power supplies): |
| 165 | 165 | |
| 166 | 166 | MPC555LFMZP40NXP - 3300V, 90A, Silicon Carbide (SiC) Schottky Diode, 2-lead T-MAX® package. |
| 167 | 167 | |
| 168 | +1N5619 |
|
| 169 | + |
|
| 170 | + |
|
| 168 | 171 | |
| 169 | 172 | ## Rectifier Diode vs Schottky Diode |
| 170 | 173 |
Board-dat/SMO/SMO1050-dat/SMO1050-dat.md
| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | + |
|
| 2 | +# SMO1050-dat |
|
| 3 | + |
|
| 4 | +## Info |
|
| 5 | + |
|
| 6 | +[product url - 3PCs Laser Point, Pointer 650nm](https://www.electrodragon.com/product/laser-point-header-module-650nm-infrared-detector-readable/) |
|
| 7 | + |
|
| 8 | +### Board Map, Dimension, Pins, chip info, Use Guide, Setup Jumper, etc. |
|
| 9 | + |
|
| 10 | +- [[laser-dat]] |
|
| 11 | + |
|
| 12 | + |
|
| 13 | +## Applications, category, tags, etc. |
|
| 14 | + |
|
| 15 | +## Demo Code and Video |
|
| 16 | + |
|
| 17 | + |
|
| 18 | + |
|
| 19 | + |
|
| 20 | +## ref |
|
| 21 | + |
|
| 22 | +- [[SMO1050]] |
|
| 23 | + |
|
| 24 | +- [legacy wiki page](https://www.electrodragon.com/w/Laser_Pointer) |
|
| 25 | + |
|
| 26 | + |
|
| 27 | + |
Board-new-dat/30-dual-foot-dat/30-dual-foot-dat.md
| ... | ... | @@ -6,4 +6,4 @@ |
| 6 | 6 | |
| 7 | 7 | - [[Motor-reduction-Gear]] |
| 8 | 8 | |
| 9 | - |
|
| 9 | +- [[code-dat]] |
Tech-dat/Sensor-dat/sensor-motion-dat/sensor-tilt-switch-dat/sensor-tilt-switch-dat.md
| ... | ... | @@ -2,16 +2,26 @@ |
| 2 | 2 | |
| 3 | 3 | # sensor-tilt-switch-dat |
| 4 | 4 | |
| 5 | +== [[sensor-vibration-dat]] |
|
| 6 | + |
|
| 7 | + |
|
| 8 | +- [[SMO1040-dat]] - [[SMO1045-dat]] - [[SMO1048-dat]] |
|
| 9 | + |
|
| 10 | + |
|
| 11 | + |
|
| 5 | 12 | |
| 6 | 13 | ## mercury type tilt switch sensor |
| 7 | 14 | |
| 8 | -- [[smo1040-dat]] |
|
| 15 | +- [[smo1040-dat]] |
|
| 9 | 16 | |
| 10 | 17 | |
| 11 | 18 | ## Reed Tilt Switch Sensor |
| 12 | 19 | |
| 13 | 20 |  |
| 14 | 21 | |
| 22 | + |
|
| 23 | + |
|
| 24 | + |
|
| 15 | 25 | ### Structure |
| 16 | 26 | |
| 17 | 27 | ✅ **Described Structure** |
Tech-dat/light-dat/laser-dat/laser-dat.md
| ... | ... | @@ -17,6 +17,11 @@ Lasers can be used in sensors through the following key principles: |
| 17 | 17 | | **Laser interferometry** | Measures phase change of reflected laser light | Ultra-high precision displacement | |
| 18 | 18 | |
| 19 | 19 | |
| 20 | +激光的发射原理及产生过程的特殊性决定了激光具有普通光所不具有的特点:即三好(单色性好、相干性好、方向性好)一高(亮度高)。 |
|
| 21 | + |
|
| 22 | +- [[SMO1050-dat]] |
|
| 23 | + |
|
| 24 | + |
|
| 20 | 25 | ## ref |
| 21 | 26 | |
| 22 | 27 | - [[TOF-dat]] |
code-dat/esp32-ledc-dat/esp32-ledc-dat.md
| ... | ... | @@ -0,0 +1,52 @@ |
| 1 | + |
|
| 2 | +# esp32-ledc-dat |
|
| 3 | + |
|
| 4 | + |
|
| 5 | +## wrong part |
|
| 6 | + |
|
| 7 | +Add the LEDC driver include (put near top of your sketch) |
|
| 8 | + |
|
| 9 | + #include <driver/ledc.h> |
|
| 10 | + |
|
| 11 | +Replace ledcAttachPin calls if the core expects ledcAttach instead of ledcAttachPin. Try this variant first: |
|
| 12 | + |
|
| 13 | + ledcSetup(PWM_CHANNEL1, PWM_FREQ, PWM_RESOLUTION); |
|
| 14 | + ledcSetup(PWM_CHANNEL2, PWM_FREQ, PWM_RESOLUTION); |
|
| 15 | + // use ledcAttach(channel, pin) for this core |
|
| 16 | + ledcAttach(PWM_CHANNEL1, IN1_PIN); |
|
| 17 | + ledcAttach(PWM_CHANNEL2, IN2_PIN); |
|
| 18 | + |
|
| 19 | + |
|
| 20 | +Compilation error: 'ledcSetup' was not declared in this scope; did you mean 'ledc_stop'? |
|
| 21 | + |
|
| 22 | +### error == solution 1 |
|
| 23 | + |
|
| 24 | +If that still fails, try the original signature (some cores use this): |
|
| 25 | + |
|
| 26 | + ledcAttachPin(IN1_PIN, PWM_CHANNEL1); |
|
| 27 | + ledcAttachPin(IN2_PIN, PWM_CHANNEL2); |
|
| 28 | + |
|
| 29 | +### error == solution 2 |
|
| 30 | + |
|
| 31 | +Compilation error: 'ledcAttachPin' was not declared in this scope; did you mean 'ledcAttach'? |
|
| 32 | + |
|
| 33 | +#include <esp32-hal-ledc.h> |
|
| 34 | + |
|
| 35 | +driver/ledc.h is the IDF/ESP-IDF header; the Arduino core for ESP32 exposes ledc APIs via esp32-hal-ledc.h. This makes ledcSetup, ledcAttachPin, ledcWrite available when compiling with the Arduino ESP32 package. |
|
| 36 | + |
|
| 37 | + |
|
| 38 | + |
|
| 39 | +### correct |
|
| 40 | + |
|
| 41 | +#include <esp32-hal-ledc.h> |
|
| 42 | + |
|
| 43 | + // Configure LEDC PWM channels and attach pins (ESP32 Arduino Core 3.x API) |
|
| 44 | + ledcAttach(IN1_PIN, PWM_FREQ, PWM_RESOLUTION); |
|
| 45 | + ledcAttach(IN2_PIN, PWM_FREQ, PWM_RESOLUTION); |
|
| 46 | + |
|
| 47 | + pinMode(IN1_PIN, OUTPUT); |
|
| 48 | + pinMode(IN2_PIN, OUTPUT); |
|
| 49 | + |
|
| 50 | + // 默认停止 |
|
| 51 | + ledcWrite(IN1_PIN, 0); |
|
| 52 | + ledcWrite(IN2_PIN, 0); |
|
| ... | ... | \ No newline at end of file |
code-dat/motor-driver-code-dat/esp32-ap-drv8871-1.ino
| ... | ... | @@ -0,0 +1,263 @@ |
| 1 | +#include <WiFi.h> |
|
| 2 | +#include <WebServer.h> |
|
| 3 | + |
|
| 4 | +// ===== WiFi 配置 ===== |
|
| 5 | +const char* ssid = "motor_control"; |
|
| 6 | +const char* password = "electrodragon"; |
|
| 7 | + |
|
| 8 | +// ===== DRV8871 / GPIO 定义 (ESP32) ===== |
|
| 9 | +// DRV8871 has two inputs IN1 and IN2. Set both to control direction. |
|
| 10 | +// Forward: IN1=HIGH, IN2=LOW |
|
| 11 | +// Reverse: IN1=LOW, IN2=HIGH |
|
| 12 | +// Stop: IN1=LOW, IN2=LOW (or both HIGH for brake) |
|
| 13 | +const int IN1_PIN = 15; // IN1 -> IO15 |
|
| 14 | +const int IN2_PIN = 18; // IN2 -> IO18 |
|
| 15 | + |
|
| 16 | +// ===== Web Server ===== |
|
| 17 | +WebServer server(80); |
|
| 18 | + |
|
| 19 | +// ===== HTML Web 控制界面 ===== |
|
| 20 | +const char html_page[] PROGMEM = R"rawliteral( |
|
| 21 | +<!DOCTYPE html><html> |
|
| 22 | +<head> |
|
| 23 | +<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
| 24 | +<title>ESP32 Motor Control (DRV8871)</title> |
|
| 25 | +<style> |
|
| 26 | +body { |
|
| 27 | + font-family: Arial, sans-serif; |
|
| 28 | + max-width: 600px; |
|
| 29 | + margin: 0 auto; |
|
| 30 | + padding: 20px; |
|
| 31 | + background-color: #f0f0f0; |
|
| 32 | +} |
|
| 33 | + |
|
| 34 | +h2 { |
|
| 35 | + text-align: center; |
|
| 36 | + color: #333; |
|
| 37 | +} |
|
| 38 | +.control-panel { |
|
| 39 | + background: white; |
|
| 40 | + padding: 20px; |
|
| 41 | + border-radius: 10px; |
|
| 42 | + box-shadow: 0 2px 10px rgba(0,0,0,0.1); |
|
| 43 | +} |
|
| 44 | +.section { |
|
| 45 | + margin-bottom: 25px; |
|
| 46 | +} |
|
| 47 | +.section-title { |
|
| 48 | + font-weight: bold; |
|
| 49 | + margin-bottom: 10px; |
|
| 50 | + font-size: 18px; |
|
| 51 | + color: #555; |
|
| 52 | +} |
|
| 53 | +.speed-buttons { |
|
| 54 | + display: grid; |
|
| 55 | + grid-template-columns: repeat(6, 1fr); |
|
| 56 | + gap: 8px; |
|
| 57 | + margin-bottom: 10px; |
|
| 58 | +} |
|
| 59 | +.speed-btn { |
|
| 60 | + padding: 15px 10px; |
|
| 61 | + font-size: 16px; |
|
| 62 | + font-weight: bold; |
|
| 63 | + border: 2px solid #4CAF50; |
|
| 64 | + background-color: white; |
|
| 65 | + color: #4CAF50; |
|
| 66 | + border-radius: 8px; |
|
| 67 | + cursor: pointer; |
|
| 68 | + transition: all 0.2s; |
|
| 69 | +} |
|
| 70 | +.speed-btn:active { |
|
| 71 | + background-color: #4CAF50; |
|
| 72 | + color: white; |
|
| 73 | + transform: scale(0.95); |
|
| 74 | +} |
|
| 75 | +.dir-buttons { |
|
| 76 | + display: grid; |
|
| 77 | + grid-template-columns: 1fr 1fr; |
|
| 78 | + gap: 10px; |
|
| 79 | +} |
|
| 80 | +.dir-btn { |
|
| 81 | + padding: 20px; |
|
| 82 | + font-size: 18px; |
|
| 83 | + font-weight: bold; |
|
| 84 | + border: none; |
|
| 85 | + border-radius: 8px; |
|
| 86 | + cursor: pointer; |
|
| 87 | + transition: all 0.2s; |
|
| 88 | +} |
|
| 89 | +.forward-btn { |
|
| 90 | + background-color: #2196F3; |
|
| 91 | + color: white; |
|
| 92 | +} |
|
| 93 | +.reverse-btn { |
|
| 94 | + background-color: #FF9800; |
|
| 95 | + color: white; |
|
| 96 | +} |
|
| 97 | +.dir-btn:active { |
|
| 98 | + transform: scale(0.95); |
|
| 99 | + opacity: 0.8; |
|
| 100 | +} |
|
| 101 | +.stop-btn { |
|
| 102 | + width: 100%; |
|
| 103 | + padding: 20px; |
|
| 104 | + font-size: 20px; |
|
| 105 | + font-weight: bold; |
|
| 106 | + background-color: #f44336; |
|
| 107 | + color: white; |
|
| 108 | + border: none; |
|
| 109 | + border-radius: 8px; |
|
| 110 | + cursor: pointer; |
|
| 111 | + margin-top: 20px; |
|
| 112 | +} |
|
| 113 | +.stop-btn:active { |
|
| 114 | + transform: scale(0.98); |
|
| 115 | + opacity: 0.8; |
|
| 116 | +} |
|
| 117 | +</style> |
|
| 118 | +</head> |
|
| 119 | +<body> |
|
| 120 | +<h2>Motor Control</h2> |
|
| 121 | +<div class="control-panel"> |
|
| 122 | + <div class="section"> |
|
| 123 | + <div class="section-title">Speed Control (0-10)</div> |
|
| 124 | + <div class="speed-buttons"> |
|
| 125 | + <button class="speed-btn" onclick="setSpeed(0)">0</button> |
|
| 126 | + <button class="speed-btn" onclick="setSpeed(1)">1</button> |
|
| 127 | + <button class="speed-btn" onclick="setSpeed(2)">2</button> |
|
| 128 | + <button class="speed-btn" onclick="setSpeed(3)">3</button> |
|
| 129 | + <button class="speed-btn" onclick="setSpeed(4)">4</button> |
|
| 130 | + <button class="speed-btn" onclick="setSpeed(5)">5</button> |
|
| 131 | + <button class="speed-btn" onclick="setSpeed(6)">6</button> |
|
| 132 | + <button class="speed-btn" onclick="setSpeed(7)">7</button> |
|
| 133 | + <button class="speed-btn" onclick="setSpeed(8)">8</button> |
|
| 134 | + <button class="speed-btn" onclick="setSpeed(9)">9</button> |
|
| 135 | + <button class="speed-btn" onclick="setSpeed(10)">10</button> |
|
| 136 | + </div> |
|
| 137 | + </div> |
|
| 138 | + <div class="section"> |
|
| 139 | + <div class="section-title">Direction Control</div> |
|
| 140 | + <div class="dir-buttons"> |
|
| 141 | + <button class="dir-btn forward-btn" onclick="setDirection('fw')">Forward</button> |
|
| 142 | + <button class="dir-btn reverse-btn" onclick="setDirection('rv')">Reverse</button> |
|
| 143 | + </div> |
|
| 144 | + </div> |
|
| 145 | + <button class="stop-btn" onclick="stopMotor()">STOP</button> |
|
| 146 | +</div> |
|
| 147 | +<script> |
|
| 148 | +let currentSpeed = 0; |
|
| 149 | +let currentDir = 'fw'; |
|
| 150 | +function setSpeed(speed) { |
|
| 151 | + currentSpeed = speed; |
|
| 152 | + sendControl(); |
|
| 153 | +} |
|
| 154 | +function setDirection(dir) { |
|
| 155 | + currentDir = dir; |
|
| 156 | + sendControl(); |
|
| 157 | +} |
|
| 158 | +function sendControl() { |
|
| 159 | + let pwmValue = Math.round((currentSpeed / 10) * 1023); |
|
| 160 | + fetch('/control?speed=' + pwmValue + '&dir=' + currentDir) |
|
| 161 | + .then(response => response.text()) |
|
| 162 | + .catch(error => console.error('Error:', error)); |
|
| 163 | +} |
|
| 164 | +function stopMotor() { |
|
| 165 | + currentSpeed = 0; |
|
| 166 | + fetch('/control?speed=0&dir=' + currentDir) |
|
| 167 | + .then(response => response.text()) |
|
| 168 | + .catch(error => console.error('Error:', error)); |
|
| 169 | +} |
|
| 170 | +</script> |
|
| 171 | +</body></html> |
|
| 172 | +)rawliteral"; |
|
| 173 | + |
|
| 174 | +// ===== 处理 Web 请求 ===== |
|
| 175 | +void handleRoot() { |
|
| 176 | + server.send(200, "text/html", html_page); |
|
| 177 | +} |
|
| 178 | + |
|
| 179 | +void handleControl() { |
|
| 180 | + // Check for direction control |
|
| 181 | + if (server.hasArg("dir")) { |
|
| 182 | + String d = server.arg("dir"); |
|
| 183 | + if (d == "fw") { |
|
| 184 | + // Forward: IN1=HIGH, IN2=LOW |
|
| 185 | + digitalWrite(IN1_PIN, HIGH); |
|
| 186 | + digitalWrite(IN2_PIN, LOW); |
|
| 187 | + Serial.println("Motor: Forward"); |
|
| 188 | + } else if (d == "rv") { |
|
| 189 | + // Reverse: IN1=LOW, IN2=HIGH |
|
| 190 | + digitalWrite(IN1_PIN, LOW); |
|
| 191 | + digitalWrite(IN2_PIN, HIGH); |
|
| 192 | + Serial.println("Motor: Reverse"); |
|
| 193 | + } |
|
| 194 | + } |
|
| 195 | + |
|
| 196 | + // Check for stop command |
|
| 197 | + if (server.hasArg("stop") || (server.hasArg("speed") && server.arg("speed").toInt() == 0)) { |
|
| 198 | + // Stop: both LOW |
|
| 199 | + digitalWrite(IN1_PIN, LOW); |
|
| 200 | + digitalWrite(IN2_PIN, LOW); |
|
| 201 | + Serial.println("Motor: Stop"); |
|
| 202 | + } |
|
| 203 | + |
|
| 204 | + server.sendHeader("Location", "/"); |
|
| 205 | + server.send(302, "text/plain", ""); |
|
| 206 | +} |
|
| 207 | + |
|
| 208 | +// ===== 初始化 ===== |
|
| 209 | +void setup() { |
|
| 210 | + Serial.begin(115200); |
|
| 211 | + delay(100); |
|
| 212 | + Serial.println("ESP32 DRV8871 AP Motor Controller starting..."); |
|
| 213 | + |
|
| 214 | + // Configure GPIO pins as outputs |
|
| 215 | + pinMode(IN1_PIN, OUTPUT); |
|
| 216 | + pinMode(IN2_PIN, OUTPUT); |
|
| 217 | + |
|
| 218 | + // 默认停止 (both LOW) |
|
| 219 | + digitalWrite(IN1_PIN, LOW); |
|
| 220 | + digitalWrite(IN2_PIN, LOW); |
|
| 221 | + |
|
| 222 | + // 启动为 WiFi AP 模式,使用固定 IP |
|
| 223 | + Serial.println("Starting WiFi AP..."); |
|
| 224 | + |
|
| 225 | + WiFi.disconnect(true); |
|
| 226 | + WiFi.mode(WIFI_AP); |
|
| 227 | + delay(100); |
|
| 228 | + |
|
| 229 | + bool apStarted = WiFi.softAP(ssid, password); |
|
| 230 | + if (!apStarted) { |
|
| 231 | + Serial.println("AP start failed! Retrying..."); |
|
| 232 | + delay(1000); |
|
| 233 | + WiFi.softAP(ssid, password); |
|
| 234 | + } |
|
| 235 | + |
|
| 236 | + delay(1000); |
|
| 237 | + IPAddress apIP(192, 168, 50, 1); |
|
| 238 | + IPAddress apGateway(192, 168, 50, 1); |
|
| 239 | + IPAddress apSubnet(255, 255, 255, 0); |
|
| 240 | + if (!WiFi.softAPConfig(apIP, apGateway, apSubnet)) { |
|
| 241 | + Serial.println("softAPConfig failed"); |
|
| 242 | + } |
|
| 243 | + |
|
| 244 | + delay(500); |
|
| 245 | + Serial.println(""); |
|
| 246 | + Serial.println("=========================="); |
|
| 247 | + Serial.print("AP SSID: "); |
|
| 248 | + Serial.println(ssid); |
|
| 249 | + Serial.print("AP Password: "); |
|
| 250 | + Serial.println(password); |
|
| 251 | + Serial.print("AP IP: "); |
|
| 252 | + Serial.println(WiFi.softAPIP()); |
|
| 253 | + Serial.println("=========================="); |
|
| 254 | + |
|
| 255 | + // Web 服务 |
|
| 256 | + server.on("/", handleRoot); |
|
| 257 | + server.on("/control", handleControl); |
|
| 258 | + server.begin(); |
|
| 259 | +} |
|
| 260 | + |
|
| 261 | +void loop() { |
|
| 262 | + server.handleClient(); |
|
| 263 | +} |
|
| ... | ... | \ No newline at end of file |
code-dat/motor-driver-code-dat/esp32-ap-drv8871-pwm-1.ino
| ... | ... | @@ -0,0 +1,276 @@ |
| 1 | +#include <WiFi.h> |
|
| 2 | +#include <WebServer.h> |
|
| 3 | +#include <esp32-hal-ledc.h> |
|
| 4 | + |
|
| 5 | +// ===== WiFi 配置 ===== |
|
| 6 | +const char* ssid = "motor_control"; |
|
| 7 | +const char* password = "electrodragon"; |
|
| 8 | + |
|
| 9 | +// ===== DRV8871 / GPIO 定义 (ESP32) ===== |
|
| 10 | +// DRV8871 has two inputs IN1 and IN2. Apply PWM on one input and keep the other low to set direction. |
|
| 11 | +const int IN1_PIN = 15; // IN1 -> IO15 |
|
| 12 | +const int IN2_PIN = 18; // IN2 -> IO18 |
|
| 13 | + |
|
| 14 | +// LEDC (ESP32 PWM) settings |
|
| 15 | +const int PWM_FREQ = 20000; // 20 kHz PWM frequency |
|
| 16 | +const int PWM_RESOLUTION = 10; // 10-bit resolution -> 0-1023 |
|
| 17 | +const int PWM_CHANNEL1 = 0; // channel for IN1 |
|
| 18 | +const int PWM_CHANNEL2 = 1; // channel for IN2 |
|
| 19 | + |
|
| 20 | +// ===== Web Server ===== |
|
| 21 | +WebServer server(80); |
|
| 22 | + |
|
| 23 | +// ===== HTML Web 控制界面 ===== |
|
| 24 | +const char html_page[] PROGMEM = R"rawliteral( |
|
| 25 | +<!DOCTYPE html><html> |
|
| 26 | +<head> |
|
| 27 | +<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
| 28 | +<title>ESP32 Motor Control (DRV8871)</title> |
|
| 29 | +<style> |
|
| 30 | +body { |
|
| 31 | + font-family: Arial, sans-serif; |
|
| 32 | + max-width: 600px; |
|
| 33 | + margin: 0 auto; |
|
| 34 | + padding: 20px; |
|
| 35 | + background-color: #f0f0f0; |
|
| 36 | +} |
|
| 37 | + |
|
| 38 | +h2 { |
|
| 39 | + text-align: center; |
|
| 40 | + color: #333; |
|
| 41 | +} |
|
| 42 | +.control-panel { |
|
| 43 | + background: white; |
|
| 44 | + padding: 20px; |
|
| 45 | + border-radius: 10px; |
|
| 46 | + box-shadow: 0 2px 10px rgba(0,0,0,0.1); |
|
| 47 | +} |
|
| 48 | +.section { |
|
| 49 | + margin-bottom: 25px; |
|
| 50 | +} |
|
| 51 | +.section-title { |
|
| 52 | + font-weight: bold; |
|
| 53 | + margin-bottom: 10px; |
|
| 54 | + font-size: 18px; |
|
| 55 | + color: #555; |
|
| 56 | +} |
|
| 57 | +.speed-buttons { |
|
| 58 | + display: grid; |
|
| 59 | + grid-template-columns: repeat(6, 1fr); |
|
| 60 | + gap: 8px; |
|
| 61 | + margin-bottom: 10px; |
|
| 62 | +} |
|
| 63 | +.speed-btn { |
|
| 64 | + padding: 15px 10px; |
|
| 65 | + font-size: 16px; |
|
| 66 | + font-weight: bold; |
|
| 67 | + border: 2px solid #4CAF50; |
|
| 68 | + background-color: white; |
|
| 69 | + color: #4CAF50; |
|
| 70 | + border-radius: 8px; |
|
| 71 | + cursor: pointer; |
|
| 72 | + transition: all 0.2s; |
|
| 73 | +} |
|
| 74 | +.speed-btn:active { |
|
| 75 | + background-color: #4CAF50; |
|
| 76 | + color: white; |
|
| 77 | + transform: scale(0.95); |
|
| 78 | +} |
|
| 79 | +.dir-buttons { |
|
| 80 | + display: grid; |
|
| 81 | + grid-template-columns: 1fr 1fr; |
|
| 82 | + gap: 10px; |
|
| 83 | +} |
|
| 84 | +.dir-btn { |
|
| 85 | + padding: 20px; |
|
| 86 | + font-size: 18px; |
|
| 87 | + font-weight: bold; |
|
| 88 | + border: none; |
|
| 89 | + border-radius: 8px; |
|
| 90 | + cursor: pointer; |
|
| 91 | + transition: all 0.2s; |
|
| 92 | +} |
|
| 93 | +.forward-btn { |
|
| 94 | + background-color: #2196F3; |
|
| 95 | + color: white; |
|
| 96 | +} |
|
| 97 | +.reverse-btn { |
|
| 98 | + background-color: #FF9800; |
|
| 99 | + color: white; |
|
| 100 | +} |
|
| 101 | +.dir-btn:active { |
|
| 102 | + transform: scale(0.95); |
|
| 103 | + opacity: 0.8; |
|
| 104 | +} |
|
| 105 | +.stop-btn { |
|
| 106 | + width: 100%; |
|
| 107 | + padding: 20px; |
|
| 108 | + font-size: 20px; |
|
| 109 | + font-weight: bold; |
|
| 110 | + background-color: #f44336; |
|
| 111 | + color: white; |
|
| 112 | + border: none; |
|
| 113 | + border-radius: 8px; |
|
| 114 | + cursor: pointer; |
|
| 115 | + margin-top: 20px; |
|
| 116 | +} |
|
| 117 | +.stop-btn:active { |
|
| 118 | + transform: scale(0.98); |
|
| 119 | + opacity: 0.8; |
|
| 120 | +} |
|
| 121 | +</style> |
|
| 122 | +</head> |
|
| 123 | +<body> |
|
| 124 | +<h2>Motor Control</h2> |
|
| 125 | +<div class="control-panel"> |
|
| 126 | + <div class="section"> |
|
| 127 | + <div class="section-title">Speed Control (0-10)</div> |
|
| 128 | + <div class="speed-buttons"> |
|
| 129 | + <button class="speed-btn" onclick="setSpeed(0)">0</button> |
|
| 130 | + <button class="speed-btn" onclick="setSpeed(1)">1</button> |
|
| 131 | + <button class="speed-btn" onclick="setSpeed(2)">2</button> |
|
| 132 | + <button class="speed-btn" onclick="setSpeed(3)">3</button> |
|
| 133 | + <button class="speed-btn" onclick="setSpeed(4)">4</button> |
|
| 134 | + <button class="speed-btn" onclick="setSpeed(5)">5</button> |
|
| 135 | + <button class="speed-btn" onclick="setSpeed(6)">6</button> |
|
| 136 | + <button class="speed-btn" onclick="setSpeed(7)">7</button> |
|
| 137 | + <button class="speed-btn" onclick="setSpeed(8)">8</button> |
|
| 138 | + <button class="speed-btn" onclick="setSpeed(9)">9</button> |
|
| 139 | + <button class="speed-btn" onclick="setSpeed(10)">10</button> |
|
| 140 | + </div> |
|
| 141 | + </div> |
|
| 142 | + <div class="section"> |
|
| 143 | + <div class="section-title">Direction Control</div> |
|
| 144 | + <div class="dir-buttons"> |
|
| 145 | + <button class="dir-btn forward-btn" onclick="setDirection('fw')">Forward</button> |
|
| 146 | + <button class="dir-btn reverse-btn" onclick="setDirection('rv')">Reverse</button> |
|
| 147 | + </div> |
|
| 148 | + </div> |
|
| 149 | + <button class="stop-btn" onclick="stopMotor()">STOP</button> |
|
| 150 | +</div> |
|
| 151 | +<script> |
|
| 152 | +let currentSpeed = 0; |
|
| 153 | +let currentDir = 'fw'; |
|
| 154 | +function setSpeed(speed) { |
|
| 155 | + currentSpeed = speed; |
|
| 156 | + sendControl(); |
|
| 157 | +} |
|
| 158 | +function setDirection(dir) { |
|
| 159 | + currentDir = dir; |
|
| 160 | + sendControl(); |
|
| 161 | +} |
|
| 162 | +function sendControl() { |
|
| 163 | + let pwmValue = Math.round((currentSpeed / 10) * 1023); |
|
| 164 | + fetch('/control?speed=' + pwmValue + '&dir=' + currentDir) |
|
| 165 | + .then(response => response.text()) |
|
| 166 | + .catch(error => console.error('Error:', error)); |
|
| 167 | +} |
|
| 168 | +function stopMotor() { |
|
| 169 | + currentSpeed = 0; |
|
| 170 | + fetch('/control?speed=0&dir=' + currentDir) |
|
| 171 | + .then(response => response.text()) |
|
| 172 | + .catch(error => console.error('Error:', error)); |
|
| 173 | +} |
|
| 174 | +</script> |
|
| 175 | +</body></html> |
|
| 176 | +)rawliteral"; |
|
| 177 | + |
|
| 178 | +// ===== 处理 Web 请求 ===== |
|
| 179 | +void handleRoot() { |
|
| 180 | + server.send(200, "text/html", html_page); |
|
| 181 | +} |
|
| 182 | + |
|
| 183 | +void handleControl() { |
|
| 184 | + if (server.hasArg("speed")) { |
|
| 185 | + int speed = server.arg("speed").toInt(); // PWM 值 0-1023 |
|
| 186 | + if (speed < 0) speed = 0; |
|
| 187 | + if (speed > 1023) speed = 1023; |
|
| 188 | + |
|
| 189 | + // 根据方向选择将 PWM 输出到 IN1 或 IN2 (DRV8871) |
|
| 190 | + if (server.hasArg("dir")) { |
|
| 191 | + String d = server.arg("dir"); |
|
| 192 | + if (d == "fw") { |
|
| 193 | + // forward: PWM on IN1, IN2 low |
|
| 194 | + ledcWrite(IN1_PIN, speed); |
|
| 195 | + ledcWrite(IN2_PIN, 0); |
|
| 196 | + } else { |
|
| 197 | + // reverse: PWM on IN2, IN1 low |
|
| 198 | + ledcWrite(IN1_PIN, 0); |
|
| 199 | + ledcWrite(IN2_PIN, speed); |
|
| 200 | + } |
|
| 201 | + } else { |
|
| 202 | + // if no dir provided, just set both channels to speed |
|
| 203 | + ledcWrite(IN1_PIN, speed); |
|
| 204 | + ledcWrite(IN2_PIN, speed); |
|
| 205 | + } |
|
| 206 | + } |
|
| 207 | + |
|
| 208 | + // allow stopping regardless of speed/dir args |
|
| 209 | + if (server.hasArg("stop")) { |
|
| 210 | + ledcWrite(IN1_PIN, 0); |
|
| 211 | + ledcWrite(IN2_PIN, 0); |
|
| 212 | + } |
|
| 213 | + |
|
| 214 | + server.sendHeader("Location", "/"); |
|
| 215 | + server.send(302, "text/plain", ""); |
|
| 216 | +} |
|
| 217 | + |
|
| 218 | +// ===== 初始化 ===== |
|
| 219 | +void setup() { |
|
| 220 | + Serial.begin(115200); |
|
| 221 | + delay(100); |
|
| 222 | + Serial.println("ESP32 DRV8871 AP Motor Controller starting..."); |
|
| 223 | + |
|
| 224 | + // Configure LEDC PWM channels and attach pins (ESP32 Arduino Core 3.x API) |
|
| 225 | + ledcAttach(IN1_PIN, PWM_FREQ, PWM_RESOLUTION); |
|
| 226 | + ledcAttach(IN2_PIN, PWM_FREQ, PWM_RESOLUTION); |
|
| 227 | + |
|
| 228 | + pinMode(IN1_PIN, OUTPUT); |
|
| 229 | + pinMode(IN2_PIN, OUTPUT); |
|
| 230 | + |
|
| 231 | + // 默认停止 |
|
| 232 | + ledcWrite(IN1_PIN, 0); |
|
| 233 | + ledcWrite(IN2_PIN, 0); |
|
| 234 | + |
|
| 235 | + // 启动为 WiFi AP 模式,使用固定 IP |
|
| 236 | + Serial.println("Starting WiFi AP..."); |
|
| 237 | + |
|
| 238 | + WiFi.disconnect(true); |
|
| 239 | + WiFi.mode(WIFI_AP); |
|
| 240 | + delay(100); |
|
| 241 | + |
|
| 242 | + bool apStarted = WiFi.softAP(ssid, password); |
|
| 243 | + if (!apStarted) { |
|
| 244 | + Serial.println("AP start failed! Retrying..."); |
|
| 245 | + delay(1000); |
|
| 246 | + WiFi.softAP(ssid, password); |
|
| 247 | + } |
|
| 248 | + |
|
| 249 | + delay(1000); |
|
| 250 | + IPAddress apIP(192, 168, 50, 1); |
|
| 251 | + IPAddress apGateway(192, 168, 50, 1); |
|
| 252 | + IPAddress apSubnet(255, 255, 255, 0); |
|
| 253 | + if (!WiFi.softAPConfig(apIP, apGateway, apSubnet)) { |
|
| 254 | + Serial.println("softAPConfig failed"); |
|
| 255 | + } |
|
| 256 | + |
|
| 257 | + delay(500); |
|
| 258 | + Serial.println(""); |
|
| 259 | + Serial.println("=========================="); |
|
| 260 | + Serial.print("AP SSID: "); |
|
| 261 | + Serial.println(ssid); |
|
| 262 | + Serial.print("AP Password: "); |
|
| 263 | + Serial.println(password); |
|
| 264 | + Serial.print("AP IP: "); |
|
| 265 | + Serial.println(WiFi.softAPIP()); |
|
| 266 | + Serial.println("=========================="); |
|
| 267 | + |
|
| 268 | + // Web 服务 |
|
| 269 | + server.on("/", handleRoot); |
|
| 270 | + server.on("/control", handleControl); |
|
| 271 | + server.begin(); |
|
| 272 | +} |
|
| 273 | + |
|
| 274 | +void loop() { |
|
| 275 | + server.handleClient(); |
|
| 276 | +} |
|
| ... | ... | \ No newline at end of file |