Delay within an extra void not working?

so im very new to all this, well sorta i played around with it all a few years ago but im only just getting back into it recently, anyway...

im trying to use a toggle switch (not momentary) to change a mode via an interupt, i know i could use an interupt and a momentary switch, but i have hardware limitations that mean i need to use the current switch, anyway, what i wanna do is when the toggle switch is engaged i want it to advance a mode once every second, then when the switch is released for it to hold the mode its set into, the eeprom isbecause i want it to hold the mode when powered off, i know its not quite implenmented correctly atm.... the problem im having is in the change mode void, the delay doesn't seem to work, the reason i have a seperate void is because i want the delay (which i realise is currently set to 5 sec) and i understand they dont work in interupts? im totally lost!!!

#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>

#define NUM_LEDS 11
#define PIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);

byte selectedEffect=0;
int var1 = 0;

void setup()
{
Serial.begin(9600);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
pinMode(2,INPUT_PULLUP); // internal pull-up resistor
attachInterrupt (digitalPinToInterrupt (2), changeEffect, CHANGE); // pressed
}
void loop()
{
Serial.print("main");
Serial.println(selectedEffect);
delay(500);
}
void changeEffect() {
if(digitalRead (2) == LOW)
{
Changemode();
}
}
void Changemode()
{
while(digitalRead (2) == LOW)
{
selectedEffect++;
delay(5000);
Serial.print("button");
Serial.println(selectedEffect);
EEPROM.put(0, selectedEffect);

}
}

The easier you make it to read and copy your code the more likely it is that you will get help

Please follow the advice given in the link below when posting code , use code tags and post the code here

If you get errors when compiling please copy them from the IDE using the "Copy error messages" button and paste the clipboard here in code tags

It is a beginners mistake to think that an interrupt can change the flow of a program, it can not. It is in effect a hardware triggered call to a function, which then returns to where the program was before the triggering. So all it can do is to change values in a variable.

In order for a push button switch to change the effects showing you have to have a pattern number that directs the code to the right place, but it will only do this when a pattern has finished if you use the delay function in your pattern code. In the mean time if the pattern sequence is long a user will have no idea that the button has been recognised.

Hint:- littering 5 second delays about in your code is not the brightest idea you will have today.

Hello @haroll

The things you are calling 'voids' are called 'functions'. The word immediately preceding the function name specifies what kind of variable the function returns, with the word 'void' meaning the function does not return any variable.

Some tutorials to help you:

https://www.cplusplus.com/

if you just want to do something with some delay every second, consider. of course you can call more than one function in loop()


const byte ledPin = 13;
const byte butPin = A1;

void
setup (void)
{
    pinMode (ledPin, OUTPUT);
    pinMode (butPin, INPUT_PULLUP);
}

void
doSomething (void)
{
    digitalWrite (ledPin, ! digitalRead (ledPin));
    delay (1000);
}

void
loop (void)
{
    // toggle led every second while but switch is LOW
    if (LOW == digitalRead (butPin))  {
        doSomething ();
    }
}

Hi,

first of all you should get rid of your delays, look up the Arduino example "blink without delay" and get used to that concept. If you want to controll LED strips , you have to use it all the time anyway.

After that you should take a look at the reference page of attacheInterrupt, it has a Rising and Falling option (also a High or Low option, but you really don't want to write your EEPROM continiously).

And last but not least, don't use delays in interrupts. Calling functions out of interrupts should be avoided as well.

No I can't see that as a problem, I do it about 50% of the time I use an interrupt. As long as you realise what you are doing you will be fine.

The reason is that erasing an EEPROM can only be done a specific number of times before it wares out. The expected life is about 1 million times, which might sound a lot but can be reached in a few seconds with the "wrong sort" of code.

You should post code by using code-tags
There is an automatic function for doing this in the Arduino-IDE
just three steps

  1. press Ctrl-T for autoformatting your code
  2. do a rightclick with the mouse and choose "copy for forum"
  3. paste clipboard into write-window of a posting

Neopixel sounds like LEDs shall blink or change color and you want to change the mode what blinkpattern should be shown.

It is always a good idea to give an overview about your project.
Having the overview wil create better and more ideas how to solve your specific problem.

best regards Stefan

trying a 5 minute google-search is ALWAYS worth it

very simple pattern do do

as first keyword write "arduino" add some more keywords to what you are searching for in your case this is
"button" "mode" "change"

So your google search looks like this

arduino button change mode

the results look promising
best regards Stefan

ok wow!! so much info!!

firstly thanks UKheliBob. i knew something wasnt right but wasn't sure how to fix it!

ok, whys that?? my eventual plan is for that delay to be 1 second or maybe half, when the switch is activated i want it to go to the next mode every 1 second, but im guessing your saying delays are bad in general?

awesome!! i knew ii was missing something there but wasn't sure what, cause searching void on the arduino page wasn't super helpful!

yeah im aware of the eeprom issue, im not programming on the final chip atm, just an old one i got day one years ago when i first started playing with this stuff... my plan is to get a working code and then upload it to the final chip and install, not ideal for the eeprom, but not the end of the world if i kill it, although i suspect im not reading from it properly cause the program currently starts with selected effect at zero, but thats another issue for later!

so the overall plan is

a set of 11 neopixels that will do different effects
i want it to remeber the last state after a power off

to change mode i have a toggle switch (non momentary) when activated i want it to 'cycle' through the modes, going to the next mode every second, and indicating which mode it is in by sequentialy lighting the next led in the strip (1 for mode 1, 2 for mode 2 etc.) it is wired to the interupt because i want to be able to pull the program out of whatever mode it is in to achieve this

finally i want to have it be able to turn off after a user selectable time, using a similary concept to the mode switching to chose (15min? undecided) intervals and then at the end of this put the code into an 'everything off" mode, im hoping (although i havent googled it yet cause i haven't got that far, so please be kind..) that i can use a timer interupt or something like that to send it to the 'everything off' mode

for anyone interested here is more code formatted properly (i hope!!)

also the switch for the timer will be wired to the 2nd interupt on the board

#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>

#define NUM_LEDS 11
#define PIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);

byte selectedEffect = 0;
int var1 = 0;


void setup()
{
  Serial.begin(9600);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  pinMode(2, INPUT_PULLUP); // internal pull-up resistor
  attachInterrupt (digitalPinToInterrupt (2), changeEffect, CHANGE); // pressed
}
void loop()
{
  Serial.print("main");
  Serial.println(selectedEffect);
  delay(500);
}
void changeEffect() {
  if (digitalRead (2) == LOW)
  {
    Changemode();
  }
}
void Changemode()
{
  while (digitalRead (2) == LOW)
  {
    selectedEffect++;
    delay(5000);
    Serial.print("button");
    Serial.println(selectedEffect);
    EEPROM.put(0, selectedEffect);

  }
}

obviously some parts have been pulled from sample code of others, some parts are my own

the multiple serial references are how im seeing what the code is doing as its not connected to the neopixels atm, and obviously i havent really programmed anything for them in this script yet anyway....

the
delay(500);
is just there to make the program managble while im trying to figure it out, makes it so i can acyually read the text being outputted

the
delay(5000);
is there because i actually want the program to freeze for those 5 seconds it'll check the switch state after those 5 seconds, and if its off, itll hold the current mode number, and if its on itll advance the mode by 1 and hold that for the next 5 seconds

Well interrupts can't do that because as soon as an interrupt has finished doing its thing then it returns to the exact place in the code it was in before the interrupt took place. If this is in the middle of a delay it will still be in the delay and the delay will finish as if nothing has happened. It is only when your code gets to look at the pattern number to run will any change be noticed, which unless you do something special, will be at the end of the pattern cycle.
Most of the patterns you see on line are written with delay functions in them so can't just copy any code once you get round to adding patterns to your code.

The secrete to stopping this is to write everything as a state machine. This is a sort of pseudo multitasking where the code is written so that checking what needs to be done next is done as fast as possible.

When you do this it solves your other two problems at the same time.

  1. Checking if your next button has been presses can be done in the code with no need to use interrupts.
  2. Checking if you need to shut down does not need an interrupt or a timer, it is just another task your code checks to see if it is time to do.

The blink without delay is an ultra simplified version of this technique that just blinks two LEDs independently, the idea is that you take that technique and make it do as many tasks as you want, but that is a big step for a beginner and there are many posts that explain that task.
I have my own take on that task here:-
http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html
The forum has a large stick post dedicated to this problem as well:-
https://forum.arduino.cc/t/demonstration-code-for-several-things-at-the-same-time/217158

I have posted an example of LED patterns written as a state machine before but here it is again. It takes patterns given as examples in two main addressable strip libraries and shows how they should be written as a state machine.

// Multiple patterns in a state machine format
// using the FastLED library
// by Mike Cook 2017

#include "FastLED.h"

// first set up the parameters to use in the pattern calling
unsigned long patternInterval [] = { 500, 40, 20, 200, 5 }; // how often each pattern updates
unsigned long lastUpdate [5] ;  // for millis() when last update occurred
boolean patternEnabled [] = {true,true,false,true,true}; // should the pattern be called at all
byte patternState[5]; // state machine variable for patterns - this initialises them to zero

// now set up the LEDs to use
#define NUM_LEDS 64
#define DATA_PIN 3
#define CLOCK_PIN 13


CRGB leds[NUM_LEDS];

// Constants for patterns
// for Fire2012
#define COOLING  20
#define SPARKING 50
#define COLOR_ORDER BGR

// now set up the array of pointers to each pattern
void (*patternPtrs[5])(int index,byte state); //the array of pattern pointers

void setup() {
  //initialises the array of pattern pointers
  patternPtrs[0] = blinkOne; 
  patternPtrs[1] = cylon;
  patternPtrs[2] = fire;
  patternPtrs[3] = colorWipe;
  patternPtrs[4] = rainbowCycle;
  //initialises the FastLED driver you want
  //FastLED.addLeds<APA102,leds, NUM_LEDS); // 13 clock  and 11 data
  FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, BGR>(leds, NUM_LEDS); // 13 clock  and 11 data
  FastLED.setBrightness(8);
}

void loop() {
  for(int i = 0; i<5; i++) { // go through all the patterns and see if it is time to call one
    if(patternEnabled[i] && millis() - lastUpdate[i] > patternInterval[i]){
      lastUpdate[i] = millis();
      callPatterns(i, patternState[i]);
    }
  }
}

void callPatterns(int index, byte state) {
  (*patternPtrs[index])(index,state); //calls the pattern at the index of `index` in the array
}

// These are the pattern functions written as a state machine
// this is the Blink program in FastLED's example folder
void blinkOne(int index,byte state) {
  if(state == 0){
    leds[3] = CRGB::Blue;
    FastLED.show();
    patternState[index] = 1; // move on the state machine for the next call
  }
  if(state == 1){
     leds[3] = CRGB::Black;
     FastLED.show();
     patternState[index] = 0;
   }
  }

// this is the Cylon program in FastLED's example folder
// we will use LEDs 8 to 15 to show this
void cylon(int index,byte state) {
  static int i = 8; // replaces the loop index
  if(state == 0){
    leds[i] = CRGB::Red;
    FastLED.show();
    patternState[index] = 1; // move on the state machine for the next call
  }
   if(state == 1){
    // now that we've shown the leds, reset the i'th led to black
    leds[i] = CRGB::Black;
    i++; // increment what was the loop variable
    if(i >= 16){ // we have finished one direction
     patternState[index] = 2;
     i--;
    }
    else {
    patternState[index] = 0;
    }
   }
   // Now go in the other direction only green
   if(state == 2){
     leds[i] = CRGB::Green;
    FastLED.show();
    patternState[index] = 3; // move on the state machine for the next call
   }
  if(state == 3){
    // now that we've shown the leds, reset the i'th led to black
    leds[i] = CRGB::Black;
    i--; // decrement what was the loop variable
    if(i < 8){ // we have finished the return, go back to the start
     patternState[index] = 0;
     i= 8; // ready to start again
    }
    else {
    patternState[index] = 2;
    } 
    // note that this could be better implemented but it has been written like this to keep it close to the original example
    // so you can see what changes have been made 
  }
}

// this is the Fire2012 program in FastLED's example folder
void fire(int index,byte state) {
// using LEDs 16 to 32
// Array of temperature readings at each simulation cell
  const byte startLED = 16; // first LED in section
  const byte numLEDs = 16;
  static byte heat[numLEDs];

  random16_add_entropy( random());
  // Step 1.  Cool down every cell a little
    for( int i = 0; i < numLEDs; i++) {
      heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
    }
  
    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for( int k= numLEDs - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
    }
    
    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if( random8() < SPARKING ) {
      int y = random8(7);
      heat[y] = qadd8( heat[y], random8(160,255) );
    }

    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < numLEDs; j++) {
        leds[j+startLED] = HeatColor( heat[j]); // transfer heat array to LEDs
    }
    FastLED.show(); // display this frame
  }

//colorWipe  modified from Adafruit example to make it a state machine
// uses LEDs 32 to 40
void colorWipe(int index,byte state) {
  static int i =0; // used as state variable
  static byte firstLED = 32;
  static byte numLEDs = 8;
    leds[i+firstLED] = CRGB::Yellow;
    FastLED.show();
    i++;
  if(i >= numLEDs){
    i = 0;
    for(int j;j<numLEDs; j++) leds[j+firstLED] = CRGB::Black; // blank out strip
  }
}

 // rainbowCycle modified from Adafruit example to make it a state machine
 // uses LEDs 48 to 64
void rainbowCycle(int index,byte state) {
  static uint16_t j=0; // used as state variable
  static byte firstLED = 48;
  static byte numLEDs = 16;
    for(int i=0; i< numLEDs; i++) {
      leds[i+firstLED].setHSV( (((i * 256 / numLEDs) + j) & 255), 255, 255);
    }
    FastLED.show();
    j++;
    if(j >= 256*5) j=0;
}
1 Like

ok so after doing some more looking ive found that if i use

  asm volatile ("  jmp 0");

it'll take it back to the very beginning of the sketch, basically resets, but it'll hold the state of the mode count and the time count because i plan to store them in eeprom,

maybe it'll damage the chip, maybe it won't, they're cheap, i'll use one of my others if it does....

i may be wrong about how all of this works, but im only gonna figure this out byt trying stuff out,

a state machine might be the way to do it, but i dont know what that is, i know wht this is, my plan is something similar to the alleffects sketch found here

but removing some modes that aren't relevant and obviously a modification the the mode change as well as the addition of the timer script that will send it to a mode that will basically be everything off

so i think currently all i really need to know is, is the delay going to work inside the change mode function, if not what will? a variation *"of blink without delay?" because thats way more complicated then a simple delay

i do understand the concept of blink without delay, i've used that concept before (without knowing what it was called) and if i need to use that i can but i think the basic concept is going to work as i've seen it work before.

Sigh,
So after all this was an XY problem
X-Y Problem

And you are at it again.

Does not sit very nicely with:-

What you are proposing is known as a soft restart by vectoring through zero.
It will not damage the processor board but it is an almost futile concept.

Why bother asking questions if all you are going to do is ignore the answers and come up with crackpot ideas of your own?

Delay will not work in any function called from an ISR.

I have told you the answer in complete detail.

Yes it is and you will have to cope with it. Delay is a blocking function no code other than ISRs can do anything while it is running.

if you want high responsiveness to button-switches in combination with blinking in different patterns the basic concept is

looping through loop very fast
so mutliple things are repeated
in sequence at high frequency
creating a user-experience of doing it in "parallel"

quickly do one single step of a blinkpattern
quickly check a short moment for a button-press

quickly do one single step of a blinkpattern
quickly check a short moment for a button-press

if the quickly check for button-press detects a button-press
increment a variable that controls which pattern shall be shown

So within the next run of loop
the newly chosen pattern starts to execute

As you want to do a more complex thing than
button-unpressed LED OFF
button-pressed LED ON

this requieres some more complexness than just
command
delay
command
delay...

If you want to program this on your own it requires understanding
some medium advanced concepts of programming.

Perfoming a backflip with a BMX-bike in a halfpipe requires more than
explain: this is the front brake, this is the back-wheel-brake these are the pedals.

Same thing with programming you want to do.
You have to understand:

  • what a state-machine is and how it works
  • what arrays are
  • how non-blocking timing works (=avoiding delay )

And then combine these techniques to create the functionality you want.

So with which part do you want to start?
best regards Stefan

Awesome! thanks mate!!

are you sure that it is "awesome" that it is NOT working in an ISR?

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