// 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.
//
// Modified from https://wokwi.com/arduino/libraries/Adafruit_NeoPixel/buttoncycler and
// and the repaired https://wokwi.com/arduino/projects/325075981102482003
// making wipe loopless
// See https://forum.arduino.cc/t/stopping-a-for-loop-part-way-through/967148
// Code at https://wokwi.com/projects/325673372202566226
// DaveX 2022-03-22 CC 3.0 BY-SA
#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
int j = -1; // extra state variable for some modes
void setup() {
Serial.begin(115200);
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 > 20) mode = 0; // Advance to next mode, wrap around
j = -1 ; //
}
}
// 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;
case 6: {
static int ii = 0;
uint32_t cc = 0;
uint32_t cca[] = {strip.Color(0, 255, 0),
strip.Color(0, 255, 0),
strip.Color(0, 0, 255),
strip.Color(255, 0, 0),
strip.Color(127, 127, 0),
strip.Color(0, 127, 127),
strip.Color(127, 0, 127)
};
switch (ii) {
case 0: cc = strip.Color(0, 255, 0); break;
case 1: cc = strip.Color(0, 0, 255); break;
case 2: cc = strip.Color(255, 0, 0); break;
case 3: cc = strip.Color(0, 0, 0); break;
}
colorWipe(cca[ii], 50);
if (j >= strip.numPixels()) {
++ii %= 4;
j = 0;
}
// Serial.print(ii);
}
break;
case 7:
ciaranhBreatheLEDS(30);
if (j == 255) {
mode = 8;
}
break;
case 8:
fadeToBlack(100);
if (j <= 0 ) {
mode = 9;
j = -1;
}
break;
case 9:
if (j < 0) {
colorFill(strip.Color(255, 0, 0), 0); // Red
strip.show();
j = 255;
}
if (j > 0) {
fadeToBlack(10);
strip.show();
}
if (j == 0) {
j = -1;
mode = 10;
}
break;
default:
mode = 0;
j = -1;
break;
}
report();
}
void report(void) {
unsigned long interval = 500;
static unsigned long last = -interval;
if (millis() - last >= interval) {
last += interval;
Serial.print(mode);
Serial.print(',');
Serial.print(j);
Serial.print("; ");
}
}
// 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), 50); // Black/off
}
on = !on;
j = 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) {
static unsigned long last = 0;
if (millis() - last > wait) {
last = millis();
if (j > strip.numPixels() || j < 0) j = 0;
strip.setPixelColor(j, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
j++;
}
}
void ciaranhBreatheLEDS(int wait) {
// per https://forum.arduino.cc/t/stopping-a-for-loop-part-way-through/967148
//depends on a j as a global state variable.
static int a, b;
static unsigned long last = 0;
if (millis() - last >= wait) {
last = millis();
if (j <= 0 || j > 255 ) { // initialize state vars
j = 0;
a = 100;
b = 100;
Serial.println();
}
for (int i = 0; i < PIXEL_COUNT; i++) {
strip.setPixelColor(i, strip.Color(j, a, b));
}
strip.show();
// Serial.print(j); Serial.print('.');
// Serial.print(a); Serial.print('.');
// Serial.print(b); Serial.print(" ");
++j; ++a &= 255 ; ++b &= 255; // update internal and external state vars
}
}
void ciaranhOffLEDS(int wait) {
static unsigned long last = 0;
if (millis() - last >= wait) {
last = millis();
for (int i = 0; i < PIXEL_COUNT; i++) {
strip.setPixelColor(i, strip.Color(0, 0, 0));
}
strip.show();
j--;
}
}
void fadeToBlack(int wait) {
static unsigned long last = 0;
unsigned long now = millis();
if (now - last >= wait) {
last = now;
unsigned long checksum = 0;
for (int i = 0; i < PIXEL_COUNT; i++) {
uint32_t newpix = fadePixel(strip.getPixelColor(i));
checksum += newpix;
strip.setPixelColor(i, newpix);
}
// Serial.print(';');
// Serial.print(checksum);
// Serial.print(',');
strip.show();
if (checksum == 0) {
j = 0;
}
}
}
uint32_t fadePixel(uint32_t pix) {
// https://adafruit.github.io/Adafruit_NeoPixel/html/class_adafruit___neo_pixel.html#abccaba27e35bfae27c856e76b46c7924
// following https://forums.adafruit.com/viewtopic.php?t=87012
uint32_t W = (pix >> 24) & 0xFF;
uint32_t R = (pix >> 16) & 0xFF;
uint32_t G = (pix >> 8) & 0xFF;
uint32_t B = pix & 0xFF;
uint32_t retval = ((W ? W - 1 : 0) << 24)
| ((R ? R - 1 : 0 ) << 16 )
| ((G ? G - 1 : 0) << 8)
| ((B ? B - 1 : 0));
// Serial.print(retval, HEX); Serial.print('.');
//return (W ? W - 1 : 0) << 24 | (R ? R - 1 : 0 ) << 16 | (G ? G - 1 : 0) << 8 | (B ? B - 1 : 0);
return (retval);
}