help for PID algorithm in C!


Thread Starter

B. George

I need an algorithm, schematics of PID algorithm (C code) for temperature control.
thank you!
Your question is about equivalent to:
I need going to the moon.
Temperature control may become very complex.
Depending upon the process configuration, the type of black box that will do it, is there feedforward, cascade ...
This browser does not allow for attaching drawing, neither document.
How can we help each other ?
The following is a pretty basic C PID controller, but it has worked very well with good stability when tuned correctly. The code doesn't have alot of features and uses a independant gains equation, but you can dress it up or down as much as you want. I just pasted it from another application, adapt as needed. Good Luck.

struct _pid {
int *pv; /*pointer to an integer that contains the process value*/
int *sp; /*pointer to an integer that contains the set point*/
float integral;
float pgain;
float igain;
float dgain;
int deadband;
int last_error;


DESCRIPTION This function initializes the pointers in the _pid structure
to the process variable and the setpoint. *pv and *sp are
integer pointers.
void pid_init(a, pv, sp)
struct _pid *a;
int *pv, *sp;
a->pv = pv;
a->sp = sp;


DESCRIPTION Sets the proportional gain (p_gain), integral gain (i_gain),
derivitive gain (d_gain), and the dead band (dead_band) of
a pid control structure _pid.
void pid_tune(a, p_gain, i_gain, d_gain, dead_band)
struct _pid *a;
float p_gain, i_gain, d_gain;
int dead_band;
a->pgain = p_gain;
a->igain = i_gain;
a->dgain = d_gain;
a->deadband = dead_band;


DESCRIPTION Returns the gains and dead band in a _pid control structure
in the locations pointed to by the p_gain, i_gain, d_gain,
and dead_band pointers.

ALSO SEE pid_tune
void get_gains(a, p_gain, i_gain, d_gain, dead_band)
struct _pid *a;
float *p_gain, *i_gain, *d_gain;
int *dead_band;
*p_gain = a->pgain;
*i_gain = a->igain;
*d_gain = a->dgain;
*dead_band = a->deadband;


DESCRIPTION Set a new value for the integral term of the pid equation.
This is useful for setting the initial output of the
pid controller at start up.
void pid_setinteg(a,new_integ)
struct _pid *a;
float new_integ;


DESCRIPTION Bumpless transfer algorithim. When suddenly changing
setpoints, or when restarting the PID equation after an
extended pause, the derivative of the equation can cause
a bump in the controller output. This function will help
smooth out that bump. The process value in *pv should
be the updated just before this function is used.
void pid_bumpless(a)
struct _pid *a;
a->last_error = *(a->sp) - *(a->pv);


DESCRIPTION Performs PID calculations for the _pid structure *a. This
function uses the positional form of the pid equation, and
incorporates an integral windup prevention algorithim.
Rectangular integration is used, so this function must be
repeated on a consistent time basis for accurate control.

RETURN VALUE The new output value for the pid loop.

USAGE #include "control.h"
main() {
struct _pid PID;
int process_variable, set_point;
pid_init(&PID, &process_variable, &set_point);
pid_tune(&PID, 4.3, 0.2, 0.1, 2);
set_point = 500;
process_varialbe = read_temp();
for(;;) {
process_variable = read_temp();
output( pid_calc(&PID) );
float pid_calc(a)
struct _pid *a;
int err;
float pterm, dterm, result, ferror;
err = *(a->sp) - *(a->pv);
if (abs(err) > a->deadband) {
ferror = (float) err; /*do integer to float conversion only once*/
pterm = a->pgain * ferror;
if (pterm > 100 || pterm < -100)
a->integral = 0.0;
else {
a->integral += a->igain * ferror;
if (a->integral > 100.0) a->integral=100.0;
else if (a->integral < 0.0) a->integral=0.0;
dterm = (err - a->last_error) * a->dgain;
result = pterm + a->integral + dterm;
else result = a->integral;
return (result > 100.0 ? 100.0 : (result < 0.0 ? 0.0 : result));

Curt Wuollet

The world and I thank you for sharing your code. I hope the person who
asked for it does as well.


I thank you as well. Even though I see no imediate need, I saved a copy when I saw how well written it was. Now all we need is a routine to self tune it!

Larry Seib

Johan Bengtsson

Some things should really be adjusted to get a better PID

1. The igain and dgain should be calculated as follows:
This helps tuning the PID a lot, at least for those used to tuning
PID controllers.
timeStep is the time between calls to pid_calc in the same time unit
as iTime and dTime is entered, if the scan time can not be held
constant this have to be adjusted for each scan

2. For better D action this part should be filtered separately,
normally this is done by passing the dterm thru a first order low
pass filter with a time constant equal to dTime/leadQuote where
leadQuoute is a number around 5 to 15, a common value is 9
This heavily reduces the noice in the ouput if the D-part is used

/Johan Bengtsson

P&L, Innovation in training
Box 252, S-281 23 H{ssleholm SWEDEN
Tel: +46 451 49 460, Fax: +46 451 89 833
E-mail: [email protected]
(1) For a PID loop (including PID controller design and filter design) design please take a look at: Although the source code was written in Matlab and the PID loop was simulated using Simulink, it is easy to transfer them to C either manually or by means of a compiler from MathWorks Inc.

(2) I believe that the filter is a necessary part of most control loops, e.g., PID loops and MPC or DMC loops. But the design of a controller and
the design of a filter should be separated, although they can be put into one piece of program or even inside one function. The major role of the filter is to reduce the noise in the signal, usually the PV. Please note that the side effect of a filter is that it will canuse an additional delay to the signal PV. This will increase the response time of the controller to the setpoint change. This trade-off should be take into account when design a filter.

(3) In a PID controller, the D-term is most sensitive to the noise, the P-term is the second, and the I-term is the last. **However** this does
not mean that only D-term need to use the filtered PV. It is often better to also use the filtered PV in the P-term and I-term, not only just in the D-term.

You can confirm what I said by trying the PID loop simulation software at Since this is an open source code, you can make any changes to it, e.g., increase the variance of the
noise, change the time constant for the filter or use a different filter, or even only let D-term use the filtered PV and let P-term and I-term use
the raw PV (i.e., unfiltered PV).

And then share your experience with us.


This is a great peace of code (thank You very much)and works very well - but i do need some help - i would like to use this for a linear motion control project when using this code the motor control works very well when going in a negitave direction ie. goes fast then slows down and then come to a stop very nicely - when going in the other direction it does about the same but will over shoot and then come back to where it needs to be. does any body know what needs to be modifyed in this code to get the same type of movement in both directions.
Thank You,

Johan Bengtsson

I might not have expressed myself clear enough.

PV should normally be filtered (this is most effectively, however, done by analog circuits (ie RC, RL or even LC networks) before the signal is sampled this is to prevent aliasing), this can then be combined with digital filters to further
reduce noice if decired.

What I was talking about was that the D-part should have yet one filter, only affecting that part. This does not remove the need of a filter on PV before the controller. What this filter effectively do is to limit high frequency parts in the PV to pass the D part with an amplitude more than leadQuote times the amplitude passed thru the P part.

/Johan Bengtsson

P&L, Innovation in training
Box 252, S-281 23 H{ssleholm SWEDEN
Tel: +46 451 49 460, Fax: +46 451 89 833
E-mail: [email protected]
Can this code be used in a program where i would be able to change the values of P,I and D when required, if so what changes are required,
also i am having problems running this code, I would be delighted if someone could please help me with these problems thanks!!!!!
T. Connolly,

I should have posted this awhile ago - but I've used your code as a basis for a couple(so far) of real-world PID applications and it has worked out quite well.

This light-weight structure (so to speak) works well on even small embedded processors, so it's a great starting point for just about any 'size' of project, big or small that needs a PID. And, rectangular integration is OK for a fairly large family of applications.

Thanks - and kudos to you!
John W.
San Jose, CA