wifi car
This commit is contained in:
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
* Simple car controlled by any wifi device with browser.
|
||||
* Nodemcu with L293D driver and 6V battery pack.
|
||||
* Nodemcu will act as AccessPoint.
|
||||
* After connecting to AP, open 192.168.4.1
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
|
||||
#define IN1 D1 // left motor
|
||||
#define IN2 D2 // left motor
|
||||
#define IN3 D5 // right motor
|
||||
#define IN4 D6 // right motor
|
||||
|
||||
const char* ssid = "RobotV1";
|
||||
const char* password = "12345678";
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
|
||||
|
||||
// =============================
|
||||
// Motor control
|
||||
// =============================
|
||||
|
||||
void leftForward() {
|
||||
digitalWrite(IN1, HIGH);
|
||||
digitalWrite(IN2, LOW);
|
||||
}
|
||||
|
||||
void leftBackward() {
|
||||
digitalWrite(IN1, LOW);
|
||||
digitalWrite(IN2, HIGH);
|
||||
}
|
||||
|
||||
void rightForward() {
|
||||
digitalWrite(IN3, HIGH);
|
||||
digitalWrite(IN4, LOW);
|
||||
}
|
||||
|
||||
void rightBackward() {
|
||||
digitalWrite(IN3, LOW);
|
||||
digitalWrite(IN4, HIGH);
|
||||
}
|
||||
|
||||
void stopRobot() {
|
||||
digitalWrite(IN1, LOW);
|
||||
digitalWrite(IN2, LOW);
|
||||
digitalWrite(IN3, LOW);
|
||||
digitalWrite(IN4, LOW);
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// Drive
|
||||
// =============================
|
||||
|
||||
void drive(int left, int right) {
|
||||
|
||||
// Left motor
|
||||
if (left > 0) {
|
||||
leftForward();
|
||||
}
|
||||
else if (left < 0) {
|
||||
leftBackward();
|
||||
}
|
||||
else {
|
||||
digitalWrite(IN1, LOW);
|
||||
digitalWrite(IN2, LOW);
|
||||
}
|
||||
|
||||
// Right motor
|
||||
if (right > 0) {
|
||||
rightForward();
|
||||
}
|
||||
else if (right < 0) {
|
||||
rightBackward();
|
||||
}
|
||||
else {
|
||||
digitalWrite(IN3, LOW);
|
||||
digitalWrite(IN4, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// Web page
|
||||
// =============================
|
||||
|
||||
const char webpage[] PROGMEM = R"rawliteral(
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
|
||||
<meta name="viewport"
|
||||
content="width=device-width,
|
||||
initial-scale=1.0,
|
||||
maximum-scale=1.0,
|
||||
user-scalable=no">
|
||||
|
||||
<title>Robot Control</title>
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #111;
|
||||
color: white;
|
||||
font-family: Arial;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
#joystick {
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
|
||||
background: #333;
|
||||
|
||||
border: 3px solid #555;
|
||||
border-radius: 50%;
|
||||
|
||||
margin: 40px auto;
|
||||
|
||||
position: relative;
|
||||
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
#stick {
|
||||
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
|
||||
background: #2196F3;
|
||||
|
||||
border-radius: 50%;
|
||||
|
||||
position: absolute;
|
||||
|
||||
left: 90px;
|
||||
top: 90px;
|
||||
|
||||
box-shadow: 0 0 20px #2196F3;
|
||||
}
|
||||
|
||||
#status {
|
||||
font-size: 20px;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<h2>🤖 Robot</h2>
|
||||
|
||||
<div id="joystick">
|
||||
|
||||
<div id="stick"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="status">
|
||||
STOP
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
const joystick = document.getElementById("joystick");
|
||||
const stick = document.getElementById("stick");
|
||||
const status = document.getElementById("status");
|
||||
|
||||
let active = false;
|
||||
|
||||
const center = 140;
|
||||
const maxDistance = 90;
|
||||
|
||||
|
||||
// --------------------------------
|
||||
// Send motor command
|
||||
// --------------------------------
|
||||
|
||||
function sendCommand(x, y) {
|
||||
|
||||
// Up = positive
|
||||
let forward = -y / maxDistance;
|
||||
let turn = x / maxDistance;
|
||||
|
||||
// Tank steering
|
||||
|
||||
let left = forward + turn;
|
||||
let right = forward - turn;
|
||||
|
||||
// Limit values
|
||||
|
||||
left = Math.max(-1, Math.min(1, left));
|
||||
right = Math.max(-1, Math.min(1, right));
|
||||
|
||||
|
||||
let leftCmd = Math.round(left * 100);
|
||||
let rightCmd = Math.round(right * 100);
|
||||
|
||||
|
||||
fetch(
|
||||
"/drive?left=" +
|
||||
leftCmd +
|
||||
"&right=" +
|
||||
rightCmd
|
||||
);
|
||||
|
||||
|
||||
// Status
|
||||
|
||||
if (Math.abs(leftCmd) < 5 &&
|
||||
Math.abs(rightCmd) < 5) {
|
||||
|
||||
status.innerText = "STOP";
|
||||
|
||||
}
|
||||
else if (forward > 0.3 &&
|
||||
Math.abs(turn) < 0.3) {
|
||||
|
||||
status.innerText = "FORWARD";
|
||||
|
||||
}
|
||||
else if (forward < -0.3 &&
|
||||
Math.abs(turn) < 0.3) {
|
||||
|
||||
status.innerText = "BACKWARD";
|
||||
|
||||
}
|
||||
else if (turn > 0.3) {
|
||||
|
||||
status.innerText = "RIGHT";
|
||||
|
||||
}
|
||||
else if (turn < -0.3) {
|
||||
|
||||
status.innerText = "LEFT";
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
status.innerText = "DRIVING";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------
|
||||
// Move joystick
|
||||
// --------------------------------
|
||||
|
||||
function moveStick(clientX, clientY) {
|
||||
|
||||
const rect = joystick.getBoundingClientRect();
|
||||
|
||||
let x = clientX - rect.left - center;
|
||||
let y = clientY - rect.top - center;
|
||||
|
||||
|
||||
let distance =
|
||||
Math.sqrt(x * x + y * y);
|
||||
|
||||
|
||||
if (distance > maxDistance) {
|
||||
|
||||
x =
|
||||
x / distance *
|
||||
maxDistance;
|
||||
|
||||
y =
|
||||
y / distance *
|
||||
maxDistance;
|
||||
}
|
||||
|
||||
|
||||
stick.style.left =
|
||||
(center - 50 + x) + "px";
|
||||
|
||||
stick.style.top =
|
||||
(center - 50 + y) + "px";
|
||||
|
||||
|
||||
sendCommand(x, y);
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------
|
||||
// Reset joystick
|
||||
// --------------------------------
|
||||
|
||||
function resetStick() {
|
||||
|
||||
active = false;
|
||||
|
||||
|
||||
stick.style.left = "90px";
|
||||
stick.style.top = "90px";
|
||||
|
||||
|
||||
fetch("/stop");
|
||||
|
||||
|
||||
status.innerText = "STOP";
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------
|
||||
// Touch
|
||||
// --------------------------------
|
||||
|
||||
joystick.addEventListener(
|
||||
"touchstart",
|
||||
function(e) {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
active = true;
|
||||
|
||||
const touch = e.touches[0];
|
||||
|
||||
moveStick(
|
||||
touch.clientX,
|
||||
touch.clientY
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
joystick.addEventListener(
|
||||
"touchmove",
|
||||
function(e) {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
const touch = e.touches[0];
|
||||
|
||||
moveStick(
|
||||
touch.clientX,
|
||||
touch.clientY
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
joystick.addEventListener(
|
||||
"touchend",
|
||||
function(e) {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
resetStick();
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// --------------------------------
|
||||
// Mouse support
|
||||
// --------------------------------
|
||||
|
||||
joystick.addEventListener(
|
||||
"mousedown",
|
||||
function(e) {
|
||||
|
||||
active = true;
|
||||
|
||||
moveStick(
|
||||
e.clientX,
|
||||
e.clientY
|
||||
);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
document.addEventListener(
|
||||
"mousemove",
|
||||
function(e) {
|
||||
|
||||
if (active) {
|
||||
|
||||
moveStick(
|
||||
e.clientX,
|
||||
e.clientY
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
document.addEventListener(
|
||||
"mouseup",
|
||||
function() {
|
||||
|
||||
if (active)
|
||||
resetStick();
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
)rawliteral";
|
||||
|
||||
|
||||
// =============================
|
||||
// Web server
|
||||
// =============================
|
||||
|
||||
void handleRoot() {
|
||||
|
||||
server.send(
|
||||
200,
|
||||
"text/html",
|
||||
webpage
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void handleDrive() {
|
||||
|
||||
int left =
|
||||
server.arg("left").toInt();
|
||||
|
||||
int right =
|
||||
server.arg("right").toInt();
|
||||
|
||||
|
||||
drive(left, right);
|
||||
|
||||
|
||||
server.send(
|
||||
200,
|
||||
"text/plain",
|
||||
"OK"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void handleStop() {
|
||||
|
||||
stopRobot();
|
||||
|
||||
server.send(
|
||||
200,
|
||||
"text/plain",
|
||||
"STOP"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// Setup
|
||||
// =============================
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
|
||||
pinMode(IN1, OUTPUT);
|
||||
pinMode(IN2, OUTPUT);
|
||||
|
||||
pinMode(IN3, OUTPUT);
|
||||
pinMode(IN4, OUTPUT);
|
||||
|
||||
|
||||
stopRobot();
|
||||
|
||||
|
||||
// Create WiFi network
|
||||
|
||||
WiFi.softAP(
|
||||
ssid,
|
||||
password
|
||||
);
|
||||
|
||||
|
||||
Serial.println();
|
||||
Serial.println("Robot WiFi started");
|
||||
|
||||
Serial.print("IP address: ");
|
||||
|
||||
Serial.println(
|
||||
WiFi.softAPIP()
|
||||
);
|
||||
|
||||
|
||||
// Web server routes
|
||||
|
||||
server.on(
|
||||
"/",
|
||||
handleRoot
|
||||
);
|
||||
|
||||
server.on(
|
||||
"/drive",
|
||||
handleDrive
|
||||
);
|
||||
|
||||
server.on(
|
||||
"/stop",
|
||||
handleStop
|
||||
);
|
||||
|
||||
|
||||
server.begin();
|
||||
|
||||
Serial.println(
|
||||
"Web server started"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// =============================
|
||||
// Main loop
|
||||
// =============================
|
||||
|
||||
void loop() {
|
||||
|
||||
server.handleClient();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user