NeoPixel state controlled by another Arduino output

I am searching for an answer and possible example to handle the following:

I have multiple Arduinos working multiple puzzles and I have a Noepixel ring to show state and progress. I want to initially start off with a pattern (lets say Red/ White/Blue) and replay that pattern until the first puzzle is solved; that puzzle will have a relay and/or other outputs available. Once each successful puzzle solution is realized and the output is triggerred, I want the pixels to do something different. I can get the sketch example to work and I can modify the colors and chase patterns, but adding the multiple outputs from the other Arduinos as Inputs to the LED control NANO to control things eludes me.

Here is the code from the example:

// 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. As written, the button does not interrupt an
// animation in-progress, it works only when idle.

#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 45  // 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'
  strip.setBrightness(10); // Set BRIGHTNESS to about 1/5 (max = 255)
}

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 > 8) mode = 0; // Advance to next mode, wrap around after #8
      switch(mode) {           // Start the new animation...
        case 0:
          colorWipe(strip.Color(  0,   0,   0), 50);    // Black/off
          break;
        case 1:
          colorWipe(strip.Color(255,   0,   0), 50);    // Red
          break;
        case 2:
          colorWipe(strip.Color(  0, 255,   0), 50);    // Green
          break;
        case 3:
          colorWipe(strip.Color(  0,   0, 255), 50);    // Blue
          break;
        case 4:
          theaterChase(strip.Color(127, 127, 127), 50); // White
          break;
        case 5:
          theaterChase(strip.Color(127,   0,   0), 50); // Red
          break;
        case 6:
          theaterChase(strip.Color(  0,   0, 127), 50); // Blue
          break;
        case 7:
          rainbow(10);
          break;
        case 8:
          theaterChaseRainbow(50);
          break;
      }
    }
  }

  // Set the last-read button state to the old state.
  oldState = newState;
}

// 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
  }
}

// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
  for(int a=0; a<10; a++) {  // Repeat 10 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in steps of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show(); // Update strip with new contents
      delay(wait);  // Pause for a moment
    }
  }
}

// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // Hue of first pixel runs 3 complete loops through the color wheel.
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 3*65536. Adding 256 to firstPixelHue each time
  // means we'll make 3*65536/256 = 768 passes through this outer loop:
  for(long firstPixelHue = 0; firstPixelHue < 3*65536; firstPixelHue += 256) {
    for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
      // Offset pixel hue by an amount to make one full revolution of the
      // color wheel (range of 65536) along the length of the strip
      // (strip.numPixels() steps):
      int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
      // strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
      // optionally add saturation and value (brightness) (each 0 to 255).
      // Here we're using just the single-argument hue variant. The result
      // is passed through strip.gamma32() to provide 'truer' colors
      // before assigning to each pixel:
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
    }
    strip.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
  }
}

// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
  int firstPixelHue = 0;     // First pixel starts at red (hue 0)
  for(int a=0; a<30; a++) {  // Repeat 30 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in increments of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        // hue of pixel 'c' is offset by an amount to make one full
        // revolution of the color wheel (range 65536) along the length
        // of the strip (strip.numPixels() steps):
        int      hue   = firstPixelHue + c * 65536L / strip.numPixels();
        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show();                // Update strip with new contents
      delay(wait);                 // Pause for a moment
      firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
    }
  }
}

I'm fairly certain I need to put in some code such as:

const byte BUTTON_PINS[] = {2, 3, 4, 5};

But how does this tie into the loop?

perhaps you wanted to say "inputs" instead of outputs?

Yes, In a way I did. I edited the post as such. I meant to say the multiple outputs as inputs to the Nano LED controller.

Your answer is still not clear. As I understand it, you have several inputs (buttons), depending on which you want to show different patterns on the led.
What is the "led controller" - you mean your arduino board or external device?

I have multiple Arduino Uno's controlling their own separate puzzle games and one Arduino Nano controlling the LEDS. Upon completion of each puzzle, I want the arduino of that game to output a signal either by an output pin or it's contact closure (preferably by the contact itself so as to avoid ground issues) The Arduino Nano would receive those signals through an input pin and that will decide which pattern to show. This should be instantaneous and interrupt whichever pattern loop it is currently displaying.

And what is your problem with it?

I'm getting a bit lost on how to call out the different patterns using a simple If pin high then . Am I overthinking this?

I suppose that you have a several such pins - one from the each of Uno ?

Each of the respective Uno's can be given their own output pin, sure. For a standalone I am experimenting with a simple "button" for the time being.

My main question is "is this as simple as calling out an input pin, then reading that input pin with an 'if' and a 'then' statement?

The answer is perhaps "yes" if you know which pattern should be shown for each combination of input pin states.

Yes as I understand what you are doing.

If you can make your code work with seven pushbuttons on input lines on the Ring Arduino,

those seven inputs can come from anywhere, as long as the voltage is between 0 and Vcc (5 or 3,3 volts often).

The Ring Arduino cannot tell and could not care from where the inputs come.

There may some practical issues, at the very least you must know that there will need to be a common ground.

Someone will point out that you could use optoisolators, and need no common ground.

How far away are the satellite Arduinos?

Wwhy have you multiple boards? Sounds like an interesting projext.

a7

Alto, This is in direct correlation to the other project with the toggle switches. I finally got that ironed out. This is part of an "escape room" scenario, but a portable setup. The script reads something to the effect of the following;

The setup; a clear box situated on top of a pillar with a large "diamond" on top

The object is to work through the puzzles to get to the diamond

Puzzle one is simple; a physical four digit combo file drawer lock; the code is going to be derived through a color wheel being presented under (and through) the diamond which corresponds to a multicolor serial number etched all officially looking and riveted on the side.

Puzzle two is where the Arduinos come in; a simple program of a membrane keypad and multidigit code; Code derived through a schematic and "worn out" copy of the manual with a specific section on "resetting" the security on the diamond; essentially hacking the security protocols.

In the initial setup, you would already have in your possession an employee spreadsheet with a ton of useless information and RFID cards.

Puzzle two will be the actual "bringing the computer security back online" where the toggle switches have to be thrown in an order being derived through the schematics.

Puzzle three will be a "connect the wire" puzzle where the schematic and manual will come into play again.

Puzzle four will be the RFID badge reader where you have to put the employees in order and scan their badge; the employee spreadsheet and accompanying photos will dictate a subtle order of a few of the employees and the keycards will be placed in their respective slots.

Puzzle five will be back to the keypad to enter the "master arm code associated with the top ranking employee VIA keycard (the actual code produced by scanning the RFID will be the code) and that will produce a four digit code on the "go" button which is padlocked with a button cover.

That finally will open the clear top lock and give access to the "diamond"

WHEW!!

Most of the logistics are done and construction is beginning when I have time. Just a few odds and ends to clean up. I guess the reason for the separate Arduino's was keeping things simple and keeping their respective codes compartmentalized for easy updating. Each one will have a relay that will pop open either a maglock or a spring loaded latch.

So, the LEDS are the only thing holding me up right now and I though "how hard can that be" and seeing the delays versus millis in a few online posts really had me wondering; is this really that hard or am I overthinking the logic here.

Oh, hey, didn't even notice that was you. It's a glorious warm not too breezy day here on the beach; to read your words above now woukd not be giving them the attention they no doubt deserve, that shall come later or even later.

a7

Thank you. Additionally, there might also be an MP3 player (Arduino mounted) to play sounds as well. Might be a full timer as well started when the first door is opened until the "GO" button is hit, working through my thoughts on that as well.

Getting there. Still have a few bugs to kick out....or I am completely going down the wrong rabbit hole altogether.


#include <Adafruit_NeoPixel.h>
#define NB 4
#define PIXEL_COUNT 45  // Number of NeoPixels
#define PIXEL_PIN    6  // Digital IO pin connected to the NeoPixels.


//BUTTONS
////Momentary button vars
int bp[NB] = {7, 8, 9, 10}; //arduino input pin numbers
boolean bg[NB] = {false, false, false, false};
//Toggle button vars
int btv[NB] = {0, 0, 0, 0};
int btamt[NB] = {2, 2, 2, 2};;
boolean btg[NB] = {true, true, true, true};
Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  Serial.begin(9600);
  for (int i = 0; i < NB; i++) pinMode(bp[i], INPUT_PULLUP);
  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(200); // Set BRIGHTNESS (max = 255)
}

void loop() {

  //BUTTONS
  for (int i = 0; i < NB; i++) {

    if ( digitalRead(bp[i]) == LOW) { //button pushed

      //momentary
      if (bg[i]) {
        bg[i] = false;
        colorWipe(strip.Color(  0, 255,   0), 50); // Green
        colorWipe(strip.Color(  0,   0, 255), 50); // Blue
        Serial.print("b");
        Serial.print(String(i));
        Serial.print(":");
       // Serial.print( "b" + String(i) + ":");
        Serial.println(1);
      }
      

      //momentary
      if (bg[i+1]) {
        bg[i+1] = false;
        colorWipe(strip.Color(255,   0,   0), 50); // Red
        colorWipe(strip.Color(  0,   0, 255), 50); // Blue
        Serial.print("b");
        Serial.print(String(i));
        Serial.print(":");
       // Serial.print( "b" + String(i) + ":");
        Serial.println(1);
      }

      //momentary
      if (bg[i+2]) {
        bg[i+2] = false;
        theaterChase(strip.Color(127, 127, 127), 50); // White, half brightness
        Serial.print("b");
        Serial.print(String(i));
        Serial.print(":");
       // Serial.print( "b" + String(i) + ":");
        Serial.println(1);
      }

      //momentary
      if (bg[i+3]) {
        bg[i+3] = false;
        colorWipe(strip.Color(255,   0,   0), 50); // Red
        colorWipe(strip.Color(  0,   0, 255), 50); // Blue
        Serial.print("b");
        Serial.print(String(i));
        Serial.print(":");
       // Serial.print( "b" + String(i) + ":");
        Serial.println(1);
      }      



    }
    else { //button released

      //momentary
      if (!bg[i]) {
        bg[i] = true;
        Serial.print( "b" + String(i) + ":");
        Serial.println(0);
      }



    }

  }
  delay(30);
}
// Some functions of our own for creating animated effects -----------------

// 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
  }
}

// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
  for(int a=0; a<10; a++) {  // Repeat 10 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in steps of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show(); // Update strip with new contents
      delay(wait);  // Pause for a moment
    }
  }
}

// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // Hue of first pixel runs 5 complete loops through the color wheel.
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
  // means we'll make 5*65536/256 = 1280 passes through this loop:
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    // strip.rainbow() can take a single argument (first pixel hue) or
    // optionally a few extras: number of rainbow repetitions (default 1),
    // saturation and value (brightness) (both 0-255, similar to the
    // ColorHSV() function, default 255), and a true/false flag for whether
    // to apply gamma correction to provide 'truer' colors (default true).
    strip.rainbow(firstPixelHue);
    // Above line is equivalent to:
    // strip.rainbow(firstPixelHue, 1, 255, 255, true);
    strip.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
  }
}

// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
  int firstPixelHue = 0;     // First pixel starts at red (hue 0)
  for(int a=0; a<30; a++) {  // Repeat 30 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in increments of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        // hue of pixel 'c' is offset by an amount to make one full
        // revolution of the color wheel (range 65536) along the length
        // of the strip (strip.numPixels() steps):
        int      hue   = firstPixelHue + c * 65536L / strip.numPixels();
        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show();                // Update strip with new contents
      delay(wait);                 // Pause for a moment
      firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
    }
  }
}


I do like the "sandbox" to play around in.

The wrong rabbit hole vs. the right rabbit hole, LOL!

Programming is going down one kind or another every time.

I'm on the hot sand under an umbrella and wokwi isn't terribly good on my tablet but…

...it looks like you are testing using pushbutton proxies for the signals which are going to come from the satellite puzzle Arduinos.

And that seems to be working.

The only thing that may not be a flaw but is certainly true is that the system is incapable of interrupting an effect, and blind to changes on the input lines during.

So what? Only you can say that's no problem, I can easily see that it might not be.

About the whole thing, dude you having way too much fun and if you are also getting paid for this I am sorry to only be able to enjoy your success vicariously.

Generally we like to make ppl (!) do everything on one board, as can often be the case. Noobs alla time think throwing boards at a problem is better/easier than learning how to code.

In this case of a very thin interface between the various part, that is to say one digital line with an infrequently changing signal, you are probably better off with multiple boards, easier for all the reasons I think you stated.

Also easy to test, both the display thing and the satellites, either with pushbutton as in your wokwi, or an LED for the satellites.

IRL there may be some excitement when these units are deployed all over the room, easily extinguished excitement for sure, and a bridge to cross when the time comes. Above my pay grade, TBH, but simple as far as I have seen to solve issues.

I'll take a close look at the code in the lab where we have some real computers.

But if it works, proceed. You did mention some hiccups, elaborate if you can't hold your breath long enough for them to go away.

a7

More Progress, I have all the buttons working, rid the program of some extraneous code and organized it somewhat to what I want the order to be. I also switched to the FASTLED protocol to see if I like it better; meh! a bit more controllable with the parameters, but almost the same. We will see.

The only thing, and I think Alto reffered to it

And that may not be a problem. I am going to try to explain the flow I want to work and then what it is doing I don't seem to like.

FLOW: upon startup a looping colorwipe of four colors - red, blue, green and orange. When a button is pressed or a relay is kicked, what have you, lets say input realized, it goes into another pattern, like Red and Blue Marquee (simulated alarm) and then back to the original colorwipe.

ACTUAL FLOW: Upon startup, there is a Colorwipe happening that cycles through four times, once for each button to realize they are not pushed, then a pause until a button is pressed. Then a color cycle until the button is released then once through the original colorwipe and pause.

I want the original colorwipe to continue looping until a button is pressed, that button will be held for an amount of time (to allow the cycle to go around once at least I guess). I don't want to hold that relay indefinitely though as it would not allow any other buttons to be realized.

Here is my code so far:


#include <FastLED_NeoPixel.h>
#define NB 4
#define NUM_LEDS 24 // Number of NeoPixels
#define DATA_PIN 6  // Digital IO pin connected to the NeoPixels.
#define BRIGHTNESS 10 // LED brightness, 0 (min) to 255 (max)

//BUTTONS
////Momentary button vars
int bp[NB] = {7, 8, 9, 10}; //arduino input pin numbers
boolean bg[NB] = {false, false, false, false};
//Toggle button vars
int btv[NB] = {0, 0, 0, 0};
int btamt[NB] = {2, 2, 2, 2};;
boolean btg[NB] = {true, true, true, true};
FastLED_NeoPixel<NUM_LEDS, DATA_PIN, NEO_GRB> strip;      // <- FastLED NeoPixel version


void setup() {
  Serial.begin(9600);
  for (int i = 0; i < NB; i++) pinMode(bp[i], INPUT_PULLUP);
  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(200); // Set BRIGHTNESS (max = 255)
  
}

void loop() {

  //BUTTONS
  for (int i = 0; i < NB; i++) {

    if ( digitalRead(bp[i]) == LOW) { //button pushed
  

      //momentary
      if (bg[i]) {
        bg[i] = false;
        Serial.print("b");
        Serial.print(String(i));
        Serial.print(":");
       // Serial.print( "b" + String(i) + ":");
        Serial.println(1);
      }

      //momentary
      if ( digitalRead(bp[0]) == LOW) {
        bg[0] = false;
        rainbow(10, 3);   

      }
      

      //momentary
      if ( digitalRead(bp[1]) == LOW) {
        bg[1] = false;
        colorWipe(strip.Color(255,   0,   0), 50); // Red
        colorWipe(strip.Color(  0,   0, 255), 50); // Blue

      }

      //momentary
      if ( digitalRead(bp[2]) == LOW) {
        bg[2] = false;
        colorWipe(strip.Color(255,   0,   0), 50); // Red
        colorWipe(strip.Color(  0,   0, 255), 50); // Blue

      }

      //momentary
      if ( digitalRead(bp[3]) == LOW) {
        bg[3] = false;
        theaterChase(strip.Color(127, 127, 127), 100, 3, 5); // White, half brightness
      }      



    }
    else { //button released

      //momentary
      if (!bg[i]) {
        bg[i] = true;          
        Serial.print( "b" + String(i) + ":");
        Serial.println(0);
        colorWipe(strip.Color(255,   0,   0), 50); // Red
        colorWipe(strip.Color(  0, 255,   0), 50); // Green
        colorWipe(strip.Color(  0,   0, 255), 50); // Blue
        colorWipe(strip.Color(255, 165,   0), 50); // Orange  
      }



    }

  }
  delay(30);
}
/*
* Fills a strip with a specific color, starting at 0 and continuing
* until the entire strip is filled. Takes two arguments:
* 
*     1. the color to use in the fill
*     2. the amount of time to wait after writing each LED
*/
void colorWipe(uint32_t color, unsigned long wait) {
	for (unsigned int i = 0; i < strip.numPixels(); i++) {
		strip.setPixelColor(i, color);
		strip.show();
		delay(wait);
	}
}

/*
* Runs a marquee style "chase" sequence. Takes three arguments:
*
*     1. the color to use in the chase
*     2. the amount of time to wait between frames
*     3. the number of LEDs in each 'chase' group
*     3. the number of chases sequences to perform
*/
void theaterChase(uint32_t color, unsigned long wait, unsigned int groupSize, unsigned int numChases) {
	for (unsigned int chase = 0; chase < numChases; chase++) {
		for (unsigned int pos = 0; pos < groupSize; pos++) {
			strip.clear();  // turn off all LEDs
			for (unsigned int i = pos; i < strip.numPixels(); i += groupSize) {
				strip.setPixelColor(i, color);  // turn on the current group
			}
			strip.show();
			delay(wait);
		}
	}
}

/*
* Simple rainbow animation, iterating through all 8-bit hues. LED color changes
* based on position in the strip. Takes two arguments:
* 
*     1. the amount of time to wait between frames
*     2. the number of rainbows to loop through
*/
void rainbow(unsigned long wait, unsigned int numLoops) {
	for (unsigned int count = 0; count < numLoops; count++) {
		// iterate through all 8-bit hues, using 16-bit values for granularity
		for (unsigned long firstPixelHue = 0; firstPixelHue < 65536; firstPixelHue += 256) {
			for (unsigned int i = 0; i < strip.numPixels(); i++) {
				unsigned long pixelHue = firstPixelHue + (i * 65536UL / strip.numPixels()); // vary LED hue based on position
				strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));  // assign color, using gamma curve for a more natural look
			}
			strip.show();
			delay(wait);
		}
	}
}


This is the problem area. If you want an input to trigger a rapid shift from a background or resting effect that is to be continuously displayed when there are no triggers, the background effect cannot block, that is to say it cannot totally steal away the attention of the processor in order to do the effect.

The possibilities range on a spectrum. The coding challenge in parallel is on a spectrum.

On one end of that, a continuous effect might be non-interruptible. Bad. No trigger would ever be seen. Bad but easy.

Next, an effect might go for some time, then need to be kicked off again giving the appearance of continuity but affording a brief window every so often, where triggers might be seen and reacted upon. Better. But if those windows of opportunity only show up every, say, seven seconds, the response will be inconsistent and usually sluggish feeling. Better, and only a bit more of a challenge. Not too hard.

At the far end, a background effect could be stopped more or less instantaneously, at least to human perception, allowing for the triggers to be seen and a trigger effect to take over immediately. Best. Best but hardest to program, as this is where an entirely different method of doing the background effect would need to be implemented. Which method is so far not part of anything you have done, at least so far as I remember.

I'll look closer when I am in better circumstances.

Right away however I note differences between the posted code and the code in the wowki you link - insignificant the ones I saw, are there any substantive differences?

I also see some unused variables which lead to me request that you outline your thinking about what they might mean and do when you get around to using them.

L8R - it's another spectacular day here, perfect for you know what and watching the news about the truly wretched weather other ppl are "enjoying". :expressionless:

It may be easier to jettison all use of the ring pixels for the momentand substitute simple serial printing statements to the tune of "would do effect blah blah blah".

Right now, such a proxy effect would be equivalent to

  Serial.println("color wiping blah blah blah see you in 12345 milliseconds");
  delay(12345); 

so you can get a real idea of why delay() is what messes this up and will need to be expunged.

Test the basic trigger logic without the delays, then put the delays in and see what becomes of the behaviour when you do.

There are a variety of low-rent and higher rent hacks that can also be deployed. Which takes more daring and effort is unclear. I prefer to avoid hacks, but it is hard to argue with success on aesthetic bases.

a7

And there you have it folks, it works they way my mind wanted it to. I moved the standard color cycle right up to the top of the loop so it cycles continually until a button is pressed. I changed the "IF" to a "WHILE" command and it holds that cycle for as long as necessary which will be provided by the output relay from whatever activity just got completed.

I will put the effects for each output as I see fit and hold the relay for as long as I need to.

Thanks all, I will try to post a build story once I get everything built.

Nice. I never argue with success.

Just out of abject curiosity I put some code in to measure you blocking.

effect time away
loop color wipe 4883
bg[0] rainbow 9429
bg[1] colorWipe 282
bg[2] theaterChase 1513
bg[3] theaterChase 1513

The loop color wipe goes away for 4.9 seconds, average response to a button is half that, ~2.5 seconds.

The rainbow will run for an average of 4.5 seconds after its input goes away but might take 9.4 seconds.

The color wipe response is the most rapid, average 140 milliseconds.

The two theater chases are the same, and will run on for 0.75 seconds on average.

The slow response is less a problem to me than the fact that response times will be inconsistent.

It works the way you want. The slow and inconsistent response of the base station is not a problem. Should any of those delays ever be a problem, as I have said they needn't remain. Problematic.

The loop would be fairly easy to make 4x faster response on average simply by doing the next of four color wipes each time through the loop in a cylce instead of all four, viz:

//... 

const unsigned long wipeColors[4] = {
0xff00000,  // Red
0x00ff00,   // Green
0x0000ff,   // Blue
0xffa500,   // Orange
};

void loop() {

  static byte whichColor;
  colorWipe(strip.Color(wipeColors[whichColor]), 50);
  whichColor++; if (whichColor >= 4) whichColor = 0;

  //BUTTONS

//...

Now the loop goes 4x faster, with a maximum delay to responding of 1.2 seconds. This is a demonstration of one basic technique for coding around blocking.

In case there is anyone else following along.

a7