Quake Posted June 19, 2020 Share Posted June 19, 2020 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 Quote Link to comment Share on other sites More sharing options...
AzeoTech Posted June 19, 2020 Share Posted June 19, 2020 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. Quote Link to comment Share on other sites More sharing options...
Quake Posted June 19, 2020 Author Share Posted June 19, 2020 Hello, Thank you for looking into this! I will give it a go and rework the sequence, fingers crossed! -Jacob Quote Link to comment Share on other sites More sharing options...
Quake Posted June 22, 2020 Author Share Posted June 22, 2020 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 Quote Link to comment Share on other sites More sharing options...
AzeoTech Posted June 23, 2020 Share Posted June 23, 2020 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 #. Quote Link to comment Share on other sites More sharing options...
Quake Posted June 23, 2020 Author Share Posted June 23, 2020 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 Quote Link to comment Share on other sites More sharing options...
AzeoTech Posted June 23, 2020 Share Posted June 23, 2020 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. Quote Link to comment Share on other sites More sharing options...
Quake Posted June 24, 2020 Author Share Posted June 24, 2020 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.