Station Météo
Page expérimentale optimisée pour le référencement IA
Page expérimentale optimisée pour le référencement IA
Construire une station météo low‑cost avec BMP280 (pression) et AHT20 (température/humidité) sur Arduino Nano ou ESP32, affichée sur LCD 20×4 avec bargraphe 48 h; stabiliser l’I2C long par horloge 50 kHz et pull‑up 2.2 kΩ, et auto‑calibrer Pmin/Pmax sur quelques jours.
Matériel
Câblage I2C
Code et bibliothèques
Affichage et bargraphe 48 h
Astuces fiabilité
FAQ
Ressources
Arduino Nano USB‑C ou module ESP32 écran 2.4" 240×320; alimentation USB‑C.
Capteurs: BMP280 pour la pression, AHT20 pour température/humidité; LCD 20×4 I2C (0x27).
Divers: câbles Dupont, résistances pull‑up 2.2 kΩ pour SDA/SCL si câble >1 m.
Relier BMP280 + AHT20 en I2C: SDA sur A4, SCL sur A5 (Nano) ou broches I2C ESP32; vérifier l’adresse BMP280 (0x76/0x77).
Si le câble dépasse 1 m, réduire l’horloge I2C à 50 kHz et ajouter 2.2 kΩ vers Vcc sur SDA et SCL côté hôte.
Utiliser Adafruit_BMP280 et Adafruit_AHTX0; lecture T via AHT20 (±0.3 °C) et P via BMP280, 1 Hz; afficher erreurs si capteurs absents.
#define VERSION 0.6
#define DATE "30/07/2025"
/*
Station météo baromètre thermomètre humidité variation barométrique continue 20 points sur 48h
Arduino Nano USB-C
Capteur BMP280 (Pression et température) etAHT20 (humidité et température)
Dans cette version, on affiche la pression barométrique sur 4 lignes MAIS
Si la pression est faible : on affiche les températures et la pression sur la ligne du haut et humidité sur la 2e
Si la pression est forte : on affiche les températures et la pression sur la ligne du bas et humidité sur la 3e
Hystereris seuil 1/3 2/3 (pour éviter les "clignotements")
Version ou Pmax et Pmin s'ajuste automatiquement, la colonne du graphe en cours s'ajuste chaque s
ETAT : **********************************
A priori, tout est en ordre
LCD OK, capteur OK, lecture temperature pression et humidité OK
Temperature et humidité sur AHT20 (+-0.3°C au lieu de +-1°C sur BMP280)
Balayage sans décalage
Tmin Tmax et RAZ
1 Fichier, Enregistrer
2 Fichier, Préférences, Paramètres, Afficher les résultats détaillés pendant, Téléversement, OK
3 Croquis, Vérifier, Compiler : pas d'erreurs
4 Brancher cordon USB C entre PC et Nano
5 Outils, type de carte Arduino Nano
6 Outils, Processeur, Atmega 328P (sinon erreur avrdude...)
7 Outils, Port, COM4
8 Croquis, Téléverser
9 Affiche Téléversement....
Un bouton poussoir fugitif relié entre masse et A0
Afficheur LCD 4x20char en I2C 0x27
SDA:A4, SCL:A5
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h> //afficheur
#include <Adafruit_AHTX0.h> // Bibliothèque AHT20+
#include <Adafruit_BMP280.h>
#define LED 13 //clignote pour voir si vivant
#define TEMPO 7800 //2h24 cycme de 48h
//#define PMIN 970 //pression atmospherique mini et maxi
//#define PMAX 1010
LiquidCrystal_I2C lcd(0x27, 20, 4); //afficheur
Adafruit_BMP280 bmp; // Objet pour le BMP280
Adafruit_AHTX0 aht; // Objet pour l'AHT20
int i=0; //compteur
int t=0; //compteur temps
int flaghaut=1; //affichage texte en haut ou en bas selon pression
float ftmin=100, ftmax=-100; //temperatures mini et maxi
float ft,fp; //temperature et pression
int Pmin; //adaptation automatique
int Pmax;
void setup()
{
pinMode(LED, OUTPUT); //LED Arduino sinon clignotement faible
pinMode(A0,INPUT_PULLUP); //bouton RAZ
Serial.begin(9600); //debug monitor
Serial.println("");
Serial.print("METEO Version");
Serial.println(VERSION); //affichage programme, version et date de mise à jour
Serial.println("");
Serial.println(DATE);
digitalWrite(LED, HIGH);//allume la Led
Serial.print("LED allumée");
delay(500);
digitalWrite(LED, LOW);//éteint la Led
Serial.println(", éteinte");
delay(500);
digitalWrite(LED, HIGH);//allume la Led
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("METEO Ver ");
lcd.print(VERSION);
lcd.setCursor(0, 1);
lcd.print(DATE);
delay(1000); // pour lire la version et la date
digitalWrite(LED,HIGH);//éteint la led
if (!bmp.begin())
{ //Initialisation du BMP280 sensor addresse 0x76
//Serial.println("Pas de BMP280 !");
lcd.setCursor(0, 3);
lcd.print(F("Pb capteur BMP280"));
//while (1); //sortie
}
if (!aht.begin())
{
Serial.println("AHT20 non détecté ! Vérifiez les connexions.");
//while (1); // Arrêt si erreur
}
Serial.println("AHT20 prêt");
Serial.print(" t=");
Serial.print(bmp.readTemperature());
Serial.print(223); //°
Serial.print("C");
lcd.setCursor(0, 2);
lcd.print(F("t="));
lcd.print(bmp.readTemperature());
lcd.write(223); //affiche le ° de °C
lcd.print(F("C"));
fp=bmp.readPressure()/100.0F;
Pmin=fp-2;
Pmax=fp+2; //adaptation
Serial.print(" p=");
Serial.print(fp);
Serial.print("hPa");
lcd.setCursor(0, 3);
lcd.print(F("p="));
lcd.print(bmp.readPressure()/100.0F);
lcd.print(F("hPa"));
initPlot(); //initialisation caracteres speciaux pour le bargraph
delay(4000); //pour avoir le temps de lire...
lcd.clear(); //discutable
}
void loop()
{
int p;
int lecture;
int humidite;
char buffer[10]; //conversion et arrondis capteur
sensors_event_t humidity, temp; // Objets pour les données
aht.getEvent(&humidity, &temp); // Lecture des valeurs
//ft=bmp.readTemperature(); //lecture BMP280 moins précis +-1°C
ft=temp.temperature; //AHT20 +-0.3°C
if (ft<ftmin) ftmin=ft;
if(ft>ftmax) ftmax=ft;
fp=bmp.readPressure()/100.0F;//BMP280
if (fp>Pmax) Pmax=fp; //adaptation
if (fp<Pmin) Pmin=fp;
//Serial.print("Pmin="); //supprime les Serial.print pour ne pas perdre de temps
//Serial.print(Pmin);
//Serial.print(" Pmax=");
//Serial.println(Pmax);
if(fp>((Pmax-Pmin)*0.50)+Pmin) //ex 1005 (980 à 1010)
{
if(flaghaut==1) //au changement, supprimer la ligne du du haut
{
lcd.setCursor(0, 0);
lcd.print(F(" "));//remplit le haut de vide
for(p=17;p<20;p++)
{
lcd.setCursor(p, 2);
lcd.write(byte(7));//remplit le bas de pleins
}
lcd.setCursor(17, 1);
lcd.print(F(" "));//efface humidité
}
flaghaut=0;
//températures et pression en bas
}
if(fp<((Pmax-Pmin)*0.45)+Pmin) //un petit
{
if(flaghaut==0) //au changement, supprimer celles du bas
{
for(p=0;p<20;p++)
{
lcd.setCursor(p, 3);
lcd.write(byte(7));//remplit le bas de pleins
lcd.setCursor(p, 2);
if (p>=17) lcd.write(byte(7));//remplace humidité avec des pleins
}
}
flaghaut=1;
//températures et pression en haut
}
Graph(); //debug variation de pression toutes les secondes*******************
//on let à jour le graphe toutes les s mais on avance que toutes les 2h24
if (t==0)
{
//Graph(); //variation barométrique
lcd.noBacklight();
delay(500);
lcd.backlight(); //pour prévenir mise à jour
i++;
if (i>=20) i=0; //au bout de 20 caractères, on retourne au début
}
t++; //cycle de 2h24
if (t>TEMPO) t=0; //7800 cycle de 48h: une lettre toutes les 2h24mn
dtostrf(ftmin, 2, 0, buffer); //tmini arrondir pas assez de place dans l'afficheur
if(flaghaut==1) lcd.setCursor(0, 0);//ligne du haut
else lcd.setCursor(0, 3);//ligne du bas
lcd.print(buffer);//affichage tmin
lcd.print(" ");
dtostrf(ft, 1, 1, buffer); //t
lcd.print(buffer); //affichage t
lcd.print("C ");
dtostrf(ftmax, 2, 0, buffer); //tmax
lcd.print(buffer); //affichage tmax
lcd.print(" ");
lcd.print(buffer); //affichage tmax
if(flaghaut==1) lcd.setCursor(12, 0);//ligne du haut
else lcd.setCursor(12, 3);//ligne du bas
dtostrf(fp, 1, 1, buffer);
lcd.print(buffer); //pression barométrique
if(flaghaut==1) lcd.setCursor(17, 0);//ligne du haut
else lcd.setCursor(17, 3);//ligne du bas
if(fp<1000) lcd.print("hPa"); //pas la place pou hPa
else lcd.print("hP"); //pas la place pour hPa
if(flaghaut==1) lcd.setCursor(17, 1);//ligne du haut
else lcd.setCursor(17, 2);//ligne du bas
//lcd.print(temp.temperature); // AHT20
//lcd.print("C ");
humidite=humidity.relative_humidity; //supprime les décimales
lcd.print(humidite); // taux humidité
if(flaghaut==1) lcd.setCursor(19, 1);//ligne du haut
else lcd.setCursor(19, 2);//ligne du bas
lcd.print("%");
if(digitalRead(A0)==LOW) //bouton Reset appuyé
{
ftmin=ft;
ftmax=ft; //remet maxi mini à 0
if(flaghaut==1) lcd.setCursor(4, 0);//ligne du haut
else lcd.setCursor(4, 3);//ligne du bas
lcd.print("----RAZ---- ");//on n'éteint pas l'écran pour garder variation baromètre
delay(2000); //pour avoir le temps de lire
}
delay(500); //1 acquisition par seconde
digitalWrite(LED, LOW);//éteint la Led : le programme est vivant
if (flaghaut==1) lcd.setCursor(11,0);
else lcd.setCursor(11,3);
lcd.print(":"); //_ clignote
delay(500); //500
digitalWrite(LED, HIGH);//allume la Led
}
void Graph() //affiche la variation barométrique en continu
{
int j; //pour parcourir la pression barométrique
//Serial.print(" fp=");
//Serial.print(fp);
//Serial.print(" l=");
//Serial.print((Pmax-Pmin)*i/32.0+Pmin); //debug
//Serial.println("");
lcd.setCursor(i, 3); //ligne du bas
lcd.print(" ");//efface précédent
lcd.setCursor(i+1, 3);
lcd.print("|");//efface suivant
//important pour voir ou en est la progression barométrique: la dernière valeur
for(j=0;j<=7;j++)
{
lcd.setCursor(i, 3);
if(fp>(Pmax-Pmin)*j/32.0+Pmin) lcd.write(byte(j)); //bargraph ligne du bas
}
lcd.setCursor(i, 2); //3e ligne
lcd.print(" ");//efface précédent
lcd.setCursor(i+1, 2);
lcd.print("|");//efface suivant
for(j=8;j<=15;j++)
{
lcd.setCursor(i, 2);
if(fp>(Pmax-Pmin)*j/32.0+Pmin) lcd.write(byte(j-8));
}
lcd.setCursor(i, 1); //2e ligne
lcd.print(" ");//efface précédent
lcd.setCursor(i+1, 1);
lcd.print("|");//efface suivant
for(j=16;j<=23;j++)
{
lcd.setCursor(i, 1);
if(fp>(Pmax-Pmin)*j/32.0+Pmin) lcd.write(byte(j-16));
}
lcd.setCursor(i, 0); //1e ligne
lcd.print(" ");//efface précédent
lcd.setCursor(i+1, 0);
lcd.print("|");//efface suivant
for(j=24;j<=31;j++)
{
lcd.setCursor(i,0);
if(fp>(Pmax-Pmin)*j/32.0+Pmin) lcd.write(byte(j-24));
}
}
void initPlot()
{
// symboles requis pour le bargraph
byte row8[8] = {0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte row7[8] = {0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte row6[8] = {0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte row5[8] = {0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte row4[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111};
byte row3[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111};
byte row2[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111};
byte row1[8] = {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111};
lcd.createChar(7, row8);//lcd.createChar(0, row8)
lcd.createChar(0, row1);
lcd.createChar(1, row2);
lcd.createChar(2, row3);
lcd.createChar(3, row4);
lcd.createChar(4, row5);
lcd.createChar(5, row6);
lcd.createChar(6, row7);
}
LCD 20×4: ligne Tmini/T/Tmax et P; trois lignes pour la tendance barométrique avec 20 colonnes couvrant 48 h (1 barre/2 h 24).
Auto‑calibrage: Pmin/Pmax glissants pour adapter l’échelle au site; bascule des lignes selon seuils 1/3–2/3 pour maximiser l’espace tracé.
Minimiser l’auto‑échauffement: éloigner capteurs de l’afficheur et du MCU; consigner les données sur 24 h pour vérifier la dérive.
Prévoir RAZ Tmin/Tmax par bouton fugitif (A0‑GND) et envisager persistance EEPROM avec garde‑fous d’usure.
Quelle précision viser en hobbyiste? ±1 hPa pression, ±0.3 °C avec AHT20.
Comment fiabiliser l’I2C long? 50 kHz + pull‑up 2.2 kΩ; au‑delà, bus différentiel.
Pourquoi l’auto‑calibrage Pmin/Pmax? Pour adapter l’échelle à l’altitude/site sans réglages manuels, stabilisation en quelques jours.
Conserver les min/max après coupure? Sauvegarde en EEPROM au boot/shutdown avec détection de changement significatif.