Scheduling a function on Photon

I am trying to schedule a function on the Photon to run once a day using the timer mentioned in the documentation. I am a novice coder, so I think I have missed something as the function is not triggered. My project is a Force Sensitive Resistor attached to A0 on the photon that provides a number between 0 and 40000ish. I want the particle.publish function to publish “Buy Milk” when it gets below 400 and I use IFTTT to send an SMS to my phone. I want that to happen once a day, and it doesn’t need to be too precise. Here is my code:

int FSR_Pin = A0; //analog pin 0

Timer MilkTimer(5000, Milk_Check);

void setup(){
  Serial.begin(9600);
}

void Milk_Check(){
  int FSRReading = analogRead(FSR_Pin);
  if (FSRReading < 400)
{
   Particle.publish("Milk is low", "buy milk");
   Serial.println(FSRReading);
   }
   
    else 
{
    Particle.publish("Milk is fine", "all is well");
   Serial.println(FSRReading);}
}

@drwilson, you are currently triggering MilkCheck() every 5000ms or every 5 seconds. So every 5 seconds something will be published. The software timer maximum interval is 65535ms or 65 seconds so it can’t be used for a 24hr delay.

In order to only publish once a day, you may want to use the built-in Time library to check for a specific time each day to trigger the check. The idea is to keep the next day/hour/minute you want to do the next check and compare with the current day/hour/minute/sec until they match. You then do your sensor check, update your “future” day/hour/minute/sec and the cycle starts over.

Another entirely different approach is to use the web IDE TIMEALARMS library to set an alarm and trigger your sensor read. It does all the heavy lifting and makes things simpler for a new coder. :smiley:

2 Likes

do something once a day at the top of the hour:

void loop() 
{
  static int lastHour = 24;  // hour() returns zero through twenty-three
  int currentHour = Time.hour();
  if(lastHour != currentHour && currrentHour = 16)
  {
     // do something at 4:00pm every day!!
  }
  lastHour = currentHour;
  
  // put your main code here, to run repeatedly:
}
1 Like

Here is my entire code. The function never runs. I changed the time zone to -6 since I am in the Central Time Zone. What am I missing?

// This #include statement was automatically added by the Particle IDE.
#include "TimeAlarms/TimeAlarms.h"

int FSR_Pin = A0; //analog pin 0


void setup(){
  Time.zone(-6);
  Serial.begin(9600);
  Alarm.alarmRepeat(13,10,0, Milk_Check);
}

void Milk_Check(){
  int FSRReading = analogRead(FSR_Pin);
  if (FSRReading < 400)
{
   Particle.publish("Milk is low", "buy milk");
   Serial.println(FSRReading);
   }
   
    else 
{
    Particle.publish("Milk is fine", "all is well");
   Serial.println(FSRReading);}
}

@drwilson, you need a loop() function and an Alarm.delay():smile:

void  loop(){  
  Alarm.delay(1000); // wait 1000 ms (1 sec) between alarm checks

Without this, the alarms will not work!

1 Like

That worked! Thanks! :smiley:

2 Likes

Hi All,

I have a more general question.
In the cases above, why not just use something like delay(86400000) (24h in milliseconds).
What is the problem in delaying the loop().

Thanks a lot
Lukas

It would somehow work, but millis() is by far not comparable with the RTCs precission (especially over 24h) and if you’d like to do more than just hanging in a loop while waiting your massive delay would not really allow for that either.

I have another question. What if I did away with the timer and added a system.sleep(SLEEP_MODE_DEEP,86400) to conserve battery power? Is there a more elegant way to do that?

That’s the way to save most energy.
In what way do you want it more elegant?

I feel like that is too simple and I am forgetting something important. Here is the code I came up with.

  int FSR_Pin = A0; //analog pin 0


void loop(){
  System.sleep(SLEEP_MODE_DEEP,86400);
  delay(60000);
}

void Milk_Check(){
  int FSRReading = analogRead(FSR_Pin);
  if (FSRReading < 700)
{
   Particle.publish("Milk is low", "buy milk");
   Serial.println(FSRReading);
   }
   
    else 
{
    Particle.publish("Milk is fine", "all is well");
   Serial.println(FSRReading);}
}

Is this the whole code? If so, this won’t do anything, since you never call Milk_Check. You could call it in setup(), since that will be called when the device wakes from deep sleep. What is the purpose of the 60 second delay – I don’t think that line will execute, since you put the device to sleep in the previous line.

1 Like

I am a novice coder. What does the code for calling Milk_Check look like?

I read somewhere that the device needs time to connect to wifi so that is the purpose of the delay. Should it go in the set up before the call for the Milk_Check?

void setup(){
  
  delay(60000);
  Milk_Check();
System.sleep(SLEEP_MODE_DEEP,86400);
}

I recommend that you modify Milk_Check() to milkCheck() which is in keeping with the Arduino style guide. This also makes it easier to obtain assistance as it is inline with what more experienced folks are used to seeing.

EDIT: Corrected after @ScruffR spot on comment about oder of operations!

1 Like

If you are not changing the default SYSTEM_MODE() your own code will not start before you actually have a working cloud connection. So no extra delay required.

void loop(){
  System.sleep(SLEEP_MODE_DEEP,86400);
  delay(60000);
  Milk_Check();
}

Will never ever do anything but sleep, since your device goes to sleep immediately and on next wake will run through setup() into loop() and immediately go to sleep again.

For your loop() code you’d only need setup() anyhow.

void setup()
{
  //whatever you need to prepare
  ... 
  
  checkMilk();
  System.sleep(SLEEP_MODE_DEEP, 86400);
}

void loop() { } // will never execute

As for your implementation of checkMilk() (or whatever you’ll call it), try to avoid blanks in event names and try to choose one event name for all possible status (e.g. "MilkLevel") and put the actual status/level into the payload (second parameter) - exception: if you only use the event name without payload/data.

3 Likes

since @ScruffR was too fast (again), I'll just casually leave these references to the docs, and be on my way again:

System.sleep(SLEEP_MODE_DEEP, long seconds) can be used to put the entire device into a deep sleep mode. In this particular mode, the device shuts down the network subsystem and puts the microcontroller in a stand-by mode. When the device awakens from deep sleep, it will reset and run all user code from the beginning with no values being maintained in memory from before the deep sleep.

https://docs.particle.io/reference/firmware/photon/#system-sleep-sleep-

2 Likes

Thanks for all the help. Here is my revised complete code:

int FSR_Pin = A0; //analog pin 0


void setup(){
 Serial.begin(9600);
  checkMilk();
  System.sleep(SLEEP_MODE_DEEP,86400);
}

void checkMilk(){
  int FSRReading = analogRead(FSR_Pin);
  if (FSRReading < 700)
{
   Particle.publish("Milk is low", "buy milk");
   Serial.println(FSRReading);
   }
   
    else 
{
    Particle.publish("Milk is fine", "all is well");
   Serial.println(FSRReading);}
}

I had forgotten the Serial.begin, and discovered that in my testing at home. Apparently I am missing something else. The device goes to sleep and wakes just fine (I changed the time parameter to be 90 seconds for testing purposes). The checkMilk() never runs.

I think you need at least one Particle.process() call before sending your device to sleep, to finish the Particle.publish() transmission.

And for serial I think your device is not long enough online to USB enumerate and start the serial terminal before it goes back to sleep.

As @ScruffR said, you need to give the system time to publish before you put it to sleep. When I tested your code, it took 2 Particle.process() statements to give it enough time. Another way, that I think is safer, to make sure you have enough time for the publish, is to subscribe to it yourself, and put the device to sleep in the handler for subscribe call back. Also, to give the serial print enough time to complete, move those lines to before the publish line. If you do that, then it would be best to have only one event to publish (“milkCondition” in my example); you can distinguish them by their data, no need to have two different event names.

int FSR_Pin = A0; //analog pin 0

void setup(){
 Serial.begin(9600);
 Particle.subscribe("milkCondition", handler);
 delay(1000);
 checkMilk();
}

void checkMilk(){
  int FSRReading = analogRead(FSR_Pin);
  if (FSRReading < 700)
{
    Serial.println(FSRReading);
   Particle.publish("milkCondition", "buy milk");
   
   }
   
    else 
{
    Serial.println(FSRReading);
    Particle.publish("milkCondition", "all is well");
   
   }
}


void handler(const char *event, const char *data) {
    System.sleep(SLEEP_MODE_DEEP,86400);
}
3 Likes