Mr. Seng Theara
By the end of the lesson, student will be able to:
Understand how a joystick works electrically
Explain pull-up button logic
Read analog and digital signals
Map controller inputs to robot movements
Design basic control logic for robots
By the end of the lesson, student will be able to:
Explain what a web server is
Understand how ESP32 acts as a web server
Create a simple web page hosted by ESP32
Control robot movement from a browser
Understand HTTP request flow
Debug basic network problems
4 Buttons
Joystick
Joystick
Joystick
Web Browser
Browser sends HTTP request.
ESP32 receives it
ESP32 executes function
Robot
Motor move
You click button
Client
Server
4 Buttons
Joystick
4 Buttons
Joystick
A web server is a system that:
Listens for requests from clients
Processes those requests
Sends back responses (usually HTML)
A web server is simply a device that waits for someone to ask for something.
4 Buttons
A web server has 3 responsibilities:
1. Listen:
2. Understand Request
3. Send Response
It waits for incoming connections.
WiFiServer server(80);
Port 80 is opened
Server waits
When client sends: GET /forward HTTP/1.1
Read this text
Interpret it
Decide what to do
Server
Server replies: HTTP/1.1 200 OK
Content-Type: text/html
Then sends HTML page.
4 Buttons
Joystick
4 Buttons
Joystick
HTTP(HyperText Transfer Protocol) is a protocol that defines:
How a client sends a request
How a server sends a response
Client → HTTP Request
Server → HTTP Response
GET /forward HTTP/1.1
Host: 192.168.1.105
HTTP/1.1 200 OK
Content-Type: text/html
WiFi Connection Process:
1. ESP32 starts WiFi
2. It tries to connect to your router
3. Router checks password
4. Router assigns IP address automatically (DHCP)
5. ESP32 prints its IP
#include <WiFi.h>
const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";
void setup() {
Serial.begin(115200);
Serial.println("Connecting to WiFi...");
// Start WiFi connection
WiFi.begin(ssid, password);
// Wait until connected
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected to WiFi!");
// Print assigned IP address (DHCP)
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}
void loop() {
}4 Buttons
Joystick
4 Buttons
Joystick
A web page is not magic. It is just text written in HTML.
Basic HTML Structure
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1>Hello</h1>
</body>
</html>Creating a Button in HTML
<button>Turn ON</button>This only creates a visual button.It does NOT control LED yet.
ESP32 Sends a Web Page
server.send(200, "text/html", html);Joystick
HTML in arduino Code
String html = "<html>";
html += "<body>";
html += "<h1>ESP32 LED</h1>";
html += "</body>";
html += "</html>";Adding Style (CSS)
<style>
button {
padding: 15px;
font-size: 20px;
}
</style>CSS controls appearance.
We build HTML like building a long text message.
+= means: Add more text to existing string.
Joystick
Create a simple Button
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "project";
const char* password = "1234567890";
WebServer server(80);
void handleRoot() {
String html = "<!DOCTYPE html><html><body>";
html += "<h1>ESP32 Web Page</h1>";
html += "<button>Turn ON</button>";
html += "<button>Turn OFF</button>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void setup() {
Serial.begin(115200);
// Connect to WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// Define route
server.on("/", handleRoot);
server.begin();
Serial.println("Web server started");
}
void loop() {
server.handleClient();
}Joystick
Create a simple Button
#include <WiFi.h>
#include <WebServer.h>
// Replace with your WiFi credentials
const char* ssid = "project";
const char* password = "1234567890";
WebServer server(80);
void handleRoot() {
String html = "<!DOCTYPE html><html>";
html += "<head>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<title>ESP32 Button Page</title>";
html += "<style>";
html += "body { font-family: Arial; text-align: center; margin-top: 50px; }";
html += "button { padding:15px 30px; font-size:20px; margin:10px; border:none; color:white; cursor:pointer; }";
html += ".blueButton { background-color: blue; }";
html += ".redButton { background-color: red; }";
html += "</style>";
html += "</head>";
html += "<body>";
html += "<h1>ESP32 Web Page</h1>";
html += "<button class='blueButton'>Turn ON</button>";
html += "<button class='redButton'>Turn OFF</button>";
html += "</body>";
html += "</html>";
server.send(200, "text/html", html);
}
void setup() {
Serial.begin(115200);
// Connect to WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// Define route
server.on("/", handleRoot);
server.begin();
Serial.println("Web server started");
}
void loop() {
server.handleClient();
}Joystick
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "project";
const char* password = "1234567890";
WebServer server(80);
const int ledPin = 12; // LED pin
void handleRoot() {
String html = "<!DOCTYPE html><html><head>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<style>";
html += "body { font-family: Arial; text-align: center; margin-top: 50px; }";
html += "button { padding:15px 30px; font-size:20px; margin:10px; border:none; color:white; cursor:pointer; }";
html += ".blueButton { background-color: blue; }";
html += ".redButton { background-color: red; }";
html += "</style>";
html += "</head><body>";
html += "<h1>ESP32 LED Control</h1>";
html += "<a href='/on'><button class='blueButton'>Turn ON</button></a>";
html += "<a href='/off'><button class='redButton'>Turn OFF</button></a>";
html += "</body></html>";
server.send(200, "text/html", html);
}
// -------- LED ON --------
void handleOn() {
digitalWrite(ledPin, HIGH);
handleRoot(); // Refresh page
}
// -------- LED OFF --------
void handleOff() {
digitalWrite(ledPin, LOW);
handleRoot(); // Refresh page
}
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
// Connect to WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// Routes
server.on("/", handleRoot);
server.on("/on", handleOn);
server.on("/off", handleOff);
server.begin();
Serial.println("Web server started");
}
void loop() {
server.handleClient();
}Design a web page hosted by the ESP32 that contains five control buttons to operate the robot. Create a web interface with the following five buttons: Forward, Backward, Left, Right, and Stop
4 Buttons
Joystick
4 Buttons
Joystick
Right now, our webpage can control the LED. But it does not know whether the LED is ON or OFF. So in order to that, we can:
1. Add a State Variable
String ledState = "OFF";2. Update State Inside Handlers
void handleOn() {
digitalWrite(ledPin, HIGH);
ledState = "ON";
handleRoot();
}
void handleOff() {
digitalWrite(ledPin, LOW);
ledState = "OFF";
handleRoot();
}3. Modify the webpage
html += "<h2>LED State: " + ledState + "</h2>";MIT App Inventor is a block-based programming platform to create apps.
Features:
Drag and drop UI
Block programming (no complex syntax)
Works with Bluetooth / WiFi / IoT devices
There are 3 mains part in the MIT App inventor
Designer
Used to build the app interface
Examples:
Buttons
Labels
Images
Blocks
Used to create the logic
Components
Devices used by the app
Examples:
Bluetooth
WiFi
Sensors
There are 3 mains part in the MIT App inventor
Designer
Used to build the app interface
Examples:
Buttons
Labels
Images
Blocks
Used to create the logic
Components
Devices used by the app
Examples:
Bluetooth
WiFi
Sensors
We create 3 button in here to move backward, stop, and Forward
A slider here is used to adjust the speed
This horizontal Arrangement is just for spacing
Backward Appearance
We create 3 button in here to move backward, stop, and Forward
A slider here is used to adjust the speed
This horizontal Arrangement is just for spacing
Stop Appearance
We create 3 button in here to move backward, stop, and Forward
A slider here is used to adjust the speed
This horizontal Arrangement is just for spacing
Forward Appearance
We create 3 button in here to move backward, stop, and Forward
A slider here is used to adjust the speed
This horizontal Arrangement is just for spacing
Slider Appearance
In here, we use the wifi component. You can actually use the bluetooth, but it supports only the android
You need to adjust this with your own IP from the esp32
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "WIFI";
const char* password = "Password";
WebServer server(80);
void forward() {
Serial.println("Command: FORWARD");
server.send(200, "text/plain", "Forward received");
}
void backward() {
Serial.println("Command: BACKWARD");
server.send(200, "text/plain", "Backward received");
}
void stopMotor() {
Serial.println("Command: STOP");
server.send(200, "text/plain", "Stop received");
}
void setSpeed() {
if (server.hasArg("value")) {
String speedValue = server.arg("value");
Serial.print("Speed value received: ");
Serial.println(speedValue);
}
server.send(200, "text/plain", "Speed received");
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Connected!");
Serial.print("ESP32 IP: ");
Serial.println(WiFi.localIP());
server.on("/forward", forward);
server.on("/backward", backward);
server.on("/stop", stopMotor);
server.on("/speed", setSpeed);
server.begin();
}
void loop() {
server.handleClient();
}Download the MIT App Inventor from your mobile phone and scan QR using MIT App Inventor AI Companion as shown below
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "project";
const char* password = "1234567890";
WebServer server(80);
float getFakeDistance() {
return random(1, 100);
}
String htmlPage() {
return R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Front Radar</title>
<style>
body {
margin: 0;
background: black;
overflow: hidden;
}
canvas {
display: block;
}
</style>
</head>
<body>
<canvas id="radar"></canvas>
<script>
const canvas = document.getElementById("radar");
const ctx = canvas.getContext("2d");
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
let distance = 100;
function drawRadar() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const centerX = canvas.width / 2;
const centerY = canvas.height * 0.95;
const maxRadius = canvas.height * 0.9;
ctx.strokeStyle = "lime";
ctx.lineWidth = 0.8;
let arcCount = 15;
for (let i = 1; i <= arcCount; i++) {
let r = (maxRadius / arcCount) * i;
ctx.beginPath();
ctx.arc(centerX, centerY, r, Math.PI, 2*Math.PI);
ctx.stroke();
}
let lineCount = 30;
for (let i = 0; i <= lineCount; i++) {
let angle = Math.PI + (i / lineCount) * Math.PI;
let x = centerX + maxRadius * Math.cos(angle);
let y = centerY + maxRadius * Math.sin(angle);
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(x, y);
ctx.stroke();
}
let scaled = (distance / 100) * maxRadius;
let objectY = centerY - scaled;
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(centerX, objectY, 16, 0, 2*Math.PI);
ctx.fill();
ctx.fillStyle = "white";
ctx.font = "32px Arial";
ctx.fillText("Distance: " + distance.toFixed(1) + " cm", 30, 60);
}
function updateDistance() {
fetch("/distance")
.then(res => res.text())
.then(data => {
distance = parseFloat(data);
});
}
setInterval(drawRadar, 30);
setInterval(updateDistance, 300);
</script>
</body>
</html>
)rawliteral";
}
void handleRoot() {
server.send(200, "text/html", htmlPage());
}
void handleDistance() {
float d = getFakeDistance();
server.send(200, "text/plain", String(d));
}
void setup() {
Serial.begin(115200);
randomSeed(analogRead(34));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected!");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/distance", handleDistance);
server.begin();
}
void loop() {
server.handleClient();
}In here I just create a fake distance, you have to use the ultrasonic in here by changing the function getFakeDistance()