transitions between phases!

This commit is contained in:
Alexander Belov 2024-08-30 02:45:48 +07:00
parent 8ec2f57e15
commit 1270e487fa

View File

@ -23,22 +23,27 @@ struct Phase {
struct Profile { struct Profile {
const char* name; const char* name;
int transitionMinutesPerDegree;
Phase phases[6]; // Maximum of 6 phases per profile Phase phases[6]; // Maximum of 6 phases per profile
int numPhases; int numPhases;
}; };
// Profiles definition // Profiles definition
Profile profiles[] = { Profile profiles[] = {
{"Test", {{49, 1}, {51, 1}}, 2}, {"Test", 0, {{49, 1}, {51, 1}}, 2},
{"Пшеница", {{47, 40}, {55, 40}, {65, 20}, {72, 20}, {85, 20}}, 5}, {"Пшеница", 1, {{47, 40}, {55, 40}, {65, 20}, {72, 20}, {85, 20}}, 5},
{"Veggies Sous Vide", {{85, 120}}, 1}, {"Veggies Sous Vide", 0, {{85, 120}}, 1},
{"Фитаза/Протеаза", {{47, 120}, {53, 120}, {65, 150}, {72, 60}, {90, 105}, {50, 60}}, 6}, {"Фитаза/Протеаза", 2, {{47, 120}, {53, 120}, {65, 150}, {72, 60}, {90, 105}, {50, 60}}, 6},
}; };
// Select active profile (constant at this point) // Select active profile (constant at this point)
const int activeProfileIndex = 0; // Index of the active profile, starting from 0 const int activeProfileIndex = 0; // Index of the active profile, starting from 0
Profile activeProfile = profiles[activeProfileIndex]; Profile activeProfile = profiles[activeProfileIndex];
// Global variables for current phase, setpoint, and transition state
int currentPhase;
bool isInTransition = false;
// PID Control variables // PID Control variables
double Setpoint, Input, Output; double Setpoint, Input, Output;
double Kp = 2, Ki = 0.5, Kd = 0.25; double Kp = 2, Ki = 0.5, Kd = 0.25;
@ -67,9 +72,10 @@ void setup() {
oled.update(); oled.update();
oled.setScale(1); // Set text scale back to 1 for more detailed information oled.setScale(1); // Set text scale back to 1 for more detailed information
calculateTotalTime();
// Begin the first phase // Begin the first phase
phaseStartTime = totalStartTime = millis(); phaseStartTime = totalStartTime = millis();
Setpoint = activeProfile.phases[0].temperature;
myPID.SetMode(AUTOMATIC); myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(0, 1); // SSR is either ON or OFF myPID.SetOutputLimits(0, 1); // SSR is either ON or OFF
digitalWrite(ssrPin, HIGH); // Start with heater on digitalWrite(ssrPin, HIGH); // Start with heater on
@ -77,37 +83,22 @@ void setup() {
} }
void loop() { void loop() {
static int currentPhase = 0;
Input = (int) thermocouple.readCelsius(); // Cast to integer for display and control
unsigned long currentTime = millis(); 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(); myPID.Compute();
// Switch SSR based on PID output and interval control // Switch SSR based on PID output and interval control
if (currentTime - ssrLastSwitchTime > ssrSwitchInterval) { if (currentTime - ssrLastSwitchTime > ssrSwitchInterval) {
digitalWrite(ssrPin, Output > 0.5 ? HIGH : LOW); digitalWrite(ssrPin, Output > 0.5 ? HIGH : LOW);
ssrLastSwitchTime = currentTime; ssrLastSwitchTime = currentTime;
} }
// Display time calculations if (isComplete && currentPhase >= activeProfile.numPhases) {
totalElapsedTime = (currentTime - totalStartTime) / 1000; // Total elapsed time in seconds finishTime = currentTime;
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;
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 // Display all phases and highlight the current one
@ -118,6 +109,60 @@ void loop() {
delay(1000); // Update every second 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) { void printPhases(int currentPhase, unsigned long phaseElapsedTime, unsigned long currentTime) {
oled.clear(); oled.clear();
@ -125,13 +170,14 @@ void printPhases(int currentPhase, unsigned long phaseElapsedTime, unsigned long
// Show completion time and current temperature instead of the title // Show completion time and current temperature instead of the title
oled.setCursor(0, 0); oled.setCursor(0, 0);
oled.invertText(true); oled.invertText(true);
oled.print("Done! "); oled.print("Done!");
oled.invertText(false);
oled.print(" ");
formatTime((currentTime - finishTime) / 1000, timeBuffer); // Time since completion formatTime((currentTime - finishTime) / 1000, timeBuffer); // Time since completion
oled.print(timeBuffer); oled.print(timeBuffer);
oled.print(" "); oled.print(" ");
oled.print((int)Input); oled.print((int)Input);
oled.print("c"); oled.print("c");
oled.invertText(false);
} else { } else {
oled.setCursor(0, 0); oled.setCursor(0, 0);
oled.print(activeProfile.name); oled.print(activeProfile.name);
@ -156,7 +202,11 @@ void printPhases(int currentPhase, unsigned long phaseElapsedTime, unsigned long
oled.print(". "); oled.print(". ");
oled.print((int)Input); oled.print((int)Input);
oled.print("c "); 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("c ");
oled.print(timeBuffer); oled.print(timeBuffer);
} else { } else {