Pages: [1] 2 3   Go Down
Author Topic: Ethernet MP3 Player  (Read 9086 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

I would like to build a small box being able to play MP3 from the network (Ethernet cable for the moment, Wifi maybe later) and output the sound to my amplifier. Being brand new to Arduino and electronics, I want to check my understanding of it before ordering the parts so thanks for your feedback.

From what I understood, I of course need an Ethernet shield and an MP3 shield. Using Ethernet.Client I would read the bytes of the MP3 stream and feed them to the MP3 shield most probably using a serial interface and Serial.write.

I have two possible choices in mind:

1) Aduino Uno + Ethernet shield + Sparkfun VS1053D MP3
2) Rogue Robotics rMP3 + Ethernet shield. Not sure at all about this but I kind of understood that the rMP3 had its own microcontroller...

So please can you please give me your feedback about this:
- Am I right with my bytes read from Ethernet and fed to the MP3 shield using serial?
- Of the two choices, which one is good one?

If it is feasible I would prefer to use the 1st design so I can also do other stuff with Arduino Uno in the learning stage...

Thanks!
mynab
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There is someone else working on this with the rMP3 module.
Although it has its own processor, that is for controlling the module and you are much better off using an arduino as well to control the ethernet shield and the rMP3

As someone is already working on it, I'd be tempted towards the rMP3 if I were you. It also has a load of features that the MP3 board from sparkfun does not have.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks! Do you know who is working on that?

What about PIN conflicts? I see that rMP3 both use D11 to D13. Are these shareable?
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks! Do you know who is working on that?

What about PIN conflicts? I see that rMP3 both use D11 to D13. Are these shareable?
Have a read from the last post on this thread onwards.

The rMP3 only requires pins +5, GND, Digital 6 and Digital 7. The other pins are unused in normal operation.

You can however connect pins 6 and 7 to whatever pins you like on the board as they are simply used for serial communication (NewSoftSerial) and you can define them as any pins on the arduino.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks. I contacted LB01 but from what I understand, music MP3 (128kps+) will not be possible with that kind of design because of the serial communication between Ethernet and MP3 right? So I need Rogue Robotics to put an Ethernet plug on their card  smiley
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yeah, I thought that might be the bottleneck in the system.

I'll nudge bhagman this way later and see if he has any ideas.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi. Did you have a chance to talk to bhagman? I sent him a message but got no answer   smiley-sad
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oh sorry I forgot...

I'll send him an email. smiley
I know he's a busy man though smiley-wink
« Last Edit: February 18, 2011, 08:27:53 am by mowcius » Logged

Toronto, ON
Offline Offline
Full Member
***
Karma: 10
Posts: 233
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry guys - got a few things on my plate.

I've been wanting to write this streaming MP3 player, but I just don't have the time.

I'm pretty sure it can be done - with some limitations.  The MP3 bitrate will have to be pretty low (96 kbps or lower), but in most cases, that can still sound pretty good - especially mono (which you can do at 64 kbps and get pretty decent quality).

You will need to communicate with the rMP3 using a hardware serial port @ 115200 or better.  Some boards, like our LEDHead for example, have 2 or more hardware serial ports.  You can use the first serial port, but you won't have any feedback to the serial monitor, if that's your intention (and you may have to disconnect the USB cable, if your board has an on-board USB controller).

Furthermore, it will require buffering data to files, and switching on the fly as data is received.  I can get into more detail about this, if someone wants to take on the task of building the streaming MP3 player.


b
Logged


0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all,

I hope mynab is still working on that project? I did something similar last December. I have been stopped for a while but I managed to restart recently.

I use an ethershield with the ship enc28j60 (the cheap one), and that needs a special library. You may not be able to re-use my code if you don't use that kind of ethernet shield.

So far, I have been able to capture a stream on my serial port. My arduino connects to a given site, requests the stream, find the beggining of the data and sends the rest to the serial port so that if you capture it, you get an mp3 file that can be played normally.

Here is the code, modified from the ethershield Pachube example (to connect to a french radio):

Code:
/*
 * Read a pachube data streem and report results as csv
 */
# define WWW_client

#include <EtherShield.h>

static uint8_t mymac[6] = {0x54,0x55,0x99,0x10,0x00,0x25};
static uint8_t myip[4] = {192,168,1,1};
// Default gateway. The ip address of your DSL router. It can be set to the same as
// websrvip the case where there is no default GW to access the
// web server (=web server is on the same lan as this host)
static uint8_t gwip[4] = {192,168,1,254};

//============================================================================================================
// Pachube declarations
//============================================================================================================
#define PORT 80                   // HTTP

// the etherShield library does not really support sending additional info in a get request
// here we fudge it in the host field to add the API key
// Http header is
// Host: GET http://mp3.live.tv-radio.com/franceinter/all/franceinterhautdebit.mp3
// User-Agent: Arduino/1.0
// Accept: text/html
#define HOSTNAME "mp3.live.tv-radio.com\r\nKeep-Alive: 300"      // API key
static uint8_t websrvip[4] = { 0,0,0,0 }; // Get pachube ip by DNS call
#define WEBSERVER_VHOST "mp3.live.tv-radio.com"
#define HTTPPATH "/franceinter/all/franceinterhautdebit.mp3"      // Set your own feed ID here

//#define HOSTNAME "www.pachube.com\r\nX-PachubeApiKey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"      // API key
//static uint8_t websrvip[4] = { 0,0,0,0 }; // Get pachube ip by DNS call
//#define WEBSERVER_VHOST "www.pachube.com"
//#define HTTPPATH "/api/0000.csv"      // Set your own feed ID here

static uint8_t resend=0;
static int8_t dns_state=0;

EtherShield es=EtherShield();

#define BUFFER_SIZE 900
static uint8_t buf[BUFFER_SIZE+1];

void browserresult_callback(uint8_t statuscode,uint16_t datapos){
//  if (datapos != 0)
//  {
//    // now search for the csv data - it follows the first blank line
//    // I'm sure that there is an easier way to search for a blank line - but I threw this together quickly
//    // and it works for me.
//    uint16_t pos = datapos;
//    while (buf[pos])    // loop until end of buffer (or we break out having found what we wanted)
//    {
//      while (buf[pos]) if (buf[pos++] == '\n') break;   // find the first line feed
//      if (buf[pos] == 0) break; // run out of buffer
//      if (buf[pos++] == '\r') break; // if it is followed by a carriage return then it is a blank line (\r\n\r\n)
//    }
//    if (buf[pos])  // we didn't run out of buffer
//    {
//      pos++;  //skip over the '\n' remaining
//      Serial.println((char*)&buf[pos]);
//    }
//  }
}

void setup(){
  Serial.begin(57600);

  /*initialize enc28j60*/
  es.ES_enc28j60Init(mymac);

  //init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mymac, myip, PORT);

  // init the web client:
  es.ES_client_set_gwip(gwip);  // e.g internal IP of dsl router

}

void loop()
{
  static uint32_t timetosend;
  uint16_t dat_p;
  int sec = 0;
  long lastDnsRequest = 0L;
  int plen = 0;
  int reqsent = 0; // flags the request sending
  int streamon = 0; // flags the streaming

  dns_state=0;

  while(1) {
    // handle ping and wait for a tcp packet - calling this routine powers the sending and receiving of data
    plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);
    dat_p=es.ES_packetloop_icmp_tcp(buf,plen);
    if( plen > 0 ) {
      // We have a packet     
      //Serial.println("Packet received"); // debug
      // Check if IP data
      if (dat_p == 0 && reqsent == 0) {
        if (es.ES_client_waiting_gw() ){
          // No ARP received for gateway
          continue;
        }
        // It has IP data
        if (dns_state==0){
          sec=0;
          dns_state=1;
          lastDnsRequest = millis();
          es.ES_dnslkup_request(buf,(uint8_t*)WEBSERVER_VHOST);
          continue;
        }
        if (dns_state==1 && es.ES_udp_client_check_for_dns_answer( buf, plen ) ){
          dns_state=2;
          es.ES_client_set_wwwip(es.ES_dnslkup_getip());
        }
        if (dns_state!=2){
          // retry every minute if dns-lookup failed:
          if (millis() > (lastDnsRequest + 60000L) ){
            dns_state=0;
            lastDnsRequest = millis();
          }
          // don't try to use web client before
          // we have a result of dns-lookup
          Serial.println("DS lookup failed");
          continue;
        }
      }
      else
      {
        if (dns_state==1 && es.ES_udp_client_check_for_dns_answer( buf, plen ) ){
          dns_state=2;
          es.ES_client_set_wwwip(es.ES_dnslkup_getip());
        }
      }
      if (dat_p == 0 && reqsent == 1)  // case where data was received
      {
       
        // case where the request was sent previously and we keep receiving data
        if ( dns_state == 2 && reqsent == 1)
        {
         
          if (streamon==0) // here the stream has not started yet
          //The stream data starts after a double linefeed (0x0a 0x0d 0x0a 0x0d), so we need to find that first
          {
            uint16_t pos = 54;  // we start at 54 bytes, that falls just after the minimum TCP header
            while (pos<=plen)    // loop until end of buffer (or we break out having found what we wanted)
            {
              while (buf[pos]) if (buf[pos++] == '\n') break;   // find the first line feed
              if (pos>plen) break; // run out of buffer
              if (buf[pos++] == '\r') // if it is followed by a carriage return then it is a blank line (\r\n\r\n)
              {
                pos++;  //skip over the '\n' remaining
                if (pos>=plen) continue; // if there are no data, wait for the next packet
                streamon = 1; // if the data is there, yeepee ! start the streaming officially
                while (pos<=plen) // write the remaining data to the buffer
                {
                  Serial.print(buf[pos]); // could use pos++, need to see that later
                  pos++;
                }
                continue;
              }
            }
          }
          else // if we are here, the streaming has already started and we just copy the data
          {
            for (int ii=dat_p+54;ii<plen;ii++)
            {
              Serial.print(buf[ii]);
            }
          }
         
          // need acknowledgement ?
          //es.ES_enc28j60PacketSend(plen,buf);
        }
       
      }
    }
    // If we have IP address for server and its time then request data

    if( dns_state == 2 && reqsent == 0 && millis() - timetosend > 31000)  // every 31 seconds
    {
      timetosend = millis();
      // note the use of PSTR - this puts the string into code space and is compulsory in this call
      // second parameter is a variable string to append to HTTPPATH, this string is NOT a PSTR
      es.ES_client_browse_url(PSTR(HTTPPATH), NULL, PSTR(HOSTNAME), &browserresult_callback);
      reqsent = 1; // data sent, so flag up
    }   
  }
}

Now, I have a problem with the rMP3: I cannot connect it. I remember having that problem before, but I don't remember how I solved it. Basically, when I load a basic rMP3 script, the program stops when it tries to use the rmp3 object. For instance, it stops when calling rmp3.getmoduletype().
I tried to change the baudrate, but it does not make a difference. I don't understand where the problem is...
Logged

Toronto, ON
Offline Offline
Full Member
***
Karma: 10
Posts: 233
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@LB01: Which Arduino board are you using?  Can you post the code you used to connect to the rMP3?
Logged


0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, the issue resolved on its own. I am not sure of what I did but now it works. I was probably confused with the baudrates. For info, I am using the arduino demilanove, with atmega 328.

Anyway, I tidied up the code and I regrouped all the writing operations in one callback (fillsdbuffer). If you just leave the serial.print operation in this function, and you remove all the operations on the rMP3, this code sends the stream to the serial port, ready to be read.

The problem I have now is that, when I added the rMP3 code, I could not open the file /buf1 where I plan to write the stream (line 108). Is there anything obviously wrong?

Here is the code:

Code:
/*
 * Stores a stream on the SD card, in buffer 1 (/stream/buf1)
 */
# define WWW_client

#include <EtherShield.h>
#include <NewSoftSerial.h>
#include <RogueMP3.h>
#include <RogueSD.h>

static uint8_t mymac[6] = {0x54,0x55,0x99,0x10,0x00,0x25};
static uint8_t myip[4] = {192,168,1,1};
// Default gateway. The ip address of your DSL router. It can be set to the same as
// websrvip the case where there is no default GW to access the
// web server (=web server is on the same lan as this host)
static uint8_t gwip[4] = {192,168,1,254};

//=========================== =================================================================================
// Website declarations
//============================================================================================================
#define PORT 80                   // HTTP

// the etherShield library does not really support sending additional info in a get request
// here we fudge it in the host field to add the API key
// Http header is
// Keep-Alive: 330
// Host: mp3.live.tv-radio.com
// User-Agent: Arduino/1.0
// Accept: text/html
#define HOSTNAME "mp3.live.tv-radio.com\r\nKeep-Alive: 300"      // API key
static uint8_t websrvip[4] = { 0,0,0,0 }; // Get pachube ip by DNS call
#define WEBSERVER_VHOST "mp3.live.tv-radio.com"
#define HTTPPATH "/franceinter/all/franceinterhautdebit.mp3"      // Set your own feed ID here

static uint8_t resend=0;
static int8_t dns_state=0;

EtherShield es=EtherShield();
#define BUFFER_SIZE 900
static uint8_t buf[BUFFER_SIZE+1];

// setting the rMP3
NewSoftSerial rmp3_serial(6, 7);
RogueMP3 rmp3(rmp3_serial);
RogueSD filecommands(rmp3_serial);
char path[96];  // path of the buffers
uint8_t volume = 40; // sound volume
#define SD_BUFFER_SIZE 128000 // size of each buffer on the SD card


void browserresult_callback(uint8_t statuscode,uint16_t datapos){
// left here in case it is needed later
}

// fillsdbuffer transfers the data from the ethernet buffer to the SD cards
// fillsdbuffer( ethernet buffer, starting position, ending position,
//               SD buffer handle, current SD position)
void fillsdbuffer(uint8_t *buf, int pos, int plen, uint8_t filehandle, uint16_t sdpos){
  
  char s;
  while (pos<=plen) // write the remaining data to the buffer
  {
    s = char(buf[pos]);
    filecommands.write(filehandle,1,&s);
    Serial.print(buf[pos]); // could use pos++, need to see that later
    pos++;
    sdpos++;
  }
                    
}

void setup(){
  Serial.begin(230400);

  /*initialize enc28j60*/
  es.ES_enc28j60Init(mymac);

  //init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mymac, myip, PORT);

  // init the web client:
  es.ES_client_set_gwip(gwip);  // e.g internal IP of dsl router
  
  rmp3_serial.begin(9600); // parameter D3 in the RMP3.CFG config file (root dir)
  
  rmp3.sync();
  rmp3.stop();
  rmp3.setvolume(volume);
  filecommands.sync();
  
}

void loop()
{
  static uint32_t timetosend;
  int pos;
  int dat_p;
  int sec = 0;
  long lastDnsRequest = 0L;
  int plen = 0;
  int reqsent = 0; // flags the request sending
  int streamon = 0; // flags the streaming
  int8_t filehandle; // handle to the active SD buffer
  uint16_t sdpos = 0; // position of last data in the SD  buffer
  dns_state=0;

  // open the 1st SD buffer, directory /stream/
  filehandle = filecommands.open("/buf1",open_mode(OPEN_WRITE));
  if (filehandle<1) // in case of an error during the opening
  {
    Serial.println("Error: cannot open the file.");
    while (1) {}; // stops here, cannot continue anyway.
  }

  while(1) {
    // handle ping and wait for a tcp packet - calling this routine powers the sending and receiving of data
    plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);
    dat_p=es.ES_packetloop_icmp_tcp(buf,plen);
    if( plen > 0 ) {
      // We have a packet
      // Check if IP data
      if (dat_p == 0 && reqsent == 0) {
        if (es.ES_client_waiting_gw() ){
          // No ARP received for gateway
          continue;
        }
        // It has IP data
        if (dns_state==0){
          sec=0;
          dns_state=1;
          lastDnsRequest = millis();
          es.ES_dnslkup_request(buf,(uint8_t*)WEBSERVER_VHOST);
          continue;
        }
        if (dns_state==1 && es.ES_udp_client_check_for_dns_answer( buf, plen ) ){
          dns_state=2;
          es.ES_client_set_wwwip(es.ES_dnslkup_getip());
        }
        if (dns_state!=2){
          // retry every minute if dns-lookup failed:
          if (millis() > (lastDnsRequest + 60000L) ){
            dns_state=0;
            lastDnsRequest = millis();
          }
          // don't try to use web client before
          // we have a result of dns-lookup
          Serial.println("DS lookup failed");
          continue;
        }
      }
      else
      {
        if (dns_state==1 && es.ES_udp_client_check_for_dns_answer( buf, plen ) ){
          dns_state=2;
          es.ES_client_set_wwwip(es.ES_dnslkup_getip());
        }
      }
      if (dat_p == 0 && reqsent == 1)  // case where data was received
      {
        
        // case where the request was sent previously and we keep receiving data
        if ( dns_state == 2 && reqsent == 1)
        {
          
          if (streamon==0) // here the stream has not started yet
          //The stream data starts after a double linefeed (0x0a 0x0d 0x0a 0x0d), so we need to find that first
          {
            pos = 54;  // we start at 54 bytes, that falls just after the minimum TCP header
            while (pos<=plen)    // loop until end of buffer (or we break out having found what we wanted)
            {
              while (buf[pos]) if (buf[pos++] == '\n') break;   // find the first line feed
              if (pos>plen) break; // run out of buffer
              if (buf[pos++] == '\r') // if it is followed by a carriage return then it is a blank line (\r\n\r\n)
              {
                pos++;  //skip over the '\n' remaining
                if (pos>=plen) continue; // if there are no data, wait for the next packet
                streamon = 1; // if the data is there, yeepee ! start the streaming officially
                fillsdbuffer(buf,pos,plen,filehandle,sdpos);
                continue;
              }
            }
          }
          else // if we are here, the streaming has already started and we just copy the data
          {
            fillsdbuffer(buf,dat_p+54,plen,filehandle,sdpos);
//            for (int ii=dat_p+54;ii<plen;ii++)
//            {
//              Serial.print(buf[ii]);
//            }
          }
          
          // need acknowledgement ?
          //es.ES_enc28j60PacketSend(plen,buf);
        }
        
      }
    }
    // If we have IP address for server and its time then request data

    if( dns_state == 2 && reqsent == 0 && millis() - timetosend > 31000)  // every 31 seconds
    {
      timetosend = millis();
      // note the use of PSTR - this puts the string into code space and is compulsory in this call
      // second parameter is a variable string to append to HTTPPATH, this string is NOT a PSTR
      es.ES_client_browse_url(PSTR(HTTPPATH), NULL, PSTR(HOSTNAME), &browserresult_callback);
      reqsent = 1; // data sent, so flag up
    }
    
  }
}



Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, I am nearly there, but I have a problem with the rMP3 module. It has problems to sync when the baudrate is different from 9600.
I have tried to include the baudrate change in the code because I first thought that the problem was coming from the SD card. So I start th communication at 9600, change to 57600, ask a confirmation by checking the baudrate parameters from the rMP3 (it gives a '3', indicating that it has changed correctly and it is still woking), and then I use the sync function and the program stops. What is the problem?
I upgraded the rMP3 firmware last December to reduce the latency between two files. Could there be a problem in the new firmware?

Here is the section of code I use for changing the baudrate:
Code:
Serial.println("starting rmp3");
  rmp3_serial.begin(9600); // default baudrate

  char setbaud = 0x44; //D
  uint8_t baudratecode = 3; //230400: parameter D7 in the config file
  //rmp3.changesetting(setbaud, baudratecode);
  Serial.println(rmp3.getsetting(setbaud));
  rmp3.changesetting(setbaud, baudratecode);
  delay(1000);
  rmp3_serial.begin(57600);
  Serial.println(rmp3.getsetting(setbaud)); // check that everything is fine
 
//  // back to 9600, for debugging purposes (so you don't have to power off the arduino every time)
//  baudratecode = 0;
//  rmp3.changesetting(setbaud, baudratecode);
//  delay(1000);
//  rmp3_serial.begin(9600);
//  Serial.println(rmp3.getsetting(setbaud));
 
  Serial.println("Sync");
  rmp3.sync();
  Serial.println("Stop");
  rmp3.stop();
  Serial.println("Set volume");
  rmp3.setvolume(volume);
  Serial.println("Sync filesystem");
  filecommands.sync();
 
  Serial.println("ready");
Logged

North Yorkshire, UK
Offline Offline
Faraday Member
**
Karma: 104
Posts: 5531
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Have you tried setting the baud rate with the boot config file on the SD card instead of in code?

http://www.roguerobotics.com/wikidocs/rmp3/documentation/boot_config
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, I did that first and I could not sync either when I used D5 and D7 as parameters.
Using D0 was not a problem, but 9600 is too low for streaming.
Logged

Pages: [1] 2 3   Go Up
Jump to: