This outdoor-compensated supply-air setpoint function stayed dead in emulation for three reasons. The GRAPH call had no destination for its output. The function body was missing its final block of code. The program POU_1 was never called. Once those three were fixed, the curve produced values. One defect remained: at exactly +10 °C outdoor the function returned 0. That is a logic fault, not a thermal one, and it would command the heater coil to a 0 °C supply setpoint.
Fixes that leave the setpoint curve dead
Engineers usually try the following when an outdoor-reset function shows no values in simulation. None of them touches the actual defect.
| Attempted fix | Why it fails |
|---|---|
| Delete and re-create the function POU | Declaration was never the problem. An unassigned result, a truncated body, or an uncalled program survives re-creation unchanged. |
| Port the project from CODESYS 2.3 to 3.5 hoping emulation behaves differently | The same curve logic ran correctly in 2.3. Porting to 3.5 adds task-configuration and library differences on top of the original defects without removing any of them. |
| Retune the PID gains | The PID receives a setpoint of 0 or a stale value. No gain set tracks a wrong setpoint correctly. |
| Watch the function's internal variables in online mode | A function has no instance. If nothing calls it, or its result goes nowhere, there is nothing to monitor. The online view looks like a data-binding failure when the real cause is that the code never executes. |
Execution and return-value mechanics of a CODESYS function
A FUNCTIONEvery call starts from initialized locals. It delivers exactly one result, and the body writes that result by assigning to the function name. Three consequences follow directly.
-
No call chain, no execution. In CODESYS 2.3, runs cyclically by default. Any other program, such as
POU_1, runs only if calls it or a task in the task configuration calls it. In 3.5, a program runs only when it is listed under a task, usuallyMainTask, or called from a program that is. An uncalledPOU_1meansGRAPHnever runs. - No output assignment, no visible value. In LD or FBD, a function box dropped into a network with an unconnected output computes and discards its result. Place the call in CFC or ST, or wire the output to a declared variable such as the PID setpoint input.
-
Incomplete body, default result. If the final block of code is missing, some input ranges never reach an assignment to the function name. The result then returns at its initial value, 0.0 for a
REAL. The missing tail in a curve function is usually the last segment, the clamp above the top breakpoint, or the final assignment of the computed value to the function name.
For troubleshooting, read one quantity first: the value that arrives at the PID setpoint input. If it reads 0 or never changes while the outdoor temperature input moves, the fault is logic. If it tracks the curve but the supply air still misses setpoint, the fault is thermal or in the loop: coil capacity, valve authority, or PID tuning.
The +10 °C zero: a breakpoint that falls between comparisons
A stepped or piecewise curve selects its segment with a chain of comparisons against the outdoor-temperature breakpoints. When adjacent tests both use strict inequalities, for example < 10 for one segment and > 10 for the next, an input exactly equal to the breakpoint matches neither branch. A function then returns its default 0. On this installation the gap sat at +10 °C.
Rewriting the logic as a made the zero disappear. An FB instance keeps its output between scans, so an unmatched input leaves the previous setpoint in place instead of resetting it to 0. That hides the gap rather than closing it. The output still depends on the direction from which the input approached the breakpoint. Close the gap properly:
- Make every segment test inclusive on one side,
<=on the lower segment and>on the next, so each input value maps to exactly one branch. - End the chain with an unconditional
ELSEthat clamps to the last setpoint, so no input can fall through. - Convert to an FB anyway if you want hysteresis or filtering on the outdoor input. Those need memory between scans, which an FB provides.
Thermally, a 0 °C supply setpoint on a heating application drives the PID to close the hot-water valve. At an outdoor temperature near +10 °C this is unlikely to trip a frost stat, but the supply air drops sharply and the loop saturates as the input crosses the breakpoint. Treat any 0 output as a code defect to fix.
Symptom-to-cause map for outdoor-reset setpoint logic
| Observed quantity | Likely cause | Where to read it |
|---|---|---|
| No values in emulation anywhere in the curve POU | Program containing the call (POU_1) not called by or any task |
Task configuration; call tree of |
| Function computes, but target variable stays at 0 | Output not wired or assigned; function box placed in LD/FBD with open output | Online view of the calling network; the variable connected to the output pin |
| Correct output over part of the range, 0 elsewhere | Truncated function body; no assignment to the function name for that range | Function body end; any branch without an assignment to the function name |
| 0 at exactly one outdoor temperature (here +10 °C) | Strict comparisons on both sides of a breakpoint | Force the outdoor input to each breakpoint value |
| Setpoint correct but supply air misses it | Thermal or loop problem, not curve logic | PID output %, valve position, coil water temperatures |
Bringing the GRAPH function into the scan
- Open the task configuration. In 2.3, confirm that exists and runs. In 3.5, confirm that or your program sits under a cyclic task.
- Add a call to
POU_1from , or attachPOU_1directly to the task. - Inside
POU_1, place theGRAPHcall in a CFC or ST body. Assign its result to a declaredREALvariable that feeds the PID setpoint input. - Walk the function body from top to bottom. Every branch must end in an assignment to the function name, and the final segment or clamp must be present.
- Fix the breakpoint comparisons as described above, or replace the function with the interpolating FB below.
- Build with zero errors, log in to the emulator, and run the verification sweep.
Two-point linear curve as the simpler alternative
A stepped table is not required. A straight line between two points works just as well: supply setpoint at a cold outdoor temperature and supply setpoint at a mild outdoor temperature, clamped beyond both ends. This mirrors how weather-compensation controllers such as the OWEN TRM32 define their heating curve. The line has no internal breakpoints, so the +10 °C class of bug cannot occur. Both approaches suited this installation equally well.
For more than two points, store breakpoints in arrays and interpolate the same way. Loop irOutdoor <= aOut[i+1]. Clamp below aOut[1] and above aOut[N]. An existing library FB, GRAPH_TEMP, implements this curve for CODESYS 2.3. It has been in field use combined with return-water overheat protection, and it can be redrawn in 3.5.
Where outdoor temperature belongs in the ventilation loop
The PID still closes on the supply-air or room sensor. Outdoor temperature never becomes the process variable. It plays two roles:
- Setpoint shift. The curve lowers or raises the supply setpoint with outdoor conditions. This limits heater-coil thermal load in mild weather and keeps it from overheating the supply stream.
- Mode selection. A threshold on outdoor temperature switches winter/summer operation. Add hysteresis to that comparison so the mode does not chatter near the threshold.
Hot-water heater suppliers often require return-water overheat protection. Implement it as a separate limit that overrides the PID when return-water temperature exceeds its own outdoor-dependent curve. Do not fold it into the supply setpoint. The supply curve and the return-water limit use the same interpolation block with different point sets.
Sweep test for the setpoint curve in emulation
- Force the outdoor-temperature input below the coldest breakpoint. The setpoint must equal the cold-end value.
- Step the input through every breakpoint value exactly, including +10 °C here, and one tenth of a degree either side. No reading may be 0, and no two adjacent readings may jump in the wrong direction.
- Force the input above the mildest breakpoint. The setpoint must equal the mild-end value.
- Ramp the input slowly across the full range and trend the setpoint. A linear curve gives a straight line between the clamps. A stepped curve gives flat plateaus with transitions only at the breakpoints.
- Confirm that the PID setpoint input carries the same value as the curve output. A mismatch means the wiring or assignment between them is wrong.
- With the loop simulated, confirm that the winter/summer switch and the return-water limit each take over at their thresholds without dropping the setpoint to 0.
FAQ
Can I call a CODESYS function from a ladder network without assigning its output?
It executes, but the result is discarded and nothing downstream sees a value. Place the call in CFC or ST, or wire the output to a declared variable such as the PID setpoint input.
It masks the fault, because the FB instance holds its previous output when no branch matches. The real fix is inclusive comparisons (<= on one side) plus a final ELSE clamp, so every outdoor temperature, +10 °C included, maps to exactly one segment.
Does the PID need the outdoor temperature as its process variable for weather compensation?
No. The PID controls on the supply-air or room sensor. Outdoor temperature only shifts the setpoint through the curve and drives the winter/summer switch, with return-water overheat protection as a separate override.
Escalate to CODESYS or your PLC vendor's official support if the sweep test passes in emulation but the target controller returns different setpoint values. Also escalate if a program attached to a task never executes, or if the project will not compile after migrating from 2.3 to 3.5. Include the runtime and compiler versions from the device information page and an export of the curve POU.