[SOLVED] How to break out of a loop using particle.function?

Hello, this is my first post and first time coding with the photon. I’m playing with leds and trying to make a night rider sweep motion that can be turned on and off using a web page or the ifttt buttons on my phone. The code I’m using is a tweaked version of an arduino tutorial I found, mixed with the particle tutorial on cloud functions here.

I’m able to get the cloud function to work no problem for a simple turning on or off of leds. However, once I put the led sweep in a loop (so it continually goes back and forth using the for loops to light the leds) I can’t figure out how to make the “off” command from the cloud, actually kill the loop.

Does anyone know how to kill a forever running loop using particle.function? or some other method that makes it just as easy?

note: Here is the code im playing with. I’m a complete coding neophyte, and this code has not been cleaned, so please be nice. If you have suggestions, please, I am all ears! I feel like this should be easy, but it has been frustrating me for hours.

    /*
      For Loop Iteration
    
     Demonstrates the use of a for() loop.
     Lights multiple LEDs in sequence, then in reverse.
    
     The circuit:
     * LEDs from pins 1 through 6 to ground
    
     Modded from http://www.arduino.cc/en/Tutorial/ForLoop
     */
    
    int ledDelay = 85; // delay between LEDs as they cycle through the line
    int startDelay = 275; // pause between full sweep of the LEDs - makes it look more nighrider-y
    int midDelay = 70; // pause before the LED's sweep back to complete the cycle
    int commandCheck = 0; //trying to use this varble to break out of the ledsweep() function, but its not working
    
    void setup() {
        // use a for loop to initialize each pin as an output:
        for (int thisPin = 1; thisPin < 7; thisPin++) {
            pinMode(thisPin, OUTPUT);
        }
        
        //Declare cloud function(s):
        Particle.function("led",ledToggle);
    }
    
    void loop() {
        //commandCheck = 0;
    }
    
    int ledToggle(String command) {
        if (command=="on") {
            commandCheck = 1;
            ledSweep();
            // return 1;
        }
        else if (command=="off") {
            commandCheck = 0;
            ledOff();
            // return 0;
        }
        
        else {
            return -1;
        }
    }
    
    void ledSweep() {
    //    while (commandCheck = 1) {
            // loop from the lowest pin to the highest:
            for (int thisPin = 1; thisPin < 7; thisPin++) {
                digitalWrite(thisPin, HIGH);
                delay(ledDelay);
                digitalWrite(thisPin, LOW);
            }
            // loop from the highest pin to the lowest:
            for (int thisPin = 6; thisPin >= 1; thisPin--) {
                digitalWrite(thisPin, HIGH);
                delay(ledDelay);
                digitalWrite(thisPin, LOW);
            }
            digitalWrite(1, HIGH);
            delay(startDelay);
    //    }
    }
    
    void ledOff() {
        for (int thisPin = 1; thisPin < 7; thisPin++) {
                // turn the pin off:
                digitalWrite(thisPin, LOW);
        }
    }
1 Like

Currently you’ve got a (never ending) loop within a function call. Seeing as you should treat those as interrupts, it’s well recommended to keep those as short as possible and return a value as quickly as possible. Don’t try to do actual work inside them.

The ‘while’ you’ve got in there does nothing more than looping if a variable is true.

Now, where else in the code do you have a ‘natural’ loop that you could use to replace that ‘while’ part ;)?

@Moors7 thank you for the reply! If I’m understanding your :wink: hint, you want me to put the LED sweep loop inside the void loop() function. How do I then control whether it is ON (looping) or is OFF via the cloud function calls for “on” or “off” via Particle.function("led",ledToggle);… or some other method?

So far, so good :smile:

The 'while' loops as long as a variable is true.
You've now got something that will loop regardless, but you only want it to execute (parts) if said variable is true.
Does that help ;)?

1 Like

I thought I had tried that when I first started down this road last night. But I will go back and try again with fresh eyes this evening and report back. Thanks for the guidance.

1 Like

So I ended up back where I started with the looping all done within the loop() func, as you suggested… turns out I was using a = instead of == in my while statement. that fixed it and now im able to break and start the looping of leds using on and off commands through cloud function! woohoo.

thanks for the help!!

final working code for anyone interested:

/*
 Purpose:
    * Lights multiple LEDs in sequence, then in reverse - as directed by cloud commands.
    * Makes you feel like the handsome owner of a talking car named "Kit"

 Circuit:
    * pins "firstPin" through "lastPin" to resistors to LEDs to ground
*/

//variables for led sweep timing
int ledDelay = 85; // delay between LEDs as they cycle through the line
int startDelay = 275; // pause between full sweep of the LEDs - makes it look more nighrider-y
int midDelay = 70; // pause before the LED's sweep back to complete the cycle

//variables for pin wiring - change if needed
int firstPin = 1; //input the first pin the led array is wired to
int lastPin = 6; //input the last pin the led array is wired to

//varable for passing from the cloud function to the loop() to control the lights
int commandCheck = 0; //set to zero initally so that leds do not start flashing unless given the "on" command; set to 1 to start with the leds on

void setup() {
    //initialize each pin as an output:
    for (int thisPin = firstPin; thisPin <= lastPin; thisPin++) {
        pinMode(thisPin, OUTPUT);
    }

    //declare cloud function(s):
    Particle.function("led",ledToggle);
}

void loop() {
    //perform the sweeping of the leds ONLY iff commandCheck = 0:
    while (commandCheck == 1) {
          //loop from the first pin to the last:
          for (int thisPin = firstPin; thisPin <= lastPin; thisPin++) {
              digitalWrite(thisPin, HIGH);
              delay(ledDelay);
              digitalWrite(thisPin, LOW);
          }
          //loop back from the last pin to the first:
          for (int thisPin = lastPin; thisPin >= firstPin; thisPin--) {
              digitalWrite(thisPin, HIGH);
              delay(ledDelay);
              digitalWrite(thisPin, LOW);
          }
          digitalWrite(1, HIGH);
          delay(startDelay);
    }
    //turn all the leds off:
    for (int thisPin = firstPin; thisPin <= lastPin; thisPin++) {
        digitalWrite(thisPin, LOW);
    }
}

//define cloud function to accept commands "on" or "off" and set commandCheck var accordingly
int ledToggle(String command) {
    if (command=="on") {
        commandCheck = 1;
        return 1;
    }
    else if (command=="off") {
        commandCheck = 0;
        return 0;
    }
    else {
        return -1;
    }
}
1 Like

Great to hear you’ve got it working! If I may, I’d suggest not using while loops unless absolutely mandatory (for timing related things, for example). A while loop is often blocking, which may cause some issues (cloud dropping out, etc).
Over here, you’ve already got the ‘natural’ loop() that does the looping for you. You just need to act on it if a certain condition is met, or ignore it otherwise.
You even mention it in the comment above the ‘while’ by saying “only execute if command check is true”. So you’re trying to check if something is true, and if so, execute the corresponding code. if you implement it like that, doing away with the ‘while’ loop, you might find your device running smoother, while being less prone to issues in the future.
If you need some more hints as to what to use instead of the ‘while’, let me know. If within my capabilities, I’ll gladly help out. If not, I can probably some someone who can :wink:

2 Likes

IF you could be a bit more clear about what you’re proposing … :wink: This works - how does it look. Any other suggestions for a newb?

    /*
     Purpose:
        * Lights multiple LEDs in sequence, then in reverse - as directed by cloud commands.
        * Makes you feel like the handsome owner of a talking car named "Kit"
    
     Circuit:
        * pins "firstPin" through "lastPin" to resistors to LEDs to ground
    */
    
    //variables for led sweep timing
    int ledDelay = 85; // delay between LEDs as they cycle through the line
    int startDelay = 275; // pause between full sweep of the LEDs - makes it look more nighrider-y
    int midDelay = 70; // pause before the LED's sweep back to complete the cycle
    
    //variables for pin wiring - change if needed
    int firstPin = 1; //input the first pin the led array is wired to
    int lastPin = 6; //input the last pin the led array is wired to
    
    //varable for passing from the cloud function to the loop() to control the lights
    int commandCheck = 0; //set to zero initally so that leds do not start flashing unless given the "on" command; set to 1 to start with the leds on
    
    void setup() {
        //initialize each pin as an output:
        for (int thisPin = firstPin; thisPin <= lastPin; thisPin++) {
            pinMode(thisPin, OUTPUT);
        }
        //declare cloud function(s):
        Particle.function("led",ledToggle);
    }
    
    void loop() {
        //perform the sweeping of the leds ONLY if commandCheck = 0:
        if (commandCheck == 1) {
            //loop from the first pin to the last:
            for (int thisPin = firstPin; thisPin <= lastPin; thisPin++) {
                digitalWrite(thisPin, HIGH);
                delay(ledDelay);
                digitalWrite(thisPin, LOW);
            }
            //loop back from the last pin to the first:
            for (int thisPin = lastPin; thisPin >= firstPin; thisPin--) {
                digitalWrite(thisPin, HIGH);
                delay(ledDelay);
                digitalWrite(thisPin, LOW);
            }
            digitalWrite(1, HIGH);
            delay(startDelay);
        }
        else {
            //turn all the leds off:
            for (int thisPin = firstPin; thisPin <= lastPin; thisPin++) {
                digitalWrite(thisPin, LOW);
            }
        }
    }
    
    //define cloud function to accept commands "on" or "off" and set commandCheck var accordingly
    int ledToggle(String command) {
        if (command=="on") {
            commandCheck = 1;
            return 1;
        }
        else if (command=="off") {
            commandCheck = 0;
            return 0;
        }
        else {
            return -1;
        }
    }
1 Like

Working and running smooth is not always the same.
What @Moors7 is trying to convay is that you should consider moving away from a “blocking” loop with delay() calls, which renders your code less responsive to cloud calls and may interfere with OTA updates, and adopt a non-blocking coding style.

e.g. instead of

void loop() {
  for (int i=0; i < 100; i++) {
    doSomething(i);
    delay(100);
  }
}

make use of the “looping” nature of loop() like this

void loop() {
  static int i = 0;
  static uint32_t msDelay = 0;

  if (millis() - msDelay >= 100) {
    msDelay = millis();
    doSomething(i++);
    if (i >= 100) i = 0;
  }
}

This way the cloud tasks will run hundreds of times more often than with the blocking for() and hence your Particle.function() will become more responsive and you’re less likely to have an OTA update fail.
Also if you had other tasks to do, you’d leave lots of µC time for these too.

2 Likes