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

Copito de Nieve LED DIY con Arduino: Pantalla de 30 LEDs Programable

Componentes y materiales

Componente Cantidad
Arduino Nano R3 1

Sobre el proyecto

El copito de nieve consta de 30 LEDs agrupados en 17 segmentos independientes controlables por separado desde el microcontrolador Arduino Nano. Cada grupo puede atenuarse con PWM para crear hermosas animaciones.

Herramientas

Solo necesitas soldador, estaña y alicates.

Construcción

Primero elige un patrón. Elegí un copito de nieve simple y lo imprimí al tamaño para que quepa el Arduino Nano dentro del hexágono central.

La estructura de soporte (que también actúa como cableado) se crea con varillas de latón de 0.8mm soldadas con estaño. Usé 2m en total. ¿Por qué freeform? Porque siempre quise probarlo y pone a prueba tu paciencia y habilidad.

Primero creé el hexágono central doblando una varilla y soldando los extremos. Añadiendo 6 varillas más a los vértices completé el cableado de tierra (cátodos); todos los cátodos de LEDs se sueldan a esta estructura para formar el patrón del copito. Lo difícil fue colocar los LEDs SMD; me ayudé con una plantilla de cartón y cinta doble cara.

Luego coloqué el Arduino Nano bajo la estructura central dejando espacio para 3 capas de varillas que conectan pines del microcontrolador a los ánodos de los LEDs. Requiere mucha paciencia: evitar cortocircuitos, añadir resistencias limitadoras y que quede estético.

LEDs de las "hojas" van cada uno a un pin de salida del Arduino. LEDs de las "ramas" se agrupan de dos en dos a pines PWM. LEDs del "núcleo" también de dos en dos al resto de pines. El Arduino Nano tiene 18 pines de salida (A6 y A7 solo entrada) y necesito uno para sensor táctil, quedándome 17 pines; así que dos pares de LEDs del núcleo se unen en grupo de 4. Uso resistencias 220Ω limitando corriente a ~8mA por pin (240mA total, límite seguro del ATmega328 es 200mA pero funciona).

Sensor táctil

Para interactuar añadí otra varilla de latón como sensor capacitivo. Usé la gran librería y tutorial de Paul Stoffregen. El sensor táctil permite: cambiar animación, encender/apagar, efecto "brillo" al tocar, etc.

Nota final

¡Verifica todos los diodos antes de soldar! Será muy difícil cambiarlos si están rotos o con polaridad invertida.

Código

Originalmente pensé que solo podría atenuar LEDs de ramas (pines PWM hardware). Pero existe una librería PWM por software excelente que permite usar todos los pines como PWM. ¡Esto abre infinitas posibilidades de animación! Código adjunto con las primeras animaciones.

Código fuente (snowflake.ino)

#include "SoftPWM.h"
#include <ADCTouchSensor.h>

byte edgeLedPins[] = {13, A4, A5, 2, 8, 12};
byte middleLedPins[] = {10, 6, 3, 5, 9, 11};
byte innerLedPins[] = {A2, A3, A1, 4, 7, A1}; // Nota: A1 repetido en original
ADCTouchSensor touchSensor = ADCTouchSensor(A0, 1);

void setup() {
  Serial.begin(115200);
  SoftPWMBegin();
}

byte animation = 0;
long touchAt = 0;

void loop() {
  switch (animation) {
    case 0: _fill(100); break;
    case 1: shinyAnimation(); break;
    case 2: circleAnimation(); break;
    case 3: loopAnimation(); break;
    case 4: fireworkAnimation(); fireworkAnimation(); fireworkAnimation(); fireworkAnimation(); fireworkAnimation(); fireworkAnimation(); animation++; break;
    case 5: smileAnimation(); break;
    default: animation = 0; break;
  }
  int touchValue = touchSensor.read();
  if (touchAt + 2000 < millis() && touchValue > 1000) {
    touchAt = millis(); animation++; _fill(0);
  }
}

// Animación fuegos artificiales
void fireworkAnimation() {
  for (int i=0; i<4; i++) { SoftPWMSet(innerLedPins[i], 100); delay(100); }
  SoftPWMSet(innerLedPins[4], 100);
  for (int i=0; i<6; i++) SoftPWMSet(middleLedPins[i], 255);
  delay(50);
  for (int i=0; i<6; i++) { SoftPWMSet(innerLedPins[i], 0); SoftPWMSet(edgeLedPins[i], 255); }
  delay(50);
  for (int i=0; i<6; i++) SoftPWMSet(middleLedPins[i], 0);
  delay(50);
  _fill(0);
}

// Animación sonrisa
void smileAnimation() {
  SoftPWMSet(innerLedPins[1], 100); SoftPWMSet(innerLedPins[3], 100);
  SoftPWMSet(middleLedPins[0], 255); SoftPWMSet(middleLedPins[5], 255);
  SoftPWMSet(edgeLedPins[2], 255); SoftPWMSet(edgeLedPins[3], 255);
  delay(2000);
  SoftPWMSet(innerLedPins[1], 0); SoftPWMSet(innerLedPins[3], 0);
  delay(100); SoftPWMSet(innerLedPins[1], 100); SoftPWMSet(innerLedPins[3], 100);
  delay(100); SoftPWMSet(innerLedPins[1], 0); SoftPWMSet(innerLedPins[3], 0);
  delay(100);
}

// Animación círculos concéntricos
byte circleState[] = {100, 55, 10};
byte circleStateAnimation[] = {1, 1, 1};
void circleAnimation() {
  for (int i=0; i<3; i++) {
    if (circleState[i] >= 100) circleStateAnimation[i] = -1;
    else if (circleState[i] <= 10) circleStateAnimation[i] = 1;
    circleState[i] += circleStateAnimation[i];
  }
  for (int i=0; i<6; i++) {
    SoftPWMSet(innerLedPins[i], circleState[0]);
    SoftPWMSet(middleLedPins[i], circleState[1]);
    SoftPWMSet(edgeLedPins[i], circleState[2]);
  }
  delay(5);
}

// Animación onda
byte waveState[] = {100, 55, 10, 10, 55, 100};
byte waveStateAnimation[] = {1, 1, 1, -1, -1, -1};
void waveAnimation() {
  for (int i=0; i<6; i++) {
    if (waveState[i] >= 100) waveStateAnimation[i] = -1;
    else if (waveState[i] <= 10) waveStateAnimation[i] = 1;
    waveState[i] += waveStateAnimation[i];
  }
  for (int i=0; i<6; i+=2) {
    SoftPWMSet(innerLedPins[i], waveState[0]); SoftPWMSet(middleLedPins[i], waveState[1]);
    SoftPWMSet(edgeLedPins[i], waveState[2]);
    SoftPWMSet(innerLedPins[i+1], waveState[3]); SoftPWMSet(middleLedPins[i+1], waveState[4]);
    SoftPWMSet(edgeLedPins[i+1], waveState[5]);
  }
  delay(10);
}

// Animación bucle
byte loopCounter = 0; byte loopState = 150;
void loopAnimation() {
  SoftPWMSet(innerLedPins[loopCounter], loopState);
  SoftPWMSet(middleLedPins[loopCounter], loopState);
  SoftPWMSet(edgeLedPins[loopCounter], loopState);
  loopCounter = _nextIndex(loopCounter, 1);
  if (loopCounter == 0) loopState = (loopState == 150 ? 0 : 150);
  delay(100);
}

// Animación encendido lento aleatorio
byte slowOnCounter = 0; byte slowOnState = 150;
void slowOnAnimation() {
  byte randomLed = random(0, 18);
  if (randomLed < 6) SoftPWMSet(innerLedPins[randomLed], slowOnState);
  else if (randomLed < 12) SoftPWMSet(middleLedPins[randomLed-6], slowOnState);
  else SoftPWMSet(edgeLedPins[randomLed-12], slowOnState);
  slowOnCounter++;
  if (slowOnCounter >= 50) { slowOnCounter = 0; slowOnState = (slowOnState == 150 ? 0 : 150); }
  delay(50);
}

// Animación brillo (shiny)
byte shinyState[] = {0, 100, 0, 100, 0, 100};
byte shinyStateAnimation[] = {1, 1, 1, 1, 1, 1};
byte shinyCounter = 0;
void shinyAnimation() {
  for (int i=0; i<6; i++) {
    if (shinyState[i] >= 100) shinyStateAnimation[i] = -1;
    else if (shinyState[i] <= 0) shinyStateAnimation[i] = 1;
    shinyState[i] += shinyStateAnimation[i];
    SoftPWMSet(edgeLedPins[i], shinyState[i]);
  }
  shinyCounter++;
  if (shinyCounter > 10) {
    shinyCounter = 0;
    for (byte r=random(1,3); r>0; r--) {
      byte randomLed = random(0,12);
      if (randomLed < 6) SoftPWMSet(innerLedPins[random(0,6)], 255);
      else SoftPWMSet(middleLedPins[random(0,6)], 255);
    }
  } else {
    for (int i=0; i<6; i++) { SoftPWMSet(innerLedPins[i], 20); SoftPWMSet(middleLedPins[i], 20); }
  }
  delay(30);
}

// Animación desvanecimiento
byte fadingState[] = {0, 100, 0, 100, 0, 100};
byte fadingStateAnimation[] = {1, 1, 1, 1, 1, 1};
void fadingAnimation() {
  for (int i=0; i<6; i++) {
    if (fadingState[i] >= 100) fadingStateAnimation[i] = -1;
    else if (fadingState[i] <= 0) fadingStateAnimation[i] = 1;
    fadingState[i] += fadingStateAnimation[i];
    SoftPWMSet(edgeLedPins[i], fadingState[i]);
    SoftPWMSet(middleLedPins[_nextIndex(i,1)], fadingState[i]);
    SoftPWMSet(innerLedPins[i], 50);
  }
  delay(20);
}

// Funciones auxiliares
void _fill(byte value) {
  for (int i=0; i<6; i++) {
    SoftPWMSet(edgeLedPins[i], value);
    SoftPWMSet(middleLedPins[i], value);
    SoftPWMSet(innerLedPins[i], value);
  }
}
byte _prevIndex(short index, byte step) {
  index -= step; while (index < 0) index += 6; return index;
}
byte _nextIndex(short index, byte step) {
  index += step; while (index > 5) index -= 6; return index;
}

Esquemáticos

Diagrama de conexiones incluido.

Proceso de manufactura

  1. El rodillo de cocina: de los orígenes etruscos a la artesanía moderna
  2. La Evolución y la Artesanía de los Bolos de Bolera Modernos
  3. De los Fíbulas Antiguos a la Fabricación Moderna: El Imperdible Explicado
  4. Construye un Cable Breakout I/O y Adaptador Plugboard para Raspberry Pi – Guía Paso a Paso
  5. Arte LED Triangular Dinámico: Construye una Pantalla RGB de 45 LEDs con Arduino UNO
  6. Proyecto Sensor DHT11 Temperatura y Humedad con Indicadores LED y Altavoz Piezo
  7. Ball Grid Array (BGA): Qué Es y Por Qué Importa
  8. Cómo los pasadores de espiga mantienen sus proyectos precisos: usos en muebles, estanterías y maquinaria
  9. Materiales de los pasadores cilíndricos: Madera, metal y plástico explicados
  10. 5 factores clave para seleccionar los pasadores (dowel pins) adecuados
  11. TinyML: Ingenieros se unen para estandarizar IA en sistemas de ultra-bajo consumo