Arduino PID on Timers

I am trying to configure my arduino mega with PID functionality. The arduino example initiateS a Relay that turns on and off that is based off the ‘millis’ function. However, I would like to know if it is possible to have the PID on a timer call so after lets say 6 minutes it checks the sensor reading. Based off the sensor reading and how aggressive the parameters are; this will turn on or off a relay. My question is, can this be done with timers instead of ‘millis’? Below is the example arduino provided. Below that, is something i concocted. Please give advice. Thanks.

    #include <PID_v1.h>
    #define RelayPin 6
    
    //Define Variables we'll be connecting to
    double Setpoint, Input, Output;
    
    //Specify the links and initial tuning parameters
    PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);
    
    int WindowSize = 5000;
    unsigned long windowStartTime;
    void setup()
    {
      windowStartTime = millis();
      
      //initialize the variables we're linked to
      Setpoint = 100;
    
      //tell the PID to range between 0 and the full window size
      myPID.SetOutputLimits(0, WindowSize);
    
      //turn the PID on
      myPID.SetMode(AUTOMATIC);
    }
    
    void loop()
    {
      Input = analogRead(0);
      myPID.Compute();
    
      /************************************************
       * turn the output pin on/off based on pid output
       ************************************************/
      if(millis() - windowStartTime>WindowSize)
      { //time to shift the Relay Window
        windowStartTime += WindowSize;
      }
      if(Output < millis() - windowStartTime) digitalWrite(RelayPin,HIGH);
      else digitalWrite(RelayPin,LOW);
    
    }

Here is my code:

    #include "Wire.h"
    #include "DS1307RTC.h"
    #include "PID_v1.h"
    #include "SPI.h"
    #include "Time.h"
    #include "TimeAlarms.h"
    
    #define RELAY_ON 1
    #define RELAY_OFF 0
    #define Relay1  2 
    
    int analogChannel0 = 0;
    double Setpoint, Input, Output;
    double Kp=1, Ki=0.5, Kd=0.25;  //PID Tuning Parameters
    PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT); //PID Tuning Parameters
    
    
    void setup() {
    
      
         
         digitalWrite(Relay1, RELAY_ON);
    	 
    	 pinMode(Relay1, OUTPUT);   
    	 
    Serial.begin(9600);
         setSyncProvider(RTC.get);
    	 Alarm.timerRepeat(360, Pid); 
      
         Input = analogRead(analogChannel0);
         myPID.SetMode(AUTOMATIC);     //turn the PID on
         Setpoint = 7.0;
    	 
    	 }
    	 
    void loop() {                           
                Alarm.delay(0); 
                } 
    			
    			
    void Pid()
                 {
                 Input =  analogRead(analogChannel0);              
                 myPID.SetTunings(Kp, Ki, Kd);   
                 myPID.Compute(); 
    			 ////FROM HERE I DONT KNOW WHAT TO DO
                 }

Moderator edit:
</mark> <mark>[code]</mark> <mark>

</mark> <mark>[/code]</mark> <mark>
tags added.

Accessing a PID instance in an interrupt service routine and in loop is extremely unlikely to work correctly.

arios85: My question is, can this be done with timers instead of 'millis'?

Why do you want to do that? What's wrong with the first example?

I don't necessarily want to have an interrupt service for this. I simply want a timer to be called. I have more code that i left out in my post so more things are going on. I like having nothing in the loop except the alarm delay. There are other timers that are called so i prefer not to have much interference in my loop. Besides i don't need to check the sensor every 10 seconds or 5 or whatever. Once every six minutes or even an hour is fine. So the question becomes,without interrupts or inside the loop, can a PID be called on timers and be checked every XX amount of minutes instead of the millis function? Nothing inside the loop or no delays establishes a very efficient program that wont have things interfere with each other especially if you got a lot going on. Any helpful advice is much appreciated. Thanks

arios85:
I don’t necessarily want to have an interrupt service for this.

I got it in my head you were using the TimerOne library. Sorry about that.

No worries. Its the last part of the code where i am wondering about. The void part. After the myPID.Compute() how would one go about coding it? The adaptive tuning example simply gives an analogWrite(3,Output); I cant just simply write a digitalwrite after the myPID.Compute part can I? It doesn't seem to work right because i need the name of the relay, the output, and the HIGH or LOW in my parenthesis. Does anyone have an idea about the last part and how to integrate the Output , Relay name, and the HIGH or LOW command in the void call? Thanks for any help. It is much appreciated.

Once every six minutes or even an hour is fine. So the question becomes,without interrupts or inside the loop, can a PID be called on timers and be checked every XX amount of minutes instead of the millis function?

It can be checked at whatever rate you want. Just call SetSampleTime to set the rate. You will have to call Compute at twice that rate (every 3 minutes if the SampleTime is 6 minutes).

Bear in mind that the PID is a discrete quantization algorithm. If you choose a SampleTime that is too long you will not be able to tune the loop / the loop will not function correctly.

Hey, I am in the library with NotePad++ and i have identified the SetSampleTime. Thanks. I don't understand when you mean I need to compute at twice the rate. What do you mean? And how do I code that to compute that twice as fast? And inside my void below this text, how do i incorporate my Output, my relay name, and the HIGH or LOW bit to turn on and off the relay with the digitalWrite command?

void Pid() { Input = analogRead(analogChannel0); myPID.SetTunings(Kp, Ki, Kd); myPID.Compute(); digitalWrite(Relay1, HIGH); // Where or how do i include the Output part?? }

Call SetSampleTime in setup…

void setup() 
{
...    
  myPID.SetSampleTime( 30000 ); // <<<<< Call SetSampleTime in setup

The parameter is an int so the longest supported SampleTime is 32767 milliseconds.

When you configure the periodic call for Pid, the rate has to be 1/2 of the SampleTime or less…

Alarm.timerRepeat( 15000, Pid );  // <<<<< 30000 / 2 = 15000.  Do not go above 15000 for a SetSampleTime of 30000

I understand your explanation. Thank you . My last question is within the void. How do i execute the Output and my relay? The relay example says to " if(Output < millis() - windowStartTime) digitalWrite(RelayPin,HIGH); else digitalWrite(RelayPin,LOW);" . Do i still need this in my void even if the void is on a timer and i have a set sample time? Im just unsure about what this line does with the millis and the windowStartTime and if i need it in my timer call. Thanks fr all the help.

My last question is within the void.

void is a return type. If a function returned an int, you wouldn't say "within the int" would you? Use the name of the function you are talking about. It is NOT void.

How do i execute the Output

You don't execute a value.

Do i still need this in my void even if the void is on a timer and i have a set sample time?

If the purpose is to turn the relay on or off as required, how could you possibly NOT need that?