How to break a loop with Button

Hi,

I’m running a Led animation on my InternetButton with a while loop.
Is there a way to end it with a button press?
The only thing that works for me right now is to check in the loop but that means I need to have the button pressed at that certain moment when it’s checked during the animation.
How i can I check for a button press while running the loop?

thanks in advance

Jan

One thing is that your loop will probably be fast enough to catch a normal push of the button anyhow (unless you’ve got a really slow loop).

But an asyncronous way to catch a button press is via interrupts.
Have a look at the docs for attachInterrupt()

BTW: If you want to have cloud support, you need to call Particle.process() at least every 10sec (more often is better to allow propper OTA flashing and speedy response).

2 Likes

I respect @ScruffR’s response, but assuming your led animation is largely not-blocking you could do it like this pseudo code:

while(!ButtonPressed)
{
  LightShow();
  buttonPressed = digitalRead(myButton) == HIGH;
}

or close to that

2 Likes

I understood that the loop is blocking, is it not?

But making it non-blocking would be even better and would then work with @BulldogLowell’s code too (or with my point about the speed of loop :sunglasses:)

1 Like

Thanks for the quick replies.
Its’s just a simple pulsing animation of the leds. But it takes around 6 seconds.
So right now I have to press the button in the exact moment (when the loop ends and starts again) to be able to stop it.

I’ll look into the interrupt thing and see if that’s what I’m looking for.

Thanks again guys. :thumbsup:

2 Likes

@HansWurst90, there are ways to do LED animation that are non-blocking. Even with an interrupt, you will still need to sample the flag that the interrupt service routine will set within you animation code. If you can share you code, we can probably advise you on how to make it non-blockiing :smile:

@peekay123, thats my code:

void loop()
{
    if (button.buttonOn(1))
    {
    	animate = false;
        doAnimation("allOff");
    }
}


int doAnimation(String animation)
{
    animate = false;
    if (animation=="allOff")
    {
        button.allLedsOff();
        return 1;
    }
    else if (animation=="allOn")
    {
        button.allLedsOn(255, 255, 255);
        return 1;
    }
    else if (animation=="breathe")
    {
        animate = true;
        while (animate)
        {
	        for (int i=0; i<150; i++)
	        {
	            button.allLedsOn(i, i, i);
	            delay(13);
	        }
	        for (int j=150; j>=0; j--)
	        {
	            button.allLedsOn(j, j, j);
	            delay(13);
	        }
        }
        return 1;
    }
    else
    {
        return -1;
    }
}

@HansWurst90, if I may, I suggest that in doAnimation() you simply set a mode variable that you sample in loop(). So when you get a “breathe” command you can set the “mode” variable to say 3. Then in loop, you can read the mode value, do the action and reset the value so it waits for the next doAnimation command. Add to that a non-blocking version of “breath” that, when activated does the up and down allLedsOn() stuff in steps every 13ms until completed. I’ll pull some code together for you later when I’m at home. :smile:

1 Like

Changed my Code according to your suggestion. You’re right, it’s a little cleaner now.
But I’m still not really getting behind the interrupt thing.
What I forgot to say is, that I want to intercept and break the loop via the button, but also with a function that I call via the cloud API.
Is that possible with an attached interrupt?

sure, but it will respond similarly, waiting for the programmed break in your code to "stop" the LED effects.

I promise you, learning to get your LEDs working in a non-blocking fashion will bring you to a new level!

1 Like

How do I learn that? :smile:

The key for that is not to use long running loops (e.g. with delays).

Instead of entering into the loop and staying there till it is finished, you first check if it’s time for a change in state

uint32_t msLastChange;
...
void loop()
{
  ...
  if (millis() - msLastChange > timeToChange)
  {
    doChange() // this should keep track of the states and
              // just do stuff without any delay
    msLastChange = millis();
  }
  ...
}

and if not you just fall through, do other stuff and come back later.
That would be the basic (“soft delay”) way. To save you some typing, you can use the ElapsedMillis library.

More advanced techniques would involve interrupts which can be handled rather comfortably by using SparkTimerInterval library.

You’ll find some topics on these subjects in this forum.

I didn't test this, but it compiles. You will have to add your header information which was omitted from your post.

I'm introducing a couple of concepts including the "non-blocking" breathing effect, a basic State Machine approach, and using the enum resource.

You can look it over and think about other use-cases for this simple State Machine:

enum LedState {
  LEDS_OFF, LEDS_ON, LEDS_BREATHING
};

LedState myState = LEDS_OFF;
LedState lastState = LEDS_OFF;

void loop()
{
  if (button.buttonOn(1))
  {
    /*animate = false;
    doAnimation("allOff");*/
    myState = LEDS_OFF;
  }
  if (myState == LEDS_OFF)
  {
    if (lastState != LEDS_OFF)
    {
      button.allLedsOff();
    }
  }
  else if (myState == LEDS_ON)
  {
    if (lastState != LEDS_ON)
    {
      button.allLedsOn(255, 255, 255);
    }
  }
  if (myState == LEDS_BREATHING)
  {
    breathe();
  }
  lastState = myState;
}

void breathe()
{
  static int direction = 1;
  static int index = 0;
  static unsigned long myTimer = millis();
  if (millis() - myTimer >= 13)
  {
    index += direction;
    if (index >= 255 || index <= 0 )
    {
      direction = direction * -1;
    }
    button.allLedsOn(index, index, index);
    myTimer = millis();
  }
}

int doAnimation(String animation)
{
  if (animation == "allOff")
  {
    myState = LEDS_OFF;
    return 0;
  }
  else if (animation == "allOn")
  {
    myState = LEDS_ON;
    return 1;
  }
  else if (animation == "breathe")
  {
    myState = LEDS_BREATHING;
    return 2;
  }
  else
  {
    return -1;
  }
}

try it and see if your button does what you want it to do, you can then learn to make the button cycle through the states or toggle on/off, or the like.

1 Like