A primeira coisa boa pra motivar um projeto em muitos casos é a facilidade de “dar a saída”. Isso porque às vezes a ideia já está pronta, indo um pouco além dos estudos conceituais. Nesse artigo veremos a simplicidade de “dar a largada” com NeoPixel (WS2811) usando um Arduino, sem complicações – mas não só, vamos entender “alguns” dos recursos oferecidos pela biblioteca e criar algum movimento também.
LED endereçável com Arduino
Os LEDs endereçáveis iniciados em WS28xxx (tem um WS28xxB também) são controlados por apenas 1 pino de dados. A primeira vantagens de ter o controle individual dos LEDs é a possibilidade de fazer polling em cada pixel ou, se preferir, fazer multiplexação.
Fazer o chaveamento entre os pixels em uma velocidade tão alta quanto possível causará o efeito POV (Persistence Of Vision), onde nossos olhos não são capazes de registrar a mudança de estado dos LEDs, que são ligados e desligados um a um.
Juntando esse efeito ao controle absoluto sobre cada pixel, podemos criar apresentações visuais atraentes, mas para isso precisamos ir além dos códigos de exemplo do NeoPixel.
Outra vantagem da multiplexação é que ligando e desligando os LEDs de forma alternada podemos considerar um consumo de corrente bastante baixo, algo em torno de 50mA. Porém, dependendo da forma que for utilizá-lo, da quantidade de pixels e do número de pixels ligados simultaneamente, pode ser necessário uma alimentação externa.
Nesse artigo apresento o círculo de LEDs endereçáveis RGB WS2811, do nosso parceiro UsinaInfo, alimentado diretamente pelos 5V do Arduino, o que é suficiente para seus 18 pixels.
Já escrevi outros dois artigos a respeito de LEDs endereçáveis, como o artigo LED RGB endereçável, com a barrinha de pixels linear. Também escrevi sobre seu uso com ESP8266, que pode ser visto nesse outro artigo.
Biblioteca NeoPixel com Arduino
Acredito que para todos os artigos relacionados utilizarei a biblioteca NeoPixel, disponível no repositório oficial do Arduino. Instale-o através da (horrorosa) IDE do Arduino, ou se utilizar alguma IDE melhor com o plugin PlatformIO (como o Visual Studio Code da Microsoft, disponível para 3 plataformas diferentes nesse link) você ainda poderá visualizar códigos de exemplo na própria aba do plugin PlatformIO sem precisar abrir um sketch de exemplos.
Wiring do NeoPixel com Arduino
Não será necessário nem desenho, basta entender as conexões.
Na parte de trás do NeoPixel circular temos 6 ilhas para solda. São 3 ilhas de entrada e 3 ilhas de saída, o que significa que podemos interconectar vários NeoPixels sem maiores complicações.
As ilhas de entrada são GND, VCC e DI. O DI é de Digital Input. Nas ilhas de saída muda apenas essa última referência, sendo a ilha DO, de Digital Output.
Para conectar ao Arduino usamos as ilhas de entrada. A solda é bastante simples de fazer; estanhe as ilhas, estanhe os fios e então solde-os.
O fio preto vai ao GND, o fio vermelho vai ao 5V e o fio amarelo vai ao D2.
Código 1: teste de fadding circular
Esse é o puríssimo código de exemplo, que nos servirá como base. A partir dele faremos alguns efeitos para compreender melhor seu funcionamento e criar nossas próprias interações:
#include <Arduino.h> #include <NeoPixelBus.h> #include <NeoPixelAnimator.h> const uint16_t PixelCount = 18; // make sure to set this to the number of pixels in your strip const uint16_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 const uint16_t AnimCount = 1; // we only need one const uint16_t TailLength = 6; // length of the tail, must be shorter than PixelCount const float MaxLightness = 0.4f; // max lightness at the head of the tail (0.5f is full bright) NeoGamma<NeoGammaTableMethod> colorGamma; // for any fade animations, best to correct gamma NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin); // for esp8266 omit the pin //NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount); NeoPixelAnimator animations(AnimCount); // NeoPixel animation management object void SetRandomSeed() { uint32_t seed; // random works best with a seed that can use 31 bits // analogRead on a unconnected pin tends toward less than four bits seed = analogRead(0); delay(1); for (int shifts = 3; shifts < 31; shifts += 3) { seed ^= analogRead(0) << shifts; delay(1); } // Serial.println(seed); randomSeed(seed); } void LoopAnimUpdate(const AnimationParam& param) { // wait for this animation to complete, // we are using it as a timer of sorts if (param.state == AnimationState_Completed) { // done, time to restart this position tracking animation/timer animations.RestartAnimation(param.index); // rotate the complete strip one pixel to the right on every update strip.RotateRight(1); } } void DrawTailPixels() { // using Hsl as it makes it easy to pick from similiar saturated colors float hue = random(360) / 360.0f; for (uint16_t index = 0; index < strip.PixelCount() && index <= TailLength; index++) { float lightness = index * MaxLightness / TailLength; RgbColor color = HslColor(hue, 1.0f, lightness); strip.SetPixelColor(index, colorGamma.Correct(color)); } } void setup() { strip.Begin(); strip.Show(); SetRandomSeed(); // Draw the tail that will be rotated through all the rest of the pixels DrawTailPixels(); // we use the index 0 animation to time how often we rotate all the pixels animations.StartAnimation(0, 66, LoopAnimUpdate); } void loop() { // this is all that is needed to keep it running // and avoiding using delay() is always a good thing for // any timing related routines animations.UpdateAnimations(); strip.Show(); }
O código é auto-explicativo, com comentários (em inglês) bastante claros. Bem, exceto alguns pontos, que precisam ser experimentados, na falta dos conceitos.
Um vídeo demonstrativo desse efeito pode ser visto nesse link.
Documentação da biblioteca NeoPixelBus
Na documentação da biblioteca encontramos detalhes de todos os recursos disponíveis.
Você pode acessar a documentação a partir desse link.
NeoPixelBus<FEATURE, METHOD> strip(PIXELS, PIN);
Aqui fazemos uma instância da classe NeoPixelBus. As features podem ser uma das várias descritas nesse link, conforme as características do NeoPixel utilizado. NeoRgbFeature é adequado para o WS2811, mas os outros funcionam também.
Nos métodos estamos utilizando o mais comum para a maioria dos Arduinos e modelos de NeoPixel, mas outros são permitidos, como o NeoWs2811Method. Existem métodos específicos para ESP8266 e ESP32, que disporei em outro artigo relacionado.
SetPixelColor(PIXEL,COR);
Com esse método podemos manipular individualmente um pixel. Por exemplo, trocando o conteúdo da função loop por:
strip.SetPixelColor(i,0x22); // this is all that is needed to keep it running // and avoiding using delay() is always a good thing for // any timing related routines //animations.UpdateAnimations(); strip.Show(); delay(100); strip.SetPixelColor(i,0x00); i = i < 1 ? 17 : i-1;
Isso deve fazer os pixels rodarem (sem efeito) para a direita, diferente do efeito de fadding que rodava para a esquerda – mas sem efeito. Esse código deve ser inserido na função loop().
Outros métodos da API podem ser vistos nesse outro link.
Como mudar as cores no NeoPixel
Existem diversos métodos para controle de cores. No exemplo que disponho mais adiante, passei o formato de cores para HTML, que é o mais comum, onde as cores RGB são representadas por 3 Bytes, indo de 0x00 à 0xFF para cada uma das 3.
RgbColor yellow(HtmlColor(0x222200)); RgbColor red(HtmlColor(0x220000)); RgbColor green(HtmlColor(0x002200)); RgbColor blue(HtmlColor(0x000022));
Basta adicionar essas linhas antes das funções para ter as variáveis globais relacionadas a essas cores.
Código com algumas animações
A maneira correta de fazê-lo em Arduino é criando as animações a partir do exemplo supracitado, mas usando em um ESP32 com o recurso de tasks, fica fácil implementar sem problemas com delays.
No código a seguir adicionei algumas animações para demonstração.
#include <Arduino.h> #include <NeoPixelBus.h> #include <NeoPixelAnimator.h> const uint16_t PixelCount = 18; // make sure to set this to the number of pixels in your strip const uint16_t PixelPin = 2; // make sure to set this to the correct pin, ignored for Esp8266 const uint16_t AnimCount = 1; // we only need one const uint16_t TailLength = 6; // length of the tail, must be shorter than PixelCount const float MaxLightness = 0.4f; // max lightness at the head of the tail (0.5f is full bright) NeoGamma<NeoGammaTableMethod> colorGamma; // for any fade animations, best to correct gamma NeoPixelBus<NeoGrbFeature, NeoWs2811Method> strip(PixelCount, PixelPin); //NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin); //NeoPixelBus<NeoRgbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin); // for esp8266 omit the pin //NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount); NeoPixelAnimator animations(AnimCount); // NeoPixel animation management object uint8_t i = 17; void clear(); /* CORES Assim gera as cores passadas no formato HTML, sendo 1 Byte para cada cor: RR GG BB */ RgbColor yellow(HtmlColor(0x222200)); RgbColor red(HtmlColor(0x220000)); RgbColor green(HtmlColor(0x002200)); RgbColor blue(HtmlColor(0x000022)); RgbColor rosa(HtmlColor(0x401310)); RgbColor mostarda(HtmlColor(0xF38000)); void makeStar(){ Serial.println("makeStar"); for (i = 18;i>0;i--){ if (i%2 == 0){ strip.SetPixelColor(i,green); strip.Show(); delay(100); } } for (i = 18;i>0;i--){ if (i%2 != 0){ strip.SetPixelColor(i,yellow); strip.Show(); delay(100); } } for (i = 0;i<19;i++){ if (i%2 == 0){ strip.SetPixelColor(i,0); strip.Show(); delay(100); //strip.SetPixelColor(i,0); //delay(100); //strip.Show(); } } for (i = 0;i<19;i++){ if (i%2 != 0){ strip.SetPixelColor(i,0); strip.Show(); delay(100); //strip.SetPixelColor(i,0); //delay(100); //strip.Show(); } } } //roda para a direita a cada 15 segundos void walkRight(uint8_t times){ Serial.println("walkRight"); for (i = 18;i>0;i--){ strip.SetPixelColor(i,blue); strip.Show(); delay(60); strip.SetPixelColor(i,0); strip.Show(); } } //loop rápido para a direita por 'times' vezes com intervalo de 'interval' void loopRight(uint8_t times,uint8_t interval){ Serial.println("loopRight"); for (uint8_t t = 0; t< times;t++){ for (i = 18;i>0;i--){ strip.SetPixelColor(i,mostarda); strip.Show(); delay(interval); strip.SetPixelColor(i,0); strip.Show(); delay(interval); i = i < 1 ? 17 : i-1; } } } //loop rápido para a esquerda por 'times' vezes com intervalo de 'interval' void loopLeft(uint8_t times,uint8_t interval){ Serial.println("loopLeft"); for (uint8_t t = 0; t< times;t++){ for (i = 0;i<18;i++){ strip.SetPixelColor(i,rosa); strip.Show(); delay(interval); strip.SetPixelColor(i,0); strip.Show(); delay(interval); i = i >17 ? 0 : i+1; } } } void clear(){ Serial.println("Clear"); for (i = 17;i>0;i--){ strip.SetPixelColor(i,0); delay(60); strip.Show(); } } void setup() { Serial.begin(9600); strip.Begin(); } void loop() { delay(1000); loopLeft(3,60); delay(1000); Serial.println("ok"); loopRight(3,60); delay(1000); Serial.println("ok"); walkRight(3); delay(1000); Serial.println("ok"); makeStar(); delay(1000); Serial.println("ok"); }
As cores estão próximas à linha 30 do código, mas é fácil compor as suas.
Vídeo
Também fiz um vídeo demonstrativo de todas essas funções rodando, disponível em nosso canal DobitaobyteBrasil no Youtube.
Qual a aplicação do NeoPixel?
É divertido, bonito, mas qual a aplicação? Bem, em breve devo fazer o primeiro projeto envolvendo o NeoPixel circular, espero que acompanhe para ver um resultado bastante interessante.
Onde comprar NeoPixel ring?
Como citado no link mais ao início do Artigo, esse NeoPixel está disponível na UsinaInfo. O preço está ótimo, considerando 18 LEDs RGB endereçáveis em uma placa e considerando também o projeto que virá.
Até a próxima!
Revisão: Ricardo Amaral de Andrade
1 comentário
Comments are closed, but trackbacks and pingbacks are open.