Przenośna lampa LED RGB

Przenośna lampa LED RGB

Jednym z ciekawszych zastosowań kolorowego oświetlenia jest jego użycie do doświetlania zdjęć, co nadaje im unikalnego charakteru. Odpowiednio dobierając barwę można uzyskać niepowtarzalny efekt. Typowo światło do zdjęcia zabarwia się filtrami na lampach, jednak jest to rozwiązanie niezbyt wygodne i niepraktyczne. Ciekawą alternatywą jest lampa RGB, która może świecić na dowolny kolor. W artykule zaprezentowano opis budowy i programowania kontrolera dla modułów RGB typu WS2812B. Następnie pokazano, jak zastosować taki kontroler do budowy kompaktowej, przenośnej lampy RGB, którą można używać do doświetlania fotografowanej przestrzeni.

Projekt zawiera moduły RGB, czyli takie komponenty, które integrują w sobie trzy diody LED – czerwoną (red), zieloną (green) oraz niebieską (blue). Są to kolory podstawowe, które można sumować w różnych proporcjach dla uzyskania dowolnego koloru ze spektrum światła widzialnego. Regulując niezależnie jasność każdej z diod w module, można sterować kolorem wypadkowym. Na ogół diodami steruje się za pomocą sygnałów PWM (impulsy o zmiennym wypełnieniu), ale wiele modułów LED RGB jest wyposażonych w zintegrowane sterowniki cyfrowe, które regulują intensywność barw (generując sygnał PWM) na podstawie odpowiednich komend cyfrowych.

Moduł WS2812B jest dość popularny, ponieważ jest łatwy w użyciu i oprogramowaniu, a jednocześnie jest dosyć tani. Do sterowania tym modułem wymagane jest tylko jedno wyjście mikrokontrolera. Dodatkowo, można je łączyć ze sobą szeregowo, dzięki czemu nawet wiele modułów można kontrolować w łatwy sposób. Dzięki temu można wybrać niewielki mikrokontroler, który ma tylko kilka wyprowadzeń GPIO, aby sterować wieloma modułami LED RGB.

Autor projektu zastosował moduł ESP-01 w roli kontrolera LED RGB. Moduł ten nie ma wielu pinów GPIO, ale jest idealny do tego projektu. Ma między innymi kompletny interfejs Wi-Fi, więc można go używać do sterowania przez Internet. Moduł RGB WS2812B korzysta tylko z jednego cyfrowego pinu do sterowania. Dzięki unikalnemu interfejsowi szeregowemu kontrolowanie tych diod jest niesamowicie proste. W środowiskach takich jak Arduino, obsługa tego rodzaju modułów często jest już zaimplementowana w dostępnych bibliotekach.

Potrzebne elementy

W systemie zastosowano komponenty SMD oraz przewlekane, aby uzyskać kompaktowy moduł sterujący diodami RGB. Do zestawienia urządzenia potrzebne będą:

  • moduł ESP-01,
  • stabilizator 3,3 V LM1117,
  • cztery rezystory 10 kΩ (SMD),
  • dwa kondensatory 10 μF (SMD),
  • dwa przyciski typu microswitch,
  • złącza śrubowe (2-torowe oraz 3-torowe),
  • kilka szpilek typu goldpin.

Schemat układu

W systemie zaimplementowano najprostszy, podstawowy schemat użycia modułu ESP-01. Rezystor 10 kΩ służy do podciągnięcia pinów konfiguracyjnych ESP-01, wymuszając tryb normalnej pracy. W systemie są da przyciski – jeden służy do restartowania systemu, a drugi do uruchomienia trybu programowania. Jak pokazano na rysunku 1, przyciski podłączone są pod linie RST oraz GPIO0. Jest to bardzo istotne, gdyż pozwala na wykorzystanie wbudowanego w ESP-01 bootloadera i programowanie mikrokontrolera przez prosty interfejs UART.

Rysunek 1. Schemat kontrolera modułów LED RGB

Złącze goldpin o 5 wyprowadzeniach (J1) służy do programowania ESP-01. Odbywa się to poprzez podłączenie modułu do portu USB z użyciem emulatora portu szeregowego. W razie potrzeby do programowania posłużyć może dodatkowe złącze z trzema pinami (J2). Na płytce, która została pokazana na rysunku 2, widać także dwa większe złącza.

Rysunek 2. Rysunek płytki drukowanej kontrolera LED RGB

Mniejsze, 2-pinowe, służy do podłączania do układu zewnętrznego zasilacza. Złącze z trzema pinami służy do podłączenia modułu z diodą (lub diodami) WS2812B.

Na płytce znajduje się także stabilizator IC1. Jest to liniowy stabilizator LDO, który stabilizuje napięcie zasilania dla ESP-01 do wartości 3,3 V z zasilania wejściowego o napięciu 5 V. Stabilizatorowi towarzyszą dwa kondensatory, które są konieczne do zapewnienia mu stabilnej pracy.

Na stronie z projektem (link znajduje się na końcu artykułu) znajdują się pliki schematu oraz płytki drukowanej, wykonane w programie Eagle. To pozwala każdemu zainteresowanemu na pobranie i wygenerowanie plików produkcyjnych do projektu. W ten sposób można je zamówić w wybranym przez siebie zakładzie (autor projektu zamawiał je w PCBWay. Na fotografii 1 pokazano gotowy, zmontowany moduł.

Fotografia 1. Zmontowany kontroler LED RGB

Oprogramowanie firmware

Oprogramowanie samego kontrolera bazuje na przykładzie pochodzącym z biblioteki WS2812 FX dla Arduino, opracowanej przez Harma Aldicka. Skrypt, który służy do sterowania modułu LED RGB, pozwala na kontrolowanie jego działania poprzez sieć. Na listingu 1 zaprezentowano w jaki sposób mikrokontroler hostuje stronę web.

Listing 1. Listing skryptu do obsługi kontrolera LED RGB. Jest to uproszczona wersja skryptu Harma Aldicka, z której usunięto niekrytyczne elementy, oraz definicje wykorzystywanych funkcji. Pełna wersja dostępna jest w repozytorium autora na GitHubie

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WS2812FX.h>

extern const char index_html[];
extern const char main_js[];

#define WIFI_SSID "YOURSSID"        // SSID sieci Wi-Fi
#define WIFI_PASSWORD "YOURPASSWORD"    // Hasło do sieci Wi-Fi

//#define STATIC_IP                         // Odkomentuj, aby ustawić statyczne IP (patrz niżej)
#ifdef STATIC_IP
 IPAddress ip(192,168,0,123);
 IPAddress gateway(192,168,0,1);
 IPAddress subnet(255,255,255,0);
#endif

#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))

#define LED_PIN 2                         // Pin do którego podłączono wejście LED RGB 0 = GPIO0, 2=GPIO2
#define LED_COUNT 24            // Ilość połączonych ze sobą szeregowo modułó RGB

#define WIFI_TIMEOUT 30000                // Okres sprawdzania WiFi (w ms). Resetuje się po tym czasie, jeśli nie można dołączyć Wi-Fi
#define HTTP_PORT 80

#define DEFAULT_COLOR 0xFF5900
#define DEFAULT_BRIGHTNESS 128
#define DEFAULT_SPEED 1000
#define DEFAULT_MODE FX_MODE_STATIC

unsigned long auto_last_change = 0;
unsigned long last_wifi_check_time = 0;
String modes = "";
uint8_t myModes[] = {};
bool auto_cycle = false;

WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
ESP8266WebServer server(HTTP_PORT);

void setup(){
 Serial.begin(115200);
 delay(500);
 Serial.println("\n\nStarting...");

 modes.reserve(5000);
 modes_setup();

 Serial.println("WS2812FX setup");
 ws2812fx.init();
 ws2812fx.setMode(DEFAULT_MODE);
 ws2812fx.setColor(DEFAULT_COLOR);
 ws2812fx.setSpeed(DEFAULT_SPEED);
 ws2812fx.setBrightness(DEFAULT_BRIGHTNESS);
 ws2812fx.start();

 Serial.println("Wifi setup");
 wifi_setup();

 Serial.println("HTTP server setup");
 server.on("/", srv_handle_index_html);
 server.on("/main.js", srv_handle_main_js);
 server.on("/modes", srv_handle_modes);
 server.on("/set", srv_handle_set);
 server.onNotFound(srv_handle_not_found);
 server.begin();
 Serial.println("HTTP server started.");

 Serial.println("ready!");
}


void loop() {
 unsigned long now = millis();

 server.handleClient();
 ws2812fx.service();

 if(now - last_wifi_check_time > WIFI_TIMEOUT) {
   Serial.print("Checking WiFi... ");
   if(WiFi.status() != WL_CONNECTED) {
     Serial.println("WiFi connection lost. Reconnecting...");
     wifi_setup();
   } else {
     Serial.println("OK");
   }
   last_wifi_check_time = now;
 }

 if(auto_cycle && (now - auto_last_change > 10000)) { // cycle effect mode every 10 seconds
   uint8_t next_mode = (ws2812fx.getMode() + 1) % ws2812fx.getModeCount();
   if(sizeof(myModes) > 0) { // if custom list of modes exists
     for(uint8_t i=0; i < sizeof(myModes); i++) {
       if(myModes[i] == ws2812fx.getMode()) {
         next_mode = ((i + 1) < sizeof(myModes)) ? myModes[i + 1] : myModes[0];
         break;
       }
     }
   }
   ws2812fx.setMode(next_mode);
   Serial.print("mode is "); Serial.println(ws2812fx.getModeName(ws2812fx.getMode()));
   auto_last_change = now;
 }
}



void wifi_setup() {
 Serial.println();
 Serial.print("Connecting to ");
 Serial.println(WIFI_SSID);

 WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
 WiFi.mode(WIFI_STA);
 #ifdef STATIC_IP  
   WiFi.config(ip, gateway, subnet);
 #endif

 unsigned long connect_start = millis();
 while(WiFi.status() != WL_CONNECTED) {
   delay(500);
   Serial.print(".");

   if(millis() - connect_start > WIFI_TIMEOUT) {
     Serial.println();
     Serial.print("Tried ");
     Serial.print(WIFI_TIMEOUT);
     Serial.print("ms. Resetting ESP now.");
     ESP.reset();
   }
 }

 Serial.println("");
 Serial.println("WiFi connected");  
 Serial.print("IP address: ");
 Serial.println(WiFi.localIP());
 Serial.println();
}

void modes_setup() {
 modes = "";
 uint8_t num_modes = sizeof(myModes) > 0 ? sizeof(myModes) : ws2812fx.getModeCount();
 for(uint8_t i=0; i < num_modes; i++) {
   uint8_t m = sizeof(myModes) > 0 ? myModes[i] : i;
   modes += "<li><a href=’#’>";
   modes += ws2812fx.getModeName(m);
   modes += "</a></li>";
 }
}

void srv_handle_not_found() {
 server.send(404, "text/plain", "File Not Found");
}

void srv_handle_index_html() {
 server.send_P(200,"text/html", index_html);
}

void srv_handle_main_js() {
 server.send_P(200,"application/javascript", main_js);
}

void srv_handle_modes() {
 server.send(200,"text/plain", modes);
}

void srv_handle_set() {
 for (uint8_t i=0; i < server.args(); i++){
   if(server.argName(i) == "c") {
     uint32_t tmp = (uint32_t) strtol(server.arg(i).c_str(), NULL, 10);
     if(tmp >= 0x000000 && tmp <= 0xFFFFFF) {
       ws2812fx.setColor(tmp);
     }
   }

   if(server.argName(i) == "m") {
     uint8_t tmp = (uint8_t) strtol(server.arg(i).c_str(), NULL, 10);
     uint8_t new_mode = sizeof(myModes) > 0 ? myModes[tmp % sizeof(myModes)] : tmp % ws2812fx.getModeCount();
     ws2812fx.setMode(new_mode);
     Serial.print("mode is "); Serial.println(ws2812fx.getModeName(ws2812fx.getMode()));
   }

   if(server.argName(i) == "b") {
     if(server.arg(i)[0] == ‘-’) {
       ws2812fx.setBrightness(ws2812fx.getBrightness() * 0.8);
     } else if(server.arg(i)[0] == ‘ ‘) {
       ws2812fx.setBrightness(min(max(ws2812fx.getBrightness(), 5) * 1.2, 255));
     } else { // set brightness directly
       uint8_t tmp = (uint8_t) strtol(server.arg(i).c_str(), NULL, 10);
       ws2812fx.setBrightness(tmp);
     }
     Serial.print("brightness is "); Serial.println(ws2812fx.getBrightness());
   }

   if(server.argName(i) == "s") {
     if(server.arg(i)[0] == ‘-’) {
       ws2812fx.setSpeed(max(ws2812fx.getSpeed(), 5) * 1.2);
     } else if(server.arg(i)[0] == ‘ ‘) {
       ws2812fx.setSpeed(ws2812fx.getSpeed() * 0.8);
     } else {
       uint16_t tmp = (uint16_t) strtol(server.arg(i).c_str(), NULL, 10);
       ws2812fx.setSpeed(tmp);
     }
     Serial.print("speed is "); Serial.println(ws2812fx.getSpeed());
   }

   if(server.argName(i) == "a") {
     if(server.arg(i)[0] == ‘-’) {
       auto_cycle = false;
     } else {
       auto_cycle = true;
       auto_last_change = 0;
     }
   }
 }
 server.send(200, "text/plain", "OK");
}

W skrypcie tym trzeba zmodyfikować nazwę SSID oraz hasło do naszej sieci, aby moduł z ESP mógł zalogować się do naszej sieci Wi-Fi. Jeżeli chcemy, aby pracował on ze stałym IP (domyślnie korzysta z DHCP w sieci), należy odkomentować odpowiednią linię oraz uzupełnić adres IP urządzenia, bramy i maskę sieci.

Z skryptu na listingu 1 usunięto elementy odpowiedzialne za komunikację poprzez port szeregowy – są one istotne tylko na etapie debugowania, oraz definicję funkcji zapewniających interfejs sieciowy. Do jego działania potrzebne są jeszcze dodatkowe pliki (index.html.cpp oraz index.js.cpp), które znajdują się w repozytorium. Szkic korzysta z biblioteki WS2812FX.h, która ma wszystkie zaawansowane funkcje do obsługi modułów z rodziny WS2812.

Programowanie

Aby zaprogramować moduł ESP-01 będziemy potrzebowali przejściówki USB-UART z wyjściem 3,3 V TTL. Można ją podłączyć bezpośrednio do modułu ESP-01, jak pokazano na rysunku 3, lub do 5-pinowego złącza na płytce kontrolera, zgodnie z oznaczeniami na schemacie układu (rysunek 1).

Rysunek 3. Sposób podłączania wyprowadzeń interfejsu UART do modułu ESP-01

Do przeprowadzenia programowania niezbędne jest środowisko Arduino IDE. Po upewnieniu się, że posiadamy na komputerze wymaganą bibliotekę WS2812FX.h oraz pobrane z repozytorium GitHub pliki szkicu, można skompilować całość, a następnie załadować do modułu. Aby przetestować działanie należy dołączyć moduł LED RGB i zasilanie, tak jak pokazano na rysunku 4.

Rysunek 4. Kontroler RGB podłączony do modułu LED (po lewej stronie) i zasilania (po prawej stronie)

Obudowa

Lampa zawiera opisany powyżej kontroler oraz panel RGB, który stanowi macierz 8×8 modułów LED RGB. Cały system uzupełnia akumulator typu 18650, zasilający całość i czyniący całe urządzenie przenośnym. Obudowa lampy wykonana jest w technologii druku 3D. Projekt obudowy można pobrać ze strony z projektem. Jest ona relatywnie prostą konstrukcją otwartą z jednej strony.

Fotografia 2. Obudowa lampy RGB

Jak widać na fotografii 2 jest ona podzielona na dwie sekcje – jedną, w której znajduje się elektronika i drugą, gdzie instalowany jest panel z modułami WS2812B.

Koszyk dla akumulatora instalowany jest na zewnątrz, na tylnej ścianie lampki. Po wydrukowaniu i wykończeniu obudowy można umieścić w niej przyciski i przełączniki, które następnie podłączone zostaną do kontrolera. Na fotografii 3 pokazano sposób osadzenia przycisków w obudowie.

Fotografia 3. Sposób zamocowania przycisków w obudowie

Aby uprościć proces montażu, w kontrolerze nie zainstalowano złącz, a przewody zostały przylutowane bezpośrednio do kontrolera. Schemat połączenia poszczególnych elementów pokazano na rysunku 5.

Rysunek 5. Schemat połączeń komponentów

Po połączeniu wszystkich elementów, możliwe jest osadzenie obu kluczowych płytek w obudowie, aby uzyskać konstrukcje taką, jak pokazano na fotografii tytułowej.

Oprogramowanie

Oprogramowanie, które opracował autor, jest dość proste. Pozwala na wyświetlanie kilku kolorów. Aby zmienić kolor, wystarczy raz nacisnąć przycisk trybu. Można dodać dowolny inny kolor lampy, po prostu modyfikując kilka linijek kodu, gdzie zawarto definicję barw. Kod źródłowy tej aplikacji zaprezentowano na listingu 2.

Listing 2. Kod źródłowy aplikacji kontrolującej lampę RGB

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Wymagane do działania 16 MHz Adafruit Trinket
#endif

#define PIN        2   // Pin podłączenia diody
#define NUMPIXELS  64   // Liczba diod
#define BUTTON     0  // Pin podłączenia przycisku

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

int button;
int count;

void setup() {
 pixels.begin(); // Inicjalizacja obiektu NeoPixel
 pixels.setBrightness(255);
 pinMode(BUTTON, INPUT);
}

void loop() {
 button = digitalRead(BUTTON);
 pixels.clear();

 if(button == 0){
   count++;
   delay(300);
   if(count==1){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(128,128,128));
     }
     }
   pixels.show();
   }

   if(count==2){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(255,0,0));
     }
   pixels.show();
   }

   if(count==3){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(0,255,0));
     }
     pixels.show();
   }

   if(count==4){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(128,225,0));
     }
     pixels.show();
   }

   if(count==5){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(0,0,255));
     }
     pixels.show();
   }

   if(count==6){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(0,128,255));
     }
     pixels.show();
   }

   if(count==7){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(255,128,0));
     }
     pixels.show();
   }

   if(count==8){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(255,255,0));
     }
     pixels.show();
   }

   if(count==9){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(0,25,255));
     }
     pixels.show();
   }

   if(count==10){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(127,0,255));
     }
     pixels.show();
   }

   if(count==11){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(255,0,255));
     }
     pixels.show();
   }

   if(count==12){
     for(int i=0; i<NUMPIXELS; i++) {
     pixels.setPixelColor(i, pixels.Color(255,0,127));
     }
     pixels.show();
     count=0;
   }


 }

Aby uprościć działanie systemu, szkic ten wykorzystuje bibliotekę Adafruit_NeoPixel.h, która również pozwala na obsługę diod RGB, takich jak WS2812B.

Fotografia 4. Gotowa lampa RGB

Podsumowanie

Gotowa lampa doskonale oświetla biurko. Na fotografii 5 pokazano, w jaki sposób różne oświetlenie może zmieniać zdjęcie.

Fotografia 5. Przykładowe fotografie wykonane przy różnym oświetleniu lampy RGB

Teraz każdy może osiągnąć podobne efekty samodzielnie. Kontroler może sterować nawet większą ilością paneli RGB, aby uzyskać lampę o większym natężeniu światła. Należy tylko pamiętać o dostarczeniu do diod odpowiedniego zasilania.

Nikodem Czechowski, EP

Źródła:
https://bit.ly/3DbtpiH
https://bit.ly/387CN8K
https://bit.ly/3gmOigM
https://bit.ly/3zaGd6q

Artykuł ukazał się w
Elektronika Praktyczna
wrzesień 2021
DO POBRANIA
Materiały dodatkowe

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik maj 2024

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio maj - czerwiec 2024

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje kwiecień 2024

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna kwiecień 2024

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich maj 2024

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów