diff --git a/hot_fermentation.ino b/hot_fermentation.ino index bb68036..82557df 100644 --- a/hot_fermentation.ino +++ b/hot_fermentation.ino @@ -42,8 +42,6 @@ GyverOLED oled; #define SW 7 Encoder enc1(CLK, DT, SW, TYPE2); -// SSR pin configuration -const int ssrPin = 2; const int triacPwm = 10; const int activeBuzzerPin = 8; @@ -122,7 +120,6 @@ int failedReadingCount = 0; #define PARTS 8 void setup() { - pinMode(ssrPin, OUTPUT); pinMode(activeBuzzerPin, OUTPUT); Serial.begin(9600); @@ -149,38 +146,3 @@ void setup() { void 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) -} diff --git a/output.ino b/output.ino index aaa48b1..9ad7618 100644 --- a/output.ino +++ b/output.ino @@ -26,10 +26,10 @@ void printPhases() { oled.setCursor(0, 1); oled.print(" "); oled.setCursor(18, 1); - formatTime(totalElapsedTime, timeBuffer); + formatTime((long)totalElapsedTime, timeBuffer); oled.print(timeBuffer); oled.print(" / "); - formatTime(totalProcessTime, timeBuffer); + formatTime((long)totalProcessTime, timeBuffer); oled.print(timeBuffer); oled.print(" "); @@ -38,7 +38,7 @@ void printPhases() { oled.invertText(true); // Invert text for the current 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); oled.print(i + 1); oled.print(". "); @@ -58,7 +58,7 @@ void printPhases() { oled.invertText(false); // Normal text for other phases 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(". "); oled.print(activeProfile.phases[i].temperature); @@ -84,13 +84,12 @@ void printPhases() { if (failedReadingCount) { oled.setCursor(0, 7); oled.print(failedReadingCount); - oled.print(" ("); - oled.print(failedReadingLastValue); - oled.print(") "); + oled.print("!"); } oled.invertText(false); // Ensure text inversion is off after the loop + HandlePwmHeaterDisplay(); // oled.setCursor(110,7); // sprintf(timeBuffer, "%3d", (int)(Output*100)); // oled.print(timeBuffer); @@ -129,4 +128,68 @@ void formatTime(long seconds, char* buffer) { if (secs > 0) { sprintf(buffer + strlen(buffer), "%ds", secs); } -} \ No newline at end of file +} + + +#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); +} + diff --git a/t_heater.ino b/t_heater.ino index 77aa8dd..d49b485 100644 --- a/t_heater.ino +++ b/t_heater.ino @@ -3,121 +3,36 @@ LP_TIMER(1000, []() { setPWMDutyCycle(power); }); -#define graphItems 32 -LP_TIMER(1000, HandlePwmHeaterDisplay); -void HandlePwmHeaterDisplay() { - static byte graphStates[graphItems]; - static byte currentGraphItemNumber = 0; +// Function to configure Timer1 for PWM with the specified window duration +void configureTimer1(unsigned long windowMillis) { + // Disable interrupts while configuring the timer + noInterrupts(); - if (inSelectionMode) { - digitalWrite(ssrPin, LOW); - Setpoint = 0; - return; - } + // Reset Timer/Counter1 Control Registers + TCCR1A = 0; + TCCR1B = 0; + TCNT1 = 0; - currentGraphItemNumber++; - if (currentGraphItemNumber >= graphItems) { - currentGraphItemNumber = 0; - } + // Set the prescaler to 256 + TCCR1B |= (1 << CS12); // Set prescaler to 256 - byte graphHeight = 8; - byte currentAmount = Output==0 ? 0 : (Output-1)/99.0 * (graphHeight) + 1; + // Calculate the top value for Timer1 based on the window duration + 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 = 128; - byte topMargin = 63-graphHeight; - byte itemsDisplayed = 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 - 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); - } + // Set Phase Correct PWM mode with ICR1 as TOP + TCCR1B |= (1 << WGM13); // No WGM12 - itemsDisplayed++; - } - - 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); + // Enable interrupts again + interrupts(); } -// LP_TIMER(1000, HandleHeater); -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 - +void setPWMDutyCycle(uint8_t duty) { + OCR1B = (unsigned long)duty * ICR1 / 255; // Set the duty cycle for Pin 10 (OC1B) } diff --git a/t_main.ino b/t_main.ino index 7c7c764..498cd06 100644 --- a/t_main.ino +++ b/t_main.ino @@ -14,6 +14,9 @@ void HandleExecution() { regulator.getResultNow(); Output = regulator.output; } + else { + Setpoint = 0; + } if (isComplete && currentPhase >= activeProfile.numPhases && !finishTime) { finishTime = currentTime; @@ -27,19 +30,19 @@ void getPhaseAndTemperature() { long accumulatedTime = 0; 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 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; + long transitionDuration = tempDiff * 60 * activeProfile.transitionMinutesPerDegree; if (totalElapsedTime < accumulatedTime + transitionDuration) { isInTransition = true; 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); phaseStartTime = totalStartTime + accumulatedTime * 1000; // Set phase start time return; @@ -49,7 +52,7 @@ void getPhaseAndTemperature() { } // Check if we're within the current phase - if (totalElapsedTime < accumulatedTime + phaseDuration) { + if (totalElapsedTime < (long) (accumulatedTime + phaseDuration)) { isInTransition = false; currentPhase = i; Setpoint = activeProfile.phases[i].temperature; @@ -73,10 +76,10 @@ void calculateTotalTime() { activeProfile = profiles[activeProfileIndex]; totalProcessTime = 0; 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) { - int tempDiff = abs(activeProfile.phases[i + 1].temperature - activeProfile.phases[i].temperature); - totalProcessTime += tempDiff * 60 * activeProfile.transitionMinutesPerDegree; + long tempDiff = abs(activeProfile.phases[i + 1].temperature - activeProfile.phases[i].temperature); + totalProcessTime += (long) tempDiff * 60 * activeProfile.transitionMinutesPerDegree; } } }