
Projekt polega na wyświetlaniu animowanej grafiki na matrycy LED, która składa się z 256 diod RGB, ułożonych w formie siatki 16x16 sterowanej przez ESP32-CAM. Dzięki bibliotece Adafruit NeoPixel możliwe jest sterowanie każdym pikselem z osobna, co umożliwia tworzenie dowolnych wzorów i animacji.
W kodzie zaprogramowano kolejne klatki animacji przez przypisanie kolorów konkretnym pikselom. Animacja odtwarzana jest w pętli, co daje efekt ruchomego obrazu.
Ramka SIC PixelArt
Komputer PC z Arduino IDE
Inicjalizacja
Na początku programu definiowane są podstawowe parametry, takie jak numer pinu, do którego podłączona jest taśma NeoPixel, oraz liczba diod (w tym przypadku 256, czyli matryca 16x16). Tworzony jest obiekt Adafruit_NeoPixel, który służy do zarządzania wszystkimi diodami RGB. W funkcji setup() wywoływana jest metoda begin(), która inicjuje komunikację z matrycą, oraz show(), dzięki której diody zostają wyłączone i wyzerowane przed rozpoczęciem animacji.
Projektowanie animacji – przygotowanie graficzne
Na etapie planowania animacji kluczowym krokiem było zaprojektowanie wyglądu postaci oraz poszczególnych klatek ruchu. Do tego celu wykorzystaliśmy program graficzny Krita, który pozwala na precyzyjne rysowanie pikselowej grafiki w siatce odpowiadającej rozdzielczości naszej matrycy LED (16x16 pikseli). W pierwszej kolejności stworzyliśmy statyczny wizerunek bohatera – postaci z mieczem. Następnie, bazując na tej sylwetce, opracowaliśmy kolejne klatki animacji, które odwzorowywały jego ruch. Dzięki temu mogliśmy wcześniej zaplanować zarówno dynamikę, jak i estetykę całej sekwencji. Pozwoliło nam to dokładnie rozplanować, które piksele powinny się świecić w danym momencie oraz w jakim kolorze. Przekładanie rysunków z programu Krita na konkretne współrzędne i kolory diod RGB w kodzie Arduino było dzięki temu znacznie łatwiejsze i bardziej intuicyjne. Cały ten proces nie tylko usprawnił realizację projektu, ale również zapewnił spójność graficzną i czytelność animacji, mimo ograniczeń wynikających z niewielkiej rozdzielczości.
Utworzenie własnej animacji
Animacja zrealizowana została w formie pętli w funkcji loop(). Składa się ona z dwudziestu trzech klatek – każda z nich to zestaw instrukcji przypisujących konkretne kolory do wybranych pikseli matrycy. Po ustawieniu kolorów dla jednej klatki, wywoływana jest metoda show(), która aktualizuje stan diod, a następnie krótka pauza (delay) umożliwia widoczność efektu. Dzięki przechodzeniu przez kolejne klatki w zapętleniu, powstaje wrażenie ruchu – w tym przypadku chodzi o przedstawienie bohatera z mieczem.
Użyte kolory
W animacji zastosowano szeroką paletę barw w formacie RGB, co pozwala na szczegółowe odwzorowanie wyglądu postaci. Kolor bordowy RGB(145, 21, 11) został użyty do przedstawienia włosów bohatera. Beżowe odcienie RGB(213, 179, 141) symbolizują skórę postaci. Zieleń RGB(9, 128, 64) symbolizuje spodnie bohatera, a granatowy RGB(33, 11, 145) oraz jasnoniebieski RGB(123, 106, 198) jego koszulkę. Kolor turkusowy RGB(82, 167, 164) został użyty do przedstawienia miecza. Dzięki temu nawet przy ograniczonej rozdzielczości 16x16 uzyskano czytelną i wyrazistą sylwetkę.
Opis poszczególnych funkcji
Funkcja setup() odpowiada za jednorazową inicjalizację matrycy. Funkcja loop() działa nieustannie i odpowiada za odtwarzanie animacji – to tu znajdują się kolejne klatki, które nadają postaci ruch. W każdej klatce funkcje pixels.setPixelColor(index, color) przypisują odpowiedni kolor do konkretnego piksela, a pixels.show() aktualizuje cały obraz na matrycy. Funkcja delay(ms) dodaje przerwę między kolejnymi klatkami, co pozwala widzowi dostrzec animację w odpowiednim tempie.





#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 15
#define NUMPIXELS 256
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#define DELAYVAL 500
void setup() {
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
pixels.begin();
}
void loop() {
pixels.clear();
pixels.setPixelColor(43, pixels.Color(159, 54, 45));
pixels.setPixelColor(42, pixels.Color(159, 54, 45));
pixels.setPixelColor(52, pixels.Color(159, 54, 45));
pixels.setPixelColor(53, pixels.Color(159, 54, 45));
pixels.setPixelColor(54, pixels.Color(159, 54, 45));
pixels.setPixelColor(55, pixels.Color(159, 54, 45));
pixels.setPixelColor(74, pixels.Color(159, 54, 45));
pixels.setPixelColor(73, pixels.Color(203, 167, 145));
pixels.setPixelColor(72, pixels.Color(203, 167, 145));
pixels.setPixelColor(66, pixels.Color(128, 186, 187));
pixels.setPixelColor(85, pixels.Color(159, 54, 45));
pixels.setPixelColor(86, pixels.Color(203, 167, 145));
pixels.setPixelColor(87, pixels.Color(203, 167, 145));
pixels.setPixelColor(92, pixels.Color(128, 186, 187));
pixels.setPixelColor(107, pixels.Color(133, 116, 197));
pixels.setPixelColor(106, pixels.Color(133, 116, 197));
pixels.setPixelColor(105, pixels.Color(46, 27, 147));
pixels.setPixelColor(104, pixels.Color(46, 27, 147));
pixels.setPixelColor(103, pixels.Color(46, 27, 147));
pixels.setPixelColor(100, pixels.Color(128, 186, 187));
pixels.setPixelColor(115, pixels.Color(133, 116, 197));
pixels.setPixelColor(116, pixels.Color(133, 116, 197));
pixels.setPixelColor(117, pixels.Color(133, 116, 197));
pixels.setPixelColor(118, pixels.Color(46, 27, 147));
pixels.setPixelColor(119, pixels.Color(46, 27, 147));
pixels.setPixelColor(120, pixels.Color(46, 27, 147));
pixels.setPixelColor(121, pixels.Color(46, 27, 147));
pixels.setPixelColor(122, pixels.Color(128, 186, 187));
pixels.setPixelColor(140, pixels.Color(203, 167, 145));
pixels.setPixelColor(139, pixels.Color(203, 167, 145));
pixels.setPixelColor(138, pixels.Color(46, 27, 147));
pixels.setPixelColor(137, pixels.Color(46, 27, 147));
pixels.setPixelColor(136, pixels.Color(46, 27, 147));
pixels.setPixelColor(135, pixels.Color(46, 27, 147));
pixels.setPixelColor(134, pixels.Color(203, 167, 145));
pixels.setPixelColor(133, pixels.Color(203, 167, 145));
pixels.setPixelColor(147, pixels.Color(203, 167, 145));
pixels.setPixelColor(148, pixels.Color(203, 167, 145));
pixels.setPixelColor(149, pixels.Color(46, 27, 147));
pixels.setPixelColor(150, pixels.Color(46, 27, 147));
pixels.setPixelColor(151, pixels.Color(46, 27, 147));
pixels.setPixelColor(152, pixels.Color(46, 27, 147));
pixels.setPixelColor(153, pixels.Color(203, 167, 145));
pixels.setPixelColor(154, pixels.Color(203, 167, 145));
pixels.setPixelColor(171, pixels.Color(25, 134, 76));
pixels.setPixelColor(170, pixels.Color(25, 134, 76));
pixels.setPixelColor(169, pixels.Color(25, 134, 76));
pixels.setPixelColor(168, pixels.Color(25, 134, 76));
pixels.setPixelColor(167, pixels.Color(25, 134, 76));
pixels.setPixelColor(166, pixels.Color(25, 134, 76));
pixels.setPixelColor(180, pixels.Color(25, 134, 76));
pixels.setPixelColor(181, pixels.Color(25, 134, 76));
pixels.setPixelColor(182, pixels.Color(25, 134, 76));
pixels.setPixelColor(183, pixels.Color(25, 134, 76));
pixels.setPixelColor(184, pixels.Color(25, 134, 76));
pixels.setPixelColor(185, pixels.Color(25, 134, 76));
pixels.setPixelColor(186, pixels.Color(25, 134, 76));
pixels.setPixelColor(204, pixels.Color(25, 134, 76));
pixels.setPixelColor(203, pixels.Color(25, 134, 76));
pixels.setPixelColor(202, pixels.Color(25, 134, 76));
pixels.setPixelColor(201, pixels.Color(25, 134, 76));
pixels.setPixelColor(200, pixels.Color(25, 134, 76));
pixels.setPixelColor(199, pixels.Color(25, 134, 76));
pixels.setPixelColor(198, pixels.Color(25, 134, 76));
pixels.setPixelColor(197, pixels.Color(25, 134, 76));
pixels.setPixelColor(211, pixels.Color(25, 134, 76));
pixels.setPixelColor(212, pixels.Color(25, 134, 76));
pixels.setPixelColor(213, pixels.Color(25, 134, 76));
pixels.setPixelColor(216, pixels.Color(25, 134, 76));
pixels.setPixelColor(217, pixels.Color(25, 134, 76));
pixels.setPixelColor(218, pixels.Color(25, 134, 76));
pixels.setPixelColor(236, pixels.Color(203, 167, 145));
pixels.setPixelColor(235, pixels.Color(203, 167, 145));
pixels.setPixelColor(230, pixels.Color(203, 167, 145));
pixels.setPixelColor(229, pixels.Color(203, 167, 145));
pixels.show();
delay(50);