Compare commits

..

No commits in common. "3aa9ab5cbd85a1fbdd9ab6102e2e9c5af52b5eb7" and "ef791654b47613e326d03f96500c46824fb56994" have entirely different histories.

4 changed files with 166 additions and 111 deletions

View File

@ -42,6 +42,8 @@ GyverOLED<SSD1306_128x64, OLED_NO_BUFFER> oled;
#define SW 7 #define SW 7
Encoder enc1(CLK, DT, SW, TYPE2); Encoder enc1(CLK, DT, SW, TYPE2);
// SSR pin configuration
const int ssrPin = 2;
const int triacPwm = 10; const int triacPwm = 10;
const int activeBuzzerPin = 8; const int activeBuzzerPin = 8;
@ -90,7 +92,7 @@ bool inSelectionMode = true;
// #include "GyverPID.h" // #include "GyverPID.h"
#include "pid.h" #include "pid.h"
double Setpoint, Input, Output; double Setpoint, Input, Output;
GyverPID regulator(10, 2, 10, 1000); GyverPID regulator(50, 1, 20, 1000);
bool temperatureSensorError = false; bool temperatureSensorError = false;
@ -120,6 +122,7 @@ int failedReadingCount = 0;
#define PARTS 8 #define PARTS 8
void setup() { void setup() {
pinMode(ssrPin, OUTPUT);
pinMode(activeBuzzerPin, OUTPUT); pinMode(activeBuzzerPin, OUTPUT);
Serial.begin(9600); Serial.begin(9600);
@ -146,3 +149,38 @@ void setup() {
void loop() { void loop() {
Looper.loop(); Looper.loop();
} }
// Function to configure Timer1 for PWM with the specified window duration
void configureTimer1(unsigned long windowMillis) {
// Disable interrupts while configuring the timer
noInterrupts();
// Reset Timer/Counter1 Control Registers
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
// Set the prescaler to 256
TCCR1B |= (1 << CS12); // Set prescaler to 256
// Calculate the top value for Timer1 based on the window duration
unsigned long ticks = (62500 * windowMillis) / 1000 / 2;
// Set the top value for Timer1 (ICR1) based on the calculated ticks
ICR1 = ticks - 1; // Subtract 1 because the counter starts at 0
// Configure Timer1 for Phase Correct PWM with ICR1 as TOP
// Enable non-inverted PWM on OC1B (Pin 10)
TCCR1A = (1 << WGM11) | (1 << COM1B1); // Only configure OC1B for PWM
// Set Phase Correct PWM mode with ICR1 as TOP
TCCR1B |= (1 << WGM13); // No WGM12
// Enable interrupts again
interrupts();
}
void setPWMDutyCycle(uint8_t duty) {
OCR1B = (unsigned long)duty * ICR1 / 255; // Set the duty cycle for Pin 10 (OC1B)
}

View File

@ -26,10 +26,10 @@ void printPhases() {
oled.setCursor(0, 1); oled.setCursor(0, 1);
oled.print(" "); oled.print(" ");
oled.setCursor(18, 1); oled.setCursor(18, 1);
formatTime((long)totalElapsedTime, timeBuffer); formatTime(totalElapsedTime, timeBuffer);
oled.print(timeBuffer); oled.print(timeBuffer);
oled.print(" / "); oled.print(" / ");
formatTime((long)totalProcessTime, timeBuffer); formatTime(totalProcessTime, timeBuffer);
oled.print(timeBuffer); oled.print(timeBuffer);
oled.print(" "); oled.print(" ");
@ -38,7 +38,7 @@ void printPhases() {
oled.invertText(true); // Invert text for the current phase oled.invertText(true); // Invert text for the current phase
oled.setCursor(0, i + 2); // Set cursor to the row corresponding to the phase oled.setCursor(0, i + 2); // Set cursor to the row corresponding to the phase
long timeRemaining = ((long) activeProfile.phases[i].duration * 60) - ((currentTime - phaseStartTime) / 1000); long timeRemaining = (activeProfile.phases[i].duration * 60) - ((currentTime - phaseStartTime) / 1000);
formatTime(timeRemaining, timeBuffer); formatTime(timeRemaining, timeBuffer);
oled.print(i + 1); oled.print(i + 1);
oled.print(". "); oled.print(". ");
@ -58,7 +58,7 @@ void printPhases() {
oled.invertText(false); // Normal text for other phases oled.invertText(false); // Normal text for other phases
oled.setCursor(0, i + 2); // Set cursor to the row corresponding to the phase oled.setCursor(0, i + 2); // Set cursor to the row corresponding to the phase
formatTime((long)activeProfile.phases[i].duration * 60, timeBuffer); formatTime(activeProfile.phases[i].duration * 60, timeBuffer);
oled.print(i + 1); oled.print(i + 1);
oled.print(". "); oled.print(". ");
oled.print(activeProfile.phases[i].temperature); oled.print(activeProfile.phases[i].temperature);
@ -84,12 +84,13 @@ void printPhases() {
if (failedReadingCount) { if (failedReadingCount) {
oled.setCursor(0, 7); oled.setCursor(0, 7);
oled.print(failedReadingCount); oled.print(failedReadingCount);
oled.print("!"); oled.print(" (");
oled.print(failedReadingLastValue);
oled.print(") ");
} }
oled.invertText(false); // Ensure text inversion is off after the loop oled.invertText(false); // Ensure text inversion is off after the loop
HandlePwmHeaterDisplay();
// oled.setCursor(110,7); // oled.setCursor(110,7);
// sprintf(timeBuffer, "%3d", (int)(Output*100)); // sprintf(timeBuffer, "%3d", (int)(Output*100));
// oled.print(timeBuffer); // oled.print(timeBuffer);
@ -129,67 +130,3 @@ void formatTime(long seconds, char* buffer) {
sprintf(buffer + strlen(buffer), "%ds", secs); sprintf(buffer + strlen(buffer), "%ds", secs);
} }
} }
#define graphItems 30
// LP_TIMER(1000, HandlePwmHeaterDisplay);
void HandlePwmHeaterDisplay() {
static byte graphStates[graphItems];
static byte currentGraphItemNumber = 0;
if (inSelectionMode) {
Setpoint = 0;
return;
}
currentGraphItemNumber++;
if (currentGraphItemNumber >= graphItems) {
currentGraphItemNumber = 0;
}
byte graphHeight = 7;
byte currentAmount = Output==0 ? 0 : (Output-1)/99.0 * (graphHeight) + 1;
graphStates[currentGraphItemNumber] = currentAmount;
byte rightMargin = 128;
byte topMargin = 63-graphHeight;
byte itemsDisplayed = 0;
while (itemsDisplayed < graphItems) {
int currentItemN = currentGraphItemNumber - itemsDisplayed;
if (currentItemN < 0) {
currentItemN += graphItems;
}
byte CurrentItemValue = graphStates[currentItemN];
oled.fastLineV(rightMargin - graphItems + itemsDisplayed, topMargin, topMargin+graphHeight-CurrentItemValue+1, 0);
if (CurrentItemValue) {
oled.fastLineV(rightMargin - graphItems + itemsDisplayed, topMargin+graphHeight-CurrentItemValue+1, topMargin+graphHeight, 1);
}
itemsDisplayed++;
}
byte leftPosition = rightMargin - graphItems - 3*6 - 1;
oled.setCursor(leftPosition, 7);
sprintf(timeBuffer, "%3d", (int)(Output));
oled.invertText(true);
oled.print(timeBuffer);
oled.invertText(false);
leftPosition -= 6*3+2;
oled.setCursor(leftPosition, 7);
sprintf(timeBuffer, "%3d", (int)(regulator.lastD));
oled.print(timeBuffer);
leftPosition -= 6*3+2;
oled.setCursor(leftPosition, 7);
sprintf(timeBuffer, "%3d", (int)(regulator.lastI));
oled.print(timeBuffer);
leftPosition -= 6*3+2;
oled.setCursor(leftPosition, 7);
sprintf(timeBuffer, "%3d", (int)(regulator.lastP));
oled.print(timeBuffer);
}

View File

@ -3,36 +3,121 @@ LP_TIMER(1000, []() {
setPWMDutyCycle(power); setPWMDutyCycle(power);
}); });
// Function to configure Timer1 for PWM with the specified window duration #define graphItems 32
void configureTimer1(unsigned long windowMillis) { LP_TIMER(1000, HandlePwmHeaterDisplay);
// Disable interrupts while configuring the timer void HandlePwmHeaterDisplay() {
noInterrupts(); static byte graphStates[graphItems];
static byte currentGraphItemNumber = 0;
// Reset Timer/Counter1 Control Registers if (inSelectionMode) {
TCCR1A = 0; digitalWrite(ssrPin, LOW);
TCCR1B = 0; Setpoint = 0;
TCNT1 = 0; return;
}
// Set the prescaler to 256 currentGraphItemNumber++;
TCCR1B |= (1 << CS12); // Set prescaler to 256 if (currentGraphItemNumber >= graphItems) {
currentGraphItemNumber = 0;
}
// Calculate the top value for Timer1 based on the window duration byte graphHeight = 10;
unsigned long ticks = (62500 * windowMillis) / 1000 / 2; byte currentAmount = Output==0 ? 0 : (Output-1)/99.0 * (graphHeight) + 1;
// Set the top value for Timer1 (ICR1) based on the calculated ticks graphStates[currentGraphItemNumber] = currentAmount;
ICR1 = ticks - 1; // Subtract 1 because the counter starts at 0
// Configure Timer1 for Phase Correct PWM with ICR1 as TOP byte rightMargin = 127;
// Enable non-inverted PWM on OC1B (Pin 10) byte topMargin = 63-graphHeight;
TCCR1A = (1 << WGM11) | (1 << COM1B1); // Only configure OC1B for PWM byte itemsDisplayed = 0;
// Set Phase Correct PWM mode with ICR1 as TOP while (itemsDisplayed < graphItems) {
TCCR1B |= (1 << WGM13); // No WGM12 int currentItemN = currentGraphItemNumber - itemsDisplayed;
if (currentItemN < 0) {
currentItemN += graphItems;
}
byte CurrentItemValue = graphStates[currentItemN];
oled.fastLineV(rightMargin - graphItems + itemsDisplayed, topMargin, topMargin+graphHeight-CurrentItemValue+1, 0);
if (CurrentItemValue) {
oled.fastLineV(rightMargin - graphItems + itemsDisplayed, topMargin+graphHeight-CurrentItemValue+1, topMargin+graphHeight, 1);
}
// Enable interrupts again itemsDisplayed++;
interrupts(); }
byte leftPosition = 126-graphItems - 3*6;
oled.setCursor(leftPosition, 7);
sprintf(timeBuffer, "%3d", (int)(Output));
oled.invertText(true);
oled.print(timeBuffer);
oled.invertText(false);
leftPosition -= 6*3;
oled.setCursor(leftPosition, 7);
sprintf(timeBuffer, "%3d", (int)(regulator.lastD));
oled.print(timeBuffer);
leftPosition -= 6*3;
oled.setCursor(leftPosition, 7);
sprintf(timeBuffer, "%3d", (int)(regulator.lastI));
oled.print(timeBuffer);
leftPosition -= 6*3;
oled.setCursor(leftPosition, 7);
sprintf(timeBuffer, "%3d", (int)(regulator.lastP));
oled.print(timeBuffer);
} }
void setPWMDutyCycle(uint8_t duty) { // LP_TIMER(1000, HandleHeater);
OCR1B = (unsigned long)duty * ICR1 / 255; // Set the duty cycle for Pin 10 (OC1B) void HandleHeater() {
static int currentPart = 0;
static bool states[PARTS];
if (inSelectionMode) {
digitalWrite(ssrPin, LOW);
Setpoint = 0;
return;
}
int current = Output/100 * PARTS;
int last = 0;
for (int i = 1; i < PARTS; i++) {
last += states[i] ? 1 : 0;
if (i) {
states[i-1] = states[i];
}
}
bool next = current >= last ? true : false;
if (Output < 1) next = false;
states[PARTS-1] = next;
oled.setCursor(128-6*PARTS, 7);
sprintf(timeBuffer, "%3d", (int)(Output));
char symbol;
for (int i = 0; i < PARTS; i++) {
int index = i - (PARTS - 3);
symbol = index < 0 ? ' ' : timeBuffer[index];
oled.invertText(states[i]);
oled.print(symbol);
oled.invertText(false);
}
if (temperatureSensorError) {
next = 0;
}
digitalWrite(ssrPin, next);
currentPart++;
if (currentPart >= PARTS) {
currentPart = 0;
}
#ifdef PlotValues
Serial.print(Setpoint);
Serial.print(",");
Serial.print(Input);
Serial.print(",");
Serial.print(Output);
Serial.println(",0,100");
#endif
} }

View File

@ -8,15 +8,10 @@ void HandleExecution() {
getPhaseAndTemperature(); getPhaseAndTemperature();
if (Input > 20 && Input < 95) { regulator.input = (float)Input;
regulator.input = (float)Input; regulator.setpoint = Setpoint;
regulator.setpoint = Setpoint; regulator.getResultNow();
regulator.getResultNow(); Output = regulator.output;
Output = regulator.output;
}
else {
Setpoint = 0;
}
if (isComplete && currentPhase >= activeProfile.numPhases && !finishTime) { if (isComplete && currentPhase >= activeProfile.numPhases && !finishTime) {
finishTime = currentTime; finishTime = currentTime;
@ -30,19 +25,19 @@ void getPhaseAndTemperature() {
long accumulatedTime = 0; long accumulatedTime = 0;
for (int i = 0; i < activeProfile.numPhases; i++) { for (int i = 0; i < activeProfile.numPhases; i++) {
long phaseDuration = activeProfile.phases[i].duration * 60; int phaseDuration = activeProfile.phases[i].duration * 60;
// Check for transition // Check for transition
if (i > 0) { if (i > 0) {
int previousTemp = activeProfile.phases[i - 1].temperature; int previousTemp = activeProfile.phases[i - 1].temperature;
int targetTemp = activeProfile.phases[i].temperature; int targetTemp = activeProfile.phases[i].temperature;
int tempDiff = abs(targetTemp - previousTemp); int tempDiff = abs(targetTemp - previousTemp);
long transitionDuration = tempDiff * 60 * activeProfile.transitionMinutesPerDegree; int transitionDuration = tempDiff * 60 * activeProfile.transitionMinutesPerDegree;
if (totalElapsedTime < accumulatedTime + transitionDuration) { if (totalElapsedTime < accumulatedTime + transitionDuration) {
isInTransition = true; isInTransition = true;
currentPhase = i - 1; // Keep currentPhase as the previous phase currentPhase = i - 1; // Keep currentPhase as the previous phase
long timeInTransition = totalElapsedTime - accumulatedTime; int timeInTransition = totalElapsedTime - accumulatedTime;
Setpoint = previousTemp + (double)timeInTransition / (60 * activeProfile.transitionMinutesPerDegree) * (targetTemp > previousTemp ? 1 : -1); Setpoint = previousTemp + (double)timeInTransition / (60 * activeProfile.transitionMinutesPerDegree) * (targetTemp > previousTemp ? 1 : -1);
phaseStartTime = totalStartTime + accumulatedTime * 1000; // Set phase start time phaseStartTime = totalStartTime + accumulatedTime * 1000; // Set phase start time
return; return;
@ -52,7 +47,7 @@ void getPhaseAndTemperature() {
} }
// Check if we're within the current phase // Check if we're within the current phase
if (totalElapsedTime < (long) (accumulatedTime + phaseDuration)) { if (totalElapsedTime < accumulatedTime + phaseDuration) {
isInTransition = false; isInTransition = false;
currentPhase = i; currentPhase = i;
Setpoint = activeProfile.phases[i].temperature; Setpoint = activeProfile.phases[i].temperature;
@ -76,10 +71,10 @@ void calculateTotalTime() {
activeProfile = profiles[activeProfileIndex]; activeProfile = profiles[activeProfileIndex];
totalProcessTime = 0; totalProcessTime = 0;
for (int i = 0; i < activeProfile.numPhases; i++) { for (int i = 0; i < activeProfile.numPhases; i++) {
totalProcessTime += (long) activeProfile.phases[i].duration * 60; totalProcessTime += activeProfile.phases[i].duration * 60;
if (i < activeProfile.numPhases - 1) { if (i < activeProfile.numPhases - 1) {
long tempDiff = abs(activeProfile.phases[i + 1].temperature - activeProfile.phases[i].temperature); int tempDiff = abs(activeProfile.phases[i + 1].temperature - activeProfile.phases[i].temperature);
totalProcessTime += (long) tempDiff * 60 * activeProfile.transitionMinutesPerDegree; totalProcessTime += tempDiff * 60 * activeProfile.transitionMinutesPerDegree;
} }
} }
} }