Jukebox é uma máquina antiga que toca músicas conforme você coloca moedas. Nos tempos modernos, podemos colocar tags RFID no lugar de moedas e fazer com que cada uma toque uma música específica. Este post descreve a montagem de um jukebox controlado por RFID onde a música a tocar é escolhida através de um tag RFID.
Material necessário para o jukebox controlado por RFID
- Placa Nano (você pode usar outros modelos com o Uno ou o Pro Mini)
- Leitor RFID RDM6300 125KHz
- Tags RFID 125KHz (você pode usar também cartões RFID 125 KHz)
- Módulo MP3 DFPlayer Mini
- Cartão micro SD
- Alto falante
- Display 7 Segmentos 1 Dígito catodo comum
- Botão
- Protoboard
- Jumpers
Montagem do circuito
A figura abaixo mostra a interconexão entre as partes.
Coloque no diretório \MP3 do cartão SD até nove músicas no formato MP3. Os arquivos devem ter os nomes começando com “0001” a “0009” (sem pular nenhum número).
Operação do jukebox controlado por RFID
O primeiro passo é associar os tags às músicas. Aperte o botão, vai aparecer 1 no display. Você tem até 5 segundos para ler o tag que será associado à musica 1. Se você não ler um tag neste tempo, a configuração será encerrada e o display irá apagar. Lido um tag, o display passa a apresentar o número seguinte, para você ler um outro tag. O processo se repete até você ler 9 tags ou ficar 5 segundos sem ler um tag.
Esta associação só precisa ser feita uma vez, pois ela é salva na EEProm do Arduino e continuará valendo mesmo que você reinicie ou desligue o Arduino.
Feita a configuração, basta aproximar um tag da antena para ser tocada a música correspondente.
Programação
Vamos dar uma examinada no código parte por parte.
No programa principal temos a tabela que armazena os tags conhecidos, a iniciação, o loop principal e a rotina de configuração (associação dos tags às músicas).
// jukebox controlado por RFID #include <SoftwareSerial.h> #include <EEPROM.h> // Tags associados as musicas #define MAX_TAGS 9 #define T_IDTAG 5 static int nTags; static uint8_t tags[MAX_TAGS][T_IDTAG]; // Iniciacao void setup() { dispInit(); botaoInit(); leTags(); delay (2000); // tempo para modulo MP3 iniciar mp3Init(); rfidInit(); // precisa ser depois de mp3Init } // Loop Principal void loop() { static bool tocando = false; uint8_t id[T_IDTAG]; // Verifica se acabou musica if (tocando && !mp3Tocando()) { tocando = false; dispApaga(); } // Trata configuracao if (botaoApertado()) { mp3Para(); configura(); return; } // Trata leitura de tag if (rfidLe(id)) { for (int i = 0; i < nTags; i++) { if (memcmp(id, tags[i], T_IDTAG) == 0) { dispNum(i+1); mp3Para(); mp3Toca(i+1); tocando = true; return; } } } } // Associa tags as musicas static void configura() { uint8_t id[T_IDTAG]; long timeout; int musica; dispPonto(true); botaoEsperaSoltar(); for (musica = 0; musica < MAX_TAGS; musica++) { // Informa musica a associar dispNum(musica+1); // Espera ler tag timeout = millis()+5000; // 5 segundos de timeout while (!rfidLe(id) && (millis() < timeout)) ; if (millis() >= timeout) { break; } // Associa o taga a musica memcpy(tags[musica], id, T_IDTAG); } if (musica > 0) { nTags = musica; gravaTags(); } dispApaga(); dispPonto(false); }
O tratamento do botão é o costumeiro: é usada uma entrada digital com pullup.
static const int pinBotao = A4; void botaoInit() { pinMode (pinBotao, INPUT_PULLUP); } bool botaoApertado() { return digitalRead (pinBotao) == LOW; } bool botaoEsperaSoltar() { while (botaoApertado()) { delay (100); } }
O display de 7 segmentos é controlado por oito saídas digitais (uma para cada segmento mais uma para o ponto decimal).
static const int pinSegto[] = { 10, 9, A2, A1, A0, 11, 12, A3 }; static const uint8_t digito[10] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F }; // Inicia o display void dispInit() { for (uint8_t i = 0; i < 8; i++) { pinMode (pinSegto[i], OUTPUT); digitalWrite (pinSegto[i], LOW); } } // Apaga o display void dispApaga() { for (uint8_t i = 0; i < 7; i++) { digitalWrite (pinSegto[i], LOW); } } // Controla o ponto void dispPonto(bool aceso) { digitalWrite (pinSegto[7], aceso ? HIGH : LOW); } // Mostra numero de 0 a 9 void dispNum(uint8_t num) { uint8_t segtos = digito[num]; for (uint8_t iSegto = 0; iSegto < 7; iSegto++) { digitalWrite (pinSegto[iSegto], (segtos & 1) ? HIGH : LOW); segtos = segtos >> 1; } }
O leitor RFID utiliza comunicação serial. Como o Arduino Nano usa a serial de hardware para a comunicação com o micro, utilizei uma SoftwareSerial. Nela entrada e saída são feitas usando uma entrada digital e uma saída digital. No caso do leitor, a saída não está conectada, apenas a entrada é usada. Uma limitação da SoftwareSerial é que apenas uma pode receber por vez. Vamos usar uma outra SoftwareSerial para comunicar com o módulo MP3, porém lá iremos somente transmitir. Para receber na SoftwareSerial ligada ao módulo RFID ela deve ser iniciada por último. Obs: como um módulo apenas transmite e o outro apenas recebe e a taxa usada é a mesma nos dois, poderia ter sido usada uma única SoftwareSerial, mas acho que ficaria mais confuso. A parte principal do tratamento do leitor é reconhecer e validar a mensagem recebida. Isto é feito através de uma máquina de estados, onde o estado atual é a posição do caracter sendo recebido. Para tratar em paralelo o botão e a leitura de RFID, a rotina rfidLe mantém o conteúdo de suas variáveis entre chamadas (reparar no uso de static nas declarações).
static const int pinRfidRx = 4; static const int pinRfidTx = 5; // nao conectado static SoftwareSerial rfidSerial(pinRfidRx, pinRfidTx); static const uint8_t STX = 0x02; static const uint8_t ETX = 0x03; // Inicia void rfidInit() { rfidSerial.begin(9600); } // Verifica se tem leitura // Transmissao do leitor: STX hhhhhhhhhh cc ETX bool rfidLe(uint8_t *id) { static uint8_t pos = 0; static uint8_t dig = 0; static uint8_t chk; static uint8_t msg[6]; while (rfidSerial.available() > 0) { uint8_t c = rfidSerial.read(); switch (pos) { case 0: // Aguardando inicio da mensagem if (c == STX) { chk = 0; dig = 0; pos++; } break; case 13: // Aguardando fim da mensagem if (c == ETX) { // sucesso for (int i = 0; i < T_IDTAG; i++) { id[i] = msg[i]; } pos = 0; return true; } else { // erro, aguarda nova mensagem pos = 0; } break; default: // Recebendo corpo da mensagem if ((c >= '0') && (c <= '9')) c = c - '0'; else if ((c >= 'A') && (c <= 'F')) c = c - 'A' + 10; else { // invalido, aguarda nova mensagem pos = 0; break; } if (pos & 1) { // primeiro digito msg [dig] = c << 4; } else { // segundo digito msg [dig] |= c; chk ^= msg [dig]; dig++; if (pos == 12) { // confere check if (chk != 0) { // erro, aguarda nova mensagem pos = 0; break; } } } pos++; break; } } return false; }
Como dito, o módulo MP3 também utiliza comunicação serial. Vamos apenas enviar comandos para ele; o sinal Busy (ocupado) é usado para determinar se está tocando uma música. O MP3 DFPlayer Mini possui vários recursos interessantes, aqui usamos somente o básico.
static const int pinMp3Tx = 2; static const int pinMp3Rx = 3; static const int pinMp3Busy = 8; static SoftwareSerial mp3Serial(pinMp3Rx, pinMp3Tx); static const uint16_t volume = 15; // 0 a 48 static const uint16_t equalizacao = 1; // 0=normal, 1=pop, 2=rock, 3=jazz // 4=classic, 5=bass static uint8_t bufCmdMp3[10]; void mp3Init() { // prepara o buffer de envio de comand com os valores fixos bufCmdMp3[0] = 0x7E; // marca do inicio bufCmdMp3[1] = 0xFF; // versão do protocolo bufCmdMp3[2] = 6; // tamanho dos dados bufCmdMp3[4] = 0; // não queremos resposta bufCmdMp3[9] = 0xEF; // marca do fim // pino para detectar fim da musica pinMode(pinMp3Busy, INPUT); // configura o módulo mp3Serial.begin(9600); mp3EnviaCmd(0x06, volume); delay(30); mp3EnviaCmd(0x07, equalizacao); delay(30); } // Inicia uma faixa no direrio \MP3 void mp3Toca(uint16_t faixa) { // envia o comando mp3EnviaCmd(0x12, faixa); // aguarda começar a tocar long timeout = millis()+3000; // 3 segundos de timeout while ((digitalRead(pinMp3Busy) == HIGH) && (millis() < timeout)) { delay(10); } } // Para de tocar void mp3Para() { mp3EnviaCmd(0x16, 0); long timeout = millis()+3000; // 3 segundos de timeout while ((digitalRead(pinMp3Busy) == LOW) && (millis() < timeout)) { delay(10); } } // Verifica se acabou de tocar bool mp3Tocando() { return digitalRead(pinMp3Busy) == LOW; } // Envia comando ao módulo static void mp3EnviaCmd(uint8_t cmd, uint16_t param) { // coloca comando e parametro no buffer bufCmdMp3[3] = cmd; bufCmdMp3[5] = param >> 8; bufCmdMp3[6] = param & 0xFF; // calcula o checksum e coloca no buffer uint16_t check = 0; for (int i = 1; i < 7; i++) { check += bufCmdMp3[i]; } check = -check; bufCmdMp3[7] = check >> 8; bufCmdMp3[8] = check & 0xFF; // transmite o buffer for (int i = 0; i < 10; i++) { mp3Serial.write(bufCmdMp3[i]); } }
Por último temos as rotinas para ler e gravar a configuração na EEProm:
// Carrega da EEPROM as tags void leTags() { nTags = EEPROM.read(0); if ((nTags < 1) || (nTags > MAX_TAGS)) { // assume que nao configurou tags nTags = 0; return; } int pos = 1; for (int i = 0; i < nTags; i++) { for (int j = 0; j < T_IDTAG; j++) { tags[i][j] = EEPROM.read(pos); pos++; } } } // Salva as tags na EEPROM void gravaTags() { EEPROM.write(0, nTags); int pos = 1; for (int i = 0; i < nTags; i++) { for (int j = 0; j < T_IDTAG; j++) { EEPROM.write(pos, tags[i][j]); pos++; } } }
Aperfeiçoamentos
Se você gostar bastante deste projeto, você pode montá-lo de uma forma mais definitiva em uma caixa, como a sugestão abaixo.
Para suportar mais de 9 músicas é preciso, além de alterar o define MAX_TAGS no código, substituir o display de 1 dígito. Você pode, por exemplo, usar um módulo com quatro dígitos (como https://www.makerhero.com/produto/modulo-74hc595-com-display-4-digitos/ e https://www.makerhero.com/produto/modulo-tm1637-com-display-7-segmentos-4-digitos/) com as devidas mudanças no software.
A montagem apresentada produz som monofônico, amplificado pelo módulo MP3. Você pode também conectar um amplificador estéreo aos pinos DAC_R e DAC_L para obter um som estereofônico e com mais potência.
E aí? curtiu fazer seu próprio jukebox controlado por RFID? Ajude-nos a melhorar o blog comentando abaixo sobre este tutorial.
Muito bom.
Boa tarde,
Seria possível tirar uma dúvida minha?
Eu fiz a ligação de todos os pinos como indicado no esquema da protoboard mas quando aperto o batão o display não acende.
Coloque um Serial.print com uma mensagem “OK” após pressionar o batão a mensagem aparece no monitor serial enquanto estou pressionando o botão, mas quando o solto a mensagem cessa. Já para fazer aparecer algo no display tive que no comando “digitalWrite (pinSegto[i], LOW);” no lugar de “LOW” coloquei “HIGH” mas nada acontece depois de ele mostrar “0” no display. O que estou fazendo de errado?
Desculpe-me a ignorância no assunto.
Olá José,
Seu display é catodo comum ou anodo comum?
Abraço!
Rosana – Equipe MakerHero
Catodo comun
Olá José,
Você chegou a testar seu display separadamente? Está funcionando certinho?
Abraço!
Rosana – Equipe MakerHero
Sim. Liguei-o no mesmo Arduíno nano associado a um resistor de 220 ohms, mas somente utilizei os pinos de +5 V e o GND. Sem botões e utilização de outros pinos
José,
Tenho a impressão que o seu problema não é no display. Para conferir, experimente colocar um dispNum(8) no setup(), após o dispInit(). O display deve apresentar todos os segmentos acesos (numero 8). Supondo que passou neste teste, repare se quando você aperta o botão o ponto do display acende. Se sim, solte o botão e aguarde até 3 segundos (timeout em mp3Para), depois disso deveria aparecer 1 no display. Se demorar 3 segundos, verifique a ligação do D8 do Arduino ao BUSY do DFPlayer.
Abraço
DQ
Muito obrigado pela ajuda! Vou fazer os testes essa semana e lhes dou um retorno.
O display deve apresentar todos os segmentos acesos (numero 8). – OK
Quando você aperta o botão o ponto do display acende. – Não aparece o ponto.
Eu consegui fazer o rfid e o display funcionar. A rotina de gravar e ler a tag esta funcionando mas ela não está acionando o df player mini. Você acha q poderia ser só uma ligação do busy?
Para fazer o df player funcionar separadamente tive que colocar um resistor de 330 ohms.
Obg pela ajuda!
Boa tarde,
A loja do site vende o kit para esse jukebox?
Olá José,
Infelizmente não vendemos como um kit. Apenas vendemos os materiais necessários separadamente.
Abraço!
Rosana – Equipe MakerHero