Перевод статьи ESP32: 64 WS2812B RGB LED 8×8 Matrix (Arduino IDE)Rui Santos & Sara Santos, Random Nerd Tutorials.

В этом руководстве показано, как использовать панель-матрицу 8×8 из 64 RGB-светодиодов WS2812B на ESP32 через Arduino IDE. Мы покажем, как управлять каждым светодиодом, задавать разные цвета и яркость, рисовать фигуры/иконки и создавать радужный RGB-эффект.

Есть и отдельное руководство про адресные RGB-светодиоды WS2812B (Neopixels) на ESP32.

Что понадобится заранее#

Убедитесь, что установлена Arduino IDE с поддержкой плат ESP32:

Знакомимся с панелью-матрицей 8×8 из WS2812B (Neopixels)#

Панель 8×8 на WS2812B содержит 64 RGB-светодиода (они же neopixels), каждым из которых можно управлять независимо — задавать любой цвет и яркость всего одним цифровым пином ESP32.

Панель-матрица 8×8 из 64 светодиодов WS2812B крупным планом

Панель 8×8 на 64 светодиодах WS2812B.

Питание панели-матрицы WS2812B#

Панель следует питать от источника 5 В. При 5 В каждый светодиод на полной яркости потребляет около 50 мА. То есть при полной яркости белым на каждые 30 светодиодов панель может брать до 1,5 А. Подойдёт адаптер AC/DC на 5 В 2 А.

Если используете внешний источник, не забудьте соединить его землю с пином GND ESP32. В этом руководстве мы управляем 64 светодиодами (квадратная матрица 8×8) от внешнего источника питания.

Что нужно из железа#

  • плата ESP32 — любая модель;
  • панель-матрица 8×8 на 64 светодиодах WS2812B;
  • адаптер питания 5 В 2 А;
  • соединительные провода.

Подключение матрицы 8×8 к ESP32#

Подключение простое — нужен всего один цифровой пин (DIN).

Матрица 8×8ESP32
V−GND
DINЛюбой цифровой пин (например, GPIO 2)*
Адаптер питания
GNDGND ESP32
GNDV− матрицы
5VV+ матрицы

* можно использовать любые другие подходящие GPIO — сверьтесь с распиновкой ESP32.

Пины панели WS2812B: V−, DIN, V+

Пины панели: V−, DIN, V+.

Ниже — схема с внешним адаптером; но можно питать и напрямую от 5 В ESP32, если не используете белый цвет на полной яркости. При питании от адаптера: GND к V−, GPIO 2 к DIN, а 5 В адаптера к V+ и GND к V−.

Схема подключения матрицы 8×8 к ESP32 с внешним адаптером питания

Схема подключения матрицы 8×8 к ESP32 и адаптеру питания.

Установка библиотеки FastLED#

  • откройте менеджер библиотек Arduino IDE;
  • найдите FastLED;
  • установите библиотеку FastLED от Daniel Garcia.
Установка библиотеки FastLED в менеджере библиотек Arduino IDE

Установка FastLED.

Пример №1 — управление светодиодами матрицы по отдельности#

Следующий код задаёт свой цвет каждому светодиоду матрицы по координатам (X, Y). Загрузите его на плату — он заработает сразу.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-64-ws2812b-8x8-matrix-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <FastLED.h>

#define DIN_PIN        2
#define MATRIX_WIDTH   8
#define MATRIX_HEIGHT  8
#define NUM_LEDS       (MATRIX_WIDTH * MATRIX_HEIGHT)
#define BRIGHTNESS     80          // Brightness (0-255)
#define LED_TYPE       WS2812B
#define COLOR_ORDER    GRB
#define ZIGZAG         true        // Set false if your panel is wired in straight rows instead of zigzag
#define DELAY_MS       500

CRGB leds[NUM_LEDS];

// Convert (x,y) coordinates to the corresponding index in the matrix
uint16_t coordToIndex(uint8_t x, uint8_t y) {
  if (ZIGZAG && (y & 0x01)) {
    x = (MATRIX_WIDTH - 1) - x;
  }
  return (y * MATRIX_WIDTH) + x;
}

// Set the color of a pixel at (x,y)
void setPixel(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t brightness = 255) {
  if (x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT) 
    return;
  CRGB color = CRGB(r, g, b);
  color.nscale8(brightness);
  leds[coordToIndex(x, y)] = color;
}

void setup() {
  FastLED.addLeds<LED_TYPE, DIN_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
}

void loop() {
  setPixel(0, 0, 255, 0, 0);         // First LED x=0 and y=0  |  Red - Full brightness
  FastLED.show();
  delay(DELAY_MS);

  setPixel(3, 3,  0, 255, 0, 128);   // LED x=3 and y=3  |  Green - 50% brightness
  FastLED.show();
  delay(DELAY_MS);

  setPixel(7, 7, 0, 0, 255, 200);    // Last LED x=7 and y=7  |  Blue - ~78% brightness
  FastLED.show();
  delay(DELAY_MS);

  FastLED.clear();                   // All LEDs off
  FastLED.show();
  delay(DELAY_MS);
}

Как работает код#

Подключение библиотеки. Подключаем FastLED.h.

#include <FastLED.h>

Глобальные переменные. Пин данных подключён к GPIO 2. Задаём ширину и высоту матрицы, а их произведение даёт общее число светодиодов.

#define DIN_PIN   2
#define MATRIX_WIDTH     8
#define MATRIX_HEIGHT   8
#define NUM_LEDS            (MATRIX_WIDTH * MATRIX_HEIGHT)

Задаём тип светодиодов WS2812B и порядок цветов GRB.

#define LED_TYPE              WS2812B
#define COLOR_ORDER    GRB

Переменная ZIGZAG используется в функции coordToIndex() для правильной адресации светодиода в зависимости от того, разведены ли ряды матрицы зигзагом или все в одном направлении.

#define ZIGZAG     true

coordToIndex(). Переводит координаты (x, y) в соответствующий индекс в матрице:

uint16_t coordToIndex(uint8_t x, uint8_t y) {
  if (ZIGZAG && (y & 0x01)) {
    x = (MATRIX_WIDTH - 1) - x;
  }
  return (y * MATRIX_WIDTH) + x;
}

setPixel(). Задаёт цвет и яркость одного пикселя матрицы: проверяет корректность координат, строит цвет CRGB из компонент, масштабирует интенсивность параметром яркости и записывает цвет в глобальный массив leds по индексу от coordToIndex().

void setPixel(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t brightness = 255) {
  if (x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT) 
    return;
  CRGB color = CRGB(r, g, b);
  color.nscale8(brightness);
  leds[coordToIndex(x, y)] = color;
}

Для матрицы с зигзагообразной разводкой координаты каждого светодиода задаются так:

Система координат (x, y) для матрицы с зигзагообразной разводкой

Координаты (x, y) при зигзагообразной разводке.

loop(). Сначала задаём первому светодиоду (x=0, y=0) красный цвет, вызываем show(), ждём, затем задаём цвет и яркость двум другим светодиодам и в конце гасим всё вызовом clear().

Демонстрация#

Матрица 8×8: отдельные светодиоды горят разными цветами

Пример 1 в работе.

Пример №2 — рисуем фигуру на матрице 8×8#

Следующий код рисует фигуру с эффектом сердцебиения.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-64-ws2812b-8x8-matrix-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <FastLED.h>

#define DIN_PIN       2
#define MATRIX_WIDTH  8
#define MATRIX_HEIGHT 8
#define NUM_LEDS      (MATRIX_WIDTH * MATRIX_HEIGHT)
#define BRIGHTNESS    80         // Brightness (0-255)
#define LED_TYPE      WS2812B
#define COLOR_ORDER   GRB
#define ZIGZAG        true       // Set false if your panel is wired in straight rows instead of zigzag
#define FLIP_H        false      // Set true if the image displays mirrored left to right
#define FLIP_V        false      // Set true if the image displays upside down

CRGB leds[NUM_LEDS];

// Heart shape (8x8)
const bool heart[MATRIX_HEIGHT][MATRIX_WIDTH] = {
  {0,0,0,0,0,0,0,0},
  {0,1,1,0,0,1,1,0},
  {1,1,1,1,1,1,1,1},
  {1,1,1,1,1,1,1,1},
  {1,1,1,1,1,1,1,1},
  {0,1,1,1,1,1,1,0},
  {0,0,1,1,1,1,0,0},
  {0,0,0,1,1,0,0,0}
};

// Convert (x,y) coordinates to the corresponding index in the matrix
uint16_t coordToIndex(uint8_t x, uint8_t y) {
  if (FLIP_H) x = (MATRIX_WIDTH - 1) - x;
  if (FLIP_V) y = (MATRIX_HEIGHT - 1) - y;

  if (ZIGZAG && (y & 0x01)) {
    x = (MATRIX_WIDTH - 1) - x;
  }
  return (y * MATRIX_WIDTH) + x;
}

// Set the color of a pixel at (x,y)
void setPixel(uint8_t x, uint8_t y, CRGB color) {
  if (x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT) 
    return;
  leds[coordToIndex(x, y)] = color;
}

// Draws the heart shape - scales its color by a brightness factor (0.0 - 1.0)
void drawShape(CRGB color, float scale) {
  FastLED.clear();
  CRGB c = color;
  c.nscale8_video((uint8_t)(scale * 255));
  for (uint8_t y = 0; y < MATRIX_HEIGHT; y++) {
    for (uint8_t x = 0; x < MATRIX_WIDTH; x++) {
      if (heart[y][x]) setPixel(x, y, c);
    }
  }
}

// Pulses a heartrate LED pattern by changing the brightness over durationMs
// It creates a fade in and fade out effect
void pulse(CRGB color, float from, float to, uint16_t durationMs) {
  const uint8_t steps = 24;
  for (uint8_t i = 0; i <= steps; i++) {
    float t = (float)i / steps;
    float eased = (1 - cos(t * PI)) / 2.0;
    float scale = from + (to - from) * eased;
    drawShape(color, scale);
    FastLED.show();
    delay(durationMs / steps);
  }
}

void setup() {
  FastLED.addLeds<LED_TYPE, DIN_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  FastLED.show();
}

void loop() {
  // Red color
  CRGB color = CRGB(255, 0, 0);
  // Heartbeat animation
  pulse(color, 0.30, 1.00, 120);
  pulse(color, 1.00, 0.55, 140);
  pulse(color, 0.55, 0.85, 100);
  pulse(color, 0.85, 0.30, 160);
  drawShape(color, 0.30);
  FastLED.show();
  delay(300);
}

Обзор кода#

Инициализация похожа на пример №1, но вместо управления отдельными светодиодами мы рисуем фигуру-сердце 8×8, заданную битовой картой.

const bool heart[MATRIX_HEIGHT][MATRIX_WIDTH] = {
  {0,0,0,0,0,0,0,0},
  {0,1,1,0,0,1,1,0},
  {1,1,1,1,1,1,1,1},
  {1,1,1,1,1,1,1,1},
  {1,1,1,1,1,1,1,1},
  {0,1,1,1,1,1,1,0},
  {0,0,1,1,1,1,0,0},
  {0,0,0,1,1,0,0,0}
};

Если нужно отразить фигуру по горизонтали или вертикали — задайте эти переменные:

#define FLIP_H        false
#define FLIP_V        false

Функция drawShape() рисует сердце: очищает дисплей, копирует цвет и масштабирует его яркость коэффициентом scale (0.0–1.0), затем перебирает все пиксели и зажигает те, что заданы в массиве heart[y][x].

void drawShape(CRGB color, float scale) {
  FastLED.clear();
  CRGB c = color;
  c.nscale8_video((uint8_t)(scale * 255));
  for (uint8_t y = 0; y < MATRIX_HEIGHT; y++) {
    for (uint8_t x = 0; x < MATRIX_WIDTH; x++) {
      if (heart[y][x]) setPixel(x, y, c);
    }
  }
}

Функция pulse() плавно меняет яркость сердца от одного уровня к другому за заданное время — за 24 небольших шага перерисовывает фигуру с новой яркостью, создавая эффект пульса.

void pulse(CRGB color, float from, float to, uint16_t durationMs) {
  const uint8_t steps = 24;
  for (uint8_t i = 0; i <= steps; i++) {
    float t = (float)i / steps;
    float eased = (1 - cos(t * PI)) / 2.0;
    float scale = from + (to - from) * eased;
    drawShape(color, scale);
    FastLED.show();
    delay(durationMs / steps);
  }
}

Главный цикл loop() бесконечно проигрывает красный эффект сердцебиения: задаёт красный цвет и вызывает pulse() четыре раза, чтобы сердце ярко вспыхнуло, чуть погасло, снова мягко вспыхнуло и угасло.

Демонстрация#

Матрица 8×8: красное сердце с эффектом сердцебиения

Пример 2: красное сердце с эффектом сердцебиения.

Пример №3 — эффект радуги на матрице 8×8#

Следующий код создаёт радужный эффект — цвета плавно движутся по всем светодиодам.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-64-ws2812b-8x8-matrix-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <FastLED.h>

#define DIN_PIN        2
#define MATRIX_WIDTH   8
#define MATRIX_HEIGHT  8
#define NUM_LEDS       (MATRIX_WIDTH * MATRIX_HEIGHT)
#define BRIGHTNESS     80          // Brightness (0-255)
#define LED_TYPE       WS2812B
#define COLOR_ORDER    GRB
#define ZIGZAG         true        // Set false if your panel is wired in straight rows instead of zigzag

// Rainbow animation 
#define BASE_HUE           160    // Initial blue hue for new LED (0-255)
#define HUE_STEP_PER_TICK  3      // Amount the LED hue shifts per frame
#define DELAY_MS           80     // Animation speed
uint16_t ledsLit = 0;             // Number of LEDs currently turned on
uint16_t frame = 0;               // Counts animation frames

CRGB leds[NUM_LEDS];

// Convert (x,y) coordinates to the corresponding index in the matrix
uint16_t coordToIndex(uint8_t x, uint8_t y) {
  if (ZIGZAG && (y & 0x01)) {
    x = (MATRIX_WIDTH - 1) - x;
  }
  return (y * MATRIX_WIDTH) + x;
}

// Set the color of a pixel at (x,y)
void setPixel(uint8_t x, uint8_t y, CRGB color, uint8_t brightness = 255) {
    if (x >= MATRIX_WIDTH || y >= MATRIX_HEIGHT) 
      return;
    
    color.nscale8(brightness);
    leds[coordToIndex(x, y)] = color;
}

void setup() {
  FastLED.addLeds<LED_TYPE, DIN_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  FastLED.show();
}

void loop() {
  // Turns on 1 LED per frame until fully lit
  if (ledsLit < NUM_LEDS) {
    ledsLit++;
  }

  // Update colors of all currently lit LEDs
  for (uint16_t i = 0; i < ledsLit; i++) {
    uint8_t x = i % MATRIX_WIDTH;
    uint8_t y = i / MATRIX_WIDTH;

    // Calculate hue based on how long this LED has been lit
    // Newer LEDs start at BASE_HUE, older ones shift further along the color wheel
    uint16_t age = frame - i;
    uint8_t  hue = BASE_HUE + (uint8_t)(age * HUE_STEP_PER_TICK);

    setPixel(x, y, CHSV(hue, 255, 255));
  }

  FastLED.show();
  delay(DELAY_MS);
  frame++;
}

Обзор кода#

Начало похоже на предыдущие примеры, разберём в основном новый код в loop(). Константы задают начальный синий тон и то, насколько цвет сдвигается за кадр:

#define BASE_HUE           160
#define HUE_STEP_PER_TICK  3

Два счётчика хранят значения между кадрами: ledsLit — сколько светодиодов сейчас горит, frame — счётчик кадров анимации.

В loop(), пока матрица не заполнена целиком, каждый кадр зажигается ещё один светодиод. Затем для каждого горящего светодиода вычисляется его позиция (x, y) и тон в зависимости от того, как давно он горит: недавно зажжённые ближе к синему, а «старые» смещаются по цветовому кругу — так и получается радуга.

for (uint16_t i = 0; i < ledsLit; i++) {
  uint8_t x = i % MATRIX_WIDTH;
  uint8_t y = i / MATRIX_WIDTH;
  uint16_t age = frame - i;
  uint8_t  hue = BASE_HUE + (uint8_t)(age * HUE_STEP_PER_TICK);
  setPixel(x, y, CHSV(hue, 255, 255));
}

Демонстрация#

Матрица 8×8 с радужным эффектом

Пример 3: радужный эффект.

Итог#

В этом руководстве вы научились управлять панелью-матрицей 8×8 из WS2812B через библиотеку FastLED: адресовать каждый светодиод отдельно, рисовать фигуры и показывать радужный эффект. Теперь эти приёмы можно применять в своих проектах.

Спасибо за чтение.