[ ACCESSING_ARCHIVE ]

DIY Radio Mini ESP32: Sentuhan Klasik Kontrol Putar Modern

November 08, 2026 • BY azzar
[ READ_TIME: 11 MIN ] |
. . .

Pendahuluan: Kenapa Orang Gila Ini Milih Ngerakit Radio ESP32?

Salam Wong Edan! Halo para penggila solder, pemuja LED kelap-kelip, dan ksatria kabel jumper yang sering lupa tidur gara-gara nyari kernel panic atau stack overflow! Kalau kalian berpikir zaman sekarang mendengarkan radio itu harus pakai smartphone yang layarnya penuh notifikasi tagihan paylater, kalian salah besar. Hari ini, otak gila saya membawa kita kembali ke era keemasan audio analog, tapi dengan jeroan abad ke-21 yang sangat modern!

Kita akan merakit sebuah proyek radio internet mini (web radio) berbasis mikrokontroler ESP32. Kontrolnya? Bukan layar sentuh yang gampang kotor kena minyak gorengan, melainkan rotary encoder putar tak terbatas yang memberikan sensasi taktil klik-klik tak tertandingi. Inspirasi pembuatan perangkat mini berkontrol dial yang estetis dan fungsional ini bermula dari tren fabrikasi proyek DIY kreatif yang juga diulas di XDA Developers, di mana antarmuka fisik klasik dipadukan secara elegan dengan konektivitas IoT modern.

Di artikel yang panjang, mendalam, dan teknis sampai ke akar-akarnya ini, kita tidak hanya akan menghubungkan dua buah kabel lalu berdoa pada Dewa Elektronika. Kita bakal membahas arsitektur audio digital I2S, manajemen ring buffer untuk mencegah audio stuttering, metode interupsi perkodean quadrature pada rotary encoder, hingga desain manajemen daya yang bersih dari interferensi ground loop. Tarik napas dalam-dalam, panaskan solder 40 Watt kalian, dan mari kita mulai kegilaan ini!

1. Arsitektur Sistem & Hardware: Dapur Pacu Mikro-Radio

Sebelum memasang komponen, kita harus memahami bagaimana sinyal data dari awan (cloud) berubah menjadi getaran fisik di gendang telinga kita. Jalur pemrosesan data pada radio mini ESP32 ini dapat dibagi menjadi empat blok utama:

  • Blok Pengolahan Data & Networking: ESP32-WROOM-32 (Dual-core Xtensa LX6, 240 MHz, SRAM 520 KB).
  • Blok Antarmuka Pengguna (UI/UX): Rotary Encoder EC11 (Quad encoder + Push Button) dan Layar OLED SSD1306 0.96 inci (I2C interface).
  • Blok Konversi Sinyal Digital ke Analog (DAC): Modul PCM5102A (I2S Protocol, 24-bit/32-bit, SNR 112dB).
  • Blok Amplifikasi & Output Suara: Amplifier Class-D PAM8403 + Speaker Neodymium Full-Range 3 Watt 4 Ohm.

Mengapa Memakai DAC Eksternal I2S (PCM5102A) dibanding DAC Internal ESP32?

ESP32 sebenarnya memiliki 2 saluran DAC internal 8-bit pada GPIO25 dan GPIO26. Tapi dengarlah wahai kawan: menyalurkan musik audio MP3/AAC 320kbps lewat DAC internal 8-bit ESP32 adalah sebuah dosa kejahatan musik! DAC internal 8-bit hanya memiliki Dynamic Range sekitar 48 dB dengan quantization noise yang sangat parah. Suaranya akan terdengar seperti kaleng kerupuk tersiram air hujan.

Oleh karena itu, kita wajib menggunakan DAC eksternal berbasis protokol I2S (Inter-IC Sound) seperti PCM5102A. Protokol I2S mentransmisikan data audio digital tanpa kompresi secara serial melalui tiga jalur utama: Continuous Serial Clock (BCLK), Word Select / Left-Right Clock (LRCK), dan Serial Data (DIN). Hasilnya? Resolusi hingga 24-bit, Signal-to-Noise Ratio (SNR) mencapai 112 dB, dan total harmonic distortion (THD) yang sangat rendah.

2. Skema Rangkaian & Pinout (Pinoutan Tanpa Asap Suci)

Jangan sampai ada insiden “asap suci” keluar dari ESP32 kalian karena salah colok VCC ke GND. Berikut adalah pemetaan pinout standar yang stabil dan bebas konflik dengan pin Strapping ESP32:

A. Pinout Modul I2S DAC (PCM5102A)

  • VCC: 5V (diambil dari VIN ESP32 atau Bus 5V USB)
  • GND: Ground (Wajib disatukan dengan Star Ground)
  • BCK (Bit Clock): GPIO 26
  • LRCK (Left/Right Clock): GPIO 25
  • DIN (Data Input): GPIO 27
  • SCK (System Clock): Hubungkan jumper HW ke GND pada modul PCM5102A (karena modul ini mendukung internal PLL clock generation).

B. Pinout Rotary Encoder (EC11)

  • CLK (Output A): GPIO 18 (Membutuhkan Pull-up internal/eksternal)
  • DT (Output B): GPIO 19 (Membutuhkan Pull-up internal/eksternal)
  • SW (Switch Button): GPIO 23 (Membutuhkan Pull-up internal)
  • VCC / GND: 3.3V dan GND

C. Pinout Display OLED 0.96″ SSD1306 (I2C)

  • SDA: GPIO 21
  • SCL: GPIO 22
  • VCC / GND: 3.3V dan GND

3. Manajemen Sinyal Audio Digital & FreeRTOS Dual-Core Architecture

ESP32 dikaruniai dua inti prosesor: Core 0 (PRO_CPU) dan Core 1 (APP_CPU). Ini adalah berkah dari langit untuk proyek audio streaming! Mengapa?

Proses mendengarkan radio internet melibatkan dua tugas berat yang saling bertabrakan jika dijalankan secara sekuensial pada satu inti:

  1. Menerima paket HTTP/TCP dari WiFi secara terus-menerus tanpa boleh mengalami drop paket.
  2. Mendekodekan stream data MP3/AAC menjadi sampel mentah PCM 16-bit dan mengalirkannya ke bus I2S dengan waktu presisi tinggi (44.1 kHz).

Jika proses membaca encoder atau memperbarui tampilan OLED dilakukan di inti yang sama dengan proses audio decoding, buffer audio akan mendadak kosong (buffer underrun), menyebabkan suara radio patah-patah atau mengeluaran bunyi pop/click. Dalam firmware kustom kita, kita akan membagi beban ini menggunakan FreeRTOS Task Pinning:

  • Core 0: Mengurus tumpukan koneksi WiFi, pemeliharaan koneksi HTTP, dan penarikan data buffer audio streaming.
  • Core 1: Mengurus pembacaan Rotary Encoder (Interrupt-driven), kalkulasi antarmuka tampilan OLED, serta eksekusi decoding I2S Audio.

4. Implementasi Perkodean Rotary Encoder (Quadrature Decoding)

Rotary encoder EC11 bekerja dengan cara menghasilkan dua sinyal gelombang kotak yang saling berbeda fase 90 derajat (Fase A dan Fase B). Untuk membaca arah putaran (searah jarum jam / CW atau berlawanan / CCW) dengan presisi tanpa terjadinya debouncing mekanis yang membingungkan mikrokontroler, kita memanfaatkan teknik State Machine di dalam Interrupt Service Routine (ISR).

Setiap kali ada perubahan status pada pin CLK atau DT, interupsi hardware diaktifkan. Algoritma membaca status gabungan 2-bit dari fase lama dan fase baru:


// Matriks Transisi Encoder (State Machine Table)
// 0: Tidak ada gerakan, 1: Searah Jarum Jam (CW), -1: Berlawanan Jarum Jam (CCW)
const int8_t encoder_states[] = {
0, -1,  1,  0,
1,  0,  0, -1,
-1,  0,  0,  1,
0,  1, -1,  0
};
static uint8_t old_AB = 0;
void IRAM_ATTR readEncoderISR() {
old_AB <<= 2; // Geser status lama
old_AB |= ((digitalRead(PIN_ROT_CLK) << 1) | digitalRead(PIN_ROT_DT)) & 0x03;
int8_t state = encoder_states[(old_AB & 0x0F)];
if (state != 0) {
encoderValue += state;
}
}

5. Kode Sumber Firmware Lengkap (Arduino Framework / ESP32)

Berikut adalah firmware produksi lengkap untuk Radio Mini ESP32. Kode ini memanfaatkan pustaka populer ESP32-audioI2S karya schreibfaul1 yang sangat teroptimasi untuk decoding hardware I2S, serta pustaka Adafruit_SSD1306 untuk visualisasi antarmuka.


#include <Arduino.h>
#include <WiFi.h>
#include "Audio.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// --- DEFINISI PIN ---
#define I2S_DSPO  27
#define I2S_BCLK  26
#define I2S_LRCK  25
#define ROTARY_CLK 18
#define ROTARY_DT  19
#define ROTARY_SW  23
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// --- OBJEK AUDIO ---
Audio audio;
// --- KREDENSIAL WIFI ---
const char* ssid     = "KONTRAKAN_WONG_EDAN";
const char* password = "kopihitamnopaste";
// --- DAFTAR STASIUN RADIO INTERNET (STREAM URL) ---
struct Station {
String name;
String url;
};
Station stationList[] = {
{"Jazz FM Classic", "http://stream.jazzfm.com/live"},
{"Retro Synthwave", "http://stream.synthwave.zone/radio.mp3"},
{"Lo-Fi Chill Hop", "http://streaming.shoutcast.com/lofi-beats"},
{"Rock Classic HQ", "http://icecast.dmd2.ch/rsn_mp3-128"}
};
const int totalStations = sizeof(stationList) / sizeof(stationList[0]);
volatile int currentStationIdx = 0;
volatile int volumeLevel = 12; // Skala 0-21
volatile bool isVolumeMode = false;
// Variables untuk tracking encoder
volatile int32_t encoderCounter = 0;
int32_t lastEncoderCounter = 0;
// State Machine Matriks untuk Encoder
const int8_t KNOB_STATES[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
volatile uint8_t bState = 0;
void IRAM_ATTR encoder_isr() {
bState <<= 2;
bState |= ((digitalRead(ROTARY_CLK) << 1) | digitalRead(ROTARY_DT)) & 0x03;
int8_t action = KNOB_STATES[(bState & 0x0F)];
if (action != 0) {
encoderCounter += action;
}
}
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// Header Status
display.setCursor(0, 0);
display.print("RADIO ESP32 - ");
display.print(isVolumeMode ? "[VOL]" : "[CH]");
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
// Nama Stasiun Radio
display.setTextSize(1);
display.setCursor(0, 20);
display.print("Stasiun:");
display.setCursor(0, 32);
display.setTextSize(1);
display.print(stationList[currentStationIdx].name);
// Baris Volume Level
display.setCursor(0, 52);
display.print("Vol: ");
display.print(volumeLevel);
display.print("/21 ");
// Visualizer volume sederhana
int barWidth = map(volumeLevel, 0, 21, 0, 50);
display.drawRect(70, 52, 52, 8, SSD1306_WHITE);
display.fillRect(71, 53, barWidth, 6, SSD1306_WHITE);
display.display();
}
void setup() {
Serial.begin(115200);
// Setup Pinout Encoder
pinMode(ROTARY_CLK, INPUT_PULLUP);
pinMode(ROTARY_DT, INPUT_PULLUP);
pinMode(ROTARY_SW, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ROTARY_CLK), encoder_isr, CHANGE);
attachInterrupt(digitalPinToInterrupt(ROTARY_DT), encoder_isr, CHANGE);
// Setup OLED Display
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("OLED SSD1306 Gagal diinisialisasi!"));
for(;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 20);
display.print("Connecting WiFi...");
display.display();
// Koneksi WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi Terhubung!");
// Setup I2S Audio Output
audio.setPinout(I2S_BCLK, I2S_LRCK, I2S_DSPO);
audio.setVolume(volumeLevel); // 0-21
audio.connecttohost(stationList[currentStationIdx].url.c_str());
updateDisplay();
}
void loop() {
// Jalankan siklus audio loop secara konsisten (non-blocking)
audio.loop();
// Cek Tombol Rotary Encoder (Toggle Mode Volume / Stasiun)
static uint32_t lastBtnPress = 0;
if (digitalRead(ROTARY_SW) == LOW & (millis() - lastBtnPress > 300)) {
lastBtnPress = millis();
isVolumeMode = !isVolumeMode; // Toggle mode
updateDisplay();
}
// Cek Perubahan Putaran Encoder
if (encoderCounter != lastEncoderCounter) {
int diff = (encoderCounter - lastEncoderCounter) / 2; // Deviasi pulsa encoder
if (abs(diff) >= 1) {
if (isVolumeMode) {
// Adjust Volume
volumeLevel += diff;
if (volumeLevel < 0) volumeLevel = 0;
if (volumeLevel > 21) volumeLevel = 21;
audio.setVolume(volumeLevel);
} else {
// Adjust Stasiun Radio
currentStationIdx += diff;
if (currentStationIdx < 0) currentStationIdx = totalStations - 1;
if (currentStationIdx >= totalStations) currentStationIdx = 0;
// Ganti URL Stream Audio
audio.connecttohost(stationList[currentStationIdx].url.c_str());
}
lastEncoderCounter = encoderCounter;
updateDisplay();
}
}
}
// Optional Audio Event Callbacks
void audio_info(const char *info){
Serial.print("info        "); Serial.println(info);
}
void audio_bitrate(const char *info){
Serial.print("bitrate     "); Serial.println(info);
}

6. Mengapa Memakai ESP32 dan Bukan Single Board Computer (SBC)?

Mungkin ada di antara kalian yang nyeletuk: “Kang Wong Edan, kenapa nggak pakai Raspberry Pi aja sih yang OS-nya tinggal di-install, kelar urusan?”

Eits, jangan salah! Memang benar Raspberry Pi atau SBC sejenisnya memiliki kapabilitas pemrosesan audio yang luar biasa tinggi. Tren penggunaan sistem tertanam untuk berbagai kebutuhan khusus memang membuat perangkat seperti Raspberry Pi tetap relevan dan dicari, sebagaimana sering dibahas pada artikel perbandingan komputer mikro seperti di How-To Geek. Namun, untuk sebuah Radio Mini Spesifik yang ditaruh di meja samping tempat tidur atau dapur, mikrokontroler seperti ESP32 menang telak dalam beberapa aspek krusial:

  1. Waktu Booting Instant (Instant-On): ESP32 hanya butuh waktu kurang dari 1 detik untuk booting dan menyalakan audio setelah dicolok listrik. Raspberry Pi butuh waktu 30–60 detik hanya untuk loading OS Linux-nya.
  2. Konsumsi Daya (Power Efficiency): ESP32 yang sedang melakukan streaming audio I2S hanya menyedot daya sekitar 160mA–240mA pada tegangan 5V (sekitar 1 Watt). Raspberry Pi 4 bisa memakan daya 3 Watt hingga 7 Watt! Ini krusial jika kalian ingin memasang baterai Li-ion 18650 agar radio bisa dibawa jalan-jalan (portable).
  3. Kekebalan dari File-System Corruption: Raspberry Pi yang dimatikan mendadak dengan mencabut kabel power bisa merusak SD Card (corrupted filesystem). ESP32 menggunakan Flash Memory XIP (Execute-in-Place) yang aman dimatikan kapan saja tanpa rasa takut!

7. Rekayasa Peredaman Derau & Tata Letak Aksial (Acoustics & Power Decoupling)

Merakit radio internet bukan cuma urusan coding dan colok kabel, kawan! Musuh terbesar dari perangkat audio mini digital berbasis IoT adalah Ground Loop Noise dan WiFi Switching Ripple.

Pernahkah kalian mendengar suara *”tit-tit-tit-buzzing”* halus dari speaker saat ESP32 sedang melakukan koneksi WiFi? Itu adalah derau frekuensi radio (RF Noise) yang masuk ke jalur catu daya amplifier PAM8403 melalui modul DAC!

Langkah-Langkah Mengeliminasi Noise:

  • Skema Star Grounding: Jangan menghubungkan Ground amplifier, DAC, dan ESP32 secara berantai (daisy-chain). Tarik kabel Ground dari masing-masing modul menuju satu titik pusat di terminal pembuangan negatif power supply/baterai.
  • Filter Kapasitor Decoupling: Pasang kapasitor Elektrolit (Elco) berkapasitas 470µF hingga 1000µF paralel dengan kapasitor Keramik 100nF persis di dekat pin VCC dan GND modul amplifier PAM8403. Kapasitor 100nF menyaring derau frekuensi tinggi (RF Ripple), sedangkan Elco menyerap penurunan tegangan saat dentuman bass (transient load).
  • Isolasi Casing Akustik: Jangan letakkan speaker 3 Watt tanpa ruang akustik (enclosure) yang terisolasi. Buatlah rongga tertutup (sealed box) dari bahan kayu MDF atau hasil 3D-Print dengan pengisi peredam (cotton/polyfil) di dalamnya. Suara bass 100 Hz tidak akan bisa terpancar jika gelombang suara depan dan belakang speaker saling meniadakan (acoustic phase cancellation).

8. Troubleshooting & Diagnosis Kerusakan

Bagi kalian yang mengalami keanehan saat pengujian, berikut adalah lembar diagnosis cepat ala Wong Edan:

Gejala: Suara radio tersendat-sendat (stuttering) tiap 5 detik sekali.
Penyebab: Alokasi buffer audio habis akibat kecepatan WiFi yang tidak stabil, atau fungsi audio.loop() terhalang oleh fungsi delay() di dalam fungsi loop() utama.
Solusi: Hapus semua sintaks delay() pada loop utama! Gunakan metode waktu non-blocking berbasis millis().

Gejala: Rotary encoder membaca angka meloncat-loncat (misal: dari vol 12 melompat ke vol 20).
Penyebab: Kontak sakelar mekanis internal encoder mengalami bouncing atau tidak ada kapasitor filter.
Solusi: Pasang kapasitor keramik 10nF dari pin CLK ke GND, serta dari pin DT ke GND sebagai filter hardware low-pass. Atau gunakan matriks state-machine ISR yang sudah dicantumkan pada kode di atas.

Kesimpulan: Kemenangan Seni Analog di Era Digital

Merakit DIY Radio Mini ESP32 ini bukan sekadar membuktikan bahwa kita bisa membuat barang yang dijual murah di toko online. Ini adalah kemenangan kepuasan batin! Kita berhasil menyatukan keindahan antarmuka fisik masa lalu—dengan putaran dial tactile yang memuaskan—dengan fleksibilitas ekosistem streaming audio digital masa kini.

Dengan modal komponen yang tidak seberapa, sedikit timah solder, dan ketelitian baris kode C++, kalian sekarang resmi memegang kendali penuh atas perangkat audio kalian sendiri. Tidak ada pelacakan data privasi, tidak ada iklan pop-up yang mengganggu, hanya ada kalian, tombol putar vintage, dan alunan musik favorit yang terpancar jernih dari modul ESP32.

Sekian panduan gila tapi sangat ilmiah ini. Sekarang ganti baju kalian, bersihkan sisa fluks di meja kerja, dan nikmati alunan radio buatan tangan sendiri. Salam Edan, dan selamat berkarya!

[ END_OF_ENTRY ]
[ SUCCESS: COPIED_TO_CLIPBOARD ]
[ ARCHIVAL_COMMAND_INDEX ]
SHOW_COMMANDS?
SEARCH_ARCHIVECTRL+K / /
GOTO_INDEXSHIFT+H
NEXT_ENTRY_PAGE]
PREV_ENTRY_PAGE[
COPY_LINKSHIFT+S
CITE_SPECIMENC
MOVE_FOCUSW / S
ACTION_KEYENTER
PRINT_SPECIMENCTRL+P
PRECISION_DOWNJ
PRECISION_UPK
CLOSE_ALLESC
[ ARCHIVAL_CITATION_SPECIMEN ]
APA_FORMAT
azzar. (2026). DIY Radio Mini ESP32: Sentuhan Klasik Kontrol Putar Modern. Glass Gallery. Retrieved from https://wp.glassgallery.my.id/diy-radio-mini-esp32-sentuhan-klasik-kontrol-putar-modern/
[ CLICK_TO_COPY ]
MLA_FORMAT
azzar. "DIY Radio Mini ESP32: Sentuhan Klasik Kontrol Putar Modern." Glass Gallery, 2026, November 08, https://wp.glassgallery.my.id/diy-radio-mini-esp32-sentuhan-klasik-kontrol-putar-modern/.
[ CLICK_TO_COPY ]
CHICAGO_STYLE
azzar. "DIY Radio Mini ESP32: Sentuhan Klasik Kontrol Putar Modern." Glass Gallery. Last modified 2026, November 08. https://wp.glassgallery.my.id/diy-radio-mini-esp32-sentuhan-klasik-kontrol-putar-modern/.
[ CLICK_TO_COPY ]
BIBTEX_ENTRY
@misc{glassgallery_1841,
  author = "azzar",
  title = "DIY Radio Mini ESP32: Sentuhan Klasik Kontrol Putar Modern",
  howpublished = "\url{https://wp.glassgallery.my.id/diy-radio-mini-esp32-sentuhan-klasik-kontrol-putar-modern/}",
  year = "2026",
  note = "Retrieved from Glass Gallery"
}
[ CLICK_TO_COPY ]
TECHNICAL_REF
[ REF: DIY RADIO MINI ESP32: SENTUHAN KLASIK KONTROL PUTAR MODERN | SRC: GLASS GALLERY | INDEX: 1841 ]
[ CLICK_TO_COPY ]