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