First of all, great library and thank you for the effort.
I'm using an ESP32 Arduino.
I have an implementation where a user can create any number of thermSystems
which is represented as a class for me.
In every thermSystem
there is it's own set
and measured
temperature coming from different sensors.
For every thermSystem
there is only one boiler
with it's own input which controlls if it has to start to heat or not.
For me, the esp can not directly set a pin to high or low, so i modified your library a little bit in the following ways:
sTune.h
float softDigit(boolean &heatControllBit, float input, float output, float setpoint, uint32_t windowSize, uint8_t debounce);
sTune.cpp
float sTune::softDigit(boolean &heatControllBit, float input, float output, float setpoint, uint32_t windowSize, uint8_t debounce) {
// software PWM timer
uint32_t msNow = millis();
static uint32_t windowStartTime, nextSwitchTime;
if (msNow - windowStartTime >= windowSize) {
windowStartTime = msNow;
}
// SSR optimum AC half-cycle controller
static float optimumOutput;
static bool reachedSetpoint;
if (input > setpoint) reachedSetpoint = true;
if (reachedSetpoint && !debounce && setpoint > 0 && input > setpoint) optimumOutput = output - 8;
else if (reachedSetpoint && !debounce && setpoint > 0 && input < setpoint) optimumOutput = output + 8;
else optimumOutput = output;
if (optimumOutput < 0) optimumOutput = 0;
// PWM relay output
static bool relayStatus;
if (!relayStatus && optimumOutput > (msNow - windowStartTime)) {
if (msNow > nextSwitchTime) {
nextSwitchTime = msNow + debounce;
relayStatus = true;
heatControllBit = true;
}
} else if (relayStatus && optimumOutput < (msNow - windowStartTime)) {
if (msNow > nextSwitchTime) {
nextSwitchTime = msNow + debounce;
relayStatus = false;
heatControllBit = false;
}
}
return optimumOutput;
}
As you can see i have added a modification to the softpwm function to be able to set a boolean
variable by value
instead of a digitalWrite
. This is to ensure that no pin is changed in my setup and controlls only at software level.
So with that in mind, i have the following controll functions for the auto tune PID.
/* PID DETAILS */
const uint8_t inputPin = 0;
uint32_t settleTimeSec = 10;
uint32_t testTimeSec = 1000;
const uint16_t samples = 500;
const float inputSpan = 150;
const float outputSpan = 1000;
float outputStart = 0;
float outputStep = 300;
float tempLimit = 350;
// variables
float Input, Output, Setpoint = 50, Kp, Ki, Kd;
sTune *tuner;
QuickPID *myPID;
void pidInit();
void pidLoop();
/* PID DETAILS */
/*
* This function is called at startup of the thermSystem
*/
void thermSystem::pidInit() {
tuner = new sTune(&Input, &Output, tuner->ZN_PID, tuner->directIP, tuner->printOFF);
myPID = new QuickPID(&Input, &Output, &Setpoint);
tuner->Configure(inputSpan, outputSpan, outputStart, outputStep, testTimeSec, settleTimeSec, samples);
tuner->SetEmergencyStop(tempLimit);
}
/*
* This function is called in every iteration of the loop()
*/
void thermSystem::pidLoop() {
tuner->softDigit(shouldStartHeat, Input, Output, 0, outputSpan, 1);
switch (tuner->Run()) {
case tuner->sample: // active once per sample during test
Input = currentTemp;
//Input = analogRead(inputPin) * 0.322265625 - 50.0; // get degC (using 3.3v AREF)
tuner->plotter(Input, Output * 0.1, Setpoint, 1, 3);
break;
case tuner->tunings: // active just once when sTune is done
tuner->GetAutoTunings(&Kp, &Ki, &Kd); // sketch variables updated by sTune
myPID->SetOutputLimits(0, outputSpan);
myPID->SetSampleTimeUs(outputSpan * 1000 - 1);
myPID->SetMode(myPID->Control::automatic); // the PID is turned on
myPID->SetProportionalMode(myPID->pMode::pOnMeas);
myPID->SetAntiWindupMode(myPID->iAwMode::iAwClamp);
myPID->SetTunings(Kp, Ki, Kd); // update PID with the new tunings
break;
case tuner->runPid: // active once per sample after tunings
//Input = analogRead(inputPin) * 0.322265625 - 50.0; // get degC (using 3.3v AREF)
Input = currentTemp;
myPID->Compute();
tuner->plotter(Input, Output, Setpoint, 0.1f, 3);
break;
}
}
I have the following output on the serial monitor:
Setpoint:150.00, Input:197.00, Output:nan,
Setpoint:225.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:230.00, Input:0.00, Output:nan,
Setpoint:150.00, Input:198.00, Output:nan,
Setpoint:225.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:230.00, Input:0.00, Output:nan,
Setpoint:150.00, Input:198.00, Output:nan,
Setpoint:225.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:230.00, Input:0.00, Output:nan,
Setpoint:150.00, Input:199.00, Output:nan,
Setpoint:225.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:230.00, Input:0.00, Output:nan,
Setpoint:150.00, Input:199.00, Output:nan,
Setpoint:225.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:210.00, Input:0.00, Output:nan,
Setpoint:230.00, Input:0.00, Output:nan,
Setpoint:150.00, Input:199.00, Output:nan,
Setpoint:225.00, Input:0.00, Output:nan,
And my heating can not turn off.
It is always on but the current set temperature is 15.5 celsius which is represented as 155 decimal and the current temperature is 20 celsius which is 200 decimal.
current temperature value is coming in Serial modbus communication which sample rate is roughly equal to the loop iteration in my setup.
What could be the problem?
EDIT
If i restart the system with 155 set temperature and 200 measured temperature the pid is turning on and off my boolean variable for a while, even if the set temperature never exceeds the measured.
At the start of the system it looks like this:
Setpoint:225.00, Input:0.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:230.00, Input:0.00, Output:30.00,
Setpoint:150.00, Input:202.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:225.00, Input:0.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:230.00, Input:0.00, Output:30.00,
Setpoint:150.00, Input:202.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:225.00, Input:0.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:230.00, Input:0.00, Output:30.00,
Setpoint:150.00, Input:202.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:225.00, Input:0.00, Output:41.23,
Setpoint:210.00, Input:0.00, Output:40.47,
Setpoint:230.00, Input:0.00, Output:30.00,
Setpoint:150.00, Input:202.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:225.00, Input:0.00, Output:58.07,
Setpoint:210.00, Input:0.00, Output:56.18,
Setpoint:230.00, Input:0.00, Output:30.00,
Setpoint:150.00, Input:202.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:225.00, Input:0.00, Output:74.91,
Setpoint:210.00, Input:0.00, Output:71.89,
Setpoint:230.00, Input:0.00, Output:30.00,
Setpoint:150.00, Input:202.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:225.00, Input:0.00, Output:91.75,
Setpoint:210.00, Input:0.00, Output:87.60,
Setpoint:230.00, Input:0.00, Output:30.00,
Setpoint:150.00, Input:202.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:225.00, Input:0.00, Output:100.00,
Setpoint:210.00, Input:0.00, Output:100.00,
Setpoint:230.00, Input:0.00, Output:30.00,
Setpoint:150.00, Input:202.00, Output:30.00,
Setpoint:210.00, Input:0.00, Output:30.00,
Setpoint:225.00, Input:0.00, Output:100.00,
Setpoint:210.00, Input:0.00, Output:100.00,
Setpoint:230.00, Input:0.00, Output:30.00,
Also there is valve open time
which i have to wait before i start the boiler. I don't know how to integrate it into the PID, since it can vari between 1 min to 5 or 10 min while the valve is opening.