transitions between phases!
This commit is contained in:
parent
8ec2f57e15
commit
1270e487fa
@ -23,22 +23,27 @@ struct Phase {
|
||||
|
||||
struct Profile {
|
||||
const char* name;
|
||||
int transitionMinutesPerDegree;
|
||||
Phase phases[6]; // Maximum of 6 phases per profile
|
||||
int numPhases;
|
||||
};
|
||||
|
||||
// Profiles definition
|
||||
Profile profiles[] = {
|
||||
{"Test", {{49, 1}, {51, 1}}, 2},
|
||||
{"Пшеница", {{47, 40}, {55, 40}, {65, 20}, {72, 20}, {85, 20}}, 5},
|
||||
{"Veggies Sous Vide", {{85, 120}}, 1},
|
||||
{"Фитаза/Протеаза", {{47, 120}, {53, 120}, {65, 150}, {72, 60}, {90, 105}, {50, 60}}, 6},
|
||||
{"Test", 0, {{49, 1}, {51, 1}}, 2},
|
||||
{"Пшеница", 1, {{47, 40}, {55, 40}, {65, 20}, {72, 20}, {85, 20}}, 5},
|
||||
{"Veggies Sous Vide", 0, {{85, 120}}, 1},
|
||||
{"Фитаза/Протеаза", 2, {{47, 120}, {53, 120}, {65, 150}, {72, 60}, {90, 105}, {50, 60}}, 6},
|
||||
};
|
||||
|
||||
// Select active profile (constant at this point)
|
||||
const int activeProfileIndex = 0; // Index of the active profile, starting from 0
|
||||
Profile activeProfile = profiles[activeProfileIndex];
|
||||
|
||||
// Global variables for current phase, setpoint, and transition state
|
||||
int currentPhase;
|
||||
bool isInTransition = false;
|
||||
|
||||
// PID Control variables
|
||||
double Setpoint, Input, Output;
|
||||
double Kp = 2, Ki = 0.5, Kd = 0.25;
|
||||
@ -67,9 +72,10 @@ void setup() {
|
||||
oled.update();
|
||||
oled.setScale(1); // Set text scale back to 1 for more detailed information
|
||||
|
||||
calculateTotalTime();
|
||||
|
||||
// Begin the first phase
|
||||
phaseStartTime = totalStartTime = millis();
|
||||
Setpoint = activeProfile.phases[0].temperature;
|
||||
myPID.SetMode(AUTOMATIC);
|
||||
myPID.SetOutputLimits(0, 1); // SSR is either ON or OFF
|
||||
digitalWrite(ssrPin, HIGH); // Start with heater on
|
||||
@ -77,10 +83,12 @@ void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static int currentPhase = 0;
|
||||
Input = (int) thermocouple.readCelsius(); // Cast to integer for display and control
|
||||
unsigned long currentTime = millis();
|
||||
totalElapsedTime = (currentTime - totalStartTime) / 1000; // Total elapsed time in seconds
|
||||
|
||||
getPhaseAndTemperature(totalElapsedTime);
|
||||
|
||||
Input = (int) thermocouple.readCelsius(); // Cast to integer for display and control
|
||||
myPID.Compute();
|
||||
|
||||
// Switch SSR based on PID output and interval control
|
||||
@ -89,25 +97,8 @@ void loop() {
|
||||
ssrLastSwitchTime = currentTime;
|
||||
}
|
||||
|
||||
// Display time calculations
|
||||
totalElapsedTime = (currentTime - totalStartTime) / 1000; // Total elapsed time in seconds
|
||||
totalProcessTime = 0;
|
||||
for (int i = 0; i < activeProfile.numPhases; i++) {
|
||||
totalProcessTime += activeProfile.phases[i].duration * 60;
|
||||
}
|
||||
|
||||
// Check if the phase duration is complete
|
||||
if (!isComplete && (unsigned long)((currentTime - phaseStartTime) / 1000) >= (unsigned long)activeProfile.phases[currentPhase].duration * 60) {
|
||||
currentPhase++;
|
||||
if (currentPhase >= activeProfile.numPhases) {
|
||||
isComplete = true;
|
||||
if (isComplete && currentPhase >= activeProfile.numPhases) {
|
||||
finishTime = currentTime;
|
||||
Setpoint = 45; // Set target temperature to 45°C
|
||||
digitalWrite(ssrPin, LOW); // Turn off the heater
|
||||
} else {
|
||||
phaseStartTime = currentTime; // Reset the start time for the new phase
|
||||
Setpoint = activeProfile.phases[currentPhase].temperature;
|
||||
}
|
||||
}
|
||||
|
||||
// Display all phases and highlight the current one
|
||||
@ -118,6 +109,60 @@ void loop() {
|
||||
delay(1000); // Update every second
|
||||
}
|
||||
|
||||
void calculateTotalTime() {
|
||||
// Calculate total process time, including transitions
|
||||
totalProcessTime = 0;
|
||||
for (int i = 0; i < activeProfile.numPhases; i++) {
|
||||
totalProcessTime += activeProfile.phases[i].duration * 60;
|
||||
if (i < activeProfile.numPhases - 1) {
|
||||
int tempDiff = abs(activeProfile.phases[i + 1].temperature - activeProfile.phases[i].temperature);
|
||||
totalProcessTime += tempDiff * 60 * activeProfile.transitionMinutesPerDegree;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getPhaseAndTemperature(unsigned long elapsedSeconds) {
|
||||
unsigned long accumulatedTime = 0;
|
||||
|
||||
for (int i = 0; i < activeProfile.numPhases; i++) {
|
||||
int phaseDuration = activeProfile.phases[i].duration * 60;
|
||||
|
||||
// Check for transition
|
||||
if (i > 0) {
|
||||
int previousTemp = activeProfile.phases[i - 1].temperature;
|
||||
int targetTemp = activeProfile.phases[i].temperature;
|
||||
int tempDiff = abs(targetTemp - previousTemp);
|
||||
int transitionDuration = tempDiff * 60 * activeProfile.transitionMinutesPerDegree;
|
||||
|
||||
if (elapsedSeconds < accumulatedTime + transitionDuration) {
|
||||
isInTransition = true;
|
||||
currentPhase = i;
|
||||
int timeInTransition = elapsedSeconds - accumulatedTime;
|
||||
Setpoint = previousTemp + ((targetTemp > previousTemp ? (double)timeInTransition / 60 : -(double)timeInTransition / 60) / (double)activeProfile.transitionMinutesPerDegree);
|
||||
return;
|
||||
}
|
||||
|
||||
accumulatedTime += transitionDuration;
|
||||
}
|
||||
|
||||
// Check if we're within the current phase
|
||||
if (elapsedSeconds < accumulatedTime + phaseDuration) {
|
||||
isInTransition = false;
|
||||
currentPhase = i;
|
||||
Setpoint = activeProfile.phases[i].temperature;
|
||||
return;
|
||||
}
|
||||
|
||||
accumulatedTime += phaseDuration;
|
||||
}
|
||||
|
||||
// If the elapsed time exceeds the total duration, indicate completion
|
||||
currentPhase = activeProfile.numPhases; // Indicate completion
|
||||
Setpoint = 45; // Default to 45°C after completion
|
||||
isInTransition = false;
|
||||
isComplete = true; // Mark the process as complete
|
||||
}
|
||||
|
||||
void printPhases(int currentPhase, unsigned long phaseElapsedTime, unsigned long currentTime) {
|
||||
oled.clear();
|
||||
|
||||
@ -126,12 +171,13 @@ void printPhases(int currentPhase, unsigned long phaseElapsedTime, unsigned long
|
||||
oled.setCursor(0, 0);
|
||||
oled.invertText(true);
|
||||
oled.print("Done!");
|
||||
oled.invertText(false);
|
||||
oled.print(" ");
|
||||
formatTime((currentTime - finishTime) / 1000, timeBuffer); // Time since completion
|
||||
oled.print(timeBuffer);
|
||||
oled.print(" ");
|
||||
oled.print((int)Input);
|
||||
oled.print("c");
|
||||
oled.invertText(false);
|
||||
} else {
|
||||
oled.setCursor(0, 0);
|
||||
oled.print(activeProfile.name);
|
||||
@ -156,7 +202,11 @@ void printPhases(int currentPhase, unsigned long phaseElapsedTime, unsigned long
|
||||
oled.print(". ");
|
||||
oled.print((int)Input);
|
||||
oled.print("c ");
|
||||
oled.print((int)Setpoint);
|
||||
if (fabs(Setpoint - round(Setpoint)) < 0.05) {
|
||||
oled.print((int)Setpoint); // Print without decimals
|
||||
} else {
|
||||
oled.print(Setpoint, 1); // Print with 1 decimal place
|
||||
}
|
||||
oled.print("c ");
|
||||
oled.print(timeBuffer);
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user