Mini station météo Arduino

Toujours dans la continuité des projets Arduino, dans le cadre de mon projet global, j’ai commencé la fabrication d’une station météo basée sur le module BME280 avec un arduino nano.

Composants de la station

La station est composée des éléments suivants :

  • arduino nano like, c’est un modèle Elegoo avec un chipset CH340
  • BME280 : c’est un composant que l’on connecte via le protocole I2C et qui permet de récupérer :
      • la température
      • l’humidité
      • la pression atmosphérique
      • l’altitude
  • un écran nokia 5110 pour l’affichage
  • des fils breadboard
  • une platine d’essai

Résultat de recherche d'images pour "elegoo nano"

Schéma

Le schéma est fait comme suit :

Le BME280, en I2C, doit être connecté via les 4 pin suivantes :

  • Vin sur Vin
  • GND sur GND
  • SCK sur A5
  • SDI sur A4

L’écran Nokia que je possède a des pins de 1 à 8, connectée comme suit :

  • Pin 1 : RST sur D6
  • Pin 2 : CE sur D7
  • Pin 3 : DC sur D5
  • Pin 4 : DIN sur D4
  • Pin 5 : CLK sur D3
  • Pin 6 : VCC sur 5V
  • Pin 7 : LIGHT sur GND (pour activer le rétroéclairage, il faut le connecter au GND sinon ne pas le brancher)
  • Pin 8 : GND sur GND

Les boutons serviront à augmenter ou diminuer l’altitude (pour corriger la pression atmosphérique qui est donnée pour le niveau de la mer).

Le bouton 1 sera connecté d’un côté au GND, de l’autre côté à la pin A0 (pour diminuer de 10m en 10m).

Le bouton 2 sera connecté d’un côté au GND, de l’autre côté à la pin A1 (pour augmenter de 10m en 10m).

Ce qui nous donne le schéma de cablage final suivant :

thermo_lcd

Le code source

Je vais vous expliquer quelques lignes du code source avant de vous donner le code complet.

Premièrement, il vous faudra installer les 2 librairies Adafruit suivantes pour les capteurs et le BME280 :

Pour ajouter une librairie à votre programme arduino, il faut aller dans Croquis puis Inclure une bibliothèque puis Ajouter la bibliothèque .zip … Ensuite, sélectionner votre fichier .zip et les bibliothèques seront automatiquement installées.

librairie

Dans le setup du programme, il faut initialiser le BME280.

bool status;
status = bme.begin();

Puis dans le loop principal, pour que l’écran du nokia ne se rafraichisse pas  toutes les secondes (sinon c’est illisible), j’ai choisi de mettre un refresh à 60sec. Super, mais comment gérer les boutons + et – ?

Pour gérer ces boutons, j’ai pris le parti de dire que lors de la première minute de lancement de l’arduino, nous allons pouvoir augmenter/réduire l’altitude par tranche de 10m avant que le code ne reprenne son cours normal. Le refresh de l’écran est alors de 100ms mais c’est suffisant pour ajuster. J’affiche les secondes restantes à l’écran également.
Les fonctions printValuesStart() et printValues() sont les fonctions d’affichage des données.

time = millis();
//phase d'initialisation pour paramétrage altitude
if (time < 60000) {
valm = digitalRead(14);
if (valm == LOW) {
altitude = altitude - 10;
delay(100);
}

valh = digitalRead(15);
if (valh == LOW) {
altitude = altitude + 10;
delay(100);
}
LcdInitialise();
LcdClear();
printValuesStart();
delay(100);
}
//phase normale
else {
LcdInitialise();
LcdClear();
printValues();
delay(60000);
}

La fonction de correction de la pression en fonction de l’altitude se fait grâce à une formule très savante que nous restranscrivons en code.

//fonction qui corrige la pression en fonction de l'altitude
double getP(double Pact, double temp) {
return Pact * pow((1 - ((0.0065 * altitude) / (temp + 0.0065 * altitude + 273.15))), -5.257);
}

L’affichage des données se fait via la fonction printValues() qui récupère les valeurs du BME280 et les affiche. Pour mettre en forme l’écran, je joue avec le nombre de caractères par ligne (12 maxi). Aussi, il faut convertir les valeurs (température, humidité, pression) de double/float en string pour qu’elles s’affichent comme il faut.
Pour cela nous utilisons la fonction dtostrf pour formater le texte comme il faut.

char tempBuffer[5];
LcdString("Temp:");
dtostrf(bme.readTemperature(), 4, 1, tempBuffer);
LcdString(tempBuffer);
LcdString(" C ");

P = getP((bme.readPressure() / 100.0F), bme.readTemperature());
char PresBuffer[5];
LcdString("Pres:");
dtostrf(P, 4, 0, PresBuffer);
LcdString(PresBuffer);
LcdString("hPa");

char humBuffer[5];
LcdString("Humi:");
dtostrf(bme.readHumidity(), 2, 0, humBuffer);
LcdString(humBuffer);
LcdString("%    ");

char altiBuffer[4];
LcdString("Alti:");
dtostrf(altitude, 4, 0,  altiBuffer);
LcdString(altiBuffer);
LcdString("m  ");

int forecast = sample(P);
LcdString("Prev:");
LcdString(weather[forecast]);

Les prévisions méteo

Maintenant que l’on a un baromètre, nous allons pouvoir analyser un minimum les différences de pression au fur et à mesure du temps passé pour savoir si nous avons une dépréssion ou un anticyclone qui arrive (augmentation ou diminution de la pression atmosphérique dans des intervalles donnés.

En fouillant le net, je suis tombé sur ce topic de forum qui donne un code prêt à l’emploi. Qu’à cela ne tienne, je teste le code et cela semble fonctionner parfaitement !
Il suffit d’appeler la fonction sample(P) avec P = pression atmosphérique corrigée.
Le code est le suivant :


//prévision meteo
const char *weather[] = { "stable", "soleil", "nuage", "instab", "tempete", "indef" };
enum FORECAST
{
STABLE = 0,     // "Stable Weather Pattern"
SUNNY = 1,      // "Slowly rising Good Weather", "Clear/Sunny "
CLOUDY = 2,     // "Slowly falling L-Pressure ", "Cloudy/Rain "
UNSTABLE = 3,   // "Quickly rising H-Press",     "Not Stable"
THUNDERSTORM = 4, // "Quickly falling L-Press",    "Thunderstorm"
UNKNOWN = 5     // "Unknown (More Time needed)
};
// for forecast
float lastPressure = -1;
float lastTemp = -1;
int lastForecast = -1;
const int LAST_SAMPLES_COUNT = 5;
float lastPressureSamples[LAST_SAMPLES_COUNT];
// this CONVERSION_FACTOR is used to convert from Pa to kPa in forecast algorithm
// get kPa/h be dividing hPa by 10
#define CONVERSION_FACTOR (1.0/10.0)
int minuteCount = 0;
bool firstRound = true;
// average value is used in forecast algorithm.
float pressureAvg;
// average after 2 hours is used as reference value for the next iteration.
float pressureAvg2;
float dP_dt;

float getLastPressureSamplesAverage()
{
float lastPressureSamplesAverage = 0;
for (int i = 0; i < LAST_SAMPLES_COUNT; i++) { lastPressureSamplesAverage += lastPressureSamples[i]; } lastPressureSamplesAverage /= LAST_SAMPLES_COUNT; return lastPressureSamplesAverage; } // Algorithm found here // http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf // Pressure in hPa -->  forecast done by calculating kPa/h
int sample(float pressure)
{
// Calculate the average of the last n minutes.
int index = minuteCount % LAST_SAMPLES_COUNT;
lastPressureSamples[index] = pressure;

minuteCount++;
if (minuteCount > 185)
{
minuteCount = 6;
}

if (minuteCount == 5)
{
pressureAvg = getLastPressureSamplesAverage();
}
else if (minuteCount == 35)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change * 2; // note this is for t = 0.5hour
}
else
{
dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value.
}
}
else if (minuteCount == 65)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) //first time initial 3 hour
{
dP_dt = change; //note this is for t = 1 hour
}
else
{
dP_dt = change / 2; //divide by 2 as this is the difference in time from 0 value
}
}
else if (minuteCount == 95)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 1.5; // note this is for t = 1.5 hour
}
else
{
dP_dt = change / 2.5; // divide by 2.5 as this is the difference in time from 0 value
}
}
else if (minuteCount == 125)
{
float lastPressureAvg = getLastPressureSamplesAverage();
pressureAvg2 = lastPressureAvg; // store for later use.
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 2; // note this is for t = 2 hour
}
else
{
dP_dt = change / 3; // divide by 3 as this is the difference in time from 0 value
}
}
else if (minuteCount == 155)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 2.5; // note this is for t = 2.5 hour
}
else
{
dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value
}
}
else if (minuteCount == 185)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 3; // note this is for t = 3 hour
}
else
{
dP_dt = change / 4; // divide by 4 as this is the difference in time from 0 value
}
pressureAvg = pressureAvg2; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past.
firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop.
}

int forecast = UNKNOWN;
if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval.
{
forecast = UNKNOWN;
}
else if (dP_dt < (-0.25)) { forecast = THUNDERSTORM; } else if (dP_dt > 0.25)
{
forecast = UNSTABLE;
}
else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05))) { forecast = CLOUDY; } else if ((dP_dt > 0.05) && (dP_dt < 0.25)) { forecast = SUNNY; } else if ((dP_dt > (-0.05)) && (dP_dt < 0.05)) { forecast = STABLE; } else { forecast = UNKNOWN; } // uncomment when debugging //Serial.print(F("Forecast at minute ")); //Serial.print(minuteCount); //Serial.print(F(" dP/dt = ")); //Serial.print(dP_dt); //Serial.print(F("kPa/h --> "));
//Serial.println(weather[forecast]);

return forecast;
}

Au démarrage, voici l’écran :

station1

Après quelques minutes de fonctionnement, voici les prévisions à jour sur l’écran :

station2

Code complet

Le code complet du programme est à télécharger ici : code_station_meteo_complet ou sur Github :

Little weather station with BME280
https://github.com/doddyfab/Arduino_WeatherStation
0 forks.
0 stars.
0 open issues.

Recent commits:

La suite ?

La suite se fera avec Aurélie pour imprimer une boite en 3D et mettre notre montage en réel à l’intérieur. Cela fera l’objet d’un article sur le sujet.

3 Comments

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.