Panduan Lengkap ESP32 + P10 LED Matrix Display

Posted on

Panduan Step-by-Step: Dari Nol sampai Display Menyala

Step 1: Persiapan Hardware

Yang Anda Butuhkan:

  • ESP32 DevKit V1
  • P10 LED Matrix Panel (32×16, single color red)
  • Power Supply 5V 5A (minimum 3A)
  • Kabel jumper female-to-female (minimal 7 buah)
  • Kabel USB Micro (untuk program ESP32)
  • Breadboard (optional, untuk testing)
  • Multimeter (optional, untuk check voltage)

Step 2: Library yang Dibutuhkan:

DMD32Plus (https://github.com/ahmadfathan/DMD32Plus)

Saat artikel ini dibuat, saya menggunakan DMD32Plus versi 1.2.0 dan Arduino IDE versi 2.3.7.

Step 3: Rangkaian

PENTING – Baca Ini Dulu:

  • ⚠️ JANGAN colok power dulu sebelum wiring selesai!
  • ⚠️ JANGAN power panel dari ESP32 atau USB!
  • ⚠️ Panel butuh 5V dengan ampere BESAR (3-8A)
  • ⚠️ GND harus common antara ESP32 dan Power Supply

Wiring Diagram:

Step 3: Kode Program

1. Blink LED di Panel:

#include <DMD32Plus.h>

#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1

DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
hw_timer_t *timer = NULL;

void IRAM_ATTR triggerScan() {
  dmd.scanDisplayBySPI();
}

void setup() {
  Serial.begin(115200);
  Serial.println("ESP32 P10 Test");
  
  // Setup timer
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &triggerScan, true);
  timerAlarmWrite(timer, 1000, true);
  timerAlarmEnable(timer);
  
  dmd.clearScreen(true);
}

void loop() {
  // All ON
  Serial.println("All pixels ON");
  dmd.clearScreen(false);
  delay(2000);
  
  // All OFF
  Serial.println("All pixels OFF");
  dmd.clearScreen(true);
  delay(2000);
}

PENTING:

  • Panel HARUS menggunakan power supply external 5V 3-5A
  • JANGAN power panel dari ESP32 atau USB!
  • Pastikan GND common antara ESP32 dan power supply

2. Tampilkan Teks

#include <DMD32Plus.h>
#include "fonts/SystemFont5x7.h"
#include "fonts/Arial_black_16.h"

#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1

DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
hw_timer_t *timer = NULL;

// Brightness setup
#define PWM_FREQ 5000
#define PWM_CHANNEL 0
#define PWM_RESOLUTION 8
#define PIN_DMD_nOE 22

void IRAM_ATTR triggerScan() {
  dmd.scanDisplayBySPI();
}

void setupBrightnessPWM() {
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
  ledcAttachPin(PIN_DMD_nOE, PWM_CHANNEL);
}

void setBrightness(uint8_t brightness) {
  ledcWrite(PWM_CHANNEL, brightness);
}

void setup() {
  Serial.begin(115200);
  
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &triggerScan, true);
  timerAlarmWrite(timer, 1000, true);
  timerAlarmEnable(timer);
  
  dmd.clearScreen(true);
  setupBrightnessPWM();
  setBrightness(200);
  
  Serial.println("Display Text Demo");
}

void loop() {
  // Text dengan System Font
  dmd.clearScreen(true);
  dmd.selectFont(System5x7);
  dmd.drawString(2, 4, "HELLO", 5, GRAPHICS_NORMAL);
  delay(2000);
  
  // Text dengan font besar
  dmd.clearScreen(true);
  dmd.selectFont(Arial_Black_16);
  dmd.drawString(1, 0, "DMD", 3, GRAPHICS_NORMAL);
  delay(2000);
  
  // Multi-line text
  dmd.clearScreen(true);
  dmd.selectFont(System5x7);
  dmd.drawString(1, 0, "Line1", 5, GRAPHICS_NORMAL);
  dmd.drawString(1, 8, "Line2", 5, GRAPHICS_NORMAL);
  delay(2000);
}

Tips Text:

  • System5x7: Cocok untuk multi-line text
  • Arial_Black_16: Cocok untuk text besar single line
  • Untuk 1 panel (32×16), jangan terlalu panjang

3. Demo Jam Digital

#include <DMD32Plus.h>
#include "fonts/SystemFont5x7.h"

#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1

DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
hw_timer_t *timer = NULL;

// Brightness
#define PWM_FREQ 5000
#define PWM_CHANNEL 0
#define PWM_RESOLUTION 8
#define PIN_DMD_nOE 22

// Variabel jam (simulasi)
int hours = 12;
int minutes = 30;
int seconds = 0;
unsigned long lastUpdate = 0;
boolean colonOn = true;

void IRAM_ATTR triggerScan() {
  dmd.scanDisplayBySPI();
}

void setupBrightnessPWM() {
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
  ledcAttachPin(PIN_DMD_nOE, PWM_CHANNEL);
}

void setBrightness(uint8_t brightness) {
  ledcWrite(PWM_CHANNEL, brightness);
}

void updateTime() {
  seconds++;
  if (seconds >= 60) {
    seconds = 0;
    minutes++;
    if (minutes >= 60) {
      minutes = 0;
      hours++;
      if (hours >= 24) hours = 0;
    }
  }
}

void displayClock() {
  dmd.clearScreen(true);
  dmd.selectFont(System5x7);
  
  char h[3], m[3];
  sprintf(h, "%02d", hours);
  sprintf(m, "%02d", minutes);
  
  // Format HH:MM
  dmd.drawString(5, 4, h, 2, GRAPHICS_NORMAL);
  
  if (colonOn) {
    dmd.drawString(15, 4, ":", 1, GRAPHICS_OR);
  } else {
    dmd.drawString(15, 4, " ", 1, GRAPHICS_NORMAL);
  }
  
  dmd.drawString(19, 4, m, 2, GRAPHICS_NORMAL);
}

void setup() {
  Serial.begin(115200);
  
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &triggerScan, true);
  timerAlarmWrite(timer, 1000, true);
  timerAlarmEnable(timer);
  
  dmd.clearScreen(true);
  setupBrightnessPWM();
  setBrightness(150);  // Brightness medium untuk clock
  
  Serial.println("Digital Clock Demo");
}

void loop() {
  // Update waktu setiap 1 detik
  if (millis() - lastUpdate >= 1000) {
    updateTime();
    lastUpdate = millis();
  }
  
  // Toggle colon setiap 500ms
  static unsigned long lastBlink = 0;
  if (millis() - lastBlink >= 500) {
    colonOn = !colonOn;
    lastBlink = millis();
  }
  
  displayClock();
  delay(50);
}

Jika menggunakan RTC Real-time:

Install library RTClib dan tambahkan:

#include <RTClib.h>
RTC_DS3231 rtc;

// Di setup():
rtc.begin();

// Di loop():
DateTime now = rtc.now();
hours = now.hour();
minutes = now.minute();
seconds = now.second();

4. Graphics & Animasi dengan Fade

#include <DMD32Plus.h>

#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1

DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
hw_timer_t *timer = NULL;

// Brightness
#define PWM_FREQ 5000
#define PWM_CHANNEL 0
#define PWM_RESOLUTION 8
#define PIN_DMD_nOE 22

void IRAM_ATTR triggerScan() {
  dmd.scanDisplayBySPI();
}

void setupBrightnessPWM() {
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
  ledcAttachPin(PIN_DMD_nOE, PWM_CHANNEL);
}

void setBrightness(uint8_t brightness) {
  ledcWrite(PWM_CHANNEL, brightness);
}

// Fade in effect
void fadeIn(int duration) {
  for (int b = 0; b <= 255; b += 5) {
    setBrightness(b);
    delay(duration / 51);  // 255/5 = 51 steps
  }
}

// Fade out effect
void fadeOut(int duration) {
  for (int b = 255; b >= 0; b -= 5) {
    setBrightness(b);
    delay(duration / 51);
  }
}

void setup() {
  Serial.begin(115200);
  
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &triggerScan, true);
  timerAlarmWrite(timer, 1000, true);
  timerAlarmEnable(timer);
  
  dmd.clearScreen(true);
  setupBrightnessPWM();
  setBrightness(0);  // Mulai dari gelap
  
  Serial.println("Graphics & Animation Demo");
}

void loop() {
  // === Animasi 1: Box dengan Fade In ===
  dmd.clearScreen(true);
  dmd.drawBox(0, 0, 31, 15, GRAPHICS_NORMAL);
  dmd.drawFilledBox(10, 5, 21, 10, GRAPHICS_NORMAL);
  
  fadeIn(1000);  // Fade in 1 detik
  delay(2000);
  fadeOut(1000); // Fade out 1 detik
  delay(500);
  
  // === Animasi 2: Circle dengan Fade ===
  setBrightness(0);
  dmd.clearScreen(true);
  dmd.drawCircle(16, 8, 7, GRAPHICS_NORMAL);
  dmd.drawCircle(16, 8, 4, GRAPHICS_NORMAL);
  
  fadeIn(1000);
  delay(2000);
  fadeOut(1000);
  delay(500);
  
  // === Animasi 3: Expanding Circle dengan Fade ===
  setBrightness(200);
  for (int r = 1; r <= 15; r++) {
    dmd.clearScreen(true);
    dmd.drawCircle(16, 8, r, GRAPHICS_NORMAL);
    delay(80);
  }
  
  fadeOut(800);
  delay(500);
  
  // === Animasi 4: Loading Bar dengan Fade ===
  setBrightness(0);
  dmd.clearScreen(true);
  dmd.drawBox(2, 6, 29, 10, GRAPHICS_NORMAL);
  
  fadeIn(500);
  
  // Animate loading bar
  for (int x = 3; x < 29; x++) {
    dmd.drawFilledBox(3, 7, x, 9, GRAPHICS_NORMAL);
    delay(50);
  }
  
  delay(1000);
  fadeOut(500);
  delay(500);
  
  // === Animasi 5: Bouncing Box ===
  setBrightness(200);
  
  int x = 0, y = 0;
  int dx = 1, dy = 1;
  int boxSize = 5;
  
  for (int frame = 0; frame < 80; frame++) {
    dmd.clearScreen(true);
    dmd.drawFilledBox(x, y, x + boxSize, y + boxSize, GRAPHICS_NORMAL);
    
    x += dx;
    y += dy;
    
    if (x <= 0 || x >= (32 - boxSize)) dx = -dx;
    if (y <= 0 || y >= (16 - boxSize)) dy = -dy;
    
    delay(40);
  }
  
  fadeOut(800);
  delay(500);
}

Efek Fade Tambahan:

// Pulse effect (loop terang-redup)
void pulseEffect(int cycles) {
  for (int i = 0; i < cycles; i++) {
    fadeIn(500);
    fadeOut(500);
  }
}

// Quick flash
void flashEffect(int times) {
  for (int i = 0; i < times; i++) {
    setBrightness(255);
    delay(100);
    setBrightness(0);
    delay(100);
  }
}

5. Running Text dengan Container

#include <DMD32Plus.h>
#include <DMDContainer.h>
#include "fonts/SystemFont5x7.h"

#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1

DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
hw_timer_t *timer = NULL;

// Brightness
#define PWM_FREQ 5000
#define PWM_CHANNEL 0
#define PWM_RESOLUTION 8
#define PIN_DMD_nOE 22

// Container untuk running text
DMDContainer container(0, 0, 31, 15);

void IRAM_ATTR triggerScan() {
  dmd.scanDisplayBySPI();
}

void setupBrightnessPWM() {
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
  ledcAttachPin(PIN_DMD_nOE, PWM_CHANNEL);
}

void setBrightness(uint8_t brightness) {
  ledcWrite(PWM_CHANNEL, brightness);
}

void setup() {
  Serial.begin(115200);
  
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &triggerScan, true);
  timerAlarmWrite(timer, 1000, true);
  timerAlarmEnable(timer);
  
  dmd.clearScreen(true);
  setupBrightnessPWM();
  setBrightness(200);
  
  container.setFont(System5x7);
  
  Serial.println("Running Text Demo");
}

void loop() {
  char text[] = "Welcome to Indonesia - ESP32 P10 Display";
  
  // Start dari kanan luar (pixel 32)
  int16_t posX = 32;
  
  dmd.clearScreen(true);
  
  // Loop sampai text keluar sempurna di kiri
  while (true) {
    container.clear();
    
    // Draw text dan dapatkan width
    uint16_t textWidth = container.appendText(posX, 4, text, strlen(text));
    
    // Draw container ke DMD
    dmd.drawContainer(&container);
    
    // Gerakkan ke kiri
    posX--;
    
    // Jika text sudah keluar sempurna, break
    if (posX + textWidth < 0) {
      break;
    }
    
    delay(30);  // Kecepatan scroll
  }
  
  delay(500);
}

Running Text dengan Border:

void loop() {
  char text[] = "Bordered Text Demo";
  int16_t posX = 32;
  
  while (true) {
    // Clear dan redraw border
    dmd.clearScreen(true);
    dmd.drawBox(0, 0, 31, 15, GRAPHICS_NORMAL);
    
    container.clear();
    uint16_t w = container.appendText(posX, 4, text, strlen(text));
    dmd.drawContainer(&container);
    
    posX--;
    if (posX + w < 0) break;
    
    delay(30);
  }
  delay(500);
}

Step 4. Tips & Troubleshooting

Brightness Settings:

setBrightness(255);  // Maksimal (outdoor siang)
setBrightness(200);  // Terang (indoor siang)
setBrightness(150);  // Medium (indoor)
setBrightness(100);  // Redup (malam)
setBrightness(50);   // Sangat redup (hemat daya)

Common Issues:

1. Display tidak menyala:

  • Cek power supply 5V terhubung ke panel
  • Cek GND common antara ESP32 dan PSU
  • Cek semua kabel pin

2. Display flicker:

  • Cek koneksi CLK dan LAT
  • Kabel jangan terlalu panjang (max 20cm)

3. Brightness tidak berubah:

  • Pastikan setupBrightnessPWM() dipanggil setelah DMD init
  • Cek koneksi pin GPIO 22 (nOE)

4. Text terpotong:

  • Untuk 1 panel (32×16), gunakan System5x7 untuk text panjang
  • Arial_Black_16 hanya untuk text pendek

Auto Brightness dengan LDR:

#define LDR_PIN 34

void loop() {
  int ldrValue = analogRead(LDR_PIN);
  int brightness = map(ldrValue, 0, 4095, 50, 255);
  setBrightness(brightness);
  
  // ... rest of your code
}

Multiple Panel (2 panel horizontal):

#define DISPLAYS_ACROSS 2  // Ubah dari 1 ke 2
#define DISPLAYS_DOWN 1

DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
// Container juga berubah
DMDContainer container(0, 0, 63, 15);  // 64x16 untuk 2 panel

Step 5. Template Project Lengkap (Opsional)

Copy template ini untuk project baru:

#include <DMD32Plus.h>
#include <DMDContainer.h>
#include "fonts/SystemFont5x7.h"
#include "fonts/Arial_black_16.h"

#define DISPLAYS_ACROSS 1
#define DISPLAYS_DOWN 1

DMD dmd(DISPLAYS_ACROSS, DISPLAYS_DOWN);
hw_timer_t *timer = NULL;

// Brightness Control
#define PWM_FREQ 5000
#define PWM_CHANNEL 0
#define PWM_RESOLUTION 8
#define PIN_DMD_nOE 22

void IRAM_ATTR triggerScan() {
  dmd.scanDisplayBySPI();
}

void setupBrightnessPWM() {
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
  ledcAttachPin(PIN_DMD_nOE, PWM_CHANNEL);
}

void setBrightness(uint8_t brightness) {
  ledcWrite(PWM_CHANNEL, brightness);
}

void setup() {
  Serial.begin(115200);
  
  // Setup timer
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &triggerScan, true);
  timerAlarmWrite(timer, 1000, true);
  timerAlarmEnable(timer);
  
  // Init display
  dmd.clearScreen(true);
  
  // Setup brightness
  setupBrightnessPWM();
  setBrightness(200);
  
  Serial.println("ESP32 P10 Project Ready!");
}

void loop() {
  // Your code here
}

Selamat berkreasi dengan ESP32 dan P10 LED Matrix!