Zabawa adresowalnymi diodami LED [Część 2]

W poprzedniej części bloga opisałem mój plan zabawy z wyświetlaczem LED, który zbudowałem z macierzy adresowalnych diod LED o rozdzielczości 16×16. Planowałem podłączyć go do Arduino i wyświetlić na nim serduszko z pewnej popularnej kanciastej gry. Opisałem również wybór odpowiednich bibliotek oraz program ‘strandtest’, który wykorzystałem do sprawdzenia działania wyświetlacza. Oczywiście trzeba pamiętać o tym, że taka ilość diod pobiera już znaczną ilość prądu, co wymusiło posiadanie zewnętrznego źródła zasilania. Najlepszym rozwiązaniem byłoby użycie zewnętrznego zasilacza. Jednak zanim przeszedłem do podłączania, musiałem przygotować kable tak, by łatwiej się nimi sterowało. Przez cały czas miałem ciągły problem z wypadającym rezystorem z płytki Arduino. Przylutowanie rezystora na stałe do przewodów powinno rozwiązać ten problem raz na zawsze.
Obrazek przedstawia rezystor przylutowany do kabla
Żeby wszystko wyglądało estetyczniej, użyłem koszulek termokurczliwych.
Obrazek przedstawia kable z koszulkami termokurczliwymi
Po przygotowaniu wszystkiego I wgraniu kodu podłączyłem arduino oraz macierz LEDów do zewnętrznego zasilacza I ustawiłem napięcie na 5V. Efekt można zobaczyć poniżej:
Obrazek przedstawia macierz LED z efektem fali
Mając już działający układ, mogę przystąpić do wyświetlenia ostatecznego wzoru. Jednak dokładniej przyglądając się, uświadomiłem sobie, że nie mam całkowitej pewności, jak dokładnie wyświetlić to serduszko. Rozbiłem więc całość na mniejsze kawałki. Pierwszym krokiem było wydobycie danych na temat każdego piksela obrazu serduszka. Musiałem przekonwertować obrazek na trzy tabele, które będą zawierać po 256 wartości dla kolorów zielonego, czerwonego i niebieskiego. Następnie użyję tych danych do zapalenia każdej diody LED w macierzy zgodnie z danymi wydobytymi z obrazka. Część druga wydaje się być trudniejsza, ponieważ nie udało mi się znaleźć funkcji, która zapaliłaby każdą diodę z osobna. Z pomocą chatu GPT napisałem poniższy program w Pythonie:
  1. from PIL import Image
  2.  
  3. def extract_channels(image_path):
  4.     image = Image.open(image_path)
  5.     width, height = image.size
  6.     red_channel = []
  7.     green_channel = []
  8.     blue_channel = []
  9.  
  10.     for y in range(height):
  11.         for x in range(width):
  12.             pixel = image.getpixel((x, y))
  13.             if len(pixel) == 4:  # If there’s an alpha channel
  14.                 r, g, b, a = pixel
  15.                 if a == 0:  # Treat transparent pixels as black
  16.                     r, g, b = 0, 0, 0
  17.             else:
  18.                 r, g, b = pixel
  19.             red_channel.append(r)
  20.             green_channel.append(g)
  21.             blue_channel.append(b)
  22.  
  23.     return red_channel, green_channel, blue_channel
  24.  
  25. def write_to_txt(filename, data):
  26.     with open(filename, ‘w’) as f:
  27.         f.write(“char[] ” + filename.split(‘.’)[0] + ” = {\n”)
  28.         for i, val in enumerate(data):
  29.             f.write(f”0x{val:02X}, “)
  30.             if (i + 1) % 16 == 0:
  31.                 f.write(“\n”)
  32.         f.write(“};\n”)
  33.  
  34. image_path = “/home/baniel/Desktop/Blog1/image.png”
  35. red, green, blue = extract_channels(“/home/baniel/Desktop/Blog1/image.png”)
  36.  
  37. write_to_txt(“red.txt”, red)
  38. write_to_txt(“green.txt”, green)
  39. write_to_txt(“blue.txt”, blue)
Powyższy program ma za zadanie wydobyć wartości kanału zielonego, czerwonego, oraz niebieskiego I zapisać je do pliku txt w formacie który jest zrozumiały dla języka “C” Poniżej przykład takiej tabeli dla kanału czerwonego:

char[] red = {

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00,

0x00, 0xE6, 0xFF, 0xFF, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00,

0x00, 0xE6, 0xFF, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00,

0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00,

0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00,

0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00,

0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

};

Mimo że są to tylko wartości, można dostrzec w nich prześwitujący wzór serduszka, który ukazuje się przez inne litery i cyfry użyte do zapisania danych. Teraz, kiedy miałem już dane, pozostało mi nic innego jak wgrać je do macierzy LED. Jak wspomniałem wcześniej, przeglądając kod, nie znalazłem odpowiedniej funkcji. Potrzebowałem znaleźć funkcję, która przyjmie trzy kanały zapisane w pliku txt. W poszukiwaniu właściwej funkcji zauważyłem, że opcja sprawdzania definicji nie działa w Arduino IDE. Ta niedogodność sprawiła że chwilowo musiałem posłużyć się Visual Studio Code. Zainstalowałem więc VSCode. Pierwszym krokiem jest wybór folderu z moim projektem.
Obrazek przedstawia zrzut ekranu z programu Visual Studio Code z monitem o otworzenie folderu
By VSCode współpracowało z arduino muszę zainstalować odpowiednie rozszerzenie.
Obrazek przedstawia zrzut ekranu z programu Visual Studio Code z podświetloną zakładką dodatki
W zakładce rozszerzenie muszę znaleźć rozszerzenie do arduino i zainstalować je Następnym krokiem jest wpisanie w konsoli komendy “Arduino: Initialize”. Zainicjuje ona VSCode jako projekt arduino. By wywołać konsole należy kliknąć ctrl+shift+p
Obrazek przedstawia zrzut ekranu z programu Visual Studio Code wraz z wywołaną konsolą
Po zainicjowaniu VSCode zapyta się o nazwę pliku .ino, w moim przypadku będzie to “program.ino”. Prócz samego rozszerzenia do arduino VSCode zapytało się o zainstalowanie rozszerzenia C/C++. Kliknąłem więc zainstaluj. Po zainicjowaniu wszystko wyglądało jak poniżej
Obrazek przedstawia zrzut ekranu z Visual Studio Code wraz z listą plików
Następnym krokiem jest pobranie biblioteki z repozytorium NeoPixel Pobrałem I umieściłem bibliotekę w głównym folderze projektu:
Obrazek przedstawia zrzut ekranu z programu Visual Studio Code wraz z dodatkiem AdaFruit
Podobnie jak w arduino IDE musiałem wybrać programator oraz typ płytki. W moim przypadku będzie to ArdiuinoISP oraz Arduino UNO
Obrazek przedstawia stopkę z Visual Studio code wraz z m.in numerem linijki kodu
Teraz przytrzymując Ctrl mogę kliknąć na
Obrazek przedstawia fragmentu kodu z inicjalizacją biblioteki adafruit
by zobaczyć definicję.
Obrazek przedstawia listę funkcji w programie
Jest to ficzer który nie do końca działa w obecnej wersji arduinoIDE, więc dla tego konkretnego zadania musiałem użyć innego edytora kodu. Od razu widzę funkcję która mnie interesuję. Najpierw muszę wywolać “begin()’ następnie “setPin(6)” (w moim przypadku podłaczone wszystko jest do pinu “6” A za pomocą “setPixelColor()” mogę wgrać pixele do macierzy LED. Mając w sumie wszystko co jest potrzebne do napisania programu mogłem coś w końcu napisać. Napisałem program widoczny poniżej:

#include <Adafruit_NeoPixel.h>

#define LED_PIN 6

// How many NeoPixels are attached to the Arduino?

#define LED_COUNT 256

char blue[] = {

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,

0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

};

char green[] = {

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,

0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

};

char red[] = {

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00,

0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xFF, 0xFF, 0xE6, 0x00,

0x00, 0xE6, 0xFF, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00,

0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00,

0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00,

0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00,

0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

};

Adafruit_NeoPixel matrix(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup()

{

matrix.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)

matrix.show(); // Turn OFF all pixels ASAP

matrix.setBrightness(50);

for (int i = 0; i < 256; i++)

{

matrix.setPixelColor(i,red[i],green[i],blue[i]);

matrix.show();

}

}

void loop()

{

}

Program rozpoczyna się od definicji biblioteki oraz określenia pinu używanego do transmisji danych oraz liczby diod LED w macierzy. Następnie definiowane są trzy tablice char dla kolorów: czerwonego, zielonego i niebieskiego. Zawierają one dane na temat tego, jak wygenerować serduszko, i zostały wyeksportowane z pliku PNG za pomocą programu w Pythonie opisanego wcześniej. Kolejna linijka kodu inicjuje bibliotekę i ustawia ją jako zmienną “matrix”. Do tego potrzebna jest jeszcze metoda “begin()”. Po wykonaniu obu linijek kodu wyświetlacz jest ustawiany, aby wyłączył wszystkie diody LED, a jasność ustawiana jest na 50. Ostatnim krokiem jest pętla, która odczytuje każdy piksel z danych zawierających informacje o tym, jak wyświetlić serduszko, i odpowiednio zapala odpowiadającą diodę LED. Końcowy efekt wygląda tak:
Obrazek przedstawia macierz LED wyświetlającą założone na wstępie serduszko

Koniec...

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *