// Simple demonstration on using an input device to trigger changes on your
// NeoPixels. Wire a momentary push button to connect from ground to a
// digital IO pin. When the button is pressed it will change to a new pixel
// animation. Initial state has all pixels off -- press the button once to
// start the first animation.
//
// Code:  https://github.com/adafruit/Adafruit_NeoPixel/blob/master/examples/buttoncycler/buttoncycler.ino
// Original Simulation: https://wokwi.com/arduino/libraries/Adafruit_NeoPixel/buttoncycler
// Fixed simulation: https://wokwi.com/arduino/projects/325075981102482003
// This is a modified version of https://wokwi.com/arduino/libraries/Adafruit_NeoPixel/buttoncycler
// per the comments in https://forums.adafruit.com/viewtopic.php?f=47&t=189023
// and https://github.com/wokwi/wokwi-docs/issues/98

// Modified from https://wokwi.com/arduino/libraries/Adafruit_NeoPixel/buttoncycler
// 1) Moved the pin 2 from blue:2:r to the blue:1:r contact
// 2) Changed to black/red/blink cycle
// 3) Moved switch(mode){} out of button conditional for responsive event loop
// for https://arduino.stackexchange.com/questions/88925/how-exactly-do-bicycle-leds-work-arduino-project/88952#88952

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Digital IO pin connected to the button. This will be driven with a
// pull-up resistor so the switch pulls the pin to ground momentarily.
// On a high -> low transition the button press logic will execute.
#define BUTTON_PIN   2

#define PIXEL_PIN    6  // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 16  // Number of NeoPixels

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)

boolean oldState = HIGH;
int     mode     = 0;    // Currently-active animation mode, 0-9

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  strip.begin(); // Initialize NeoPixel strip object (REQUIRED)
  strip.show();  // Initialize all pixels to 'off'
}

void loop() {
  // Get current button state.
  boolean newState = digitalRead(BUTTON_PIN);

  // Check if state changed from high to low (button press).
  if ((newState == LOW) && (oldState == HIGH)) {
    // Short delay to debounce button.
    delay(20);
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {     // Yes, still low
      if (++mode > 5) mode = 0; // Advance to next mode, wrap around
    }
  }
  // Set the last-read button state to the old state.
  oldState = newState;
  // Manage the LED state based on mode
  switch (mode) {          // Start the new animation...
    case 0:
      colorFill(strip.Color(  0,   0,   0), 0);    // Black/off
      mode = 2;
      break;
    case 2: // idle in off
      break;
    case 3:
      colorFill(strip.Color(255,   0,   0), 0);    // Red
      mode = 4;
      break;
    case 4: // idle in red
      break;
    case 5:
      blinkRed();
      break;
    default:
      mode = 0;
      break;
  }
}

// Blink Red
// this function is patterned after the example in
// https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay
void blinkRed(void) {
  const unsigned long interval = 500;
  static unsigned long last = 0;
  static bool on = true;
  if (millis() - last >= interval) {
    last = millis();  // or last += interval;
    if (on) {
      colorFill(strip.Color(255,   0,   0), 0);    // Red
    } else {
      colorFill(strip.Color(  0,   0,   0), 0);    // Black/off
    }
    on = !on;
  }
}

// Fill strip pixels with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorFill(uint32_t color, int wait) {
  for (int i = 0; i < strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
  }
  strip.show();                          //  Update strip to match
  delay(wait);                           //  Pause for a moment
}

// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
  for (int i = 0; i < strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(wait);                           //  Pause for a moment
  }
}