2

I have tried several different methods found around the internet, however none of them seem to work. This code works for cases 0-2 but when it goes into case 3 which is the rainbow chase loop, the pressing of the button does not interrupt the loop and move the counter forward. I'm assuming, as usual, I'm missing something stupid so many thanks in advance.

#define FASTLED_ALLOW_INTERRUPTS 1
#define FASTLED_INTERRUPT_RETRY_COUNT 1
#include <FastLED.h>

#define AnalogIn A0
#define SwIn 2
#define LED_Out 12
#define NUM_LEDS 5
int pushCounterz = 0;
volatile int buttonState;  // Set volatile for interrupt DO NOT SHAKE!
int lastButtonState;

CRGB leds[NUM_LEDS];

void setup() {
  // put your setup code here, to run once:
  FastLED.setMaxRefreshRate(250);
  FastLED.addLeds<WS2812, LED_Out, GRB>(leds, NUM_LEDS);
  pinMode(SwIn, INPUT);
  pinMode(LED_Out, OUTPUT);

  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB ( 255, 0, 255 );  
  }
  FastLED.show();
  delay(120);
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB ( 0, 0, 0 );  
  }

  FastLED.show();
  Serial.begin(115200);
  Serial.println(pushCounterz);
  lastButtonState = digitalRead(SwIn); // Set the button state to the startup state 

  attachInterrupt((SwIn-2), button_ISR, CHANGE);  // Set SwIn button as an interrupt pin  // Change to Low???

}

void loop() {
  if (pushCounterz != 3) {
    FastLED.show();
  }
Serial.println(pushCounterz);
delay(120);
}

void button_ISR () {
    buttonState = digitalRead(SwIn);
    digitalWrite(13, buttonState);
  if (buttonState == LOW && buttonState != lastButtonState) {
    if (pushCounterz > 3) {
      //Serial.println("Reset to 0: ");
      pushCounterz = 0;
    } else {
      pushCounterz = pushCounterz + 1;
      //Serial.println("Incerment");
    }
    //Serial.println(pushCounterz);

      switch (pushCounterz) {
      case 0:
        for (int i = 0; i < NUM_LEDS; i++) {
          leds[i] = CRGB (255, 0, 0);
        }
        break;
      case 1:
        for (int i = 0; i < NUM_LEDS; i++) {
          leds[i] = CRGB ( 0, 255, 0);
         }
         break;
      case 2:
        for (int i = 0; i < NUM_LEDS; i++) {
           leds[i] = CRGB ( 0, 0, 255);
         }
        break;
      case 3:
        theaterChaseRainbow(1,50);
        break;
      default:
       for (int i = 0; i < NUM_LEDS; i++) {
         leds[i] = CRGB ( 0, 0, 0);
       }
       break;
      }
  }

lastButtonState = buttonState;
}

// Theater-style crawling lights with rainbow effect
void theaterChaseRainbow(int cycles, int speed) { // TODO direction, duration
  for (int j = 0; j < 256 * cycles; j++) {   // cycle all 256 colors in the wheel
    for (int q = 0; q < 3; q++) {
      for (int i = 0; i < NUM_LEDS; i = i + 3) {
        int pos = i + q;
        leds[pos] = Wheel( (i + j) % 255);  //turn every third pixel on
      }
      FastLED.show();

      delay(speed);

      for (int i = 0; i < NUM_LEDS; i = i + 3) {
        leds[i + q] = CRGB::Black; //turn every third pixel off
      }
    }
  }
}

CRGB Wheel(byte WheelPos) {
  if (WheelPos < 85) {
    return CRGB(WheelPos * 3, 255 - WheelPos * 3, 0);
  }
  else if (WheelPos < 170) {
    WheelPos -= 85;
    return CRGB(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  else {
    WheelPos -= 170;
    return CRGB(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}
Joel Spolsky
  • 33,372
  • 17
  • 89
  • 105
Beeker
  • 383
  • 4
  • 20

3 Answers3

2

Your issue is not that the button is not changing the value, but rather your code has no exit point if it does; the button will change the value, but nothing in theaterChaseRainbow tells it to stop.

Simply add a check in the method to return out if the button state changes:

// Theater-style crawling lights with rainbow effect
void theaterChaseRainbow(int cycles, int speed) { // TODO direction, duration
  for (int j = 0; j < 256 * cycles; j++) {   // cycle all 256 colors in the wheel
    for (int q = 0; q < 3; q++) {
      for (int i = 0; i < NUM_LEDS; i = i + 3) {
        int pos = i + q;
        leds[pos] = Wheel( (i + j) % 255);  //turn every third pixel on
      }
      FastLED.show();
      if (pushCounterz != 3) return; //ADDED THIS HERE*****    

      delay(speed);

      for (int i = 0; i < NUM_LEDS; i = i + 3) {
        leds[i + q] = CRGB::Black; //turn every third pixel off
      }
    }
  }
}

In addition, I would suggest simplifying your ISR to just increment the button and not have it handle the logic of the program as well. That should either be contained in the loop method or called from the loop method. This should make for some cleaner and less confusing code, as the ISR's job is simply to adjust the value of the button counter, and the loops job is to deal with the state that the program is currently in.

Danny_ds
  • 11,201
  • 1
  • 24
  • 46
Easton Bornemeier
  • 1,918
  • 8
  • 22
  • 2
    You are a gentleman and a scholar. Now I have to go back and fix my debounce (or find my blasted debounce switches). – Beeker Jul 10 '18 at 16:25
  • 2
    Indeed, minimizing the ISR is the most important part here. If I remember correctly, while inside a ISR no other ISR can be called, which could cause trouble for serial, delay, timers, etc. – Danny_ds Jul 10 '18 at 16:37
  • Excellent suggestion. It's currently misbehaving (somehow crashing on increment) so I'll attempt that. I also have what I would call an overrun, not sure of the correct term as I'm rather new to this, somewhere that's jacking up my counter making it not be 0-4. Used to intelligent memory management or explicit (I start programming micro controllers with machine code int, jmp, mov etc) – Beeker Jul 10 '18 at 17:06
1

Also - you can't allow interrupts on AVR, or rather I should say it does nothing. I should put in a warning message when that's happening - AVR/arduino's ISR handling is so slow that even the clock tick ISR would be enough to disrupt writing out WS2812 data (resulting in FastLED cutting the frame off) so I yanked that code out of the avr WS2812 asm implementation. Most arm and esp platforms that FastLED supports do allow for interrupt handling to occur during in the small window between writing out each led's data - courtesy of their higher clock speeds.

If you're using an ARM or ESP based platform, then feel free to ignore this comment (mostly putting it here so folks who stumble on this question in a good search know what's up).

rDg
  • 56
  • 4
  • So the allow interrupts i have defined does noting on the Arduino duo? – Beeker Jul 10 '18 at 20:04
  • 1
    I have a fork of FastLED which allows interrupts on AVR without messing up the pixel data. The trade off is that it takes an extra 48 clocks per pixel - but that's only 3 microseconds at 16MHz (so, it takes 10% longer to push the pixels out - 3.3ms for 100 pixels instead of 3ms). Most people don't need interrupts that can respond that fast - but I did. https://github.com/ben-xo/FastLED – Ben XO Jul 19 '19 at 07:31
  • This fork solved my problems, very much appreciated for your sharing of it. – Systembolaget Jun 05 '21 at 20:17
0

As a reference, the working code with the ISR cleanup. (mind you there is still some serial debugging code in there as I have more work to do with brightness etc)

#define FASTLED_ALLOW_INTERRUPTS 1
#define FASTLED_INTERRUPT_RETRY_COUNT 1
#include <FastLED.h>

#define AnalogIn A0
#define SwIn 2
#define LED_Out 12
#define NUM_LEDS 5
int pushCounterz = 4; // 4 = off
volatile int buttonState;  // Set volatile for interrupt DO NOT SHAKE!
int lastButtonState;

CRGB leds[NUM_LEDS];

void setup() {
  // put your setup code here, to run once:
  FastLED.setMaxRefreshRate(250);
  FastLED.addLeds<WS2812, LED_Out, GRB>(leds, NUM_LEDS);
  pinMode(SwIn, INPUT);
  pinMode(LED_Out, OUTPUT);

  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB ( 255, 0, 255 );  
  }
  FastLED.show();
  delay(120);
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB ( 0, 0, 0 );  
  }

  FastLED.show();
  Serial.begin(19200);
  Serial.println(pushCounterz);
  lastButtonState = digitalRead(SwIn); // Set the button state to the startup state 

  attachInterrupt((SwIn-2), button_ISR, LOW);  // Set SwIn button as an interrupt pin  // Change to Low???

}

void loop() {
//  if (pushCounterz != 3) {
    //FastLED.show();
    //Serial.println(pushCounterz);
//  }

//delay(20);
switch (pushCounterz) {
  case 0:
    for (int i = 0; i < NUM_LEDS; i++) {
      leds[i] = CRGB (255, 0, 0);
    }
    FastLED.show();
    break;
  case 1:
    for (int i = 0; i < NUM_LEDS; i++) {
      leds[i] = CRGB ( 0, 255, 0);
    }
    FastLED.show();
    break;
  case 2:
    for (int i = 0; i < NUM_LEDS; i++) {
      leds[i] = CRGB ( 0, 0, 255);
    }
    FastLED.show();
    break;
  case 3:
    theaterChaseRainbow(1,50);
    break;
  default:
    for (int i = 0; i < NUM_LEDS; i++) {
      leds[i] = CRGB ( 0, 0, 0);
    }
    FastLED.show();
    break;
    }
}

void button_ISR () {
    buttonState = digitalRead(SwIn);
    //digitalWrite(13, buttonState);
  if (buttonState == LOW && buttonState != lastButtonState) {
    if (pushCounterz > 3 || pushCounterz < 0) {
      Serial.println("Reset to 0: ");
      pushCounterz = 0;
    } else {
      pushCounterz = pushCounterz + 1;
      Serial.println("Incerment");
    }
    Serial.println(pushCounterz);
  }
lastButtonState = buttonState;
}

// Theater-style crawling lights with rainbow effect
void theaterChaseRainbow(int cycles, int speed) { // TODO direction, duration
  for (int j = 0; j < 256 * cycles; j++) {   // cycle all 256 colors in the wheel
    for (int q = 0; q < 3; q++) {
      for (int i = 0; i < NUM_LEDS; i = i + 3) {
        int pos = i + q;
        leds[pos] = Wheel( (i + j) % 255);  //turn every third pixel on
      }
      FastLED.show();
      if (pushCounterz != 3) return;

      delay(speed);

      for (int i = 0; i < NUM_LEDS; i = i + 3) {
        leds[i + q] = CRGB::Black; //turn every third pixel off
      }
    }
  }
}

CRGB Wheel(byte WheelPos) {
  if (WheelPos < 85) {
    return CRGB(WheelPos * 3, 255 - WheelPos * 3, 0);
  }
  else if (WheelPos < 170) {
    WheelPos -= 85;
    return CRGB(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  else {
    WheelPos -= 170;
    return CRGB(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}
Danny_ds
  • 11,201
  • 1
  • 24
  • 46
Beeker
  • 383
  • 4
  • 20
  • So, is everything working now, or are there still problems with interrupts? (see rDg's answer). – Danny_ds Jul 11 '18 at 14:03
  • This code works fine. I've since added 2 analogs for (now fade) speed and brightness, tweaked a few other things and am going to start building my laser crystal display shelf that its for. I queried him above to see if those constants i defined even do anything for my uno – Beeker Jul 11 '18 at 14:09