Skip to main content

CRÉER SA STATION MÉTÉO À L’AIDE DU RASPBERRY PI ET DE SON ÉCRAN 3.5’’

ÉTAPE 1 : INSTALLER LE RASPBERRY PI ET SON ÉCRAN LCD 3.5’’

image.png

Nous allons nous baser sur cet écran, disponible sur Amazon : https://www.amazon.fr/gp/product/B07ZQCM57G?pf_rd_p=3369e5a6-6989-43dc-ad01-b2b5ee1dcd12&pf_rd_r=V9EJBB7VE4VDETNKZ50Y ainsi que le raspberry pi 4.
Lors de ce tutoriel, nous allons supposer que votre Raspberry Pi ainsi que son écran LCD 3.5’’ sont déjà correctement installés.
Si tel n’est pas le cas, v
oici tout de même l’installation simplifiée que vous pourrez retrouver ICI de manière plus détaillée :

sudo su
rm -rf LCD-show
git clone https://github.com/goodtft/LCD-show.git
chmod -R 755 LCD-show
cd LCD-show/
./MHS35-show 90

 

ÉTAPE 2 : RÉFLÉCHIR SON ARCHITECTURE

image.png

Pour une station météo, les données essentielles sont : les prévisions météorologiques ainsi que la température et l’humidité extérieure.Les prévisions météorologiques seront téléchargées du site OpenWeatherMap. La température extérieure sera mesurée par un capteur Bluetooth BeeWi-BBW200. Ce capteur a été choisi, car la communauté open source a développé une librairie d’accès python et que c’est un produit français facile à trouver. Toutes les données seront stockées dans une base de données MySQL/MariaDB (la base de données utilisée sera MariaDB, mais certains packages continuent à porter le nom MySQL).

Le capteur BeeWi peut être acheté ICI

ÉTAPE 3 : CRÉER LE PROJET

La première chose à faire est de créer le répertoire qui accueillera notre projet :


1
2
mkdir ~/meteo
cd ~/meteo


Créons maintenant notre fichier de configuration meteo.ini :


1
nano ./meteo.ini


Copier, coller dedans le contenu ci-dessous :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[display]
dimension = 320,480
fullscreen = true
refresh = 60
font = /home/pi/meteo/fonts/MeteoFont.ttf
img_dir = /home/pi/meteo/img/

[mysql]
user = meteo
database = meteo
password = meteo
location = localhost

[sensor_0]
type = BeeWi
UUID = a8b3fb43-4834-4051-89d0-3de95cddd318
MAC_ADDRESS = XX:XX:XX:XX:XX:XX
#A modifier dès que nous aurons récupéré l'adresse MAC du capteur bluetooth
retry = 5

[openweathermap]
url = http://api.openweathermap.org/data/2.5/forecast
APPID = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#A modifier dès que nous aurons récupéré l'APPID du capteur bluetooth
id = XXXXXXX
#A modifier dès que nous aurons récupéré l'id sur le site openweathermap
units = metric

[translation]
days = LUNDI,MARDI,MERCREDI,JEUDI,VENDREDI,SAMEDI,DIMANCHE


Un certain nombre de paquets sera utilisé pour réaliser ce projet qu'il faut installer :


1
2
3
4
apt update
apt -y upgrade
apt -y install mariadb-server-10.0 mariadb-client-10.0 python3-mysql.connector
pip3 install pexpect nose pygatt


ÉTAPE 4 : CONFIGURER MYSQL/MARIADB

Pour stocker les informations, nous allons créer 2 tables : openweathermap et sensor_data qui nous permettront de stocker les prévisions météo OpenWeatherMap et le relevé des températures.

Voici à quoi elles ressembleront une fois créées :

database_tables_openweathermap

database_tables_sensor_data

Pour la création des tables, arrêter le service SQL :


1
systemctl stop mysql


Création du script de changement de mot de passe meteo.passwd.sql :


1
nano ./meteo.passwd.sql


Puis copier, coller le contenu suivant et sauvegarder :


1
ALTER USER 'root'@'localhost' = PASSWORD('Secret')


Configuration du mot de passe root pour MySQL/MariaDB :


1
mysqld --init-file=/home/pi/meteo/meteo.passwd.sql &


Création du script de création de la base de données, des tables ainsi que de l’utilisateur meteo.database.sql :


1
nano ./meteo.database.sql


Puis copier, coller le contenu suivant et sauvegarder :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE DATABASE meteo;
CREATE USER 'meteo'@'%' IDENTIFIED BY 'meteo';
GRANT ALL PRIVILEGES ON meteo.* TO 'meteo'@'%';
FLUSH PRIVILEGES;
CREATE TABLE meteo.openweathermap (
date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
temperature decimal(3,1) DEFAULT NULL,
pressure decimal(6,2) DEFAULT NULL,
humidity decimal(3,0) DEFAULT NULL,
icon char(3) DEFAULT NULL,
PRIMARY KEY (date)
);
CREATE TABLE meteo.sensor_data (
sensor_id tinyint(3) unsigned NOT NULL,
date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
temperature decimal(3,1) DEFAULT NULL,
humidity decimal(3,0) DEFAULT NULL,
battery decimal(3,0) DEFAULT NULL,
PRIMARY KEY (sensor_id,date)
);


Exécutons maintenant le script dans la base de données MySQL/MariaDB :


1
sudo mysql -u root -p Secret < meteo.database.sql


ÉTAPE 5 : ACCÉDER AUX INFORMATIONS OPENWEATHERMAP

OpenWeaterMap

prevision

Les données qui nous intéresseront pour ce projet sont :

  • la température (list[0].main.temp) ;
  • la pression atmosphérique (list[0].main.pressure) ;
  • l’humidité (list[0].main.humidity) ;
  • l’icône de prévision météo (list[0].weather[0].icon) ;
  • la date et l’heure de la prévision (list[0].dt_txt).

Téléchargeons la liste de villes :


1
wget http://bulk.openweathermap.org/sample/city.list.json.gz


Extrayons le fichier et récupérons le numéro de ville (id) :


1
2
gunzip city.list.json.gz
nano city.list.json


Par exemple, l'id de Vernon dans le 27 est : 6453910


1
2
3
4
5
6
7
8
9
  {
"id": 6453910,
"name": "Vernon",
"country": "FR",
"coord": {
"lon": 1.48333,
"lat": 49.083328
}
},


Pour pouvoir utiliser le service OpenWeatherMap, il faut nous enregistrer pour récupérer une clef d’accès APPID :

https://home.openweathermap.org/users/sign_up.

Avant d’aller plus loin, on teste que tout fonctionne bien en tapant l’URL dans son navigateur :

http://api.openweathermap.org/data/2.5/forecast?id=xxxxx&APPID=xxxxxxxxxxxxxxxxxxxxxx&units=metric

Il faut bien sûr saisir l’id et l’APPID correctement.

Dans le fichier de configuration meteo.ini, positionner l’id et l’APPID OpenWeatherMap. Editer le fichier de configuration meteo.ini :


1
nano ./meteo.ini


Puis copier, coller le contenu suivant et sauvegarder :


1
2
3
4
5
[openweathermap]
url = http://api.openweathermap.org/data/2.5/forecast
APPID = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
id = XXXXXXX
units = metric


ÉTAPE 6 : CODER l'ACCÈS À OPENWEATHERMAP

Maintenant que nous avons accès aux données météo, nous allons créer un script récupérant ces informations pour les stocker dans notre base de données Weather.py :


1
nano ./Weather.py


Puis copier, coller le contenu suivant et sauvegarder :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#! /usr/bin/env python3
import configparser, json, datetime
import urllib.request
import mysql.connector

# Lecture du fichier de configuration
settings = configparser.ConfigParser()
settings._interpolation = configparser.ExtendedInterpolation()
settings.read('/home/pi/meteo/meteo.ini')

# Construction de l'url OpenWeatherMap
url_openweathermap = 'http://api.openweathermap.org/data/2.5/forecast?id=2976879&APPID=45b086df1d0050022426ab4ce5f2e9d2&units=metric'

#+ '?id=' + settings.get('openweathermap', 'id') \
#+ '&APPID=' + settings.get('openweathermap', 'APPID') \
#+ '&units=' + settings.get('openweathermap', 'units')

try:

# Récupération des prévisions météo et décodage JSON
webURL = urllib.request.urlopen(url_openweathermap)
data = webURL.read()
encoding = webURL.info().get_content_charset('utf-8')
infos=json.loads(data.decode(encoding))

except:

print("error reading url: "+url_openweathermap);
exit(1)

# Connexion à la base de données à l'aide des paramètres de configuration

cnx = mysql.connector.connect(user=settings.get('mysql', 'user'),
database=settings.get('mysql', 'database'),
password=settings.get('mysql', 'password'),
host=settings.get('mysql', 'location'))
cursor = cnx.cursor()

# Pour chaque prévision météo, on écrit ou remplace l'information en base

for item in infos["list"]:
date=item["dt_txt"]
icon=item["weather"][0]["icon"]
temperature=item["main"]["temp"]
humidity=item["main"]["humidity"]
pressure=item["main"]["pressure"]
cursor.execute('REPLACE INTO openweathermap VALUES (%s,%s,%s,%s,%s);',
(date,str(temperature),str(pressure),str(humidity),icon))

# validation et fermeture de la connexion à la base de données

cnx.commit()
cursor.close()
cnx.close()


Notez bien que la requête SQL est un REPLACE et non pas un INSERT.
Cette requête propre à MySQL/MariaDB permet de remplacer la ligne si elle existe déjà, l’insérer sinon.

Testons notre programme :

e95YPrRT5RRKa0tb-image-1580137361204.png

ÉTAPE 7 : ACCÉDER AUX INFORMATIONS DU CAPTEUR BEEWI-BBW200

Tout d’abord, il nous faut récupérer l’adresse Bluetooth du capteur :

uZ0BZIwqYN5d19B2-image-1580137504626.png

Dans cet exemple, l’adresse Bluetooth de mon capteur est F0:C7:00:00:00:00 (il s’agit d’une adresse factice).

Nous allons maintenant télécharger la librairie d’accès au capteur :

FGM9Rd2ZDwFNCIz6-image-1580137542274.png

Insérer l’adresse Bluetooth dans le fichier Constants.py :


1
nano ./utils/Constants.py


Puis copier, coller le contenu suivant et sauvegarder :


1
2
CHARACTERISTIC_UUID = "a8b3fb43-4834-4051-89d0-3de95cddd318"
MAC_ADDRESS = "F0:C7:00:00:00:00"


Test :

20Eet7S8RswWqkDL-image-1580137741317.png

Copions la partie python dont nous avons besoin pour notre projet :


1
2
cp -fr * /home/pi/meteo
cd /home/pi/meteo


Le principal problème de cette librairie est que l’adresse Bluetooth ainsi que l’identifiant de l’appareil sont codés « en dur » dans le fichier utils/Constants.py. Nous voulons pouvoir configurer ces informations dans notre fichier meteo.ini. Modifions donc la fonction se chargeant de la lecture du capteur pour lui passer ces informations : reader/GattSensorReader.py :


1
nano ./reader/GattSensorReader.py


Puis copier, coller le contenu suivant et sauvegarder :


1
2
3
4
5
6
7
8
9
10
11
12
import pygatt
class GattSensorReader():
def readRawData(self,MAC_ADDRESS,CHARACTERISTIC_UUID):
if(MAC_ADDRESS == ""):
raise ValueError('Mac address missing')
if(CHARACTERISTIC_UUID == ""):
raise ValueError('Characteristic UUID missing')
adapter = pygatt.GATTToolBackend()
adapter.start()
device = adapter.connect(MAC_ADDRESS, 15)
value = device.char_read(CHARACTERISTIC_UUID)
return ''.join('{:02x} '.format(x) for x in value)


Il faut maintenant donner l’adresse Bluetooth de notre capteur à notre fichier de configuration meteo.ini :


1
nano ./meteo.ini


Puis copier, coller le contenu suivant (en ayant pris compte la bonne adresse bluetooth du capteur) et sauvegarder :


1
2
3
4
5
[sensor_0]
type = BeeWi
UUID = a8b3fb43-4834-4051-89d0-3de95cddd318
MAC_ADDRESS = F0:C7:00:00:00:00
retry = 5


ÉTAPE 8 : CODER l'ACCÈS AU CAPTEUR BEEWI-BBW200

Maintenant que nous avons accès aux données du capteur BeeWi, nous allons créer un script récupérant ces informations pour les stocker dans notre base de données BeeWi.py :


1
nano ./BeeWi.py


Puis copier, coller le contenu suivant et sauvegarder :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#! /usr/bin/env python3
from reader.GattSensorReader import GattSensorReader
from utils.ParseUtils import ParseUtils
import mysql.connector, time, configparser

# Lecture du fichier de configuration
settings = configparser.ConfigParser()
settings._interpolation = configparser.ExtendedInterpolation()
settings.read('/home/pi/meteo/meteo.ini')

# Connexion à la base de données à l'aide des paramètres de configuration
cnx = mysql.connector.connect(user=settings.get('mysql', 'user'),
database=settings.get('mysql', 'database'),
password=settings.get('mysql', 'password'),
host=settings.get('mysql', 'location'))
cursor = cnx.cursor()

# Parfois, il est difficile de connecter le capteur, on fait donc plusieurs tentatives
try_count=int(settings.get('sensor_0', 'retry'));
while try_count>0 :
try:
sensorReader = GattSensorReader()
rawData = sensorReader.readRawData(settings.get('sensor_0', 'MAC_ADDRESS'),settings.get('sensor_0', 'UUID'))
sensorData = ParseUtils.parseSensorData(rawData.split(" "))
try_count=0

# On écrit les données en base
cursor.execute('INSERT INTO sensor_data (sensor_id,temperature,humidity,battery) VALUES (%s,%s,%s,%s);', ('1',str(sensorData.temperature),str(sensorData.humidity),str(sensorData.battery)))
except:
print(time.strftime("%c") + " : Tentative "+str(try_count))
try_count-=1
if (try_count==0):
print(time.strftime("%c") +" Lecture du capteur impossible")

# validation et fermeture de la connexion à la base de données
cnx.commit()
cursor.close()
cnx.close()


Testons notre programme :

MvYU4CyHQwPK37G0-image-1580138297583.png

ÉTAPE 9 : PRÉPARER L'AFFICHAGE

affichage

Réfléchissons maintenant à l’affichage des données. Quelle orientation pour l’écran LCD ? Quelle police de caractères ? Quelles icônes à afficher ?

Pour la police de caractères, j’ai créé la mienne en suivant le tutoriel « Créer votre fonte symbole » de    Linux Pratique n°101. Ne pas oublier les symboles ° et %.

Pour les icônes, j’ai trouvé des icônes libres d’utilisation que j’ai redimensionnées : https://d3stroy.deviantart.com/art/SILq-Weather-Icons-356609017.

Téléchargement des ressources :


1
2
wget -q http://colas.sebastien.free.fr/projets/meteo/ressources.tar.gz
tar -xzf ressources.tar.gz


ÉTAPE 10 : CODER L'AFFICHAGE DES DONNÉES MÉTÉORLOGIQUES

Tout est prêt, il ne nous reste plus qu’à coder le programme principal qui se chargera de l’affichage sur l’écran LCD. Ici, la librairie graphique qui a été retenue est pygame tout simplement, car je la maîtrise. Attention de bien commenter le code pour pouvoir le maintenir facilement. Créer le fichier Meteo.py :


1
nano ./Meteo.py


Puis copier, coller le contenu suivant et sauvegarder :


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#! /usr/bin/env python3

seconds_since_last_refesh = refresh_rate

# Paramétrage MySQL
mysql_config={
'user': settings.get('mysql', 'user'),
'password': settings.get('mysql', 'password'),
'database': settings.get('mysql', 'database'),
'host': settings.get('mysql', 'location')

}# Connexion à MySQL
cnx = mysql.connector.connect(**mysql_config)

# Initialisation de l’affichage
pygame.init()
pygame.mouse.set_visible(0)
screen = pygame.display.set_mode(dimension,fullscreen,32)
font = pygame.font.Font(font_file, 20)
font_big = pygame.font.Font(font_file, 54)

# Le programme ne s'arrête jamais
while True:
now = datetime.datetime.now()

# Mise à jour de l'affichage des données ?
if(seconds_since_last_refesh >= refresh_rate) :
forecast_3d_icons=[]
forecast_icon=[]
txt_temperature="--.-"
txt_humidity="---"
cursor = cnx.cursor()

# Température et humidité extérieures
cursor.execute(sql_sensor)
for (date, temperature, humidity, battery) in cursor:
txt_temperature = "%.1f"%float(temperature) +"°"
txt_humidity = "%.0f"%int(humidity) + "%"

# Date du jour à minuit
midnight_date=now.strftime('%Y-%m-%d 00:00:00');

# prochaine prévision météo
cursor.execute(sql_forecast.replace('{DATE}',now.strftime('%Y-%m-%d %H:%M:%S')))
for (icon,date) in cursor:
forecast_icon.append(icon)

# prévision météo sur les 3 jours à venir
cursor.execute(sql_forecast_3d.replace('{DATE}',midnight_date))
for (icon_3d,date) in cursor:
forecast_3d_icons.append(icon_3d)
seconds_since_last_refesh=0
cursor.close()
cnx.commit()

# On efface l'écran pour le nouvel affichage
screen.fill((0, 0, 0))

# Affichage de l'image de prévision météo
for icon in forecast_icon :

# image_weather = pygame.image.load(img_dir+icon+'.png')
image_weather = pygame.image.load('/home/pi/meteo/img/'+icon+'.png')
screen.blit(image_weather, (-6, 60))

# Affichage de la date en haut
label = font.render(settings.get('translation','days').split(',')[datetime.datetime.today().weekday()]+now.strftime(' %d/%m/%Y'), 1, color_green_lcd)
screen.blit(label, (dimension[0]/2-label.get_width()/2, 2))

# Affichage de l'heure (les secondes seront plus petites)
label = font_big.render(now.strftime('%H:%M'), 1, color_green_lcd)
screen.blit(label, (90, 30))
label = font.render(now.strftime('%S'), 1, color_green_lcd)
screen.blit(label, (240, 64))

# Affichage de la température
label = font_big.render(txt_temperature, 1, color_red_lcd)
screen.blit(label, (dimension[0]-label.get_width()-4, 100))

# Affichage de l'humidité
label = font_big.render(txt_humidity, 1, color_blue_lcd)
screen.blit(label, (dimension[0]-label.get_width(), 190))

# Affichage des prévisions météo sur les 3 prochains jours
index=0
for icon in forecast_3d_icons :
image_weather = pygame.image.load('/home/pi/meteo/img/'+icon+'_1.png')
screen.blit(image_weather, ((index-(index%2))/2*100+5, 260+(index%2)*116))
if (index%2)==0 :
label = font.render(settings.get('translation','days').split(',')[(datetime.datetime.today().weekday()+1+int(index/2))%7][:3], 1, color_green_lcd)
screen.blit(label, (index/2*100+34, 364))
index+=1

# Affichage et attente 1s pour le prochain affichage
pygame.display.flip()
seconds_since_last_refesh+=1
time.sleep(1)
cnx.close()


Testons notre programme :

yXXNFz8ZbfLqn1Jn-image-1580138709213.png

Tout fonctionne, il ne nous reste plus qu’à automatiser le démarrage de notre station météorologique.

Nous allons collecter les données périodiquement grâce au service crontab : la lecture du capteur de température toutes les 5 minutes et les prévisions météorologiques toutes les 15 minutes.

Crééons le fichier de configuration meteo.crontab :


1
nano ./meteo.crontab


Puis copier, coller le contenu suivant et sauvegarder :


1
2
*/5 * * * * pi /home/pi/meteo/BeeWi.py
*/15 * * * * pi /home/pi/meteo/Weather.py


On ajoute les lignes à la crontab avec la commande suivante :


1
cat meteo.crontab | sudo tee -a /etc/crontab


Il ne nous reste plus qu’à désactiver l’économiseur d’écran et à lancer automatiquement notre affichage des données météorologiques. On crée le fichier meteo.autostart :


1
nano ./meteo.autostart


Puis copier, coller le contenu suivant et sauvegarder :


1
2
3
4
@xset s noblank
@xset s off
@xset -dpms
/home/pi/meteo/Meteo.py


On ajoute ces commandes au démarrage de LXDE :


1
cat meteo.autostart >> /etc/xdg/lxsession/LXDE-pi/autostart


Notre station météorologique est désormais opérationnelle. À vous de rajouter ce dont vous avez besoin…
Je citerai comme exemple : un capteur de température intérieure, le support d’autres capteurs, l’affichage des phases de la lune, l’affichage des marées...
Au niveau du code, vous pourrez aussi retirer toutes les constantes de localisation d’élément pour les mettre dans le fichier de configuration.