switch to percentages and reflow refactor.

This commit is contained in:
Andrea Baccega 2024-01-28 14:37:38 +01:00
parent cb93f600b8
commit c5576140b2
4 changed files with 65 additions and 297 deletions

View File

@ -35,7 +35,6 @@ void TFT_Display::init(ReflowProfile *profile)
tft.setCursor(xy.x, xy.y); tft.setCursor(xy.x, xy.y);
tft.println(profile->name); tft.println(profile->name);
getMaxTempFromProfile(profile); getMaxTempFromProfile(profile);
getTotalTimeFromProfile(profile);
this->profile = profile; this->profile = profile;
@ -47,7 +46,7 @@ void TFT_Display::init(ReflowProfile *profile)
drawGraph(); drawGraph();
} }
void TFT_Display::drawRealTemp(double *temp, float time) void TFT_Display::drawRealTemp(double *temp, float percentage)
{ {
uint32_t timeSinceLastDraw = drawTimer.elapsed(); uint32_t timeSinceLastDraw = drawTimer.elapsed();
@ -57,11 +56,9 @@ void TFT_Display::drawRealTemp(double *temp, float time)
if (timeSinceLastDraw >= 930) if (timeSinceLastDraw >= 930)
{ {
float timeElapsed = time;
float temperature = static_cast<float>(*temp); float temperature = static_cast<float>(*temp);
time = time / 1000;
// Serial.print("Time:"); // Serial.print("Time:");
// Serial.print(time); // Serial.print(time);
// Serial.print(" Temp:"); // Serial.print(" Temp:");
@ -71,7 +68,7 @@ void TFT_Display::drawRealTemp(double *temp, float time)
// Get the xy and y pixel of the temp // Get the xy and y pixel of the temp
uint16_t x = timeXonGraph(time); uint16_t x = percentageToX(percentage);
uint16_t y = tempYonGraph(temperature); uint16_t y = tempYonGraph(temperature);
// Draw the pixel // Draw the pixel
@ -84,9 +81,7 @@ void TFT_Display::drawRealTemp(double *temp, float time)
drawTimer.reset(); drawTimer.reset();
drawTimer.start(); drawTimer.start();
} }
else
{
};
} }
void TFT_Display::clear() void TFT_Display::clear()
@ -174,9 +169,9 @@ void TFT_Display::getMaxTempFromProfile(ReflowProfile *profile)
for (uint8_t i = 0; i < 5; i++) for (uint8_t i = 0; i < 5; i++)
{ {
if (profile->steps[i].targetTempAtEnd > maxTemp) if (profile->steps[i].targetTemp > maxTemp)
{ {
maxTemp = profile->steps[i].targetTempAtEnd; maxTemp = profile->steps[i].targetTemp;
} }
} }
@ -201,31 +196,6 @@ void TFT_Display::getMaxTempFromProfile(ReflowProfile *profile)
} }
} }
void TFT_Display::getTotalTimeFromProfile(ReflowProfile *profile)
{
totalTIme = 0;
for (uint8_t i = 0; i < 5; i++)
{
totalTIme += profile->steps[i].duration;
}
Serial.println("Total time");
Serial.println(String(totalTIme));
}
TFT_XY TFT_Display::getXYWithinGraphBounds(uint8_t temp, uint8_t time)
{
TFT_XY xy;
xy.x = graphXY.x + (graphWidth * (time / totalTIme));
xy.y = graphXY.y - (graphHeight * ((temp - minTemp) / (maxTemp - minTemp)));
return xy;
}
void TFT_Display::drawGraph() void TFT_Display::drawGraph()
{ {
@ -328,16 +298,17 @@ void TFT_Display::drawGraphAxisTickLabels()
tft.println("0"); tft.println("0");
// Always ends at totalTime // Always ends at totalTime
char *totalTimeCharPtr = numberToCharPtr(totalTIme); uint16_t totalTime = profile->endTimes[4];
char *totalTimeCharPtr = numberToCharPtr(totalTime);
position = getCenterAlignedBottomTextXY(totalTimeCharPtr, graphXY.x + graphWidth, graphXY.y); position = getCenterAlignedBottomTextXY(totalTimeCharPtr, graphXY.x + graphWidth, graphXY.y);
tft.setCursor(position.x, position.y + tickMarkLength + 1); tft.setCursor(position.x, position.y + tickMarkLength + 1);
tft.println(totalTIme); tft.println(totalTime);
delete[] totalTimeCharPtr; delete[] totalTimeCharPtr;
// Draw three tick labels on the time axis at 1/4, 1/2 and 3/4 of the way along // Draw three tick labels on the time axis at 1/4, 1/2 and 3/4 of the way along
for (int i = 1; i <= 3; i++) for (int i = 1; i <= 3; i++)
{ {
uint8_t time = totalTIme * i / 4; uint8_t time = totalTime * i / 4;
char *timeCharPtr = numberToCharPtr(time); char *timeCharPtr = numberToCharPtr(time);
position = getCenterAlignedBottomTextXY(timeCharPtr, graphXY.x + (graphWidth * i / 4), graphXY.y); position = getCenterAlignedBottomTextXY(timeCharPtr, graphXY.x + (graphWidth * i / 4), graphXY.y);
tft.setCursor(position.x, position.y + tickMarkLength + 1); tft.setCursor(position.x, position.y + tickMarkLength + 1);
@ -348,207 +319,36 @@ void TFT_Display::drawGraphAxisTickLabels()
void TFT_Display::drawGraphReflowStagesBackground() void TFT_Display::drawGraphReflowStagesBackground()
{ {
uint16_t x = graphXY.x + 1; uint16_t colors[4] = {preheat_COLOR, soak_COLOR, reflow_COLOR, cool_COLOR};
uint16_t previopusWidth;
// Draw the background for the reflow stages // Draw the background for the reflow stages
for (int i = 0; i < 4; i++) uint16_t x= graphXY.x + 1;
{ uint16_t y= graphXY.y;
uint16_t totalDuration = profile->endTimes[4];
Serial.println("Time x"); for (int i=0; i<4; i++) {
Serial.println(String(x));
uint16_t y = graphXY.y - graphHeight; uint16_t y = graphXY.y - graphHeight;
uint16_t height = graphHeight; uint16_t height = graphHeight;
float duration = profile->endTimes[i];
float percAtEndStep = duration / totalDuration;
float newX = percentageToX(percAtEndStep);
Serial.println(String(i) + " - duration: "+ String(duration) + " |Perc: "+ String(percAtEndStep)+ " - x:" + String(x) + " y:" + String(y) + " newX:" + String(newX) + " height:" + String(height) + " color:" + String(colors[i]));
tft.drawRect(x, y, newX-x, graphHeight, colors[i]);
x = newX;
// Get the width by getting the duration of each step in relation to the total time
float duration = profile->steps[i].duration;
uint16_t width = (duration / totalTIme) * graphWidth;
switch (i)
{
case 0:
tft.drawRect(x, y, width, height, preheat_COLOR);
previopusWidth = width;
break;
case 1:
x += previopusWidth;
tft.drawRect(x, y, width, height, soak_COLOR);
previopusWidth = width;
break;
case 2:
x += previopusWidth;
tft.drawRect(x, y, width, height, reflow_COLOR);
previopusWidth = width;
break;
case 3:
x += previopusWidth;
tft.drawRect(x, y, width, height, cool_COLOR);
previopusWidth = width;
break;
default:
break;
}
} }
} }
void TFT_Display::drawReflowTargetTempLine()
{
float startTemp = 20; void TFT_Display::drawReflowTargetTempLine() {
int startTime = 0; uint16_t y = tempYonGraph(20);
uint16_t startX = graphXY.x; uint16_t x = percentageToX(0);
uint16_t startY = graphXY.y; for (float i=0.00; i<=1; i+=0.01) {
int endTime; float temp = profile->getTargetTempFromPercentage(i);
uint16_t y2 = tempYonGraph(temp);
for (int i = 0; i < 4; i++) uint16_t x2 = percentageToX(i);
{ Serial.println(String(i) + " - temp: "+ String(temp) + " - x:" + String(x) + " y:" + String(y) + " x2:" + String(x2) + " y2:" + String(y2));
// Get the end temp and time of the current step tft.drawLine(x, y, x2, y2, ST77XX_WHITE);
int endTemp = profile->steps[i].targetTempAtEnd; x = x2;
int duration = profile->steps[i].duration; y = y2;
endTime = duration + startTime;
uint16_t endX = timeXonGraph(endTime);
uint16_t endY = tempYonGraph(endTemp);
// Serial.print("State:");
// Serial.println(String(STATE_STR(profile->steps[i].state)));
// Serial.print("Start x:");
// Serial.println(String(startX));
// Serial.print("Start y:");
// Serial.println(String(startY));
// Serial.print("End x:");
// Serial.println(String(endX));
// Serial.print("End y:");
// Serial.println(String(endY));
// Serial.print("Start temp:");
// Serial.println(String(startTemp));
// Serial.print("End temp:");
// Serial.println(String(endTemp));
// Serial.print("Start time:");
// Serial.println(String(startTime));
// Serial.print("End time:");
// Serial.println(String(endTime));
// Serial.println(" ");
// Check for the half sine function flag if its flagged draw the line using the ending temp of the step 2 before the current step
if (profile->steps[i].flagged)
{
// tft.drawCircle(startX, startY, 5, ST77XX_RED);
startTemp = profile->steps[i - 2].targetTempAtEnd;
}
// Draw the line pixel by pixel
switch (profile->steps[i].easeFunction)
{
case LINEAR:
tft.drawLine(startX, startY, endX, endY, ST77XX_WHITE);
break;
case EASE_IN_OUT:
for (int i = 0; i <= 100; i++)
{
// Convert i to a percentage
float percentage = i / 100.0;
// calculate the temp at the current percentage of the line
float temp = startTemp + (endTemp - startTemp) * -(cos(percentage * PI) - 1) / (double)2;
float time = startTime + (duration * percentage);
// Calculate the x and y position of the temp on the graph
uint16_t y = tempYonGraph(temp);
uint16_t x = timeXonGraph(time);
tft.drawPixel(x, y, ST77XX_WHITE);
}
break;
case EASE_IN:
for (int i = 0; i <= 100; i++)
{
// Convert i to a percentage
float percentage = i / 100.0;
// calculate the temp at the current percentage of the line
float temp = startTemp + (endTemp - startTemp) * (1 - cos(percentage * PI / (double)2));
// Calculate the x and y position of the temp on the graph
uint16_t y = tempYonGraph(temp);
uint16_t x = timeXonGraph(startTime + (duration * percentage));
tft.drawPixel(x, y, ST77XX_WHITE);
}
break;
case EASE_OUT:
for (int i = 0; i <= 100; i++)
{
// Convert i to a percentage
float percentage = i / 100.0;
// calculate the temp at the current percentage of the line
float temp = startTemp + (endTemp - startTemp) * (sin(percentage * PI / (double)2));
// Calculate the x and y position of the temp on the graph
uint16_t y = tempYonGraph(temp);
uint16_t x = timeXonGraph(startTime + (duration * percentage));
tft.drawPixel(x, y, ST77XX_WHITE);
}
break;
case HALF_SINE:
for (int i = 0; i <= 100; i++)
{
// Convert i to a percentage
float percentage = i / 100.0;
// calculate the temp at the current percentage of the line
float temp = startTemp + (endTemp - startTemp) * (sin(percentage * PI));
// Calculate the x and y position of the temp on the graph
uint16_t y = tempYonGraph(temp);
uint16_t x = timeXonGraph(startTime + (duration * percentage));
tft.drawPixel(x, y, ST77XX_WHITE);
}
break;
default:
// tft.drawLine(startX, startY, endX, endY, ST77XX_WHITE);
break;
}
// tft.drawLine(startX, startY, endX, endY, ST77XX_WHITE);
// Set the start temp and time to the end temp and time of the current step
startTemp = endTemp;
startTime = endTime;
// Get the start and end x and y positions of the line
startX = endX;
startY = endY;
} }
} }
@ -569,11 +369,9 @@ uint16_t TFT_Display::tempYonGraph(float *temp)
return y; return y;
} }
uint16_t TFT_Display::timeXonGraph(float time) uint16_t TFT_Display::percentageToX(float percentage)
{ {
// Calculate the x position of the time on the graph based on the min and max times and the min and max x of the graph // Calculate the x position of the time on the graph based on the min and max times and the min and max x of the graph
float x = graphXY.x + (graphWidth * (time / totalTIme)); return graphXY.x + (graphWidth * percentage);
return x;
} }

View File

@ -40,17 +40,13 @@ private:
TFT_XY getLeftAlignedTopTextXY(char *text, uint16_t x, uint16_t y); TFT_XY getLeftAlignedTopTextXY(char *text, uint16_t x, uint16_t y);
TFT_XY getCenterAlignedBottomTextXY(char *text, uint16_t x, uint16_t y); TFT_XY getCenterAlignedBottomTextXY(char *text, uint16_t x, uint16_t y);
void getMaxTempFromProfile(ReflowProfile *profile); void getMaxTempFromProfile(ReflowProfile *profile);
void getTotalTimeFromProfile(ReflowProfile *profile);
uint16_t graphHeight = 180; uint16_t graphHeight = 180;
uint16_t graphWidth = 255; uint16_t graphWidth = 255;
TFT_XY graphXY = {32, 220}; TFT_XY graphXY = {32, 220};
uint16_t maxTemp = 0; uint16_t maxTemp = 0;
uint16_t totalTIme =0;
uint8_t minTemp = 20; uint8_t minTemp = 20;
TFT_XY getXYWithinGraphBounds(uint8_t temp, uint8_t time);
void drawGraph(); void drawGraph();
void drawGraphAxis(); void drawGraphAxis();
void drawGraphAxisLabels(); void drawGraphAxisLabels();
@ -61,7 +57,7 @@ private:
uint16_t tempYonGraph(float temp); uint16_t tempYonGraph(float temp);
uint16_t tempYonGraph(float *temp); uint16_t tempYonGraph(float *temp);
uint16_t timeXonGraph(float time); uint16_t percentageToX(float time);
template <typename T> template <typename T>
char *numberToCharPtr(T number); char *numberToCharPtr(T number);

View File

@ -110,8 +110,10 @@ void loop()
{ {
pidController.loop(); pidController.loop();
#ifdef DEBUG
pidController.debug(); pidController.debug();
tftDisplay.drawRealTemp(pidController.getInput(), chosenReflowProfile.getCurrentTime()); #endif
tftDisplay.drawRealTemp(pidController.getInput(), chosenReflowProfile.getPercentage());
ReflowStep step = chosenReflowProfile.reflowStep(); ReflowStep step = chosenReflowProfile.reflowStep();
// Here we draw the actual temp vs time to the display // Here we draw the actual temp vs time to the display

View File

@ -39,33 +39,32 @@ class ReflowStep
{ {
public: public:
ReflowStep() {} ReflowStep() {}
ReflowStep(ReflowProcessState state, uint8_t time, uint8_t targetTempAtEnd) : state(state), duration(time), targetTempAtEnd(targetTempAtEnd), easeFunction(LINEAR) {} ReflowStep(ReflowProcessState state, uint8_t time, uint8_t targetTemp) : state(state), duration(time), targetTemp(targetTemp), easeFunction(LINEAR) {}
ReflowStep(ReflowProcessState state, uint8_t time, uint8_t targetTempAtEnd, ReflowStepEaseFunction fn) : state(state), ReflowStep(ReflowProcessState state, uint8_t time, uint8_t targetTemp, ReflowStepEaseFunction fn) : state(state),
duration(time), duration(time),
targetTempAtEnd(targetTempAtEnd), targetTemp(targetTemp),
easeFunction(fn) easeFunction(fn)
{ {
} }
uint8_t duration; uint8_t duration;
uint8_t targetTempAtEnd; uint8_t targetTemp;
ReflowProcessState state; ReflowProcessState state;
ReflowStepEaseFunction easeFunction; ReflowStepEaseFunction easeFunction;
bool flagged = false;
float calcTempAtPercentage(uint8_t startTemp, float percentage) float calcTempAtPercentage(uint8_t startTemp, float percentage)
{ {
switch (this->easeFunction) switch (this->easeFunction)
{ {
case LINEAR: case LINEAR:
return startTemp + (this->targetTempAtEnd - startTemp) * percentage; return startTemp + (this->targetTemp - startTemp) * percentage;
case EASE_IN_OUT: case EASE_IN_OUT:
return startTemp + (this->targetTempAtEnd - startTemp) * -(cos(percentage * PI) - 1) / (double)2; return startTemp + (this->targetTemp - startTemp) * -(cos(percentage * PI) - 1) / (double)2;
case EASE_IN: case EASE_IN:
return startTemp + (this->targetTempAtEnd - startTemp) * (1 - cos(percentage * PI / (double)2)); return startTemp + (this->targetTemp - startTemp) * (1 - cos(percentage * PI / (double)2));
case EASE_OUT: case EASE_OUT:
return startTemp + (this->targetTempAtEnd - startTemp) * (sin(percentage * PI / (double)2)); return startTemp + (this->targetTemp - startTemp) * (sin(percentage * PI / (double)2));
case HALF_SINE: case HALF_SINE:
return startTemp + (this->targetTempAtEnd - startTemp) * (sin(percentage * PI)); return startTemp + (this->targetTemp - startTemp) * (sin(percentage * PI));
} }
} }
}; };
@ -80,51 +79,31 @@ public:
ReflowProfile(ReflowStep steps[5], char name[20]) ReflowProfile(ReflowStep steps[5], char name[20])
{ {
bool flag = false;
uint8_t flagPosistion = 0;
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
// TODO: Important we need to flag the step after half sine because its starting temp is the PEAK of the sine wave and the sine wave ends at the start of the sine wave
// this creates a problem because the next step will start at the peak of the sine wave and not the end of the sine wave
// We can fix this by flagging the effect step and then when we are calculating the target temp we can check if the step is flagged and if it is we can use the target temp of the step prevous to the sine wave
if (steps[i].easeFunction == HALF_SINE && i != 3 && i != 0)
{
this->steps[i] = steps[i]; this->steps[i] = steps[i];
flag = true;
flagPosistion = i + 1;
}
else
{
this->steps[i] = steps[i];
}
}
if (flag)
{
this->steps[flagPosistion].flagged = true;
} }
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
this->name[i] = name[i]; this->name[i] = name[i];
} }
calculateValues();
} }
ReflowStep steps[5]; ReflowStep steps[5];
char name[20]; char name[20];
float endTimes[5] = {0}; float endTimes[5] = {0};
float startTimes[5] = {0}; float startTimes[5] = {0};
float endTemps[5] = {0};
StopWatch timer; StopWatch timer;
void start() void start()
{ {
timer.setResolution(StopWatch::MILLIS); timer.setResolution(StopWatch::MILLIS);
timer.start(); timer.start();
calculateTimes();
} }
void calculateTimes() void calculateValues()
{ {
endTimes[0] = steps[0].duration; endTimes[0] = steps[0].duration;
endTimes[1] = endTimes[0] + steps[1].duration; endTimes[1] = endTimes[0] + steps[1].duration;
@ -137,6 +116,12 @@ public:
startTimes[2] = endTimes[1]; startTimes[2] = endTimes[1];
startTimes[3] = endTimes[2]; startTimes[3] = endTimes[2];
startTimes[4] = endTimes[3]; startTimes[4] = endTimes[3];
endTemps[0] = steps[0].calcTempAtPercentage(20, 1);
endTemps[1] = steps[1].calcTempAtPercentage(endTemps[0], 1);
endTemps[2] = steps[2].calcTempAtPercentage(endTemps[1], 1);
endTemps[3] = steps[3].calcTempAtPercentage(endTemps[2], 1);
endTemps[4] = steps[4].calcTempAtPercentage(endTemps[3], 1);
} }
ReflowStep reflowStep() ReflowStep reflowStep()
@ -174,8 +159,6 @@ public:
float timerElapsed = timer.elapsed(); float timerElapsed = timer.elapsed();
float endTimesFloat[4];
return timerElapsed / (float)(endTimes[4] * 1000); return timerElapsed / (float)(endTimes[4] * 1000);
} }
@ -194,10 +177,10 @@ public:
ReflowStep curStep = reflowStep(elapsedMS); ReflowStep curStep = reflowStep(elapsedMS);
if (curStep.state > PREHEAT) if (curStep.state > PREHEAT)
{ {
startTemp = steps[STEPINDEX(curStep) - 1].targetTempAtEnd; startTemp = endTemps[STEPINDEX(curStep) - 1];
} }
// startTemp => 20 or the targetTempAtEnd of the previous step // startTemp => 20 or the temp at 100% of the previous step
uint32_t startTimeMS = startTimes[STEPINDEX(curStep)]; uint32_t startTimeMS = startTimes[STEPINDEX(curStep)];
@ -212,17 +195,8 @@ public:
float percentage = relativeElapsedTime / duration; float percentage = relativeElapsedTime / duration;
// Serial.println(String(percentage)+ "%" + String(STATE_STR(curStep.state)) + " Elapsed: " + String(elapsedMS) + " ___ " + String(curStep.duration * 1000)); // Serial.println(String(percentage)+ "%" + String(STATE_STR(curStep.state)) + " Elapsed: " + String(elapsedMS) + " ___ " + String(curStep.duration * 1000));
if (curStep.flagged)
{
startTemp = steps[STEPINDEX(curStep) - 2].targetTempAtEnd;
return curStep.calcTempAtPercentage(startTemp, percentage); return curStep.calcTempAtPercentage(startTemp, percentage);
} }
else
{
return curStep.calcTempAtPercentage(startTemp, percentage);
}
}
/** /**
* @brief Get the Target Temp At Process Percentage. * @brief Get the Target Temp At Process Percentage.
@ -231,9 +205,7 @@ public:
*/ */
float getTargetTempFromPercentage(double processPercentage) float getTargetTempFromPercentage(double processPercentage)
{ {
uint16_t duration = endTimes[4]; return getTargetTemp(endTimes[4] * (1000 * processPercentage));
uint8_t startTemp = 20; // always assume 20 degrees at the start
return getTargetTemp(duration * 1000 * processPercentage);
} }
uint8_t getCurrentStepRelativeTime() uint8_t getCurrentStepRelativeTime()
@ -255,7 +227,7 @@ public:
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
b[PROFILE_SERIALIZED_NAME_SIZE + i * 3] = steps[i].duration; b[PROFILE_SERIALIZED_NAME_SIZE + i * 3] = steps[i].duration;
b[PROFILE_SERIALIZED_NAME_SIZE + 1 + i * 3] = steps[i].targetTempAtEnd; b[PROFILE_SERIALIZED_NAME_SIZE + 1 + i * 3] = steps[i].targetTemp;
b[PROFILE_SERIALIZED_NAME_SIZE + 2 + i * 3] = steps[i].easeFunction; b[PROFILE_SERIALIZED_NAME_SIZE + 2 + i * 3] = steps[i].easeFunction;
} }
} }