control neopixel ring with button and millis

Hi all,

I have been interested in the world of arduino for a few weeks now and I am now embarking on the design of a program to control neopixel rings.

My goal would be to integrate two different modes managed with a button. (a pause mode and an active mode).

Here in 4 points the idea that I have of the program:

  • When the button is disabled you can see the rainbow animation

  • If the button is activated, the neopixel switches to a fixed color for a certain time.

-If during this time the button is reactivated, this resets the duration of this time.

  • At the end of this certain time without pressing the button, we find our rainbow animation

here is an extract of the code that I am trying to make, in this program the animation is replaced by simple setPixel. It is probably very imperfect and does not work for the moment.

it seems to me that I lose myself a little with these millis () function that's why I want to ask for your help. It reads well the fact that my button is not activated, but as soon as I press it this is where it poses problem, the program crashes.

Thank you for your answers.

TESTboutonencore.ino (2.2 KB)

The OP's code Auto formatted in the IDE and posted here using code tags
Please do this in future rather than attaching small programs

#include <Adafruit_NeoPixel.h>

#define LED_PIN12     12
#define LED_NUM       16

#define BOUTON_PIN  2
int etatBouton; //variable qui enregistre l'état du bouton
unsigned long currentMillis;
unsigned long enclenchementMillis;
unsigned long pauseIntervalle;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_NUM, LED_PIN12, NEO_GRB + NEO_KHZ800);

void setup()
{
  Serial.begin(9600);
  pinMode(BOUTON_PIN, INPUT_PULLUP);
  etatBouton = HIGH;
  strip.begin();
  strip.setBrightness(100);
  strip.show(); // Initialize all pixels to 'off'
  currentMillis = 0;
}

void loop()
{
  etatBouton = digitalRead(2);
  Serial.println(etatBouton, DEC);
  Serial.println(enclenchementMillis);
  if (etatBouton == HIGH)
  {
    currentMillis = millis();
    strip.fill(strip.Color(0, 0, 255), 0, 16);
    strip.show();
    else
    {
      if (etatBouton == LOW)
      {
        currentMillis = millis();
        enclenchementMillis = currentMillis;
        strip.fill(strip.Color(255, 20, 50), 0, 16);
        strip.show();
      }
      else
      {
        currentMillis = millis();
        pauseIntervalle = currentMillis - enclenchementMillis;
      }
      if (pauseIntervalle > 5000)
      {
        strip.fill(strip.Color(0, 0, 255), 0, 16);
        strip.show();
      }
      else
      {
        strip.fill(strip.Color(255, 20, 50), 0, 16);
        strip.show();
      }
    }
  }
}

void rainbow(int rainbowLoops, int F = 100, int speed = 3, int S = 255)
{
  int fadeVal = 0, fadeMax;
  fadeMax = constrain(F, 1, 100);
  for (uint32_t firstPixelHue = 0; firstPixelHue < rainbowLoops * 65536;
       firstPixelHue += 256)
  {
    for (int i = 0; i < strip.numPixels(); i++)
    {
      uint32_t pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
      strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue, S, 255 * fadeVal / fadeMax )));
    }
    strip.show();
    delay(speed);
    if (firstPixelHue < 65536)                               // Premiere boucle,
    {
      if (fadeVal < fadeMax) fadeVal++;                      // fade in
    }
    else if (firstPixelHue >= ((rainbowLoops - 1) * 65536))  // Derniere boucle loop,
    {
      if (fadeVal > 0) fadeVal--;                            // fade out
    }
    else
    {
      fadeVal = fadeMax; // boucle intermediaire fade=fademax
    }
  }
}

ok sorry and thank you for putting it like this.

does the code structure seem correct?

I continue my tests but do not understand exactly what to change for it to work.

herralex35:
does the code structure seem correct?

No. You have the following construct:
if x
{...}
else
{ if not x (redundant because you already tested it in the first line)
{...}
else
{... nothing in here can ever be executed...}
}

because if x is true in the first line, nothing in the else clause is ever executed.

Thank you for your answer.

There is now a bit of progress. I rewrote the code with tasks_, I find it clearer, what do you think?

#include <Adafruit_NeoPixel.h>

#define LED_PIN12     12
#define LED_NUM       16
#define BOUTON_PIN  2 

int etatBouton; //variable qui enregistre l'état du bouton
int ancienEtatBouton = 0;
int etatStrip = 0;
int ancienEtatStrip = 0;
int lastDebounceTime = 0; 
int debutPauseIntervalle = 0 ;
int reading =0;

unsigned long currentMillis;
int pauseIntervalle = 10000L;


Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_NUM, LED_PIN12, NEO_GRB + NEO_KHZ800);


void setup() {
  Serial.begin(9600);
  
  pinMode(BOUTON_PIN, INPUT_PULLUP);
  etatBouton = HIGH;
  strip.begin();
  strip.setBrightness(100);
  strip.show(); // Initialize all pixels to 'off'
  currentMillis = 0;
}


void loop(){

  etatBouton = digitalRead(2);
  Serial.println(millis());
  Serial.println(millis()-debutPauseIntervalle);
   
  task_alive();
  task_rest();
}

void task_alive(){
  
   if(etatBouton == LOW){ //Bouton Appuyé 
     
      ancienEtatBouton = etatBouton;
      currentMillis = millis();
      debutPauseIntervalle = millis();
      
      strip.fill(strip.Color(255,20,50),0,16);
      strip.show();
   }
   if((millis()-debutPauseIntervalle) <= pauseIntervalle){
      ancienEtatBouton = etatBouton;
      currentMillis = millis();
      
      strip.fill(strip.Color(255,20,50),0,16);
      strip.show();
     }
   }


void task_rest(){

   if(etatBouton == HIGH & (millis()-debutPauseIntervalle) >= pauseIntervalle){ //Bouton Appuyé 
     ancienEtatBouton = etatBouton;
     currentMillis = millis();

     strip.fill(strip.Color(0,255,0),0,16);
     strip.show();
   }
}

Although the program works at the start, a problem arises when without pressing the button the waiting interval is exceeded. Then the program no longer starts the countdown of the interval so the LED changes color but does not remain the desired time.

Hello,
Here is an upgrade of my program. it is now functional and corresponds to what I expected.

#include <Adafruit_NeoPixel.h>

#define LED_PIN12     12
#define LED_NUM       16
#define BOUTON_PIN     2 

boolean etatBouton;       //variable qui enregistre l'état du bouton

long debutPauseIntervalle;
long temps;
long tempsBreath;
const long pauseIntervalle = 10000;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_NUM, LED_PIN12, NEO_GRB + NEO_KHZ800);


void setup() {
 // Serial.begin(9600);
  
  pinMode(BOUTON_PIN, INPUT_PULLUP);
  etatBouton = HIGH;
  strip.begin();
  strip.setBrightness(100);
  strip.show();
  temps = millis();
}


void loop(){ 
  etatBouton = digitalRead(BOUTON_PIN);
  
 // Serial.println();
 // Serial.println();

  task_rest();
  task_alive();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void task_alive(){  
  if(etatBouton == LOW){     //Bouton Appuyé       
    strip.fill(strip.Color(255,0,0),0,16);
    strip.show();
    
    temps = millis();       // Enregistre le temps
    debutPauseIntervalle = temps;
   }    
  else{
  if(millis() - debutPauseIntervalle <= pauseIntervalle){
    strip.fill(strip.Color(255,0,0),0,16);
    strip.show();

    temps = millis();    
   } 
 } 
}

void task_rest(){
  if(millis() - debutPauseIntervalle > pauseIntervalle){
  if(etatBouton == HIGH){  //Bouton Relaché    
    strip.fill(strip.Color(0,255,0),0,16);
    
    temps = millis();
   }
 }
}

however, it works with setPixels. When I add an animation the program refuses to come out of the loop.

#include <Adafruit_NeoPixel.h>

#define LED_PIN12     12
#define LED_NUM       16
#define BOUTON_PIN     2 

boolean etatBouton;       //variable qui enregistre l'état du bouton

long debutPauseIntervalle;
long temps;
long tempsBreath;
const long pauseIntervalle = 10000;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_NUM, LED_PIN12, NEO_GRB + NEO_KHZ800);


void setup() {
 // Serial.begin(9600);
  
  pinMode(BOUTON_PIN, INPUT_PULLUP);
  etatBouton = HIGH;
  strip.begin();
  strip.setBrightness(100);
  strip.show();
  temps = millis();
}


void loop(){ 
  etatBouton = digitalRead(BOUTON_PIN);
  
 // Serial.println();
 // Serial.println();

  task_rest();
  task_alive();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void task_alive(){  
  if(etatBouton == LOW){     //Bouton Appuyé       
    strip.fill(strip.Color(255,0,0),0,16);
    strip.show();
    
    temps = millis();       // Enregistre le temps
    debutPauseIntervalle = temps;
   }    
  else{
  if(millis() - debutPauseIntervalle <= pauseIntervalle){
    strip.fill(strip.Color(255,0,0),0,16);
    strip.show();

    temps = millis();    
   } 
 } 
}

void task_rest(){
  if(millis() - debutPauseIntervalle > pauseIntervalle){
  if(etatBouton == HIGH){  //Bouton Relaché    
    Breath();
    
    temps = millis();
   }
 }
}
//////////////////////////////////////////////////////////////////////////////////////////////

void Breath(){

int TOTAL_LEDS = 16;
float MaximumBrightness = 100;
float SpeedFactor = 0.0015; // I don't actually know what would look good
float StepDelay = 5; // ms for a step delay on the lights

for (int i = 0; i < 65535; i++){
// Intensity will go from 10 - MaximumBrightness in a "breathing" manner
float intensity = MaximumBrightness /2.0 * (1.0 + sin(SpeedFactor * i));
strip.setBrightness(intensity);
// Now set every LED to that color
for (int ledNumber=0; ledNumber<TOTAL_LEDS; ledNumber++) {
strip.setPixelColor(ledNumber, 255, 0, 0);
}
strip.show();

}
}

I would like during task_rest () to perform a raibow animation and a Breathe our Fade. HELP ?

  for (int i = 0; i < 65535; i++)

What is the maximum value that a signed int can hold ?

actually it can only contain 2 bytes.

this is part of a predefined animation that I reuse. But whatever I do doesn't change anything whether I lower its value or change it to "int long"

My code has progressed well. it is now functional with the animations I wanted.

there is still a small problem. During the "task_rest" which manages my two animation, I note that the rainbow effect makes a small pause when the fade effect is executed.

Can someone tell me more?

#include <Adafruit_NeoPixel.h>

#define LED1_PIN11    11
#define LED1_NUM      64
#define LED2_PIN12    12
#define LED2_NUM      16

#define BOUTON_PIN     2
boolean etatBouton; 

const long pauseIntervalle = 10000;      
int rainbowCyclesInterval = 12; 
int long fadeIntervalle = 3200;

int rainbowCycles = 0;
int rainbowCycleCycles = 0;
int fade = 0;
int fadeFade = 0;

unsigned long rainbowCyclesPreviousMillis=0;
unsigned long fadePreviousMillis = 0;

long debutPauseIntervalle;
long temps;
long tempsBreath;
long tempsRainbow;


Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(LED1_NUM, LED1_PIN11, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(LED2_NUM, LED2_PIN12, NEO_GRB + NEO_KHZ800);

////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
 // Serial.begin(9600);
  
  pinMode(BOUTON_PIN, INPUT_PULLUP);
  etatBouton = HIGH;
  strip1.begin();
  strip2.begin();
  strip1.setBrightness(70);
  strip2.setBrightness(70);
  strip1.show();
  strip2.show();
  temps = millis();
}


void loop(){ 
  etatBouton = digitalRead(BOUTON_PIN);
  
 // Serial.println();
 // Serial.println();

  task_alive();
  task_rest();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void task_alive(){  
  if((etatBouton == LOW)){     //Bouton Appuyé       
    strip1.fill(strip1.Color(255,0,0),0,LED1_NUM);
    strip2.fill(strip2.Color(255,255,255),0,LED2_NUM);
    strip1.show();
    strip2.show();
    
    temps = millis();       // Enregistre le temps
    debutPauseIntervalle = temps;
   }    
  
  if((etatBouton == HIGH)  && (millis() - debutPauseIntervalle <= pauseIntervalle)){
    strip1.fill(strip1.Color(255,0,0),0,LED1_NUM);
    strip2.fill(strip2.Color(255,255,255),0,LED2_NUM);
    strip1.show();
    strip2.show();
    temps = millis();    
   } 
 } 

void task_rest(){
   if((etatBouton == HIGH) && (millis() - debutPauseIntervalle > pauseIntervalle)){  //Bouton Relaché   
    
   if((unsigned long)(millis() - rainbowCyclesPreviousMillis) >= rainbowCyclesInterval) {
      rainbowCyclesPreviousMillis = millis();
      rainbowCycle();
   }  
   if((unsigned long)(millis() - fadePreviousMillis) >= fadeIntervalle){
      fadePreviousMillis = millis();
      Fade(255, 0, 0, fadeIntervalle);
   }    
     temps = millis();
   }
}

//////////////////////////////////////////////////////////////////////////////////////////////
void rainbowCycle() {
  uint16_t i;

  //for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip2.numPixels(); i++) {
      strip2.setPixelColor(i, Wheel(((i * 256 / strip2.numPixels()) + rainbowCycleCycles) & 255));
    }
    strip2.show();

  rainbowCycleCycles++;
  if(rainbowCycleCycles >= 256*5) rainbowCycleCycles = 0;
}

uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip2.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip2.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip2.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}



void Fade(uint8_t red, uint8_t green, uint8_t blue, uint8_t wait){
  for(uint8_t b = 0; b <255; b++) {
  for(uint8_t i=0; i < strip1.numPixels(); i++) {
  strip1.setPixelColor(i, red * b/255, green * b/255, blue * b/255);
  }
  strip1.show();
  wait = fadeIntervalle;
    
  fadeFade++;
  if(fadeFade >= 256*5) fadeFade = 0;
}
  for(uint8_t b=255; b > 0; b--) {
     for(uint8_t i = 0; i < strip1.numPixels(); i++) {
        strip1.setPixelColor(i, red * b/255, green * b/255, blue * b/255);
     }
     strip1.show();
     wait = fadeIntervalle;
     
     fadeFade++;
  if(fadeFade >= 256*5) fadeFade = 0;
  }
}