Replacing Neopixel Color within Loop

I am wanting to write a program that will run a 'for loop' when a momentary button is pressed, but the color of that loop changes if a magnet is present on the reed switch. I am using an arduino nano every, but final project will be uno R3. I am attempting to change two things: 1. the color of the void flash() and colorWipe(), and 2. whether darken() or darken2() run. As of this moment, the loop will start when button is pressed, and strip will run white. No change with reed switch. Also, not sure why, but my while loop appears like it may also be broken now, as I can only start the program once, then I have to unplug and replug the arduino if I want to be able to press the button again. I have pulled how I am changing things from a previous tutorial that changed colors based on button counter, however that strip fill was started in the setup(), whereas I will eventually have different loops depending on which of my multiple buttons is pushed. Hardware has been tested, so all buttons appear to work.

#include <FastLED_NeoPixel.h>

const int DATA_PIN = 6;
const int NUM_LEDS = 20;

const int button = 2;
int status = false;

const int reed = 5;
int reedval = false;

int currentColor = 0;
int darkenColor = 0;
int purple = CRGB::Plum;
int white = CRGB::White;

FastLED_NeoPixel<NUM_LEDS, DATA_PIN, NEO_GRB> strip;


void setup() {
  pinMode(button, INPUT);
  pinMode(reed, INPUT);
  strip.begin();
}

void loop() {
  int buttonval = digitalRead(button);

  if (buttonval == true) {
    status = !status;
    checkreed();
    colorWipe(strip.Color(currentColor), NUM_LEDS);  // white
    delay(100);
    darkencheck();
    flash();
    blank(1600);
    colorWipe(strip.Color(currentColor), NUM_LEDS);  // white
    delay(100);
    darkencheck();
    flash();
    blank(1600);
  }
  while (buttonval == true)
    ;
  delay(50);
}

void checkreed() {
  reedval = digitalRead(reed);
  if (reedval == HIGH) {
    currentColor = white;
  } else if (reedval == LOW) {
    currentColor = purple;
  }
}

void darkencheck() {
  reedval = digitalRead(reed);
  if (reedval == HIGH) {
    darken2();
  } else if (reedval == LOW) {
    darken();
  }
}

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);
  }
}

void blank(unsigned long wait) {
  strip.clear();
  strip.show();
  delay(wait);
}

void darken() {
  //Serial.begin(9600)
  uint16_t i, j;
  for (j = 255; j > 0; j--) {
    for (i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, j, j, j);
    }
    strip.show();
  }
}

void darken2() {
  uint16_t i, j;
  for (j = 255; j > 0; j--) {
    for (i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, j, j, j);
    }
    strip.show();
  }
}

void flash() {
  strip.fill(currentColor, 0, NUM_LEDS);
  strip.show();
  delay(125);
  darken();
}

  while (buttonval == true)
    ;

How will this while loop ever end when there is nothing in it that changes the value of buttonval ?

1 Like

I don't necessarily want it to end. To be honest, I pulled that from a tutorial using momentary buttons. I thought it was a touch weird, but it worked in my previous test codes, so I copied the principle over. This one is using buttonval instead of a straight "while (digitalRead(button) == true); delay(50);". This was what I wanted, to be able to press the button, run the loop, then press the button again, rerun loop, etc. I have no idea why it's not working now.

Because nothing changes buutonVal. It is not magically associated with the state of the button.

The button state must be read (again) if you expect to see changes.

The first if statement did that, right there in the condition part:

  while (digitalRead(button) == true);

a7

It is because there is nothing in the while loop that changes the value of buttonval so once in the loop the code will never end

Read the state of the button pin in the while loop and exit if the button becomes pressed

but didn't I assign buttonval as equaling digitalRead(button) using the int buttonval = digitalRead(button)? I thought that would assign buttonval as reading the state of the button. If it's not really doing anything, I'll replace it with the origial digitalRead(button), it's just more to type.

Would this change if I moved the int buttonval = digitalRead(button) into the if section, rather than at the start of the void loop?

Yes you did and the code did it once before you entered the while loop then doesn't do it again

Read the state of the pin inside the while loop

Think like a microprocessor. That line of code does something: it assigns the current value of the button it digitalReads to buutonVal.

  buttonval = digitalRead(button);

If the button subsequently changes state, the value you placed in a variable earlier does not change. Only assigning again to that variable will make it track the current button state.

It is a common beginner's mistake to assume, as you did, that a statement can establish a magic connection between elements that persists. The assignment statement as in your code will not do that.

HTH

a7

2 Likes

Okay, thank you. I thought I had put it in the larger loop, so I just replaced the buttonval with the digitalRead(button), and it works. Following that logic, I replaced the reedval in the same way, so now all places should directly be checking for the value when I want it. The check for the reedswitch, to see what color to use, is a function that starts at the beginning of the if set. I'm not sure if that works though, as I don't know if that value will be simply stored with whatever value was at the start of the loop. Is there a way to continuously check a value within the if loop?

Also, thank both you and alto777, for making me rethink when a value is being assessed. I adjusted it in both the reedcheck() and darkencheck(), and was able to get darkencheck() to work (after I realized that darken and darken2 were accidentally set to the same color). Since I got the darkencheck() to work with having an if else loop in a separate void function, would it be smarter to have a similar set up, but with the colorWipe function? I guess I don't really understand why the assigning values isn't working. This is more for my own better understanding (as I am very new), but logically to me, I was saying if(digitalRead) == HIGH, variable colorState = ::CRGBWhite. So to me, anywhere colorState is listed should be changed to ::CRGBWhite, but that's not what's happening. Is this an error with the equal setup, or is this still to do with not checking the setup. Updated code that darken works.

#include <FastLED_NeoPixel.h>

const int DATA_PIN = 6;
const int NUM_LEDS = 20;

const int button = 2;
int status = false;

const int reed = 5;
int reedval = false;

int currentColor = 0;
int darkenColor = 0;

FastLED_NeoPixel<NUM_LEDS, DATA_PIN, NEO_GRB> strip;


void setup() {
  pinMode(button, INPUT);
  pinMode(reed, INPUT);
  strip.begin();
}

void loop() {
  if (digitalRead(button) == true) {
    status = !status;
    checkreed();
    colorWipe(strip.Color(currentColor), NUM_LEDS);  // white
    delay(1000);
    darkencheck();
    blank(1000);
    flash();
    blank(1000);
    colorWipe(strip.Color(currentColor), NUM_LEDS);  // white
    delay(1000);
    darkencheck();
    delay(1000);
    flash();
    blank(1000);
  }
  while (digitalRead(button) == true)
    ;
  delay(50);
}

void checkreed() {
  if (digitalRead(reed) == HIGH) {
    currentColor = CRGB::White;
  } else if (digitalRead(reed) == LOW) {
    currentColor = CRGB::Plum;
  }
}

void darkencheck() {
  if (digitalRead(reed) == HIGH) {
    darken2();
    } else {
    darken();
  }
}

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);
  }
}

void blank(unsigned long wait) {
  strip.clear();
  strip.show();
  delay(wait);
}

void darken() {
  //Serial.begin(9600)
  uint16_t i, j;
  for (j = 255; j > 0; j--) {
    for (i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, j, j, j);
    }
    strip.show();
  }
}

void darken2() {
  uint16_t i, j;
  for (j = 255; j > 0; j--) {
    for (i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, j, i, j);
    }
    strip.show();
  }
}

void flash() {
  strip.fill(currentColor, 0, NUM_LEDS);
  strip.show();
  delay(125);
  darken();
}

If you mean that you read the value in the loop() function then that is fine until the loop() function is prevented from running because you have an infinite while loop within it. The loop() function only repeats the code within it, ie it loops, if the code within it is allowed to run freely

1 Like

That's worth sinking in.

In a common pattern with small programs, the loop runs freely, perhaps thousands of times a second.

Maybe most of the time not doing anything other than deciding it's not time to do anything.

In these programs, inputs can be solicited once at the top of the loop. The middle part of the loop can examine the new inputs and add facts about what we are up to and how that should be moved forward and. The last part of the loop can do all the output, turning motors on or lighting up neopixels or telling the servo where to go.

So you might see a slew of digitalRead()ing. The reason we get away with it in the case of a free running loop is that we do not expect, plan for or even check changes on the values on the pins while we are in the rest of the loop, since we're only going to be there for very short time.

Some algorithms would fail if a switch was read every time its (current) state was needed. A switch can show up as different the very next time you read it! So debouncing always reads the button once and uses that for whatever step it is taking.

In code that does not block, in a loop() that run freely, there are good reasons to read once, or really in general do for once anything that needs doing.

IPO Model

a7

1 Like

Huh, I didn't think about that. That's super helpful and makes a lot of sense! Thank you!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.