Particle.function loop

I am trying to have a simple html page to arm a PIR sensor, which in tern sends me updates. I had the PIR sensor working and sending updates prior to trying to interface to the html page with Particle.function, now I am now having issues. Forgive my idiocy as it is 03:00 and I’ve been beating my head on the wall for a bit. I think the issue is if I try and follow the syntax of the examples I have seen the digitalread doesnt occur within a loop, so nothing happens, but I have also tried to loop the function called from online to no avail. Any help is greatly appreciated, thanks.

I have attached the atrocity below

//the time we give the sensor to calibrate (10-60 secs according to the datasheet)
int calibrationTime = 30;

//the time when the sensor outputs a low impulse
long unsigned int lowIn;

//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int pause = 5000;

boolean lockLow = true;
boolean takeLowTime;

int pirPin = 4;    //the digital pin connected to the PIR sensor's output
int BlueLED = 1;
int GreenLED = 6;
int RedLED = 0;
int dummy = 1;  //prevents multiple emails from being sent

int ledToggle(String command);
/////////////////////////////
//SETUP
void setup(){
  pinMode(pirPin, INPUT);
  pinMode(BlueLED, OUTPUT);
  pinMode(GreenLED, OUTPUT);
  pinMode(RedLED, OUTPUT);

  Particle.function("led", ledToggle);

  digitalWrite(pirPin, LOW);
  digitalWrite(BlueLED, LOW);
  digitalWrite(GreenLED, LOW);
  digitalWrite(RedLED, LOW);



  //give the sensor some time to calibrate
    for(int i = 0; i < calibrationTime; i++){
      delay(1000);
      }
    delay(50);
  }

////////////////////////////
//LOOP
void loop()
{
  // this loops forever
}

int ledToggle(String command){

  if (command=="on") {
     digitalWrite(RedLED, HIGH);
     if(digitalRead(pirPin) == HIGH){
       digitalWrite(BlueLED, HIGH);   //the led visualizes the sensors output pin state
       if(lockLow){
         //makes sure we wait for a transition to LOW before any further output is made:
         lockLow = false;            
         delay(50);
         }
         takeLowTime = true;
       }

     if(digitalRead(pirPin) == LOW){
       digitalWrite(BlueLED, LOW);  //the led visualizes the sensors output pin state

       if(takeLowTime){
        lowIn = millis();          //save the time of the transition from high to LOW
        takeLowTime = false;       //make sure this is only done at the start of a LOW phase
        if(dummy== 1){
            Particle.publish("movement");
            digitalWrite(GreenLED, HIGH);
            dummy=dummy+1;
        }

        }
       //if the sensor is low for more than the given pause,
       //we assume that no more motion is going to happen
       if(!lockLow && millis() - lowIn > pause){
           //makes sure this block of code is only executed again after
           //a new motion sequence has been detected
           lockLow = true;
           delay(50);
           }
       }
  return 1;
  }
  else if (command=="off") {
      digitalWrite(RedLED, LOW);
    return 0;
  }
  else {
        return -1;
    }
}

What exactly is your problem?
You explained how your problem started but I don’t quite see what it is.

Does the function not fire?
Do you not get the events?

Try adding some Serial.print() statements to pinpoint the location where your problem (whatever it is) starts occuring.

And please use the D1 notation over merely 1 when refering to pins.


But there might be a fundamental misunderstanding - also looking at the topic title

Particle.function()s are functions and no loops. So when you call the function it executes only once and returns. It won’t execute again unless you call it again.

Like @ScruffR mentioned, a function call doesn’t loop. You should also try to keep it rather short, since it might cause problems with the HTML return if it isn’t quick enough.
Personally, I’d use an interrupt for a motion sensor, so I wouldn’t have to poll the thing constantly. Once it detects movement, you could have it check for a ‘send message’ boolean. If true, send a message/light LED, if false, do something else/nothing. You can then toggle that Boolean using a function call.
Even without the use of interrupts, that strategy is still applicable if you were to have the loop poll the sensor. Give it a try and let us know if you need further help.

1 Like

The issue is that it never calls my function "Particle.publish(“movement”). Also, I am on windows 10 and can’t seem to get the serial terminal to show spark notices, so I am debugging with LEDS. In short, the BlueLED doesn’t light signifying motion was detected, and the GreenLED doesnt light signifying my function was called.

I guess I can start fresh with interrupts, but I don’t think i understand how to use Particle.function within a loop, or in general

The code only runs when you call the function from the API (in this case), thus you’d have to trigger that function, and then hope that movement occurs with in the few milliseconds thereafter.
The Particle.function() is ‘simply’ a function you can call from the Web. It allows you to trigger things, change values, etc. In this case, I’d use it to toggle a ‘send message’ boolean value which gets checked in the main loop.
In the interrupt I would toggle a ‘motion detected’ boolean, which also gets checked in the loop.
In the loop you could then check for

if (motion_detected){
  RGB.control(true); // enable LED control
  RGB.color(0,0,255); //blue
  if (send_message){
    Particle.publish("movement detected");
    RGB.color(0,255,0); //green
  }
  delay(1000); // respect publish limit
  RGB.control(false);
}

I just wrote this on my phone, so it’s untested, but it gets the idea across, I hope. Let me know if anything is unclear.

Hey, I don’t know if I am understanding your explanation completely. The above code would be within the loop? Why is an interrupt necessary? I know I can respond to an interrupt correctly and toggle a function from the web, its the tying them together that is screwing me up. If you have time could you attach code from setup() on?

Yes, that code would be in the loop (as stated in the sentence above ;)).
The interrupt isn’t necessary, but allows for less ‘work’. Then the Photon wouldn’t have to poll the sensor continuously to check whether or not it’s triggered. An added benefit is that you could have the Photon sleep, and wake up on an interrupt.
So no, you don’t have to use interrupts.
That said, here’s an (untested) version with interrupts. It compiles, but I can’t guarantee that it works.

Example removed due to some inherent mistakes as pointed out by @ScruffR 

This should do the same thing, without interrupts:

volatile bool send_message = false;

int motion_pin = D1;


void setup() {
    Serial.begin(9600);
    pinMode(motion_pin, INPUT_PULLDOWN);
    Particle.function("pubMessage", message_toggle);
}

void loop() {
    if (digitalRead(motion_pin)){
        RGB.control(true); // enable LED control
        RGB.color(0,0,255); //blue
        if (send_message){
            Particle.publish("movement detected");
            RGB.color(0,255,0); //green
        }
        delay(1000); // respect publish limit
        RGB.control(false);
    }
}

int message_toggle(String argument){
    int value = argument.toInt();
    
    if (value == 0){
        send_message = false;
    }
    else if (value == 1){
        send_message = true;
    }

  return value; // ScruffR added the missing return statement ;-)
}
2 Likes

Thank you for your help. I tried both examples. I could not get the interrupts to work at all, even testing other interrupt examples. Anyway, I could worry about that at another time. I tried the second example you posted, and still no luck. Doesn’t this suffer from the same problem of particle.function only being called once?

Here is the code I adapted from your example

volatile bool send_message = false;

int calibrationTime = 30;
long unsigned int lowIn;
long unsigned int pause = 5000;
boolean lockLow = true;
boolean takeLowTime;

int motion_pin = D2;
int RedLED = D5;
int GreenLED = D4;


void setup() {
  pinMode(motion_pin, INPUT);
  pinMode(RedLED, OUTPUT);
  pinMode(GreenLED, OUTPUT);
  Particle.function("led", ledToggle);

  digitalWrite(GreenLED, LOW);
  digitalWrite(motion_pin, LOW);
  digitalWrite(RedLED, LOW);

  for (int i = 0; i < calibrationTime; i++) {
    delay(1000);
  }
  delay(50);
}

void loop() {
  Particle.function("led", ledToggle);
  if (digitalRead(motion_pin) == HIGH) {
    digitalWrite(RedLED, HIGH);
    if (lockLow) {
      //makes sure we wait for a transition to LOW before any further output is made:
      lockLow = false;
      delay(50);
    }
    takeLowTime = true;
  }

  if (digitalRead(motion_pin) == LOW) {
    digitalWrite(RedLED, LOW);  //the led visualizes the sensors output pin state

    if (takeLowTime) {
      lowIn = millis();          //save the time of the transition from high to LOW
      takeLowTime = false;       //make sure this is only done at the start of a LOW phase

      if (send_message == true) {
        Particle.publish("movement");
        digitalWrite(GreenLED, HIGH);
      }
      delay(1000); // respect publish limit
    }

    //if the sensor is low for more than the given pause,
    //we assume that no more motion is going to happen
    if (!lockLow && millis() - lowIn > pause) {
      //makes sure this block of code is only executed again after
      //a new motion sequence has been detected
      lockLow = true;
      delay(50);
    }
  }
}


int ledToggle(String command) {

  if (command == "on") {
    send_message = false;
  }
  else if (command == "off") {
    send_message = true;
  }
}

@enque, want does “still no luck” mean? Is ledToggle not being called? How are you calling it? You definitely should not have Particle.function in loop() – it only needs to be defined once, and should be done in setup().

1 Like

Still no luck means that although the motion triggers the RedLED the way it normally would offline, the green LED does not illuminate when the detector is armed from the web and motion is detected, furthermore the Particle.publish(“movement”); which is intended to send me an email through ifttt does not happen either.

@enque, I’ll ask again. Is ledToggle being called? Does the code ever reach the else-if clause where you set send_message to true?

Yes, I would think so. But it isnt acting like it. My code is above.

If your green LED is never lighting, then send_message is never being set to true. So, either ledToggle is not being called, or command is “on” rather than “off”. Where are you calling your Particle function from?

It is being called from an html website. I basically copied and pasted the code from the “Control LEDs over the 'net” example in https://docs.particle.io/guide/getting-started/examples/core/. I can reflash the code for the LED example and the website calls the function properly, so I thought it should work here.

You should post your HTML code, here. Also, if you haven’t already, take the Particle.function call out of loop().

Okay, thank you for your help. I have added the html code below, the only difference is that i took out my deviceID and access token.

<!DOCTYPE blahblah>
<html>
<head>

  <script charset="utf-8" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript">
  </script>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <title>Arm Security</title>
  <script type="text/javascript">
    var accessToken = "my token";
    var deviceID = "mydeviceid"
    var url = "https://api.spark.io/v1/devices/" + deviceID + "/led/";
    
    
    function switchOn()
    {
        $.post(url, {params: "on", access_token: accessToken });
    }
    
    function switchOff()
    {
        $.post(url, {params: "off", access_token: accessToken });
    }
  </script>
</head>
<body>
  Arm/Disarm Security<br>
  <form>
    <input name="args" onclick="switchOn()" type="button" value="ON"> 
    <input name="args" onclick="switchOff()" type="button" value="OFF"><br>
    <br>
    <br>
    <br>
  </form>
</body>
</html>

Put a Serial.print line in ledToggle to print out the value of command. That will let you know if it’s being called, and if command has the correct value (the HTML looks ok to me, but I’m not a web programmer so I’m no expert on that).

Ok, I just got the serial monitor working, after some trouble. Let me try.

Out of curiosity, have you replaced above strings with your own token an device ID?
Can you try calling your Particle.function() via CLI or one of these
http://jordymoors.nl/interface/
http://suda.github.io/particle-web-interface/

I tested your code, and it seems to work fine for me (with my correct access token and device ID). The ledToggle function was called successfully from the web page, and the green LED lit up when motion_pin was taken from high to low.

There are a couple of odd things in your code that I changed. There’s no need for the for-loop you have in setup() to make the 30 second delay – just use delay(30000) if you want a 30 second delay. Second, you have a digitalWrite to motion_pin, but that pin is set as an input, so that won’t do anything. If you want that pin to be low until the motion sensor pulls it high, then set it to INPUT_PULLDOWN instead of INPUT (you might not need that, depending on what your motion sensor’s output is).

It’s not really clear what you want your code to do. I think an interrupt approach would probably be simpler. If you can describe what result you want, and what kind of output your motion sensor has, I can help you with that code.