0

Help: AB004 Music from the tubes into the tubes
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Help: AB004 Music from the tubes into the tubes

by ac2ev on Sat May 19, 2018 4:01 pm

I decided to take my Adabox004 feathr music to the next level and if the music is coming from the tubes then it should be played through the tubes. Here's the run down. I've got an Eico HFT-90 which is a vacuum tube FM radio with a nice magic eye tuner (in the shape of a ! that is mounted to the tuning position indicator). This is a pretty compact unit measuring 9w x 12l x 3h inches. The idea is to be able to tune the radio across the FM dial an tune in up to 18 Soma FM stations. I'm using a rotary encoder to provide the tuning since the tuning mechanism make 1.25 turn for full scale. However I may have to go less as I'm not getting the result I desired but that's where you who are reading and better at coding than I am. The code is loosely based on the adabox004 and Haunted music player by John Park

Here's some pictures:
https://photos.app.goo.gl/MiUOaNv0hO6rp6xQ2

Hardware Overview:

Rotary Encoder - 0 to 36 counts for full scale tuning
It feeds NewStation() which translates the encoder to 1 of 18 radio stations. The case structure is set up to 1 station per 2 encoder positions.

Software Overview:
Setup:
[*] Boring gets everything ready
[*] Reads the ini file for 3 variables last saved: encoder postion, channel, period
[*] Most important re-initialize the encoder with the ini file value ( knob.write(pos*4) ) otherwise we always start at 0 and that's not good, we want to load the last 'tuned' station

ignore the channelist variables as they're not currently being used, there's some other detractors as well, it's a work in progress.

Loop:
First time
we haven't starting playing music (stationOn == false) so starting playing some music
we hopefully haven't turned the dial yet so the encoder shouldn't have changed yet so isSaved == true

Check for music if no new music then let's do some magic, and that's where there's some issues.

What works:
ini file read/write
rotary encoder read/write
changing stations usually that's where I need help.



Code: Select all | TOGGLE FULL SIZE
 
//Internet Radio player with tuning control
//by Don Kiser
//based on Haunted Radio by John Park and fether_player by Limor Fried
//
//Uses a rotary encoder attached to radio tuner dial
//Plays static file from Music Maker FeatherWing and radio play files from internet stream
//connected to Feather microcontroller
/*/////////// Hardware ////////////////////////////////

 Feather (of any variety, in this case ESP8266 Huzzah)
 Music Maker MP3 FeatherWing
 Rotary Encoder connected to digital input pin 4,5

Wiring is left (pin 1) to digital input 4, middle (2) to GND, right (3) to digital input 5
 |||||||||
 |||||||||
 |||||||||
 |_______|
 __|___|__
 | |   | |
 |_______|
  /  |  \
 |   |   |
 |   |   |
 |   |   |
 |   |   |
Out GND Out
 |   |   |
 
/////////////////////////////////////////////////////*/

///////////////////////////CALIBRATION//////////////////
//On First run you will need to calibrate the unit with the tuner in
//the left most or lowest frequency. This will put the first station at this spot
//and write it to the SD card. When you change stations it will be saved to the SD
//card and then read the next time you turn on the unit.


// include SPI, MP3 and SD libraries
#include <SPI.h>
#include <SD.h>
#include <Adafruit_VS1053.h>
#include <ESP8266WiFi.h>
#include <Encoder.h>
#include <IniFile.h> //by Steve Marple

/********* Encoder Setup ***************/
#define PIN_ENCODER_SWITCH 11
Encoder knob(4, 5);
uint8_t activeRow = 0;
long pos = -999;
int prevButtonState = HIGH;
bool needsRefresh = true;
bool advanced = false;
unsigned long startTime;
File cfgFile;
long startMillis;
long currentMillis;
long period = 30000;
/*
#0 -LED
#2 -LED
#4 -Encoder 1
#5 -Encoder 2
#12 -SPI SCK
#13 -SPI MOSI
#14 -MISO
#15 -boot mode
#16 -WAKE
#ADC -volume
*/

char* ssid     = "Occidentalis";
const char* password = "secret";

int m = 0; //variable to increment through menu list

//Internet Radio streams
const char *host = "ice1.somafm.com";
const char *path01 = "/groovesalad-128-mp3";
const char *path02 = "/dronezone-128-mp3";
const char *path03 ="/poptron-128-mp3";
const char *path04 ="/deepspaceone-128-mp3";
const char *path05 ="/spacestation-128-mp3";
const char *path06 ="/lush-128-mp3";
const char *path07 ="/digitalis-128-mp3";
const char *path08 ="/missioncontrol-128-mp3";
const char *path09 ="/illstreet-128-mp3";
const char *path10 ="/indiepop-128-mp3";
const char *path11 ="/seventies-128-mp3";
const char *path12 ="/suburbsofgoa-128-mp3";
const char *path13 ="/u80s-128-mp3";
const char *path14 ="beatblender-128-mp3";
const char *path15 ="/thetrip-128-mp3";
const char *path16 ="/secretagent-128-mp3";
const char *path17 = "/defcon-128-mp3";
const char *path18 = "/fluid-128-mp3";

int httpPort = 80;

// These are the pins used
#define VS1053_RESET   -1     // VS1053 reset pin (not used!)
#define VS1053_CS      16     // VS1053 chip select pin (output)
#define VS1053_DCS     15     // VS1053 Data/command select pin (output)
#define VS1053_DREQ     0     // VS1053 Data request, ideally an Interrupt pin
#define VOLUME_KNOB    A0
#define CARDCS          2     // Card chip select pin
 
boolean isPaused = false;
boolean isSaved = true;
boolean isFirst = true;
boolean staticOn = false;  // variable to store state of static being played
boolean stationOn = false; // variable to store state of station being selected

uint8_t volume = 10;
int lastvol = 30;
char foundname[20];


Adafruit_VS1053_FilePlayer musicPlayer =
  Adafruit_VS1053_FilePlayer(VS1053_RESET, VS1053_CS, VS1053_DCS, VS1053_DREQ, CARDCS);

// Use WiFiClient class to create HTTP/TCP connection
WiFiClient client;


void printErrorMessage(uint8_t e, bool eol = true)
{
  switch (e) {
  case IniFile::errorNoError:
    Serial.print("no error");
    break;
  case IniFile::errorFileNotFound:
    Serial.print("file not found");
    break;
  case IniFile::errorFileNotOpen:
    Serial.print("file not open");
    break;
  case IniFile::errorBufferTooSmall:
    Serial.print("buffer too small");
    break;
  case IniFile::errorSeekError:
    Serial.print("seek error");
    break;
  case IniFile::errorSectionNotFound:
    Serial.print("section not found");
    break;
  case IniFile::errorKeyNotFound:
    Serial.print("key not found");
    break;
  case IniFile::errorEndOfFile:
    Serial.print("end of file");
    break;
  case IniFile::errorUnknownError:
    Serial.print("unknown error");
    break;
  default:
    Serial.print("unknown error value");
    break;
  }
  if (eol)
    Serial.println();
}

/////////////////////////// Setup /////////////////////////////////////////////

void setup() {

 
  Serial.begin(115200);

  Serial.println("\n\n Adafruit VS1053 Feather WiFi Radio");

  /************************* INITIALIZE MP3 WING */
  if (! musicPlayer.begin()) { // initialise the music player
     Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
     while (1) delay(10);
  }

  Serial.println(F("VS1053 found"));
  musicPlayer.sineTest(0x44, 500);    // Make a tone to indicate VS1053 is working

  if (!SD.begin(CARDCS)) {
    Serial.println(F("SD failed, or not present"));
    while (1);  // don't do anything more
  }
  Serial.println("SD OK!");


//----------------------------------------- ini file
  Serial.println("reading ini file");
  const size_t bufferLen = 80;
  char buffer[bufferLen];

  const char *filename = "/cfg.ini";
 
  IniFile ini(filename);
  if (!ini.open()) {
    Serial.print("Ini file ");
    Serial.print(filename);
    Serial.println(" does not exist");
    // Cannot do anything else
    while (1)
      ;
  }
  Serial.println("Ini file exists");

  // Check the file is valid. This can be used to warn if any lines
  // are longer than the buffer.
  if (!ini.validate(buffer, bufferLen)) {
    Serial.print("ini file ");
    Serial.print(ini.getFilename());
    Serial.print(" not valid: ");
    printErrorMessage(ini.getError());
    // Cannot do anything else
    while (1)
      ;
  }

  if (ini.getValue("station", "enc", buffer, bufferLen)) {
    Serial.print("section 'station' has an entry 'enc' with value ");
    Serial.println(buffer);
    pos = atoi(buffer);
    knob.write(pos*4) ; // Re-initialize the encoder position to saved value
   }
  else {
    Serial.print("Could not read 'enc' from section 'station', error was ");
    printErrorMessage(ini.getError());
    pos = 0;
  }

  if (ini.getValue("station", "ch", buffer, bufferLen)) {
    Serial.print("section 'station' has an entry 'ch' with value ");
    Serial.println(buffer);
    m = atoi(buffer);
    }
  else {
    Serial.print("Could not read 'ch' from section 'station', error was ");
    printErrorMessage(ini.getError());
    m = 0;
  }
 
  if (ini.getValue("station", "period", buffer, bufferLen)) {
    Serial.print("section 'station' has an entry 'period' with value ");
    Serial.println(buffer);
    period = atoi(buffer);
  }
  else {
    Serial.print("Could not read 'period' from section 'station', error was ");
    printErrorMessage(ini.getError());
    period=5000;
  }
//----------------------------------------- ini file


 
  // Set volume for left, right channels. lower numbers == louder volume!
  musicPlayer.setVolume(lastvol, lastvol);
 
  digitalPinToInterrupt(4); //on M0, Encoder library doesn't auto set these as interrupts
  digitalPinToInterrupt(5);
 
  /************************* INITIALIZE WIFI */
  Serial.print("Connecting to SSID "); Serial.println(ssid);
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("WiFi connected"); 
  Serial.println("IP address: ");  Serial.println(WiFi.localIP());
  startMillis = millis();
} // END Setup



// our little buffer of mp3 data
uint8_t mp3buff[32];   // vs1053 likes 32 bytes at a time
int loopcounter = 0;


void writeIni() {
   /* open the file. note that only one file can be open at a time,
    *   so you have to close this one before opening another.
    */
  cfgFile = SD.open("cfg.ini",  O_CREAT | O_TRUNC | O_WRITE);
  // if the file opened okay, write to it:
  if (cfgFile) {
    cfgFile.println("[station]");
    cfgFile.print("enc = ");
    cfgFile.println(pos);
    cfgFile.print("ch = ");
    cfgFile.println(m);
    cfgFile.print("period = ");
    cfgFile.println(period);
    cfgFile.flush();
    cfgFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening cfg.ini");
  }
 
}

void newstation() {
Serial.print("Requesting URL:");
switch (m) {
   
    case 0:
    case 1:
      Serial.println(path01);
       
        client.print(String("GET ") + path01 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
    break;
     
    case 2:
    case 3: 
       Serial.println(path02);
         client.print(String("GET ") + path02 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
     
    case 4:
    case 5: 
       Serial.println(path03);
         client.print(String("GET ") + path03 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
     
    case 6:
    case 7: 
       Serial.println(path04);
         client.print(String("GET ") + path04 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
     
    case 8:
    case 9: 
        Serial.printlnint(path05);
          client.print(String("GET ") + path05 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
     
    case 10:
    case 11: 
     Serial.println(path06);
       client.print(String("GET ") + path06 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
     
     case 12:
     case 13: 
       Serial.println(path07);
         client.print(String("GET ") + path07 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      break;
     
     case 14:
     case 15:
       Serial.println(path08);
         client.print(String("GET ") + path08 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
     
    case 16:
    case 17:
      Serial.println(path09);
        client.print(String("GET ") + path09 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
     
    case 18:
    case 19: 
      Serial.println(path10);
        client.print(String("GET ") + path10 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;

     case 20:
     case 21:
        Serial.println(path11);
          client.print(String("GET ") + path11 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
   
    case 22:
    case 23: 
     Serial.println(path12);
       client.print(String("GET ") + path12 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      break;
   
    case 24:
    case 25:
       Serial.println(path13);
         client.print(String("GET ") + path13 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;     
   
    case 26:
    case 27:
       Serial.println(path14);
         client.print(String("GET ") + path14 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
   
    case 28:
    case 29:
       Serial.println(path15);
         client.print(String("GET ") + path15 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;
   
    case 30:
    case 31:
       Serial.println(path16);
         client.print(String("GET ") + path16 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;

    case 32:
    case 33:
       Serial.println(path17);
         client.print(String("GET ") + path17 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;     
   
    case 34:
    case 35:
    case 36:
       Serial.println(path18);
         client.print(String("GET ") + path18 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
     break;

  }
}

 
/////////////////////////  Loop  //////////////////////////////////////////////
void loop() {

  loopcounter++;

/*
 *  if(digitalRead(VS1053_DREQ) && !musicPlayer.stopped() && !isPaused) {
    musicPlayer.feedBuffer();
  }
 */

 currentMillis = millis();  //get the current "time" (actually the number of milliseconds since the program started)
 //(currentMillis - startMillis >= (period/2)) &&
       
  if (stationOn == false){
     client.stop();
     client.connect(host, httpPort);
     newstation();
     stationOn = true;
  } 
       
  if ( (stationOn = true) && (isSaved == false) ){
      writeIni();
      isSaved = true;
  } 
 
  // wait till mp3 wants more data
  if (musicPlayer.readyForData()) {
   
    //wants more data! check we have something available from the stream
    if (client.available() > 0) {
      // yea! read up to 32 bytes
      uint8_t bytesread = client.read(mp3buff, 32);
      // push to mp3
      musicPlayer.playData(mp3buff, bytesread);
    }
  } else {
    if (loopcounter >= 1000) {
        loopcounter = 0;
        // adjust volume!
        int vol = 0;
        vol = analogRead(VOLUME_KNOB);
        vol /= 10;
        if (abs(vol - lastvol) > 3) {lastvol = vol;musicPlayer.setVolume(lastvol, lastvol);}

      //adjust the tuning
      long newpos = knob.read() / 4;//divide for encoder detents
     
      if(newpos != pos){
        int diff = newpos - pos;//check the different between old and new position
        if(diff>=1){m++;m = (m+36) % 36;}//modulo to roll over the m variable through the list size which is set to max encoder value
        if(diff==-1){m--;m = (m+36) % 36;}//rotating backwards
        //uncomment for debugging or general curiosity
       // Serial.print("Diff = ");
       // Serial.print(diff);
       // Serial.print("  pos= ");
       // Serial.print(pos);
       // Serial.print(", newpos=");
       // Serial.println(newpos);
        Serial.print(" channel ");
        Serial.println(m);
        pos = newpos;
        isSaved = false;
        stationOn = false;
      } //encoder position check
    } //Loopcounter
    } //else
} //loop


After boot here's the serial output, everything looks normal
Adafruit VS1053 Feather WiFi Radio
VS1053 found
SD OK!
reading ini file
Ini file exists
section 'station' has an entry 'enc' with value 2
section 'station' has an entry 'ch' with value 2
section 'station' has an entry 'period' with value 5000
Connecting to SSID Occidentalis
...WiFi connected
IP address:
192.168.200.152
connecting to ice1.somafm.com
Requesting URL: /groovesalad-128-mp3
Saving station info to cfg.ini...[station]
enc = 2
ch = 2
period = 5000
done.


Now let's start turning the knob. Here's a breakdown of what the output is:

4 current encoder value it's supposed to be "channel" m but I'll look into that later
Requesting URL:/dronezone-128-mp3 new station based on encoder value is being used
Saving station info to cfg.ini...[station]
enc = 3
ch = 3
period = 5000
done.
write out the ini file

What I'm expecting to see is the following stations to be played:
path01 = "/groovesalad-128-mp3";
path02 = "/dronezone-128-mp3";
path03 ="/poptron-128-mp3";
path04 ="/deepspaceone-128-mp3";

carriage returns added for clarity

4
Requesting URL:/dronezone-128-mp3

Saving station info to cfg.ini...[station]
enc = 3
ch = 3
period = 5000
done.


5
Saving station info to cfg.ini...[station]
enc = 4
ch = 4
period = 5000
done.


6
Requesting URL:/poptron-128-mp3

Saving station info to cfg.ini...[station]
enc = 5
ch = 5
period = 5000
done.

7
Requesting URL:/deepspaceone-128-mp3

Saving station info to cfg.ini...[station]
enc = 6
ch = 6
period = 5000
done.
Why no station loaded?

8
Saving station info to cfg.ini...[station]
enc = 7
ch = 7
period = 5000
done.
Why no station loaded? Houston we have a problem
9
Saving station info to cfg.ini...[station]
enc = 8
ch = 8
period = 5000
done.

Why no station loaded? Oh geez
10
Saving station info to cfg.ini...[station]
enc = 9
ch = 9
period = 5000
done.


At this point if I keep turning the dial it keeps incrementing but the station doesn't keep changing as evidenced by this next try:

Requesting URL:/lush-128-mp3

Saving station info to cfg.ini...[station]
enc = 10
ch = 10
period = 5000
done.

12
Saving station info to cfg.ini...[station]
enc = 11
ch = 11
period = 5000
done.

13
Saving station info to cfg.ini...[station]
enc = 12
ch = 12
period = 5000
done.

14
Saving station info to cfg.ini...[station]
enc = 13
ch = 13
period = 5000
done.

15
Saving station info to cfg.ini...[station]
enc = 14
ch = 14
period = 5000
done.

16
Saving station info to cfg.ini...[station]
enc = 15
ch = 15
period = 5000
done.


Now I know it's a timing issue but those seem to be the hardest ones for me to figure out. I have this idea that adding more Serial.print() will slow things down and mask the issue or worse create a new one.

I feel it has something to do with the loopcounter variable and that perhaps I should go to constrained if-then statements instead and let the loop run free.

My other comp-out is to instead go to small potentiometer and mount that in place of the encoder. I'll have analog input but I'll lose resolution and span, and it's not as cool.
Besides right now it mostly works and it fits in the case.

What are your thoughts?

For those that are curious, the end game is to rebuild the FM tuner (new caps and resistors) and add the needed tubes to power power the pre-amp and magic eye (DM-70), then feed the audio into the right spot* and hook it up to it's matching amplifier (Eico HF-12) which I already rebuilt and is waiting for input.

*I know some old timers that can help with that

ac2ev
 
Posts: 47
Joined: Mon Sep 16, 2013 5:13 pm

Re: Help: AB004 Music from the tubes into the tubes

by johnpark on Fri Jun 08, 2018 2:16 pm

I can't offer much help because I always get tripped up using rotary encoders for this sort of thing, too, but just wanted to say what a cool project this is! I'll see if some of our more rotary encoder savvy people can have a look.

johnpark
 
Posts: 515
Joined: Wed Mar 25, 2009 2:15 pm

Re: Help: AB004 Music from the tubes into the tubes

by ac2ev on Fri Jun 08, 2018 5:40 pm

I spent some time revising the code and I think I've got it mostly figured out. I added a few ways to ignore things if the conditions aren't right, this should speed things up.

I added

stationOn if (musicPlayer.readyForData()&& stationOn) { No sense in trying if the stream isn't valid.
lastpos - save the lastpos of the encoder

each case in NewStation as an additional IF-THEN check

if ((lastpos != 0) && (lastpos != 1)) {
If we're on the same station there's no reason to close the stream only to reopen it, just exit. This saves valuable time. This seemed to fix the issue.

The if case is basically repeated for all the cases just subsitute the new path. I thought about making this a function call as I think it would save space, however I'm not sure if the added time involved for the processor to go to the function, process, return would actually be slower. Also, I'm terrible when it comes to C and processing string and how to pas char* to char to whatever they are in C.


Code: Select all | TOGGLE FULL SIZE
     
 if ((lastpos != 0) && (lastpos != 1))  {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path01);
        client.print(String("GET ") + path01 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }         
    break;



The concept of
const char *path01
VS
char* path01
VS
const char* path01

eludes me. I tried reading up on it but didn't find anything that made sense for me. I'd love if you guys/girls would write a guide for the non-C trained programmer about how char works and how to pass it to functions.



Code: Select all | TOGGLE FULL SIZE
 
//Internet Radio player with tuning control
//by Don Kiser
//based on Haunted Radio by John Park and fether_player by Limor Fried
//
//Uses a rotary encoder attached to radio tuner dial
//Plays static file from Music Maker FeatherWing and radio play files from internet stream
//connected to Feather microcontroller
/*/////////// Hardware ////////////////////////////////

 Feather (of any variety, in this case ESP8266 Huzzah)
 Music Maker MP3 FeatherWing
 Rotary Encoder connected to digital input pin 4,5

Wiring is left (pin 1) to digital input 4, middle (2) to GND, right (3) to digital input 5
 |||||||||
 |||||||||
 |||||||||
 |_______|
 __|___|__
 | |   | |
 |_______|
  /  |  \
 |   |   |
 |   |   |
 |   |   |
 |   |   |
Out GND Out
 |   |   |
 
/////////////////////////////////////////////////////*/

///////////////////////////CALIBRATION//////////////////
//On First run you will need to calibrate the unit with the tuner in
//the left most or lowest frequency. This will put the first station at this spot
//and write it to the SD card. When you change stations it will be saved to the SD
//card and then read the next time you turn on the unit.


// include SPI, MP3 and SD libraries
#include <SPI.h>
#include <SD.h>
#include <Adafruit_VS1053.h>
#include <ESP8266WiFi.h>
#include <Encoder.h>
#include <IniFile.h> //by Steve Marple

/********* Encoder Setup ***************/
#define PIN_ENCODER_SWITCH 11
Encoder knob(4, 5);
uint8_t activeRow = 0;
long pos = -999;
int prevButtonState = HIGH;
bool needsRefresh = true;
bool advanced = false;
unsigned long startTime;
File cfgFile;
long startMillis;
long currentMillis;
long period = 30000;
/*
#0 -LED
#2 -LED
#4 -Encoder 1
#5 -Encoder 2
#12 -SPI SCK
#13 -SPI MOSI
#14 -MISO
#15 -boot mode
#16 -WAKE
#ADC -volume
*/

char* ssid     = "Occidentalis";
const char* password = "*******";

int m = 0; //variable to increment through stations
int lastpos = -1;

//Internet Radio streams
const char *host = "ice1.somafm.com";
const char *path01 = "/groovesalad-128-mp3";
const char *path02 = "/dronezone-128-mp3";
const char *path03 ="/poptron-128-mp3";
const char *path04 ="/deepspaceone-128-mp3";
const char *path05 ="/spacestation-128-mp3";
const char *path06 ="/lush-128-mp3";
const char *path07 ="/digitalis-128-mp3";
const char *path08 ="/missioncontrol-128-mp3";
const char *path09 ="/illstreet-128-mp3";
const char *path10 ="/indiepop-128-mp3";
const char *path11 ="/seventies-128-mp3";
const char *path12 ="/suburbsofgoa-128-mp3";
const char *path13 ="/u80s-128-mp3";
const char *path14 ="/beatblender-128-mp3";
const char *path15 ="/thetrip-128-mp3";
const char *path16 ="/secretagent-128-mp3";
const char *path17 = "/defcon-128-mp3";
const char *path18 = "/fluid-128-mp3";

int httpPort = 80;

// These are the pins used
#define VS1053_RESET   -1     // VS1053 reset pin (not used!)
#define VS1053_CS      16     // VS1053 chip select pin (output)
#define VS1053_DCS     15     // VS1053 Data/command select pin (output)
#define VS1053_DREQ     0     // VS1053 Data request, ideally an Interrupt pin
#define VOLUME_KNOB    A0
#define CARDCS          2     // Card chip select pin
 
boolean isPaused = false;
boolean isSaved = true;
boolean isFirst = true;
boolean staticOn = false;  // variable to store state of static being played
boolean stationOn = false; // variable to store state of station being selected

uint8_t volume = 10;
int lastvol = 30;
char foundname[20];


Adafruit_VS1053_FilePlayer musicPlayer =
  Adafruit_VS1053_FilePlayer(VS1053_RESET, VS1053_CS, VS1053_DCS, VS1053_DREQ, CARDCS);

// Use WiFiClient class to create HTTP/TCP connection
WiFiClient client;


void printErrorMessage(uint8_t e, bool eol = true)
{
  switch (e) {
  case IniFile::errorNoError:
    Serial.print("no error");
    break;
  case IniFile::errorFileNotFound:
    Serial.print("file not found");
    break;
  case IniFile::errorFileNotOpen:
    Serial.print("file not open");
    break;
  case IniFile::errorBufferTooSmall:
    Serial.print("buffer too small");
    break;
  case IniFile::errorSeekError:
    Serial.print("seek error");
    break;
  case IniFile::errorSectionNotFound:
    Serial.print("section not found");
    break;
  case IniFile::errorKeyNotFound:
    Serial.print("key not found");
    break;
  case IniFile::errorEndOfFile:
    Serial.print("end of file");
    break;
  case IniFile::errorUnknownError:
    Serial.print("unknown error");
    break;
  default:
    Serial.print("unknown error value");
    break;
  }
  if (eol)
    Serial.println();
}

/////////////////////////// Setup /////////////////////////////////////////////

void setup() {

 
  Serial.begin(115200);

  Serial.println("\n\n Adafruit VS1053 Feather WiFi Radio");

  /************************* INITIALIZE MP3 WING */
  if (! musicPlayer.begin()) { // initialise the music player
     Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
     while (1) delay(10);
  }

  Serial.println(F("VS1053 found"));
  musicPlayer.sineTest(0x44, 500);    // Make a tone to indicate VS1053 is working

  if (!SD.begin(CARDCS)) {
    Serial.println(F("SD failed, or not present"));
    while (1);  // don't do anything more
  }
  Serial.println("SD OK!");


//----------------------------------------- ini file
  Serial.println("reading ini file");
  const size_t bufferLen = 80;
  char buffer[bufferLen];

  const char *filename = "/cfg.ini";
 
  IniFile ini(filename);
  if (!ini.open()) {
    Serial.print("Ini file ");
    Serial.print(filename);
    Serial.println(" does not exist");
    // Cannot do anything else
    while (1)
      ;
  }
  Serial.println("Ini file exists");

  // Check the file is valid. This can be used to warn if any lines
  // are longer than the buffer.
  if (!ini.validate(buffer, bufferLen)) {
    Serial.print("ini file ");
    Serial.print(ini.getFilename());
    Serial.print(" not valid: ");
    printErrorMessage(ini.getError());
    // Cannot do anything else
    while (1)
      ;
  }

  if (ini.getValue("station", "enc", buffer, bufferLen)) {
    Serial.print("section 'station' has an entry 'enc' with value ");
    Serial.println(buffer);
    pos = atoi(buffer);
    knob.write(pos*4) ; // Re-initialize the encoder position to saved value
   }
  else {
    Serial.print("Could not read 'enc' from section 'station', error was ");
    printErrorMessage(ini.getError());
    pos = 0;
  }

  if (ini.getValue("station", "ch", buffer, bufferLen)) {
    Serial.print("section 'station' has an entry 'ch' with value ");
    Serial.println(buffer);
    m = atoi(buffer);
    }
  else {
    Serial.print("Could not read 'ch' from section 'station', error was ");
    printErrorMessage(ini.getError());
    m = 0;
  }
 
  if (ini.getValue("station", "period", buffer, bufferLen)) {
    Serial.print("section 'station' has an entry 'period' with value ");
    Serial.println(buffer);
    period = atoi(buffer);
  }
  else {
    Serial.print("Could not read 'period' from section 'station', error was ");
    printErrorMessage(ini.getError());
    period=5000;
  }
//----------------------------------------- ini file


 
  // Set volume for left, right channels. lower numbers == louder volume!
  musicPlayer.setVolume(lastvol, lastvol);
 
  digitalPinToInterrupt(4); //on M0, Encoder library doesn't auto set these as interrupts
  digitalPinToInterrupt(5);
 
  /************************* INITIALIZE WIFI */
  Serial.print("Connecting to SSID "); Serial.println(ssid);
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("WiFi connected"); 
  Serial.println("IP address: ");  Serial.println(WiFi.localIP());
  startMillis = millis();
} // END Setup



// our little buffer of mp3 data
uint8_t mp3buff[32];   // vs1053 likes 32 bytes at a time
int loopcounter = 0;


void writeIni() {
   /* open the file. note that only one file can be open at a time,
    *   so you have to close this one before opening another.
    */
  cfgFile = SD.open("cfg.ini",  O_CREAT | O_TRUNC | O_WRITE);
  // if the file opened okay, write to it:
  if (cfgFile) {
    cfgFile.println("[station]");
    cfgFile.print("enc = ");
    cfgFile.println(pos);
    cfgFile.print("ch = ");
    cfgFile.println(m);
    cfgFile.print("period = ");
    cfgFile.println(period);
    cfgFile.flush();
    cfgFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening cfg.ini");
  }
 
}

void newstation() {
switch (pos) {
    case 0:
    case 1:
      if ((lastpos != 0) && (lastpos != 1))  {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path01);
        client.print(String("GET ") + path01 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }         
    break;
     
    case 2:
    case 3: 
      if ((lastpos != 2) && (lastpos != 3)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path02);
        client.print(String("GET ") + path02 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      } 
     break;
     
    case 4:
    case 5: 
      if ((lastpos != 4) && (lastpos != 5)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path03);
        client.print(String("GET ") + path03 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      } 
     break;
     
    case 6:
    case 7: 
      if ((lastpos != 6) && (lastpos != 7)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path04);
        client.print(String("GET ") + path04 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      } 
     break;
     
    case 8:
    case 9: 
      if ((lastpos != 8) && (lastpos != 9)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path05);
        client.print(String("GET ") + path05 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      } 
     break;
     
    case 10:
    case 11: 
      if ((lastpos != 10) && (lastpos != 11)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path06);
        client.print(String("GET ") + path06 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;
     
     case 12:
     case 13: 
      if ((lastpos != 12) && (lastpos != 13)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path07);
        client.print(String("GET ") + path07 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
      break;
     
     case 14:
     case 15:
      if ((lastpos != 14) && (lastpos != 15)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path08);
        client.print(String("GET ") + path08 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;
     
    case 16:
    case 17:
       if ((lastpos != 16) && (lastpos != 17)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path09);
        client.print(String("GET ") + path09 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;
     
    case 18:
    case 19: 
      if ((lastpos != 18) && (lastpos != 19)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path10);
        client.print(String("GET ") + path10 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;

    case 20:
    case 21:
      if ((lastpos != 20) && (lastpos != 21)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path11);
        client.print(String("GET ") + path11 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;
   
    case 22:
    case 23: 
      if ((lastpos != 22) && (lastpos != 23)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path12);
        client.print(String("GET ") + path12 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
      break;
   
    case 24:
    case 25:
      if ((lastpos != 24) && (lastpos != 25)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path13);
        client.print(String("GET ") + path13 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;     
   
    case 26:
    case 27:
      if ((lastpos != 26) && (lastpos != 27)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path14);
        client.print(String("GET ") + path14 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;
   
    case 28:
    case 29:
      if ((lastpos != 28) && (lastpos != 29)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path15);
        client.print(String("GET ") + path15 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;
   
    case 30:
    case 31:
      if ((lastpos != 30) && (lastpos != 31)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path16);
        client.print(String("GET ") + path16 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;

    case 32:
    case 33:
      if ((lastpos != 32) && (lastpos != 33)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path17);
        client.print(String("GET ") + path17 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;     
   
    case 34:
    case 35:
    case 36:
      if ((lastpos != 34) && (lastpos != 35) && (lastpos != 36)) {
        client.stop();
        client.connect(host, httpPort);
        Serial.println(path18);
        client.print(String("GET ") + path18 + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
      }
     break;

  }
}

long newpos;
int vol;
/////////////////////////  Loop  //////////////////////////////////////////////
void loop() {

  loopcounter++;

/*
 *  if(digitalRead(VS1053_DREQ) && !musicPlayer.stopped() && !isPaused) {
    musicPlayer.feedBuffer();
  }
 */

 currentMillis = millis();  //get the current "time" (actually the number of milliseconds since the program started)
 //(currentMillis - startMillis >= (period/2)) &&
       
  if (stationOn == false){

     newstation();
     stationOn = true;
  } 
       
  if ( (stationOn = true) && (isSaved == false) ){
      writeIni();
      isSaved = true;
  } 
 
  // wait till mp3 wants more data
  if (musicPlayer.readyForData()&& stationOn) {
   
    //wants more data! check we have something available from the stream
    if (client.available() > 0) {
      // yea! read up to 32 bytes
      uint8_t bytesread = client.read(mp3buff, 32);
      // push to mp3
      musicPlayer.playData(mp3buff, bytesread);
    }
  } else {
    if (loopcounter >= 1000) {
        loopcounter = 0;
        // adjust volume!
        vol = 0;
        vol = analogRead(VOLUME_KNOB);
        vol /= 10;
        if (abs(vol - lastvol) > 3) {lastvol = vol;musicPlayer.setVolume(lastvol, lastvol);}

      //adjust the tuning
      newpos = knob.read() / 4;//divide for encoder detents
     
      if((newpos != pos) ){
        int diff = newpos - pos;//check the different between old and new position
        if(diff>=1){m++;m = (m+36) % 36;}//modulo to roll over the m variable through the list size which is set to max encoder value
        if(diff==-1){m--;m = (m+36) % 36;}//rotating backwards
        Serial.print(" channel ");
        Serial.println(m);
        lastpos = pos;
        pos = newpos;
        isSaved = false;
        stationOn = false;
      } //encoder position check
    } //Loopcounter
    } //else
} //loop

ac2ev
 
Posts: 47
Joined: Mon Sep 16, 2013 5:13 pm

Please be positive and constructive with your questions and comments.