Tech-dat/tech-dat.md
... ...
@@ -249,9 +249,9 @@
249 249
250 250
## Tools and Soldering
251 251
252
-- [[soldering-dat]] - [[desoldering-dat]]
252
+- [[fab-PCB-soldering-dat]] - [[fab-PCB-desoldering-dat]]
253 253
254
-- [[PCB-dat]] - [[PCBA-dat]] - [[PCB-design-dat]] - [[PCB-form-dat]] - [[PCB-footprint-dat]]
254
+- [[PCB-dat]] - [[fab-PCBA-dat]] - [[PCB-design-dat]] - [[PCB-form-dat]] - [[PCB-footprint-dat]]
255 255
256 256
- [[fab-tools-dat]] - [[fab-instrument-dat]] - [[fab-workspace-dat]]
257 257
... ...
@@ -260,15 +260,15 @@
260 260
261 261
## Mechnicals
262 262
263
-- [[mechanics-dat]] - [[CAD-dat]] - [[CNC-dat]] - [[screw-dat]]
263
+- [[fab-tools-mechanical-dat]] - [[CAD-dat]] - [[CNC-dat]] - [[screw-dat]]
264 264
265 265
- [[mechanical-structure-dat]] - [[tube-dat]] - [[tube-bend-dat]]
266 266
267 267
- [[openpnp-dat]] - [[3d-printer-dat]] - [[pnp-machine-dat]] - [[glue-dat]] - [[heatsink-dat]]
268 268
269
-- [[3D-dat]] - [[3d-print-dat]]
269
+- [[3D-dat]] - [[fab-3d-print-dat]]
270
+
270 271
271
-- [[mechanic-tools-dat]]
272 272
273 273
## Circuits
274 274
code-dat/projects/ESP8266-WIFI-motor-1.ino
... ...
@@ -1,149 +0,0 @@
1
-#include <ESP8266WiFi.h>
2
-#include <ESP8266WebServer.h>
3
-
4
-// WiFi AP settings (fixed IP)
5
-const char *ssid = "MotorAP";
6
-const char *password = "motorpass"; // set to "" for open AP
7
-IPAddress apIP(192, 168, 4, 1);
8
-IPAddress netMsk(255, 255, 255, 0);
9
-
10
-// Define pins for motor control
11
-// Motor 1
12
-const int M1_IN1 = 4;
13
-const int M1_IN2 = 5;
14
-// Motor 2
15
-const int M2_IN1 = 0;
16
-const int M2_IN2 = 2;
17
-
18
-int motorControl = 50; // 0..100, default mid-point
19
-
20
-ESP8266WebServer server(80);
21
-
22
-void applyMotorControl()
23
-{
24
- // Deadband: treat 40..60 as stop
25
- if (motorControl > 60)
26
- {
27
- // Forward
28
- int motorSpeed = map(motorControl, 61, 100, 0, 255);
29
- motorSpeed = constrain(motorSpeed, 0, 255);
30
-
31
- analogWrite(M1_IN1, motorSpeed);
32
- digitalWrite(M1_IN2, LOW);
33
- analogWrite(M2_IN1, motorSpeed);
34
- digitalWrite(M2_IN2, LOW);
35
- }
36
- else if (motorControl < 40)
37
- {
38
- // Reverse
39
- int motorSpeed = map(motorControl, 0, 39, 255, 0);
40
- motorSpeed = constrain(motorSpeed, 0, 255);
41
-
42
- digitalWrite(M1_IN1, LOW);
43
- analogWrite(M1_IN2, motorSpeed);
44
- digitalWrite(M2_IN1, LOW);
45
- analogWrite(M2_IN2, motorSpeed);
46
- }
47
- else
48
- {
49
- // Stop motors
50
- digitalWrite(M1_IN1, LOW);
51
- digitalWrite(M1_IN2, LOW);
52
- digitalWrite(M2_IN1, LOW);
53
- digitalWrite(M2_IN2, LOW);
54
- }
55
-}
56
-
57
-String pageRoot()
58
-{
59
- String html = "<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
60
- html += "<title>Motor AP Control</title></head><body>";
61
- html += "<h2>Motor Control (0-100)</h2>";
62
- html += "<input type=\"range\" id=\"s\" min=\"0\" max=\"100\" value=\"" + String(motorControl) + "\" oninput=\"update(this.value)\"/>";
63
- html += "<span id=\"v\">" + String(motorControl) + "</span>";
64
- html += "<script>function update(v){document.getElementById('v').innerText=v;fetch('/set?val='+v);}setInterval(function(){fetch('/status').then(r=>r.text()).then(t=>{document.getElementById('s').value=t;document.getElementById('v').innerText=t;});},1000);</script>";
65
- html += "</body></html>";
66
- return html;
67
-}
68
-
69
-void handleRoot()
70
-{
71
- server.send(200, "text/html", pageRoot());
72
-}
73
-
74
-void handleSet()
75
-{
76
- if (!server.hasArg("val"))
77
- {
78
- server.send(400, "text/plain", "missing val");
79
- return;
80
- }
81
- String v = server.arg("val");
82
- int val = v.toInt();
83
- val = constrain(val, 0, 100);
84
- motorControl = val;
85
- applyMotorControl();
86
- server.send(200, "text/plain", String(motorControl));
87
-}
88
-
89
-void handleStatus()
90
-{
91
- server.send(200, "text/plain", String(motorControl));
92
-}
93
-
94
-void setup()
95
-{
96
- // Initialize pins
97
- pinMode(M1_IN1, OUTPUT);
98
- pinMode(M1_IN2, OUTPUT);
99
- pinMode(M2_IN1, OUTPUT);
100
- pinMode(M2_IN2, OUTPUT);
101
-
102
- // Initialize motors to off
103
- digitalWrite(M1_IN1, LOW);
104
- digitalWrite(M1_IN2, LOW);
105
- digitalWrite(M2_IN1, LOW);
106
- digitalWrite(M2_IN2, LOW);
107
-
108
- Serial.begin(115200);
109
- delay(100);
110
-
111
- Serial.println("Test...");
112
- delay(1000);
113
- Serial.println("Test...");
114
- delay(1000);
115
- Serial.println("Test...");
116
- delay(1000);
117
- // Configure AP with fixed IP
118
- WiFi.softAPConfig(apIP, apIP, netMsk);
119
- WiFi.softAP(ssid, password);
120
-
121
- IPAddress myIP = WiFi.softAPIP();
122
- Serial.print("AP IP address: ");
123
- Serial.println(myIP);
124
-
125
- // Configure server routes
126
- server.on("/", handleRoot);
127
- server.on("/set", handleSet);
128
- server.on("/status", handleStatus);
129
- server.begin();
130
- Serial.println("HTTP server started");
131
-
132
- // Ensure PWM range 0-255
133
- analogWriteRange(255);
134
-
135
- // Apply initial motor state
136
- applyMotorControl();
137
-}
138
-
139
-void loop()
140
-{
141
- server.handleClient();
142
- // Optional: keep motor state applied in case other code modifies it
143
- // applyMotorControl();
144
- delay(10);
145
-}
146
-
147
-
148
-
149
-
code-dat/projects/ESP8266-WIFI-motor-1/ESP8266-WIFI-motor-1.ino
... ...
@@ -0,0 +1,283 @@
1
+#include <ESP8266WiFi.h>
2
+#include <ESP8266WebServer.h>
3
+
4
+// WiFi AP settings (fixed IP)
5
+const char *ssid = "MotorAP";
6
+const char *password = "motorpass"; // set to "" for open AP
7
+IPAddress apIP(192, 168, 4, 1);
8
+IPAddress netMsk(255, 255, 255, 0);
9
+
10
+// Define pins for motor control
11
+// Motor 1 (Left)
12
+const int M1_IN1 = 4;
13
+const int M1_IN2 = 5;
14
+// Motor 2 (Right)
15
+const int M2_IN1 = 0;
16
+const int M2_IN2 = 2;
17
+
18
+int carSpeed = 200; // 0..255 (Base Speed)
19
+int minSpeed = 100; // Minimum speed to avoid stalling
20
+int turnDiff = 85; // Increased for more "agility" (sharper turns)
21
+int carAction = 0; // 0: stop, 1: forward, 2: backward, 3: left, 4: right
22
+unsigned long lastCommandTime = 0;
23
+const unsigned long SAFETY_TIMEOUT = 300; // Even tighter for high responsiveness
24
+
25
+int batteryS = 3; // Default 3S (11.1V)
26
+
27
+ESP8266WebServer server(80);
28
+
29
+int getCompensatedSpeed(int baseSpeed) {
30
+ if (baseSpeed == 0) return 0;
31
+ // Simple inverse scaling:
32
+ // If it's 3S (11.1V), we scale DOWN the speed compared to 2S (7.4V)
33
+ // Scale factor = 7.4 / 11.1 = ~0.66
34
+ if (batteryS == 3) {
35
+ return (int)(baseSpeed * 0.66);
36
+ }
37
+ return baseSpeed; // Use raw speed for 2S
38
+}
39
+
40
+void applyMotorControl()
41
+{
42
+ int leftSpeed = 0;
43
+ int rightSpeed = 0;
44
+
45
+ int compensatedSpeed = getCompensatedSpeed(carSpeed);
46
+ int compensatedTurnDiff = getCompensatedSpeed(turnDiff);
47
+
48
+ // Helper to ensure motor gets enough power or is OFF
49
+ auto normalizeSpeed = [&](int s) {
50
+ if (s <= 0) return 0;
51
+ if (s < minSpeed) return minSpeed;
52
+ if (s > 255) return 255;
53
+ return s;
54
+ };
55
+
56
+ switch (carAction)
57
+ {
58
+ case 0: // Stop
59
+ leftSpeed = rightSpeed = 0;
60
+ break;
61
+ case 1: // Forward
62
+ leftSpeed = rightSpeed = compensatedSpeed;
63
+ break;
64
+ case 2: // Backward
65
+ leftSpeed = rightSpeed = -compensatedSpeed;
66
+ break;
67
+ case 3: // Spin Left
68
+ leftSpeed = -compensatedSpeed;
69
+ rightSpeed = compensatedSpeed;
70
+ break;
71
+ case 4: // Spin Right
72
+ leftSpeed = compensatedSpeed;
73
+ rightSpeed = -compensatedSpeed;
74
+ break;
75
+ case 5: // Forward Left (Curve) - Swapped
76
+ leftSpeed = compensatedSpeed + compensatedTurnDiff;
77
+ rightSpeed = compensatedSpeed - compensatedTurnDiff;
78
+ break;
79
+ case 6: // Forward Right (Curve) - Swapped
80
+ leftSpeed = compensatedSpeed - compensatedTurnDiff;
81
+ rightSpeed = compensatedSpeed + compensatedTurnDiff;
82
+ break;
83
+ case 7: // Backward Left (Curve)
84
+ leftSpeed = -(compensatedSpeed + compensatedTurnDiff);
85
+ rightSpeed = -(compensatedSpeed - compensatedTurnDiff);
86
+ break;
87
+ case 8: // Backward Right (Curve)
88
+ leftSpeed = -(compensatedSpeed - compensatedTurnDiff);
89
+ rightSpeed = -(compensatedSpeed + compensatedTurnDiff);
90
+ break;
91
+ default:
92
+ leftSpeed = rightSpeed = 0;
93
+ break;
94
+ }
95
+
96
+ // Apply Left Motor
97
+ int absL = abs(leftSpeed);
98
+ int finalL = normalizeSpeed(absL);
99
+ if (leftSpeed > 0) {
100
+ analogWrite(M1_IN1, finalL);
101
+ digitalWrite(M1_IN2, LOW);
102
+ } else if (leftSpeed < 0) {
103
+ digitalWrite(M1_IN1, LOW);
104
+ analogWrite(M1_IN2, finalL);
105
+ } else {
106
+ digitalWrite(M1_IN1, LOW);
107
+ digitalWrite(M1_IN2, LOW);
108
+ }
109
+
110
+ // Apply Right Motor
111
+ int absR = abs(rightSpeed);
112
+ int finalR = normalizeSpeed(absR);
113
+ if (rightSpeed > 0) {
114
+ analogWrite(M2_IN1, finalR);
115
+ digitalWrite(M2_IN2, LOW);
116
+ } else if (rightSpeed < 0) {
117
+ digitalWrite(M2_IN1, LOW);
118
+ analogWrite(M2_IN2, finalR);
119
+ } else {
120
+ digitalWrite(M2_IN1, LOW);
121
+ digitalWrite(M2_IN2, LOW);
122
+ }
123
+}
124
+
125
+String pageRoot()
126
+{
127
+ String html = "<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">";
128
+ html += "<style>";
129
+ html += "body { font-family: sans-serif; text-align: center; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #f0f0f0; }";
130
+ html += ".btn { width: 80px; height: 80px; margin: 10px; border-radius: 15px; border: none; background: #3498db; color: white; font-size: 24px; font-weight: bold; cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent; box-shadow: 0 4px #2980b9; }";
131
+ html += ".btn:active { background: #2980b9; box-shadow: 0 2px #1c5982; transform: translateY(2px); }";
132
+ html += ".stop-btn { background: #e74c3c; box-shadow: 0 4px #c0392b; }";
133
+ html += ".stop-btn:active { background: #c0392b; box-shadow: 0 2px #962d22; }";
134
+ html += ".row { display: flex; justify-content: center; }";
135
+ html += ".speed-container { margin-top: 20px; width: 80%; }";
136
+ html += "input[type=range] { width: 100%; height: 25px; cursor: pointer; }";
137
+ html += "select { padding: 8px; font-size: 16px; border-radius: 5px; }";
138
+ html += "</style></head><body>";
139
+ html += "<h2>Motor Control</h2>";
140
+
141
+ auto btnHtml = [&](int act, String icon) {
142
+ String s_act = String(act);
143
+ return " <button class='btn' onmousedown=\"setAction(" + s_act + ", event)\" ontouchstart=\"setAction(" + s_act + ", event)\" "
144
+ "onmouseup=\"setAction(0, event)\" onmouseleave=\"setAction(0, event)\" ontouchend=\"setAction(0, event)\" ontouchcancel=\"setAction(0, event)\">" + icon + "</button>";
145
+ };
146
+
147
+ html += "<div class='row'>";
148
+ html += btnHtml(5, "&#8598;");
149
+ html += btnHtml(1, "&#8593;");
150
+ html += btnHtml(6, "&#8599;");
151
+ html += "</div>";
152
+
153
+ html += "<div class='row'>";
154
+ html += btnHtml(3, "&#8592;");
155
+ html += " <button class='btn stop-btn' onclick=\"setAction(0, event)\" ontouchstart=\"setAction(0, event)\">OFF</button>";
156
+ html += btnHtml(4, "&#8594;");
157
+ html += "</div>";
158
+
159
+ html += "<div class='row'>";
160
+ html += btnHtml(7, "&#8601;");
161
+ html += btnHtml(2, "&#8595;");
162
+ html += btnHtml(8, "&#8600;");
163
+ html += "</div>";
164
+
165
+ html += "<div class='speed-container'>";
166
+ html += " Speed: <span id='v'>" + String(carSpeed) + "</span>";
167
+ html += " <input type='range' id='s' min='100' max='255' value='" + String(carSpeed) + "' oninput='setSpeed(this.value)' />";
168
+ html += "</div>";
169
+
170
+ html += "<div class='speed-container'>";
171
+ html += " Battery Mode: <select onchange=\"setBattery(this.value)\">";
172
+ html += " <option value='2'" + String(batteryS == 2 ? " selected" : "") + ">2S (7.4V)</option>";
173
+ html += " <option value='3'" + String(batteryS == 3 ? " selected" : "") + ">3S (11.1V)</option>";
174
+ html += " </select>";
175
+ html += "</div>";
176
+
177
+ html += "<script>";
178
+ html += "let lastA = -1; let lastS = -1;";
179
+ html += "function setAction(a, e, f) { if(e) { e.preventDefault(); } if(!f && a == lastA && a != 0) return; lastA = a; fetch('/set?action=' + a).catch(e => console.error(e)); }";
180
+ html += "function setSpeed(s) { if(s == lastS) return; lastS = s; document.getElementById('v').innerText = s; fetch('/set?speed=' + s).catch(e => console.error(e)); }";
181
+ html += "function setBattery(b) { fetch('/set?batt=' + b); }";
182
+ html += "setInterval(() => { if(lastA > 0) setAction(lastA, null, true); }, 100);"; // "Pro" agility: 100ms heartbeat
183
+ html += "</script></body></html>";
184
+ return html;
185
+}
186
+
187
+void handleRoot()
188
+{
189
+ server.send(200, "text/html", pageRoot());
190
+}
191
+
192
+void handleSet()
193
+{
194
+ server.client().setNoDelay(true); // Disable Nagle's algorithm for faster response
195
+ lastCommandTime = millis(); // Refresh safety watchdog
196
+ if (server.hasArg("action"))
197
+ {
198
+ carAction = server.arg("action").toInt();
199
+ }
200
+ if (server.hasArg("speed"))
201
+ {
202
+ carSpeed = server.arg("speed").toInt();
203
+ carSpeed = constrain(carSpeed, 0, 255);
204
+ }
205
+ if (server.hasArg("batt"))
206
+ {
207
+ batteryS = server.arg("batt").toInt();
208
+ }
209
+ applyMotorControl();
210
+ server.send(200, "text/plain", "OK");
211
+}
212
+
213
+void handleStatus()
214
+{
215
+ String status = "Action: " + String(carAction) + ", Speed: " + String(carSpeed) + ", Batt: " + String(batteryS) + "S";
216
+ server.send(200, "text/plain", status);
217
+}
218
+
219
+void setup()
220
+{
221
+ // Initialize pins
222
+ pinMode(M1_IN1, OUTPUT);
223
+ pinMode(M1_IN2, OUTPUT);
224
+ pinMode(M2_IN1, OUTPUT);
225
+ pinMode(M2_IN2, OUTPUT);
226
+
227
+ // Initialize motors to off
228
+ digitalWrite(M1_IN1, LOW);
229
+ digitalWrite(M1_IN2, LOW);
230
+ digitalWrite(M2_IN1, LOW);
231
+ digitalWrite(M2_IN2, LOW);
232
+
233
+ // Set PWM frequency for DRV8871 (supports up to 200kHz, but 20kHz is standard for silence and efficiency)
234
+ analogWriteFreq(20000);
235
+
236
+ Serial.begin(115200);
237
+ delay(100);
238
+
239
+ Serial.println("Test...");
240
+ delay(1000);
241
+ Serial.println("Test...");
242
+ delay(1000);
243
+ Serial.println("Test...");
244
+ delay(1000);
245
+ // Configure AP with fixed IP
246
+ WiFi.softAPConfig(apIP, apIP, netMsk);
247
+ WiFi.softAP(ssid, password);
248
+
249
+ IPAddress myIP = WiFi.softAPIP();
250
+ Serial.print("AP IP address: ");
251
+ Serial.println(myIP);
252
+
253
+ // Configure server routes
254
+ server.on("/", handleRoot);
255
+ server.on("/set", handleSet);
256
+ server.on("/status", handleStatus);
257
+ server.begin();
258
+ Serial.println("HTTP server started");
259
+
260
+ // Ensure PWM range 0-255
261
+ analogWriteRange(255);
262
+
263
+ // Apply initial motor state
264
+ applyMotorControl();
265
+}
266
+
267
+void loop()
268
+{
269
+ server.handleClient();
270
+
271
+ // Safety Watchdog: Stop motors if no command received for > 1 second
272
+ if (carAction != 0 && (millis() - lastCommandTime > SAFETY_TIMEOUT)) {
273
+ carAction = 0;
274
+ applyMotorControl();
275
+ Serial.println("Safety Timeout: Motors stopped");
276
+ }
277
+
278
+ delay(10);
279
+}
280
+
281
+
282
+
283
+
code-dat/projects/ESP8266-WIFI-motor-1/features.md
... ...
@@ -0,0 +1,32 @@
1
+# ESP8266 WiFi Motor Chassis Features
2
+
3
+## 1. Directional Control (9-Way)
4
+- **8 Directions**: Full support for Forward, Backward, Left, Right, and all four diagonal directions (Forward-Left, Forward-Right, Backward-Left, Backward-Right).
5
+- **Dedicated Stop**: Immediate halt on button release and a centralized "OFF" emergency stop button.
6
+
7
+## 2. Advanced Motor Driving
8
+- **Differential Steering**: Implements $Base \pm Offset$ logic for smooth, proportional turning during diagonal movement and pivot rotation.
9
+- **Stall Protection**: Enforces a `minSpeed` floor (100 PWM) to ensure motors maintain torque at low speeds.
10
+- **PWM Optimization**: Optimized for high-frequency (20kHz) PWM modulation for smooth speed control.
11
+
12
+## 3. Safety & Reliability
13
+- **Software Watchdog**: A hardware-safe timeout system stops the motors if no command is received within **500ms**, preventing runaway cars in case of WiFi disconnect.
14
+- **Active Heartbeat**: The client browser sends a "keep-alive" signal every **200ms** while any button is held, ensuring seamless motion through minor signal jitters.
15
+- **Interface Sanity**: Prevents "ghost" presses with event `preventDefault()` and stops motors automatically if the browser window loses focus (`blur` event).
16
+
17
+## 4. Power Management
18
+- **Battery Compensation**: Selectable modes for **2S (7.4V)** and **3S (11.1V)** battery packs.
19
+- **Voltage Scaling**: Automatically reduces PWM duty cycle in 3S mode (scaling factor $\approx 0.66$) to match 2S speed levels and protect motors from over-voltage.
20
+
21
+## 5. Web Interface & Connectivity
22
+- **Standalone Hotspot**: Creates its own WiFi Access Point (`MotorAP`) for direct control without an external router.
23
+- **Responsive Web UI**: A mobile-first controller interface with large touch-friendly buttons and a real-time speed slider.
24
+- **Low Latency**: Lightweight HTTP GET handlers for near-instant control responses.
25
+
26
+## 6. Technical Specifications (Default)
27
+- **Controller**: ESP8266
28
+- **Motor Pins**:
29
+ - Left (M1): Pins 4, 5
30
+ - Right (M2): Pins 0, 2
31
+- **Default Speed**: 200/255
32
+- **Safety Window**: 500ms