Build an RFID jukebox that plays a song for every card

Tap a card or key fob on the printed jukebox and it plays that card's song on the buzzer. The first four cards you show it are learned automatically.
  • Level:Beginner
  • Duration: 50 min
  • Ages 10+
  • Platform: Arduino

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Build an RFID jukebox that plays a song for every card – EEMENTOR STEM™ lesson

A jukebox plays the record you choose. This one plays the card you choose: tap a card or the blue key fob from your kit on the reader, and the passive buzzer plays a tune that belongs to that card. You don't have to type any card numbers into the sketch. The first four cards it sees are learned automatically, each with its own song, and a green LED flashes along with the notes.

What you'll learn

  • How RFID cards are read without touching them
  • How every card has its own ID number, the UID
  • How to store songs as lists of notes and lengths
  • How a sketch can remember new cards while it runs

What you need

  • UNO R3 board and USB cable (UNO R3 Starter Kit)
  • RC522 RFID module with card and key fob (UNO R3 Starter Kit)
  • Passive buzzer (UNO R3 Starter Kit)
  • Green LED and 1 resistor, 220 Ω (UNO R3 Starter Kit)
  • Up to 2 more RFID cards or tags, if you have them
  • The printed parts from the 3D printing box below (jukebox case), or the cardboard version described there
  • Breadboard and jumper wires (UNO R3 Starter Kit)

Wiring

Breadboard wiring diagram: Build an RFID jukebox that plays a song for every card
Breadboard view. Check each connection against the wiring table below.
From To
RFID SDA UNO pin 10
RFID SCK UNO pin 13
RFID MOSI UNO pin 11
RFID MISO UNO pin 12
RFID IRQ Not used
RFID GND GND
RFID RST UNO pin 9
RFID 3.3V UNO 3.3V (not 5V!)
Passive buzzer + UNO pin 8
Passive buzzer − GND
UNO pin 6 220 Ω resistor, then the green LED's long leg
LED short leg GND

The RFID module runs on 3.3 V. Connecting its power pin to 5V can damage it, so double-check that one wire before you plug in the USB cable.

The sketch

In the Arduino IDE, open Library Manager and install the MFRC522 library by GithubCommunity.

#include <SPI.h>
#include <MFRC522.h>

const int SS_PIN = 10;
const int RST_PIN = 9;
const int BUZZER_PIN = 8;
const int LED_PIN = 6;
MFRC522 reader(SS_PIN, RST_PIN);

// Note frequencies in hertz
const int NOTE_C4 = 262, NOTE_D4 = 294, NOTE_E4 = 330, NOTE_F4 = 349, NOTE_G4 = 392, NOTE_A4 = 440, NOTE_B4 = 494, NOTE_C5 = 523;
const int BEAT = 250;   // one beat in milliseconds

// Each song: the notes, and how many beats each note lasts
const int twinkle[] = {NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_C4};
const byte twinkleBeats[] = {1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2};
const int ode[] = {NOTE_E4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_D4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4};
const byte odeBeats[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2};
const int jacques[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_E4, NOTE_F4, NOTE_G4};
const byte jacquesBeats[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2};
const int bells[] = {NOTE_E4, NOTE_E4, NOTE_E4, NOTE_E4, NOTE_E4, NOTE_E4, NOTE_E4, NOTE_G4, NOTE_C4, NOTE_D4, NOTE_E4};
const byte bellsBeats[] = {1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 4};

struct Song {
  const char* name;
  const int* notes;
  const byte* beats;
  int length;
};
const Song SONGS[] = {
  {"Twinkle, Twinkle, Little Star", twinkle, twinkleBeats, sizeof(twinkle) / sizeof(int)},
  {"Ode to Joy", ode, odeBeats, sizeof(ode) / sizeof(int)},
  {"Frere Jacques", jacques, jacquesBeats, sizeof(jacques) / sizeof(int)},
  {"Jingle Bells", bells, bellsBeats, sizeof(bells) / sizeof(int)},
};
const int MAX_CARDS = 4;

unsigned long knownCards[MAX_CARDS];
int cardCount = 0;

// Turn the first 4 bytes of the card's UID into one number
unsigned long cardId() {
  unsigned long id = 0;
  for (byte i = 0; i < reader.uid.size && i < 4; i++) id = (id << 8) | reader.uid.uidByte[i];
  return id;
}

// Find the card's song, or learn the card if there is room. Returns -1 when all songs are taken.
int songFor(unsigned long id) {
  for (int i = 0; i < cardCount; i++) {
    if (knownCards[i] == id) return i;
  }
  if (cardCount < MAX_CARDS) {
    knownCards[cardCount] = id;
    Serial.print("New card learned for song ");
    Serial.println(cardCount + 1);
    return cardCount++;
  }
  return -1;
}

void play(const Song& song) {
  Serial.print("Playing: ");
  Serial.println(song.name);
  for (int i = 0; i < song.length; i++) {
    int length = song.beats[i] * BEAT;
    tone(BUZZER_PIN, song.notes[i], length * 9 / 10);   // a tiny gap between notes
    digitalWrite(LED_PIN, HIGH);
    delay(length / 2);
    digitalWrite(LED_PIN, LOW);
    delay(length - length / 2);
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  SPI.begin();
  reader.PCD_Init();
  Serial.println("Jukebox ready. Tap a card!");
}

void loop() {
  if (!reader.PICC_IsNewCardPresent() || !reader.PICC_ReadCardSerial()) return;

  unsigned long id = cardId();
  Serial.print("Card ");
  Serial.println(id, HEX);
  int song = songFor(id);
  if (song >= 0) {
    play(SONGS[song]);
  } else {
    tone(BUZZER_PIN, 150, 600);        // low buzz: all four songs already have a card
    Serial.println("No free song left for this card");
  }
  reader.PICC_HaltA();                 // finish with this card
  delay(500);
}

How it works

The reader's antenna sends out a weak radio field. When a card comes close, the field powers a tiny chip inside it, and the chip answers with its UID, a serial number that is different for every card. That's why RFID cards need no battery.

The sketch keeps a short list, knownCards. When a card arrives, songFor() looks for its number in the list. If it's new and there is still room, it's added at the next free place, and its place in the list is also the number of its song. The list lives in the UNO's working memory, so after a reset the jukebox learns the cards again.

Each song is two lists of the same length: the notes in hertz and the beats for each note. A struct keeps the name, both lists and the length together, so play() can handle any song the same way.

Try this next

  • Write your own song as two new lists and add it to SONGS. Remember to raise MAX_CARDS.
  • Make a "stop" card that silences the jukebox in the middle of a song.
  • Save the learned cards in EEPROM so they survive a reset.

Troubleshooting

  • Nothing happens when you tap a card: check the 3.3V and GND wires, then SDA on pin 10 and RST on pin 9. Hold the card flat, right on the tray, for a second.
  • The Serial Monitor shows a card, but there is no sound: use the passive buzzer (no sticker, open green board underneath), not the active one.
  • Every card plays the same song: you are tapping the same card. Each card or fob has its own number; look at the numbers in the Serial Monitor.
  • A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

UNO R3 Starter Kit | Arduino IDE Compatible | Free Tutorials $56.00

Shop the kit