Tocadiscos – Grupo 04

Índice

Descripción del proyecto

Este proyecto consiste en la creación de un tocadiscos interactivo y automatizado que transforma objetos físicos en notas musicales. El sistema utiliza un gran disco giratorio donde se colocan piezas de diferentes alturas en cuatro carriles distintos.

A medida que el disco gira, unos sensores situados sobre cada carril detectan la presencia y la pieza según su altura. Esta información se procesa para activar sonidos específicos, permitiendo crear una melodía colocando los objetos en distintas posiciones del disco.

Para garantizar un funcionamiento fluido, el sistema separa el control del movimiento (el giro del plato) del procesamiento del sonido, asegurando que la música suene de forma estable, sin ruidos accidentales y en perfecta sincronía con la rotación del disco.

Ideas Iniciales

La idea inicial era utilizar fichas de colores del mismo tamaño. Esta idea fue descartada debido a los problemas que nos traerían los sensores de color por los cambios de luminosidad en el ambiente o los destellos provocados por el movimiento del disco.

Luego, pensamos en implementarlo mediante tecnología RFID, con etiquetas y lectores. No obstante, tuvimos que descartar esta opción debido a la falta de selectividad de los sensores, que detectaban múltiples etiquetas simultáneamente. Además, la latencia de lectura del protocolo RFID limitaba excesivamente la velocidad de rotación operativa.

Finalmente, se implementó un sistema de fichas con alturas diferenciadas como la solución más robusta y eficiente. Aunque inicialmente se proyectó una nota por cada nivel de altura, las limitaciones en la precisión de los sensores ultrasónicos llevaron a simplificar el diseño a dos niveles distintos. A diferencia de los métodos ópticos, este enfoque físico garantiza estabilidad frente a variaciones lumínicas y asegura una respuesta inmediata al paso de las piezas. 

Hardware

Esquema de conexiones (Wowki.com): Motor

Como la página web en la que hemos diseñado el circuito no se encuentra el controlador ULN2003, que es el que hemos utilizado en el circuito real, hemos seleccionado el A4988 para hacer una representación aproximada.

Esquema de conexiones (Wowki.com): Sensores y altavoz

En este caso, el altavoz está representado con un buzzer y el DFPlayer mini con un lector de tarjetas microSD, puesto que tampoco se encuentran los componentes exactos en la página web.

Conexiones físicas

En esta imagen se pueden observar las conexiones del motor y el botón. Observamos la primera placa Arduino que hemos usado, alimentada con una fuente externa de 9V. Esta parte es la encargada de hacer girar el plato negro.

Aquí se muestran las conexiones del segundo Arduino que hemos utilizado. En esta parte, tras varias pruebas, hemos tenido que conectar una fuente externa a parte de la fuente del ordenador.

Tabla de Costes

ComponenteCantidadPrecio por unidad (€)Subtotal (€)
Motor 28BYJ-4812.752.75
Driver ULN200312.12.1
Pulsador10.060.06
Pila 9V3412
Sensor HC-SR0441.87.2
Fichas 3cm40.351.4
Fichas 6cm40.41.6
DFPlayer mini11.81.8
Altavoz 8 ohm y 2W11.61.6
Micro SD110.210.2
Resistencia 1K ohm10.140.14
Cables17.877.87
Total48.72

Software

Código del motor

Este código controla el giro del plato mediante un motor paso a paso y un pulsador con lógica de interruptor. Utiliza la librería Stepper.h para gestionar el movimiento del motor, configurando una velocidad constante y un sistema de seguridad que desactiva las bobinas al detenerse para evitar el sobrecalentamiento. El software implementa además un filtrado de «rebotes” en el botón, asegurando que el motor solo cambie de estado tras una pulsación física clara y estable. 

#include <Stepper.h>

const int pinBoton = 2;
const int stepsPerRev = 2048;
Stepper myStepper(stepsPerRev, 8, 10, 9, 11);

bool motorEncendido = false;
bool botonPresionado = false;

void setup() {
  pinMode(pinBoton, INPUT_PULLUP); // Resistencia interna
  myStepper.setSpeed(10);          // Velocidad de giro del motor
}

void loop() {
  // 1. LEER EL BOTÓN
  if (digitalRead(pinBoton) == LOW && !botonPresionado) {
    motorEncendido = !motorEncendido; 
    botonPresionado = true;
    delay(200);
  }

  if (digitalRead(pinBoton) == HIGH) {
    botonPresionado = false;
  }

  //2. MOVER EL MOTOR
  if (motorEncendido) {
    myStepper.step(10); 
  } else {
    // Si está apagado, nos aseguramos de apagar las luces (bobinas)
    digitalWrite(8, LOW);
    digitalWrite(9, LOW);
    digitalWrite(10, LOW);
    digitalWrite(11, LOW);
  }
}

Código de los sensores y el altavoz

El siguiente código configura el sistema de audio y la lectura de los sensores ultrasónicos. Inicializa la comunicación con el reproductor MP3 (DFPlayer) y define los pines de disparo y eco para cuatro carriles de detección. La función central “medirDistancia” envía un pulso sónico y calcula, mediante el tiempo de rebote, la distancia exacta de las fichas para su posterior procesamiento musical. 

#include <DFRobotDFPlayerMini.h>
#include <SoftwareSerial.h>

#define ALTA_MIN 6.0
#define ALTA_MAX 9.0
#define BAJA_MIN 9.1
#define BAJA_MAX 11.5
#define ALTURA_MAXIMA 12.3
#define UMBRAL_CONFIRMACION 2
#define TIEMPO_ESPERA 150

const int Trig[] = {2, 4, 6, A0};
const int Echo[] = {3, 5, 7, A1};

int estadoSensor[]      = {0, 0, 0, 0};
int contadorCandidato[] = {0, 0, 0, 0};
int estadoCandidato[]   = {0, 0, 0, 0};
int notaPendiente[]     = {0, 0, 0, 0};
unsigned long tiempoUltimaDeteccion[] = {0, 0, 0, 0};

DFRobotDFPlayerMini reproductor;
SoftwareSerial puertoMP3(12, 13);

void setup() {
  Serial.begin(115200);
  puertoMP3.begin(9600);

  for (int i = 0; i < 4; i++) {
    pinMode(Trig[i], OUTPUT);
    pinMode(Echo[i], INPUT);
  }

  delay(1000);
  if (reproductor.begin(puertoMP3)) {
    Serial.println("DFPlayer OK");
    reproductor.volume(15);
  } else {
    Serial.println("Error: DFPlayer no encontrado");
  }
}

float medirDistancia(int i) {
  digitalWrite(Trig[i], LOW);
  delayMicroseconds(2);
  digitalWrite(Trig[i], HIGH);
  delayMicroseconds(10);
  digitalWrite(Trig[i], LOW);

  long tiempo = pulseIn(Echo[i], HIGH, 15000);
  if (tiempo == 0) return 999.0;
  return tiempo * 0.034 / 2.0;
}

int clasificarDistancia(float distancia) {
  if (distancia >= ALTA_MIN && distancia <= ALTA_MAX) return 2;
  if (distancia >= BAJA_MIN && distancia <= BAJA_MAX) return 1;
  if (distancia > ALTURA_MAXIMA)                      return 0;
  return -1;
}

void loop() {
  unsigned long ahora = millis();

  for (int i = 0; i < 4; i++) {
    float distancia = medirDistancia(i);
    int lecturaActual = clasificarDistancia(distancia);

    if (lecturaActual == -1) {
      contadorCandidato[i] = 0;
      continue;
    }

    if (lecturaActual == estadoCandidato[i]) {
      contadorCandidato[i]++;
    } else {
      estadoCandidato[i] = lecturaActual;
      contadorCandidato[i] = 1;
    }

    if (contadorCandidato[i] >= UMBRAL_CONFIRMACION && lecturaActual != estadoSensor[i]) {

      if (lecturaActual == 1) {
        // Ignorar borde de salida de ficha grande
        if (estadoSensor[i] == 2) continue;

        // Ficha pequeña: guardar y esperar por si viene una grande
        notaPendiente[i] = (i * 2) + 1;
        tiempoUltimaDeteccion[i] = ahora;
        estadoSensor[i] = 1;
        contadorCandidato[i] = 0;

      } else if (lecturaActual == 2) {
        // Ficha grande: reproducir ya y cancelar pequeña pendiente
        notaPendiente[i] = 0;
        estadoSensor[i] = 2;
        contadorCandidato[i] = 0;
        int notaATocar = (i * 2) + 2;
        Serial.print("Sensor "); Serial.print(i + 1);
        Serial.print(" - Nota: "); Serial.println(notaATocar);
        reproductor.play(notaATocar);
        delay(10);

      } else if (lecturaActual == 0) {
        estadoSensor[i] = 0;
        contadorCandidato[i] = 0;
      }
    }

    // Si hay nota pequeña pendiente y ha pasado el tiempo, reproducirla
    if (notaPendiente[i] > 0 && (ahora - tiempoUltimaDeteccion[i]) > TIEMPO_ESPERA) {
      Serial.print("Sensor "); Serial.print(i + 1);
      Serial.print(" - Nota: "); Serial.println(notaPendiente[i]);
      reproductor.play(notaPendiente[i]);
      delay(10);
      notaPendiente[i] = 0;
    }
  }
}

Problemas encontrados

  • Sincronización de procesos: Durante la integración del sistema, se detectó un conflicto en la gestión de recursos al centralizar el motor, los sensores y el altavoz en una única placa Arduino Uno. A pesar de emplear una fuente externa de 9V para el motor, la ejecución simultánea de procesos provocaba vibraciones mecánicas y fallos de detección en los sensores. Esta inestabilidad se debe a que el microcontrolador de Arduino no puede realizar los dos procesos simultáneamente.
  • Detección correcta de fichas: El principal desafío con los sensores fue su falta de precisión ante el movimiento: al ser de naturaleza ultrasónica, el rebote del sonido se volvía inestable al chocar con los bordes de las fichas mientras el disco giraba. Esto provocaba lecturas ‘fantasma’ o erráticas que disparaban sonidos en momentos equivocados. Para corregirlo, tuvimos que desarrollar un sistema de validación por software que ignora los picos de distancia y solo activa una nota cuando la lectura es constante. Así, logramos que el sistema sea capaz de distinguir entre una vibración mecánica y una ficha real, garantizando una melodía limpia y rítmica.
  • Lectura secuencial: Otro desafío importante fue la interferencia entre sensores, ya que al estar situados en carriles contiguos, el eco de una ficha podía ser captado accidentalmente por el sensor vecino. Esto generaba activaciones falsas en pistas donde no había objetos. La solución consistió enasegurar que cada sensor opere en una ventana de tiempo única para evitar que las señales acústicas se solapen entre sí.
  • Embotellamiento de datos: Al enviar múltiples comandos de audio en intervalos de tiempo muy cortos, el reproductor MP3 sufría retardos en su respuesta, provocando la pérdida de notas en la secuencia musical. Este problema de latencia se resolvió mediante una gestión de eventos única, donde el software asegura que cada instrucción de sonido sea enviada solo una vez por ficha detectada, incorporando pausas que permiten al DFPlayer procesar la información sin bloqueos.
  • Voltaje e Intensidad: Debido al consumo de los sensores y el altavoz, la intensidad suministrada por el puerto USB resultó insuficiente para alimentar el sistema completo. Para solucionar este déficit de corriente, se implementó una fuente de alimentación externa mediante una pila de 9V, trabajando en conjunto con la conexión del ordenador. 

Demostración

Se incluye un vídeo demostración con un enlace a OneDrive porque el archivo es demasiado pesado: Demostracion-Tocadiscos.mp4

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *