“Pulsar ESP32 para control de Animaciones con Sensores Táctiles DevLab”

Banner.Pulsar-ESP32-Sensores-Táctiles-DevLab

En este tutorial, vamos a dar vida a un letrero animado utilizando dos módulos de LEDs RGB WS28B20. El corazón del proyecto es la tarjeta UNIT Pulsar ESP32-C6, y para llevar la interactividad al siguiente nivel, utilizaremos sensores capacitivos Touch TTP223B. Prepárate para descubrir una nueva forma de interactuar con tus proyectos electrónicos.

Materiales necesarios

Conexiones
Para formar nuestra pantalla de 32×8, utilizaremos dos Módulos Matriz 8×16 LED RGB (WS28B20-1010). La interconexión se realiza mediante cables Arnés QWIIC de 4 pines (1mm). El flujo de datos debe ser el siguiente: la señal parte de la UNIT Pulsar ESP32-C6 y se conecta al puerto de entrada (IN) de la primera matriz. Posteriormente, conectamos un segundo arnés desde la salida (OUT) de esta primera matriz hacia la entrada (IN) del segundo módulo.

Configuración de los Sensores Táctiles

  • Utilizaremos dos Sensores Capacitivos TTP223B, conectados mediante cables SH1.0mm (28 AWG). Es fundamental observar la serigrafía en la tarjeta para una conexión correcta:
  • VCC: Conectado a la salida de 3.3V de la UNIT Pulsar.
  • GND: Ambos sensores pueden compartir el mismo pin de tierra (GND) de la tarjeta.
  • OUT: El pin de señal de cada sensor se conectará a los pines  4 y 5 de la UNIT Pulsar, respectivamente.
  • Nota: El cuarto cable del arnés SH1.0 no será utilizado en esta ocasión, por lo que se recomienda dejarlo sin conexión o aislado
Letrero LED Inteligente Control de Animaciones con Sensores Táctiles_1

Instalación de la Biblioteca

Para poder usar nuestro modulo instalaremos las siguientes bibliotecas  

  • Adafruit_GFX.h
  • Adafruit_NeoMatrix.h
  • Adafruit_NeoPixel.h

Para cargarlas al Arduino IDE sigue los siguientes pasos:

  1. Abre el entorno de desarrollo Arduino en tu computadora.
  2. Ve a “Sketch” > “Incluir Biblioteca” > “Gestor de Bibliotecas”.
  3. Busca Adafruit_GFX y Adafruit_NeoMatrix, Adafruit_NeoPixel  selecciona la biblioteca y da clic en instalar.

Realización del Código

El siguiente programa nos permitirá asignar animaciones a cada uno de los sensores touch. estas se visualizaran en los modulos RGB.

En los comentarios del codigo se describe el funcionamiento para que puedas modificarlo y agregar las animaciones que mas te agraden.  

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>

#define PIN 6   // Comunicacion al modulo RGB
#define ANCHO_TOTAL 32 // total de ancho de hambos modulos
#define ALTO_TOTAL 8  // 

// MATRIZ: 2 paneles 16x8 formando 32x8
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(
  16, 8,           // Tamaño de CADA panel
  2, 1,            // 2 paneles de ancho, 1 de alto
  PIN,
  NEO_MATRIX_BOTTOM + NEO_MATRIX_LEFT +    
  NEO_MATRIX_COLUMNS +                  // Los datos avanzan por columnas
  NEO_MATRIX_PROGRESSIVE +              // Forma instalada de los leds
  NEO_TILE_TOP + NEO_TILE_LEFT +        // El primer PANEL está arriba a la izquierda
  NEO_TILE_ROWS,                // Los paneles están puestos de forma HORIZONTAL
  NEO_GRB + NEO_KHZ800             
);

const int TOUCH_PIN1 = 15;         // Sensor touch 1 
const int TOUCH_PIN2 = 19;         // Sensor touch 2 

volatile bool sensor1 = false;     // bandera , sabe cuando es tocado el sensor 
volatile bool sensor2 = false;     // bandera , sabe cuando es tocado el sensor 

// INTERRUPCIONES
void IRAM_ATTR pulso1() { sensor1 = true; }
void IRAM_ATTR pulso2() { sensor2 = true; }


// ---------- ANIMACIÓN PAISAJE ----------
void paisajeAnimado(int duracionMS) {
  unsigned long inicio = millis();
  int offset = 0;
  int anchoTotal = matrix.width(); //  32 leds

  while (millis() - inicio < duracionMS) {
    if (sensor1) { sensor1 = false; return; }
    matrix.fillScreen(0);

    // MONTAÑAS: Dibujamos 4 montañas para cubrir el flujo de 32 píxeles
    for (int i = 0; i < 4; i++) {
      // Cada montaña tiene una base de 12. 
      // Las espaciamos y restamos el offset para que caminen a la izquierda.
      int base = (i * 12) - offset; 
      
      matrix.fillTriangle(
        base, 7,      
        base + 6, 0,  
        base + 12, 7, 
        matrix.Color(0, 60, 0)
      );
    }

    matrix.drawFastHLine(0, 7, 32, matrix.Color(40, 20, 0));
    matrix.show();

    offset++;
    // Reiniciamos solo cuando la primera montaña ya salió y la última entró
    if (offset >= 12) offset = 0; 

    delay(60);
  }
}

// ---------- ANIMACIÓN pista de carreras ----------
void pistaCarreras(int duracionMS) {
  unsigned long inicio = millis();
  int offset = 0;

  while (millis() - inicio < duracionMS) {
    if (sensor1) { sensor1 = false; return; }

    matrix.fillScreen(0);

    // 1. PASTO (Bordes verdes arriba y abajo)
    matrix.drawFastHLine(0, 0, 32, matrix.Color(0, 40, 0)); // Borde superior
    matrix.drawFastHLine(0, 7, 32, matrix.Color(0, 40, 0)); // Borde inferior

    // 2. ASFALTO (Gris oscuro en el centro)
    matrix.fillRect(0, 1, 32, 6, matrix.Color(15, 15, 15));

    // 3. LÍNEAS DE LA CARRETERA (Blancas y discontinuas)
    for (int i = 0; i < 6; i++) {
      // El movimiento del offset hace que las líneas "vengan" hacia nosotros
      int lineaX = (i * 8) - (offset % 8);
      matrix.drawFastHLine(lineaX, 4, 3, matrix.Color(150, 150, 150));
    }

    // 4. EL COCHE (Visto desde arriba)
    // El coche se mueve un poco de arriba a abajo para simular que conduce
    int cocheY = (offset % 20 < 10) ? 2 : 5; // Cambia de carril cada 10 ciclos
    
    // Cuerpo del coche (Rojo)
    matrix.fillRect(4, cocheY, 4, 2, matrix.Color(200, 0, 0)); 
    // Spoilers o detalles (Negro/Gris)
    matrix.drawPixel(4, cocheY, matrix.Color(0, 0, 0));
    matrix.drawPixel(4, cocheY + 1, matrix.Color(0, 0, 0));
    // Luces traseras (Casi siempre se ven desde atrás, pero aquí ponemos brillo)
    matrix.drawPixel(3, cocheY, matrix.Color(100, 0, 0));
    matrix.drawPixel(3, cocheY + 1, matrix.Color(100, 0, 0));

    // 5. OTRO COCHE (Obstáculo que rebasamos)
    int rivalX = 32 - (offset % 64);
    if (rivalX > -4) {
      matrix.fillRect(rivalX, 3, 4, 2, matrix.Color(0, 0, 200)); // Coche azul
    }

    matrix.show();
    offset++;
    delay(50); // Velocidad rápida para sensación de carrera
  }
}


void setup() {

  Serial.begin(115200); // comuniacion serial 
  matrix.begin(); 
  matrix.setTextWrap(false);
  matrix.setBrightness(40);

  pinMode(TOUCH_PIN1, INPUT_PULLDOWN); // activa la resistencia interna
  pinMode(TOUCH_PIN2, INPUT_PULLDOWN); // activa la resistencia interna

  attachInterrupt(digitalPinToInterrupt(TOUCH_PIN1), pulso1, RISING);
  attachInterrupt(digitalPinToInterrupt(TOUCH_PIN2), pulso2, RISING);

  matrix.fillScreen(0); // reinicia los leds
  matrix.show();  // manda la animacion
}

// ---------- LOOP ----------
void loop() {

  if (sensor1) { // detecta que el sensor fue pulsado

    Serial.println("Sensor 1"); // imprime mensaje del sensor pulsado
    sensor1 = false;          // realizar el cambio de la bandera 
    paisajeAnimado(5000);     // llamado de la animacion y duracion
    matrix.fillScreen(0);      // reinicia los leds 
    matrix.show();              // manda la animacion   
  }

  if (sensor2) {  // detecta que el sensor fue pulsado
    Serial.println("Sensor 2"); // imprime mensaje del sensor pulsado
    sensor2 = false;// realizar el cambio de la bandera 
   pistaCarreras(4000);// llamado de la animacion y duracion
    matrix.fillScreen(0); // reinicia los leds
    matrix.show();   // manda la animacion
  }
}

Demostración

Conclusiones

Esta práctica demuestra la versatilidad de la UNIT Pulsar ESP32-C6 para gestionar sistemas de visualización extendidos.

A diferencia de las matrices tradicionales, estos módulos destacan por su direccionamiento individual, lo que nos permite controlar el color y brillo de cada uno de los 128 LEDs de forma independiente. 

Deja un comentario

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