FM Transmitter - Car MP3 Player

 

FM Transmitter - Car MP3 Player - Software

January 2016

The Arduino Software

Download Software   (Right-click link and "Save link as...")

The FMTX library can be downloaded from here: http://www.elechouse.com/elechouse/.../FMTX.rar

................................................................................................................
NOTE: The FMTX library needs to be edited slightly in order to compile in later versions of the Ardiuno IDE:

In FMTX.h at line 32 comment out typedef uint16_t u16

 //typedef uint16_t u16;

In FMTX.cpp

at line 140:  replace u16 with uint16_t

at line 147: replace u16 with uint16_t 


Also, as noted in the comments in the sketch:

in FMTX.h at line 71  uncomment :

void fmtx_set_rfgain(u8 rfgain); 
................................................................................................................




// FM Transmitter - Car MP3 Player. (c) 2016 vwlowen.co.uk

#include <EEPROM.h>
#include <SoftwareSerial.h>
#include <SPI.h>

// https://www.pjrc.com/teensy/td_libs_IRremote.html
#include <IRremote.h>



// http://www.elechouse.com/elechouse/images/product/FM%20Transmitter%20Module/FMTX.rar
// http://www.elechouse.com/elechouse/index.php?main_page=product_info&cPath=168_170&products_id=2206

#include <FMTX.h>

// https://github.com/adafruit/Adafruit-GFX-Library
// https://github.com/adafruit/Adafruit-ST7735-Library

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific TFT Display library

#define TFT_CS     10                       // Define TFT SPI pins (11, 12 & 13 are implicit with SPI)
#define TFT_RST    A0  
#define TFT_DC     A1

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);

#define txPin 2                             // Software Serial Tx to MP3 Module Rx pin.
#define rxPin 3                             // Software Serial Rx from MP3 Module Tx pin.


SoftwareSerial mp3(rxPin, txPin);           // Create software serial for MP3 module.

int RECV_PIN = A2;                          // IR sensor connected to A2 (as a digital input)
IRrecv irrecv(RECV_PIN);                    // Create IR receiver.

decode_results results;                     // Returned results codes from IR sensor.

// Define the MP3 Module's Commands that we might use...
#define cmdNextTrack  0x01                  // Next track.  (Not used)
#define cmdPrevTrack  0x02                  // Previous track. (Not used)
#define cmdSetTrack   0x03                  // Play track. Param = track number.
#define cmdRepeatPlay 0x11                  // Play/stop. Param: 1 = Play, 0 = Stop.
#define cmdPlay       0x0d                  // Resume current track.

#define cmdSetVolume  0x06                  // Set Volume to a specified value. Param = volume 0-31.
#define cmdSetEq      0x07                  // Set Equalizer to a specified value.
#define cmdReset      0x0c                  // Reset MP3 Module.
#define cmdPause      0x0e                  // Pause current track.

// Define some FM Transmitter parameters
#define FREQ_MAX      108.0                 // Set FM frequency limits and step size.
#define FREQ_MIN       87.5                 // MHz
#define FREQ_STEP       0.05                // MHz

#define RF_GAIN_MIN     4
#define RF_GAIN_MAX    15

// Define the codes returned from the IR remote control. The codes for your remote can be captured on the Serial monitor and entered here,
#define IR_PAUSE      0xFD00FF              // IR remote Pause
#define IR_NEXT       0xFD50AF              // IR remote 'Next'
#define IR_PREV       0xFD906F              // IR remote 'Previous'
#define IR_VOL_UP     0xFD609F
#define IR_VOL_DWN    0xFDA05F
#define IR_EQU        0xFD20DF
#define IR_CH_UP      0xFD40BF              // Channel+ duplicate 'Next' command
#define IR_CH_DWN     0xFD807F              // Channel - duplicat 'Previous' command

#define IR_0          0xFD10EF              // Codes from number keys
#define IR_1          0xFD30CF
#define IR_2          0xFDB04F
#define IR_3          0xFD708F
#define IR_4          0xFD08F7
#define IR_5          0xFD8877
#define IR_6          0xFD48B7
#define IR_7          0xFD28D7
#define IR_8          0xFDA857
#define IR_9          0xFD6897


int PWM_LED = 9;                            // PWM output to TFT Backlight

int pinBusy = 4;                            // Input from MP3 player. LOW = track playing (Busy).

int pinButtonMode = 5;                      // Mode selection button
int pinPrev = 6;                            // Back/Down button
int pinPausePlay = 7;                       // Pause/Resume button
int pinNext = 8;                            // Next/Up button


char* equName[6] = {"NORMAL", "POP", "ROCK", "JAZZ", "CLASSICAL", "BASS"};   // Names for equalizer settings.

byte equ = 0;                       // Equalizer setting 0-5 (Normal/Pop/Rock/Jazz/Classical/Bass)
byte volume = 25;                   // Initial MP3 module volume (0 - 31).

byte led = 255;                     // LED backlight brightness. 255 = full on.


byte mode = 0;                      // 0-7. (or 0-6) Sets what Next/Previous buttons do. Track/FM Freq/Volume/Equalizer/Auto Start/RF Gain*
                                    // Uncomment line 71 [void fmtx_set_rfgain(u8 rfgain);] in FMTX.h  to enable gain-setting function (Mode 7) 
                                     

byte modeMax = 7;                      // * or modeMax = 6 if fmtx_set_rfgain is not available, modes 0-6 only.

unsigned int track;             // Currently-playing track.

unsigned long trackTimer;                    // Time interval between tracks when auto-incrementing
unsigned long nextTimer;                     // Time interval between tracks when using Next button
unsigned long modeTimer;                     // Time interval when not on normal playing mode (mode 0)
unsigned long irTimer;
unsigned long holdTimer;                     // Time how long the Next/Previous buttons are held down when adjusting frequency...
int delayTimer;                              // ... and change loop delay timer.


unsigned long trackTimerInterval = 2000;     // Next track must start within 2 seconds on auto increment.
unsigned long nextTimerInterval = 100;       // Next track must start within this msec  using Next button.
unsigned long modeTimerInterval = 7000;      // Return to mode 0 (normal playing), 7 seconds after no Next or Prev button press.
unsigned long irTimerInterval = 2000;        // Stop waiting for three-digit number from IR remote after this time.


boolean volChanged = false;                  // Flags used to indicate Volume, FM Frequency, Equalization, AutoStart or RF Gain...
boolean freqChanged = false;                 // ... has probably been changed using Next/Previous buttons.
boolean equChanged = false;                  // Avoids unnecessary writing to EEPROM.
boolean autoChanged = false;
boolean rfGainChanged = false;
boolean saveTrackChanged = false;


boolean autoStart = false;
boolean paused = false;
boolean manualStarted = false;
boolean saveTrack = false;

boolean isNumber = false;
boolean irMessage = false;
int digit = -1;
int numberOfDigits = 0;

unsigned int number = 0;

float freq = FREQ_MIN;                       // Initial FM Freq if EEPROM read fails.  
byte rfGain = 4;                             // Uncomment line 71 in FMTX.h - otherwise gain defaults to 4.


// Initialize the send buffer for MP3 module command string.
byte cmdBuff[10] = {0x7e,                    // Start Byte, always 0x7e.
                    0xff,                    // Version, always 0xff.
                    0x06,                    // Data length, always 0x06 for us.
                    0x00,                    // Command value.
                    0x00,                    // Feedback, always 0x00 ('No feedback') for us.
                    0x00,                    // Command parameter Hi Byte.
                    0x00,                    // Command paramemer Lo Byte.
                    0x00,                    // Checksum Hi Byte.
                    0x00,                    // Checksum Lo Byte.
                    0xef                     // End Byte, always 0xef.
                    };

// Refresh entire display                    
                    
void updateDisplay(boolean clr) {

  if (clr) tft.fillScreen(ST7735_BLACK);     // Clear screen first if 'clr' is true
  tft.setCursor(2, 25);
  tft.setTextColor(ST7735_GREEN);
  tft.setTextSize(2);
  tft.print("Track: ");  
  tft.print(track);
  tft.setTextSize(1);
  
  tft.setTextColor(ST7735_YELLOW);
   
  tft.setCursor(2, 58);
  tft.print("Backlight:       ");
  tft.print(led);    
  
  tft.setCursor(2, 68);
  tft.print("Volume (0-31):   ");
  tft.print(volume);  
  
  tft.setCursor(2, 78);
  saveTrack ? tft.print("Save last track: ON"):tft.print("Save last track: OFF");
  
  tft.setCursor(2, 88);
  tft.print("FM Freq (MHz):   ");  
  tft.print(freq, 2);  tft.print("0");
  
  tft.setCursor(2, 98);
  tft.print("Equalization:    ");
  tft.print(equName[equ]); 
  
  tft.setCursor(2, 108);
  autoStart ? tft.print("Auto Start:      ON"):tft.print("Auto Start:      OFF");
  
  if (modeMax == 7) {
    tft.setCursor(2, 118);
    tft.print("RF Gain (0-15):  ");
    tft.print(rfGain);
  }
} 


// Update only a single value. The display does not overwrite previous values so it's necessary
// to clear a small rectangle to wipe the original value before printing the new value. Clearing and
// updating the entire screen is unnecessarily slow when changing an individual value.

void updateValue(float value, byte dec) {
  
  byte y = (mode * 10) + 48;                                   // Calculate vertical position of line on screen
  tft.fillRect(80, y, 178, 10, ST7735_BLACK);
  tft.setCursor(105, y);
  tft.setTextColor(ST7735_WHITE);
  tft.setTextSize(1);
  switch(mode) {
    case 0:   tft.fillRect(2, 25, 178, 20, ST7735_BLACK);                             // New track
              paused ? tft.setTextColor(ST7735_RED) : tft.setTextColor(ST7735_GREEN);
              tft.setTextSize(2);  
              tft.setCursor(2, 25);
              tft.print("Track: ");
              tft.print(track);  
              break;
    case 3:   saveTrack ? tft.print("Save last track: ON"):tft.print("Save last track: OFF");
              break;    
    case 5:   tft.print(equName[equ]);                               // New equalization setting              
              break;
    case 6:   autoStart ? tft.print("ON"):tft.print("OFF");          // Auto Start setting changed
              break;
   
    default:  tft.print(value, dec);                                 // All other values not track, equ or Auto Start.
              break;
  }
  if (mode == 4) tft.print("0");                                     // If it's a new frequency, add a "0" to display (Hz).
}  

// Highlight a single line in white to indicate which value the Next & Prev buttons will change.
void updateLine() {
  updateDisplay(false); 
  
  byte y = (mode * 10) + 48;                        // Calculate vertical position of line on screen
  tft.fillRect(2, y, 178, 10, ST7735_BLACK);
  tft.setCursor(2, y);
  
  tft.setTextColor(ST7735_WHITE);  
  switch (mode) {
    case 0: break; 
   
    case 1: tft.print("Backlight:       ");
            tft.print(led);  
            break;
    case 2: tft.print("Volume (0-31):   ");
            tft.print(volume);  
            break;
    case 3: saveTrack ? tft.print("Save last track: ON"):tft.print("Save last track: OFF");
            break;    
    case 4: tft.print("FM Freq (MHz):   ");  
            tft.print(freq, 2);  tft.print("0");   
            break;
    case 5: tft.print("Equalization:    ");
            tft.print(equName[equ]); 
            break;
    case 6: autoStart ? tft.print("Auto Start:      ON"):tft.print("Auto Start:      OFF");
            break;
    case 7: tft.print("RF Gain (0-15):  ");
            tft.print(rfGain);
            break;  

  }
  tft.setTextColor(ST7735_YELLOW);
  
}


// Routine to build 10-byte command buffer and write to software serial Tx (MP3 module Rx).

void sendCommand(byte Cmd, unsigned int Param) {
  unsigned int sum = 0;
  
  cmdBuff[3] = Cmd;                    // Command.

  cmdBuff[5] = (byte) (Param >> 8);    // Param Hi byte.
  cmdBuff[6] = (byte) (Param);         // Param Lo byte. 
  
  for (int i=1; i<7; i++) {            // Calculate 16-bit checksum from buffer bytes 1 to 6.
    sum -= cmdBuff[i];
  }
 
  cmdBuff[7] = (byte) (sum >> 8);      // Add checksum Hi byte to buffer.
  cmdBuff[8] = (byte) sum;             // Add checksum Lo byte to buffer.

  for (int i = 0; i<10; i++) {         // Write entire buffer to software serial (mp3).
    mp3.write(cmdBuff[i]);
  }      
}

// Long 'self-contained' function to handle the IR remote control.  Omit if not required.
void irRemoteHandler() {
  digit = -1; 
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);              // Show results on Serial monitor to get codes initially.
    irrecv.resume();                                 // Get ready for the next value.
    
    // Perform appropriate action depending on code received.
    switch (results.value) {
      case IR_PAUSE:  track == 0 ? track = 1 : paused = !paused;                      // Pause playback.
                      paused ? sendCommand(cmdPause, 0) : sendCommand(cmdPlay, 0);
                      updateValue(track, 0);
                      break;
      case IR_CH_UP:  
      case IR_NEXT:   manualStarted = true;                       // Next track
                      paused = false;
                      track++;

                      sendCommand(cmdRepeatPlay, 0);              // Stop the current rack
                      delay(100);
                      sendCommand(cmdSetTrack, track);
                      updateValue(track, 0); 
                      nextTimer = millis();
                      while (digitalRead(pinBusy) == HIGH) {                 
                        if (millis() > (nextTimer + nextTimerInterval)) {          // If still "not busy" after interval, we must have reached 
                          track = 1;                                               // the last track number that played successfully.
                          sendCommand(cmdSetTrack, track);
                          updateDisplay(true);
                          break;
                        }
                      }                      
                      break;
      case IR_CH_DWN:  
      case IR_PREV:   if (track == 0) track = 2;                   // Previous track
                      if (track > 1) track--;
                      paused = false;
                      sendCommand(cmdSetTrack, track);
                      updateValue(track, 0); 
                      break; 
      case IR_VOL_UP: if (volume < 31) {                           // Increase volume from MP3 player to FM TX input.
                        volume++;
                        mode = 2;
                        updateValue(volume, 0);  
                        modeTimer = millis();  
                        sendCommand(cmdSetVolume, volume);
                      }
                      volChanged = true; 
                      break;
      case IR_VOL_DWN: if (volume > 0) {                           // Reduce volume from MP3 player to FM TX input.
                        volume--;
                        mode = 2;
                        updateValue(volume, 0);
                        modeTimer = millis();
                        sendCommand(cmdSetVolume, volume);
                      }
                      volChanged = true;
                      break;
       case IR_EQU:   equ < 5 ? equ++ : equ = 0;                   // Change equaization setting.
                      sendCommand(cmdSetEq, equ);
                      mode = 5;
                      updateValue(equ, 0);
                      equChanged = true;
                      modeTimer = millis();
                      break; 
                      
       isNumber = false; 
       digit = -1;
       case IR_0:     digit = 0; break;                            // Set 'digit' to received numerical key code
       case IR_1:     digit = 1; break;
       case IR_2:     digit = 2; break;
       case IR_3:     digit = 3; break;
       case IR_4:     digit = 4; break; 
       case IR_5:     digit = 5; break;
       case IR_6:     digit = 6; break;
       case IR_7:     digit = 7; break;
       case IR_8:     digit = 8; break;
       case IR_9:     digit = 9; break;
       
    }
  } 

  isNumber = ((digit > -1) && (digit < 10));                  // Flag if the received code was from a number key.
  
  if (isNumber) {                                             // Track numbers can only be changed in mode 0.
    if (mode != 0) {
      mode = 0;
      updateDisplay(true);
    }
    if (paused) {                                             // Re-start playing if paused.
      paused = !paused;
      updateValue(track, 0);
      sendCommand(cmdRepeatPlay, 0);                          // Stop current track
      irMessage = true;                                       // 
    }    
    
    // Build three-digit number from the three received number keys.
    
    if (numberOfDigits == 0) {number = digit; numberOfDigits = 1;} else
    if (numberOfDigits == 1) {number = (number * 10) + digit; numberOfDigits = 2;} else
    if (numberOfDigits == 2) {number = (number * 10) + digit; numberOfDigits = 3;} else
    if (numberOfDigits == 3) {
       track = number;
       
    // Stop currently-plaing track to create "not-busy" signal. This initiates 'Play track number' in main loop.
       sendCommand(cmdRepeatPlay, 0);               // '0' parameter means 'Stop'
       tft.fillRect(90, 5, 50, 17, ST7735_BLACK);   // Clear the BLUE prompt digits.
       delay(50);   
       digit = -1;    
       irMessage = true;
       numberOfDigits = 0;
       number = 0;
    }
    digit = -1;
    tft.fillRect(90, 5, 50, 17, ST7735_BLACK);
    tft.setTextColor(ST7735_BLUE);                 // Print each digit as it is entered from the remote
    tft.setTextSize(2);
    tft.setCursor(90, 5);
    if (number < 100) tft.print("0");
    if (number < 10) tft.print("0");
    tft.print(number);
    irTimer = millis();                            // Set timer. This will over-ride having to enter 3 digits when it times out.

  } 
  
  if ((number > 0) &&(millis() > (irTimer + irTimerInterval))) {     // Can't wait for 3 digits so assume the missing...
    tft.fillRect(90, 5, 50, 17, ST7735_BLACK);                       // ... digits (100's and possibly 10's) are '0'

      track = number;

    irMessage = true;                                                // Tell main loop not to increment previous track number
    sendCommand(cmdRepeatPlay, 0);                                   // '0' parameter means 'Stop' playing current track.
    delay(50);  
    digit = -1;     
    numberOfDigits = 0;
    number = 0;    
  }  
}

                    
void setup() {
  
  pinMode(PWM_LED, OUTPUT);
  pinMode(TFT_RST, OUTPUT);
  pinMode(TFT_DC, OUTPUT);
  
  pinMode(pinButtonMode, INPUT_PULLUP);             // Set button pins as inputs and
                                                    // enable internal pullups.
  pinMode(pinPrev, INPUT_PULLUP);
  pinMode(pinNext, INPUT_PULLUP); 
  pinMode(pinPausePlay, INPUT_PULLUP);
  pinMode(pinBusy, INPUT_PULLUP);                   // Busy input from MP3 player.  LOW = track playing.
  
  Serial.begin(9600);                               // Serial monitor used to capture IR remote's codes....
  Serial.println("Start");                          // .... Not required once the codes have been captured.
  randomSeed(analogRead(3));
  
  irrecv.enableIRIn();                              // Start IR the receiver  
  
  analogWrite(PWM_LED, 255);                        // Backlight full on.

  
  tft.initR(INITR_BLACKTAB);                        // Initialize a ST7735S chip, black tab
  tft.setRotation(3);                               // Display horizontal
  
  fmtx_init(freq, EUROPE);                          // Initialize the FM transmitter.
  delay(500);
  mp3.begin(9600);                                  // Initialise software serial mp3.
  
  // Initial values stored in EEPROM could be unpredictable so the values are constrained within sensible limits.
  // Once a value has been changed by the Next/Prev buttons, it's automatically saved to EEPROM.
  
  freq = constrain(eepromReadFloat(0), 87.5, 108.0);    // Retrieve FM transmitter frequency
  
  if (isnan(freq)) freq=87.5;                       // not a valid floating point number
  
  volume = constrain(EEPROM.read(4), 0, 31);        // Retrieve MP3 player volume setting
  equ = constrain(EEPROM.read(5), 0, 5);            // Retrieve MP3 player equalization setting
  autoStart = constrain(EEPROM.read(6), 0, 1);      // Retrieve Auto Start (Yes/no)
  saveTrack = constrain(EEPROM.read(8), 0, 1);
  
  if (saveTrack) {
    track = eepromReadInt(9) - 1;                   // Replay last track
  } else
    track = 0;

  
  fmtx_set_freq(freq);                              // Set the FM transmitter frequency
  delay(100);
  
  // Changing the RF Gain (ie output power) of the FM TX module requires that a funtion declaration in FMTX.h is un-commented.
  // The default RG Gain is set in the 'fmtx_init()' function to a value of 4. Any value between 0 and 15 is possible but the
  // manufacturers of the module may have had good reason to restrict it to 4 (besides possible interference to others) so it's your choice!
  

  if (modeMax == 7) {
    rfGain = EEPROM.read(7);                                  // If the function is available, set the FM transmitter RF gain
    if ((rfGain < RF_GAIN_MIN) || (rfGain > RF_GAIN_MAX)){  
      rfGain = RF_GAIN_MIN;                                   // Set default RF gain to minimum if invalid value is stored in EEPROM.  
    }         
    fmtx_set_rfgain(rfGain);                                  // (4 - 15) ****** Uncomment line 71 in FMTX.h  *******
    delay(100);
  }  

  sendCommand(cmdReset, 0);                        // Reset MP3 module (command, parameter).
  delay(1000);

  
  sendCommand(cmdSetVolume, volume);               // Set the MP3 player volume. Adjusts how much audio the FMTX module is given....
  delay(100);                                      // ... set to highest value without distortion.
  sendCommand(cmdSetEq, equ);                      // Set the MP3 player equalization setting. (Normal/Pop/Rock/Jazz/Classical/Bass)
  delay(100);

  updateDisplay(true);                             // Clear display and print all labels and values.
}



void loop() {

  irRemoteHandler();                    // Check if any valid code has been received by the IR sensor.
  
  // Check for MP3 "not busy" signal and increment track number but not if Paused.
  if ((digitalRead(pinBusy) == HIGH) && (!paused) && (autoStart || manualStarted)) {

    if (!irMessage) {
         track++;  
    }       
    irMessage = false;
    updateValue(track, 0);                                // Display new track number
    sendCommand(cmdSetTrack, track);                      // Play new track
    delay(100);
    
    
    trackTimer = millis();
    while(digitalRead(pinBusy) == HIGH) {
      if (millis() > (trackTimer + trackTimerInterval)) {     // Track number still not playing so must be greater than last track
        track = 1;                                            // Reset track number to 1 and start again.
        sendCommand(cmdSetTrack, track);
        updateDisplay(true);                                  // Completely refresh display.
        break;
      } 
    } 
  }  

  if (digitalRead(pinButtonMode) == LOW) {                    // Mode button.  Set mode to select what Next/Prev
    mode < modeMax ?  mode++ : mode = 0;                      // buttons adjust.  Track/FM Freq/Volume/Equalizer/Auto Start/RF Gain            
    updateLine(); 
    while (digitalRead(pinButtonMode) == LOW); 
    delay(50);
    modeTimer = millis();                                     // Set mode timer to allow auto return to mode 0. (Normal mode)
  }  
  
  if ((millis() > (modeTimer + modeTimerInterval)) & (mode != 0)){     // Auto reset mode button to mode 0 (track Next/Prev) 
     mode = 0;                                                         // to avoid having to cycle through mode options manually.
     modeTimer = millis();
     updateDisplay(true);
  }
  
 
  if (mode == 7) {                                                    // RF gain.  Mode 7 is only available if FTMX.h line 71
    if (digitalRead(pinNext) == LOW) {                                // has been uncommented.
      if (rfGain < RF_GAIN_MAX ) rfGain++ ;
      fmtx_set_rfgain(rfGain); 
      updateValue(rfGain, 0);
      rfGainChanged = true;
      delay(200);
      modeTimer = millis();
    }
    
    if (digitalRead(pinPrev) == LOW) {                     
      if (rfGain > RF_GAIN_MIN) rfGain-- ;
      fmtx_set_rfgain(rfGain);  
      updateValue(rfGain, 0);
      rfGainChanged = true;
      delay(200);
      modeTimer = millis();
    } 

  }  

  
  if (mode == 6) {                                                              // Auto Start on/off. Choose whther player starts playing as soon as
    if ((digitalRead(pinNext) == LOW) || (digitalRead(pinPrev) == LOW)) {       // power is applied or if Next button needs to be pressed to start
          autoStart = !autoStart;                                               // playing.
        while ((digitalRead(pinNext) == LOW) || (digitalRead(pinPrev) == LOW));
        autoChanged = true;
        updateDisplay(true);
        mode = 0;
        delay(50);
        modeTimer = millis();
    }
  }   
  
  if (mode == 5) {                                                               // Adjust equalization. 0-5 = Normal/Pop/Rock/Jazz/Classical/Bass
    if (digitalRead(pinNext) == LOW) { 
      equ < 5 ? equ++ : equ = 0;
      sendCommand(cmdSetEq, equ);
      updateValue(equ, 0);
      equChanged = true;
      delay(150);
      modeTimer = millis();
    }
    if (digitalRead(pinPrev) == LOW) {
      equ > 0 ? equ-- : equ = 5;
      sendCommand(cmdSetEq, equ);
      updateValue(equ, 0);
      equChanged = true;
      delay(150);
      modeTimer = millis();
    }    
  }
  
  if (mode == 4) {                                                               // Adjust frequecncy of FM transmitter module
    holdTimer = millis();
    while (digitalRead(pinNext) == LOW) { 
      
      if (freq + FREQ_STEP <= FREQ_MAX + 0.01) {
        if (millis() > (holdTimer + 3000)) {                                     // If temporary timer exceeds 3 seconds, button has been held
          delayTimer = 500;                                                      // down for 3 seconds or more so temporarily increase
          freq = freq + 1.0;                                                     // frequency increment to 1 MHz
        } else {
          delayTimer = 250;        
          freq = freq + FREQ_STEP;
        }  
        fmtx_set_freq(freq);                       
        updateValue(freq, 2);

        delay(delayTimer);
      }
      freqChanged = true;
      modeTimer = millis();
    }
    
    while (digitalRead(pinPrev) == LOW) {  
      if (freq - FREQ_STEP >= FREQ_MIN - 0.01) {    
        if (millis() > (holdTimer + 3000)) {
          delayTimer = 500;
          freq = freq - 1.0;
        } else {
          delayTimer = 250;  
          freq = freq - FREQ_STEP;
        }
        fmtx_set_freq(freq);    
        updateValue(freq, 2);
        
        delay(delayTimer);
      }
      freqChanged = true;
      modeTimer = millis();
    } 
  }
  
  if (mode == 3) {                                                              // Save last track - ON/OFF
    if ((digitalRead(pinNext) == LOW) || (digitalRead(pinPrev) == LOW)) {       // Select whether to resume from last-played track or
          saveTrack = !saveTrack;                                               // restart from track 1 when power is next restored.
        while ((digitalRead(pinNext) == LOW) || (digitalRead(pinPrev) == LOW));
        saveTrackChanged = true;
        updateDisplay(true);
        mode = 0;
        delay(50);
        modeTimer = millis();
    }
  }     
  
  if (mode == 2) {                              // Adjust Volume from MP3 player.
  
    if (digitalRead(pinNext) == LOW) {               
      if (volume < 31) {
        volume++;
        updateValue(volume, 0);        
        sendCommand(cmdSetVolume, volume);
      }
      delay(150);
      volChanged = true;
      modeTimer = millis();
    }
    
    if (digitalRead(pinPrev) == LOW) {                     
      if (volume > 0) {
        volume--;
        updateValue(volume, 0);
        sendCommand(cmdSetVolume, volume);
      }
      delay(150);
      volChanged = true;
      modeTimer = millis();
    }
  }
  
  if (mode == 1) {                                      //. LED Backlight
    if (digitalRead(pinNext) == LOW) {               
      if (led < 255) {
        led++;
        analogWrite(PWM_LED, led);
        updateValue(led, 0);  
      }
      delay(10);
      modeTimer = millis();
    }
    
    if (digitalRead(pinPrev) == LOW) {                     
      if (led > 0) {
        led--;
        analogWrite(PWM_LED, led);
        updateValue(led, 0);
      }
      delay(10);
      modeTimer = millis();
    }
  } 

  if (mode == 0) {
    if (digitalRead(pinNext) == LOW) {                       // Next Track button
      manualStarted = true;
      paused = false;
      track++;  

      sendCommand(cmdRepeatPlay, 0);                              // Stop playing current track to create "not busy" signal from MP3 player.
      delay(100);
      while(digitalRead(pinNext) == LOW); 
      sendCommand(cmdSetTrack, track);
      updateValue(track, 0);
      delay(150);
      
      nextTimer = millis();                                        // Set next-track timer.  
      while (digitalRead(pinBusy) == HIGH) {                 
        if (millis() > (nextTimer + nextTimerInterval)) {          // If still "not busy" after interval, we must have reached last track 
          track = 1;
          sendCommand(cmdSetTrack, track);
          updateDisplay(true);
          break;
        }
      }
      modeTimer = millis();
    }
  
    
    if (digitalRead(pinPrev) == LOW) {                            // Previous Track button
      if (track == 0) track = 2;
      if (track > 1) track--;
      sendCommand(cmdSetTrack, track);

      paused = false;
      
      updateValue(track, 0);
      while(digitalRead(pinPrev) == LOW);
      delay(150);
      modeTimer = millis();
    } 
   
    if (digitalRead(pinPausePlay) == LOW) {                       // Pause / Resume button
      track == 0 ? track = 1 : paused = !paused;
      paused ? sendCommand(cmdPause, 0) : sendCommand(cmdPlay, 0);
      
      updateValue(track, 0);
      while(digitalRead(pinPausePlay) == LOW);
      delay(150);
      modeTimer = millis();
    }    
  } 
  
  if (freqChanged) {
    freqChanged = false;
    if (eepromReadFloat(0) != freq) eepromWriteFloat(0, freq);
  }  
  
  if (volChanged) {                                            // Save changes to Volume, Frequency, Equalization, AutoStart and RF gain
    volChanged = false;                                        // in EEPROM to be recalled at power-on next time.
    if (EEPROM.read(4) != volume) EEPROM.write(4, volume);
  }
  
  if (equChanged) {
    equChanged = false;
    if (EEPROM.read(5) != equ) EEPROM.write(5, equ);
  }  
  if (autoChanged) {
    autoChanged = false;
    if (EEPROM.read(6) != autoStart) EEPROM.write(6, autoStart);
  }   
  if (rfGainChanged) {
    rfGainChanged = false;
    if (EEPROM.read(7) != rfGain) EEPROM.write(7, rfGain);
  } 
  
  if (saveTrackChanged) {
    saveTrackChanged = false;
    if (EEPROM.read(8) != saveTrack) EEPROM.write(8, saveTrack);
  }
  
  if (saveTrack) {
    if (eepromReadInt(9) != track+1) eepromWriteInt(9, track+1);           //9, 10
  }

}  

// http://www.elcojacobs.com/storing-settings-between-restarts-in-eeprom-on-arduino/

int eepromReadInt(int address){
   int value = 0x0000;
   value = value | (EEPROM.read(address) << 8);
   value = value | EEPROM.read(address+1);
   return value;
}
 
void eepromWriteInt(int address, int value){
   EEPROM.write(address, (value >> 8) & 0xFF );
   EEPROM.write(address+1, value & 0xFF);
}
 
float eepromReadFloat(int address){                            // Read/Write floating point numbers to EEPROM
   union u_tag {
     byte b[4];
     float fval;
   } u;   
   u.b[0] = EEPROM.read(address);
   u.b[1] = EEPROM.read(address+1);
   u.b[2] = EEPROM.read(address+2);
   u.b[3] = EEPROM.read(address+3);
   return u.fval;
}

void eepromWriteFloat(int address, float value){
   union u_tag {
     byte b[4];
     float fval;
   } u;
   u.fval=value;
 
   EEPROM.write(address  , u.b[0]);
   EEPROM.write(address+1, u.b[1]);
   EEPROM.write(address+2, u.b[2]);
   EEPROM.write(address+3, u.b[3]);
}

 

Back to Index | Page 1 | Page 2 | Page 3