Radio Streaming ESP8266
/*
* ============================================================================
* ESP8266 Internet Radio dengan OLED 0.96" & PAM8405 (Class D)
* Fitur:
* - Output Audio PWM via Pin RX (I2SNoDAC) untuk PAM8405
* - Penghemat Memori Flash / EEPROM (Delayed Write / Dirty Flag)
* - Tampilan OLED 0.96" I2C
* - Navigasi Stasiun dengan Tombol D5 (GPIO14)
* ============================================================================
*/
#include <ESP8266WiFi.h>
#include <EEPROM.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Library ESP8266Audio
#include "AudioFileSourceHTTPStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2SNoDAC.h"
// ----------------------------------------------------------------------------
// CONFIG WIFI & OLED
// ----------------------------------------------------------------------------
const char* WIFI_SSID = "NAMA_WIFI_KAMU"; // Ganti dengan SSID WiFi kamu
const char* WIFI_PASSWORD = "PASSWORD_WIFI_KAMU"; // Ganti dengan Password WiFi kamu
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ----------------------------------------------------------------------------
// PIN CONFIGURATION
// ----------------------------------------------------------------------------
#define BUTTON_NEXT_PIN 14 // D5 / GPIO14 (Mode INPUT_PULLUP)
// ----------------------------------------------------------------------------
// DAFTAR STASIUN RADIO (Stream URL MP3)
// ----------------------------------------------------------------------------
struct RadioStation {
const char* name;
const char* url;
};
const RadioStation STATIONS[] = {
{"Prambors FM", "http://masima.it-mks.com:8000/prambors"},
{"Hard Rock FM", "http://stream.mra.co.id:8000/hardrockfm"},
{"I-Radio Jakarta", "http://stream.mra.co.id:8000/iradio"},
{"Jazz Radio", "http://stream.zeno.fm/f3wvbbqmdg8uv"}
};
const uint8_t TOTAL_STATIONS = sizeof(STATIONS) / sizeof(STATIONS[0]);
// ----------------------------------------------------------------------------
// GLOBAL AUDIO OBJECTS
// ----------------------------------------------------------------------------
AudioGeneratorMP3 *mp3 = NULL;
AudioFileSourceHTTPStream *file = NULL;
AudioFileSourceBuffer *buff = NULL;
AudioOutputI2SNoDAC *out = NULL;
const int BUFFER_SIZE = 2048; // Buffer RAM (2KB) untuk mencegah buffering/stuttering
// ----------------------------------------------------------------------------
// LOGIKA PENGHEMAT EEPROM (DELAYED WRITE / DIRTY FLAG)
// ----------------------------------------------------------------------------
#define EEPROM_ADDR_STATION 0
#define WRITE_DELAY_MS 5000 // Tulis ke EEPROM setelah 5 detik tanpa penekanan tombol baru
uint8_t currentStation = 0;
bool pendingEEPROMWrite = false;
unsigned long lastButtonPressTime = 0;
// Debouncing tombol
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long DEBOUNCE_DELAY = 50;
// Prototype Fungsi
void updateDisplay(const char* statusText);
void stopStream();
void startStream(uint8_t stationIdx);
void handleButton();
void checkEEPROMCommit();
// ----------------------------------------------------------------------------
// SETUP
// ----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
delay(500);
// Pin Tombol Navigasi
pinMode(BUTTON_NEXT_PIN, INPUT_PULLUP);
// Inisialisasi OLED (SDA = D2/GPIO4, SCL = D1/GPIO5)
Wire.begin(4, 5);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("OLED Gagal Diinisialisasi"));
for(;;);
}
updateDisplay("Connecting WiFi...");
// Inisialisasi EEPROM
EEPROM.begin(512);
currentStation = EEPROM.read(EEPROM_ADDR_STATION);
if (currentStation >= TOTAL_STATIONS) {
currentStation = 0; // Reset ke 0 jika data corrupt / flash pertama
}
// Koneksi ke WiFi
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi Connected!");
// Audio Output via Delta-Sigma PWM Pin RX (GPIO3)
out = new AudioOutputI2SNoDAC();
// Jalankan Stasiun Terakhir yang Tersimpan
startStream(currentStation);
}
// ----------------------------------------------------------------------------
// LOOP
// ----------------------------------------------------------------------------
void loop() {
// 1. Cek Penekanan Tombol
handleButton();
// 2. Cek Timer Tulis EEPROM (Dirty Flag)
checkEEPROMCommit();
// 3. Panggil Pemutar MP3 Non-Blocking
if (mp3 && mp3->isRunning()) {
if (!mp3->loop()) {
mp3->stop();
Serial.println("Stream terhenti. Merestart...");
startStream(currentStation);
}
}
}
// ----------------------------------------------------------------------------
// FUNGSI MANAJEMEN STREAM AUDIO
// ----------------------------------------------------------------------------
void stopStream() {
if (mp3) {
if (mp3->isRunning()) mp3->stop();
delete mp3;
mp3 = NULL;
}
if (buff) {
delete buff;
buff = NULL;
}
if (file) {
delete file;
file = NULL;
}
}
void startStream(uint8_t stationIdx) {
stopStream();
updateDisplay("Buffering...");
Serial.print("Membuka Stasiun: ");
Serial.println(STATIONS[stationIdx].name);
file = new AudioFileSourceHTTPStream(STATIONS[stationIdx].url);
buff = new AudioFileSourceBuffer(file, BUFFER_SIZE);
mp3 = new AudioGeneratorMP3();
if (mp3->begin(buff, out)) {
updateDisplay("PLAYING");
} else {
updateDisplay("ERROR STREAM");
Serial.println("Gagal memulai MP3 Stream");
}
}
// ----------------------------------------------------------------------------
// FUNGSI TOMBOL & DELAYED WRITE EEPROM
// ----------------------------------------------------------------------------
void handleButton() {
bool reading = digitalRead(BUTTON_NEXT_PIN);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
static bool buttonState = HIGH;
if (reading != buttonState) {
buttonState = reading;
// Saat tombol ditekan ke GND
if (buttonState == LOW) {
currentStation = (currentStation + 1) % TOTAL_STATIONS;
Serial.print("Pindah Stasiun (RAM): ");
Serial.println(STATIONS[currentStation].name);
// Ganti stream audio langsung di RAM
startStream(currentStation);
// Tandai bahwa ada data baru yang menunggu disimpan ke EEPROM
pendingEEPROMWrite = true;
lastButtonPressTime = millis();
}
}
}
lastButtonState = reading;
}
void checkEEPROMCommit() {
// Hanya tulis ke EEPROM jika ada data baru DAN sudah lewat 5 detik tanpa penekanan tombol
if (pendingEEPROMWrite && (millis() - lastButtonPressTime >= WRITE_DELAY_MS)) {
EEPROM.write(EEPROM_ADDR_STATION, currentStation);
EEPROM.commit(); // Tulis fisik ke flash memory
pendingEEPROMWrite = false;
Serial.println(">> EEPROM BERHASIL DIPERBARUI <<");
}
}
// ----------------------------------------------------------------------------
// FUNGSI DISPLAY OLED
// ----------------------------------------------------------------------------
void updateDisplay(const char* statusText) {
display.clearDisplay();
// Header
display.fillRect(0, 0, 128, 14, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
display.setTextSize(1);
display.setCursor(12, 3);
display.print("ESP8266 RADIO");
// Nama Stasiun
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 22);
display.print("Stasiun [");
display.print(currentStation + 1);
display.print("/");
display.print(TOTAL_STATIONS);
display.println("]:");
display.setTextSize(1);
display.setCursor(0, 34);
display.println(STATIONS[currentStation].name);
// Status Streaming & Indikator Pending EEPROM
display.setCursor(0, 52);
display.print("Status: ");
display.print(statusText);
// Tanda asterisk (*) muncul jika stasiun baru belum disimpan ke EEPROM
if (pendingEEPROMWrite) {
display.print(" *");
}
display.display();
}
Post a Comment for "Radio Streaming ESP8266"