Plataforma giratoria para pequeñas esculturas con luz controlada por Arduino mediante mando a distancia por infrarrojos.

Materiales necesarios

  • 1 Arduino Nano.
  • Motor Step 28BYJ-48.
  • Driver ULN2003 para motor steeper.
  • Step-Down Switching Regulator LM2596.
  • Receptor de infrarrojos VS1838B.
  • 1 Transistor 2N2222.
  • 60 cm de tira led de 12 voltios (36 leds aproximadamente).
  • Fuente de alimentación de 12V
  • Pequeño interruptor de encendido.
  • Mando a distancia antiguo en desuso.
  • Jack para entrada de corriente.
  • 4 rodamientos (diámetro exterior 22mm, diámetro interior 8mm, anchura 7mm).
  • Tornillos M3 de 10mm y tuercas.
  • Tornillos de madera (2×10).
  • 3 Resistencias. (100, 150, 20K).

Esquema eléctrico.

Esquema eléctrico del expositor rotativo,
Componentes eléctricos

Un reto importante en este proyecto ha sido evitar en la medida de lo posible el exceso de calor en los componentes pues es un artefacto que estará mucho tiempo encendido sin olvidad que estará encerrado en una carcasa de material poco resistente al calor aunque en mi caso me he decidido por el ABS en lugar del PLA por este y otros motivos. Se podría suprimir el integrado LM2596 cuya función es rebajar la entrada de 12V a 5 ó 6 voltios en el circuito que alimenta el controlador pues el mismo arduino posee un L7805 capaz de hacer esto pero además de elevar muchísimo la emisión de calor, creo que no es capaz de suministrar al motor la corriente necesaria para su funcionamiento. Además, el uso de este componente me permite la opción de alimentar el motor con algunos voltios más en caso de necesitarse más fuerza(6V como describe el esquema electrónico) pues así lo permite el 28BYJ-48 según su hoja de datos, aunque esto volvería a aumentar algo el exceso de calor. Casos a parte, la opción de añadir el LM2596 me ha subsanado esos grandes inconvenientes en mi proyecto aunque la gran desventaja es la falta de espacio en el interior.

Los leds serán alimentados directamente de la fuente de alimentación por lo que es importante que no exceda de 12V. Los leds demandan una corriente de 12,5mA cada uno. Como usaremos 36 leds, éstos consumirán alrededor de 450mA a 12V. Para controlar las luces usaremos un transistor 2N2222 que permite un paso de 600mA suficientes para esta tarea, que será controlado por un pin PWM (D5) para poder usar intensidades de luz. Para el correcto funcionamiento de esta parte es muy importante calcular el valor de la resistencia de base. Para ello es necesario los datos de ICmax, VCEmax y hFE del datasheet del transistor que sabemos que son 600mA, 40V y ganancia de 40 en el caso de una corriente menor de 500mA a 10V. Después de hacer los cálculos el valor de la resistencia ha de ser de 150ohm.

Mando que usaré en el proyecto, perteneciente a un antiguo aparato.
Mando que usaré en mi proyecto, perteneciente a un antiguo aparato.

Por último tener atención en las resistencias de la parte de alimentación del VS1838B que es un receptor de infrarrojos que leerá las señales del mando a distancia para controlar la velocidad de giro, la dirección , parada y arranque, así como la intensidad de las luces e incluso programar giros de determinados pasos.

Piezas para imprimir en 3D

Como he mencionado antes, conviene el uso de ABS en lugar de PLA por sus propiedades térmicas, aun así algunas piezas se pueden hacer con materiales menos exigentes.

Piezas de soporte de rodamientos

Hay que imprimir tres iguales. Son las que soportarán la plataforma giratoria. Constan de un anclaje de 3 tornillos de M3 a ser posible de cabeza cónica. 

Puede ser necesario el uso de una arandela para la sujeción del rodamiento en la pieza.

El cuarto rodamiento irá encajado en la parte central de la base de la caja como se aprecia en las imágenes. Al final del montaje, será atornillado con un tornillo M3x20mm que presionará la tapadera con la base.

Pieza de base

Contendrá la electrónica. Está separada el suelo por cuatro patas que deben atornillarse a ella. Cuenta con unos orificios laterales que permiten la sujeción de los «brazos» de soporte sobre los que irá el aro de luz.

Cuenta con unos grandes agujeros de ventilación en la parte baja, además posee huecos para el interruptor de palanca y la entrada de Vcc a 12V.

Soportes laterales

Son tres piezas distintas pues una de ellas cuanta con una ranura rectangular para albergar el VS1838B por el exterior y otra de ellas posee un orificio circular para permitir el paso de los cables de corriente de los leds. Una vez colocadas añadiremos los embellecedores translúcidos para ocultar la tornillería, el cableado y el receptor IR.

Aro de luz

Consta de tres piezas iguales que forman un aro donde pondremos la tira de leds. 

Una vez colocados los leds, pondremos las piezas transparentes sobre el aro con una ligera presión como se muestra en el detalle.

Plataforma y engranaje

Por último colocaremos la electrónica junto con el motor y su pieza de engranaje. Un rodamiento encajará en la parte central de la base por el exterior y atornillaremos.


Código de programación

Las librerías que usaremos son EEPROM.h incluida en el IDE de programación, Stepper.h también incluida en el IDE y IRremote.h, que podemos descargar de aquí.

Lo primero que veremos será cómo saber los códigos que genera cada botón del mando a distancia que usemos. Para ello nos valdremos del siguiente código que realiza esa decodificación. 

Recepción de datos del mando a distancia

#include <IRremote.h>
int RECV_PIN = 7; 

IRrecv irrecv(RECV_PIN);

decode_results results;

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // Empezamos la recepción  por IR
}


void dump(decode_results *results) {
  // Dumps out the decode_results structure.
  // Call this after IRrecv::decode()
  
  Serial.print("(");
  Serial.print(results->bits, DEC);
  Serial.print(" bits)");
  
  if (results->decode_type == UNKNOWN) {
    Serial.print("Unknown encoding: ");
  }
  else if (results->decode_type == NEC) {
    Serial.print("Decoded NEC: ");

  }
  else if (results->decode_type == SONY) {
    Serial.print("Decoded SONY: ");
  }
  else if (results->decode_type == RC5) {
    Serial.print("Decoded RC5: ");
  }
  else if (results->decode_type == RC6) {
    Serial.print("Decoded RC6: ");
  }
  else if (results->decode_type == PANASONIC) {
    Serial.print("Decoded PANASONIC - Address: ");
    Serial.print(results->address, HEX);
    Serial.print(" Value: ");
  }
  else if (results->decode_type == LG) {
    Serial.print("Decoded LG ");
  }
  else if (results->decode_type == JVC) {
    Serial.print("Decoded JVC ");
  }
 
  else if (results->decode_type == WHYNTER) {
    Serial.print("Decoded Whynter ");
  }
  Serial.print(results->value, HEX);
  Serial.print(" (HEX) , ");
  Serial.print(results->value, BIN);
  Serial.println(" (BIN)");

}

void loop() {
  if (irrecv.decode(&results)) {
    dump(&results);
    irrecv.resume(); // empezamos una nueva recepción
  }
  delay(300);
}

Recordad conectar el pin de datos del IR en el D7.

Abrimos el Serial port y pulsamos los botones de los que deseemos obtener el código en hexagesimal.

Copiaremos los códigos en algún lugar para más tarde usarlos en el programa final a los que añadiremos el prefijo 0x00.

Código final

Algunos detalles a tener en cuenta a la hora de la programación y que me ha costado más de un dolor de cabeza es que la librería IRremote.h altera los pines PWM 3 y 11, así que usa otros alternativos como por ejemplo el D5 para el control de la intensidad de los leds.

Otra cosa a tener en cuenta es que la librería Stepper.h en mi caso, altera (desordena) los pines que van al ULN2003, así que tuve que hacer varias pruebas hasta darme cuenta del desorden de los pines de conexión (D9, D11, D10, D12).

#include <EEPROM.h>
#include <Stepper.h>
#include <IRremote.h>
int RECV_PIN = 7;
int pinleds = 5;

int velocidad = EEPROM.read(1);
boolean anda = EEPROM.read(2);
int leds = EEPROM.read(3);
int direccion = EEPROM.read(4);

int cont = 0;
int anteplay;
int topeI;
int topeD;
int pasos;
boolean pulsoI = false;
boolean programar = 0;
boolean act = 0;

IRrecv irrecv(RECV_PIN);
decode_results results;

const int stepsPerRevolution = 2000;  // Número de pasos por revolución del motor
Stepper myStepper(stepsPerRevolution, 9, 11, 10, 12);   //Parece que la librería tiene algún fallo en la asignación de los pines del ULN2003 (Lo he arreglado probando)

void setup() {
  Serial.begin(9600);
  Serial.println ("Saludos");
  pinMode (pinleds, OUTPUT);
  for (int i = 0; i <= 255; i++) {
    analogWrite (pinleds, i);
    delay (2);
  }
  for (int i = 255; i >= leds; i--) {
    analogWrite (pinleds, i);
    delay (2);
  }
  memoria();
  valores();
  analogWrite (pinleds, leds);
  irrecv.enableIRIn();
}


void memoria() {      //aquí comprobamos que si es la primera vez que se usa el micro no guarde valores alterados en la eprom. Obligamos a grabar en eprom valores por defecto si no existieran.
  if (velocidad > 25) {
    velocidad = 1;
    EEPROM.write(1, velocidad);
  }
  if (anda > 1) {
    anda = 1;
    EEPROM.write(2, anda);
  }
  if (leds != 0 && leds != 51 && leds != 102 && leds != 153 && leds != 204 && leds != 255) {
    leds = 0;
    EEPROM.write(3, leds);
  }
  if (direccion != 1 && direccion != 2) {
    direccion = 1;
    EEPROM.write(4, direccion);
  }
  if (direccion == 2) {
    direccion = -1;
  }
}

void reaccion() {       //luces cuando recibe una señal del mando reconocible.
  if (leds <= 110) {
    for (int i = leds; i <= 255; i++) {
      analogWrite (pinleds, i);
      // delay (1);
    }

    for (int i = 255; i >= leds; i--) {
      analogWrite (pinleds, i);
      // delay (1);
    }
  }
  else {
    for (int i = leds; i >= 0; i--) {
      analogWrite (pinleds, i);
      //  delay (1);
    }
    for (int i = 0; i <= leds; i++) {
      analogWrite (pinleds, i);
      // delay (1);
    }
  }
  analogWrite (pinleds, leds);
}

void valores() {              //muestra valores esenciales para verificar
  Serial.print ("Velocidad=");
  Serial.print (velocidad);
  Serial.print ("  Sentido=");
  Serial.print (direccion);
  Serial.print ("  Andando=");
  Serial.print (anda);
  Serial.print ("  Programa=");
  Serial.print (programar);
  Serial.print ("  Luces=");
  Serial.println (leds);
}

void loop() {
  myStepper.setSpeed(velocidad);

  if (anda != 0) {

    if (act == false) {
      myStepper.step(direccion);
      if (pulsoI == true) {
        pasos++;
      }
    }
    else {

      if (cont < pasos) {
        cont++;
        myStepper.step(1);

      }
      if (cont >= pasos) {
        cont++;
        myStepper.step(-1);

      }
      if (cont == pasos + pasos) {
        cont = 0;
      }
    }
  }

  if (irrecv.decode(&results))                      //rutina del mando, reconocimiento de teclas y acciones.
  {
    switch (results.value)
    {
      case 0x00FF609F: Serial.println("Tecla: Volumen +");
        velocidad++;
        if ( velocidad <= 0) velocidad = 1;
        if (velocidad >= 25) velocidad = 25;
        reaccion();
        EEPROM.write(1, velocidad);
        valores();
        break;
      case 0x00FF50AF: Serial.println("Tecla: Volumen -");
        velocidad--;
        if (velocidad <= 0) velocidad = 1;
        if (velocidad >= 25) velocidad = 25;
        reaccion();
        EEPROM.write(1, velocidad);
        valores();
        break;
      case 0x00FFC03F: Serial.println("Tecla: A derecha");
        direccion = 1;
        EEPROM.write(4, 1);
        reaccion();
        valores();
        break;
      case 0x00FFE01F: Serial.println("Tecla:a Izquierda");
        direccion = -1;
        EEPROM.write(4, 2);
        reaccion();
        valores();
        break;
      case 0x00FF807F: Serial.println("Tecla: STOP");
        reaccion();
        anda = 0;
        act = 0;
        cont = 0;
        EEPROM.write(2, anda);
        digitalWrite (9, LOW);
        digitalWrite (10, LOW);
        digitalWrite (11, LOW);
        digitalWrite (12, LOW);
        leds = 0;
        EEPROM.write(3, leds);
        analogWrite (pinleds, leds);
        valores();
        break;
      case 0x00FF20DF: Serial.println("Tecla:PLAY PAUSE");
        reaccion();
        if (anda == 1) {
          anda = 0;
          digitalWrite (9, LOW);
          digitalWrite (10, LOW);
          digitalWrite (11, LOW);
          digitalWrite (12, LOW);
          EEPROM.write(2, anda);
        }
        else
        {
          anda = 1;
          EEPROM.write(2, anda);
        }
        valores();
        break;
      case 0x00FFD827: Serial.println("Tecla:-10");
        if (programar == 1) {
          reaccion();
          delay (1000);
          direccion = -1;
          pasos = 0;
          topeD = 0;
          pulsoI = true;
        }

        break;
      case 0x00FF708F: Serial.println("Tecla:+10");
        if (programar == 1 && pulsoI == true) {
          reaccion();
          pulsoI = false;
          programar = 0;
          act = 1;
          delay (1000);
          cont = 0;
        }
        break;
      case 0x00FFA05F: Serial.println("Tecla:PROGRAMAR");
        reaccion();
        programar = 1;
        act = 0;
        cont = 0;
        delay (1000);
        direccion = 1;
        valores();
        break;
      case 0x00FF10EF: Serial.println("Tecla:REPEAT");
        leds = leds + 51;
        if (leds > 255) {
          leds = 0;
        }
        EEPROM.write(3, leds);
        if (leds > 0) {
          for (int i = leds - 51; i <= leds; i++) {
            analogWrite (pinleds, i);
            delay (2);
          }
        }
        else {
          for (int i = 255; i >= 0; i--) {
            analogWrite (pinleds, i);
            delay (2);
          }
        }

        valores();
        break;
    }
    irrecv.resume();
  }
 }