Stazione meteo

Descrizione

Questo progetto è una stazione meteorologica per Arduino costruita con un sensore di temperatura e umidità DHT22, un sensore di umidità del suolo, un sensore pioggia e un display OLED 128x64. Il sistema legge la temperatura dell'aria, l'umidità dell'aria, l'umidità del suolo e lo stato della pioggia, quindi mostra tutti i valori sullo schermo OLED in un'interfaccia chiara e compatta. Il display utilizza la comunicazione I2C, quindi richiede solo due fili dati e lascia più pin di Arduino disponibili per i sensori. Questo progetto è utile per imparare il monitoraggio ambientale, i display OLED, la comunicazione I2C, la lettura di sensori analogici, la lettura di sensori digitali e i sistemi di stazione meteorologica basati su Arduino.

Componenti necessari:

Schema:

Circuit Scheme

Codice:

weather_station.ino
// https://nemiatools.com
#include <Wire.h> // Include libreria I2C
#include <Adafruit_GFX.h> // Include libreria grafica
#include <Adafruit_SSD1306.h> // Include libreria OLED
#include <DHT.h> // Include libreria DHT

#define SCREEN_WIDTH 128 // Definisci larghezza OLED
#define SCREEN_HEIGHT 64 // Definisci altezza OLED
#define OLED_RESET -1 // Disabilita pin reset
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Crea display OLED

#define DHTPIN 8 // Definisci pin DHT
#define DHTTYPE DHT22 // Definisci tipo sensore DHT
DHT dht(DHTPIN, DHTTYPE); // Crea oggetto DHT
#define SOIL_PIN A0 // Definisci pin suolo
#define RAIN_PIN 6 // Definisci pin pioggia
#define RAIN_ACTIVE LOW // Definisci stato attivo pioggia
const int SOIL_DRY = 0; // Imposta valore secco
const int SOIL_WET = 1023; // Imposta valore bagnato

int readSoilPercent() { // Legge percentuale terreno
  long total = 0; // Memorizza somma letture
  for (byte i = 0; i < 10; i++) { // Ripete dieci letture
    total += analogRead(SOIL_PIN); // Aggiunge lettura terreno
    delay(5); // Attende brevemente
  } 
  int value = total / 10; // Calcola valore medio
  int percentage = map(value, SOIL_DRY, SOIL_WET, 0, 100); // Converte in percentuale
  percentage = constrain(percentage, 0, 100); // Limita intervallo percentuale
  return percentage; // Restituisce percentuale terreno
} 

void setup() { 
  Wire.begin(); // Avvia bus I2C
  dht.begin(); // Avvia sensore DHT
  pinMode(RAIN_PIN, INPUT_PULLUP); // Imposta ingresso pioggia
  uint8_t oledAddress = 0x3C; // Memorizza indirizzo OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, oledAddress)) { // Inizializza display OLED
    while (true) { // Ferma in caso di errore
    } 
  }

  display.clearDisplay(); // Pulisce buffer display
  display.setTextColor(SSD1306_WHITE); // Imposta colore testo
  display.setTextSize(1); // Imposta dimensione testo
  display.setCursor(0, 0); // Imposta posizione testo
  display.println(F("Weather station")); // Stampa testo titolo
  display.println(F("Starting...")); // Stampa testo avvio
  display.display(); // Aggiorna schermo OLED
  delay(1500); // Attende prima del loop
} 

void loop() {
  float tempC = dht.readTemperature(); // Legge temperatura in Celsius
  float airHumidity = dht.readHumidity(); // Legge umidita aria
  int soilHumidity = readSoilPercent(); // Legge umidita terreno
  bool isRaining = digitalRead(RAIN_PIN) == RAIN_ACTIVE; // Legge stato pioggia

  display.clearDisplay(); // Pulisce buffer display
  display.setTextSize(1); // Imposta dimensione testo
  display.setCursor(0, 0); // Imposta posizione testo
  display.println(F("WEATHER STATION")); // Stampa titolo schermata
  display.println(F("by nemiatools.com")); // Stampa testo autore
  display.println(); // Stampa riga vuota

  if (isnan(tempC) || isnan(airHumidity)) { // Controlla errore DHT
    display.println(F("DHT11 error")); // Stampa errore DHT
  } else { // Mostra valori validi
    display.print(F("Temp: ")); // Stampa etichetta temperatura
    display.print(tempC, 1); // Stampa valore temperatura
    display.println(F(" C")); // Stampa unita Celsius
    display.print(F("Air humidity: ")); // Stampa etichetta aria
    display.print(airHumidity, 0); // Stampa valore umidita
    display.println(F(" %")); // Stampa unita percentuale
  } 
  
  display.print(F("Soil humidity: ")); // Stampa etichetta terreno
  display.print(soilHumidity); // Stampa valore terreno
  display.println(F(" %")); // Stampa unita percentuale

  display.print(F("Rain: ")); // Stampa etichetta pioggia
  if (isRaining) { // Controlla stato pioggia
    display.println(F("YES")); // Stampa pioggia si
  } else { // Gestisce assenza pioggia
    display.println(F("NO")); // Stampa pioggia no
  }
  display.display(); // Aggiorna schermo OLED

  delay(2000); // Attende due secondi
}

Come funziona:

Questo progetto funziona come una piccola stazione meteorologica per Arduino. Raccoglie dati da diversi sensori e mostra i risultati su un display OLED 128x64. Il sistema misura la temperatura, l'umidità dell'aria, l'umidità del suolo e il rilevamento della pioggia.

Le prime librerie importanti sono #include <Wire.h>, #include <Adafruit_GFX.h>, #include <Adafruit_SSD1306.h> e #include <DHT.h>.

La libreria Wire.h abilita la comunicazione I2C. I2C è il protocollo di comunicazione usato dal display OLED. Permette ad Arduino di controllare il display usando solo due fili principali di comunicazione: SDA e SCL. Questo è molto utile perché mantiene il cablaggio semplice e lascia pin di Arduino disponibili per altri sensori.

La libreria Adafruit_GFX.h è la libreria grafica generale. Fornisce le funzioni base di disegno e testo usate dai display Adafruit, come impostare la posizione del cursore, cambiare la dimensione del testo, stampare testo e gestire grafiche semplici.

La libreria Adafruit_SSD1306.h è la libreria driver specifica per i display OLED basati sul controller SSD1306. L'SSD1306 è il chip che gestisce molti comuni display OLED 128x64. Questa libreria dice ad Arduino come inizializzare il display e come inviare testo o grafica al display.

La libreria DHT.h permette ad Arduino di comunicare con i sensori DHT di temperatura e umidità. In questo codice, il tipo di sensore scelto è il DHT22, che può misurare sia la temperatura dell'aria sia l'umidità dell'aria.

Le righe #define SCREEN_WIDTH 128 e #define SCREEN_HEIGHT 64 definiscono la risoluzione dell'OLED. Questo significa che il display ha 128 pixel in orizzontale e 64 pixel in verticale. La riga #define OLED_RESET -1 significa che il pin di reset dell'OLED non viene usato separatamente da Arduino.

La riga Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); crea l'oggetto display OLED. Dice al programma che il display è da 128x64 pixel e che comunica tramite il bus I2C gestito da Wire.

Una cosa importante di questa libreria OLED è che usa un buffer del display. Quando il codice scrive testo con funzioni come display.print() o display.println(), il testo viene prima preparato in memoria. Lo schermo OLED reale viene aggiornato solo quando il codice chiama display.display();. Questo rende l'aggiornamento dello schermo più pulito ed evita testo parziale o sfarfallio.

Le righe #define DHTPIN 8 e #define DHTTYPE DHT22 definiscono il pin del sensore DHT e il modello del sensore. La riga DHT dht(DHTPIN, DHTTYPE); crea l'oggetto del sensore DHT, così Arduino può leggere temperatura e umidità dal pin 8.

La riga #define SOIL_PIN A0 definisce il pin analogico usato dal sensore di umidità del suolo. Questo sensore fornisce un valore analogico che cambia in base a quanto il terreno è bagnato o asciutto.

Le righe #define RAIN_PIN 6 e #define RAIN_ACTIVE LOW definiscono l'ingresso del sensore pioggia. In questo progetto, la pioggia viene rilevata quando l'uscita del sensore pioggia diventa LOW. Per questo il codice in seguito controlla se la lettura digitale è uguale a RAIN_ACTIVE.

I valori const int SOIL_DRY = 0; e const int SOIL_WET = 1023; vengono usati per convertire la lettura del sensore del suolo in una percentuale. In questo codice, 0 significa completamente asciutto e 1023 significa completamente bagnato.

La funzione int readSoilPercent() legge il livello di umidità del suolo e lo converte in una percentuale. Invece di usare una sola lettura, il codice esegue dieci letture con analogRead(SOIL_PIN) e calcola la media. Questo rende il risultato più stabile e riduce le piccole variazioni casuali del sensore.

La riga int percentage = map(value, SOIL_DRY, SOIL_WET, 0, 100); converte il valore analogico grezzo in una percentuale da 0% a 100%. Poi percentage = constrain(percentage, 0, 100); assicura che il valore finale non scenda mai sotto 0 o superi 100.

All'interno di setup(), la riga Wire.begin(); avvia il bus I2C, così Arduino può comunicare con il display OLED. La riga dht.begin(); avvia il sensore DHT22.

La riga pinMode(RAIN_PIN, INPUT_PULLUP); configura il pin del sensore pioggia come ingresso con la resistenza di pull-up interna di Arduino attivata. Questo significa che il pin rimane normalmente HIGH e diventa LOW quando il sensore pioggia si attiva.

La riga uint8_t oledAddress = 0x3C; memorizza l'indirizzo I2C del display OLED. Molti display OLED SSD1306 usano l'indirizzo 0x3C. La riga display.begin(SSD1306_SWITCHCAPVCC, oledAddress) inizializza l'OLED usando quell'indirizzo.

Se il display OLED non viene trovato, il codice entra in while (true) e si ferma lì. Questo impedisce al resto del programma di continuare senza un display funzionante.

La schermata iniziale viene creata con display.clearDisplay();, display.setTextColor(SSD1306_WHITE);, display.setTextSize(1); e display.setCursor(0, 0);. Queste righe cancellano il buffer, impostano il testo bianco, scelgono la dimensione del testo e posizionano il cursore nell'angolo in alto a sinistra.

Le righe display.println(F("Weather station")); e display.println(F("Starting...")); scrivono il testo di avvio nel buffer del display. La riga display.display(); infine invia il buffer allo schermo OLED, rendendo visibile il testo.

All'interno di loop(), la riga float tempC = dht.readTemperature(); legge la temperatura in gradi Celsius. La riga float airHumidity = dht.readHumidity(); legge la percentuale di umidità dell'aria.

La riga int soilHumidity = readSoilPercent(); legge il sensore di umidità del suolo e converte il valore in una percentuale. La riga bool isRaining = digitalRead(RAIN_PIN) == RAIN_ACTIVE; controlla se il sensore pioggia è attivo.

Prima di stampare i nuovi valori, il codice usa display.clearDisplay();. Questo cancella il contenuto precedente dello schermo dal buffer, così le nuove letture possono essere mostrate chiaramente.

Il display stampa poi il titolo con display.println(F("WEATHER STATION")); e il testo dell'autore con display.println(F("by nemiatools.com"));.

La condizione if (isnan(tempC) || isnan(airHumidity)) controlla se la lettura del DHT22 è fallita. Se il sensore non restituisce valori validi, l'OLED mostra un messaggio di errore. Altrimenti, il codice stampa sul display i valori di temperatura e umidità dell'aria.

L'umidità del suolo viene stampata con display.print(F("Soil humidity: ")), display.print(soilHumidity) e display.println(F(" %")). Questo mostra l'umidità del terreno come semplice percentuale.

Lo stato della pioggia viene stampato con display.print(F("Rain: ")). Se isRaining è true, il display mostra YES. Altrimenti mostra NO.

La riga display.display(); è essenziale perché aggiorna lo schermo OLED fisico con tutto il testo preparato nel buffer. Senza questa riga, i valori verrebbero scritti in memoria ma non apparirebbero sul display.

Infine, delay(2000); attende due secondi prima di prendere nuove letture. Questo dà allo schermo un ritmo di aggiornamento stabile ed evita di aggiornare i dati meteorologici troppo velocemente.

Nel complesso, Arduino legge i sensori ambientali, prepara le informazioni nel buffer del display OLED e aggiorna lo schermo ogni due secondi. Il display OLED rende il progetto compatto e leggibile, mentre l'interfaccia I2C mantiene il cablaggio semplice e lascia più pin disponibili per futuri sensori o moduli.

Per l'uso reale, i valori del sensore di umidità del suolo potrebbero richiedere una calibrazione. Alcuni sensori di umidità del suolo forniscono valori più alti quando il terreno è bagnato, mentre altri forniscono valori più bassi quando è bagnato. Se la percentuale appare invertita, i valori di SOIL_DRY e SOIL_WET dovrebbero essere scambiati o regolati.

Sviluppi futuri:

Questo progetto potrebbe essere ampliato aggiungendo un anemometro per misurare l'intensità del vento e visualizzare la velocità del vento sul display OLED. Un altro miglioramento utile sarebbe utilizzare l'uscita analogica del sensore di pioggia invece della sola uscita digitale, in modo che il sistema possa rilevare non solo se sta piovendo, ma anche l'intensità della pioggia. Con queste aggiunte, il progetto diventerebbe una stazione meteorologica più completa, in grado di monitorare temperatura, umidità, umidità del suolo, livello di pioggia e condizioni del vento.