Go Down

Topic: Interrupting the NeoPixels [Help needed]  (Read 269 times) previous topic - next topic

avik111

Hello, I wonder how can I make couple of patterns with NeoPixel library that can be selected with rotary encoder, and than after pressing the button displayed? Unfortunately attachInterrupt doesn't work always in my case (don't know why).

I only need help with interrupting the loop whenever I rotate the encoder to chose pattern (eg. number 2), and I don't want to cycle through pattern 0, 1, 2. The key is to select number, press the button, and watch... Is there anybody who can help novice?

I have searched arduino.cc for references to understand interrupt, also internet for sample code, but none of them doesn't work...

Regards, Lucas

Sketch is in attachments. It's too long to post it here.

BulldogLowell

I have searched arduino.cc for references to understand interrupt, also internet for sample code, but none of them doesn't work...
One of the problems in that the NeoPixel library temporarily disables all interrupts when issuing data, then re-enables them when done.  Since a lot of those functions are essentially always updating the LEDs, well then you don't have room much for anything using interrupts.

There are examples out there that use polling instead of interrupts to determine rotation of the encoder, you may want to look for those.  Depending on your Arduino, you may have access to some other timers that can accomplish what you want to do as well.

Finally, refactoring some of those pixel animations to use non-blocking functions may help as well.  I'd put some effort at that and see what can be accomplished there it will be particularly useful if you wat to try polling to manage the encoder.

I suggest trying Google...

larryd

I've done this with a push button, but you need to hold the button until the current animation is over.

The LED libraries are blocking so instant results is problematic.




No technical PMs.
The last thing you did is where you should start looking.

avik111

#3
Dec 08, 2017, 07:28 am Last Edit: Dec 08, 2017, 08:14 am by avik111
@BulldogLowell thank you very much, but as I can see I'm not be able to write sketch, because it's too complicated for me, maybe there is library providing polling, which will be easy to understand and user-friendly?
Also I'll be gratefull for sample code for rotary encoder (or 2 buttons - one for counting and another one for select). Currently I'm prototyping on Arduino UNO.
Please, I really need some sort of help making this work :)

@larryd, the button is an option, but it's necessary for project to instatly change pattern.
I wonder what about FastLed library? Author mentioned that it's non-blocking interrupt operations...

Leds will be under motorcycle (trike), and control panel has to be fast responding.  

Regards

BulldogLowell

...but as I can see I'm not be able to write sketch, because it's too complicated for me, maybe there is library providing polling
It is a shame that the Adafruit team presents blocking code as examples for their (much loved) LED animations.

It would be nice if someone spent some time on getting some of those into non-blocking versions...

Regarding your issue, there may be a way for you to manipulate the timers to get what you want; you may want to ask for help on the Adafruit forum

gfvalvo

It is a shame that the Adafruit team presents blocking code as examples for their (much loved) LED animations.

It would be nice if someone spent some time on getting some of those into non-blocking versions...
Have a look here: https://learn.adafruit.com/multi-tasking-the-arduino-part-3?view=all

avik111

I somehow managed to make this works... Maybe it's not the prettiest and super optimised sketch, but:

Code: [Select]
#include "FastLED.h"
#define NUM_LEDS 300
CRGB leds[NUM_LEDS];
#define PIN 8

#define  Select 2
int buttonPushCounter = -1;
bool buttonState = 0;
bool lastButtonState = 0;

#define  Choose 3
bool buttonChooseState = 0;
bool lastButtonChoose = 0;
bool choose = 0;



void setup()
{
  Serial.begin(9600);
   
  pinMode(Select, INPUT_PULLUP);
  pinMode(Choose, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(Select), counter, CHANGE);
  attachInterrupt(digitalPinToInterrupt(Choose), selector, CHANGE);
 
  FastLED.addLeds<WS2812B, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
}


void counter()
{
  buttonState = digitalRead(Select);

  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      buttonPushCounter++;
      choose = 0;
      if (buttonPushCounter > 10)
      {
        buttonPushCounter = 0;
      }   
      Serial.println(buttonPushCounter);
    }
    lastButtonState = buttonState;
  }
  choose = 0;
 
}
void selector()
{
  buttonChooseState = digitalRead(Choose);
  if (buttonChooseState != lastButtonChoose) {
    if (buttonChooseState == HIGH) {
      choose = 1;   
      Serial.println("Choose pressed!");
    }
    lastButtonChoose = buttonChooseState;
  }
}

void loop()
{

  if (buttonPushCounter == 0 && (choose == 0 || choose == 1))
  {
    setAllOff();
  }

  if (buttonPushCounter == 1 && choose == 1)
  {
    Strobe(0xff, 0, 0, 3, 10, 100);
    Strobe(0, 0, 0xff, 3, 10, 1000);
  }

  if (buttonPushCounter == 2 && choose == 1)
  {
    CylonBounce(0, 0xff, 0xff, 3);
  }

  if (buttonPushCounter == 3 && choose == 1)
  {
    RGBLoop();
  }

}

void CylonBounce(byte red, byte green, byte blue, int EyeSize) {

  for (int i = 0; i < NUM_LEDS - EyeSize - 2; i++) {
    setAll(0, 0, 0);
    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
    showStrip();
  }


  for (int i = NUM_LEDS - EyeSize - 2; i > 0; i--) {
    setAll(0, 0, 0);
    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
    showStrip();
  }

}

void RGBLoop() {
  for (int j = 0; j < 3; j++ ) {
    // Fade IN
    for (int k = 0; k < 256; k++) {
      switch (j) {
        case 0: setAll(k, 0, 0); break;
        case 1: setAll(0, k, 0); break;
        case 2: setAll(0, 0, k); break;
      }
      showStrip();
    }
    // Fade OUT
    for (int k = 255; k >= 0; k--) {
      switch (j) {
        case 0: setAll(k, 0, 0); break;
        case 1: setAll(0, k, 0); break;
        case 2: setAll(0, 0, k); break;
      }
      showStrip();
    }
  }
}

void Strobe(byte red, byte green, byte blue, int StrobeCount, int FlashDelay, int EndPause) {
  for (int j = 0; j < StrobeCount; j++) {
    setAll(red, green, blue);
    showStrip();
    delay(FlashDelay);
    setAll(0, 0, 0);
    showStrip();
    delay(FlashDelay);
  }

  delay(EndPause);
}




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

void setPixel(int Pixel, byte red, byte green, byte blue) {
#ifdef ADAFRUIT_NEOPIXEL_H
  // NeoPixel
  strip.setPixelColor(Pixel, strip.Color(red, green, blue));
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
  // FastLED
  leds[Pixel].r = red;
  leds[Pixel].g = green;
  leds[Pixel].b = 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 setAllOff() {

  for (int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, 0, 0, 0);
  }
  showStrip();
}


gfvalvo

Minor Points:
You've defined variables like 'buttonState' as type 'bool' which has values of 'true' and 'false'. Your code is comparing them to values '1' and '0' which is minorly incorrect. It would be better to define them as 'byte' and compare to values 'HIGH' and 'LOW' or '1' and '0'.

The part of this 'if' statement involving the variable 'choose' (after the '&&') will always be true. You can remove it:
Code: [Select]
if (buttonPushCounter == 0 && (choose == 0 || choose == 1))

Major Problems:
Don't do 'Serial.print()' inside an ISR, it's bad juju.

Variable 'buttonPushCounter' needs to be declared 'volatile' since it's accessed in both ISR and non-ISR code.

Since variable 'buttonPushCounter' is type 'int' (two bytes), accesses to it will not be atomic on an 8-bit processor. So, in non-ISR code you need to protect those accesses with 'noInterrupt() / interrupt()' pairs.

avik111

Thank you for respond, I'll do my best, also I know, that I'm not the programming master, even good beginner, but it's something :) I will be grateful if you can fix my code and comment what is wrong and why, as you mentioned with eg. volatile.

Regards

Go Up