Quake

Temperature Profiling Sequence - Rookie at coding

Recommended Posts

Hello!

I have been attempting to write a sequence for a simple temperature control profile with different soak times at each step, to be used with a temperature calibration bath.

For the most part, it is just a repeated code over the number of steps. In this example, I am trying four steps. Write to a setpoint, check if you are within +3 -3 range of process value, start timing the soak time, once complete, move to next step.

The idea is this: I have a readout of a Process Value (C1_PV), and control an output to a Control Loop Setpoint (C1_SP) via channels.

I have also declared global variables:

CAL(1-4) - desired Calibration Point or, step

SOAK(1-4) - desired soak time entered in Minutes

I have also declared:

Global variables "READY" (1-4), which will tell if the calibration point is reached/within range of -3 - +3 of the calibration point, and start a separate sequence "STEP_TIMER".

Global variables "starttime" (1-4", which will store the time when did the step has started.

Global variable "STEPTIME" which should store a product of "STEP_TIMER" sequence

 

The other sequence "STEP_TIMER" should check for which "READY" variable is on, pick the appropriate "starttime" variable, compare it against SOAK time and write to "STEPTIME", ultimately used for counting down remaining time of the current step
 

Profile sequence:

Quote

private currentTime = systime()
if (CAL1 == 0)
   ? "Missing Calibration Point Values. Enter Calibration Point Values and restart the profile"
   delay(1)
   return 0
   else
   C1_SP = CAL1
   ? "Calibration Point 1 Start"
endif
while (C1_PV > CAL1-1) && (C1_PV < CAL1+1)
   delay(1)
endwhile
while ((C1_PV > CAL1-3) && (C1_PV < CAL1+3))
   ? "Calibration Point 1 Reached. Starting Soak"
   READY1 = 1
   global starttime1 = systime()
   beginseq(STEP_TIMER)
   waituntil(currentTime + (SOAK1*60))
   else
   READY1 = 0
   endseq(STEP_TIMER)
   delay(1)
endwhile
if (CAL2 == 0)
   ? "Calibration Completed."
   return 0
   else
   C1_SP = CAL2
   ? "Calibration Point 2 Start"
endif
while (C1_PV != CAL2)
   delay(1)
endwhile
while ((C1_PV > CAL2-3) && (C1_PV < CAL2+3))
   ? "Calibration Point 2 Reached. Starting Soak"
   READY2 = 1
   global starttime2 = systime()
   beginseq(STEP_TIMER)
   waituntil(currentTime + (SOAK2*60))
   else
   READY2 = 0
   endseq(STEP_TIMER)
   delay(1)
endwhile
if (CAL3 == 0)
   ? "Calibration Completed."
   return 0
   else
   C1_SP = CAL3
   ? "Calibration Point 3 Start"
endif
while (C1_PV != CAL3)
   delay(1)
endwhile
while ((C1_PV > CAL3-3) && (C1_PV < CAL3+3))
   ? "Calibration Point 3 Reached. Starting Soak"
   READY3 = 1
   global starttime3 = systime()
   beginseq(STEP_TIMER)
   waituntil(currentTime + (SOAK3*60))
   else
   READY3 = 0
   endseq(STEP_TIMER)
   delay(1)
endwhile
if (CAL4 == 0)
   ? "Calibration Completed."
   return 0
   else
   C1_SP = CAL4
   ? "Calibration Point 4 Start"
endif
while (C1_PV != CAL4)
   delay(1)
endwhile
while ((C1_PV > CAL4-3) && (C1_PV < CAL4+3))
   ? "Calibration Point 4 Reached. Starting Soak"
   READY4 = 1
   global starttime4 = systime()
   beginseq(STEP_TIMER)
   waituntil(currentTime + (SOAK4*60))
   else
   READY4 = 0
   endseq(STEP_TIMER)
   delay(1)
endwhile
? "Calibration Completed."

 

Step Timer Sequence:

Quote

if (READY1 == 1)
   STEPTIME = SOAK1 - (systime()-starttime1)
endif
if (READY2 == 1)
   STEPTIME = SOAK2 - (systime()-starttime2)
endif
if (READY3 == 1)
   STEPTIME = SOAK3 - (systime()-starttime3)
endif
if (READY4 == 1)
   STEPTIME = SOAK4 - (systime()-starttime4)
endif

 

This is my first journey with coding and been digging through DAQFactory forum implementing little bits here and there.

Currently when I run this code with process value below Step 1 calibration point, it flies through step 1 immediately to step 2 and stays there. Once I input simulated C1_PV process value that matches CAL2 point, i get "Calibration Point 2 Reached. Starting Soak" and immediately restarts to "Calibration Point 1 Start" without soaking. Next, for testing.. I reduced simulated C1_PV process value to CAL1. I get "Calibration Point 1 Reached. Starting Soak". It waits the SOAK1 time, and after that it restarts back with "Calibration Point 1  Start" into "Calibration Point 1 Reached"...

I guess I made some serious syntax errors. Even with calibration entered for point 3, It does not get there. It's as if its flip-flopping between Point 1 and 2.

 

Any advice would be appreciated. I attached the template file.

 

-Jacob

Profiling Working File 2.ctl

Share this post


Link to post
Share on other sites

I didn't take a super detailed look, but in general it is usually best when doing step wise (or really state based) procedures, such as recipes, to use a simple step counter, and I don't see that.  The general form would be something like:

global recipeState = 0
while (1)
   switch
      case(recipeState == 0)
         // do whatever for the first state. 
         if (readyToMoveOn)
            recipeState = 1
         endif
      case(recipeState == 1)
         // do whatever for the first state. 
         if (readyToMoveOn)
            recipeState = 2
         endif
     // etc for each state
   endcase
   delay(0.1)
endwhile

Likewise, instead of using waitUntil() I will typically use a "nexttime" variable which holds the time stamp of when I want to do something next.  This allows the code to loop and me to keep an eye on things while waiting a particular amount of time.  For example:
 

private nexttime = systime() // do it right away
while(1)
    if (systime() >= nextTime)
      // do something
      // and set the next time to do it:
      nexttime += 600  // in 10 minutes
   endif
   // we should also check that we didn't over temp:
   if (temperature[0] > 100)
      // overtemp! so break out
      break
   endif
   delay(0.1)
endwhile

Use that sort of design to handle the timing, combined with the state tracking and case statement and I think you'll find your script will be much neater.

If the recipes really are just doing ramps or soaks, then eventually you might want to move to writing subroutines to handle these cases with parameters, and then use arrays, or an array of objects to hold the parameters for each step.  But I'd start by hard coding it like you have done.


 

Share this post


Link to post
Share on other sites

Hello,

Thank you for looking into this!

I will give it a go and rework the sequence, fingers crossed!

 

-Jacob

Share this post


Link to post
Share on other sites

1. I have implemented the first suggestion for "state" / step checks and it does help with cleaning the code tremendously.

2. To my understanding the second suggestion would act as a self-resetting timer performing a specific action every interval. I can't find use for it in this application since I am just going from step to step, and do not have an action to be run at exact specific interval (or, do it again in this many minutes)

I re-wrote this sequence multiple times, and just can't get it right. I "dumbed" it down, skipped the "steptimer", skipped most of notifications.

The sequence should

-> Set the C1_SP (controller setpoint) to CAL"..." (calibration point) variable.

-> Using "while", keep checking if it's within the band (the temperature needs to stabilize and it cannot start timing immediately).

-> If it is within the band, time out the Soak time and move to the next profile step (or recipeState). If at any point the temperature gets out of the band,  the "while" should stop executing and restart when it is back in the band

For some reason in my sequence the "while" acts more like "if" ? Essentially - as long as the process value enters the band, it executes all statements under "while", even if the process value went out of the range for "while" condition before the "waituntil()" finished.

For example, first calibration point is 100°C. Soak 1 value is 1 (1*10 = 10 seconds). Start off with simulated temperature 20°C. I begin sequence - it waits. I simulate 100, it completes all "while" steps including the proper Soak timing, but then keeps restarting the ProfileStep 0. Does not move to ProfileStep 1

If I start off again with 20°C and enter 200°C (just fly by the 100°C without stopping), the while executes, times out the soak and keeps repeating, even though the process value is 200°C...

 

Quote

private currentTime = systime()
global ProfileStep = 0
if (CAL1 == 0)
   ? "Missing Calibration Point Values. Enter Calibration Point Values and restart the profile"
   delay(1)
   return 0
endif
while (1)
    switch
        case(ProfileStep == 0)
         C1_SP = CAL1 // set the output channel setpoint for the first profile step setpoint
             while (C1_PV > CAL1 - 3) && (C1_PV < CAL1 + 3) // When the temperature is within +3 -3 degrees of first point, start timing soak time.
                delay (3) // has to be at least 3 seconds in the band to start (avoiding erratic messaging in case it did not stabilize yet)
                ? "Calibration Point 1 Reached. Starting Soak"
                currentTime = systime()
                waituntil(currentTime + (SOAK1*10))
                ? "Calibration Point 1 Completed"
                ProfileStep = 1
             endwhile
      case(ProfileStep == 1)
         C1_SP = CAL2 // set the output channel setpoint for the second profile step setpoint
             while (C1_PV > CAL2 - 3) && (C1_PV < CAL2 + 3) // When the process is within +3 -3 degrees of second point, start timing soak time.
                delay (3) // has to be at least 3 seconds in the band to start (avoiding erratic messaging near outer bands)
                ? "Calibration Point 2 Reached. Starting Soak"
                currentTime = systime()
                waituntil(currentTime + (SOAK2*10))
                ? "Calibration Point 2 Completed"
                ProfileStep = 2
             endwhile

.... any other cases follow the same logic.......

      endcase
      delay(0.1)
endwhile

 

Share this post


Link to post
Share on other sites

The syntax on the while() is wrong.  The entire condition has to be in parenthesis:

while ((C1_PV > CAL1 - 3) && (C1_PV < CAL1 + 3))

 

Also, I don't put while()'s in my individual steps.  Instead I would put it as a step, and let the outer while handle it.  Something like (note renumbering of steps):

private starttime
while (1)
    switch
        case(ProfileStep == 0)
            C1_SP = CAL1 // set the output channel setpoint for the first profile step setpoint
            starttime = systime()
            profileStep = 1
         case(ProfileStep == 1)
             if (systime() >= starttime + (SOAK1 * 10)) // move on:
               profileStep = 2
             else
                if ((C1_PV <= CAL1 - 3) || (C1_PV >= CAL1 + 3)) // When the temperature is within +3 -3 degrees of first point, start timing soak time.
                   if (systime() > starttime + 3) // don't evaluate for first 3 seconds
                     profileStep = 0 // go back to step 0 to restart the timer
                   endif
                endif
              endif
      case(ProfileStep == 2)
         //...etc...
   endcase
   delay(0.1)
endwhile

Feel free to use partial steps, say 0, 0.5 for half steps, like what I did in 0 and 1 above.  Then you can use floor(profileStep) to get the actual step #.

 

Share this post


Link to post
Share on other sites

I looked at the provided suggestion, but I do not like the use of "startime" as a reference point. Because it would not ensure the temperature has stabilized and keep referring to the point in time the Setpoint was written (when it may take who knows how long for the temperature to climb/cool to set point, overshoot a bit, undershoot and finally be within the +3 -3 band)

After many trials and errors and with friend's expertise, I finally have it working like a charm.

The key things that were missing:

1. The "if" condition for moving to next step inside the "while"... -> When temperature would get out of while() condition, it would pause and wait for it to be back within the band.

However, with the "if" statement completed and while() condition met, it kept repeating the first case...it wouldn't move to the next case...so:

2. Updated the "while" condition by adding "&& ProfileStep ==" -> this ensured the while() gets kicked out after the "if" statement completes writing ProfileStep that invalidates the while()

Couple other nits and bits revised: the mean(channel[0,9]) is used instead of C1_PV[0]. It does the job well ensuring the temperature is within the band before starting timing soak. The profileStep numbering shifted from 0 to start from 1 for the coherence. Also "default" was added to case.

Next steps on the list...adding ramp rate and step timer. Should be smooth sailing from here on!

Quote

private currentTime = systime()
global ProfileStep = 1
if (CAL1 == 0)
   ? "Missing Calibration Point Values. Enter Calibration Point Values and restart the profile"
   delay(1)
   return 0
endif
while (1)
    switch
        case((ProfileStep == 1) && (CAL1 != 0))
         C1_SP = CAL1 // set the output channel setpoint for the first profile step setpoint
            while ((mean(C1_PV[0,9]) >= CAL1 - 3) && (mean(C1_PV[0,9]) <= CAL1 + 3) && ProfileStep == 1) // The average of 10 recent readings must be within +3 -3 degrees band to execute steps below. Otherwise..wait
               ? "Calibration Point 1 Reached. Starting Soak"
               currentTime = systime() //refresh the currentTime variable
               waituntil(currentTime + (SOAK1*10)) //waits until the systime() + Soak time
               if ((mean(C1_PV[0,9]) >= CAL1 - 3) && (mean(C1_PV[0,9]) <= CAL1 + 3))
                  ? "Calibration Point 1 Completed"
                  ProfileStep = 2 // move to next case
               endif
            endwhile
      case((ProfileStep == 2) && (CAL2 != 0))
         C1_SP = CAL2
            while ((mean(C1_PV[0,9]) >= CAL2 - 3) && (mean(C1_PV[0,9]) <= CAL2 + 3) && ProfileStep == 2)
               ? "Calibration Point 2 Reached. Starting Soak"
               currentTime = systime()
               waituntil(currentTime + (SOAK2*10))
               if ((mean(C1_PV[0,9]) >= CAL2 - 3) && (mean(C1_PV[0,9]) <= CAL2 + 3))
                  ? "Calibration Point 2 Completed"
                  ProfileStep = 3
               endif
            endwhile

//...............Repeat for how many cases needed.............
      default
         ? "Calibration Completed"
         return 0
      endcase
      delay(0.1)
endwhile

 

Share this post


Link to post
Share on other sites

There are ways around stabilized temperature.  Just use another step, i.e. step 1 sets the setpoint and moves to step 2.  Step 2 waits for the temp to stabilize in range, then sets starttime and moves to step 3.  Or just combine step 1 and step 2.  My example was just a simplified version.

I strongly recommend against putting those while loops inside the case, but it is up to you. I've seen it done with loops inside of states inside of loops many times in the past with customers needing help and it always causes issues.  They'll get it to work, then as soon as they go and tweak something it stops working and they just end up adding more embedded code.  

Same goes with using WaitUntil().  There is no way to break out of it, or to check on the system while waiting other than stopping the sequence.  So, for example, you are doing waituntil() the soak time is complete, but you have no way of monitoring the temperature at the same time and correcting.  You have to wait until the waituntil() is complete to do anything.  If you don't like using starttime, that is fine, but I still recommend using a single while() and monitoring the system time against your calculated end time with each loop iteration.

Share this post


Link to post
Share on other sites

Hello,

I see your point. The multi-stepping can be implemented for greater control of the process and increased level of safety.

For my application, I am using a PID temperature controller with Modbus communications and a built-in Alarm functionality. That controller does not have the profiling feature (or some companies call it "programmer" option, "segments", ramping to soak etc... many names of the same animal, where steps for the profiling cycle are user defined i.e step 1 - go to this setpoint at this rate, step 2 - soak there for this time. step 3 - go to another point...)

In case there is any thermal runaway condition, I have means of protection via the Temperature Controller's built-in Alarm, and a redundant separate Limit Controller with a designated (separate) temperature sensor.

The waituntil() while executing will lock the whole sequence until it's done, but for what I am doing it's fine, because the Temperature Controller's control loop handles the PID and controlling the process to setpoint, plus the last 10 readings must have been within the band for timing to start so it is unlikely anything funny will happen. To be ultra-safe, I could add in a separate sequence to start before waituntil() and stop after waituntil() that would monitor for runaway and stop the profiling sequence when necessary, or follow your suggestion with the "starttime".

Also please note this sequence is not meant to be iterative. Once the calibration points and soak times are pre-defined and sequence is started, it should follow through all the steps and end the sequence without repeating.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now