Skip to main content

Web Radio - Measuring and caracterizing an audio signal

·863 words·5 mins
Wipeey
Author
Wipeey
French 18 years old IT Student with a passion for cybersecurity and computer science in general.
Table of Contents

Introduction
#

When I first saw the assignment to build a portable web radio, I was both excited and daunted. Combining microcontroller programming, audio processing, and networking felt like threading a needle through multiple disciplines. Over the course of several weeks, I went from prototype sketches on breadboards to a polished device that streams live radio stations, complete with remote-control capabilities. In this detailed account, I share what I learned, the challenges I faced, and the pride of hearing my own creation come to life.


1. Conceptualization & Planning
#

Before soldering any wires, I sketched a block diagram detailing the ESP32’s connections to the VS1053 codec, power supply, and user interface (buttons, LEDs). Researching similar projects on GitHub and the Adafruit forums helped me outline key features:

  • Wi-Fi-based streaming rather than local SD playback.
  • Physical controls: buttons for station navigation, volume, and EQ.
  • Dynamic network configuration: avoid hard-coding SSIDs.
  • Remote triggers via MQTT for smartphone control.

Sketching on paper saved me hours of rework later.


2. Hardware Exploration
#

2.1 ESP32 Feather Details
#

The Adafruit HUZZAH32 Feather’s compact form factor and LiPo charger made it ideal:

  • Pins: 21 GPIOs, two analog inputs, hardware UARTs, I²C bus.
  • Power: Micro-USB or 3.7 V LiPo battery input.
  • Peripherals: Integrated 2.4 GHz radio and BLE.

I spent an afternoon wiring the Feather to a breadboard, toggling the user LED and reading the reset button, establishing a basic loop:

#include <Arduino.h>
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(0, INPUT_PULLUP); // Boot button
}
void loop() {
  digitalWrite(LED_BUILTIN, !digitalRead(0));
  delay(200);
}

This confirmed my environment was solid.

2.2 VS1053 & Music Maker FeatherWing
#

Adafruit’s Music Maker FeatherWing includes the VS1053 codec, headphone amp, and microSD slot. Key insights:

  • The SCI bus (pins XCS, XDCS, DREQ) controls codec registers.
  • The SDI bus (MOSI, SCK) streams MP3 data.
  • DREQ line indicates buffer ready—critical for flow control.

I wired each pin to specific GPIOs on the Feather and practiced reading the device ID register:

uint16_t id = musicPlayer.readRegister(SCI_STATUS);
Serial.printf("VS1053 Status: 0x%04X\n", id);

3. Software Setup & Core Sketch
#

3.1 Arduino IDE Configuration
#

Installing support for the ESP32 required adding https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json to Preferences → Additional Boards Manager URLs. Then:

  1. Boards Manager → install esp32 by Espressif Systems.
  2. Reboot IDE and select Adafruit ESP32 Feather under Tools → Board.
  3. Verify upload speed at 921600 for fast flashing.

3.2 Library Integration
#

I installed via Library Manager:

  • Adafruit VS1053 for codec control.
  • ESP32_VS1053_Stream for HTTP streaming.
  • WiFiManager by tzapu.
  • PubSubClient by Nick O’Leary.

3.3 Basic Audio Streaming Sketch
#

My first working code used ESP32_VS1053_Stream. Key snippet:

#include <ESP32_VS1053_Stream.h>
ESP32_VS1053_Stream radio;
void setup() {
  Serial.begin(115200);
  WiFiManager wm;
  wm.autoConnect("WebRadioAP");
  radio.begin();
  radio.connectStream("http://icecast.example.com:8000/stream");
}
void loop() {
  radio.feedBuffer();
}

Hearing the first playback was surreal—my code became audible!


4. Advanced Features & Controls
#

4.1 Physical Button Handlers
#

Instead of polling, I used attachInterrupt on rising edges:

ButtonGPIOFunction
Next14Next station
Prev27Previous station
Up26Volume +
Down25Volume –
void IRAM_ATTR nextISR() { stationIndex = (stationIndex + 1) % numStations; }
attachInterrupt(digitalPinToInterrupt(14), nextISR, RISING);

Interrupts prevented missing fast presses.

4.2 Tone & Spatialization
#

Register writes allowed me to tweak EQ:

musicPlayer.setTreble(0x0A, -5); // +10 dB treble, -5 dB frequency cutoff
musicPlayer.writeRegister(SCI_MODE, musicPlayer.readRegister(SCI_MODE) | 0x08); // enable spatial

Testing different values taught me how subtle register changes shape sound.

4.3 Dynamic Wi-Fi with Captive Portal
#

WiFiManager created a hotspot named “WebRadioAP” if no saved networks. The captive portal lets me pick from local Wi-Fi SSIDs—no more recompiling for each network.


5. IoT Integration: MQTT Remote Control
#

5.1 MQTT Basics & Topics
#

I ran a local Mosquitto broker on my laptop and defined topics:

  • radio/command/station
  • radio/command/volume
  • radio/status/connected

Using PubSubClient, I implemented callbacks:

client.subscribe("radio/command/#");
void callback(char* topic, byte* payload, unsigned int len) {
  // parse payload and adjust station or volume
}

5.2 Smartphone Dashboard
#

With IoT MQTT Panel, I created:

  • Sliders: volume (0–100).
  • Buttons: next/prev station presets.
  • Status light: green when connected to broker.

Testing on the go was magical—my radio responded instantly to my phone taps.


6. Challenges & Learnings
#

ChallengeSolution
Audio buffer underrunIncreased stream buffer to 8 KB, tuned feedBuffer() timing.
Debouncing button bouncesUsed hardware RC filter + software debounce in ISR.
MQTT reconnect reliabilityImplemented reconnect() routine with exponential backoff.
Power consumption on batteryPut ESP32 in light sleep between songs, reduced LED usage.

Each hurdle deepened my understanding of embedded systems nuances.


7. Future Enhancements
#

  • OLED Interface: display station name, signal strength, volume bar.
  • Multi-band Equalizer: develop a 5-band EQ with interactive UI.
  • Web UI: serve a HTML/CSS control panel from SPIFFS on the ESP32.
  • SD-Card Playback: allow offline playback of MP3/WAV files.
  • Battery Gauge: monitor LiPo voltage and display warnings.

Conclusion
#

This project was a turning point in my studies. It bridged theory and practice—forcing me to debug electrical signals, wrestle with buffer limits, and appreciate wireless reliability. Beyond the code and circuits, I gained confidence: I can now conceive, prototype, and refine IoT devices end to end. The simple pleasure of tuning into my own creation’s stations reminds me why I love this field and fuels my excitement for future projects.