Radar Deteksi Objek Menggunakan Sensor Ultrasonic HC-SR04 dan ESP32 sebagai Server (Web Radar PHP)

✅ 1. Judul Praktikum
Radar Deteksi Objek Menggunakan Sensor Ultrasonic HC-SR04 dengan ESP32 sebagai Server dan PHP sebagai Dashboard Visual Radar
✅ 2. Tujuan Praktikum
Setelah praktikum ini mahasiswa mampu:
-
- Menghubungkan sensor Ultrasonic HC-SR04 ke ESP32.
- Mengirim data jarak secara real-time melalui WebServer ESP32.
- Membuat dashboard radar sederhana menggunakan PHP + AJAX.
- Menampilkan visual radar bergerak (sweep 0–180°) berbasis web.
- Mempraktikkan konsep REST API, JSON, dan komunikasi HTTP.
✅ 3. Alat & Bahan
| No | Nama Peralatan | Jumlah |
| 1 | ESP32 Devkit | 1 |
| 2 | Sensor Ultrasonic HC-SR04 | 1 |
| 3 | Servo Motor SG90/MG90S | 1 |
| 4 | Kabel jumper | secukupnya |
| 5 | Breadboard | 1 |
| 6 | Laptop + Arduino IDE | 1 |
| 7 | Web Server lokal (XAMPP/Laragon) | 1 |
✅ 4. Dasar Teori
4.1 Sensor Ultrasonic HC-SR04
Sensor bekerja dengan memancarkan gelombang ultrasonik 40KHz dan menghitung waktu pantul (echo).
Rumus jarak:
Jarak = Waktu X 0.0343 /2
4.2 ESP32 Web Server
ESP32 dapat membuat REST API berbasis HTTP sehingga data sensor bisa diakses melalui URL, contoh:
http://192.168.1.50/data
Output JSON:
{"sudut":45,"jarak":120}
4.3 PHP Web Dashboard
PHP membaca data dari API ESP32 menggunakan AJAX untuk menampilkan radar bergerak.
✅ 5. Rangkaian

🟦 ESP32 ke HC-SR04
| HC-SR04 | ESP32 |
| VCC | 5V |
| GND | GND |
| Trig | GPIO 5 |
| Echo | GPIO 18 |
🟩 ESP32 ke Servo
| Servo | ESP32 |
| VCC | 5V |
| GND | GND |
| Signal | GPIO 13 |
✅ 6. Program ESP32 (Web API Radar)
Upload menggunakan Arduino IDE.
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "bisaioti_1";
const char* password = "bisaioti123";
#define trigPin 5
#define echoPin 18
WebServer server(80);
// =======================================================
// Fungsi baca jarak ultrasonik
// =======================================================
int getDistance() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000); // timeout 30ms
int distance = duration * 0.034 / 2;
if (distance <= 0 || distance > 400) distance = 0;
return distance;
}
// =======================================================
// Handler data untuk radar
// =======================================================
void handleData() {
int angle = random(0, 181); // Sudut 0–180
int dist = getDistance(); // Baca jarak
// Serial Monitor
Serial.printf("Sudut: %d°, Jarak: %d cm\n", angle, dist);
// JSON
String json = "{\"sudut\":" + String(angle) +
",\"jarak\":" + String(dist) + "}";
// Header WAJIB DULUAN
server.sendHeader("Access-Control-Allow-Origin", "*");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
// Baru kirim JSON
server.send(200, "application/json", json);
}
// =======================================================
void setup() {
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
Serial.begin(115200);
delay(1000);
Serial.println("\nStarting AP...");
WiFi.softAP(ssid, password);
delay(500); // penting agar AP stabil
Serial.print("AP IP: ");
Serial.println(WiFi.softAPIP());
server.on("/data", handleData);
server.begin();
Serial.println("Server Ready!");
}
// =======================================================
void loop() {
server.handleClient();
}
✅ ESP32 akan membuat WiFi sendiri:
SSID: bisaioti_1 Password: bisaioti123
URL API data real-time:
http://192.168.4.1/data
✅ PENJELASAN PROGRAM
✅ 1. Library yang Dipakai
#include <WiFi.h> #include <WebServer.h>
- WiFi.h → mengaktifkan fitur WiFi pada ESP32.
- WebServer.h → membuat web server (HTTP server) di port 80.
✅ 2. Konfigurasi Access Point
const char* ssid = "bisaioti_1"; const char* password = "bisaioti123";
ESP32 akan membuat WiFi Hotspot sendiri dengan nama:
- SSID : bisaioti_1
- Password : wasiswa123
Client (HP/Laptop) harus terhubung ke AP ini untuk membaca data sensor.
✅ 3. Pin Ultrasonik HC-SR04
#define trigPin 5 #define echoPin 18
- Pin 5 → TRIG
- Pin 18 → ECHO
✅ 4. Objek Web Server
WebServer server(80);
ESP32 akan membuat server di:
- Port 80
- Endpoint utama: /data
✅ 5. Fungsi Baca Jarak Ultrasonik
int getDistance() { digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); long duration = pulseIn(echoPin, HIGH, 30000); // timeout 30ms int distance = duration * 0.034 / 2; if (distance <= 0 || distance > 400) distance = 0; return distance; }
Cara kerja:
- TRIG diberi pulsa 10 mikrodetik → sensor mulai “menembak” ultrasonik.
- ECHO membaca waktu pantulan (dalam mikrodetik).
- Rumus:
- jarak = durasi * kecepatan suara (0.034 cm/µs) / 2
- Jarak dibatasi 0–400 cm (range aman HC-SR04).
- Jika tidak valid, jarak di-set 0.
✅ 6. Handler /data – untuk JSON RADAR
void handleData() { int angle = random(0, 181); // Sudut radar 0-180 derajat int dist = getDistance(); // Baca jarak sensor
- Sudut dibuat random → nanti digunakan untuk animasi radar web.
- Jarak diambil dari ultrasonik.
Debug Print:
Serial.printf("Sudut: %d°, Jarak: %d cm\n", angle, dist);
Format JSON yang dikirim ke browser:
{"sudut": 90, "jarak": 35}
✅ 7. Header Anti-Cache (Wajib untuk grafis real-time)
server.sendHeader("Access-Control-Allow-Origin", "*"); server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Pragma", "no-cache"); server.sendHeader("Expires", "-1");
Ini memastikan:
✅ Browser tidak menyimpan data
✅ Data selalu real-time
✅ 8. Kirim Data ke Client
server.send(200, "application/json", json);
✅ 9. Setup()
WiFi.softAP(ssid, password); Serial.println(WiFi.softAPIP());
- ESP32 membuat Access Point (AP Mode)
- Mencetak IP default AP biasanya: 192.168.4.1
Register endpoint:
server.on("/data", handleData); server.begin();
Artinya:
- Jika user mengakses http://192.168.4.1/data → ESP32 kirim data JSON radar
✅ 10. Loop()
server.handleClient();
Dipanggil terus untuk melayani request HTTP.
✅ RANGKUMAN CARA KERJA PROGRAM
- ESP32 membuat WiFi hotspot bernama bisaioti_1.
- Browser tersambung ke AP tersebut.
- Browser/web app melakukan fetch ke:
- http://192.168.4.1/data
- ESP32 membaca jarak ultrasonik.
- ESP32 membuat sudut random 0–180°.
- Data dikirim dalam bentuk JSON:
- {“sudut”:90,”jarak”:25}
- Browser menampilkan animasi radar bergerak (canvas atau chart).
✅ 7. Program PHP (Dashboard Radar)
Buat folder di htdocs/ bernama radar/
Buat file index.php berikut:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8" />
<title>Radar Ultrasonic ESP32 — Pro</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
:root {
--bg: #000;
--grid: rgba(0,255,0,.20);
--grid-soft: rgba(0,255,0,.10);
--glow: #31ff5f;
--sweep: #00ff6a;
--dot: #ff4040;
--text: #e8fff0;
}
html,body{height:100%}
body{
margin:0;background:var(--bg);color:var(--text);
font-family:Segoe UI,Roboto,Arial,Helvetica,sans-serif; display:flex; flex-direction:column; align-items:center;
}
h1{font-size:clamp(18px,2.6vw,32px); margin:16px 0 6px}
.wrap{width:min(96vw,1100px); display:grid; gap:14px}
.panel{background:#040804; border:1px solid #0e3016; border-radius:14px; padding:10px 12px}
.grid2{display:grid; grid-template-columns:1fr; gap:14px}
@media (min-width:980px){ .grid2{grid-template-columns: 2fr 1fr} }
canvas{display:block; width:100%; height:auto; aspect-ratio:1/1; border-radius:12px}
.row{display:flex; gap:8px; align-items:center; flex-wrap:wrap}
.pill{background:#0d2b17; border:1px solid #154d2a; color:#baffcf; padding:6px 10px; border-radius:999px; font-size:12px}
.muted{opacity:.75}
.chart{aspect-ratio: 5/2}
.legend{display:flex; gap:10px; font-size:12px; opacity:.85}
.legend span::before{content:""; display:inline-block; width:10px; height:10px; margin-right:6px; border-radius:2px; vertical-align:baseline}
.legend .dist::before{background:var(--sweep)}
.legend .near::before{background:var(--dot)}
</style>
</head>
<body>
<h1>Radar Ultrasonic ESP32</h1>
<div class="wrap">
<div class="row">
<span class="pill">Mode: <b id="mode">LIVE</b></span>
<span class="pill">ESP32: <b id="status">menunggu...</b></span>
<span class="pill muted">IP: <span id="ip">http://192.168.4.1/data</span></span>
<span class="pill muted">Refresh: 100 ms</span>
</div>
<div class="grid2">
<div class="panel">
<canvas id="radar"></canvas>
</div>
<div class="panel">
<canvas id="chart" class="chart"></canvas>
<div class="legend" style="margin-top:6px">
<span class="dist">Jarak (cm)</span>
<span class="near">Objek dekat (<= 40 cm)</span>
</div>
<div class="row" style="margin-top:10px">
<span class="pill">Sudut: <b id="angleLabel">0°</b></span>
<span class="pill">Jarak: <b id="distLabel">0 cm</b></span>
<span class="pill">Objek: <b id="objLabel">0</b></span>
</div>
</div>
</div>
</div>
<script>
/* ================== Konfigurasi ================== */
const ESP32_URL = "http://192.168.4.1/data"; // ganti bila perlu
const FETCH_INTERVAL = 100; // ms
const MAX_RANGE_CM = 300; // jangkauan visual radar
const NEAR_THRESHOLD = 40; // penanda objek dekat
/* ================================================= */
const radar = document.getElementById('radar');
const rctx = radar.getContext('2d');
const chart = document.getElementById('chart');
const cctx = chart.getContext('2d');
const statusEl = document.getElementById('status');
const modeEl = document.getElementById('mode');
document.getElementById('ip').textContent = ESP32_URL;
let W, H, CX, CY, R;
let sweep = 0; // sudut sweep animasi (0..360)
let dotHistory = []; // riwayat titik (fade-out)
let distSeries = []; // seri jarak untuk grafik
let lastFetchOK = false;
let lastTargets = []; // target terakhir dari ESP32
// Helper: resize sesuai pixel ratio
function fitCanvas(can, ctx, w, h){
const dpr = Math.max(1, window.devicePixelRatio || 1);
can.width = Math.floor(w*dpr);
can.height = Math.floor(h*dpr);
can.style.width = w + "px";
can.style.height = h + "px";
ctx.setTransform(dpr,0,0,dpr,0,0);
}
// Inisialisasi ukuran canvas responsif
function layout(){
// radar selalu persegi, ambil lebar panel
const box = radar.parentElement.getBoundingClientRect();
const size = Math.min(box.width-24, 700);
fitCanvas(radar, rctx, size, size);
W = radar.width / (window.devicePixelRatio||1);
H = radar.height / (window.devicePixelRatio||1);
CX = W/2; CY = H/2; R = Math.min(W,H)*0.46;
// chart ratio 5:2
const cbox = chart.parentElement.getBoundingClientRect();
const ch = Math.max(180, Math.round(cbox.width*0.4));
fitCanvas(chart, cctx, cbox.width-24, ch);
}
layout();
window.addEventListener('resize', layout);
// ================== Radar Drawing ==================
function drawRadarBase(){
// fade-out: layer hitam transparan agar jejak memudar
rctx.fillStyle = "rgba(0,0,0,0.20)";
rctx.fillRect(0,0,W,H);
// grid 3D/holo: radial gradient
const g = rctx.createRadialGradient(CX, CY, 0, CX, CY, R);
g.addColorStop(0, "rgba(0,255,120,0.08)");
g.addColorStop(1, "rgba(0,255,80,0.02)");
rctx.beginPath();
rctx.fillStyle = g;
rctx.arc(CX, CY, R, 0, Math.PI*2);
rctx.fill();
// rings
rctx.strokeStyle = "rgba(0,255,0,.20)";
rctx.lineWidth = 2;
[R, R*0.66, R*0.33].forEach(r=>{
rctx.beginPath(); rctx.arc(CX,CY,r,0,Math.PI*2); rctx.stroke();
});
}
function drawSweep(){
const a = sweep * Math.PI/180;
const x = CX + R*Math.cos(a);
const y = CY + R*Math.sin(a);
// sweep glow
rctx.save();
rctx.lineWidth = 2;
rctx.strokeStyle = getGlow(0.95);
rctx.shadowBlur = 18;
rctx.shadowColor = "#30ff7a";
rctx.beginPath();
rctx.moveTo(CX, CY);
rctx.lineTo(x, y);
rctx.stroke();
rctx.restore();
}
function getGlow(alpha=1){
const grad = rctx.createLinearGradient(0,0,W,0);
grad.addColorStop(0, `rgba(0,255,120,${0.00*alpha})`);
grad.addColorStop(0.2,`rgba(0,255,120,${0.35*alpha})`);
grad.addColorStop(0.6,`rgba(0,255,120,${0.95*alpha})`);
grad.addColorStop(1, `rgba(0,255,120,${0.65*alpha})`);
return grad;
}
// titik + fade-out
function drawDots(){
// kurangi alpha tiap frame (fade)
dotHistory.forEach(p=> p.a *= 0.93);
dotHistory = dotHistory.filter(p=> p.a > 0.05);
dotHistory.forEach(p=>{
const d = Math.min(Math.max(p.dist,5), MAX_RANGE_CM);
const scale = d * (R / MAX_RANGE_CM);
const rad = p.angle * Math.PI/180;
const x = CX + scale*Math.cos(rad);
const y = CY + scale*Math.sin(rad);
rctx.beginPath();
rctx.fillStyle = `rgba(255,64,64,${p.a})`;
rctx.shadowBlur = 12*p.a;
rctx.shadowColor = "rgba(255,64,64,.7)";
rctx.arc(x,y, 6, 0, Math.PI*2);
rctx.fill();
rctx.shadowBlur = 0;
});
}
// ================== Chart Drawing ==================
function drawChart(){
cctx.clearRect(0,0,chart.width/(window.devicePixelRatio||1), chart.height/(window.devicePixelRatio||1));
const w = chart.width/(window.devicePixelRatio||1);
const h = chart.height/(window.devicePixelRatio||1);
// grid
cctx.strokeStyle = "rgba(0,255,0,.1)";
cctx.lineWidth = 1;
for(let i=0;i<4;i++){
const y = h*(i+1)/4;
cctx.beginPath(); cctx.moveTo(0,y); cctx.lineTo(w,y); cctx.stroke();
}
if(distSeries.length<2) return;
// scale
const N = distSeries.length;
const maxV = MAX_RANGE_CM;
const stepX = w/(N-1);
// line jarak
cctx.beginPath();
cctx.lineWidth = 2;
cctx.strokeStyle = "#00ff6a";
cctx.shadowBlur = 8;
cctx.shadowColor = "#00ff6a";
for(let i=0;i<N;i++){
const x = i*stepX;
const y = h - (distSeries[i]/maxV)*h;
if(i===0) cctx.moveTo(x,y); else cctx.lineTo(x,y);
}
cctx.stroke();
cctx.shadowBlur = 0;
// area danger (<= NEAR_THRESHOLD)
const yDanger = h - (NEAR_THRESHOLD/maxV)*h;
cctx.fillStyle = "rgba(255,64,64,.10)";
cctx.fillRect(0,yDanger,w,h-yDanger);
}
// ================== Data Fetch ==================
async function pollData(){
try{
const res = await fetch(ESP32_URL + "?t=" + Math.random(), {cache:'no-store'});
const js = await res.json();
lastFetchOK = true;
statusEl.textContent = "ONLINE";
statusEl.style.color = "#4dff9c";
let targets = [];
if(js.targets && Array.isArray(js.targets)){ // multi target
targets = js.targets.map(t => ({
angle: Number(t.sudut||t.angle||0),
dist: Number(t.jarak||t.distance||0)
}));
}else{ // single target
targets = [{ angle:Number(js.sudut||0), dist:Number(js.jarak||0) }];
}
lastTargets = targets;
// update UI ringkas (pakai target terdekat)
const nearest = targets
.filter(t=>t.dist>0)
.sort((a,b)=>a.dist-b.dist)[0] || {angle:0,dist:0};
document.getElementById('angleLabel').textContent = Math.round(nearest.angle) + "°";
document.getElementById('distLabel').textContent = Math.round(nearest.dist) + " cm";
document.getElementById('objLabel').textContent = targets.length;
// simpan ke grafik
distSeries.push(Math.min(nearest.dist || 0, MAX_RANGE_CM));
if(distSeries.length>150) distSeries.shift();
// tambahkan ke jejak titik (fade-out)
targets.forEach(t=>{
dotHistory.push({ angle: t.angle, dist: t.dist, a: 1.0 });
});
}catch(err){
// fallback DEMO MODE
lastFetchOK = false;
statusEl.textContent = "OFFLINE (DEMO)";
statusEl.style.color = "#ffaa4d";
modeEl.textContent = "DEMO";
demoInject();
}
}
function demoInject(){
// jika offline, generate data demo agar UI tetap hidup
const demoAngle = (sweep + Math.random()*6-3 + 360)%360;
const demoDist = 30 + Math.abs(Math.sin(sweep*Math.PI/180))*220 + Math.random()*15;
lastTargets = [{ angle: demoAngle, dist: demoDist }];
dotHistory.push({ angle: demoAngle, dist: demoDist, a:1.0 });
distSeries.push(Math.min(demoDist, MAX_RANGE_CM));
if(distSeries.length>150) distSeries.shift();
}
// ================== Main Loop ==================
function frame(){
drawRadarBase();
drawDots();
sweep = (sweep + 2) % 360; // sweep halus
drawSweep();
drawChart();
requestAnimationFrame(frame);
}
// Start
setInterval(pollData, FETCH_INTERVAL);
frame();
</script>
</body>
</html>
✅ Dashboard menampilkan radar bergerak 0–180°
✅ Titik merah muncul saat objek terdeteksi
✅ Komunikasi memakai AJAX fetch JSON
✅ 8. Langkah Kerja Praktikum
-
- Rangkai ESP32, HC-SR04, dan servo seperti diagram.
- Upload program ESP32 melalui Arduino IDE.
- ESP32 akan membuat WiFi hotspot → koneksi laptop ke SSID tersebut.
- Jalankan XAMPP / Laragon → aktifkan Apache.
- Masukkan file PHP ke folder htdocs/radar/.
- Buka browser → akses:
-
http://localhost/radar/
Amati radar bergerak mengikuti sweep servo (jika menggunakankan servo).
Uji objek di depan sensor → radar menampilkan titik deteksi.
✅ 9. Hasil & Analisis
Mahasiswa harus mampu:
✅ Menghasilkan visual radar bergerak
✅ Menjelaskan hubungan ESP32 WebServer & PHP
✅ Menganalisis akurasi sensor HC-SR04
✅ Mengukur kestabilan pengiriman data real-time
✅ 10. Pertanyaan Evaluasi
-
- Apa fungsi Trig dan Echo pada HC-SR04?
- Mengapa servo harus melakukan sweep 0–180°?
- Apa kelebihan menggunakan JSON untuk pertukaran data?
- Bagaimana cara menangani noise jarak sensor?
- Apa yang akan terjadi jika WiFi ESP32 overload?







