Módulo 4

RMT Remote Control: control de LEDs RGB WS2812

Periféricos Digitales

ESP32 Mecatrónica IoT UNAM

Fundamentos del RMT y LEDs RGB Inteligentes

El RMT (Remote Control Transceiver) del ESP32 es un periférico especializado diseñado para generar y recibir señales con temporización precisa. Este módulo es fundamental para el control de dispositivos que requieren protocolos de comunicación no estándar, como los LEDs RGB direccionables WS2812B, también conocidos como NeoPixels.

El protocolo WS2812B utiliza una codificación específica donde cada bit de datos se representa mediante pulsos de diferente duración:

  • Bit 0: Pulso alto de 350ns ± 150ns, pulso bajo de 800ns ± 150ns
  • Bit 1: Pulso alto de 700ns ± 150ns, pulso bajo de 600ns ± 150ns
  • Reset: Pulso bajo mayor a 50μs para reiniciar la secuencia

En aplicaciones industriales de mecatrónica, estos sistemas de iluminación inteligente son esenciales para:

  • Señalización de estado en líneas de producción
  • Indicadores visuales de alarmas y procesos
  • Sistemas de navegación para robots y AGVs
  • Interfaces hombre-máquina intuitivas
  • Ambientación dinámica en espacios industriales

La ventaja del RMT sobre métodos tradicionales como bit-banging es su capacidad de generar señales precisas sin ocupar tiempo de CPU, permitiendo operaciones concurrentes críticas en sistemas de tiempo real.

Especificaciones Técnicas del RMT en ESP32

Características Hardware
  • 8 canales independientes (0-7)
  • Resolución temporal: 12.5ns (con divisor 1)
  • Buffer de memoria: 64 elementos × 32 bits por canal
  • Soporte para transmisión y recepción
  • Generación automática de pulsos carrier
  • Interrupciones por fin de transmisión/recepción
Especificaciones WS2812B
  • Voltaje de operación: 3.7V - 5.3V
  • Corriente por LED: 60mA (máximo)
  • Frecuencia de datos: 800kHz
  • Profundidad de color: 24 bits (8 bits por canal RGB)
  • Cascada: Hasta 1024 LEDs por puerto
  • Temperatura operativa: -25°C a +80°C

Implementación Profesional con RMT Nativo

Para aplicaciones profesionales, es recomendable utilizar directamente la API del RMT en lugar de librerías de alto nivel, lo que proporciona mayor control y eficiencia.

Sistema Avanzado de Control RGB con RMT Nativo
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// Configuración RMT y Hardware
#define RMT_CHANNEL RMT_CHANNEL_0
#define LED_DATA_PIN 18
#define NUM_LEDS 300
#define RMT_CLK_DIV 2
#define RMT_TICK_10_US (80000000/RMT_CLK_DIV/100000)

// Configuraciones del Sistema
#define MAX_BRIGHTNESS 255
#define DEFAULT_FPS 60
#define PATTERN_BUFFER_SIZE 50

// Definiciones WS2812B
#define WS2812_T0H_NS 350
#define WS2812_T1H_NS 700
#define WS2812_T0L_NS 800
#define WS2812_T1L_NS 600
#define WS2812_RESET_US 50

// Configuración WiFi
const char* ssid = "RGB_LED_System";
const char* password = "Industrial2024";

class AdvancedRGBController {
private:
    // Estructuras de datos
    struct LEDPixel {
        uint8_t r, g, b, w;  // RGBW support
        uint8_t brightness;
        bool enabled;
    };
    
    struct AnimationPattern {
        String name;
        uint16_t duration_ms;
        bool loop;
        std::vector keyframes;
        uint8_t fps;
        bool active;
    };
    
    struct SystemState {
        uint32_t frame_count;
        uint32_t fps_counter;
        unsigned long last_fps_time;
        float cpu_usage;
        uint32_t memory_usage;
        bool auto_brightness;
        uint8_t global_brightness;
    };
    
    // Variables del sistema
    LEDPixel leds[NUM_LEDS];
    rmt_item32_t rmt_buffer[NUM_LEDS * 24];
    WebServer server;
    
    SystemState system_state;
    std::vector patterns;
    uint8_t current_pattern = 0;
    
    // Efectos y animaciones
    float phase = 0.0;
    uint32_t effect_timer = 0;
    
    // Sensor de luz ambiente
    uint8_t ambient_sensor_pin = 36;
    uint16_t ambient_light_level = 512;

public:
    AdvancedRGBController() : server(80) {
        memset(leds, 0, sizeof(leds));
        memset(&system_state, 0, sizeof(system_state));
        system_state.global_brightness = 128;
        system_state.auto_brightness = false;
    }
    
    bool initialize() {
        Serial.begin(115200);
        Serial.println("=== SISTEMA AVANZADO RGB INICIANDO ===");
        
        // Inicializar SPIFFS
        if (!SPIFFS.begin(true)) {
            Serial.println("Error: SPIFFS no inicializado");
            return false;
        }
        
        // Configurar RMT
        if (!setupRMT()) {
            Serial.println("Error: RMT no configurado");
            return false;
        }
        
        // Inicializar WiFi
        setupWiFi();
        
        // Configurar servidor web
        setupWebServer();
        
        // Cargar patrones predefinidos
        loadDefaultPatterns();
        
        // Configurar sensor de luz
        pinMode(ambient_sensor_pin, INPUT);
        
        Serial.println("Sistema RGB avanzado inicializado");
        return true;
    }
    
private:
    bool setupRMT() {
        rmt_config_t config = RMT_DEFAULT_CONFIG_TX(LED_DATA_PIN, RMT_CHANNEL);
        config.clk_div = RMT_CLK_DIV;
        config.mem_block_num = 2;  // Usar 2 bloques de memoria
        config.tx_config.loop_en = false;
        config.tx_config.carrier_en = false;
        config.tx_config.idle_output_en = true;
        config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
        
        esp_err_t ret = rmt_config(&config);
        if (ret != ESP_OK) {
            Serial.printf("Error configurando RMT: %s\n", esp_err_to_name(ret));
            return false;
        }
        
        ret = rmt_driver_install(RMT_CHANNEL, 0, 0);
        if (ret != ESP_OK) {
            Serial.printf("Error instalando driver RMT: %s\n", esp_err_to_name(ret));
            return false;
        }
        
        Serial.println("RMT configurado correctamente");
        return true;
    }
    
    void pixelToRMTBuffer(uint8_t r, uint8_t g, uint8_t b, uint16_t index) {
        uint32_t color = (g << 16) | (r << 8) | b;  // GRB para WS2812B
        uint16_t buffer_offset = index * 24;
        
        for (int bit = 23; bit >= 0; bit--) {
            bool bit_val = (color >> bit) & 1;
            
            if (bit_val) {
                // Bit 1
                rmt_buffer[buffer_offset + (23 - bit)].level0 = 1;
                rmt_buffer[buffer_offset + (23 - bit)].duration0 = 
                    WS2812_T1H_NS / (RMT_TICK_10_US * 10);
                rmt_buffer[buffer_offset + (23 - bit)].level1 = 0;
                rmt_buffer[buffer_offset + (23 - bit)].duration1 = 
                    WS2812_T1L_NS / (RMT_TICK_10_US * 10);
            } else {
                // Bit 0
                rmt_buffer[buffer_offset + (23 - bit)].level0 = 1;
                rmt_buffer[buffer_offset + (23 - bit)].duration0 = 
                    WS2812_T0H_NS / (RMT_TICK_10_US * 10);
                rmt_buffer[buffer_offset + (23 - bit)].level1 = 0;
                rmt_buffer[buffer_offset + (23 - bit)].duration1 = 
                    WS2812_T0L_NS / (RMT_TICK_10_US * 10);
            }
        }
    }
    
public:
    void updateLEDs() {
        // Preparar buffer RMT
        for (uint16_t i = 0; i < NUM_LEDS; i++) {
            uint8_t r = (leds[i].r * leds[i].brightness * system_state.global_brightness) / (255 * 255);
            uint8_t g = (leds[i].g * leds[i].brightness * system_state.global_brightness) / (255 * 255);
            uint8_t b = (leds[i].b * leds[i].brightness * system_state.global_brightness) / (255 * 255);
            
            pixelToRMTBuffer(r, g, b, i);
        }
        
        // Transmitir datos
        esp_err_t ret = rmt_write_items(RMT_CHANNEL, rmt_buffer, NUM_LEDS * 24, false);
        if (ret != ESP_OK) {
            Serial.printf("Error transmitiendo RMT: %s\n", esp_err_to_name(ret));
        }
        
        system_state.frame_count++;
    }
    
    void setSolidColor(uint8_t r, uint8_t g, uint8_t b) {
        for (uint16_t i = 0; i < NUM_LEDS; i++) {
            leds[i].r = r;
            leds[i].g = g;
            leds[i].b = b;
            leds[i].brightness = 255;
            leds[i].enabled = true;
        }
        updateLEDs();
    }
    
    void runRainbowEffect(float speed = 1.0) {
        phase += speed * 0.01;
        if (phase > 2 * PI) phase = 0;
        
        for (uint16_t i = 0; i < NUM_LEDS; i++) {
            float hue = (float(i) / NUM_LEDS) * 2 * PI + phase;
            
            leds[i].r = (sin(hue) + 1) * 127.5;
            leds[i].g = (sin(hue + 2*PI/3) + 1) * 127.5;
            leds[i].b = (sin(hue + 4*PI/3) + 1) * 127.5;
            leds[i].brightness = 255;
            leds[i].enabled = true;
        }
        updateLEDs();
    }
    
    void processMainLoop() {
        static unsigned long last_update = 0;
        static unsigned long last_fps_calc = 0;
        static uint32_t fps_counter = 0;
        
        unsigned long now = millis();
        
        // Control de FPS
        uint16_t target_interval = 1000 / DEFAULT_FPS;
        if (now - last_update >= target_interval) {
            // Ejecutar patrón activo
            if (current_pattern < patterns.size() && patterns[current_pattern].active) {
                executeCurrentPattern();
            }
            
            last_update = now;
            fps_counter++;
        }
        
        // Calcular FPS cada segundo
        if (now - last_fps_calc >= 1000) {
            system_state.fps_counter = fps_counter;
            fps_counter = 0;
            last_fps_calc = now;
        }
    }
};

// Instancia global del controlador RGB
AdvancedRGBController rgb_controller;

void setup() {
    if (!rgb_controller.initialize()) {
        Serial.println("Error crítico en inicialización");
        while (1) {
            delay(1000);
        }
    }
    
    Serial.println("\n=== SISTEMA RGB AVANZADO INICIADO ===");
    Serial.printf("LEDs configurados: %d\n", NUM_LEDS);
    Serial.println("RMT nativo habilitado");
    Serial.println("====================================\n");
}

void loop() {
    rgb_controller.processMainLoop();
}

Ejercicios Prácticos Avanzados

1

RMT Nativo Básico

Básico 25 min

Implementa control directo del RMT sin librerías externas para controlar un LED WS2812B individual con temporización precisa.

Objetivos:
  • Configurar manualmente el canal RMT
  • Generar señales con timing exacto
  • Implementar protocolo WS2812B desde cero
  • Verificar señales con osciloscopio
2

Sistema Multi-Canal

Intermedio 40 min

Desarrolla un sistema que controle múltiples tiras de LEDs independientes usando diferentes canales RMT simultáneamente.

Características:
  • Control de 4 tiras independientes
  • Sincronización perfecta entre canales
  • Efectos coordinados multi-zona
  • Gestión eficiente de memoria
3

Audio Visualización

Avanzado 60 min

Crea un visualizador de audio que reaccione a música en tiempo real usando análisis FFT y patrones dinámicos.

Funcionalidades:
  • Captura de audio por micrófono I2S
  • Análisis FFT en tiempo real
  • Mapeo de frecuencias a colores
  • Efectos de inercia y suavizado
4

Sistema IoT Distribuido

Profesional 90 min

Desarrolla un sistema distribuido con múltiples nodos ESP32 sincronizados para crear instalaciones de gran escala.

Componentes:
  • Protocolo de sincronización custom
  • Nodo maestro y esclavos
  • Compensación de latencia de red
  • Dashboard de control centralizado

Referencias Técnicas y Documentación

Documentación Oficial
Herramientas de Desarrollo
  • ESP32 Arduino Core: Framework de desarrollo
  • PlatformIO: IDE profesional
  • Oscilloscope Analysis: Verificación de señales
  • MQTT Explorer: Cliente MQTT para debugging