EEPROM/Interrupt/Function Problems

Due to the 9000 char limit I've attached my .ino file to this post.

My current project is an led strip with three push buttons. One for changing the effect, color, and brightness. I'm using pin change interrupts to do this on pins 3,4,5.

Here is the first section of my code:

#include <EEPROM.h>
#include "FastLED.h"
#include "EnableInterrupt.h"

#define NUM_LEDS 20
#define PIN 7
#define EFFECT_BUTTON 3
#define COLOR_BUTTON 4
#define BRIGHTNESS_BUTTON 5

CRGB leds[NUM_LEDS];
CRGB row1[NUM_LEDS]; // array used by colorWheel function

// Color       (HEX Code)          Array Position
// gGreen       (0x00, 0xFF, 0x00)  0,1,2
// gGreenCyan   (0x00, OxFF, 0x7F)  3,4,5
// Cyan        (0x00, 0xFF, 0xFF)  6,7,8
// gBlueCyan    (0x00, 0x7F, 0xFF)  9,10,11
// gBlue        (0x00, 0x00, 0xFF)  12,13,14
// gBlueMagenta (0x7F, 0x00, 0xFF)  15,16,17
// Magenta     (0xFF, 0x00, 0xFF)  18,19,20
// gRedMagenta  (0xFF, 0x00, 0x7F)  21,22,23
// gRed         (0xFF, 0x00, 0x00)  24,25,26
// Orange      (0xFF, 0x7F, 0x00)  27,28,29
// Yellow      (0xFF, 0xFF, 0x00)  30,31,32
// gGreenYellow (0x7F, 0xFF, 0x00)  33,34,35
// White       (0xFF, 0xFF, 0xFF)  36,37,38
byte ColorArray[] = {0x00, 0xFF, 0x00, 0x00, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0xFF, 0x7F, 0x00, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF};
byte gRed = ColorArray[0];
byte gGreen = ColorArray[1];
byte gBlue = ColorArray[2];

volatile byte selectedEffect = 0;
volatile byte selectedColor = 0;
volatile byte selectedBrightness = 0;

void setup() {
  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  pinMode (EFFECT_BUTTON, INPUT_PULLUP);  // internal pull-up resistor
  pinMode (COLOR_BUTTON, INPUT_PULLUP);
  pinMode (BRIGHTNESS_BUTTON, INPUT_PULLUP);
  enableInterrupt(EFFECT_BUTTON, changeEffect, CHANGE); // EFFECT_BUTTON pressed
  enableInterrupt(COLOR_BUTTON, changeColor, CHANGE); // COLOR_BUTTON pressed
  enableInterrupt(BRIGHTNESS_BUTTON, changeBrightness, CHANGE); // BRIGHTNESS_BUTTON pressed
  EEPROM.get(0, selectedEffect);
  EEPROM.get(1, selectedColor);
  EEPROM.get(2, selectedBrightness);

  Serial.begin(9600);

  if (selectedEffect > 14) {
    selectedEffect = 0;
    EEPROM.put(0, 0);
  }
  if (selectedColor > 12) {
    selectedColor = 0;
    EEPROM.put(1, 0);
  }
  if (selectedBrightness > 4) {
    selectedBrightness = 0;
    EEPROM.put(2, 0);
  }
}

I'm utilizing EEPROM.put and EEPROM.get to keep track of button presses, in conjunction with three different switches in the main loop to determine the action and then calling a particular function, or passing color variables from the predefined array, or setting brightness. "asm volatile (" jmp 0");" forces the board to reset once the value is stored in EEPROM.

void changeEffect() {
  if (digitalRead (EFFECT_BUTTON) == HIGH) {
    selectedEffect++;
    EEPROM.put(0, selectedEffect);
    asm volatile ("  jmp 0");
  }
}

void changeColor() {
  if (digitalRead (COLOR_BUTTON) == HIGH) {
    selectedColor++;
    EEPROM.put(1, selectedColor);
    asm volatile ("  jmp 0");
  }
}

void changeBrightness() {
  if (digitalRead (BRIGHTNESS_BUTTON) == HIGH) {
    selectedBrightness++;
    EEPROM.put(2, selectedBrightness);
    asm volatile ("  jmp 0");
  }
}

I've ran into a number of rather strange issues and I was hoping someone in the forums could help explain. The first issue is that this code only seems to work when only one interrupt is enabled. For example, the functions in the code (please see attached .ino file) work perfectly fine when the enableInterrupt function is enabled for the EFFECT_BUTTON, but as soon as I add the second or third line of code several of the effect functions either only run once, or freeze the entire program to where button presses have no effect. Even the serial port freezes. If I remove the offending line of code, it works as before, switching between functions seamlessly. Even if I have only two buttons set up for external interrupts on pins 2 and 3, and call them using attachInterrupt, it doesn't appear to matter, the effect is the same. I'm at a total loss as to how/why this is happening. Below is the code i'm referring to

enableInterrupt(EFFECT_BUTTON, changeEffect, CHANGE); // EFFECT_BUTTON pressed
  enableInterrupt(COLOR_BUTTON, changeColor, CHANGE); // COLOR_BUTTON pressed
  enableInterrupt(BRIGHTNESS_BUTTON, changeBrightness, CHANGE); // BRIGHTNESS_BUTTON

The second major issue I've ran into while attempt to debug is that EEPROM.put appears to be clearing all three values stored at addresses 0,1, and 2 (effect, color, and brightness variables)when the following code is ran

if (selectedEffect > 14) {
    selectedEffect = 0;
    EEPROM.put(0, 0);
  }

Watching the serial port shows that when selectedEffect reaches a value of 1, it sets selectedColor & selectedBrightness to 0. My understanding of the .put function is that it only replaces values at the 0 address if they are different, it shouldn't be affecting the 1 and 2 address values. Although this is rather new to me, I'm hoping someone can help me understand what exactly is going on so I can hopefully get the desired result.

The last issue has briefly been mentioned but I wanted to provide an example. When the colorWipe function is called from the selectedEffect switch it will only run once (its suppose to loop) if I have anything more than one interrupt enabled. Yet if I only have the interrupt for the effects button it loops perfectly fine....I don't understand why this would affect the function in this way, does anyone have any idea why this would happen?

void colorWipe(byte gRed, byte gGreen, byte gBlue, int SpeedDelay) {
  for (uint16_t i = 0; i < NUM_LEDS; i++) {
    setPixel(i, gRed, gGreen, gBlue);
    showStrip();
    delay(SpeedDelay);
  }
}

I'm hoping someone can help me debug/learn from this. Any help is greatly appreciated!

Goggle_Code_FinalVersion.ino (20.9 KB)

I forgot to mention that most of this code was taken from //Tweaking4All.com - Arduino – All LEDStrip effects in one (NeoPixel and FastLED)

Hans has been an extremely great help in trying to adapt this code to fit my project needs. I recommend checking out the site as there is tons of useful information there!

This is joke code right! Buttons with interrupts WTF, resetting the micro because of your idiotic mis-design and the insane way you use EEPROM.

Mark

This is my first project and I'm learning as I go. If you have something constructive to contribute I'm all ears. I'm not sure how else to have a responsive button when I have looping functions and want to be able to switch between effects without delay and multiple inputs. Any suggestions are welcome.

At the top of the 'project guidance' topic is pinned 'demonstration code for...'. Check that out. Also do a search for 'blink without delay'. These sketches and accompanying explanation show how to handle your three switches without interrupts and still keep the code doing its other work - aka non-blocking code. Interrupts are generally used to capture a short duration event and flag it for later processing elsewhere in the main loop. Also helpful too is to have a grasp of the concepts in the first five demos in IDE -> file/examples/digital.

This will mean some reworking but it'll be better in the end.

Kuusou:
I'm utilizing EEPROM.put and EEPROM.get to keep track of button presses, in conjunction with three different switches in the main loop to determine the action and then calling a particular function, or passing color variables from the predefined array, or setting brightness. "asm volatile (" jmp 0");" forces the board to reset once the value is stored in EEPROM.

I understand storing settings to EEPROM but what's with the reset?

dougp:
At the top of the 'project guidance' topic is pinned 'demonstration code for...'. Check that out. Also do a search for 'blink without delay'. These sketches and accompanying explanation show how to handle your three switches without interrupts and still keep the code doing its other work - aka non-blocking code. Interrupts are generally used to capture a short duration event and flag it for later processing elsewhere in the main loop. Also helpful too is to have a grasp of the concepts in the first five demos in IDE -> file/examples/digital.

This will mean some reworking but it'll be better in the end.

I understand storing settings to EEPROM but what's with the reset?

I've looked at the blink without delay example but I'm still at a loss for how to apply that to my case. I want to be able to change the effect OR the color OR the brightness at the push of a button regardless of where the effect function is in its loop. In my first try at this project I was able to get it working without interrupts or a reset, but I had to hold down the button to wait for the loop to finish before it would register the press. In this code there is a non EEPROM version which uses a volatile bool statement but i'm not able to get the functions in the code to work with an if statement, it becomes a nightmare for me to adapt the code. The only way i've found to do that is to use interrupts. The reset basically forces the board out of the loop and EEPROM saves the three values, for my purposes it appears this would work. If there is another way to do it then an example for my particular case would be very helpful, or maybe i'm just too dumb for this programming stuff :frowning:

I certainly appreciate you taking the time to share some knowledge with me, but at the moment i'm extremely curious to know why my code is behaving in the way it is, do you have any insight into that? I made this post to try and understand whats going on, I know there are probably better ways to do it, but i'm more of a hands on learner and at the moment this is the best I can do.

for example with the .ino file I posted. I can just remove the functions that don't seem to work with the code and then my project has the desired effect I was after EXCEPT for the EEPROM.put issue where its wiping the values at the first three addresses whenever it gets to selectedEffect case 1 or has a value of 1 in EEPROM, when it should only be changing address 0 once it's value gets above 14. Any idea on why that is?

Kuusou:
I've looked at the blink without delay example but I'm still at a loss for how to apply that to my case.

Here's an experiment. Load up 'blink without delay' (BWOD) and rename it as whatever you like. Right before the final closing brace '}' in loop() add code to make one switch increment an integer variable. Display the value of that variable on the serial monitor. Or, even simpler, just continually display the switch state. Observe that the built-in LED continues to blink, unaffected by what you're doing with the switch.

The idea is that the processor checks the switch and acts on input then checks the BWOD timer and a yes/no decision is made to act on that and so on. The processor is doing many small tasks very quickly so it looks to us puny humans like it's all happening simultaneously.

Kuusou:
at the moment i'm extremely curious to know why my code is behaving in the way it is, do you have any insight into that?

I do not. My aim was to clear up the switch handling first and work from there.

Kuusou:
maybe i'm just too dumb for this programming stuff

Nah. You just have to get to that point where it 'clicks', the "Aha!" moment. Then things will make a lot more sense. That's not to say it'll suddenly get easy.

I forgot to mention that most of this code was taken from //Tweaking4All.com - Arduino – All LEDStrip effects in one (NeoPixel and FastLED)

I understand what you have creatively done to allow for immediate input to blocking code.
With others, I think that reformulating the entire code to be non blocking so that button input can be recognized is the correct way to go.

However, if you choose to continue working with what you have and try to get it to work with multiple interrupts (external or pin change), the first thing I would do is change the reset proceedure to that recommended by the chip manufacturer.

Replace the asm volatile (" jmp 0"); reset with a watchdog timer reset.
https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_softreset.html

https://www.codeproject.com/Articles/1012319/Arduino-Software-Reset