// --- MACRO PER GESTIONE INTERRUPT ---
#define disableInterrupts() cli()
#define enableInterrupts() sei()
// -------------------------------------------------------------
// Struttura e Variabili dei Sensori DHT22
// -------------------------------------------------------------
typedef struct {
uint8_t pin; // Pin a cui è collegato il sensore
float temperatura; // Temperatura in °C
float umidita; // Umidità in %
} SensoreDHT22;
const uint8_t SENSOR_NULL = 255;
const uint8_t PIN_SENSORE_ESTERNO = 2;
const uint8_t PIN_SENSORE_INTERNO = 3;
SensoreDHT22 sensoreEsterno = { PIN_SENSORE_ESTERNO, SENSOR_NULL, SENSOR_NULL };
SensoreDHT22 sensoreInterno = { PIN_SENSORE_INTERNO, SENSOR_NULL, SENSOR_NULL };
// -------------------------------------------------------------
// Variabili di Stato
// -------------------------------------------------------------
bool allarmeAttivo = false;
bool serrandaAperta = false;
float percAperturaSerranda = 0.0;
// -------------------------------------------------------------
// Parametri Ambientali Desiderati (serranda)
// -------------------------------------------------------------
float tempMinSerranda = 15.0; // Temperatura minima desiderata (°C)
float tempMaxSerranda = 35.0; // Temperatura massima desiderata (°C)
const float UMID_MIN_SERRANDA = 30.0; // Umidità minima desiderata (%)
const float UMID_MAX_SERRANDA = 70.0; // Umidità massima desiderata (%)
float tempPercepitaSerranda = 0.0;
float umiditaPercepitaSerranda = 0.0;
// -------------------------------------------------------------
// Funzioni Serial (ridotte)
// -------------------------------------------------------------
#define CANALE_SERIAL_LIBERO() (UCSR0A & (1 << UDRE0))
bool serialAvailable() { return (UCSR0A & (1 << RXC0)); }
void serialBegin(unsigned int baudrate) {
uint16_t ubrr = (F_CPU / (16UL * baudrate)) - 1;
UBRR0H = (uint8_t)(ubrr >> 8);
UBRR0L = (uint8_t)ubrr;
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
void serialPrintChar(char c) { while (!CANALE_SERIAL_LIBERO()); UDR0 = c; }
void serialPrint(const char *msg) { while(*msg) { serialPrintChar(*msg++); } }
void serialPrintln(const char *msg) { serialPrint(msg); serialPrintChar('\n'); }
void serialPrintFloat(float n) { char buf[20]; dtostrf(n, 4, 2, buf); serialPrint(buf); }
void serialPrintInt(uint16_t n) { char buf[20]; itoa(n, buf, 10); serialPrint(buf); }
char serialReadChar() { while(!serialAvailable()); return UDR0; }
#define INPUT_LENGTH 15
//funzione per leggere input da serial
bool serialReadInput(char* inputBuffer) {
//essendo static non perde il valore tra le varie chiamate
static uint8_t index = 0;//memorizziamo indice del carattere ricevuto
while(serialAvailable()){
//finche' riceviamo dei bit da serial
char c = serialReadChar();//leggiamo il carattere ricvuto
serialPrintChar(c);//lo stampiamo
if(c == '\n'){//quando l'utente preme "invio"
inputBuffer[index] = '\0';//terminiamo la stringa con il carattere terminatore '\0'
index = 0;
return true;//lettura riuscita
} else if(index < INPUT_LENGTH - 1) {
inputBuffer[index++] = c;//memorizziamo il carattere ricevuto
}
}
return false;//lettura non riuscita
}
// Funzione per mostrare il menu con la lista dei comandi eseguibili dall'utente
void mostraMenu() {
const char* menu = "Lista comandi:\n"
"----------------------------\n"
"TEMP_MIN_UP - Aumenta temp min serranda di 5 C\n"
"TEMP_MIN_DOWN - Diminuisci temp min serranda di 5 C\n"
"TEMP_MAX_UP - Aumenta temp max serranda di 5 C\n"
"TEMP_MAX_DOWN - Diminuisci temp max serranda di 5 C\n"
"MENU - Mostra il menu\n"
"----------------------------\n";
serialPrintln(menu);
}
// Funzioni Temporizzate e I/O
volatile uint32_t contatoreMillis = 0;
//funzione per contare i millisecondi
uint32_t millis_custom() {
uint32_t t;
disableInterrupts();
t = contatoreMillis;
enableInterrupts();
return t;
}
//funzione per contare i microsecondi
uint32_t micros_custom() {
uint16_t cnt;
disableInterrupts();
cnt = TCNT1;// usiamo il contatore del timer1
enableInterrupts();
return cnt / 2; // ~0.5 µs per tick
}
//Funzione per delay in millisecondi
void custom_delay_ms(uint16_t ms) {
uint32_t start = millis_custom(); while(millis_custom() - start < ms);
}
//Funzione per delay in microsecondi
void custom_delay_us(uint16_t us) {
uint32_t start = micros_custom(); while(micros_custom() - start < us);
}
// -------------------------------------------------------------
// Funzioni di I/O dirette (pinMode, digitalWrite, digitalRead)
// -------------------------------------------------------------
//funzione per settare stato dei pin (OUTPUT o INPUT)
void custom_pinMode(uint8_t pin, uint8_t mode) {
if(mode == OUTPUT) {
if(pin < 8) DDRD |= (1 << pin);
else DDRB |= (1 << (pin - 8));//-8 per la porta b
} else {
if(pin < 8) DDRD &= ~(1 << pin);
else DDRB &= ~(1 << (pin - 8));//-8 per la porta b
}
}
//funzione per settare stato dei pin (HIGH o LOW)
void custom_digitalWrite(uint8_t pin, uint8_t state) {
if(pin < 8) {
if(state == HIGH) PORTD |= (1 << pin);
else PORTD &= ~(1 << pin);
} else {
if(state == HIGH) PORTB |= (1 << (pin - 8));//-8 per la porta b
else PORTB &= ~(1 << (pin - 8));//-8 per la porta b
}
}
//funzione per leggere stato dei pin (HIGH o LOW)
uint8_t custom_digitalRead(uint8_t pin) {
if(pin < 8) return (PIND & (1 << pin)) ? HIGH : LOW;
else return (PINB & (1 << (pin - 8))) ? HIGH : LOW;//-8 per la porta b
}
// -------------------------------------------------------------
// Gestione Motore
// -------------------------------------------------------------
const uint8_t PIN_MOTORE_BM = 8, PIN_MOTORE_BP = 9, PIN_MOTORE_AP = 10, PIN_MOTORE_AM = 11;
const uint8_t NUM_PIN_MOTORE = 4;
const uint8_t listaPinMotore[NUM_PIN_MOTORE] = { PIN_MOTORE_BM, PIN_MOTORE_BP, PIN_MOTORE_AP, PIN_MOTORE_AM };
const uint8_t sequenzePinMotore[NUM_PIN_MOTORE][NUM_PIN_MOTORE] = {
{HIGH, LOW, LOW, HIGH},
{LOW, HIGH, LOW, HIGH},
{LOW, HIGH, HIGH, LOW},
{HIGH, LOW, HIGH, LOW}
};//Sequenza di passi necessaria per far girare il motore
void configuraMotore() {
for(uint8_t i = 0; i < NUM_PIN_MOTORE; i++){
custom_pinMode(listaPinMotore[i], OUTPUT);//impostiamo i pin del motore come OUTPUT
custom_digitalWrite(listaPinMotore[i], LOW);//impostiamo i pin del motore a LOW
}
}
enum DirezioneGiroMotore { ORARIO, ANTI_ORARIO };
bool motoreInMovimento = false;
const uint16_t POS_MAX_MOTORE = 500;//num passi a serranda completamente aperta
const uint16_t VELOCITA_MOTORE = 200;
const uint16_t delayStepMotoreMs = 1000 / VELOCITA_MOTORE;
int16_t posizioneMotore = 0;//attuale numero di passo del motore
uint8_t getIndicePassoMotore(uint16_t i, uint8_t direzione) {
//calcoliamo l'indice del passo in base alla direzione (partendo dalla fine se anti ORARIO)
uint8_t step = (i - 1) % NUM_PIN_MOTORE;
uint8_t ultimo = NUM_PIN_MOTORE - 1;
return (direzione == ORARIO) ? ultimo - step : step;
}
void giraMotoreFinoA(uint16_t numPassi, uint8_t direzione) {
if(motoreInMovimento || numPassi == 0) return;
motoreInMovimento = true;
if(direzione != ORARIO && direzione != ANTI_ORARIO) direzione = ORARIO;
for(uint16_t i = 0; i <= (numPassi + 1); i++){
if(i == (numPassi + 1) && direzione == ANTI_ORARIO) break;
uint8_t indice = getIndicePassoMotore(i, direzione);
for(uint8_t p = 0; p < NUM_PIN_MOTORE; p++)
custom_digitalWrite(listaPinMotore[p], sequenzePinMotore[indice][p]);
custom_delay_ms(delayStepMotoreMs);
}
motoreInMovimento = false;
}
// Gestione LED e Serranda
const uint8_t PIN_CHIUSURA = 5,PIN_APERTURA = 6,PIN_ALLARME = 7;
bool soddisfaClimaSerranda(float tempPercepita, float umidPercepita) {
//controlliamo se i dati percepiti sono nel range di clima definito
return tempPercepita >= tempMinSerranda && tempPercepita <= tempMaxSerranda &&
umidPercepita >= UMID_MIN_SERRANDA && umidPercepita <= UMID_MAX_SERRANDA;
}
void aggiornaClimaPercepitoSerranda(float nuovaTempPercepita, float nuovaUmiditaPercepita) {
tempPercepitaSerranda = nuovaTempPercepita;
umiditaPercepitaSerranda = nuovaUmiditaPercepita;
}
void mostraDatiSistema() {
serialPrintln("------------------------------------------------------");
serialPrint("Temp da mantenere (C): ");
serialPrintFloat(tempMinSerranda);
serialPrint(" - ");
serialPrintFloat(tempMaxSerranda);
serialPrint(", Percepita: ");
serialPrintFloat(tempPercepitaSerranda);
serialPrintln(" C");
serialPrint("Umid da mantenere (%): ");
serialPrintFloat(UMID_MIN_SERRANDA);
serialPrint(" - ");
serialPrintFloat(UMID_MAX_SERRANDA);
serialPrint(", Percepita: ");
serialPrintFloat(umiditaPercepitaSerranda);
serialPrintln(" %");
if(serrandaAperta) {
serialPrint("Serranda aperta al: ");
serialPrintFloat(percAperturaSerranda * 100);
serialPrint("% , Posizione: ");
serialPrintInt(posizioneMotore);
serialPrintln(" ");
} else {
if(allarmeAttivo) serialPrintln("Serranda chiusa (Allarme attivo)");
else {
bool climaSerrandaOk = soddisfaClimaSerranda(tempPercepitaSerranda, umiditaPercepitaSerranda);
if(climaSerrandaOk) serialPrintln("Serranda chiusa (Ambiente rispetta clima desiderato)");
else serialPrintln("Serranda chiusa (Ambiente non rispetta i valori desiderati)");
}
}
serialPrintln("Scrivi 'MENU' per mostrare lista comandi.");
serialPrintln("------------------------------------------------------");
}
//impostiamo tutti i pin a LOW
void spegniTuttiLed() {
custom_digitalWrite(PIN_CHIUSURA, LOW);
custom_digitalWrite(PIN_APERTURA, LOW);
custom_digitalWrite(PIN_ALLARME, LOW);
}
float calcolaPercAperturaSerranda() {
float tempInterna = sensoreInterno.temperatura;
float umiditaInterna = sensoreInterno.umidita;
float tempEsterna = sensoreEsterno.temperatura;
float umiditaEsterna = sensoreEsterno.umidita;
if (soddisfaClimaSerranda(tempInterna, umiditaInterna)) {
aggiornaClimaPercepitoSerranda(tempInterna, umiditaInterna);
serrandaAperta = false;
allarmeAttivo = false;
return 0.0;
}
static const uint8_t RANGE_TEMP_DHT22 = 120;//DHT22: -40 a 80°C
static const uint8_t RANGE_UMID_DHT22 = 100;//DHT22: 0 a 100%
// Normalizzazione rispetto al range del DHT22
float percTemp = (tempInterna - tempEsterna) / RANGE_TEMP_DHT22;
if (percTemp > 1.0) percTemp = 1.0;
float percUmid = (umiditaInterna - umiditaEsterna) / RANGE_UMID_DHT22;
if (percUmid > 1.0) percUmid = 1.0;
const float PESO_CLIMA_INTERNO = 0.3;
const float PESO_CLIMA_ESTERNO = 0.7;
// Pesi per interpolazione (almeno 30% interno)
float influenzaInternaTemp = PESO_CLIMA_INTERNO + percTemp * PESO_CLIMA_ESTERNO;
float influenzaInternaUmid = PESO_CLIMA_INTERNO + (1.0 - percUmid) * PESO_CLIMA_ESTERNO;
// Calcolo dei valori percepiti
float tempPercepitaDaAperta = influenzaInternaTemp * tempInterna + (1.0 - influenzaInternaTemp) * tempEsterna;
float umidPercepitaDaAperta = influenzaInternaUmid * umiditaInterna + (1.0 - influenzaInternaUmid) * umiditaEsterna;
// Valore di apertura massimo tra i due valori percepiti
float aperturaPerc = percTemp > percUmid ? percTemp : percUmid;
// Controllo se il clima percepito rientra nei range desiderati
if (soddisfaClimaSerranda(tempPercepitaDaAperta, umidPercepitaDaAperta)) {
aggiornaClimaPercepitoSerranda(tempPercepitaDaAperta, umidPercepitaDaAperta);
serrandaAperta = true;
allarmeAttivo = false;
return aperturaPerc;//Apertura necessaria per raggiungere il clima desiderato
} else {
aggiornaClimaPercepitoSerranda(tempInterna, umiditaInterna);
serrandaAperta = false;
allarmeAttivo = true;
return -1;// Allarme: non è possibile raggiungere il range desiderato anche aprendo la serranda
}
}
void aggiornaAllarme(bool attivo){
allarmeAttivo = attivo;
custom_digitalWrite(PIN_ALLARME, attivo ? HIGH : LOW);// Accendiamo o spegniamo il led di allarme
}
void spegniAllarme() { aggiornaAllarme(false); }
void accendiAllarme() { aggiornaAllarme(true); }
void apriSerranda(float percApertura){
// Calcola la nuova posizione desiderata in base alla percentuale di apertura.
// POS_MAX_MOTORE rappresenta il numero massimo di passi del motore, corrispondente a un'apertura completa.
uint16_t nuovaPosizione = (uint16_t)(percApertura * (POS_MAX_MOTORE));
// numero di passi da compiere, differenza tra la nuova posizione e quella attuale.
int16_t numPassi = nuovaPosizione - posizioneMotore;
// direzione di rotazione: ORARIO se il numero di passi è positivo, altrimenti ANTI_ORARIO.
uint8_t direzione = (numPassi >= 0) ? ORARIO : ANTI_ORARIO;
// Calcola il numero assoluto di passi da compiere, eliminando il segno per ottenere il valore positivo.
uint16_t numPassiAssoluti = (numPassi >= 0) ? numPassi : -numPassi;
// Se è necessario muovere il motore (passi > 0)
if(numPassiAssoluti > 0){
// Rotazione del motore per il numero di passi richiesto nella direzione specificata.
giraMotoreFinoA(numPassiAssoluti, direzione);
// Imposta lo stato della serranda come aperta.
serrandaAperta = true;
// Spegne tutti i LED, probabilmente per resettare lo stato visivo prima di segnalare l'azione corrente.
spegniTuttiLed();
// Accende il LED associato all'apertura della serranda per indicare visivamente che l'apertura è in corso.
custom_digitalWrite(PIN_APERTURA, HIGH);
}
posizioneMotore = nuovaPosizione;// Aggiorna la posizione attuale del motore
}
void chiudiSerranda(){
serrandaAperta = false;
custom_digitalWrite(PIN_APERTURA, LOW);// Spegniamo il led di apertura
custom_digitalWrite(PIN_CHIUSURA, HIGH);// Accendiamo il led di chiusura
giraMotoreFinoA(posizioneMotore, ANTI_ORARIO);
posizioneMotore = 0;
}
void gestisciCambioDatiSensori(){
if(motoreInMovimento) return;
if(sensoreInterno.temperatura == SENSOR_NULL || sensoreInterno.umidita == SENSOR_NULL) return;
percAperturaSerranda = calcolaPercAperturaSerranda();
// Caso in cui non è possibile raggiungere il range desiderato: allarme
bool allarmeDaAttivare = percAperturaSerranda == -1.0;
if(allarmeDaAttivare){
accendiAllarme();
chiudiSerranda();
return;
}
bool serrandaDaChiudere = percAperturaSerranda == 0.0;
// Se la serranda deve rimanere chiusa (percentuale 0)
if(serrandaDaChiudere){ spegniTuttiLed(); chiudiSerranda(); return; }
// Caso normale: apertura parziale per migliorare le condizioni
apriSerranda(percAperturaSerranda);
}
// -------------------------------------------------------------
// Gestione DHT22 e ISR
// -------------------------------------------------------------
// Funzione che attende che il pin raggiunga uno stato specifico entro un tempo massimo (timeout) in microsecondi
bool waitForPinState(uint8_t pin, uint8_t stato, uint32_t timeout_us) {
uint32_t start = micros_custom();//tempo iniziale in microsecondi per gestire il timeout
// Cicla finché lo stato letto dal pin non corrisponde allo stato desiderato
while(custom_digitalRead(pin) != stato) {
// Se il tempo trascorso supera il timeout, esce e ritorna false
if(micros_custom() - start > timeout_us) return false;
}
return true;//stati raggiunto nel tempo previsto
}
// Funzione che attende la sequenza di start richiesta dal sensore DHT22
bool dht22_waitForStartSequence(uint8_t pin) {
// Attende che il pin scenda a LOW entro 100 microsecondi
if(!waitForPinState(pin, LOW, 100)) return false;
// Attende che il pin salga a HIGH entro 100 microsecondi
if(!waitForPinState(pin, HIGH, 100)) return false;
// Attende nuovamente che il pin scenda a LOW entro 100 microsecondi
if(!waitForPinState(pin, LOW, 100)) return false;
return true;//transizioni avvenute come necessario per corretto funzionamento del dht22
}
bool readDHT22(SensoreDHT22 *s) {
const uint8_t NUM_BYTE = 5;//num di byte attesi dalla trasmissione del sensore (5 byte = 40 bit)
// Array per contenere i dati letti
uint8_t dati[NUM_BYTE] = {0, 0, 0, 0, 0};
uint8_t pin = s->pin;
// Disabilita gli interrupt per assicurare una gestione precisa dei tempi critici
disableInterrupts();
// Configura il pin in modalità OUTPUT per inviare il segnale iniziale al DHT22
custom_pinMode(pin, OUTPUT);
// Porta il pin a LOW per almeno 20 ms per segnalare al sensore l'inizio della comunicazione
custom_digitalWrite(pin, LOW);
custom_delay_ms(20);
// Porta il pin a HIGH per 40 microsecondi: questo segnala al sensore di prepararsi a rispondere
custom_digitalWrite(pin, HIGH);
custom_delay_us(40);
// Imposta il pin in modalità INPUT per leggere la risposta dal sensore
custom_pinMode(pin, INPUT);
// Attesa che il sensore invii la sequenza di start; se fallisce, riabilita gli interrupt e ritorna false
if(!dht22_waitForStartSequence(pin)){enableInterrupts(); return false;}
const uint8_t NUM_BIT = NUM_BYTE * 8;// numero totale di bit da leggere (cioè 5 byte * 8 = 40bit)
// Timeout per l'attesa di ciascun bit (in microsecondi) per evitare blocchi in attesa infinita
const uint32_t TIMEOUT_BIT = 100;
// Ciclo per leggere ciascuno dei 40 bit inviati dal sensore
for(uint8_t i = 0; i < NUM_BIT; i++){
// Attendiamo che il pin diventi HIGH, segnale che l'invio del bit sta per iniziare
if(!waitForPinState(pin, HIGH, TIMEOUT_BIT)){enableInterrupts();return false;}
// Registra il tempo in cui il segnale HIGH inizia
uint32_t inizio = micros_custom();
// Attendiamo che il pin torni a LOW, che indica la fine del bit
if(!waitForPinState(pin, LOW, TIMEOUT_BIT)){enableInterrupts();return false;}
// Calcola la durata del segnale HIGH; questa durata differenzia un bit 0 da un bit 1
uint32_t durata = micros_custom() - inizio;
dati[i/8] <<= 1; // Sposta il byte verso sinistra per fare spazio al nuovo bit
// Se la durata supera 50 microsecondi, il bit viene interpretato come '1'
if(durata > 50) dati[i/8] |= 1;
}
// Riabilitiamo gli interrupt, terminata la fase critica di lettura dei bit
enableInterrupts();
uint8_t checksum = dati[0] + dati[1] + dati[2] + dati[3];// Calcola il checksum sommando i primi 4 byte
if(checksum != dati[4]) return false;//errore checksum
// Combina i primi due byte per ottenere il valore grezzo dell'umidità
uint16_t umidRaw = (dati[0] << 8) | dati[1];
// Combina i due byte successivi per ottenere il valore grezzo della temperatura
uint16_t tempRaw = (dati[2] << 8) | dati[3];
// Controlla il bit più significativo del valore della temperatura per determinare se è negativo
bool tempNeg = tempRaw & 0x8000;
// Ottiene il valore assoluto della temperatura eliminando il bit di segno
uint16_t tempAbs = tempRaw & 0x7FFF;
// Calcola la temperatura in gradi; divide per 10.0 per ottenere il valore corretto,
// applicando il segno negativo se indicato
float nuovaTemp = tempNeg ? -(tempAbs / 10.0) : (tempRaw / 10.0);
// Calcola l'umidità in percentuale dividendo il valore grezzo per 10.0
float nuovaUmid = umidRaw / 10.0;
// Verifica se i nuovi dati sono diversi dai dati precedentemente memorizzati (o se erano inizialmente non validi [nulli])
bool cambiati = s->temperatura == SENSOR_NULL || s->umidita == SENSOR_NULL ||
nuovaTemp != s->temperatura || nuovaUmid != s->umidita;
if(cambiati){
// Aggiorna sensori con i nuovi valori letti
s->temperatura = nuovaTemp;
s->umidita = nuovaUmid;
// Stampa i dati letti sul monitor seriale per il debug o il monitoraggio
serialPrint("Dati letti Sensore ");
serialPrint( s->pin == sensoreEsterno.pin ? "esterno: " : "interno: ");
serialPrint("T=");
serialPrintFloat(s->temperatura);
serialPrint(" C, U=");
serialPrintFloat(s->umidita);
serialPrintln(" %");
gestisciCambioDatiSensori();
mostraDatiSistema();
}
return true;//lettura avvenuta con successo
}
// Funzione per configurare i pin utilizzati dai sensori
void configuraPinSensori(){
// Imposta i pin associati ai sensori come input.
// I bit corrispondenti ai pin di 'sensoreEsterno' e 'sensoreInterno' vengono azzerati nel registro DDRD.
DDRD &= ~((1 << sensoreEsterno.pin) | (1 << sensoreInterno.pin));
// Abilita le resistenze di pull-up interne sui pin dei sensori.
// Ciò assicura che i pin leggano un livello logico alto quando non sono collegati ad altri segnali attivi.
PORTD |= (1 << sensoreEsterno.pin) | (1 << sensoreInterno.pin);
}
void leggiSensori(){
// Variabili per indicare se la lettura dei sensori è stata effettuata con successo.
bool okEsterno = false, okInterno = false;
const uint8_t TENTATIVI = 5; // Numero massimo di tentativi per la lettura dei sensori.
// Effettua la lettura dei sensori fino a TENTATIVI volte o finché entrambe le letture non risultano corrette.
for(uint8_t i = 0; i < TENTATIVI; i++){
if(!okEsterno) okEsterno = readDHT22(&sensoreEsterno);
if(!okInterno) okInterno = readDHT22(&sensoreInterno);
if(okEsterno && okInterno) break;
}
// Verifica se uno dei dati letti risulta essere nullo
bool datiNull = (sensoreEsterno.temperatura == SENSOR_NULL || sensoreInterno.temperatura == SENSOR_NULL ||
sensoreEsterno.umidita == SENSOR_NULL || sensoreInterno.umidita == SENSOR_NULL);
if(!okEsterno || !okInterno || datiNull){
accendiAllarme();
serialPrintln("Errore lettura sensori!");
}
}
// ISR e Configurazioni Timer
// Definisce l'intervallo in millisecondi (2000 ms = 2 secondi) tra le letture dei sensori
const uint32_t INTERVALLO_SENSORI = 2000;
ISR(TIMER0_COMPA_vect) {
// Incrementa il contatore dei millisecondi ad ogni interrupt del Timer0
contatoreMillis++;
// Variabile statica per memorizzare l'ultimo momento in cui sono stati letti i sensori
// Essendo statica, mantiene il valore tra le chiamate alla ISR
static uint32_t ultimaLettura = 0;
// Controlla se è trascorso l'intervallo definito tra le letture dei sensori
if (contatoreMillis - ultimaLettura >= INTERVALLO_SENSORI) {
ultimaLettura = contatoreMillis;// Aggiorna il tempo dell'ultima lettura al tempo attuale
leggiSensori();
}
}
void configuraPinDeiLed() {
// Imposta i pin collegati ai LED come uscite:
// Utilizzando l'operatore OR bit a bit, si setta a 1 il bit corrispondente a ciascun pin
DDRD |= (1 << PIN_ALLARME) | (1 << PIN_APERTURA) | (1 << PIN_CHIUSURA); // nel registro DDRD
}
void configuraTimer0PerMillis() {
// Configura il Timer0 in modalità CTC (Clear Timer on Compare Match)
TCCR0A = (1 << WGM01); // Imposta il bit WGM01 per abilitare la modalità CTC
// Configura il prescaler del Timer0 a 64 (CS01 e CS00)
TCCR0B = (1 << CS01) | (1 << CS00); // Prescaler = 64, per rallentare il clock del timer
// Imposta il valore del registro di confronto OCR0A a 249
OCR0A = 249; // Il timer scatterà ogni 1 ms, calcolato come:
// (OCR0A + 1) * (prescaler / clock CPU) = (249 + 1) * (64 / 16000000) = 1 ms
// Abilita l'interrupt per il confronto di Timer0 (OCIE0A)
TIMSK0 |= (1 << OCIE0A); // Abilita l'interrupt sul match con OCR0A
}
void configuraTimer1PerMicros() {
// Configura il Timer1 in modalità normale (no PWM, nessuna modalità speciale)
TCCR1A = 0; // Nessuna modalità PWM, configura tutti i bit a 0
// Configura il prescaler del Timer1 a 8 (CS11)
TCCR1B = (1 << CS11); // Prescaler = 8, rallenta il clock del timer per tempi più precisi
// Resetta il valore del contatore del Timer1
TCNT1 = 0; // Inizia il conteggio del timer da zero
}
void configuraTimers() {configuraTimer0PerMillis();configuraTimer1PerMicros();}
// -------------------------------------------------------------
// Setup e Loop Principale
// -------------------------------------------------------------
void setup() {
configuraPinDeiLed();
configuraMotore();
serialBegin(9600);
configuraPinSensori();
configuraTimers();
enableInterrupts();
sensoreEsterno.temperatura = sensoreEsterno.umidita = SENSOR_NULL;
sensoreInterno.temperatura = sensoreInterno.umidita = SENSOR_NULL;
serialPrintln("Setup completato.");
mostraDatiSistema();
serialPrint("\n");
mostraMenu();
uint8_t pin = serrandaAperta ? PIN_APERTURA : PIN_CHIUSURA;
custom_digitalWrite(pin, HIGH);//accendiamo il pin in base allo stato di apertura della serranda
}
//Lista comandi disponibili per l'utente
#define COMANDO_MENU "MENU"
#define COMANDO_TEMP_MIN_UP "TEMP_MIN_UP"
#define COMANDO_TEMP_MIN_DOWN "TEMP_MIN_DOWN"
#define COMANDO_TEMP_MAX_UP "TEMP_MAX_UP"
#define COMANDO_TEMP_MAX_DOWN "TEMP_MAX_DOWN"
#define str_equals(s1, s2) (strcasecmp(s1, s2) == 0)
void eseguiComando(const char *cmd) {
if (!cmd || !strlen(cmd)) return;
//In base al comando ricevuto dall'utente eseguiamo diverse azioni
if(str_equals(cmd,COMANDO_MENU)){ mostraMenu();return;}
bool comandoTempMinUp = str_equals(cmd, COMANDO_TEMP_MIN_UP);
bool comandoTempMinDown = str_equals(cmd, COMANDO_TEMP_MIN_DOWN);
bool comandoTempMaxUp = str_equals(cmd, COMANDO_TEMP_MAX_UP);
bool comandoTempMaxDown = str_equals(cmd, COMANDO_TEMP_MAX_DOWN);
if(comandoTempMinUp || comandoTempMinDown) {
// Aumenta/Diminuisce la temperatura minima della serranda di 5°C
float nuovoValore = comandoTempMinUp ? tempMinSerranda + 5.0 : tempMinSerranda - 5.0;
if(nuovoValore < 0 || nuovoValore > tempMaxSerranda) {
serialPrint("Impossibile impostare la temperatura minima a "); serialPrintFloat(nuovoValore);
serialPrintln(" C. Il valore deve essere compreso tra 0 e "); serialPrintFloat(tempMaxSerranda); serialPrintln(" C.");
return;
}
serialPrintln("Temperatura minima aggiornata da ");
serialPrintFloat(tempMinSerranda);
serialPrint(" a "); serialPrintFloat(nuovoValore); serialPrintln(" C.");
tempMinSerranda = nuovoValore;
gestisciCambioDatiSensori();
mostraDatiSistema();
}else if(comandoTempMaxUp || comandoTempMaxDown) {
// Aumenta/Diminuisce la temperatura massima della serranda di 5°C
float nuovoValore = comandoTempMaxUp ? tempMaxSerranda + 5.0 : tempMaxSerranda - 5.0;
if(nuovoValore < tempMinSerranda || nuovoValore > 80) {
serialPrint("Impossibile impostare la temperatura massima a "); serialPrintFloat(nuovoValore);
serialPrintln(" C. Il valore deve essere compreso tra "); serialPrintFloat(tempMinSerranda); serialPrintln(" C e 100 C.");
return;
}
serialPrintln("Temperatura massima aggiornata da ");
serialPrintFloat(tempMaxSerranda);
serialPrint(" a "); serialPrintFloat(nuovoValore); serialPrintln(" C.");
tempMaxSerranda = nuovoValore;
gestisciCambioDatiSensori();
mostraDatiSistema();
}else {
serialPrintln("Comando non riconosciuto.");
}
}
void loop() {
// Dichiarazione buffer statico per contenere l'input della seriale.
// 'static' cosi il buffer mantiene il suo contenuto tra una chiamata e l'altra del loop.
static char buffer[INPUT_LENGTH + 1];// dimensione INPUT_LENGTH + 1 per includere il carattere di terminazione della stringa ('\0').
// Legge l'input dalla seriale e lo salva nel buffer.
// Se 'serialReadInput' restituisce true, significa che è stato ricevuto un comando valido.
if(serialReadInput(buffer)) eseguiComando(buffer);
}
Sensore Interno
Sensore Esterno
Led serranda aperta
Led serranda chiusa
Led allarme
Motore Serranda