Создание "Эффекта Матрицы" на ESP32-C3 и WS2812B​

2 февраля 2025

В мире программирования и электроники даже простые задачи могут потребовать значительных усилий, особенно если речь идет о создании визуальных эффектов. Одним из таких эффектов является знаменитый "эффект Матрицы" — падающие строки зеленых символов, которые стали визитной карточкой этого фильма. Я решил воссоздать этот эффект на светодиодной матрице 8x8, используя светодиоды WS2812 и микроконтроллер ESP32 C3. Однако, как это часто бывает, задача оказалась сложнее, чем я ожидал. В этой статье я расскажу о своем опыте и о том, как ChatGPT помог мне справиться с этой задачей. 

Поиск готового решения

Первым шагом я решил поискать готовое решение в интернете. Я перерыл множество форумов, но, к своему удивлению, не нашел ничего подходящего. Оказалось, что реализация "эффекта Матрицы" на светодиодной матрице WS2812 — не такая уж популярная задача, как я предполагал. Я понял, что придется писать код с нуля.

Применение ChatGPT

Поскольку я не хотел тратить слишком много времени на написание кода с нуля, я решил обратиться за помощью к ChatGPT. Я описал задачу: необходимо было создать анимацию падающих пикселей, имитирующую "эффект Матрицы". ChatGPT предложил использовать библиотеку FastLED для управления светодиодной матрицей и генерации случайных пикселей, которые будут "падать" сверху вниз.

Первые несколько попыток генерации попыток не увенчались успехом. Код либо не работал, либо эффект был далек от того, что я хотел увидеть. То пиксели двигались однообразно, то анимация выглядела неестественно. Каждый раз приходилось вносить правки и корректировать код, чтобы приблизиться к желаемому результату.

Финальный результат

После нескольких десятков генераций кода я наконец выбрал наиболее подходящий вариант кода и отредактировал его под свои потребности. В итоге мне удалось добиться эффекта, который напоминал знаменитые падающие строки из "Матрицы". Пиксели плавно двигаются сверху вниз, создавая иллюзию бесконечного потока данных. Результат можно увидеть на видео, которое я приложил к статье.

Итоговый вариант кода:

#include <FastLED.h>

#define LED_PIN       4      // data pin подключаем к ws2812
#define MATRIX_WIDTH  8       // количество светодиодов по горизонтали
#define MATRIX_HEIGHT 8       // количество светодиодов по вертикали
#define NUM_LEDS      MATRIX_WIDTH*MATRIX_HEIGHT //общее количество светодиодов
#define BRIGHTNESS    64      // яркость свечения
#define LED_TYPE      WS2812  // тип светодиодов
#define COLOR_ORDER   GRB     // цветовая последовательность
CRGB leds[NUM_LEDS];          // массив экрана

// Параметры эффекта
#define MAX_TRAILS  4   // количество одновременно выпадающих сверху экрана пикселей
#define TAIL_MIN    2   // мин. длина хвоста падающего пикселя не менее 1
#define TAIL_MAX    6   // макс. длина хвоста падающего пикселя не более 7
#define MIN_SPEED   10  // минимальная скорость пикселя
#define MAX_SPEED   100 // максимальная скорость пикселя
#define MIN_FADING  BRIGHTNESS  // минимальная яркость хвоста
#define MAX_FADING  200 // максимальная яркость хвоста
#define FRAME       90  // длительность показа кадра

struct PixelTrail       // структура падающего пикселя
{
  uint8_t x;
  uint8_t y;
  uint8_t speed;
  uint8_t length;
  uint8_t delay;
};

PixelTrail trails[MAX_TRAILS];

void setup() 
{
  // Инициализация светодиодов
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);

 for (uint8_t i = 0; i < MAX_TRAILS; i++)             // Инициализация параметров стартовых пикселей
  {
    trails[i].x = random(MATRIX_WIDTH);               // количество пикселей матрицы по горизонтали
    trails[i].y = random(MATRIX_HEIGHT);              // количество пикселей матрицы по вертикали
    trails[i].speed = random(MIN_SPEED, MAX_SPEED);  // Скорость падения
    trails[i].length = random(TAIL_MIN,TAIL_MAX);    // длина хвоста падающего пикселя
    trails[i].delay = millis();                      // таймер старта выпадающего пикселя 
  }
}

void loop() 
{
  fadeToBlackBy(leds, NUM_LEDS,random(MIN_FADING,MAX_FADING)); // Уменьшить яркость пикселей

  for (uint8_t i = 0; i < MAX_TRAILS; i++) 
  {
    if (millis() - trails[i].delay > trails[i].speed) 
    {
      trails[i].y++;
      if (trails[i].y >= MATRIX_HEIGHT) 
      {
        trails[i].y = 0;
        trails[i].x = random(MATRIX_WIDTH);
        trails[i].speed = random(MIN_SPEED, MAX_SPEED);
        trails[i].length = random(TAIL_MIN, TAIL_MAX); // длина хвоста тянущегося за пикселем
      }
      trails[i].delay = millis();
    }

    for (uint8_t j = 0; j < trails[i].length; j++) 
    {
      uint8_t yPos = trails[i].y - j;
      if (yPos >= 0)                  // формируем затухающий хвост пикселя
      {
        leds[XY(trails[i].x, yPos)] = CHSV(96, 255, 255 - j * (255 / trails[i].length)); // CHSV(hue, saturation, val) тон. насыщенность, яркость
      }
    }
  }

  FastLED.show();
  delay(FRAME);                  // длительность кадра
}

uint8_t XY(uint8_t x, uint8_t y) // расчет координат точек на матрице
{
  return (y * MATRIX_WIDTH) + x;
}

Схема подключения

В этом проекте я использовал следующие компоненты:

ESP32 C3 SuperMini

Матрица 8x8 ws2812

Провода Dupont

Выводы

Этот опыт показал мне, что даже с помощью современных инструментов, таких как ChatGPT, создание уникальных визуальных эффектов требует времени и терпения. Хотя ChatGPT предоставил хорошую основу для кода, мне все равно пришлось вносить правки и дорабатывать его, чтобы добиться желаемого результата. Тем не менее, использование ИИ значительно ускорило процесс разработки и помогло мне справиться с задачей, которая изначально казалась сложной.

В заключение хочу сказать, что создание "эффекта Матрицы" на ESP32 и WS2812 - это Ардуино проект который может быть интересен как новичкам, так и опытным разработчикам. И если у вас есть доступ к инструментам, таким как ChatGPT, не бойтесь экспериментировать и пробовать что-то новое.