// Exemplo do Franzininho com Anéis de LEDs NeoPixel
#include <Adafruit_NeoPixel.h>

// Qual pino do Franzininho está conectado aos NeoPixels?
#define LED_PIN    1

// Quantos NeoPixels estão anexados ao Franzininho?
#define LED_COUNT  4 * 16

// Brilho do NeoPixel (0 a 255)
#define BRIGHTNESS 255 // Set BRIGHTNESS to about 1/5 (max = 255)

// Declara nosso objeto de tira NeoPixel
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  strip.begin();                   // Inicialize o objeto do NeoPixel
  strip.show();                    // Desliga todos os pixels
  strip.setBrightness(BRIGHTNESS); // Brilho do NeoPixel
}

void loop() {
  // Preencha ao longo do comprimento da tira em várias cores
  colorWipe(strip.Color(255,   0,   0), 50);      // Vermelho
  colorWipe(strip.Color(  0, 255,   0), 50);      // Verde
  colorWipe(strip.Color(  0,   0, 255), 50);      // Azul
  colorWipe(strip.Color(  0,   0,   0, 255), 50); // Brando (não o branco RGB)

  whiteOverRainbow(75, 5);

  pulseWhite(5);

  rainbowFade2White(3, 3, 1);
}

// Preenche os pixels da faixa um após o outro com uma cor
void colorWipe(uint32_t color, int wait) {
  for (int i = 0; i < strip.numPixels(); i++) { // Para cada pixel na faixa...
    strip.setPixelColor(i, color);              // Define a cor do pixel (na RAM)
    strip.show();                               // Atualize a faixa
    delay(wait);                                // Pausa por um momento
  }
}

void whiteOverRainbow(int whiteSpeed, int whiteLength) {
  int      head          = whiteLength - 1;
  int      tail          = 0;
  int      loops         = 3;
  int      loopNum       = 0;

  uint32_t lastTime      = millis();
  uint32_t firstPixelHue = 0;

  if (whiteLength >= strip.numPixels())
    whiteLength = strip.numPixels() - 1;

  for (;;) { // Repete para sempre (ou até uma 'pausa' ou 'retorno')
    for (int i = 0; i < strip.numPixels(); i++) { // Para cada pixel na faixa...
      if (((i >= tail) && (i <= head)) ||
          ((tail > head) && ((i >= tail) || (i <= head)))) {
        strip.setPixelColor(i, strip.Color(0, 0, 0, 255)); // Define como branco
      } else {                                             // se não define como arco-íris
        int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
        strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
      }
    }

    strip.show(); // Atualiza a faixa
    // Não há atraso aqui, ele apenas roda em full-tilt até o cronômetro e
    // a combinação do contador abaixo se esgotar

    firstPixelHue += 40; // Avança um pouco ao longo do anél de cores

    if ((millis() - lastTime) > whiteSpeed) { // Tempo de atualizar head/tail?
      if (++head >= strip.numPixels()) {      // Avança o head, envolve em torno
        head = 0;
        if (++loopNum >= loops) return;
      }

      if (++tail >= strip.numPixels()) {      // Avança o tail, envolve em torno
        tail = 0;
      }

      lastTime = millis();                    // Economiza tempo do último movimento
    }
  }
}

void pulseWhite(uint8_t wait) {
  for (int j = 0; j < 256; j++) { // Aumenta de 0 a 255
    // Preenche toda a tira com branco no nível de brilho corrigido por gama
    strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
    strip.show();
    delay(wait);
  }

  for (int j = 255; j >= 0; j--) { // Diminui de 255 para 0
    strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
    strip.show();
    delay(wait);
  }
}

void rainbowFade2White(int wait, int rainbowLoops, int whiteLoops) {
  int fadeVal = 0, fadeMax = 100;

  for (uint32_t firstPixelHue = 0; firstPixelHue < rainbowLoops * 65536;
       firstPixelHue += 256) {

    for (int i = 0; i < strip.numPixels(); i++) { // Para cada pixel na faixa...

      // Desloca a matiz do pixel em uma quantidade para fazer uma revolução
      // completa da roda de cores (intervalo de 65536) ao longo do comprimento da faixa
      uint32_t pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());

      // strip.ColorHSV() pode receber 1 ou 3 argumentos: um matiz (0 a 65535) ou
      // opcionalmente adiciona saturação e valor (brilho) (cada 0 a 255).
      // Aqui estamos usando apenas a variante de três argumentos, embora o
      // o segundo valor (saturação) é uma constante 255.
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue, 255,
                                           255 * fadeVal / fadeMax)));
    }

    strip.show();
    delay(wait);

    if (firstPixelHue < 65536) {                                // Primeiro loop
      if (fadeVal < fadeMax) fadeVal++;                         // Fade in
    } else if (firstPixelHue >= ((rainbowLoops - 1) * 65536)) { // Último loop
      if (fadeVal > 0) fadeVal--;                               // Fade out
    } else {
      fadeVal = fadeMax; // Certifica-se de que o fade está no máximo
    }
  }

  for (int k = 0; k < whiteLoops; k++) {
    for (int j = 0; j < 256; j++) { // Aumenta de 0 a 255
      // Preencha toda a faixa com branco no nível de brilho corrigido por gama
      strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
      strip.show();
    }

    delay(1000); // Pausa 1 segundo

    for (int j = 255; j >= 0; j--) { // Ramp down 255 to 0
      strip.fill(strip.Color(0, 0, 0, strip.gamma8(j)));
      strip.show();
    }
  }

  delay(500); // Pausa 1/2 segundo
}