using more than one loop

Hello all I am trying to run a PID program to control a heater, but I also want to print out the digital input, and control a pump on for 10 seconds, off for 1 minute. The PID is heating the water no problem. An when I pull up the serial monitor it will give me the initial reading but will not change over time. And when I include the pump on/off nothing but the pump on off works. How can I do all these things in one code. Can I use three different loops or a loop within a loop or what? Thank you for any responses.

http://arduino.cc/forum/index.php/topic,97455.0.html… IS a Good place to start before you ask the unanswerable
No Code, No description of the project… Diagrams; pictures or drawings are a good start along with the code which you neglected to post…

Doc

Sorry I meant to add the code. Here it is. I want the PID to work simultaniously with the serial.println(TempIn) and work with the digitalWrite(pump,HIGH), DELAY, LOW, DELAY. Thank you.

#include <PID_v1.h>

int heat = 8; //initialize heat
int pump = 13; //initialize pump
int Input; //digital read from rtd
double TempSet, Output, TempIn; //Define Variables for PID
double gap; //Help in agressivness level of PID
double aggKp=4, aggKi=0.2, aggKd=1;
double consKp=1, consKi=0.05, consKd=0.25; //Define the aggressive and conservative Tuning Parameters

PID myPID(&TempIn, &Output, &TempSet, consKp, consKi, consKd, DIRECT); //Specify the links and initial tuning parameters

int WindowSize = 5000;
unsigned long windowStartTime;
void setup()
{
pinMode(pump,OUTPUT); //initialize pump as output
pinMode(heat,OUTPUT); //initialize heater as output
Serial.begin(9600); //initialize serial communication at 9600 bits per second
analogReference(INTERNAL); //set ref voltage 0 - 1.1 V
windowStartTime = millis();
TempSet = 700; //insert desired temp
myPID.SetOutputLimits(0, WindowSize); //tell the PID to range between 0 and the full window size
myPID.SetMode(AUTOMATIC); //turn the PID on automatically
}

void loop()
{
Input = analogRead(A0); //reads in digital value
TempIn=Input;
//TempIn = 5*Input + 6; //converts digital value to temperature
gap = abs(TempSet-TempIn); //distance away from setpoint
if(gap<60)
{ //we’re close to setpoint, use conservative tuning parameters
myPID.SetTunings(consKp, consKi, consKd);
}
else
{ //we’re far from setpoint, use aggressive tuning parameters
myPID.SetTunings(aggKp, aggKi, aggKd);
}
myPID.Compute(); //Turns relay on/off based on temp read in

unsigned long now = millis();
if(now - windowStartTime>WindowSize)
{ //time to shift the Relay Window
windowStartTime += WindowSize;
}
if(Output > now - windowStartTime) digitalWrite(heat,HIGH);
else digitalWrite(heat,LOW);
digitalWrite(pump,HIGH); //turn pump on
delay(7000);
digitalWrite(pump,LOW);
delay(120000);
Serial.println(TempIn);
}

When I run the code without the pump commands, the PID works and i can pull up the serial monitor and will read out the input at the instant the program is loaded but it will not change while its heating. and when I have the pump commands in there all that works is the pump, or what i think it happening it runs the pump commands at the end of the loop and what till there done before cycling back around to the PID control

You need to re-write you code to not use blocking delay() calls.

You also need to use CODE tags when you're posting code.

Thanks. I will re-write my code for the pump control without using delays. However, can anyone tell me why my serialprint is just staying the same once i upload the program?

The code you posted looks as if it would do an analog read of input A0, run the PID once and print the input value, roughly every two minutes. If that's not what you see, perhaps you could explain in more detail what you actually see.

Only running your PID once every two minutes seems to negate the point of having a PID. You would be better off deleting these four lines, and re-implementing them using the non-blocking approach demonstrated in blink without delay:

    digitalWrite(pump,HIGH);                           //turn pump on
    delay(7000);
    digitalWrite(pump,LOW);
    delay(120000);

However, can anyone tell me why my serialprint is just staying the same once i upload the program?

Serial.print() is a method. It SHOULD stay the same throughout your program. Mine certainly doesn't move around in memory.

Now, the value that it prints, that's a different story.

Perhaps if you tried printing the value before the PID process has used it, and before the endless delay()s, you might detect a pattern that made sense.

Sorry I meant to add the code. Here it is. I want the PID to work simultaniously with the serial.println(TempIn) and work with the digitalWrite(pump,HIGH), DELAY, LOW, DELAY. Thank you.

Please use code tags when posting code. Also, if you don’t have a good
preferred coding style, use the format button before you post code, to make it more readable. And posting some sample output would be helpful in diagnosing the problem as well. You say the value printed is the same: is it always 0? -12343464332? 5000? How long do you wait to see if it changes? Do you print out the Output value as well?

A few over-all problems to start with.

First thing is the way you set this up. You want to control the temperature by turning a heater
on and off, from what I can tell. Have you considered using a PWM output on the heat pin? PWM basically turns a pin on and off rapidly, in a specific ratio. It has the effect of “dimming” the
power output. That would make the code and PID implementation considerably easier. See analogWrite() in the reference links.

Secondly, I’m not sure you understand how to get the most out of PID algorithm. Your input
is the temperature, and your output is how long to turn on the heater. But, every time you Compute you are going to get a different time value. So the amount of time you wait changes during the waiting period. This will make your program act a bit irregularly. Either only do Compute after each window (but the window is too long) or use PWM as suggested above.

Then the problem with the pump code is that you are waiting 127 seconds in your loop function. That causes everything else to wait over two minutes to update. It’s also much greater than your window value, which probably means that the heater is probably either always on or always off.

Lets look at the code.

#include <PID_v1.h>

int heat = 8;                                        //initialize heat 
int pump = 13;                                       //initialize pump

heat and pump don’t change, so you can make them either const or #defines.

int Input;                                           //digital read from rtd
double TempSet, Output, TempIn;                      //Define Variables for PID
double gap;                                          //Help in agressivness level of PID 
double aggKp=4, aggKi=0.2, aggKd=1;
double consKp=1, consKi=0.05, consKd=0.25;           //Define the aggressive and conservative Tuning Parameters

PID myPID(&TempIn, &Output, &TempSet, consKp, consKi, consKd, DIRECT); //Specify the links and initial tuning parameters

int WindowSize = 5000;

If you are going to compare WindowSize to other milllis() values, it should also be an unsigned long. Apples to apples and all that. Sometimes integer type conversions will bite you when you least expect it.

unsigned long windowStartTime;
void setup()
{
  pinMode(pump,OUTPUT);                              //initialize pump as output
  pinMode(heat,OUTPUT);                              //initialize heater as output
  Serial.begin(9600);                                //initialize serial communication at 9600 bits per second
  analogReference(INTERNAL);                         //set ref voltage 0 - 1.1 V
  windowStartTime = millis();
  TempSet = 700;                                     //insert desired temp
  myPID.SetOutputLimits(0, WindowSize);              //tell the PID to range between 0 and the full window size
  myPID.SetMode(AUTOMATIC);                          //turn the PID on automatically
}

void loop()
{
  Input = analogRead(A0);                            //reads in digital value
  TempIn=Input;
  //TempIn = 5*Input + 6;                            //converts digital value to temperature

OK so far.

  gap = abs(TempSet-TempIn);                         //distance away from setpoint
  if(gap<60)
  {                                                  //we're close to setpoint, use conservative tuning parameters
    myPID.SetTunings(consKp, consKi, consKd);
  }
  else
  {                                                 //we're far from setpoint, use aggressive tuning parameters
     myPID.SetTunings(aggKp, aggKi, aggKd);
  }

If you choose your tuning parameters correctly, there should be no need to switch from conservative to aggressive settings. PID is supposed to do that for you. It will either aggressively or conservatively change the output depending on how far from the set point you are (that’s the P part of PID). I would delete all the code for agg versus cons settings and just try to find one set of tuning parameters that works. Tuning PID parameters is tricky, so you should read up on it.

  myPID.Compute();                                  //Turns relay on/off based on temp read in
  unsigned long now = millis();
  if(now - windowStartTime>WindowSize)
  {                                                 //time to shift the Relay Window
    windowStartTime += WindowSize;
  }

This clause is kind of a problem. What you want to do is start a new “window” every time the current window expires. But consider the case where now is 127000 and windowStartTime is 15000 (which might happen with the long delays below). It will never catch up. What you want to do is set windowStartTime to now.

  if(Output > now - windowStartTime) {
   digitalWrite(heat,HIGH);
  } else {
   digitalWrite(heat,LOW);
  }

As mentioned above, the long window time is going to cause weird effects with your Output computations. If Output is set to say 1000 the first time through the loop, and then you call Compute and it might get set to 3000, then the heat may be turned on or off depending on where you are in the window. If you used PWM then you would set your output range to 0-255 and simply do an analogWrite() with the output value. No need for windows at all.

If that’s not feasible (since we don’t know your hardware setup), then I would still suggest abandoning these windows and just turn the heat on or off depending on whether the Output is over or under the halfway mark. Then make the loop fairly tight, delaying no more than a second anywhere in it.

  digitalWrite(pump,HIGH);                           //turn pump on
  delay(7000);
  digitalWrite(pump,LOW);
  delay(120000);

This is the biggest problem. Long delays are a Bad Thing. Most single purpose sketches, like the blink() example, use delays because the program isn’t doing anything else, so it can afford to waste time. In general this is a bad example. This code will turn the pump on for 7 seconds and off for two minutes. To do this without delay(), the idea is to use millis() instead. You keep track of the current state of the pump and the last time it changed state. Then using the current time (now) you can figure out if you need to turn the pump on or off. When you change the pump state, update the saved state and timestamp.

  Serial.println(TempIn);
}

It would also be a good idea to print the Output value, the time values and maybe the gap. More information is better.

I hope this gives you some ideas to improve the code.

Thank you that is very helpful. I knew that the pump delays were screwing things up but being a beginner to programming I didn't know another way to run the pump other than the simple blink example.

As far as the PID is concerned. A 1500W water heater is what is being controlled. I am using a SSR with the arduino and PID to control the heater. The analogread is coming from a PT100 RTD sensor. The sensor is in the bottom of pot of water i am trying to heat, and while the water is heating I would like to circulate the water for about 5-10 seconds every minute or so. I have not tuned in the PID yet, but that is what I am trying to do. I am looking to hold a temp within one degree. Based on the resolution calculation, the reading from my RTD ought to be with +/- .1 C. Once the water is heated to its desired temp, the pump will then run constantly. I obviously haven't gotten this far in the code but my main question was to find a way to run the pump without using delays.

An also, I was trying to tune in my PID before adding in any more bits of code. So I set the Setpoint for the PID to 600(that being the analog read in based on a reference voltage set to 1.1V). It began heating the water at around 60 F, which was a read of 171. Now the heater then runs, and continues running, and as it got closer to Setpoint I could see the SSR cycling on and off via the LED on the SSR. The problem is however, while my water was heating up, my print out of my analogread did not change( and this was without the pump commands anywhere in there. those were only in the coded i posted so people could see what I was looking to do). However, I know the RTD is working because once the water was heated to around 130 F, I uploaded just the analogread program, and it would print out 480 or so. Then i uploaded my PID program again, and the initial print out would be 480, but again it would not change. Im sorry I was vague in the beginning, this is my first time posting with programming questions.

Try using this function to set the status of the pump (you can call it in every loop, without wait cycle, and it will work)
dutyON is the number of seconds you want the pump on
dutyOFF is the number of seconds you want the pump to be off
The total cycle of the pump is (dutyON+dutyOFF) in seconds.

boolean SquareWaveForm (int dutyON, int dutyOFF) { 
unsigned long cycle=(dutyON+dutyOFF);
long m=millis()
int rest= m % cycle;
boolean result=(rest<dutyON);
return result;
}

oops, sorry. One error. third line should be:

long m=millis()/1000