FWIW here is the code from one of the PID loops in use in the One Touch application
private static double pVWa; // process variable
private static double kPWa; // Proportional
private static double kIWa; // Integral
private static double kDWa; // Derivative
private static double kLWa; // Integral limit to control windup
private static double kGWa; // Integral gain factor for loops with small process variable value range
private static double kCWa; // Time constant for recalculation of PID loop
private static double setpointWa; // Loop setpoint
private static double pGainWa; // internal proportional gain varaible
private static double intGainWa; // internal integral gain variable
private static double difGainWa; // internal differential gain variable
private static double errorWa; // internal setpoint error variable
private static double preverrorWa = 0; // internal error value used for derivative
private static double errorSumWa = 0; // internal error value used for integral calc
private static double rampWa; // internal variable used for ramping output
private static double rampWadf; // internal ramping variable
private static int sRWa; // calculation cycle delay variable
if (l1 < 10)
{
l1++;
}
if (l1 == 1)
{
kPWa = 0;
kIWa = 0;
kDWa = 0;
kLWa = 0;
kGWa = 0;
kCWa = 0;
pGainWa = 0;
intGainWa = 0;
difGainWa = 0;
errorSumWa = 0;
pidOutWa = 0;
}
if (l1 == 2)
{ // set valve to minimum opening in preperation for use
DataObject.boilerFlowValve = ((int)(SettingsObject.boilerValveMinimum * 40.95));
// ramping variable set
rampWadf = SettingsObject.waterSetpoint - .13;
}
if (l1 == 3)
{
if (SettingsObject.waterSetpoint >= .4)
{
// passing stored optimized variables setup for high flow use
// variables set in PID Tune application and stored in MySQL table
// and accessed through SettingsObject variables
kPWa = SettingsObject.water_FlowH_P;
kIWa = SettingsObject.water_FlowH_I;
kDWa = SettingsObject.water_FlowH_D;
kLWa = SettingsObject.water_FlowH_L;
kGWa = SettingsObject.water_FlowH_G;
kCWa = SettingsObject.water_FlowH_C;
}
if (SettingsObject.waterSetpoint <= .3)
{
// passing stored optimized variables setup for low flow use
// variables set in PID Tune application and stored in MySQL table
// and accessed through SettingsObject variables
kPWa = SettingsObject.water_FlowL_P;
kIWa = SettingsObject.water_FlowL_I;
kDWa = SettingsObject.water_FlowL_D;
kLWa = SettingsObject.water_FlowL_L;
kGWa = SettingsObject.water_FlowL_G;
kCWa = SettingsObject.water_FlowL_C;
}
}
// this sets the internal setpoint variable for the loop
// kept outside presets so it can be adjusted on the fly
setpointWa = SettingsObject.waterSetpoint;
// this passes the process variable to the loop calculation
pVWa = DataObject.boilerFlow;
//this increments the scan cycle variable based on the value of kCWa passed to PID Loop
if (sRWa < (int)(kCWa * 10))
{
sRWa++;
}
else
{ // this is the calculation scan test
if (sRWa >= (int)(kCWa * 10))
{
scanWa = true;
}
}
if (scanWa == true)
{
// this is used in other loops for ramping output limit
rampWa = 1;
errorWa = (setpointWa - pVWa);
errorSumWa += ((errorWa * kGWa) * rampWa);
pGainWa = kPWa * errorWa;
if (errorSumWa > (kLWa))
{
errorSumWa = (kLWa);
}
if (errorSumWa < (-kLWa))
{
errorSumWa = (-kLWa);
}
intGainWa = kIWa * errorSumWa;
difGainWa = kDWa * (errorWa - preverrorWa);
preverrorWa = errorWa;
// this is where output value is set
pidOutWa = SettingsObject.boilerValveMinimum + (pGainWa + intGainWa + difGainWa);
scanWa = false;
sRWa = 0;
}
if (pidOutWa > SettingsObject.boilerValveMaximum)
{
//check step to make sure it is within limits, fix if not
pidOutWa = SettingsObject.boilerValveMaximum;
}
if (pidOutWa < SettingsObject.boilerValveMinimum)
{
//check step to make sure it is within limits, fix if not
pidOutWa = SettingsObject.boilerValveMinimum;
}
//set output from 0 - 4096 for Opto22 output , scale to output value needed
DataObject.boilerFlowValve = ((int)(pidOutWa * 40.95));