Sabtu, 11 April 2026

PESAN DARI CLOUD Papan Pesan Digital IoT dengan NodeMCU ESP8266 & OLED

Membaca Pesan dari Database ke OLED dengan ESP8266

🖥️ Menampilkan Pesan dari Database MySQL ke Layar OLED (ESP8266)

Tanggal: April 2026  |  Oleh: Penulis Blog

Beberapa waktu lalu saya ingin membuat papan informasi kecil yang bisa saya update dari jarak jauh tanpa harus memprogram ulang perangkat. Akhirnya saya membuat proyek sederhana ini: ESP8266 membaca pesan dari database MySQL melalui API, lalu menampilkannya ke layar OLED 0.96 inci. Artikel ini adalah catatan perjalanan saya, bukan template massal. Saya harap bisa menginspirasi Anda yang sedang belajar IoT.

📌 Catatan: Proyek ini saya buat untuk keperluan belajar dan penggunaan pribadi. Tidak ada afiliasi dengan merek atau hosting tertentu.

🛠️ Komponen yang Saya Gunakan

  • NodeMCU ESP8266 (versi CH340)
  • Layar OLED 0.96" I2C (SSD1306)
  • Kabel jumper Female to Female (4 buah)
  • Kabel micro USB untuk power dan upload
  • Hosting + domain pribadi (bisa juga menggunakan free hosting seperti 000webhost untuk uji coba)

🔌 Skema Koneksi (Hasil Eksperimen Saya)

Setelah beberapa kali gagal karena salah pin, akhirnya skema yang berhasil adalah:

  • OLED VCC → NodeMCU 3.3V (jangan ke 5V!)
  • OLED GND → NodeMCU GND
  • OLED SCL → NodeMCU D1 (GPIO5)
  • OLED SDA → NodeMCU D2 (GPIO4)
⚠️ Peringatan dari pengalaman pahit: Saya pernah merusak 2 buah OLED karena menyambung ke pin 5V. Pastikan tegangan 3.3V!

🗄️ Persiapan Database dan API

Saya membuat database MySQL dengan satu tabel sederhana bernama tbl_pesan. Berikut struktur yang saya gunakan:

CREATE TABLE tbl_pesan (
    id INT AUTO_INCREMENT PRIMARY KEY,
    isi_pesan TEXT NOT NULL,
    pengirim VARCHAR(50),
    waktu_dibuat TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Kemudian saya buat file PHP bernama ambil_pesan.php di hosting. Isinya kurang lebih seperti ini (sederhana, tanpa embel-embel berlebihan):

<?php
header("Content-Type: application/json");
header("Access-Control-Allow-Origin: *");

$koneksi = new mysqli("localhost", "username_anda", "password_anda", "nama_database");

if ($koneksi->connect_error) {
    echo json_encode(["sukses" => false, "error" => "Koneksi gagal"]);
    exit;
}

$hasil = $koneksi->query("SELECT isi_pesan, pengirim FROM tbl_pesan ORDER BY id DESC LIMIT 1");

if ($hasil->num_rows > 0) {
    $data = $hasil->fetch_assoc();
    echo json_encode(["sukses" => true, "pesan" => $data["isi_pesan"], "dari" => $data["pengirim"]]);
} else {
    echo json_encode(["sukses" => false, "pesan" => "Belum ada pesan"]);
}
?>

📡 Kode Program untuk ESP8266

Setelah API siap, saya menulis kode Arduino IDE berikut. Saya menggunakan library Adafruit SSD1306, Adafruit GFX, ArduinoJson, dan ESP8266WiFi.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

const char* ssid = "WIFI_ANDA";
const char* password = "PASSWORD_WIFI";
const char* apiURL = "http://domainanda.com/ambil_pesan.php";

String pesanSaatIni = "Menunggu pesan...";
String pengirim = "Sistem";

void ambilPesan() {
  if (WiFi.status() != WL_CONNECTED) return;
  
  HTTPClient http;
  WiFiClient client;
  http.begin(client, apiURL);
  int kodeHTTP = http.GET();
  
  if (kodeHTTP == 200) {
    String response = http.getString();
    DynamicJsonDocument doc(512);
    deserializeJson(doc, response);
    
    if (doc["sukses"]) {
      pesanSaatIni = doc["pesan"].as<String>();
      pengirim = doc["dari"].as<String>();
    }
  }
  http.end();
}

void tampilkanKeOLED() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println("Pesan Masuk:");
  display.drawLine(0, 9, 128, 9, WHITE);
  
  display.setCursor(0, 12);
  display.print("Dari: ");
  display.println(pengirim);
  
  display.setCursor(0, 24);
  // Membatasi panjang pesan agar tidak keluar layar
  String teks = pesanSaatIni;
  if (teks.length() > 48) teks = teks.substring(0, 45) + "...";
  display.println(teks);
  
  display.display();
}

void setup() {
  Serial.begin(115200);
  
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("Gagal init OLED");
    for(;;);
  }
  display.clearDisplay();
  
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Terhubung ke WiFi");
  
  ambilPesan();
  tampilkanKeOLED();
}

void loop() {
  static unsigned long waktuTerakhir = 0;
  if (millis() - waktuTerakhir > 30000) { // Update tiap 30 detik
    ambilPesan();
    tampilkanKeOLED();
    waktuTerakhir = millis();
  }
  delay(100);
}

🐞 Kendala yang Saya Alami (dan Solusinya)

Selama membuat proyek ini, saya beberapa kali hampir menyerah. Berikut catatan masalah yang saya temui:

  • OLED tidak menyala: Ternyata alamat I2C saya bukan 0x3C tetapi 0x3D. Cek menggunakan scanner I2C.
  • ESP8266 tidak bisa konek WiFi: Sinyal router terlalu lemah, saya pindahkan perangkat lebih dekat.
  • JSON parsing error: File PHP saya mengeluarkan warning (spasi kosong). Saya bersihkan semua whitespace di luar tag PHP.
  • Port COM tidak terdeteksi: Driver CH340 belum terinstal di Windows.

✨ Pengembangan yang Saya Lakukan Setelahnya

Setelah proyek dasar berhasil, saya menambahkan beberapa fitur sesuai kebutuhan:

  • Menampilkan semua pesan bergantian (rolling) dengan interval 7 detik.
  • Menambahkan indikator koneksi WiFi di pojok OLED.
  • Membuat form web sederhana untuk mengirim pesan tanpa harus masuk phpMyAdmin.

📚 Penutup

Proyek ini mengajarkan saya banyak hal: dari koneksi hardware, pemrograman ESP8266, hingga membuat API dengan PHP. Jika Anda ingin mencoba, saya sarankan untuk memahami setiap baris kode dan menyesuaikan dengan kebutuhan Anda sendiri, bukan sekadar copy-paste. Selamat bereksperimen!

Artikel ini adalah dokumentasi pribadi. Silakan bagikan jika bermanfaat, tapi jangan dijadikan konten massal atau spam.

Kamis, 09 April 2026

Membuat WiFi Scanner Portabel dengan NodeMCU ESP8266 dan Layar OLED

WiFi Scanner Portabel dengan NodeMCU ESP8266 dan OLED | Tutorial Lengkap

📡 MEMBUAT WIFI SCANNER PORTABEL
dengan NodeMCU ESP8266 dan Layar OLED 0.96"


📡 Apa Itu WiFi Scanner?

WiFi scanner adalah alat atau perangkat lunak yang digunakan untuk mendeteksi dan menampilkan semua jaringan WiFi yang tersedia di sekitar suatu lokasi. Alat ini menampilkan informasi penting seperti SSID (nama jaringan), BSSID (MAC address access point), RSSI (kekuatan sinyal dalam dBm), channel yang digunakan, serta jenis enkripsi (WPA2, WPA3, WEP, atau Open).

Dalam proyek kali ini, kita akan membuat WiFi scanner portabel berukuran saku yang dapat Anda bawa ke mana saja. Berbeda dengan scanner di smartphone yang terbatas, alat buatan sendiri ini memberikan Anda kendali penuh atas data yang ditampilkan dan dapat dikustomisasi sesuai kebutuhan.

💡 Fakta Menarik: Istilah "wardriving" populer pada awal 2000-an, dimana para penggemar WiFi berkeliling kota dengan laptop dan antena untuk memetakan jaringan WiFi. Sekarang, Anda bisa melakukannya dengan alat sekecil genggaman tangan!

🎯 Mengapa Anda Membutuhkan Alat Ini?

Berikut adalah 7 skenario nyata di mana WiFi scanner portabel sangat berguna:

  • 🔍 Survei Lokasi Pemasangan WiFi - Sebelum memasang access point di rumah atau kantor, Anda bisa scan dulu area mana yang sinyalnya paling kuat.
  • 🏢 Audit Keamanan Jaringan - Deteksi jaringan terbuka (tanpa password) yang berpotensi disalahgunakan.
  • 📊 Analisis Interferensi Channel - Lihat channel apa yang paling padat, lalu pilih channel yang lebih sepi untuk router Anda.
  • 🎓 Belajar IoT & Pemrograman ESP8266 - Proyek sempurna untuk pemula karena langsung ada hasil visualnya.
  • 🛠 Troubleshooting WiFi - Cek apakah tetangga menggunakan channel yang sama dengan router Anda.
  • 📱 Membandingkan Kualitas ISP - Scan di berbagai lokasi untuk melihat provider mana yang paling banyak tersedia.
  • 🚗 WarDriving (Pemetaan WiFi) - Buat peta sebaran WiFi di kota Anda untuk proyek komunitas.

🛠 Alat dan Bahan (Lengkap dengan Estimasi Biaya)

Semua komponen mudah didapatkan di Tokopedia, Shopee, atau toko elektronik lokal. Total biaya sekitar Rp 150.000 - Rp 250.000.

KomponenSpesifikasiFungsiHarga (Rp)
NodeMCU ESP8266ESP-12E, CP2102/CH340Otak alat + WiFi60.000 - 85.000
Layar OLED 0.96"128x64 pixel, I2C, SSD1306Menampilkan hasil scan40.000 - 65.000
Kabel Jumper F-F20cm, 4-5 buahKoneksi5.000 - 10.000
Power Bank (opsional)5V 1A outputPower portabel50.000 - 100.000
Kabel Micro USBUntuk data & powerUpload program15.000 - 30.000
Project Box (opsional)6x9 cmTempat/casing10.000 - 25.000
💡 Tips Hemat: Jika sudah memiliki power bank dan kabel USB, Anda hanya perlu membeli NodeMCU, OLED, dan kabel jumper (total sekitar Rp 110.000)!

🔌 Skema Koneksi (Wiring Diagram)

Layar OLED 0.96" yang menggunakan protokol I2C hanya membutuhkan 4 kabel. Berikut adalah tabel koneksi yang harus Anda buat:

Layar OLEDNodeMCU ESP8266GPIOKeterangan
VCC3.3V-Tegangan 3.3 Volt (JANGAN 5V!)
GNDGND-Ground / Tanah
SCLD1GPIO5Clock I2C
SDAD2GPIO4Data I2C
⚠️ PERINGATAN PENTING: NodeMCU ESP8266 menggunakan logika 3.3 VOLT. Pastikan layar OLED Anda mendukung 3.3V. Jika tidak yakin, periksa datasheet. Menyambungkan ke pin 5V (misalnya dari Arduino Uno) dapat merusak layar secara permanen!

⚙️ Persiapan Arduino IDE

Sebelum memprogram, Anda perlu menambahkan board ESP8266 ke Arduino IDE. Berikut langkah-langkahnya:

Langkah 1: Tambahkan URL Board Manager

Buka Arduino IDE → File → Preferences. Pada kolom "Additional Boards Manager URLs", tambahkan URL berikut:

https://arduino.esp8266.com/stable/package_esp8266com_index.json

Langkah 2: Instal Board ESP8266

Klik Tools → Board → Boards Manager. Cari "esp8266", lalu instal "esp8266 by ESP8266 Community".

Langkah 3: Pilih Board NodeMCU

Setelah instalasi selesai, pilih board: Tools → Board → ESP8266 Boards → NodeMCU 1.0 (ESP-12E Module)

Langkah 4: Pilih Port COM

Hubungkan NodeMCU ke komputer, lalu pilih port yang sesuai di Tools → Port (misal COM3, COM4, atau /dev/cu.usbserial-xxx).

💡 Driver CH340/CP2102: Jika port tidak muncul, Anda mungkin perlu menginstal driver USB-to-serial. Cari "Driver CH340" atau "Driver CP2102" sesuai chip di board Anda.

📚 Instalasi Library yang Diperlukan

Buka Sketch → Include Library → Manage Libraries, lalu cari dan instal:

LibraryNama PencarianFungsi
Adafruit GFX"Adafruit GFX Library"Fungsi grafis dasar (lingkaran, garis, persegi)
Adafruit SSD1306"Adafruit SSD1306"Driver khusus untuk layar OLED SSD1306
ESP8266WiFi(Built-in, sudah ada)Fungsi koneksi dan scan WiFi

Catatan: Library Adafruit SSD1306 mungkin meminta instalasi dependensi. Setujui semua permintaan instalasi tambahan.

💻 Kode Program LENGKAP (Copy-Paste)

Berikut adalah kode program lengkap yang siap diupload ke NodeMCU. Kode ini berukuran sekitar 250 baris dan sudah mencakup semua fitur yang dijelaskan.

/*
    ================================================================
    PROJECT: WiFi Scanner Portabel dengan NodeMCU ESP8266 + OLED
    AUTHOR: Tutorial IoT
    VERSION: 2.0 (Lengkap dengan fitur sorting, icon signal, dll)
    ================================================================
    
    Fitur:
    - Scan otomatis setiap 10 detik
    - Menampilkan SSID, RSSI (dBm), dan jenis keamanan
    - Icon bar sinyal (1-4 bar)
    - Icon gembok untuk jaringan terenkripsi
    - Sorting dari sinyal terkuat ke terlemah
    - Pagination (3 jaringan per halaman)
    - Tampilan jam digital di bagian atas (opsional dengan NTP)
*/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP8266WiFi.h>

// ========== KONFIGURASI OLED ==========
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
#define OLED_ADDRESS  0x3C   // Coba 0x3D jika tidak muncul

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ========== VARIABEL GLOBAL ==========
unsigned long lastScan = 0;
int scanInterval = 10000;      // Scan setiap 10 detik (10000 ms)
int currentPage = 0;
int totalNetworks = 0;
int networksPerPage = 3;       // Jumlah jaringan per halaman

// Array penyimpanan data (maks 20 jaringan)
String networkNames[20];
int networkRSSI[20];
int networkEncryption[20];
String networkBSSID[20];
int networkChannel[20];

// ========== FUNGSI SCAN JARINGAN ==========
void scanWiFi() {
    // Tampilkan pesan scanning
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(25, 28);
    display.println("Scanning WiFi...");
    display.display();
    
    // Lakukan scan (mode aktif)
    int n = WiFi.scanNetworks();
    
    if (n == 0) {
        display.clearDisplay();
        display.setCursor(20, 28);
        display.println("No networks found");
        display.display();
        delay(2000);
        return;
    }
    
    // Batasi maksimal 20 jaringan
    totalNetworks = min(n, 20);
    
    // Simpan hasil scan ke array
    for (int i = 0; i < totalNetworks; i++) {
        networkNames[i] = WiFi.SSID(i);
        if (networkNames[i] == "") networkNames[i] = "(Hidden Network)";
        
        networkRSSI[i] = WiFi.RSSI(i);
        networkEncryption[i] = WiFi.encryptionType(i);
        networkBSSID[i] = WiFi.BSSIDstr(i);
        networkChannel[i] = WiFi.channel(i);
    }
    
    // ===== SORTING: Urutkan dari sinyal TERKUAT ke TERLEMAH =====
    for (int i = 0; i < totalNetworks - 1; i++) {
        for (int j = i + 1; j < totalNetworks; j++) {
            if (networkRSSI[j] > networkRSSI[i]) {
                // Tukar SSID
                String tempName = networkNames[i];
                networkNames[i] = networkNames[j];
                networkNames[j] = tempName;
                
                // Tukar RSSI
                int tempRSSI = networkRSSI[i];
                networkRSSI[i] = networkRSSI[j];
                networkRSSI[j] = tempRSSI;
                
                // Tukar enkripsi
                int tempEnc = networkEncryption[i];
                networkEncryption[i] = networkEncryption[j];
                networkEncryption[j] = tempEnc;
                
                // Tukar BSSID
                String tempBSSID = networkBSSID[i];
                networkBSSID[i] = networkBSSID[j];
                networkBSSID[j] = tempBSSID;
                
                // Tukar channel
                int tempCh = networkChannel[i];
                networkChannel[i] = networkChannel[j];
                networkChannel[j] = tempCh;
            }
        }
    }
    
    // Reset ke halaman pertama
    currentPage = 0;
    
    // Hapus hasil scan dari memory (hemat RAM)
    WiFi.scanDelete();
}

// ========== FUNGSI IKON KEKUATAN SINYAL ==========
void drawSignalIcon(int x, int y, int rssi) {
    int bars = 0;
    
    // Konversi RSSI ke jumlah bar (1-4)
    if (rssi > -50) bars = 4;      // Excellent
    else if (rssi > -60) bars = 3; // Good
    else if (rssi > -70) bars = 2; // Fair
    else if (rssi > -80) bars = 1; // Weak
    else bars = 0;                  // Very Weak
    
    // Gambar bar sinyal
    for (int i = 0; i < 4; i++) {
        int barHeight = (i + 1) * 2;
        int barX = x + (i * 3);
        int barY = y + 8 - barHeight;
        
        if (i < bars) {
            display.fillRect(barX, barY, 2, barHeight, SSD1306_WHITE);
        } else {
            display.drawRect(barX, barY, 2, barHeight, SSD1306_WHITE);
        }
    }
}

// ========== FUNGSI IKON KEAMANAN ==========
void drawSecurityIcon(int x, int y, int encryptionType) {
    switch(encryptionType) {
        case ENC_TYPE_TKIP:   // WPA
        case ENC_TYPE_CCMP:   // WPA2
        case ENC_TYPE_AUTO:   // WPA/WPA2
            // Gembok TERTUTUP (terenkripsi)
            display.drawRect(x, y + 2, 5, 5, SSD1306_WHITE);
            display.fillRect(x + 1, y + 1, 3, 2, SSD1306_BLACK);
            display.fillRect(x + 2, y + 3, 1, 2, SSD1306_WHITE);
            break;
        case ENC_TYPE_WEP:    // WEP (lama, tidak aman)
            display.drawRect(x, y + 2, 5, 5, SSD1306_WHITE);
            display.drawLine(x + 1, y + 1, x + 3, y + 3, SSD1306_WHITE);
            break;
        default:               // OPEN (tanpa password)
            display.drawRect(x, y + 2, 5, 5, SSD1306_WHITE);
            display.drawLine(x + 2, y + 1, x + 2, y + 3, SSD1306_WHITE);
            break;
    }
}

// ========== FUNGSI MENAMPILKAN HASIL SCAN ==========
void displayScanResults() {
    display.clearDisplay();
    
    // ===== HEADER =====
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.println("WiFi Scanner v2.0");
    display.drawLine(0, 9, 128, 9, SSD1306_WHITE);
    
    // ===== ISI (3 jaringan per halaman) =====
    int startIdx = currentPage * networksPerPage;
    int endIdx = min(startIdx + networksPerPage, totalNetworks);
    
    for (int i = startIdx; i < endIdx; i++) {
        int yPos = 12 + ((i - startIdx) * 16);
        
        // Icon sinyal
        drawSignalIcon(5, yPos + 2, networkRSSI[i]);
        
        // Nama SSID (potong jika terlalu panjang)
        String ssid = networkNames[i];
        if (ssid.length() > 12) {
            ssid = ssid.substring(0, 10) + "..";
        }
        display.setCursor(18, yPos);
        display.print(ssid);
        
        // Icon keamanan
        drawSecurityIcon(110, yPos + 2, networkEncryption[i]);
        
        // Nilai RSSI (dBm)
        display.setCursor(18, yPos + 8);
        display.setTextSize(0.8);
        display.print(String(networkRSSI[i]) + " dBm");
        display.setTextSize(1);
    }
    
    // ===== FOOTER (info halaman) =====
    if (totalNetworks > 0) {
        display.setCursor(0, 56);
        display.print("Page ");
        display.print(currentPage + 1);
        display.print("/");
        int totalPages = (totalNetworks + networksPerPage - 1) / networksPerPage;
        display.print(totalPages);
        display.print("  ");
        display.print(totalNetworks);
        display.print(" nets");
        
        // Indikator scan berikutnya
        int remaining = (scanInterval - (millis() - lastScan)) / 1000;
        if (remaining < 0) remaining = 0;
        display.setCursor(85, 56);
        display.print("Next:");
        display.print(remaining);
        display.print("s");
    }
    
    display.display();
}

// ========== SPLASH SCREEN AWAL ==========
void showSplashScreen() {
    display.clearDisplay();
    display.setTextSize(2);
    display.setCursor(15, 12);
    display.println("WiFi");
    display.setCursor(10, 32);
    display.println("Scanner");
    display.setTextSize(1);
    display.setCursor(18, 52);
    display.println("NodeMCU + OLED");
    display.display();
    delay(2500);
}

// ========== SETUP ==========
void setup() {
    Serial.begin(115200);
    Serial.println("\nWiFi Scanner Tool Starting...");
    
    // Inisialisasi OLED
    if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
        Serial.println("OLED gagal diinisialisasi! Cek koneksi.");
        for(;;); // Hentikan program jika OLED tidak terdeteksi
    }
    
    display.clearDisplay();
    showSplashScreen();
    
    // Set WiFi ke mode station (bukan access point)
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);
    
    Serial.println("NodeMCU siap melakukan scan WiFi");
    
    // Scan pertama kali
    scanWiFi();
    lastScan = millis();
}

// ========== LOOP UTAMA ==========
void loop() {
    unsigned long now = millis();
    
    // Scan ulang setiap interval
    if (now - lastScan > scanInterval) {
        scanWiFi();
        lastScan = now;
    }
    
    // Tampilkan hasil
    if (totalNetworks > 0) {
        displayScanResults();
    } else {
        display.clearDisplay();
        display.setCursor(15, 28);
        display.println("No WiFi found");
        display.display();
    }
    
    // ===== NAVIGASI TOMBOL (Opsional) =====
    // Jika Anda menambahkan tombol fisik di pin D3 dan D4, 
    // uncomment kode di bawah ini:
    /*
    pinMode(0, INPUT_PULLUP);  // D3 untuk NEXT
    pinMode(2, INPUT_PULLUP);  // D4 untuk PREV
    
    if (digitalRead(0) == LOW) {
        delay(200);
        int totalPages = (totalNetworks + networksPerPage - 1) / networksPerPage;
        currentPage = (currentPage + 1) % totalPages;
    }
    
    if (digitalRead(2) == LOW) {
        delay(200);
        int totalPages = (totalNetworks + networksPerPage - 1) / networksPerPage;
        currentPage = (currentPage - 1 + totalPages) % totalPages;
    }
    */
    
    delay(500); // Refresh rate 0.5 detik
}

📖 Penjelasan Kode (DETAIL Setiap Bagian)

Agar Anda bisa memodifikasi dan memahami program, berikut penjelasan mendetail setiap fungsi:

1. Bagian Header dan Library

#include <Wire.h> : Library untuk komunikasi I2C antara NodeMCU dan OLED.
#include <Adafruit_GFX.h> : Library grafis yang menyediakan fungsi menggambar seperti drawCircle(), drawLine(), fillRect().
#include <Adafruit_SSD1306.h> : Driver spesifik untuk layar OLED SSD1306.
#include <ESP8266WiFi.h> : Library bawaan ESP8266 untuk semua fungsi WiFi, termasuk scan jaringan.

2. Konfigurasi Layar OLED

#define SCREEN_WIDTH 128 dan SCREEN_HEIGHT 64 : Resolusi layar OLED (umum untuk model 0.96 inch).
#define OLED_ADDRESS 0x3C : Alamat I2C default layar OLED. Jika tidak merespon, coba ganti ke 0x3D.

3. Variabel Global

scanInterval = 10000 : Interval scan ulang dalam milidetik (10 detik).
networksPerPage = 3 : Jumlah jaringan yang ditampilkan per halaman (karena tinggi layar terbatas).
Array networkNames[20], networkRSSI[20], dll : Menyimpan data hingga 20 jaringan.

4. Fungsi scanWiFi()

Fungsi ini adalah jantung dari program. Cara kerjanya:

  • WiFi.scanNetworks() - Melakukan scan sinkronus, mengembalikan jumlah jaringan ditemukan.
  • Hasil scan disimpan ke array menggunakan WiFi.SSID(i), WiFi.RSSI(i), dll.
  • Jaringan tersembunyi (SSID kosong) diberi label "(Hidden Network)".
  • Algoritma Bubble Sort - Mengurutkan jaringan dari RSSI tertinggi (sinyal terkuat) ke terendah.
  • WiFi.scanDelete() - Membersihkan memory agar tidak bocor (memory leak).

5. Fungsi drawSignalIcon()

Menggambar bar sinyal (1-4 bar) berdasarkan nilai RSSI:

  • RSSI > -50 dBm → 4 bar (Excellent)
  • RSSI -50 s/d -60 dBm → 3 bar (Good)
  • RSSI -60 s/d -70 dBm → 2 bar (Fair)
  • RSSI -70 s/d -80 dBm → 1 bar (Weak)
  • RSSI < -80 dBm → 0 bar (Very Weak)

6. Fungsi drawSecurityIcon()

Menggambar ikon gembok untuk menunjukkan jenis keamanan:

  • Gembok Tertutup → WPA/WPA2 (aman)
  • Gembok dengan garis miring → WEP (lama, kurang aman)
  • Gembok Terbuka → Tanpa password (Open)

7. Fungsi displayScanResults()

Menampilkan hasil scan ke layar OLED dengan format:

  • Header "WiFi Scanner v2.0" dan garis pemisah
  • 3 jaringan per halaman (sesuai networksPerPage)
  • Footer: nomor halaman, total jaringan, dan countdown scan berikutnya

8. Fungsi loop()

Loop utama yang berjalan terus:

  • Mengecek apakah sudah waktunya scan ulang (millis() - lastScan > scanInterval)
  • Memanggil displayScanResults() untuk memperbarui tampilan
  • Delay 500ms untuk menghemat resource (tidak perlu refresh terlalu cepat)

📱 Hasil dan Tampilan

Setelah program berhasil diupload, layar OLED akan menampilkan:

📋 Contoh Tampilan:

WiFi Scanner v2.0
────────────────
▂▃▄▅ HomeWiFi 🔒
-45 dBm
▂▃▄ Indihome_2G 🔒
-58 dBm
▂▃ WiFi Publik 🔓
-72 dBm
────────────────
Page 1/3 8 nets Next:5s

Informasi yang ditampilkan untuk setiap jaringan:

  • Ikon bar : Kekuatan sinyal secara visual
  • SSID : Nama jaringan (dipotong jika >12 karakter)
  • Ikon gembok : Jenis keamanan
  • Nilai dBm : Kekuatan sinyal numerik (semakin mendekati 0, semakin kuat)

🐛 Troubleshooting (10+ Solusi Masalah)

Masalah 1: Layar OLED Tidak Menampilkan Apa-apa

Solusi:
1. Periksa koneksi VCC ke 3.3V (BUKAN 5V!)
2. Periksa koneksi SCL ke D1, SDA ke D2
3. Coba ganti alamat I2C dari 0x3C menjadi 0x3D di kode
4. Gunakan sketch scanner I2C untuk mendeteksi alamat yang benar

Masalah 2: Gagal Upload ke NodeMCU

Solusi:
1. Tekan dan tahan tombol FLASH pada NodeMCU saat proses "Connecting..."
2. Lepaskan FLASH setelah tulisan "Uploading..." muncul
3. Coba ganti kabel USB (pastikan bisa transfer data, bukan hanya charging)
4. Instal driver CH340/CP2102 jika port COM tidak muncul

Masalah 3: Tidak Ada Jaringan yang Terdeteksi

Solusi:
1. Pastikan antena NodeMCU tidak tertutup atau terhalang
2. Coba di area yang banyak WiFi (mall, kantor, perumahan)
3. Periksa apakah WiFi module tidak dalam mode flight/power save
4. Reset NodeMCU dan coba lagi

Masalah 4: Tampilan Berkedip atau Rusak

Solusi:
1. Kurangi nilai delay di loop() menjadi 100ms atau 50ms
2. Pastikan power supply stabil (gunakan power bank berkualitas)
3. Periksa koneksi ground (GND) antara OLED dan NodeMCU

Masalah 5: Kompilasi Error "Adafruit_SSD1306.h not found"

Solusi:
1. Buka Library Manager (Sketch → Include Library → Manage Libraries)
2. Cari "Adafruit SSD1306" dan instal
3. Instal juga "Adafruit GFX Library"
4. Restart Arduino IDE

Masalah 6: NodeMCU Panas / Overheat

Solusi:
1. Pastikan tidak ada korsleting antar pin
2. Jangan menyambungkan VCC OLED ke 5V (gunakan 3.3V)
3. Kurangi frekuensi scan (perpanjang scanInterval menjadi 30000)

🚀 10 Ide Pengembangan Lebih Lanjut

Setelah berhasil membuat WiFi Scanner dasar, Anda bisa mengembangkannya dengan fitur-fitur berikut:

  1. 🔘 Tombol Navigasi Fisik - Tambahkan 2 tombol (NEXT/PREV) di pin D3 dan D4 untuk scroll halaman manual.
  2. 💾 Penyimpanan Data - Gunakan EEPROM atau kartu SD untuk merekam riwayat scan.
  3. 📊 Mode Grafik - Tampilkan grafik kekuatan sinyal dalam bentuk chart batang.
  4. 🔍 Filter SSID - Filter hanya menampilkan jaringan dengan nama tertentu.
  5. 🔊 Buzzer Indikator - Bunyi saat menemukan jaringan target tertentu.
  6. 📱 Bluetooth Serial - Kirim data scan ke smartphone via Bluetooth.
  7. 🌐 Web Server - Buat akses point sendiri untuk melihat hasil scan via browser.
  8. 🔋 Battery Monitor - Tampilkan level baterai jika menggunakan power bank.
  9. 📈 Export ke CSV - Simpan data dalam format CSV untuk analisis di Excel.
  10. 🗺 GPS Integration - Tambahkan module GPS untuk wardriving (pemetaan lokasi).

❓ FAQ (Pertanyaan yang Sering Diajukan)

Q: Apakah alat ini bisa digunakan untuk "hack" WiFi?

A: TIDAK. Alat ini hanya untuk scan dan menampilkan jaringan, BUKAN untuk membobol password. WiFi scanner hanya mendeteksi keberadaan jaringan, bukan menembus keamanannya.

Q: Berapa jarak maksimal deteksi?

A: Tergantung kondisi lingkungan. Di ruang terbuka bisa mencapai 50-100 meter. Di dalam ruangan dengan banyak tembok, sekitar 10-30 meter.

Q: Apakah bisa mendeteksi jaringan 5GHz?

A: NodeMCU ESP8266 hanya mendukung frekuensi 2.4 GHz. Untuk 5GHz, gunakan board ESP32.

Q: Berapa lama baterai bertahan?

A: Dengan power bank 10.000 mAh, alat ini bisa bertahan 12-24 jam non-stop.

Q: Apakah bisa menampilkan channel yang digunakan?

A: Bisa! Tambahkan WiFi.channel(i) pada kode untuk menampilkan channel setiap jaringan.

📦 Kesimpulan

Selamat! Anda telah berhasil membuat WiFi Scanner portabel menggunakan NodeMCU ESP8266 dan layar OLED 0.96". Alat ini tidak hanya berguna untuk survei jaringan, tetapi juga merupakan proyek yang sempurna untuk belajar pemrograman ESP8266, antarmuka grafis dengan OLED, dan pemrosesan data scan WiFi.

Dengan memahami kode di atas, Anda sekarang memiliki fondasi untuk mengembangkan berbagai proyek IoT lainnya seperti pemantau kualitas udara, stasiun cuaca, atau smart home controller. Kunci utamanya adalah memahami cara kerja library ESP8266WiFi.h dan Adafruit_GFX.h.

💡 Pesan Penutup: Jangan ragu untuk bereksperimen dengan kode! Ubah interval scan, tambahkan fitur baru, atau bahkan buat casing 3D untuk alat Anda. Semakin sering Anda mencoba, semakin cepat Anda menguasai IoT. Selamat berkarya!

© 2026 - Tutorial WiFi Scanner Portabel | Dibuat dengan ❤️ untuk komunitas IoT Indonesia

Dukung terus perkembangan tutorial elektronika dengan membagikan artikel ini!

Rabu, 08 April 2026

Membuat Weather Station dengan NodeMCU & OLED: Studi Kasus Pangandaran (Solusi API Key Error)

Membuat Weather Station NodeMCU & OLED: Studi Kasus Pangandaran (Solusi API Key Error)

🌤️ Membuat Weather Station dengan NodeMCU & OLED: Studi Kasus Pangandaran (Solusi API Key Error)


PS
Pidin Saripudin, S.Kom

Praktisi IoT & Embedded System | 8+ tahun pengalaman

📅 Publikasi: 7 April 2026 | Diperbarui: 7 April 2026 ⏱️ Estimasi baca: 12 menit 🏷️ #NodeMCU #ESP8266 #WeatherStation #Pangandaran

📍 1. Pendahuluan

Pernahkah Anda ingin mengetahui suhu dan kondisi cuaca terkini di Pangandaran secara real-time tanpa membuka ponsel? Dengan kombinasi NodeMCU ESP8266 (mikrokontroler murah dengan WiFi) dan OLED display 0.96 inci, Anda bisa membuat stasiun cuaca mini sendiri. Alat ini akan mengambil data cuaca langsung dari internet melalui API OpenWeatherMap, lalu menampilkannya di layar OLED. Artikel ini tidak hanya menyajikan kode lengkap, tetapi juga membahas tuntas masalah umum error 401 Invalid API Key yang sering dialami pemula.

📌 Lokasi Studi Kasus: Pangandaran, Jawa Barat (Koordinat: -7.6956, 108.6500). Cocok untuk pemantau cuaca pantai selatan Jawa.

🛠️ 2. Komponen & Skema Koneksi

Komponen yang dibutuhkan sangat sederhana dan mudah ditemukan:

  • NodeMCU ESP8266 (board dengan chip ESP-12E)
  • OLED Display 0.96" SSD1306 (komunikasi I2C)
  • Kabel jumper Female-to-Female (4 buah)
  • Power bank atau kabel USB (untuk catu daya)

Skema Koneksi I2C (4 kabel):

OLED PinNodeMCU PinKeterangan
GNDGNDGround
VCC3.3VPower 3.3 Volt
SDAD2 (GPIO4)Data I2C
SCLD1 (GPIO5)Clock I2C
⚠️ Catatan: Pastikan alamat I2C OLED adalah 0x3C (default). Jika tidak muncul, gunakan scanner I2C atau coba alamat 0x3D.

🔑 3. Mendapatkan API Key OpenWeatherMap

OpenWeatherMap menyediakan data cuaca gratis (60 kali/menit) dengan registrasi. Langkah-langkahnya:

  1. Buka OpenWeatherMap API
  2. Klik "Sign Up" dan buat akun (email & password)
  3. Verifikasi email melalui tautan yang dikirimkan
  4. Login ke akun, lalu buka menu "API Keys" (https://home.openweathermap.org/api_keys)
  5. Salin API key default (campuran huruf dan angka)

❌ 4. Mengatasi Error 401 "Invalid API Key"

Error ini adalah kendala paling umum saat pertama kali mencoba. Berikut penyebab dan solusinya:

📟 Pesan Error yang Muncul:
{"cod":401, "message": "Invalid API key. Please see https://openweathermap.org/faq#error401"}

Penyebab Error 401:

  • API key salah ketik — pastikan tidak ada spasi atau karakter tambahan.
  • API key belum diaktifkan — OpenWeatherMap membutuhkan waktu 2-6 jam setelah verifikasi email untuk mengaktifkan key baru.
  • Akun belum diverifikasi — cek email dan klik tautan verifikasi.
  • Menggunakan key dari akun yang berbeda — key bersifat privat per akun.

Langkah Verifikasi API Key (Sebelum Upload ke NodeMCU):

Uji key terlebih dahulu di browser dengan URL berikut (ganti KUNCI_ANDA):

🔗 Uji Coba URLCopy ke browser
https://api.openweathermap.org/data/2.5/weather?lat=-7.6956&lon=108.6500&units=metric&appid=KUNCI_ANDA

Jika berhasil, browser akan menampilkan data JSON cuaca Pangandaran. Jika error 401, tunggu 2-6 jam atau cek kembali key Anda.

💡 Tips Penting: Jangan membuat banyak API key baru. Cukup gunakan key default yang tertera di dashboard. Key baru juga memerlukan waktu aktivasi yang sama.

💻 5. Kode Program Lengkap (Arduino IDE)

Berikut kode yang sudah diuji coba untuk lokasi Pangandaran. Ganti YOUR_API_KEY dengan API key asli Anda yang sudah aktif.

📟 Weather_Station_Pangandaran.inoArduino IDE
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>

// ==================== KONFIGURASI OLED ====================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ==================== KONFIGURASI WIFI ====================
const char* ssid = "7G";           // Ganti dengan SSID WiFi Anda
const char* password = "forget22"; // Ganti password WiFi Anda

// ==================== API KEY OPENWEATHERMAP ====================
// Dapatkan dari: https://home.openweathermap.org/api_keys
const char* apiKey = "YOUR_API_KEY";  // <-- -="" 0="" 0x3c="" 10="" 25="" 30="" 40="" 80="" angandaran="" api="" apikey="" apisuccess="false;" asli="" assword="" attempts="" bool="" client.available="" client.connect="" client.print="" client="" close="" const="" cuaca="===================" data...="" delay="" dengan="" deskripsi="" display.begin="" display.cleardisplay="" display.display="" display.print="" display.println="" display.setcursor="" display.settextcolor="" display.settextsize="" ek="" else="" engambil="" enghubungkan...="" enghubungkan="" feelslike="0;" float="" for="" gagal="" ganti="" host.c_str="" host="" http="" i="" if="" ifi.status="" ifi="" inisialisasi="" int="" isi.length="" isi="" judul.length="" judul="" kecepatanangin="0;" kelembaban="0," key="" kondisi="" konek="" koneksi="" koordinat="" lasterror="" lastupdate="0;" latitude="" lon=" + String(longitude,4) + 
                " long="" longitude="108.6500;" menit="" millis="" n="" ngagal="" nwifi="" ok="" oled="" onnection:="" ost:="" pangandaran="==" password="" path="" r="" return="" rror="" serial.begin="" serial.print="" serial.println="" setup="" ssid="" station="" string="" suhu="0," tampilkanpesan="" tekanan="0;" terhubung="" timeout="" tring="" ukses="" units="metric&appid=" unsigned="" updateinterval="600000;" updateweatherdata="" variabel="" void="" weather="" while="" wifi.begin="" wifi="" wificlient="" wl_connected=""> 10000) {
      lastError = "Timeout API";
      tampilkanPesan("Error", lastError);
      client.stop();
      return;
    }
    delay(100);
  }
  
  String response = "";
  while (client.available()) response += (char)client.read();
  client.stop();
  
  if (response.indexOf("401") > 0) {
    lastError = "API Key Invalid!";
    tampilkanPesan("Error", "Cek API Key");
    return;
  }
  
  int jsonStart = response.indexOf("{");
  if (jsonStart == -1) {
    lastError = "Response invalid";
    tampilkanPesan("Error", lastError);
    return;
  }
  
  String jsonString = response.substring(jsonStart);
  DynamicJsonDocument doc(4096);
  DeserializationError error = deserializeJson(doc, jsonString);
  
  if (error) {
    lastError = "Parse JSON gagal";
    tampilkanPesan("Error", lastError);
    return;
  }
  
  suhu = doc["main"]["temp"];
  feelsLike = doc["main"]["feels_like"];
  kelembaban = doc["main"]["humidity"];
  kondisi = doc["weather"][0]["main"].as();
  deskripsi = doc["weather"][0]["description"].as();
  tekanan = doc["main"]["pressure"];
  kecepatanAngin = doc["wind"]["speed"];
  
  apiSuccess = true;
  Serial.println("Cuaca Pangandaran: " + String(suhu) + "°C, " + kondisi);
}

void tampilkanCuaca() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(28, 0);
  display.println("PANGANDARAN");
  display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
  
  // Ikon sederhana
  display.fillCircle(20, 32, 10, SSD1306_WHITE);
  display.fillCircle(20, 32, 8, SSD1306_BLACK);
  
  display.setTextSize(2);
  display.setCursor(48, 20);
  display.print(int(suhu));
  display.cp437(true);
  display.write(167);
  display.print("C");
  
  display.setTextSize(1);
  display.setCursor(48, 40);
  display.print("RH: ");
  display.print(kelembaban);
  display.print("%");
  
  display.setCursor(4, 54);
  display.print(kondisi);
  display.display();
}

void tampilkanDetail() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(2, 2);
  display.print("Suhu: "); display.print(suhu,1); display.write(167); display.print("C");
  display.setCursor(2, 14);
  display.print("Kelembaban: "); display.print(kelembaban); display.print("%");
  display.setCursor(2, 26);
  display.print("Angin: "); display.print(kecepatanAngin,1); display.print(" m/s");
  display.setCursor(2, 38);
  display.print("Tekanan: "); display.print(tekanan); display.print(" hPa");
  display.setCursor(2, 50);
  if(deskripsi.length()>16) deskripsi = deskripsi.substring(0,14);
  display.print(deskripsi);
  display.display();
}

void loop() {
  unsigned long now = millis();
  if (now - lastUpdate > updateInterval || lastUpdate == 0) {
    updateWeatherData();
    lastUpdate = now;
  }
  if (apiSuccess) {
    tampilkanCuaca();
    delay(6000);
    tampilkanDetail();
    delay(5000);
  } else {
    tampilkanPesan("Error", lastError);
    delay(30000);
    updateWeatherData();
    lastUpdate = millis();
  }
}

Library yang Diperlukan (Install via Library Manager):

  • Adafruit SSD1306 — driver untuk OLED
  • Adafruit GFX — library grafis
  • ArduinoJson — parsing data JSON (versi 6.x)

⚙️ 6. Cara Kerja Program

  1. Koneksi WiFi — NodeMCU terhubung ke jaringan internet.
  2. HTTP Request ke API — Mengirim request GET ke OpenWeatherMap dengan koordinat Pangandaran.
  3. Parsing JSON — Data suhu, kelembaban, kondisi diekstrak dari respons.
  4. Tampilan OLED — Dua halaman bergantian setiap 6 detik (halaman utama dan detail).
  5. Update Otomatis — Data diperbarui setiap 10 menit.

🖥️ 7. Tampilan OLED

Halaman 1 (Cuaca Utama): Menampilkan nama kota (PANGANDARAN), suhu besar, kelembaban, dan kondisi cuaca singkat.

Halaman 2 (Detail): Menampilkan suhu, kelembaban, kecepatan angin, tekanan udara, dan deskripsi cuaca lengkap.

📊 Contoh Data yang Ditampilkan:
Suhu: 29°C | Kelembaban: 78% | Angin: 3.2 m/s | Tekanan: 1011 hPa | Kondisi: Light Rain

📝 8. Kesimpulan

Proyek Weather Station dengan NodeMCU dan OLED sangat memungkinkan untuk dibuat sendiri dengan biaya terjangkau. Kendala utama biasanya terletak pada aktivasi API Key OpenWeatherMap yang membutuhkan waktu 2-6 jam. Dengan mengikuti panduan di atas, Anda dapat memantau cuaca Pangandaran secara real-time dari rumah. Kembangkan lebih lanjut dengan menambahkan sensor lokal atau menyimpan data ke database. Selamat berkarya!

© 2026 Pidin Saripudin, S.Kom | Artikel ini dilisensikan untuk edukasi. Dilarang menyalin tanpa izin.

Selasa, 07 April 2026

7 Ide Proyek NodeMCU ESP8266 dengan OLED Display

7 Ide Proyek NodeMCU ESP8266 dengan OLED Display | Tutorial IoT Lengkap

💡 7 Ide Proyek NodeMCU ESP8266 dengan OLED Display

PS
Pidin Saripudin, S.Kom

Praktisi IoT & Embedded System | 8+ tahun pengalaman

📅 Publikasi: 7 April 2026 | Diperbarui: 7 April 2026 ⏱️ Estimasi baca: 15 menit 🏷️ #NodeMCU #ESP8266 #OLED #IoT

NodeMCU ESP8266 adalah salah satu mikrokontroler favorit di kalangan penghobi IoT karena harganya yang terjangkau, memiliki WiFi terintegrasi, dan mudah diprogram dengan Arduino IDE. Ketika dipadukan dengan OLED display 0.96 inci berbasis SSD1306, potensi proyek yang bisa dibuat menjadi sangat luas. Artikel ini menyajikan 7 ide proyek menarik yang bisa Anda bangun sendiri, lengkap dengan tingkat kesulitan, komponen yang dibutuhkan, serta konsep kode sederhana. Semua proyek telah diuji coba dan aman untuk dipraktikkan.

📌 Mengapa OLED? Layar OLED memiliki kontras tinggi, konsumsi daya rendah, dan ukuran kecil (128x64 piksel) — sangat cocok untuk antarmuka pengguna pada perangkat IoT portabel.

🎯 Ringkasan Proyek

NoNama ProyekTingkatFitur Utama
1Weather Station⭐️⭐️ MenengahCuaca real-time, suhu, kelembaban
2WiFi Scanner Tool⭐️ PemulaScan & tampilkan jaringan WiFi sekitar
3Pesan dari Cloud⭐️⭐️ MenengahTampilkan pesan via web/cloud
4Desktop Pet⭐️⭐️⭐️ MahirAnimasi ekspresi lucu interaktif
5GPS Tracker⭐️⭐️⭐️ MahirTampilkan posisi real-time
6News Ticker⭐️⭐️ MenengahBerita bergulir dari internet
7Countdown Timer⭐️ PemulaHitung mundur acara spesial

🌤️ 1. Weather Station (Stasiun Cuaca)

Weather Station berbasis API
Menengah API OpenWeatherMap

Tampilkan informasi cuaca terkini dari internet menggunakan API OpenWeatherMap. Layar OLED menampilkan suhu, kelembaban, kondisi cuaca, dan ikon sederhana (cerah, hujan, mendung). Proyek ini cocok untuk meja kerja atau sebagai informasi cuaca di ruang keluarga.

Komponen: NodeMCU, OLED 0.96", kabel jumper, power bank atau USB.

Fitur: Update data setiap 10 menit, tampilan ikon grafis, suhu dalam Celsius/Fahrenheit, kecepatan angin (opsional).

Konsep kode: HTTP GET ke API, parsing JSON, menampilkan data ke OLED dengan fungsi display.

// Contoh parsing JSON cuaca
DynamicJsonDocument doc(2048);
deserializeJson(doc, http.getString());
float suhu = doc["main"]["temp"];
String kondisi = doc["weather"][0]["main"];

Pengembangan: Tambahkan sensor DHT11 untuk membaca suhu lokal, simpan data ke database, tampilkan grafik tren suhu.

📡 2. WiFi Scanner Tool

Pemindai Jaringan WiFi Portabel
Pemula ESP8266WiFi

Buat alat portable yang bisa menampilkan semua jaringan WiFi di sekitar Anda, lengkap dengan kekuatan sinyal (RSSI) dan jenis enkripsi. Sangat berguna untuk survey jaringan, troubleshooting, atau sekadar eksplorasi lingkungan.

Komponen: NodeMCU, OLED, power bank (portabel).

Fitur: Scan otomatis setiap 10 detik, tampilan SSID, bar sinyal visual, enkripsi (WPA2, WPA, Open). Scrolling manual menggunakan tombol.

#include <ESP8266WiFi.h>
int jumlah = WiFi.scanNetworks();
for(int i=0; i<jumlah; i++) {
  String ssid = WiFi.SSID(i);
  int rssi = WiFi.RSSI(i);
  // tampilkan di OLED
}

Pengembangan: Filter berdasarkan kekuatan sinyal, simpan hasil ke file, deteksi jaringan tidak aman.

💬 3. Pesan dari Cloud / IoT Text Display

Papan Pesan Digital
Menengah Web Server

Kirim pesan dari smartphone atau komputer ke OLED display melalui cloud. Cocok untuk papan pengumuman digital di rumah, pesan untuk anggota keluarga, atau notifikasi dari aplikasi eksternal.

Komponen: NodeMCU, OLED.

Fitur: Mode AP (langsung) atau Station (via router), scroll teks panjang, multiple pesan, penyimpanan pesan terakhir di EEPROM.

// ESP8266 sebagai web server
server.on("/send", [](){
  String msg = server.arg("message");
  tampilkanDiOLED(msg);
  server.send(200, "text/plain", "OK");
});

Pengembangan: Integrasi dengan Blynk, notifikasi dari Google Forms, tampilan animasi saat pesan baru tiba.

🐾 4. Desktop Pet (Hewan Peliharaan Desktop)

Tamagotchi Modern
Mahir Animasi & Interaksi

Buat karakter lucu di OLED yang bereaksi terhadap waktu, interaksi tombol, atau bahkan cuaca. Mirip dengan Tamagotchi versi modern — bisa diberi makan, diajak bermain, dan tidur.

Komponen: NodeMCU, OLED, 2-3 tombol push button, buzzer (opsional).

Fitur: 5+ ekspresi (senang, sedih, lapar, tidur, marah), perubahan berdasarkan waktu nyata (pagi/siang/malam), interaksi tombol, status "kesehatan" dan "kebahagiaan".

// Ekspresi berdasarkan jam
if (jam >= 6 && jam < 11) expression = HAPPY;
else if (jam >= 22 || jam < 5) expression = SLEEP;
else expression = NORMAL;

Pengembangan: Integrasi dengan WiFi untuk update cuaca (karakter pakai jas hujan jika hujan), penyimpanan status ke EEPROM, multi karakter.

📍 5. GPS Tracker

Pelacak Posisi Real-time
Mahir Modul GPS

Tampilkan posisi GPS real-time di OLED. Sangat berguna untuk proyek navigasi, pelacakan kendaraan, atau data logger lokasi. Modul GPS NEO-6M yang murah sudah cukup akurat untuk keperluan hobi.

Komponen: NodeMCU, OLED, modul GPS NEO-6M (atau NEO-7M/8M).

Fitur: Tampilkan koordinat Latitude/Longitude, kecepatan (km/h), arah (course), jumlah satelit, ketinggian. Update setiap 1 detik.

Koneksi GPS: VCC ke 5V, GND ke GND, TX ke RX (D3/GPIO0).

#include <TinyGPS++.h>
TinyGPSPlus gps;
while (Serial.available()) {
  gps.encode(Serial.read());
  if (gps.location.isUpdated()) {
    float lat = gps.location.lat();
    float lng = gps.location.lng();
  }
}

Pengembangan: Kirim data lokasi ke server (tracking online), tampilkan peta sederhana dengan titik-titik, simpan track log ke SD card.

📰 6. News Ticker

Berita Bergulir dari Internet
Menengah RSS Feed

Tampilkan berita terbaru dari RSS feed atau API news secara bergulir (scrolling) di OLED. Cocok untuk meja kerja agar tetap update dengan berita tanpa harus membuka HP.

Komponen: NodeMCU, OLED.

Fitur: Scroll teks horizontal, update setiap 15 menit, support RSS feed Indonesia (detik, kompas, tempo), efek transisi antar berita.

void scrollText(String text, int y) {
  for (int i = 0; i <= text.length() * 6; i++) {
    display.clearDisplay();
    display.setCursor(-i, y);
    display.print(text);
    display.display();
    delay(40);
  }
}

Pengembangan: Pilih kategori berita (olahraga, teknologi, politik), integrasi dengan Google News RSS, tampilkan waktu update terakhir.

⏰ 7. Countdown Timer / Event Reminder

Hitung Mundur Acara Spesial
Pemula NTP Client

Tampilkan hitung mundur menuju acara spesial seperti ulang tahun, liburan, atau hari raya. Dengan koneksi internet, waktu akan selalu sinkron sehingga countdown tetap akurat.

Komponen: NodeMCU, OLED, buzzer (opsional untuk alarm).

Fitur: Hitung mundur dalam hari, jam, menit, detik, ambil waktu dari NTP server, progress bar visual, ganti ekspresi mata sesuai waktu tersisa.

#include <NTPClient.h>
unsigned long target = 1704067200; // 1 Jan 2025
unsigned long now = timeClient.getEpochTime();
unsigned long selisih = target - now;
int hari = selisih / 86400;
int jam = (selisih % 86400) / 3600;

Pengembangan: Multiple event, penyimpanan target di EEPROM, alarm dengan buzzer saat countdown habis, tampilan grafis balon atau kue ulang tahun.

🔌 Panduan Koneksi OLED ke NodeMCU

Semua proyek di atas menggunakan protokol I2C yang sama. Berikut adalah skema koneksi standar:

OLED PinNodeMCU Pin
GNDGND
VCC3.3V
SDAD2 (GPIO4)
SCLD1 (GPIO5)
⚠️ Catatan penting: Jika layar tidak merespons, coba ganti alamat I2C antara 0x3C atau 0x3D saat inisialisasi display. Gunakan scanner I2C untuk mendeteksi alamat yang benar.

📝 Kesimpulan

Ketujuh ide proyek di atas membuktikan bahwa kombinasi NodeMCU ESP8266 dan OLED display sangat powerful untuk berbagai aplikasi IoT. Mulai dari yang sederhana seperti WiFi scanner hingga yang kompleks seperti desktop pet atau GPS tracker, semuanya bisa Anda bangun dengan biaya terjangkau. Kunci sukses adalah memahami dasar-dasar pemrograman Arduino, penggunaan library, dan koneksi hardware. Jangan ragu untuk memodifikasi dan mengembangkan proyek-proyek ini sesuai kreativitas Anda. Selamat berkarya dan terus belajar IoT!

Jika ada pertanyaan atau ingin berbagi hasil proyek Anda, silakan tinggalkan komentar di bawah. Saya, Pidin Saripudin, S.Kom, akan dengan senang hati membantu.

© 2026 Pidin Saripudin, S.Kom | Artikel ini dilisensikan untuk edukasi publik. Dilarang menyalin tanpa izin.

Minggu, 05 April 2026

DESKTOP PET - ROBOT MEJA INTERAKTIF dengan NodeMCU ESP8266 & OLED (2 Karakter)

Desktop Pet - Robot Meja Interaktif dengan NodeMCU & OLED | Dua Karakter (Mo-chan & Pikachu)

🤖 DESKTOP PET - ROBOT MEJA INTERAKTIF
dengan NodeMCU ESP8266 & OLED (2 Karakter)

🎯 Apa Itu Desktop Pet?

Desktop Pet adalah robot mini interaktif yang diletakkan di meja kerja, dengan wajah ekspresif di layar OLED yang bisa berkedip, melihat ke sekeliling, bahkan bereaksi saat disentuh. Proyek ini seperti "Tamagotchi" versi modern yang berwujud robot kecil imut di meja Anda!

💡 Mengapa Proyek Ini Menarik?
✅ Biaya sangat murah (Rp 100.000 - 150.000)
✅ Hanya butuh 2 komponen utama (NodeMCU + OLED)
✅ Tidak perlu koneksi internet (100% offline)
✅ Bisa dikembangkan dengan berbagai sensor
✅ Cocok untuk belajar pemrograman ESP8266 dan grafis OLED

📊 10 Manfaat Memiliki Desktop Pet

Berikut adalah alasan mengapa Anda perlu membuat Desktop Pet sendiri:

NoManfaatKeterangan
1Menghilangkan stresHewan peliharaan digital yang lucu di meja kerja
2Meningkatkan fokusGerakan mata yang lucu membuat suasana lebih rileks
3Media belajar IoTProyek sempurna untuk memahami ESP8266 dan OLED
4Hadiah unikBisa dijadikan kado untuk teman yang suka elektronika
5InteraktifBisa bereaksi saat disentuh (dengan touch sensor)
6Kustomisasi tinggiBisa mengganti karakter sesuai keinginan
7Portofolio proyekMenambah nilai portfolio IoT Anda
8MurahHanya dengan komponen bekas pun bisa
9Edukasi anakMengenalkan elektronika dan coding pada anak
10Bisa dikembangkanTambahkan suara, LED, atau sensor lain

🛠 Alat & Bahan yang Diperlukan

Karena Anda sudah memiliki pengalaman dengan NodeMCU dari proyek sebelumnya, Anda hanya perlu:

KomponenSpesifikasiFungsiEstimasi Harga
NodeMCU ESP8266ESP-12E, CH340/CP2102Otak alat + koneksiRp 60.000 - 85.000
Layar OLED 0.96"128x64, I2C, SSD1306Menampilkan wajah karakterRp 40.000 - 65.000
Kabel Jumper F-F4 buahKoneksiRp 5.000 - 10.000
Touch Sensor (opsional)TP223 CapacitiveDeteksi sentuhanRp 10.000 - 20.000
Kabel Micro USBSupport dataPower & uploadRp 15.000 - 30.000

Total biaya minimal: Rp 105.000 - 160.000 (jika sudah punya kabel USB)

🔌 Skema Koneksi (Wiring)

Koneksi sangat sederhana karena OLED menggunakan protokol I2C:

Layar OLEDNodeMCU ESP8266GPIOKeterangan
VCC3.3V-Tegangan 3.3V (JANGAN 5V!)
GNDGND-Ground
SCLD1GPIO5Clock I2C
SDAD2GPIO4Data I2C
⚠️ PERINGATAN PENTING: NodeMCU ESP8266 menggunakan logika 3.3 VOLT. Pastikan layar OLED Anda mendukung 3.3V. Menyambungkan ke pin 5V dapat merusak layar secara permanen!

🤖 KARAKTER 1: Robot Mo-chan

🐱 MO-CHAN - Robot Imut ala Anime

Mo-chan adalah karakter robot bergaya anime dengan mata bulat besar, pupil hitam dengan sorotan putih (efek berbinar), dan ekspresi yang sangat imut. Karakter ini terinspirasi dari maskot-maskot anime Jepang yang lucu dan menggemaskan.

✨ Fitur Khusus Mo-chan

👀 Mata besar ala anime ✨ Efek sorotan ganda 😊 Ekspresi bahagia ^^ 😲 Ekspresi terkejut O.O 😴 Ekspresi tidur (Zzz) 😠 Ekspresi marah kecil 😉 Ekspresi wink 📦 Kotak bingkai mata

📝 Kode Program Mo-chan

/*
    PROJECT: Desktop Pet - Robot Mo-chan Edition
    BOARD: NodeMCU ESP8266
    DISPLAY: OLED 128x64 SSD1306
*/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP8266WiFi.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
#define OLED_ADDRESS  0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ========== KONFIGURASI ==========
#define TOUCH_PIN D3
bool useTouchSensor = false;

// ========== VARIABEL ==========
unsigned long lastBlink = 0;
unsigned long lastEyeMove = 0;
unsigned long lastExpressionChange = 0;
unsigned long lastIdleAnimation = 0;

bool eyesOpen = true;
int eyeDirection = 0;      // 0:center, 1:kiri, 2:kanan, 3:atas, 4:bawah
int expression = 0;        // 0:normal, 1:happy, 2:surprised, 3:sleep, 4:wink, 5:angry

bool isBlinking = false;
int blinkStep = 0;
bool isPetting = false;
unsigned long pettingEndTime = 0;

// ========== GAMBAR KOTAK BINGKAI ==========
void drawBoxFrame(int x, int y, int w, int h) {
    display.drawRoundRect(x, y, w, h, 5, SSD1306_WHITE);
    display.drawRect(x + 1, y + 1, w - 2, h - 2, SSD1306_WHITE);
}

// ========== GAMBAR MATA MO-CHAN ==========
void drawMoChanEyes() {
    int leftEyeX = 44, rightEyeX = 84, eyeY = 32;
    int pupilOffsetX = 0, pupilOffsetY = 0;
    
    switch(eyeDirection) {
        case 1: pupilOffsetX = -5; break;
        case 2: pupilOffsetX = 5; break;
        case 3: pupilOffsetY = -4; break;
        case 4: pupilOffsetY = 4; break;
    }
    
    // Kotak bingkai
    drawBoxFrame(leftEyeX - 15, eyeY - 12, 30, 24);
    drawBoxFrame(rightEyeX - 15, eyeY - 12, 30, 24);
    
    if(expression == 3) {
        // Tidur
        display.drawLine(leftEyeX - 10, eyeY, leftEyeX + 10, eyeY, SSD1306_WHITE);
        display.drawLine(rightEyeX - 10, eyeY, rightEyeX + 10, eyeY, SSD1306_WHITE);
        display.setCursor(leftEyeX - 18, eyeY - 8); display.print("z");
        display.setCursor(leftEyeX - 14, eyeY - 12); display.print("Z");
    }
    else if(expression == 1) {
        // Bahagia ^^
        display.drawLine(leftEyeX - 8, eyeY - 2, leftEyeX - 3, eyeY - 6, SSD1306_WHITE);
        display.drawLine(leftEyeX - 3, eyeY - 6, leftEyeX + 3, eyeY - 6, SSD1306_WHITE);
        display.drawLine(leftEyeX + 3, eyeY - 6, leftEyeX + 8, eyeY - 2, SSD1306_WHITE);
        display.drawLine(rightEyeX - 8, eyeY - 2, rightEyeX - 3, eyeY - 6, SSD1306_WHITE);
        display.drawLine(rightEyeX - 3, eyeY - 6, rightEyeX + 3, eyeY - 6, SSD1306_WHITE);
        display.drawLine(rightEyeX + 3, eyeY - 6, rightEyeX + 8, eyeY - 2, SSD1306_WHITE);
    }
    else if(expression == 2) {
        // Terkejut O.O
        display.fillCircle(leftEyeX, eyeY, 10, SSD1306_WHITE);
        display.fillCircle(rightEyeX, eyeY, 10, SSD1306_WHITE);
        display.fillCircle(leftEyeX + pupilOffsetX, eyeY + pupilOffsetY, 4, SSD1306_BLACK);
        display.fillCircle(rightEyeX + pupilOffsetX, eyeY + pupilOffsetY, 4, SSD1306_BLACK);
    }
    else if(expression == 4) {
        // Wink
        display.fillCircle(leftEyeX, eyeY, 9, SSD1306_WHITE);
        display.fillCircle(leftEyeX + pupilOffsetX, eyeY + pupilOffsetY, 5, SSD1306_BLACK);
        display.drawLine(rightEyeX - 8, eyeY, rightEyeX + 8, eyeY, SSD1306_WHITE);
    }
    else if(isBlinking) {
        // Kedip
        display.drawLine(leftEyeX - 10, eyeY, leftEyeX + 10, eyeY, SSD1306_WHITE);
        display.drawLine(rightEyeX - 10, eyeY, rightEyeX + 10, eyeY, SSD1306_WHITE);
    }
    else {
        // Normal
        display.fillCircle(leftEyeX, eyeY, 9, SSD1306_WHITE);
        display.fillCircle(rightEyeX, eyeY, 9, SSD1306_WHITE);
        display.fillCircle(leftEyeX + pupilOffsetX, eyeY + pupilOffsetY, 5, SSD1306_BLACK);
        display.fillCircle(rightEyeX + pupilOffsetX, eyeY + pupilOffsetY, 5, SSD1306_BLACK);
        display.fillCircle(leftEyeX + pupilOffsetX - 2, eyeY + pupilOffsetY - 2, 2, SSD1306_WHITE);
        display.fillCircle(rightEyeX + pupilOffsetX - 2, eyeY + pupilOffsetY - 2, 2, SSD1306_WHITE);
    }
}

// ========== GAMBAR WAJAH MO-CHAN ==========
void drawMoChanFace() {
    // Kepala bulat
    display.drawCircle(64, 32, 26, SSD1306_WHITE);
    // Antena
    display.fillRect(62, 4, 4, 8, SSD1306_WHITE);
    display.fillCircle(64, 2, 4, SSD1306_WHITE);
    // Hidung
    display.fillTriangle(62, 44, 66, 44, 64, 48, SSD1306_WHITE);
    // Mulut
    if(expression != 2) {
        display.drawLine(56, 52, 60, 54, SSD1306_WHITE);
        display.drawLine(60, 54, 68, 54, SSD1306_WHITE);
        display.drawLine(68, 54, 72, 52, SSD1306_WHITE);
    }
    // Pipi
    display.fillCircle(42, 42, 3, SSD1306_WHITE);
    display.fillCircle(86, 42, 3, SSD1306_WHITE);
}

// ========== SPLASH SCREEN ==========
void showSplash() {
    display.clearDisplay();
    display.setTextSize(2);
    display.setCursor(15, 20);
    display.println("Mo-chan");
    display.setTextSize(1);
    display.setCursor(20, 45);
    display.println("Desktop Pet");
    display.display();
    delay(2000);
}

// ========== SETUP & LOOP ==========
void setup() {
    Serial.begin(115200);
    if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
        for(;;);
    }
    display.clearDisplay();
    showSplash();
    if(useTouchSensor) pinMode(TOUCH_PIN, INPUT);
}

void loop() {
    unsigned long now = millis();
    
    // Kedip setiap 3 detik
    if(!isBlinking && (now - lastBlink > 3000)) {
        isBlinking = true;
        lastBlink = now;
    }
    if(isBlinking && (now - lastBlink > 150)) {
        isBlinking = false;
    }
    
    // Gerakan mata setiap 4 detik
    if(now - lastEyeMove > 4000) {
        eyeDirection = (eyeDirection + 1) % 5;
        lastEyeMove = now;
    }
    
    display.clearDisplay();
    drawMoChanFace();
    drawMoChanEyes();
    display.display();
    delay(30);
}

⚡ KARAKTER 2: Pikachu (Pokemon)

⚡ PIKACHU - Pokemon Paling Populer ⚡

Pikachu adalah karakter Pokemon paling ikonik di dunia! Dengan telinga panjang runcing berujung hitam, pipi merah yang bisa bersinar seperti listrik, dan ekor petir yang khas, Pikachu akan menjadi teman meja yang sempurna untuk para penggemar Pokemon.

✨ Fitur Khusus Pikachu

👂 Telinga panjang runcing ⚡ Pipi merah (bisa bersinar) 🔋 Ekor petir zigzag 😊 Ekspresi bahagia ^^ 😲 Ekspresi terkejut O.O 😴 Ekspresi tidur (Zzz) ⚡ Efek listrik di pipi 😠 Ekspresi marah (alis turun)

📝 Kode Program Pikachu

/*
    PROJECT: Desktop Pet - Pikachu Edition
    BOARD: NodeMCU ESP8266
    DISPLAY: OLED 128x64 SSD1306
*/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP8266WiFi.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
#define OLED_ADDRESS  0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ========== VARIABEL ==========
unsigned long lastBlink = 0;
unsigned long lastEyeMove = 0;
unsigned long lastCheekGlow = 0;
bool eyesOpen = true;
int eyeDirection = 0;
int expression = 0;
bool isBlinking = false;
bool cheeksGlowing = false;

// ========== GAMBAR TELINGA PIKACHU ==========
void drawPikachuEars() {
    // Telinga kiri
    int earLeftX[] = {28, 20, 28};
    int earLeftY[] = {20, 5, 8};
    display.fillTriangle(earLeftX[0], earLeftY[0], earLeftX[1], earLeftY[1], earLeftX[2], earLeftY[2], SSD1306_WHITE);
    // Ujung hitam
    int earLeftTipX[] = {24, 20, 28};
    int earLeftTipY[] = {12, 5, 8};
    display.fillTriangle(earLeftTipX[0], earLeftTipY[0], earLeftTipX[1], earLeftTipY[1], earLeftTipX[2], earLeftTipY[2], SSD1306_BLACK);
    
    // Telinga kanan
    int earRightX[] = {100, 108, 100};
    int earRightY[] = {20, 5, 8};
    display.fillTriangle(earRightX[0], earRightY[0], earRightX[1], earRightY[1], earRightX[2], earRightY[2], SSD1306_WHITE);
    // Ujung hitam
    int earRightTipX[] = {104, 108, 100};
    int earRightTipY[] = {12, 5, 8};
    display.fillTriangle(earRightTipX[0], earRightTipY[0], earRightTipX[1], earRightTipY[1], earRightTipX[2], earRightTipY[2], SSD1306_BLACK);
}

// ========== GAMBAR WAJAH PIKACHU ==========
void drawPikachuFace() {
    // Kepala
    display.fillCircle(64, 34, 26, SSD1306_WHITE);
    display.drawCircle(64, 34, 26, SSD1306_WHITE);
    
    // Pipi merah
    if(cheeksGlowing || expression == 1) {
        display.fillCircle(44, 42, 6, SSD1306_WHITE);
        display.fillCircle(84, 42, 6, SSD1306_WHITE);
    } else {
        display.fillCircle(44, 42, 5, SSD1306_WHITE);
        display.fillCircle(84, 42, 5, SSD1306_WHITE);
    }
    
    // Hidung
    display.fillCircle(64, 44, 2, SSD1306_BLACK);
    
    // Mulut
    if(expression == 2) {
        display.drawCircle(64, 54, 5, SSD1306_WHITE);
        display.fillCircle(64, 54, 2, SSD1306_BLACK);
    } else {
        display.drawLine(56, 52, 60, 56, SSD1306_WHITE);
        display.drawLine(60, 56, 68, 56, SSD1306_WHITE);
        display.drawLine(68, 56, 72, 52, SSD1306_WHITE);
    }
    
    // Alis
    if(expression == 5) {
        display.drawLine(48, 26, 56, 30, SSD1306_WHITE);
        display.drawLine(72, 30, 80, 26, SSD1306_WHITE);
    } else {
        display.drawLine(48, 26, 56, 26, SSD1306_WHITE);
        display.drawLine(72, 26, 80, 26, SSD1306_WHITE);
    }
}

// ========== GAMBAR MATA PIKACHU ==========
void drawPikachuEyes() {
    int leftEyeX = 52, rightEyeX = 76, eyeY = 34;
    int pupilOffsetX = 0, pupilOffsetY = 0;
    
    switch(eyeDirection) {
        case 1: pupilOffsetX = -4; break;
        case 2: pupilOffsetX = 4; break;
        case 3: pupilOffsetY = -3; break;
        case 4: pupilOffsetY = 3; break;
    }
    
    if(expression == 3) {
        display.drawLine(leftEyeX - 6, eyeY, leftEyeX + 6, eyeY, SSD1306_WHITE);
        display.drawLine(rightEyeX - 6, eyeY, rightEyeX + 6, eyeY, SSD1306_WHITE);
    }
    else if(expression == 1) {
        display.drawLine(leftEyeX - 5, eyeY - 1, leftEyeX - 2, eyeY - 4, SSD1306_WHITE);
        display.drawLine(leftEyeX - 2, eyeY - 4, leftEyeX + 2, eyeY - 4, SSD1306_WHITE);
        display.drawLine(leftEyeX + 2, eyeY - 4, leftEyeX + 5, eyeY - 1, SSD1306_WHITE);
        display.drawLine(rightEyeX - 5, eyeY - 1, rightEyeX - 2, eyeY - 4, SSD1306_WHITE);
        display.drawLine(rightEyeX - 2, eyeY - 4, rightEyeX + 2, eyeY - 4, SSD1306_WHITE);
        display.drawLine(rightEyeX + 2, eyeY - 4, rightEyeX + 5, eyeY - 1, SSD1306_WHITE);
    }
    else if(isBlinking) {
        display.drawLine(leftEyeX - 6, eyeY, leftEyeX + 6, eyeY, SSD1306_WHITE);
        display.drawLine(rightEyeX - 6, eyeY, rightEyeX + 6, eyeY, SSD1306_WHITE);
    }
    else {
        display.fillCircle(leftEyeX, eyeY, 6, SSD1306_WHITE);
        display.fillCircle(rightEyeX, eyeY, 6, SSD1306_WHITE);
        display.fillCircle(leftEyeX + pupilOffsetX, eyeY + pupilOffsetY, 3, SSD1306_BLACK);
        display.fillCircle(rightEyeX + pupilOffsetX, eyeY + pupilOffsetY, 3, SSD1306_BLACK);
        display.fillCircle(leftEyeX + pupilOffsetX - 1, eyeY + pupilOffsetY - 1, 1, SSD1306_WHITE);
        display.fillCircle(rightEyeX + pupilOffsetX - 1, eyeY + pupilOffsetY - 1, 1, SSD1306_WHITE);
    }
}

// ========== GAMBAR EKOR PIKACHU ==========
void drawPikachuTail() {
    int tailX[] = {105, 112, 108, 115, 110};
    int tailY[] = {52, 48, 44, 40, 36};
    for(int i = 0; i < 4; i++) {
        display.drawLine(tailX[i], tailY[i], tailX[i+1], tailY[i+1], SSD1306_WHITE);
    }
}

// ========== SETUP & LOOP ==========
void setup() {
    Serial.begin(115200);
    if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
        for(;;);
    }
    display.clearDisplay();
    display.setTextSize(2);
    display.setCursor(10, 20);
    display.println("Pika!");
    display.display();
    delay(2000);
}

void loop() {
    unsigned long now = millis();
    
    if(!isBlinking && (now - lastBlink > 3500)) {
        isBlinking = true;
        lastBlink = now;
    }
    if(isBlinking && (now - lastBlink > 150)) {
        isBlinking = false;
    }
    
    if(now - lastEyeMove > 4500) {
        eyeDirection = (eyeDirection + 1) % 5;
        lastEyeMove = now;
    }
    
    if(now - lastCheekGlow > 8000) {
        cheeksGlowing = true;
        lastCheekGlow = now;
        delay(300);
        cheeksGlowing = false;
    }
    
    display.clearDisplay();
    drawPikachuEars();
    drawPikachuFace();
    drawPikachuEyes();
    drawPikachuTail();
    display.display();
    delay(30);
}

📊 Perbandingan Kedua Karakter

AspekMo-chan (Robot)Pikachu (Pokemon)
GayaRobot anime imutPokemon ikonik
Ciri KhasKotak bingkai mata, sorotan gandaTelinga panjang, pipi merah, ekor petir
EkspresiNormal, bahagia, terkejut, tidur, wink, marahNormal, bahagia, terkejut, tidur, marah
Fitur UnikEfek sorotan mata berbinarEfek listrik di pipi
Kesulitan Kode⭐️⭐️⭐️ Menengah⭐️⭐️⭐️⭐️ Agak kompleks

🐛 Troubleshooting (8 Masalah Umum)

NoMasalahSolusi
1OLED tidak menyalaCek VCC ke 3.3V (BUKAN 5V!). Coba ganti alamat I2C ke 0x3D
2Error "Adafruit_SSD1306.h"Install library Adafruit SSD1306 dan Adafruit GFX via Library Manager
3Gagal upload ke NodeMCUTekan tombol FLASH saat proses "Connecting..."
4Port COM tidak terdeteksiInstal driver CH340/CP2102, coba ganti kabel USB
5Tampilan rusak/berkedipKurangi delay di loop, atau periksa power supply
6Touch sensor tidak bekerjaPastikan useTouchSensor = true, cek koneksi SIG ke D3
7Ekspresi tidak bergantiPeriksa variabel expression dan timer di loop
8NodeMCU panasJangan sambungkan VCC OLED ke 5V, periksa korsleting

🚀 10 Ide Pengembangan Lebih Lanjut

Setelah berhasil membuat Desktop Pet, Anda bisa mengembangkannya menjadi lebih canggih:

  1. ➕ Touch Sensor Interaktif - Tambahkan sensor sentuhan TP223, Desktop Pet akan bereaksi saat dielus (ekspresi bahagia)
  2. 🔊 Buzzer / Suara - Tambahkan buzzer untuk mengeluarkan suara "Pika pika!" atau "Beep" saat ekspresi berganti
  3. 💡 LED RGB - Tambahkan LED WS2812 di sekitar mata untuk efek cahaya warna-warni
  4. 🎤 Sensor Suara (MIC) - Desktop Pet bereaksi terhadap tepuk tangan atau suara keras
  5. 🌡️ Sensor Suhu & Kelembaban - Tampilkan suhu ruangan di bagian bawah layar
  6. 🔋 Monitor Baterai - Tampilkan level baterai jika menggunakan power bank portabel
  7. 📱 Bluetooth Control - Gunakan HC-05 untuk mengubah ekspresi dari smartphone
  8. 🌙 Mode Malam Otomatis - Gunakan LDR untuk mendeteksi gelap, ubah ekspresi jadi tidur
  9. 🕒 Jam Digital - Tambahkan fitur jam di pojok layar menggunakan NTP (perlu WiFi)
  10. 🖨️ Casing 3D Print - Buat badan robot dengan printer 3D, desain karakter yang berdiri
💡 Tips Pengembangan: Mulailah dari yang termudah (touch sensor atau buzzer) sebelum beralih ke yang lebih kompleks. Setiap pengembangan akan menambah nilai jual proyek Anda!

❓ FAQ (Pertanyaan yang Sering Diajukan)

PertanyaanJawaban
Apakah perlu koneksi internet?Tidak perlu! Desktop Pet berjalan 100% offline. WiFi hanya untuk pengembangan NTP clock.
Bisa pakai Arduino Uno?Bisa, tapi perlu pin A4 (SDA) dan A5 (SCL) untuk I2C, serta adaptasi kode.
Berapa lama baterai bertahan?Dengan power bank 10.000mAh, bisa 12-24 jam non-stop.
Bisa menambah karakter lain?Bisa! Modifikasi fungsi drawFace() dan drawEyes() sesuai karakter yang diinginkan.
Apakah bisa untuk pemula?Proyek ini level Mahir, tapi dengan panduan lengkap ini, pemula yang tekun pasti bisa!

📦 Kesimpulan

Selamat! Anda telah berhasil membuat Desktop Pet dengan dua pilihan karakter: Robot Mo-chan yang imut bergaya anime, dan Pikachu yang ikonik dari Pokemon. Proyek ini membuktikan bahwa dengan komponen sederhana (NodeMCU + OLED), Anda bisa menciptakan robot interaktif yang lucu dan menghibur.

✅ Apa yang Telah Anda Pelajari:
☐ Menggambar grafis di OLED dengan Adafruit GFX
☐ Membuat animasi mata berkedip dan lirik
☐ Mengimplementasikan berbagai ekspresi karakter
☐ (Opsional) Interaksi dengan touch sensor
☐ Dasar-dasar pemrograman ESP8266 untuk animasi real-time

Proyek ini juga menjadi fondasi yang sempurna untuk pengembangan lebih lanjut. Jangan ragu untuk bereksperimen dengan karakter baru, menambahkan sensor, atau membuat casing 3D yang keren. Selamat berkarya dan semoga Desktop Pet Anda menemani setiap momen produktif di meja kerja!

© 2024 - Tutorial Desktop Pet | NodeMCU ESP8266 + OLED | 2 Karakter (Mo-chan & Pikachu)

Dibuat dengan ❤️ untuk komunitas IoT Indonesia. Bagikan artikel ini jika bermanfaat!

Panduan Lengkap NodeMCU ESP8266 dan OLED 0.96" untuk Pemula

NodeMCU ESP8266 dan OLED 0.96" - Panduan Lengkap untuk Pemula 📟 NODEMCU ESP8266 DAN OLED 0.96" Panduan Lengkap untuk...