Belajar membangun sistem monitoring udara realtime dari sensor fisik hingga dashboard web — lengkap dengan Machine Learning Decision Tree.
Download MateriSetelah workshop ini selesai, kamu akan bisa membangun sistem IoT end-to-end dari nol.
Begini cara semua komponen saling terhubung.
Setiap sesi dirancang fokus dan padat agar semua materi tersampaikan.
⏱️ 15 menit | 🌐 Konsep dasar dan gambaran sistem
Internet of Things (IoT) adalah konsep menghubungkan perangkat fisik ke internet agar bisa mengumpulkan, mengirim, dan menerima data secara otomatis — tanpa campur tangan manusia setiap saat.
⏱️ 20 menit | 🔌 Wiring, library, dan kode Arduino
// ── Ganti dengan data WiFi dan IP server kalian ── const char* WIFI_SSID = "NAMA_WIFI_ANDA"; const char* WIFI_PASSWORD = "PASSWORD_WIFI_ANDA"; const char* SERVER_URL = "http://192.168.1.100/goiot/api/sensor.php"; // ^^^^^^^^^^^ // Ganti dengan IP komputer kalian // Cek dengan: ipconfig (Windows) // ── Pin & Interval ── #define MQ135_PIN 34 // ADC Pin #define DHT_PIN 4 // Data Pin DHT11 const unsigned long SEND_INTERVAL = 10000; // 10 detik
void readAndSendData() { // Baca MQ135 — ADC 12-bit: nilai 0 sampai 4095 int rawMQ135 = analogRead(MQ135_PIN); // Konversi ke ppm (kalibrasi linear sederhana) float mq135 = map(rawMQ135, 0, 4095, 0, 500); // Baca suhu dan kelembaban dari DHT11 float suhu = dht.readTemperature(); float kelembaban = dht.readHumidity(); // Validasi — DHT11 kadang gagal baca if (isnan(suhu) || isnan(kelembaban)) { Serial.println("[ERROR] Gagal baca DHT11!"); return; } // Kirim ke server PHP sendToServer(mq135, suhu, kelembaban); } void sendToServer(float mq135, float suhu, float kelembaban) { // Buat JSON payload StaticJsonDocument<200> doc; doc["mq135"] = mq135; doc["suhu"] = suhu; doc["kelembaban"] = kelembaban; String payload; serializeJson(doc, payload); // → {"mq135":85.2,"suhu":28.0,"kelembaban":65.0} // HTTP POST HTTPClient http; http.begin(SERVER_URL); http.addHeader("Content-Type", "application/json"); int httpCode = http.POST(payload); }
⏱️ 20 menit | 🖥️ XAMPP, MySQL, dan REST API
localhost/phpmyadmin di browser.goiot → klik Create.database.sql → klik Go.CREATE TABLE sensor_data ( id INT AUTO_INCREMENT PRIMARY KEY, mq135 FLOAT NOT NULL, -- Nilai polutan (ppm) suhu FLOAT NOT NULL, -- Suhu Celsius kelembaban FLOAT NOT NULL, -- Kelembaban % prediksi VARCHAR(20), -- Baik / Sedang / Buruk timestamp DATETIME DEFAULT CURRENT_TIMESTAMP );
define('DB_HOST', 'localhost'); define('DB_USER', 'root'); // default XAMPP define('DB_PASS', ''); // default XAMPP (kosong) define('DB_NAME', 'goiot');
// 1. Terima JSON dari ESP32 $body = json_decode(file_get_contents('php://input'), true); $mq135 = (float) $body['mq135']; $suhu = (float) $body['suhu']; $kelembaban = (float) $body['kelembaban']; // 2. Jalankan Decision Tree → dapat label prediksi $prediksi = DecisionTree::predict($mq135, $suhu, $kelembaban); // 3. Simpan ke database $model->insert($mq135, $suhu, $kelembaban, $prediksi['label']); // 4. Kirim response JSON ke ESP32 echo json_encode(['status' => 'success', 'prediksi' => $prediksi]);
| Method | URL | Fungsi |
|---|---|---|
| POST | /api/sensor.php | Terima data dari ESP32 |
| GET | /api/data.php?type=latest | 1 data terbaru (realtime) |
| GET | /api/data.php?type=stats | Statistik ringkasan |
| GET | /api/data.php?type=chart | Data grafik 24 jam |
| GET | /api/data.php?type=analysis | Analisis 7 hari |
| GET | /api/predict.php?mq135=120&suhu=30&kelembaban=70 | Prediksi manual |
⏱️ 25 menit | 🤖 Machine Learning untuk klasifikasi udara
Decision Tree adalah algoritma ML yang membuat keputusan seperti diagram alur. Setiap node adalah pertanyaan, setiap cabang adalah jawaban, dan setiap daun adalah hasil akhir. Analogi: seperti kunci determinasi biologi.
| MQ135 (ppm) | Suhu (°C) | Kelembaban (%) | Hasil | Confidence |
|---|---|---|---|---|
| < 100 | < 30 | — | ✅ Baik | 95% |
| < 100 | ≥ 30 | — | ⚠️ Sedang | 80% |
| 100 – 129 | — | < 70 | ⚠️ Sedang | 85% |
| 100 – 129 | — | ≥ 70 | ⚠️ Sedang | 70% |
| ≥ 130 | — | — | 🚨 Buruk | 88–95% |
public static function predict(float $mq135, float $suhu, float $kelembaban): array { if ($mq135 < 100) { // Polutan rendah — cek suhu sebagai faktor sekunder if ($suhu < 30) { return self::result('Baik', 95.0, 'MQ135 rendah dan suhu normal'); } else { return self::result('Sedang', 80.0, 'MQ135 rendah tapi suhu tinggi'); } } elseif ($mq135 < 130) { // Polutan sedang — kelembaban memperburuk kondisi if ($kelembaban < 70) { return self::result('Sedang', 85.0, 'MQ135 sedang, kelembaban normal'); } else { return self::result('Sedang', 70.0, 'MQ135 sedang + kelembaban tinggi'); } } else { // Polutan tinggi — langsung Buruk return self::result('Buruk', 95.0, 'MQ135 melewati batas aman'); } }
⏱️ 25 menit | 📊 Frontend, AJAX, dan Chart.js
document.addEventListener('DOMContentLoaded', () => { initCharts(); // Buat chart kosong dulu fetchAll(); // Fetch data pertama kali setInterval(fetchAll, 5000); // Ulangi setiap 5 detik }); async function fetchAll() { // Jalankan semua fetch secara paralel (lebih cepat) await Promise.all([ fetchLatest(), fetchStats(), fetchChartData(), fetchTableData(), fetchAnalysis(), ]); }
async function fetchLatest() { try { // Panggil API — async/await lebih mudah dibaca dari .then() const res = await fetch(`${API_BASE}/data.php?type=latest`); const json = await res.json(); if (json.status !== 'success' || !json.data) return; const d = json.data; // Update elemen HTML berdasarkan id document.getElementById('val-mq135').textContent = parseFloat(d.mq135).toFixed(1); document.getElementById('val-suhu').textContent = parseFloat(d.suhu).toFixed(1); } catch (e) { console.error('fetchLatest error:', e); } }
| Masalah | Penyebab | Solusi |
|---|---|---|
| Data tidak tampil | Path API salah | Cek API_BASE di dashboard.js |
| Grafik kosong | Belum ada data 24 jam | Insert data dummy dari database.sql |
| ESP32 tidak konek | IP server salah | Cek ipconfig, pastikan satu jaringan |
| DHT11 error | Wiring / resistor kurang | Cek pin dan pasang resistor 10kΩ |
| 500 Error API | Koneksi DB gagal | Cek config/database.php |
⏱️ 15 menit | 🚀 Demo end-to-end dan pengembangan lanjutan
# Kirim data simulasi ke server curl -X POST http://localhost/goiot/api/sensor.php \ -H "Content-Type: application/json" \ -d '{"mq135": 145, "suhu": 32, "kelembaban": 78}'
| Ide | Teknologi | Kesulitan |
|---|---|---|
| Notifikasi WhatsApp saat udara buruk | Fonnte / Twilio API | Mudah |
| Simpan data ke cloud | Firebase / AWS IoT | Menengah |
| Model ML yang lebih akurat | Python scikit-learn | Menengah |
| Tambah sensor CO2 (MH-Z19) | Arduino library | Mudah |
| Export data ke Excel | PHP PhpSpreadsheet | Mudah |
| Autentikasi login dashboard | PHP Session / JWT | Menengah |
| Prediksi dengan Random Forest | Python + PHP bridge | Lanjutan |