commit b7d6c8ac2dc527b815d4dd61902a8530e90b698c Author: Dejvino Date: Sat Jun 4 19:15:17 2022 +0200 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..c38b6bb --- /dev/null +++ b/README.md @@ -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. + diff --git a/player.jpg b/player.jpg new file mode 100755 index 0000000..8955c6b Binary files /dev/null and b/player.jpg differ diff --git a/tapedeck-player.ino b/tapedeck-player.ino new file mode 100755 index 0000000..df669b3 --- /dev/null +++ b/tapedeck-player.ino @@ -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 + + + *************************************************** + 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 for details. + All above must be included in any redistribution + ****************************************************/ + +/***********Notice and Trouble shooting*************** + 1.Connection and Diagram can be found here + + 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 +#include +#include +#include + +#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(); +}