1#include <WiFi.h>
2#include <DNSServer.h>
3#include <WebServer.h>
4
5// --- Wi-Fi & Captive Portal Settings ---
6const char* ssid = " Rover";
7const char* password = "kabya123";
8const byte DNS_PORT = 53;
9IPAddress apIP(192, 168, 4, 1);
10DNSServer dnsServer;
11WebServer server(80);
12
13// --- L298N #1 (Motors 1 & 2) ---
14#define ENA_m1 13
15#define IN_11 12
16#define IN_12 14
17#define IN_13 22
18#define IN_14 23
19#define ENB_m1 21
20
21// --- L298N #2 (Motors 3 & 4) ---
22#define ENA_m2 19
23#define IN_21 18
24#define IN_22 5
25#define IN_23 17
26#define IN_24 16
27#define ENB_m2 4
28
29// --- VOLATILE GLOBALS FOR RTOS CROSS-CORE COMMUNICATION ---
30volatile char currentCommand = 'S';
31volatile int speedCar = 200;
32volatile unsigned long lastCommandTime = 0;
33
34const int speed_Coeff = 2; // Slows down inner wheels on diagonal turns
35const int SAFETY_TIMEOUT = 150; // Strict 150ms timeout for instant stopping
36
37// RTOS Task Handle
38TaskHandle_t NetworkTask;
39
40// =========================================================================
41// HTML & JS for the Web Interface (Old Design Restored + Diagonals + Zero Lag)
42// =========================================================================
43const char index_html[] PROGMEM = R"rawliteral(
44<!DOCTYPE html>
45<html lang="en">
46<head>
47 <meta charset="UTF-8">
48 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
49 <title>Rover Control</title>
50 <style>
51 * { box-sizing: border-box; touch-action: manipulation; user-select: none; -webkit-user-select: none; }
52 body { margin: 0; background: #222; color: #fff; font-family: sans-serif; display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
53 .header { background: #111; text-align: center; padding: 10px; font-size: 1.2rem; font-weight: bold; border-bottom: 1px solid #444;}
54
55 .dashboard { flex: 1; display: flex; justify-content: space-between; align-items: center; padding: 10px; gap: 10px;}
56
57 .pad-container { display: flex; flex-direction: column; gap: 10px; width: 22%; }
58 .pad-container.horizontal { flex-direction: row; width: 35%; justify-content: center;}
59
60
61 .diag-container { display: flex; flex-direction: column; gap: 10px; width: 25%; }
62 .diag-row { display: flex; gap: 10px; height: 100%;}
63
64 button {
65 background: #444; color: white; border: 2px solid #555; border-radius: 8px;
66 font-size: 35px; padding: 25px 0; cursor: pointer; width: 100%;
67 display: flex; justify-content: center; align-items: center;
68 }
69 button:active { background: #00bcd4; border-color: #00bcd4; color: #000;}
70 .btn-green { background: #2e7d32; border-color: #1b5e20; }
71 .btn-green:active { background: #4caf50; border-color: #4caf50; }
72
73 .diag-btn { font-size: 28px; padding: 15px 0; }
74
75 .slider-container { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; width: 15%;}
76 input[type=range] { -webkit-appearance: none; width: 200px; height: 30px; background: #555; border-radius: 5px; outline: none; transform: rotate(-90deg); margin: 80px 0;}
77 input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 40px; height: 40px; background: #00bcd4; cursor: pointer;}
78
79 .status-bar { text-align: center; padding: 10px; background: #000; font-family: monospace; font-size: 14px; border-top: 1px solid #444;}
80 </style>
81</head>
82<body>
83 <div class="header">STRICT CONTROL ROVER</div>
84
85 <div class="dashboard">
86 <div class="pad-container">
87 <button class="btn-green" onpointerdown="setCmd('F')" onpointerup="setCmd('S')" onpointerleave="setCmd('S')" onpointercancel="setCmd('S')">▲</button>
88 <button class="btn-green" onpointerdown="setCmd('B')" onpointerup="setCmd('S')" onpointerleave="setCmd('S')" onpointercancel="setCmd('S')">▼</button>
89 </div>
90
91 <div class="diag-container">
92 <div class="diag-row">
93 <button class="diag-btn" onpointerdown="setCmd('G')" onpointerup="setCmd('S')" onpointerleave="setCmd('S')" onpointercancel="setCmd('S')">↖</button>
94 <button class="diag-btn" onpointerdown="setCmd('I')" onpointerup="setCmd('S')" onpointerleave="setCmd('S')" onpointercancel="setCmd('S')">↗</button>
95 </div>
96 <div class="diag-row">
97 <button class="diag-btn" onpointerdown="setCmd('H')" onpointerup="setCmd('S')" onpointerleave="setCmd('S')" onpointercancel="setCmd('S')">↙</button>
98 <button class="diag-btn" onpointerdown="setCmd('J')" onpointerup="setCmd('S')" onpointerleave="setCmd('S')" onpointercancel="setCmd('S')">↘</button>
99 </div>
100 </div>
101
102 <div class="slider-container">
103 <input type="range" id="speedSlider" min="0" max="255" value="200" oninput="updateSpeed(this.value)">
104 </div>
105
106 <div class="pad-container horizontal">
107 <button onpointerdown="setCmd('L')" onpointerup="setCmd('S')" onpointerleave="setCmd('S')" onpointercancel="setCmd('S')">◀</button>
108 <button onpointerdown="setCmd('R')" onpointerup="setCmd('S')" onpointerleave="setCmd('S')" onpointercancel="setCmd('S')">▶</button>
109 </div>
110 </div>
111
112 <div class="status-bar" id="statusText">CMD: S | SPD: 200</div>
113
114 <script>
115 let currentCmd = 'S';
116 let currentSpeed = 200;
117 let isFetching = false;
118 const statusText = document.getElementById('statusText');
119
120 function setCmd(c) {
121 if (currentCmd !== c) {
122 currentCmd = c;
123 statusText.innerText = `CMD: ${c} | SPD: ${currentSpeed}`;
124 sendData(true);
125 }
126 }
127
128 function updateSpeed(val) {
129 currentSpeed = val;
130 statusText.innerText = `CMD: ${currentCmd} | SPD: ${currentSpeed}`;
131 }
132
133 function sendData(isPriority = false) {
134 if (isFetching && !isPriority && currentCmd !== 'S') return;
135
136 isFetching = true;
137 fetch(`/cmd?c=${currentCmd}&s=${currentSpeed}`)
138 .then(() => isFetching = false)
139 .catch(() => isFetching = false);
140 }
141
142 setInterval(() => {
143 if (currentCmd !== 'S') {
144 sendData(false);
145 }
146 }, 100);
147 </script>
148</body>
149</html>
150)rawliteral";
151
152// =========================================================================
153// Web Server Request Handlers
154// =========================================================================
155void handleRoot() {
156 server.send(200, "text/html", index_html);
157}
158
159void handleCommand() {
160 if (server.hasArg("c") && server.hasArg("s")) {
161 currentCommand = server.arg("c").charAt(0);
162 speedCar = server.arg("s").toInt();
163 lastCommandTime = millis();
164 server.send(200, "text/plain", "OK");
165 } else {
166 server.send(400, "text/plain", "BAD");
167 }
168}
169
170// =========================================================================
171// CORE 0 TASK: Network Management Only
172// =========================================================================
173void networkTaskCode( void * pvParameters ){
174 for(;;){
175 dnsServer.processNextRequest();
176 server.handleClient();
177 vTaskDelay(10 / portTICK_PERIOD_MS);
178 }
179}
180
181// =========================================================================
182// Setup
183// =========================================================================
184void setup() {
185 pinMode(ENA_m1, OUTPUT); pinMode(ENB_m1, OUTPUT);
186 pinMode(ENA_m2, OUTPUT); pinMode(ENB_m2, OUTPUT);
187 pinMode(IN_11, OUTPUT); pinMode(IN_12, OUTPUT);
188 pinMode(IN_13, OUTPUT); pinMode(IN_14, OUTPUT);
189 pinMode(IN_21, OUTPUT); pinMode(IN_22, OUTPUT);
190 pinMode(IN_23, OUTPUT); pinMode(IN_24, OUTPUT);
191 stopRobot();
192
193 WiFi.setSleep(false);
194 WiFi.mode(WIFI_AP);
195 WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
196 WiFi.softAP(ssid, password);
197
198 dnsServer.start(DNS_PORT, "*", apIP);
199 server.on("/", handleRoot);
200 server.on("/cmd", handleCommand);
201 server.onNotFound([]() {
202 server.sendHeader("Location", String("http://") + apIP.toString(), true);
203 server.send(302, "text/plain", "");
204 });
205 server.begin();
206
207 xTaskCreatePinnedToCore(
208 networkTaskCode,
209 "NetworkTask",
210 10000,
211 NULL,
212 1,
213 &NetworkTask,
214 0);
215}
216
217// =========================================================================
218// CORE 1 TASK (Standard Loop): Hardware Control Only
219// =========================================================================
220void loop() {
221 if (millis() - lastCommandTime > SAFETY_TIMEOUT && currentCommand != 'S') {
222 currentCommand = 'S';
223 }
224
225 switch (currentCommand) {
226 case 'F': goAhead(); break;
227 case 'B': goBack(); break;
228 case 'L': goLeft(); break;
229 case 'R': goRight(); break;
230 case 'I': goAheadRight(); break;
231 case 'G': goAheadLeft(); break;
232 case 'J': goBackRight(); break;
233 case 'H': goBackLeft(); break;
234 case 'S': stopRobot(); break;
235 }
236
237 vTaskDelay(10 / portTICK_PERIOD_MS);
238}
239
240// =========================================================================
241// Motor Control Functions
242// =========================================================================
243void goAhead(){
244 digitalWrite(IN_11, HIGH); digitalWrite(IN_12, LOW); analogWrite(ENA_m1, speedCar);
245 digitalWrite(IN_13, LOW); digitalWrite(IN_14, HIGH); analogWrite(ENB_m1, speedCar);
246 digitalWrite(IN_21, LOW); digitalWrite(IN_22, HIGH); analogWrite(ENA_m2, speedCar);
247 digitalWrite(IN_23, HIGH); digitalWrite(IN_24, LOW); analogWrite(ENB_m2, speedCar);
248}
249
250void goBack(){
251 digitalWrite(IN_11, LOW); digitalWrite(IN_12, HIGH); analogWrite(ENA_m1, speedCar);
252 digitalWrite(IN_13, HIGH); digitalWrite(IN_14, LOW); analogWrite(ENB_m1, speedCar);
253 digitalWrite(IN_21, HIGH); digitalWrite(IN_22, LOW); analogWrite(ENA_m2, speedCar);
254 digitalWrite(IN_23, LOW); digitalWrite(IN_24, HIGH); analogWrite(ENB_m2, speedCar);
255}
256
257void goRight(){
258 int turnSpeed = (speedCar * 3) / 4;
259 digitalWrite(IN_11, LOW); digitalWrite(IN_12, HIGH); analogWrite(ENA_m1, turnSpeed);
260 digitalWrite(IN_13, HIGH); digitalWrite(IN_14, LOW); analogWrite(ENB_m1, turnSpeed);
261 digitalWrite(IN_21, LOW); digitalWrite(IN_22, HIGH); analogWrite(ENA_m2, turnSpeed);
262 digitalWrite(IN_23, HIGH); digitalWrite(IN_24, LOW); analogWrite(ENB_m2, turnSpeed);
263}
264
265void goLeft(){
266 int turnSpeed = (speedCar * 3) / 4;
267 digitalWrite(IN_11, HIGH); digitalWrite(IN_12, LOW); analogWrite(ENA_m1, turnSpeed);
268 digitalWrite(IN_13, LOW); digitalWrite(IN_14, HIGH); analogWrite(ENB_m1, turnSpeed);
269 digitalWrite(IN_21, HIGH); digitalWrite(IN_22, LOW); analogWrite(ENA_m2, turnSpeed);
270 digitalWrite(IN_23, LOW); digitalWrite(IN_24, HIGH); analogWrite(ENB_m2, turnSpeed);
271}
272
273void goAheadRight(){
274 digitalWrite(IN_11, HIGH); digitalWrite(IN_12, LOW); analogWrite(ENA_m1, speedCar/speed_Coeff);
275 digitalWrite(IN_13, LOW); digitalWrite(IN_14, HIGH); analogWrite(ENB_m1, speedCar/speed_Coeff);
276 digitalWrite(IN_21, LOW); digitalWrite(IN_22, HIGH); analogWrite(ENA_m2, speedCar);
277 digitalWrite(IN_23, HIGH); digitalWrite(IN_24, LOW); analogWrite(ENB_m2, speedCar);
278}
279
280void goAheadLeft(){
281 digitalWrite(IN_11, HIGH); digitalWrite(IN_12, LOW); analogWrite(ENA_m1, speedCar);
282 digitalWrite(IN_13, LOW); digitalWrite(IN_14, HIGH); analogWrite(ENB_m1, speedCar);
283 digitalWrite(IN_21, LOW); digitalWrite(IN_22, HIGH); analogWrite(ENA_m2, speedCar/speed_Coeff);
284 digitalWrite(IN_23, HIGH); digitalWrite(IN_24, LOW); analogWrite(ENB_m2, speedCar/speed_Coeff);
285}
286
287void goBackRight(){
288 digitalWrite(IN_11, LOW); digitalWrite(IN_12, HIGH); analogWrite(ENA_m1, speedCar/speed_Coeff);
289 digitalWrite(IN_13, HIGH); digitalWrite(IN_14, LOW); analogWrite(ENB_m1, speedCar/speed_Coeff);
290 digitalWrite(IN_21, HIGH); digitalWrite(IN_22, LOW); analogWrite(ENA_m2, speedCar);
291 digitalWrite(IN_23, LOW); digitalWrite(IN_24, HIGH); analogWrite(ENB_m2, speedCar);
292}
293
294void goBackLeft(){
295 digitalWrite(IN_11, LOW); digitalWrite(IN_12, HIGH); analogWrite(ENA_m1, speedCar);
296 digitalWrite(IN_13, HIGH); digitalWrite(IN_14, LOW); analogWrite(ENB_m1, speedCar);
297 digitalWrite(IN_21, HIGH); digitalWrite(IN_22, LOW); analogWrite(ENA_m2, speedCar/speed_Coeff);
298 digitalWrite(IN_23, LOW); digitalWrite(IN_24, HIGH); analogWrite(ENB_m2, speedCar/speed_Coeff);
299}
300
301void stopRobot(){
302 digitalWrite(IN_11, LOW); digitalWrite(IN_12, LOW); analogWrite(ENA_m1, 0);
303 digitalWrite(IN_13, LOW); digitalWrite(IN_14, LOW); analogWrite(ENB_m1, 0);
304 digitalWrite(IN_21, LOW); digitalWrite(IN_22, LOW); analogWrite(ENA_m2, 0);
305 digitalWrite(IN_23, LOW); digitalWrite(IN_24, LOW); analogWrite(ENB_m2, 0);
306}