<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simulasi Sensor DHT11 & OLED Display v3.2 + Sirene Alarm</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Inter & Font Awesome -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
body { font-family: 'Inter', sans-serif; }
.font-mono { font-family: 'JetBrains Mono', monospace; }
.glass-panel { background: rgba(15, 23, 42, 0.88); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.08); }
.circuit-line-gnd { stroke: #94a3b8; stroke-width: 2.5; fill: none; stroke-dasharray: 6; animation: dash 1.5s linear infinite; }
.circuit-line-vcc { stroke: #ef4444; stroke-width: 2.5; fill: none; stroke-dasharray: 6; animation: dash 1.5s linear infinite; }
.circuit-line-data { stroke: #38bdf8; stroke-width: 2.5; fill: none; stroke-dasharray: 6; animation: dash 1.5s linear infinite; }
.circuit-line-scl { stroke: #eab308; stroke-width: 2.5; fill: none; stroke-dasharray: 6; animation: dash 1.5s linear infinite; }
.circuit-line-sda { stroke: #22c55e; stroke-width: 2.5; fill: none; stroke-dasharray: 6; animation: dash 1.5s linear infinite; }
.circuit-line-buzzer { stroke: #a855f7; stroke-width: 2.5; fill: none; stroke-dasharray: 6; animation: dash 1.5s linear infinite; }
@keyframes dash { to { stroke-dashoffset: -12; } }
@keyframes popIn {
0% { transform: scale(0.5); opacity: 0; }
70% { transform: scale(1.05); opacity: 1; }
100% { transform: scale(1); opacity: 1; }
}
.pop-animation { animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; }
@keyframes pulseAlert {
0%, 100% { border-color: rgba(239, 68, 68, 0.8); box-shadow: 0 0 15px rgba(239, 68, 68, 0.4); }
50% { border-color: rgba(239, 68, 68, 0.2); box-shadow: 0 0 5px rgba(239, 68, 68, 0.1); }
}
.alert-active { animation: pulseAlert 1s infinite; }
</style>
</head>
<body class="bg-slate-950 text-slate-100 min-h-screen flex flex-col justify-between selection:bg-cyan-500 selection:text-white">
<header class="border-b border-slate-800 bg-slate-900/85 backdrop-blur sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 py-3 flex flex-wrap items-center justify-between gap-4">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 rounded-xl bg-gradient-to-tr from-cyan-600 to-emerald-500 flex items-center justify-center shadow-lg shadow-cyan-500/30">
<i class="fa-solid fa-microchip text-white text-lg"></i>
</div>
<div>
<h1 class="font-bold text-base md:text-lg flex items-center gap-2">
Simulasi DHT11 + OLED + Sirene Alarm
<span class="text-xs bg-rose-500/20 text-rose-400 px-2 py-0.5 rounded-full border border-rose-500/30 font-semibold">v3.2</span>
</h1>
<p class="text-xs text-slate-400">Lab IoT dengan Efek Suara Sirene (≥ 45°C)</p>
</div>
</div>
<!-- Navigation Tabs -->
<nav class="flex items-center gap-1.5 md:gap-2 overflow-x-auto pb-1 md:pb-0 text-xs md:text-sm">
<button onclick="window.switchTab('simulator')" id="nav-simulator" class="px-3 py-1.5 rounded-lg bg-cyan-600 text-white font-medium flex items-center gap-2 shadow-md shadow-cyan-900/40 transition">
<i class="fa-solid fa-compass-drafting"></i> Lab Simulator
</button>
<button onclick="window.switchTab('terminal')" id="nav-terminal" class="px-3 py-1.5 rounded-lg bg-slate-800 text-slate-300 hover:bg-slate-700 font-medium flex items-center gap-2 transition">
<i class="fa-solid fa-terminal"></i> Terminal & Coding
</button>
<button onclick="window.switchTab('materi')" id="nav-materi" class="px-3 py-1.5 rounded-lg bg-slate-800 text-slate-300 hover:bg-slate-700 font-medium flex items-center gap-2 transition">
<i class="fa-solid fa-book"></i> Materi Teori
</button>
<button onclick="window.switchTab('evaluasi')" id="nav-evaluasi" class="px-3 py-1.5 rounded-lg bg-slate-800 text-slate-300 hover:bg-slate-700 font-medium flex items-center gap-2 transition">
<i class="fa-solid fa-graduation-cap"></i> Kuis Evaluasi
</button>
</nav>
</div>
</header>
<main class="flex-grow max-w-7xl mx-auto w-full p-4 md:p-6">
<div id="view-simulator" class="space-y-6">
<!-- Subheader Banner with Alarm Status Indicator -->
<div id="banner-container" class="glass-panel rounded-2xl p-4 md:p-5 flex flex-col md:flex-row justify-between items-start md:items-center gap-4 transition-all duration-300">
<div>
<div id="system-status-pill" class="flex items-center gap-2 text-cyan-400 text-xs font-semibold mb-1">
<i class="fa-solid fa-circle text-[8px] animate-pulse"></i> Sistem Normal: Memantau Suhu Lingkungan
</div>
<h2 class="text-xl md:text-2xl font-bold text-white">Rangkaian Arduino Uno, Sensor DHT11, OLED & Buzzer Sirene</h2>
<p class="text-xs md:text-sm text-slate-300 mt-1 max-w-2xl">
Atur suhu hingga mencapai <b class="text-amber-400">45°C atau lebih</b> untuk memicu efek suara sirene darurat pada audio browser dan layar OLED.
</p>
</div>
<div class="flex items-center gap-4 bg-slate-900/80 px-4 py-2.5 rounded-xl border border-slate-800 self-stretch md:self-auto justify-between md:justify-start">
<div>
<div class="text-[10px] uppercase tracking-wider text-slate-400 font-medium">Suhu Udara</div>
<div id="temp-status-display" class="text-base font-bold text-amber-400 flex items-center gap-1.5 mt-0.5">
28.0°C
</div>
</div>
<div class="h-8 w-px bg-slate-800"></div>
<div class="text-right">
<div class="text-[10px] uppercase tracking-wider text-slate-400 font-medium">Kelembapan</div>
<div id="hum-status-display" class="text-base font-bold text-cyan-400 mt-0.5">65% RH</div>
</div>
</div>
</div>
<!-- Workspace Grid -->
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
<!-- Circuit Breadboard & Realistic Sensor + OLED + Buzzer Canvas (7 Cols) -->
<div class="lg:col-span-7 glass-panel rounded-2xl p-5 flex flex-col justify-between relative overflow-hidden">
<div class="flex items-center justify-between border-b border-slate-800/80 pb-3 mb-4">
<div class="flex items-center gap-2">
<div id="circuit-dot" class="w-2.5 h-2.5 rounded-full bg-cyan-500 animate-pulse"></div>
<span class="text-sm font-semibold text-slate-200">Skematik Fisik: Arduino + DHT11 + OLED + Buzzer Sirene</span>
</div>
<span class="text-xs bg-slate-800/80 text-cyan-300 px-2.5 py-1 rounded-md border border-slate-700">Pin 2 (DHT) & Pin 8 (Buzzer)</span>
</div>
<!-- SVG Visual Circuit Schematic with Realistic DHT11, OLED, and Buzzer -->
<div class="relative w-full h-[380px] md:h-[430px] bg-slate-950/70 rounded-xl border border-slate-900 flex items-center justify-center p-2 overflow-hidden">
<svg class="w-full h-full" viewBox="0 0 740 380" id="circuit-svg">
<!-- Circuit Path Wires -->
<!-- GND Wire (Grey) -->
<path id="wire-gnd" d="M 125 290 L 125 340 L 320 340 L 320 300 L 440 300 L 440 250" class="circuit-line-gnd" />
<!-- VCC Wire (Red 5V) -->
<path id="wire-vcc" d="M 145 290 L 145 325 L 340 325 L 340 280 L 470 280 L 470 250" class="circuit-line-vcc" />
<!-- Data Signal Wire (Blue Pin 2) -->
<path id="wire-data" d="M 30 150 L 190 150 L 190 265 L 500 265 L 500 250" class="circuit-line-data" />
<!-- I2C SCL Wire (Yellow A5) -->
<path id="wire-scl" d="M 165 290 L 165 355 L 400 355 L 400 310 L 630 310 L 630 250" class="circuit-line-scl" />
<!-- I2C SDA Wire (Green A4) -->
<path id="wire-sda" d="M 185 290 L 185 370 L 420 370 L 420 295 L 600 295 L 600 250" class="circuit-line-sda" />
<!-- Buzzer Wire (Purple Pin 8) -->
<path id="wire-buzzer" d="M 75 115 L 75 80 L 300 80 L 300 220 L 375 220" class="circuit-line-buzzer" />
<!-- ARDUINO UNO BOARD -->
<g transform="translate(20, 110)">
<rect width="180" height="190" rx="10" fill="#1e293b" stroke="#0284c7" stroke-width="2" />
<text x="90" y="20" fill="#38bdf8" font-size="11" font-weight="bold" text-anchor="middle">ARDUINO UNO</text>
<!-- USB Port -->
<rect x="15" y="45" width="35" height="22" rx="4" fill="#475569" />
<text x="32" y="60" fill="#cbd5e1" font-size="7" text-anchor="middle">USB</text>
<!-- Power & Analog Area -->
<rect x="105" y="80" width="70" height="95" rx="6" fill="#0f172a" stroke="#334155" />
<text x="140" y="94" fill="#facc15" font-size="8" font-weight="bold" text-anchor="middle">POWER / ANALOG</text>
<circle cx="118" cy="115" r="3.5" fill="#ef4444" />
<text x="135" y="118" fill="#cbd5e1" font-size="7">5V</text>
<circle cx="118" cy="135" r="3.5" fill="#94a3b8" />
<text x="135" y="138" fill="#cbd5e1" font-size="7">GND</text>
<circle cx="118" cy="155" r="3.5" fill="#22c55e" />
<text x="135" y="158" fill="#cbd5e1" font-size="7">A4 (SDA)</text>
<circle cx="118" cy="172" r="3.5" fill="#eab308" />
<text x="135" y="175" fill="#cbd5e1" font-size="7">A5 (SCL)</text>
<!-- Digital Pins Area -->
<rect x="15" y="80" width="80" height="95" rx="6" fill="#0f172a" stroke="#334155" />
<text x="55" y="94" fill="#38bdf8" font-size="8" font-weight="bold" text-anchor="middle">DIGITAL</text>
<circle cx="28" cy="115" r="3.5" fill="#a855f7" />
<text x="42" y="118" fill="#cbd5e1" font-size="7">Pin 8</text>
<circle cx="28" cy="135" r="3.5" fill="#38bdf8" />
<text x="42" y="138" fill="#cbd5e1" font-size="7">Pin 2</text>
</g>
<!-- BUZZER SIRENE MODULE -->
<g transform="translate(340, 150)" id="realistic-buzzer-group">
<circle cx="35" cy="35" r="30" fill="#334155" stroke="#64748b" stroke-width="3" filter="drop-shadow(0px 4px 8px rgba(0,0,0,0.5))" />
<circle cx="35" cy="35" r="22" fill="#1e293b" />
<circle cx="35" cy="35" r="14" fill="#0f172a" />
<!-- Sound waves icon effect when active -->
<path d="M 55 20 Q 65 35 55 50" fill="none" stroke="#a855f7" stroke-width="2" id="buzzer-wave-1" opacity="0" />
<path d="M 62 12 Q 78 35 62 58" fill="none" stroke="#a855f7" stroke-width="2" id="buzzer-wave-2" opacity="0" />
<text x="35" y="78" fill="#a855f7" font-size="8" font-weight="bold" text-anchor="middle">SIRENE (PIN 8)</text>
</g>
<!-- REALISTIC SENSOR DHT11 -->
<g transform="translate(420, 95)" id="realistic-dht-group">
<rect x="15" y="15" width="80" height="120" rx="10" fill="#0284c7" stroke="#0369a1" stroke-width="3" filter="drop-shadow(0px 6px 10px rgba(0,0,0,0.5))" />
<rect x="25" y="28" width="60" height="42" rx="3" fill="#0369a1" />
<line x1="33" y1="38" x2="77" y2="38" stroke="#bae6fd" stroke-width="2" />
<line x1="33" y1="47" x2="77" y2="47" stroke="#bae6fd" stroke-width="2" />
<line x1="33" y1="56" x2="77" y2="56" stroke="#bae6fd" stroke-width="2" />
<text x="55" y="92" fill="#ffffff" font-size="10" font-weight="bold" text-anchor="middle">DHT11</text>
<text x="55" y="108" fill="#e0f2fe" font-size="7" text-anchor="middle">Sensor Suhu</text>
<rect x="25" y="135" width="5" height="20" fill="#e2e8f0" />
<rect x="40" y="135" width="5" height="20" fill="#e2e8f0" />
<rect x="55" y="135" width="5" height="20" fill="#e2e8f0" />
<rect x="70" y="135" width="5" height="20" fill="#e2e8f0" />
</g>
<!-- REALISTIC OLED DISPLAY 0.96" (I2C) -->
<g transform="translate(540, 85)" id="realistic-oled-group">
<!-- PCB Board -->
<rect width="130" height="160" rx="8" fill="#475569" stroke="#334155" stroke-width="2" filter="drop-shadow(0px 8px 14px rgba(0,0,0,0.6))" />
<text x="65" y="18" fill="#ffffff" font-size="9" font-weight="bold" text-anchor="middle">OLED 128x64 I2C</text>
<!-- Screen Bezel -->
<rect x="15" y="28" width="100" height="70" rx="4" fill="#020617" stroke="#1e293b" stroke-width="2" />
<!-- OLED Live Display Content Inside SVG -->
<g transform="translate(20, 33)">
<rect width="90" height="60" rx="2" fill="#030712" />
<text x="45" y="12" fill="#38bdf8" font-size="7" font-weight="bold" text-anchor="middle">-- IOT MONITOR --</text>
<line x1="5" y1="16" x2="85" y2="16" stroke="#0284c7" stroke-width="0.8" />
<text x="8" y="28" fill="#94a3b8" font-size="7">SUHU:</text>
<text x="75" y="28" fill="#f59e0b" font-size="8" font-weight="bold" text-anchor="end" id="oled-svg-temp">28.0°C</text>
<text x="8" y="42" fill="#94a3b8" font-size="7">LEMBAP:</text>
<text x="75" y="42" fill="#22d3ee" font-size="8" font-weight="bold" text-anchor="end" id="oled-svg-hum">65%</text>
<!-- Dynamic status on OLED screen -->
<text x="45" y="55" fill="#10b981" font-size="6" font-weight="bold" text-anchor="middle" id="oled-svg-status">STATUS: AMAN</text>
</g>
<!-- 4 Pins at bottom -->
<rect x="30" y="110" width="6" height="30" fill="#e2e8f0" />
<rect x="50" y="110" width="6" height="30" fill="#e2e8f0" />
<rect x="70" y="110" width="6" height="30" fill="#e2e8f0" />
<rect x="90" y="110" width="6" height="30" fill="#e2e8f0" />
</g>
</svg>
</div>
<!-- Live Telemetry Badges -->
<div class="mt-4 pt-3 border-t border-slate-800/80 grid grid-cols-2 md:grid-cols-4 gap-3">
<div class="bg-slate-900/90 rounded-xl p-2.5 border border-slate-800 text-center">
<div class="text-[10px] text-slate-400">Sirene Status</div>
<div id="badge-buzzer-status" class="text-sm font-bold text-slate-400 mt-0.5">OFF (Aman)</div>
</div>
<div class="bg-slate-900/90 rounded-xl p-2.5 border border-slate-800 text-center">
<div class="text-[10px] text-slate-400">Suhu Terbaca</div>
<div class="text-sm font-bold text-amber-400 mt-0.5" id="val-temp">28.0°C</div>
</div>
<div class="bg-slate-900/90 rounded-xl p-2.5 border border-slate-800 text-center">
<div class="text-[10px] text-slate-400">Batas Threshold</div>
<div class="text-sm font-bold text-rose-400 mt-0.5">≥ 45.0°C</div>
</div>
<div class="bg-slate-900/90 rounded-xl p-2.5 border border-slate-800 text-center">
<div class="text-[10px] text-slate-400">OLED Status</div>
<div class="text-sm font-bold text-emerald-400 mt-0.5" id="badge-oled-status">Normal</div>
</div>
</div>
</div>
<!-- Controls & Manual Setting Panel (5 Cols) -->
<div class="lg:col-span-5 space-y-6">
<!-- Manual Temperature Control Card -->
<div class="glass-panel rounded-2xl p-5 space-y-4">
<div class="flex justify-between items-center">
<div>
<span class="text-xs font-semibold text-amber-400 uppercase tracking-wider">Simulasi Suhu Lingkungan (0°C - 60°C)</span>
<h3 class="text-sm font-bold text-slate-200 mt-0.5">Atur Slider Suhu Ruangan</h3>
</div>
<div class="bg-slate-900 px-3 py-1.5 rounded-xl border border-slate-800 text-amber-400 font-mono font-bold text-base" id="temp-display">
28°C
</div>
</div>
<div class="space-y-2">
<input type="range" id="temp-slider" min="0" max="60" step="0.5" value="28" oninput="window.updateEnvironment(this.value, document.getElementById('hum-slider').value)" class="w-full accent-amber-500 bg-slate-800 rounded-lg appearance-none h-2.5 cursor-pointer">
<div class="flex justify-between text-[11px] text-slate-400 font-medium px-1">
<span>0°C (Dingin)</span>
<span class="text-rose-400 font-bold">45°C (Sirene Aktif)</span>
<span>60°C (Maks)</span>
</div>
</div>
</div>
<!-- Manual Humidity Control Card -->
<div class="glass-panel rounded-2xl p-5 space-y-4">
<div class="flex justify-between items-center">
<div>
<span class="text-xs font-semibold text-cyan-400 uppercase tracking-wider">Simulasi Kelembapan Udara (20% - 90% RH)</span>
<h3 class="text-sm font-bold text-slate-200 mt-0.5">Atur Slider Kelembapan</h3>
</div>
<div class="bg-slate-900 px-3 py-1.5 rounded-xl border border-slate-800 text-cyan-400 font-mono font-bold text-base" id="hum-display">
65%
</div>
</div>
<div class="space-y-2">
<input type="range" id="hum-slider" min="20" max="90" step="1" value="65" oninput="window.updateEnvironment(document.getElementById('temp-slider').value, this.value)" class="w-full accent-cyan-500 bg-slate-800 rounded-lg appearance-none h-2.5 cursor-pointer">
<div class="flex justify-between text-[11px] text-slate-400 font-medium px-1">
<span>20% (Kering)</span>
<span>60% (Ideal)</span>
<span>90% (Lembap)</span>
</div>
</div>
</div>
<!-- Alur Logika Sirene Penjelasan Box -->
<div id="explanation-box" class="glass-panel rounded-2xl p-5 space-y-2 border-l-4 border-l-cyan-500 transition-all duration-300">
<div class="text-xs font-bold text-cyan-400 flex items-center gap-1.5" id="explanation-title">
<i class="fa-solid fa-circle-info"></i> LOGIKA PERCABANGAN (IF-ELSE):
</div>
<p id="informatics-explanation" class="text-xs text-slate-300 leading-relaxed">
Jika suhu < 45°C, sistem berada pada status normal. Jika suhu ≥ 45°C, mikrokontroler mengeksekusi perintah menyalakan sirene pada Pin 8 dan menampilkan peringatan <b class="text-rose-400">"WASPADA PANAS BERLEBIH!"</b>.
</p>
</div>
</div>
</div>
</div>
<div id="view-terminal" class="hidden space-y-6">
<div class="glass-panel rounded-2xl p-6 space-y-5">
<div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 border-b border-slate-800 pb-4">
<div>
<h2 class="text-xl font-bold text-white flex items-center gap-2">
<i class="fa-solid fa-code text-cyan-400"></i> Arduino IDE Code Sandbox (with Siren Alarm Logic)
</h2>
<p class="text-xs text-slate-400 mt-1">Skrip Arduino lengkap dengan fungsi percabangan kondisi suhu panas berlebih & efek nada sirene.</p>
</div>
<button onclick="window.runArduinoCode()" class="px-5 py-2.5 bg-emerald-600 hover:bg-emerald-500 text-white text-xs font-semibold rounded-xl shadow-lg shadow-emerald-900/30 transition flex items-center gap-2">
<i class="fa-solid fa-play"></i> Upload & Run Code
</button>
</div>
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
<!-- Code Editor (7 Cols) -->
<div class="lg:col-span-7 bg-slate-950 rounded-xl border border-slate-800 overflow-hidden flex flex-col">
<div class="bg-slate-900 px-4 py-2 border-b border-slate-800 flex justify-between items-center text-xs text-slate-400">
<span class="font-mono"><i class="fa-solid fa-file-code text-cyan-400"></i> dht11_sirene_alarm.ino</span>
<span class="text-emerald-400 font-semibold"><i class="fa-solid fa-circle text-[8px]"></i> Ready to compile</span>
</div>
<textarea id="arduino-code-editor" class="w-full h-[320px] bg-slate-950 text-cyan-300 p-4 font-mono text-xs md:text-sm focus:outline-none resize-none leading-relaxed" spellcheck="false">#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define DHTPIN 2
#define DHTTYPE DHT11
#define BUZZERPIN 8 // Pin untuk Buzzer Sirene
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(9600);
dht.begin();
pinMode(BUZZERPIN, OUTPUT);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 alokasi gagal"));
for(;;);
}
display.clearDisplay();
display.setTextColor(WHITE);
}
void loop() {
delay(2000);
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Gagal membaca DHT!");
return;
}
// Logika Percabangan Sirene Suhu Panas Berlebih (>= 45°C)
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.println(F("== IOT MONITOR =="));
if (t >= 45.0) {
// Efek Sirene (Naik-Turun Frekuensi Nada)
for (int freq = 1000; freq <= 2500; freq += 200) {
tone(BUZZERPIN, freq, 100);
delay(80);
}
for (int freq = 2500; freq >= 1000; freq -= 200) {
tone(BUZZERPIN, freq, 100);
delay(80);
}
Serial.print("SIRENE AKTIF! Suhu Panas Berlebih: "); Serial.print(t); Serial.println("°C");
display.setCursor(0, 18);
display.println(F("! SIRENE BAHAYA !"));
display.setCursor(0, 32);
display.print(F("Suhu: ")); display.print(t); display.println(F(" C"));
display.setCursor(0, 48);
display.println(F("STATUS: OVERHEAT!"));
} else {
noTone(BUZZERPIN); // Matikan Buzzer
Serial.print("Suhu: "); Serial.print(t); Serial.print("°C | Kelembapan: "); Serial.print(h); Serial.println("%");
display.setCursor(0, 20);
display.print(F("Suhu: ")); display.print(t); display.println(F(" C"));
display.setCursor(0, 35);
display.print(F("Lembap: ")); display.print(h); display.println(F(" %"));
display.setCursor(0, 50);
display.println(F("STATUS: AMAN"));
}
display.display();
}</textarea>
</div>
<!-- Serial Monitor / Output Log (5 Cols) -->
<div class="lg:col-span-5 bg-slate-950 rounded-xl border border-slate-800 overflow-hidden flex flex-col justify-between">
<div class="bg-slate-900 px-4 py-2 border-b border-slate-800 flex justify-between items-center text-xs text-slate-400">
<span class="font-mono"><i class="fa-solid fa-desktop text-amber-400"></i> Serial Monitor Output</span>
<button onclick="window.clearConsole()" class="text-slate-400 hover:text-white transition text-[11px]">Clear</button>
</div>
<div id="serial-console" class="p-4 font-mono text-xs text-slate-300 h-[280px] overflow-y-auto space-y-1.5 bg-slate-950/80">
<div class="text-emerald-400">[System] Arduino IDE v1.8.19 initialized.</div>
<div class="text-cyan-400">[Compiler] Sketch compiled successfully. Sirene Buzzer on Pin 8 configured.</div>
<div class="text-slate-400">[Serial] Memantau sensor DHT11 & ambang batas sirene 45°C...</div>
</div>
<div class="p-3 bg-slate-900 border-t border-slate-800 flex gap-2">
<input type="text" id="serial-input" placeholder="Ketik perintah serial..." class="bg-slate-950 border border-slate-700 rounded-lg px-3 py-1.5 text-xs text-white w-full font-mono focus:outline-none focus:border-cyan-500">
<button onclick="window.sendSerialData()" class="bg-cyan-600 hover:bg-cyan-500 text-white px-4 py-1.5 rounded-lg text-xs font-semibold transition shrink-0">Kirim</button>
</div>
</div>
</div>
</div>
</div>
<div id="view-materi" class="hidden space-y-6">
<div class="glass-panel rounded-2xl p-6 space-y-4">
<div class="border-b border-slate-800 pb-4">
<h2 class="text-xl font-bold text-white flex items-center gap-2">
<i class="fa-solid fa-book-open text-cyan-400"></i> Materi Pembelajaran: Logika Percabangan & Efek Sirene
</h2>
<p class="text-xs text-slate-400 mt-1">Panduan pemrograman mikrokontroler untuk menciptakan modul suara sirene darurat berbasis perubahan frekuensi.</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 pt-2">
<div class="bg-slate-900/80 p-5 rounded-xl border border-slate-800 space-y-3">
<div class="w-10 h-10 rounded-lg bg-cyan-500/10 text-cyan-400 flex items-center justify-center font-bold text-lg">1</div>
<h3 class="font-bold text-slate-200 text-sm">Percabangan If-Else</h3>
<p class="text-xs text-slate-400 leading-relaxed">
Digunakan untuk menguji kondisi variabel suhu. Jika <code class="text-cyan-400">t >= 45.0</code>, jalankan fungsi sirene darurat; jika tidak, matikan suara.
</p>
</div>
<div class="bg-slate-900/80 p-5 rounded-xl border border-slate-800 space-y-3">
<div class="w-10 h-10 rounded-lg bg-purple-500/10 text-purple-400 flex items-center justify-center font-bold text-lg">2</div>
<h3 class="font-bold text-slate-200 text-sm">Fungsi tone() & sweep frekuensi</h3>
<p class="text-xs text-slate-400 leading-relaxed">
Mengubah frekuensi suara buzzer secara bertahap dari rendah ke tinggi lalu kembali lagi untuk menciptakan efek suara naik turun khas sirene (Wail).
</p>
</div>
<div class="bg-slate-900/80 p-5 rounded-xl border border-slate-800 space-y-3">
<div class="w-10 h-10 rounded-lg bg-rose-500/10 text-rose-400 flex items-center justify-center font-bold text-lg">3</div>
<h3 class="font-bold text-slate-200 text-sm">Sistem Peringatan Dini</h3>
<p class="text-xs text-slate-400 leading-relaxed">
Sangat penting dalam penerapan IoT industri untuk memberikan perhatian segera kepada operator ketika terjadi overheat.
</p>
</div>
</div>
</div>
</div>
<div id="view-evaluasi" class="hidden space-y-6">
<div class="glass-panel rounded-2xl p-6 space-y-6 max-w-4xl mx-auto">
<div class="border-b border-slate-800 pb-4 flex justify-between items-center">
<div>
<h2 class="text-xl font-bold text-white flex items-center gap-2">
<i class="fa-solid fa-graduation-cap text-emerald-400"></i> Kuis Evaluasi Sistem Sirene Suhu
</h2>
<p class="text-xs text-slate-400 mt-1">Jawab 5 pertanyaan pilihan ganda berikut untuk menguji pemahaman Anda.</p>
</div>
<span class="text-xs bg-emerald-500/20 text-emerald-400 px-3 py-1 rounded-full border border-emerald-500/30 font-semibold">5 Soal Pilihan Ganda</span>
</div>
<div id="quiz-container" class="space-y-6">
<!-- Soal 1 -->
<div class="bg-slate-900/90 p-5 rounded-xl border border-slate-800 space-y-3">
<div class="text-xs font-bold text-cyan-400">Pertanyaan 1 dari 5</div>
<p class="text-sm font-semibold text-slate-200">Struktur kontrol apa yang paling tepat digunakan untuk memicu sirene saat suhu mencapai 45°C?</p>
<div class="space-y-2 pt-1">
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q1" value="A" class="accent-cyan-500"> <span>A. Perulangan for loop</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q1" value="B" class="accent-cyan-500"> <span data-correct="true">B. Percabangan if (t >= 45.0) else</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q1" value="C" class="accent-cyan-500"> <span>C. Array multi-dimensi</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q1" value="D" class="accent-cyan-500"> <span>D. Deklarasi library SPI</span>
</label>
</div>
</div>
<!-- Soal 2 -->
<div class="bg-slate-900/90 p-5 rounded-xl border border-slate-800 space-y-3">
<div class="text-xs font-bold text-cyan-400">Pertanyaan 2 dari 5</div>
<p class="text-sm font-semibold text-slate-200">Bagaimana cara menciptakan efek suara naik-turun (sirene) pada buzzer Arduino?</p>
<div class="space-y-2 pt-1">
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q2" value="A" class="accent-cyan-500"> <span>A. Menggunakan perintah digitalWrite(PIN, HIGH) terus-menerus</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q2" value="B" class="accent-cyan-500"> <span data-correct="true">B. Mengubah nilai frekuensi secara bertahap menggunakan perulangan pada fungsi tone()</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q2" value="C" class="accent-cyan-500"> <span>C. Membaca sensor analog dengan analogRead()</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q2" value="D" class="accent-cyan-500"> <span>D. Mengosongkan memori SRAM Arduino</span>
</label>
</div>
</div>
<!-- Soal 3 -->
<div class="bg-slate-900/90 p-5 rounded-xl border border-slate-800 space-y-3">
<div class="text-xs font-bold text-cyan-400">Pertanyaan 3 dari 5</div>
<p class="text-sm font-semibold text-slate-200">Mode pin apa yang harus diset di dalam fungsi <code class="text-cyan-400">setup()</code> untuk pin yang terhubung ke Buzzer?</p>
<div class="space-y-2 pt-1">
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q3" value="A" class="accent-cyan-500"> <span>A. INPUT</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q3" value="B" class="accent-cyan-500"> <span data-correct="true">B. OUTPUT</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q3" value="C" class="accent-cyan-500"> <span>C. INPUT_PULLUP</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q3" value="D" class="accent-cyan-500"> <span>D. ANALOG_OUT</span>
</label>
</div>
</div>
<!-- Soal 4 -->
<div class="bg-slate-900/90 p-5 rounded-xl border border-slate-800 space-y-3">
<div class="text-xs font-bold text-cyan-400">Pertanyaan 4 dari 5</div>
<p class="text-sm font-semibold text-slate-200">Apa tujuan utama dari fitur peringatan sirene panas berlebih pada sistem IoT?</p>
<div class="space-y-2 pt-1">
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q4" value="A" class="accent-cyan-500"> <span>A. Mempercepat proses kompilasi kode</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q4" value="B" class="accent-cyan-500"> <span data-correct="true">B. Memberikan perhatian audio yang jelas dan respons cepat (early warning) terhadap potensi bahaya overheat</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q4" value="C" class="accent-cyan-500"> <span>C. Mengurangi konsumsi daya baterai secara drastis</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q4" value="D" class="accent-cyan-500"> <span>D. Menghentikan total aliran listrik utama</span>
</label>
</div>
</div>
<!-- Soal 5 -->
<div class="bg-slate-900/90 p-5 rounded-xl border border-slate-800 space-y-3">
<div class="text-xs font-bold text-cyan-400">Pertanyaan 5 dari 5</div>
<p class="text-sm font-semibold text-slate-200">Jika suhu terukur bernilai tepat 45.0°C, apakah sirene akan berbunyi berdasarkan kode program di atas?</p>
<div class="space-y-2 pt-1">
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q5" value="A" class="accent-cyan-500"> <span>A. Tidak, karena harus lebih dari 45.0°C (> 45)</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q5" value="B" class="accent-cyan-500"> <span data-correct="true">B. Ya, karena menggunakan operator lebih besar atau sama dengan (>= 45.0)</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q5" value="C" class="accent-cyan-500"> <span>C. Hanya jika kelembapan di bawah 30%</span>
</label>
<label class="flex items-center gap-3 p-2.5 rounded-lg bg-slate-950/60 border border-slate-800 hover:border-slate-700 cursor-pointer text-xs transition">
<input type="radio" name="q5" value="D" class="accent-cyan-500"> <span>D. Tergantung pada tombol Serial Monitor</span>
</label>
</div>
</div>
</div>
<div class="pt-2 text-center">
<button onclick="window.submitQuiz()" class="px-8 py-3 bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-sm rounded-xl shadow-lg shadow-emerald-900/40 transition">
<i class="fa-solid fa-check-double mr-2"></i> Periksa Jawaban Kuis
</button>
</div>
</div>
</div>
</main>
<footer class="border-t border-slate-800/80 bg-slate-900/40 py-4 px-4 text-center text-xs text-slate-500 flex flex-col md:flex-row items-center justify-between max-w-7xl mx-auto w-full gap-2">
<div>© Simulasi Sensor DHT11 & OLED v3.2 • Media Pembelajaran Interaktif Informatika</div>
<div class="flex items-center gap-4 text-slate-400 font-medium">
<span>IoT & Embedded Systems Lab</span>
<span>•</span>
<span class="hover:text-cyan-400 cursor-pointer transition" onclick="window.alertBox('Simulasi ini telah dilengkapi efek suara sirene darurat (naik-turun frekuensi) ketika suhu mencapai ambang batas (≥ 45°C).')">Tentang Fitur Sirene</span>
</div>
</footer>
<script>
let currentTemp = 28.0;
let currentHum = 65;
window.switchTab = function(tabId) {
const tabs = ['simulator', 'terminal', 'materi', 'evaluasi'];
tabs.forEach(t => {
const el = document.getElementById('view-' + t);
const btn = document.getElementById('nav-' + t);
if (t === tabId) {
el.classList.remove('hidden');
btn.className = 'px-3 py-1.5 rounded-lg bg-cyan-600 text-white font-medium flex items-center gap-2 shadow-md shadow-cyan-900/40 transition';
} else {
el.classList.add('hidden');
btn.className = 'px-3 py-1.5 rounded-lg bg-slate-800 text-slate-300 hover:bg-slate-700 font-medium flex items-center gap-2 transition';
}
});
};
window.alertBox = function(message) {
const modal = document.createElement('div');
modal.className = 'fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm p-4';
modal.innerHTML = `
<div class="glass-panel max-w-sm w-full p-6 rounded-2xl border border-slate-700 space-y-4 text-center shadow-2xl pop-animation">
<div class="w-12 h-12 rounded-full bg-cyan-500/20 text-cyan-400 flex items-center justify-center mx-auto text-xl">
<i class="fa-solid fa-circle-info"></i>
</div>
<div class="text-sm font-semibold text-slate-200">${message}</div>
<button onclick="this.closest('.fixed').remove()" class="w-full py-2.5 bg-cyan-600 hover:bg-cyan-500 text-white font-semibold text-xs rounded-xl transition">
Mengerti
</button>
</div>
`;
document.body.appendChild(modal);
};
// Update Environment Temperature & Humidity and Siren State
window.updateEnvironment = function(temp, hum) {
currentTemp = parseFloat(temp);
currentHum = parseInt(hum);
document.getElementById('temp-slider').value = currentTemp;
document.getElementById('hum-slider').value = currentHum;
document.getElementById('temp-display').textContent = currentTemp.toFixed(1) + '°C';
document.getElementById('hum-display').textContent = currentHum + '%';
document.getElementById('temp-status-display').textContent = currentTemp.toFixed(1) + '°C';
document.getElementById('hum-status-display').textContent = currentHum + '% RH';
document.getElementById('val-temp').textContent = currentTemp.toFixed(1) + '°C';
const bannerEl = document.getElementById('banner-container');
const statusPill = document.getElementById('system-status-pill');
const circuitDot = document.getElementById('circuit-dot');
const badgeBuzzer = document.getElementById('badge-buzzer-status');
const badgeOled = document.getElementById('badge-oled-status');
const oledSvgTemp = document.getElementById('oled-svg-temp');
const oledSvgHum = document.getElementById('oled-svg-hum');
const oledSvgStatus = document.getElementById('oled-svg-status');
const buzzerWave1 = document.getElementById('buzzer-wave-1');
const buzzerWave2 = document.getElementById('buzzer-wave-2');
const explanationBox = document.getElementById('explanation-box');
const explanationTitle = document.getElementById('explanation-title');
const explanationText = document.getElementById('informatics-explanation');
if (oledSvgTemp) oledSvgTemp.textContent = currentTemp.toFixed(1) + '°C';
if (oledSvgHum) oledSvgHum.textContent = currentHum + '%';
// Check if temperature exceeds 45°C threshold
if (currentTemp >= 45.0) {
// SIRENE STATE ACTIVE
bannerEl.className = 'glass-panel rounded-2xl p-4 md:p-5 flex flex-col md:flex-row justify-between items-start md:items-center gap-4 transition-all duration-300 alert-active bg-rose-950/40 border-rose-500/50';
statusPill.className = 'flex items-center gap-2 text-rose-400 text-xs font-semibold mb-1';
statusPill.innerHTML = '<i class="fa-solid fa-triangle-exclamation text-[10px] animate-bounce"></i> PERINGATAN: Suhu Mencapai Ambang Batas Sirene!';
circuitDot.className = 'w-2.5 h-2.5 rounded-full bg-rose-500 animate-ping';
badgeBuzzer.className = 'text-sm font-bold text-rose-400 mt-0.5 animate-pulse';
badgeBuzzer.textContent = 'SIRENE AKTIF!';
badgeOled.className = 'text-sm font-bold text-rose-400 mt-0.5';
badgeOled.textContent = 'Waspada Panas';
if (oledSvgStatus) {
oledSvgStatus.textContent = 'BAHAYA OVERHEAT';
oledSvgStatus.setAttribute('fill', '#f43f5e');
}
if (buzzerWave1) buzzerWave1.setAttribute('opacity', '1');
if (buzzerWave2) buzzerWave2.setAttribute('opacity', '1');
// Trigger Web Audio API Siren Sound Effect
window.playSirenSound();
explanationBox.className = 'glass-panel rounded-2xl p-5 space-y-2 border-l-4 border-l-rose-500 transition-all duration-300';
explanationTitle.className = 'text-xs font-bold text-rose-400 flex items-center gap-1.5';
explanationTitle.innerHTML = '<i class="fa-solid fa-triangle-exclamation"></i> STATUS SIRENE AKTIF (SUHU ≥ 45°C):';
explanationText.innerHTML = `Suhu terukur saat ini adalah <b>${currentTemp.toFixed(1)}°C</b>. Karena bernilai ≥ 45°C, kondisi percabangan <code class="text-rose-400">if (t >= 45.0)</code> terpenuhi: Buzzer memainkan efek suara sirene naik-turun (Wail Siren) dan layar OLED menampilkan peringatan <b>WASPADA PANAS BERLEBIH!</b>`;
} else {
// NORMAL STATE
bannerEl.className = 'glass-panel rounded-2xl p-4 md:p-5 flex flex-col md:flex-row justify-between items-start md:items-center gap-4 transition-all duration-300';
statusPill.className = 'flex items-center gap-2 text-cyan-400 text-xs font-semibold mb-1';
statusPill.innerHTML = '<i class="fa-solid fa-circle text-[8px] animate-pulse"></i> Sistem Normal: Memantau Suhu Lingkungan';
circuitDot.className = 'w-2.5 h-2.5 rounded-full bg-cyan-500 animate-pulse';
badgeBuzzer.className = 'text-sm font-bold text-slate-400 mt-0.5';
badgeBuzzer.textContent = 'OFF (Aman)';
badgeOled.className = 'text-sm font-bold text-emerald-400 mt-0.5';
badgeOled.textContent = 'Normal';
if (oledSvgStatus) {
oledSvgStatus.textContent = 'STATUS: AMAN';
oledSvgStatus.setAttribute('fill', '#10b981');
}
if (buzzerWave1) buzzerWave1.setAttribute('opacity', '0');
if (buzzerWave2) buzzerWave2.setAttribute('opacity', '0');
// Stop siren sound loop
window.stopSirenSound();
explanationBox.className = 'glass-panel rounded-2xl p-5 space-y-2 border-l-4 border-l-cyan-500 transition-all duration-300';
explanationTitle.className = 'text-xs font-bold text-cyan-400 flex items-center gap-1.5';
explanationTitle.innerHTML = '<i class="fa-solid fa-circle-info"></i> LOGIKA PERCABANGAN (IF-ELSE):';
explanationText.innerHTML = `Suhu terukur <b>${currentTemp.toFixed(1)}°C</b> (< 45°C). Mikrokontroler menjaga Buzzer tetap OFF dan menampilkan status AMAN pada layar OLED. Geser slider suhu ke 45°C atau lebih untuk menguji sirene.`;
}
};
// Web Audio API for Realistic Siren Sound Synthesis (Up and Down Frequency Sweep)
let audioCtx = null;
let sirenOscillator = null;
let sirenGain = null;
let sirenSweepInterval = null;
window.playSirenSound = function() {
if (sirenOscillator) return; // Already playing siren loop
try {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
sirenOscillator = audioCtx.createOscillator();
sirenGain = audioCtx.createGain();
sirenOscillator.type = 'sawtooth'; // Sawtooth gives a rich buzzer/siren harmonic tone
sirenOscillator.frequency.setValueAtTime(800, audioCtx.currentTime);
sirenGain.gain.setValueAtTime(0.12, audioCtx.currentTime);
sirenOscillator.connect(sirenGain);
sirenGain.connect(audioCtx.destination);
sirenOscillator.start();
// Sweep frequency up and down continuously (Emergency Siren Wail)
let goingUp = true;
let currentFreq = 800;
sirenSweepInterval = setInterval(() => {
if (!audioCtx || currentTemp < 45.0) {
window.stopSirenSound();
return;
}
if (goingUp) {
currentFreq += 120;
if (currentFreq >= 2400) goingUp = false;
} else {
currentFreq -= 120;
if (currentFreq <= 800) goingUp = true;
}
try {
sirenOscillator.frequency.setTargetAtTime(currentFreq, audioCtx.currentTime, 0.05);
} catch (e) {
console.error(e);
}
}, 80);
} catch (err) {
console.log("Web Audio API not allowed or blocked by browser policy until user interaction.", err);
}
};
window.stopSirenSound = function() {
if (sirenSweepInterval) {
clearInterval(sirenSweepInterval);
sirenSweepInterval = null;
}
if (sirenOscillator) {
try {
sirenOscillator.stop();
sirenOscillator.disconnect();
} catch (e) {}
sirenOscillator = null;
}
sirenGain = null;
};
window.runArduinoCode = function() {
const consoleEl = document.getElementById('serial-console');
consoleEl.innerHTML += `<div class="text-cyan-400">[Compiler] Uploading sketch & siren logic...</div>`;
consoleEl.scrollTop = consoleEl.scrollHeight;
setTimeout(() => {
consoleEl.innerHTML += `<div class="text-emerald-400">[Success] Upload complete! Pin 8 (Sirene Buzzer) & OLED ready.</div>`;
consoleEl.scrollTop = consoleEl.scrollHeight;
if (currentTemp >= 45.0) {
setTimeout(() => { logSerial(`SIRENE AKTIF! Suhu Panas Berlebih: ${currentTemp.toFixed(1)}°C`); }, 800);
} else {
setTimeout(() => { logSerial(`Suhu: ${currentTemp.toFixed(1)}°C | Kelembapan: ${currentHum}%`); }, 800);
}
}, 800);
};
function logSerial(msg) {
const consoleEl = document.getElementById('serial-console');
const colorClass = msg.includes('SIRENE') ? 'text-rose-400 font-bold' : 'text-slate-300';
consoleEl.innerHTML += `<div class="${colorClass}">[Serial] ${msg}</div>`;
consoleEl.scrollTop = consoleEl.scrollHeight;
}
window.clearConsole = function() {
document.getElementById('serial-console').innerHTML = `<div class="text-emerald-400">[System] Serial monitor cleared.</div>`;
};
window.sendSerialData = function() {
const val = document.getElementById('serial-input').value;
if (val.trim() !== '') {
logSerial(`Command sent: ${val}`);
document.getElementById('serial-input').value = '';
}
};
window.submitQuiz = function() {
let score = 0;
const totalQuestions = 5;
for (let i = 1; i <= totalQuestions; i++) {
const selected = document.querySelector(`input[name="q${i}"]:checked`);
if (selected) {
const parentLabel = selected.closest('label');
const spanCorrect = parentLabel.querySelector('[data-correct="true"]');
if (spanCorrect) {
score += 20;
}
}
}
const modal = document.createElement('div');
modal.className = 'fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-md p-4';
let badgeColor = score >= 80 ? 'text-emerald-400 bg-emerald-500/10 border-emerald-500/30' : (score >= 60 ? 'text-amber-400 bg-amber-500/10 border-amber-500/30' : 'text-rose-400 bg-rose-500/10 border-rose-500/30');
let feedbackText = score >= 80 ? 'Luar Biasa! Pemahaman logika percabangan sirene suhu Anda sangat baik.' : (score >= 60 ? 'Bagus! Terus tingkatkan pemahaman pemrograman embedded systems.' : 'Belajar lagi yuk! Cek menu Materi Teori.');
modal.innerHTML = `
<div class="glass-panel max-w-md w-full p-8 rounded-3xl border border-slate-700 space-y-6 text-center shadow-2xl pop-animation">
<div class="w-16 h-16 rounded-2xl bg-cyan-500/20 text-cyan-400 flex items-center justify-center mx-auto text-3xl shadow-inner">
<i class="fa-solid fa-award"></i>
</div>
<div>
<div class="text-xs font-bold uppercase tracking-wider text-slate-400">Hasil Evaluasi Kuis</div>
<h3 class="text-2xl font-bold text-white mt-1">Skor Akhir Anda</h3>
</div>
<div class="inline-block px-6 py-3 rounded-2xl border text-5xl font-black font-mono tracking-wider ${badgeColor} transition-transform transform hover:scale-105 duration-300">
${score} <span class="text-xl font-normal opacity-70">/ 100</span>
</div>
<p class="text-xs text-slate-300 leading-relaxed max-w-xs mx-auto">
${feedbackText}
</p>
<button onclick="this.closest('.fixed').remove()" class="w-full py-3 bg-cyan-600 hover:bg-cyan-500 text-white font-bold text-xs rounded-xl transition shadow-lg shadow-cyan-900/40">
Tutup & Kembali Belajar
</button>
</div>
`;
document.body.appendChild(modal);
};
</script>
</body>
</html>
0 Comments