Esp8266 Internet Radio

by Isaacs86 in Circuits > Arduino

18501 Views, 37 Favorites, 0 Comments

Esp8266 Internet Radio

IMG_20211123_100801.jpg

This Is a Small Lo Fi Internet. Radio poject based on esp8266 lolin microcontroller. With oled screen and rotary encoder for Channel selection.

The radio code is based on this project:

The code is ready with 10 Station selection menu and you can modify it for many more if you want.

The radio code designed to remember your last station in eeprom. so when you turn the radio off and then on it will connect automatically to the last station you heard. If in any case ther is buffer error or the wifi is disconnected for some reason. The microcontroller will try automatically to connect to that station again forever.

When you ar connected to a station you can turn the encoder left or right to choose another station. then a short click on the button will connect you to that station.

If for some reason the station stream is bad and the radio can't connect to that specific

Station. press long press of 3 seconds on the butten when the radio trying to connect the station. that will reset the MCU and will bring you to the station selection menu. turn the encoder to choose a different station and then a short click on the button will connect you to that station.

I also added 2 leds for indication. a red led for power indication and green led that's light's up when the radio is connected to station.

Rota

Supplies

Supplies

-Esp 8266 lolin microcontroller

-Oled screen ssd1306 128x64

-Rotary encoder with swוtch

-Lm386

-Speaker

-Veraboard

-wire's

Resistors

10k Pot x1

10k x2

220R x2

10R x1

Capacitors

0.01 uf x1

0.05 uf x1

10uf x2

220 uf x2

Leds

-Green led

-Red led

Schematics

ESP8266_oled_display_wiring.png
esp8266ArduinoEsp32pcf8574EncodersModuleSchema.jpg
LM386.png
esp32-potentiometer-pinout.jpg

Here is all schematics for this project.

Use the pdf file to see all pin order connections between esp8266 to components


Full schematic

-Esp8266 Lo Fi internet radio schematic.pdf.


External info schematic

-Oled ssd1306 with esp8266 schematic

-Encoder connection schemaric

-Lm368 Amp schematic

-potentiometer pin out

-

Build

IMG_20211121_092259.jpg
IMG_20211123_092709.jpg
IMG_20211129_070945.jpg
IMG_20211123_100801.jpg

For the building of the circuit I recommend to follow this steps:

First. place a female header socket on the esp8266 pins and solder it to a veraboard

Then you can remove the the esp8266 for the rest of the soldering work.

I recommend to use a row of male headers on veraboard for comfortable connections between esp8266 boatd to the other components.

Connecting the screen

Pin SCL of the screen connected to D1 pin in the esp8266

Pin SDA of the screen connected to D2 pin in the esp8266

Pin VCC of the screen connected to 3.3V pin in the esp8266

Pin GND of the screen connected to GND pin in the esp8266

Connecting the encoder

Pin A (CLK) of the encoder connected to D5 pin in the esp8266

Pin B (DT) of the encoder connected to D6 pin in the esp8266

Pin SW of the encoder connected to D7 pin in the esp8266

Pin C (GND) And SW GND pin of the encoder connected to GND pin in the esp8266

Pin 3.3V of the esp8266 connected to pin A in the encoder through 10K reisistor

Pin 3.3V of the esp8266 connected to pin B in the encoder through 10K reisistor

Building the amp

Pin 1 of the lm386 connected to pin 8 of the lm386 through a 10uf cap

Pin 2 of the lm386 connected to OUTPUT pin of the 10K potentiometer

VCC pin ot the potentiometer connected to RX Pin of the esp8266

GND pin ot the potentiometer connected to GND pin of the esp8266

Pin 3 and pin 4 of the lm386 connected to GND pin of th esp8266

Pin 7 of the lm386 connected to GND through 10uf capacitor.

Pin 6 of the lm386 connected to VIN pin of the esp8266

Pin 6 of the lm386 connected to GND through 0.01uf capacitor

Pin 6 of the lm386 connected to GND through 220uf capacitor

Pin 5 of the lm386 connected to the + (positive) of the speaker through 220uf capacitor

Pin 5 of the lm386 connected to GND through 0.05uf cap and 10R resistor in series connecrion.

Leds

Pin D3 of the esp8266 connected to GND through Green led and 220R resistor in series connection.

Pin D4 of the esp8266 connected to GND through Red led and 220R resistor in series connection

The Code

Before compileing the code in arduino software be ensure You installed to the arduino library all the required libraries that used in this code.

Here is the required libraries

ESP8266Audio

Esp rotary

Button2

Adafruit SSD1306


Read Here For more info about how to add libraries to arduino IDE software.

If you haven’t setup your Arduino IDE for Esp8266, First you need to setup Arduino IDE for ESP8266.

1. Open Arduino IDE, go to tools → Board → NodeMCU 1.0

2. Go to tools → change CPU frequency to 160 MHz

3. compile the code.


#include <Arduino.h>
#include <EEPROM.h>
#include <ESP8266WiFi.h>


#include "Button2.h" //  https://github.com/LennartHennigs/Button2
#include "ESPRotary.h"


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


#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2SNoDAC.h"


#define ROTARY_PIN1 D5
#define ROTARY_PIN2 D6
#define BUTTON_PIN  D7


#define CLICKS_PER_STEP 4   // this number depends on your rotary encoder
#define MIN_POS         0
#define MAX_POS         9
#define START_POS       0
#define INCREMENT       1   // this number is the counter increment on each step


// Enter your WiFi setup here:
#ifndef STASSID
#define STASSID "YOUR WIFI NAME"
#define STAPSK  "PASSWORD"
#endif


#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels


// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


AudioGeneratorMP3 *mp3;
AudioFileSourceICYStream *file;
AudioFileSourceBuffer *buff;
AudioOutputI2SNoDAC *out;


ESPRotary r;
Button2 b;    // https://github.com/LennartHennigs/Button2


const char* ssid = STASSID;
const char* password = STAPSK;


int addr = 0;
int address = 0;


int ch = 0;
int autorun = 0;


//URL'S
const char *URL[] = {"http://jazz.streamr.ru/jazz-64.mp3",
                     "http://a1rj.streams.com.br:7801/sm",
                     "http://www.golden-apple.com:680/;",
                     "http://stm14.mfmedios.info:8048/;",
                     "http://stream002.radio.hu:80/mr2.mp3",
                     "http://live02.rfi.fr/rfimonde-64.mp3",
                     "http://live.wbcb1490.com:88/broadwavehigh.mp3",
                     "http://14543.live.streamtheworld.com:3690/XHFO_FM_SC",
                     "http://14523.live.streamtheworld.com:3690/KNBAFM_SC",
                     "http://sa.mp3.icecast.magma.edge-access.net:7200/sc_rad31",
                     "http://stream.lt8.com.ar:8080/delsiglo995.mp3"
                    };
//URL'S Names
const char *ChName[] = {"Jazz RU", //0
                        "ALJ",     //1
                        "Golden Apple", //2
                        "Mfmedios",     //3
                        "Petőfi Rádió", //4
                        "RFI - Monde",  //5
                        "WBCB UK",      //6
                        "XHFO FM",       //7
                        "KNBA FM",       //8
                        "Radio Nacional" //9
                       };


// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
{
  const char *ptr = reinterpret_cast<const char *>(cbData);
  (void) isUnicode; // Punt this ball for now
  // Note that the type and string may be in PROGMEM, so copy them to RAM for printf
  char s1[32], s2[64];
  strncpy_P(s1, type, sizeof(s1));
  s1[sizeof(s1) - 1] = 0;
  strncpy_P(s2, string, sizeof(s2));
  s2[sizeof(s2) - 1] = 0;
  Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2);
  Serial.flush();
}


// Called when there's a warning or error (like a buffer underflow or decode hiccup)
void StatusCallback(void *cbData, int code, const char *string)
{
  const char *ptr = reinterpret_cast<const char *>(cbData);
  // Note that the string may be in PROGMEM, so copy it to RAM for printf
  char s1[64];
  strncpy_P(s1, string, sizeof(s1));
  s1[sizeof(s1) - 1] = 0;
  Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
  Serial.flush();
}


void setup()
{
  Serial.begin(115200);
  EEPROM.begin(512);
  EepromRead();


  b.begin(BUTTON_PIN);
  b.setTapHandler(ShorPress);
  b.setLongClickHandler(LongPress);
  r.begin(ROTARY_PIN1, ROTARY_PIN2, CLICKS_PER_STEP, MIN_POS, MAX_POS, ch, INCREMENT);
  r.setChangedHandler(rotate);


  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }
  display.clearDisplay();
  display.display();
  
  pinMode(D3, OUTPUT);
  pinMode(D4, OUTPUT);
  digitalWrite(D3, LOW);
  digitalWrite(D4, HIGH);
  
  ConnectToWifi();
  RunStream();
}


void ConnectToWifi() {
  Serial.println("Connecting to WiFi");
  WiFi.disconnect();
  WiFi.softAPdisconnect(true);
  WiFi.mode(WIFI_STA);
  WiFi.setPhyMode(WIFI_PHY_MODE_11G); // <== IMPORTANT to get stable connections!!!!
  WiFi.begin(ssid, password);


  // Try forever
  while (WiFi.status() != WL_CONNECTED) {
    triangle(); //test//
  }
    draw();
}
void EepromRead() {
  ch = EEPROM.read(0);
  delay(500);
}


void EepromWrite() {
  EEPROM.write(0, ch);
  EEPROM.commit();
  delay(100);
}


void RunStream() {
  audioLogger = &Serial;
  for (int i = 0; i < 10; i++) {
    if (ch == i) {
      file = new AudioFileSourceICYStream(URL[i]);
    }
  }
  file->RegisterMetadataCB(MDCallback, (void*)"ICY");
  buff = new AudioFileSourceBuffer(file, 1024);
  buff->RegisterStatusCB(StatusCallback, (void*)"buffer");
  out = new AudioOutputI2SNoDAC();
  mp3 = new AudioGeneratorMP3();
  mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
  mp3->begin(buff, out);
}


void StopStream()
{
  if (mp3) {
    mp3->stop();
    delete mp3;
    mp3 = NULL;
  }
  if (out) {
    out->stop();
    delete out;
    out = NULL;
  }
  if (buff) {
    buff->close();
    delete buff;
    buff = NULL;
  }
  if (file) {
    file->close();
    delete file;
    file = NULL;
  }
}


void StreamHendler() {
  static int lastms = 0;


  if (mp3->isRunning()) {
    autorun = 1;
    if (millis() - lastms > 1000) {
      lastms = millis();
      Serial.printf("Running for %d ms...\n", lastms);
      Serial.flush();
      digitalWrite(D3, HIGH);
    }
    if (!mp3->loop()) mp3->stop();
  }
  else {
    digitalWrite(D3, LOW);
    /// if wifi disconnected ///
    if (WiFi.status() != WL_CONNECTED) {
      StopStream();
      ConnectToWifi();
      RunStream();
    }
    /// run if MP3 done by interrupts ///
    if (autorun == 1 ) {
      StopStream();
      RunStream();
      autorun = 0;
    }
  }
}


// on change
void rotate(ESPRotary& r) {
  //Serial.println(r.getPosition());
  ch = r.getPosition();
  mp3->stop();
  draw();
  autorun = 0;
}
// single click
void ShorPress(Button2& b) {
  EepromWrite();
  StopStream();
  RunStream();
}
void LongPress(Button2& btn) { 
   mp3->stop();
   StopStream();
   ESP.reset();
}


void draw(void) {
  display.clearDisplay();
  display.setTextSize(2);             // Normal 1:1 pixel scale
  display.setTextColor(WHITE);        // Draw white text
  display.setCursor(0, 0);            // Start at top-left corner
  for (int i = 0; i < 10; i++) {
    if (ch == i) {
      display.println(ChName[i]);
    }
  }
  display.display();
}


void triangle(void) {
  display.clearDisplay();
  for (int16_t i = 0; i < max(display.width(), display.height()) / 4; i += 5) {
    display.drawTriangle(
      display.width() / 2  , display.height() / 2 - i,
      display.width() / 2 - i, display.height() / 2 + i,
      display.width() / 2 + i, display.height() / 2 + i, WHITE);
    display.display();
    delay(100);
  }
}


void loop() {
  r.loop();
  b.loop();
  StreamHendler();
}

Setup Your WiFi

before flushing the code into the Esp8266 set your wifi hotspot details in the code:

Change "YOUR WIFI NAME" to your wifi hotspot name:

#define STASSID "YOUR WIFI NAME"

Change "PASSWORD" to your wifi hotspot password:

#define STAPSK  "PASSWORD"

Update Stations Url's and Names

you can update the stations urls and name by replacing them.

when you replace url keep in mind to replace also the name from the name list that represent that station.

so for example when you replace "http://www.golden-apple.com:680/;"

in a different url you should also change the "Golden Apple" in the station url name list to your new station name.


//URL'S

const char *URL[] = {"http://jazz.streamr.ru/jazz-64.mp3",

           "http://a1rj.streams.com.br:7801/sm",

           "http://www.golden-apple.com:680/;",

           "http://stm14.mfmedios.info:8048/;",

           "http://stream002.radio.hu:80/mr2.mp3",

           "http://live02.rfi.fr/rfimonde-64.mp3",

           "http://live.wbcb1490.com:88/broadwavehigh.mp3",

           "http://14543.live.streamtheworld.com:3690/XHFO_FM_SC",

           "http://14523.live.streamtheworld.com:3690/KNBAFM_SC",

           "http://sa.mp3.icecast.magma.edge-access.net:7200/sc_rad31",

           "http://stream.lt8.com.ar:8080/delsiglo995.mp3"

          };

//URL'S Names

const char *ChName[] = {"Jazz RU", //0

            "ALJ",   //1

            "Golden Apple", //2

            "Mfmedios",   //3

            "Petőfi Rádió", //4

            "RFI - Monde", //5

            "WBCB UK",   //6

            "XHFO FM",    //7

            "KNBA FM",    //8

            "Radio Nacional" //9

            };

Listening to Stations With Esp8266

Esp8266 lo fi internet radio

After setting up the WiFi and flushing the code in to the esp8266 the screen will turn on and the radio will connect to a station.

All the stations that I used in the url list is 64kbps rate for light stream buffer.

The esp8266 have very small buffer space so for my experience its not able to stream flowently above 96kbps and its also depends on your Internet bandwidth and traffic And the server that's transmit the stream some of them weeker and some of them stronger.

The internal DAC of the esp8266 is a 10 bit DAC so don't expect great sound from this DAC.

But In other hand that Lo Fi 10 bit sound is charming when listening to oldies And it's have is own magical sound.


UPDTAE: i build the code again and fixed some buffer problems when mp3 is stoped or interrupts.

also setting the wifi to WiFi.setPhyMode(WIFI_PHY_MODE_11G) makes the connection and streaming very stable.


ENJOY