Considera che essendo di ridotte dimensioni e adattando il tempo di coltura con il clima stagionale, ho stimato un delta tra temperatura esterna (t_outside) di 5°C rispetto alla temperatura interna (t?inside).
Ho la possibilità di inserire un tappeto riscaldante per terreni formato da una resistenza delle dimensioni di una vaschetta di gelato, non darà un grande contributo ma probabilmente non necessiterò di un utilizzo prolungato della cella di Peltier.
Pensando a questo elemento raffreddante potrei acquistare un elemento riscaldante PTC utilizzando per il solo raffreddamento la cella di Peltier.
#include <Wire.h>
#include <Adafruit_SHT31.h>
#include <LiquidCrystal_I2C.h>
#include <Time.h>
// pin per controllare il riscaldatore e il condizionatore
const int heaterPin = 9;
const int coolerPin = 10;
const int buttonUpPin = 2;
const int buttonDownPin = 3;
// temperatura interna desiderata e differenza di temperatura per attivare il riscaldamento/raffreddamento
int desiredTemp = 72;
const int tempDiffThreshold = 2;
// costante per il controllo PID
const double kp = 0.2;
const double ki = 0.1;
const double kd = 0.05;
// variabili per il controllo PID
double error, prevError, integral, derivative;
// variabili per il controllo dei tempi di attivazione
unsigned long heatingStartTime = 0;
unsigned long coolingStartTime = 0;
// oggetto per la lettura della temperatura e umidità
Adafruit_SHT31 sht31 = Adafruit_SHT31();
// oggetto per il display LCD
LiquidCrystal_I2C lcd(0x27, 20, 4);
void setup() {
// imposta i pin di uscita per il riscaldatore e il condizionatore
pinMode(heaterPin, OUTPUT);
pinMode(coolerPin, OUTPUT);
pinMode(buttonUpPin, INPUT);
pinMode(buttonDownPin, INPUT);
// inizializza il display LCD
lcd.begin();
lcd.clear();
lcd.print("Initializing...");
// inizializza la comunicazione I2C
Wire.begin();
// inizializza il sensore SHT31
if (!sht31.begin(0x44)) {
lcd.clear();
lcd.print("Error initializing");
while (1);
}
lcd.clear();
lcd.print("Temperature:");
lcd.setCursor(0, 1);
lcd.print("Humidity:");
lcd.setCursor(0, 2);
lcd.print("Power: ");
lcd.setCursor(0, 3);
lcd.print("Status: ");
}
void loop() {
// leggi la temperatura e l'umidità
float temperature = sht31.readTemperature();
float humidity = sht31.readHumidity();
// visualizza la temperatura e l'umidità sullo schermo
lcd.setCursor(12, 0);
lcd.print(temperature);
lcd.print((char)223);
lcd.print("C ");
lcd.setCursor(11, 1);
lcd.print(humidity);
lcd.print("% ");
// leggi i tasti per la regolazione della temperatura desiderata
int buttonUpState = digitalRead(buttonUpPin);
int buttonDownState = digitalRead(buttonDownPin);
if (buttonUpState == LOW) {
desiredTemp++;
delay(200);
}
if (buttonDownState == LOW) {
desiredTemp--;
delay(200);
}
// Calcola la differenza tra la temperatura desiderata e quella corrente
int tempDiff = desiredTemp - temperature;
// se la differenza di temperatura è maggiore del limite
if (abs(tempDiff) > tempDiffThreshold) {
// calcola l'errore per il controllo PID
error = tempDiff;
integral += error;
derivative = error - prevError;
prevError = error;
// calcola la potenza di riscaldamento/raffreddamento necessaria utilizzando il controllo PID
int power = kp * error + ki * integral + kd * derivative;
power = constrain(power, -255, 255);
// visualizza la potenza sullo schermo
lcd.setCursor(7, 2);
lcd.print(power);
// modula l'uscita per riscaldare o raffreddare
adjustOutput(power);
} else {
// la temperatura è stabile, spegni riscaldatore e condizionatore
digitalWrite(heaterPin, LOW);
digitalWrite(coolerPin, LOW);
integral = 0;
lcd.setCursor(7, 2);
lcd.print(" ");
}
// aggiorna lo stato sullo schermo
lcd.setCursor(8, 3);
if (digitalRead(heaterPin) == HIGH) {
lcd.print("Risc. ");
lcd.print(getFormattedTime(heatingStartTime));
} else if (digitalRead(coolerPin) == HIGH) {
lcd.print("Raff. ");
lcd.print(getFormattedTime(coolingStartTime));
} else {
lcd.print(" ");
}
delay(1000); // aspetta 1 secondo prima di controllare di nuovo la temperatura e i tasti
}
void adjustOutput(int power) {
// utilizzare la potenza calcolata per regolare l'uscita del riscaldatore o del condizionatore
// esempio:
// se power > 0, attiva il riscaldatore con la potenza specificata
// se power < 0, attiva il condizionatore con la potenza specificata (in modo negativo)
// se power == 0, spegni riscaldatore e condizionatore
if (power > 0) {
if (digitalRead(heaterPin) == LOW) {
heatingStartTime = millis();
}
analogWrite(heaterPin, power);
digitalWrite(coolerPin, LOW);
} else if (power < 0) {
if (digitalRead(coolerPin) == LOW) {
coolingStartTime = millis();
}
analogWrite(coolerPin, -power);
digitalWrite(heaterPin, LOW);
} else {
digitalWrite(heaterPin, LOW);
digitalWrite(coolerPin, LOW);
}
}
// funzione per formattare il tempo di attivazione in hh:mm:ss
String getFormattedTime(unsigned long startTime) {
unsigned long currentTime = millis();
unsigned long elapsedTime = (currentTime - startTime) / 1000; // convert to seconds
int hours = elapsedTime / 3600;
int minutes = (elapsedTime % 3600) / 60;
int seconds = elapsedTime % 60;
String formattedTime = String(hours) + ":" + String(minutes) + ":" + String(seconds);
return formattedTime;
}