MP3 Player With RFID

by AZDelivery in Circuits > Arduino

242 Views, 3 Favorites, 0 Comments

MP3 Player With RFID

1111.png

It's been a while that I have one Christmas raffle with RFID tags programmed for the ESP32. The contribution with the Speech output of measurement results back. I recorded the scraps of language with my cell phone and transferred to an SD card. Now you could of course also save music pieces on the SD card and call up via RFID tags-an MP3 player with expansion potential is ready. I tried various approaches for programming. What initially looked promising and interesting, an approach with threading, finally had to be abandoned in favor of conventional programming By grinding because the performance did not meet my ideas. I am now introducing this solution to you with the new post from the series.


Strictly speaking, I came up with the idea of ​​this post through the articles on the subject of serial interfaces on ESP32/ESP8266 dem Raspberry Pi Pico and that PC. I wanted to show some other applications on the RS232 ports. Well, the DFPlayer Mini, which is used here, is controlled via the serial interface. And because a bidirectional data exchange is used, only one ESP32 or a Raspberry Pi Pico as a controller. A PC would also be able to parlice directly with a DFPlayer Mini via an RS232 TTL converter, but I leave that out. We have already arrived at the hardware list.


The assembly on two mini-breakboards looks through the many jumper cables bird game. After all, four interfaces are used, SPI (RFID reader), I2C (OLED display), RS232 (DFPlayer Mini) and ADC (Poti).

Supplies

MP3 Player With RFID

Micropython on the ESP32 and ESP8266

Today:


illustration 1: MP3 player - structure with SH1106

There are a few things to consider. The DFPlayer Mini must be supplied with 5V. Therefore, impulses with a level of 5V are also on his PIN TX, as is the busy pin. Because the ESP32 can only tolerate 3.3V to its GPIOs, the levels must be reduced By voltage divisors. I chose 1kΩ and 2.2kΩ. You can also take other values ​​that are in a ratio of 1: 2. Conversely, the DFPlayer at the RX input copes well with the 3.3V signals from the ESP32.

If the speakers are placed on GND with the black line, the 5V circle flows into electricity of approx. 450mA! This is because the connections SP1 and SP2 lead 5V level. Therefore, only connect the two speakers to SP1 and SP2, not to GND. Even if you only use one speaker, its lines belong on SP1 and SP2 (in Figure 2 yellow). Otherwise you would have to connect the speakers via an ELKO.

illustration 2: DFPlayer Mini with 4 GB SD card

The ADC input on the ESP32 can process tensions up to 2.4V. However, the potentiometer is 3.3V. Therefore, I reduce the offset voltage By counting a resistance of 2.2kΩ. So that the potentiometer can be put on the Breadboard, I have provided the connections with a 5-pin pen strip.

illustration 3: Poti with pen strip for the Bread Board Montage

You have to be particularly careful with the displays mentioned. The connections for SDA and SCK agree, but the pins for +VCC and GND with the 96 "module (pen strip above) are exchanged against each other in the 1.3" module (pen bar below). I used a 1.3 "module in the circuit of Figure 4.

illustration 4: SH1106 in operation

illustration 5: MP3 player - circuit

For autonomous operation, I have provided a battery owner for a Li-ion battery of the 16850. It can be connected directly to the ESP32 with the enclosed cable, which then takes over the supply of the other modules.

illustration 6: Battery owner 18650

The connection plan may be helpful for placing the circuit together.

illustration 7: Connecting plan

An ESP32 takes over the chief position. For its use, two Breadboards are required, which are connected to a power rail in the middle, so that you can get there with the intervals of the pin rows and cable can also be inserted on the edge.

The little brother of the ESP32, the ESP8266, is unsuitable for our purpose, because it cannot offer a second, full uart interface, and we need it to control the DFPlayer.

Uart is acronym for Universal Asynchronous Receiver / Transmitter. We also talk to our controller from the terminal in Thonny about such an interface. The ESP32 /ESP8266 is connected to the PC via the USB cable and a USB-RS232-TTL converter on the controllerboard passes on data traffic to the UART0 interface of the controller. This in turn feeds the data stream sys.stdin. The input command is served from this file object. sys.stdout sends output data via UART0 to Thonny's terminal. Therefore, we cannot use UART0 to send commands to the mini-MP3 player and receive data from it. A UART interface can only have one counterpart. The ESP8266 has a second UART interface, but that is actually only half because only one TXD line (transmission line) and no RXD line (reception management) is available. But there would also be a Raspberry Pi Pico (W), which has two freely available Uarts on offer. The program is the same, only the pin assignment would be different.

The Software

For flashing and the programming of the ESP32:

Thonny or

µpycraft


Used firmware for the ESP32:

Micropython firmware Overview

V1.18 (2022-01-17). (tested) or

V1.22.1 (2024-01-05).(Latest 2024-01-24)

The micropython programs for the project:

SSD1306.PY Hardware driver for the 0.96 "-OLED display and

oled.py API for OLED display or

sh1106.py Hardware driver for the 1.3 "" OLED display and

oled_sh1106.py API for OLED display

dfplayer.py Driver module for the DFPlayer Mini

mfrc522.py Driver module for the RFID reader

mp3player.py Operating program

timeout.py Softwaretimer not blocking

Micropython - Language - Modules and Programs

To install Thonny you will find one here Detailed instructions (English version). There is also a description of how that Micropython firmware (As of 01/25/2024) on the ESP chip burned becomes.

Micropython is an interpreter language. The main difference to the Arduino IDE, where you always flash entire programs, is that you only have to flash the Micropython firmware once on the ESP32 so that the controller understands micropython instructions. You can use Thonny, µpycraft or ESPTOOL.PY. For Thonny I have the process here described.

As soon as the firmware has flashed, you can easily talk to your controller in a dialogue, test individual commands and see the answer immediately without having to compile and transmit an entire program beforehand. That is exactly what bothers me on the Arduino IDE. You simply save an enormous time if you can check simple tests of the syntax and hardware to trying out and refining functions and entire program parts via the command line before knitting a program from it. For this purpose, I always like to create small test programs. As a kind of macro, they summarize recurring commands. Whole applications then develop from such program fragments.

Autostart

If the program is to start autonomously By switching on the controller, copy the program text into a newly created blank tile. Save this file under boot.py in WorkSpace and upload it to the ESP chip. The program starts automatically the next time the reset or switching on.

Test programs

Programs from the current editor window in the Thonny-IDE are started manually via the F5 button. This can be done faster than the mouse click on the start button, or via the menu run. Only the modules used in the program must be in the flash of the ESP32.

In between, Arduino id again?

Should you later use the controller together with the Arduino IDE, just flash the program in the usual way. However, the ESP32/ESP8266 then forgot that it has ever spoken Micropython. Conversely, any espressif chip that contains a compiled program from the Arduino IDE or AT-Firmware or Lua or ... can be easily provided with the micropython firmware. The process is always like here described.


The Program

An overview

The selection of the playlists is made in this project By RFID The selection of the playlists is made in this project By data.The selection of the playlists is made in this project By reading the ID on the map. However, this clearly goes beyond the scope of this article and could perhaps be the subject of another blog sequence. Based on the ID, an 8-digit hexadecimal number, the ESP32 can instruct the DFPlayer Mini to play the MP3 files from a folder on the SD card. Order name and number are displayed on the Display together with the title number. User instructions are also shown in the Display.

The RFID reader

Due to the SPI bus, the wiring is also more complex than the I2C bus with its 2 lines alone. SPI bus devices do not have a hardware device address, but they have a chip-select connection (CS), which must be on low if the device is to be addressed. The data transfer also works a little differently, it is always sent and received at the same time. Clarifying the closer procedure is not necessary here, because the class Mfrc522 does that for us. We will only inform the constructor the follow -up assignments and the transmission speed. The transfer works with a fleet 3.2MHz. For comparison, I2C works to up to 400kHz.

Our own function readuid() reads the clear identification of a card and returns it as a decimal number and as a hexadecimal dial. The cards are requested via the OLED display. So that the function does not block the entire process, a timeout ensures an orderly withdrawal. In this case, the value None is returned instead of the card ID.

So that the playlist cards come into play, we need a master card. To do this, we take any card or a chip out of the stack, read the ID and thus prove the variable with the decimal value right at the start of the program, here:

Masteride = 4217116188.

At the first start, the ESP32 notes that there is still no file with the Playlist cards data and requires the master card. After this has been recognized, a playlist card is requested. After reading the ID, it is written in the file and the master card is requested again. Reading is continued until the last playlist card. If no playlist card is offered for 10 seconds after the request for the master card, the detection process is ended and the main program starts. To start completely from the front, we can slavecards.txt Delete with the Playlist card IDs via the Thonny-Console. The file can of course also be edited in the Thonny editor window.


Dfplayer mini

Communication with the DF player always happens in the same way. Blocks of 10 Bytes are sent and received. The structure of such a block looks like this. The method broadcommand() of the class Dfplayer takes care of the correct transmission.

Start -Byte 0x7e

Version number 0xff

Length of the Payoad 0x06 (From version number to parameter 2 incl.)

Command

Feedback 0x00 (no) or 0x01 (yes)

Parameter high

Parameter low

Checks high

Checks Low

EndeByte 0xef

The command codes can be found in Data sheet find. I used this to make the most important codes in methods of the module dfplayer.py to implement. We only need a few of them for the current project. But take a look at the file pfplayer.py (Rev 1.3 of January 24, 2024)

The processing of sound files is with the freewaretool Audacity It is also very easy to recode sound file formats to MP3. A greater effort means to change the file names in such a way that they meet the requirements of DFPlayer Mini. The files are located in folders that are named in two digits. These are the playlists.

illustration 8: Folder view of the SD card

The file names can keep their old name, but must be provided with a Prefix consisting of a three -digit decimal number.

illustration 9: File name with Prefix

The manual renaming may still tackle for a few files, with more than ten it becomes very annoying. That's why I did that with another free tool Advanced Renamer. The installation file invite you from Chip server down.

illustration 10: Download Advanced Renamer

For installation, simply follow the assistant. The best way to collect the files selected for a folder on the SD-Card is in a work directory or on the SD card. Mark all files in the folder and pull the package onto the Renamer-Window. As Batch method choose Add and then place in the filter window 1: Add the properties as in Figure 11.

illustration 11: Filter setting in Advaced Renamer

Every change in this window has an immediate effect on the preview. To take over, click on the top right Batch start. That's it.

illustration 12: Source names and result


MP3 player

After all the preparations, it is time for the coding of the project. So let's start. As always, the program begins with the import business. It will be the best if you mp3player.py Open in Thonny in the editor window, then you can read an overview there while we go through the way of working here.

# mp3player.py

# workes with RC522 @ 13,56MHz

from dfplayer import DFPlayer

import mfrc522

from machine import Pin, SoftI2C, SPI, ADC, reset

# from oled import OLED # Vorsicht: GND Vcc SCK SDA

from oled_SH1106 import OLED # Vorsicht: Vdd GND SCK SDA

from time import sleep

from sys import exit

from timeout import *

From dfplayer.py we import the class DFPlayer while from mfrc522.py everything is imported. These two files have to be shoveled into the flash of the ESP32. Both mark both in the film manager of Thonny, right -click, Upload to /.

From the module machine come the classes for the operation of the interfaces. Depending on which display module is used, one of the following two lines must be activated or commented on.

For short, passive breaks we get sleep() From the Time module. We secure a clean program exit with the function exit() out of sys. From the module time-out we get everything (*) In our Scopewhich is needed for non -blocking softwaretimers. The stern does not apply to the module when calling a method from this module.

Without a star, for example:

Timeout.timeoutms (1000)

With star:

Timeoutms (1000)

Next, we instance our objects in corresponding packages. We start with the ADC object that we need to read in the tension from the potentiometer. We will control the volume of the DFPlayer Mini.

vol=ADC(Pin(36))
vol.atten(vol.ATTN_11DB)
vol.width(vol.WIDTH_10BIT)

The grinding contact of the potent is connected to GPIO36. If the weaker becomes 11 dB and thus reach a scan of up to 2.4V. We set up the resolution to 10-bit.

RX_Pin = 16
TX_Pin = 17
busyPin = 27
df=DFPlayer(busyPinNbr=busyPin,
txd=TX_Pin,
rxd=RX_Pin,
volume=15)
sleep(2) # wait for player initializing
numOfTitles=df.getNumberOfFiles()
print("Titel gesamt:",numOfTitles)

The DFPlayer class sets off the UART1 of the ESP32 itself. The constructor only needs the numbers of the GPIOs on them busy-Pin, RX-Pin and tx-Pin are connected. By default,Pin are connected. By asking the total number of files on the SD card.

spi=SPI(2,baudrate=3200000)
# cs=sda
rdr = mfrc522.MFRC522(spi, cs=5, baudrate=3200000)

The Spi2 hardware interface occupies the GPIOS 18 (SCK), 23 (Mosi) and 19 (Miso), also 5 (CS). The chip-select connection on the RFID board is named with SDA. We define the baud rate at 3200000Hz.

i2c=SoftI2C(scl=Pin(22),sda=Pin(21))
d=OLED(i2c)

For the display, we instantiate an I2C object that we give the constructor of the OLED object on the way.

The master card is the RFID day with which the Playlist tags are announced to the system. We determine the numerical value as soon as the program has been completed and then enter it at this point.

MasterID=4217116188 # 0XFB5C161C

The list list Contains the names of the playlists.

listen=[
"COUNTRY",
"BLACKMOORSNIGHT",
"YOUTUBE",
"SCHLAGER",
"HITS",
"VON CD"
"OLDIES"
]


The declarations of some functions follow. readuid() reads the identification number of the tag. We hand over the OLED Display object, a string for the card type and a period of time in milliseconds, after the end of which the function will in any case leave. The variable song is declared globally so that the value assigned in the function is available in the main program. I do not go into all editions, both in the Display and in ReplaB, they serve partly to inform the user and troubleshooting during development and explain By self -made texts.

The non -blocking software timer reap is through the method Timeoutms() set. In Timeoutms() the function is hidden compare(), a so -called Closure. Timeoutms() gives a reference compare() back that we the identifier reap assign. Different than with sleep() other instructions can be executed during the timer. So the While loop is run through until the function reap(), alias compare(), True returns. The background to this procedure can be found in the document Closures on decorators read.

ef readUID(display,kartentyp,timeout):
global song
display.clearFT(0,1,15,2,False)
display.writeAt("PUT ON",0,1,False)
display.writeAt(kartentyp,0,2)
readTimeOut=TimeOutMs(timeout)
while not readTimeOut():
(stat, tag_type) = rdr.request(rdr.REQIDL)
if stat == rdr.OK:
(stat, raw_uid) = rdr.anticoll()
if stat == rdr.OK:
display.clearFT(0,3,15,3)
display.writeAt("Card OK",0,3)
userID=0
for i in range(4):
userID=(userID<<8) | raw_uid[i]
userIDS="{:#X}".format(userID)
display.writeAt(userIDS+" ",0,4)
sleep(2)
df.reset()
song=0
return userID,userIDS
return None

With RDR.MEQUEST() Let's put an input process. If the request was successful, we will pick up the ID in the next step. If this step was also successful, let's get to the evaluation. From MFRC522 we get one Bytes-Plet back, which we convert into a 32-bit number in four steps. The previous result in useride is pushed to the left By 8 is pushed to the left By episode ornamented. We then transform the overall result into a hexadecimal string, which will usually be 8 digits.

Two seconds for reading the display, then we put the DFPlayer Mini back and the song counter to 0, because a new playlist should also be started with reading the ID. Instead of the ID in number and string form, None is returned if the reading attempt was unsuccessful or the time has just expired.

If you have entered the program up to this point, you can already start to read the ID of the master card. Alternatively, you can mp3player.py Download and enter the following instructions in line 73. Then save and start the program in the editor window.

Exit ()


According to which the Repl-Prompt has been released, enter the following instructions. Now keep your master card to the reader. The output Numerical value Wear above as Masteride a.

>>> Readuid (D, "Test", 6000)

0xFB5C161C

(4217116188, '0xfb5c161c')

This master card uses the next function adduid() that is responsible for registering the playlist cards. It is called up By the main program at the first start if there is no file with the name yet slavecards.txt in the File System of the ESP32. But can also be handed By hand adduid() are called up to create the file and/or to register other playlist tags.

First we request the master card and read the ID. M receives that Tupel From the decimal value and the hex string, if the action was successful, otherwise None. We crumble the tuel By unzipping into the number and the string and offset on the master ID. There remain three seconds to put on a new playlist card as soon as the reading instruction has been made.

def addUID(display):
display.clearAll()
m=readUID(display,"Master",3000)
print("Master",m)
if m is not None:
mid,mids= m
if mid==MasterID:
print("Master OK")
u=readUID(display,"Slavecard",3000)
if u is not None:
uid,uids=u
if uid is not None and uid != MasterID:
with open("slavecards.txt","a") as f:
f.write("{}\n".format(uids))
display.writeAt("New slave written",0,4)
display.writeAt("{}".format(uids),0,5)
sleep(5)
return True
Else:
display.writeAt("ERROR!!!",0,3)
display.writeAt("Card not added!",0,4)
return False
Else:
display.writeAt("ERROR!!!",0,3)
display.writeAt("Not mastercard",0,4)
sleep(3)
return False

As before MSo now contains U The ID tupel when the reading process was successful. The card is registered if uid not None is and the card was not the master card. With the wither-A file object is created and for attaching text lines under the identifier F open. We also write the hex string of the read in with an attached line end sign "\ n" = 0x0a. By leaving the wither-Blocks the file is automatically closed. So we don't have to take care of this necessary measure. After five seconds of reading time for the Display message, the function gives True back. Two possible errors must be treated, a reading error from the RC522 and an incorrect master card.


At the start of the program will Reading tags () Called and after a file slavecards.txt In the root directory of the ESP32 file system. The empty list the day Should record the hex strings of the cards. Can the wither-Instructions do not open the file because it is not yet available, remains the day Empty, and the Else part of the IF construct returns None. If the file exists, then line By line is read in, the line end sign is removed and the string attached to the list until the loop detects the end of the file. In this case, the list is not empty and is returned.

def readTags():
tags=[]
with open("slavecards.txt","r") as f:
for line in f:
tags.append(line.strip("\n"))
if tags:
return tags
Else:
return None

Each folder on the SD card can contain different number of music titles. We have to know the number to do it in one for-to be able to play loop. Get() Determines the number of titles in all folders and puts it in the list filecount away. We start with the empty list and let ourselves be whispered with the number of folders. In order to address every folder, we alludes to the first title in the For-loop in it briefly, and so that you are not irritated By cracking and other noises, we switch the volume to 0 beforehand.

def getNumberOfTitels():
fileCount=[]
nof=df.getNumberOfFolders()
df.volume(0)
sleep(0.2)
for t in range(nof):
df.play(t,0)
sleep(0.2)
df.stop()
sleep(0.2)
n=df.getNumberOfFilesInFolder()
d.writeAt("Track:{}".format(t),0,4,False)
d.writeAt("Songs:{}".format(n),0,5)
fileCount.append(n)
df.reset()
volume=int(vol.read()*100/1024)
df.volume(volume)
sleep(0.2)
return fileCount

In the for loop, we hang the number of titles on the list at the back. After reset the DFPlayer, we restore the volume to the value that the potentiometer specifies. The list of title numbers is returned.

As the last function we declare player(). During the runtime of the playback loop, a constant reading of playlist cards takes place. A new card breaks off the playlist just played. The variable acard In this case, gives the empty string back to the main program. So that this is possible from the function, we declare the variable globally. So that the player starts properly, we stop it. Then it goes into the for loop. The player behaves stubborn here and skipped the first title, so I leave the loop with the running index S Start at -1. is playing() Should now False return what we check right away and then the title S in the folder T let play.

def player(d,t,nof):
global acard
df.stop()
for s in range(-1,nof):
if not df.isPlaying():
df.play(t,s)
d.clearFT(0,5,15,5,False)
d.writeAt("Titel: {}".format(s),0,5)
while df.isPlaying():
volume=int(vol.read()*100/1024)
df.volume(volume)
u=readUID(d,"TAG TO STOP",100)
if u is not None:
d.clearFT(0,3,15)
D.Cleart (0,3,15)
df.stop()
acard=""
return
d.clearFT(0,3,15)
df.stop()

While the title is running, there is a lot to do. The potentiometer must be queried and the value to the DFPlayer has been passed on. Then we give a reading order to the RC522 with the message for a potential demolition. Comes after 100 milliseconds = 0.1 seconds None Back, it goes into the next round of the While loop. But if a new card was registered, we stop the DFPlayer and prepare with acard = "" playing a new playlist. If the playlist has been completely played, we delete the lower part of the display and give another stop command to the DFPlayer. Sometimes this disgusted all kinds of commands. This resulted in various disappointing moments when developing the program, which always made it necessary to open new solutions.

Some variables have to be initialized for the main program.

cards=[] # Liste der Karten-IDs leeren
acard="" # aktuelle Karten-ID
ncard="" # neue Karten-ID
song=0 # Stets mit Song 0 starten

The first action is now the reading of the card IDs if the file slavecards.txt gives. When the program starts for the first time, there is no sign here. Therefore we secure the call from reap() with try and except away. We create the file in the Except block. So we conjure up a non -blocking software timer allread() By means of Timeoutms() from the retort. While the timer runs, we call cyclical adduid() on. We have already discussed the way of working this function. When the routine True Returned, a card was registered and the timer is restarted. If there is no further card, we simply wait for the timer and get the IDS into the list in a second attempt cards.

try:
cards=readTags()
except OSError as e:
allRead=TimeOutMs(10000)
while not allRead():
if addUID(d):
allRead=TimeOutMs(10000)
d.clearFT(0,3,15,4,False)
d.writeAt(" ALL CARDS READ",0,3)
cards=readTags()

If the list is not empty, it goes into the main loop that is constantly going through, even while a title is played.

IF Cards:
d.writeAt("***MP3-PLAYER***",0,0,False)
d.writeAt("PUT ON MP3-TAG",0,1,False)
d.writeAt("Hole die Anzahl",0,2,False)
d.writeAt("Songs/Folder",0,3)
filesInFolder=getNumberOfTitels()
d.clearFT(0,2,15)
df.stop()
while 1:
uid=readUID(d,"PLAYLIST-TAG",1000)
if uid is not None:
ncard=uid[1]
volume=int(vol.read()*100/1024)
df.volume(volume)

We try to read a card. If that worked, we show the hex string of the variables Ncard To and ask the potentiometer next.

The playing process of a new playlist is started when the content of acard not with that of Ncard matches. In this case will be acard already brought up to date.

if acard != ncard:
acard=ncard

Then we check whether the ID in the list cards is included. If so, we determine the index of the entry and have found the number of the folder on the SD card. We hand over the organization to play the playlist player(). She gets the reference to the OLED object, the folder number and the number of titles on the way.

if acard in cards:
d.clearFT(0,2,15,5, False)
t=cards.index(acard) # Ordnernummer
d.writeAt("PLAYLIST {}".format(t),0,3,False)
d.writeAt(listen[t],0,4)
player(d,t,filesInFolder[t])

The card ID was not in cards Found, we get an error message before going to the next round of the Mainloop.

For an autonomous company, without connection to the PC, the program must be under the name main.py are uploaded to the flash of the ESP32. The process is described above. Alternatively, you can also in Thonny Save AS from the File-Menu call up. Then choose the destination Micropython Device. If you now infect the ESP32 on the battery board, the program starts without a PC.

We are at the end with the meeting. There are other options for the equipment of the player. If you want it louder, put an amplifier on the stereo outputs of the DFPlayer Mini. With a few buttons you could scroll in the titles and with a WLAN connection you can take over the full control with an Android app that is easy with the MIT App-Inventor 2 can be created.

As a purpose for the player, all situations in which one would like to restrict or control the play program By the cards that are handed over. Wenn die Karten in eine Schautafel eingebaut werden, kann durch Auflegen des Players ein entsprechender Informationstext abgespielt werden. Or let the Teddibär tell different good night stories ...

Have fun tinkering, programming and implementing many other ideas.