Manual
do
Maker
.
com
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.
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.
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.
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 amarelovai ao D2.
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.
Na documentação da biblioteca encontramos detalhes de todos os recursos disponíveis.
Você pode acessar a documentação a partir desse link.
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.
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.
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.
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.
Também fiz um vídeo demonstrativo de todas essas funções rodando, disponível em nosso canal DobitaobyteBrasil no Youtube.
É 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.
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
Inscreva-se no nosso canal Manual do Maker no YouTube.
Também estamos no Instagram.
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.