/***************************************************************************
sTune QuickPID relay control with sevseg display and programmable setpoint
This sketch does on-the-fly tuning and PID control. Tuning parameters are
quickly determined and applied during temperature ramp-up to setpoint.
View results using serial plotter.
Reference: https://github.com/Dlloydev/sTune/wiki/Examples_MAX6675_PTC_SSR
***************************************************************************/
#include <sTune.h>
#include "SevSeg.h"
#include <ezButton.h>
#include <M2M_LM75A.h>
#include <EEPROM.h>
#include <sTune.h>
#include <QuickPID.h>
class FakeTemp {
private:
float temp;
public:
FakeTemp(const float initialTemp) {
temp = initialTemp;
}
float operator+=(const float addend) {
return temp += addend;
}
float operator-=(const float subtrahend) {
return temp -= subtrahend;
}
float readCelsius() {
return temp;
}
};
FakeTemp module = FakeTemp(10);
// pins
const uint8_t inputPin = 0;
const uint8_t relayPin = 14;
// pid settings
uint32_t settleTimeSec = 2;
uint32_t testTimeSec = 60; // runPid interval = testTimeSec / samples
const uint16_t samples = 200;
const float inputSpan = 15;
const float outputSpan = 2000;
float outputStart = 0;
float outputStep = 2000;
float tempLimit = 29.0f;
uint8_t debounce = 1;
// variables
float Input = 10.0f, Output, Setpoint, Kp, Ki, Kd;
sTune tuner = sTune(&Input, &Output, tuner.Mixed_PID, tuner.directIP, tuner.printSUMMARY);
QuickPID myPID(&Input, &Output, &Setpoint);
ezButton upButton(A2, INPUT_PULLUP), downButton(A1, INPUT_PULLUP);
// display
SevSeg sevseg;
unsigned long inputSettingTimeout;
float getInitialSetpoint() {
//TODO: read setpoint from EEPROM
return 25.0f;
}
void setup() {
//PID
pinMode(relayPin, OUTPUT);
Serial.begin(115200);
Output = 0;
tuner.Configure(inputSpan, outputSpan, outputStart, outputStep, testTimeSec, settleTimeSec, samples);
tuner.SetEmergencyStop(tempLimit);
Setpoint = getInitialSetpoint();
//display,
byte numDigits = 4;
byte digitPins[] = {10, 11, 12, 13};
byte segmentPins[] = {2, 3, 4, 5, 6, 7, 8, 9};
bool resistorsOnSegments = false; // 'false' means resistors are on digit pins
byte hardwareConfig = COMMON_CATHODE; // See README.md for options
bool updateWithDelays = false; // Default 'false' is Recommended
bool leadingZeros = false; // Use 'true' if you'd like to keep the leading zeros
bool disableDecPoint = false; // Use 'true' if your decimal point doesn't exist or isn't connected
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments,
updateWithDelays, leadingZeros, disableDecPoint);
sevseg.setBrightness(50);
upButton.setDebounceTime(50);
downButton.setDebounceTime(50);
}
void loop() {
float optimumOutput = tuner.softPwm(relayPin, Input, Output, Setpoint, outputSpan, debounce);
Output = optimumOutput;
Input = module.readCelsius();
switch (tuner.Run()) {
case tuner.sample: // active once per sample during test
tuner.plotter(Input, Output, Setpoint, 0.05f, 3); // output scale 0.5, plot every 3rd sample
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 - 1) * 500);
debounce = 0; // ssr mode
Output = outputStep;
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
myPID.Compute();
tuner.plotter(Input, Output, Setpoint, 0.05f, 3);
break;
}
//simulate temp change
if (digitalRead(relayPin) == HIGH) {
module += 0.0001f;
} else {
module -= 0.00005f;
}
upButton.loop();
downButton.loop();
unsigned long now = millis();
if ((upButton.isPressed() || downButton.isPressed())) {
inputSettingTimeout = now + 4000UL;
if (upButton.isPressed()) {
Setpoint += 0.5f;
} else if (downButton.isPressed()) {
Setpoint -= 0.5f;
}
}
if (now > inputSettingTimeout) {
if (inputSettingTimeout > 0) {
//EEPROM.put(SETPOINT_EEPROM_ADDRESS, Setpoint);
}
inputSettingTimeout = 0;
sevseg.setNumberF(Input, 2);
} else {
if (now / 200 % 2 == 0) {
sevseg.blank();
} else {
sevseg.setNumberF(Setpoint, 2);
}
}
sevseg.refreshDisplay();
}