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.
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.
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.
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.
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.
// 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