Browse Source

Initial commit

master
Dejvino 1 year ago
commit
b7d6c8ac2d
3 changed files with 352 additions and 0 deletions
  1. +33
    -0
      README.md
  2. BIN
      player.jpg
  3. +319
    -0
      tapedeck-player.ino

+ 33
- 0
README.md View File

@@ -0,0 +1,33 @@
# Tapedeck Player

Replace the old electronics of your tapedeck with an mp3 player, all while keeping the retro controls working!

![TapeDeck Player](player.jpg)

## Hardware

- Tapedeck. Anything will do, I used a [Universum Cassetten Recorder - Stereo Tape Deck CT 2746](https://www.radiomuseum.org/r/quelle_ct2746.html) from 1974.
- Arduino
- [DFPlayer Mini MP3 Player](https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299) with an SD card
- OLED Display. I used a 1.3" I2C display, 128x64 pixels, SH1106 controller.
- audio amplifier to drive the speaker
- one microswitch to detect the fast-forward button being pressed

I reused a lot of the original components from the tapedeck (including wires), namely the user-facing electronics: volume and bass/treble potentiometers, mode-select switches, lights, speaker, power-on switch, audio input/output DIN connectors. And of course the tape motor.

## How This Works

Everything gets powered when a button on the cassette drive is pressed, activating the motor among other things. After the Arduino and the MP3 player boots (after a few seconds), the OLED display activates and the music starts. When the player is stopped, power is cut and the whole device is truly off.

Every power-on is a fresh boot for the MCU. Therefore it keeps track of what song to play in its EEPROM; whenever a song finishes, the song index is increased and stored.

The Fast-Forward button has a microswitch that tells the MCU to only play a short beginning of each song, skipping through them quickly.

The OLED display shows the song time, song index and the number of songs on the SD card.

The volume potentiometer controls the volume (gain) on the audio amplifier.

The MCU scans the tone potentiometer and changes the equalizer preset on the MP3 player: BASS, NORMAL, POP.

The "chrome cassette" switch overrides the selected equalizer ROCK.


BIN
player.jpg View File

Before After
Width: 800  |  Height: 600  |  Size: 173 KiB

+ 319
- 0
tapedeck-player.ino View File

@@ -0,0 +1,319 @@
// === OVERVIEW ===
// Tape Deck player replacement
//
// VU meter --> OLED display
// Cassette player --> MP3 player

// === MP3 PLAYER ===
/***************************************************
DFPlayer - A Mini MP3 Player For Arduino
<https://www.dfrobot.com/product-1121.html>
***************************************************
This example shows the basic function of library for DFPlayer.
Created 2016-12-07
By [Angelo qiao](Angelo.qiao@dfrobot.com)
GNU Lesser General Public License.
See <http://www.gnu.org/licenses/> for details.
All above must be included in any redistribution
****************************************************/

/***********Notice and Trouble shooting***************
1.Connection and Diagram can be found here
<https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram>
2.This code is tested on Arduino Uno, Leonardo, Mega boards.
****************************************************/

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#include "EEPROM.h"

#define PIN_EQ A1
#define PIN_CHROME 6
#define PIN_FORWARD 10
#define EEPROM_SONG_INDEX 1
#define SKIP_LIMIT 250
#define EEPROM_SKIP 2

SoftwareSerial mySoftwareSerial(9, 8); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

int song_index = 0;
unsigned long song_started = 0;
int files_max = -1;
bool skipping = false;

void setup_player()
{
pinMode(PIN_EQ, INPUT);
pinMode(PIN_CHROME, INPUT);
pinMode(PIN_FORWARD, INPUT);
analogReference(DEFAULT);
Serial.begin(9600);
while (!Serial && millis() < 2000) { delay(10); }
song_index = EEPROM.read(EEPROM_SONG_INDEX);
unsigned int song_skip = EEPROM.read(EEPROM_SKIP);
if (song_skip > SKIP_LIMIT) {
song_skip = 0;
}
song_index += song_skip;
if (song_index >= 255) {
song_index = 0;
}
Serial.println();
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
mySoftwareSerial.begin(9600);
if (!myDFPlayer.begin(mySoftwareSerial)) {
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while(true){
delay(0);
}
}
Serial.println(F("DFPlayer Mini online."));

myDFPlayer.volume(20); //Set volume value. From 0 to 30
myDFPlayer.play(song_index+1); //Play the first mp3
do {
delay(100);
files_max = myDFPlayer.readFileCounts();
} while (files_max <= -1);

if (digitalRead(PIN_FORWARD) == 1) {
// FastForward
skipping = true;
while (true) {
delay(500);
if (digitalRead(PIN_FORWARD) == 1) {
song_skip = min(song_skip + 1, SKIP_LIMIT);
EEPROM.update(EEPROM_SKIP, song_skip);
Serial.print("Skipping +1 ... ");
} else {
Serial.print("Skipping +0 ... ");
}
if (song_index + song_skip >= files_max) {
song_index = 0;
song_skip = 0;
}
myDFPlayer.play(song_index + song_skip + 1);
}
}

myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);

if (files_max > 0 && song_index >= files_max) {
Serial.println("Song index out of bounds, skipping to next.");
nextSong();
}
song_started = millis();
}

int eq_now = DFPLAYER_EQ_NORMAL;

void loop_player()
{
static unsigned long eq_timer = millis();

if (millis() - eq_timer > 100) {
eq_timer = millis();
int eq_next = eq_now;
int eqChrome = digitalRead(PIN_CHROME);
if (eqChrome == 0) {
//analogReference(DEFAULT);
int eqRaw = analogRead(PIN_EQ);
//Serial.print("EQ: ");
//Serial.println(eqRaw);
if (eqRaw < 512) {
eq_next = DFPLAYER_EQ_BASS;
//Serial.println("EQ: Bass");
} else if (eqRaw < 900) {
eq_next = DFPLAYER_EQ_NORMAL;
//Serial.println("EQ: Normal");
} else {
eq_next = DFPLAYER_EQ_POP;
//Serial.println("EQ: POP");
}
} else {
eq_next = DFPLAYER_EQ_ROCK;
//Serial.println("EQ: Rock");
}
if (eq_now != eq_next) {
eq_now = eq_next;
myDFPlayer.EQ(eq_now);
}
}
if (myDFPlayer.available()) {
printDetail(myDFPlayer.readType(), myDFPlayer.read());
}
}

void nextSong() {
int files_max = myDFPlayer.readFileCounts();
song_index = (song_index + 1) % files_max;
EEPROM.update(EEPROM_SONG_INDEX, song_index);
EEPROM.update(EEPROM_SKIP, 0);
myDFPlayer.play(song_index + 1);
song_started = millis();
Serial.println("Next song...");
}

void printDetail(uint8_t type, int value){
switch (type) {
case TimeOut:
Serial.println(F("Time Out!"));
break;
case WrongStack:
Serial.println(F("Stack Wrong!"));
break;
case DFPlayerCardInserted:
Serial.println(F("Card Inserted!"));
break;
case DFPlayerCardRemoved:
Serial.println(F("Card Removed!"));
myDFPlayer.stop();
break;
case DFPlayerCardOnline:
Serial.println(F("Card Online!"));
myDFPlayer.start();
break;
case DFPlayerUSBInserted:
Serial.println("USB Inserted!");
break;
case DFPlayerUSBRemoved:
Serial.println("USB Removed!");
break;
case DFPlayerPlayFinished:
Serial.print(F("Number:"));
//Serial.print(value);
Serial.println(F(" Play Finished!"));
nextSong();
break;
case DFPlayerError:
Serial.print(F("DFPlayerError:"));
switch (value) {
case Busy:
Serial.println(F("Card not found"));
break;
case Sleeping:
Serial.println(F("Sleeping"));
break;
case SerialWrongStack:
Serial.println(F("Get Wrong Stack"));
break;
case CheckSumNotMatch:
Serial.println(F("Check Sum Not Match"));
break;
case FileIndexOut:
Serial.println(F("File Index Out of Bound"));
delay(1000);
song_index = 0;
myDFPlayer.play(song_index + 1);
break;
case FileMismatch:
Serial.println(F("Cannot Find File"));
break;
case Advertise:
Serial.println(F("In Advertise"));
break;
default:
break;
}
break;
default:
break;
}
}

// === DISPLAY ===
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>

#define PIN_VU A0
#define VU_SCALE 1
#define VU_WARN 700
#define VU_CRIT 900
#define VU_SLOWDOWN 10
#define VU_ATTACK 1

/* Uncomment the initialize the I2C address , uncomment only one, If you get a totally blank screen try the other*/
#define i2c_Address 0x3c //initialize with the I2C addr 0x3C Typically eBay OLED's
//#define i2c_Address 0x3d //initialize with the I2C addr 0x3D Typically Adafruit OLED's

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // QT-PY / XIAO
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup_display() {
//Serial.begin(9600);

delay(250); // wait for the OLED to power up
display.begin(i2c_Address, true); // Address 0x3C default
display.setContrast (0); // dim display
// Clear the buffer.
display.clearDisplay();
display.display();

display.setTextSize(2);
display.setTextColor(SH110X_WHITE);
display.setCursor(0, 20);
display.println(" UNIVERSUM");
display.setTextSize(1);
display.display();
delay(500);
display.clearDisplay();
}

void loop_display()
{
display.clearDisplay();

display.setTextSize(2);
display.setTextColor(SH110X_WHITE);
display.setCursor(35, 20);
unsigned long song_time = (millis() - song_started) / 1000;
char song_time_text[16];
sprintf(song_time_text, "%02d", song_time / 60);
display.print(song_time_text);
display.print(":");
sprintf(song_time_text, "%02d", song_time % 60);
display.println(song_time_text);

display.setTextSize(1);
display.setCursor(37, 50);
char song_index_text[16];
sprintf(song_index_text, "%03d", song_index+1);
display.print(song_index_text);
display.print(" / ");
char song_count_text[16];
sprintf(song_count_text, "%03d", files_max);
display.print(song_count_text);
display.display();
}

// === MAIN ===

void setup() {
setup_player();
setup_display();
}

void loop() {
loop_player();
loop_display();
}

Loading…
Cancel
Save