LED-owy znikopis RGB

LED-owy znikopis RGB

Znikopis to popularna zabawka dla dzieci, wymyślona w połowie XX wieku we Francji. Oryginalny znikopis to prosty ploter – za pomocą dwóch pokręteł przemieszcza się rysik, który przesuwa aluminiowy proszek, tworzący obraz. Jak sugeruje nazwa, rysunek może łatwo „zniknąć” – wystarczy potrząsnąć zabawką, a kreski z aluminiowego proszku znikają. Zaprezentowany w artykule projekt przenosi to urządzenie w XXI wiek. Jest obsługiwane w identyczny sposób, jednak do wyświetlania obrazów zastosowano diody LED RGB.

Zaprezentowana konstrukcja to bardzo ciekawy projekt, który korzysta ze znanego mechanizmu obsługi klasycznej mechanicznej zabawki i łączy go z nowoczesną elektroniką. Sercem całego układu jest moduł Arduino, który kontroluje macierz diod RGB i steruje nimi zgodnie z ruchami dwóch enkoderów obrotowych.
To nowoczesne podejście do starego klasyka pozwala tworzyć kolorowe pikselowe obrazki, przez dwa obrotowe enkodery, które kontrolują poziomą i pionową pozycję kursora.

Działanie układu jest bardzo proste. Kliknięcie enkodera pozwala na zmianę koloru. Obracanie lewym enkoderem przesuwa kursor w lewo i w prawo. Naciśnięcie go przechodzi do przodu przez tablicę ośmiu kolorów. Przekręcanie prawego enkodera przesuwa kursor w górę i w dół. Naciśnięcie powoduje zmianę koloru w drugim kierunku tablicy. Po przesunięciu kursora wybrany kolor pozostaje w poprzednim „pikselu”. Kursor jest jaśniejszy niż inne piksele, dzięki czemu można łatwo zobaczyć, gdzie się znajduje w danym momencie.

Potrzebne elementy

Do zbudowania tej konstrukcji będą potrzebne następujące komponenty:

  • moduł Arduino Nano,
  • dwa enkodery obrotowe z wbudowanymi przyciskami,
  • dwie plastikowe nakładki – pokrętła, na osie enkoderów,
  • matryca 16×16 z diod WS2812B,
  • płytka uniwersalna lub stykowa,
  • przewody do wykonania połączeń (15 sztuk),
  • zasilacz 5 V o wydajności odpowiedniej do zasilenia Arduino i tak dużej liczby diod RGB LED (w teorii nawet do 13 A, jeśli planujemy zapalić wszystkie diody naraz na biało, w praktyce nie więcej niż 5 A).

Następnie, jeśli chcemy skonstruować obudowę taką jak w prezentowanym urządzeniu, będziemy musieli mieć:

  • płytę MDF lub sklejkę,
  • klej do drewna,
  • czerwoną farbę,
  • klej na gorąco.

Budowa układu

Opisywana konstrukcja jest całkiem prosta. Składa się z zaledwie kilku modułów elektronicznych, połączonych ze sobą i umieszczonych w estetycznej obudowie.

Rysunek 1. Schemat elektryczny układu

Na rysunku 1 został pokazany schemat elektryczny układu. Masy i linie zasilania obu modułów z enkoderami oraz macierzy RGB LED połączone są ze sobą we wspólnych punktach, do których należy dołączyć zasilacz. Każdy z enkoderów dołączony jest za pomocą trzech linii GPIO do modułu Arduino – są to CLK, DT oraz SW. Dwie pierwsze to sygnały impulsów z enkodera – A oraz B, a SW to sygnał z wbudowanego w enkoder przycisku.

Macierz diod RGB podłączona jest do mikrokontrolera za pomocą zaledwie jednej linii cyfrowej – diody WS2812B połączone są kaskadowo, dzięki czemu nie ma konieczności zużywania większej liczby linii GPIO. Zmontowany na płytce stykowej układ został pokazany na fotografii 1.

Fotografia 1. Układ zmontowany na płytce stykowej

Obudowa układu została wykonana z płyty MDF o grubości 6 mm. Dwie warstwy tej płyty sklejone zostały ze sobą w taki sposób, że formują zagłębienie, co pokazano na fotografii 2.

Fotografia 2. Elementy obudowy wykonane z płyty MDF

W nim umieszczone są wszystkie elementy elektroniczne konstruowanej zabawki. Enkodery obrotowe zamocowane są za pomocą dostarczonych z nimi nakrętek, pozostałe elementy zamocowane są za pomocą kleju na ciepło. Jeśli chcemy zbudować urządzenie w takiej samej obudowie, to na fotografii 3 zostało pokazane rozmieszczenie komponentów na tylnej ściance. Macierz diod RGB instalowana jest z drugiej strony – na frontowej ściance.

Fotografia 3. Elektronika rozmieszczona w obudowie (widok od tyłu)

Oprogramowanie

Kluczowym elementem tego projektu jest oprogramowanie, które kontroluje pracę urządzenia. Zanim przystąpimy do kompilacji szkicu w Arduino IDE, konieczne jest zainstalowanie dwóch bibliotek – FastLED i LEDMatrix. Można je pobrać z repozytoriów na GitHubie (linki znajdują się na końcu artykułu). Szkic dla Arduino IDE został pokazany na listingu 1.

Listing 1. Kod programu kontrolującego LED-owy znikopis

//  https://github.com/FastLED/FastLED
#include <FastLED.h>
//  https://github.com/AaronLiddiment/LEDMatrix
#include <LEDMatrix.h>

// Kolejne 6 linii definiuje rodzaj macierzy LED
#define LED_PIN        10     // Pin do którego podłączone są LEDy
#define COLOR_ORDER    GRB    // Kolejność kolorów w konfiguracji diody
#define CHIPSET        WS2812 // Rodzaj diody RGB
// Ustaw wartość ujemną, jeśli fizyczna dioda 0
// ma być po drugiej stronie logicznej diody 0
#define MATRIX_WIDTH   -16
// Ustaw wartość ujemną, jeśli fizyczna dioda 0
// ma być po drugiej stronie logicznej diody 0
#define MATRIX_HEIGHT  -16
// Rodzaj połączenia macierzy (patrz dokumentacja LEDMatrix.h)
#define MATRIX_TYPE    VERTICAL_ZIGZAG_MATRIX

cLEDMatrix<MATRIX_WIDTH, MATRIX_HEIGHT, MATRIX_TYPE> leds;

// Enkoder A - Ruch poziomy
int horizontalA = 9;  // DT
int horizontalB = 8;  // CLK
int buttonA = 7;    // Przycisk
int btnA;         // Wartość przycisku A
int horizontalALast;
int horizontal, horizontalCursor = 0;
unsigned char encoder_horizontalA;
unsigned char encoder_horizontalB;
unsigned char encoder_horizontalA_prev = 0;

// Enkoder B - Ruch pionowy
int verticalA = 6;    // DT
int verticalB = 5;    // CLK
int buttonB = 4;    // Przycisk
int btnB;         // Wartość przycisku B
int verticalALast;
int vertical, verticalCursor = 0;
unsigned char encoder_verticalA;
unsigned char encoder_verticalB;
unsigned char encoder_verticalA_prev = 0;
unsigned long currentTime;
unsigned long loopTime;
bool moved = false;

// Tablica kolorów
const int numberColours = 9; // Liczba kolorów
unsigned long colours[numberColours][2] = {
 {0xFF0000, 0x330000},
 {0xFF6600, 0x331100},
 {0xFFFF00, 0x333300},
 {0x00FF00, 0x003300},
 {0x00FFFF, 0x001111},
 {0x0000FF, 0x000033},
 {0xFF00FF, 0x330033},
 {0xFFFFFF, 0x111111},
 {0x111111, 0x000000}
};
int currentColour = 0;

void setup() {
 FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds[0], leds.Size());
 FastLED.setBrightness(255);
 int colourBounce = 1;
 for (int i = 0; i < 16; i++){
   leds.DrawLine(0, i, 15, i, colours[currentColour][0]);
   leds.DrawLine(i, 0, i, 15, colours[currentColour][0]);
   FastLED.show();
   delay(25);
   leds.DrawLine(0, i, 15, i, CRGB(0, 0, 0));
   leds.DrawLine(i, 0, i, 15, CRGB(0, 0, 0));
   currentColour = currentColour + colourBounce;
   if (i == 7) {
     colourBounce = -1;
   }
 }
 FastLED.clear(true);
 // Enkoder A - Poziomy
 pinMode (horizontalA, INPUT);
 pinMode (horizontalB, INPUT);
 pinMode (buttonA, INPUT);
 horizontalALast = digitalRead(horizontalA);
 // Enkoder B - Pionowy
 pinMode (verticalA, INPUT);
 pinMode (verticalB, INPUT);
 pinMode (buttonB, INPUT);
 verticalALast = digitalRead(verticalA);
 currentTime = millis();
 loopTime = currentTime;
}

void loop() {
 currentTime = millis();       // Pobierz aktualny czas
 if (currentTime >= (loopTime + 5)) {  // Odczytaj piny enkodera
   encoder_horizontalA = digitalRead(horizontalA);
   encoder_horizontalB = digitalRead(horizontalB);
   encoder_verticalA = digitalRead(verticalA);
   encoder_verticalB = digitalRead(verticalB);
   btnA = digitalRead(buttonA);    // Stan przycisków A
   btnB = digitalRead(buttonB);    // Stan przycisków B

   if (btnA == 0) {
     currentColour++;
     if (currentColour > (numberColours - 1)) {
       currentColour = 0;
     }
     delay(500);
   }
   if (btnB == 0) {
     currentColour--;
     if (currentColour < 0) {
       currentColour = numberColours - 1;
     }
     delay(500);
   }
   // Enkoder A
   if ((!encoder_horizontalA) && (encoder_horizontalA_prev)) {
     // Zbocze opadające sygnału A
     if (encoder_horizontalB) {
       // Sygnał B jest w stanie wysokiem,
       // więc obrót w kierunku przeciwnym do wskazówek zegara
       if (horizontalCursor > 0) {
         horizontalCursor --;
         moved = true;
       }
     }
     else {
       // Sygnał B jest w stanie niskim,
       // więc obrót w kierunku wskazówek zegara
       if (horizontalCursor < 15) {
         horizontalCursor ++;
         moved = true;
       }
     }
   }
   // Store value of A for next time
   encoder_horizontalA_prev = encoder_horizontalA;
   // Enkoder B
   if ((!encoder_verticalA) && (encoder_verticalA_prev)) {
     // Zbocze opadające sygnału A
     if (encoder_verticalB) {
       // Sygnał B jest w stanie wysokiem,
       // więc obrót w kierunku przeciwnym do wskazówek zegara
       if (verticalCursor < 15) {
         verticalCursor ++;
         moved = true;
       }
     } else {
       // Sygnał B jest w stanie niskim,
       // więc obrót w kierunku wskazówek zegara
       if (verticalCursor > 0) {
         verticalCursor --;
         moved = true;
       }
     }
   }
   // Przechowuje wartość A
   encoder_verticalA_prev = encoder_verticalA;
   // Aktualizuje czas pętli
   loopTime = currentTime;
   leds(horizontalCursor, verticalCursor) = colours[currentColour][0];
   // Ruch aktywnej diody LED
   if (moved) {
     leds(horizontal, vertical) = colours[currentColour][1];
     horizontal = horizontalCursor;
     vertical = verticalCursor;
     moved = false;
   }
   FastLED.show();
 }
}

Nie wszystkie panele LED są skonfigurowane w ten sam wzór. Biblioteka LEDMatrix zawiera różne konfiguracje, może być konieczne poeksperymentowanie z wierszami 14, 15 i 16, zależnie od zastosowanej przez nas macierzy LED RGB i jej wewnętrznych połączeń. Więcej informacji znaleźć można w dokumentacji biblioteki LEDMatrix.

Nikodem Czechowski, EP

Źródła
https://bit.ly/3yJB04h
https://bit.ly/3DKeewS
https://bit.ly/3mTaOlH

Artykuł ukazał się w
Elektronika Praktyczna
wrzesień 2021

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik październik 2021

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio wrzesień - październik 2021

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka Podzespoły Aplikacje październik 2021

Automatyka Podzespoły Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna październik 2021

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich październik 2021

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów