Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Dado Arduino Activado por Sacudida con Matriz LED 8x8

Componentes y materiales

Componente Cantidad
Arduino Nano R3 1
Matriz de puntos 8x8 3mm con MAX7219 1
Módulo sensor de inclinación (interruptor de bola) con interfaz digital 1
Interruptor deslizante 1
Módulo elevador MT3608 DC 2A (2V-24V a 5V/9V/12V/28V) 1

Herramientas necesarias

Software y servicios en línea

Sobre el proyecto

Hace aproximadamente un año, mi hijo y yo construimos un dado electrónico con Arduino y unos pocos LEDs. Sigue siendo un proyecto ideal para principiantes. Hemos aprendido mucho este año, así que era hora de una actualización.

Versión 2.0

El dado es fantástico para dar un paso más: vincular otras áreas. Hoy existen componentes listos para casi todo. Lo vemos claramente en los kits de inicio para Arduino o Raspberry Pi: LEDs, pulsadores, sensores... todo viene en placas listas para conectar con pocos cables a los GPIO.

Impresoras 3D sencillas están disponibles por menos de 140€. Casi cualquier soporte, montaje o carcasa puede crearse con ellas. Hay muchas oportunidades para desarrollar proyectos propios. Un ejemplo es nuestro nuevo dado 2.0.

Sensores de inclinación

Implementamos el dado de forma completamente distinta. En el interior trabaja un Arduino Nano por razones de espacio. Mantiene un interruptor on-off, pero no tiene botón: el lanzamiento se hace agitando el cubo completo.

Para ello usamos sensores de inclinación o vibración. Funcionan como un botón: en un tubo de vidrio una bola se mueve; al golpear los contactos, cierra el circuito. Ese contacto, a veces muy breve, se usa como señal. El movimiento de la bola metálica se oye claramente al agitar el sensor.

Normalmente la posición de instalación es decisiva para un sensor de inclinación (debe disparar en un ángulo determinado). En nuestro caso, al agitar la carcasa en cualquier dirección se genera señal. Para mayor fiabilidad usamos dos sensores montados a 90° entre sí, garantizando detección fiable.

Interrupciones hardware

Para detectar la agitación, los pines de los sensores deberían consultarse constantemente en el sketch. Pero dependiendo del momento y de otras tareas, podrían perderse eventos. La solución robusta son interrupciones hardware mediante attachInterrupt(). El Arduino Nano proporciona dos: pines D2 y D3.

Pantalla: Matriz LED 8x8 con MAX7219

En lugar de 7 LEDs individuales, usamos una matriz 8x8 con driver MAX7219 integrada. Ocupa poco espacio, es barata y fácil de programar. Según el proveedor, hay que soldar y ensamblar las piezas; el único error común es colocar la matriz girada 180° en los zócalos (se corrige sacándola y rotándola).

Física simulada: movimiento y rebote

La matriz no solo muestra el resultado final. Ofrece efectos visuales: los seis "ojos" del dado se mueven por la matriz, rebotan en los bordes y pierden velocidad gradualmente. Los sensores de inclinación detectan cuánto se agita y esa información se traduce en "velocidad" inicial para los ojos. Al mostrar el resultado, los ojos ruedan desde su posición aleatoria hasta la correcta en la cara del dado.

Alimentación

Inicialmente usamos dos pilas botón CR2032 (3V cada una). Funcionaban al principio, pero tras pocos minutos la tensión colapsaba. La solución: dos pilas AAA (3V totales) + elevador MT3608 a 5V, conectado al pin VIN del Arduino.

Diseño de carcasa (impresión 3D)

La mejor opción es diseño propio e impresión 3D. Usamos Autodesk Fusion 360 (genial para impresión 3D con Print Studio integrado). La carcasa tiene 3 partes:

Sketch (código Arduino)

Para controlar la matriz LED se necesita la librería LedControl (disponible en playground.arduino.cc).

En el código se definen las coordenadas de los "ojos" del dado en la matriz (cada ojo = 4 puntos LED). El array DicePic[8][6][2] almacena las posiciones para cada cara (1-6) y una posición inicial. ¡Puedes personalizar los patrones!

Variables para posición, dirección y velocidad (X/Y) de cada ojo. Se usan interrupciones en pines 2 y 3 para incrementar velocidad al detectar agitación. El bucle principal mueve los ojos paso a paso, reduciendo velocidad, y tras 2 segundos sin agitación elige un número aleatorio (1-6) y anima la transición a la cara correcta.

Montaje electrónico

La carcasa encaja a presión (holgura 0.1mm) y permite abrir para cambio de pilas.

Código completo (wuerfel8x8.ino)

/* Blog-Artículo: Dado agitado con matriz LED y simulación de movimiento
   https://techpluscode.de/schuettel-wuerfel-mit-led-matrix-und-bewegungssimulation/
   techpluscode.de Copyright 2019 Thomas Angielsky */

#include "LedControl.h"

int PinTiltX = 2;
int PinTiltY = 3;
int PinCLK = 7;
int PinCS = 6;
int PinDIN = 5;

LedControl lc = LedControl(PinDIN, PinCLK, PinCS, 1);

// Coordenadas de los puntos del dado en la matriz LED
int DicePic[8][6][2] = {
  { // Matriz vacía y posición inicial
    {9,9}, {9,8}, {9,7}, {9,6}, {9,5}, {9,4}
  },
  { // Cara 1
    {4,4}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}
  },
  { // Cara 2
    {2,2}, {6,6}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}
  },
  { // Cara 3
    {2,6}, {6,2}, {4,4}, {-1,-1}, {-1,-1}, {-1,-1}
  },
  { // Cara 4
    {2,2}, {2,6}, {6,2}, {6,6}, {-1,-1}, {-1,-1}
  },
  { // Cara 5
    {2,2}, {2,6}, {6,2}, {6,6}, {4,4}, {-1,-1}
  },
  { // Cara 6
    {2,1}, {2,4}, {2,7}, {6,1}, {6,4}, {6,7}
  },
  { // Inicio (fuera de pantalla)
    {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1}
  }
};

float DiceXpos[6], DiceYpos[6];
float DiceXdir[6], DiceYdir[6];
volatile byte DiceXspeed[6], DiceYspeed[6];
int DiceValue;
unsigned long timestamp;
byte Mode;
volatile int shakes = 0;
int ShakesPerSecond = 0;
int step = 0;

void InterruptChecks() {
  shakes++;
  timestamp = millis();
}

void SetSpeedX() {
  if (Mode == 0) {
    for (int i = 0; i < 6; i++) if (DiceXspeed[i] < 255) DiceXspeed[i] += 5;
  }
  InterruptChecks();
}

void SetSpeedY() {
  if (Mode == 0) {
    for (int i = 0; i < 6; i++) if (DiceYspeed[i] < 255) DiceYspeed[i] += 5;
  }
  InterruptChecks();
}

void ShowLed(int x, int y, bool onoff) {
  if (x >= 0 && x < 8 && y >= 0 && y < 8) lc.setLed(0, x, y, onoff);
}

void ShowDot(int x, int y, bool onoff) {
  ShowLed(x-1, y-1, onoff);
  ShowLed(x,   y-1, onoff);
  ShowLed(x-1, y,   onoff);
  ShowLed(x,   y,   onoff);
}

void ShowDicePic(int value) {
  boolean done;
  for (int i = 0; i < 6; i++) {
    DiceXspeed[i] = 100; DiceYspeed[i] = 100;
    DiceXdir[i] = (int(DiceXpos[i]) > DicePic[value][i][0]) ? -1 : (int(DiceXpos[i]) < DicePic[value][i][0]) ? 1 : 0;
    DiceYdir[i] = (int(DiceYpos[i]) > DicePic[value][i][1]) ? -1 : (int(DiceYpos[i]) < DicePic[value][i][1]) ? 1 : 0;
  }
  do {
    for (int i = 0; i < 6; i++) {
      if (int(DiceXpos[i]) != DicePic[value][i][0]) DoStep(DiceXpos[i], DiceXdir[i], DiceXspeed[i], false);
      if (int(DiceYpos[i]) != DicePic[value][i][1]) DoStep(DiceYpos[i], DiceYdir[i], DiceYspeed[i], false);
    }
    lc.clearDisplay(0);
    for (int i = 0; i < 6; i++) ShowDot(int(DiceXpos[i]), int(DiceYpos[i]), true);
    delay(50);
    done = true;
    for (int i = 0; i < 6; i++) {
      if (int(DiceXpos[i]) != DicePic[value][i][0]) done = false;
      if (int(DiceYpos[i]) != DicePic[value][i][1]) done = false;
    }
  } while (!done);
  lc.clearDisplay(0);
  for (int i = 0; i < 6; i++) ShowDot(DicePic[value][i][0], DicePic[value][i][1], true);
}

void DoStep(float &pos, float &dir, volatile byte &sp, bool check) {
  pos += float(sp) / 255 * dir;
  if (check) {
    if (pos > 7) { pos = 7; dir *= -1; }
    if (pos < 1) { pos = 1; dir *= -1; }
  }
  if (sp > 0) sp--;
}

void MoveDots() {
  for (int i = 0; i < 6; i++) {
    DoStep(DiceXpos[i], DiceXdir[i], DiceXspeed[i], true);
    DoStep(DiceYpos[i], DiceYdir[i], DiceYspeed[i], true);
  }
  lc.clearDisplay(0);
  for (int i = 0; i < 6; i++) ShowDot(int(DiceXpos[i]), int(DiceYpos[i]), true);
}

void setup() {
  lc.shutdown(0, false);
  lc.setIntensity(0, 8);
  lc.clearDisplay(0);
  randomSeed(analogRead(0));
  DiceValue = 0;
  for (int i = 0; i < 6; i++) {
    DiceXpos[i] = DicePic[7][i][0];
    DiceYpos[i] = DicePic[7][i][1];
    DiceXdir[i] = random(3) - 1;
    DiceYdir[i] = random(3) - 1;
    DiceXspeed[i] = random(126) + 120;
    DiceYspeed[i] = random(126) + 120;
  }
  pinMode(PinTiltX, INPUT_PULLUP);
  pinMode(PinTiltY, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PinTiltX), SetSpeedX, CHANGE);
  attachInterrupt(digitalPinToInterrupt(PinTiltY), SetSpeedY, CHANGE);
  lc.clearDisplay(0);
  timestamp = millis();
  Mode = 1;
  ShowDicePic(6);
  delay(1000);
  lc.clearDisplay(0);
  Mode = 0;
  Serial.begin(9600);
  step = 0; shakes = 0;
}

void loop() {
  delay(50);
  step++;
  if (step > 20) { step = 0; ShakesPerSecond = shakes; shakes = 0; }
  if (Mode == 0) {
    MoveDots();
    if (millis() - timestamp > 2000) {
      Mode = 1;
      DiceValue = random(6) + 1;
      ShowDicePic(DiceValue);
    }
  }
  if (ShakesPerSecond > 5) Mode = 0;
}

Esquemático

Consulta el esquema de conexiones en la sección "Schematics" (imagen del circuito dibujado a mano).

Proceso de manufactura

  1. Historia, Diseño y Fabricación de Dados: De Herramientas Ancestrales al Juego Moderno
  2. Panel de interruptores y LEDs para Microsoft Flight Simulator 2020 con Arduino
  3. Proyecto Dados Digitales Arduino: Construye tu Propio Generador de Números Aleatorios con LCD
  4. Construye un Spybot controlado por Bluetooth con Arduino
  5. FlickMote: Mando IR por gestos para Smart Home
  6. TV‑B‑Gone DIY con Arduino: Mando universal para apagar cualquier TV
  7. Construye un Reloj Maestro LED Personalizado con Alarma – Visible desde 12 Metros
  8. Construye un juego Tetris con Arduino Nano en matriz LED 16x8 DIY
  9. Find Me: Localizador Inteligente de Objetos con Arduino y Bluetooth
  10. Soluciones de Alimentación Optimizadas para Proyectos Arduino
  11. Controlador Inalámbrico Inteligente de Interruptores: Manipulador Remoto Basado en Arduino