esp32-pixel

Juego con ESP32 y una matriz LED conectado a Twitch

Hoy vamos a ver un proyecto divertido para hacer como una comunidad de Twitch. Se trata de un juego colaborativo en el que los usuarios pueden escribir píxeles de colores en una matriz LED a través de comandos escritos en el chat.

Si recordáis hace unos meses hubo un juegos publicado en reddit que se hizo muy popular que consistía en que los usuarios tenían que escribir un pixel de color.

De esta forma entre todos los usuarios del mundo se fueron formando de forma colaborativa dibujos e imágenes. Fue un evento que duró un par de días y fue muy entretenido.

La idea es hacer algo parecido, pero controlado a traves de comandos del chat de Twitch, y ponerlo como un overlay en OBS… Nada especialmente complicado. De hecho varios streamers hicieron desarrollos similares.

esp32-pixel-game

Pero, evidentemente, siendo que nuestro blog es un sitio dedicado a la electrónica y al “cacharreo”, nosotros vamos a dar una vuelta para hacer el proyecto algo más interesante.

Así que vamos a conectar una matriz LED de WS2812B de 16 x 16 píxeles conectada a un ESP32. Este se controlará por WiFi, mediante una aplicación de nET6 conectada al chat de Twitch, y la comunicación la realizaremos a través de MQTT.

Para ello, en entradas anteriores ya hemos visto cómo hacer un bot de Twitch en NET6, como usar MQTT desde una aplicación de NET6, y cómo controlar una matriz LED WS2812B de forma muy sencilla desde un ESP32

Con eso tenemos todos los componentes necesarios para realizar el proyecto ello. Yo voy a utilizar un M5StickC Plus, porque ya sabéis que me gusta este dispositivo. Pero cualquier ESP32, Arduino con WiFi, o similar, os va a servir igualmente.

Código NET6

Empezamos viendo el código con la parte de backend. En en nuestro caso está hecha con una aplicación en NET6. Recordemos que NET6 es compatible con múltiples sistemas, que incluyen en Windows y Linux. Por lo que sería posible alojar la aplicación backend en una Raspberry Pi, por ejemplo.

Ya vimos lo básico de cómo crear un bot en NET6 que se conecte a Twitch en esta entrada. Por tanto solo vamos a poner la parte relativa a gestionar a la gestión del comando por Twitch, y a su envío por MQTT. No obstante os dejo todo el código en un repositorio al final de la entrada.

La parte que nos interesa del bot de Twitch es el handler del evento de comando recibido, que tiene la siguiente forma.

if (e.Command.CommandText == "pixel" && e.Command.ArgumentsAsList.Count == 3)
{
    string x = e.Command.ArgumentsAsList[0];
    string y = e.Command.ArgumentsAsList[1];
    string color = e.Command.ArgumentsAsList[2];

    bool valid_x = int.TryParse(x, out int x_position);
    bool valid_y = int.TryParse(y, out int y_position);
    bool valid_color = ColorMapper.TryParse(color, out string color_hex);

    if (valid_x && valid_y && valid_color)
    {
        if (x_position >= 0 &amp;&amp; x_position < GRID_SIZE_X &amp;&amp;
            y_position >= 0 &amp;&amp; y_position < GRID_SIZE_Y)
        {
            SendByMqtt_AddPixel(x_position, y_position, color_hex);
        }
    }                
}
else if (e.Command.CommandText == "clear")
{
    SendByMqtt_Clear();
}
else if (e.Command.CommandText == "help")
{
    client.SendMessage(e.Command.ChatMessage.Channel, "DarkestRed - Red - LightRed - Orange - Yellow - Paleyellow - DarkGreen - Green - LightGreen - DarkTeal - Teal - LightTeal - DarkBlue - Blue - LightBlue - Indigo - Periwinkle - Lavender - DarkPurple - Purple - Palepurple - Magenta - Pink - LightPink - DarkBrown - Brown - Beige - Black - Darkgray - Gray - LightGray - White");
}

Básicamente estamos comprobando que los datos recibidos por el comando son correctos, y en caso afirmativo ejecutamos el comando clear o el comando set, con los parámetros oportunos.

En función del comando, realizamos el envío por MQTT. Para ello usamos las funciones relativas al envío a través de MQTT que son las siguientes.

private void SendByMqtt_AddPixel(int x_position, int y_position, string color)
{
    dynamic product = new ExpandoObject();
    product.x = x_position;
    product.y = y_position;
    product.c = Utils.ColorUtils.Hex_to_RGB565(color);

    string json = JsonConvert.SerializeObject(product);
    clientMqtt.Publish("twitch/pixel/set", Encoding.UTF8.GetBytes(json), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
}

private void SendByMqtt_Clear()
{
    clientMqtt.Publish("twitch/pixel/clear", Encoding.UTF8.GetBytes("clear"), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
}

Código JS

Por otro lado tenemos la aplicación en frontend en HTML, que vamos a usar como overlay en el OBS. ES una web muy sencilla, formada por un canvas y un script con las funciones necesarias para hacer el dibujo. Tenéis el código en repositorio, aquí nuevamente nos encargaremos únicamente de la parte relativa a la comunicación en el JavaScript.

function onMessageArrived(message) {   
    let json = JSON.parse(message.payloadString);
    let command = json.command;

    if(command === "clear")
    {
        clearCanvas();
    }
    else if(command === "set")
    {
        let x = json.x;
        let y = json.y;
        let color = json.color;
        drawPixel(x, y, color);
    }
}

En la parte relativa a la comunicación simplemente recibimos el comando a través de MQTT, parseamos el json recibidas. Con ello, ejecutamos las funciones de dibujado en el canvas oportunas, definidas en su propio fichero independiente de JavaScript.

Código ESP32

Finalmente tenemos la parte que más nos interesa para esta entrada, que es la parte relativa al ESP32 y la Matrix LED WS2812B. Nuevamente tenéis todo el código en el repositorio, aquí vamos a centrarnos en la parte de gestión del mensaje recibido por MQTT.

void OnMqttReceived(char* topic, byte* payload, unsigned int length)
{
  String topicStr = topic;
  content = GetPayloadContent((char*)payload, length);

  if (topicStr == "twitch/pixel/clear")
  {
    ClearScreen();
  }
  if (topicStr == "twitch/pixel/set")
  {
    StaticJsonDocument<200> doc;
    DeserializationError error = deserializeJson(doc, content);
    if (error) return;

    uint8_t x = doc["x"];
    uint8_t y = doc["y"];
    uint32_t color = doc["rgb565"];
    DrawPixel(x, y, color);
  }
}

Como vemos, en el fondo es muy similar al caso del javascript. De forma resumida, recibimos el Json a través del topic oportuno, obtenemos los parámetros del Json, y realizamos las acciones oportunas para dibujar en la pantalla TFT y enviar a la matriz Led.

Conclusión

Así de sencillo hemos visto cómo hacer un juego colaborativo que se conecte al chat de twitch para hacer un tablero similar al de pixel Reddit. Para ello Hemos realizado una aplicación en NET6 un overlay en HTML y conectado un ESP32 con una TFT y una matriz led RGB de 16 x 16 pixels.

La parte complicada la comunicación la hemos realizado a traves de MQTT, lo cuál ha hecho que sea una parte muy sencilla. Pese a ello, un un proyecto representativo de lo que podríamos encontrar en una aplicación de IoT real, aunque en este caso el servicio de notificaciones estaría alojado en la nube (en una VPS, en AWS, Azure… etc). Pero la arquitectura es similar.

Un proyecto interesante y divertido para realizar hacer tanto como proyecto de electrónica, como ejercicio de integración con una aplicación de terceros (en este caso, como un bot de Twitch).

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github. github-full