MP3 Cassette Player

by danieljsherwood in Circuits > Arduino

19 Views, 0 Favorites, 0 Comments

MP3 Cassette Player

IMG_1690.jpg

This project is silly and maybe a little bit overkill, but fun nonetheless. I had this idea when reminiscing about how gifting audio to someone just doesn't have the same romance anymore. You can make a Spotify playlist, or record some MP3s and send a drop box link, but it doesn't have the same old-school charm that physical media do. A mixtape or CD has a lot more emotional weight in my opinion.

I wanted to create something that can be used to make playing music or audio a more physical experience. I wanted to use the cassette format as there would be room for artwork and track lists on the outside of the case.

The issue is that the average cassette tape only contains 3 hours of audio, and I wanted to record audio books, which average at more than 7 hours. Rather than record onto 2 or 3 tapes per audiobook, I decided to use an Arduino to hack a cassette player so that it could "play" tapes while storing the audio on an SD card inside the tape player. The tape is used to trigger the playback, but the audio was stored in the tape player instead of in the tape.

So the experience is exactly like using a tape player, but without the limitations of recording onto a tape. I was also able to save money by using a 'spares or repairs' tape player, as all the internals were replaced using my own circuit.

See more on my website. This project is inspired by Juuke and Old school cassette player with new school cassette.

Functions


Audio storage

Instead of storing audio on the tapes, it is stored as MP3 files in an SD card in the tape player.

Reading tapes

When the tape is in the deck, an NFC sticker lines up with a reader in the tape player. This tells the tape player which track numbers correspond to the current tape.

Playback

The play, pause, forward and rewind functions work slightly differently to a traditional tape player:

  1. Playback starts when the play button is pressed, and continues until you press stop, which releases the play button.
  2. Pressing the pause button pauses and resumes playback.
  3. Forward: short press → next song/chapter, long press → skip forward 15s.
  4. Rewind: short press → previous song/chapter, long press → skip back 15s.
  5. If you skip past the last track in a tape, it rewinds to the first track, and vice versa.

Power management and sleep

There is a slider on the top of the tape deck which selects between 3 modes:

  1. OFF: tape player is off – entire circuit is disconnected from power supply.
  2. ON: tape player can be turned on by holding START button for 2 seconds.
  3. SLEEP: player will go to sleep (stop playback) if you don’t touch it for 10 mins.

Auto power-down

As a safety feature, the power supply is automatically disconnected 10 mins after playback stops. To reconnect, you will need to slide to OFF, and back to ON, then hold down START for 2 seconds. This is a bit unwieldy, but I built it in to avoid the player drawing power for hours or days at a time if you forget to turn it off.

Supplies

  1. Old cassette player (does not need to work)
  2. Arduino Nano - £5.49
  3. Cable for Arduino (Mini USB B) - £1.49
  4. DY-SV5W - £7.90
  5. NTAG213 NFC stickers x20 - £4.45
  6. Micro SD card - £6.28
  7. PN532 RFID reader - £6.06
  8. Matrix board (2.54mm pitch) - £2.19
  9. Leaf switches - £7.49 for 10
  10. 5V Power supply and barrel jack
  11. 4.7kΩ resistor
  12. Diode (e.g. 1N4007) - £1.40 for 100
  13. Transistor (e.g. BC547) - £3 for 25
  14. Relay (e.g. RY9W-K) - £2.68

Build Prototype Circuit

Schematic.png

First, set up all the components using a breadboard, so that you can test them before soldering anything. The circuit includes the following components:

  1. Power supply: 5V power supply and jack, controlled by the power management circuit (see below) using an existing slider.
  2. Arduino nano: Controls the tape player.
  3. MP3 player: Playback and audio amplification is managed by the DY-SV5W MP3 player. Arduino communicates with MP3 player over UART using pins D7 & D8.
  4. Speakers: The MP3 player powers the existing speaker of the tape player, but can power speakers up to 5W.
  5. NFC reader: NFC stickers on the tapes are read by PN532 NFC reader. Arduino communicates with the NFC reader over SPI using pins D9-D13.
  6. Buttons: Leaf switches are glued inside the cassette player, so that they are closed when the respective mechanical button is pressed (for play, pause, forward, and previous). They are wired between pins D2 – D6 and ground.
  7. Volume dial: The existing dial is used, which is a voltage divider. The voltage is read at pin A0.

Power management circuit

The circuit that cuts power works as follows:

  1. A relay connects the entire circuit to power. So if the relay is off, the power supply is disconnected.
  2. A transistor is used to control the relay, so that the relay is on if digital output pin A1 is high.
  3. Sliding to OFF drains pin A1 to ground, so the relay turns off and the supply is disconnected.
  4. Sliding back to ON disconnects pin A1 from the ground, but it is still low since the Arduino is OFF.
  5. So, pressing START temporarily bridges the relay and reconnects the power supply. You just have to hold START for enough time for the Arduino to start up and set pin A1 to high.
  6. Then pin A1 will hold the relay on and keep the power supply connected.
  7. The Arduino can switch off the circuit by setting pin A1 to low.

MP3 player notes

  1. The UART mode is used. Therefore the configuration switches must be set to OFF, OFF, ON.
  2. The voltage supply is 5V, and it seems okay to connect the board to 5V logic.
  3. A 9600 baud SoftwareSerial connection is used, since the Arduino Nano only has one physical serial port, and this is used for monitoring during debugging.
  4. I initially ordered a DFPlayer Mini on Ebay, but the chip I used was faulty (it would 'randomly' go into an error state and response to player status and current track requests would be invalid). Reading online, this is quite common and it is difficult to guarantee that you get an authentic chip. After watching this video, I concluded that it was a better idea to start from scratch using the DY-SV5W. The implementation was straightforward and I had no issues with reliability after this. The video is well worth a watch if you are unsure what module to use for your project.

NFC reader notes

  1. The SPI mode is used, since this will not interfere with the software serial communication of the DY-SV5W. Therefore, the configuration switches are set to OFF, ON.
  2. The Vcc must be between 3.3V and 5V. The chip itself uses 3.3V transistor logic, but there are 100 Ohm resistors in series with the pins on the breakout board, therefore, they may be connected directly to 5V logic.

Write Audio Files to SD Card

It’s important to store the audio for songs or audio books in an organised structure on the SD card, so that the location can be encoded on the NFC stickers and the player knows which tracks to play.

Storing tracks on SD card

I wanted to allow skipping forward and backwards by 15s, but the MP3 player doesn’t have this function built in. Instead, I split each song or audio book chapter into multiple MP3 files of ~15s, and placed all the files for a side of the tape in one folder. A folder may therefore contain audio files for several songs or audio book chapters.

Naming has to be consistent:

  1. Folders numbered sequentially from 000 to 999
  2. Files numbered sequentially from 0000 to 3843 (continued across folders rather than resetting for each new folder)
  3. Final track in each folder should be 60s silent audio. This gives the cassette player time to recognise that the chapter has ended.

Write Data to NFC Tags

IMG_1684.jpg
IMG_1685.jpg

Information to be stored on NFC tags

Each tape side represents a sequence of songs or chapters. Information about where to find the relevant files is contained within the NFC sticker on that side of each tape. For each side, the following information is encoded:

  1. Folder number where the files for that side are stored.
  2. The track number for the start of each song or chapter.
  3. The final track number of the final song or chapter.

For example, a side of the tape may represent 3 songs:

  1. All files are stored in folder 003 on the root of the SD card.
  2. The filename for the first track of the first song is ‘0011.mp3’.
  3. The filenames for the first track of the subsequent songs are ‘0014.mp3’ and ‘0017.mp3’
  4. The filename for the final track of the final song is ‘0019.mp3’

In this example, the information to be stored on the sticker is an array: 003,0011,0014,0017,0019

Even in this short example, there is a lot of information to be encoded. Reading long arrays off an NFC can cause loss or errors. Therefore, to save space each number in the array is encoded as a pair of alphanumeric characters.

E.g. the above becomes 010B0E0H0J, which is much shorter.

Encoding NFC payload

  1. For each number in the original array, divide it first by 62.
  2. The rounded down answer becomes the first digit. Convert each number from base 10 to base 62 using this mapping:
  3. 0-9 → 0-9, 10-35 → A-Z, 36-61 → a-z
  4. The remainder becomes the second digit. Convert each number from base 10 to base 62.
  5. Repeat for all numbers in the array.

For example, if the unencoded number is 2738:

  1. 2738 / 62 = 44 remainder 10.
  2. First digit is 44, which when converted to base 62 is i.
  3. Second digit is 10, which when converted to base 62 is A.
  4. So 2738 encoded is iA.

I built the following Excel to speed up the encoding process.

Writing to NFC tags

On iOS, the free ‘NFC Tools’ app on the app store allows writing plaintext to NFC stickers. Copy the encoded string, and write it to the tag using the ‘text’ option. This means the encoded string is added to the payload as plaintext.

Reading NFC

When playback is started, the Arduino reads the NFC tag payload, extracts the folder number and track numbers, and stores them in dynamic variables for the duration of playback.

Continuous playback

The MP3 player will loop through tracks within a single folder, since setCycleMode() is set to DY::PlayMode::SequenceDir during start-up. This is the reason for the 60s blank track at the end of each folder, to give the tape player time to recognise that the end of the side has been reached, and stop playback.

Starting playback from where left off

When playback is stopped, the current track number for a tape side will be saved in EEPROM memory. So at a later date, if the tape is played again, playback will start from the same place.

Test Code

EXCEL_VL0K5A3NUv.png

The tape player has to keep track of several things at the same time, which is challenging on the Arduino, so I wrote a state machine. The code is on GitHub along with supporting libraries.

Startup function

  1. Starts NFC reader
  2. Starts MP3 player
  3. Sets volume
  4. Sets playback mode to loop

Main loop

The main code loop takes a very short time to run since each function called within it is non-blocking. This means these functions do not contain delays, instead relying on timers and conditional statements to ensure that actions are performed when they need to be. If there is no action to take on this iteration, the function hands back control to the loop using a break. During each iteration of the loop, the following non-blocking functions are called:

  1. Check and process state of buttons and the volume dial.
  2. Serial communications function.
  3. State machine
  4. Sleep timer
  5. Track saving function
  6. Volume manager

Check and handle buttons

The state of the buttons is read and interpreted as follows:

  1. Stop (0) - no buttons pressed
  2. Play (1) - play button pressed
  3. Pause (2) - play and pause button pressed
  4. Skip 15s (3) - play and long press of next button
  5. Next song/chapter (4) - play and short press of next button
  6. Rewind 15s (5) - play and long press of back button
  7. Previous song/chapter (6) - play and short press of back button

External messages

To avoid overloading the SPI and UART feeds, a message is sent to the NFC reader and MP3 every 300ms, managed using a queue system and a timer.

E.g. if the state machine wishes to initiate playback, it can queue a ‘play’ message to the MP3 player.

The external messages function is called during every loop cycle, but only acts every 300ms. It cycles through 3 steps:

  1. Get player status – sends a message to MP3 player demanding the current playback status.
  2. Get playing track – sends a message to MP3 player demanding the current track number.
  3. Send queued message – at this point, it checks if there are any queued messages from other functions, and sends them. Possible messages are:
  4. ‘play X track from X folder’, ‘pause’, ‘resume’, ‘stop’, ‘read NFC’

Since the state machine is called far more often than the message function, the message queue is cleared at the start of each loop cycle and set again by the state machine.

State machine

The state machine runs once per loop cycle. It checks the status of variables and queues messages to the MP3 player and NFC reader. It is designed to be watertight so that an action must be complete before any other can be taken.

E.g. if the pause button is pressed, the state machine enters the ‘pause’ state. In the pause state, during each loop cycle, it queues a pause message for the message function to pick up. During each loop cycle it also checks the player status. This will repeat over many loop cycles until the message function has had time to send the ‘pause’ message to the MP3 player, and then to query the player status and receive ‘paused’ as a response. The state machine will only enter the ‘paused’ state once the player status is ‘paused’. In the paused state, it no longer queues a ‘pause’ message during each loop cycle.

There are 10 possible states:

  1. Stop (0)
  2. Stopped (1)
  3. readNFC (2)
  4. Play (3)
  5. Playing (4)
  6. Pause (5)
  7. Paused (6)
  8. Resume (7)
  9. Sleep (8)
  10. Sleeping (9)

Other functions

There are several simple functions:

  1. Volume: sends a volume message to the MP3 player if the volume dial has moved.
  2. Saving track number: Track number for the tape side is saved when playback is stopped, and at regular intervals during playback. EEPROM memory keeps its value even when the Arduino is powered off, but has limited ~10,000 writes before it degrades and can no longer be written to. To prevent unnecessary writing of EEPROM memory, the EEPROM.update() function was used, so the write will only occur if value to be written is different from value currently stored at that EEPROM address.

Solder and Assemble Cassette Player

Switches - Copy.png
IMG_1593.JPG

Before soldering the circuit, play around with the location for all the components within the cassette player, especially the NFC reader as it has to line up with the tape when the deck is closed. I found that by removing the mechanism behind the tape spools, I was able to tuck the NFC reader into a slot behind the tape deck.

Then add switches for each of the buttons, so that they are closed when the button is pressed. I used leaf switches for this. The soldering was straightforward - just make sure that any wires do not interfere with moving parts.