WS2812B в качестве экрана для самоделок на Arduino

- Цена: $1.87
- Перейти в магазин
Всем доброго времени суток!
Хочу поделиться с Вами своими мыслями, о том можно ли приспособить WS2812B в качестве экрана для самоделок.
Конечного устройства я все еще не собрал, но вот небольшой дисплей, по-моему, получился. Всем кому интересно прощу под кат.
Давно хотелось попробовать поработать с такими светодиодами, но как обычно то руки не доходили, то зеленая в пупырышках голос подавала, а тут как то все сложилось и я стал счастливым обладателем 8 плат со светодиодами WS2812B.
От продавца каждая плата приходит запечатанной в антистатический пакет, так что поездку платы пережили хорошо.


Платы небольшие, размером всего лишь 3х3 см.

В конечном итоге после сборки дисплея его размер получился 6х12 см.

Сзади все выглядит не так хорошо как спереди, но это пока что тестовый образец.

На каждой плате есть две группы по 4 контакта, все контакты подписаны так, что сборка не составляет какого либо труда. Для правильной передачи сигнала данных в матрице главным условием является последовательное подключения контакта OUT предыдущей платы, к контакту IN последующей платы. Контакты VCC отвечают за "+" питания, их можно подключать к источнику питания в произвольном порядке. Идеальным решением будет параллельное подключение всех плат, что позволит обеспечить наиболее равномерную яркость светодиодов во всей матрице. Контакт GND отвечает за подачу "0" или "-", все четыре контакта GND каждой платы соединены между собой и для работы платы достаточно подключить хотя бы один из них.
Как видно на фото, я использовал GND контакты для сборки всех плат в одну матрицу. Решение спорное, но для тестирования возможно. Так как изначально планировал разделить дисплей программно на четыре модуля, то и питание подавал на каждый из таких модулей отдельно. Линия данных в свою очередь подключена к каждой группе из двух плат последовательно: от нижнего модуля к верхнему и опять к нижнему. Можно было бы и не заводить данные именно так, но как сказал выше первой идеей было собрать матрицу состоящую из четырех логических индикаторов и для упрощения математики при нахождении номера требуемого светодиода в индикаторе решил собирать модули именно так.

На фото видно, что при такой сборке линии данных как раз и получается матрица из практически независимых сегментов. Для того что бы включить один и тот же светодиод в каждом сегменте достаточно знать номер светодиода для первого сегмента, количество светодиодов в сегменте (в моем случае их 32), и номер сегмента в котором нужно включить светодиод, а это уже всего лишь работа с прямоугольным массивом.
Но это я уже убежал немного вперед в программирование.
Так как дисплей получился не совсем жесткий, решил для него сделать временный корпус из пары кусочков полупрозрачного пластика (за пластик отдельное спасибо софтверной компании на букву М и ее детищу с цифрой 7 в названии).

Как оказалось, без рассеивателя диоды светят слишком жестко, поэтому в качестве рассеивателя был использован кусочек тонкого белого пластика.

Знаю что получилось несколько топорно, но о корпусе пока не думал, главным было собрать дисплей и в работе понять для чего он подойдет.
Перед тем как окончательно перейти к программированию пару слов об энергопотреблении.
Согласно datasheet напряжение питания для таких светодиодов составляет от +3,5 до +5,3 вольта. А с током потребления все как то странно, в datasheet пишут 18 мА на цвет, другими словами при работе в белом цвете на максимальной яркости светодиод должен потреблять около 54 мА, а на форумах пишут о токе около 60-70 мА минимум.
Мой дисплей на 128 светодиодов питается напряжением 4,6В и при таком напряжении для белого света он потребляет ни много ни мало 2,49 А

Получается что ток 18 мА указанный в datasheet это ток не одного цвета, а всех трех.
Заливка одним из основных цветов (зеленым) потребляет 1.23 А.

А вот разноцветная мозаика потребляет всего 0,65А

Ну а теперь немного программирования:
Для управления дисплеем использовал библиотеку NeoPixel, поэтому все ниже сказанное относится в первую очередь к ней.
Сначала пару слов о том, как собственно можно управлять светодиодами с точки зрения программиста, не забираясь в то как сигнал передается по проводу.
Каждый светодиод в дисплее имеет свой порядковый номер, нумерация начинается с нуля, номера считаются последовательно в порядке подключения светодиодов. Для включения или выключения светодиода используется функция setPixelColor которая в качестве параметров принимает номер светодиода и его цвет в кодировке rgb. После установки/изменения цвета всех требуемых светодиодов необходимо выполнить функцию Show, которая отправит команду светодиодам принять установленный цвет.
По-большому счету только этих двух функций достаточно чтобы выводить на такой дисплей практически любую информацию.
Как только начал свои эксперименты с дисплеем, сразу обнаружил его главное преимущество: светодиоды запоминают полученный цвет. При работе с семисегментными индикаторами обычно требуется либо установить много регистров для хранения состояний каждого диода, либо динамически раз 40 в секунду обновлять картинку, а тут вывел один раз и хоть в сон отправляй контроллер.
Как и писал выше изначально думал поделить весь дисплей на 4 одинаковых индикатора и простым смещением на количество светодиодов выводить символы на нужный индикатор. Немного подумав, решил, что памяти пока что хватает, так что сделал единый массив констант, в который и занес массив номеров светодиодов.
Создание такого массива индексов позволяет просто по координатам X Y обратиться к любому светодиоду, не занимаясь вычислением его координат, что упрощает анимацию символов. Но с другой стороны такой подход, увеличивает размер программы и работать так с большими дисплеями не получится.
Следующим этапом было решить как хранить шрифт и где его нарисовать. Вспомнив, что в языке Arduino есть хорошая функция bitRead, которая позволяет обращаться непосредственно к каждому биту числа по его номеру, я решил, что хранить шрифт буду в виде массива двоичных чисел, а рисовать их получилось довольно удобно в Calc.
В итоге, получился файл с вот таким содержимым

Итогом этого всего получился вот такой массив:
и функция, позволяющая вывести любой символ из из этого массива в любые координаты дисплея:

Небольшое видео работы:
Еще попробовал использовать это дисплей для программного анализатора спекта работающего на основе библиотек быстрого преобразования фурье


Небольшое видео работы:
Для работы анализатора необходимо соединить левый или правый канал с аналоговым входом Ардуино (в моем случае вход 0), а общий провод соединить с землей.
На видео видео паразитный сигнал в первую очередь он вызван тем что не поставил на вход Ардуино даже элементарного фильтра, ну и конечно с качеством телефонного генератора.
Так же из за отсутствия ограничения уровня сигнала вся ответственность жизнь порта Ардуино ложиться на пользователя и подключать такой анализатор напрямую к выходу усилителя категорически не рекомендуется.
В конце кода есть строка задержки delay(15) добавил ее что бы картинка не так быстро мигала при работе с музыкой.
Выводы:
Использование светодиодов WS2812B для создания электронных табло почти идеально:
К плюсам можно отнести:
— простоту управления;
— простоту схемотехники;
— самостоятельное удержание картинки без необходимости обновления состояния светодиодов.
А вот к минусам можно отнести:
— большую по сравнения с обычными светодиодами цену;
— довольно крупный размер кристалла, из-за чего дисплей небольшого размера имеет очень маленькое разрешение.
Хочу поделиться с Вами своими мыслями, о том можно ли приспособить WS2812B в качестве экрана для самоделок.
Конечного устройства я все еще не собрал, но вот небольшой дисплей, по-моему, получился. Всем кому интересно прощу под кат.
Давно хотелось попробовать поработать с такими светодиодами, но как обычно то руки не доходили, то зеленая в пупырышках голос подавала, а тут как то все сложилось и я стал счастливым обладателем 8 плат со светодиодами WS2812B.
От продавца каждая плата приходит запечатанной в антистатический пакет, так что поездку платы пережили хорошо.
Упаковка


Платы небольшие, размером всего лишь 3х3 см.

В конечном итоге после сборки дисплея его размер получился 6х12 см.

Сзади все выглядит не так хорошо как спереди, но это пока что тестовый образец.

На каждой плате есть две группы по 4 контакта, все контакты подписаны так, что сборка не составляет какого либо труда. Для правильной передачи сигнала данных в матрице главным условием является последовательное подключения контакта OUT предыдущей платы, к контакту IN последующей платы. Контакты VCC отвечают за "+" питания, их можно подключать к источнику питания в произвольном порядке. Идеальным решением будет параллельное подключение всех плат, что позволит обеспечить наиболее равномерную яркость светодиодов во всей матрице. Контакт GND отвечает за подачу "0" или "-", все четыре контакта GND каждой платы соединены между собой и для работы платы достаточно подключить хотя бы один из них.
Как видно на фото, я использовал GND контакты для сборки всех плат в одну матрицу. Решение спорное, но для тестирования возможно. Так как изначально планировал разделить дисплей программно на четыре модуля, то и питание подавал на каждый из таких модулей отдельно. Линия данных в свою очередь подключена к каждой группе из двух плат последовательно: от нижнего модуля к верхнему и опять к нижнему. Можно было бы и не заводить данные именно так, но как сказал выше первой идеей было собрать матрицу состоящую из четырех логических индикаторов и для упрощения математики при нахождении номера требуемого светодиода в индикаторе решил собирать модули именно так.

На фото видно, что при такой сборке линии данных как раз и получается матрица из практически независимых сегментов. Для того что бы включить один и тот же светодиод в каждом сегменте достаточно знать номер светодиода для первого сегмента, количество светодиодов в сегменте (в моем случае их 32), и номер сегмента в котором нужно включить светодиод, а это уже всего лишь работа с прямоугольным массивом.
Но это я уже убежал немного вперед в программирование.
Так как дисплей получился не совсем жесткий, решил для него сделать временный корпус из пары кусочков полупрозрачного пластика (за пластик отдельное спасибо софтверной компании на букву М и ее детищу с цифрой 7 в названии).

Как оказалось, без рассеивателя диоды светят слишком жестко, поэтому в качестве рассеивателя был использован кусочек тонкого белого пластика.

Знаю что получилось несколько топорно, но о корпусе пока не думал, главным было собрать дисплей и в работе понять для чего он подойдет.
Перед тем как окончательно перейти к программированию пару слов об энергопотреблении.
Согласно datasheet напряжение питания для таких светодиодов составляет от +3,5 до +5,3 вольта. А с током потребления все как то странно, в datasheet пишут 18 мА на цвет, другими словами при работе в белом цвете на максимальной яркости светодиод должен потреблять около 54 мА, а на форумах пишут о токе около 60-70 мА минимум.
Мой дисплей на 128 светодиодов питается напряжением 4,6В и при таком напряжении для белого света он потребляет ни много ни мало 2,49 А

Получается что ток 18 мА указанный в datasheet это ток не одного цвета, а всех трех.
Заливка одним из основных цветов (зеленым) потребляет 1.23 А.

А вот разноцветная мозаика потребляет всего 0,65А

Ну а теперь немного программирования:
Для управления дисплеем использовал библиотеку NeoPixel, поэтому все ниже сказанное относится в первую очередь к ней.
Сначала пару слов о том, как собственно можно управлять светодиодами с точки зрения программиста, не забираясь в то как сигнал передается по проводу.
Каждый светодиод в дисплее имеет свой порядковый номер, нумерация начинается с нуля, номера считаются последовательно в порядке подключения светодиодов. Для включения или выключения светодиода используется функция setPixelColor которая в качестве параметров принимает номер светодиода и его цвет в кодировке rgb. После установки/изменения цвета всех требуемых светодиодов необходимо выполнить функцию Show, которая отправит команду светодиодам принять установленный цвет.
По-большому счету только этих двух функций достаточно чтобы выводить на такой дисплей практически любую информацию.
Как только начал свои эксперименты с дисплеем, сразу обнаружил его главное преимущество: светодиоды запоминают полученный цвет. При работе с семисегментными индикаторами обычно требуется либо установить много регистров для хранения состояний каждого диода, либо динамически раз 40 в секунду обновлять картинку, а тут вывел один раз и хоть в сон отправляй контроллер.
Как и писал выше изначально думал поделить весь дисплей на 4 одинаковых индикатора и простым смещением на количество светодиодов выводить символы на нужный индикатор. Немного подумав, решил, что памяти пока что хватает, так что сделал единый массив констант, в который и занес массив номеров светодиодов.
Массив индексов
Для отдельного индикатора
#define sm 32 // количество светодиодов в индикаторе
// матрица первого индикатора
byte seg[8][4]={{19,20,27,28},{18,21,26,29},{17,22,25,30},{16,23,24,31},{3,4,11,12},{2,5,10,13},{1,6,9,14},{0,7,8,15}};
//для индикатора N ID светодиода равно seg[Y][X]+(N*sm)
// матрица для все дисплея
byte seg[8][16]={{19,20,27,28,51,52,59,60,83,84,91,92,115,116,123,124}
,{18,21,26,29,50,53,58,61,82,85,90,93,114,117,122,125}
,{17,22,25,30,48,54,57,62,81,86,89,94,113,118,121,126}
,{16,23,24,31,48,55,56,63,80,87,88,95,112,119,120,128}
,{3 ,4 ,11,12,35,36,43,44,67,68,75,76,99,100,107,108}
,{2 ,5 ,10,13,34,37,42,45,66,69,74,77,98,101,106,109}
,{1 ,6 ,9 ,14,33,38,41,46,65,70,73,78,97,102,105,110}
,{0 ,7 ,8 ,15,32,39,40,47,64,71,72,79,96,103,104,111}
};
Создание такого массива индексов позволяет просто по координатам X Y обратиться к любому светодиоду, не занимаясь вычислением его координат, что упрощает анимацию символов. Но с другой стороны такой подход, увеличивает размер программы и работать так с большими дисплеями не получится.
Следующим этапом было решить как хранить шрифт и где его нарисовать. Вспомнив, что в языке Arduino есть хорошая функция bitRead, которая позволяет обращаться непосредственно к каждому биту числа по его номеру, я решил, что хранить шрифт буду в виде массива двоичных чисел, а рисовать их получилось довольно удобно в Calc.
В итоге, получился файл с вот таким содержимым

Немного пояснения
Для закрашивания ячеек использована функция условного форматирования, а для формирования готового массива байт использовал функцию СЦЕПИТЬ.
В итоге, в ячейке получается готовый массив, который просто надо скопировать в код.
Например это цифра 5
В итоге, в ячейке получается готовый массив, который просто надо скопировать в код.
Например это цифра 5
{B0100111,B1001001,B1001001,B0110001}
Итогом этого всего получился вот такой массив:
byte num[23][4]={{B01111110,B01000010,B01111110,B00000000}, //0
{B01000100,B01111110,B01000000,B00000000}, //1
{B01100100,B01010010,B01001100,B00000000}, //2
{B01000010,B01001010,B01111110,B00000000}, //3
{B00111000,B00100100,B01111110,B00000000}, //4
{B01001110,B01001010,B01111010,B00000000}, //5
{B01111110,B01001010,B01111010,B00000000}, //6
{B01100010,B00010010,B00001110,B00000000}, //7
{B01111110,B01001010,B01111110,B00000000}, //8
{B01001110,B01001010,B01111110,B00000000}, //9
{B00001000,B00011100,B00001000,B00000000}, //+
{B00001000,B00001000,B00001000,B00000000}, //-
{B01111110,B00000010,B01111110,B00000000},//П
{B01111110,B01001010,B01110100,B00000000}, //В
{B01111110,B01000010,B01100110,B00000000}, //С
{B00011110,B00010000,B01111110,B00000000}, //Ч
{B01111100,B00010000,B01111100,B00000000},//н
{B00000100,B01111100,B00000100,B00000000},//т
{B01111100,B00010100,B00011100,B00000000},//р
{B01111100,B01010100,B01100100,B00000000},//б
{B01111100,B01000100,B01101100,B00000000},//с
{B00000000,B00001110,B00001010,B00001110},// градусы
{B00000000,B00000000,B00000000,B00000000}// пустота
};
и функция, позволяющая вывести любой символ из из этого массива в любые координаты дисплея:
#define matx 16
#define maty 8
void ShowSumbol(byte id, int x, int y, byte r, byte g, byte b,byte black)
// id индекс символа в таблице символов
//x,y координаты левого нижнего угла
//r,g,b цвет
//black выключить или нет неиспользуемые пиксели 1 выключать
{
for (int xi=0; xi<segx;xi++)
{
for (int yj=0; yj<segy;yj++)
{
if (bitRead(num[id][xi],yj)==1)
{
if ((y+yj)<maty && (x+xi)<matx)
{
if (((x+xi)>=0)&&((y+yj)>=0)) strip.setPixelColor(seg[y+yj][x+xi],strip.Color(r,g,b));
}
}
if (bitRead(num[id][xi],yj)==0)
{
if (black==1) strip.setPixelColor(seg[y+yj][x+xi],strip.Color(0,0,0));
}
}
}
}

Небольшое видео работы:
Еще попробовал использовать это дисплей для программного анализатора спекта работающего на основе библиотек быстрого преобразования фурье


Небольшое видео работы:
Для работы анализатора необходимо соединить левый или правый канал с аналоговым входом Ардуино (в моем случае вход 0), а общий провод соединить с землей.
На видео видео паразитный сигнал в первую очередь он вызван тем что не поставил на вход Ардуино даже элементарного фильтра, ну и конечно с качеством телефонного генератора.
Так же из за отсутствия ограничения уровня сигнала вся ответственность жизнь порта Ардуино ложиться на пользователя и подключать такой анализатор напрямую к выходу усилителя категорически не рекомендуется.
В конце кода есть строка задержки delay(15) добавил ее что бы картинка не так быстро мигала при работе с музыкой.
Исходный код
#include <fix_fft.h>
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define count_led 128 // количество светодиодов
Adafruit_NeoPixel strip = Adafruit_NeoPixel(count_led, PIN, NEO_GRB + NEO_KHZ800); //first number change does distance between colors
byte seg[8][16]={{0 ,7 ,8 ,15,32,39,40,47,64,71,72,79,96,103,104,111}
,{1 ,6 ,9 ,14,33,38,41,46,65,70,73,78,97,102,105,110}
,{2 ,5 ,10,13,34,37,42,45,66,69,74,77,98,101,106,109}
,{3 ,4 ,11,12,35,36,43,44,67,68,75,76,99,100,107,108}
,{16,23,24,31,48,55,56,63,80,87,88,95,112,119,120,128}
,{17,22,25,30,48,54,57,62,81,86,89,94,113,118,121,126}
,{18,21,26,29,50,53,58,61,82,85,90,93,114,117,122,125}
,{19,20,27,28,51,52,59,60,83,84,91,92,115,116,123,124}
};
const int AUDIOPIN=A0;
char im[128], data[128], lastpass[64];
char data_a[16];
char data_d[16];
int i=0, val, vd=0;
char x=17, y=8;
int maxValue=30;
void setup() {
analogReference(DEFAULT);
for (int z=0; z<64; z++) {lastpass[z]=80;};
strip.begin();
strip.show();
}
void loop() {
for (i=0; i < 128; i++){
val = analogRead(AUDIOPIN);
data[i] = val;
im[i] = 0;
}
fix_fft(data,im,7,0);
for (i=0; i< 64;i++){
data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
}
for (i=1; i<x; i++) {
data_a[i] = data[i*4] + data[i*4 + 1] + data[i*4 + 2] + data[i*4 + 3];
if(data_a[i]>maxValue)
{
data_a[i]=maxValue;
}
data_a[i] = map(data_a[i], 0, maxValue, 0, y);
for (int j=0; j<y;j++)
{
if (data_a[i]<j)
{
strip.setPixelColor(seg[j][i-1],strip.Color(0,0,0));
}else
{
strip.setPixelColor(seg[j][i-1],strip.Color(map(j, 0, y, 0, 255),20,map(i, 0, x, 0, 255)));
}
}
}
strip.show();
delay(15);
}
Выводы:
Использование светодиодов WS2812B для создания электронных табло почти идеально:
К плюсам можно отнести:
— простоту управления;
— простоту схемотехники;
— самостоятельное удержание картинки без необходимости обновления состояния светодиодов.
А вот к минусам можно отнести:
— большую по сравнения с обычными светодиодами цену;
— довольно крупный размер кристалла, из-за чего дисплей небольшого размера имеет очень маленькое разрешение.
Самые обсуждаемые обзоры
+15 |
1794
53
|
+54 |
1543
36
|
На моей практике около 500 диодов Arduino Nano едва заметна задержка(библиотека fastled), на ESP8266 не заметна.
Зачем было изобретать велосипед?
Надо только проверить как у нее с кириллицей.
А даже если бы знал раньше что есть такая библиотека то скорее всего попытался бы написать сам, основной профит был пощупать как оно работает, а не вкинуть одной строкой то что написал кто-то другой.
Если бы чуть лучше знал ASM то и протокол попытался бы реализовать сам вообще без библиотеки. :)
learn.adafruit.com/adafruit-gfx-graphics-library?view=all
Действительно, NeoMatrix выводит текст
Но при попытке изменить шрифт упираемся в размерность матрицы.
Создаём шрифт 3х5 и выводим его в любую точку матрицы.
Ясное дело, что шрифт высотой 20 пикселей не влезет на матрицу размером 16 пикселей, например. Но при чём здесь библиотека?
Лучшая цена 2 ~ 1000 шт. IC apa106 F8 8 мм/f5 5 мм круглые RGB полном Цвет Neo Pixel arduino матовый светодиодные Чип (как WS2812B) 5 В
https://aliexpress.com/item/item/10-1000Pcs-RGB-Full-Color-IC-Built-IN-APA106-F8-8mm-F5-5mm-Neo-pixel-Arduino/32704656076.html
Надо будет попробовать, хотя цена :(
Да и цена — 158$ за 1000 штук. То есть табло в 32х32 и шагом 6 мм (будем очень большими оптимистами) обойдётся баксов в 200. Готовая панелька 32х32 с шагом 6 мм обойдётся с доставкой в 20$.
Сам такую гирлянду на ёлку вешал на новый год :)
www.led-color.com/upload/201604/APA102-2020%20SMD%20LED.pdf
Тот же функционал, но корпус 2ммх2мм.
Бонусом — несущая частота встроенного шим более 20кГц, что делает их намного приятнее для глаза в плане мерцания, если сравнивать с WS2812
Главное, не берите SKxxxx — поганенькие клоны…
Да и тут плюсы тоже есть, хотя цена их перекрывает.
koticik
а цветные варианты — не менее требовательны?
ружья должны вроде умещаться
хотя как-то на ардуино.ру я заспорил с одним парнем… по его словам оптимизация как таковая — изначально не верный подход к выбранному оборудованию и что проще перейти на более производительный камень, чем заниматься вычесыванием кода.
Единственно о чем еще с времен института приучили думать это о размерности переменных, типа не брать Инт там где хватит Байт и так далее ибо сменить тип переменной бывает очень грустно и больно. Хотя и боролись мы с нашим преподом по программированию на предмет это в ваше время 640 килобайт было, а мы уже скоро до гига дорастем зачем ее экономить то :)
без PROGMEM
Скетч использует 5 446 байт (17%) памяти устройства. Всего доступно 30 720 байт.
Глобальные переменные используют 518 байт (25%) динамической памяти, оставляя 1 530 байт для локальных переменных. Максимум: 2 048 байт.
c PROGMEM
Скетч использует 5 446 байт (17%) памяти устройства. Всего доступно 30 720 байт.
Глобальные переменные используют 390 байт (19%) динамической памяти, оставляя 1 658 байт для локальных переменных. Максимум: 2 048 байт.
Выигрыш есть и он не маленький, Но это только компиляция, как оно при исполнении будет не проверял :)
Это особенность светодиода.
Этот диод нормально работает на статических основных цветах и на белом цвете, а вот на быстрой заливке промежуточным цветом иногда не включается или включается но другим цветом. Я пытался выяснить почему так но так и не смог, но дело явно не в коде.
Пробовал и без резистора и без кондеров ничего не меняется. Диод то показывает то нет иногда помогает если выключенным светикам давать цвет не 0,0,0 а скажем 5,5,5 они все равно не горят но тогда этот может проснуться, но в зависимости от интенсивности картинки может опять не пахать.
При этом как видно его такая работа совсем не мешает работать другим и что немаловажно не сбивает порядок включения других диодов (сбойный стоит по-моему номером 30) а всего их 128, то есть за себя данные он успешно транслирует, а вот сам почему то ведет себя немного неадеквато.
А, «проблемный» не в начале цепочки? Переставьте диод или кластер целиком в другую позицию для контроля.
Пытался менять местами еще когда только собирал и делал первые тесты, результат тот же потому и решил что это аппаратный глюк конкретного пикселя.
А скетч буков выложу но только вечером.
char data_a[16];
char x=17
for (i=1; i<x; i++) {
data_a[i] =
У вас i щелкает от 1 до 16. А элементы массива data_a имеют номера от 0 до 15. Соответственно — вы пишете уже в ячейку памяти за границей массива.
Это вот только то, что просто бросается в глаза. Ну и код надо чистить и чистить, конечно.
В первом столбце без фильтров на входе идет постоянный шум и он просто не гаснет даже в тишине поэтому матрица была пересчитана на 17 столбцов, а потом при выводе seg[j][i-1]
Номер столбца массива и номер столбца дисплея смещались на единицу. А вот о том что стоило бы увеличить размер массива я как то не подумал :)
То что код надо чистит тут я даже спорить не буду и чистить и оптимизировать но дело в другом.
Щелкает 6 светодиод 4 столбца, и это вряд ли связано конкретно с этой ошибкой. Он щелкает даже на стандартных тестах библиотеки на градиенте.
но она считается давно устранённой:
fastled.io/
Для подобных поделок она даже поудобнее.
имеенно так я вчера и воспринял вашу проблему
сейчас гадаю — почему?))
(разве что интерференция «первом»-«один» на LighTning; видео никогда не смотрю)