Struggling to loop this code round 15 times

Hi there,

I have written some code to activate a neopixel when the tilt switch is activated. The light then goes through a sequence lasting about 12 seconds. The idea is that it goes through this sequence 15 times before switching off. If the tilt switch is turned while the light sequence is happening, it turns off after that loop (12 seconds). I want this to continue to happen while also looping 15 times if that makes sense. So any point through the 15 times, it can still stop if tilted back.

I know the code is probably scrappy and maybe better ways to do it but I really just need it to loop 15 times then switch off and wait for the tilt switch to be activated again. It seems simple but just isnt working for me. Thanks guys!

// Basic code NeoPixel
// wwww.arduinoplatform.com

#include <Adafruit_NeoPixel.h>
#define PIN 6
#define NUM_LEDS 7
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
int last = 0;
int off = 0;

void setup() {
  pinMode(7, INPUT_PULLUP);
  Serial.begin(9600);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

void loop() {

  if (digitalRead(7) == HIGH) {
    FadeInOut(255, 222, 40);
    //  FadeInOut(0x00, 0xff, 0x00);
  }
  else {
    ;
  }
}


void FadeInOut(byte red, byte green, byte blue) {
  float r, g, b;
    for (int k = 0; k < 256; k = k + 1) {
      r = (k / 256.0) * red;
      g = (k / 256.0) * green;
      b = (k / 256.0) * blue;
      setAll(r, g, b);
      delay(15);
      showStrip();
      check();
    }
    for (int i = 0; i < NUM_LEDS; i++) {
      strip.setPixelColor(i, strip.Color(200, 200, 50));
    }
    strip.show();
    delay(2000);
  
    check();
    for (int k = 255; k >= 0; k = k - 1) {
      r = (k / 256.0) * red;
      g = (k / 256.0) * green;
      b = (k / 256.0) * blue;
      setAll(r, g, b);
      delay(23);
      showStrip();
      check();
    }
}


void setPixel(int Pixel, byte red, byte green, byte blue) {
#ifdef ADAFRUIT_NEOPIXEL_H
  // NeoPixel
  strip.setPixelColor(Pixel, strip.Color(red, green, blue));
#endif
}

void setAll(byte red, byte green, byte blue) {
  for (int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue);
  }
  showStrip();
}

void showStrip() {
#ifdef ADAFRUIT_NEOPIXEL_H
  // NeoPixel
  strip.show();
#endif
}

void check() {
  if (digitalRead(7) == LOW) {
    //  loop();
  }
}

Where is your attempt?

Using a state machine would be well suited for this

I'm not sure I understand your requirements so let me restate them:

  • if the switch is tilted the 15x sequence starts
  • if the switch is tilted back the sequence is stopped
  • if the switch is not tilted back during the sequence then it stops after 15x and waits for the switch to be tilted back and then tilted again

I took it out as it wouldnt work. I was checking in loop() if the counter, which was getting added in the FadeInOut(), was hitting 15, but if digitalRead(7) was still HIGH it would just continue the sequence and reset the counter. I tried a previous code using a statemachine but just couldnt get it to work either.

Here is that attempt:

#include <Adafruit_NeoPixel.h> // Library
#define PIN        6 // On Trinket or Gemma, suggest changing this to 1
#define NUMPIXELS 7 // Popular NeoPixel ring size
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#define DELAYVAL 500 // Time (in milliseconds) to pause between pixels



int lightState = 0;
int tiltCount = 0;
int lastTilt = 0;
// Breathing in colours
int a = 0;
int b = 0;
int c = 0;
// Holding breath colours
int d = 150;
int e = 150;
int f = 50;
// Breathing out colours
int g = 255;
int h = 222;
int j = 90;

// Return timer
int t  = 0;

bool isRunning = false;
unsigned long lastChangeTime;

boolean oldState = HIGH;
int mode = 0;
int inhaleWait = 4000;
int holdTime = 2000;
int exhaleWait = 6000;
int seqTime = 180000;
unsigned long prevMillis=0;

enum myModes{INHALE, HOLD, EXHALE, RETURN, OFF} currentMode = INHALE;

void setup(){
  
  pixels.begin();
  pinMode(7, INPUT);
  Serial.begin(9600);
}

void loop() {
  pixels.clear();
  boolean tiltState = digitalRead(7);
  //Serial.print("Tilt state: ");
  Serial.println(currentMode);
  
  
  switch (currentMode){

   case INHALE:
    inhaleSeq(16);
     break;
     
  case HOLD:
    holdSeq(2000);
    break;
    
  case EXHALE:
    exhaleSeq(23, t);
    if (a == 0){
      currentMode = RETURN;
    }
    break;
    
  case RETURN:
    while (t < 15){
      currentMode = INHALE;
    }
    
    if (t >= 15){
      currentMode = OFF;
    }
  
    break;
    
  case OFF:
    // set counter to 0;
    t = 0;
    pixels.clear();
    pixels.show();
    if (digitalRead(7) == HIGH){
      currentMode = INHALE;
    } else if (digitalRead(7) == LOW) {
      currentMode = OFF;
    }
    break;
  }
  
  if( digitalRead(7) == LOW ){
    currentMode = OFF;
  }
}

void inhaleSeq(int wait){
  // per https://forum.arduino.cc/t/stopping-a-for-loop-part-way-through/967148
  static int b,c;
  static unsigned long last = 0;
  if(millis() - last >= wait){
    if(a < 0 or a > 255 ){ // initialize state vars
      a = 255;
      b = 222;
      c = 90;
    }
    for (int i = 0; i < NUMPIXELS; i++){
      pixels.setPixelColor(i,pixels.Color(a, b, c));
    }
    pixels.show();
    a++; b++ ; c++; // update internal and external state vars
  }
  if (a == 255){
    currentMode = HOLD;
  }
}

void holdSeq(int wait){
  static unsigned long last = 0;
  a = 150; b = 150; c = 50;
  if (millis() - last >= wait){
    for (int i = 0; i < NUMPIXELS; i++){
        pixels.setPixelColor(i,pixels.Color(a, b, c));
    }
    pixels.show();
  }
  currentMode = EXHALE;
}

void exhaleSeq(int wait, int t){
  static int b,c;
  static unsigned long last = 0;
  a = 255; b = 222; c = 90;
  if(millis() - last >= wait){
    if(a < 0 or a > 255 ){ // initialize state vars
      a = 255;
      b = 222;
      c = 90;
    }
    for (int i = 0; i < NUMPIXELS; i++){
      pixels.setPixelColor(i,pixels.Color(a, b, c));
    }
    pixels.show();
    a--; b--; c--; // update internal and external state vars
  }
  t++;
}

// void report(void){
//   const unsigned long interval = 200; // ms
//   static unsigned long last = -interval;
//   unsigned long now = millis();
//   if(now - last >= interval){
//   last += interval;
//   Serial.print(now);
//   Serial.print("ms, state = ");
//   Serial.print(currentMode);
//   Serial.print(" a = ");
//   Serial.print(a);
//   //...
//   Serial.println();
//   }
// }

Yeah that is correct, I worded it really badly but you got it on the nose. The turning off if tilted back works, but not the counter. Need to almost tell it to read LOW if the counter is hit.

one of your challenge is that the FadeInOut() function does not check the tilt and won't be interrupted if the status changes

you need to rewrite the animation so that a step is taken when necessary (using millis() to decide when it's time — For extra information and examples look at Using millis() for timing. A beginners guide and Several things at the same time) and use a small state machine to decide what to do depending on the state of the tilt sensor and if you have run the animation 15 times or not

I understand that using millis() and a state machine would be the best way to do it, but I have spent so long trying to get this code working that I just want the simple way. Surely as I have it working how I like, it is quite simple to just add a counter that goes to 15 then stops and switches the LED off. I am running out of time to start looking at these things I tried already and couldn't get to work. How can I make the FadeInOut() function check tilt and be interrupted without using a state machine?

I appreciate your answer and I understand that is the best way but its just not something I have time to look at, at this stage.

Thanks!

If what you have meets your needs then indeed no need to go further

solution? really?

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