Светодиодные матрицы для "чайников" (часть 2)

Ну что, дружище, надеюсь ты освоил первую часть моего лонгрида и уже запилил свою лампу Гайвера с блэкджеком и всем остальным? Тогда вперед, через тернии к звездам, гексагонам, кругам, гирляндам.

Во многих подобных конструкциях раскладка светодиодов не имеет строк и столбцов, а порой она и вовсе хаотичная. Да и и соединены диоды не рядами, а как было удобно для разводки платы.

Возьмем, к примеру, мою новогоднюю звезду . Диоды там уложены концентрическими звездами, да еще и змейкой.

Светодиодные матрицы для "чайников" (часть 2) Arduino, Программирование, Длиннопост, Светодиоды, Видео, Без звука

Но отчаиваться не будем, попробуем мыслить логически. У каждого диода на звезде есть реальные координаты центра в миллиметрах, или координаты на картинке в пикселях.

Светодиодные матрицы для "чайников" (часть 2) Arduino, Программирование, Длиннопост, Светодиоды, Видео, Без звука

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

uint16_t mapTable[][2] = {

{ 206, 340 },

{ 181, 354 },

{ 156, 369 },

{ 132, 383 },

{ 107, 398 },

{ 89, 404 },

{ 89, 385 },

{ 95, 357 },

{ 101, 329 },

{ 107, 301 },

{ 114, 273 },

... // и так далее, для всех 180 диодов

};

struct ledCoords {

uint16_t x;

uint16_t y;

};

ledCoords mapIdxToXY( byte index ) {

return { mapTable[index][0], mapTable[index][1] };

}

void loop() {

for ( byte i = 0; i < NUM_LEDS; i++ ) {

ledCoords lc = mapIdxToXY(i);

leds[i] = effectColorByCoords(lc.x, lc.y);

}

FastLED.show();

}

На практике это оказывается не очень удачным решением. Координаты - штука относительная. У кого-то линейка в дюймах, а у кого-то 4К картинка с четырехзначными координатами. Замучаешься подгонять масштаб анимации под каждое устройство. Чтобы избежать этого выполняют нормализацию координат.
Звучит страшно, но на практике это просто пересчет в некую фиксированную систему координат. Например, давай примем за правило, что минимальная координата по X среди всех диодов это 0 в нашей "нормализованной" системе, а максимальная - 1. И все остальные координаты пересчитаем пропорционально Xn = ( x - Xmin )/(Xmax - Xmin). Аналогично поступим с координатами Y.

Теперь у нас все координаты на любом устройстве лежат в пространстве 0..1
Такой подход используется в программируемых контроллерах Pixelblaze.

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

  • Во-первых все эти дробные координаты для контроллера являются числами с плавающей точкой (float) и он тебе не скажет спасибо за их использование. В отместку контроллер будет тратить кучу времени на расчеты даже простой арифметики, не говоря уж о корнях, степенях и тригонометрии.

  • Во-вторых, некоторые алгоритмы эффектов требуют расчета по всей площади, а не только в точках расположения диодов. Например операция размытия (blur) требует значения цветов всех соседних точек в пределах радиуса размытия.

  • Ну и в-третьих, тут уж мое личное мнение, куча дробных чисел в формулах снижает читабельность кода, а отладка такого кода вызывает у меня тихий ужас.

И тут ты подкидываешь наивную идею: "А можно как-то так чтобы координаты остались целыми, но не такими большими и более-менее схожей размерности на разных устройствах?"

Можно!

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

Светодиодные матрицы для "чайников" (часть 2) Arduino, Программирование, Длиннопост, Светодиоды, Видео, Без звука

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

byte mapTable[][2] = {

{ 12, 18 },

{ 11, 19 },

{ 9, 20 },

{ 7, 21 },

{ 6, 22 },

{ 5, 22 },

{ 5, 21 },

{ 5, 19 },

{ 6, 18 },

{ 6, 16 },

{ 6, 15 },

... // и так далее, для всех 180 диодов

}

Вроде неплохо получается.

А чтобы все алгоритмы были похожи на работу с обычной прямоугольной матрицей,можно развернуть мэппинг. Будем хранить в таблице не координаты, а индексы диодов для каждой ячейки нашей матрицы 27x23
Только как же быть с пустыми клеточками, ведь их нужно как-то пропустить при обработке? Да запиши в них просто какое-то значение индекса которое ты отловишь в цикле и пропустишь обработку. Например любое число большее общего количества диодов. И таблица примет такой вид:

byte mapTableIndex[23][27] = {

{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 27,

255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },

{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 26, 255,

28, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },

{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 25, 77,

29, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },

{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 78, 255,

76, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },

{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 24, 255, 255,

255, 30, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },

{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 23, 255, 79, 117,

75, 255, 31, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },

... // и еще 17 строк

}

Тогда функция мэппинга и рабочий цикл будут выглядеть как-то так:

byte mapXYtoIdx( byte x, byte y ) {

return mapTableIndex[y][x];

}

void loop() {

for ( byte y = 0; y < 23; y++ ) {

for ( byte x = 0; x < 27; x++ ) {

byte ledIndex = mapXYToIdx(x,y);

if ( ledIndex == 255 ) continue;

leds[ledIndex] = effectColorByCoords(x, y);

}

}

FastLED.show();

}

Конечно, хранить такие большие массивы с таблицами мэппинга в оперативной памяти не стоит, обычно их загоняют во flash используя PROGMEM.

Собираем все полученные знания в кучу и зажигаем)

(Продолжение следует, stay tuned)

Arduino & Pi

1.4K постов20.6K подписчик

Добавить пост

Правила сообщества

В нашем сообществе запрещается:

• Добавлять посты не относящиеся к тематике сообщества, либо не несущие какой-либо полезной нагрузки (флуд)

• Задавать очевидные вопросы в виде постов, не воспользовавшись перед этим поиском

• Выкладывать код прямо в посте - используйте для этого сервисы ideone.com, gist.github.com или схожие ресурсы (pastebin запрещен)

• Рассуждать на темы политики

• Нарушать установленные правила Пикабу