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

| 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 raiseMAX_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.