Compare commits
2 Commits
ef791654b4
...
3aa9ab5cbd
| Author | SHA1 | Date | |
|---|---|---|---|
| 3aa9ab5cbd | |||
| f395491f45 |
@ -42,8 +42,6 @@ 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;
|
||||||
|
|
||||||
@ -92,7 +90,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(50, 1, 20, 1000);
|
GyverPID regulator(10, 2, 10, 1000);
|
||||||
|
|
||||||
bool temperatureSensorError = false;
|
bool temperatureSensorError = false;
|
||||||
|
|
||||||
@ -122,7 +120,6 @@ 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);
|
||||||
|
|
||||||
@ -149,38 +146,3 @@ 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)
|
|
||||||
}
|
|
||||||
|
|||||||
79
output.ino
79
output.ino
@ -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(totalElapsedTime, timeBuffer);
|
formatTime((long)totalElapsedTime, timeBuffer);
|
||||||
oled.print(timeBuffer);
|
oled.print(timeBuffer);
|
||||||
oled.print(" / ");
|
oled.print(" / ");
|
||||||
formatTime(totalProcessTime, timeBuffer);
|
formatTime((long)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 = (activeProfile.phases[i].duration * 60) - ((currentTime - phaseStartTime) / 1000);
|
long timeRemaining = ((long) 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(activeProfile.phases[i].duration * 60, timeBuffer);
|
formatTime((long)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,13 +84,12 @@ 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,4 +128,68 @@ void formatTime(long seconds, char* buffer) {
|
|||||||
if (secs > 0) {
|
if (secs > 0) {
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
131
t_heater.ino
131
t_heater.ino
@ -3,121 +3,36 @@ LP_TIMER(1000, []() {
|
|||||||
setPWMDutyCycle(power);
|
setPWMDutyCycle(power);
|
||||||
});
|
});
|
||||||
|
|
||||||
#define graphItems 32
|
// Function to configure Timer1 for PWM with the specified window duration
|
||||||
LP_TIMER(1000, HandlePwmHeaterDisplay);
|
void configureTimer1(unsigned long windowMillis) {
|
||||||
void HandlePwmHeaterDisplay() {
|
// Disable interrupts while configuring the timer
|
||||||
static byte graphStates[graphItems];
|
noInterrupts();
|
||||||
static byte currentGraphItemNumber = 0;
|
|
||||||
|
|
||||||
if (inSelectionMode) {
|
// Reset Timer/Counter1 Control Registers
|
||||||
digitalWrite(ssrPin, LOW);
|
TCCR1A = 0;
|
||||||
Setpoint = 0;
|
TCCR1B = 0;
|
||||||
return;
|
TCNT1 = 0;
|
||||||
}
|
|
||||||
|
|
||||||
currentGraphItemNumber++;
|
// Set the prescaler to 256
|
||||||
if (currentGraphItemNumber >= graphItems) {
|
TCCR1B |= (1 << CS12); // Set prescaler to 256
|
||||||
currentGraphItemNumber = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte graphHeight = 10;
|
// Calculate the top value for Timer1 based on the window duration
|
||||||
byte currentAmount = Output==0 ? 0 : (Output-1)/99.0 * (graphHeight) + 1;
|
unsigned long ticks = (62500 * windowMillis) / 1000 / 2;
|
||||||
|
|
||||||
graphStates[currentGraphItemNumber] = currentAmount;
|
// Set the top value for Timer1 (ICR1) based on the calculated ticks
|
||||||
|
ICR1 = ticks - 1; // Subtract 1 because the counter starts at 0
|
||||||
|
|
||||||
byte rightMargin = 127;
|
// Configure Timer1 for Phase Correct PWM with ICR1 as TOP
|
||||||
byte topMargin = 63-graphHeight;
|
// Enable non-inverted PWM on OC1B (Pin 10)
|
||||||
byte itemsDisplayed = 0;
|
TCCR1A = (1 << WGM11) | (1 << COM1B1); // Only configure OC1B for PWM
|
||||||
|
|
||||||
while (itemsDisplayed < graphItems) {
|
// Set Phase Correct PWM mode with ICR1 as TOP
|
||||||
int currentItemN = currentGraphItemNumber - itemsDisplayed;
|
TCCR1B |= (1 << WGM13); // No WGM12
|
||||||
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++;
|
// Enable interrupts again
|
||||||
}
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LP_TIMER(1000, HandleHeater);
|
void setPWMDutyCycle(uint8_t duty) {
|
||||||
void HandleHeater() {
|
OCR1B = (unsigned long)duty * ICR1 / 255; // Set the duty cycle for Pin 10 (OC1B)
|
||||||
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
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
27
t_main.ino
27
t_main.ino
@ -8,10 +8,15 @@ void HandleExecution() {
|
|||||||
|
|
||||||
getPhaseAndTemperature();
|
getPhaseAndTemperature();
|
||||||
|
|
||||||
regulator.input = (float)Input;
|
if (Input > 20 && Input < 95) {
|
||||||
regulator.setpoint = Setpoint;
|
regulator.input = (float)Input;
|
||||||
regulator.getResultNow();
|
regulator.setpoint = Setpoint;
|
||||||
Output = regulator.output;
|
regulator.getResultNow();
|
||||||
|
Output = regulator.output;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Setpoint = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (isComplete && currentPhase >= activeProfile.numPhases && !finishTime) {
|
if (isComplete && currentPhase >= activeProfile.numPhases && !finishTime) {
|
||||||
finishTime = currentTime;
|
finishTime = currentTime;
|
||||||
@ -25,19 +30,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++) {
|
||||||
int phaseDuration = activeProfile.phases[i].duration * 60;
|
long 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);
|
||||||
int transitionDuration = tempDiff * 60 * activeProfile.transitionMinutesPerDegree;
|
long 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
|
||||||
int timeInTransition = totalElapsedTime - accumulatedTime;
|
long 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;
|
||||||
@ -47,7 +52,7 @@ void getPhaseAndTemperature() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if we're within the current phase
|
// Check if we're within the current phase
|
||||||
if (totalElapsedTime < accumulatedTime + phaseDuration) {
|
if (totalElapsedTime < (long) (accumulatedTime + phaseDuration)) {
|
||||||
isInTransition = false;
|
isInTransition = false;
|
||||||
currentPhase = i;
|
currentPhase = i;
|
||||||
Setpoint = activeProfile.phases[i].temperature;
|
Setpoint = activeProfile.phases[i].temperature;
|
||||||
@ -71,10 +76,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 += activeProfile.phases[i].duration * 60;
|
totalProcessTime += (long) activeProfile.phases[i].duration * 60;
|
||||||
if (i < activeProfile.numPhases - 1) {
|
if (i < activeProfile.numPhases - 1) {
|
||||||
int tempDiff = abs(activeProfile.phases[i + 1].temperature - activeProfile.phases[i].temperature);
|
long tempDiff = abs(activeProfile.phases[i + 1].temperature - activeProfile.phases[i].temperature);
|
||||||
totalProcessTime += tempDiff * 60 * activeProfile.transitionMinutesPerDegree;
|
totalProcessTime += (long) tempDiff * 60 * activeProfile.transitionMinutesPerDegree;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user