hot-fermentation/hot_fermentation.ino

187 lines
5.0 KiB
C++

#include <Arduino.h>
#include <Looper.h>
#define useEEPROM 1
// #define TempSensorMax
#define TempSensorDallas
// #define PlotValues
// #include <Wire.h>
#include <GyverOLED.h>
#include "GyverEncoder.h"
#if useEEPROM
#include <EEPROM.h>
#endif
#ifdef TempSensorMax
#include <max6675.h>
// MAX6675 configuration
int max_SO = 12;
int max_CS = 10;
int max_SCK = 13;
MAX6675 thermocouple(max_SCK, max_CS, max_SO);
#endif
#ifdef TempSensorDallas
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 9
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
#endif
// OLED configuration
// GyverOLED<SSD1306_128x64> oled;
GyverOLED<SSD1306_128x64, OLED_NO_BUFFER> oled;
// Encoder configuration
#define CLK 5
#define DT 6
#define SW 7
Encoder enc1(CLK, DT, SW, TYPE2);
// SSR pin configuration
const int ssrPin = 2;
const int triacPwm = 10;
const int activeBuzzerPin = 8;
// Profile structure definition
struct Phase {
int temperature;
int duration; // in minutes
};
struct Profile {
const char* name;
int transitionMinutesPerDegree;
Phase phases[6]; // Maximum of 6 phases per profile
int numPhases;
};
// Profiles definition
Profile profiles[] = {
{"Chickpeas", 1, {{46, 120}, {55, 60}, {65, 150}, {70, 60}, {90, 105}}, 5},
{"Chickpeas long", 1, {{46, 120}, {55, 120}, {65, 120}, {72, 240}, {85, 300}}, 5},
{"Lentils", 3, {{46, 120}, {53, 120}, {65, 180}, {72, 90}, {90, 15}}, 5},
{"Gradual", 10, {{46, 120}, {90, 30}}, 2},
{"Long gradual", 15, {{46, 120}, {80, 60}, {90, 15}}, 3},
{"Wheat", 1, {{46, 120}, {53, 40}, {65, 40}, {72, 40}, {85, 40}}, 5},
// {"Amazake", 1, {{51, 480}}, 1},
{"37, 3 days", 1, {{37, 1440}, {37, 1440}, {37, 1440}}, 3},
// {"SV", 0, {{46, 45}}, 1},
// {"Yoghurt maker", 0, {{40, 300}, {40, 300}, {30, 300}}, 3},
// {"TEST", 0, {{46, 1}}, 1},
};
// Global variables for profile selection and execution
int activeProfileIndex = 100; // 100 indicates no profile selected yet
int selectedProfileIndex = 0; // Index of the currently selected profile in the selection mode
Profile activeProfile; // The active profile once selected
// Global variables for current phase, setpoint, and transition state
int currentPhase;
bool isInTransition = false;
bool inSelectionMode = true;
// PID Control variables
// double Kp = 0.5, Ki = 1, Kd = 0.05;
// PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
// #include "GyverPID.h"
#include "pid.h"
double Setpoint, Input, Output;
GyverPID regulator(50, 1, 20, 1000);
bool temperatureSensorError = false;
// Timing variables
long phaseStartTime;
long totalStartTime;
long ssrLastSwitchTime;
long totalElapsedTime;
long totalProcessTime;
long finishTime = 0;
long currentTime = 0;
long lastEEPROMWriteTime = 0;
bool isClick = false;
bool isLeft = false;
bool isRight = false;
bool isComplete = false;
const int ssrSwitchInterval = 1000; // SSR switching interval in milliseconds
// Buffer for formatted time strings
char timeBuffer[10];
bool boolLastCompletedState = false;
float failedReadingLastValue = 0;
int failedReadingCount = 0;
#define PARTS 8
void setup() {
pinMode(ssrPin, OUTPUT);
pinMode(activeBuzzerPin, OUTPUT);
Serial.begin(9600);
oled.init(); // Initialize the OLED
oled.clear(); // Clear the display
readEEPROM();
regulator.setDirection(NORMAL); // направление регулирования (NORMAL/REVERSE). ПО УМОЛЧАНИЮ СТОИТ NORMAL
regulator.setLimits(0, 100); // пределы (ставим для 8 битного ШИМ). ПО УМОЛЧАНИЮ СТОЯТ 0 И 255
regulator.setpoint = 0;
pinMode(triacPwm, OUTPUT);
configureTimer1(1000);
setPWMDutyCycle(0);
#ifdef TempSensorDallas
sensors.begin();
DeviceAddress tempDeviceAddress;
sensors.getAddress(tempDeviceAddress, 0);
sensors.setResolution(tempDeviceAddress, 12);
sensors.setWaitForConversion(false);
#endif
}
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)
}